CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320)
};
-static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef)
+static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef,
+ u32 primary_center, u32 punctured)
{
- u32 idx, i, start_freq, primary_center = chandef->chan->center_freq;
+ u32 idx, i, start_freq;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_80:
start_freq = chandef->center_freq1 - 160;
break;
default:
- return chandef->punctured == 0;
+ return punctured == 0;
}
- if (!chandef->punctured)
+ if (!punctured)
return true;
/* check if primary channel is punctured */
- if (chandef->punctured & (u16)BIT((primary_center - start_freq) / 20))
+ if (punctured & (u16)BIT((primary_center - start_freq) / 20))
return false;
for (i = 0; i < per_bw_puncturing[idx].len; i++) {
- if (per_bw_puncturing[idx].valid_values[i] == chandef->punctured)
+ if (per_bw_puncturing[idx].valid_values[i] == punctured)
return true;
}
if (!cfg80211_chandef_valid_control_freq(chandef, control_freq))
return false;
+ if (chandef->npca_chan) {
+ bool pri_upper, npca_upper;
+ u32 cf1;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_320:
+ break;
+ default:
+ return false;
+ }
+
+ if (!cfg80211_chandef_valid_control_freq(chandef,
+ chandef->npca_chan->center_freq))
+ return false;
+
+ cf1 = chandef->center_freq1;
+ pri_upper = chandef->chan->center_freq > cf1;
+ npca_upper = chandef->npca_chan->center_freq > cf1;
+
+ if (pri_upper == npca_upper)
+ return false;
+
+ if (!valid_puncturing_bitmap(chandef,
+ chandef->npca_chan->center_freq,
+ chandef->npca_punctured) ||
+ (chandef->punctured & chandef->npca_punctured) !=
+ chandef->punctured)
+ return false;
+ } else if (chandef->npca_punctured) {
+ return false;
+ }
+
if (!cfg80211_valid_center_freq(chandef->center_freq1, chandef->width))
return false;
if (!cfg80211_chandef_is_s1g(chandef) && chandef->s1g_primary_2mhz)
return false;
- return valid_puncturing_bitmap(chandef);
+ return valid_puncturing_bitmap(chandef, control_freq,
+ chandef->punctured);
}
EXPORT_SYMBOL(cfg80211_chandef_valid);
if (c1->width == c2->width)
return NULL;
+ /*
+ * We need NPCA to be compatible for some scenarios such as
+ * multiple APs, but in this case userspace should configure
+ * identical chandefs including NPCA, even if perhaps one of
+ * the AP interfaces doesn't even advertise it.
+ */
+ if (c1->npca_chan || c2->npca_chan)
+ return NULL;
+
/*
* can't be compatible if one of them is 5/10 MHz or S1G
* but they don't have the same width.
[NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_NAN_PEER_MAPS] =
NLA_POLICY_NESTED_ARRAY(nl80211_nan_peer_map_policy),
+ [NL80211_ATTR_NPCA_PRIMARY_FREQ] = { .type = NLA_U32 },
+ [NL80211_ATTR_NPCA_PUNCT_BITMAP] =
+ NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
};
/* policy for the key attributes */
static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct netlink_ext_ack *extack,
struct nlattr **attrs, bool monitor,
- struct cfg80211_chan_def *chandef)
+ struct cfg80211_chan_def *chandef,
+ bool permit_npca)
{
u32 control_freq;
}
}
+ if (attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ]) {
+ if (!permit_npca) {
+ NL_SET_ERR_MSG_ATTR(extack,
+ attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ],
+ "NPCA not supported");
+ return -EINVAL;
+ }
+
+ chandef->npca_chan =
+ ieee80211_get_channel(&rdev->wiphy,
+ nla_get_u32(attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ]));
+ if (!chandef->npca_chan) {
+ NL_SET_ERR_MSG_ATTR(extack,
+ attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ],
+ "invalid NPCA primary channel");
+ return -EINVAL;
+ }
+
+ chandef->npca_punctured =
+ nla_get_u32_default(attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP],
+ chandef->punctured);
+ } else if (attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP]) {
+ NL_SET_ERR_MSG_ATTR(extack,
+ attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP],
+ "NPCA puncturing only valid with NPCA");
+ return -EINVAL;
+ }
+
if (!cfg80211_chandef_valid(chandef)) {
NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ],
"invalid channel definition");
int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct netlink_ext_ack *extack,
struct nlattr **attrs,
- struct cfg80211_chan_def *chandef)
+ struct cfg80211_chan_def *chandef,
+ bool permit_npca)
{
- return _nl80211_parse_chandef(rdev, extack, attrs, false, chandef);
+ return _nl80211_parse_chandef(rdev, extack, attrs, false, chandef,
+ permit_npca);
}
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
struct wireless_dev *wdev = NULL;
int link_id = _link_id;
+ bool permit_npca;
if (dev)
wdev = dev->ieee80211_ptr;
link_id = 0;
}
+ /* allow parsing it - will check on start_ap or below */
+ permit_npca = iftype == NL80211_IFTYPE_AP ||
+ iftype == NL80211_IFTYPE_P2P_GO;
+
result = _nl80211_parse_chandef(rdev, info->extack, info->attrs,
iftype == NL80211_IFTYPE_MONITOR,
- &chandef);
+ &chandef, permit_npca);
if (result)
return result;
return -EBUSY;
/* Only allow dynamic channel width changes */
+ cur_chan = wdev->links[link_id].ap.chandef.npca_chan;
+ if (chandef.npca_chan != cur_chan)
+ return -EBUSY;
cur_chan = wdev->links[link_id].ap.chandef.chan;
if (chandef.chan != cur_chan)
return -EBUSY;
nla_put_flag(msg, NL80211_ATTR_S1G_PRIMARY_2MHZ))
return -ENOBUFS;
+ if (chandef->npca_chan &&
+ nla_put_u32(msg, NL80211_ATTR_NPCA_PRIMARY_FREQ,
+ chandef->npca_chan->center_freq))
+ return -ENOBUFS;
+ if (chandef->npca_punctured &&
+ nla_put_u32(msg, NL80211_ATTR_NPCA_PUNCT_BITMAP,
+ chandef->npca_punctured))
+ return -ENOBUFS;
+
return 0;
}
EXPORT_SYMBOL(nl80211_send_chandef);
return 0;
}
+static int nl80211_check_npca(struct cfg80211_registered_device *rdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype,
+ struct netlink_ext_ack *extack)
+{
+ const struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_uhr_cap *uhr_cap;
+
+ if (!chandef->npca_chan)
+ return 0;
+
+ sband = rdev->wiphy.bands[chandef->chan->band];
+ uhr_cap = ieee80211_get_uhr_iftype_cap(sband, iftype);
+
+ if (uhr_cap &&
+ (uhr_cap->mac.mac_cap[0] & IEEE80211_UHR_MAC_CAP0_NPCA_SUPP))
+ return 0;
+
+ NL_SET_ERR_MSG(extack, "NPCA not supported");
+ return -EINVAL;
+}
+
static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- ¶ms->chandef);
+ ¶ms->chandef, true);
if (err)
goto out;
} else if (wdev->valid_links) {
if (err)
goto out;
+ err = nl80211_check_npca(rdev, ¶ms->chandef, wdev->iftype,
+ info->extack);
+ if (err)
+ goto out;
+
beacon_check.iftype = wdev->iftype;
beacon_check.relax = true;
beacon_check.reg_power =
if (dfs_region == NL80211_DFS_UNSET)
return -EINVAL;
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef);
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef,
+ false);
if (err)
return err;
return -EINVAL;
}
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef);
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef,
+ false);
if (err) {
GENL_SET_ERR_MSG(info, "Unable to extract chandef info");
return err;
int err;
bool need_new_beacon = false;
bool need_handle_dfs_flag = true;
+ bool permit_npca = false;
u32 cs_count;
if (!rdev->ops->channel_switch ||
*/
need_handle_dfs_flag = false;
+ permit_npca = true;
+
/* useless if AP is not running */
if (!wdev->links[link_id].ap.beacon_interval)
return -ENOTCONN;
params.count = cs_count;
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- ¶ms.chandef);
+ ¶ms.chandef, permit_npca);
+ if (err)
+ goto free;
+
+ err = nl80211_check_npca(rdev, ¶ms.chandef, wdev->iftype,
+ info->extack);
if (err)
goto free;
}
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &ibss.chandef);
+ &ibss.chandef, false);
if (err)
return err;
duration > rdev->wiphy.max_remain_on_channel_duration)
return -EINVAL;
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef);
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef,
+ false);
if (err)
return err;
chandef.chan = NULL;
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &chandef);
+ &chandef, false);
if (err)
return err;
}
int err;
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &setup.chandef);
+ &setup.chandef, false);
if (err)
return err;
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &setup.chandef);
+ &setup.chandef, false);
if (err)
return err;
} else {
return ret;
ret = nl80211_parse_chandef(rdev, info->extack, channel_parsed,
- &chandef);
+ &chandef, false);
if (ret)
return ret;
!info->attrs[NL80211_ATTR_OPER_CLASS])
return -EINVAL;
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef);
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef,
+ false);
if (err)
return err;
__field(u32, center_freq1) \
__field(u32, freq1_offset) \
__field(u32, center_freq2) \
- __field(u16, punctured)
+ __field(u16, punctured) \
+ __field(u32, npca_pri_freq) \
+ __field(u16, npca_punctured)
#define CHAN_DEF_ASSIGN(chandef) \
do { \
if ((chandef) && (chandef)->chan) { \
__entry->freq1_offset = (chandef)->freq1_offset;\
__entry->center_freq2 = (chandef)->center_freq2;\
__entry->punctured = (chandef)->punctured; \
+ __entry->npca_pri_freq = \
+ (chandef)->npca_chan ? \
+ (chandef)->npca_chan->center_freq : 0; \
+ __entry->npca_punctured = \
+ (chandef)->npca_punctured; \
} else { \
__entry->band = 0; \
__entry->control_freq = 0; \
__entry->freq1_offset = 0; \
__entry->center_freq2 = 0; \
__entry->punctured = 0; \
+ __entry->npca_pri_freq = 0; \
+ __entry->npca_punctured = 0; \
} \
} while (0)
#define CHAN_DEF_PR_FMT \
- "band: %d, control freq: %u.%03u, width: %d, cf1: %u.%03u, cf2: %u, punct: 0x%x"
+ "band: %d, control freq: %u.%03u, width: %d, cf1: %u.%03u, cf2: %u, punct: 0x%x, npca:%u, npca_punct:0x%x"
#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \
__entry->freq_offset, __entry->width, \
__entry->center_freq1, __entry->freq1_offset, \
- __entry->center_freq2, __entry->punctured
+ __entry->center_freq2, __entry->punctured, \
+ __entry->npca_pri_freq, __entry->npca_punctured
#define FILS_AAD_ASSIGN(fa) \
do { \