105491d2cSKalle Valo /*
205491d2cSKalle Valo * Copyright (c) 2010 Broadcom Corporation
305491d2cSKalle Valo * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
405491d2cSKalle Valo *
505491d2cSKalle Valo * Permission to use, copy, modify, and/or distribute this software for any
605491d2cSKalle Valo * purpose with or without fee is hereby granted, provided that the above
705491d2cSKalle Valo * copyright notice and this permission notice appear in all copies.
805491d2cSKalle Valo *
905491d2cSKalle Valo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1005491d2cSKalle Valo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1105491d2cSKalle Valo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1205491d2cSKalle Valo * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1305491d2cSKalle Valo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
1405491d2cSKalle Valo * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1505491d2cSKalle Valo * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1605491d2cSKalle Valo */
1705491d2cSKalle Valo
1805491d2cSKalle Valo #define __UNDEF_NO_VERSION__
1905491d2cSKalle Valo #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005491d2cSKalle Valo
2105491d2cSKalle Valo #include <linux/etherdevice.h>
2205491d2cSKalle Valo #include <linux/sched.h>
2305491d2cSKalle Valo #include <linux/firmware.h>
2405491d2cSKalle Valo #include <linux/interrupt.h>
2505491d2cSKalle Valo #include <linux/module.h>
2605491d2cSKalle Valo #include <linux/bcma/bcma.h>
2705491d2cSKalle Valo #include <net/mac80211.h>
2805491d2cSKalle Valo #include <defs.h>
2905491d2cSKalle Valo #include "phy/phy_int.h"
3005491d2cSKalle Valo #include "d11.h"
3105491d2cSKalle Valo #include "channel.h"
3205491d2cSKalle Valo #include "scb.h"
3305491d2cSKalle Valo #include "pub.h"
3405491d2cSKalle Valo #include "ucode_loader.h"
3505491d2cSKalle Valo #include "mac80211_if.h"
3605491d2cSKalle Valo #include "main.h"
3705491d2cSKalle Valo #include "debug.h"
3805491d2cSKalle Valo #include "led.h"
3905491d2cSKalle Valo
4005491d2cSKalle Valo #define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */
4105491d2cSKalle Valo #define BRCMS_FLUSH_TIMEOUT 500 /* msec */
4205491d2cSKalle Valo
4305491d2cSKalle Valo /* Flags we support */
4405491d2cSKalle Valo #define MAC_FILTERS (FIF_ALLMULTI | \
4505491d2cSKalle Valo FIF_FCSFAIL | \
4605491d2cSKalle Valo FIF_CONTROL | \
4705491d2cSKalle Valo FIF_OTHER_BSS | \
4805491d2cSKalle Valo FIF_BCN_PRBRESP_PROMISC | \
4905491d2cSKalle Valo FIF_PSPOLL)
5005491d2cSKalle Valo
5105491d2cSKalle Valo #define CHAN2GHZ(channel, freqency, chflags) { \
5257fbcce3SJohannes Berg .band = NL80211_BAND_2GHZ, \
5305491d2cSKalle Valo .center_freq = (freqency), \
5405491d2cSKalle Valo .hw_value = (channel), \
5505491d2cSKalle Valo .flags = chflags, \
5605491d2cSKalle Valo .max_antenna_gain = 0, \
5705491d2cSKalle Valo .max_power = 19, \
5805491d2cSKalle Valo }
5905491d2cSKalle Valo
6005491d2cSKalle Valo #define CHAN5GHZ(channel, chflags) { \
6157fbcce3SJohannes Berg .band = NL80211_BAND_5GHZ, \
6205491d2cSKalle Valo .center_freq = 5000 + 5*(channel), \
6305491d2cSKalle Valo .hw_value = (channel), \
6405491d2cSKalle Valo .flags = chflags, \
6505491d2cSKalle Valo .max_antenna_gain = 0, \
6605491d2cSKalle Valo .max_power = 21, \
6705491d2cSKalle Valo }
6805491d2cSKalle Valo
6905491d2cSKalle Valo #define RATE(rate100m, _flags) { \
7005491d2cSKalle Valo .bitrate = (rate100m), \
7105491d2cSKalle Valo .flags = (_flags), \
7205491d2cSKalle Valo .hw_value = (rate100m / 5), \
7305491d2cSKalle Valo }
7405491d2cSKalle Valo
7505491d2cSKalle Valo struct firmware_hdr {
7605491d2cSKalle Valo __le32 offset;
7705491d2cSKalle Valo __le32 len;
7805491d2cSKalle Valo __le32 idx;
7905491d2cSKalle Valo };
8005491d2cSKalle Valo
8105491d2cSKalle Valo static const char * const brcms_firmwares[MAX_FW_IMAGES] = {
8205491d2cSKalle Valo "brcm/bcm43xx",
8305491d2cSKalle Valo NULL
8405491d2cSKalle Valo };
8505491d2cSKalle Valo
8605491d2cSKalle Valo static int n_adapters_found;
8705491d2cSKalle Valo
8805491d2cSKalle Valo MODULE_AUTHOR("Broadcom Corporation");
8905491d2cSKalle Valo MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver.");
9005491d2cSKalle Valo MODULE_LICENSE("Dual BSD/GPL");
9105491d2cSKalle Valo /* This needs to be adjusted when brcms_firmwares changes */
9205491d2cSKalle Valo MODULE_FIRMWARE("brcm/bcm43xx-0.fw");
9305491d2cSKalle Valo MODULE_FIRMWARE("brcm/bcm43xx_hdr-0.fw");
9405491d2cSKalle Valo
9505491d2cSKalle Valo /* recognized BCMA Core IDs */
9605491d2cSKalle Valo static struct bcma_device_id brcms_coreid_table[] = {
9705491d2cSKalle Valo BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 17, BCMA_ANY_CLASS),
9805491d2cSKalle Valo BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 23, BCMA_ANY_CLASS),
9905491d2cSKalle Valo BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 24, BCMA_ANY_CLASS),
10005491d2cSKalle Valo {},
10105491d2cSKalle Valo };
10205491d2cSKalle Valo MODULE_DEVICE_TABLE(bcma, brcms_coreid_table);
10305491d2cSKalle Valo
10405491d2cSKalle Valo #if defined(CONFIG_BRCMDBG)
10505491d2cSKalle Valo /*
10605491d2cSKalle Valo * Module parameter for setting the debug message level. Available
10705491d2cSKalle Valo * flags are specified by the BRCM_DL_* macros in
10805491d2cSKalle Valo * drivers/net/wireless/brcm80211/include/defs.h.
10905491d2cSKalle Valo */
1102ef00c53SJoe Perches module_param_named(debug, brcm_msg_level, uint, 0644);
11105491d2cSKalle Valo #endif
11205491d2cSKalle Valo
11305491d2cSKalle Valo static struct ieee80211_channel brcms_2ghz_chantable[] = {
11405491d2cSKalle Valo CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS),
11505491d2cSKalle Valo CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS),
11605491d2cSKalle Valo CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS),
11705491d2cSKalle Valo CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS),
11805491d2cSKalle Valo CHAN2GHZ(5, 2432, 0),
11905491d2cSKalle Valo CHAN2GHZ(6, 2437, 0),
12005491d2cSKalle Valo CHAN2GHZ(7, 2442, 0),
12105491d2cSKalle Valo CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS),
12205491d2cSKalle Valo CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS),
12305491d2cSKalle Valo CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS),
12405491d2cSKalle Valo CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS),
12505491d2cSKalle Valo CHAN2GHZ(12, 2467,
12605491d2cSKalle Valo IEEE80211_CHAN_NO_IR |
12705491d2cSKalle Valo IEEE80211_CHAN_NO_HT40PLUS),
12805491d2cSKalle Valo CHAN2GHZ(13, 2472,
12905491d2cSKalle Valo IEEE80211_CHAN_NO_IR |
13005491d2cSKalle Valo IEEE80211_CHAN_NO_HT40PLUS),
13105491d2cSKalle Valo CHAN2GHZ(14, 2484,
13205491d2cSKalle Valo IEEE80211_CHAN_NO_IR |
13305491d2cSKalle Valo IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS |
13405491d2cSKalle Valo IEEE80211_CHAN_NO_OFDM)
13505491d2cSKalle Valo };
13605491d2cSKalle Valo
13705491d2cSKalle Valo static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
13805491d2cSKalle Valo /* UNII-1 */
13905491d2cSKalle Valo CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS),
14005491d2cSKalle Valo CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS),
14105491d2cSKalle Valo CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS),
14205491d2cSKalle Valo CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS),
14305491d2cSKalle Valo /* UNII-2 */
14405491d2cSKalle Valo CHAN5GHZ(52,
14505491d2cSKalle Valo IEEE80211_CHAN_RADAR |
14605491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
14705491d2cSKalle Valo CHAN5GHZ(56,
14805491d2cSKalle Valo IEEE80211_CHAN_RADAR |
14905491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
15005491d2cSKalle Valo CHAN5GHZ(60,
15105491d2cSKalle Valo IEEE80211_CHAN_RADAR |
15205491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
15305491d2cSKalle Valo CHAN5GHZ(64,
15405491d2cSKalle Valo IEEE80211_CHAN_RADAR |
15505491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
15605491d2cSKalle Valo /* MID */
15705491d2cSKalle Valo CHAN5GHZ(100,
15805491d2cSKalle Valo IEEE80211_CHAN_RADAR |
15905491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
16005491d2cSKalle Valo CHAN5GHZ(104,
16105491d2cSKalle Valo IEEE80211_CHAN_RADAR |
16205491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
16305491d2cSKalle Valo CHAN5GHZ(108,
16405491d2cSKalle Valo IEEE80211_CHAN_RADAR |
16505491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
16605491d2cSKalle Valo CHAN5GHZ(112,
16705491d2cSKalle Valo IEEE80211_CHAN_RADAR |
16805491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
16905491d2cSKalle Valo CHAN5GHZ(116,
17005491d2cSKalle Valo IEEE80211_CHAN_RADAR |
17105491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
17205491d2cSKalle Valo CHAN5GHZ(120,
17305491d2cSKalle Valo IEEE80211_CHAN_RADAR |
17405491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
17505491d2cSKalle Valo CHAN5GHZ(124,
17605491d2cSKalle Valo IEEE80211_CHAN_RADAR |
17705491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
17805491d2cSKalle Valo CHAN5GHZ(128,
17905491d2cSKalle Valo IEEE80211_CHAN_RADAR |
18005491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
18105491d2cSKalle Valo CHAN5GHZ(132,
18205491d2cSKalle Valo IEEE80211_CHAN_RADAR |
18305491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
18405491d2cSKalle Valo CHAN5GHZ(136,
18505491d2cSKalle Valo IEEE80211_CHAN_RADAR |
18605491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
18705491d2cSKalle Valo CHAN5GHZ(140,
18805491d2cSKalle Valo IEEE80211_CHAN_RADAR |
18905491d2cSKalle Valo IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS |
19005491d2cSKalle Valo IEEE80211_CHAN_NO_HT40MINUS),
19105491d2cSKalle Valo /* UNII-3 */
19205491d2cSKalle Valo CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS),
19305491d2cSKalle Valo CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS),
19405491d2cSKalle Valo CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS),
19505491d2cSKalle Valo CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS),
19605491d2cSKalle Valo CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
19705491d2cSKalle Valo };
19805491d2cSKalle Valo
19905491d2cSKalle Valo /*
20005491d2cSKalle Valo * The rate table is used for both 2.4G and 5G rates. The
20105491d2cSKalle Valo * latter being a subset as it does not support CCK rates.
20205491d2cSKalle Valo */
20305491d2cSKalle Valo static struct ieee80211_rate legacy_ratetable[] = {
20405491d2cSKalle Valo RATE(10, 0),
20505491d2cSKalle Valo RATE(20, IEEE80211_RATE_SHORT_PREAMBLE),
20605491d2cSKalle Valo RATE(55, IEEE80211_RATE_SHORT_PREAMBLE),
20705491d2cSKalle Valo RATE(110, IEEE80211_RATE_SHORT_PREAMBLE),
20805491d2cSKalle Valo RATE(60, 0),
20905491d2cSKalle Valo RATE(90, 0),
21005491d2cSKalle Valo RATE(120, 0),
21105491d2cSKalle Valo RATE(180, 0),
21205491d2cSKalle Valo RATE(240, 0),
21305491d2cSKalle Valo RATE(360, 0),
21405491d2cSKalle Valo RATE(480, 0),
21505491d2cSKalle Valo RATE(540, 0),
21605491d2cSKalle Valo };
21705491d2cSKalle Valo
21805491d2cSKalle Valo static const struct ieee80211_supported_band brcms_band_2GHz_nphy_template = {
21957fbcce3SJohannes Berg .band = NL80211_BAND_2GHZ,
22005491d2cSKalle Valo .channels = brcms_2ghz_chantable,
22105491d2cSKalle Valo .n_channels = ARRAY_SIZE(brcms_2ghz_chantable),
22205491d2cSKalle Valo .bitrates = legacy_ratetable,
22305491d2cSKalle Valo .n_bitrates = ARRAY_SIZE(legacy_ratetable),
22405491d2cSKalle Valo .ht_cap = {
22505491d2cSKalle Valo /* from include/linux/ieee80211.h */
22605491d2cSKalle Valo .cap = IEEE80211_HT_CAP_GRN_FLD |
22705491d2cSKalle Valo IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40,
22805491d2cSKalle Valo .ht_supported = true,
22905491d2cSKalle Valo .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
23005491d2cSKalle Valo .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
23105491d2cSKalle Valo .mcs = {
23205491d2cSKalle Valo /* placeholders for now */
23305491d2cSKalle Valo .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
23405491d2cSKalle Valo .rx_highest = cpu_to_le16(500),
23505491d2cSKalle Valo .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
23605491d2cSKalle Valo }
23705491d2cSKalle Valo };
23805491d2cSKalle Valo
23905491d2cSKalle Valo static const struct ieee80211_supported_band brcms_band_5GHz_nphy_template = {
24057fbcce3SJohannes Berg .band = NL80211_BAND_5GHZ,
24105491d2cSKalle Valo .channels = brcms_5ghz_nphy_chantable,
24205491d2cSKalle Valo .n_channels = ARRAY_SIZE(brcms_5ghz_nphy_chantable),
24305491d2cSKalle Valo .bitrates = legacy_ratetable + BRCMS_LEGACY_5G_RATE_OFFSET,
24405491d2cSKalle Valo .n_bitrates = ARRAY_SIZE(legacy_ratetable) -
24505491d2cSKalle Valo BRCMS_LEGACY_5G_RATE_OFFSET,
24605491d2cSKalle Valo .ht_cap = {
24705491d2cSKalle Valo .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 |
24805491d2cSKalle Valo IEEE80211_HT_CAP_SGI_40,
24905491d2cSKalle Valo .ht_supported = true,
25005491d2cSKalle Valo .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
25105491d2cSKalle Valo .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
25205491d2cSKalle Valo .mcs = {
25305491d2cSKalle Valo /* placeholders for now */
25405491d2cSKalle Valo .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
25505491d2cSKalle Valo .rx_highest = cpu_to_le16(500),
25605491d2cSKalle Valo .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
25705491d2cSKalle Valo }
25805491d2cSKalle Valo };
25905491d2cSKalle Valo
26005491d2cSKalle Valo /* flags the given rate in rateset as requested */
brcms_set_basic_rate(struct brcm_rateset * rs,u16 rate,bool is_br)26105491d2cSKalle Valo static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br)
26205491d2cSKalle Valo {
26305491d2cSKalle Valo u32 i;
26405491d2cSKalle Valo
26505491d2cSKalle Valo for (i = 0; i < rs->count; i++) {
26605491d2cSKalle Valo if (rate != (rs->rates[i] & 0x7f))
26705491d2cSKalle Valo continue;
26805491d2cSKalle Valo
26905491d2cSKalle Valo if (is_br)
27005491d2cSKalle Valo rs->rates[i] |= BRCMS_RATE_FLAG;
27105491d2cSKalle Valo else
27205491d2cSKalle Valo rs->rates[i] &= BRCMS_RATE_MASK;
27305491d2cSKalle Valo return;
27405491d2cSKalle Valo }
27505491d2cSKalle Valo }
27605491d2cSKalle Valo
2777eae8c73SLee Jones /*
27805491d2cSKalle Valo * This function frees the WL per-device resources.
27905491d2cSKalle Valo *
28005491d2cSKalle Valo * This function frees resources owned by the WL device pointed to
28105491d2cSKalle Valo * by the wl parameter.
28205491d2cSKalle Valo *
28305491d2cSKalle Valo * precondition: can both be called locked and unlocked
28405491d2cSKalle Valo */
brcms_free(struct brcms_info * wl)28505491d2cSKalle Valo static void brcms_free(struct brcms_info *wl)
28605491d2cSKalle Valo {
28705491d2cSKalle Valo struct brcms_timer *t, *next;
28805491d2cSKalle Valo
28905491d2cSKalle Valo /* free ucode data */
29005491d2cSKalle Valo if (wl->fw.fw_cnt)
29105491d2cSKalle Valo brcms_ucode_data_free(&wl->ucode);
29205491d2cSKalle Valo if (wl->irq)
29305491d2cSKalle Valo free_irq(wl->irq, wl);
29405491d2cSKalle Valo
29505491d2cSKalle Valo /* kill dpc */
29605491d2cSKalle Valo tasklet_kill(&wl->tasklet);
29705491d2cSKalle Valo
29805491d2cSKalle Valo if (wl->pub) {
29905491d2cSKalle Valo brcms_debugfs_detach(wl->pub);
30005491d2cSKalle Valo brcms_c_module_unregister(wl->pub, "linux", wl);
30105491d2cSKalle Valo }
30205491d2cSKalle Valo
30305491d2cSKalle Valo /* free common resources */
30405491d2cSKalle Valo if (wl->wlc) {
30505491d2cSKalle Valo brcms_c_detach(wl->wlc);
30605491d2cSKalle Valo wl->wlc = NULL;
30705491d2cSKalle Valo wl->pub = NULL;
30805491d2cSKalle Valo }
30905491d2cSKalle Valo
31005491d2cSKalle Valo /* virtual interface deletion is deferred so we cannot spinwait */
31105491d2cSKalle Valo
31205491d2cSKalle Valo /* wait for all pending callbacks to complete */
31305491d2cSKalle Valo while (atomic_read(&wl->callbacks) > 0)
31405491d2cSKalle Valo schedule();
31505491d2cSKalle Valo
31605491d2cSKalle Valo /* free timers */
31705491d2cSKalle Valo for (t = wl->timers; t; t = next) {
31805491d2cSKalle Valo next = t->next;
31905491d2cSKalle Valo #ifdef DEBUG
32005491d2cSKalle Valo kfree(t->name);
32105491d2cSKalle Valo #endif
32205491d2cSKalle Valo kfree(t);
32305491d2cSKalle Valo }
32405491d2cSKalle Valo }
32505491d2cSKalle Valo
32605491d2cSKalle Valo /*
32705491d2cSKalle Valo * called from both kernel as from this kernel module (error flow on attach)
32805491d2cSKalle Valo * precondition: perimeter lock is not acquired.
32905491d2cSKalle Valo */
brcms_remove(struct bcma_device * pdev)33005491d2cSKalle Valo static void brcms_remove(struct bcma_device *pdev)
33105491d2cSKalle Valo {
33205491d2cSKalle Valo struct ieee80211_hw *hw = bcma_get_drvdata(pdev);
33305491d2cSKalle Valo struct brcms_info *wl = hw->priv;
33405491d2cSKalle Valo
33505491d2cSKalle Valo if (wl->wlc) {
33605491d2cSKalle Valo brcms_led_unregister(wl);
33705491d2cSKalle Valo wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
33805491d2cSKalle Valo wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
33905491d2cSKalle Valo ieee80211_unregister_hw(hw);
34005491d2cSKalle Valo }
34105491d2cSKalle Valo
34205491d2cSKalle Valo brcms_free(wl);
34305491d2cSKalle Valo
34405491d2cSKalle Valo bcma_set_drvdata(pdev, NULL);
34505491d2cSKalle Valo ieee80211_free_hw(hw);
34605491d2cSKalle Valo }
34705491d2cSKalle Valo
34805491d2cSKalle Valo /*
34905491d2cSKalle Valo * Precondition: Since this function is called in brcms_pci_probe() context,
35005491d2cSKalle Valo * no locking is required.
35105491d2cSKalle Valo */
brcms_release_fw(struct brcms_info * wl)35205491d2cSKalle Valo static void brcms_release_fw(struct brcms_info *wl)
35305491d2cSKalle Valo {
35405491d2cSKalle Valo int i;
35505491d2cSKalle Valo for (i = 0; i < MAX_FW_IMAGES; i++) {
35605491d2cSKalle Valo release_firmware(wl->fw.fw_bin[i]);
35705491d2cSKalle Valo release_firmware(wl->fw.fw_hdr[i]);
35805491d2cSKalle Valo }
35905491d2cSKalle Valo }
36005491d2cSKalle Valo
36105491d2cSKalle Valo /*
36205491d2cSKalle Valo * Precondition: Since this function is called in brcms_pci_probe() context,
36305491d2cSKalle Valo * no locking is required.
36405491d2cSKalle Valo */
brcms_request_fw(struct brcms_info * wl,struct bcma_device * pdev)36505491d2cSKalle Valo static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev)
36605491d2cSKalle Valo {
36705491d2cSKalle Valo int status;
36805491d2cSKalle Valo struct device *device = &pdev->dev;
36905491d2cSKalle Valo char fw_name[100];
37005491d2cSKalle Valo int i;
37105491d2cSKalle Valo
37205491d2cSKalle Valo memset(&wl->fw, 0, sizeof(struct brcms_firmware));
37305491d2cSKalle Valo for (i = 0; i < MAX_FW_IMAGES; i++) {
37405491d2cSKalle Valo if (brcms_firmwares[i] == NULL)
37505491d2cSKalle Valo break;
37605491d2cSKalle Valo sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i],
37705491d2cSKalle Valo UCODE_LOADER_API_VER);
37805491d2cSKalle Valo status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
37905491d2cSKalle Valo if (status) {
38005491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
38105491d2cSKalle Valo KBUILD_MODNAME, fw_name);
38205491d2cSKalle Valo return status;
38305491d2cSKalle Valo }
38405491d2cSKalle Valo sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i],
38505491d2cSKalle Valo UCODE_LOADER_API_VER);
38605491d2cSKalle Valo status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
38705491d2cSKalle Valo if (status) {
38805491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
38905491d2cSKalle Valo KBUILD_MODNAME, fw_name);
39005491d2cSKalle Valo return status;
39105491d2cSKalle Valo }
39205491d2cSKalle Valo wl->fw.hdr_num_entries[i] =
39305491d2cSKalle Valo wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr));
39405491d2cSKalle Valo }
39505491d2cSKalle Valo wl->fw.fw_cnt = i;
39605491d2cSKalle Valo status = brcms_ucode_data_init(wl, &wl->ucode);
39705491d2cSKalle Valo brcms_release_fw(wl);
39805491d2cSKalle Valo return status;
39905491d2cSKalle Valo }
40005491d2cSKalle Valo
brcms_ops_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * control,struct sk_buff * skb)40105491d2cSKalle Valo static void brcms_ops_tx(struct ieee80211_hw *hw,
40205491d2cSKalle Valo struct ieee80211_tx_control *control,
40305491d2cSKalle Valo struct sk_buff *skb)
40405491d2cSKalle Valo {
40505491d2cSKalle Valo struct brcms_info *wl = hw->priv;
40605491d2cSKalle Valo struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
40705491d2cSKalle Valo
40805491d2cSKalle Valo spin_lock_bh(&wl->lock);
40905491d2cSKalle Valo if (!wl->pub->up) {
41005491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core, "ops->tx called while down\n");
41105491d2cSKalle Valo kfree_skb(skb);
41205491d2cSKalle Valo goto done;
41305491d2cSKalle Valo }
41405491d2cSKalle Valo if (brcms_c_sendpkt_mac80211(wl->wlc, skb, hw))
41505491d2cSKalle Valo tx_info->rate_driver_data[0] = control->sta;
41605491d2cSKalle Valo done:
41705491d2cSKalle Valo spin_unlock_bh(&wl->lock);
41805491d2cSKalle Valo }
41905491d2cSKalle Valo
brcms_ops_start(struct ieee80211_hw * hw)42005491d2cSKalle Valo static int brcms_ops_start(struct ieee80211_hw *hw)
42105491d2cSKalle Valo {
42205491d2cSKalle Valo struct brcms_info *wl = hw->priv;
42305491d2cSKalle Valo bool blocked;
42405491d2cSKalle Valo int err;
42505491d2cSKalle Valo
42605491d2cSKalle Valo if (!wl->ucode.bcm43xx_bomminor) {
42705491d2cSKalle Valo err = brcms_request_fw(wl, wl->wlc->hw->d11core);
42805491d2cSKalle Valo if (err)
42905491d2cSKalle Valo return -ENOENT;
43005491d2cSKalle Valo }
43105491d2cSKalle Valo
43205491d2cSKalle Valo ieee80211_wake_queues(hw);
43305491d2cSKalle Valo spin_lock_bh(&wl->lock);
43405491d2cSKalle Valo blocked = brcms_rfkill_set_hw_state(wl);
43505491d2cSKalle Valo spin_unlock_bh(&wl->lock);
43605491d2cSKalle Valo if (!blocked)
43705491d2cSKalle Valo wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
43805491d2cSKalle Valo
43905491d2cSKalle Valo spin_lock_bh(&wl->lock);
44005491d2cSKalle Valo /* avoid acknowledging frames before a non-monitor device is added */
44105491d2cSKalle Valo wl->mute_tx = true;
44205491d2cSKalle Valo
44305491d2cSKalle Valo if (!wl->pub->up)
44405491d2cSKalle Valo if (!blocked)
44505491d2cSKalle Valo err = brcms_up(wl);
44605491d2cSKalle Valo else
44705491d2cSKalle Valo err = -ERFKILL;
44805491d2cSKalle Valo else
44905491d2cSKalle Valo err = -ENODEV;
45005491d2cSKalle Valo spin_unlock_bh(&wl->lock);
45105491d2cSKalle Valo
45205491d2cSKalle Valo if (err != 0)
45305491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core, "%s: brcms_up() returned %d\n",
45405491d2cSKalle Valo __func__, err);
45505491d2cSKalle Valo
45605491d2cSKalle Valo bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, true);
45705491d2cSKalle Valo return err;
45805491d2cSKalle Valo }
45905491d2cSKalle Valo
brcms_ops_stop(struct ieee80211_hw * hw)46005491d2cSKalle Valo static void brcms_ops_stop(struct ieee80211_hw *hw)
46105491d2cSKalle Valo {
46205491d2cSKalle Valo struct brcms_info *wl = hw->priv;
46305491d2cSKalle Valo int status;
46405491d2cSKalle Valo
46505491d2cSKalle Valo ieee80211_stop_queues(hw);
46605491d2cSKalle Valo
46705491d2cSKalle Valo if (wl->wlc == NULL)
46805491d2cSKalle Valo return;
46905491d2cSKalle Valo
47005491d2cSKalle Valo spin_lock_bh(&wl->lock);
47105491d2cSKalle Valo status = brcms_c_chipmatch(wl->wlc->hw->d11core);
47205491d2cSKalle Valo spin_unlock_bh(&wl->lock);
47305491d2cSKalle Valo if (!status) {
47405491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core,
47505491d2cSKalle Valo "wl: brcms_ops_stop: chipmatch failed\n");
47605491d2cSKalle Valo return;
47705491d2cSKalle Valo }
47805491d2cSKalle Valo
47905491d2cSKalle Valo bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, false);
48005491d2cSKalle Valo
48105491d2cSKalle Valo /* put driver in down state */
48205491d2cSKalle Valo spin_lock_bh(&wl->lock);
48305491d2cSKalle Valo brcms_down(wl);
48405491d2cSKalle Valo spin_unlock_bh(&wl->lock);
48505491d2cSKalle Valo }
48605491d2cSKalle Valo
48705491d2cSKalle Valo static int
brcms_ops_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)48805491d2cSKalle Valo brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
48905491d2cSKalle Valo {
49005491d2cSKalle Valo struct brcms_info *wl = hw->priv;
49105491d2cSKalle Valo
49205491d2cSKalle Valo /* Just STA, AP and ADHOC for now */
49305491d2cSKalle Valo if (vif->type != NL80211_IFTYPE_STATION &&
49405491d2cSKalle Valo vif->type != NL80211_IFTYPE_AP &&
49505491d2cSKalle Valo vif->type != NL80211_IFTYPE_ADHOC) {
49605491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core,
49705491d2cSKalle Valo "%s: Attempt to add type %d, only STA, AP and AdHoc for now\n",
49805491d2cSKalle Valo __func__, vif->type);
49905491d2cSKalle Valo return -EOPNOTSUPP;
50005491d2cSKalle Valo }
50105491d2cSKalle Valo
50205491d2cSKalle Valo spin_lock_bh(&wl->lock);
5032258ee58SAli MJ Al-Nasrawy wl->wlc->vif = vif;
50405491d2cSKalle Valo wl->mute_tx = false;
50505491d2cSKalle Valo brcms_c_mute(wl->wlc, false);
50605491d2cSKalle Valo if (vif->type == NL80211_IFTYPE_STATION)
50705491d2cSKalle Valo brcms_c_start_station(wl->wlc, vif->addr);
50805491d2cSKalle Valo else if (vif->type == NL80211_IFTYPE_AP)
50905491d2cSKalle Valo brcms_c_start_ap(wl->wlc, vif->addr, vif->bss_conf.bssid,
510f276e20bSJohannes Berg vif->cfg.ssid, vif->cfg.ssid_len);
51105491d2cSKalle Valo else if (vif->type == NL80211_IFTYPE_ADHOC)
51205491d2cSKalle Valo brcms_c_start_adhoc(wl->wlc, vif->addr);
51305491d2cSKalle Valo spin_unlock_bh(&wl->lock);
51405491d2cSKalle Valo
51505491d2cSKalle Valo return 0;
51605491d2cSKalle Valo }
51705491d2cSKalle Valo
51805491d2cSKalle Valo static void
brcms_ops_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)51905491d2cSKalle Valo brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
52005491d2cSKalle Valo {
5212258ee58SAli MJ Al-Nasrawy struct brcms_info *wl = hw->priv;
5222258ee58SAli MJ Al-Nasrawy
5232258ee58SAli MJ Al-Nasrawy spin_lock_bh(&wl->lock);
5242258ee58SAli MJ Al-Nasrawy wl->wlc->vif = NULL;
5252258ee58SAli MJ Al-Nasrawy spin_unlock_bh(&wl->lock);
52605491d2cSKalle Valo }
52705491d2cSKalle Valo
brcms_ops_config(struct ieee80211_hw * hw,u32 changed)52805491d2cSKalle Valo static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed)
52905491d2cSKalle Valo {
53005491d2cSKalle Valo struct ieee80211_conf *conf = &hw->conf;
53105491d2cSKalle Valo struct brcms_info *wl = hw->priv;
53205491d2cSKalle Valo struct bcma_device *core = wl->wlc->hw->d11core;
53305491d2cSKalle Valo int err = 0;
53405491d2cSKalle Valo int new_int;
53505491d2cSKalle Valo
53605491d2cSKalle Valo spin_lock_bh(&wl->lock);
53705491d2cSKalle Valo if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
53805491d2cSKalle Valo brcms_c_set_beacon_listen_interval(wl->wlc,
53905491d2cSKalle Valo conf->listen_interval);
54005491d2cSKalle Valo }
54105491d2cSKalle Valo if (changed & IEEE80211_CONF_CHANGE_MONITOR)
54205491d2cSKalle Valo brcms_dbg_info(core, "%s: change monitor mode: %s\n",
54305491d2cSKalle Valo __func__, conf->flags & IEEE80211_CONF_MONITOR ?
54405491d2cSKalle Valo "true" : "false");
54505491d2cSKalle Valo if (changed & IEEE80211_CONF_CHANGE_PS)
54605491d2cSKalle Valo brcms_err(core, "%s: change power-save mode: %s (implement)\n",
54705491d2cSKalle Valo __func__, conf->flags & IEEE80211_CONF_PS ?
54805491d2cSKalle Valo "true" : "false");
54905491d2cSKalle Valo
55005491d2cSKalle Valo if (changed & IEEE80211_CONF_CHANGE_POWER) {
55105491d2cSKalle Valo err = brcms_c_set_tx_power(wl->wlc, conf->power_level);
55205491d2cSKalle Valo if (err < 0) {
55305491d2cSKalle Valo brcms_err(core, "%s: Error setting power_level\n",
55405491d2cSKalle Valo __func__);
55505491d2cSKalle Valo goto config_out;
55605491d2cSKalle Valo }
55705491d2cSKalle Valo new_int = brcms_c_get_tx_power(wl->wlc);
55805491d2cSKalle Valo if (new_int != conf->power_level)
55905491d2cSKalle Valo brcms_err(core,
56005491d2cSKalle Valo "%s: Power level req != actual, %d %d\n",
56105491d2cSKalle Valo __func__, conf->power_level,
56205491d2cSKalle Valo new_int);
56305491d2cSKalle Valo }
56405491d2cSKalle Valo if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
56505491d2cSKalle Valo if (conf->chandef.width == NL80211_CHAN_WIDTH_20 ||
56605491d2cSKalle Valo conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
56705491d2cSKalle Valo err = brcms_c_set_channel(wl->wlc,
56805491d2cSKalle Valo conf->chandef.chan->hw_value);
56905491d2cSKalle Valo else
57005491d2cSKalle Valo err = -ENOTSUPP;
57105491d2cSKalle Valo }
57205491d2cSKalle Valo if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
57305491d2cSKalle Valo err = brcms_c_set_rate_limit(wl->wlc,
57405491d2cSKalle Valo conf->short_frame_max_tx_count,
57505491d2cSKalle Valo conf->long_frame_max_tx_count);
57605491d2cSKalle Valo
57705491d2cSKalle Valo config_out:
57805491d2cSKalle Valo spin_unlock_bh(&wl->lock);
57905491d2cSKalle Valo return err;
58005491d2cSKalle Valo }
58105491d2cSKalle Valo
58205491d2cSKalle Valo static void
brcms_ops_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * info,u64 changed)58305491d2cSKalle Valo brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
58405491d2cSKalle Valo struct ieee80211_vif *vif,
5857b7090b4SJohannes Berg struct ieee80211_bss_conf *info, u64 changed)
58605491d2cSKalle Valo {
58705491d2cSKalle Valo struct brcms_info *wl = hw->priv;
58805491d2cSKalle Valo struct bcma_device *core = wl->wlc->hw->d11core;
58905491d2cSKalle Valo
59005491d2cSKalle Valo if (changed & BSS_CHANGED_ASSOC) {
59105491d2cSKalle Valo /* association status changed (associated/disassociated)
59205491d2cSKalle Valo * also implies a change in the AID.
59305491d2cSKalle Valo */
59405491d2cSKalle Valo brcms_err(core, "%s: %s: %sassociated\n", KBUILD_MODNAME,
595f276e20bSJohannes Berg __func__, vif->cfg.assoc ? "" : "dis");
59605491d2cSKalle Valo spin_lock_bh(&wl->lock);
597f276e20bSJohannes Berg brcms_c_associate_upd(wl->wlc, vif->cfg.assoc);
59805491d2cSKalle Valo spin_unlock_bh(&wl->lock);
59905491d2cSKalle Valo }
60005491d2cSKalle Valo if (changed & BSS_CHANGED_ERP_SLOT) {
60105491d2cSKalle Valo s8 val;
60205491d2cSKalle Valo
60305491d2cSKalle Valo /* slot timing changed */
60405491d2cSKalle Valo if (info->use_short_slot)
60505491d2cSKalle Valo val = 1;
60605491d2cSKalle Valo else
60705491d2cSKalle Valo val = 0;
60805491d2cSKalle Valo spin_lock_bh(&wl->lock);
60905491d2cSKalle Valo brcms_c_set_shortslot_override(wl->wlc, val);
61005491d2cSKalle Valo spin_unlock_bh(&wl->lock);
61105491d2cSKalle Valo }
61205491d2cSKalle Valo
61305491d2cSKalle Valo if (changed & BSS_CHANGED_HT) {
61405491d2cSKalle Valo /* 802.11n parameters changed */
61505491d2cSKalle Valo u16 mode = info->ht_operation_mode;
61605491d2cSKalle Valo
61705491d2cSKalle Valo spin_lock_bh(&wl->lock);
61805491d2cSKalle Valo brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_CFG,
61905491d2cSKalle Valo mode & IEEE80211_HT_OP_MODE_PROTECTION);
62005491d2cSKalle Valo brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_NONGF,
62105491d2cSKalle Valo mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
62205491d2cSKalle Valo brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_OBSS,
62305491d2cSKalle Valo mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT);
62405491d2cSKalle Valo spin_unlock_bh(&wl->lock);
62505491d2cSKalle Valo }
62605491d2cSKalle Valo if (changed & BSS_CHANGED_BASIC_RATES) {
62705491d2cSKalle Valo struct ieee80211_supported_band *bi;
62805491d2cSKalle Valo u32 br_mask, i;
62905491d2cSKalle Valo u16 rate;
63005491d2cSKalle Valo struct brcm_rateset rs;
63105491d2cSKalle Valo int error;
63205491d2cSKalle Valo
63305491d2cSKalle Valo /* retrieve the current rates */
63405491d2cSKalle Valo spin_lock_bh(&wl->lock);
63505491d2cSKalle Valo brcms_c_get_current_rateset(wl->wlc, &rs);
63605491d2cSKalle Valo spin_unlock_bh(&wl->lock);
63705491d2cSKalle Valo
63805491d2cSKalle Valo br_mask = info->basic_rates;
63905491d2cSKalle Valo bi = hw->wiphy->bands[brcms_c_get_curband(wl->wlc)];
64005491d2cSKalle Valo for (i = 0; i < bi->n_bitrates; i++) {
64105491d2cSKalle Valo /* convert to internal rate value */
64205491d2cSKalle Valo rate = (bi->bitrates[i].bitrate << 1) / 10;
64305491d2cSKalle Valo
64405491d2cSKalle Valo /* set/clear basic rate flag */
64505491d2cSKalle Valo brcms_set_basic_rate(&rs, rate, br_mask & 1);
64605491d2cSKalle Valo br_mask >>= 1;
64705491d2cSKalle Valo }
64805491d2cSKalle Valo
64905491d2cSKalle Valo /* update the rate set */
65005491d2cSKalle Valo spin_lock_bh(&wl->lock);
65105491d2cSKalle Valo error = brcms_c_set_rateset(wl->wlc, &rs);
65205491d2cSKalle Valo spin_unlock_bh(&wl->lock);
65305491d2cSKalle Valo if (error)
65405491d2cSKalle Valo brcms_err(core, "changing basic rates failed: %d\n",
65505491d2cSKalle Valo error);
65605491d2cSKalle Valo }
65705491d2cSKalle Valo if (changed & BSS_CHANGED_BEACON_INT) {
65805491d2cSKalle Valo /* Beacon interval changed */
65905491d2cSKalle Valo spin_lock_bh(&wl->lock);
66005491d2cSKalle Valo brcms_c_set_beacon_period(wl->wlc, info->beacon_int);
66105491d2cSKalle Valo spin_unlock_bh(&wl->lock);
66205491d2cSKalle Valo }
66305491d2cSKalle Valo if (changed & BSS_CHANGED_BSSID) {
66405491d2cSKalle Valo /* BSSID changed, for whatever reason (IBSS and managed mode) */
66505491d2cSKalle Valo spin_lock_bh(&wl->lock);
66605491d2cSKalle Valo brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid);
66705491d2cSKalle Valo spin_unlock_bh(&wl->lock);
66805491d2cSKalle Valo }
66905491d2cSKalle Valo if (changed & BSS_CHANGED_SSID) {
67005491d2cSKalle Valo /* BSSID changed, for whatever reason (IBSS and managed mode) */
67105491d2cSKalle Valo spin_lock_bh(&wl->lock);
672f276e20bSJohannes Berg brcms_c_set_ssid(wl->wlc, vif->cfg.ssid, vif->cfg.ssid_len);
67305491d2cSKalle Valo spin_unlock_bh(&wl->lock);
67405491d2cSKalle Valo }
67505491d2cSKalle Valo if (changed & BSS_CHANGED_BEACON) {
67605491d2cSKalle Valo /* Beacon data changed, retrieve new beacon (beaconing modes) */
67705491d2cSKalle Valo struct sk_buff *beacon;
67805491d2cSKalle Valo u16 tim_offset = 0;
67905491d2cSKalle Valo
68005491d2cSKalle Valo spin_lock_bh(&wl->lock);
6816e8912a5SShaul Triebitz beacon = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL, 0);
68205491d2cSKalle Valo brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset,
68305491d2cSKalle Valo info->dtim_period);
68405491d2cSKalle Valo spin_unlock_bh(&wl->lock);
68505491d2cSKalle Valo }
68605491d2cSKalle Valo
68705491d2cSKalle Valo if (changed & BSS_CHANGED_AP_PROBE_RESP) {
68805491d2cSKalle Valo struct sk_buff *probe_resp;
68905491d2cSKalle Valo
69005491d2cSKalle Valo spin_lock_bh(&wl->lock);
69105491d2cSKalle Valo probe_resp = ieee80211_proberesp_get(hw, vif);
69205491d2cSKalle Valo brcms_c_set_new_probe_resp(wl->wlc, probe_resp);
69305491d2cSKalle Valo spin_unlock_bh(&wl->lock);
69405491d2cSKalle Valo }
69505491d2cSKalle Valo
69605491d2cSKalle Valo if (changed & BSS_CHANGED_BEACON_ENABLED) {
69705491d2cSKalle Valo /* Beaconing should be enabled/disabled (beaconing modes) */
69805491d2cSKalle Valo brcms_err(core, "%s: Beacon enabled: %s\n", __func__,
69905491d2cSKalle Valo info->enable_beacon ? "true" : "false");
70005491d2cSKalle Valo if (info->enable_beacon &&
70105491d2cSKalle Valo hw->wiphy->flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) {
70205491d2cSKalle Valo brcms_c_enable_probe_resp(wl->wlc, true);
70305491d2cSKalle Valo } else {
70405491d2cSKalle Valo brcms_c_enable_probe_resp(wl->wlc, false);
70505491d2cSKalle Valo }
70605491d2cSKalle Valo }
70705491d2cSKalle Valo
70805491d2cSKalle Valo if (changed & BSS_CHANGED_CQM) {
70905491d2cSKalle Valo /* Connection quality monitor config changed */
71005491d2cSKalle Valo brcms_err(core, "%s: cqm change: threshold %d, hys %d "
71105491d2cSKalle Valo " (implement)\n", __func__, info->cqm_rssi_thold,
71205491d2cSKalle Valo info->cqm_rssi_hyst);
71305491d2cSKalle Valo }
71405491d2cSKalle Valo
71505491d2cSKalle Valo if (changed & BSS_CHANGED_IBSS) {
71605491d2cSKalle Valo /* IBSS join status changed */
71705491d2cSKalle Valo brcms_err(core, "%s: IBSS joined: %s (implement)\n",
718f276e20bSJohannes Berg __func__, vif->cfg.ibss_joined ? "true" : "false");
71905491d2cSKalle Valo }
72005491d2cSKalle Valo
72105491d2cSKalle Valo if (changed & BSS_CHANGED_ARP_FILTER) {
72205491d2cSKalle Valo /* Hardware ARP filter address list or state changed */
72305491d2cSKalle Valo brcms_err(core, "%s: arp filtering: %d addresses"
724f276e20bSJohannes Berg " (implement)\n", __func__, vif->cfg.arp_addr_cnt);
72505491d2cSKalle Valo }
72605491d2cSKalle Valo
72705491d2cSKalle Valo if (changed & BSS_CHANGED_QOS) {
72805491d2cSKalle Valo /*
72905491d2cSKalle Valo * QoS for this association was enabled/disabled.
73005491d2cSKalle Valo * Note that it is only ever disabled for station mode.
73105491d2cSKalle Valo */
73205491d2cSKalle Valo brcms_err(core, "%s: qos enabled: %s (implement)\n",
73305491d2cSKalle Valo __func__, info->qos ? "true" : "false");
73405491d2cSKalle Valo }
73505491d2cSKalle Valo return;
73605491d2cSKalle Valo }
73705491d2cSKalle Valo
73805491d2cSKalle Valo static void
brcms_ops_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * total_flags,u64 multicast)73905491d2cSKalle Valo brcms_ops_configure_filter(struct ieee80211_hw *hw,
74005491d2cSKalle Valo unsigned int changed_flags,
74105491d2cSKalle Valo unsigned int *total_flags, u64 multicast)
74205491d2cSKalle Valo {
74305491d2cSKalle Valo struct brcms_info *wl = hw->priv;
74405491d2cSKalle Valo struct bcma_device *core = wl->wlc->hw->d11core;
74505491d2cSKalle Valo
74605491d2cSKalle Valo changed_flags &= MAC_FILTERS;
74705491d2cSKalle Valo *total_flags &= MAC_FILTERS;
74805491d2cSKalle Valo
74905491d2cSKalle Valo if (changed_flags & FIF_ALLMULTI)
75005491d2cSKalle Valo brcms_dbg_info(core, "FIF_ALLMULTI\n");
75105491d2cSKalle Valo if (changed_flags & FIF_FCSFAIL)
75205491d2cSKalle Valo brcms_dbg_info(core, "FIF_FCSFAIL\n");
75305491d2cSKalle Valo if (changed_flags & FIF_CONTROL)
75405491d2cSKalle Valo brcms_dbg_info(core, "FIF_CONTROL\n");
75505491d2cSKalle Valo if (changed_flags & FIF_OTHER_BSS)
75605491d2cSKalle Valo brcms_dbg_info(core, "FIF_OTHER_BSS\n");
75705491d2cSKalle Valo if (changed_flags & FIF_PSPOLL)
75805491d2cSKalle Valo brcms_dbg_info(core, "FIF_PSPOLL\n");
75905491d2cSKalle Valo if (changed_flags & FIF_BCN_PRBRESP_PROMISC)
76005491d2cSKalle Valo brcms_dbg_info(core, "FIF_BCN_PRBRESP_PROMISC\n");
76105491d2cSKalle Valo
76205491d2cSKalle Valo spin_lock_bh(&wl->lock);
76305491d2cSKalle Valo brcms_c_mac_promisc(wl->wlc, *total_flags);
76405491d2cSKalle Valo spin_unlock_bh(&wl->lock);
76505491d2cSKalle Valo return;
76605491d2cSKalle Valo }
76705491d2cSKalle Valo
brcms_ops_sw_scan_start(struct ieee80211_hw * hw,struct ieee80211_vif * vif,const u8 * mac_addr)76805491d2cSKalle Valo static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw,
76905491d2cSKalle Valo struct ieee80211_vif *vif,
77005491d2cSKalle Valo const u8 *mac_addr)
77105491d2cSKalle Valo {
77205491d2cSKalle Valo struct brcms_info *wl = hw->priv;
77305491d2cSKalle Valo spin_lock_bh(&wl->lock);
77405491d2cSKalle Valo brcms_c_scan_start(wl->wlc);
77505491d2cSKalle Valo spin_unlock_bh(&wl->lock);
77605491d2cSKalle Valo return;
77705491d2cSKalle Valo }
77805491d2cSKalle Valo
brcms_ops_sw_scan_complete(struct ieee80211_hw * hw,struct ieee80211_vif * vif)77905491d2cSKalle Valo static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw,
78005491d2cSKalle Valo struct ieee80211_vif *vif)
78105491d2cSKalle Valo {
78205491d2cSKalle Valo struct brcms_info *wl = hw->priv;
78305491d2cSKalle Valo spin_lock_bh(&wl->lock);
78405491d2cSKalle Valo brcms_c_scan_stop(wl->wlc);
78505491d2cSKalle Valo spin_unlock_bh(&wl->lock);
78605491d2cSKalle Valo return;
78705491d2cSKalle Valo }
78805491d2cSKalle Valo
78905491d2cSKalle Valo static int
brcms_ops_conf_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,unsigned int link_id,u16 queue,const struct ieee80211_tx_queue_params * params)790b3e2130bSJohannes Berg brcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
791b3e2130bSJohannes Berg unsigned int link_id, u16 queue,
79205491d2cSKalle Valo const struct ieee80211_tx_queue_params *params)
79305491d2cSKalle Valo {
79405491d2cSKalle Valo struct brcms_info *wl = hw->priv;
79505491d2cSKalle Valo
79605491d2cSKalle Valo spin_lock_bh(&wl->lock);
79705491d2cSKalle Valo brcms_c_wme_setparams(wl->wlc, queue, params, true);
79805491d2cSKalle Valo spin_unlock_bh(&wl->lock);
79905491d2cSKalle Valo
80005491d2cSKalle Valo return 0;
80105491d2cSKalle Valo }
80205491d2cSKalle Valo
80305491d2cSKalle Valo static int
brcms_ops_sta_add(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)80405491d2cSKalle Valo brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
80505491d2cSKalle Valo struct ieee80211_sta *sta)
80605491d2cSKalle Valo {
80705491d2cSKalle Valo struct brcms_info *wl = hw->priv;
80805491d2cSKalle Valo struct scb *scb = &wl->wlc->pri_scb;
80905491d2cSKalle Valo
81005491d2cSKalle Valo brcms_c_init_scb(scb);
81105491d2cSKalle Valo
81205491d2cSKalle Valo wl->pub->global_ampdu = &(scb->scb_ampdu);
81305491d2cSKalle Valo wl->pub->global_ampdu->max_pdu = 16;
81405491d2cSKalle Valo
81505491d2cSKalle Valo /*
81605491d2cSKalle Valo * minstrel_ht initiates addBA on our behalf by calling
81705491d2cSKalle Valo * ieee80211_start_tx_ba_session()
81805491d2cSKalle Valo */
81905491d2cSKalle Valo return 0;
82005491d2cSKalle Valo }
82105491d2cSKalle Valo
82205491d2cSKalle Valo static int
brcms_ops_ampdu_action(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_ampdu_params * params)82305491d2cSKalle Valo brcms_ops_ampdu_action(struct ieee80211_hw *hw,
82405491d2cSKalle Valo struct ieee80211_vif *vif,
82550ea05efSSara Sharon struct ieee80211_ampdu_params *params)
82605491d2cSKalle Valo {
82705491d2cSKalle Valo struct brcms_info *wl = hw->priv;
82805491d2cSKalle Valo struct scb *scb = &wl->wlc->pri_scb;
82905491d2cSKalle Valo int status;
83050ea05efSSara Sharon struct ieee80211_sta *sta = params->sta;
83150ea05efSSara Sharon enum ieee80211_ampdu_mlme_action action = params->action;
83250ea05efSSara Sharon u16 tid = params->tid;
83305491d2cSKalle Valo
83405491d2cSKalle Valo if (WARN_ON(scb->magic != SCB_MAGIC))
83505491d2cSKalle Valo return -EIDRM;
83605491d2cSKalle Valo switch (action) {
83705491d2cSKalle Valo case IEEE80211_AMPDU_RX_START:
83805491d2cSKalle Valo break;
83905491d2cSKalle Valo case IEEE80211_AMPDU_RX_STOP:
84005491d2cSKalle Valo break;
84105491d2cSKalle Valo case IEEE80211_AMPDU_TX_START:
84205491d2cSKalle Valo spin_lock_bh(&wl->lock);
84305491d2cSKalle Valo status = brcms_c_aggregatable(wl->wlc, tid);
84405491d2cSKalle Valo spin_unlock_bh(&wl->lock);
84505491d2cSKalle Valo if (!status) {
84696fca788SAli MJ Al-Nasrawy brcms_dbg_ht(wl->wlc->hw->d11core,
84705491d2cSKalle Valo "START: tid %d is not agg\'able\n", tid);
84805491d2cSKalle Valo return -EINVAL;
84905491d2cSKalle Valo }
8502ce113deSJohannes Berg return IEEE80211_AMPDU_TX_START_IMMEDIATE;
85105491d2cSKalle Valo
85205491d2cSKalle Valo case IEEE80211_AMPDU_TX_STOP_CONT:
85305491d2cSKalle Valo case IEEE80211_AMPDU_TX_STOP_FLUSH:
85405491d2cSKalle Valo case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
85505491d2cSKalle Valo spin_lock_bh(&wl->lock);
85605491d2cSKalle Valo brcms_c_ampdu_flush(wl->wlc, sta, tid);
85705491d2cSKalle Valo spin_unlock_bh(&wl->lock);
85805491d2cSKalle Valo ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
85905491d2cSKalle Valo break;
86005491d2cSKalle Valo case IEEE80211_AMPDU_TX_OPERATIONAL:
86105491d2cSKalle Valo /*
86205491d2cSKalle Valo * BA window size from ADDBA response ('buf_size') defines how
86305491d2cSKalle Valo * many outstanding MPDUs are allowed for the BA stream by
8640701519fSDmitry Antipov * recipient and traffic class (this is actually unused by the
8650701519fSDmitry Antipov * rest of the driver). 'ampdu_factor' gives maximum AMPDU size.
86605491d2cSKalle Valo */
86705491d2cSKalle Valo spin_lock_bh(&wl->lock);
8680701519fSDmitry Antipov brcms_c_ampdu_tx_operational(wl->wlc, tid,
86905491d2cSKalle Valo (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
870046d2e7cSSriram R sta->deflink.ht_cap.ampdu_factor)) - 1);
87105491d2cSKalle Valo spin_unlock_bh(&wl->lock);
87205491d2cSKalle Valo /* Power save wakeup */
87305491d2cSKalle Valo break;
87405491d2cSKalle Valo default:
87505491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core,
87605491d2cSKalle Valo "%s: Invalid command, ignoring\n", __func__);
87705491d2cSKalle Valo }
87805491d2cSKalle Valo
87905491d2cSKalle Valo return 0;
88005491d2cSKalle Valo }
88105491d2cSKalle Valo
brcms_ops_rfkill_poll(struct ieee80211_hw * hw)88205491d2cSKalle Valo static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw)
88305491d2cSKalle Valo {
88405491d2cSKalle Valo struct brcms_info *wl = hw->priv;
88505491d2cSKalle Valo bool blocked;
88605491d2cSKalle Valo
88705491d2cSKalle Valo spin_lock_bh(&wl->lock);
88805491d2cSKalle Valo blocked = brcms_c_check_radio_disabled(wl->wlc);
88905491d2cSKalle Valo spin_unlock_bh(&wl->lock);
89005491d2cSKalle Valo
89105491d2cSKalle Valo wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
89205491d2cSKalle Valo }
89305491d2cSKalle Valo
brcms_tx_flush_completed(struct brcms_info * wl)89405491d2cSKalle Valo static bool brcms_tx_flush_completed(struct brcms_info *wl)
89505491d2cSKalle Valo {
89605491d2cSKalle Valo bool result;
89705491d2cSKalle Valo
89805491d2cSKalle Valo spin_lock_bh(&wl->lock);
89905491d2cSKalle Valo result = brcms_c_tx_flush_completed(wl->wlc);
90005491d2cSKalle Valo spin_unlock_bh(&wl->lock);
90105491d2cSKalle Valo return result;
90205491d2cSKalle Valo }
90305491d2cSKalle Valo
brcms_ops_flush(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u32 queues,bool drop)90405491d2cSKalle Valo static void brcms_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
90505491d2cSKalle Valo u32 queues, bool drop)
90605491d2cSKalle Valo {
90705491d2cSKalle Valo struct brcms_info *wl = hw->priv;
90805491d2cSKalle Valo int ret;
90905491d2cSKalle Valo
91005491d2cSKalle Valo no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false");
91105491d2cSKalle Valo
91205491d2cSKalle Valo ret = wait_event_timeout(wl->tx_flush_wq,
91305491d2cSKalle Valo brcms_tx_flush_completed(wl),
91405491d2cSKalle Valo msecs_to_jiffies(BRCMS_FLUSH_TIMEOUT));
91505491d2cSKalle Valo
91605491d2cSKalle Valo brcms_dbg_mac80211(wl->wlc->hw->d11core,
91705491d2cSKalle Valo "ret=%d\n", jiffies_to_msecs(ret));
91805491d2cSKalle Valo }
91905491d2cSKalle Valo
brcms_ops_get_tsf(struct ieee80211_hw * hw,struct ieee80211_vif * vif)92005491d2cSKalle Valo static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
92105491d2cSKalle Valo {
92205491d2cSKalle Valo struct brcms_info *wl = hw->priv;
92305491d2cSKalle Valo u64 tsf;
92405491d2cSKalle Valo
92505491d2cSKalle Valo spin_lock_bh(&wl->lock);
92605491d2cSKalle Valo tsf = brcms_c_tsf_get(wl->wlc);
92705491d2cSKalle Valo spin_unlock_bh(&wl->lock);
92805491d2cSKalle Valo
92905491d2cSKalle Valo return tsf;
93005491d2cSKalle Valo }
93105491d2cSKalle Valo
brcms_ops_set_tsf(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u64 tsf)93205491d2cSKalle Valo static void brcms_ops_set_tsf(struct ieee80211_hw *hw,
93305491d2cSKalle Valo struct ieee80211_vif *vif, u64 tsf)
93405491d2cSKalle Valo {
93505491d2cSKalle Valo struct brcms_info *wl = hw->priv;
93605491d2cSKalle Valo
93705491d2cSKalle Valo spin_lock_bh(&wl->lock);
93805491d2cSKalle Valo brcms_c_tsf_set(wl->wlc, tsf);
93905491d2cSKalle Valo spin_unlock_bh(&wl->lock);
94005491d2cSKalle Valo }
94105491d2cSKalle Valo
brcms_ops_beacon_set_tim(struct ieee80211_hw * hw,struct ieee80211_sta * sta,bool set)9422258ee58SAli MJ Al-Nasrawy static int brcms_ops_beacon_set_tim(struct ieee80211_hw *hw,
9432258ee58SAli MJ Al-Nasrawy struct ieee80211_sta *sta, bool set)
9442258ee58SAli MJ Al-Nasrawy {
9452258ee58SAli MJ Al-Nasrawy struct brcms_info *wl = hw->priv;
9462258ee58SAli MJ Al-Nasrawy struct sk_buff *beacon = NULL;
9472258ee58SAli MJ Al-Nasrawy u16 tim_offset = 0;
9482258ee58SAli MJ Al-Nasrawy
9492258ee58SAli MJ Al-Nasrawy spin_lock_bh(&wl->lock);
9502258ee58SAli MJ Al-Nasrawy if (wl->wlc->vif)
9512258ee58SAli MJ Al-Nasrawy beacon = ieee80211_beacon_get_tim(hw, wl->wlc->vif,
9526e8912a5SShaul Triebitz &tim_offset, NULL, 0);
9532258ee58SAli MJ Al-Nasrawy if (beacon)
9542258ee58SAli MJ Al-Nasrawy brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset,
9552258ee58SAli MJ Al-Nasrawy wl->wlc->vif->bss_conf.dtim_period);
9562258ee58SAli MJ Al-Nasrawy spin_unlock_bh(&wl->lock);
9572258ee58SAli MJ Al-Nasrawy
9582258ee58SAli MJ Al-Nasrawy return 0;
9592258ee58SAli MJ Al-Nasrawy }
9602258ee58SAli MJ Al-Nasrawy
96105491d2cSKalle Valo static const struct ieee80211_ops brcms_ops = {
96205491d2cSKalle Valo .tx = brcms_ops_tx,
963a790cc3aSAlexander Wetzel .wake_tx_queue = ieee80211_handle_wake_tx_queue,
96405491d2cSKalle Valo .start = brcms_ops_start,
96505491d2cSKalle Valo .stop = brcms_ops_stop,
96605491d2cSKalle Valo .add_interface = brcms_ops_add_interface,
96705491d2cSKalle Valo .remove_interface = brcms_ops_remove_interface,
96805491d2cSKalle Valo .config = brcms_ops_config,
96905491d2cSKalle Valo .bss_info_changed = brcms_ops_bss_info_changed,
97005491d2cSKalle Valo .configure_filter = brcms_ops_configure_filter,
97105491d2cSKalle Valo .sw_scan_start = brcms_ops_sw_scan_start,
97205491d2cSKalle Valo .sw_scan_complete = brcms_ops_sw_scan_complete,
97305491d2cSKalle Valo .conf_tx = brcms_ops_conf_tx,
97405491d2cSKalle Valo .sta_add = brcms_ops_sta_add,
97505491d2cSKalle Valo .ampdu_action = brcms_ops_ampdu_action,
97605491d2cSKalle Valo .rfkill_poll = brcms_ops_rfkill_poll,
97705491d2cSKalle Valo .flush = brcms_ops_flush,
97805491d2cSKalle Valo .get_tsf = brcms_ops_get_tsf,
97905491d2cSKalle Valo .set_tsf = brcms_ops_set_tsf,
9802258ee58SAli MJ Al-Nasrawy .set_tim = brcms_ops_beacon_set_tim,
98105491d2cSKalle Valo };
98205491d2cSKalle Valo
brcms_dpc(struct tasklet_struct * t)983427a06beSAllen Pais void brcms_dpc(struct tasklet_struct *t)
98405491d2cSKalle Valo {
98505491d2cSKalle Valo struct brcms_info *wl;
98605491d2cSKalle Valo
987427a06beSAllen Pais wl = from_tasklet(wl, t, tasklet);
98805491d2cSKalle Valo
98905491d2cSKalle Valo spin_lock_bh(&wl->lock);
99005491d2cSKalle Valo
99105491d2cSKalle Valo /* call the common second level interrupt handler */
99205491d2cSKalle Valo if (wl->pub->up) {
99305491d2cSKalle Valo if (wl->resched) {
99405491d2cSKalle Valo unsigned long flags;
99505491d2cSKalle Valo
99605491d2cSKalle Valo spin_lock_irqsave(&wl->isr_lock, flags);
99705491d2cSKalle Valo brcms_c_intrsupd(wl->wlc);
99805491d2cSKalle Valo spin_unlock_irqrestore(&wl->isr_lock, flags);
99905491d2cSKalle Valo }
100005491d2cSKalle Valo
100105491d2cSKalle Valo wl->resched = brcms_c_dpc(wl->wlc, true);
100205491d2cSKalle Valo }
100305491d2cSKalle Valo
100405491d2cSKalle Valo /* brcms_c_dpc() may bring the driver down */
100505491d2cSKalle Valo if (!wl->pub->up)
100605491d2cSKalle Valo goto done;
100705491d2cSKalle Valo
100805491d2cSKalle Valo /* re-schedule dpc */
100905491d2cSKalle Valo if (wl->resched)
101005491d2cSKalle Valo tasklet_schedule(&wl->tasklet);
101105491d2cSKalle Valo else
101205491d2cSKalle Valo /* re-enable interrupts */
101305491d2cSKalle Valo brcms_intrson(wl);
101405491d2cSKalle Valo
101505491d2cSKalle Valo done:
101605491d2cSKalle Valo spin_unlock_bh(&wl->lock);
101705491d2cSKalle Valo wake_up(&wl->tx_flush_wq);
101805491d2cSKalle Valo }
101905491d2cSKalle Valo
brcms_isr(int irq,void * dev_id)102005491d2cSKalle Valo static irqreturn_t brcms_isr(int irq, void *dev_id)
102105491d2cSKalle Valo {
102205491d2cSKalle Valo struct brcms_info *wl;
102305491d2cSKalle Valo irqreturn_t ret = IRQ_NONE;
102405491d2cSKalle Valo
102505491d2cSKalle Valo wl = (struct brcms_info *) dev_id;
102605491d2cSKalle Valo
102705491d2cSKalle Valo spin_lock(&wl->isr_lock);
102805491d2cSKalle Valo
102905491d2cSKalle Valo /* call common first level interrupt handler */
103005491d2cSKalle Valo if (brcms_c_isr(wl->wlc)) {
103105491d2cSKalle Valo /* schedule second level handler */
103205491d2cSKalle Valo tasklet_schedule(&wl->tasklet);
103305491d2cSKalle Valo ret = IRQ_HANDLED;
103405491d2cSKalle Valo }
103505491d2cSKalle Valo
103605491d2cSKalle Valo spin_unlock(&wl->isr_lock);
103705491d2cSKalle Valo
103805491d2cSKalle Valo return ret;
103905491d2cSKalle Valo }
104005491d2cSKalle Valo
104105491d2cSKalle Valo /*
104205491d2cSKalle Valo * is called in brcms_pci_probe() context, therefore no locking required.
104305491d2cSKalle Valo */
ieee_hw_rate_init(struct ieee80211_hw * hw)104405491d2cSKalle Valo static int ieee_hw_rate_init(struct ieee80211_hw *hw)
104505491d2cSKalle Valo {
104605491d2cSKalle Valo struct brcms_info *wl = hw->priv;
104705491d2cSKalle Valo struct brcms_c_info *wlc = wl->wlc;
104805491d2cSKalle Valo struct ieee80211_supported_band *band;
104905491d2cSKalle Valo u16 phy_type;
105005491d2cSKalle Valo
105157fbcce3SJohannes Berg hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
105257fbcce3SJohannes Berg hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
105305491d2cSKalle Valo
105405491d2cSKalle Valo phy_type = brcms_c_get_phy_type(wl->wlc, 0);
105505491d2cSKalle Valo if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) {
105605491d2cSKalle Valo band = &wlc->bandstate[BAND_2G_INDEX]->band;
105705491d2cSKalle Valo *band = brcms_band_2GHz_nphy_template;
105805491d2cSKalle Valo if (phy_type == PHY_TYPE_LCN) {
105905491d2cSKalle Valo /* Single stream */
106005491d2cSKalle Valo band->ht_cap.mcs.rx_mask[1] = 0;
106105491d2cSKalle Valo band->ht_cap.mcs.rx_highest = cpu_to_le16(72);
106205491d2cSKalle Valo }
106357fbcce3SJohannes Berg hw->wiphy->bands[NL80211_BAND_2GHZ] = band;
106405491d2cSKalle Valo } else {
106505491d2cSKalle Valo return -EPERM;
106605491d2cSKalle Valo }
106705491d2cSKalle Valo
106805491d2cSKalle Valo /* Assume all bands use the same phy. True for 11n devices. */
106905491d2cSKalle Valo if (wl->pub->_nbands > 1) {
107005491d2cSKalle Valo if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) {
107105491d2cSKalle Valo band = &wlc->bandstate[BAND_5G_INDEX]->band;
107205491d2cSKalle Valo *band = brcms_band_5GHz_nphy_template;
107357fbcce3SJohannes Berg hw->wiphy->bands[NL80211_BAND_5GHZ] = band;
107405491d2cSKalle Valo } else {
107505491d2cSKalle Valo return -EPERM;
107605491d2cSKalle Valo }
107705491d2cSKalle Valo }
107805491d2cSKalle Valo return 0;
107905491d2cSKalle Valo }
108005491d2cSKalle Valo
108105491d2cSKalle Valo /*
108205491d2cSKalle Valo * is called in brcms_pci_probe() context, therefore no locking required.
108305491d2cSKalle Valo */
ieee_hw_init(struct ieee80211_hw * hw)108405491d2cSKalle Valo static int ieee_hw_init(struct ieee80211_hw *hw)
108505491d2cSKalle Valo {
108605491d2cSKalle Valo ieee80211_hw_set(hw, AMPDU_AGGREGATION);
108705491d2cSKalle Valo ieee80211_hw_set(hw, SIGNAL_DBM);
108805491d2cSKalle Valo ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
1089*d22d72e2SArend van Spriel ieee80211_hw_set(hw, MFP_CAPABLE);
109005491d2cSKalle Valo
109105491d2cSKalle Valo hw->extra_tx_headroom = brcms_c_get_header_len();
109205491d2cSKalle Valo hw->queues = N_TX_QUEUES;
109305491d2cSKalle Valo hw->max_rates = 2; /* Primary rate and 1 fallback rate */
109405491d2cSKalle Valo
109505491d2cSKalle Valo /* channel change time is dependent on chip and band */
109605491d2cSKalle Valo hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
109705491d2cSKalle Valo BIT(NL80211_IFTYPE_AP) |
109805491d2cSKalle Valo BIT(NL80211_IFTYPE_ADHOC);
109905491d2cSKalle Valo
110005491d2cSKalle Valo /*
110105491d2cSKalle Valo * deactivate sending probe responses by ucude, because this will
110205491d2cSKalle Valo * cause problems when WPS is used.
110305491d2cSKalle Valo *
110405491d2cSKalle Valo * hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
110505491d2cSKalle Valo */
110605491d2cSKalle Valo
1107ae44b502SAndrew Zaborowski wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
1108ae44b502SAndrew Zaborowski
110905491d2cSKalle Valo hw->rate_control_algorithm = "minstrel_ht";
111005491d2cSKalle Valo
111105491d2cSKalle Valo hw->sta_data_size = 0;
111205491d2cSKalle Valo return ieee_hw_rate_init(hw);
111305491d2cSKalle Valo }
111405491d2cSKalle Valo
11157eae8c73SLee Jones /*
111605491d2cSKalle Valo * attach to the WL device.
111705491d2cSKalle Valo *
111805491d2cSKalle Valo * Attach to the WL device identified by vendor and device parameters.
111905491d2cSKalle Valo * regs is a host accessible memory address pointing to WL device registers.
112005491d2cSKalle Valo *
112105491d2cSKalle Valo * is called in brcms_bcma_probe() context, therefore no locking required.
112205491d2cSKalle Valo */
brcms_attach(struct bcma_device * pdev)112305491d2cSKalle Valo static struct brcms_info *brcms_attach(struct bcma_device *pdev)
112405491d2cSKalle Valo {
112505491d2cSKalle Valo struct brcms_info *wl = NULL;
112605491d2cSKalle Valo int unit, err;
112705491d2cSKalle Valo struct ieee80211_hw *hw;
112805491d2cSKalle Valo u8 perm[ETH_ALEN];
112905491d2cSKalle Valo
113005491d2cSKalle Valo unit = n_adapters_found;
113105491d2cSKalle Valo err = 0;
113205491d2cSKalle Valo
113305491d2cSKalle Valo if (unit < 0)
113405491d2cSKalle Valo return NULL;
113505491d2cSKalle Valo
113605491d2cSKalle Valo /* allocate private info */
113705491d2cSKalle Valo hw = bcma_get_drvdata(pdev);
113805491d2cSKalle Valo if (hw != NULL)
113905491d2cSKalle Valo wl = hw->priv;
114005491d2cSKalle Valo if (WARN_ON(hw == NULL) || WARN_ON(wl == NULL))
114105491d2cSKalle Valo return NULL;
114205491d2cSKalle Valo wl->wiphy = hw->wiphy;
114305491d2cSKalle Valo
114405491d2cSKalle Valo atomic_set(&wl->callbacks, 0);
114505491d2cSKalle Valo
114605491d2cSKalle Valo init_waitqueue_head(&wl->tx_flush_wq);
114705491d2cSKalle Valo
114805491d2cSKalle Valo /* setup the bottom half handler */
1149427a06beSAllen Pais tasklet_setup(&wl->tasklet, brcms_dpc);
115005491d2cSKalle Valo
115105491d2cSKalle Valo spin_lock_init(&wl->lock);
115205491d2cSKalle Valo spin_lock_init(&wl->isr_lock);
115305491d2cSKalle Valo
115405491d2cSKalle Valo /* common load-time initialization */
115505491d2cSKalle Valo wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err);
115605491d2cSKalle Valo if (!wl->wlc) {
115705491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n",
115805491d2cSKalle Valo KBUILD_MODNAME, err);
115905491d2cSKalle Valo goto fail;
116005491d2cSKalle Valo }
116105491d2cSKalle Valo wl->pub = brcms_c_pub(wl->wlc);
116205491d2cSKalle Valo
116305491d2cSKalle Valo wl->pub->ieee_hw = hw;
116405491d2cSKalle Valo
116505491d2cSKalle Valo /* register our interrupt handler */
116605491d2cSKalle Valo if (request_irq(pdev->irq, brcms_isr,
116705491d2cSKalle Valo IRQF_SHARED, KBUILD_MODNAME, wl)) {
116805491d2cSKalle Valo wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit);
116905491d2cSKalle Valo goto fail;
117005491d2cSKalle Valo }
117105491d2cSKalle Valo wl->irq = pdev->irq;
117205491d2cSKalle Valo
117305491d2cSKalle Valo /* register module */
117405491d2cSKalle Valo brcms_c_module_register(wl->pub, "linux", wl, NULL);
117505491d2cSKalle Valo
117605491d2cSKalle Valo if (ieee_hw_init(hw)) {
117705491d2cSKalle Valo wiphy_err(wl->wiphy, "wl%d: %s: ieee_hw_init failed!\n", unit,
117805491d2cSKalle Valo __func__);
117905491d2cSKalle Valo goto fail;
118005491d2cSKalle Valo }
118105491d2cSKalle Valo
118205491d2cSKalle Valo brcms_c_regd_init(wl->wlc);
118305491d2cSKalle Valo
118405491d2cSKalle Valo memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN);
118505491d2cSKalle Valo if (WARN_ON(!is_valid_ether_addr(perm)))
118605491d2cSKalle Valo goto fail;
118705491d2cSKalle Valo SET_IEEE80211_PERM_ADDR(hw, perm);
118805491d2cSKalle Valo
118905491d2cSKalle Valo err = ieee80211_register_hw(hw);
119005491d2cSKalle Valo if (err)
119105491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status"
119205491d2cSKalle Valo "%d\n", __func__, err);
119305491d2cSKalle Valo
119405491d2cSKalle Valo if (wl->pub->srom_ccode[0] &&
119505491d2cSKalle Valo regulatory_hint(wl->wiphy, wl->pub->srom_ccode))
119605491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: regulatory hint failed\n", __func__);
119705491d2cSKalle Valo
119805491d2cSKalle Valo brcms_debugfs_attach(wl->pub);
119905491d2cSKalle Valo brcms_debugfs_create_files(wl->pub);
120005491d2cSKalle Valo n_adapters_found++;
120105491d2cSKalle Valo return wl;
120205491d2cSKalle Valo
120305491d2cSKalle Valo fail:
120405491d2cSKalle Valo brcms_free(wl);
120505491d2cSKalle Valo return NULL;
120605491d2cSKalle Valo }
120705491d2cSKalle Valo
120805491d2cSKalle Valo
120905491d2cSKalle Valo
12107eae8c73SLee Jones /*
121105491d2cSKalle Valo * determines if a device is a WL device, and if so, attaches it.
121205491d2cSKalle Valo *
121305491d2cSKalle Valo * This function determines if a device pointed to by pdev is a WL device,
121405491d2cSKalle Valo * and if so, performs a brcms_attach() on it.
121505491d2cSKalle Valo *
121605491d2cSKalle Valo * Perimeter lock is initialized in the course of this function.
121705491d2cSKalle Valo */
brcms_bcma_probe(struct bcma_device * pdev)121805491d2cSKalle Valo static int brcms_bcma_probe(struct bcma_device *pdev)
121905491d2cSKalle Valo {
122005491d2cSKalle Valo struct brcms_info *wl;
122105491d2cSKalle Valo struct ieee80211_hw *hw;
12229a25344dSChristophe JAILLET int ret;
122305491d2cSKalle Valo
122405491d2cSKalle Valo dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n",
122505491d2cSKalle Valo pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class,
122605491d2cSKalle Valo pdev->irq);
122705491d2cSKalle Valo
122805491d2cSKalle Valo if ((pdev->id.manuf != BCMA_MANUF_BCM) ||
122905491d2cSKalle Valo (pdev->id.id != BCMA_CORE_80211))
123005491d2cSKalle Valo return -ENODEV;
123105491d2cSKalle Valo
123205491d2cSKalle Valo hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops);
123305491d2cSKalle Valo if (!hw) {
123405491d2cSKalle Valo pr_err("%s: ieee80211_alloc_hw failed\n", __func__);
123505491d2cSKalle Valo return -ENOMEM;
123605491d2cSKalle Valo }
123705491d2cSKalle Valo
123805491d2cSKalle Valo SET_IEEE80211_DEV(hw, &pdev->dev);
123905491d2cSKalle Valo
124005491d2cSKalle Valo bcma_set_drvdata(pdev, hw);
124105491d2cSKalle Valo
124205491d2cSKalle Valo memset(hw->priv, 0, sizeof(*wl));
124305491d2cSKalle Valo
124405491d2cSKalle Valo wl = brcms_attach(pdev);
124505491d2cSKalle Valo if (!wl) {
124605491d2cSKalle Valo pr_err("%s: brcms_attach failed!\n", __func__);
12479a25344dSChristophe JAILLET ret = -ENODEV;
12489a25344dSChristophe JAILLET goto err_free_ieee80211;
124905491d2cSKalle Valo }
125005491d2cSKalle Valo brcms_led_register(wl);
125105491d2cSKalle Valo
125205491d2cSKalle Valo return 0;
12539a25344dSChristophe JAILLET
12549a25344dSChristophe JAILLET err_free_ieee80211:
12559a25344dSChristophe JAILLET ieee80211_free_hw(hw);
12569a25344dSChristophe JAILLET return ret;
125705491d2cSKalle Valo }
125805491d2cSKalle Valo
brcms_suspend(struct bcma_device * pdev)125905491d2cSKalle Valo static int brcms_suspend(struct bcma_device *pdev)
126005491d2cSKalle Valo {
126105491d2cSKalle Valo struct brcms_info *wl;
126205491d2cSKalle Valo struct ieee80211_hw *hw;
126305491d2cSKalle Valo
126405491d2cSKalle Valo hw = bcma_get_drvdata(pdev);
126505491d2cSKalle Valo wl = hw->priv;
126605491d2cSKalle Valo if (!wl) {
126705491d2cSKalle Valo pr_err("%s: %s: no driver private struct!\n", KBUILD_MODNAME,
126805491d2cSKalle Valo __func__);
126905491d2cSKalle Valo return -ENODEV;
127005491d2cSKalle Valo }
127105491d2cSKalle Valo
127205491d2cSKalle Valo /* only need to flag hw is down for proper resume */
127305491d2cSKalle Valo spin_lock_bh(&wl->lock);
127405491d2cSKalle Valo wl->pub->hw_up = false;
127505491d2cSKalle Valo spin_unlock_bh(&wl->lock);
127605491d2cSKalle Valo
127705491d2cSKalle Valo brcms_dbg_info(wl->wlc->hw->d11core, "brcms_suspend ok\n");
127805491d2cSKalle Valo
127905491d2cSKalle Valo return 0;
128005491d2cSKalle Valo }
128105491d2cSKalle Valo
brcms_resume(struct bcma_device * pdev)128205491d2cSKalle Valo static int brcms_resume(struct bcma_device *pdev)
128305491d2cSKalle Valo {
128405491d2cSKalle Valo return 0;
128505491d2cSKalle Valo }
128605491d2cSKalle Valo
128705491d2cSKalle Valo static struct bcma_driver brcms_bcma_driver = {
128805491d2cSKalle Valo .name = KBUILD_MODNAME,
128905491d2cSKalle Valo .probe = brcms_bcma_probe,
129005491d2cSKalle Valo .suspend = brcms_suspend,
129105491d2cSKalle Valo .resume = brcms_resume,
129205491d2cSKalle Valo .remove = brcms_remove,
129305491d2cSKalle Valo .id_table = brcms_coreid_table,
129405491d2cSKalle Valo };
129505491d2cSKalle Valo
12967eae8c73SLee Jones /*
129705491d2cSKalle Valo * This is the main entry point for the brcmsmac driver.
129805491d2cSKalle Valo *
129905491d2cSKalle Valo * This function is scheduled upon module initialization and
130005491d2cSKalle Valo * does the driver registration, which result in brcms_bcma_probe()
130105491d2cSKalle Valo * call resulting in the driver bringup.
130205491d2cSKalle Valo */
brcms_driver_init(struct work_struct * work)130305491d2cSKalle Valo static void brcms_driver_init(struct work_struct *work)
130405491d2cSKalle Valo {
130505491d2cSKalle Valo int error;
130605491d2cSKalle Valo
130705491d2cSKalle Valo error = bcma_driver_register(&brcms_bcma_driver);
130805491d2cSKalle Valo if (error)
130905491d2cSKalle Valo pr_err("%s: register returned %d\n", __func__, error);
131005491d2cSKalle Valo }
131105491d2cSKalle Valo
131205491d2cSKalle Valo static DECLARE_WORK(brcms_driver_work, brcms_driver_init);
131305491d2cSKalle Valo
brcms_module_init(void)131405491d2cSKalle Valo static int __init brcms_module_init(void)
131505491d2cSKalle Valo {
131605491d2cSKalle Valo brcms_debugfs_init();
131705491d2cSKalle Valo if (!schedule_work(&brcms_driver_work))
131805491d2cSKalle Valo return -EBUSY;
131905491d2cSKalle Valo
132005491d2cSKalle Valo return 0;
132105491d2cSKalle Valo }
132205491d2cSKalle Valo
13237eae8c73SLee Jones /*
132405491d2cSKalle Valo * This function unloads the brcmsmac driver from the system.
132505491d2cSKalle Valo *
132605491d2cSKalle Valo * This function unconditionally unloads the brcmsmac driver module from the
132705491d2cSKalle Valo * system.
132805491d2cSKalle Valo *
132905491d2cSKalle Valo */
brcms_module_exit(void)133005491d2cSKalle Valo static void __exit brcms_module_exit(void)
133105491d2cSKalle Valo {
133205491d2cSKalle Valo cancel_work_sync(&brcms_driver_work);
133305491d2cSKalle Valo bcma_driver_unregister(&brcms_bcma_driver);
133405491d2cSKalle Valo brcms_debugfs_exit();
133505491d2cSKalle Valo }
133605491d2cSKalle Valo
133705491d2cSKalle Valo module_init(brcms_module_init);
133805491d2cSKalle Valo module_exit(brcms_module_exit);
133905491d2cSKalle Valo
134005491d2cSKalle Valo /*
134105491d2cSKalle Valo * precondition: perimeter lock has been acquired
134205491d2cSKalle Valo */
brcms_txflowcontrol(struct brcms_info * wl,struct brcms_if * wlif,bool state,int prio)134305491d2cSKalle Valo void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
134405491d2cSKalle Valo bool state, int prio)
134505491d2cSKalle Valo {
134605491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core, "Shouldn't be here %s\n", __func__);
134705491d2cSKalle Valo }
134805491d2cSKalle Valo
134905491d2cSKalle Valo /*
135005491d2cSKalle Valo * precondition: perimeter lock has been acquired
135105491d2cSKalle Valo */
brcms_init(struct brcms_info * wl)135205491d2cSKalle Valo void brcms_init(struct brcms_info *wl)
135305491d2cSKalle Valo {
135405491d2cSKalle Valo brcms_dbg_info(wl->wlc->hw->d11core, "Initializing wl%d\n",
135505491d2cSKalle Valo wl->pub->unit);
135605491d2cSKalle Valo brcms_reset(wl);
135705491d2cSKalle Valo brcms_c_init(wl->wlc, wl->mute_tx);
135805491d2cSKalle Valo }
135905491d2cSKalle Valo
136005491d2cSKalle Valo /*
136105491d2cSKalle Valo * precondition: perimeter lock has been acquired
136205491d2cSKalle Valo */
brcms_reset(struct brcms_info * wl)136305491d2cSKalle Valo uint brcms_reset(struct brcms_info *wl)
136405491d2cSKalle Valo {
136505491d2cSKalle Valo brcms_dbg_info(wl->wlc->hw->d11core, "Resetting wl%d\n", wl->pub->unit);
136605491d2cSKalle Valo brcms_c_reset(wl->wlc);
136705491d2cSKalle Valo
136805491d2cSKalle Valo /* dpc will not be rescheduled */
136905491d2cSKalle Valo wl->resched = false;
137005491d2cSKalle Valo
137105491d2cSKalle Valo /* inform publicly that interface is down */
137205491d2cSKalle Valo wl->pub->up = false;
137305491d2cSKalle Valo
137405491d2cSKalle Valo return 0;
137505491d2cSKalle Valo }
137605491d2cSKalle Valo
brcms_fatal_error(struct brcms_info * wl)137705491d2cSKalle Valo void brcms_fatal_error(struct brcms_info *wl)
137805491d2cSKalle Valo {
137905491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core, "wl%d: fatal error, reinitializing\n",
138005491d2cSKalle Valo wl->wlc->pub->unit);
138105491d2cSKalle Valo brcms_reset(wl);
138205491d2cSKalle Valo ieee80211_restart_hw(wl->pub->ieee_hw);
138305491d2cSKalle Valo }
138405491d2cSKalle Valo
138505491d2cSKalle Valo /*
138605491d2cSKalle Valo * These are interrupt on/off entry points. Disable interrupts
138705491d2cSKalle Valo * during interrupt state transition.
138805491d2cSKalle Valo */
brcms_intrson(struct brcms_info * wl)138905491d2cSKalle Valo void brcms_intrson(struct brcms_info *wl)
139005491d2cSKalle Valo {
139105491d2cSKalle Valo unsigned long flags;
139205491d2cSKalle Valo
139305491d2cSKalle Valo spin_lock_irqsave(&wl->isr_lock, flags);
139405491d2cSKalle Valo brcms_c_intrson(wl->wlc);
139505491d2cSKalle Valo spin_unlock_irqrestore(&wl->isr_lock, flags);
139605491d2cSKalle Valo }
139705491d2cSKalle Valo
brcms_intrsoff(struct brcms_info * wl)139805491d2cSKalle Valo u32 brcms_intrsoff(struct brcms_info *wl)
139905491d2cSKalle Valo {
140005491d2cSKalle Valo unsigned long flags;
140105491d2cSKalle Valo u32 status;
140205491d2cSKalle Valo
140305491d2cSKalle Valo spin_lock_irqsave(&wl->isr_lock, flags);
140405491d2cSKalle Valo status = brcms_c_intrsoff(wl->wlc);
140505491d2cSKalle Valo spin_unlock_irqrestore(&wl->isr_lock, flags);
140605491d2cSKalle Valo return status;
140705491d2cSKalle Valo }
140805491d2cSKalle Valo
brcms_intrsrestore(struct brcms_info * wl,u32 macintmask)140905491d2cSKalle Valo void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask)
141005491d2cSKalle Valo {
141105491d2cSKalle Valo unsigned long flags;
141205491d2cSKalle Valo
141305491d2cSKalle Valo spin_lock_irqsave(&wl->isr_lock, flags);
141405491d2cSKalle Valo brcms_c_intrsrestore(wl->wlc, macintmask);
141505491d2cSKalle Valo spin_unlock_irqrestore(&wl->isr_lock, flags);
141605491d2cSKalle Valo }
141705491d2cSKalle Valo
141805491d2cSKalle Valo /*
141905491d2cSKalle Valo * precondition: perimeter lock has been acquired
142005491d2cSKalle Valo */
brcms_up(struct brcms_info * wl)142105491d2cSKalle Valo int brcms_up(struct brcms_info *wl)
142205491d2cSKalle Valo {
142305491d2cSKalle Valo int error = 0;
142405491d2cSKalle Valo
142505491d2cSKalle Valo if (wl->pub->up)
142605491d2cSKalle Valo return 0;
142705491d2cSKalle Valo
142805491d2cSKalle Valo error = brcms_c_up(wl->wlc);
142905491d2cSKalle Valo
143005491d2cSKalle Valo return error;
143105491d2cSKalle Valo }
143205491d2cSKalle Valo
143305491d2cSKalle Valo /*
143405491d2cSKalle Valo * precondition: perimeter lock has been acquired
143505491d2cSKalle Valo */
brcms_down(struct brcms_info * wl)143605491d2cSKalle Valo void brcms_down(struct brcms_info *wl)
143740fb232cSJules Irenge __must_hold(&wl->lock)
143805491d2cSKalle Valo {
143905491d2cSKalle Valo uint callbacks, ret_val = 0;
144005491d2cSKalle Valo
144105491d2cSKalle Valo /* call common down function */
144205491d2cSKalle Valo ret_val = brcms_c_down(wl->wlc);
144305491d2cSKalle Valo callbacks = atomic_read(&wl->callbacks) - ret_val;
144405491d2cSKalle Valo
144505491d2cSKalle Valo /* wait for down callbacks to complete */
144605491d2cSKalle Valo spin_unlock_bh(&wl->lock);
144705491d2cSKalle Valo
144805491d2cSKalle Valo /* For HIGH_only driver, it's important to actually schedule other work,
144905491d2cSKalle Valo * not just spin wait since everything runs at schedule level
145005491d2cSKalle Valo */
145105491d2cSKalle Valo SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000);
145205491d2cSKalle Valo
145305491d2cSKalle Valo spin_lock_bh(&wl->lock);
145405491d2cSKalle Valo }
145505491d2cSKalle Valo
145605491d2cSKalle Valo /*
145705491d2cSKalle Valo * precondition: perimeter lock is not acquired
145805491d2cSKalle Valo */
_brcms_timer(struct work_struct * work)145905491d2cSKalle Valo static void _brcms_timer(struct work_struct *work)
146005491d2cSKalle Valo {
146105491d2cSKalle Valo struct brcms_timer *t = container_of(work, struct brcms_timer,
146205491d2cSKalle Valo dly_wrk.work);
146305491d2cSKalle Valo
146405491d2cSKalle Valo spin_lock_bh(&t->wl->lock);
146505491d2cSKalle Valo
146605491d2cSKalle Valo if (t->set) {
146705491d2cSKalle Valo if (t->periodic) {
146805491d2cSKalle Valo atomic_inc(&t->wl->callbacks);
146905491d2cSKalle Valo ieee80211_queue_delayed_work(t->wl->pub->ieee_hw,
147005491d2cSKalle Valo &t->dly_wrk,
147105491d2cSKalle Valo msecs_to_jiffies(t->ms));
147205491d2cSKalle Valo } else {
147305491d2cSKalle Valo t->set = false;
147405491d2cSKalle Valo }
147505491d2cSKalle Valo
147605491d2cSKalle Valo t->fn(t->arg);
147705491d2cSKalle Valo }
147805491d2cSKalle Valo
147905491d2cSKalle Valo atomic_dec(&t->wl->callbacks);
148005491d2cSKalle Valo
148105491d2cSKalle Valo spin_unlock_bh(&t->wl->lock);
148205491d2cSKalle Valo }
148305491d2cSKalle Valo
148405491d2cSKalle Valo /*
148505491d2cSKalle Valo * Adds a timer to the list. Caller supplies a timer function.
148605491d2cSKalle Valo * Is called from wlc.
148705491d2cSKalle Valo *
148805491d2cSKalle Valo * precondition: perimeter lock has been acquired
148905491d2cSKalle Valo */
brcms_init_timer(struct brcms_info * wl,void (* fn)(void * arg),void * arg,const char * name)149005491d2cSKalle Valo struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
149105491d2cSKalle Valo void (*fn) (void *arg),
149205491d2cSKalle Valo void *arg, const char *name)
149305491d2cSKalle Valo {
149405491d2cSKalle Valo struct brcms_timer *t;
149505491d2cSKalle Valo
149605491d2cSKalle Valo t = kzalloc(sizeof(struct brcms_timer), GFP_ATOMIC);
149705491d2cSKalle Valo if (!t)
149805491d2cSKalle Valo return NULL;
149905491d2cSKalle Valo
150005491d2cSKalle Valo INIT_DELAYED_WORK(&t->dly_wrk, _brcms_timer);
150105491d2cSKalle Valo t->wl = wl;
150205491d2cSKalle Valo t->fn = fn;
150305491d2cSKalle Valo t->arg = arg;
150405491d2cSKalle Valo t->next = wl->timers;
150505491d2cSKalle Valo wl->timers = t;
150605491d2cSKalle Valo
150705491d2cSKalle Valo #ifdef DEBUG
150805491d2cSKalle Valo t->name = kstrdup(name, GFP_ATOMIC);
150905491d2cSKalle Valo #endif
151005491d2cSKalle Valo
151105491d2cSKalle Valo return t;
151205491d2cSKalle Valo }
151305491d2cSKalle Valo
151405491d2cSKalle Valo /*
151505491d2cSKalle Valo * adds only the kernel timer since it's going to be more accurate
151605491d2cSKalle Valo * as well as it's easier to make it periodic
151705491d2cSKalle Valo *
151805491d2cSKalle Valo * precondition: perimeter lock has been acquired
151905491d2cSKalle Valo */
brcms_add_timer(struct brcms_timer * t,uint ms,int periodic)152005491d2cSKalle Valo void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic)
152105491d2cSKalle Valo {
152205491d2cSKalle Valo struct ieee80211_hw *hw = t->wl->pub->ieee_hw;
152305491d2cSKalle Valo
152405491d2cSKalle Valo #ifdef DEBUG
152505491d2cSKalle Valo if (t->set)
152605491d2cSKalle Valo brcms_dbg_info(t->wl->wlc->hw->d11core,
152705491d2cSKalle Valo "%s: Already set. Name: %s, per %d\n",
152805491d2cSKalle Valo __func__, t->name, periodic);
152905491d2cSKalle Valo #endif
153005491d2cSKalle Valo t->ms = ms;
153105491d2cSKalle Valo t->periodic = (bool) periodic;
153205491d2cSKalle Valo if (!t->set) {
153305491d2cSKalle Valo t->set = true;
153405491d2cSKalle Valo atomic_inc(&t->wl->callbacks);
153505491d2cSKalle Valo }
153605491d2cSKalle Valo
153705491d2cSKalle Valo ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms));
153805491d2cSKalle Valo }
153905491d2cSKalle Valo
154005491d2cSKalle Valo /*
154105491d2cSKalle Valo * return true if timer successfully deleted, false if still pending
154205491d2cSKalle Valo *
154305491d2cSKalle Valo * precondition: perimeter lock has been acquired
154405491d2cSKalle Valo */
brcms_del_timer(struct brcms_timer * t)154505491d2cSKalle Valo bool brcms_del_timer(struct brcms_timer *t)
154605491d2cSKalle Valo {
154705491d2cSKalle Valo if (t->set) {
154805491d2cSKalle Valo t->set = false;
154905491d2cSKalle Valo if (!cancel_delayed_work(&t->dly_wrk))
155005491d2cSKalle Valo return false;
155105491d2cSKalle Valo
155205491d2cSKalle Valo atomic_dec(&t->wl->callbacks);
155305491d2cSKalle Valo }
155405491d2cSKalle Valo
155505491d2cSKalle Valo return true;
155605491d2cSKalle Valo }
155705491d2cSKalle Valo
155805491d2cSKalle Valo /*
155905491d2cSKalle Valo * precondition: perimeter lock has been acquired
156005491d2cSKalle Valo */
brcms_free_timer(struct brcms_timer * t)156105491d2cSKalle Valo void brcms_free_timer(struct brcms_timer *t)
156205491d2cSKalle Valo {
156305491d2cSKalle Valo struct brcms_info *wl = t->wl;
156405491d2cSKalle Valo struct brcms_timer *tmp;
156505491d2cSKalle Valo
156605491d2cSKalle Valo /* delete the timer in case it is active */
156705491d2cSKalle Valo brcms_del_timer(t);
156805491d2cSKalle Valo
156905491d2cSKalle Valo if (wl->timers == t) {
157005491d2cSKalle Valo wl->timers = wl->timers->next;
157105491d2cSKalle Valo #ifdef DEBUG
157205491d2cSKalle Valo kfree(t->name);
157305491d2cSKalle Valo #endif
157405491d2cSKalle Valo kfree(t);
157505491d2cSKalle Valo return;
157605491d2cSKalle Valo
157705491d2cSKalle Valo }
157805491d2cSKalle Valo
157905491d2cSKalle Valo tmp = wl->timers;
158005491d2cSKalle Valo while (tmp) {
158105491d2cSKalle Valo if (tmp->next == t) {
158205491d2cSKalle Valo tmp->next = t->next;
158305491d2cSKalle Valo #ifdef DEBUG
158405491d2cSKalle Valo kfree(t->name);
158505491d2cSKalle Valo #endif
158605491d2cSKalle Valo kfree(t);
158705491d2cSKalle Valo return;
158805491d2cSKalle Valo }
158905491d2cSKalle Valo tmp = tmp->next;
159005491d2cSKalle Valo }
159105491d2cSKalle Valo
159205491d2cSKalle Valo }
159305491d2cSKalle Valo
159405491d2cSKalle Valo /*
1595b1c2d0f2STakashi Iwai * precondition: no locking required
159605491d2cSKalle Valo */
brcms_ucode_init_buf(struct brcms_info * wl,void ** pbuf,u32 idx)159705491d2cSKalle Valo int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx)
159805491d2cSKalle Valo {
159905491d2cSKalle Valo int i, entry;
160005491d2cSKalle Valo const u8 *pdata;
160105491d2cSKalle Valo struct firmware_hdr *hdr;
160205491d2cSKalle Valo for (i = 0; i < wl->fw.fw_cnt; i++) {
160305491d2cSKalle Valo hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
160405491d2cSKalle Valo for (entry = 0; entry < wl->fw.hdr_num_entries[i];
160505491d2cSKalle Valo entry++, hdr++) {
160605491d2cSKalle Valo u32 len = le32_to_cpu(hdr->len);
160705491d2cSKalle Valo if (le32_to_cpu(hdr->idx) == idx) {
160805491d2cSKalle Valo pdata = wl->fw.fw_bin[i]->data +
160905491d2cSKalle Valo le32_to_cpu(hdr->offset);
16106c3efbe7STakashi Iwai *pbuf = kvmalloc(len, GFP_KERNEL);
161105491d2cSKalle Valo if (*pbuf == NULL)
161205491d2cSKalle Valo goto fail;
16136c3efbe7STakashi Iwai memcpy(*pbuf, pdata, len);
161405491d2cSKalle Valo return 0;
161505491d2cSKalle Valo }
161605491d2cSKalle Valo }
161705491d2cSKalle Valo }
161805491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core,
161905491d2cSKalle Valo "ERROR: ucode buf tag:%d can not be found!\n", idx);
162005491d2cSKalle Valo *pbuf = NULL;
162105491d2cSKalle Valo fail:
162205491d2cSKalle Valo return -ENODATA;
162305491d2cSKalle Valo }
162405491d2cSKalle Valo
162505491d2cSKalle Valo /*
162605491d2cSKalle Valo * Precondition: Since this function is called in brcms_bcma_probe() context,
162705491d2cSKalle Valo * no locking is required.
162805491d2cSKalle Valo */
brcms_ucode_init_uint(struct brcms_info * wl,size_t * n_bytes,u32 idx)162905491d2cSKalle Valo int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx)
163005491d2cSKalle Valo {
163105491d2cSKalle Valo int i, entry;
163205491d2cSKalle Valo const u8 *pdata;
163305491d2cSKalle Valo struct firmware_hdr *hdr;
163405491d2cSKalle Valo for (i = 0; i < wl->fw.fw_cnt; i++) {
163505491d2cSKalle Valo hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
163605491d2cSKalle Valo for (entry = 0; entry < wl->fw.hdr_num_entries[i];
163705491d2cSKalle Valo entry++, hdr++) {
163805491d2cSKalle Valo if (le32_to_cpu(hdr->idx) == idx) {
163905491d2cSKalle Valo pdata = wl->fw.fw_bin[i]->data +
164005491d2cSKalle Valo le32_to_cpu(hdr->offset);
164105491d2cSKalle Valo if (le32_to_cpu(hdr->len) != 4) {
164205491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core,
164305491d2cSKalle Valo "ERROR: fw hdr len\n");
164405491d2cSKalle Valo return -ENOMSG;
164505491d2cSKalle Valo }
164605491d2cSKalle Valo *n_bytes = le32_to_cpu(*((__le32 *) pdata));
164705491d2cSKalle Valo return 0;
164805491d2cSKalle Valo }
164905491d2cSKalle Valo }
165005491d2cSKalle Valo }
165105491d2cSKalle Valo brcms_err(wl->wlc->hw->d11core,
165205491d2cSKalle Valo "ERROR: ucode tag:%d can not be found!\n", idx);
165305491d2cSKalle Valo return -ENOMSG;
165405491d2cSKalle Valo }
165505491d2cSKalle Valo
165605491d2cSKalle Valo /*
165705491d2cSKalle Valo * precondition: can both be called locked and unlocked
165805491d2cSKalle Valo */
brcms_ucode_free_buf(void * p)165905491d2cSKalle Valo void brcms_ucode_free_buf(void *p)
166005491d2cSKalle Valo {
16616c3efbe7STakashi Iwai kvfree(p);
166205491d2cSKalle Valo }
166305491d2cSKalle Valo
166405491d2cSKalle Valo /*
166505491d2cSKalle Valo * checks validity of all firmware images loaded from user space
166605491d2cSKalle Valo *
166705491d2cSKalle Valo * Precondition: Since this function is called in brcms_bcma_probe() context,
166805491d2cSKalle Valo * no locking is required.
166905491d2cSKalle Valo */
brcms_check_firmwares(struct brcms_info * wl)167005491d2cSKalle Valo int brcms_check_firmwares(struct brcms_info *wl)
167105491d2cSKalle Valo {
167205491d2cSKalle Valo int i;
167305491d2cSKalle Valo int entry;
167405491d2cSKalle Valo int rc = 0;
167505491d2cSKalle Valo const struct firmware *fw;
167605491d2cSKalle Valo const struct firmware *fw_hdr;
167705491d2cSKalle Valo struct firmware_hdr *ucode_hdr;
167805491d2cSKalle Valo for (i = 0; i < MAX_FW_IMAGES && rc == 0; i++) {
167905491d2cSKalle Valo fw = wl->fw.fw_bin[i];
168005491d2cSKalle Valo fw_hdr = wl->fw.fw_hdr[i];
168105491d2cSKalle Valo if (fw == NULL && fw_hdr == NULL) {
168205491d2cSKalle Valo break;
168305491d2cSKalle Valo } else if (fw == NULL || fw_hdr == NULL) {
168405491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: invalid bin/hdr fw\n",
168505491d2cSKalle Valo __func__);
168605491d2cSKalle Valo rc = -EBADF;
168705491d2cSKalle Valo } else if (fw_hdr->size % sizeof(struct firmware_hdr)) {
168805491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: non integral fw hdr file "
168905491d2cSKalle Valo "size %zu/%zu\n", __func__, fw_hdr->size,
169005491d2cSKalle Valo sizeof(struct firmware_hdr));
169105491d2cSKalle Valo rc = -EBADF;
169205491d2cSKalle Valo } else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) {
169305491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: out of bounds fw file size %zu\n",
169405491d2cSKalle Valo __func__, fw->size);
169505491d2cSKalle Valo rc = -EBADF;
169605491d2cSKalle Valo } else {
169705491d2cSKalle Valo /* check if ucode section overruns firmware image */
169805491d2cSKalle Valo ucode_hdr = (struct firmware_hdr *)fw_hdr->data;
169905491d2cSKalle Valo for (entry = 0; entry < wl->fw.hdr_num_entries[i] &&
170005491d2cSKalle Valo !rc; entry++, ucode_hdr++) {
170105491d2cSKalle Valo if (le32_to_cpu(ucode_hdr->offset) +
170205491d2cSKalle Valo le32_to_cpu(ucode_hdr->len) >
170305491d2cSKalle Valo fw->size) {
170405491d2cSKalle Valo wiphy_err(wl->wiphy,
170505491d2cSKalle Valo "%s: conflicting bin/hdr\n",
170605491d2cSKalle Valo __func__);
170705491d2cSKalle Valo rc = -EBADF;
170805491d2cSKalle Valo }
170905491d2cSKalle Valo }
171005491d2cSKalle Valo }
171105491d2cSKalle Valo }
171205491d2cSKalle Valo if (rc == 0 && wl->fw.fw_cnt != i) {
171305491d2cSKalle Valo wiphy_err(wl->wiphy, "%s: invalid fw_cnt=%d\n", __func__,
171405491d2cSKalle Valo wl->fw.fw_cnt);
171505491d2cSKalle Valo rc = -EBADF;
171605491d2cSKalle Valo }
171705491d2cSKalle Valo return rc;
171805491d2cSKalle Valo }
171905491d2cSKalle Valo
172005491d2cSKalle Valo /*
172105491d2cSKalle Valo * precondition: perimeter lock has been acquired
172205491d2cSKalle Valo */
brcms_rfkill_set_hw_state(struct brcms_info * wl)172305491d2cSKalle Valo bool brcms_rfkill_set_hw_state(struct brcms_info *wl)
17242fe5efb8SJules Irenge __must_hold(&wl->lock)
172505491d2cSKalle Valo {
172605491d2cSKalle Valo bool blocked = brcms_c_check_radio_disabled(wl->wlc);
172705491d2cSKalle Valo
172805491d2cSKalle Valo spin_unlock_bh(&wl->lock);
172905491d2cSKalle Valo wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
173005491d2cSKalle Valo if (blocked)
173105491d2cSKalle Valo wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy);
173205491d2cSKalle Valo spin_lock_bh(&wl->lock);
173305491d2cSKalle Valo return blocked;
173405491d2cSKalle Valo }
1735