iwlwifi: mvm: query regdb for wmm rule if needed

Since our device is regulatory self managed it maintains its regulatory
rules by its own. However the wmm_rules values can't be set by the
device itself but only the indication about the need to set it.
In case the device set wmm indication, proactively query the regulatory
data base to get these values

Signed-off-by: Haim Dreyfuss <haim.dreyfuss@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
Haim Dreyfuss 2018-04-04 09:23:48 +03:00 committed by Kalle Valo
parent 0b5c0305e5
commit 77e30e10ee
3 changed files with 101 additions and 19 deletions

View File

@ -76,6 +76,7 @@
#include "iwl-io.h"
#include "iwl-csr.h"
#include "fw/acpi.h"
#include "fw/api/nvm-reg.h"
/* NVM offsets (in words) definitions */
enum nvm_offsets {
@ -146,8 +147,8 @@ static const u8 iwl_ext_nvm_channels[] = {
149, 153, 157, 161, 165, 169, 173, 177, 181
};
#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels)
#define IWL_NUM_CHANNELS_EXT ARRAY_SIZE(iwl_ext_nvm_channels)
#define IWL_NVM_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels)
#define IWL_NVM_NUM_CHANNELS_EXT ARRAY_SIZE(iwl_ext_nvm_channels)
#define NUM_2GHZ_CHANNELS 14
#define NUM_2GHZ_CHANNELS_EXT 14
#define FIRST_2GHZ_HT_MINUS 5
@ -301,11 +302,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
const u8 *nvm_chan;
if (cfg->nvm_type != IWL_NVM_EXT) {
num_of_ch = IWL_NUM_CHANNELS;
num_of_ch = IWL_NVM_NUM_CHANNELS;
nvm_chan = &iwl_nvm_channels[0];
num_2ghz_channels = NUM_2GHZ_CHANNELS;
} else {
num_of_ch = IWL_NUM_CHANNELS_EXT;
num_of_ch = IWL_NVM_NUM_CHANNELS_EXT;
nvm_chan = &iwl_ext_nvm_channels[0];
num_2ghz_channels = NUM_2GHZ_CHANNELS_EXT;
}
@ -720,12 +721,12 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
if (cfg->nvm_type != IWL_NVM_EXT)
data = kzalloc(sizeof(*data) +
sizeof(struct ieee80211_channel) *
IWL_NUM_CHANNELS,
IWL_NVM_NUM_CHANNELS,
GFP_KERNEL);
else
data = kzalloc(sizeof(*data) +
sizeof(struct ieee80211_channel) *
IWL_NUM_CHANNELS_EXT,
IWL_NVM_NUM_CHANNELS_EXT,
GFP_KERNEL);
if (!data)
return NULL;
@ -842,24 +843,34 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan,
return flags;
}
struct regdb_ptrs {
struct ieee80211_wmm_rule *rule;
u32 token;
};
struct ieee80211_regdomain *
iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
int num_of_ch, __le32 *channels, u16 fw_mcc)
int num_of_ch, __le32 *channels, u16 fw_mcc,
u16 geo_info)
{
int ch_idx;
u16 ch_flags;
u32 reg_rule_flags, prev_reg_rule_flags = 0;
const u8 *nvm_chan = cfg->nvm_type == IWL_NVM_EXT ?
iwl_ext_nvm_channels : iwl_nvm_channels;
struct ieee80211_regdomain *regd;
int size_of_regd;
struct ieee80211_regdomain *regd, *copy_rd;
int size_of_regd, regd_to_copy, wmms_to_copy;
int size_of_wmms = 0;
struct ieee80211_reg_rule *rule;
struct ieee80211_wmm_rule *wmm_rule, *d_wmm, *s_wmm;
struct regdb_ptrs *regdb_ptrs;
enum nl80211_band band;
int center_freq, prev_center_freq = 0;
int valid_rules = 0;
int valid_rules = 0, n_wmms = 0;
int i;
bool new_rule;
int max_num_ch = cfg->nvm_type == IWL_NVM_EXT ?
IWL_NUM_CHANNELS_EXT : IWL_NUM_CHANNELS;
IWL_NVM_NUM_CHANNELS_EXT : IWL_NVM_NUM_CHANNELS;
if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES))
return ERR_PTR(-EINVAL);
@ -875,10 +886,26 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
sizeof(struct ieee80211_regdomain) +
num_of_ch * sizeof(struct ieee80211_reg_rule);
regd = kzalloc(size_of_regd, GFP_KERNEL);
if (geo_info & GEO_WMM_ETSI_5GHZ_INFO)
size_of_wmms =
num_of_ch * sizeof(struct ieee80211_wmm_rule);
regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL);
if (!regd)
return ERR_PTR(-ENOMEM);
regdb_ptrs = kcalloc(num_of_ch, sizeof(*regdb_ptrs), GFP_KERNEL);
if (!regdb_ptrs) {
copy_rd = ERR_PTR(-ENOMEM);
goto out;
}
/* set alpha2 from FW. */
regd->alpha2[0] = fw_mcc >> 8;
regd->alpha2[1] = fw_mcc & 0xff;
wmm_rule = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd);
for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
ch_flags = (u16)__le32_to_cpup(channels + ch_idx);
band = (ch_idx < NUM_2GHZ_CHANNELS) ?
@ -927,14 +954,66 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
iwl_nvm_print_channel_flags(dev, IWL_DL_LAR,
nvm_chan[ch_idx], ch_flags);
if (!(geo_info & GEO_WMM_ETSI_5GHZ_INFO) ||
band == NL80211_BAND_2GHZ)
continue;
if (!reg_query_regdb_wmm(regd->alpha2, center_freq,
&regdb_ptrs[n_wmms].token, wmm_rule)) {
/* Add only new rules */
for (i = 0; i < n_wmms; i++) {
if (regdb_ptrs[i].token ==
regdb_ptrs[n_wmms].token) {
rule->wmm_rule = regdb_ptrs[i].rule;
break;
}
}
if (i == n_wmms) {
rule->wmm_rule = wmm_rule;
regdb_ptrs[n_wmms++].rule = wmm_rule;
wmm_rule++;
}
}
}
regd->n_reg_rules = valid_rules;
regd->n_wmm_rules = n_wmms;
/* set alpha2 from FW. */
regd->alpha2[0] = fw_mcc >> 8;
regd->alpha2[1] = fw_mcc & 0xff;
/*
* Narrow down regdom for unused regulatory rules to prevent hole
* between reg rules to wmm rules.
*/
regd_to_copy = sizeof(struct ieee80211_regdomain) +
valid_rules * sizeof(struct ieee80211_reg_rule);
return regd;
wmms_to_copy = sizeof(struct ieee80211_wmm_rule) * n_wmms;
copy_rd = kzalloc(regd_to_copy + wmms_to_copy, GFP_KERNEL);
if (!copy_rd) {
copy_rd = ERR_PTR(-ENOMEM);
goto out;
}
memcpy(copy_rd, regd, regd_to_copy);
memcpy((u8 *)copy_rd + regd_to_copy, (u8 *)regd + size_of_regd,
wmms_to_copy);
d_wmm = (struct ieee80211_wmm_rule *)((u8 *)copy_rd + regd_to_copy);
s_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd);
for (i = 0; i < regd->n_reg_rules; i++) {
if (!regd->reg_rules[i].wmm_rule)
continue;
copy_rd->reg_rules[i].wmm_rule = d_wmm +
(regd->reg_rules[i].wmm_rule - s_wmm) /
sizeof(struct ieee80211_wmm_rule);
}
out:
kfree(regdb_ptrs);
kfree(regd);
return copy_rd;
}
IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);

View File

@ -101,12 +101,14 @@ void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
*
* This function parses the regulatory channel data received as a
* MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain,
* to be fed into the regulatory core. An ERR_PTR is returned on error.
* to be fed into the regulatory core. In case the geo_info is set handle
* accordingly. An ERR_PTR is returned on error.
* If not given to the regulatory core, the user is responsible for freeing
* the regdomain returned here with kfree.
*/
struct ieee80211_regdomain *
iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
int num_of_ch, __le32 *channels, u16 fw_mcc);
int num_of_ch, __le32 *channels, u16 fw_mcc,
u16 geo_info);
#endif /* __iwl_nvm_parse_h__ */

View File

@ -311,7 +311,8 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
__le32_to_cpu(resp->n_channels),
resp->channels,
__le16_to_cpu(resp->mcc));
__le16_to_cpu(resp->mcc),
__le16_to_cpu(resp->geo_info));
/* Store the return source id */
src_id = resp->source_id;
kfree(resp);