xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c (revision ca2478a7d974f38d29d27acb42a952c7f168916e)
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