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@xxxxxxxxxxx> --- include/net/cfg80211.h | 5 ++++ net/mac80211/cfg.c | 38 +++++++++++++++++++++++++++ net/wireless/nl80211.c | 57 +++++++++++++++++++++++++++++------------ net/wireless/nl80211.h | 3 ++- net/wireless/pmsr.c | 8 +++--- net/wireless/rdev-ops.h | 20 +++++++++++++++ net/wireless/trace.h | 34 ++++++++++++++++++++++++ 7 files changed, 144 insertions(+), 21 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4675a6feb09b..9dcda3e8c1d9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4353,6 +4353,8 @@ struct mgmt_frame_regs { * @add_link_station: Add a link to a station. * @mod_link_station: Modify a link of a station. * @del_link_station: Remove a link of a station. + * + * @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); @@ -4706,6 +4708,9 @@ struct cfg80211_ops { struct link_station_parameters *params); int (*del_link_station)(struct wiphy *wiphy, struct net_device *dev, struct link_station_del_parameters *params); + int (*get_6ghz_power_mode)(struct wireless_dev *wdev, + unsigned int link_id, + enum nl80211_regulatory_power_modes *power_mode); }; /* diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8eb342300868..f5b66a30b848 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4904,6 +4904,44 @@ ieee80211_del_link_station(struct wiphy *wiphy, struct net_device *dev, return ret; } +static int +ieee80211_get_6ghz_power_mode(struct wireless_dev *wdev, + unsigned int link_id, + enum nl80211_regulatory_power_modes *power_mode_6ghz) +{ + enum nl80211_iftype iftype; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_vif vif; + enum ieee80211_ap_reg_power power_mode_6ghz_ap; + enum ieee80211_client_reg_power power_mode_6ghz_client; + + if (!wdev) + return -EINVAL; + + 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 associated AP, + * which is present in beacon data. + */ + if (iftype == NL80211_IFTYPE_AP) { + power_mode_6ghz_ap = wdev->links[link_id].ap.power_mode_6ghz; + *power_mode_6ghz = + ieee80211_ap_reg_power_to_reg_power_mode(power_mode_6ghz_ap); + } else if (iftype == NL80211_IFTYPE_STATION) { + sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + vif = sdata->vif; + power_mode_6ghz_client = wdev->links[link_id].client.power_mode_6ghz; + *power_mode_6ghz = + ieee80211_client_reg_power_to_reg_power_mode(power_mode_6ghz_client, + 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, @@ -5014,4 +5051,5 @@ const struct cfg80211_ops mac80211_config_ops = { .add_link_station = ieee80211_add_link_station, .mod_link_station = ieee80211_mod_link_station, .del_link_station = ieee80211_del_link_station, + .get_6ghz_power_mode = ieee80211_get_6ghz_power_mode, }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9dd3876f6440..19688935251f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3200,10 +3200,13 @@ static int nl80211_parse_punct_bitmap(struct cfg80211_registered_device *rdev, 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; + unsigned int link_id = nl80211_link_id(info->attrs); u32 control_freq; if (!attrs[NL80211_ATTR_WIPHY_FREQ]) { @@ -3219,7 +3222,23 @@ 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; + + power_mode_6ghz = rdev_get_6ghz_power_mode(rdev, wdev, link_id); + + if (power_mode_6ghz >= NL80211_REG_PWR_MODE_MAX) + 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; @@ -3350,7 +3369,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; @@ -5968,7 +5987,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) { @@ -9855,7 +9875,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; @@ -9935,7 +9955,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; @@ -10133,7 +10153,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; @@ -11226,6 +11246,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)); @@ -11248,7 +11269,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; @@ -11267,7 +11288,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; @@ -11366,13 +11387,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; } @@ -12238,7 +12259,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; @@ -12460,7 +12481,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; } @@ -12869,7 +12890,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; @@ -12944,7 +12966,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 { @@ -15281,7 +15304,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; diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 0278d817bb02..24966630ba27 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -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); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 2bc647720cda..501b8af547e1 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -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++; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 13b209a8db28..b119abc29593 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1494,4 +1494,24 @@ rdev_del_link_station(struct cfg80211_registered_device *rdev, return ret; } +static inline enum nl80211_regulatory_power_modes +rdev_get_6ghz_power_mode(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + unsigned int link_id) +{ + enum nl80211_regulatory_power_modes power_mode_6ghz; + int ret; + + if (!rdev->ops->get_6ghz_power_mode) + return NL80211_REG_PWR_MODE_MAX; + + trace_rdev_get_6ghz_power_mode(&rdev->wiphy, wdev, link_id); + ret = rdev->ops->get_6ghz_power_mode(wdev, link_id, &power_mode_6ghz); + trace_rdev_return_6ghz_power_mode(wdev, ret, power_mode_6ghz); + + if (ret) + return NL80211_REG_PWR_MODE_MAX; + + return power_mode_6ghz; +} #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index ca7474eec723..1f57697ec38e 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -3918,6 +3918,40 @@ TRACE_EVENT(rdev_del_link_station, __entry->link_id) ); +TRACE_EVENT(rdev_get_6ghz_power_mode, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + unsigned int link_id), + TP_ARGS(wiphy, wdev, link_id), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(u32, link_id) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->link_id = params->link_id; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", link_id: %u", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->link_id) +); + +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 ", ret: %d, power_mode: %d", + WDEV_PR_ARG, __entry->ret, __entry->power_mode) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- 2.17.1