Commit 8d4fb399 authored by Zong-Zhe Yang's avatar Zong-Zhe Yang Committed by Kalle Valo
Browse files

rtw88: add regulatory strategy by chip type



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: default avatarZong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: default avatarPing-Ke Shih <pkshih@realtek.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20210830072014.12250-3-pkshih@realtek.com
parent f8509c38
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -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);

+14 −1
Original line number Diff line number Diff line
@@ -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;
+145 −35
Original line number Diff line number Diff line
@@ -16,14 +16,14 @@
#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 @@ static void rtw_regd_apply_hw_cap_flags(struct wiphy *wiphy)
	}
}

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);

+2 −4
Original line number Diff line number Diff line
@@ -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