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