summaryrefslogtreecommitdiff
path: root/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/chan.c103
-rw-r--r--net/wireless/core.c97
-rw-r--r--net/wireless/core.h4
-rw-r--r--net/wireless/debugfs.c33
-rw-r--r--net/wireless/ethtool.c2
-rw-r--r--net/wireless/mlme.c19
-rw-r--r--net/wireless/nl80211.c831
-rw-r--r--net/wireless/reg.c76
-rw-r--r--net/wireless/scan.c32
-rw-r--r--net/wireless/sme.c5
-rw-r--r--net/wireless/sysfs.c2
-rw-r--r--net/wireless/trace.h112
-rw-r--r--net/wireless/util.c60
13 files changed, 1059 insertions, 317 deletions
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 193734b7f9dc..68221b1ab45e 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -100,6 +100,11 @@ static u32 cfg80211_get_end_freq(const struct cfg80211_chan_def *chandef,
punctured = 0) : (punctured >>= 1))) \
if (!(punctured & 1))
+#define for_each_s1g_subchan(chandef, freq_khz) \
+ for (freq_khz = cfg80211_s1g_get_start_freq_khz(chandef); \
+ freq_khz <= cfg80211_s1g_get_end_freq_khz(chandef); \
+ freq_khz += MHZ_TO_KHZ(1))
+
struct cfg80211_per_bw_puncturing_values {
u8 len;
const u16 *valid_values;
@@ -336,8 +341,7 @@ static bool cfg80211_valid_center_freq(u32 center,
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
{
- u32 control_freq, oper_freq;
- int oper_width, control_width;
+ u32 control_freq, control_freq_khz, start_khz, end_khz;
if (!chandef->chan)
return false;
@@ -363,27 +367,16 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
case NL80211_CHAN_WIDTH_4:
case NL80211_CHAN_WIDTH_8:
case NL80211_CHAN_WIDTH_16:
- if (chandef->chan->band != NL80211_BAND_S1GHZ)
- return false;
-
- control_freq = ieee80211_channel_to_khz(chandef->chan);
- oper_freq = ieee80211_chandef_to_khz(chandef);
- control_width = nl80211_chan_width_to_mhz(
- ieee80211_s1g_channel_width(
- chandef->chan));
- oper_width = cfg80211_chandef_get_width(chandef);
-
- if (oper_width < 0 || control_width < 0)
+ if (!cfg80211_chandef_is_s1g(chandef))
return false;
if (chandef->center_freq2)
return false;
- if (control_freq + MHZ_TO_KHZ(control_width) / 2 >
- oper_freq + MHZ_TO_KHZ(oper_width) / 2)
- return false;
+ control_freq_khz = ieee80211_channel_to_khz(chandef->chan);
+ start_khz = cfg80211_s1g_get_start_freq_khz(chandef);
+ end_khz = cfg80211_s1g_get_end_freq_khz(chandef);
- if (control_freq - MHZ_TO_KHZ(control_width) / 2 <
- oper_freq - MHZ_TO_KHZ(oper_width) / 2)
+ if (control_freq_khz < start_khz || control_freq_khz > end_khz)
return false;
break;
case NL80211_CHAN_WIDTH_80P80:
@@ -461,6 +454,9 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
!cfg80211_edmg_chandef_valid(chandef))
return false;
+ if (!cfg80211_chandef_is_s1g(chandef) && chandef->s1g_primary_2mhz)
+ return false;
+
return valid_puncturing_bitmap(chandef);
}
EXPORT_SYMBOL(cfg80211_chandef_valid);
@@ -725,6 +721,10 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
{
struct ieee80211_channel *c;
+ /* DFS is not required for S1G */
+ if (cfg80211_chandef_is_s1g(chandef))
+ return 0;
+
for_each_subchan(chandef, freq, cf) {
c = ieee80211_get_channel_khz(wiphy, freq);
if (!c)
@@ -1130,6 +1130,55 @@ static bool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels,
return true;
}
+static bool cfg80211_s1g_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ u32 freq_khz;
+ const struct ieee80211_channel *chan;
+ u32 pri_khz = ieee80211_channel_to_khz(chandef->chan);
+ u32 end_khz = cfg80211_s1g_get_end_freq_khz(chandef);
+ u32 start_khz = cfg80211_s1g_get_start_freq_khz(chandef);
+ int width_mhz = cfg80211_chandef_get_width(chandef);
+ u32 prohibited_flags = IEEE80211_CHAN_DISABLED;
+
+ if (width_mhz >= 16)
+ prohibited_flags |= IEEE80211_CHAN_NO_16MHZ;
+ if (width_mhz >= 8)
+ prohibited_flags |= IEEE80211_CHAN_NO_8MHZ;
+ if (width_mhz >= 4)
+ prohibited_flags |= IEEE80211_CHAN_NO_4MHZ;
+
+ if (chandef->chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY)
+ return false;
+
+ if (pri_khz < start_khz || pri_khz > end_khz)
+ return false;
+
+ for_each_s1g_subchan(chandef, freq_khz) {
+ chan = ieee80211_get_channel_khz(wiphy, freq_khz);
+ if (!chan || (chan->flags & prohibited_flags))
+ return false;
+ }
+
+ if (chandef->s1g_primary_2mhz) {
+ u32 sib_khz;
+ const struct ieee80211_channel *sibling;
+
+ sibling = cfg80211_s1g_get_primary_sibling(wiphy, chandef);
+ if (!sibling)
+ return false;
+
+ if (sibling->flags & IEEE80211_CHAN_S1G_NO_PRIMARY)
+ return false;
+
+ sib_khz = ieee80211_channel_to_khz(sibling);
+ if (sib_khz < start_khz || sib_khz > end_khz)
+ return false;
+ }
+
+ return true;
+}
+
bool _cfg80211_chandef_usable(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
u32 prohibited_flags,
@@ -1154,6 +1203,9 @@ bool _cfg80211_chandef_usable(struct wiphy *wiphy,
ext_nss_cap = __le16_to_cpu(vht_cap->vht_mcs.tx_highest) &
IEEE80211_VHT_EXT_NSS_BW_CAPABLE;
+ if (cfg80211_chandef_is_s1g(chandef))
+ return cfg80211_s1g_usable(wiphy, chandef);
+
if (edmg_cap->channels &&
!cfg80211_edmg_usable(wiphy,
chandef->edmg.channels,
@@ -1165,21 +1217,6 @@ bool _cfg80211_chandef_usable(struct wiphy *wiphy,
control_freq = chandef->chan->center_freq;
switch (chandef->width) {
- case NL80211_CHAN_WIDTH_1:
- width = 1;
- break;
- case NL80211_CHAN_WIDTH_2:
- width = 2;
- break;
- case NL80211_CHAN_WIDTH_4:
- width = 4;
- break;
- case NL80211_CHAN_WIDTH_8:
- width = 8;
- break;
- case NL80211_CHAN_WIDTH_16:
- width = 16;
- break;
case NL80211_CHAN_WIDTH_5:
width = 5;
break;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index a7e2931ffb2e..9a420d627d3c 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -34,6 +34,9 @@
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
+/* maximum length of radio debugfs directory name */
+#define RADIO_DEBUGFSDIR_MAX_LEN 8
+
MODULE_AUTHOR("Johannes Berg");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("wireless configuration support");
@@ -428,7 +431,7 @@ static void cfg80211_wiphy_work(struct work_struct *work)
if (wk) {
list_del_init(&wk->entry);
if (!list_empty(&rdev->wiphy_work_list))
- queue_work(system_unbound_wq, work);
+ queue_work(system_dfl_wq, work);
spin_unlock_irq(&rdev->wiphy_work_lock);
trace_wiphy_work_run(&rdev->wiphy, wk);
@@ -1018,6 +1021,15 @@ int wiphy_register(struct wiphy *wiphy)
rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH;
+ if (rdev->wiphy.bss_param_support & WIPHY_BSS_PARAM_P2P_CTWINDOW)
+ rdev->wiphy.features |= NL80211_FEATURE_P2P_GO_CTWIN;
+ else if (rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)
+ rdev->wiphy.bss_param_support |= WIPHY_BSS_PARAM_P2P_CTWINDOW;
+ if (rdev->wiphy.bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS)
+ rdev->wiphy.features |= NL80211_FEATURE_P2P_GO_OPPPS;
+ else if (rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)
+ rdev->wiphy.bss_param_support |= WIPHY_BSS_PARAM_P2P_OPPPS;
+
rtnl_lock();
wiphy_lock(&rdev->wiphy);
res = device_add(&rdev->wiphy.dev);
@@ -1033,6 +1045,18 @@ int wiphy_register(struct wiphy *wiphy)
/* add to debugfs */
rdev->wiphy.debugfsdir = debugfs_create_dir(wiphy_name(&rdev->wiphy),
ieee80211_debugfs_dir);
+ if (wiphy->n_radio > 0) {
+ int idx;
+ char radio_name[RADIO_DEBUGFSDIR_MAX_LEN];
+
+ for (idx = 0; idx < wiphy->n_radio; idx++) {
+ scnprintf(radio_name, sizeof(radio_name), "radio%d",
+ idx);
+ wiphy->radio_cfg[idx].radio_debugfsdir =
+ debugfs_create_dir(radio_name,
+ rdev->wiphy.debugfsdir);
+ }
+ }
cfg80211_debugfs_rdev_add(rdev);
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
@@ -1042,12 +1066,12 @@ int wiphy_register(struct wiphy *wiphy)
wiphy_regulatory_register(wiphy);
if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
- struct regulatory_request request;
-
- request.wiphy_idx = get_wiphy_idx(wiphy);
- request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
- request.alpha2[0] = '9';
- request.alpha2[1] = '9';
+ struct regulatory_request request = {
+ .wiphy_idx = get_wiphy_idx(wiphy),
+ .initiator = NL80211_REGDOM_SET_BY_DRIVER,
+ .alpha2[0] = '9',
+ .alpha2[1] = '9',
+ };
nl80211_send_reg_change_event(&request);
}
@@ -1356,6 +1380,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
cfg80211_pmsr_wdev_down(wdev);
+ cfg80211_stop_radar_detection(wdev);
cfg80211_stop_background_radar_detection(wdev);
switch (wdev->iftype) {
@@ -1689,7 +1714,7 @@ void wiphy_work_queue(struct wiphy *wiphy, struct wiphy_work *work)
list_add_tail(&work->entry, &rdev->wiphy_work_list);
spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags);
- queue_work(system_unbound_wq, &rdev->wiphy_work);
+ queue_work(system_dfl_wq, &rdev->wiphy_work);
}
EXPORT_SYMBOL_GPL(wiphy_work_queue);
@@ -1778,6 +1803,62 @@ bool wiphy_delayed_work_pending(struct wiphy *wiphy,
}
EXPORT_SYMBOL_GPL(wiphy_delayed_work_pending);
+enum hrtimer_restart wiphy_hrtimer_work_timer(struct hrtimer *t)
+{
+ struct wiphy_hrtimer_work *hrwork =
+ container_of(t, struct wiphy_hrtimer_work, timer);
+
+ wiphy_work_queue(hrwork->wiphy, &hrwork->work);
+
+ return HRTIMER_NORESTART;
+}
+EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_timer);
+
+void wiphy_hrtimer_work_queue(struct wiphy *wiphy,
+ struct wiphy_hrtimer_work *hrwork,
+ ktime_t delay)
+{
+ trace_wiphy_hrtimer_work_queue(wiphy, &hrwork->work, delay);
+
+ if (!delay) {
+ hrtimer_cancel(&hrwork->timer);
+ wiphy_work_queue(wiphy, &hrwork->work);
+ return;
+ }
+
+ hrwork->wiphy = wiphy;
+ hrtimer_start_range_ns(&hrwork->timer, delay,
+ 1000 * NSEC_PER_USEC, HRTIMER_MODE_REL);
+}
+EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_queue);
+
+void wiphy_hrtimer_work_cancel(struct wiphy *wiphy,
+ struct wiphy_hrtimer_work *hrwork)
+{
+ lockdep_assert_held(&wiphy->mtx);
+
+ hrtimer_cancel(&hrwork->timer);
+ wiphy_work_cancel(wiphy, &hrwork->work);
+}
+EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_cancel);
+
+void wiphy_hrtimer_work_flush(struct wiphy *wiphy,
+ struct wiphy_hrtimer_work *hrwork)
+{
+ lockdep_assert_held(&wiphy->mtx);
+
+ hrtimer_cancel(&hrwork->timer);
+ wiphy_work_flush(wiphy, &hrwork->work);
+}
+EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_flush);
+
+bool wiphy_hrtimer_work_pending(struct wiphy *wiphy,
+ struct wiphy_hrtimer_work *hrwork)
+{
+ return hrtimer_is_queued(&hrwork->timer);
+}
+EXPORT_SYMBOL_GPL(wiphy_hrtimer_work_pending);
+
static int __init cfg80211_init(void)
{
int err;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b6bd7f4d6385..63dcf315dba7 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -489,6 +489,7 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef);
+void cfg80211_stop_radar_detection(struct wireless_dev *wdev);
void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev);
void cfg80211_background_cac_done_wk(struct work_struct *work);
@@ -550,7 +551,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
bool signal_valid, unsigned long ts);
enum ieee80211_ap_reg_power
-cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len);
+cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,
+ u32 client_flags);
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
index 40e49074e2ee..f9e7fff1ef25 100644
--- a/net/wireless/debugfs.c
+++ b/net/wireless/debugfs.c
@@ -29,6 +29,24 @@ static const struct file_operations name## _ops = { \
.llseek = generic_file_llseek, \
}
+#define DEBUGFS_RADIO_READONLY_FILE(name, buflen, fmt, value...) \
+static ssize_t name## _read(struct file *file, char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ struct wiphy_radio_cfg *radio_cfg = file->private_data; \
+ char buf[buflen]; \
+ int res; \
+ \
+ res = scnprintf(buf, buflen, fmt "\n", ##value); \
+ return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
+} \
+ \
+static const struct file_operations name## _ops = { \
+ .read = name## _read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+}
+
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
wiphy->rts_threshold);
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
@@ -38,6 +56,9 @@ DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
wiphy->retry_long);
+DEBUGFS_RADIO_READONLY_FILE(radio_rts_threshold, 20, "%d",
+ radio_cfg->rts_threshold);
+
static int ht_print_chan(struct ieee80211_channel *chan,
char *buf, int buf_size, int offset)
{
@@ -100,15 +121,27 @@ static const struct file_operations ht40allow_map_ops = {
#define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
+#define DEBUGFS_RADIO_ADD(name, radio_idx) \
+ debugfs_create_file(#name, 0444, radiod, \
+ &rdev->wiphy.radio_cfg[radio_idx], \
+ &name## _ops)
+
void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
{
struct dentry *phyd = rdev->wiphy.debugfsdir;
+ struct dentry *radiod;
+ u8 i;
DEBUGFS_ADD(rts_threshold);
DEBUGFS_ADD(fragmentation_threshold);
DEBUGFS_ADD(short_retry_limit);
DEBUGFS_ADD(long_retry_limit);
DEBUGFS_ADD(ht40allow_map);
+
+ for (i = 0; i < rdev->wiphy.n_radio; i++) {
+ radiod = rdev->wiphy.radio_cfg[i].radio_debugfsdir;
+ DEBUGFS_RADIO_ADD(radio_rts_threshold, i);
+ }
}
struct debugfs_read_work {
diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c
index 2613d6ac0fda..46e4317cbd7e 100644
--- a/net/wireless/ethtool.c
+++ b/net/wireless/ethtool.c
@@ -23,7 +23,7 @@ void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
else
strscpy(info->fw_version, "N/A", sizeof(info->fw_version));
- strscpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)),
+ strscpy(info->bus_info, dev_name(pdev),
sizeof(info->bus_info));
}
EXPORT_SYMBOL(cfg80211_get_drvinfo);
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 46394eb2086f..3fc175f9f868 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -1295,6 +1295,25 @@ cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rde
return 0;
}
+void cfg80211_stop_radar_detection(struct wireless_dev *wdev)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ int link_id;
+
+ for_each_valid_link(wdev, link_id) {
+ struct cfg80211_chan_def chandef;
+
+ if (!wdev->links[link_id].cac_started)
+ continue;
+
+ chandef = *wdev_chandef(wdev, link_id);
+ rdev_end_cac(rdev, wdev->netdev, link_id);
+ nl80211_radar_notify(rdev, &chandef, NL80211_RADAR_CAC_ABORTED,
+ wdev->netdev, GFP_KERNEL);
+ }
+}
+
void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
{
struct wiphy *wiphy = wdev->wiphy;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 89519aa52893..c961cd42a832 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -312,6 +312,26 @@ static int validate_supported_selectors(const struct nlattr *attr,
return 0;
}
+static int validate_nan_cluster_id(const struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ const u8 *data = nla_data(attr);
+ unsigned int len = nla_len(attr);
+ static const u8 cluster_id_prefix[4] = {0x50, 0x6f, 0x9a, 0x1};
+
+ if (len != ETH_ALEN) {
+ NL_SET_ERR_MSG_ATTR(extack, attr, "bad cluster id length");
+ return -EINVAL;
+ }
+
+ if (memcmp(data, cluster_id_prefix, sizeof(cluster_id_prefix))) {
+ NL_SET_ERR_MSG_ATTR(extack, attr, "invalid cluster id prefix");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/* policy for the attributes */
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
@@ -411,6 +431,14 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
[NL80211_TXRATE_HE_LTF] = NLA_POLICY_RANGE(NLA_U8,
NL80211_RATE_INFO_HE_1XLTF,
NL80211_RATE_INFO_HE_4XLTF),
+ [NL80211_TXRATE_EHT] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_eht)),
+ [NL80211_TXRATE_EHT_GI] = NLA_POLICY_RANGE(NLA_U8,
+ NL80211_RATE_INFO_EHT_GI_0_8,
+ NL80211_RATE_INFO_EHT_GI_3_2),
+ [NL80211_TXRATE_EHT_LTF] = NLA_POLICY_RANGE(NLA_U8,
+ NL80211_RATE_INFO_EHT_1XLTF,
+ NL80211_RATE_INFO_EHT_8XLTF),
+
};
static const struct nla_policy
@@ -492,6 +520,36 @@ nl80211_s1g_short_beacon[NL80211_S1G_SHORT_BEACON_ATTR_MAX + 1] = {
IEEE80211_MAX_DATA_LEN),
};
+static const struct nla_policy
+nl80211_nan_band_conf_policy[NL80211_NAN_BAND_CONF_ATTR_MAX + 1] = {
+ [NL80211_NAN_BAND_CONF_BAND] = NLA_POLICY_MAX(NLA_U8,
+ NUM_NL80211_BANDS - 1),
+ [NL80211_NAN_BAND_CONF_FREQ] = { .type = NLA_U16 },
+ [NL80211_NAN_BAND_CONF_RSSI_CLOSE] = NLA_POLICY_MIN(NLA_S8, -59),
+ [NL80211_NAN_BAND_CONF_RSSI_MIDDLE] = NLA_POLICY_MIN(NLA_S8, -74),
+ [NL80211_NAN_BAND_CONF_WAKE_DW] = NLA_POLICY_MAX(NLA_U8, 5),
+ [NL80211_NAN_BAND_CONF_DISABLE_SCAN] = { .type = NLA_FLAG },
+};
+
+static const struct nla_policy
+nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = {
+ [NL80211_NAN_CONF_CLUSTER_ID] =
+ NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_cluster_id,
+ ETH_ALEN),
+ [NL80211_NAN_CONF_EXTRA_ATTRS] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN},
+ [NL80211_NAN_CONF_VENDOR_ELEMS] =
+ NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr,
+ IEEE80211_MAX_DATA_LEN),
+ [NL80211_NAN_CONF_BAND_CONFIGS] =
+ NLA_POLICY_NESTED_ARRAY(nl80211_nan_band_conf_policy),
+ [NL80211_NAN_CONF_SCAN_PERIOD] = { .type = NLA_U16 },
+ [NL80211_NAN_CONF_SCAN_DWELL_TIME] = NLA_POLICY_RANGE(NLA_U16, 50, 512),
+ [NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL] =
+ NLA_POLICY_RANGE(NLA_U8, 50, 200),
+ [NL80211_NAN_CONF_NOTIFY_DW] = { .type = NLA_FLAG },
+};
+
static const struct netlink_range_validation nl80211_punct_bitmap_range = {
.min = 0,
.max = 0xffff,
@@ -761,6 +819,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_ATTR_NAN_MASTER_PREF] = NLA_POLICY_MIN(NLA_U8, 1),
[NL80211_ATTR_BANDS] = { .type = NLA_U32 },
+ [NL80211_ATTR_NAN_CONFIG] = NLA_POLICY_NESTED(nl80211_nan_conf_policy),
[NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED },
[NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY,
.len = FILS_MAX_KEK_LEN },
@@ -871,6 +930,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_S1G_LONG_BEACON_PERIOD] = NLA_POLICY_MIN(NLA_U8, 2),
[NL80211_ATTR_S1G_SHORT_BEACON] =
NLA_POLICY_NESTED(nl80211_s1g_short_beacon),
+ [NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG },
+ [NL80211_ATTR_S1G_PRIMARY_2MHZ] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
@@ -1219,21 +1280,6 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
if ((chan->flags & IEEE80211_CHAN_NO_HE) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HE))
goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_1MHZ) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_1MHZ))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_2MHZ) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_2MHZ))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_4MHZ) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_4MHZ))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_8MHZ) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_8MHZ))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_16MHZ) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_16MHZ))
- goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_320MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_320MHZ))
goto nla_put_failure;
@@ -1259,6 +1305,15 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
nla_put_flag(msg,
NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY))
goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_4MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_4MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_8MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_8MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_16MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_16MHZ))
+ goto nla_put_failure;
}
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
@@ -2545,6 +2600,41 @@ fail:
return -ENOBUFS;
}
+static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg)
+{
+ struct nlattr *nan_caps;
+
+ nan_caps = nla_nest_start(msg, NL80211_ATTR_NAN_CAPABILITIES);
+ if (!nan_caps)
+ return -ENOBUFS;
+
+ if (wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC &&
+ nla_put_flag(msg, NL80211_NAN_CAPA_CONFIGURABLE_SYNC))
+ goto fail;
+
+ if ((wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE) &&
+ nla_put_flag(msg, NL80211_NAN_CAPA_USERSPACE_DE))
+ goto fail;
+
+ if (nla_put_u8(msg, NL80211_NAN_CAPA_OP_MODE,
+ wiphy->nan_capa.op_mode) ||
+ nla_put_u8(msg, NL80211_NAN_CAPA_NUM_ANTENNAS,
+ wiphy->nan_capa.n_antennas) ||
+ nla_put_u16(msg, NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME,
+ wiphy->nan_capa.max_channel_switch_time) ||
+ nla_put_u8(msg, NL80211_NAN_CAPA_CAPABILITIES,
+ wiphy->nan_capa.dev_capabilities))
+ goto fail;
+
+ nla_nest_end(msg, nan_caps);
+
+ return 0;
+
+fail:
+ nla_nest_cancel(msg, nan_caps);
+ return -ENOBUFS;
+}
+
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
@@ -3019,6 +3109,40 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
rdev->wiphy.ext_features))
goto nla_put_failure;
+ if (rdev->wiphy.bss_param_support) {
+ struct nlattr *nested;
+ u32 parsup = rdev->wiphy.bss_param_support;
+
+ nested = nla_nest_start(msg, NL80211_ATTR_BSS_PARAM);
+ if (!nested)
+ goto nla_put_failure;
+
+ if ((parsup & WIPHY_BSS_PARAM_CTS_PROT) &&
+ nla_put_flag(msg, NL80211_ATTR_BSS_CTS_PROT))
+ goto nla_put_failure;
+ if ((parsup & WIPHY_BSS_PARAM_SHORT_PREAMBLE) &&
+ nla_put_flag(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE))
+ goto nla_put_failure;
+ if ((parsup & WIPHY_BSS_PARAM_SHORT_SLOT_TIME) &&
+ nla_put_flag(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME))
+ goto nla_put_failure;
+ if ((parsup & WIPHY_BSS_PARAM_BASIC_RATES) &&
+ nla_put_flag(msg, NL80211_ATTR_BSS_BASIC_RATES))
+ goto nla_put_failure;
+ if ((parsup & WIPHY_BSS_PARAM_AP_ISOLATE) &&
+ nla_put_flag(msg, NL80211_ATTR_AP_ISOLATE))
+ goto nla_put_failure;
+ if ((parsup & WIPHY_BSS_PARAM_HT_OPMODE) &&
+ nla_put_flag(msg, NL80211_ATTR_BSS_HT_OPMODE))
+ goto nla_put_failure;
+ if ((parsup & WIPHY_BSS_PARAM_P2P_CTWINDOW) &&
+ nla_put_flag(msg, NL80211_ATTR_P2P_CTWINDOW))
+ goto nla_put_failure;
+ if ((parsup & WIPHY_BSS_PARAM_P2P_OPPPS) &&
+ nla_put_flag(msg, NL80211_ATTR_P2P_OPPPS))
+ goto nla_put_failure;
+ nla_nest_end(msg, nested);
+ }
if (rdev->wiphy.bss_select_support) {
struct nlattr *nested;
u32 bss_select_support = rdev->wiphy.bss_select_support;
@@ -3163,6 +3287,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
if (nl80211_put_radios(&rdev->wiphy, msg))
goto nla_put_failure;
+ state->split_start++;
+ break;
+ case 18:
+ if (nl80211_put_nan_capa(&rdev->wiphy, msg))
+ goto nla_put_failure;
+
/* done */
state->split_start = 0;
break;
@@ -3406,6 +3536,7 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
chandef->center_freq1 = KHZ_TO_MHZ(control_freq);
chandef->freq1_offset = control_freq % 1000;
chandef->center_freq2 = 0;
+ chandef->s1g_primary_2mhz = false;
if (!chandef->chan) {
NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ],
@@ -3413,6 +3544,9 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
+ if (cfg80211_chandef_is_s1g(chandef))
+ chandef->width = NL80211_CHAN_WIDTH_1;
+
if (attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
enum nl80211_channel_type chantype;
@@ -3449,27 +3583,20 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
} else if (attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
- chandef->width =
- nla_get_u32(attrs[NL80211_ATTR_CHANNEL_WIDTH]);
- if (chandef->chan->band == NL80211_BAND_S1GHZ) {
- /* User input error for channel width doesn't match channel */
- if (chandef->width != ieee80211_s1g_channel_width(chandef->chan)) {
- NL_SET_ERR_MSG_ATTR(extack,
- attrs[NL80211_ATTR_CHANNEL_WIDTH],
- "bad channel width");
- return -EINVAL;
- }
- }
+ chandef->width = nla_get_u32(attrs[NL80211_ATTR_CHANNEL_WIDTH]);
if (attrs[NL80211_ATTR_CENTER_FREQ1]) {
chandef->center_freq1 =
nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ1]);
- chandef->freq1_offset =
- nla_get_u32_default(attrs[NL80211_ATTR_CENTER_FREQ1_OFFSET],
- 0);
+ chandef->freq1_offset = nla_get_u32_default(
+ attrs[NL80211_ATTR_CENTER_FREQ1_OFFSET], 0);
}
+
if (attrs[NL80211_ATTR_CENTER_FREQ2])
chandef->center_freq2 =
nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ2]);
+
+ chandef->s1g_primary_2mhz = nla_get_flag(
+ attrs[NL80211_ATTR_S1G_PRIMARY_2MHZ]);
}
if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) {
@@ -4012,8 +4139,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev->wiphy.txq_quantum = old_txq_quantum;
}
- if (old_rts_threshold)
- kfree(old_radio_rts_threshold);
+ kfree(old_radio_rts_threshold);
return result;
}
@@ -4052,6 +4178,9 @@ int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *ch
if (chandef->punctured &&
nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP, chandef->punctured))
return -ENOBUFS;
+ if (chandef->s1g_primary_2mhz &&
+ nla_put_flag(msg, NL80211_ATTR_S1G_PRIMARY_2MHZ))
+ return -ENOBUFS;
return 0;
}
@@ -5393,6 +5522,164 @@ static bool he_set_mcs_mask(struct genl_info *info,
return true;
}
+static void eht_build_mcs_mask(struct genl_info *info,
+ const struct ieee80211_sta_eht_cap *eht_cap,
+ u8 mcs_nss_len, u16 *mcs_mask)
+{
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ u8 nss, mcs_7 = 0, mcs_9 = 0, mcs_11 = 0, mcs_13 = 0;
+ unsigned int link_id = nl80211_link_id(info->attrs);
+
+ if (mcs_nss_len == 4) {
+ const struct ieee80211_eht_mcs_nss_supp_20mhz_only *mcs =
+ &eht_cap->eht_mcs_nss_supp.only_20mhz;
+
+ mcs_7 = u8_get_bits(mcs->rx_tx_mcs7_max_nss,
+ IEEE80211_EHT_MCS_NSS_TX);
+ mcs_9 = u8_get_bits(mcs->rx_tx_mcs9_max_nss,
+ IEEE80211_EHT_MCS_NSS_TX);
+ mcs_11 = u8_get_bits(mcs->rx_tx_mcs11_max_nss,
+ IEEE80211_EHT_MCS_NSS_TX);
+ mcs_13 = u8_get_bits(mcs->rx_tx_mcs13_max_nss,
+ IEEE80211_EHT_MCS_NSS_TX);
+
+ } else {
+ const struct ieee80211_eht_mcs_nss_supp_bw *mcs;
+ enum nl80211_chan_width width;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ width = wdev->u.ibss.chandef.width;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ width = wdev->u.mesh.chandef.width;
+ break;
+ case NL80211_IFTYPE_OCB:
+ width = wdev->u.ocb.chandef.width;
+ break;
+ default:
+ if (wdev->valid_links)
+ width = wdev->links[link_id].ap.chandef.width;
+ else
+ width = wdev->u.ap.preset_chandef.width;
+ break;
+ }
+
+ switch (width) {
+ case NL80211_CHAN_WIDTH_320:
+ mcs = &eht_cap->eht_mcs_nss_supp.bw._320;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ mcs = &eht_cap->eht_mcs_nss_supp.bw._160;
+ break;
+ default:
+ mcs = &eht_cap->eht_mcs_nss_supp.bw._80;
+ break;
+ }
+
+ mcs_7 = u8_get_bits(mcs->rx_tx_mcs9_max_nss,
+ IEEE80211_EHT_MCS_NSS_TX);
+ mcs_9 = u8_get_bits(mcs->rx_tx_mcs9_max_nss,
+ IEEE80211_EHT_MCS_NSS_TX);
+ mcs_11 = u8_get_bits(mcs->rx_tx_mcs11_max_nss,
+ IEEE80211_EHT_MCS_NSS_TX);
+ mcs_13 = u8_get_bits(mcs->rx_tx_mcs13_max_nss,
+ IEEE80211_EHT_MCS_NSS_TX);
+ }
+
+ /* Enable MCS 14 for NSS 0 */
+ if (eht_cap->eht_cap_elem.phy_cap_info[6] &
+ IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP)
+ mcs_mask[0] |= 0x4000;
+
+ /* Enable MCS 15 for NSS 0 */
+ mcs_mask[0] |= 0x8000;
+
+ for (nss = 0; nss < NL80211_EHT_NSS_MAX; nss++) {
+ if (!mcs_7)
+ continue;
+ mcs_mask[nss] |= 0x00FF;
+ mcs_7--;
+
+ if (!mcs_9)
+ continue;
+ mcs_mask[nss] |= 0x0300;
+ mcs_9--;
+
+ if (!mcs_11)
+ continue;
+ mcs_mask[nss] |= 0x0C00;
+ mcs_11--;
+
+ if (!mcs_13)
+ continue;
+ mcs_mask[nss] |= 0x3000;
+ mcs_13--;
+ }
+}
+
+static bool eht_set_mcs_mask(struct genl_info *info, struct wireless_dev *wdev,
+ struct ieee80211_supported_band *sband,
+ struct nl80211_txrate_eht *txrate,
+ u16 mcs[NL80211_EHT_NSS_MAX])
+{
+ const struct ieee80211_sta_he_cap *he_cap;
+ const struct ieee80211_sta_eht_cap *eht_cap;
+ u16 tx_mcs_mask[NL80211_EHT_NSS_MAX] = { 0 };
+ u8 i, mcs_nss_len;
+
+ he_cap = ieee80211_get_he_iftype_cap(sband, wdev->iftype);
+ if (!he_cap)
+ return false;
+
+ eht_cap = ieee80211_get_eht_iftype_cap(sband, wdev->iftype);
+ if (!eht_cap)
+ return false;
+
+ /* Checks for MCS 14 */
+ if (txrate->mcs[0] & 0x4000) {
+ if (sband->band != NL80211_BAND_6GHZ)
+ return false;
+
+ if (!(eht_cap->eht_cap_elem.phy_cap_info[6] &
+ IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP))
+ return false;
+ }
+
+ mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
+ &eht_cap->eht_cap_elem,
+ wdev->iftype ==
+ NL80211_IFTYPE_STATION);
+
+ if (mcs_nss_len == 3) {
+ /* Supported iftypes for setting non-20 MHZ only EHT MCS */
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
+ break;
+ default:
+ return false;
+ }
+ }
+
+ /* Build eht_mcs_mask from EHT and HE capabilities */
+ eht_build_mcs_mask(info, eht_cap, mcs_nss_len, tx_mcs_mask);
+
+ memset(mcs, 0, sizeof(u16) * NL80211_EHT_NSS_MAX);
+ for (i = 0; i < NL80211_EHT_NSS_MAX; i++) {
+ if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
+ mcs[i] = txrate->mcs[i];
+ else
+ return false;
+ }
+
+ return true;
+}
+
static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
struct nlattr *attrs[],
enum nl80211_attrs attr,
@@ -5413,6 +5700,8 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
/* Default to all rates enabled */
for (i = 0; i < NUM_NL80211_BANDS; i++) {
const struct ieee80211_sta_he_cap *he_cap;
+ const struct ieee80211_sta_eht_cap *eht_cap;
+ u8 mcs_nss_len;
if (!default_all_enabled)
break;
@@ -5441,6 +5730,21 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
mask->control[i].he_gi = 0xFF;
mask->control[i].he_ltf = 0xFF;
+
+ eht_cap = ieee80211_get_eht_iftype_cap(sband, wdev->iftype);
+ if (!eht_cap)
+ continue;
+
+ mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
+ &eht_cap->eht_cap_elem,
+ wdev->iftype ==
+ NL80211_IFTYPE_STATION);
+
+ eht_build_mcs_mask(info, eht_cap, mcs_nss_len,
+ mask->control[i].eht_mcs);
+
+ mask->control[i].eht_gi = 0xFF;
+ mask->control[i].eht_ltf = 0xFF;
}
/* if no rates are given set it back to the defaults */
@@ -5512,13 +5816,27 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
mask->control[band].he_ltf =
nla_get_u8(tb[NL80211_TXRATE_HE_LTF]);
+ if (tb[NL80211_TXRATE_EHT] &&
+ !eht_set_mcs_mask(info, wdev, sband,
+ nla_data(tb[NL80211_TXRATE_EHT]),
+ mask->control[band].eht_mcs))
+ return -EINVAL;
+
+ if (tb[NL80211_TXRATE_EHT_GI])
+ mask->control[band].eht_gi =
+ nla_get_u8(tb[NL80211_TXRATE_EHT_GI]);
+ if (tb[NL80211_TXRATE_EHT_LTF])
+ mask->control[band].eht_ltf =
+ nla_get_u8(tb[NL80211_TXRATE_EHT_LTF]);
+
if (mask->control[band].legacy == 0) {
- /* don't allow empty legacy rates if HT, VHT or HE
+ /* don't allow empty legacy rates if HT, VHT, HE or EHT
* are not even supported.
*/
if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
rdev->wiphy.bands[band]->vht_cap.vht_supported ||
- ieee80211_get_he_iftype_cap(sband, wdev->iftype)))
+ ieee80211_get_he_iftype_cap(sband, wdev->iftype) ||
+ ieee80211_get_eht_iftype_cap(sband, wdev->iftype)))
return -EINVAL;
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
@@ -5533,6 +5851,10 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
if (mask->control[band].he_mcs[i])
goto out;
+ for (i = 0; i < NL80211_EHT_NSS_MAX; i++)
+ if (mask->control[band].eht_mcs[i])
+ goto out;
+
/* legacy and mcs rates may not be both empty */
return -EINVAL;
}
@@ -5546,7 +5868,7 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
enum nl80211_band band,
struct cfg80211_bitrate_mask *beacon_rate)
{
- u32 count_ht, count_vht, count_he, i;
+ u32 count_ht, count_vht, count_he, count_eht, i;
u32 rate = beacon_rate->control[band].legacy;
/* Allow only one rate */
@@ -5592,8 +5914,21 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
- if ((count_ht && count_vht && count_he) ||
- (!rate && !count_ht && !count_vht && !count_he))
+ count_eht = 0;
+ for (i = 0; i < NL80211_EHT_NSS_MAX; i++) {
+ if (hweight16(beacon_rate->control[band].eht_mcs[i]) > 1) {
+ return -EINVAL;
+ } else if (beacon_rate->control[band].eht_mcs[i]) {
+ count_eht++;
+ if (count_eht > 1)
+ return -EINVAL;
+ }
+ if (count_eht && rate)
+ return -EINVAL;
+ }
+
+ if ((count_ht && count_vht && count_he && count_eht) ||
+ (!rate && !count_ht && !count_vht && !count_he && !count_eht))
return -EINVAL;
if (rate &&
@@ -5613,6 +5948,11 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
NL80211_EXT_FEATURE_BEACON_RATE_HE))
return -EINVAL;
+ if (count_eht &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_EHT))
+ return -EINVAL;
+
return 0;
}
@@ -6411,7 +6751,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
beacon_check.relax = true;
beacon_check.reg_power =
cfg80211_get_6ghz_power_type(params->beacon.tail,
- params->beacon.tail_len);
+ params->beacon.tail_len, 0);
if (!cfg80211_reg_check_beaconing(&rdev->wiphy, &params->chandef,
&beacon_check)) {
err = -EINVAL;
@@ -6590,7 +6930,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
beacon_check.relax = true;
beacon_check.reg_power =
cfg80211_get_6ghz_power_type(params->beacon.tail,
- params->beacon.tail_len);
+ params->beacon.tail_len, 0);
if (!cfg80211_reg_check_beaconing(&rdev->wiphy,
&wdev->links[link_id].ap.chandef,
&beacon_check)) {
@@ -7062,7 +7402,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
u32 seq, int flags,
struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const u8 *mac_addr, struct station_info *sinfo)
+ const u8 *mac_addr, struct station_info *sinfo,
+ bool link_stats)
{
void *hdr;
struct nlattr *sinfoattr, *bss_param;
@@ -7283,7 +7624,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
goto nla_put_failure;
}
- if (sinfo->valid_links) {
+ if (link_stats && sinfo->valid_links) {
links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
if (!links)
goto nla_put_failure;
@@ -7574,7 +7915,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
rdev, wdev->netdev, mac_addr,
- &sinfo) < 0)
+ &sinfo, false) < 0)
goto out;
sta_idx++;
@@ -7635,7 +7976,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
info->snd_portid, info->snd_seq, 0,
- rdev, dev, mac_addr, &sinfo) < 0) {
+ rdev, dev, mac_addr, &sinfo, false) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -8829,6 +9170,9 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct bss_parameters params;
+ u32 bss_param_support = rdev->wiphy.bss_param_support;
+ u32 changed = 0;
+ bool strict;
memset(&params, 0, sizeof(params));
params.link_id = nl80211_link_id_or_invalid(info->attrs);
@@ -8841,26 +9185,54 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
params.p2p_ctwindow = -1;
params.p2p_opp_ps = -1;
- if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
+ strict = nla_get_flag(info->attrs[NL80211_ATTR_BSS_PARAM]);
+ if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) {
+ if (strict && !(bss_param_support & WIPHY_BSS_PARAM_CTS_PROT))
+ return -EINVAL;
params.use_cts_prot =
nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
- if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
+ changed |= WIPHY_BSS_PARAM_CTS_PROT;
+ }
+ if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) {
+ if (strict &&
+ !(bss_param_support & WIPHY_BSS_PARAM_SHORT_PREAMBLE))
+ return -EINVAL;
params.use_short_preamble =
nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
- if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
+ changed |= WIPHY_BSS_PARAM_SHORT_PREAMBLE;
+ }
+ if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) {
+ if (strict &&
+ !(bss_param_support & WIPHY_BSS_PARAM_SHORT_SLOT_TIME))
+ return -EINVAL;
params.use_short_slot_time =
nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
+ changed |= WIPHY_BSS_PARAM_SHORT_SLOT_TIME;
+ }
if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+ if (strict &&
+ !(bss_param_support & WIPHY_BSS_PARAM_BASIC_RATES))
+ return -EINVAL;
params.basic_rates =
nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
params.basic_rates_len =
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ changed |= WIPHY_BSS_PARAM_BASIC_RATES;
}
- if (info->attrs[NL80211_ATTR_AP_ISOLATE])
- params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
- if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
+ if (info->attrs[NL80211_ATTR_AP_ISOLATE]) {
+ if (strict && !(bss_param_support & WIPHY_BSS_PARAM_AP_ISOLATE))
+ return -EINVAL;
+ params.ap_isolate =
+ !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
+ changed |= WIPHY_BSS_PARAM_AP_ISOLATE;
+ }
+ if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) {
+ if (strict && !(bss_param_support & WIPHY_BSS_PARAM_HT_OPMODE))
+ return -EINVAL;
params.ht_opmode =
nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
+ changed |= WIPHY_BSS_PARAM_HT_OPMODE;
+ }
if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
@@ -8868,8 +9240,9 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
params.p2p_ctwindow =
nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
if (params.p2p_ctwindow != 0 &&
- !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+ !(bss_param_support & WIPHY_BSS_PARAM_P2P_CTWINDOW))
return -EINVAL;
+ changed |= WIPHY_BSS_PARAM_P2P_CTWINDOW;
}
if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
@@ -8878,9 +9251,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EINVAL;
tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+ if (tmp && !(bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS))
+ return -EINVAL;
params.p2p_opp_ps = tmp;
if (params.p2p_opp_ps &&
- !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+ !(rdev->wiphy.bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS))
return -EINVAL;
}
@@ -8891,6 +9266,10 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
+ changed &= rdev->wiphy.bss_param_support;
+ if (!changed)
+ return 0;
+
return rdev_change_bss(rdev, dev, &params);
}
@@ -10070,8 +10449,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
goto out_free;
}
- /* ignore disabled channels */
+ /* Ignore disabled / no primary channels */
if (chan->flags & IEEE80211_CHAN_DISABLED ||
+ chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY ||
!cfg80211_wdev_channel_allowed(wdev, chan))
continue;
@@ -10093,6 +10473,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
chan = &wiphy->bands[band]->channels[j];
if (chan->flags & IEEE80211_CHAN_DISABLED ||
+ chan->flags &
+ IEEE80211_CHAN_S1G_NO_PRIMARY ||
!cfg80211_wdev_channel_allowed(wdev, chan))
continue;
@@ -13397,7 +13779,9 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
break;
case NL80211_IFTYPE_NAN:
if (!wiphy_ext_feature_isset(wdev->wiphy,
- NL80211_EXT_FEATURE_SECURE_NAN))
+ NL80211_EXT_FEATURE_SECURE_NAN) &&
+ !(wdev->wiphy->nan_capa.flags &
+ WIPHY_NAN_FLAGS_USERSPACE_DE))
return -EOPNOTSUPP;
break;
default:
@@ -13458,7 +13842,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
break;
case NL80211_IFTYPE_NAN:
if (!wiphy_ext_feature_isset(wdev->wiphy,
- NL80211_EXT_FEATURE_SECURE_NAN))
+ NL80211_EXT_FEATURE_SECURE_NAN) &&
+ !(wdev->wiphy->nan_capa.flags &
+ WIPHY_NAN_FLAGS_USERSPACE_DE))
return -EOPNOTSUPP;
break;
default:
@@ -15105,6 +15491,216 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
return 0;
}
+static struct ieee80211_channel *nl80211_get_nan_channel(struct wiphy *wiphy,
+ int freq)
+{
+ struct ieee80211_channel *chan;
+ struct cfg80211_chan_def def;
+
+ /* Check if the frequency is valid for NAN */
+ if (freq != 5220 && freq != 5745 && freq != 2437)
+ return NULL;
+
+ chan = ieee80211_get_channel(wiphy, freq);
+ if (!chan)
+ return NULL;
+
+ cfg80211_chandef_create(&def, chan, NL80211_CHAN_NO_HT);
+
+ /* Check if the channel is allowed */
+ if (cfg80211_reg_can_beacon(wiphy, &def, NL80211_IFTYPE_NAN))
+ return chan;
+
+ return NULL;
+}
+
+static int nl80211_parse_nan_band_config(struct wiphy *wiphy,
+ struct nlattr **tb,
+ struct cfg80211_nan_band_config *cfg,
+ enum nl80211_band band)
+{
+ if (BIT(band) & ~(u32)wiphy->nan_supported_bands)
+ return -EINVAL;
+
+ if (tb[NL80211_NAN_BAND_CONF_FREQ]) {
+ u16 freq = nla_get_u16(tb[NL80211_NAN_BAND_CONF_FREQ]);
+
+ if (band != NL80211_BAND_5GHZ)
+ return -EINVAL;
+
+ cfg->chan = nl80211_get_nan_channel(wiphy, freq);
+ if (!cfg->chan)
+ return -EINVAL;
+ }
+
+ if (tb[NL80211_NAN_BAND_CONF_RSSI_CLOSE]) {
+ cfg->rssi_close =
+ nla_get_s8(tb[NL80211_NAN_BAND_CONF_RSSI_CLOSE]);
+ if (!tb[NL80211_NAN_BAND_CONF_RSSI_MIDDLE])
+ return -EINVAL;
+ }
+
+ if (tb[NL80211_NAN_BAND_CONF_RSSI_MIDDLE]) {
+ cfg->rssi_middle =
+ nla_get_s8(tb[NL80211_NAN_BAND_CONF_RSSI_MIDDLE]);
+ if (!cfg->rssi_close || cfg->rssi_middle >= cfg->rssi_close)
+ return -EINVAL;
+ }
+
+ if (tb[NL80211_NAN_BAND_CONF_WAKE_DW]) {
+ cfg->awake_dw_interval =
+ nla_get_u8(tb[NL80211_NAN_BAND_CONF_WAKE_DW]);
+
+ if (band == NL80211_BAND_2GHZ && cfg->awake_dw_interval == 0)
+ return -EINVAL;
+ }
+
+ cfg->disable_scan =
+ nla_get_flag(tb[NL80211_NAN_BAND_CONF_DISABLE_SCAN]);
+ return 0;
+}
+
+static int nl80211_parse_nan_conf(struct wiphy *wiphy,
+ struct genl_info *info,
+ struct cfg80211_nan_conf *conf,
+ u32 *changed_flags)
+{
+ struct nlattr *attrs[NL80211_NAN_CONF_ATTR_MAX + 1];
+ int err, rem;
+ u32 changed = 0;
+ struct nlattr *band_config;
+
+ if (info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) {
+ conf->master_pref =
+ nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
+
+ changed |= CFG80211_NAN_CONF_CHANGED_PREF;
+ }
+
+ if (info->attrs[NL80211_ATTR_BANDS]) {
+ u32 bands = nla_get_u32(info->attrs[NL80211_ATTR_BANDS]);
+
+ if (bands & ~(u32)wiphy->nan_supported_bands)
+ return -EOPNOTSUPP;
+
+ if (bands && !(bands & BIT(NL80211_BAND_2GHZ)))
+ return -EINVAL;
+
+ conf->bands = bands;
+ changed |= CFG80211_NAN_CONF_CHANGED_BANDS;
+ }
+
+ conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval = 1;
+ if (conf->bands & BIT(NL80211_BAND_5GHZ) || !conf->bands)
+ conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval = 1;
+
+ /* On 2.4 GHz band use channel 6 */
+ conf->band_cfgs[NL80211_BAND_2GHZ].chan =
+ nl80211_get_nan_channel(wiphy, 2437);
+ if (!conf->band_cfgs[NL80211_BAND_2GHZ].chan)
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_NAN_CONFIG])
+ goto out;
+
+ err = nla_parse_nested(attrs, NL80211_NAN_CONF_ATTR_MAX,
+ info->attrs[NL80211_ATTR_NAN_CONFIG], NULL,
+ info->extack);
+ if (err)
+ return err;
+
+ changed |= CFG80211_NAN_CONF_CHANGED_CONFIG;
+ if (attrs[NL80211_NAN_CONF_CLUSTER_ID])
+ conf->cluster_id =
+ nla_data(attrs[NL80211_NAN_CONF_CLUSTER_ID]);
+
+ if (attrs[NL80211_NAN_CONF_EXTRA_ATTRS]) {
+ conf->extra_nan_attrs =
+ nla_data(attrs[NL80211_NAN_CONF_EXTRA_ATTRS]);
+ conf->extra_nan_attrs_len =
+ nla_len(attrs[NL80211_NAN_CONF_EXTRA_ATTRS]);
+ }
+
+ if (attrs[NL80211_NAN_CONF_VENDOR_ELEMS]) {
+ conf->vendor_elems =
+ nla_data(attrs[NL80211_NAN_CONF_VENDOR_ELEMS]);
+ conf->vendor_elems_len =
+ nla_len(attrs[NL80211_NAN_CONF_VENDOR_ELEMS]);
+ }
+
+ if (attrs[NL80211_NAN_CONF_BAND_CONFIGS]) {
+ nla_for_each_nested(band_config,
+ attrs[NL80211_NAN_CONF_BAND_CONFIGS],
+ rem) {
+ enum nl80211_band band;
+ struct cfg80211_nan_band_config *cfg;
+ struct nlattr *tb[NL80211_NAN_BAND_CONF_ATTR_MAX + 1];
+
+ err = nla_parse_nested(tb,
+ NL80211_NAN_BAND_CONF_ATTR_MAX,
+ band_config, NULL,
+ info->extack);
+ if (err)
+ return err;
+
+ if (!tb[NL80211_NAN_BAND_CONF_BAND])
+ return -EINVAL;
+
+ band = nla_get_u8(tb[NL80211_NAN_BAND_CONF_BAND]);
+ if (conf->bands && !(conf->bands & BIT(band)))
+ return -EINVAL;
+
+ cfg = &conf->band_cfgs[band];
+
+ err = nl80211_parse_nan_band_config(wiphy, tb, cfg,
+ band);
+ if (err)
+ return err;
+ }
+ }
+
+ if (attrs[NL80211_NAN_CONF_SCAN_PERIOD])
+ conf->scan_period =
+ nla_get_u16(attrs[NL80211_NAN_CONF_SCAN_PERIOD]);
+
+ if (attrs[NL80211_NAN_CONF_SCAN_DWELL_TIME])
+ conf->scan_dwell_time =
+ nla_get_u16(attrs[NL80211_NAN_CONF_SCAN_DWELL_TIME]);
+
+ if (attrs[NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL])
+ conf->discovery_beacon_interval =
+ nla_get_u8(attrs[NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL]);
+
+ if (attrs[NL80211_NAN_CONF_NOTIFY_DW])
+ conf->enable_dw_notification =
+ nla_get_flag(attrs[NL80211_NAN_CONF_NOTIFY_DW]);
+
+out:
+ if (!conf->band_cfgs[NL80211_BAND_5GHZ].chan &&
+ (!conf->bands || conf->bands & BIT(NL80211_BAND_5GHZ))) {
+ /* If no 5GHz channel is specified use default, if possible */
+ conf->band_cfgs[NL80211_BAND_5GHZ].chan =
+ nl80211_get_nan_channel(wiphy, 5745);
+ if (!conf->band_cfgs[NL80211_BAND_5GHZ].chan)
+ conf->band_cfgs[NL80211_BAND_5GHZ].chan =
+ nl80211_get_nan_channel(wiphy, 5220);
+
+ /* Return error if user space asked explicitly for 5 GHz */
+ if (!conf->band_cfgs[NL80211_BAND_5GHZ].chan &&
+ conf->bands & BIT(NL80211_BAND_5GHZ)) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ info->attrs[NL80211_ATTR_BANDS],
+ "5 GHz band operation is not allowed");
+ return -EINVAL;
+ }
+ }
+
+ if (changed_flags)
+ *changed_flags = changed;
+
+ return 0;
+}
+
static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -15121,23 +15717,13 @@ static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info)
if (rfkill_blocked(rdev->wiphy.rfkill))
return -ERFKILL;
+ /* Master preference is mandatory for START_NAN */
if (!info->attrs[NL80211_ATTR_NAN_MASTER_PREF])
return -EINVAL;
- conf.master_pref =
- nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
-
- if (info->attrs[NL80211_ATTR_BANDS]) {
- u32 bands = nla_get_u32(info->attrs[NL80211_ATTR_BANDS]);
-
- if (bands & ~(u32)wdev->wiphy->nan_supported_bands)
- return -EOPNOTSUPP;
-
- if (bands && !(bands & BIT(NL80211_BAND_2GHZ)))
- return -EINVAL;
-
- conf.bands = bands;
- }
+ err = nl80211_parse_nan_conf(&rdev->wiphy, info, &conf, NULL);
+ if (err)
+ return err;
err = rdev_start_nan(rdev, wdev, &conf);
if (err)
@@ -15493,6 +16079,7 @@ static int nl80211_nan_change_config(struct sk_buff *skb,
struct wireless_dev *wdev = info->user_ptr[1];
struct cfg80211_nan_conf conf = {};
u32 changed = 0;
+ int err;
if (wdev->iftype != NL80211_IFTYPE_NAN)
return -EOPNOTSUPP;
@@ -15500,27 +16087,9 @@ static int nl80211_nan_change_config(struct sk_buff *skb,
if (!wdev_running(wdev))
return -ENOTCONN;
- if (info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) {
- conf.master_pref =
- nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
- if (conf.master_pref <= 1 || conf.master_pref == 255)
- return -EINVAL;
-
- changed |= CFG80211_NAN_CONF_CHANGED_PREF;
- }
-
- if (info->attrs[NL80211_ATTR_BANDS]) {
- u32 bands = nla_get_u32(info->attrs[NL80211_ATTR_BANDS]);
-
- if (bands & ~(u32)wdev->wiphy->nan_supported_bands)
- return -EOPNOTSUPP;
-
- if (bands && !(bands & BIT(NL80211_BAND_2GHZ)))
- return -EINVAL;
-
- conf.bands = bands;
- changed |= CFG80211_NAN_CONF_CHANGED_BANDS;
- }
+ err = nl80211_parse_nan_conf(&rdev->wiphy, info, &conf, &changed);
+ if (err)
+ return err;
if (!changed)
return -EINVAL;
@@ -19680,7 +20249,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
return;
if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0,
- rdev, dev, mac_addr, sinfo) < 0) {
+ rdev, dev, mac_addr, sinfo, false) < 0) {
nlmsg_free(msg);
return;
}
@@ -19710,7 +20279,7 @@ void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
}
if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
- rdev, dev, mac_addr, sinfo) < 0) {
+ rdev, dev, mac_addr, sinfo, false) < 0) {
nlmsg_free(msg);
return;
}
@@ -21243,6 +21812,88 @@ void cfg80211_epcs_changed(struct net_device *netdev, bool enabled)
}
EXPORT_SYMBOL(cfg80211_epcs_changed);
+void cfg80211_next_nan_dw_notif(struct wireless_dev *wdev,
+ struct ieee80211_channel *chan, gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_next_nan_dw_notif(wdev, chan);
+
+ if (!wdev->owner_nlportid)
+ return;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0,
+ NL80211_CMD_NAN_NEXT_DW_NOTIFICATION);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid);
+
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_next_nan_dw_notif);
+
+void cfg80211_nan_cluster_joined(struct wireless_dev *wdev,
+ const u8 *cluster_id, bool new_cluster,
+ gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_nan_cluster_joined(wdev, cluster_id, new_cluster);
+
+ memcpy(wdev->u.nan.cluster_id, cluster_id, ETH_ALEN);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_CLUSTER_JOINED);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cluster_id) ||
+ (new_cluster && nla_put_flag(msg, NL80211_ATTR_NAN_NEW_CLUSTER)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ if (!wdev->owner_nlportid)
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy),
+ msg, 0, NL80211_MCGRP_NAN, gfp);
+ else
+ genlmsg_unicast(wiphy_net(wiphy), msg,
+ wdev->owner_nlportid);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_cluster_joined);
+
/* initialisation/exit functions */
int __init nl80211_init(void)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 3b0ac3437f81..73cab51f6379 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1707,6 +1707,16 @@ static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
+ if (is_s1g) {
+ if (max_bandwidth_khz < MHZ_TO_KHZ(16))
+ bw_flags |= IEEE80211_CHAN_NO_16MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(8))
+ bw_flags |= IEEE80211_CHAN_NO_8MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(4))
+ bw_flags |= IEEE80211_CHAN_NO_4MHZ;
+ return bw_flags;
+ }
+
/* If we get a reg_rule we can assume that at least 5Mhz fit */
if (!cfg80211_does_bw_fit_range(freq_range,
center_freq_khz,
@@ -1717,59 +1727,19 @@ static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
- if (is_s1g) {
- /* S1G is strict about non overlapping channels. We can
- * calculate which bandwidth is allowed per channel by finding
- * the largest bandwidth which cleanly divides the freq_range.
- */
- int edge_offset;
- int ch_bw = max_bandwidth_khz;
-
- while (ch_bw) {
- edge_offset = (center_freq_khz - ch_bw / 2) -
- freq_range->start_freq_khz;
- if (edge_offset % ch_bw == 0) {
- switch (KHZ_TO_MHZ(ch_bw)) {
- case 1:
- bw_flags |= IEEE80211_CHAN_1MHZ;
- break;
- case 2:
- bw_flags |= IEEE80211_CHAN_2MHZ;
- break;
- case 4:
- bw_flags |= IEEE80211_CHAN_4MHZ;
- break;
- case 8:
- bw_flags |= IEEE80211_CHAN_8MHZ;
- break;
- case 16:
- bw_flags |= IEEE80211_CHAN_16MHZ;
- break;
- default:
- /* If we got here, no bandwidths fit on
- * this frequency, ie. band edge.
- */
- bw_flags |= IEEE80211_CHAN_DISABLED;
- break;
- }
- break;
- }
- ch_bw /= 2;
- }
- } else {
- if (max_bandwidth_khz < MHZ_TO_KHZ(10))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(20))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(40))
- bw_flags |= IEEE80211_CHAN_NO_HT40;
- if (max_bandwidth_khz < MHZ_TO_KHZ(80))
- bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(160))
- bw_flags |= IEEE80211_CHAN_NO_160MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(320))
- bw_flags |= IEEE80211_CHAN_NO_320MHZ;
- }
+ if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(40))
+ bw_flags |= IEEE80211_CHAN_NO_HT40;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(80))
+ bw_flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(160))
+ bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(320))
+ bw_flags |= IEEE80211_CHAN_NO_320MHZ;
+
return bw_flags;
}
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index a8339ed52404..7546647752fd 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1816,6 +1816,9 @@ static void cfg80211_update_hidden_bsses(struct cfg80211_internal_bss *known,
WARN_ON(ies != old_ies);
rcu_assign_pointer(bss->pub.beacon_ies, new_ies);
+
+ bss->ts = known->ts;
+ bss->pub.ts_boottime = known->pub.ts_boottime;
}
}
@@ -1882,6 +1885,10 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
{
lockdep_assert_held(&rdev->bss_lock);
+ /* Update time stamps */
+ known->ts = new->ts;
+ known->pub.ts_boottime = new->pub.ts_boottime;
+
/* Update IEs */
if (rcu_access_pointer(new->pub.proberesp_ies)) {
const struct cfg80211_bss_ies *old;
@@ -1916,7 +1923,8 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
*/
f = rcu_access_pointer(new->pub.beacon_ies);
- kfree_rcu((struct cfg80211_bss_ies *)f, rcu_head);
+ if (!new->pub.hidden_beacon_bss)
+ kfree_rcu((struct cfg80211_bss_ies *)f, rcu_head);
return false;
}
@@ -1944,8 +1952,6 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
if (signal_valid)
known->pub.signal = new->pub.signal;
known->pub.capability = new->pub.capability;
- known->ts = new->ts;
- known->pub.ts_boottime = new->pub.ts_boottime;
known->parent_tsf = new->parent_tsf;
known->pub.chains = new->pub.chains;
memcpy(known->pub.chain_signal, new->pub.chain_signal,
@@ -2206,7 +2212,8 @@ struct cfg80211_inform_single_bss_data {
};
enum ieee80211_ap_reg_power
-cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len)
+cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,
+ u32 client_flags)
{
const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
struct ieee80211_he_operation *he_oper;
@@ -2224,26 +2231,13 @@ cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len)
if (!he_6ghz_oper)
return IEEE80211_REG_UNSET_AP;
- switch (u8_get_bits(he_6ghz_oper->control,
- IEEE80211_HE_6GHZ_OPER_CTRL_REG_INFO)) {
- case IEEE80211_6GHZ_CTRL_REG_LPI_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_LPI_AP:
- return IEEE80211_REG_LPI_AP;
- case IEEE80211_6GHZ_CTRL_REG_SP_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_SP_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_SP_AP_OLD:
- return IEEE80211_REG_SP_AP;
- case IEEE80211_6GHZ_CTRL_REG_VLP_AP:
- return IEEE80211_REG_VLP_AP;
- default:
- return IEEE80211_REG_UNSET_AP;
- }
+ return cfg80211_6ghz_power_type(he_6ghz_oper->control, client_flags);
}
static bool cfg80211_6ghz_power_type_valid(const u8 *elems, size_t elems_len,
const u32 flags)
{
- switch (cfg80211_get_6ghz_power_type(elems, elems_len)) {
+ switch (cfg80211_get_6ghz_power_type(elems, elems_len, flags)) {
case IEEE80211_REG_LPI_AP:
return true;
case IEEE80211_REG_SP_AP:
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 826ec0a6355f..3a028ff287fb 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -900,13 +900,16 @@ void __cfg80211_connect_result(struct net_device *dev,
if (!wdev->u.client.ssid_len) {
rcu_read_lock();
for_each_valid_link(cr, link) {
+ u32 ssid_len;
+
ssid = ieee80211_bss_get_elem(cr->links[link].bss,
WLAN_EID_SSID);
if (!ssid || !ssid->datalen)
continue;
- memcpy(wdev->u.client.ssid, ssid->data, ssid->datalen);
+ ssid_len = min(ssid->datalen, IEEE80211_MAX_SSID_LEN);
+ memcpy(wdev->u.client.ssid, ssid->data, ssid_len);
wdev->u.client.ssid_len = ssid->datalen;
break;
}
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 62f26618f674..8d142856e385 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -137,7 +137,7 @@ static int wiphy_resume(struct device *dev)
if (rdev->wiphy.registered && rdev->ops->resume)
ret = rdev_resume(rdev);
rdev->suspended = false;
- queue_work(system_unbound_wq, &rdev->wiphy_work);
+ queue_work(system_dfl_wq, &rdev->wiphy_work);
wiphy_unlock(&rdev->wiphy);
if (ret)
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 34c584a215e5..2b71f1d867a0 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -304,6 +304,27 @@ TRACE_EVENT(wiphy_delayed_work_queue,
__entry->delay)
);
+TRACE_EVENT(wiphy_hrtimer_work_queue,
+ TP_PROTO(struct wiphy *wiphy, struct wiphy_work *work,
+ ktime_t delay),
+ TP_ARGS(wiphy, work, delay),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(void *, instance)
+ __field(void *, func)
+ __field(ktime_t, delay)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->instance = work;
+ __entry->func = work->func;
+ __entry->delay = delay;
+ ),
+ TP_printk(WIPHY_PR_FMT " instance=%p func=%pS delay=%llu",
+ WIPHY_PR_ARG, __entry->instance, __entry->func,
+ __entry->delay)
+);
+
TRACE_EVENT(wiphy_work_worker_start,
TP_PROTO(struct wiphy *wiphy),
TP_ARGS(wiphy),
@@ -3137,23 +3158,6 @@ DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_notify_new_peer_candidate,
TP_ARGS(netdev, macaddr)
);
-DECLARE_EVENT_CLASS(netdev_evt_only,
- TP_PROTO(struct net_device *netdev),
- TP_ARGS(netdev),
- TP_STRUCT__entry(
- NETDEV_ENTRY
- ),
- TP_fast_assign(
- NETDEV_ASSIGN;
- ),
- TP_printk(NETDEV_PR_FMT , NETDEV_PR_ARG)
-);
-
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_rx_auth,
- TP_PROTO(struct net_device *netdev),
- TP_ARGS(netdev)
-);
-
TRACE_EVENT(cfg80211_send_rx_assoc,
TP_PROTO(struct net_device *netdev,
const struct cfg80211_rx_assoc_resp_data *data),
@@ -3480,21 +3484,6 @@ TRACE_EVENT(cfg80211_reg_can_beacon,
__entry->prohibited_flags, __entry->permitting_flags)
);
-TRACE_EVENT(cfg80211_chandef_dfs_required,
- TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
- TP_ARGS(wiphy, chandef),
- TP_STRUCT__entry(
- WIPHY_ENTRY
- CHAN_DEF_ENTRY
- ),
- TP_fast_assign(
- WIPHY_ASSIGN;
- CHAN_DEF_ASSIGN(chandef);
- ),
- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
- WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
-);
-
TRACE_EVENT(cfg80211_ch_switch_notify,
TP_PROTO(struct net_device *netdev,
struct cfg80211_chan_def *chandef,
@@ -3862,30 +3851,6 @@ DEFINE_EVENT(cfg80211_bss_evt, cfg80211_return_bss,
TP_ARGS(pub)
);
-TRACE_EVENT(cfg80211_return_uint,
- TP_PROTO(unsigned int ret),
- TP_ARGS(ret),
- TP_STRUCT__entry(
- __field(unsigned int, ret)
- ),
- TP_fast_assign(
- __entry->ret = ret;
- ),
- TP_printk("ret: %d", __entry->ret)
-);
-
-TRACE_EVENT(cfg80211_return_u32,
- TP_PROTO(u32 ret),
- TP_ARGS(ret),
- TP_STRUCT__entry(
- __field(u32, ret)
- ),
- TP_fast_assign(
- __entry->ret = ret;
- ),
- TP_printk("ret: %u", __entry->ret)
-);
-
TRACE_EVENT(cfg80211_report_wowlan_wakeup,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_wowlan_wakeup *wakeup),
@@ -4222,6 +4187,41 @@ TRACE_EVENT(cfg80211_epcs_changed,
WDEV_PR_ARG, __entry->enabled)
);
+TRACE_EVENT(cfg80211_next_nan_dw_notif,
+ TP_PROTO(struct wireless_dev *wdev,
+ struct ieee80211_channel *chan),
+ TP_ARGS(wdev, chan),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ CHAN_ASSIGN(chan);
+ ),
+ TP_printk(WDEV_PR_FMT " " CHAN_PR_FMT,
+ WDEV_PR_ARG, CHAN_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_nan_cluster_joined,
+ TP_PROTO(struct wireless_dev *wdev,
+ const u8 *cluster_id,
+ bool new_cluster),
+ TP_ARGS(wdev, cluster_id, new_cluster),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ MAC_ENTRY(cluster_id)
+ __field(bool, new_cluster)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ MAC_ASSIGN(cluster_id, cluster_id);
+ __entry->new_cluster = new_cluster;
+ ),
+ TP_printk(WDEV_PR_FMT " cluster_id %pMF%s",
+ WDEV_PR_ARG, __entry->cluster_id,
+ __entry->new_cluster ? " [new]" : "")
+);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 240c68baa3d1..27e8a2f52f04 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -106,33 +106,6 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
}
EXPORT_SYMBOL(ieee80211_channel_to_freq_khz);
-enum nl80211_chan_width
-ieee80211_s1g_channel_width(const struct ieee80211_channel *chan)
-{
- if (WARN_ON(!chan || chan->band != NL80211_BAND_S1GHZ))
- return NL80211_CHAN_WIDTH_20_NOHT;
-
- /*S1G defines a single allowed channel width per channel.
- * Extract that width here.
- */
- if (chan->flags & IEEE80211_CHAN_1MHZ)
- return NL80211_CHAN_WIDTH_1;
- else if (chan->flags & IEEE80211_CHAN_2MHZ)
- return NL80211_CHAN_WIDTH_2;
- else if (chan->flags & IEEE80211_CHAN_4MHZ)
- return NL80211_CHAN_WIDTH_4;
- else if (chan->flags & IEEE80211_CHAN_8MHZ)
- return NL80211_CHAN_WIDTH_8;
- else if (chan->flags & IEEE80211_CHAN_16MHZ)
- return NL80211_CHAN_WIDTH_16;
-
- pr_err("unknown channel width for channel at %dKHz?\n",
- ieee80211_channel_to_khz(chan));
-
- return NL80211_CHAN_WIDTH_1;
-}
-EXPORT_SYMBOL(ieee80211_s1g_channel_width);
-
int ieee80211_freq_khz_to_channel(u32 freq)
{
/* TODO: just handle MHz for now */
@@ -1230,28 +1203,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
dev->ieee80211_ptr->use_4addr = false;
rdev_set_qos_map(rdev, dev, NULL);
- switch (otype) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_P2P_GO:
- cfg80211_stop_ap(rdev, dev, -1, true);
- break;
- case NL80211_IFTYPE_ADHOC:
- cfg80211_leave_ibss(rdev, dev, false);
- break;
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_P2P_CLIENT:
- cfg80211_disconnect(rdev, dev,
- WLAN_REASON_DEAUTH_LEAVING, true);
- break;
- case NL80211_IFTYPE_MESH_POINT:
- /* mesh should be handled? */
- break;
- case NL80211_IFTYPE_OCB:
- cfg80211_leave_ocb(rdev, dev);
- break;
- default:
- break;
- }
+ cfg80211_leave(rdev, dev->ieee80211_ptr);
cfg80211_process_rdev_events(rdev);
cfg80211_mlme_purge_registrations(dev->ieee80211_ptr);
@@ -2584,7 +2536,7 @@ int cfg80211_get_radio_idx_by_chan(struct wiphy *wiphy,
}
}
- return -ENOENT;
+ return -EINVAL;
}
EXPORT_SYMBOL(cfg80211_get_radio_idx_by_chan);
@@ -2969,9 +2921,8 @@ cfg80211_get_iftype_ext_capa(struct wiphy *wiphy, enum nl80211_iftype type)
}
EXPORT_SYMBOL(cfg80211_get_iftype_ext_capa);
-static bool
-ieee80211_radio_freq_range_valid(const struct wiphy_radio *radio,
- u32 freq, u32 width)
+bool ieee80211_radio_freq_range_valid(const struct wiphy_radio *radio,
+ u32 freq, u32 width)
{
const struct wiphy_radio_freq_range *r;
int i;
@@ -2985,6 +2936,7 @@ ieee80211_radio_freq_range_valid(const struct wiphy_radio *radio,
return false;
}
+EXPORT_SYMBOL(ieee80211_radio_freq_range_valid);
bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
const struct cfg80211_chan_def *chandef)
@@ -2992,7 +2944,7 @@ bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
u32 freq, width;
freq = ieee80211_chandef_to_khz(chandef);
- width = cfg80211_chandef_get_width(chandef);
+ width = MHZ_TO_KHZ(cfg80211_chandef_get_width(chandef));
if (!ieee80211_radio_freq_range_valid(radio, freq, width))
return false;