mwifiex: add custom regulatory domain support
authorAmitkumar Karwar <akarwar@marvell.com>
Tue, 9 Aug 2016 14:50:46 +0000 (20:20 +0530)
committerKalle Valo <kvalo@codeaurora.org>
Sat, 3 Sep 2016 10:05:46 +0000 (13:05 +0300)
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>
drivers/net/wireless/marvell/mwifiex/cfg80211.c
drivers/net/wireless/marvell/mwifiex/fw.h
drivers/net/wireless/marvell/mwifiex/main.c
drivers/net/wireless/marvell/mwifiex/main.h
drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c

index 235fb3931e0c3a659a4a686d81dfc66b887548ae..876d420e936ec4cb309d919569fee87ec78b63c2 100644 (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");
+                       }
                }
        }
 
index 085db9921c385ba1b59b4cfe5bf08fa598da8f6d..18aa5256e88bb1f3e9c546627ce9daa4c2552ee6 100644 (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
index db4925db39aa4f07d716040306d6041a037ae0a3..51d4dfcf12143f36126167f203d52c9e62d850ea 100644 (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;
index 59026005e4d7006963a5dab5e077897b78214dad..cd9a4f1525307519570f5cdce6b4ee2845090bec 100644 (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);
index db85330ffb20b67e3b39d17d91cde43a9edcbbff..3344a26bed03f3207fcb1dd8a48730ab48ef2eb9 100644 (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;
                }