iwlwifi: mvm: query regdb for wmm rule if needed
authorHaim Dreyfuss <haim.dreyfuss@intel.com>
Wed, 4 Apr 2018 06:23:48 +0000 (09:23 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 9 Apr 2018 15:59:45 +0000 (18:59 +0300)
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>
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c

index 8928613..ca01746 100644 (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);
+
+       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);
+       }
 
-       return regd;
+out:
+       kfree(regdb_ptrs);
+       kfree(regd);
+       return copy_rd;
 }
 IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);
index 306736c..3071a23 100644 (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__ */
index 51b3042..90f8c89 100644 (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);