From 4b7ad021a981c2e1c6b7b65ab8f03d24a7173042 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Mon, 21 Sep 2026 22:54:54 +0300 Subject: [PATCH] wifi: nl80211: defer AP beacon regulatory check to start_ap Defer the cfg80211_reg_can_beacon_relax() check from __nl80211_set_channel() for AP and P2P-GO. At __nl80211_set_channel() stage AP type is not fully known before start_ap, so this early check incorrectly rejects valid VLP AP bring-up. start_ap has the required AP context and already performs the regulatory validation, so start checking after start_ap. This way, VLP APs can be brought up. Also handle the CSA scenario by running regulatory beaconing validation during channel switch and preserving/updating AP reg_power across CSA completion. Signed-off-by: Pagadala Yesu Anjaneyulu Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260921224731.67fbc8a28a2c.I5633284cb4c82ffb4e4e32e6f2dcc9a71fd5b06a@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/ap.c | 7 +++---- net/wireless/nl80211.c | 31 ++++++++++++++++++++++++++----- net/wireless/reg.c | 9 +++++++-- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0b3fcb2a9576..b603f1804cc2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -7371,6 +7371,8 @@ struct wireless_dev { union { struct { unsigned int beacon_interval; + enum ieee80211_ap_reg_power reg_power; + enum ieee80211_ap_reg_power csa_reg_power; struct cfg80211_chan_def chandef; } ap; struct { diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 9a9a870806f5..fa5441987a90 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Parts of this file are - * Copyright (C) 2022-2023 Intel Corporation + * Copyright (C) 2022-2023, 2026 Intel Corporation */ #include #include @@ -33,9 +33,8 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev, err = rdev_stop_ap(rdev, dev, link_id); if (!err) { wdev->conn_owner_nlportid = 0; - wdev->links[link_id].ap.beacon_interval = 0; - memset(&wdev->links[link_id].ap.chandef, 0, - sizeof(wdev->links[link_id].ap.chandef)); + memset(&wdev->links[link_id].ap, 0, + sizeof(wdev->links[link_id].ap)); wdev->u.ap.ssid_len = 0; rdev_set_qos_map(rdev, dev, NULL); if (notify) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 91e26b93a523..ae994568c86e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4156,12 +4156,17 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &chandef, - iftype)) - return -EINVAL; if (wdev->links[link_id].ap.beacon_interval) { struct ieee80211_channel *cur_chan; + struct cfg80211_beaconing_check_config config = { + .iftype = iftype, + .reg_power = wdev->links[link_id].ap.reg_power, + .relax = true, + }; + if (!cfg80211_reg_check_beaconing(&rdev->wiphy, + &chandef, &config)) + return -EINVAL; if (!dev || !rdev->ops->set_ap_chanwidth || !(rdev->wiphy.features & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) @@ -7397,6 +7402,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params->beacon.tail_len, 0); if (!cfg80211_reg_check_beaconing(&rdev->wiphy, ¶ms->chandef, &beacon_check)) { + GENL_SET_ERR_MSG(info, "Channel rejected by regulatory"); err = -EINVAL; goto out; } @@ -7511,6 +7517,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) err = rdev_start_ap(rdev, dev, params); if (!err) { wdev->links[link_id].ap.beacon_interval = params->beacon_interval; + wdev->links[link_id].ap.reg_power = beacon_check.reg_power; wdev->links[link_id].ap.chandef = params->chandef; wdev->u.ap.ssid_len = params->ssid_len; memcpy(wdev->u.ap.ssid, params->ssid, @@ -7603,6 +7610,8 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) } err = rdev_change_beacon(rdev, dev, params); + if (!err) + wdev->links[link_id].ap.reg_power = beacon_check.reg_power; out: kfree(params->beacon.mbssid_ies); @@ -12314,6 +12323,7 @@ static int nl80211_parse_counter_offsets(struct cfg80211_registered_device *rdev static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_beaconing_check_config beacon_check = {}; unsigned int link_id = nl80211_link_id(info->attrs); struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -12438,8 +12448,13 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) goto free; skip_beacons: - if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef, - wdev->iftype)) { + beacon_check.iftype = wdev->iftype; + beacon_check.relax = true; + beacon_check.reg_power = + cfg80211_get_6ghz_power_type(params.beacon_after.tail, + params.beacon_after.tail_len, 0); + if (!cfg80211_reg_check_beaconing(&rdev->wiphy, ¶ms.chandef, + &beacon_check)) { err = -EINVAL; goto free; } @@ -12475,6 +12490,10 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) params.link_id = link_id; err = rdev_channel_switch(rdev, dev, ¶ms); + if (!err && (wdev->iftype == NL80211_IFTYPE_AP || + wdev->iftype == NL80211_IFTYPE_P2P_GO)) + wdev->links[link_id].ap.csa_reg_power = beacon_check.reg_power; + free: kfree(params.beacon_after.mbssid_ies); kfree(params.beacon_csa.mbssid_ies); @@ -22747,6 +22766,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: wdev->links[link_id].ap.chandef = *chandef; + wdev->links[link_id].ap.reg_power = + wdev->links[link_id].ap.csa_reg_power; break; case NL80211_IFTYPE_ADHOC: wdev->u.ibss.chandef = *chandef; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 11665e0a7efc..035b95508565 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2361,6 +2361,10 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) } for (link = 0; link < ARRAY_SIZE(wdev->links); link++) { + struct cfg80211_beaconing_check_config config = { + .iftype = iftype, + .relax = true, + }; struct ieee80211_channel *chan; if (!wdev->valid_links && link > 0) @@ -2373,6 +2377,7 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) if (!wdev->links[link].ap.beacon_interval) continue; chandef = wdev->links[link].ap.chandef; + config.reg_power = wdev->links[link].ap.reg_power; break; case NL80211_IFTYPE_MESH_POINT: if (!wdev->u.mesh.beacon_interval) @@ -2426,8 +2431,8 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: - ret = cfg80211_reg_can_beacon_relax(wiphy, &chandef, - iftype); + ret = cfg80211_reg_check_beaconing(wiphy, &chandef, + &config); if (!ret) return ret; break;