The driver maintains its own block_* bitmaps on top of cfg80211's regulatory enforcement. These bitmaps are indexed by the position of an entry in the active regd_map (either the built-in rtw89_regd_map or the table loaded from a firmware element). The ww domain is handled outside the regd_map; it lives in a standalone rtw89_ww_regd object, and rtw89_regd_get_index() returns the RTW89_REGD_MAX_COUNTRY_NUM sentinel for it. Every block_* lookup site guards against that sentinel and bails out, which means none of the block_* checks ever apply to ww. The 6GHz check therefore treats ww as unconditionally blocked at the driver layer, independently of whatever cfg80211/wireless-regdb permit. Make ww a first-class entry in the regd_map instead: - Reserve slot 0 of rtw89_regd_map for the ww entry and drop the standalone rtw89_ww_regd object. - In rtw89_recognize_regd_from_elm(), reserve slot 0 of the firmware-loaded map for ww and populate it from the built-in definition via the new rtw89_regd_static_ww_entry() helper. - Drop the rtw89_regd_is_ww() special case in rtw89_regd_get_index() and identify ww by alpha2 so the check works for entries in either map. - Have rtw89_regd_find_reg_by_name() return NULL on miss instead of silently substituting ww. Callers that need the previous "fall back to ww" behaviour (rtw89_regd_notifier_apply()) now do so explicitly against the active map's slot 0, and rtw89_regd_get_index_by_name() translates NULL into the RTW89_REGD_MAX_COUNTRY_NUM sentinel so the existing "unknown alpha2" branches in the block_* setup paths remain. With ww at a real index the driver's block_* checks apply uniformly for all bands and cfg80211 / wireless-regdb become the sole source of truth for what is permitted on the ww domain. Signed-off-by: Matthew Leach --- Changes in v2: - Re-word the commit message, clarifying the motivation behind the patch (removing the explicit 6GHz disablement). - Re-work the patch to play nicely with firmware loaded regulatory tables by reserving slot 0 in both static and firmware regulatory lists for the WW domain. - Link to v1: https://patch.msgid.link/20260617-rtw89-6ghz-regd-fixes-v1-1-33d744a07d16@collabora.com To: Ping-Ke Shih Cc: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/fw.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/regd.c | 32 +++++++++++++++++++++---------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index fd29dbbb120d..dc9eae2cee69 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -7862,6 +7862,7 @@ void rtw89_chip_cfg_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev, bool rtw89_legacy_rate_to_bitrate(struct rtw89_dev *rtwdev, u8 legacy_rate, u16 *bitrate); int rtw89_regd_setup(struct rtw89_dev *rtwdev); int rtw89_regd_init_hint(struct rtw89_dev *rtwdev); +const struct rtw89_regd *rtw89_regd_static_ww_entry(void); const char *rtw89_regd_get_string(enum rtw89_regulation_type regd); void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 17704f054727..77f320cae01b 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1347,10 +1347,11 @@ int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev, { const struct __rtw89_fw_regd_element *regd_elm = &elm->u.regd; struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; - u32 num_ents = le32_to_cpu(regd_elm->num_ents); struct rtw89_regd_data *p; struct rtw89_regd regd; - u32 i = 0; + /* +1 because we reserve index 0 for WW */ + u32 num_ents = le32_to_cpu(regd_elm->num_ents) + 1; + u32 i = 1; if (num_ents > RTW89_REGD_MAX_COUNTRY_NUM) { rtw89_warn(rtwdev, @@ -1373,6 +1374,7 @@ int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev, return -ENOMEM; p->nr = num_ents; + p->map[0] = *rtw89_regd_static_ww_entry(); rtw89_for_each_in_regd_element(®d, regd_elm) p->map[i++] = regd; diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 28466cb35ea2..27ac87374e6e 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -21,10 +21,8 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request static_assert(BITS_PER_TYPE(unsigned long) >= NUM_OF_RTW89_REGD_FUNC); -static const struct rtw89_regd rtw89_ww_regd = - COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0); - static const struct rtw89_regd rtw89_regd_map[] = { + COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0), COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0), COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), @@ -316,12 +314,12 @@ static const struct rtw89_regd *rtw89_regd_find_reg_by_name(struct rtw89_dev *rt return ®d_ctrl->map[i]; } - return &rtw89_ww_regd; + return NULL; } static bool rtw89_regd_is_ww(const struct rtw89_regd *regd) { - return regd == &rtw89_ww_regd; + return !memcmp(regd->alpha2, "00", 2); } static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd *regd) @@ -331,9 +329,6 @@ static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM); - if (rtw89_regd_is_ww(regd)) - return RTW89_REGD_MAX_COUNTRY_NUM; - return regd - regd_ctrl->map; } @@ -342,6 +337,10 @@ static u8 rtw89_regd_get_index_by_name(struct rtw89_dev *rtwdev, const char *alp const struct rtw89_regd *regd; regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2); + + if (!regd) + return RTW89_REGD_MAX_COUNTRY_NUM; + return rtw89_regd_get_index(rtwdev, regd); } @@ -721,7 +720,7 @@ int rtw89_regd_init_hint(struct rtw89_dev *rtwdev) return -EINVAL; chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code); - if (!rtw89_regd_is_ww(chip_regd)) { + if (chip_regd && !rtw89_regd_is_ww(chip_regd)) { rtwdev->regulatory.regd = chip_regd; rtwdev->regulatory.programmed = true; @@ -743,6 +742,11 @@ int rtw89_regd_init_hint(struct rtw89_dev *rtwdev) return 0; } +const struct rtw89_regd *rtw89_regd_static_ww_entry(void) +{ + return &rtw89_regd_map[0]; +} + static void rtw89_regd_apply_policy_unii4(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { @@ -859,7 +863,15 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, struct wiphy *wiphy, struct regulatory_request *request) { - rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2); + const struct rtw89_regd *regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2); + + if (!regd) { + /* Fallback to WW domain if name not found. */ + regd = &rtwdev->regulatory.ctrl.map[0]; + } + + rtwdev->regulatory.regd = regd; + /* This notification might be set from the system of distros, * and it does not expect the regulatory will be modified by * connecting to an AP (i.e. country ie). --- base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6 change-id: 20260616-rtw89-6ghz-regd-fixes-d8f816880bd0 Best regards, -- Matt