@@ -4238,6 +4238,8 @@ struct mgmt_frame_regs {
* radar channel.
* The caller is expected to set chandef pointer to NULL in order to
* disable background CAC/radar detection.
+ *
+ * @get_6ghz_power_mode: Get the 6 GHz power mode for the given interface.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -4583,6 +4585,8 @@ struct cfg80211_ops {
struct cfg80211_fils_aad *fils_aad);
int (*set_radar_background)(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef);
+ int (*get_6ghz_power_mode)(struct wireless_dev *wdev,
+ enum nl80211_regulatory_power_modes *power_mode_6ghz);
};
/*
@@ -4549,6 +4549,40 @@ static void ieee80211_del_intf_link(struct wiphy *wiphy,
ieee80211_vif_set_links(sdata, wdev->valid_links);
}
+static int
+ieee80211_get_6ghz_power_mode(struct wireless_dev *wdev,
+ enum nl80211_regulatory_power_modes *power_mode_6ghz)
+{
+ enum nl80211_iftype iftype;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_vif vif;
+
+ if (!wdev)
+ return 1;
+
+ iftype = wdev->iftype;
+
+ /* For APs, 6 GHz power mode is taken from the user configured
+ * value. However, for clients, power mode is also dependent
+ * upon the APs power mode to which this client has associated.
+ * Hence for client, need to take power mode of asscoiated AP,
+ * which is present in beacon data.
+ */
+ if (iftype == NL80211_IFTYPE_AP) {
+ *power_mode_6ghz =
+ ieee80211_ap_reg_power_to_reg_power_mode(wdev->ap_6ghz_power);
+ } else if (iftype == NL80211_IFTYPE_STATION) {
+ sdata = container_of(wdev, struct ieee80211_sub_if_data, wdev);
+ vif = sdata->vif;
+
+ *power_mode_6ghz =
+ ieee80211_client_reg_power_to_reg_power_mode(wdev->client_6ghz_power,
+ vif.bss_conf.power_type);
+ }
+
+ return 0;
+}
+
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -4656,4 +4690,5 @@ const struct cfg80211_ops mac80211_config_ops = {
.set_radar_background = ieee80211_set_radar_background,
.add_intf_link = ieee80211_add_intf_link,
.del_intf_link = ieee80211_del_intf_link,
+ .get_6ghz_power_mode = ieee80211_get_6ghz_power_mode,
};
@@ -3163,10 +3163,12 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct genl_info *info,
- struct cfg80211_chan_def *chandef)
+ struct cfg80211_chan_def *chandef,
+ struct wireless_dev *wdev)
{
struct netlink_ext_ack *extack = info->extack;
struct nlattr **attrs = info->attrs;
+ enum nl80211_regulatory_power_modes power_mode_6ghz = NL80211_REG_AP_LPI;
u32 control_freq;
if (!attrs[NL80211_ATTR_WIPHY_FREQ])
@@ -3179,7 +3181,26 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]);
memset(chandef, 0, sizeof(*chandef));
- chandef->chan = ieee80211_get_channel_khz(&rdev->wiphy, control_freq);
+
+ if (control_freq >= MHZ_TO_KHZ(5945) && control_freq <= MHZ_TO_KHZ(7125)) {
+ if (!wdev)
+ return -EINVAL;
+
+ if (!rdev->ops->get_6ghz_power_mode)
+ return -EINVAL;
+
+ power_mode_6ghz = rdev_get_6ghz_power_mode(rdev, wdev);
+
+ if (power_mode_6ghz >= NL80211_REG_MAX_POWER_MODES + 1)
+ return -EINVAL;
+
+ chandef->chan = ieee80211_get_6ghz_channel_khz(&rdev->wiphy,
+ control_freq,
+ power_mode_6ghz);
+ } else {
+ chandef->chan = ieee80211_get_channel_khz(&rdev->wiphy, control_freq);
+ }
+
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
chandef->center_freq1 = KHZ_TO_MHZ(control_freq);
chandef->freq1_offset = control_freq % 1000;
@@ -3310,7 +3331,7 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
link_id = 0;
}
- result = nl80211_parse_chandef(rdev, info, &chandef);
+ result = nl80211_parse_chandef(rdev, info, &chandef, wdev);
if (result)
return result;
@@ -5786,7 +5807,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- err = nl80211_parse_chandef(rdev, info, ¶ms->chandef);
+ err = nl80211_parse_chandef(rdev, info, ¶ms->chandef,
+ wdev);
if (err)
goto out;
} else if (wdev->valid_links) {
@@ -9580,7 +9602,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (dfs_region == NL80211_DFS_UNSET)
goto unlock;
- err = nl80211_parse_chandef(rdev, info, &chandef);
+ err = nl80211_parse_chandef(rdev, info, &chandef, wdev);
if (err)
goto unlock;
@@ -9660,7 +9682,7 @@ static int nl80211_notify_radar_detection(struct sk_buff *skb,
return -EINVAL;
}
- err = nl80211_parse_chandef(rdev, info, &chandef);
+ err = nl80211_parse_chandef(rdev, info, &chandef, wdev);
if (err) {
GENL_SET_ERR_MSG(info, "Unable to extract chandef info");
return err;
@@ -9858,7 +9880,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
}
skip_beacons:
- err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
+ err = nl80211_parse_chandef(rdev, info, ¶ms.chandef, wdev);
if (err)
goto free;
@@ -10903,6 +10925,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_ibss_params ibss;
struct wiphy *wiphy;
struct cfg80211_cached_keys *connkeys = NULL;
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
memset(&ibss, 0, sizeof(ibss));
@@ -10925,7 +10948,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->join_ibss)
return -EOPNOTSUPP;
- if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
+ if (wdev->iftype != NL80211_IFTYPE_ADHOC)
return -EOPNOTSUPP;
wiphy = &rdev->wiphy;
@@ -10944,7 +10967,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
- err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
+ err = nl80211_parse_chandef(rdev, info, &ibss.chandef, wdev);
if (err)
return err;
@@ -11043,13 +11066,13 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
ibss.userspace_handles_dfs =
nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
- wdev_lock(dev->ieee80211_ptr);
+ wdev_lock(wdev);
err = __cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
if (err)
kfree_sensitive(connkeys);
else if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
- dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
- wdev_unlock(dev->ieee80211_ptr);
+ wdev->conn_owner_nlportid = info->snd_portid;
+ wdev_unlock(wdev);
return err;
}
@@ -11918,7 +11941,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
duration > rdev->wiphy.max_remain_on_channel_duration)
return -EINVAL;
- err = nl80211_parse_chandef(rdev, info, &chandef);
+ err = nl80211_parse_chandef(rdev, info, &chandef, wdev);
if (err)
return err;
@@ -12132,7 +12155,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
*/
chandef.chan = NULL;
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- err = nl80211_parse_chandef(rdev, info, &chandef);
+ err = nl80211_parse_chandef(rdev, info, &chandef, wdev);
if (err)
return err;
}
@@ -12525,7 +12548,8 @@ static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
struct ocb_setup setup = {};
int err;
- err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef,
+ dev->ieee80211_ptr);
if (err)
return err;
@@ -12600,7 +12624,8 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
cfg.auto_open_plinks = false;
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef,
+ dev->ieee80211_ptr);
if (err)
return err;
} else {
@@ -14933,7 +14958,7 @@ static int nl80211_tdls_channel_switch(struct sk_buff *skb,
!info->attrs[NL80211_ATTR_OPER_CLASS])
return -EINVAL;
- err = nl80211_parse_chandef(rdev, info, &chandef);
+ err = nl80211_parse_chandef(rdev, info, &chandef, wdev);
if (err)
return err;
@@ -24,7 +24,8 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct genl_info *info,
- struct cfg80211_chan_def *chandef);
+ struct cfg80211_chan_def *chandef,
+ struct wireless_dev *wdev);
int nl80211_parse_random_mac(struct nlattr **attrs,
u8 *mac_addr, u8 *mac_addr_mask);
@@ -184,7 +184,8 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
struct nlattr *peer,
struct cfg80211_pmsr_request_peer *out,
- struct genl_info *info)
+ struct genl_info *info,
+ struct wireless_dev *wdev)
{
struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
@@ -213,7 +214,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
if (err)
return err;
- err = nl80211_parse_chandef(rdev, info, &out->chandef);
+ err = nl80211_parse_chandef(rdev, info, &out->chandef, wdev);
if (err)
return err;
@@ -316,7 +317,8 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
idx = 0;
nla_for_each_nested(peer, peers, rem) {
/* NB: this reuses info->attrs, but we no longer need it */
- err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info);
+ err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info,
+ wdev);
if (err)
goto out_err;
idx++;
@@ -1448,4 +1448,23 @@ rdev_del_intf_link(struct cfg80211_registered_device *rdev,
trace_rdev_return_void(&rdev->wiphy);
}
+static inline enum nl80211_regulatory_power_modes
+rdev_get_6ghz_power_mode(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ enum nl80211_regulatory_power_modes power_mode_6ghz = NL80211_REG_AP_LPI;
+ int ret;
+
+ if (!rdev->ops->get_6ghz_power_mode)
+ return NL80211_REG_MAX_POWER_MODES + 1;
+
+ trace_rdev_get_6ghz_power_mode(&rdev->wiphy, wdev);
+ ret = rdev->ops->get_6ghz_power_mode(wdev, &power_mode_6ghz);
+ trace_rdev_return_6ghz_power_mode(wdev, ret, power_mode_6ghz);
+
+ if (ret)
+ return NL80211_REG_MAX_POWER_MODES + 1;
+
+ return power_mode_6ghz;
+}
#endif /* __CFG80211_RDEV_OPS */
@@ -3775,6 +3775,27 @@ TRACE_EVENT(cfg80211_assoc_comeback,
WDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->timeout)
);
+DEFINE_EVENT(wiphy_wdev_evt, rdev_get_6ghz_power_mode,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_return_6ghz_power_mode,
+ TP_PROTO(struct wireless_dev *wdev, int ret, u8 power_mode),
+ TP_ARGS(wdev, ret, power_mode),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(u8, ret)
+ __field(u8, power_mode)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->ret = ret;
+ __entry->power_mode = power_mode;
+ ),
+ TP_printk(WDEV_PR_FMT ", function returns: %d, function filled: %d",
+ WDEV_PR_ARG, __entry->ret, __entry->power_mode)
+);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
Currently, nl80211_parse_chandef function just selects a channel based on the control frequency provided. However, for 6 GHz, power mode also needs to be considered since 6 GHz has got multiple channel pools based on the power mode. Modify logic to consider power mode as well for 6 GHz interface and accordingly select the channel for the given control frequency. Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> --- include/net/cfg80211.h | 4 +++ net/mac80211/cfg.c | 35 ++++++++++++++++++++++++ net/wireless/nl80211.c | 59 +++++++++++++++++++++++++++++------------ net/wireless/nl80211.h | 3 ++- net/wireless/pmsr.c | 8 +++--- net/wireless/rdev-ops.h | 19 +++++++++++++ net/wireless/trace.h | 21 +++++++++++++++ 7 files changed, 128 insertions(+), 21 deletions(-)