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