brcmfmac: use static superset of channels for wiphy bands
authorArend van Spriel <arend@broadcom.com>
Tue, 14 Apr 2015 18:10:24 +0000 (20:10 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Sat, 9 May 2015 13:26:46 +0000 (16:26 +0300)
The driver was constructing a list of channels per wiphy band
by querying the device. This list is not what the hardware is
able to do as it is already filtered by the country setting in
the device. As user-space may change the country this would
require updating the channel list which is not recommended [1].
This patch introduces a superset of channels. The individual
channels are disabled appropriately by querying the device.

[1] http://mid.gmane.org/1426706320.3001.21.camel@sipsolutions.net

Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c

index 8a15ebbce4a360ff054e08c197118ddf43f75381..76dff0e114746173ae2cd06556d0723f383df631 100644 (file)
@@ -129,13 +129,47 @@ static struct ieee80211_rate __wl_rates[] = {
        RATETAB_ENT(BRCM_RATE_54M, 0),
 };
 
-#define wl_a_rates             (__wl_rates + 4)
-#define wl_a_rates_size        8
 #define wl_g_rates             (__wl_rates + 0)
-#define wl_g_rates_size        12
+#define wl_g_rates_size                ARRAY_SIZE(__wl_rates)
+#define wl_a_rates             (__wl_rates + 4)
+#define wl_a_rates_size                (wl_g_rates_size - 4)
+
+#define CHAN2G(_channel, _freq) {                              \
+       .band                   = IEEE80211_BAND_2GHZ,          \
+       .center_freq            = (_freq),                      \
+       .hw_value               = (_channel),                   \
+       .flags                  = IEEE80211_CHAN_DISABLED,      \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
+
+#define CHAN5G(_channel) {                                     \
+       .band                   = IEEE80211_BAND_5GHZ,          \
+       .center_freq            = 5000 + (5 * (_channel)),      \
+       .hw_value               = (_channel),                   \
+       .flags                  = IEEE80211_CHAN_DISABLED,      \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
+
+static struct ieee80211_channel __wl_2ghz_channels[] = {
+       CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
+       CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
+       CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
+       CHAN2G(13, 2472), CHAN2G(14, 2484)
+};
+
+static struct ieee80211_channel __wl_5ghz_channels[] = {
+       CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
+       CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
+       CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
+       CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
+       CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
+       CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
+};
 
 /* Band templates duplicated per wiphy. The channel info
- * is filled in after querying the device.
+ * above is added to the band during setup.
  */
 static const struct ieee80211_supported_band __wl_band_2ghz = {
        .band = IEEE80211_BAND_2GHZ,
@@ -143,7 +177,7 @@ static const struct ieee80211_supported_band __wl_band_2ghz = {
        .n_bitrates = wl_g_rates_size,
 };
 
-static const struct ieee80211_supported_band __wl_band_5ghz_a = {
+static const struct ieee80211_supported_band __wl_band_5ghz = {
        .band = IEEE80211_BAND_5GHZ,
        .bitrates = wl_a_rates,
        .n_bitrates = wl_a_rates_size,
@@ -5253,40 +5287,6 @@ dongle_scantime_out:
        return err;
 }
 
-/* Filter the list of channels received from firmware counting only
- * the 20MHz channels. The wiphy band data only needs those which get
- * flagged to indicate if they can take part in higher bandwidth.
- */
-static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg,
-                                      struct brcmf_chanspec_list *chlist,
-                                      u32 chcnt[])
-{
-       u32 total = le32_to_cpu(chlist->count);
-       struct brcmu_chan ch;
-       int i;
-
-       for (i = 0; i < total; i++) {
-               ch.chspec = (u16)le32_to_cpu(chlist->element[i]);
-               cfg->d11inf.decchspec(&ch);
-
-               /* Firmware gives a ordered list. We skip non-20MHz
-                * channels is 2G. For 5G we can abort upon reaching
-                * a non-20MHz channel in the list.
-                */
-               if (ch.bw != BRCMU_CHAN_BW_20) {
-                       if (ch.band == BRCMU_CHAN_BAND_5G)
-                               break;
-                       else
-                               continue;
-               }
-
-               if (ch.band == BRCMU_CHAN_BAND_2G)
-                       chcnt[0] += 1;
-               else if (ch.band == BRCMU_CHAN_BAND_5G)
-                       chcnt[1] += 1;
-       }
-}
-
 static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
                                           struct brcmu_chan *ch)
 {
@@ -5322,7 +5322,6 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
        u32 i, j;
        u32 total;
        u32 chaninfo;
-       u32 chcnt[2] = { 0, 0 };
        u32 index;
 
        pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
@@ -5339,42 +5338,15 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
                goto fail_pbuf;
        }
 
-       brcmf_count_20mhz_channels(cfg, list, chcnt);
        wiphy = cfg_to_wiphy(cfg);
-       if (chcnt[0]) {
-               band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
-                              GFP_KERNEL);
-               if (band == NULL) {
-                       err = -ENOMEM;
-                       goto fail_pbuf;
-               }
-               band->channels = kcalloc(chcnt[0], sizeof(*channel),
-                                        GFP_KERNEL);
-               if (band->channels == NULL) {
-                       kfree(band);
-                       err = -ENOMEM;
-                       goto fail_pbuf;
-               }
-               band->n_channels = 0;
-               wiphy->bands[IEEE80211_BAND_2GHZ] = band;
-       }
-       if (chcnt[1]) {
-               band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a),
-                              GFP_KERNEL);
-               if (band == NULL) {
-                       err = -ENOMEM;
-                       goto fail_band2g;
-               }
-               band->channels = kcalloc(chcnt[1], sizeof(*channel),
-                                        GFP_KERNEL);
-               if (band->channels == NULL) {
-                       kfree(band);
-                       err = -ENOMEM;
-                       goto fail_band2g;
-               }
-               band->n_channels = 0;
-               wiphy->bands[IEEE80211_BAND_5GHZ] = band;
-       }
+       band = wiphy->bands[IEEE80211_BAND_2GHZ];
+       if (band)
+               for (i = 0; i < band->n_channels; i++)
+                       band->channels[i].flags = IEEE80211_CHAN_DISABLED;
+       band = wiphy->bands[IEEE80211_BAND_5GHZ];
+       if (band)
+               for (i = 0; i < band->n_channels; i++)
+                       band->channels[i].flags = IEEE80211_CHAN_DISABLED;
 
        total = le32_to_cpu(list->count);
        for (i = 0; i < total; i++) {
@@ -5389,6 +5361,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
                        brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
                        continue;
                }
+               if (!band)
+                       continue;
                if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
                    ch.bw == BRCMU_CHAN_BW_40)
                        continue;
@@ -5416,9 +5390,9 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
                } else if (ch.bw == BRCMU_CHAN_BW_40) {
                        brcmf_update_bw40_channel_flag(&channel[index], &ch);
                } else {
-                       /* disable other bandwidths for now as mentioned
-                        * order assure they are enabled for subsequent
-                        * chanspecs.
+                       /* enable the channel and disable other bandwidths
+                        * for now as mentioned order assure they are enabled
+                        * for subsequent chanspecs.
                         */
                        channel[index].flags = IEEE80211_CHAN_NO_HT40 |
                                               IEEE80211_CHAN_NO_80MHZ;
@@ -5437,16 +5411,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
                                                IEEE80211_CHAN_NO_IR;
                        }
                }
-               if (index == band->n_channels)
-                       band->n_channels++;
        }
-       kfree(pbuf);
-       return 0;
 
-fail_band2g:
-       kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
-       kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
-       wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
 fail_pbuf:
        kfree(pbuf);
        return err;
@@ -5779,7 +5745,12 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
 
 static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
 {
+       struct ieee80211_supported_band *band;
        struct ieee80211_iface_combination ifc_combo;
+       __le32 bandlist[3];
+       u32 n_bands;
+       int err, i;
+
        wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
        wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
        wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
@@ -5821,7 +5792,52 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
        if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
                brcmf_wiphy_wowl_params(wiphy);
 
-       return brcmf_setup_wiphybands(wiphy);
+       err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
+                                    sizeof(bandlist));
+       if (err) {
+               brcmf_err("could not obtain band info: err=%d\n", err);
+               return err;
+       }
+       /* first entry in bandlist is number of bands */
+       n_bands = le32_to_cpu(bandlist[0]);
+       for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
+               if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
+                       band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
+                                      GFP_KERNEL);
+                       if (!band)
+                               return -ENOMEM;
+
+                       band->channels = kmemdup(&__wl_2ghz_channels,
+                                                sizeof(__wl_2ghz_channels),
+                                                GFP_KERNEL);
+                       if (!band->channels) {
+                               kfree(band);
+                               return -ENOMEM;
+                       }
+
+                       band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
+                       wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+               }
+               if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
+                       band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
+                                      GFP_KERNEL);
+                       if (!band)
+                               return -ENOMEM;
+
+                       band->channels = kmemdup(&__wl_5ghz_channels,
+                                                sizeof(__wl_5ghz_channels),
+                                                GFP_KERNEL);
+                       if (!band->channels) {
+                               kfree(band);
+                               return -ENOMEM;
+                       }
+
+                       band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
+                       wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+               }
+       }
+       err = brcmf_setup_wiphybands(wiphy);
+       return err;
 }
 
 static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
@@ -6012,6 +6028,9 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
 
 static void brcmf_free_wiphy(struct wiphy *wiphy)
 {
+       if (!wiphy)
+               return;
+
        kfree(wiphy->iface_combinations);
        if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
                kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);