@@ -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);
};
/*
@@ -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,
};
@@ -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;
@@ -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++;
@@ -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 */
@@ -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
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 | 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(-)