mwifiex: add custom regulatory domain support

This patch creates custom regulatory rules based on the information
received from firmware and enable them during wiphy registration.

Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
Amitkumar Karwar 2016-08-09 20:20:46 +05:30 committed by Kalle Valo
parent 41960b4dfd
commit 7253979910
5 changed files with 127 additions and 14 deletions

View File

@ -4141,9 +4141,12 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
wiphy->cipher_suites = mwifiex_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
if (adapter->region_code)
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
if (adapter->regd) {
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
REGULATORY_DISABLE_BEACON_HINTS |
REGULATORY_COUNTRY_IE_IGNORE;
wiphy_apply_custom_regulatory(wiphy, adapter->regd);
}
ether_addr_copy(wiphy->perm_addr, adapter->perm_addr);
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
@ -4209,19 +4212,27 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
return ret;
}
if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) {
mwifiex_dbg(adapter, INFO,
"driver hint alpha2: %2.2s\n", reg_alpha2);
regulatory_hint(wiphy, reg_alpha2);
} else {
if (adapter->region_code == 0x00) {
mwifiex_dbg(adapter, WARN, "Ignore world regulatory domain\n");
if (!adapter->regd) {
if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) {
mwifiex_dbg(adapter, INFO,
"driver hint alpha2: %2.2s\n", reg_alpha2);
regulatory_hint(wiphy, reg_alpha2);
} else {
country_code =
mwifiex_11d_code_2_region(adapter->region_code);
if (country_code &&
regulatory_hint(wiphy, country_code))
mwifiex_dbg(priv->adapter, ERROR, "regulatory_hint() failed\n");
if (adapter->region_code == 0x00) {
mwifiex_dbg(adapter, WARN,
"Ignore world regulatory domain\n");
} else {
wiphy->regulatory_flags |=
REGULATORY_DISABLE_BEACON_HINTS |
REGULATORY_COUNTRY_IE_IGNORE;
country_code =
mwifiex_11d_code_2_region(
adapter->region_code);
if (country_code &&
regulatory_hint(wiphy, country_code))
mwifiex_dbg(priv->adapter, ERROR,
"regulatory_hint() failed\n");
}
}
}

View File

@ -416,6 +416,14 @@ enum P2P_MODES {
P2P_MODE_CLIENT = 3,
};
enum mwifiex_channel_flags {
MWIFIEX_CHANNEL_PASSIVE = BIT(0),
MWIFIEX_CHANNEL_DFS = BIT(1),
MWIFIEX_CHANNEL_NOHT40 = BIT(2),
MWIFIEX_CHANNEL_NOHT80 = BIT(3),
MWIFIEX_CHANNEL_DISABLED = BIT(7),
};
#define HostCmd_RET_BIT 0x8000
#define HostCmd_ACT_GEN_GET 0x0000
#define HostCmd_ACT_GEN_SET 0x0001

View File

@ -139,6 +139,8 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter)
adapter->nd_info = NULL;
}
kfree(adapter->regd);
vfree(adapter->chan_stats);
kfree(adapter);
return 0;

View File

@ -1005,6 +1005,7 @@ struct mwifiex_adapter {
bool usb_mc_status;
bool usb_mc_setup;
struct cfg80211_wowlan_nd_info *nd_info;
struct ieee80211_regdomain *regd;
};
void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter);

View File

@ -1022,6 +1022,93 @@ static int mwifiex_ret_robust_coex(struct mwifiex_private *priv,
return 0;
}
static struct ieee80211_regdomain *
mwifiex_create_custom_regdomain(struct mwifiex_private *priv,
u8 *buf, u16 buf_len)
{
u16 num_chan = buf_len / 2;
struct ieee80211_regdomain *regd;
struct ieee80211_reg_rule *rule;
bool new_rule;
int regd_size, idx, freq, prev_freq = 0;
u32 bw, prev_bw = 0;
u8 chflags, prev_chflags = 0, valid_rules = 0;
if (WARN_ON_ONCE(num_chan > NL80211_MAX_SUPP_REG_RULES))
return ERR_PTR(-EINVAL);
regd_size = sizeof(struct ieee80211_regdomain) +
num_chan * sizeof(struct ieee80211_reg_rule);
regd = kzalloc(regd_size, GFP_KERNEL);
if (!regd)
return ERR_PTR(-ENOMEM);
for (idx = 0; idx < num_chan; idx++) {
u8 chan;
enum nl80211_band band;
chan = *buf++;
if (!chan)
return NULL;
chflags = *buf++;
band = (chan <= 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
freq = ieee80211_channel_to_frequency(chan, band);
new_rule = false;
if (chflags & MWIFIEX_CHANNEL_DISABLED)
continue;
if (band == NL80211_BAND_5GHZ) {
if (!(chflags & MWIFIEX_CHANNEL_NOHT80))
bw = MHZ_TO_KHZ(80);
else if (!(chflags & MWIFIEX_CHANNEL_NOHT40))
bw = MHZ_TO_KHZ(40);
else
bw = MHZ_TO_KHZ(20);
} else {
if (!(chflags & MWIFIEX_CHANNEL_NOHT40))
bw = MHZ_TO_KHZ(40);
else
bw = MHZ_TO_KHZ(20);
}
if (idx == 0 || prev_chflags != chflags || prev_bw != bw ||
freq - prev_freq > 20) {
valid_rules++;
new_rule = true;
}
rule = &regd->reg_rules[valid_rules - 1];
rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10);
prev_chflags = chflags;
prev_freq = freq;
prev_bw = bw;
if (!new_rule)
continue;
rule->freq_range.start_freq_khz = MHZ_TO_KHZ(freq - 10);
rule->power_rule.max_eirp = DBM_TO_MBM(19);
if (chflags & MWIFIEX_CHANNEL_PASSIVE)
rule->flags = NL80211_RRF_NO_IR;
if (chflags & MWIFIEX_CHANNEL_DFS)
rule->flags = NL80211_RRF_DFS;
rule->freq_range.max_bandwidth_khz = bw;
}
regd->n_reg_rules = valid_rules;
regd->alpha2[0] = '9';
regd->alpha2[1] = '9';
return regd;
}
static int mwifiex_ret_chan_region_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
@ -1050,6 +1137,10 @@ static int mwifiex_ret_chan_region_cfg(struct mwifiex_private *priv,
mwifiex_dbg_dump(priv->adapter, CMD_D, "CHAN:",
(u8 *)head + sizeof(*head),
tlv_buf_len);
priv->adapter->regd =
mwifiex_create_custom_regdomain(priv,
(u8 *)head +
sizeof(*head), tlv_buf_len);
break;
}