rtw88: add regulatory strategy by chip type
authorZong-Zhe Yang <kevin_yang@realtek.com>
Mon, 30 Aug 2021 07:20:12 +0000 (15:20 +0800)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 21 Sep 2021 14:51:56 +0000 (17:51 +0300)
Realtek chips can program a specific country domain on efuse to
indicate what is the expected rtw_regulatory. For chips with a
programmed country domain, we set REGULATORY_STRICT_REG to tell
stack to consider follow-up regulatory_hint() as the superset of
our regulatory rule. Besides, on driver side, only the request via
NL80211_REGDOM_SET_BY_DRIVER, which matches programmed country
domain, will be handled to keep rtw_regulatory unchanged.

For worldwide roaming chips, i.e. ones without a specific programmed
country domain, system of distro can set expected regulatory via
NL80211_REGDOM_SET_BY_USER. With setting from it, rtw_regulatory
will handle the requests only via NL80211_REGDOM_SET_BY_USER to
follow setting from system of distro. REGULATORY_COUNTRY_IE_IGNORE
will then be set to tell stack to ignore country IE for us. The
restrictions mentioned above will remain until 00, i.e. worldwide,
is set via NL80211_REGDOM_SET_BY_USER.

On the other hand, for worldwide roamin chips, if there is no
specific regulatory set via NL80211_REGDOM_SET_BY_USER, requests
from all regulatory notifications will be handled by rtw_regulatory.
And REGULATORY_COUNTRY_IE_IGNORE won't be set.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20210830072014.12250-3-pkshih@realtek.com
drivers/net/wireless/realtek/rtw88/main.c
drivers/net/wireless/realtek/rtw88/main.h
drivers/net/wireless/realtek/rtw88/regd.c
drivers/net/wireless/realtek/rtw88/regd.h

index 6bb55e6..f458496 100644 (file)
@@ -1964,7 +1964,11 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
        rtw_set_supported_band(hw, rtwdev->chip);
        SET_IEEE80211_PERM_ADDR(hw, rtwdev->efuse.addr);
 
-       rtw_regd_init(rtwdev, rtw_regd_notifier);
+       ret = rtw_regd_init(rtwdev);
+       if (ret) {
+               rtw_err(rtwdev, "failed to init regd\n");
+               return ret;
+       }
 
        ret = ieee80211_register_hw(hw);
        if (ret) {
@@ -1972,8 +1976,11 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
                return ret;
        }
 
-       if (regulatory_hint(hw->wiphy, rtwdev->regd.alpha2))
-               rtw_err(rtwdev, "regulatory_hint fail\n");
+       ret = rtw_regd_hint(rtwdev);
+       if (ret) {
+               rtw_err(rtwdev, "failed to hint regd\n");
+               return ret;
+       }
 
        rtw_debugfs_init(rtwdev);
 
index 0d1bbc7..2e5cebb 100644 (file)
@@ -804,6 +804,19 @@ struct rtw_regulatory {
        u8 txpwr_regd_5g;
 };
 
+enum rtw_regd_state {
+       RTW_REGD_STATE_WORLDWIDE,
+       RTW_REGD_STATE_PROGRAMMED,
+       RTW_REGD_STATE_SETTING,
+
+       RTW_REGD_STATE_NR,
+};
+
+struct rtw_regd {
+       enum rtw_regd_state state;
+       const struct rtw_regulatory *regulatory;
+};
+
 struct rtw_chip_ops {
        int (*mac_init)(struct rtw_dev *rtwdev);
        int (*dump_fw_crash)(struct rtw_dev *rtwdev);
@@ -1833,7 +1846,7 @@ struct rtw_dev {
        struct rtw_efuse efuse;
        struct rtw_sec_desc sec;
        struct rtw_traffic_stats stats;
-       struct rtw_regulatory regd;
+       struct rtw_regd regd;
        struct rtw_bf_info bf_info;
 
        struct rtw_dm_info dm_info;
index a00ac08..cd50f41 100644 (file)
 #define rtw_dbg_regd_dump(_dev, _msg, _args...)                        \
 do {                                                           \
        struct rtw_dev *__d = (_dev);                           \
-       const struct rtw_regulatory *__r =  &__d->regd;         \
+       const struct rtw_regd *__r =  &__d->regd;               \
        rtw_dbg(__d, RTW_DBG_REGD, _msg                         \
                "apply alpha2 %c%c, regd {%d, %d}\n",           \
                ##_args,                                        \
-               __r->alpha2[0],                                 \
-               __r->alpha2[1],                                 \
-               __r->txpwr_regd_2g,                             \
-               __r->txpwr_regd_5g);                            \
+               __r->regulatory->alpha2[0],                     \
+               __r->regulatory->alpha2[1],                     \
+               __r->regulatory->txpwr_regd_2g,                 \
+               __r->regulatory->txpwr_regd_5g);                \
 } while (0)
 
 /* If country code is not correctly defined in efuse,
@@ -306,67 +306,177 @@ out_5g:
        }
 }
 
-static struct rtw_regulatory rtw_regd_find_reg_by_name(char *alpha2)
+static bool rtw_reg_is_ww(const struct rtw_regulatory *reg)
+{
+       return reg == &rtw_reg_ww;
+}
+
+static bool rtw_reg_match(const struct rtw_regulatory *reg, const char *alpha2)
+{
+       return memcmp(reg->alpha2, alpha2, 2) == 0;
+}
+
+static const struct rtw_regulatory *rtw_reg_find_by_name(const char *alpha2)
 {
        unsigned int i;
 
        for (i = 0; i < ARRAY_SIZE(rtw_reg_map); i++) {
-               if (!memcmp(rtw_reg_map[i].alpha2, alpha2, 2))
-                       return rtw_reg_map[i];
+               if (rtw_reg_match(&rtw_reg_map[i], alpha2))
+                       return &rtw_reg_map[i];
        }
 
-       return rtw_reg_ww;
+       return &rtw_reg_ww;
 }
 
-static int rtw_regd_notifier_apply(struct rtw_dev *rtwdev,
-                                  struct wiphy *wiphy,
-                                  struct regulatory_request *request)
+static
+void rtw_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request);
+
+/* call this before ieee80211_register_hw() */
+int rtw_regd_init(struct rtw_dev *rtwdev)
 {
-       if (request->initiator == NL80211_REGDOM_SET_BY_USER)
-               return 0;
-       rtwdev->regd = rtw_regd_find_reg_by_name(request->alpha2);
+       struct wiphy *wiphy = rtwdev->hw->wiphy;
+       const struct rtw_regulatory *chip_reg;
 
-       return 0;
-}
+       if (!wiphy)
+               return -EINVAL;
 
-static int
-rtw_regd_init_wiphy(struct rtw_regulatory *reg, struct wiphy *wiphy,
-                   void (*reg_notifier)(struct wiphy *wiphy,
-                                        struct regulatory_request *request))
-{
-       wiphy->reg_notifier = reg_notifier;
+       wiphy->reg_notifier = rtw_regd_notifier;
 
-       wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG;
-       wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG;
-       wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS;
+       chip_reg = rtw_reg_find_by_name(rtwdev->efuse.country_code);
+       if (!rtw_reg_is_ww(chip_reg)) {
+               rtwdev->regd.state = RTW_REGD_STATE_PROGRAMMED;
 
-       rtw_regd_apply_hw_cap_flags(wiphy);
+               /* Set REGULATORY_STRICT_REG before ieee80211_register_hw(),
+                * stack will wait for regulatory_hint() and consider it
+                * as the superset for our regulatory rule.
+                */
+               wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
+               wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
+       } else {
+               rtwdev->regd.state = RTW_REGD_STATE_WORLDWIDE;
+       }
 
+       rtwdev->regd.regulatory = &rtw_reg_ww;
+       rtw_dbg_regd_dump(rtwdev, "regd init state %d: ", rtwdev->regd.state);
+
+       rtw_regd_apply_hw_cap_flags(wiphy);
        return 0;
 }
 
-int rtw_regd_init(struct rtw_dev *rtwdev,
-                 void (*reg_notifier)(struct wiphy *wiphy,
-                                      struct regulatory_request *request))
+/* call this after ieee80211_register_hw() */
+int rtw_regd_hint(struct rtw_dev *rtwdev)
 {
        struct wiphy *wiphy = rtwdev->hw->wiphy;
+       int ret;
 
        if (!wiphy)
                return -EINVAL;
 
-       rtwdev->regd = rtw_regd_find_reg_by_name(rtwdev->efuse.country_code);
-       rtw_regd_init_wiphy(&rtwdev->regd, wiphy, reg_notifier);
+       if (rtwdev->regd.state == RTW_REGD_STATE_PROGRAMMED) {
+               rtw_dbg(rtwdev, RTW_DBG_REGD,
+                       "country domain %c%c is PGed on efuse",
+                       rtwdev->efuse.country_code[0],
+                       rtwdev->efuse.country_code[1]);
+
+               ret = regulatory_hint(wiphy, rtwdev->efuse.country_code);
+               if (ret) {
+                       rtw_warn(rtwdev,
+                                "failed to hint regulatory: %d\n", ret);
+                       return ret;
+               }
+       }
 
        return 0;
 }
 
+static bool rtw_regd_mgmt_worldwide(struct rtw_dev *rtwdev,
+                                   struct rtw_regd *next_regd,
+                                   struct regulatory_request *request)
+{
+       struct wiphy *wiphy = rtwdev->hw->wiphy;
+
+       next_regd->state = RTW_REGD_STATE_WORLDWIDE;
+
+       if (request->initiator == NL80211_REGDOM_SET_BY_USER &&
+           !rtw_reg_is_ww(next_regd->regulatory)) {
+               next_regd->state = RTW_REGD_STATE_SETTING;
+               wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
+       }
+
+       return true;
+}
+
+static bool rtw_regd_mgmt_programmed(struct rtw_dev *rtwdev,
+                                    struct rtw_regd *next_regd,
+                                    struct regulatory_request *request)
+{
+       if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+           rtw_reg_match(next_regd->regulatory, rtwdev->efuse.country_code)) {
+               next_regd->state = RTW_REGD_STATE_PROGRAMMED;
+               return true;
+       }
+
+       return false;
+}
+
+static bool rtw_regd_mgmt_setting(struct rtw_dev *rtwdev,
+                                 struct rtw_regd *next_regd,
+                                 struct regulatory_request *request)
+{
+       struct wiphy *wiphy = rtwdev->hw->wiphy;
+
+       if (request->initiator != NL80211_REGDOM_SET_BY_USER)
+               return false;
+
+       next_regd->state = RTW_REGD_STATE_SETTING;
+
+       if (rtw_reg_is_ww(next_regd->regulatory)) {
+               next_regd->state = RTW_REGD_STATE_WORLDWIDE;
+               wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
+       }
+
+       return true;
+}
+
+static bool (*const rtw_regd_handler[RTW_REGD_STATE_NR])
+       (struct rtw_dev *, struct rtw_regd *, struct regulatory_request *) = {
+       [RTW_REGD_STATE_WORLDWIDE] = rtw_regd_mgmt_worldwide,
+       [RTW_REGD_STATE_PROGRAMMED] = rtw_regd_mgmt_programmed,
+       [RTW_REGD_STATE_SETTING] = rtw_regd_mgmt_setting,
+};
+
+static bool rtw_regd_state_hdl(struct rtw_dev *rtwdev,
+                              struct rtw_regd *next_regd,
+                              struct regulatory_request *request)
+{
+       next_regd->regulatory = rtw_reg_find_by_name(request->alpha2);
+       return rtw_regd_handler[rtwdev->regd.state](rtwdev, next_regd, request);
+}
+
+static
 void rtw_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request)
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct rtw_dev *rtwdev = hw->priv;
        struct rtw_hal *hal = &rtwdev->hal;
+       struct rtw_regd next_regd = {0};
+       bool hdl;
+
+       hdl = rtw_regd_state_hdl(rtwdev, &next_regd, request);
+       if (!hdl) {
+               rtw_dbg(rtwdev, RTW_DBG_REGD,
+                       "regd state %d: ignore request %c%c of initiator %d\n",
+                       rtwdev->regd.state,
+                       request->alpha2[0],
+                       request->alpha2[1],
+                       request->initiator);
+               return;
+       }
+
+       rtw_dbg(rtwdev, RTW_DBG_REGD, "regd state: %d -> %d\n",
+               rtwdev->regd.state, next_regd.state);
 
-       rtw_regd_notifier_apply(rtwdev, wiphy, request);
+       rtwdev->regd = next_regd;
        rtw_dbg_regd_dump(rtwdev, "get alpha2 %c%c from initiator %d: ",
                          request->alpha2[0],
                          request->alpha2[1],
@@ -381,8 +491,8 @@ u8 rtw_regd_get(struct rtw_dev *rtwdev)
        u8 band = hal->current_band_type;
 
        return band == RTW_BAND_2G ?
-              rtwdev->regd.txpwr_regd_2g :
-              rtwdev->regd.txpwr_regd_5g;
+              rtwdev->regd.regulatory->txpwr_regd_2g :
+              rtwdev->regd.regulatory->txpwr_regd_5g;
 }
 EXPORT_SYMBOL(rtw_regd_get);
 
index 18b54ac..34cb13d 100644 (file)
@@ -64,10 +64,8 @@ enum country_code_type {
        COUNTRY_CODE_MAX
 };
 
-int rtw_regd_init(struct rtw_dev *rtwdev,
-                 void (*reg_notifier)(struct wiphy *wiphy,
-                                      struct regulatory_request *request));
-void rtw_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request);
+int rtw_regd_init(struct rtw_dev *rtwdev);
+int rtw_regd_hint(struct rtw_dev *rtwdev);
 u8 rtw_regd_get(struct rtw_dev *rtwdev);
 bool rtw_regd_has_alt(u8 regd, u8 *regd_alt);
 #endif