iwlwifi: Don't ignore the cap field upon mcc update
authorHaim Dreyfuss <haim.dreyfuss@intel.com>
Wed, 27 Nov 2019 19:55:58 +0000 (14:55 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Feb 2020 14:43:48 +0000 (14:43 +0000)
[ Upstream commit 2763bba6328c53c455d8f7f5302b80030551c31b ]

When receiving a new MCC driver get all the data about the new country
code and its regulatory information.
Mistakenly, we ignored the cap field, which includes global regulatory
information which should be applies to every channel.
Fix it.

Signed-off-by: Haim Dreyfuss <haim.dreyfuss@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.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 b850cca9853c8392fcccf8e272b27f634f2bed01..a6e64787a3454a11d29bdf4e9d63474f0c720dab 100644 (file)
@@ -217,6 +217,34 @@ enum iwl_nvm_channel_flags {
        NVM_CHANNEL_DC_HIGH             = BIT(12),
 };
 
+/**
+ * enum iwl_reg_capa_flags - global flags applied for the whole regulatory
+ * domain.
+ * @REG_CAPA_BF_CCD_LOW_BAND: Beam-forming or Cyclic Delay Diversity in the
+ *     2.4Ghz band is allowed.
+ * @REG_CAPA_BF_CCD_HIGH_BAND: Beam-forming or Cyclic Delay Diversity in the
+ *     5Ghz band is allowed.
+ * @REG_CAPA_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
+ *     for this regulatory domain (valid only in 5Ghz).
+ * @REG_CAPA_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
+ *     for this regulatory domain (valid only in 5Ghz).
+ * @REG_CAPA_MCS_8_ALLOWED: 11ac with MCS 8 is allowed.
+ * @REG_CAPA_MCS_9_ALLOWED: 11ac with MCS 9 is allowed.
+ * @REG_CAPA_40MHZ_FORBIDDEN: 11n channel with a width of 40Mhz is forbidden
+ *     for this regulatory domain (valid only in 5Ghz).
+ * @REG_CAPA_DC_HIGH_ENABLED: DC HIGH allowed.
+ */
+enum iwl_reg_capa_flags {
+       REG_CAPA_BF_CCD_LOW_BAND        = BIT(0),
+       REG_CAPA_BF_CCD_HIGH_BAND       = BIT(1),
+       REG_CAPA_160MHZ_ALLOWED         = BIT(2),
+       REG_CAPA_80MHZ_ALLOWED          = BIT(3),
+       REG_CAPA_MCS_8_ALLOWED          = BIT(4),
+       REG_CAPA_MCS_9_ALLOWED          = BIT(5),
+       REG_CAPA_40MHZ_FORBIDDEN        = BIT(7),
+       REG_CAPA_DC_HIGH_ENABLED        = BIT(9),
+};
+
 static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level,
                                               int chan, u16 flags)
 {
@@ -923,6 +951,7 @@ IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
 
 static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan,
                                       int ch_idx, u16 nvm_flags,
+                                      u16 cap_flags,
                                       const struct iwl_cfg *cfg)
 {
        u32 flags = NL80211_RRF_NO_HT40;
@@ -966,6 +995,20 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan,
            (flags & NL80211_RRF_NO_IR))
                flags |= NL80211_RRF_GO_CONCURRENT;
 
+       /*
+        * cap_flags is per regulatory domain so apply it for every channel
+        */
+       if (ch_idx >= NUM_2GHZ_CHANNELS) {
+               if (cap_flags & REG_CAPA_40MHZ_FORBIDDEN)
+                       flags |= NL80211_RRF_NO_HT40;
+
+               if (!(cap_flags & REG_CAPA_80MHZ_ALLOWED))
+                       flags |= NL80211_RRF_NO_80MHZ;
+
+               if (!(cap_flags & REG_CAPA_160MHZ_ALLOWED))
+                       flags |= NL80211_RRF_NO_160MHZ;
+       }
+
        return flags;
 }
 
@@ -977,7 +1020,7 @@ struct regdb_ptrs {
 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,
-                      u16 geo_info)
+                      u16 geo_info, u16 cap)
 {
        int ch_idx;
        u16 ch_flags;
@@ -1038,7 +1081,8 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
                }
 
                reg_rule_flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx,
-                                                            ch_flags, cfg);
+                                                            ch_flags, cap,
+                                                            cfg);
 
                /* we can't continue the same rule */
                if (ch_idx == 0 || prev_reg_rule_flags != reg_rule_flags ||
index 234d1009a9de40a81a660cfeb8bf77d5753ab0fd..a9bdd4aa01c7ea01a6de33162e74e0d5b11810b4 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Copyright(c) 2008 - 2015 Intel Corporation. All rights reserved.
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright(c) 2018        Intel Corporation
+ * Copyright(c) 2018 - 2019 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -34,7 +34,7 @@
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright(c) 2018        Intel Corporation
+ * Copyright(c) 2018 - 2019 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -108,7 +108,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 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,
-                      u16 geo_info);
+                      u16 geo_info, u16 cap);
 
 /**
  * struct iwl_nvm_section - describes an NVM section in memory.
index 476c44db0e64b1c5a0325a1e24bbd4aed6d42be0..58653598db1461e063227c2548bef44437d55245 100644 (file)
@@ -317,7 +317,8 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
                                      __le32_to_cpu(resp->n_channels),
                                      resp->channels,
                                      __le16_to_cpu(resp->mcc),
-                                     __le16_to_cpu(resp->geo_info));
+                                     __le16_to_cpu(resp->geo_info),
+                                     __le16_to_cpu(resp->cap));
        /* Store the return source id */
        src_id = resp->source_id;
        kfree(resp);