diff options
author | Hante Meuleman <meuleman@broadcom.com> | 2013-04-03 12:40:29 +0200 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-04-03 15:07:03 -0400 |
commit | d48200ba45dd2edfe6286abfc783a81a4a492e98 (patch) | |
tree | eaec8920f598cf42dac2500153deed26ccd2a9aa /drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | |
parent | aeecc574a428a116936cadcea06b47cffad16ba1 (diff) | |
download | linux-d48200ba45dd2edfe6286abfc783a81a4a492e98.tar.gz linux-d48200ba45dd2edfe6286abfc783a81a4a492e98.tar.bz2 linux-d48200ba45dd2edfe6286abfc783a81a4a492e98.zip |
brcmfmac: determine the wiphy->bands property correctly.
Use information from the device to determine the bands property
of the wiphy object. After this change the support of 80211n is
correctly presented in the bands property.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Piotr Haber <phaber@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 326 |
1 files changed, 247 insertions, 79 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 804473fc5c5e..c7459ae8254a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -182,64 +182,6 @@ static struct ieee80211_channel __wl_5ghz_a_channels[] = { CHAN5G(216, 0), }; -static struct ieee80211_channel __wl_5ghz_n_channels[] = { - CHAN5G(32, 0), CHAN5G(34, 0), - CHAN5G(36, 0), CHAN5G(38, 0), - CHAN5G(40, 0), CHAN5G(42, 0), - CHAN5G(44, 0), CHAN5G(46, 0), - CHAN5G(48, 0), CHAN5G(50, 0), - CHAN5G(52, 0), CHAN5G(54, 0), - CHAN5G(56, 0), CHAN5G(58, 0), - CHAN5G(60, 0), CHAN5G(62, 0), - CHAN5G(64, 0), CHAN5G(66, 0), - CHAN5G(68, 0), CHAN5G(70, 0), - CHAN5G(72, 0), CHAN5G(74, 0), - CHAN5G(76, 0), CHAN5G(78, 0), - CHAN5G(80, 0), CHAN5G(82, 0), - CHAN5G(84, 0), CHAN5G(86, 0), - CHAN5G(88, 0), CHAN5G(90, 0), - CHAN5G(92, 0), CHAN5G(94, 0), - CHAN5G(96, 0), CHAN5G(98, 0), - CHAN5G(100, 0), CHAN5G(102, 0), - CHAN5G(104, 0), CHAN5G(106, 0), - CHAN5G(108, 0), CHAN5G(110, 0), - CHAN5G(112, 0), CHAN5G(114, 0), - CHAN5G(116, 0), CHAN5G(118, 0), - CHAN5G(120, 0), CHAN5G(122, 0), - CHAN5G(124, 0), CHAN5G(126, 0), - CHAN5G(128, 0), CHAN5G(130, 0), - CHAN5G(132, 0), CHAN5G(134, 0), - CHAN5G(136, 0), CHAN5G(138, 0), - CHAN5G(140, 0), CHAN5G(142, 0), - CHAN5G(144, 0), CHAN5G(145, 0), - CHAN5G(146, 0), CHAN5G(147, 0), - CHAN5G(148, 0), CHAN5G(149, 0), - CHAN5G(150, 0), CHAN5G(151, 0), - CHAN5G(152, 0), CHAN5G(153, 0), - CHAN5G(154, 0), CHAN5G(155, 0), - CHAN5G(156, 0), CHAN5G(157, 0), - CHAN5G(158, 0), CHAN5G(159, 0), - CHAN5G(160, 0), CHAN5G(161, 0), - CHAN5G(162, 0), CHAN5G(163, 0), - CHAN5G(164, 0), CHAN5G(165, 0), - CHAN5G(166, 0), CHAN5G(168, 0), - CHAN5G(170, 0), CHAN5G(172, 0), - CHAN5G(174, 0), CHAN5G(176, 0), - CHAN5G(178, 0), CHAN5G(180, 0), - CHAN5G(182, 0), CHAN5G(184, 0), - CHAN5G(186, 0), CHAN5G(188, 0), - CHAN5G(190, 0), CHAN5G(192, 0), - CHAN5G(194, 0), CHAN5G(196, 0), - CHAN5G(198, 0), CHAN5G(200, 0), - CHAN5G(202, 0), CHAN5G(204, 0), - CHAN5G(206, 0), CHAN5G(208, 0), - CHAN5G(210, 0), CHAN5G(212, 0), - CHAN5G(214, 0), CHAN5G(216, 0), - CHAN5G(218, 0), CHAN5G(220, 0), - CHAN5G(222, 0), CHAN5G(224, 0), - CHAN5G(226, 0), CHAN5G(228, 0), -}; - static struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, .channels = __wl_2ghz_channels, @@ -256,12 +198,28 @@ static struct ieee80211_supported_band __wl_band_5ghz_a = { .n_bitrates = wl_a_rates_size, }; -static struct ieee80211_supported_band __wl_band_5ghz_n = { - .band = IEEE80211_BAND_5GHZ, - .channels = __wl_5ghz_n_channels, - .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels), - .bitrates = wl_a_rates, - .n_bitrates = wl_a_rates_size, +/* This is to override regulatory domains defined in cfg80211 module (reg.c) + * By default world regulatory domain defined in reg.c puts the flags + * NL80211_RRF_PASSIVE_SCAN and NL80211_RRF_NO_IBSS for 5GHz channels (for + * 36..48 and 149..165). With respect to these flags, wpa_supplicant doesn't + * start p2p operations on 5GHz channels. All the changes in world regulatory + * domain are to be done here. + */ +static const struct ieee80211_regdomain brcmf_regdom = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), + /* If any */ + /* IEEE 802.11 channel 14 - Only JP enables + * this and for 802.11b only + */ + REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), + /* IEEE 802.11a, channel 36..64 */ + REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), + /* IEEE 802.11a, channel 100..165 */ + REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } }; static const u32 __wl_cipher_suites[] = { @@ -4188,13 +4146,6 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) wiphy->iface_combinations = brcmf_iface_combos; wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; - wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set - * it as 11a by default. - * This will be updated with - * 11n phy tables in - * "ifconfig up" - * if phy has 11n capability - */ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->cipher_suites = __wl_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); @@ -4204,6 +4155,9 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); + brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); err = wiphy_register(wiphy); if (err < 0) { brcmf_err("Could not register wiphy device (%d)\n", err); @@ -4927,34 +4881,248 @@ dongle_scantime_out: return err; } -static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg) + +static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap) +{ + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct ieee80211_channel *band_chan_arr; + struct brcmf_chanspec_list *list; + s32 err; + u8 *pbuf; + u32 i, j; + u32 total; + u16 chanspec; + enum ieee80211_band band; + u32 channel; + u32 *n_cnt; + bool ht40_allowed; + u32 index; + u32 ht40_flag; + bool update; + u32 array_size; + + pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + + if (pbuf == NULL) + return -ENOMEM; + + list = (struct brcmf_chanspec_list *)pbuf; + + err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, + BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("get chanspecs error (%d)\n", err); + goto exit; + } + + __wl_band_2ghz.n_channels = 0; + __wl_band_5ghz_a.n_channels = 0; + + total = le32_to_cpu(list->count); + for (i = 0; i < total; i++) { + chanspec = (u16)le32_to_cpu(list->element[i]); + channel = CHSPEC_CHANNEL(chanspec); + + if (CHSPEC_IS40(chanspec)) { + if (CHSPEC_SB_UPPER(chanspec)) + channel += CH_10MHZ_APART; + else + channel -= CH_10MHZ_APART; + } else if (CHSPEC_IS80(chanspec)) { + brcmf_dbg(INFO, "HT80 center channel : %d\n", + channel); + continue; + } + if (CHSPEC_IS2G(chanspec) && (channel >= CH_MIN_2G_CHANNEL) && + (channel <= CH_MAX_2G_CHANNEL)) { + band_chan_arr = __wl_2ghz_channels; + array_size = ARRAY_SIZE(__wl_2ghz_channels); + n_cnt = &__wl_band_2ghz.n_channels; + band = IEEE80211_BAND_2GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_40ALL); + } else if (CHSPEC_IS5G(chanspec) && + channel >= CH_MIN_5G_CHANNEL) { + band_chan_arr = __wl_5ghz_a_channels; + array_size = ARRAY_SIZE(__wl_5ghz_a_channels); + n_cnt = &__wl_band_5ghz_a.n_channels; + band = IEEE80211_BAND_5GHZ; + ht40_allowed = !(bw_cap == WLC_N_BW_20ALL); + } else { + brcmf_err("Invalid channel Sepc. 0x%x.\n", chanspec); + continue; + } + if (!ht40_allowed && CHSPEC_IS40(chanspec)) + continue; + update = false; + for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { + if (band_chan_arr[j].hw_value == channel) { + update = true; + break; + } + } + if (update) + index = j; + else + index = *n_cnt; + if (index < array_size) { + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel, band); + band_chan_arr[index].hw_value = channel; + + if (CHSPEC_IS40(chanspec) && ht40_allowed) { + /* assuming the order is HT20, HT40 Upper, + * HT40 lower from chanspecs + */ + ht40_flag = band_chan_arr[index].flags & + IEEE80211_CHAN_NO_HT40; + if (CHSPEC_SB_UPPER(chanspec)) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags &= + ~IEEE80211_CHAN_NO_HT40; + band_chan_arr[index].flags |= + IEEE80211_CHAN_NO_HT40PLUS; + } else { + /* It should be one of + * IEEE80211_CHAN_NO_HT40 or + * IEEE80211_CHAN_NO_HT40PLUS + */ + band_chan_arr[index].flags &= + ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags |= + IEEE80211_CHAN_NO_HT40MINUS; + } + } else { + band_chan_arr[index].flags = + IEEE80211_CHAN_NO_HT40; + if (band == IEEE80211_BAND_2GHZ) + channel |= WL_CHANSPEC_BAND_2G; + else + channel |= WL_CHANSPEC_BAND_5G; + channel |= WL_CHANSPEC_BW_20; + err = brcmf_fil_bsscfg_int_get(ifp, + "per_chan_info", + &channel); + if (!err) { + if (channel & WL_CHAN_RADAR) + band_chan_arr[index].flags |= + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IBSS); + if (channel & WL_CHAN_PASSIVE) + band_chan_arr[index].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + } + } + if (!update) + (*n_cnt)++; + } + } +exit: + kfree(pbuf); + return err; +} + + +static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) { struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct wiphy *wiphy; s32 phy_list; + u32 band_list[3]; + u32 nmode; + u32 bw_cap = 0; s8 phy; - s32 err = 0; + s32 err; + u32 nband; + s32 i; + struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]; + s32 index; err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST, &phy_list, sizeof(phy_list)); if (err) { - brcmf_err("error (%d)\n", err); + brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err); return err; } phy = ((char *)&phy_list)[0]; - brcmf_dbg(INFO, "%c phy\n", phy); - if (phy == 'n' || phy == 'a') { - wiphy = cfg_to_wiphy(cfg); - wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n; + brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy); + + + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, + &band_list, sizeof(band_list)); + if (err) { + brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err); + return err; } + brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n", + band_list[0], band_list[1], band_list[2]); + + err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); + if (err) { + brcmf_err("nmode error (%d)\n", err); + } else { + err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &bw_cap); + if (err) + brcmf_err("mimo_bw_cap error (%d)\n", err); + } + brcmf_dbg(INFO, "nmode=%d, mimo_bw_cap=%d\n", nmode, bw_cap); + + err = brcmf_construct_reginfo(cfg, bw_cap); + if (err) { + brcmf_err("brcmf_construct_reginfo failed (%d)\n", err); + return err; + } + + nband = band_list[0]; + memset(bands, 0, sizeof(bands)); + + for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) { + index = -1; + if ((band_list[i] == WLC_BAND_5G) && + (__wl_band_5ghz_a.n_channels > 0)) { + index = IEEE80211_BAND_5GHZ; + bands[index] = &__wl_band_5ghz_a; + if ((bw_cap == WLC_N_BW_40ALL) || + (bw_cap == WLC_N_BW_20IN2G_40IN5G)) + bands[index]->ht_cap.cap |= + IEEE80211_HT_CAP_SGI_40; + } else if ((band_list[i] == WLC_BAND_2G) && + (__wl_band_2ghz.n_channels > 0)) { + index = IEEE80211_BAND_2GHZ; + bands[index] = &__wl_band_2ghz; + if (bw_cap == WLC_N_BW_40ALL) + bands[index]->ht_cap.cap |= + IEEE80211_HT_CAP_SGI_40; + } + + if ((index >= 0) && nmode) { + bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + bands[index]->ht_cap.ht_supported = true; + bands[index]->ht_cap.ampdu_factor = + IEEE80211_HT_MAX_AMPDU_64K; + bands[index]->ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_16; + /* An HT shall support all EQM rates for one spatial + * stream + */ + bands[index]->ht_cap.mcs.rx_mask[0] = 0xff; + } + } + + wiphy = cfg_to_wiphy(cfg); + wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ]; + wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ]; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); return err; } + static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg) { - return wl_update_wiphybands(cfg); + return brcmf_update_wiphybands(cfg); } static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) |