xref: /freebsd/sys/contrib/dev/broadcom/brcm80211/brcmsmac/mac80211_if.c (revision b4c3e9b5b09c829b4135aff738bd2893ed052377)
1*b4c3e9b5SBjoern A. Zeeb /*
2*b4c3e9b5SBjoern A. Zeeb  * Copyright (c) 2010 Broadcom Corporation
3*b4c3e9b5SBjoern A. Zeeb  * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
4*b4c3e9b5SBjoern A. Zeeb  *
5*b4c3e9b5SBjoern A. Zeeb  * Permission to use, copy, modify, and/or distribute this software for any
6*b4c3e9b5SBjoern A. Zeeb  * purpose with or without fee is hereby granted, provided that the above
7*b4c3e9b5SBjoern A. Zeeb  * copyright notice and this permission notice appear in all copies.
8*b4c3e9b5SBjoern A. Zeeb  *
9*b4c3e9b5SBjoern A. Zeeb  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*b4c3e9b5SBjoern A. Zeeb  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*b4c3e9b5SBjoern A. Zeeb  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12*b4c3e9b5SBjoern A. Zeeb  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*b4c3e9b5SBjoern A. Zeeb  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
14*b4c3e9b5SBjoern A. Zeeb  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15*b4c3e9b5SBjoern A. Zeeb  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*b4c3e9b5SBjoern A. Zeeb  */
17*b4c3e9b5SBjoern A. Zeeb 
18*b4c3e9b5SBjoern A. Zeeb #define __UNDEF_NO_VERSION__
19*b4c3e9b5SBjoern A. Zeeb #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20*b4c3e9b5SBjoern A. Zeeb 
21*b4c3e9b5SBjoern A. Zeeb #include <linux/etherdevice.h>
22*b4c3e9b5SBjoern A. Zeeb #include <linux/sched.h>
23*b4c3e9b5SBjoern A. Zeeb #include <linux/firmware.h>
24*b4c3e9b5SBjoern A. Zeeb #include <linux/interrupt.h>
25*b4c3e9b5SBjoern A. Zeeb #include <linux/module.h>
26*b4c3e9b5SBjoern A. Zeeb #include <linux/bcma/bcma.h>
27*b4c3e9b5SBjoern A. Zeeb #include <linux/string_choices.h>
28*b4c3e9b5SBjoern A. Zeeb #include <net/mac80211.h>
29*b4c3e9b5SBjoern A. Zeeb #include <defs.h>
30*b4c3e9b5SBjoern A. Zeeb #include "phy/phy_int.h"
31*b4c3e9b5SBjoern A. Zeeb #include "d11.h"
32*b4c3e9b5SBjoern A. Zeeb #include "channel.h"
33*b4c3e9b5SBjoern A. Zeeb #include "scb.h"
34*b4c3e9b5SBjoern A. Zeeb #include "pub.h"
35*b4c3e9b5SBjoern A. Zeeb #include "ucode_loader.h"
36*b4c3e9b5SBjoern A. Zeeb #include "mac80211_if.h"
37*b4c3e9b5SBjoern A. Zeeb #include "main.h"
38*b4c3e9b5SBjoern A. Zeeb #include "debug.h"
39*b4c3e9b5SBjoern A. Zeeb #include "led.h"
40*b4c3e9b5SBjoern A. Zeeb 
41*b4c3e9b5SBjoern A. Zeeb #define N_TX_QUEUES	4 /* #tx queues on mac80211<->driver interface */
42*b4c3e9b5SBjoern A. Zeeb #define BRCMS_FLUSH_TIMEOUT	500 /* msec */
43*b4c3e9b5SBjoern A. Zeeb 
44*b4c3e9b5SBjoern A. Zeeb /* Flags we support */
45*b4c3e9b5SBjoern A. Zeeb #define MAC_FILTERS (FIF_ALLMULTI | \
46*b4c3e9b5SBjoern A. Zeeb 	FIF_FCSFAIL | \
47*b4c3e9b5SBjoern A. Zeeb 	FIF_CONTROL | \
48*b4c3e9b5SBjoern A. Zeeb 	FIF_OTHER_BSS | \
49*b4c3e9b5SBjoern A. Zeeb 	FIF_BCN_PRBRESP_PROMISC | \
50*b4c3e9b5SBjoern A. Zeeb 	FIF_PSPOLL)
51*b4c3e9b5SBjoern A. Zeeb 
52*b4c3e9b5SBjoern A. Zeeb #define CHAN2GHZ(channel, frequency, chflags)  { \
53*b4c3e9b5SBjoern A. Zeeb 	.band = NL80211_BAND_2GHZ, \
54*b4c3e9b5SBjoern A. Zeeb 	.center_freq = (frequency), \
55*b4c3e9b5SBjoern A. Zeeb 	.hw_value = (channel), \
56*b4c3e9b5SBjoern A. Zeeb 	.flags = chflags, \
57*b4c3e9b5SBjoern A. Zeeb 	.max_antenna_gain = 0, \
58*b4c3e9b5SBjoern A. Zeeb 	.max_power = 19, \
59*b4c3e9b5SBjoern A. Zeeb }
60*b4c3e9b5SBjoern A. Zeeb 
61*b4c3e9b5SBjoern A. Zeeb #define CHAN5GHZ(channel, chflags)  { \
62*b4c3e9b5SBjoern A. Zeeb 	.band = NL80211_BAND_5GHZ, \
63*b4c3e9b5SBjoern A. Zeeb 	.center_freq = 5000 + 5*(channel), \
64*b4c3e9b5SBjoern A. Zeeb 	.hw_value = (channel), \
65*b4c3e9b5SBjoern A. Zeeb 	.flags = chflags, \
66*b4c3e9b5SBjoern A. Zeeb 	.max_antenna_gain = 0, \
67*b4c3e9b5SBjoern A. Zeeb 	.max_power = 21, \
68*b4c3e9b5SBjoern A. Zeeb }
69*b4c3e9b5SBjoern A. Zeeb 
70*b4c3e9b5SBjoern A. Zeeb #define RATE(rate100m, _flags) { \
71*b4c3e9b5SBjoern A. Zeeb 	.bitrate = (rate100m), \
72*b4c3e9b5SBjoern A. Zeeb 	.flags = (_flags), \
73*b4c3e9b5SBjoern A. Zeeb 	.hw_value = (rate100m / 5), \
74*b4c3e9b5SBjoern A. Zeeb }
75*b4c3e9b5SBjoern A. Zeeb 
76*b4c3e9b5SBjoern A. Zeeb struct firmware_hdr {
77*b4c3e9b5SBjoern A. Zeeb 	__le32 offset;
78*b4c3e9b5SBjoern A. Zeeb 	__le32 len;
79*b4c3e9b5SBjoern A. Zeeb 	__le32 idx;
80*b4c3e9b5SBjoern A. Zeeb };
81*b4c3e9b5SBjoern A. Zeeb 
82*b4c3e9b5SBjoern A. Zeeb static const char * const brcms_firmwares[MAX_FW_IMAGES] = {
83*b4c3e9b5SBjoern A. Zeeb 	"brcm/bcm43xx",
84*b4c3e9b5SBjoern A. Zeeb 	NULL
85*b4c3e9b5SBjoern A. Zeeb };
86*b4c3e9b5SBjoern A. Zeeb 
87*b4c3e9b5SBjoern A. Zeeb static int n_adapters_found;
88*b4c3e9b5SBjoern A. Zeeb 
89*b4c3e9b5SBjoern A. Zeeb MODULE_AUTHOR("Broadcom Corporation");
90*b4c3e9b5SBjoern A. Zeeb MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver.");
91*b4c3e9b5SBjoern A. Zeeb MODULE_LICENSE("Dual BSD/GPL");
92*b4c3e9b5SBjoern A. Zeeb /* This needs to be adjusted when brcms_firmwares changes */
93*b4c3e9b5SBjoern A. Zeeb MODULE_FIRMWARE("brcm/bcm43xx-0.fw");
94*b4c3e9b5SBjoern A. Zeeb MODULE_FIRMWARE("brcm/bcm43xx_hdr-0.fw");
95*b4c3e9b5SBjoern A. Zeeb 
96*b4c3e9b5SBjoern A. Zeeb /* recognized BCMA Core IDs */
97*b4c3e9b5SBjoern A. Zeeb static struct bcma_device_id brcms_coreid_table[] = {
98*b4c3e9b5SBjoern A. Zeeb 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 17, BCMA_ANY_CLASS),
99*b4c3e9b5SBjoern A. Zeeb 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 23, BCMA_ANY_CLASS),
100*b4c3e9b5SBjoern A. Zeeb 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 24, BCMA_ANY_CLASS),
101*b4c3e9b5SBjoern A. Zeeb 	{},
102*b4c3e9b5SBjoern A. Zeeb };
103*b4c3e9b5SBjoern A. Zeeb MODULE_DEVICE_TABLE(bcma, brcms_coreid_table);
104*b4c3e9b5SBjoern A. Zeeb 
105*b4c3e9b5SBjoern A. Zeeb #if defined(CONFIG_BRCMDBG)
106*b4c3e9b5SBjoern A. Zeeb /*
107*b4c3e9b5SBjoern A. Zeeb  * Module parameter for setting the debug message level. Available
108*b4c3e9b5SBjoern A. Zeeb  * flags are specified by the BRCM_DL_* macros in
109*b4c3e9b5SBjoern A. Zeeb  * drivers/net/wireless/brcm80211/include/defs.h.
110*b4c3e9b5SBjoern A. Zeeb  */
111*b4c3e9b5SBjoern A. Zeeb module_param_named(debug, brcm_msg_level, uint, 0644);
112*b4c3e9b5SBjoern A. Zeeb #endif
113*b4c3e9b5SBjoern A. Zeeb 
114*b4c3e9b5SBjoern A. Zeeb static struct ieee80211_channel brcms_2ghz_chantable[] = {
115*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS),
116*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS),
117*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS),
118*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS),
119*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(5, 2432, 0),
120*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(6, 2437, 0),
121*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(7, 2442, 0),
122*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS),
123*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS),
124*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS),
125*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS),
126*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(12, 2467,
127*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR |
128*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_HT40PLUS),
129*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(13, 2472,
130*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR |
131*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_HT40PLUS),
132*b4c3e9b5SBjoern A. Zeeb 	CHAN2GHZ(14, 2484,
133*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR |
134*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS |
135*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_OFDM)
136*b4c3e9b5SBjoern A. Zeeb };
137*b4c3e9b5SBjoern A. Zeeb 
138*b4c3e9b5SBjoern A. Zeeb static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
139*b4c3e9b5SBjoern A. Zeeb 	/* UNII-1 */
140*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS),
141*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS),
142*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS),
143*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS),
144*b4c3e9b5SBjoern A. Zeeb 	/* UNII-2 */
145*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(52,
146*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
147*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
148*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(56,
149*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
150*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
151*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(60,
152*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
153*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
154*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(64,
155*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
156*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
157*b4c3e9b5SBjoern A. Zeeb 	/* MID */
158*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(100,
159*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
160*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
161*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(104,
162*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
163*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
164*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(108,
165*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
166*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
167*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(112,
168*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
169*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
170*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(116,
171*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
172*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
173*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(120,
174*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
175*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
176*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(124,
177*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
178*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
179*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(128,
180*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
181*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
182*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(132,
183*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
184*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS),
185*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(136,
186*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
187*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS),
188*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(140,
189*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_RADAR |
190*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS |
191*b4c3e9b5SBjoern A. Zeeb 		 IEEE80211_CHAN_NO_HT40MINUS),
192*b4c3e9b5SBjoern A. Zeeb 	/* UNII-3 */
193*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS),
194*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS),
195*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS),
196*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS),
197*b4c3e9b5SBjoern A. Zeeb 	CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
198*b4c3e9b5SBjoern A. Zeeb };
199*b4c3e9b5SBjoern A. Zeeb 
200*b4c3e9b5SBjoern A. Zeeb /*
201*b4c3e9b5SBjoern A. Zeeb  * The rate table is used for both 2.4G and 5G rates. The
202*b4c3e9b5SBjoern A. Zeeb  * latter being a subset as it does not support CCK rates.
203*b4c3e9b5SBjoern A. Zeeb  */
204*b4c3e9b5SBjoern A. Zeeb static struct ieee80211_rate legacy_ratetable[] = {
205*b4c3e9b5SBjoern A. Zeeb 	RATE(10, 0),
206*b4c3e9b5SBjoern A. Zeeb 	RATE(20, IEEE80211_RATE_SHORT_PREAMBLE),
207*b4c3e9b5SBjoern A. Zeeb 	RATE(55, IEEE80211_RATE_SHORT_PREAMBLE),
208*b4c3e9b5SBjoern A. Zeeb 	RATE(110, IEEE80211_RATE_SHORT_PREAMBLE),
209*b4c3e9b5SBjoern A. Zeeb 	RATE(60, 0),
210*b4c3e9b5SBjoern A. Zeeb 	RATE(90, 0),
211*b4c3e9b5SBjoern A. Zeeb 	RATE(120, 0),
212*b4c3e9b5SBjoern A. Zeeb 	RATE(180, 0),
213*b4c3e9b5SBjoern A. Zeeb 	RATE(240, 0),
214*b4c3e9b5SBjoern A. Zeeb 	RATE(360, 0),
215*b4c3e9b5SBjoern A. Zeeb 	RATE(480, 0),
216*b4c3e9b5SBjoern A. Zeeb 	RATE(540, 0),
217*b4c3e9b5SBjoern A. Zeeb };
218*b4c3e9b5SBjoern A. Zeeb 
219*b4c3e9b5SBjoern A. Zeeb static const struct ieee80211_supported_band brcms_band_2GHz_nphy_template = {
220*b4c3e9b5SBjoern A. Zeeb 	.band = NL80211_BAND_2GHZ,
221*b4c3e9b5SBjoern A. Zeeb 	.channels = brcms_2ghz_chantable,
222*b4c3e9b5SBjoern A. Zeeb 	.n_channels = ARRAY_SIZE(brcms_2ghz_chantable),
223*b4c3e9b5SBjoern A. Zeeb 	.bitrates = legacy_ratetable,
224*b4c3e9b5SBjoern A. Zeeb 	.n_bitrates = ARRAY_SIZE(legacy_ratetable),
225*b4c3e9b5SBjoern A. Zeeb 	.ht_cap = {
226*b4c3e9b5SBjoern A. Zeeb 		   /* from include/linux/ieee80211.h */
227*b4c3e9b5SBjoern A. Zeeb 		   .cap = IEEE80211_HT_CAP_GRN_FLD |
228*b4c3e9b5SBjoern A. Zeeb 			  IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40,
229*b4c3e9b5SBjoern A. Zeeb 		   .ht_supported = true,
230*b4c3e9b5SBjoern A. Zeeb 		   .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
231*b4c3e9b5SBjoern A. Zeeb 		   .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
232*b4c3e9b5SBjoern A. Zeeb 		   .mcs = {
233*b4c3e9b5SBjoern A. Zeeb 			   /* placeholders for now */
234*b4c3e9b5SBjoern A. Zeeb 			   .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
235*b4c3e9b5SBjoern A. Zeeb 			   .rx_highest = cpu_to_le16(500),
236*b4c3e9b5SBjoern A. Zeeb 			   .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
237*b4c3e9b5SBjoern A. Zeeb 		   }
238*b4c3e9b5SBjoern A. Zeeb };
239*b4c3e9b5SBjoern A. Zeeb 
240*b4c3e9b5SBjoern A. Zeeb static const struct ieee80211_supported_band brcms_band_5GHz_nphy_template = {
241*b4c3e9b5SBjoern A. Zeeb 	.band = NL80211_BAND_5GHZ,
242*b4c3e9b5SBjoern A. Zeeb 	.channels = brcms_5ghz_nphy_chantable,
243*b4c3e9b5SBjoern A. Zeeb 	.n_channels = ARRAY_SIZE(brcms_5ghz_nphy_chantable),
244*b4c3e9b5SBjoern A. Zeeb 	.bitrates = legacy_ratetable + BRCMS_LEGACY_5G_RATE_OFFSET,
245*b4c3e9b5SBjoern A. Zeeb 	.n_bitrates = ARRAY_SIZE(legacy_ratetable) -
246*b4c3e9b5SBjoern A. Zeeb 			BRCMS_LEGACY_5G_RATE_OFFSET,
247*b4c3e9b5SBjoern A. Zeeb 	.ht_cap = {
248*b4c3e9b5SBjoern A. Zeeb 		   .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 |
249*b4c3e9b5SBjoern A. Zeeb 			  IEEE80211_HT_CAP_SGI_40,
250*b4c3e9b5SBjoern A. Zeeb 		   .ht_supported = true,
251*b4c3e9b5SBjoern A. Zeeb 		   .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
252*b4c3e9b5SBjoern A. Zeeb 		   .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
253*b4c3e9b5SBjoern A. Zeeb 		   .mcs = {
254*b4c3e9b5SBjoern A. Zeeb 			   /* placeholders for now */
255*b4c3e9b5SBjoern A. Zeeb 			   .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
256*b4c3e9b5SBjoern A. Zeeb 			   .rx_highest = cpu_to_le16(500),
257*b4c3e9b5SBjoern A. Zeeb 			   .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
258*b4c3e9b5SBjoern A. Zeeb 		   }
259*b4c3e9b5SBjoern A. Zeeb };
260*b4c3e9b5SBjoern A. Zeeb 
261*b4c3e9b5SBjoern A. Zeeb /* flags the given rate in rateset as requested */
brcms_set_basic_rate(struct brcm_rateset * rs,u16 rate,bool is_br)262*b4c3e9b5SBjoern A. Zeeb static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br)
263*b4c3e9b5SBjoern A. Zeeb {
264*b4c3e9b5SBjoern A. Zeeb 	u32 i;
265*b4c3e9b5SBjoern A. Zeeb 
266*b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < rs->count; i++) {
267*b4c3e9b5SBjoern A. Zeeb 		if (rate != (rs->rates[i] & 0x7f))
268*b4c3e9b5SBjoern A. Zeeb 			continue;
269*b4c3e9b5SBjoern A. Zeeb 
270*b4c3e9b5SBjoern A. Zeeb 		if (is_br)
271*b4c3e9b5SBjoern A. Zeeb 			rs->rates[i] |= BRCMS_RATE_FLAG;
272*b4c3e9b5SBjoern A. Zeeb 		else
273*b4c3e9b5SBjoern A. Zeeb 			rs->rates[i] &= BRCMS_RATE_MASK;
274*b4c3e9b5SBjoern A. Zeeb 		return;
275*b4c3e9b5SBjoern A. Zeeb 	}
276*b4c3e9b5SBjoern A. Zeeb }
277*b4c3e9b5SBjoern A. Zeeb 
278*b4c3e9b5SBjoern A. Zeeb /*
279*b4c3e9b5SBjoern A. Zeeb  * This function frees the WL per-device resources.
280*b4c3e9b5SBjoern A. Zeeb  *
281*b4c3e9b5SBjoern A. Zeeb  * This function frees resources owned by the WL device pointed to
282*b4c3e9b5SBjoern A. Zeeb  * by the wl parameter.
283*b4c3e9b5SBjoern A. Zeeb  *
284*b4c3e9b5SBjoern A. Zeeb  * precondition: can both be called locked and unlocked
285*b4c3e9b5SBjoern A. Zeeb  */
brcms_free(struct brcms_info * wl)286*b4c3e9b5SBjoern A. Zeeb static void brcms_free(struct brcms_info *wl)
287*b4c3e9b5SBjoern A. Zeeb {
288*b4c3e9b5SBjoern A. Zeeb 	struct brcms_timer *t, *next;
289*b4c3e9b5SBjoern A. Zeeb 
290*b4c3e9b5SBjoern A. Zeeb 	/* free ucode data */
291*b4c3e9b5SBjoern A. Zeeb 	if (wl->fw.fw_cnt)
292*b4c3e9b5SBjoern A. Zeeb 		brcms_ucode_data_free(&wl->ucode);
293*b4c3e9b5SBjoern A. Zeeb 	if (wl->irq)
294*b4c3e9b5SBjoern A. Zeeb 		free_irq(wl->irq, wl);
295*b4c3e9b5SBjoern A. Zeeb 
296*b4c3e9b5SBjoern A. Zeeb 	/* kill dpc */
297*b4c3e9b5SBjoern A. Zeeb 	tasklet_kill(&wl->tasklet);
298*b4c3e9b5SBjoern A. Zeeb 
299*b4c3e9b5SBjoern A. Zeeb 	if (wl->pub) {
300*b4c3e9b5SBjoern A. Zeeb 		brcms_debugfs_detach(wl->pub);
301*b4c3e9b5SBjoern A. Zeeb 		brcms_c_module_unregister(wl->pub, "linux", wl);
302*b4c3e9b5SBjoern A. Zeeb 	}
303*b4c3e9b5SBjoern A. Zeeb 
304*b4c3e9b5SBjoern A. Zeeb 	/* free common resources */
305*b4c3e9b5SBjoern A. Zeeb 	if (wl->wlc) {
306*b4c3e9b5SBjoern A. Zeeb 		brcms_c_detach(wl->wlc);
307*b4c3e9b5SBjoern A. Zeeb 		wl->wlc = NULL;
308*b4c3e9b5SBjoern A. Zeeb 		wl->pub = NULL;
309*b4c3e9b5SBjoern A. Zeeb 	}
310*b4c3e9b5SBjoern A. Zeeb 
311*b4c3e9b5SBjoern A. Zeeb 	/* virtual interface deletion is deferred so we cannot spinwait */
312*b4c3e9b5SBjoern A. Zeeb 
313*b4c3e9b5SBjoern A. Zeeb 	/* wait for all pending callbacks to complete */
314*b4c3e9b5SBjoern A. Zeeb 	while (atomic_read(&wl->callbacks) > 0)
315*b4c3e9b5SBjoern A. Zeeb 		schedule();
316*b4c3e9b5SBjoern A. Zeeb 
317*b4c3e9b5SBjoern A. Zeeb 	/* free timers */
318*b4c3e9b5SBjoern A. Zeeb 	for (t = wl->timers; t; t = next) {
319*b4c3e9b5SBjoern A. Zeeb 		next = t->next;
320*b4c3e9b5SBjoern A. Zeeb #ifdef DEBUG
321*b4c3e9b5SBjoern A. Zeeb 		kfree(t->name);
322*b4c3e9b5SBjoern A. Zeeb #endif
323*b4c3e9b5SBjoern A. Zeeb 		kfree(t);
324*b4c3e9b5SBjoern A. Zeeb 	}
325*b4c3e9b5SBjoern A. Zeeb }
326*b4c3e9b5SBjoern A. Zeeb 
327*b4c3e9b5SBjoern A. Zeeb /*
328*b4c3e9b5SBjoern A. Zeeb * called from both kernel as from this kernel module (error flow on attach)
329*b4c3e9b5SBjoern A. Zeeb * precondition: perimeter lock is not acquired.
330*b4c3e9b5SBjoern A. Zeeb */
brcms_remove(struct bcma_device * pdev)331*b4c3e9b5SBjoern A. Zeeb static void brcms_remove(struct bcma_device *pdev)
332*b4c3e9b5SBjoern A. Zeeb {
333*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_hw *hw = bcma_get_drvdata(pdev);
334*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
335*b4c3e9b5SBjoern A. Zeeb 
336*b4c3e9b5SBjoern A. Zeeb 	if (wl->wlc) {
337*b4c3e9b5SBjoern A. Zeeb 		brcms_led_unregister(wl);
338*b4c3e9b5SBjoern A. Zeeb 		wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
339*b4c3e9b5SBjoern A. Zeeb 		wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
340*b4c3e9b5SBjoern A. Zeeb 		ieee80211_unregister_hw(hw);
341*b4c3e9b5SBjoern A. Zeeb 	}
342*b4c3e9b5SBjoern A. Zeeb 
343*b4c3e9b5SBjoern A. Zeeb 	brcms_free(wl);
344*b4c3e9b5SBjoern A. Zeeb 
345*b4c3e9b5SBjoern A. Zeeb 	bcma_set_drvdata(pdev, NULL);
346*b4c3e9b5SBjoern A. Zeeb 	ieee80211_free_hw(hw);
347*b4c3e9b5SBjoern A. Zeeb }
348*b4c3e9b5SBjoern A. Zeeb 
349*b4c3e9b5SBjoern A. Zeeb /*
350*b4c3e9b5SBjoern A. Zeeb  * Precondition: Since this function is called in brcms_pci_probe() context,
351*b4c3e9b5SBjoern A. Zeeb  * no locking is required.
352*b4c3e9b5SBjoern A. Zeeb  */
brcms_release_fw(struct brcms_info * wl)353*b4c3e9b5SBjoern A. Zeeb static void brcms_release_fw(struct brcms_info *wl)
354*b4c3e9b5SBjoern A. Zeeb {
355*b4c3e9b5SBjoern A. Zeeb 	int i;
356*b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < MAX_FW_IMAGES; i++) {
357*b4c3e9b5SBjoern A. Zeeb 		release_firmware(wl->fw.fw_bin[i]);
358*b4c3e9b5SBjoern A. Zeeb 		release_firmware(wl->fw.fw_hdr[i]);
359*b4c3e9b5SBjoern A. Zeeb 	}
360*b4c3e9b5SBjoern A. Zeeb }
361*b4c3e9b5SBjoern A. Zeeb 
362*b4c3e9b5SBjoern A. Zeeb /*
363*b4c3e9b5SBjoern A. Zeeb  * Precondition: Since this function is called in brcms_pci_probe() context,
364*b4c3e9b5SBjoern A. Zeeb  * no locking is required.
365*b4c3e9b5SBjoern A. Zeeb  */
brcms_request_fw(struct brcms_info * wl,struct bcma_device * pdev)366*b4c3e9b5SBjoern A. Zeeb static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev)
367*b4c3e9b5SBjoern A. Zeeb {
368*b4c3e9b5SBjoern A. Zeeb 	int status;
369*b4c3e9b5SBjoern A. Zeeb 	struct device *device = &pdev->dev;
370*b4c3e9b5SBjoern A. Zeeb 	char fw_name[100];
371*b4c3e9b5SBjoern A. Zeeb 	int i;
372*b4c3e9b5SBjoern A. Zeeb 
373*b4c3e9b5SBjoern A. Zeeb 	memset(&wl->fw, 0, sizeof(struct brcms_firmware));
374*b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < MAX_FW_IMAGES; i++) {
375*b4c3e9b5SBjoern A. Zeeb 		if (brcms_firmwares[i] == NULL)
376*b4c3e9b5SBjoern A. Zeeb 			break;
377*b4c3e9b5SBjoern A. Zeeb 		sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i],
378*b4c3e9b5SBjoern A. Zeeb 			UCODE_LOADER_API_VER);
379*b4c3e9b5SBjoern A. Zeeb 		status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
380*b4c3e9b5SBjoern A. Zeeb 		if (status) {
381*b4c3e9b5SBjoern A. Zeeb 			wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
382*b4c3e9b5SBjoern A. Zeeb 				  KBUILD_MODNAME, fw_name);
383*b4c3e9b5SBjoern A. Zeeb 			return status;
384*b4c3e9b5SBjoern A. Zeeb 		}
385*b4c3e9b5SBjoern A. Zeeb 		sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i],
386*b4c3e9b5SBjoern A. Zeeb 			UCODE_LOADER_API_VER);
387*b4c3e9b5SBjoern A. Zeeb 		status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
388*b4c3e9b5SBjoern A. Zeeb 		if (status) {
389*b4c3e9b5SBjoern A. Zeeb 			wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
390*b4c3e9b5SBjoern A. Zeeb 				  KBUILD_MODNAME, fw_name);
391*b4c3e9b5SBjoern A. Zeeb 			return status;
392*b4c3e9b5SBjoern A. Zeeb 		}
393*b4c3e9b5SBjoern A. Zeeb 		wl->fw.hdr_num_entries[i] =
394*b4c3e9b5SBjoern A. Zeeb 		    wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr));
395*b4c3e9b5SBjoern A. Zeeb 	}
396*b4c3e9b5SBjoern A. Zeeb 	wl->fw.fw_cnt = i;
397*b4c3e9b5SBjoern A. Zeeb 	status = brcms_ucode_data_init(wl, &wl->ucode);
398*b4c3e9b5SBjoern A. Zeeb 	brcms_release_fw(wl);
399*b4c3e9b5SBjoern A. Zeeb 	return status;
400*b4c3e9b5SBjoern A. Zeeb }
401*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * control,struct sk_buff * skb)402*b4c3e9b5SBjoern A. Zeeb static void brcms_ops_tx(struct ieee80211_hw *hw,
403*b4c3e9b5SBjoern A. Zeeb 			 struct ieee80211_tx_control *control,
404*b4c3e9b5SBjoern A. Zeeb 			 struct sk_buff *skb)
405*b4c3e9b5SBjoern A. Zeeb {
406*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
407*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
408*b4c3e9b5SBjoern A. Zeeb 
409*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
410*b4c3e9b5SBjoern A. Zeeb 	if (!wl->pub->up) {
411*b4c3e9b5SBjoern A. Zeeb 		brcms_err(wl->wlc->hw->d11core, "ops->tx called while down\n");
412*b4c3e9b5SBjoern A. Zeeb 		kfree_skb(skb);
413*b4c3e9b5SBjoern A. Zeeb 		goto done;
414*b4c3e9b5SBjoern A. Zeeb 	}
415*b4c3e9b5SBjoern A. Zeeb 	if (brcms_c_sendpkt_mac80211(wl->wlc, skb, hw))
416*b4c3e9b5SBjoern A. Zeeb 		tx_info->rate_driver_data[0] = control->sta;
417*b4c3e9b5SBjoern A. Zeeb  done:
418*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
419*b4c3e9b5SBjoern A. Zeeb }
420*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_start(struct ieee80211_hw * hw)421*b4c3e9b5SBjoern A. Zeeb static int brcms_ops_start(struct ieee80211_hw *hw)
422*b4c3e9b5SBjoern A. Zeeb {
423*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
424*b4c3e9b5SBjoern A. Zeeb 	bool blocked;
425*b4c3e9b5SBjoern A. Zeeb 	int err;
426*b4c3e9b5SBjoern A. Zeeb 
427*b4c3e9b5SBjoern A. Zeeb 	if (!wl->ucode.bcm43xx_bomminor) {
428*b4c3e9b5SBjoern A. Zeeb 		err = brcms_request_fw(wl, wl->wlc->hw->d11core);
429*b4c3e9b5SBjoern A. Zeeb 		if (err)
430*b4c3e9b5SBjoern A. Zeeb 			return -ENOENT;
431*b4c3e9b5SBjoern A. Zeeb 	}
432*b4c3e9b5SBjoern A. Zeeb 
433*b4c3e9b5SBjoern A. Zeeb 	ieee80211_wake_queues(hw);
434*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
435*b4c3e9b5SBjoern A. Zeeb 	blocked = brcms_rfkill_set_hw_state(wl);
436*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
437*b4c3e9b5SBjoern A. Zeeb 	if (!blocked)
438*b4c3e9b5SBjoern A. Zeeb 		wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
439*b4c3e9b5SBjoern A. Zeeb 
440*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
441*b4c3e9b5SBjoern A. Zeeb 	/* avoid acknowledging frames before a non-monitor device is added */
442*b4c3e9b5SBjoern A. Zeeb 	wl->mute_tx = true;
443*b4c3e9b5SBjoern A. Zeeb 
444*b4c3e9b5SBjoern A. Zeeb 	if (!wl->pub->up)
445*b4c3e9b5SBjoern A. Zeeb 		if (!blocked)
446*b4c3e9b5SBjoern A. Zeeb 			err = brcms_up(wl);
447*b4c3e9b5SBjoern A. Zeeb 		else
448*b4c3e9b5SBjoern A. Zeeb 			err = -ERFKILL;
449*b4c3e9b5SBjoern A. Zeeb 	else
450*b4c3e9b5SBjoern A. Zeeb 		err = -ENODEV;
451*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
452*b4c3e9b5SBjoern A. Zeeb 
453*b4c3e9b5SBjoern A. Zeeb 	if (err != 0)
454*b4c3e9b5SBjoern A. Zeeb 		brcms_err(wl->wlc->hw->d11core, "%s: brcms_up() returned %d\n",
455*b4c3e9b5SBjoern A. Zeeb 			  __func__, err);
456*b4c3e9b5SBjoern A. Zeeb 
457*b4c3e9b5SBjoern A. Zeeb 	bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, true);
458*b4c3e9b5SBjoern A. Zeeb 	return err;
459*b4c3e9b5SBjoern A. Zeeb }
460*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_stop(struct ieee80211_hw * hw,bool suspend)461*b4c3e9b5SBjoern A. Zeeb static void brcms_ops_stop(struct ieee80211_hw *hw, bool suspend)
462*b4c3e9b5SBjoern A. Zeeb {
463*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
464*b4c3e9b5SBjoern A. Zeeb 	int status;
465*b4c3e9b5SBjoern A. Zeeb 
466*b4c3e9b5SBjoern A. Zeeb 	ieee80211_stop_queues(hw);
467*b4c3e9b5SBjoern A. Zeeb 
468*b4c3e9b5SBjoern A. Zeeb 	if (wl->wlc == NULL)
469*b4c3e9b5SBjoern A. Zeeb 		return;
470*b4c3e9b5SBjoern A. Zeeb 
471*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
472*b4c3e9b5SBjoern A. Zeeb 	status = brcms_c_chipmatch(wl->wlc->hw->d11core);
473*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
474*b4c3e9b5SBjoern A. Zeeb 	if (!status) {
475*b4c3e9b5SBjoern A. Zeeb 		brcms_err(wl->wlc->hw->d11core,
476*b4c3e9b5SBjoern A. Zeeb 			  "wl: brcms_ops_stop: chipmatch failed\n");
477*b4c3e9b5SBjoern A. Zeeb 		return;
478*b4c3e9b5SBjoern A. Zeeb 	}
479*b4c3e9b5SBjoern A. Zeeb 
480*b4c3e9b5SBjoern A. Zeeb 	bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, false);
481*b4c3e9b5SBjoern A. Zeeb 
482*b4c3e9b5SBjoern A. Zeeb 	/* put driver in down state */
483*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
484*b4c3e9b5SBjoern A. Zeeb 	brcms_down(wl);
485*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
486*b4c3e9b5SBjoern A. Zeeb }
487*b4c3e9b5SBjoern A. Zeeb 
488*b4c3e9b5SBjoern A. Zeeb static int
brcms_ops_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)489*b4c3e9b5SBjoern A. Zeeb brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
490*b4c3e9b5SBjoern A. Zeeb {
491*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
492*b4c3e9b5SBjoern A. Zeeb 
493*b4c3e9b5SBjoern A. Zeeb 	/* Just STA, AP and ADHOC for now */
494*b4c3e9b5SBjoern A. Zeeb 	if (vif->type != NL80211_IFTYPE_STATION &&
495*b4c3e9b5SBjoern A. Zeeb 	    vif->type != NL80211_IFTYPE_AP &&
496*b4c3e9b5SBjoern A. Zeeb 	    vif->type != NL80211_IFTYPE_ADHOC) {
497*b4c3e9b5SBjoern A. Zeeb 		brcms_err(wl->wlc->hw->d11core,
498*b4c3e9b5SBjoern A. Zeeb 			  "%s: Attempt to add type %d, only STA, AP and AdHoc for now\n",
499*b4c3e9b5SBjoern A. Zeeb 			  __func__, vif->type);
500*b4c3e9b5SBjoern A. Zeeb 		return -EOPNOTSUPP;
501*b4c3e9b5SBjoern A. Zeeb 	}
502*b4c3e9b5SBjoern A. Zeeb 
503*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
504*b4c3e9b5SBjoern A. Zeeb 	wl->wlc->vif = vif;
505*b4c3e9b5SBjoern A. Zeeb 	wl->mute_tx = false;
506*b4c3e9b5SBjoern A. Zeeb 	brcms_c_mute(wl->wlc, false);
507*b4c3e9b5SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_STATION)
508*b4c3e9b5SBjoern A. Zeeb 		brcms_c_start_station(wl->wlc, vif->addr);
509*b4c3e9b5SBjoern A. Zeeb 	else if (vif->type == NL80211_IFTYPE_AP)
510*b4c3e9b5SBjoern A. Zeeb 		brcms_c_start_ap(wl->wlc, vif->addr, vif->bss_conf.bssid,
511*b4c3e9b5SBjoern A. Zeeb 				 vif->cfg.ssid, vif->cfg.ssid_len);
512*b4c3e9b5SBjoern A. Zeeb 	else if (vif->type == NL80211_IFTYPE_ADHOC)
513*b4c3e9b5SBjoern A. Zeeb 		brcms_c_start_adhoc(wl->wlc, vif->addr);
514*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
515*b4c3e9b5SBjoern A. Zeeb 
516*b4c3e9b5SBjoern A. Zeeb 	return 0;
517*b4c3e9b5SBjoern A. Zeeb }
518*b4c3e9b5SBjoern A. Zeeb 
519*b4c3e9b5SBjoern A. Zeeb static void
brcms_ops_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)520*b4c3e9b5SBjoern A. Zeeb brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
521*b4c3e9b5SBjoern A. Zeeb {
522*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
523*b4c3e9b5SBjoern A. Zeeb 
524*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
525*b4c3e9b5SBjoern A. Zeeb 	wl->wlc->vif = NULL;
526*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
527*b4c3e9b5SBjoern A. Zeeb }
528*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_config(struct ieee80211_hw * hw,int radio_idx,u32 changed)529*b4c3e9b5SBjoern A. Zeeb static int brcms_ops_config(struct ieee80211_hw *hw, int radio_idx,
530*b4c3e9b5SBjoern A. Zeeb 			    u32 changed)
531*b4c3e9b5SBjoern A. Zeeb {
532*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_conf *conf = &hw->conf;
533*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
534*b4c3e9b5SBjoern A. Zeeb 	struct bcma_device *core = wl->wlc->hw->d11core;
535*b4c3e9b5SBjoern A. Zeeb 	int err = 0;
536*b4c3e9b5SBjoern A. Zeeb 	int new_int;
537*b4c3e9b5SBjoern A. Zeeb 
538*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
539*b4c3e9b5SBjoern A. Zeeb 	if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
540*b4c3e9b5SBjoern A. Zeeb 		brcms_c_set_beacon_listen_interval(wl->wlc,
541*b4c3e9b5SBjoern A. Zeeb 						   conf->listen_interval);
542*b4c3e9b5SBjoern A. Zeeb 	}
543*b4c3e9b5SBjoern A. Zeeb 	if (changed & IEEE80211_CONF_CHANGE_MONITOR)
544*b4c3e9b5SBjoern A. Zeeb 		brcms_dbg_info(core, "%s: change monitor mode: %s\n", __func__,
545*b4c3e9b5SBjoern A. Zeeb 			       str_true_false(conf->flags &
546*b4c3e9b5SBjoern A. Zeeb 					      IEEE80211_CONF_MONITOR));
547*b4c3e9b5SBjoern A. Zeeb 	if (changed & IEEE80211_CONF_CHANGE_PS)
548*b4c3e9b5SBjoern A. Zeeb 		brcms_err(core, "%s: change power-save mode: %s (implement)\n",
549*b4c3e9b5SBjoern A. Zeeb 			  __func__,
550*b4c3e9b5SBjoern A. Zeeb 			  str_true_false(conf->flags & IEEE80211_CONF_PS));
551*b4c3e9b5SBjoern A. Zeeb 
552*b4c3e9b5SBjoern A. Zeeb 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
553*b4c3e9b5SBjoern A. Zeeb 		err = brcms_c_set_tx_power(wl->wlc, conf->power_level);
554*b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
555*b4c3e9b5SBjoern A. Zeeb 			brcms_err(core, "%s: Error setting power_level\n",
556*b4c3e9b5SBjoern A. Zeeb 				  __func__);
557*b4c3e9b5SBjoern A. Zeeb 			goto config_out;
558*b4c3e9b5SBjoern A. Zeeb 		}
559*b4c3e9b5SBjoern A. Zeeb 		new_int = brcms_c_get_tx_power(wl->wlc);
560*b4c3e9b5SBjoern A. Zeeb 		if (new_int != conf->power_level)
561*b4c3e9b5SBjoern A. Zeeb 			brcms_err(core,
562*b4c3e9b5SBjoern A. Zeeb 				  "%s: Power level req != actual, %d %d\n",
563*b4c3e9b5SBjoern A. Zeeb 				  __func__, conf->power_level,
564*b4c3e9b5SBjoern A. Zeeb 				  new_int);
565*b4c3e9b5SBjoern A. Zeeb 	}
566*b4c3e9b5SBjoern A. Zeeb 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
567*b4c3e9b5SBjoern A. Zeeb 		if (conf->chandef.width == NL80211_CHAN_WIDTH_20 ||
568*b4c3e9b5SBjoern A. Zeeb 		    conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
569*b4c3e9b5SBjoern A. Zeeb 			err = brcms_c_set_channel(wl->wlc,
570*b4c3e9b5SBjoern A. Zeeb 						  conf->chandef.chan->hw_value);
571*b4c3e9b5SBjoern A. Zeeb 		else
572*b4c3e9b5SBjoern A. Zeeb 			err = -ENOTSUPP;
573*b4c3e9b5SBjoern A. Zeeb 	}
574*b4c3e9b5SBjoern A. Zeeb 	if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
575*b4c3e9b5SBjoern A. Zeeb 		err = brcms_c_set_rate_limit(wl->wlc,
576*b4c3e9b5SBjoern A. Zeeb 					     conf->short_frame_max_tx_count,
577*b4c3e9b5SBjoern A. Zeeb 					     conf->long_frame_max_tx_count);
578*b4c3e9b5SBjoern A. Zeeb 
579*b4c3e9b5SBjoern A. Zeeb  config_out:
580*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
581*b4c3e9b5SBjoern A. Zeeb 	return err;
582*b4c3e9b5SBjoern A. Zeeb }
583*b4c3e9b5SBjoern A. Zeeb 
584*b4c3e9b5SBjoern A. Zeeb static void
brcms_ops_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * info,u64 changed)585*b4c3e9b5SBjoern A. Zeeb brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
586*b4c3e9b5SBjoern A. Zeeb 			struct ieee80211_vif *vif,
587*b4c3e9b5SBjoern A. Zeeb 			struct ieee80211_bss_conf *info, u64 changed)
588*b4c3e9b5SBjoern A. Zeeb {
589*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
590*b4c3e9b5SBjoern A. Zeeb 	struct bcma_device *core = wl->wlc->hw->d11core;
591*b4c3e9b5SBjoern A. Zeeb 
592*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_ASSOC) {
593*b4c3e9b5SBjoern A. Zeeb 		/* association status changed (associated/disassociated)
594*b4c3e9b5SBjoern A. Zeeb 		 * also implies a change in the AID.
595*b4c3e9b5SBjoern A. Zeeb 		 */
596*b4c3e9b5SBjoern A. Zeeb 		brcms_err(core, "%s: %s: %sassociated\n", KBUILD_MODNAME,
597*b4c3e9b5SBjoern A. Zeeb 			  __func__, vif->cfg.assoc ? "" : "dis");
598*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
599*b4c3e9b5SBjoern A. Zeeb 		brcms_c_associate_upd(wl->wlc, vif->cfg.assoc);
600*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
601*b4c3e9b5SBjoern A. Zeeb 	}
602*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_ERP_SLOT) {
603*b4c3e9b5SBjoern A. Zeeb 		s8 val;
604*b4c3e9b5SBjoern A. Zeeb 
605*b4c3e9b5SBjoern A. Zeeb 		/* slot timing changed */
606*b4c3e9b5SBjoern A. Zeeb 		if (info->use_short_slot)
607*b4c3e9b5SBjoern A. Zeeb 			val = 1;
608*b4c3e9b5SBjoern A. Zeeb 		else
609*b4c3e9b5SBjoern A. Zeeb 			val = 0;
610*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
611*b4c3e9b5SBjoern A. Zeeb 		brcms_c_set_shortslot_override(wl->wlc, val);
612*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
613*b4c3e9b5SBjoern A. Zeeb 	}
614*b4c3e9b5SBjoern A. Zeeb 
615*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_HT) {
616*b4c3e9b5SBjoern A. Zeeb 		/* 802.11n parameters changed */
617*b4c3e9b5SBjoern A. Zeeb 		u16 mode = info->ht_operation_mode;
618*b4c3e9b5SBjoern A. Zeeb 
619*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
620*b4c3e9b5SBjoern A. Zeeb 		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_CFG,
621*b4c3e9b5SBjoern A. Zeeb 			mode & IEEE80211_HT_OP_MODE_PROTECTION);
622*b4c3e9b5SBjoern A. Zeeb 		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_NONGF,
623*b4c3e9b5SBjoern A. Zeeb 			mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
624*b4c3e9b5SBjoern A. Zeeb 		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_OBSS,
625*b4c3e9b5SBjoern A. Zeeb 			mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT);
626*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
627*b4c3e9b5SBjoern A. Zeeb 	}
628*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_BASIC_RATES) {
629*b4c3e9b5SBjoern A. Zeeb 		struct ieee80211_supported_band *bi;
630*b4c3e9b5SBjoern A. Zeeb 		u32 br_mask, i;
631*b4c3e9b5SBjoern A. Zeeb 		u16 rate;
632*b4c3e9b5SBjoern A. Zeeb 		struct brcm_rateset rs;
633*b4c3e9b5SBjoern A. Zeeb 		int error;
634*b4c3e9b5SBjoern A. Zeeb 
635*b4c3e9b5SBjoern A. Zeeb 		/* retrieve the current rates */
636*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
637*b4c3e9b5SBjoern A. Zeeb 		brcms_c_get_current_rateset(wl->wlc, &rs);
638*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
639*b4c3e9b5SBjoern A. Zeeb 
640*b4c3e9b5SBjoern A. Zeeb 		br_mask = info->basic_rates;
641*b4c3e9b5SBjoern A. Zeeb 		bi = hw->wiphy->bands[brcms_c_get_curband(wl->wlc)];
642*b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < bi->n_bitrates; i++) {
643*b4c3e9b5SBjoern A. Zeeb 			/* convert to internal rate value */
644*b4c3e9b5SBjoern A. Zeeb 			rate = (bi->bitrates[i].bitrate << 1) / 10;
645*b4c3e9b5SBjoern A. Zeeb 
646*b4c3e9b5SBjoern A. Zeeb 			/* set/clear basic rate flag */
647*b4c3e9b5SBjoern A. Zeeb 			brcms_set_basic_rate(&rs, rate, br_mask & 1);
648*b4c3e9b5SBjoern A. Zeeb 			br_mask >>= 1;
649*b4c3e9b5SBjoern A. Zeeb 		}
650*b4c3e9b5SBjoern A. Zeeb 
651*b4c3e9b5SBjoern A. Zeeb 		/* update the rate set */
652*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
653*b4c3e9b5SBjoern A. Zeeb 		error = brcms_c_set_rateset(wl->wlc, &rs);
654*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
655*b4c3e9b5SBjoern A. Zeeb 		if (error)
656*b4c3e9b5SBjoern A. Zeeb 			brcms_err(core, "changing basic rates failed: %d\n",
657*b4c3e9b5SBjoern A. Zeeb 				  error);
658*b4c3e9b5SBjoern A. Zeeb 	}
659*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_BEACON_INT) {
660*b4c3e9b5SBjoern A. Zeeb 		/* Beacon interval changed */
661*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
662*b4c3e9b5SBjoern A. Zeeb 		brcms_c_set_beacon_period(wl->wlc, info->beacon_int);
663*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
664*b4c3e9b5SBjoern A. Zeeb 	}
665*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_BSSID) {
666*b4c3e9b5SBjoern A. Zeeb 		/* BSSID changed, for whatever reason (IBSS and managed mode) */
667*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
668*b4c3e9b5SBjoern A. Zeeb 		brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid);
669*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
670*b4c3e9b5SBjoern A. Zeeb 	}
671*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_SSID) {
672*b4c3e9b5SBjoern A. Zeeb 		/* BSSID changed, for whatever reason (IBSS and managed mode) */
673*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
674*b4c3e9b5SBjoern A. Zeeb 		brcms_c_set_ssid(wl->wlc, vif->cfg.ssid, vif->cfg.ssid_len);
675*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
676*b4c3e9b5SBjoern A. Zeeb 	}
677*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_BEACON) {
678*b4c3e9b5SBjoern A. Zeeb 		/* Beacon data changed, retrieve new beacon (beaconing modes) */
679*b4c3e9b5SBjoern A. Zeeb 		struct sk_buff *beacon;
680*b4c3e9b5SBjoern A. Zeeb 		u16 tim_offset = 0;
681*b4c3e9b5SBjoern A. Zeeb 
682*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
683*b4c3e9b5SBjoern A. Zeeb 		beacon = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL, 0);
684*b4c3e9b5SBjoern A. Zeeb 		brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset,
685*b4c3e9b5SBjoern A. Zeeb 				       info->dtim_period);
686*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
687*b4c3e9b5SBjoern A. Zeeb 	}
688*b4c3e9b5SBjoern A. Zeeb 
689*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_AP_PROBE_RESP) {
690*b4c3e9b5SBjoern A. Zeeb 		struct sk_buff *probe_resp;
691*b4c3e9b5SBjoern A. Zeeb 
692*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
693*b4c3e9b5SBjoern A. Zeeb 		probe_resp = ieee80211_proberesp_get(hw, vif);
694*b4c3e9b5SBjoern A. Zeeb 		brcms_c_set_new_probe_resp(wl->wlc, probe_resp);
695*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
696*b4c3e9b5SBjoern A. Zeeb 	}
697*b4c3e9b5SBjoern A. Zeeb 
698*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_BEACON_ENABLED) {
699*b4c3e9b5SBjoern A. Zeeb 		/* Beaconing should be enabled/disabled (beaconing modes) */
700*b4c3e9b5SBjoern A. Zeeb 		brcms_err(core, "%s: Beacon enabled: %s\n", __func__,
701*b4c3e9b5SBjoern A. Zeeb 			  str_true_false(info->enable_beacon));
702*b4c3e9b5SBjoern A. Zeeb 		if (info->enable_beacon &&
703*b4c3e9b5SBjoern A. Zeeb 		    hw->wiphy->flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) {
704*b4c3e9b5SBjoern A. Zeeb 			brcms_c_enable_probe_resp(wl->wlc, true);
705*b4c3e9b5SBjoern A. Zeeb 		} else {
706*b4c3e9b5SBjoern A. Zeeb 			brcms_c_enable_probe_resp(wl->wlc, false);
707*b4c3e9b5SBjoern A. Zeeb 		}
708*b4c3e9b5SBjoern A. Zeeb 	}
709*b4c3e9b5SBjoern A. Zeeb 
710*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_CQM) {
711*b4c3e9b5SBjoern A. Zeeb 		/* Connection quality monitor config changed */
712*b4c3e9b5SBjoern A. Zeeb 		brcms_err(core, "%s: cqm change: threshold %d, hys %d "
713*b4c3e9b5SBjoern A. Zeeb 			  " (implement)\n", __func__, info->cqm_rssi_thold,
714*b4c3e9b5SBjoern A. Zeeb 			  info->cqm_rssi_hyst);
715*b4c3e9b5SBjoern A. Zeeb 	}
716*b4c3e9b5SBjoern A. Zeeb 
717*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_IBSS) {
718*b4c3e9b5SBjoern A. Zeeb 		/* IBSS join status changed */
719*b4c3e9b5SBjoern A. Zeeb 		brcms_err(core, "%s: IBSS joined: %s (implement)\n",
720*b4c3e9b5SBjoern A. Zeeb 			  __func__, str_true_false(vif->cfg.ibss_joined));
721*b4c3e9b5SBjoern A. Zeeb 	}
722*b4c3e9b5SBjoern A. Zeeb 
723*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_ARP_FILTER) {
724*b4c3e9b5SBjoern A. Zeeb 		/* Hardware ARP filter address list or state changed */
725*b4c3e9b5SBjoern A. Zeeb 		brcms_err(core, "%s: arp filtering: %d addresses"
726*b4c3e9b5SBjoern A. Zeeb 			  " (implement)\n", __func__, vif->cfg.arp_addr_cnt);
727*b4c3e9b5SBjoern A. Zeeb 	}
728*b4c3e9b5SBjoern A. Zeeb 
729*b4c3e9b5SBjoern A. Zeeb 	if (changed & BSS_CHANGED_QOS) {
730*b4c3e9b5SBjoern A. Zeeb 		/*
731*b4c3e9b5SBjoern A. Zeeb 		 * QoS for this association was enabled/disabled.
732*b4c3e9b5SBjoern A. Zeeb 		 * Note that it is only ever disabled for station mode.
733*b4c3e9b5SBjoern A. Zeeb 		 */
734*b4c3e9b5SBjoern A. Zeeb 		brcms_err(core, "%s: qos enabled: %s (implement)\n",
735*b4c3e9b5SBjoern A. Zeeb 			  __func__, str_true_false(info->qos));
736*b4c3e9b5SBjoern A. Zeeb 	}
737*b4c3e9b5SBjoern A. Zeeb 	return;
738*b4c3e9b5SBjoern A. Zeeb }
739*b4c3e9b5SBjoern A. Zeeb 
740*b4c3e9b5SBjoern A. Zeeb static void
brcms_ops_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * total_flags,u64 multicast)741*b4c3e9b5SBjoern A. Zeeb brcms_ops_configure_filter(struct ieee80211_hw *hw,
742*b4c3e9b5SBjoern A. Zeeb 			unsigned int changed_flags,
743*b4c3e9b5SBjoern A. Zeeb 			unsigned int *total_flags, u64 multicast)
744*b4c3e9b5SBjoern A. Zeeb {
745*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
746*b4c3e9b5SBjoern A. Zeeb 	struct bcma_device *core = wl->wlc->hw->d11core;
747*b4c3e9b5SBjoern A. Zeeb 
748*b4c3e9b5SBjoern A. Zeeb 	changed_flags &= MAC_FILTERS;
749*b4c3e9b5SBjoern A. Zeeb 	*total_flags &= MAC_FILTERS;
750*b4c3e9b5SBjoern A. Zeeb 
751*b4c3e9b5SBjoern A. Zeeb 	if (changed_flags & FIF_ALLMULTI)
752*b4c3e9b5SBjoern A. Zeeb 		brcms_dbg_info(core, "FIF_ALLMULTI\n");
753*b4c3e9b5SBjoern A. Zeeb 	if (changed_flags & FIF_FCSFAIL)
754*b4c3e9b5SBjoern A. Zeeb 		brcms_dbg_info(core, "FIF_FCSFAIL\n");
755*b4c3e9b5SBjoern A. Zeeb 	if (changed_flags & FIF_CONTROL)
756*b4c3e9b5SBjoern A. Zeeb 		brcms_dbg_info(core, "FIF_CONTROL\n");
757*b4c3e9b5SBjoern A. Zeeb 	if (changed_flags & FIF_OTHER_BSS)
758*b4c3e9b5SBjoern A. Zeeb 		brcms_dbg_info(core, "FIF_OTHER_BSS\n");
759*b4c3e9b5SBjoern A. Zeeb 	if (changed_flags & FIF_PSPOLL)
760*b4c3e9b5SBjoern A. Zeeb 		brcms_dbg_info(core, "FIF_PSPOLL\n");
761*b4c3e9b5SBjoern A. Zeeb 	if (changed_flags & FIF_BCN_PRBRESP_PROMISC)
762*b4c3e9b5SBjoern A. Zeeb 		brcms_dbg_info(core, "FIF_BCN_PRBRESP_PROMISC\n");
763*b4c3e9b5SBjoern A. Zeeb 
764*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
765*b4c3e9b5SBjoern A. Zeeb 	brcms_c_mac_promisc(wl->wlc, *total_flags);
766*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
767*b4c3e9b5SBjoern A. Zeeb 	return;
768*b4c3e9b5SBjoern A. Zeeb }
769*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_sw_scan_start(struct ieee80211_hw * hw,struct ieee80211_vif * vif,const u8 * mac_addr)770*b4c3e9b5SBjoern A. Zeeb static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw,
771*b4c3e9b5SBjoern A. Zeeb 				    struct ieee80211_vif *vif,
772*b4c3e9b5SBjoern A. Zeeb 				    const u8 *mac_addr)
773*b4c3e9b5SBjoern A. Zeeb {
774*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
775*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
776*b4c3e9b5SBjoern A. Zeeb 	brcms_c_scan_start(wl->wlc);
777*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
778*b4c3e9b5SBjoern A. Zeeb 	return;
779*b4c3e9b5SBjoern A. Zeeb }
780*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_sw_scan_complete(struct ieee80211_hw * hw,struct ieee80211_vif * vif)781*b4c3e9b5SBjoern A. Zeeb static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw,
782*b4c3e9b5SBjoern A. Zeeb 				       struct ieee80211_vif *vif)
783*b4c3e9b5SBjoern A. Zeeb {
784*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
785*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
786*b4c3e9b5SBjoern A. Zeeb 	brcms_c_scan_stop(wl->wlc);
787*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
788*b4c3e9b5SBjoern A. Zeeb 	return;
789*b4c3e9b5SBjoern A. Zeeb }
790*b4c3e9b5SBjoern A. Zeeb 
791*b4c3e9b5SBjoern A. Zeeb 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)792*b4c3e9b5SBjoern A. Zeeb brcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
793*b4c3e9b5SBjoern A. Zeeb 		  unsigned int link_id, u16 queue,
794*b4c3e9b5SBjoern A. Zeeb 		  const struct ieee80211_tx_queue_params *params)
795*b4c3e9b5SBjoern A. Zeeb {
796*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
797*b4c3e9b5SBjoern A. Zeeb 
798*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
799*b4c3e9b5SBjoern A. Zeeb 	brcms_c_wme_setparams(wl->wlc, queue, params, true);
800*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
801*b4c3e9b5SBjoern A. Zeeb 
802*b4c3e9b5SBjoern A. Zeeb 	return 0;
803*b4c3e9b5SBjoern A. Zeeb }
804*b4c3e9b5SBjoern A. Zeeb 
805*b4c3e9b5SBjoern A. Zeeb static int
brcms_ops_sta_add(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)806*b4c3e9b5SBjoern A. Zeeb brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
807*b4c3e9b5SBjoern A. Zeeb 	       struct ieee80211_sta *sta)
808*b4c3e9b5SBjoern A. Zeeb {
809*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
810*b4c3e9b5SBjoern A. Zeeb 	struct scb *scb = &wl->wlc->pri_scb;
811*b4c3e9b5SBjoern A. Zeeb 
812*b4c3e9b5SBjoern A. Zeeb 	brcms_c_init_scb(scb);
813*b4c3e9b5SBjoern A. Zeeb 
814*b4c3e9b5SBjoern A. Zeeb 	wl->pub->global_ampdu = &(scb->scb_ampdu);
815*b4c3e9b5SBjoern A. Zeeb 	wl->pub->global_ampdu->max_pdu = 16;
816*b4c3e9b5SBjoern A. Zeeb 
817*b4c3e9b5SBjoern A. Zeeb 	/*
818*b4c3e9b5SBjoern A. Zeeb 	 * minstrel_ht initiates addBA on our behalf by calling
819*b4c3e9b5SBjoern A. Zeeb 	 * ieee80211_start_tx_ba_session()
820*b4c3e9b5SBjoern A. Zeeb 	 */
821*b4c3e9b5SBjoern A. Zeeb 	return 0;
822*b4c3e9b5SBjoern A. Zeeb }
823*b4c3e9b5SBjoern A. Zeeb 
824*b4c3e9b5SBjoern A. Zeeb static int
brcms_ops_ampdu_action(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_ampdu_params * params)825*b4c3e9b5SBjoern A. Zeeb brcms_ops_ampdu_action(struct ieee80211_hw *hw,
826*b4c3e9b5SBjoern A. Zeeb 		    struct ieee80211_vif *vif,
827*b4c3e9b5SBjoern A. Zeeb 		    struct ieee80211_ampdu_params *params)
828*b4c3e9b5SBjoern A. Zeeb {
829*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
830*b4c3e9b5SBjoern A. Zeeb 	struct scb *scb = &wl->wlc->pri_scb;
831*b4c3e9b5SBjoern A. Zeeb 	int status;
832*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_sta *sta = params->sta;
833*b4c3e9b5SBjoern A. Zeeb 	enum ieee80211_ampdu_mlme_action action = params->action;
834*b4c3e9b5SBjoern A. Zeeb 	u16 tid = params->tid;
835*b4c3e9b5SBjoern A. Zeeb 
836*b4c3e9b5SBjoern A. Zeeb 	if (WARN_ON(scb->magic != SCB_MAGIC))
837*b4c3e9b5SBjoern A. Zeeb 		return -EIDRM;
838*b4c3e9b5SBjoern A. Zeeb 	switch (action) {
839*b4c3e9b5SBjoern A. Zeeb 	case IEEE80211_AMPDU_RX_START:
840*b4c3e9b5SBjoern A. Zeeb 		break;
841*b4c3e9b5SBjoern A. Zeeb 	case IEEE80211_AMPDU_RX_STOP:
842*b4c3e9b5SBjoern A. Zeeb 		break;
843*b4c3e9b5SBjoern A. Zeeb 	case IEEE80211_AMPDU_TX_START:
844*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
845*b4c3e9b5SBjoern A. Zeeb 		status = brcms_c_aggregatable(wl->wlc, tid);
846*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
847*b4c3e9b5SBjoern A. Zeeb 		if (!status) {
848*b4c3e9b5SBjoern A. Zeeb 			brcms_dbg_ht(wl->wlc->hw->d11core,
849*b4c3e9b5SBjoern A. Zeeb 				     "START: tid %d is not agg\'able\n", tid);
850*b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
851*b4c3e9b5SBjoern A. Zeeb 		}
852*b4c3e9b5SBjoern A. Zeeb 		return IEEE80211_AMPDU_TX_START_IMMEDIATE;
853*b4c3e9b5SBjoern A. Zeeb 
854*b4c3e9b5SBjoern A. Zeeb 	case IEEE80211_AMPDU_TX_STOP_CONT:
855*b4c3e9b5SBjoern A. Zeeb 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
856*b4c3e9b5SBjoern A. Zeeb 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
857*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
858*b4c3e9b5SBjoern A. Zeeb 		brcms_c_ampdu_flush(wl->wlc, sta, tid);
859*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
860*b4c3e9b5SBjoern A. Zeeb 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
861*b4c3e9b5SBjoern A. Zeeb 		break;
862*b4c3e9b5SBjoern A. Zeeb 	case IEEE80211_AMPDU_TX_OPERATIONAL:
863*b4c3e9b5SBjoern A. Zeeb 		/*
864*b4c3e9b5SBjoern A. Zeeb 		 * BA window size from ADDBA response ('buf_size') defines how
865*b4c3e9b5SBjoern A. Zeeb 		 * many outstanding MPDUs are allowed for the BA stream by
866*b4c3e9b5SBjoern A. Zeeb 		 * recipient and traffic class (this is actually unused by the
867*b4c3e9b5SBjoern A. Zeeb 		 * rest of the driver). 'ampdu_factor' gives maximum AMPDU size.
868*b4c3e9b5SBjoern A. Zeeb 		 */
869*b4c3e9b5SBjoern A. Zeeb 		spin_lock_bh(&wl->lock);
870*b4c3e9b5SBjoern A. Zeeb 		brcms_c_ampdu_tx_operational(wl->wlc, tid,
871*b4c3e9b5SBjoern A. Zeeb 			(1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
872*b4c3e9b5SBjoern A. Zeeb 			 sta->deflink.ht_cap.ampdu_factor)) - 1);
873*b4c3e9b5SBjoern A. Zeeb 		spin_unlock_bh(&wl->lock);
874*b4c3e9b5SBjoern A. Zeeb 		/* Power save wakeup */
875*b4c3e9b5SBjoern A. Zeeb 		break;
876*b4c3e9b5SBjoern A. Zeeb 	default:
877*b4c3e9b5SBjoern A. Zeeb 		brcms_err(wl->wlc->hw->d11core,
878*b4c3e9b5SBjoern A. Zeeb 			  "%s: Invalid command, ignoring\n", __func__);
879*b4c3e9b5SBjoern A. Zeeb 	}
880*b4c3e9b5SBjoern A. Zeeb 
881*b4c3e9b5SBjoern A. Zeeb 	return 0;
882*b4c3e9b5SBjoern A. Zeeb }
883*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_rfkill_poll(struct ieee80211_hw * hw)884*b4c3e9b5SBjoern A. Zeeb static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw)
885*b4c3e9b5SBjoern A. Zeeb {
886*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
887*b4c3e9b5SBjoern A. Zeeb 	bool blocked;
888*b4c3e9b5SBjoern A. Zeeb 
889*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
890*b4c3e9b5SBjoern A. Zeeb 	blocked = brcms_c_check_radio_disabled(wl->wlc);
891*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
892*b4c3e9b5SBjoern A. Zeeb 
893*b4c3e9b5SBjoern A. Zeeb 	wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
894*b4c3e9b5SBjoern A. Zeeb }
895*b4c3e9b5SBjoern A. Zeeb 
brcms_tx_flush_completed(struct brcms_info * wl)896*b4c3e9b5SBjoern A. Zeeb static bool brcms_tx_flush_completed(struct brcms_info *wl)
897*b4c3e9b5SBjoern A. Zeeb {
898*b4c3e9b5SBjoern A. Zeeb 	bool result;
899*b4c3e9b5SBjoern A. Zeeb 
900*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
901*b4c3e9b5SBjoern A. Zeeb 	result = brcms_c_tx_flush_completed(wl->wlc);
902*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
903*b4c3e9b5SBjoern A. Zeeb 	return result;
904*b4c3e9b5SBjoern A. Zeeb }
905*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_flush(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u32 queues,bool drop)906*b4c3e9b5SBjoern A. Zeeb static void brcms_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
907*b4c3e9b5SBjoern A. Zeeb 			    u32 queues, bool drop)
908*b4c3e9b5SBjoern A. Zeeb {
909*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
910*b4c3e9b5SBjoern A. Zeeb 	int ret;
911*b4c3e9b5SBjoern A. Zeeb 
912*b4c3e9b5SBjoern A. Zeeb 	no_printk("%s: drop = %s\n", __func__, str_true_false(drop));
913*b4c3e9b5SBjoern A. Zeeb 
914*b4c3e9b5SBjoern A. Zeeb 	ret = wait_event_timeout(wl->tx_flush_wq,
915*b4c3e9b5SBjoern A. Zeeb 				 brcms_tx_flush_completed(wl),
916*b4c3e9b5SBjoern A. Zeeb 				 msecs_to_jiffies(BRCMS_FLUSH_TIMEOUT));
917*b4c3e9b5SBjoern A. Zeeb 
918*b4c3e9b5SBjoern A. Zeeb 	brcms_dbg_mac80211(wl->wlc->hw->d11core,
919*b4c3e9b5SBjoern A. Zeeb 			   "ret=%d\n", jiffies_to_msecs(ret));
920*b4c3e9b5SBjoern A. Zeeb }
921*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_get_tsf(struct ieee80211_hw * hw,struct ieee80211_vif * vif)922*b4c3e9b5SBjoern A. Zeeb static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
923*b4c3e9b5SBjoern A. Zeeb {
924*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
925*b4c3e9b5SBjoern A. Zeeb 	u64 tsf;
926*b4c3e9b5SBjoern A. Zeeb 
927*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
928*b4c3e9b5SBjoern A. Zeeb 	tsf = brcms_c_tsf_get(wl->wlc);
929*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
930*b4c3e9b5SBjoern A. Zeeb 
931*b4c3e9b5SBjoern A. Zeeb 	return tsf;
932*b4c3e9b5SBjoern A. Zeeb }
933*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_set_tsf(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u64 tsf)934*b4c3e9b5SBjoern A. Zeeb static void brcms_ops_set_tsf(struct ieee80211_hw *hw,
935*b4c3e9b5SBjoern A. Zeeb 			   struct ieee80211_vif *vif, u64 tsf)
936*b4c3e9b5SBjoern A. Zeeb {
937*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
938*b4c3e9b5SBjoern A. Zeeb 
939*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
940*b4c3e9b5SBjoern A. Zeeb 	brcms_c_tsf_set(wl->wlc, tsf);
941*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
942*b4c3e9b5SBjoern A. Zeeb }
943*b4c3e9b5SBjoern A. Zeeb 
brcms_ops_beacon_set_tim(struct ieee80211_hw * hw,struct ieee80211_sta * sta,bool set)944*b4c3e9b5SBjoern A. Zeeb static int brcms_ops_beacon_set_tim(struct ieee80211_hw *hw,
945*b4c3e9b5SBjoern A. Zeeb 				 struct ieee80211_sta *sta, bool set)
946*b4c3e9b5SBjoern A. Zeeb {
947*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
948*b4c3e9b5SBjoern A. Zeeb 	struct sk_buff *beacon = NULL;
949*b4c3e9b5SBjoern A. Zeeb 	u16 tim_offset = 0;
950*b4c3e9b5SBjoern A. Zeeb 
951*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
952*b4c3e9b5SBjoern A. Zeeb 	if (wl->wlc->vif)
953*b4c3e9b5SBjoern A. Zeeb 		beacon = ieee80211_beacon_get_tim(hw, wl->wlc->vif,
954*b4c3e9b5SBjoern A. Zeeb 						  &tim_offset, NULL, 0);
955*b4c3e9b5SBjoern A. Zeeb 	if (beacon)
956*b4c3e9b5SBjoern A. Zeeb 		brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset,
957*b4c3e9b5SBjoern A. Zeeb 				       wl->wlc->vif->bss_conf.dtim_period);
958*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
959*b4c3e9b5SBjoern A. Zeeb 
960*b4c3e9b5SBjoern A. Zeeb 	return 0;
961*b4c3e9b5SBjoern A. Zeeb }
962*b4c3e9b5SBjoern A. Zeeb 
963*b4c3e9b5SBjoern A. Zeeb static const struct ieee80211_ops brcms_ops = {
964*b4c3e9b5SBjoern A. Zeeb 	.add_chanctx = ieee80211_emulate_add_chanctx,
965*b4c3e9b5SBjoern A. Zeeb 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
966*b4c3e9b5SBjoern A. Zeeb 	.change_chanctx = ieee80211_emulate_change_chanctx,
967*b4c3e9b5SBjoern A. Zeeb 	.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
968*b4c3e9b5SBjoern A. Zeeb 	.tx = brcms_ops_tx,
969*b4c3e9b5SBjoern A. Zeeb 	.wake_tx_queue = ieee80211_handle_wake_tx_queue,
970*b4c3e9b5SBjoern A. Zeeb 	.start = brcms_ops_start,
971*b4c3e9b5SBjoern A. Zeeb 	.stop = brcms_ops_stop,
972*b4c3e9b5SBjoern A. Zeeb 	.add_interface = brcms_ops_add_interface,
973*b4c3e9b5SBjoern A. Zeeb 	.remove_interface = brcms_ops_remove_interface,
974*b4c3e9b5SBjoern A. Zeeb 	.config = brcms_ops_config,
975*b4c3e9b5SBjoern A. Zeeb 	.bss_info_changed = brcms_ops_bss_info_changed,
976*b4c3e9b5SBjoern A. Zeeb 	.configure_filter = brcms_ops_configure_filter,
977*b4c3e9b5SBjoern A. Zeeb 	.sw_scan_start = brcms_ops_sw_scan_start,
978*b4c3e9b5SBjoern A. Zeeb 	.sw_scan_complete = brcms_ops_sw_scan_complete,
979*b4c3e9b5SBjoern A. Zeeb 	.conf_tx = brcms_ops_conf_tx,
980*b4c3e9b5SBjoern A. Zeeb 	.sta_add = brcms_ops_sta_add,
981*b4c3e9b5SBjoern A. Zeeb 	.ampdu_action = brcms_ops_ampdu_action,
982*b4c3e9b5SBjoern A. Zeeb 	.rfkill_poll = brcms_ops_rfkill_poll,
983*b4c3e9b5SBjoern A. Zeeb 	.flush = brcms_ops_flush,
984*b4c3e9b5SBjoern A. Zeeb 	.get_tsf = brcms_ops_get_tsf,
985*b4c3e9b5SBjoern A. Zeeb 	.set_tsf = brcms_ops_set_tsf,
986*b4c3e9b5SBjoern A. Zeeb 	.set_tim = brcms_ops_beacon_set_tim,
987*b4c3e9b5SBjoern A. Zeeb };
988*b4c3e9b5SBjoern A. Zeeb 
brcms_dpc(struct tasklet_struct * t)989*b4c3e9b5SBjoern A. Zeeb void brcms_dpc(struct tasklet_struct *t)
990*b4c3e9b5SBjoern A. Zeeb {
991*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl;
992*b4c3e9b5SBjoern A. Zeeb 
993*b4c3e9b5SBjoern A. Zeeb 	wl = from_tasklet(wl, t, tasklet);
994*b4c3e9b5SBjoern A. Zeeb 
995*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
996*b4c3e9b5SBjoern A. Zeeb 
997*b4c3e9b5SBjoern A. Zeeb 	/* call the common second level interrupt handler */
998*b4c3e9b5SBjoern A. Zeeb 	if (wl->pub->up) {
999*b4c3e9b5SBjoern A. Zeeb 		if (wl->resched) {
1000*b4c3e9b5SBjoern A. Zeeb 			unsigned long flags;
1001*b4c3e9b5SBjoern A. Zeeb 
1002*b4c3e9b5SBjoern A. Zeeb 			spin_lock_irqsave(&wl->isr_lock, flags);
1003*b4c3e9b5SBjoern A. Zeeb 			brcms_c_intrsupd(wl->wlc);
1004*b4c3e9b5SBjoern A. Zeeb 			spin_unlock_irqrestore(&wl->isr_lock, flags);
1005*b4c3e9b5SBjoern A. Zeeb 		}
1006*b4c3e9b5SBjoern A. Zeeb 
1007*b4c3e9b5SBjoern A. Zeeb 		wl->resched = brcms_c_dpc(wl->wlc, true);
1008*b4c3e9b5SBjoern A. Zeeb 	}
1009*b4c3e9b5SBjoern A. Zeeb 
1010*b4c3e9b5SBjoern A. Zeeb 	/* brcms_c_dpc() may bring the driver down */
1011*b4c3e9b5SBjoern A. Zeeb 	if (!wl->pub->up)
1012*b4c3e9b5SBjoern A. Zeeb 		goto done;
1013*b4c3e9b5SBjoern A. Zeeb 
1014*b4c3e9b5SBjoern A. Zeeb 	/* re-schedule dpc */
1015*b4c3e9b5SBjoern A. Zeeb 	if (wl->resched)
1016*b4c3e9b5SBjoern A. Zeeb 		tasklet_schedule(&wl->tasklet);
1017*b4c3e9b5SBjoern A. Zeeb 	else
1018*b4c3e9b5SBjoern A. Zeeb 		/* re-enable interrupts */
1019*b4c3e9b5SBjoern A. Zeeb 		brcms_intrson(wl);
1020*b4c3e9b5SBjoern A. Zeeb 
1021*b4c3e9b5SBjoern A. Zeeb  done:
1022*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
1023*b4c3e9b5SBjoern A. Zeeb 	wake_up(&wl->tx_flush_wq);
1024*b4c3e9b5SBjoern A. Zeeb }
1025*b4c3e9b5SBjoern A. Zeeb 
brcms_isr(int irq,void * dev_id)1026*b4c3e9b5SBjoern A. Zeeb static irqreturn_t brcms_isr(int irq, void *dev_id)
1027*b4c3e9b5SBjoern A. Zeeb {
1028*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl;
1029*b4c3e9b5SBjoern A. Zeeb 	irqreturn_t ret = IRQ_NONE;
1030*b4c3e9b5SBjoern A. Zeeb 
1031*b4c3e9b5SBjoern A. Zeeb 	wl = (struct brcms_info *) dev_id;
1032*b4c3e9b5SBjoern A. Zeeb 
1033*b4c3e9b5SBjoern A. Zeeb 	spin_lock(&wl->isr_lock);
1034*b4c3e9b5SBjoern A. Zeeb 
1035*b4c3e9b5SBjoern A. Zeeb 	/* call common first level interrupt handler */
1036*b4c3e9b5SBjoern A. Zeeb 	if (brcms_c_isr(wl->wlc)) {
1037*b4c3e9b5SBjoern A. Zeeb 		/* schedule second level handler */
1038*b4c3e9b5SBjoern A. Zeeb 		tasklet_schedule(&wl->tasklet);
1039*b4c3e9b5SBjoern A. Zeeb 		ret = IRQ_HANDLED;
1040*b4c3e9b5SBjoern A. Zeeb 	}
1041*b4c3e9b5SBjoern A. Zeeb 
1042*b4c3e9b5SBjoern A. Zeeb 	spin_unlock(&wl->isr_lock);
1043*b4c3e9b5SBjoern A. Zeeb 
1044*b4c3e9b5SBjoern A. Zeeb 	return ret;
1045*b4c3e9b5SBjoern A. Zeeb }
1046*b4c3e9b5SBjoern A. Zeeb 
1047*b4c3e9b5SBjoern A. Zeeb /*
1048*b4c3e9b5SBjoern A. Zeeb  * is called in brcms_pci_probe() context, therefore no locking required.
1049*b4c3e9b5SBjoern A. Zeeb  */
ieee_hw_rate_init(struct ieee80211_hw * hw)1050*b4c3e9b5SBjoern A. Zeeb static int ieee_hw_rate_init(struct ieee80211_hw *hw)
1051*b4c3e9b5SBjoern A. Zeeb {
1052*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = hw->priv;
1053*b4c3e9b5SBjoern A. Zeeb 	struct brcms_c_info *wlc = wl->wlc;
1054*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_supported_band *band;
1055*b4c3e9b5SBjoern A. Zeeb 	u16 phy_type;
1056*b4c3e9b5SBjoern A. Zeeb 
1057*b4c3e9b5SBjoern A. Zeeb 	hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
1058*b4c3e9b5SBjoern A. Zeeb 	hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
1059*b4c3e9b5SBjoern A. Zeeb 
1060*b4c3e9b5SBjoern A. Zeeb 	phy_type = brcms_c_get_phy_type(wl->wlc, 0);
1061*b4c3e9b5SBjoern A. Zeeb 	if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) {
1062*b4c3e9b5SBjoern A. Zeeb 		band = &wlc->bandstate[BAND_2G_INDEX]->band;
1063*b4c3e9b5SBjoern A. Zeeb 		*band = brcms_band_2GHz_nphy_template;
1064*b4c3e9b5SBjoern A. Zeeb 		if (phy_type == PHY_TYPE_LCN) {
1065*b4c3e9b5SBjoern A. Zeeb 			/* Single stream */
1066*b4c3e9b5SBjoern A. Zeeb 			band->ht_cap.mcs.rx_mask[1] = 0;
1067*b4c3e9b5SBjoern A. Zeeb 			band->ht_cap.mcs.rx_highest = cpu_to_le16(72);
1068*b4c3e9b5SBjoern A. Zeeb 		}
1069*b4c3e9b5SBjoern A. Zeeb 		hw->wiphy->bands[NL80211_BAND_2GHZ] = band;
1070*b4c3e9b5SBjoern A. Zeeb 	} else {
1071*b4c3e9b5SBjoern A. Zeeb 		return -EPERM;
1072*b4c3e9b5SBjoern A. Zeeb 	}
1073*b4c3e9b5SBjoern A. Zeeb 
1074*b4c3e9b5SBjoern A. Zeeb 	/* Assume all bands use the same phy.  True for 11n devices. */
1075*b4c3e9b5SBjoern A. Zeeb 	if (wl->pub->_nbands > 1) {
1076*b4c3e9b5SBjoern A. Zeeb 		if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) {
1077*b4c3e9b5SBjoern A. Zeeb 			band = &wlc->bandstate[BAND_5G_INDEX]->band;
1078*b4c3e9b5SBjoern A. Zeeb 			*band = brcms_band_5GHz_nphy_template;
1079*b4c3e9b5SBjoern A. Zeeb 			hw->wiphy->bands[NL80211_BAND_5GHZ] = band;
1080*b4c3e9b5SBjoern A. Zeeb 		} else {
1081*b4c3e9b5SBjoern A. Zeeb 			return -EPERM;
1082*b4c3e9b5SBjoern A. Zeeb 		}
1083*b4c3e9b5SBjoern A. Zeeb 	}
1084*b4c3e9b5SBjoern A. Zeeb 	return 0;
1085*b4c3e9b5SBjoern A. Zeeb }
1086*b4c3e9b5SBjoern A. Zeeb 
1087*b4c3e9b5SBjoern A. Zeeb /*
1088*b4c3e9b5SBjoern A. Zeeb  * is called in brcms_pci_probe() context, therefore no locking required.
1089*b4c3e9b5SBjoern A. Zeeb  */
ieee_hw_init(struct ieee80211_hw * hw)1090*b4c3e9b5SBjoern A. Zeeb static int ieee_hw_init(struct ieee80211_hw *hw)
1091*b4c3e9b5SBjoern A. Zeeb {
1092*b4c3e9b5SBjoern A. Zeeb 	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
1093*b4c3e9b5SBjoern A. Zeeb 	ieee80211_hw_set(hw, SIGNAL_DBM);
1094*b4c3e9b5SBjoern A. Zeeb 	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
1095*b4c3e9b5SBjoern A. Zeeb 	ieee80211_hw_set(hw, MFP_CAPABLE);
1096*b4c3e9b5SBjoern A. Zeeb 
1097*b4c3e9b5SBjoern A. Zeeb 	hw->extra_tx_headroom = brcms_c_get_header_len();
1098*b4c3e9b5SBjoern A. Zeeb 	hw->queues = N_TX_QUEUES;
1099*b4c3e9b5SBjoern A. Zeeb 	hw->max_rates = 2;	/* Primary rate and 1 fallback rate */
1100*b4c3e9b5SBjoern A. Zeeb 
1101*b4c3e9b5SBjoern A. Zeeb 	/* channel change time is dependent on chip and band  */
1102*b4c3e9b5SBjoern A. Zeeb 	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
1103*b4c3e9b5SBjoern A. Zeeb 				     BIT(NL80211_IFTYPE_AP) |
1104*b4c3e9b5SBjoern A. Zeeb 				     BIT(NL80211_IFTYPE_ADHOC);
1105*b4c3e9b5SBjoern A. Zeeb 
1106*b4c3e9b5SBjoern A. Zeeb 	/*
1107*b4c3e9b5SBjoern A. Zeeb 	 * deactivate sending probe responses by ucude, because this will
1108*b4c3e9b5SBjoern A. Zeeb 	 * cause problems when WPS is used.
1109*b4c3e9b5SBjoern A. Zeeb 	 *
1110*b4c3e9b5SBjoern A. Zeeb 	 * hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
1111*b4c3e9b5SBjoern A. Zeeb 	 */
1112*b4c3e9b5SBjoern A. Zeeb 
1113*b4c3e9b5SBjoern A. Zeeb 	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
1114*b4c3e9b5SBjoern A. Zeeb 
1115*b4c3e9b5SBjoern A. Zeeb 	hw->rate_control_algorithm = "minstrel_ht";
1116*b4c3e9b5SBjoern A. Zeeb 
1117*b4c3e9b5SBjoern A. Zeeb 	hw->sta_data_size = 0;
1118*b4c3e9b5SBjoern A. Zeeb 	return ieee_hw_rate_init(hw);
1119*b4c3e9b5SBjoern A. Zeeb }
1120*b4c3e9b5SBjoern A. Zeeb 
1121*b4c3e9b5SBjoern A. Zeeb /*
1122*b4c3e9b5SBjoern A. Zeeb  * attach to the WL device.
1123*b4c3e9b5SBjoern A. Zeeb  *
1124*b4c3e9b5SBjoern A. Zeeb  * Attach to the WL device identified by vendor and device parameters.
1125*b4c3e9b5SBjoern A. Zeeb  * regs is a host accessible memory address pointing to WL device registers.
1126*b4c3e9b5SBjoern A. Zeeb  *
1127*b4c3e9b5SBjoern A. Zeeb  * is called in brcms_bcma_probe() context, therefore no locking required.
1128*b4c3e9b5SBjoern A. Zeeb  */
brcms_attach(struct bcma_device * pdev)1129*b4c3e9b5SBjoern A. Zeeb static struct brcms_info *brcms_attach(struct bcma_device *pdev)
1130*b4c3e9b5SBjoern A. Zeeb {
1131*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = NULL;
1132*b4c3e9b5SBjoern A. Zeeb 	int unit, err;
1133*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_hw *hw;
1134*b4c3e9b5SBjoern A. Zeeb 	u8 perm[ETH_ALEN];
1135*b4c3e9b5SBjoern A. Zeeb 
1136*b4c3e9b5SBjoern A. Zeeb 	unit = n_adapters_found;
1137*b4c3e9b5SBjoern A. Zeeb 	err = 0;
1138*b4c3e9b5SBjoern A. Zeeb 
1139*b4c3e9b5SBjoern A. Zeeb 	if (unit < 0)
1140*b4c3e9b5SBjoern A. Zeeb 		return NULL;
1141*b4c3e9b5SBjoern A. Zeeb 
1142*b4c3e9b5SBjoern A. Zeeb 	/* allocate private info */
1143*b4c3e9b5SBjoern A. Zeeb 	hw = bcma_get_drvdata(pdev);
1144*b4c3e9b5SBjoern A. Zeeb 	if (hw != NULL)
1145*b4c3e9b5SBjoern A. Zeeb 		wl = hw->priv;
1146*b4c3e9b5SBjoern A. Zeeb 	if (WARN_ON(hw == NULL) || WARN_ON(wl == NULL))
1147*b4c3e9b5SBjoern A. Zeeb 		return NULL;
1148*b4c3e9b5SBjoern A. Zeeb 	wl->wiphy = hw->wiphy;
1149*b4c3e9b5SBjoern A. Zeeb 
1150*b4c3e9b5SBjoern A. Zeeb 	atomic_set(&wl->callbacks, 0);
1151*b4c3e9b5SBjoern A. Zeeb 
1152*b4c3e9b5SBjoern A. Zeeb 	init_waitqueue_head(&wl->tx_flush_wq);
1153*b4c3e9b5SBjoern A. Zeeb 
1154*b4c3e9b5SBjoern A. Zeeb 	/* setup the bottom half handler */
1155*b4c3e9b5SBjoern A. Zeeb 	tasklet_setup(&wl->tasklet, brcms_dpc);
1156*b4c3e9b5SBjoern A. Zeeb 
1157*b4c3e9b5SBjoern A. Zeeb 	spin_lock_init(&wl->lock);
1158*b4c3e9b5SBjoern A. Zeeb 	spin_lock_init(&wl->isr_lock);
1159*b4c3e9b5SBjoern A. Zeeb 
1160*b4c3e9b5SBjoern A. Zeeb 	/* common load-time initialization */
1161*b4c3e9b5SBjoern A. Zeeb 	wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err);
1162*b4c3e9b5SBjoern A. Zeeb 	if (!wl->wlc) {
1163*b4c3e9b5SBjoern A. Zeeb 		wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n",
1164*b4c3e9b5SBjoern A. Zeeb 			  KBUILD_MODNAME, err);
1165*b4c3e9b5SBjoern A. Zeeb 		goto fail;
1166*b4c3e9b5SBjoern A. Zeeb 	}
1167*b4c3e9b5SBjoern A. Zeeb 	wl->pub = brcms_c_pub(wl->wlc);
1168*b4c3e9b5SBjoern A. Zeeb 
1169*b4c3e9b5SBjoern A. Zeeb 	wl->pub->ieee_hw = hw;
1170*b4c3e9b5SBjoern A. Zeeb 
1171*b4c3e9b5SBjoern A. Zeeb 	/* register our interrupt handler */
1172*b4c3e9b5SBjoern A. Zeeb 	if (request_irq(pdev->irq, brcms_isr,
1173*b4c3e9b5SBjoern A. Zeeb 			IRQF_SHARED, KBUILD_MODNAME, wl)) {
1174*b4c3e9b5SBjoern A. Zeeb 		wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit);
1175*b4c3e9b5SBjoern A. Zeeb 		goto fail;
1176*b4c3e9b5SBjoern A. Zeeb 	}
1177*b4c3e9b5SBjoern A. Zeeb 	wl->irq = pdev->irq;
1178*b4c3e9b5SBjoern A. Zeeb 
1179*b4c3e9b5SBjoern A. Zeeb 	/* register module */
1180*b4c3e9b5SBjoern A. Zeeb 	brcms_c_module_register(wl->pub, "linux", wl, NULL);
1181*b4c3e9b5SBjoern A. Zeeb 
1182*b4c3e9b5SBjoern A. Zeeb 	if (ieee_hw_init(hw)) {
1183*b4c3e9b5SBjoern A. Zeeb 		wiphy_err(wl->wiphy, "wl%d: %s: ieee_hw_init failed!\n", unit,
1184*b4c3e9b5SBjoern A. Zeeb 			  __func__);
1185*b4c3e9b5SBjoern A. Zeeb 		goto fail;
1186*b4c3e9b5SBjoern A. Zeeb 	}
1187*b4c3e9b5SBjoern A. Zeeb 
1188*b4c3e9b5SBjoern A. Zeeb 	brcms_c_regd_init(wl->wlc);
1189*b4c3e9b5SBjoern A. Zeeb 
1190*b4c3e9b5SBjoern A. Zeeb 	memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN);
1191*b4c3e9b5SBjoern A. Zeeb 	if (WARN_ON(!is_valid_ether_addr(perm)))
1192*b4c3e9b5SBjoern A. Zeeb 		goto fail;
1193*b4c3e9b5SBjoern A. Zeeb 	SET_IEEE80211_PERM_ADDR(hw, perm);
1194*b4c3e9b5SBjoern A. Zeeb 
1195*b4c3e9b5SBjoern A. Zeeb 	err = ieee80211_register_hw(hw);
1196*b4c3e9b5SBjoern A. Zeeb 	if (err)
1197*b4c3e9b5SBjoern A. Zeeb 		wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status"
1198*b4c3e9b5SBjoern A. Zeeb 			  "%d\n", __func__, err);
1199*b4c3e9b5SBjoern A. Zeeb 
1200*b4c3e9b5SBjoern A. Zeeb 	if (wl->pub->srom_ccode[0] &&
1201*b4c3e9b5SBjoern A. Zeeb 	    regulatory_hint(wl->wiphy, wl->pub->srom_ccode))
1202*b4c3e9b5SBjoern A. Zeeb 		wiphy_err(wl->wiphy, "%s: regulatory hint failed\n", __func__);
1203*b4c3e9b5SBjoern A. Zeeb 
1204*b4c3e9b5SBjoern A. Zeeb 	brcms_debugfs_attach(wl->pub);
1205*b4c3e9b5SBjoern A. Zeeb 	brcms_debugfs_create_files(wl->pub);
1206*b4c3e9b5SBjoern A. Zeeb 	n_adapters_found++;
1207*b4c3e9b5SBjoern A. Zeeb 	return wl;
1208*b4c3e9b5SBjoern A. Zeeb 
1209*b4c3e9b5SBjoern A. Zeeb fail:
1210*b4c3e9b5SBjoern A. Zeeb 	brcms_free(wl);
1211*b4c3e9b5SBjoern A. Zeeb 	return NULL;
1212*b4c3e9b5SBjoern A. Zeeb }
1213*b4c3e9b5SBjoern A. Zeeb 
1214*b4c3e9b5SBjoern A. Zeeb 
1215*b4c3e9b5SBjoern A. Zeeb 
1216*b4c3e9b5SBjoern A. Zeeb /*
1217*b4c3e9b5SBjoern A. Zeeb  * determines if a device is a WL device, and if so, attaches it.
1218*b4c3e9b5SBjoern A. Zeeb  *
1219*b4c3e9b5SBjoern A. Zeeb  * This function determines if a device pointed to by pdev is a WL device,
1220*b4c3e9b5SBjoern A. Zeeb  * and if so, performs a brcms_attach() on it.
1221*b4c3e9b5SBjoern A. Zeeb  *
1222*b4c3e9b5SBjoern A. Zeeb  * Perimeter lock is initialized in the course of this function.
1223*b4c3e9b5SBjoern A. Zeeb  */
brcms_bcma_probe(struct bcma_device * pdev)1224*b4c3e9b5SBjoern A. Zeeb static int brcms_bcma_probe(struct bcma_device *pdev)
1225*b4c3e9b5SBjoern A. Zeeb {
1226*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl;
1227*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_hw *hw;
1228*b4c3e9b5SBjoern A. Zeeb 	int ret;
1229*b4c3e9b5SBjoern A. Zeeb 
1230*b4c3e9b5SBjoern A. Zeeb 	dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n",
1231*b4c3e9b5SBjoern A. Zeeb 		 pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class,
1232*b4c3e9b5SBjoern A. Zeeb 		 pdev->irq);
1233*b4c3e9b5SBjoern A. Zeeb 
1234*b4c3e9b5SBjoern A. Zeeb 	if ((pdev->id.manuf != BCMA_MANUF_BCM) ||
1235*b4c3e9b5SBjoern A. Zeeb 	    (pdev->id.id != BCMA_CORE_80211))
1236*b4c3e9b5SBjoern A. Zeeb 		return -ENODEV;
1237*b4c3e9b5SBjoern A. Zeeb 
1238*b4c3e9b5SBjoern A. Zeeb 	hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops);
1239*b4c3e9b5SBjoern A. Zeeb 	if (!hw) {
1240*b4c3e9b5SBjoern A. Zeeb 		pr_err("%s: ieee80211_alloc_hw failed\n", __func__);
1241*b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
1242*b4c3e9b5SBjoern A. Zeeb 	}
1243*b4c3e9b5SBjoern A. Zeeb 
1244*b4c3e9b5SBjoern A. Zeeb 	SET_IEEE80211_DEV(hw, &pdev->dev);
1245*b4c3e9b5SBjoern A. Zeeb 
1246*b4c3e9b5SBjoern A. Zeeb 	bcma_set_drvdata(pdev, hw);
1247*b4c3e9b5SBjoern A. Zeeb 
1248*b4c3e9b5SBjoern A. Zeeb 	memset(hw->priv, 0, sizeof(*wl));
1249*b4c3e9b5SBjoern A. Zeeb 
1250*b4c3e9b5SBjoern A. Zeeb 	wl = brcms_attach(pdev);
1251*b4c3e9b5SBjoern A. Zeeb 	if (!wl) {
1252*b4c3e9b5SBjoern A. Zeeb 		pr_err("%s: brcms_attach failed!\n", __func__);
1253*b4c3e9b5SBjoern A. Zeeb 		ret = -ENODEV;
1254*b4c3e9b5SBjoern A. Zeeb 		goto err_free_ieee80211;
1255*b4c3e9b5SBjoern A. Zeeb 	}
1256*b4c3e9b5SBjoern A. Zeeb 	brcms_led_register(wl);
1257*b4c3e9b5SBjoern A. Zeeb 
1258*b4c3e9b5SBjoern A. Zeeb 	return 0;
1259*b4c3e9b5SBjoern A. Zeeb 
1260*b4c3e9b5SBjoern A. Zeeb err_free_ieee80211:
1261*b4c3e9b5SBjoern A. Zeeb 	ieee80211_free_hw(hw);
1262*b4c3e9b5SBjoern A. Zeeb 	return ret;
1263*b4c3e9b5SBjoern A. Zeeb }
1264*b4c3e9b5SBjoern A. Zeeb 
brcms_suspend(struct bcma_device * pdev)1265*b4c3e9b5SBjoern A. Zeeb static int brcms_suspend(struct bcma_device *pdev)
1266*b4c3e9b5SBjoern A. Zeeb {
1267*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl;
1268*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_hw *hw;
1269*b4c3e9b5SBjoern A. Zeeb 
1270*b4c3e9b5SBjoern A. Zeeb 	hw = bcma_get_drvdata(pdev);
1271*b4c3e9b5SBjoern A. Zeeb 	wl = hw->priv;
1272*b4c3e9b5SBjoern A. Zeeb 	if (!wl) {
1273*b4c3e9b5SBjoern A. Zeeb 		pr_err("%s: %s: no driver private struct!\n", KBUILD_MODNAME,
1274*b4c3e9b5SBjoern A. Zeeb 		       __func__);
1275*b4c3e9b5SBjoern A. Zeeb 		return -ENODEV;
1276*b4c3e9b5SBjoern A. Zeeb 	}
1277*b4c3e9b5SBjoern A. Zeeb 
1278*b4c3e9b5SBjoern A. Zeeb 	/* only need to flag hw is down for proper resume */
1279*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
1280*b4c3e9b5SBjoern A. Zeeb 	wl->pub->hw_up = false;
1281*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
1282*b4c3e9b5SBjoern A. Zeeb 
1283*b4c3e9b5SBjoern A. Zeeb 	brcms_dbg_info(wl->wlc->hw->d11core, "brcms_suspend ok\n");
1284*b4c3e9b5SBjoern A. Zeeb 
1285*b4c3e9b5SBjoern A. Zeeb 	return 0;
1286*b4c3e9b5SBjoern A. Zeeb }
1287*b4c3e9b5SBjoern A. Zeeb 
brcms_resume(struct bcma_device * pdev)1288*b4c3e9b5SBjoern A. Zeeb static int brcms_resume(struct bcma_device *pdev)
1289*b4c3e9b5SBjoern A. Zeeb {
1290*b4c3e9b5SBjoern A. Zeeb 	return 0;
1291*b4c3e9b5SBjoern A. Zeeb }
1292*b4c3e9b5SBjoern A. Zeeb 
1293*b4c3e9b5SBjoern A. Zeeb static struct bcma_driver brcms_bcma_driver = {
1294*b4c3e9b5SBjoern A. Zeeb 	.name     = KBUILD_MODNAME,
1295*b4c3e9b5SBjoern A. Zeeb 	.probe    = brcms_bcma_probe,
1296*b4c3e9b5SBjoern A. Zeeb 	.suspend  = brcms_suspend,
1297*b4c3e9b5SBjoern A. Zeeb 	.resume   = brcms_resume,
1298*b4c3e9b5SBjoern A. Zeeb 	.remove   = brcms_remove,
1299*b4c3e9b5SBjoern A. Zeeb 	.id_table = brcms_coreid_table,
1300*b4c3e9b5SBjoern A. Zeeb };
1301*b4c3e9b5SBjoern A. Zeeb 
1302*b4c3e9b5SBjoern A. Zeeb /*
1303*b4c3e9b5SBjoern A. Zeeb  * This is the main entry point for the brcmsmac driver.
1304*b4c3e9b5SBjoern A. Zeeb  *
1305*b4c3e9b5SBjoern A. Zeeb  * This function is scheduled upon module initialization and
1306*b4c3e9b5SBjoern A. Zeeb  * does the driver registration, which result in brcms_bcma_probe()
1307*b4c3e9b5SBjoern A. Zeeb  * call resulting in the driver bringup.
1308*b4c3e9b5SBjoern A. Zeeb  */
brcms_driver_init(struct work_struct * work)1309*b4c3e9b5SBjoern A. Zeeb static void brcms_driver_init(struct work_struct *work)
1310*b4c3e9b5SBjoern A. Zeeb {
1311*b4c3e9b5SBjoern A. Zeeb 	int error;
1312*b4c3e9b5SBjoern A. Zeeb 
1313*b4c3e9b5SBjoern A. Zeeb 	error = bcma_driver_register(&brcms_bcma_driver);
1314*b4c3e9b5SBjoern A. Zeeb 	if (error)
1315*b4c3e9b5SBjoern A. Zeeb 		pr_err("%s: register returned %d\n", __func__, error);
1316*b4c3e9b5SBjoern A. Zeeb }
1317*b4c3e9b5SBjoern A. Zeeb 
1318*b4c3e9b5SBjoern A. Zeeb static DECLARE_WORK(brcms_driver_work, brcms_driver_init);
1319*b4c3e9b5SBjoern A. Zeeb 
brcms_module_init(void)1320*b4c3e9b5SBjoern A. Zeeb static int __init brcms_module_init(void)
1321*b4c3e9b5SBjoern A. Zeeb {
1322*b4c3e9b5SBjoern A. Zeeb 	brcms_debugfs_init();
1323*b4c3e9b5SBjoern A. Zeeb 	if (!schedule_work(&brcms_driver_work))
1324*b4c3e9b5SBjoern A. Zeeb 		return -EBUSY;
1325*b4c3e9b5SBjoern A. Zeeb 
1326*b4c3e9b5SBjoern A. Zeeb 	return 0;
1327*b4c3e9b5SBjoern A. Zeeb }
1328*b4c3e9b5SBjoern A. Zeeb 
1329*b4c3e9b5SBjoern A. Zeeb /*
1330*b4c3e9b5SBjoern A. Zeeb  * This function unloads the brcmsmac driver from the system.
1331*b4c3e9b5SBjoern A. Zeeb  *
1332*b4c3e9b5SBjoern A. Zeeb  * This function unconditionally unloads the brcmsmac driver module from the
1333*b4c3e9b5SBjoern A. Zeeb  * system.
1334*b4c3e9b5SBjoern A. Zeeb  *
1335*b4c3e9b5SBjoern A. Zeeb  */
brcms_module_exit(void)1336*b4c3e9b5SBjoern A. Zeeb static void __exit brcms_module_exit(void)
1337*b4c3e9b5SBjoern A. Zeeb {
1338*b4c3e9b5SBjoern A. Zeeb 	cancel_work_sync(&brcms_driver_work);
1339*b4c3e9b5SBjoern A. Zeeb 	bcma_driver_unregister(&brcms_bcma_driver);
1340*b4c3e9b5SBjoern A. Zeeb 	brcms_debugfs_exit();
1341*b4c3e9b5SBjoern A. Zeeb }
1342*b4c3e9b5SBjoern A. Zeeb 
1343*b4c3e9b5SBjoern A. Zeeb module_init(brcms_module_init);
1344*b4c3e9b5SBjoern A. Zeeb module_exit(brcms_module_exit);
1345*b4c3e9b5SBjoern A. Zeeb 
1346*b4c3e9b5SBjoern A. Zeeb /*
1347*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1348*b4c3e9b5SBjoern A. Zeeb  */
brcms_txflowcontrol(struct brcms_info * wl,struct brcms_if * wlif,bool state,int prio)1349*b4c3e9b5SBjoern A. Zeeb void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
1350*b4c3e9b5SBjoern A. Zeeb 			 bool state, int prio)
1351*b4c3e9b5SBjoern A. Zeeb {
1352*b4c3e9b5SBjoern A. Zeeb 	brcms_err(wl->wlc->hw->d11core, "Shouldn't be here %s\n", __func__);
1353*b4c3e9b5SBjoern A. Zeeb }
1354*b4c3e9b5SBjoern A. Zeeb 
1355*b4c3e9b5SBjoern A. Zeeb /*
1356*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1357*b4c3e9b5SBjoern A. Zeeb  */
brcms_init(struct brcms_info * wl)1358*b4c3e9b5SBjoern A. Zeeb void brcms_init(struct brcms_info *wl)
1359*b4c3e9b5SBjoern A. Zeeb {
1360*b4c3e9b5SBjoern A. Zeeb 	brcms_dbg_info(wl->wlc->hw->d11core, "Initializing wl%d\n",
1361*b4c3e9b5SBjoern A. Zeeb 		       wl->pub->unit);
1362*b4c3e9b5SBjoern A. Zeeb 	brcms_reset(wl);
1363*b4c3e9b5SBjoern A. Zeeb 	brcms_c_init(wl->wlc, wl->mute_tx);
1364*b4c3e9b5SBjoern A. Zeeb }
1365*b4c3e9b5SBjoern A. Zeeb 
1366*b4c3e9b5SBjoern A. Zeeb /*
1367*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1368*b4c3e9b5SBjoern A. Zeeb  */
brcms_reset(struct brcms_info * wl)1369*b4c3e9b5SBjoern A. Zeeb uint brcms_reset(struct brcms_info *wl)
1370*b4c3e9b5SBjoern A. Zeeb {
1371*b4c3e9b5SBjoern A. Zeeb 	brcms_dbg_info(wl->wlc->hw->d11core, "Resetting wl%d\n", wl->pub->unit);
1372*b4c3e9b5SBjoern A. Zeeb 	brcms_c_reset(wl->wlc);
1373*b4c3e9b5SBjoern A. Zeeb 
1374*b4c3e9b5SBjoern A. Zeeb 	/* dpc will not be rescheduled */
1375*b4c3e9b5SBjoern A. Zeeb 	wl->resched = false;
1376*b4c3e9b5SBjoern A. Zeeb 
1377*b4c3e9b5SBjoern A. Zeeb 	/* inform publicly that interface is down */
1378*b4c3e9b5SBjoern A. Zeeb 	wl->pub->up = false;
1379*b4c3e9b5SBjoern A. Zeeb 
1380*b4c3e9b5SBjoern A. Zeeb 	return 0;
1381*b4c3e9b5SBjoern A. Zeeb }
1382*b4c3e9b5SBjoern A. Zeeb 
brcms_fatal_error(struct brcms_info * wl)1383*b4c3e9b5SBjoern A. Zeeb void brcms_fatal_error(struct brcms_info *wl)
1384*b4c3e9b5SBjoern A. Zeeb {
1385*b4c3e9b5SBjoern A. Zeeb 	brcms_err(wl->wlc->hw->d11core, "wl%d: fatal error, reinitializing\n",
1386*b4c3e9b5SBjoern A. Zeeb 		  wl->wlc->pub->unit);
1387*b4c3e9b5SBjoern A. Zeeb 	brcms_reset(wl);
1388*b4c3e9b5SBjoern A. Zeeb 	ieee80211_restart_hw(wl->pub->ieee_hw);
1389*b4c3e9b5SBjoern A. Zeeb }
1390*b4c3e9b5SBjoern A. Zeeb 
1391*b4c3e9b5SBjoern A. Zeeb /*
1392*b4c3e9b5SBjoern A. Zeeb  * These are interrupt on/off entry points. Disable interrupts
1393*b4c3e9b5SBjoern A. Zeeb  * during interrupt state transition.
1394*b4c3e9b5SBjoern A. Zeeb  */
brcms_intrson(struct brcms_info * wl)1395*b4c3e9b5SBjoern A. Zeeb void brcms_intrson(struct brcms_info *wl)
1396*b4c3e9b5SBjoern A. Zeeb {
1397*b4c3e9b5SBjoern A. Zeeb 	unsigned long flags;
1398*b4c3e9b5SBjoern A. Zeeb 
1399*b4c3e9b5SBjoern A. Zeeb 	spin_lock_irqsave(&wl->isr_lock, flags);
1400*b4c3e9b5SBjoern A. Zeeb 	brcms_c_intrson(wl->wlc);
1401*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_irqrestore(&wl->isr_lock, flags);
1402*b4c3e9b5SBjoern A. Zeeb }
1403*b4c3e9b5SBjoern A. Zeeb 
brcms_intrsoff(struct brcms_info * wl)1404*b4c3e9b5SBjoern A. Zeeb u32 brcms_intrsoff(struct brcms_info *wl)
1405*b4c3e9b5SBjoern A. Zeeb {
1406*b4c3e9b5SBjoern A. Zeeb 	unsigned long flags;
1407*b4c3e9b5SBjoern A. Zeeb 	u32 status;
1408*b4c3e9b5SBjoern A. Zeeb 
1409*b4c3e9b5SBjoern A. Zeeb 	spin_lock_irqsave(&wl->isr_lock, flags);
1410*b4c3e9b5SBjoern A. Zeeb 	status = brcms_c_intrsoff(wl->wlc);
1411*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_irqrestore(&wl->isr_lock, flags);
1412*b4c3e9b5SBjoern A. Zeeb 	return status;
1413*b4c3e9b5SBjoern A. Zeeb }
1414*b4c3e9b5SBjoern A. Zeeb 
brcms_intrsrestore(struct brcms_info * wl,u32 macintmask)1415*b4c3e9b5SBjoern A. Zeeb void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask)
1416*b4c3e9b5SBjoern A. Zeeb {
1417*b4c3e9b5SBjoern A. Zeeb 	unsigned long flags;
1418*b4c3e9b5SBjoern A. Zeeb 
1419*b4c3e9b5SBjoern A. Zeeb 	spin_lock_irqsave(&wl->isr_lock, flags);
1420*b4c3e9b5SBjoern A. Zeeb 	brcms_c_intrsrestore(wl->wlc, macintmask);
1421*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_irqrestore(&wl->isr_lock, flags);
1422*b4c3e9b5SBjoern A. Zeeb }
1423*b4c3e9b5SBjoern A. Zeeb 
1424*b4c3e9b5SBjoern A. Zeeb /*
1425*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1426*b4c3e9b5SBjoern A. Zeeb  */
brcms_up(struct brcms_info * wl)1427*b4c3e9b5SBjoern A. Zeeb int brcms_up(struct brcms_info *wl)
1428*b4c3e9b5SBjoern A. Zeeb {
1429*b4c3e9b5SBjoern A. Zeeb 	int error = 0;
1430*b4c3e9b5SBjoern A. Zeeb 
1431*b4c3e9b5SBjoern A. Zeeb 	if (wl->pub->up)
1432*b4c3e9b5SBjoern A. Zeeb 		return 0;
1433*b4c3e9b5SBjoern A. Zeeb 
1434*b4c3e9b5SBjoern A. Zeeb 	error = brcms_c_up(wl->wlc);
1435*b4c3e9b5SBjoern A. Zeeb 
1436*b4c3e9b5SBjoern A. Zeeb 	return error;
1437*b4c3e9b5SBjoern A. Zeeb }
1438*b4c3e9b5SBjoern A. Zeeb 
1439*b4c3e9b5SBjoern A. Zeeb /*
1440*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1441*b4c3e9b5SBjoern A. Zeeb  */
brcms_down(struct brcms_info * wl)1442*b4c3e9b5SBjoern A. Zeeb void brcms_down(struct brcms_info *wl)
1443*b4c3e9b5SBjoern A. Zeeb 	__must_hold(&wl->lock)
1444*b4c3e9b5SBjoern A. Zeeb {
1445*b4c3e9b5SBjoern A. Zeeb 	uint callbacks, ret_val = 0;
1446*b4c3e9b5SBjoern A. Zeeb 
1447*b4c3e9b5SBjoern A. Zeeb 	/* call common down function */
1448*b4c3e9b5SBjoern A. Zeeb 	ret_val = brcms_c_down(wl->wlc);
1449*b4c3e9b5SBjoern A. Zeeb 	callbacks = atomic_read(&wl->callbacks) - ret_val;
1450*b4c3e9b5SBjoern A. Zeeb 
1451*b4c3e9b5SBjoern A. Zeeb 	/* wait for down callbacks to complete */
1452*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
1453*b4c3e9b5SBjoern A. Zeeb 
1454*b4c3e9b5SBjoern A. Zeeb 	/* For HIGH_only driver, it's important to actually schedule other work,
1455*b4c3e9b5SBjoern A. Zeeb 	 * not just spin wait since everything runs at schedule level
1456*b4c3e9b5SBjoern A. Zeeb 	 */
1457*b4c3e9b5SBjoern A. Zeeb 	SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000);
1458*b4c3e9b5SBjoern A. Zeeb 
1459*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
1460*b4c3e9b5SBjoern A. Zeeb }
1461*b4c3e9b5SBjoern A. Zeeb 
1462*b4c3e9b5SBjoern A. Zeeb /*
1463*b4c3e9b5SBjoern A. Zeeb * precondition: perimeter lock is not acquired
1464*b4c3e9b5SBjoern A. Zeeb  */
_brcms_timer(struct work_struct * work)1465*b4c3e9b5SBjoern A. Zeeb static void _brcms_timer(struct work_struct *work)
1466*b4c3e9b5SBjoern A. Zeeb {
1467*b4c3e9b5SBjoern A. Zeeb 	struct brcms_timer *t = container_of(work, struct brcms_timer,
1468*b4c3e9b5SBjoern A. Zeeb 					     dly_wrk.work);
1469*b4c3e9b5SBjoern A. Zeeb 
1470*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&t->wl->lock);
1471*b4c3e9b5SBjoern A. Zeeb 
1472*b4c3e9b5SBjoern A. Zeeb 	if (t->set) {
1473*b4c3e9b5SBjoern A. Zeeb 		if (t->periodic) {
1474*b4c3e9b5SBjoern A. Zeeb 			atomic_inc(&t->wl->callbacks);
1475*b4c3e9b5SBjoern A. Zeeb 			ieee80211_queue_delayed_work(t->wl->pub->ieee_hw,
1476*b4c3e9b5SBjoern A. Zeeb 						     &t->dly_wrk,
1477*b4c3e9b5SBjoern A. Zeeb 						     msecs_to_jiffies(t->ms));
1478*b4c3e9b5SBjoern A. Zeeb 		} else {
1479*b4c3e9b5SBjoern A. Zeeb 			t->set = false;
1480*b4c3e9b5SBjoern A. Zeeb 		}
1481*b4c3e9b5SBjoern A. Zeeb 
1482*b4c3e9b5SBjoern A. Zeeb 		t->fn(t->arg);
1483*b4c3e9b5SBjoern A. Zeeb 	}
1484*b4c3e9b5SBjoern A. Zeeb 
1485*b4c3e9b5SBjoern A. Zeeb 	atomic_dec(&t->wl->callbacks);
1486*b4c3e9b5SBjoern A. Zeeb 
1487*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&t->wl->lock);
1488*b4c3e9b5SBjoern A. Zeeb }
1489*b4c3e9b5SBjoern A. Zeeb 
1490*b4c3e9b5SBjoern A. Zeeb /*
1491*b4c3e9b5SBjoern A. Zeeb  * Adds a timer to the list. Caller supplies a timer function.
1492*b4c3e9b5SBjoern A. Zeeb  * Is called from wlc.
1493*b4c3e9b5SBjoern A. Zeeb  *
1494*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1495*b4c3e9b5SBjoern A. Zeeb  */
brcms_init_timer(struct brcms_info * wl,void (* fn)(void * arg),void * arg,const char * name)1496*b4c3e9b5SBjoern A. Zeeb struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
1497*b4c3e9b5SBjoern A. Zeeb 				     void (*fn) (void *arg),
1498*b4c3e9b5SBjoern A. Zeeb 				     void *arg, const char *name)
1499*b4c3e9b5SBjoern A. Zeeb {
1500*b4c3e9b5SBjoern A. Zeeb 	struct brcms_timer *t;
1501*b4c3e9b5SBjoern A. Zeeb 
1502*b4c3e9b5SBjoern A. Zeeb 	t = kzalloc(sizeof(*t), GFP_ATOMIC);
1503*b4c3e9b5SBjoern A. Zeeb 	if (!t)
1504*b4c3e9b5SBjoern A. Zeeb 		return NULL;
1505*b4c3e9b5SBjoern A. Zeeb 
1506*b4c3e9b5SBjoern A. Zeeb 	INIT_DELAYED_WORK(&t->dly_wrk, _brcms_timer);
1507*b4c3e9b5SBjoern A. Zeeb 	t->wl = wl;
1508*b4c3e9b5SBjoern A. Zeeb 	t->fn = fn;
1509*b4c3e9b5SBjoern A. Zeeb 	t->arg = arg;
1510*b4c3e9b5SBjoern A. Zeeb 	t->next = wl->timers;
1511*b4c3e9b5SBjoern A. Zeeb 	wl->timers = t;
1512*b4c3e9b5SBjoern A. Zeeb 
1513*b4c3e9b5SBjoern A. Zeeb #ifdef DEBUG
1514*b4c3e9b5SBjoern A. Zeeb 	t->name = kstrdup(name, GFP_ATOMIC);
1515*b4c3e9b5SBjoern A. Zeeb #endif
1516*b4c3e9b5SBjoern A. Zeeb 
1517*b4c3e9b5SBjoern A. Zeeb 	return t;
1518*b4c3e9b5SBjoern A. Zeeb }
1519*b4c3e9b5SBjoern A. Zeeb 
1520*b4c3e9b5SBjoern A. Zeeb /*
1521*b4c3e9b5SBjoern A. Zeeb  * adds only the kernel timer since it's going to be more accurate
1522*b4c3e9b5SBjoern A. Zeeb  * as well as it's easier to make it periodic
1523*b4c3e9b5SBjoern A. Zeeb  *
1524*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1525*b4c3e9b5SBjoern A. Zeeb  */
brcms_add_timer(struct brcms_timer * t,uint ms,int periodic)1526*b4c3e9b5SBjoern A. Zeeb void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic)
1527*b4c3e9b5SBjoern A. Zeeb {
1528*b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_hw *hw = t->wl->pub->ieee_hw;
1529*b4c3e9b5SBjoern A. Zeeb 
1530*b4c3e9b5SBjoern A. Zeeb #ifdef DEBUG
1531*b4c3e9b5SBjoern A. Zeeb 	if (t->set)
1532*b4c3e9b5SBjoern A. Zeeb 		brcms_dbg_info(t->wl->wlc->hw->d11core,
1533*b4c3e9b5SBjoern A. Zeeb 			       "%s: Already set. Name: %s, per %d\n",
1534*b4c3e9b5SBjoern A. Zeeb 			       __func__, t->name, periodic);
1535*b4c3e9b5SBjoern A. Zeeb #endif
1536*b4c3e9b5SBjoern A. Zeeb 	t->ms = ms;
1537*b4c3e9b5SBjoern A. Zeeb 	t->periodic = (bool) periodic;
1538*b4c3e9b5SBjoern A. Zeeb 	if (!t->set) {
1539*b4c3e9b5SBjoern A. Zeeb 		t->set = true;
1540*b4c3e9b5SBjoern A. Zeeb 		atomic_inc(&t->wl->callbacks);
1541*b4c3e9b5SBjoern A. Zeeb 	}
1542*b4c3e9b5SBjoern A. Zeeb 
1543*b4c3e9b5SBjoern A. Zeeb 	ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms));
1544*b4c3e9b5SBjoern A. Zeeb }
1545*b4c3e9b5SBjoern A. Zeeb 
1546*b4c3e9b5SBjoern A. Zeeb /*
1547*b4c3e9b5SBjoern A. Zeeb  * return true if timer successfully deleted, false if still pending
1548*b4c3e9b5SBjoern A. Zeeb  *
1549*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1550*b4c3e9b5SBjoern A. Zeeb  */
brcms_del_timer(struct brcms_timer * t)1551*b4c3e9b5SBjoern A. Zeeb bool brcms_del_timer(struct brcms_timer *t)
1552*b4c3e9b5SBjoern A. Zeeb {
1553*b4c3e9b5SBjoern A. Zeeb 	if (t->set) {
1554*b4c3e9b5SBjoern A. Zeeb 		t->set = false;
1555*b4c3e9b5SBjoern A. Zeeb 		if (!cancel_delayed_work(&t->dly_wrk))
1556*b4c3e9b5SBjoern A. Zeeb 			return false;
1557*b4c3e9b5SBjoern A. Zeeb 
1558*b4c3e9b5SBjoern A. Zeeb 		atomic_dec(&t->wl->callbacks);
1559*b4c3e9b5SBjoern A. Zeeb 	}
1560*b4c3e9b5SBjoern A. Zeeb 
1561*b4c3e9b5SBjoern A. Zeeb 	return true;
1562*b4c3e9b5SBjoern A. Zeeb }
1563*b4c3e9b5SBjoern A. Zeeb 
1564*b4c3e9b5SBjoern A. Zeeb /*
1565*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1566*b4c3e9b5SBjoern A. Zeeb  */
brcms_free_timer(struct brcms_timer * t)1567*b4c3e9b5SBjoern A. Zeeb void brcms_free_timer(struct brcms_timer *t)
1568*b4c3e9b5SBjoern A. Zeeb {
1569*b4c3e9b5SBjoern A. Zeeb 	struct brcms_info *wl = t->wl;
1570*b4c3e9b5SBjoern A. Zeeb 	struct brcms_timer *tmp;
1571*b4c3e9b5SBjoern A. Zeeb 
1572*b4c3e9b5SBjoern A. Zeeb 	/* delete the timer in case it is active */
1573*b4c3e9b5SBjoern A. Zeeb 	brcms_del_timer(t);
1574*b4c3e9b5SBjoern A. Zeeb 
1575*b4c3e9b5SBjoern A. Zeeb 	if (wl->timers == t) {
1576*b4c3e9b5SBjoern A. Zeeb 		wl->timers = wl->timers->next;
1577*b4c3e9b5SBjoern A. Zeeb #ifdef DEBUG
1578*b4c3e9b5SBjoern A. Zeeb 		kfree(t->name);
1579*b4c3e9b5SBjoern A. Zeeb #endif
1580*b4c3e9b5SBjoern A. Zeeb 		kfree(t);
1581*b4c3e9b5SBjoern A. Zeeb 		return;
1582*b4c3e9b5SBjoern A. Zeeb 
1583*b4c3e9b5SBjoern A. Zeeb 	}
1584*b4c3e9b5SBjoern A. Zeeb 
1585*b4c3e9b5SBjoern A. Zeeb 	tmp = wl->timers;
1586*b4c3e9b5SBjoern A. Zeeb 	while (tmp) {
1587*b4c3e9b5SBjoern A. Zeeb 		if (tmp->next == t) {
1588*b4c3e9b5SBjoern A. Zeeb 			tmp->next = t->next;
1589*b4c3e9b5SBjoern A. Zeeb #ifdef DEBUG
1590*b4c3e9b5SBjoern A. Zeeb 			kfree(t->name);
1591*b4c3e9b5SBjoern A. Zeeb #endif
1592*b4c3e9b5SBjoern A. Zeeb 			kfree(t);
1593*b4c3e9b5SBjoern A. Zeeb 			return;
1594*b4c3e9b5SBjoern A. Zeeb 		}
1595*b4c3e9b5SBjoern A. Zeeb 		tmp = tmp->next;
1596*b4c3e9b5SBjoern A. Zeeb 	}
1597*b4c3e9b5SBjoern A. Zeeb 
1598*b4c3e9b5SBjoern A. Zeeb }
1599*b4c3e9b5SBjoern A. Zeeb 
1600*b4c3e9b5SBjoern A. Zeeb /*
1601*b4c3e9b5SBjoern A. Zeeb  * precondition: no locking required
1602*b4c3e9b5SBjoern A. Zeeb  */
brcms_ucode_init_buf(struct brcms_info * wl,void ** pbuf,u32 idx)1603*b4c3e9b5SBjoern A. Zeeb int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx)
1604*b4c3e9b5SBjoern A. Zeeb {
1605*b4c3e9b5SBjoern A. Zeeb 	int i, entry;
1606*b4c3e9b5SBjoern A. Zeeb 	const u8 *pdata;
1607*b4c3e9b5SBjoern A. Zeeb 	struct firmware_hdr *hdr;
1608*b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < wl->fw.fw_cnt; i++) {
1609*b4c3e9b5SBjoern A. Zeeb 		hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
1610*b4c3e9b5SBjoern A. Zeeb 		for (entry = 0; entry < wl->fw.hdr_num_entries[i];
1611*b4c3e9b5SBjoern A. Zeeb 		     entry++, hdr++) {
1612*b4c3e9b5SBjoern A. Zeeb 			u32 len = le32_to_cpu(hdr->len);
1613*b4c3e9b5SBjoern A. Zeeb 			if (le32_to_cpu(hdr->idx) == idx) {
1614*b4c3e9b5SBjoern A. Zeeb 				pdata = wl->fw.fw_bin[i]->data +
1615*b4c3e9b5SBjoern A. Zeeb 					le32_to_cpu(hdr->offset);
1616*b4c3e9b5SBjoern A. Zeeb 				*pbuf = kvmemdup(pdata, len, GFP_KERNEL);
1617*b4c3e9b5SBjoern A. Zeeb 				if (*pbuf == NULL)
1618*b4c3e9b5SBjoern A. Zeeb 					return -ENOMEM;
1619*b4c3e9b5SBjoern A. Zeeb 				return 0;
1620*b4c3e9b5SBjoern A. Zeeb 			}
1621*b4c3e9b5SBjoern A. Zeeb 		}
1622*b4c3e9b5SBjoern A. Zeeb 	}
1623*b4c3e9b5SBjoern A. Zeeb 	brcms_err(wl->wlc->hw->d11core,
1624*b4c3e9b5SBjoern A. Zeeb 		  "ERROR: ucode buf tag:%d can not be found!\n", idx);
1625*b4c3e9b5SBjoern A. Zeeb 	*pbuf = NULL;
1626*b4c3e9b5SBjoern A. Zeeb 	return -ENODATA;
1627*b4c3e9b5SBjoern A. Zeeb }
1628*b4c3e9b5SBjoern A. Zeeb 
1629*b4c3e9b5SBjoern A. Zeeb /*
1630*b4c3e9b5SBjoern A. Zeeb  * Precondition: Since this function is called in brcms_bcma_probe() context,
1631*b4c3e9b5SBjoern A. Zeeb  * no locking is required.
1632*b4c3e9b5SBjoern A. Zeeb  */
brcms_ucode_init_uint(struct brcms_info * wl,size_t * n_bytes,u32 idx)1633*b4c3e9b5SBjoern A. Zeeb int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx)
1634*b4c3e9b5SBjoern A. Zeeb {
1635*b4c3e9b5SBjoern A. Zeeb 	int i, entry;
1636*b4c3e9b5SBjoern A. Zeeb 	const u8 *pdata;
1637*b4c3e9b5SBjoern A. Zeeb 	struct firmware_hdr *hdr;
1638*b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < wl->fw.fw_cnt; i++) {
1639*b4c3e9b5SBjoern A. Zeeb 		hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
1640*b4c3e9b5SBjoern A. Zeeb 		for (entry = 0; entry < wl->fw.hdr_num_entries[i];
1641*b4c3e9b5SBjoern A. Zeeb 		     entry++, hdr++) {
1642*b4c3e9b5SBjoern A. Zeeb 			if (le32_to_cpu(hdr->idx) == idx) {
1643*b4c3e9b5SBjoern A. Zeeb 				pdata = wl->fw.fw_bin[i]->data +
1644*b4c3e9b5SBjoern A. Zeeb 					le32_to_cpu(hdr->offset);
1645*b4c3e9b5SBjoern A. Zeeb 				if (le32_to_cpu(hdr->len) != 4) {
1646*b4c3e9b5SBjoern A. Zeeb 					brcms_err(wl->wlc->hw->d11core,
1647*b4c3e9b5SBjoern A. Zeeb 						  "ERROR: fw hdr len\n");
1648*b4c3e9b5SBjoern A. Zeeb 					return -ENOMSG;
1649*b4c3e9b5SBjoern A. Zeeb 				}
1650*b4c3e9b5SBjoern A. Zeeb 				*n_bytes = le32_to_cpu(*((__le32 *) pdata));
1651*b4c3e9b5SBjoern A. Zeeb 				return 0;
1652*b4c3e9b5SBjoern A. Zeeb 			}
1653*b4c3e9b5SBjoern A. Zeeb 		}
1654*b4c3e9b5SBjoern A. Zeeb 	}
1655*b4c3e9b5SBjoern A. Zeeb 	brcms_err(wl->wlc->hw->d11core,
1656*b4c3e9b5SBjoern A. Zeeb 		  "ERROR: ucode tag:%d can not be found!\n", idx);
1657*b4c3e9b5SBjoern A. Zeeb 	return -ENOMSG;
1658*b4c3e9b5SBjoern A. Zeeb }
1659*b4c3e9b5SBjoern A. Zeeb 
1660*b4c3e9b5SBjoern A. Zeeb /*
1661*b4c3e9b5SBjoern A. Zeeb  * precondition: can both be called locked and unlocked
1662*b4c3e9b5SBjoern A. Zeeb  */
brcms_ucode_free_buf(void * p)1663*b4c3e9b5SBjoern A. Zeeb void brcms_ucode_free_buf(void *p)
1664*b4c3e9b5SBjoern A. Zeeb {
1665*b4c3e9b5SBjoern A. Zeeb 	kvfree(p);
1666*b4c3e9b5SBjoern A. Zeeb }
1667*b4c3e9b5SBjoern A. Zeeb 
1668*b4c3e9b5SBjoern A. Zeeb /*
1669*b4c3e9b5SBjoern A. Zeeb  * checks validity of all firmware images loaded from user space
1670*b4c3e9b5SBjoern A. Zeeb  *
1671*b4c3e9b5SBjoern A. Zeeb  * Precondition: Since this function is called in brcms_bcma_probe() context,
1672*b4c3e9b5SBjoern A. Zeeb  * no locking is required.
1673*b4c3e9b5SBjoern A. Zeeb  */
brcms_check_firmwares(struct brcms_info * wl)1674*b4c3e9b5SBjoern A. Zeeb int brcms_check_firmwares(struct brcms_info *wl)
1675*b4c3e9b5SBjoern A. Zeeb {
1676*b4c3e9b5SBjoern A. Zeeb 	int i;
1677*b4c3e9b5SBjoern A. Zeeb 	int entry;
1678*b4c3e9b5SBjoern A. Zeeb 	int rc = 0;
1679*b4c3e9b5SBjoern A. Zeeb 	const struct firmware *fw;
1680*b4c3e9b5SBjoern A. Zeeb 	const struct firmware *fw_hdr;
1681*b4c3e9b5SBjoern A. Zeeb 	struct firmware_hdr *ucode_hdr;
1682*b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < MAX_FW_IMAGES && rc == 0; i++) {
1683*b4c3e9b5SBjoern A. Zeeb 		fw =  wl->fw.fw_bin[i];
1684*b4c3e9b5SBjoern A. Zeeb 		fw_hdr = wl->fw.fw_hdr[i];
1685*b4c3e9b5SBjoern A. Zeeb 		if (fw == NULL && fw_hdr == NULL) {
1686*b4c3e9b5SBjoern A. Zeeb 			break;
1687*b4c3e9b5SBjoern A. Zeeb 		} else if (fw == NULL || fw_hdr == NULL) {
1688*b4c3e9b5SBjoern A. Zeeb 			wiphy_err(wl->wiphy, "%s: invalid bin/hdr fw\n",
1689*b4c3e9b5SBjoern A. Zeeb 				  __func__);
1690*b4c3e9b5SBjoern A. Zeeb 			rc = -EBADF;
1691*b4c3e9b5SBjoern A. Zeeb 		} else if (fw_hdr->size % sizeof(struct firmware_hdr)) {
1692*b4c3e9b5SBjoern A. Zeeb 			wiphy_err(wl->wiphy, "%s: non integral fw hdr file "
1693*b4c3e9b5SBjoern A. Zeeb 				"size %zu/%zu\n", __func__, fw_hdr->size,
1694*b4c3e9b5SBjoern A. Zeeb 				sizeof(struct firmware_hdr));
1695*b4c3e9b5SBjoern A. Zeeb 			rc = -EBADF;
1696*b4c3e9b5SBjoern A. Zeeb 		} else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) {
1697*b4c3e9b5SBjoern A. Zeeb 			wiphy_err(wl->wiphy, "%s: out of bounds fw file size %zu\n",
1698*b4c3e9b5SBjoern A. Zeeb 				  __func__, fw->size);
1699*b4c3e9b5SBjoern A. Zeeb 			rc = -EBADF;
1700*b4c3e9b5SBjoern A. Zeeb 		} else {
1701*b4c3e9b5SBjoern A. Zeeb 			/* check if ucode section overruns firmware image */
1702*b4c3e9b5SBjoern A. Zeeb 			ucode_hdr = (struct firmware_hdr *)fw_hdr->data;
1703*b4c3e9b5SBjoern A. Zeeb 			for (entry = 0; entry < wl->fw.hdr_num_entries[i] &&
1704*b4c3e9b5SBjoern A. Zeeb 			     !rc; entry++, ucode_hdr++) {
1705*b4c3e9b5SBjoern A. Zeeb 				if (le32_to_cpu(ucode_hdr->offset) +
1706*b4c3e9b5SBjoern A. Zeeb 				    le32_to_cpu(ucode_hdr->len) >
1707*b4c3e9b5SBjoern A. Zeeb 				    fw->size) {
1708*b4c3e9b5SBjoern A. Zeeb 					wiphy_err(wl->wiphy,
1709*b4c3e9b5SBjoern A. Zeeb 						  "%s: conflicting bin/hdr\n",
1710*b4c3e9b5SBjoern A. Zeeb 						  __func__);
1711*b4c3e9b5SBjoern A. Zeeb 					rc = -EBADF;
1712*b4c3e9b5SBjoern A. Zeeb 				}
1713*b4c3e9b5SBjoern A. Zeeb 			}
1714*b4c3e9b5SBjoern A. Zeeb 		}
1715*b4c3e9b5SBjoern A. Zeeb 	}
1716*b4c3e9b5SBjoern A. Zeeb 	if (rc == 0 && wl->fw.fw_cnt != i) {
1717*b4c3e9b5SBjoern A. Zeeb 		wiphy_err(wl->wiphy, "%s: invalid fw_cnt=%d\n", __func__,
1718*b4c3e9b5SBjoern A. Zeeb 			wl->fw.fw_cnt);
1719*b4c3e9b5SBjoern A. Zeeb 		rc = -EBADF;
1720*b4c3e9b5SBjoern A. Zeeb 	}
1721*b4c3e9b5SBjoern A. Zeeb 	return rc;
1722*b4c3e9b5SBjoern A. Zeeb }
1723*b4c3e9b5SBjoern A. Zeeb 
1724*b4c3e9b5SBjoern A. Zeeb /*
1725*b4c3e9b5SBjoern A. Zeeb  * precondition: perimeter lock has been acquired
1726*b4c3e9b5SBjoern A. Zeeb  */
brcms_rfkill_set_hw_state(struct brcms_info * wl)1727*b4c3e9b5SBjoern A. Zeeb bool brcms_rfkill_set_hw_state(struct brcms_info *wl)
1728*b4c3e9b5SBjoern A. Zeeb 	__must_hold(&wl->lock)
1729*b4c3e9b5SBjoern A. Zeeb {
1730*b4c3e9b5SBjoern A. Zeeb 	bool blocked = brcms_c_check_radio_disabled(wl->wlc);
1731*b4c3e9b5SBjoern A. Zeeb 
1732*b4c3e9b5SBjoern A. Zeeb 	spin_unlock_bh(&wl->lock);
1733*b4c3e9b5SBjoern A. Zeeb 	wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
1734*b4c3e9b5SBjoern A. Zeeb 	if (blocked)
1735*b4c3e9b5SBjoern A. Zeeb 		wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy);
1736*b4c3e9b5SBjoern A. Zeeb 	spin_lock_bh(&wl->lock);
1737*b4c3e9b5SBjoern A. Zeeb 	return blocked;
1738*b4c3e9b5SBjoern A. Zeeb }
1739