staging: brcm80211: purge epivers.h
[deliverable/linux.git] / drivers / staging / brcm80211 / sys / wl_mac80211.c
CommitLineData
a9533e7e
HP
1/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#define __UNDEF_NO_VERSION__
18
a9533e7e 19#include <linux/kernel.h>
a9533e7e 20#include <linux/etherdevice.h>
a9533e7e 21#include <linux/string.h>
a9533e7e 22#include <linux/pci_ids.h>
a1c16ed2 23#include <bcmdefs.h>
c6ac24e9
BR
24#include <linux/module.h>
25#include <linux/pci.h>
26#include <linux/sched.h>
a1c16ed2 27#include <osl.h>
a9533e7e
HP
28#define WLC_MAXBSSCFG 1 /* single BSS configs */
29
a9533e7e
HP
30#include <wlc_cfg.h>
31#include <net/mac80211.h>
a9533e7e
HP
32#ifndef WLC_HIGH_ONLY
33#include <phy_version.h>
34#endif
a9533e7e
HP
35#include <bcmutils.h>
36#include <pcicfg.h>
37#include <wlioctl.h>
38#include <wlc_key.h>
a52ba66c
BR
39#include <sbhndpio.h>
40#include <sbhnddma.h>
a9533e7e
HP
41#include <wlc_channel.h>
42#include <wlc_pub.h>
43#include <wlc_scb.h>
44#include <wl_dbg.h>
45#ifdef BCMSDIO
46#include <bcmsdh.h>
47#endif
48#include <wl_export.h>
49#ifdef WLC_HIGH_ONLY
50#include "dbus.h"
51#include "bcm_rpc_tp.h"
52#include "bcm_rpc.h"
53#include "bcm_xdr.h"
54#include "wlc_rpc.h"
55#endif
56
57#include <wl_mac80211.h>
58#include <linux/firmware.h>
59#ifndef WLC_HIGH_ONLY
60#include <wl_ucode.h>
61#include <d11ucode_ext.h>
62#endif
63
64#ifdef BCMSDIO
65extern struct device *sdiommc_dev;
66#endif
67
7cc4a4c0 68extern void wlc_wme_setparams(wlc_info_t *wlc, u16 aci, void *arg,
a9533e7e 69 bool suspend);
7cc4a4c0
JC
70bool wlc_sendpkt_mac80211(wlc_info_t *wlc, void *sdu, struct ieee80211_hw *hw);
71void wlc_mac_bcn_promisc_change(wlc_info_t *wlc, bool promisc);
72void wlc_set_addrmatch(wlc_info_t *wlc, int match_reg_offset,
a9533e7e
HP
73 const struct ether_addr *addr);
74
3deea904 75static void wl_timer(unsigned long data);
7cc4a4c0 76static void _wl_timer(wl_timer_t *t);
a9533e7e 77
a9533e7e
HP
78#ifdef WLC_HIGH_ONLY
79#define RPCQ_LOCK(_wl, _flags) spin_lock_irqsave(&(_wl)->rpcq_lock, (_flags))
80#define RPCQ_UNLOCK(_wl, _flags) spin_unlock_irqrestore(&(_wl)->rpcq_lock, (_flags))
81#define TXQ_LOCK(_wl, _flags) spin_lock_irqsave(&(_wl)->txq_lock, (_flags))
82#define TXQ_UNLOCK(_wl, _flags) spin_unlock_irqrestore(&(_wl)->txq_lock, (_flags))
83static void wl_rpc_down(void *wlh);
7cc4a4c0 84static void wl_rpcq_free(wl_info_t *wl);
a9533e7e
HP
85static void wl_rpcq_dispatch(struct wl_task *task);
86static void wl_rpc_dispatch_schedule(void *ctx, struct rpc_buf *buf);
87static void wl_start_txqwork(struct wl_task *task);
7cc4a4c0
JC
88static void wl_txq_free(wl_info_t *wl);
89static void wl_timer_task(wl_task_t *task);
90static int wl_schedule_task(wl_info_t *wl, void (*fn) (struct wl_task *),
a9533e7e
HP
91 void *context);
92#endif /* WLC_HIGH_ONLY */
93
94static int ieee_hw_init(struct ieee80211_hw *hw);
95static int ieee_hw_rate_init(struct ieee80211_hw *hw);
96
97static int wl_linux_watchdog(void *ctx);
98
99/* Flags we support */
100#define MAC_FILTERS (FIF_PROMISC_IN_BSS | \
101 FIF_ALLMULTI | \
102 FIF_FCSFAIL | \
103 FIF_PLCPFAIL | \
104 FIF_CONTROL | \
105 FIF_OTHER_BSS | \
106 FIF_BCN_PRBRESP_PROMISC)
107
7e85c729 108static int wl_found;
a9533e7e
HP
109
110struct ieee80211_tkip_data {
111#define TKIP_KEY_LEN 32
112 u8 key[TKIP_KEY_LEN];
113 int key_set;
114
115 u32 tx_iv32;
116 u16 tx_iv16;
117 u16 tx_ttak[5];
118 int tx_phase1_done;
119
120 u32 rx_iv32;
121 u16 rx_iv16;
122 u16 rx_ttak[5];
123 int rx_phase1_done;
124 u32 rx_iv32_new;
125 u16 rx_iv16_new;
126
127 u32 dot11RSNAStatsTKIPReplays;
128 u32 dot11RSNAStatsTKIPICVErrors;
129 u32 dot11RSNAStatsTKIPLocalMICFailures;
130
131 int key_idx;
132
133 struct crypto_tfm *tfm_arc4;
134 struct crypto_tfm *tfm_michael;
135
136 /* scratch buffers for virt_to_page() (crypto API) */
137 u8 rx_hdr[16], tx_hdr[16];
138};
139
140#ifndef WLC_HIGH_ONLY
1a3bf747 141#define WL_DEV_IF(dev) ((wl_if_t *)netdev_priv(dev))
39dcff3f 142#define WL_INFO(dev) ((wl_info_t *)(WL_DEV_IF(dev)->wl)) /* points to wl */
7cc4a4c0
JC
143static int wl_request_fw(wl_info_t *wl, struct pci_dev *pdev);
144static void wl_release_fw(wl_info_t *wl);
a9533e7e
HP
145#endif
146
147/* local prototypes */
7cc4a4c0
JC
148static int wl_start(struct sk_buff *skb, wl_info_t *wl);
149static int wl_start_int(wl_info_t *wl, struct ieee80211_hw *hw,
a9533e7e 150 struct sk_buff *skb);
3deea904 151static void wl_dpc(unsigned long data);
a9533e7e
HP
152
153MODULE_AUTHOR("Broadcom Corporation");
154MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver.");
155MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
156MODULE_LICENSE("Dual BSD/GPL");
157
158#ifndef BCMSDIO
159/* recognized PCI IDs */
160static struct pci_device_id wl_id_table[] = {
161 {PCI_VENDOR_ID_BROADCOM, 0x4357, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 43225 2G */
162 {PCI_VENDOR_ID_BROADCOM, 0x4353, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 43224 DUAL */
163 {PCI_VENDOR_ID_BROADCOM, 0x4727, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 4313 DUAL */
164 {0}
165};
166
167MODULE_DEVICE_TABLE(pci, wl_id_table);
6f0c5bcd 168static void wl_remove(struct pci_dev *pdev);
a9533e7e
HP
169#endif /* !BCMSDIO */
170
171#ifdef BCMSDIO
172static uint sd_drivestrength = 6;
173module_param(sd_drivestrength, uint, 0);
174#endif
175
176#ifdef BCMDBG
177static int msglevel = 0xdeadbeef;
178module_param(msglevel, int, 0);
179#ifndef WLC_HIGH_ONLY
180static int phymsglevel = 0xdeadbeef;
181module_param(phymsglevel, int, 0);
182#endif /* WLC_HIGH_ONLY */
183#endif /* BCMDBG */
184
7e85c729 185static int oneonly;
a9533e7e
HP
186module_param(oneonly, int, 0);
187
7e85c729 188static int piomode;
a9533e7e
HP
189module_param(piomode, int, 0);
190
7e85c729 191static int instance_base; /* Starting instance number */
a9533e7e
HP
192module_param(instance_base, int, 0);
193
194#if defined(BCMDBG)
7e85c729 195static char *macaddr;
a9533e7e
HP
196module_param(macaddr, charp, S_IRUGO);
197#endif
198
199static int nompc = 1;
200module_param(nompc, int, 0);
201
202static char name[IFNAMSIZ] = "eth%d";
203module_param_string(name, name, IFNAMSIZ, 0);
204
205#ifndef SRCBASE
206#define SRCBASE "."
207#endif
208
209#define WL_MAGIC 0xdeadbeef
210
0d706ef4
JC
211#define HW_TO_WL(hw) (hw->priv)
212#define WL_TO_HW(wl) (wl->pub->ieee_hw)
a9533e7e
HP
213#ifdef WLC_HIGH_ONLY
214static int wl_ops_tx_nl(struct ieee80211_hw *hw, struct sk_buff *skb);
215#else
216static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
217#endif
218static int wl_ops_start(struct ieee80211_hw *hw);
219static void wl_ops_stop(struct ieee80211_hw *hw);
220static int wl_ops_add_interface(struct ieee80211_hw *hw,
221 struct ieee80211_vif *vif);
222static void wl_ops_remove_interface(struct ieee80211_hw *hw,
223 struct ieee80211_vif *vif);
224static int wl_ops_config(struct ieee80211_hw *hw, u32 changed);
225static void wl_ops_bss_info_changed(struct ieee80211_hw *hw,
226 struct ieee80211_vif *vif,
227 struct ieee80211_bss_conf *info,
228 u32 changed);
229static void wl_ops_configure_filter(struct ieee80211_hw *hw,
230 unsigned int changed_flags,
231 unsigned int *total_flags, u64 multicast);
232static int wl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
233 bool set);
234static void wl_ops_sw_scan_start(struct ieee80211_hw *hw);
235static void wl_ops_sw_scan_complete(struct ieee80211_hw *hw);
236static void wl_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf);
237static int wl_ops_get_stats(struct ieee80211_hw *hw,
238 struct ieee80211_low_level_stats *stats);
239static int wl_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
240static void wl_ops_sta_notify(struct ieee80211_hw *hw,
241 struct ieee80211_vif *vif,
242 enum sta_notify_cmd cmd,
243 struct ieee80211_sta *sta);
244static int wl_ops_conf_tx(struct ieee80211_hw *hw, u16 queue,
245 const struct ieee80211_tx_queue_params *params);
246static u64 wl_ops_get_tsf(struct ieee80211_hw *hw);
247static int wl_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
248 struct ieee80211_sta *sta);
249static int wl_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
250 struct ieee80211_sta *sta);
251static int wl_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
252 enum ieee80211_ampdu_mlme_action action,
7cc4a4c0 253 struct ieee80211_sta *sta, u16 tid, u16 *ssn);
a9533e7e
HP
254
255#ifdef WLC_HIGH_ONLY
256static int wl_ops_tx_nl(struct ieee80211_hw *hw, struct sk_buff *skb)
257{
258 int status;
259 wl_info_t *wl = hw->priv;
260 if (!wl->pub->up) {
261 WL_ERROR(("ops->tx called while down\n"));
262 status = -ENETDOWN;
263 goto done;
264 }
265 status = wl_start(skb, wl);
266 done:
267 return status;
268}
269#else
270static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
271{
272 int status;
273 wl_info_t *wl = hw->priv;
274 WL_LOCK(wl);
275 if (!wl->pub->up) {
276 WL_ERROR(("ops->tx called while down\n"));
277 status = -ENETDOWN;
278 goto done;
279 }
280 status = wl_start(skb, wl);
281 done:
282 WL_UNLOCK(wl);
283 return status;
284}
285#endif /* WLC_HIGH_ONLY */
286
287static int wl_ops_start(struct ieee80211_hw *hw)
288{
289 wl_info_t *wl = hw->priv;
290 /* struct ieee80211_channel *curchan = hw->conf.channel; */
291 WL_NONE(("%s : Initial channel: %d\n", __func__, curchan->hw_value));
292
293 WL_LOCK(wl);
294 ieee80211_wake_queues(hw);
295 WL_UNLOCK(wl);
296
297 return 0;
298}
299
300static void wl_ops_stop(struct ieee80211_hw *hw)
301{
302 wl_info_t *wl = hw->priv;
303 ASSERT(wl);
304 WL_LOCK(wl);
305 wl_down(wl);
306 ieee80211_stop_queues(hw);
307 WL_UNLOCK(wl);
308
309 return;
310}
311
312static int
313wl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
314{
315 wl_info_t *wl;
316 int err;
317
318 /* Just STA for now */
319 if (vif->type != NL80211_IFTYPE_AP &&
320 vif->type != NL80211_IFTYPE_MESH_POINT &&
321 vif->type != NL80211_IFTYPE_STATION &&
322 vif->type != NL80211_IFTYPE_WDS &&
323 vif->type != NL80211_IFTYPE_ADHOC) {
324 WL_ERROR(("%s: Attempt to add type %d, only STA for now\n",
325 __func__, vif->type));
326 return -EOPNOTSUPP;
327 }
328
329 wl = HW_TO_WL(hw);
330 WL_LOCK(wl);
331 err = wl_up(wl);
332 WL_UNLOCK(wl);
333
334 if (err != 0)
335 WL_ERROR(("%s: wl_up() returned %d\n", __func__, err));
336 return err;
337}
338
339static void
340wl_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
341{
342 return;
343}
344
345static int
346ieee_set_channel(struct ieee80211_hw *hw, struct ieee80211_channel *chan,
347 enum nl80211_channel_type type)
348{
349 wl_info_t *wl = HW_TO_WL(hw);
350 int err = 0;
351
352 switch (type) {
353 case NL80211_CHAN_HT20:
354 case NL80211_CHAN_NO_HT:
355 WL_LOCK(wl);
356 err = wlc_set(wl->wlc, WLC_SET_CHANNEL, chan->hw_value);
357 WL_UNLOCK(wl);
358 break;
359 case NL80211_CHAN_HT40MINUS:
360 case NL80211_CHAN_HT40PLUS:
361 WL_ERROR(("%s: Need to implement 40 Mhz Channels!\n",
362 __func__));
363 break;
364 }
365
366 if (err)
367 return -EIO;
368 return err;
369}
370
371static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
372{
373 struct ieee80211_conf *conf = &hw->conf;
374 wl_info_t *wl = HW_TO_WL(hw);
375 int err = 0;
376 int new_int;
377
378 if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
379 WL_NONE(("%s: Setting listen interval to %d\n",
380 __func__, conf->listen_interval));
381 if (wlc_iovar_setint
382 (wl->wlc, "bcn_li_bcn", conf->listen_interval)) {
383 WL_ERROR(("%s: Error setting listen_interval\n",
384 __func__));
385 err = -EIO;
386 goto config_out;
387 }
388 wlc_iovar_getint(wl->wlc, "bcn_li_bcn", &new_int);
389 ASSERT(new_int == conf->listen_interval);
390 }
391 if (changed & IEEE80211_CONF_CHANGE_MONITOR)
392 WL_NONE(("Need to set monitor mode\n"));
393 if (changed & IEEE80211_CONF_CHANGE_PS)
394 WL_NONE(("Need to set Power-save mode\n"));
395
396 if (changed & IEEE80211_CONF_CHANGE_POWER) {
397 WL_NONE(("%s: Setting tx power to %d dbm\n", __func__,
398 conf->power_level));
399 if (wlc_iovar_setint
400 (wl->wlc, "qtxpower", conf->power_level * 4)) {
401 WL_ERROR(("%s: Error setting power_level\n", __func__));
402 err = -EIO;
403 goto config_out;
404 }
405 wlc_iovar_getint(wl->wlc, "qtxpower", &new_int);
406 if (new_int != (conf->power_level * 4))
407 WL_ERROR(("%s: Power level req != actual, %d %d\n",
408 __func__, conf->power_level * 4, new_int));
409 }
410 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
411 err = ieee_set_channel(hw, conf->channel, conf->channel_type);
412 }
413 if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
414 WL_NONE(("%s: srl %d, lrl %d\n", __func__,
415 conf->short_frame_max_tx_count,
416 conf->long_frame_max_tx_count));
417 if (wlc_set
418 (wl->wlc, WLC_SET_SRL,
419 conf->short_frame_max_tx_count) < 0) {
420 WL_ERROR(("%s: Error setting srl\n", __func__));
421 err = -EIO;
422 goto config_out;
423 }
424 if (wlc_set(wl->wlc, WLC_SET_LRL, conf->long_frame_max_tx_count)
425 < 0) {
426 WL_ERROR(("%s: Error setting lrl\n", __func__));
427 err = -EIO;
428 goto config_out;
429 }
430 }
431
432 config_out:
433 return err;
434}
435
436static void
437wl_ops_bss_info_changed(struct ieee80211_hw *hw,
438 struct ieee80211_vif *vif,
439 struct ieee80211_bss_conf *info, u32 changed)
440{
441 wl_info_t *wl = HW_TO_WL(hw);
442 int val;
443
444#ifdef WLC_HIGH_ONLY
445 WL_LOCK(wl);
446#endif
447
448 if (changed & BSS_CHANGED_ASSOC) {
449 WL_ERROR(("Associated:\t%s\n", info->assoc ? "True" : "False"));
450 /* association status changed (associated/disassociated)
451 * also implies a change in the AID.
452 */
453 }
454 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
455 WL_NONE(("Use_cts_prot:\t%s Implement me\n",
456 info->use_cts_prot ? "True" : "False"));
457 /* CTS protection changed */
458 }
459 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
460 WL_NONE(("Short preamble:\t%s Implement me\n",
461 info->use_short_preamble ? "True" : "False"));
462 /* preamble changed */
463 }
464 if (changed & BSS_CHANGED_ERP_SLOT) {
465 WL_NONE(("Changing short slot:\t%s\n",
466 info->use_short_slot ? "True" : "False"));
467 if (info->use_short_slot)
468 val = 1;
469 else
470 val = 0;
471 wlc_set(wl->wlc, WLC_SET_SHORTSLOT_OVERRIDE, val);
472 /* slot timing changed */
473 }
474
475 if (changed & BSS_CHANGED_HT) {
476 WL_NONE(("%s: HT mode - Implement me\n", __func__));
477 /* 802.11n parameters changed */
478 }
479 if (changed & BSS_CHANGED_BASIC_RATES) {
480 WL_NONE(("Need to change Basic Rates:\t0x%x! Implement me\n",
66cbd3ab 481 (u32) info->basic_rates));
a9533e7e
HP
482 /* Basic rateset changed */
483 }
484 if (changed & BSS_CHANGED_BEACON_INT) {
485 WL_NONE(("Beacon Interval:\t%d Implement me\n",
486 info->beacon_int));
487 /* Beacon interval changed */
488 }
489 if (changed & BSS_CHANGED_BSSID) {
ba07d0cb
AS
490 WL_NONE(("new BSSID:\taid %d bss:%pM\n", info->aid,
491 info->bssid));
a9533e7e
HP
492 /* BSSID changed, for whatever reason (IBSS and managed mode) */
493 /* FIXME: need to store bssid in bsscfg */
494 wlc_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET,
495 (struct ether_addr *)info->bssid);
496 }
497 if (changed & BSS_CHANGED_BEACON) {
498 WL_ERROR(("BSS_CHANGED_BEACON\n"));
499 /* Beacon data changed, retrieve new beacon (beaconing modes) */
500 }
501 if (changed & BSS_CHANGED_BEACON_ENABLED) {
502 WL_ERROR(("Beacon enabled:\t%s\n",
503 info->enable_beacon ? "True" : "False"));
504 /* Beaconing should be enabled/disabled (beaconing modes) */
505 }
506#ifdef WLC_HIGH_ONLY
507 WL_UNLOCK(wl);
508#endif
509 return;
510}
511
512static void
513wl_ops_configure_filter(struct ieee80211_hw *hw,
514 unsigned int changed_flags,
515 unsigned int *total_flags, u64 multicast)
516{
517#ifndef WLC_HIGH_ONLY
518 wl_info_t *wl = hw->priv;
519#endif
520
521 changed_flags &= MAC_FILTERS;
522 *total_flags &= MAC_FILTERS;
523 if (changed_flags & FIF_PROMISC_IN_BSS)
524 WL_ERROR(("FIF_PROMISC_IN_BSS\n"));
525 if (changed_flags & FIF_ALLMULTI)
526 WL_ERROR(("FIF_ALLMULTI\n"));
527 if (changed_flags & FIF_FCSFAIL)
528 WL_ERROR(("FIF_FCSFAIL\n"));
529 if (changed_flags & FIF_PLCPFAIL)
530 WL_ERROR(("FIF_PLCPFAIL\n"));
531 if (changed_flags & FIF_CONTROL)
532 WL_ERROR(("FIF_CONTROL\n"));
533 if (changed_flags & FIF_OTHER_BSS)
534 WL_ERROR(("FIF_OTHER_BSS\n"));
535 if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
536 WL_NONE(("FIF_BCN_PRBRESP_PROMISC\n"));
537#ifndef WLC_HIGH_ONLY
538 WL_LOCK(wl);
539 if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
540 wl->pub->mac80211_state |= MAC80211_PROMISC_BCNS;
541 wlc_mac_bcn_promisc_change(wl->wlc, 1);
542 } else {
543 wlc_mac_bcn_promisc_change(wl->wlc, 0);
544 wl->pub->mac80211_state &= ~MAC80211_PROMISC_BCNS;
545 }
546 WL_UNLOCK(wl);
547#endif
548 }
549 return;
550}
551
552static int
553wl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
554{
555 WL_ERROR(("%s: Enter\n", __func__));
556 return 0;
557}
558
559static void wl_ops_sw_scan_start(struct ieee80211_hw *hw)
560{
561 WL_NONE(("Scan Start\n"));
562 return;
563}
564
565static void wl_ops_sw_scan_complete(struct ieee80211_hw *hw)
566{
567 WL_NONE(("Scan Complete\n"));
568 return;
569}
570
571static void wl_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf)
572{
573 WL_ERROR(("%s: Enter\n", __func__));
574 return;
575}
576
577static int
578wl_ops_get_stats(struct ieee80211_hw *hw,
579 struct ieee80211_low_level_stats *stats)
580{
581 WL_ERROR(("%s: Enter\n", __func__));
582 return 0;
583}
584
585static int wl_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
586{
587 WL_ERROR(("%s: Enter\n", __func__));
588 return 0;
589}
590
591static void
592wl_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
593 enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
594{
595 WL_NONE(("%s: Enter\n", __func__));
596 switch (cmd) {
597 default:
598 WL_ERROR(("%s: Uknown cmd = %d\n", __func__, cmd));
599 break;
600 }
601 return;
602}
603
604static int
605wl_ops_conf_tx(struct ieee80211_hw *hw, u16 queue,
606 const struct ieee80211_tx_queue_params *params)
607{
608 wl_info_t *wl = hw->priv;
609
610 WL_NONE(("%s: Enter (WME config)\n", __func__));
611 WL_NONE(("queue %d, txop %d, cwmin %d, cwmax %d, aifs %d\n", queue,
612 params->txop, params->cw_min, params->cw_max, params->aifs));
613
614 WL_LOCK(wl);
0f0881b0 615 wlc_wme_setparams(wl->wlc, queue, (void *)params, true);
a9533e7e
HP
616 WL_UNLOCK(wl);
617
618 return 0;
619}
620
621static u64 wl_ops_get_tsf(struct ieee80211_hw *hw)
622{
623 WL_ERROR(("%s: Enter\n", __func__));
624 return 0;
625}
626
627static int
628wl_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
629 struct ieee80211_sta *sta)
630{
631 struct scb *scb;
632
633 int i;
634 wl_info_t *wl = hw->priv;
635
636 /* Init the scb */
637 scb = (struct scb *)sta->drv_priv;
638 bzero(scb, sizeof(struct scb));
639 for (i = 0; i < NUMPRIO; i++)
640 scb->seqctl[i] = 0xFFFF;
641 scb->seqctl_nonqos = 0xFFFF;
642 scb->magic = SCB_MAGIC;
643
644 wl->pub->global_scb = scb;
645 wl->pub->global_ampdu = &(scb->scb_ampdu);
646 wl->pub->global_ampdu->scb = scb;
647#ifdef WLC_HIGH_ONLY
648 wl->pub->global_ampdu->max_pdu = AMPDU_NUM_MPDU;
649#else
650 wl->pub->global_ampdu->max_pdu = 16;
651#endif
652 pktq_init(&scb->scb_ampdu.txq, AMPDU_MAX_SCB_TID,
653 AMPDU_MAX_SCB_TID * PKTQ_LEN_DEFAULT);
654
0f0881b0 655 sta->ht_cap.ht_supported = true;
a9533e7e
HP
656#ifdef WLC_HIGH_ONLY
657 sta->ht_cap.ampdu_factor = AMPDU_RX_FACTOR_16K;
658#else
659 sta->ht_cap.ampdu_factor = AMPDU_RX_FACTOR_64K;
660#endif
661 sta->ht_cap.ampdu_density = AMPDU_DEF_MPDU_DENSITY;
662 sta->ht_cap.cap = IEEE80211_HT_CAP_GRN_FLD |
663 IEEE80211_HT_CAP_SGI_20 |
664 IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT;
665
666 /* minstrel_ht initiates addBA on our behalf by calling ieee80211_start_tx_ba_session() */
667 return 0;
668}
669
670static int
671wl_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
672 struct ieee80211_sta *sta)
673{
674 WL_NONE(("%s: Enter\n", __func__));
675 return 0;
676}
677
678static int
679wl_ampdu_action(struct ieee80211_hw *hw,
680 struct ieee80211_vif *vif,
681 enum ieee80211_ampdu_mlme_action action,
7cc4a4c0 682 struct ieee80211_sta *sta, u16 tid, u16 *ssn)
a9533e7e
HP
683{
684#if defined(BCMDBG)
685 struct scb *scb = (struct scb *)sta->drv_priv;
686#endif
687 wl_info_t *wl = hw->priv;
688
689 ASSERT(scb->magic == SCB_MAGIC);
690 switch (action) {
691 case IEEE80211_AMPDU_RX_START:
692 WL_NONE(("%s: action = IEEE80211_AMPDU_RX_START\n", __func__));
693 break;
694 case IEEE80211_AMPDU_RX_STOP:
695 WL_NONE(("%s: action = IEEE80211_AMPDU_RX_STOP\n", __func__));
696 break;
697 case IEEE80211_AMPDU_TX_START:
698 if (!wlc_aggregatable(wl->wlc, tid)) {
699 /* WL_ERROR(("START: tid %d is not agg' able, return FAILURE to stack\n", tid)); */
700 return -1;
701 }
702 /* XXX: Use the starting sequence number provided ... */
703 *ssn = 0;
704 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
705 break;
706
707 case IEEE80211_AMPDU_TX_STOP:
708 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
709 break;
710 case IEEE80211_AMPDU_TX_OPERATIONAL:
711 /* Not sure what to do here */
712 /* Power save wakeup */
713 WL_NONE(("%s: action = IEEE80211_AMPDU_TX_OPERATIONAL\n",
714 __func__));
715 break;
716 default:
717 WL_ERROR(("%s: Invalid command, ignoring\n", __func__));
718 }
719
720 return 0;
721}
722
723static const struct ieee80211_ops wl_ops = {
724#ifdef WLC_HIGH_ONLY
725 .tx = wl_ops_tx_nl,
726#else
727 .tx = wl_ops_tx,
728#endif
729 .start = wl_ops_start,
730 .stop = wl_ops_stop,
731 .add_interface = wl_ops_add_interface,
732 .remove_interface = wl_ops_remove_interface,
733 .config = wl_ops_config,
734 .bss_info_changed = wl_ops_bss_info_changed,
735 .configure_filter = wl_ops_configure_filter,
736 .set_tim = wl_ops_set_tim,
737 .sw_scan_start = wl_ops_sw_scan_start,
738 .sw_scan_complete = wl_ops_sw_scan_complete,
739 .set_tsf = wl_ops_set_tsf,
740 .get_stats = wl_ops_get_stats,
741 .set_rts_threshold = wl_ops_set_rts_threshold,
742 .sta_notify = wl_ops_sta_notify,
743 .conf_tx = wl_ops_conf_tx,
744 .get_tsf = wl_ops_get_tsf,
745 .sta_add = wl_sta_add,
746 .sta_remove = wl_sta_remove,
747 .ampdu_action = wl_ampdu_action,
748};
749
7cc4a4c0 750static int wl_set_hint(wl_info_t *wl, char *abbrev)
a9533e7e
HP
751{
752 WL_ERROR(("%s: Sending country code %c%c to MAC80211\n", __func__,
753 abbrev[0], abbrev[1]));
90ea2296 754 return regulatory_hint(wl->pub->ieee_hw->wiphy, abbrev);
a9533e7e
HP
755}
756
757/**
758 * attach to the WL device.
759 *
760 * Attach to the WL device identified by vendor and device parameters.
761 * regs is a host accessible memory address pointing to WL device registers.
762 *
763 * wl_attach is not defined as static because in the case where no bus
764 * is defined, wl_attach will never be called, and thus, gcc will issue
765 * a warning that this function is defined but not used if we declare
766 * it as static.
767 */
7d4df48e 768static wl_info_t *wl_attach(u16 vendor, u16 device, unsigned long regs,
a9533e7e
HP
769 uint bustype, void *btparam, uint irq)
770{
771 wl_info_t *wl;
772 osl_t *osh;
773 int unit, err;
774
3deea904 775 unsigned long base_addr;
a9533e7e 776 struct ieee80211_hw *hw;
41feb5ed 777 u8 perm[ETH_ALEN];
a9533e7e
HP
778
779 unit = wl_found + instance_base;
780 err = 0;
781
782 if (unit < 0) {
783 WL_ERROR(("wl%d: unit number overflow, exiting\n", unit));
784 return NULL;
785 }
786
787 if (oneonly && (unit != instance_base)) {
788 WL_ERROR(("wl%d: wl_attach: oneonly is set, exiting\n", unit));
789 return NULL;
790 }
791
792 /* Requires pkttag feature */
0f0881b0 793 osh = osl_attach(btparam, bustype, true);
a9533e7e
HP
794 ASSERT(osh);
795
796#ifdef WLC_HIGH_ONLY
797 hw = ieee80211_alloc_hw(sizeof(wl_info_t), &wl_ops);
798 if (!hw) {
799 WL_ERROR(("%s: ieee80211_alloc_hw failed\n", __func__));
800 ASSERT(0);
801 }
802
803 bzero(hw->priv, sizeof(*wl));
804 wl = hw->priv;
805#else
806 /* allocate private info */
807 hw = pci_get_drvdata(btparam); /* btparam == pdev */
808 wl = hw->priv;
809#endif
810 ASSERT(wl);
811
812 wl->magic = WL_MAGIC;
813 wl->osh = osh;
814 atomic_set(&wl->callbacks, 0);
815
eb4764c3 816 /* setup the bottom half handler */
3deea904 817 tasklet_init(&wl->tasklet, wl_dpc, (unsigned long) wl);
eb4764c3 818
a9533e7e
HP
819#ifdef WLC_HIGH_ONLY
820 wl->rpc_th = bcm_rpc_tp_attach(osh, NULL);
821 if (wl->rpc_th == NULL) {
822 WL_ERROR(("wl%d: %s: bcm_rpc_tp_attach failed!\n", unit,
823 __func__));
824 goto fail;
825 }
826
827 wl->rpc = bcm_rpc_attach(NULL, osh, wl->rpc_th);
828 if (wl->rpc == NULL) {
829 WL_ERROR(("wl%d: %s: bcm_rpc_attach failed!\n", unit,
830 __func__));
831 goto fail;
832 }
833
834 /* init tx work queue for wl_start/send pkt; no need to destroy workitem */
364eb72a 835 INIT_WORK(&wl->txq_task.work, (work_func_t) wl_start_txqwork);
a9533e7e
HP
836 wl->txq_task.context = wl;
837#endif /* WLC_HIGH_ONLY */
838
839#ifdef BCMSDIO
840 SET_IEEE80211_DEV(hw, sdiommc_dev);
841#endif
842
843 base_addr = regs;
844
845 if (bustype == PCI_BUS) {
846 /* piomode can be overwritten by command argument */
847 wl->piomode = piomode;
848 WL_TRACE(("PCI/%s\n", wl->piomode ? "PIO" : "DMA"));
849 } else if (bustype == RPC_BUS) {
850 /* Do nothing */
851 } else {
852 bustype = PCI_BUS;
853 WL_TRACE(("force to PCI\n"));
854 }
855 wl->bcm_bustype = bustype;
856
857#ifdef WLC_HIGH_ONLY
858 if (wl->bcm_bustype == RPC_BUS) {
859 wl->regsva = (void *)0;
860 btparam = wl->rpc;
861 } else
862#endif
ca8c1e59
JC
863 wl->regsva = ioremap_nocache(base_addr, PCI_BAR0_WINSZ);
864 if (wl->regsva == NULL) {
a9533e7e
HP
865 WL_ERROR(("wl%d: ioremap() failed\n", unit));
866 goto fail;
867 }
868#ifdef WLC_HIGH_ONLY
869 spin_lock_init(&wl->rpcq_lock);
870 spin_lock_init(&wl->txq_lock);
871
45f4d024 872 sema_init(&wl->sem, 1);
a9533e7e
HP
873#else
874 spin_lock_init(&wl->lock);
875 spin_lock_init(&wl->isr_lock);
876#endif
877
878#ifndef WLC_HIGH_ONLY
879 /* prepare ucode */
880 if (wl_request_fw(wl, (struct pci_dev *)btparam)) {
683b505b
BR
881 printf("%s: Failed to find firmware usually in %s\n",
882 KBUILD_MODNAME, "/lib/firmware/brcm");
883 wl_release_fw(wl);
884 wl_remove((struct pci_dev *)btparam);
885 goto fail1;
a9533e7e
HP
886 }
887#endif
888
889 /* common load-time initialization */
eb4764c3
BH
890 wl->wlc = wlc_attach((void *)wl, vendor, device, unit, wl->piomode, osh,
891 wl->regsva, wl->bcm_bustype, btparam, &err);
892#ifndef WLC_HIGH_ONLY
893 wl_release_fw(wl);
894#endif
895 if (!wl->wlc) {
4766ae6c
BR
896 printf("%s: wlc_attach() failed with code %d\n",
897 KBUILD_MODNAME, err);
a9533e7e
HP
898 goto fail;
899 }
a9533e7e
HP
900 wl->pub = wlc_pub(wl->wlc);
901
902 wl->pub->ieee_hw = hw;
903 ASSERT(wl->pub->ieee_hw);
904 ASSERT(wl->pub->ieee_hw->priv == wl);
905
906#ifdef WLC_HIGH_ONLY
907 REGOPSSET(osh, (osl_rreg_fn_t) wlc_reg_read,
908 (osl_wreg_fn_t) wlc_reg_write, wl->wlc);
909 wl->rpc_dispatch_ctx.rpc = wl->rpc;
910 wl->rpc_dispatch_ctx.wlc = wl->wlc;
911 bcm_rpc_rxcb_init(wl->rpc, wl, wl_rpc_dispatch_schedule, wl,
912 wl_rpc_down, NULL, NULL);
913#endif /* WLC_HIGH_ONLY */
914
915 if (nompc) {
916 if (wlc_iovar_setint(wl->wlc, "mpc", 0)) {
917 WL_ERROR(("wl%d: Error setting MPC variable to 0\n",
918 unit));
919 }
920 }
921#ifdef BCMSDIO
922 /* Set SDIO drive strength */
923 wlc_iovar_setint(wl->wlc, "sd_drivestrength", sd_drivestrength);
924#endif
925
a9533e7e
HP
926#ifdef WLC_LOW
927 /* register our interrupt handler */
928 if (request_irq(irq, wl_isr, IRQF_SHARED, KBUILD_MODNAME, wl)) {
929 WL_ERROR(("wl%d: request_irq() failed\n", unit));
930 goto fail;
931 }
932 wl->irq = irq;
933#endif /* WLC_LOW */
934
935 /* register module */
936 wlc_module_register(wl->pub, NULL, "linux", wl, NULL, wl_linux_watchdog,
937 NULL);
938
939 if (ieee_hw_init(hw)) {
940 WL_ERROR(("wl%d: %s: ieee_hw_init failed!\n", unit, __func__));
941 goto fail;
942 }
943
944 bcopy(&wl->pub->cur_etheraddr, perm, ETHER_ADDR_LEN);
945 ASSERT(is_valid_ether_addr(perm));
946 SET_IEEE80211_PERM_ADDR(hw, perm);
947
948 err = ieee80211_register_hw(hw);
949 if (err) {
950 WL_ERROR(("%s: ieee80211_register_hw failed, status %d\n",
951 __func__, err));
952 }
953
954 if (wl->pub->srom_ccode[0])
955 err = wl_set_hint(wl, wl->pub->srom_ccode);
956 else
957 err = wl_set_hint(wl, "US");
958 if (err) {
959 WL_ERROR(("%s: regulatory_hint failed, status %d\n", __func__,
960 err));
961 }
962#ifndef WLC_HIGH_ONLY
963 WL_ERROR(("wl%d: Broadcom BCM43xx 802.11 MAC80211 Driver "
4766ae6c 964 " (" PHY_VERSION_STR ")", unit));
a9533e7e 965#else
4766ae6c
BR
966 WL_ERROR(("wl%d: Broadcom BCM43xx 802.11 Splitmac MAC80211 Driver "
967 , unit));
a9533e7e
HP
968#endif
969
970#ifdef BCMDBG
971 printf(" (Compiled in " SRCBASE " at " __TIME__ " on " __DATE__ ")");
972#endif /* BCMDBG */
973 printf("\n");
974
a9533e7e
HP
975 wl_found++;
976 return wl;
977
978 fail:
979 wl_free(wl);
9e56568a 980fail1:
a9533e7e
HP
981 return NULL;
982}
983
a9533e7e 984#ifdef WLC_HIGH_ONLY
66cbd3ab
GKH
985static void *wl_dbus_probe_cb(void *arg, const char *desc, u32 bustype,
986 u32 hdrlen)
a9533e7e
HP
987{
988 wl_info_t *wl;
989 WL_ERROR(("%s:\n", __func__));
990
3deea904 991 wl = wl_attach(BCM_DNGL_VID, BCM_DNGL_BDC_PID, (unsigned long) NULL, RPC_BUS,
ca8c1e59
JC
992 NULL, 0);
993 if (!wl) {
a9533e7e
HP
994 WL_ERROR(("%s: wl_attach failed\n", __func__));
995 }
996
997 /* This is later passed to wl_dbus_disconnect_cb */
998 return wl;
999}
1000
1001static void wl_dbus_disconnect_cb(void *arg)
1002{
1003 wl_info_t *wl = arg;
1004
1005 WL_ERROR(("%s:\n", __func__));
1006
1007 if (wl) {
1008#ifdef WLC_HIGH_ONLY
1009 if (wl->pub->ieee_hw) {
1010 ieee80211_unregister_hw(wl->pub->ieee_hw);
1011 WL_ERROR(("%s: Back from down\n", __func__));
1012 }
1013 wlc_device_removed(wl->wlc);
1014 wlc_bmac_dngl_reboot(wl->rpc);
1015 bcm_rpc_down(wl->rpc);
1016#endif
1017 WL_LOCK(wl);
1018 wl_down(wl);
1019 WL_UNLOCK(wl);
1020#ifdef WLC_HIGH_ONLY
1021 if (wl->pub->ieee_hw) {
1022 ieee80211_free_hw(wl->pub->ieee_hw);
1023 WL_ERROR(("%s: Back from ieee80211_free_hw\n",
1024 __func__));
1025 wl->pub->ieee_hw = NULL;
1026 }
1027#endif
1028 wl_free(wl);
1029 }
1030}
1031#endif /* WLC_HIGH_ONLY */
1032
a9533e7e
HP
1033
1034#define CHAN2GHZ(channel, freqency, chflags) { \
1035 .band = IEEE80211_BAND_2GHZ, \
1036 .center_freq = (freqency), \
1037 .hw_value = (channel), \
1038 .flags = chflags, \
1039 .max_antenna_gain = 0, \
1040 .max_power = 19, \
1041}
1042
1043static struct ieee80211_channel wl_2ghz_chantable[] = {
1044 CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS),
1045 CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS),
1046 CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS),
1047 CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS),
1048 CHAN2GHZ(5, 2432, 0),
1049 CHAN2GHZ(6, 2437, 0),
1050 CHAN2GHZ(7, 2442, 0),
1051 CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS),
1052 CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS),
1053 CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS),
1054 CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS),
1055 CHAN2GHZ(12, 2467,
1056 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
1057 IEEE80211_CHAN_NO_HT40PLUS),
1058 CHAN2GHZ(13, 2472,
1059 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
1060 IEEE80211_CHAN_NO_HT40PLUS),
1061 CHAN2GHZ(14, 2484,
1062 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
1063 IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
1064};
1065
1066#define CHAN5GHZ(channel, chflags) { \
1067 .band = IEEE80211_BAND_5GHZ, \
1068 .center_freq = 5000 + 5*(channel), \
1069 .hw_value = (channel), \
1070 .flags = chflags, \
1071 .max_antenna_gain = 0, \
1072 .max_power = 21, \
1073}
1074
1075static struct ieee80211_channel wl_5ghz_nphy_chantable[] = {
1076 /* UNII-1 */
1077 CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS),
1078 CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS),
1079 CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS),
1080 CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS),
1081 /* UNII-2 */
1082 CHAN5GHZ(52,
1083 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1084 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1085 CHAN5GHZ(56,
1086 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1087 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1088 CHAN5GHZ(60,
1089 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1090 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1091 CHAN5GHZ(64,
1092 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1093 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1094 /* MID */
1095 CHAN5GHZ(100,
1096 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1097 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1098 CHAN5GHZ(104,
1099 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1100 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1101 CHAN5GHZ(108,
1102 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1103 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1104 CHAN5GHZ(112,
1105 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1106 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1107 CHAN5GHZ(116,
1108 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1109 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1110 CHAN5GHZ(120,
1111 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1112 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1113 CHAN5GHZ(124,
1114 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1115 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1116 CHAN5GHZ(128,
1117 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1118 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1119 CHAN5GHZ(132,
1120 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1121 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1122 CHAN5GHZ(136,
1123 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1124 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1125 CHAN5GHZ(140,
1126 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1127 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS |
1128 IEEE80211_CHAN_NO_HT40MINUS),
1129 /* UNII-3 */
1130 CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS),
1131 CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS),
1132 CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS),
1133 CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS),
1134 CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
1135};
1136
1137#define RATE(rate100m, _flags) { \
1138 .bitrate = (rate100m), \
1139 .flags = (_flags), \
1140 .hw_value = (rate100m / 5), \
1141}
1142
1143static struct ieee80211_rate wl_legacy_ratetable[] = {
1144 RATE(10, 0),
1145 RATE(20, IEEE80211_RATE_SHORT_PREAMBLE),
1146 RATE(55, IEEE80211_RATE_SHORT_PREAMBLE),
1147 RATE(110, IEEE80211_RATE_SHORT_PREAMBLE),
1148 RATE(60, 0),
1149 RATE(90, 0),
1150 RATE(120, 0),
1151 RATE(180, 0),
1152 RATE(240, 0),
1153 RATE(360, 0),
1154 RATE(480, 0),
1155 RATE(540, 0),
1156};
1157
1158static struct ieee80211_supported_band wl_band_2GHz_nphy = {
1159 .band = IEEE80211_BAND_2GHZ,
1160 .channels = wl_2ghz_chantable,
1161 .n_channels = ARRAY_SIZE(wl_2ghz_chantable),
1162 .bitrates = wl_legacy_ratetable,
1163 .n_bitrates = ARRAY_SIZE(wl_legacy_ratetable),
1164 .ht_cap = {
1165 /* from include/linux/ieee80211.h */
1166 .cap = IEEE80211_HT_CAP_GRN_FLD |
1167 IEEE80211_HT_CAP_SGI_20 |
1168 IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT,
1169#ifdef WLC_HIGH_ONLY
1170 .ht_supported = true,
1171 .ampdu_factor = AMPDU_RX_FACTOR_16K,
1172#else
1173 .ht_supported = true,
1174 .ampdu_factor = AMPDU_RX_FACTOR_64K,
1175#endif
1176 .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
1177 .mcs = {
1178 /* placeholders for now */
1179#ifdef WLC_HIGH_ONLY
1180 /*
1181 * rx_mask[0] = 0xff by default
1182 * rx_mask[1] = 0xff if number of rx chain >=2
1183 * rx_mask[2] = 0xff if number of rx chain >=3
1184 * rx_mask[4] = 1 if 40Mhz is supported
1185 */
1186 .rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0},
1187 .rx_highest = 72, /* max rate of single stream */
1188#else
1189 .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
1190 .rx_highest = 500,
1191#endif
1192 .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
1193 }
1194};
1195
1196static struct ieee80211_supported_band wl_band_5GHz_nphy = {
1197 .band = IEEE80211_BAND_5GHZ,
1198 .channels = wl_5ghz_nphy_chantable,
1199 .n_channels = ARRAY_SIZE(wl_5ghz_nphy_chantable),
1200 .bitrates = wl_legacy_ratetable + 4,
1201 .n_bitrates = ARRAY_SIZE(wl_legacy_ratetable) - 4,
1202 .ht_cap = {
1203 /* use IEEE80211_HT_CAP_* from include/linux/ieee80211.h */
1204 .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT, /* No 40 mhz yet */
1205 .ht_supported = true,
1206 .ampdu_factor = AMPDU_RX_FACTOR_64K,
1207 .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
1208 .mcs = {
1209 /* placeholders for now */
1210 .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
1211 .rx_highest = 500,
1212 .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
1213 }
1214};
1215
1216static int ieee_hw_rate_init(struct ieee80211_hw *hw)
1217{
1218 wl_info_t *wl = HW_TO_WL(hw);
1219 int has_5g;
1220 char phy_list[4];
1221
1222 has_5g = 0;
1223
1224 hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
1225 hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
1226
1227 if (wlc_get(wl->wlc, WLC_GET_PHYLIST, (int *)&phy_list) < 0) {
1228 WL_ERROR(("Phy list failed\n"));
1229 }
1230 WL_NONE(("%s: phylist = %c\n", __func__, phy_list[0]));
1231
1232#ifndef WLC_HIGH_ONLY
1233 if (phy_list[0] == 'n' || phy_list[0] == 'c') {
1234 if (phy_list[0] == 'c') {
1235 /* Single stream */
1236 wl_band_2GHz_nphy.ht_cap.mcs.rx_mask[1] = 0;
1237 wl_band_2GHz_nphy.ht_cap.mcs.rx_highest = 72;
1238 }
1239#else
1240 if (phy_list[0] == 's') {
1241#endif
1242 hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl_band_2GHz_nphy;
1243 } else {
1244 BUG();
90ea2296 1245 return -1;
a9533e7e
HP
1246 }
1247
1248 /* Assume all bands use the same phy. True for 11n devices. */
1249 if (NBANDS_PUB(wl->pub) > 1) {
1250 has_5g++;
1251#ifndef WLC_HIGH_ONLY
1252 if (phy_list[0] == 'n' || phy_list[0] == 'c') {
1253#else
1254 if (phy_list[0] == 's') {
1255#endif
1256 hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
1257 &wl_band_5GHz_nphy;
1258 } else {
90ea2296 1259 return -1;
a9533e7e
HP
1260 }
1261 }
1262
1263 WL_NONE(("%s: 2ghz = %d, 5ghz = %d\n", __func__, 1, has_5g));
1264
90ea2296 1265 return 0;
a9533e7e
HP
1266}
1267
1268static int ieee_hw_init(struct ieee80211_hw *hw)
1269{
1270 hw->flags = IEEE80211_HW_SIGNAL_DBM
1271 /* | IEEE80211_HW_CONNECTION_MONITOR What is this? */
1272 | IEEE80211_HW_REPORTS_TX_ACK_STATUS
1273 | IEEE80211_HW_AMPDU_AGGREGATION;
1274
1275 hw->extra_tx_headroom = wlc_get_header_len();
1276 /* FIXME: should get this from wlc->machwcap */
1277 hw->queues = 4;
1278 /* FIXME: this doesn't seem to be used properly in minstrel_ht.
1279 * mac80211/status.c:ieee80211_tx_status() checks this value,
1280 * but mac80211/rc80211_minstrel_ht.c:minstrel_ht_get_rate()
1281 * appears to always set 3 rates
1282 */
1283 hw->max_rates = 2; /* Primary rate and 1 fallback rate */
1284
1285 hw->channel_change_time = 7 * 1000; /* channel change time is dependant on chip and band */
1286 hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
1287
1288 hw->rate_control_algorithm = "minstrel_ht";
1289
1290 hw->sta_data_size = sizeof(struct scb);
90ea2296 1291 return ieee_hw_rate_init(hw);
a9533e7e
HP
1292}
1293
1294#ifndef BCMSDIO
1295/**
1296 * determines if a device is a WL device, and if so, attaches it.
1297 *
1298 * This function determines if a device pointed to by pdev is a WL device,
1299 * and if so, performs a wl_attach() on it.
1300 *
1301 */
1302int __devinit
1303wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
1304{
1305 int rc;
1306 wl_info_t *wl;
1307 struct ieee80211_hw *hw;
66cbd3ab 1308 u32 val;
a9533e7e
HP
1309
1310 ASSERT(pdev);
1311
1312 WL_TRACE(("%s: bus %d slot %d func %d irq %d\n", __func__,
1313 pdev->bus->number, PCI_SLOT(pdev->devfn),
1314 PCI_FUNC(pdev->devfn), pdev->irq));
1315
1316 if ((pdev->vendor != PCI_VENDOR_ID_BROADCOM) ||
1317 (((pdev->device & 0xff00) != 0x4300) &&
1318 ((pdev->device & 0xff00) != 0x4700) &&
1319 ((pdev->device < 43000) || (pdev->device > 43999))))
90ea2296 1320 return -ENODEV;
a9533e7e
HP
1321
1322 rc = pci_enable_device(pdev);
1323 if (rc) {
1324 WL_ERROR(("%s: Cannot enable device %d-%d_%d\n", __func__,
1325 pdev->bus->number, PCI_SLOT(pdev->devfn),
1326 PCI_FUNC(pdev->devfn)));
90ea2296 1327 return -ENODEV;
a9533e7e
HP
1328 }
1329 pci_set_master(pdev);
1330
1331 pci_read_config_dword(pdev, 0x40, &val);
1332 if ((val & 0x0000ff00) != 0)
1333 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
1334
1335 hw = ieee80211_alloc_hw(sizeof(wl_info_t), &wl_ops);
1336 if (!hw) {
1337 WL_ERROR(("%s: ieee80211_alloc_hw failed\n", __func__));
1338 rc = -ENOMEM;
1339 goto err_1;
1340 }
1341
1342 SET_IEEE80211_DEV(hw, &pdev->dev);
1343
1344 pci_set_drvdata(pdev, hw);
1345
1346 bzero(hw->priv, sizeof(*wl));
1347
1348 wl = wl_attach(pdev->vendor, pdev->device, pci_resource_start(pdev, 0),
1349 PCI_BUS, pdev, pdev->irq);
1350
683b505b
BR
1351 if (!wl) {
1352 WL_ERROR(("%s: %s: wl_attach failed!\n",
1353 KBUILD_MODNAME, __func__));
1354 return -ENODEV;
1355 }
a9533e7e
HP
1356 return 0;
1357 err_1:
1358 WL_ERROR(("%s: err_1: Major hoarkage\n", __func__));
1359 return 0;
1360}
1361
1362#ifdef LINUXSTA_PS
878a6673 1363static int wl_suspend(struct pci_dev *pdev, pm_message_t state)
a9533e7e
HP
1364{
1365 wl_info_t *wl;
1366 struct ieee80211_hw *hw;
1367
1368 WL_TRACE(("wl: wl_suspend\n"));
1369
1370 hw = pci_get_drvdata(pdev);
1371 wl = HW_TO_WL(hw);
1372 if (!wl) {
1373 WL_ERROR(("wl: wl_suspend: pci_get_drvdata failed\n"));
1374 return -ENODEV;
1375 }
1376
1377 WL_LOCK(wl);
1378 wl_down(wl);
0965ae88 1379 wl->pub->hw_up = false;
a9533e7e 1380 WL_UNLOCK(wl);
8ba9cfdb 1381 pci_save_state(pdev, wl->pci_psstate);
a9533e7e
HP
1382 pci_disable_device(pdev);
1383 return pci_set_power_state(pdev, PCI_D3hot);
1384}
1385
1386static int wl_resume(struct pci_dev *pdev)
1387{
1388 wl_info_t *wl;
1389 struct ieee80211_hw *hw;
1390 int err = 0;
66cbd3ab 1391 u32 val;
a9533e7e
HP
1392
1393 WL_TRACE(("wl: wl_resume\n"));
1394 hw = pci_get_drvdata(pdev);
1395 wl = HW_TO_WL(hw);
1396 if (!wl) {
1397 WL_ERROR(("wl: wl_resume: pci_get_drvdata failed\n"));
1398 return -ENODEV;
1399 }
1400
1401 err = pci_set_power_state(pdev, PCI_D0);
1402 if (err)
1403 return err;
1404
8ba9cfdb 1405 pci_restore_state(pdev, wl->pci_psstate);
a9533e7e
HP
1406
1407 err = pci_enable_device(pdev);
1408 if (err)
1409 return err;
1410
1411 pci_set_master(pdev);
1412
1413 pci_read_config_dword(pdev, 0x40, &val);
1414 if ((val & 0x0000ff00) != 0)
1415 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
1416
1417 WL_LOCK(wl);
1418 err = wl_up(wl);
1419 WL_UNLOCK(wl);
1420
90ea2296 1421 return err;
a9533e7e
HP
1422}
1423#endif /* LINUXSTA_PS */
1424
6f0c5bcd 1425static void wl_remove(struct pci_dev *pdev)
a9533e7e
HP
1426{
1427 wl_info_t *wl;
1428 struct ieee80211_hw *hw;
1429
1430 hw = pci_get_drvdata(pdev);
1431 wl = HW_TO_WL(hw);
1432 if (!wl) {
1433 WL_ERROR(("wl: wl_remove: pci_get_drvdata failed\n"));
1434 return;
1435 }
1436 if (!wlc_chipmatch(pdev->vendor, pdev->device)) {
1437 WL_ERROR(("wl: wl_remove: wlc_chipmatch failed\n"));
1438 return;
1439 }
683b505b
BR
1440 if (wl->wlc) {
1441 ieee80211_unregister_hw(hw);
1442 WL_LOCK(wl);
1443 wl_down(wl);
1444 WL_UNLOCK(wl);
1445 WL_NONE(("%s: Down\n", __func__));
1446 }
a9533e7e
HP
1447 pci_disable_device(pdev);
1448
1449 wl_free(wl);
1450
1451 pci_set_drvdata(pdev, NULL);
1452 ieee80211_free_hw(hw);
1453}
1454
1455static struct pci_driver wl_pci_driver = {
5fee2540
JC
1456 .name = "brcm80211",
1457 .probe = wl_pci_probe,
a9533e7e 1458#ifdef LINUXSTA_PS
5fee2540
JC
1459 .suspend = wl_suspend,
1460 .resume = wl_resume,
a9533e7e 1461#endif /* LINUXSTA_PS */
5fee2540
JC
1462 .remove = __devexit_p(wl_remove),
1463 .id_table = wl_id_table,
a9533e7e
HP
1464};
1465#endif /* !BCMSDIO */
1466
1467/**
1468 * This is the main entry point for the WL driver.
1469 *
1470 * This function determines if a device pointed to by pdev is a WL device,
1471 * and if so, performs a wl_attach() on it.
1472 *
1473 */
1474static int __init wl_module_init(void)
1475{
1476 int error = -ENODEV;
1477
1478#ifdef BCMDBG
1479 if (msglevel != 0xdeadbeef)
1480 wl_msg_level = msglevel;
1481 else {
1482 char *var = getvar(NULL, "wl_msglevel");
1483 if (var)
48c51a8c 1484 wl_msg_level = simple_strtoul(var, NULL, 0);
a9533e7e
HP
1485 }
1486#ifndef WLC_HIGH_ONLY
1487 {
66cbd3ab 1488 extern u32 phyhal_msg_level;
a9533e7e
HP
1489
1490 if (phymsglevel != 0xdeadbeef)
1491 phyhal_msg_level = phymsglevel;
1492 else {
1493 char *var = getvar(NULL, "phy_msglevel");
1494 if (var)
48c51a8c 1495 phyhal_msg_level = simple_strtoul(var, NULL, 0);
a9533e7e
HP
1496 }
1497 }
1498#endif /* WLC_HIGH_ONLY */
1499#endif /* BCMDBG */
1500
1501#ifndef BCMSDIO
8ba9cfdb 1502 error = pci_register_driver(&wl_pci_driver);
ca8c1e59 1503 if (!error)
90ea2296 1504 return 0;
a9533e7e
HP
1505
1506#endif /* !BCMSDIO */
1507
1508#ifdef WLC_HIGH_ONLY
1509 /* BMAC_NOTE: define hardcode number, why NODEVICE is ok ? */
1510 error =
1511 dbus_register(BCM_DNGL_VID, 0, wl_dbus_probe_cb,
1512 wl_dbus_disconnect_cb, NULL, NULL, NULL);
1513 if (error == DBUS_ERR_NODEVICE) {
1514 error = DBUS_OK;
1515 }
1516#endif /* WLC_HIGH_ONLY */
1517
90ea2296 1518 return error;
a9533e7e
HP
1519}
1520
1521/**
1522 * This function unloads the WL driver from the system.
1523 *
1524 * This function unconditionally unloads the WL driver module from the
1525 * system.
1526 *
1527 */
1528static void __exit wl_module_exit(void)
1529{
1530#ifndef BCMSDIO
1531 pci_unregister_driver(&wl_pci_driver);
1532#endif /* !BCMSDIO */
1533
1534#ifdef WLC_HIGH_ONLY
1535 dbus_deregister();
1536#endif /* WLC_HIGH_ONLY */
1537}
1538
1539module_init(wl_module_init);
1540module_exit(wl_module_exit);
1541
1542/**
1543 * This function frees the WL per-device resources.
1544 *
1545 * This function frees resources owned by the WL device pointed to
1546 * by the wl parameter.
1547 *
1548 */
7cc4a4c0 1549void wl_free(wl_info_t *wl)
a9533e7e
HP
1550{
1551 wl_timer_t *t, *next;
1552 osl_t *osh;
1553
1554 ASSERT(wl);
1555#ifndef WLC_HIGH_ONLY
a9533e7e
HP
1556 /* free ucode data */
1557 if (wl->fw.fw_cnt)
1558 wl_ucode_data_free();
a9533e7e
HP
1559 if (wl->irq)
1560 free_irq(wl->irq, wl);
1561#endif
1562
1563 /* kill dpc */
1564 tasklet_kill(&wl->tasklet);
1565
1566 if (wl->pub) {
1567 wlc_module_unregister(wl->pub, "linux", wl);
1568 }
1569
1570 /* free common resources */
1571 if (wl->wlc) {
1572 wlc_detach(wl->wlc);
1573 wl->wlc = NULL;
1574 wl->pub = NULL;
1575 }
1576
1577 /* virtual interface deletion is deferred so we cannot spinwait */
1578
1579 /* wait for all pending callbacks to complete */
1580 while (atomic_read(&wl->callbacks) > 0)
1581 schedule();
1582
1583 /* free timers */
1584 for (t = wl->timers; t; t = next) {
1585 next = t->next;
1586#ifdef BCMDBG
1587 if (t->name)
182acb3c 1588 kfree(t->name);
a9533e7e 1589#endif
182acb3c 1590 kfree(t);
a9533e7e
HP
1591 }
1592
a9533e7e
HP
1593 osh = wl->osh;
1594
1595 /*
1596 * unregister_netdev() calls get_stats() which may read chip registers
1597 * so we cannot unmap the chip registers until after calling unregister_netdev() .
1598 */
1599 if (wl->regsva && BUSTYPE(wl->bcm_bustype) != SDIO_BUS &&
1600 BUSTYPE(wl->bcm_bustype) != JTAG_BUS) {
1601 iounmap((void *)wl->regsva);
1602 }
1603 wl->regsva = NULL;
1604
1605#ifdef WLC_HIGH_ONLY
1606 wl_rpcq_free(wl);
1607
1608 wl_txq_free(wl);
1609
1610 if (wl->rpc) {
1611 bcm_rpc_detach(wl->rpc);
1612 wl->rpc = NULL;
1613 }
1614
1615 if (wl->rpc_th) {
1616 bcm_rpc_tp_detach(wl->rpc_th);
1617 wl->rpc_th = NULL;
1618 }
1619#endif /* WLC_HIGH_ONLY */
1620
a9533e7e
HP
1621 osl_detach(osh);
1622}
1623
1624#ifdef WLC_LOW
1625/* transmit a packet */
7cc4a4c0 1626static int BCMFASTPATH wl_start(struct sk_buff *skb, wl_info_t *wl)
a9533e7e
HP
1627{
1628 if (!wl)
1629 return -ENETDOWN;
1630
1631 return wl_start_int(wl, WL_TO_HW(wl), skb);
1632}
1633#endif /* WLC_LOW */
1634
1635static int BCMFASTPATH
7cc4a4c0 1636wl_start_int(wl_info_t *wl, struct ieee80211_hw *hw, struct sk_buff *skb)
a9533e7e
HP
1637{
1638#ifdef WLC_HIGH_ONLY
1639 WL_LOCK(wl);
1640#endif
1641 wlc_sendpkt_mac80211(wl->wlc, skb, hw);
1642#ifdef WLC_HIGH_ONLY
1643 WL_UNLOCK(wl);
1644#endif
90ea2296 1645 return NETDEV_TX_OK;
a9533e7e
HP
1646}
1647
7cc4a4c0 1648void wl_txflowcontrol(wl_info_t *wl, struct wl_if *wlif, bool state, int prio)
a9533e7e
HP
1649{
1650 WL_ERROR(("Shouldn't be here %s\n", __func__));
1651}
1652
1653#if defined(WLC_HIGH_ONLY)
1654/* Schedule a completion handler to run at safe time */
1655static int
7cc4a4c0 1656wl_schedule_task(wl_info_t *wl, void (*fn) (struct wl_task *task),
a9533e7e
HP
1657 void *context)
1658{
1659 wl_task_t *task;
1660
1661 WL_TRACE(("wl%d: wl_schedule_task\n", wl->pub->unit));
1662
5fcc1fcb 1663 task = kmalloc(sizeof(wl_task_t), GFP_ATOMIC);
ca8c1e59 1664 if (!task) {
97e17d0e 1665 WL_ERROR(("wl%d: wl_schedule_task: out of memory\n", wl->pub->unit));
a9533e7e
HP
1666 return -ENOMEM;
1667 }
1668
364eb72a 1669 INIT_WORK(&task->work, (work_func_t) fn);
a9533e7e
HP
1670 task->context = context;
1671
1672 if (!schedule_work(&task->work)) {
1673 WL_ERROR(("wl%d: schedule_work() failed\n", wl->pub->unit));
182acb3c 1674 kfree(task);
a9533e7e
HP
1675 return -ENOMEM;
1676 }
1677
1678 atomic_inc(&wl->callbacks);
1679
1680 return 0;
1681}
1682#endif /* defined(WLC_HIGH_ONLY) */
1683
7cc4a4c0 1684void wl_init(wl_info_t *wl)
a9533e7e
HP
1685{
1686 WL_TRACE(("wl%d: wl_init\n", wl->pub->unit));
1687
1688 wl_reset(wl);
1689
1690 wlc_init(wl->wlc);
1691}
1692
7cc4a4c0 1693uint wl_reset(wl_info_t *wl)
a9533e7e
HP
1694{
1695 WL_TRACE(("wl%d: wl_reset\n", wl->pub->unit));
1696
1697 wlc_reset(wl->wlc);
1698
1699 /* dpc will not be rescheduled */
1700 wl->resched = 0;
1701
90ea2296 1702 return 0;
a9533e7e
HP
1703}
1704
1705/*
1706 * These are interrupt on/off entry points. Disable interrupts
1707 * during interrupt state transition.
1708 */
7cc4a4c0 1709void BCMFASTPATH wl_intrson(wl_info_t *wl)
a9533e7e
HP
1710{
1711#if defined(WLC_LOW)
1712 unsigned long flags;
1713
1714 INT_LOCK(wl, flags);
1715 wlc_intrson(wl->wlc);
1716 INT_UNLOCK(wl, flags);
1717#endif /* WLC_LOW */
1718}
1719
7cc4a4c0 1720bool wl_alloc_dma_resources(wl_info_t *wl, uint addrwidth)
a9533e7e 1721{
0f0881b0 1722 return true;
a9533e7e
HP
1723}
1724
66cbd3ab 1725u32 BCMFASTPATH wl_intrsoff(wl_info_t *wl)
a9533e7e
HP
1726{
1727#if defined(WLC_LOW)
1728 unsigned long flags;
66cbd3ab 1729 u32 status;
a9533e7e
HP
1730
1731 INT_LOCK(wl, flags);
1732 status = wlc_intrsoff(wl->wlc);
1733 INT_UNLOCK(wl, flags);
1734 return status;
1735#else
1736 return 0;
1737#endif /* WLC_LOW */
1738}
1739
66cbd3ab 1740void wl_intrsrestore(wl_info_t *wl, u32 macintmask)
a9533e7e
HP
1741{
1742#if defined(WLC_LOW)
1743 unsigned long flags;
1744
1745 INT_LOCK(wl, flags);
1746 wlc_intrsrestore(wl->wlc, macintmask);
1747 INT_UNLOCK(wl, flags);
1748#endif /* WLC_LOW */
1749}
1750
7cc4a4c0 1751int wl_up(wl_info_t *wl)
a9533e7e
HP
1752{
1753 int error = 0;
1754
1755 if (wl->pub->up)
90ea2296 1756 return 0;
a9533e7e
HP
1757
1758 error = wlc_up(wl->wlc);
1759
90ea2296 1760 return error;
a9533e7e
HP
1761}
1762
7cc4a4c0 1763void wl_down(wl_info_t *wl)
a9533e7e
HP
1764{
1765 uint callbacks, ret_val = 0;
1766
1767 /* call common down function */
1768 ret_val = wlc_down(wl->wlc);
1769 callbacks = atomic_read(&wl->callbacks) - ret_val;
1770
1771 /* wait for down callbacks to complete */
1772 WL_UNLOCK(wl);
1773
1774#ifndef WLC_HIGH_ONLY
1775 /* For HIGH_only driver, it's important to actually schedule other work,
1776 * not just spin wait since everything runs at schedule level
1777 */
1778 SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000);
1779#endif /* WLC_HIGH_ONLY */
1780
1781 WL_LOCK(wl);
1782}
1783
1784irqreturn_t BCMFASTPATH wl_isr(int irq, void *dev_id)
1785{
1786#if defined(WLC_LOW)
1787 wl_info_t *wl;
1788 bool ours, wantdpc;
1789 unsigned long flags;
1790
1791 wl = (wl_info_t *) dev_id;
1792
1793 WL_ISRLOCK(wl, flags);
1794
1795 /* call common first level interrupt handler */
ca8c1e59
JC
1796 ours = wlc_isr(wl->wlc, &wantdpc);
1797 if (ours) {
a9533e7e
HP
1798 /* if more to do... */
1799 if (wantdpc) {
1800
1801 /* ...and call the second level interrupt handler */
1802 /* schedule dpc */
0965ae88 1803 ASSERT(wl->resched == false);
a9533e7e
HP
1804 tasklet_schedule(&wl->tasklet);
1805 }
1806 }
1807
1808 WL_ISRUNLOCK(wl, flags);
1809
1810 return IRQ_RETVAL(ours);
1811#else
1812 return IRQ_RETVAL(0);
1813#endif /* WLC_LOW */
1814}
1815
3deea904 1816static void BCMFASTPATH wl_dpc(unsigned long data)
a9533e7e
HP
1817{
1818#ifdef WLC_LOW
1819 wl_info_t *wl;
1820
1821 wl = (wl_info_t *) data;
1822
1823 WL_LOCK(wl);
1824
1825 /* call the common second level interrupt handler */
1826 if (wl->pub->up) {
1827 if (wl->resched) {
1828 unsigned long flags;
1829
1830 INT_LOCK(wl, flags);
1831 wlc_intrsupd(wl->wlc);
1832 INT_UNLOCK(wl, flags);
1833 }
1834
0f0881b0 1835 wl->resched = wlc_dpc(wl->wlc, true);
a9533e7e
HP
1836 }
1837
1838 /* wlc_dpc() may bring the driver down */
1839 if (!wl->pub->up)
1840 goto done;
1841
1842 /* re-schedule dpc */
1843 if (wl->resched)
1844 tasklet_schedule(&wl->tasklet);
1845 else {
1846 /* re-enable interrupts */
1847 wl_intrson(wl);
1848 }
1849
1850 done:
1851 WL_UNLOCK(wl);
1852#endif /* WLC_LOW */
1853}
1854
7cc4a4c0 1855static void wl_link_up(wl_info_t *wl, char *ifname)
a9533e7e
HP
1856{
1857 WL_ERROR(("wl%d: link up (%s)\n", wl->pub->unit, ifname));
1858}
1859
7cc4a4c0 1860static void wl_link_down(wl_info_t *wl, char *ifname)
a9533e7e
HP
1861{
1862 WL_ERROR(("wl%d: link down (%s)\n", wl->pub->unit, ifname));
1863}
1864
7cc4a4c0 1865void wl_event(wl_info_t *wl, char *ifname, wlc_event_t *e)
a9533e7e
HP
1866{
1867
1868 switch (e->event.event_type) {
1869 case WLC_E_LINK:
1870 case WLC_E_NDIS_LINK:
1871 if (e->event.flags & WLC_EVENT_MSG_LINK)
1872 wl_link_up(wl, ifname);
1873 else
1874 wl_link_down(wl, ifname);
1875 break;
1876 case WLC_E_RADIO:
1877 break;
1878 }
1879}
1880
3deea904 1881static void wl_timer(unsigned long data)
a9533e7e
HP
1882{
1883#ifndef WLC_HIGH_ONLY
1884 _wl_timer((wl_timer_t *) data);
1885#else
1886 wl_timer_t *t = (wl_timer_t *) data;
1887 wl_schedule_task(t->wl, wl_timer_task, t);
1888#endif /* WLC_HIGH_ONLY */
1889}
1890
7cc4a4c0 1891static void _wl_timer(wl_timer_t *t)
a9533e7e
HP
1892{
1893 WL_LOCK(t->wl);
1894
1895 if (t->set) {
1896 if (t->periodic) {
1897 t->timer.expires = jiffies + t->ms * HZ / 1000;
1898 atomic_inc(&t->wl->callbacks);
1899 add_timer(&t->timer);
0f0881b0 1900 t->set = true;
a9533e7e 1901 } else
0965ae88 1902 t->set = false;
a9533e7e
HP
1903
1904 t->fn(t->arg);
1905 }
1906
1907 atomic_dec(&t->wl->callbacks);
1908
1909 WL_UNLOCK(t->wl);
1910}
1911
7cc4a4c0 1912wl_timer_t *wl_init_timer(wl_info_t *wl, void (*fn) (void *arg), void *arg,
a9533e7e
HP
1913 const char *name)
1914{
1915 wl_timer_t *t;
1916
5fcc1fcb 1917 t = kmalloc(sizeof(wl_timer_t), GFP_ATOMIC);
ca8c1e59 1918 if (!t) {
97e17d0e 1919 WL_ERROR(("wl%d: wl_init_timer: out of memory\n", wl->pub->unit));
a9533e7e
HP
1920 return 0;
1921 }
1922
1923 bzero(t, sizeof(wl_timer_t));
1924
1925 init_timer(&t->timer);
3deea904 1926 t->timer.data = (unsigned long) t;
a9533e7e
HP
1927 t->timer.function = wl_timer;
1928 t->wl = wl;
1929 t->fn = fn;
1930 t->arg = arg;
1931 t->next = wl->timers;
1932 wl->timers = t;
1933
1934#ifdef BCMDBG
5fcc1fcb 1935 t->name = kmalloc(strlen(name) + 1, GFP_ATOMIC);
ca8c1e59 1936 if (t->name)
a9533e7e
HP
1937 strcpy(t->name, name);
1938#endif
1939
1940 return t;
1941}
1942
1943/* BMAC_NOTE: Add timer adds only the kernel timer since it's going to be more accurate
1944 * as well as it's easier to make it periodic
1945 */
7cc4a4c0 1946void wl_add_timer(wl_info_t *wl, wl_timer_t *t, uint ms, int periodic)
a9533e7e
HP
1947{
1948#ifdef BCMDBG
1949 if (t->set) {
1950 WL_ERROR(("%s: Already set. Name: %s, per %d\n",
1951 __func__, t->name, periodic));
1952 }
1953#endif
1954 ASSERT(!t->set);
1955
1956 t->ms = ms;
1957 t->periodic = (bool) periodic;
0f0881b0 1958 t->set = true;
a9533e7e
HP
1959 t->timer.expires = jiffies + ms * HZ / 1000;
1960
1961 atomic_inc(&wl->callbacks);
1962 add_timer(&t->timer);
1963}
1964
0965ae88 1965/* return true if timer successfully deleted, false if still pending */
7cc4a4c0 1966bool wl_del_timer(wl_info_t *wl, wl_timer_t *t)
a9533e7e
HP
1967{
1968 if (t->set) {
0965ae88 1969 t->set = false;
a9533e7e 1970 if (!del_timer(&t->timer)) {
0965ae88 1971 return false;
a9533e7e
HP
1972 }
1973 atomic_dec(&wl->callbacks);
1974 }
1975
0f0881b0 1976 return true;
a9533e7e
HP
1977}
1978
7cc4a4c0 1979void wl_free_timer(wl_info_t *wl, wl_timer_t *t)
a9533e7e
HP
1980{
1981 wl_timer_t *tmp;
1982
1983 /* delete the timer in case it is active */
1984 wl_del_timer(wl, t);
1985
1986 if (wl->timers == t) {
1987 wl->timers = wl->timers->next;
1988#ifdef BCMDBG
1989 if (t->name)
182acb3c 1990 kfree(t->name);
a9533e7e 1991#endif
182acb3c 1992 kfree(t);
a9533e7e
HP
1993 return;
1994
1995 }
1996
1997 tmp = wl->timers;
1998 while (tmp) {
1999 if (tmp->next == t) {
2000 tmp->next = t->next;
2001#ifdef BCMDBG
2002 if (t->name)
182acb3c 2003 kfree(t->name);
a9533e7e 2004#endif
182acb3c 2005 kfree(t);
a9533e7e
HP
2006 return;
2007 }
2008 tmp = tmp->next;
2009 }
2010
2011}
2012
2013static int wl_linux_watchdog(void *ctx)
2014{
2015 wl_info_t *wl = (wl_info_t *) ctx;
2016 struct net_device_stats *stats = NULL;
2017 uint id;
2018 /* refresh stats */
2019 if (wl->pub->up) {
2020 ASSERT(wl->stats_id < 2);
2021
2022 id = 1 - wl->stats_id;
2023
2024 stats = &wl->stats_watchdog[id];
2025 stats->rx_packets = WLCNTVAL(wl->pub->_cnt->rxframe);
2026 stats->tx_packets = WLCNTVAL(wl->pub->_cnt->txframe);
2027 stats->rx_bytes = WLCNTVAL(wl->pub->_cnt->rxbyte);
2028 stats->tx_bytes = WLCNTVAL(wl->pub->_cnt->txbyte);
2029 stats->rx_errors = WLCNTVAL(wl->pub->_cnt->rxerror);
2030 stats->tx_errors = WLCNTVAL(wl->pub->_cnt->txerror);
2031 stats->collisions = 0;
2032
2033 stats->rx_length_errors = 0;
2034 stats->rx_over_errors = WLCNTVAL(wl->pub->_cnt->rxoflo);
2035 stats->rx_crc_errors = WLCNTVAL(wl->pub->_cnt->rxcrc);
2036 stats->rx_frame_errors = 0;
2037 stats->rx_fifo_errors = WLCNTVAL(wl->pub->_cnt->rxoflo);
2038 stats->rx_missed_errors = 0;
2039
2040 stats->tx_fifo_errors = WLCNTVAL(wl->pub->_cnt->txuflo);
2041
2042 wl->stats_id = id;
2043
2044 }
2045
2046 return 0;
2047}
2048
2049struct wl_fw_hdr {
66cbd3ab
GKH
2050 u32 offset;
2051 u32 len;
2052 u32 idx;
a9533e7e
HP
2053};
2054
2055#ifdef WLC_HIGH_ONLY
2056static void wl_rpc_down(void *wlh)
2057{
2058 wl_info_t *wl = (wl_info_t *) (wlh);
2059
2060 wlc_device_removed(wl->wlc);
2061
2062 wl_rpcq_free(wl);
2063}
2064
7cc4a4c0 2065static int BCMFASTPATH wl_start(struct sk_buff *skb, wl_info_t *wl)
a9533e7e
HP
2066{
2067
3deea904 2068 unsigned long flags;
a9533e7e
HP
2069
2070 skb->prev = NULL;
2071
2072 /* Lock the queue as tasklet could be running at this time */
2073 TXQ_LOCK(wl, flags);
2074 if (wl->txq_head == NULL)
2075 wl->txq_head = skb;
2076 else {
2077 wl->txq_tail->prev = skb;
2078 }
2079 wl->txq_tail = skb;
2080
0965ae88 2081 if (wl->txq_dispatched == false) {
0f0881b0 2082 wl->txq_dispatched = true;
a9533e7e
HP
2083
2084 if (schedule_work(&wl->txq_task.work)) {
2085 atomic_inc(&wl->callbacks);
2086 } else {
2087 WL_ERROR(("wl%d: wl_start/schedule_work failed\n",
2088 wl->pub->unit));
2089 }
2090 }
2091
2092 TXQ_UNLOCK(wl, flags);
2093
90ea2296 2094 return 0;
a9533e7e
HP
2095
2096}
2097
2098static void wl_start_txqwork(struct wl_task *task)
2099{
2100 wl_info_t *wl = (wl_info_t *) task->context;
2101 struct sk_buff *skb;
3deea904 2102 unsigned long flags;
a9533e7e
HP
2103 uint count = 0;
2104
2105 WL_TRACE(("wl%d: wl_start_txqwork\n", wl->pub->unit));
2106
2107 /* First remove an entry then go for execution */
2108 TXQ_LOCK(wl, flags);
2109 while (wl->txq_head) {
2110 skb = wl->txq_head;
2111 wl->txq_head = skb->prev;
2112 skb->prev = NULL;
2113 if (wl->txq_head == NULL)
2114 wl->txq_tail = NULL;
2115 TXQ_UNLOCK(wl, flags);
2116
2117 /* it has WL_LOCK/WL_UNLOCK inside */
2118 wl_start_int(wl, WL_TO_HW(wl), skb);
2119
2120 /* bounded our execution, reshedule ourself next */
2121 if (++count >= 10)
2122 break;
2123
2124 TXQ_LOCK(wl, flags);
2125 }
2126
2127 if (count >= 10) {
2128 if (!schedule_work(&wl->txq_task.work)) {
2129 WL_ERROR(("wl%d: wl_start/schedule_work failed\n",
2130 wl->pub->unit));
2131 atomic_dec(&wl->callbacks);
2132 }
2133 } else {
0965ae88 2134 wl->txq_dispatched = false;
a9533e7e
HP
2135 TXQ_UNLOCK(wl, flags);
2136 atomic_dec(&wl->callbacks);
2137 }
2138
2139 return;
2140}
2141
7cc4a4c0 2142static void wl_txq_free(wl_info_t *wl)
a9533e7e
HP
2143{
2144 struct sk_buff *skb;
2145
2146 if (wl->txq_head == NULL) {
2147 ASSERT(wl->txq_tail == NULL);
2148 return;
2149 }
2150
2151 while (wl->txq_head) {
2152 skb = wl->txq_head;
2153 wl->txq_head = skb->prev;
0f0881b0 2154 PKTFREE(wl->osh, skb, true);
a9533e7e
HP
2155 }
2156
2157 wl->txq_tail = NULL;
2158}
2159
7cc4a4c0 2160static void wl_rpcq_free(wl_info_t *wl)
a9533e7e
HP
2161{
2162 rpc_buf_t *buf;
2163
2164 if (wl->rpcq_head == NULL) {
2165 ASSERT(wl->rpcq_tail == NULL);
2166 return;
2167 }
2168
2169 while (wl->rpcq_head) {
2170 buf = wl->rpcq_head;
2171 wl->rpcq_head = bcm_rpc_buf_next_get(wl->rpc_th, buf);
2172 bcm_rpc_buf_free(wl->rpc_dispatch_ctx.rpc, buf);
2173 }
2174
2175 wl->rpcq_tail = NULL;
2176}
2177
2178static void wl_rpcq_dispatch(struct wl_task *task)
2179{
2180 wl_info_t *wl = (wl_info_t *) task->context;
2181 rpc_buf_t *buf;
3deea904 2182 unsigned long flags;
a9533e7e
HP
2183
2184 /* First remove an entry then go for execution */
2185 RPCQ_LOCK(wl, flags);
2186 while (wl->rpcq_head) {
2187 buf = wl->rpcq_head;
2188 wl->rpcq_head = bcm_rpc_buf_next_get(wl->rpc_th, buf);
2189
2190 if (wl->rpcq_head == NULL)
2191 wl->rpcq_tail = NULL;
2192 RPCQ_UNLOCK(wl, flags);
2193
2194 WL_LOCK(wl);
2195 wlc_rpc_high_dispatch(&wl->rpc_dispatch_ctx, buf);
2196 WL_UNLOCK(wl);
2197
2198 RPCQ_LOCK(wl, flags);
2199 }
2200
0965ae88 2201 wl->rpcq_dispatched = false;
a9533e7e
HP
2202
2203 RPCQ_UNLOCK(wl, flags);
2204
182acb3c 2205 kfree(task);
a9533e7e
HP
2206 atomic_dec(&wl->callbacks);
2207}
2208
7cc4a4c0 2209static void wl_rpcq_add(wl_info_t *wl, rpc_buf_t *buf)
a9533e7e 2210{
3deea904 2211 unsigned long flags;
a9533e7e
HP
2212
2213 bcm_rpc_buf_next_set(wl->rpc_th, buf, NULL);
2214
2215 /* Lock the queue as tasklet could be running at this time */
2216 RPCQ_LOCK(wl, flags);
2217 if (wl->rpcq_head == NULL)
2218 wl->rpcq_head = buf;
2219 else
2220 bcm_rpc_buf_next_set(wl->rpc_th, wl->rpcq_tail, buf);
2221
2222 wl->rpcq_tail = buf;
2223
0965ae88 2224 if (wl->rpcq_dispatched == false) {
0f0881b0 2225 wl->rpcq_dispatched = true;
a9533e7e
HP
2226 wl_schedule_task(wl, wl_rpcq_dispatch, wl);
2227 }
2228
2229 RPCQ_UNLOCK(wl, flags);
2230}
2231
2232#if defined(BCMDBG)
2233static const struct name_entry rpc_name_tbl[] = RPC_ID_TABLE;
2234#endif /* BCMDBG */
2235
2236/* dongle-side rpc dispatch routine */
2237static void wl_rpc_dispatch_schedule(void *ctx, struct rpc_buf *buf)
2238{
2239 bcm_xdr_buf_t b;
2240 wl_info_t *wl = (wl_info_t *) ctx;
2241 wlc_rpc_id_t rpc_id;
2242 int err;
2243
2244 bcm_xdr_buf_init(&b, bcm_rpc_buf_data(wl->rpc_th, buf),
2245 bcm_rpc_buf_len_get(wl->rpc_th, buf));
2246
66cbd3ab 2247 err = bcm_xdr_unpack_u32(&b, &rpc_id);
a9533e7e
HP
2248 ASSERT(!err);
2249 WL_TRACE(("%s: Dispatch id %s\n", __func__,
2250 WLC_RPC_ID_LOOKUP(rpc_name_tbl, rpc_id)));
2251
2252 /* Handle few emergency ones */
2253 switch (rpc_id) {
2254 default:
2255 wl_rpcq_add(wl, buf);
2256 break;
2257 }
2258}
2259
7cc4a4c0 2260static void wl_timer_task(wl_task_t *task)
a9533e7e
HP
2261{
2262 wl_timer_t *t = (wl_timer_t *) task->context;
2263
2264 _wl_timer(t);
182acb3c 2265 kfree(task);
a9533e7e
HP
2266
2267 /* This dec is for the task_schedule. The timer related
2268 * callback is decremented in _wl_timer
2269 */
2270 atomic_dec(&t->wl->callbacks);
2271}
2272#endif /* WLC_HIGH_ONLY */
2273
2274#ifndef WLC_HIGH_ONLY
2275char *wl_firmwares[WL_MAX_FW] = {
2276 "brcm/bcm43xx",
2277 NULL
2278};
2279
2280#ifdef WLC_LOW
66cbd3ab 2281int wl_ucode_init_buf(wl_info_t *wl, void **pbuf, u32 idx)
a9533e7e
HP
2282{
2283 int i, entry;
41feb5ed 2284 const u8 *pdata;
a9533e7e
HP
2285 struct wl_fw_hdr *hdr;
2286 for (i = 0; i < wl->fw.fw_cnt; i++) {
2287 hdr = (struct wl_fw_hdr *)wl->fw.fw_hdr[i]->data;
2288 for (entry = 0; entry < wl->fw.hdr_num_entries[i];
2289 entry++, hdr++) {
2290 if (hdr->idx == idx) {
2291 pdata = wl->fw.fw_bin[i]->data + hdr->offset;
2292 *pbuf = kmalloc(hdr->len, GFP_ATOMIC);
2293 if (*pbuf == NULL) {
2294 printf("fail to alloc %d bytes\n",
2295 hdr->len);
2296 }
2297 bcopy(pdata, *pbuf, hdr->len);
2298 return 0;
2299 }
2300 }
2301 }
2302 printf("ERROR: ucode buf tag:%d can not be found!\n", idx);
2303 *pbuf = NULL;
2304 return -1;
2305}
2306
66cbd3ab 2307int wl_ucode_init_uint(wl_info_t *wl, u32 *data, u32 idx)
a9533e7e
HP
2308{
2309 int i, entry;
41feb5ed 2310 const u8 *pdata;
a9533e7e
HP
2311 struct wl_fw_hdr *hdr;
2312 for (i = 0; i < wl->fw.fw_cnt; i++) {
2313 hdr = (struct wl_fw_hdr *)wl->fw.fw_hdr[i]->data;
2314 for (entry = 0; entry < wl->fw.hdr_num_entries[i];
2315 entry++, hdr++) {
2316 if (hdr->idx == idx) {
2317 pdata = wl->fw.fw_bin[i]->data + hdr->offset;
2318 ASSERT(hdr->len == 4);
66cbd3ab 2319 *data = *((u32 *) pdata);
a9533e7e
HP
2320 return 0;
2321 }
2322 }
2323 }
2324 printf("ERROR: ucode tag:%d can not be found!\n", idx);
2325 return -1;
2326}
2327#endif /* WLC_LOW */
2328
7cc4a4c0 2329static int wl_request_fw(wl_info_t *wl, struct pci_dev *pdev)
a9533e7e
HP
2330{
2331 int status;
2332 struct device *device = &pdev->dev;
2333 char fw_name[100];
2334 int i;
2335
2336 bzero((void *)&wl->fw, sizeof(struct wl_firmware));
2337 for (i = 0; i < WL_MAX_FW; i++) {
2338 if (wl_firmwares[i] == NULL)
2339 break;
2340 sprintf(fw_name, "%s-%d.fw", wl_firmwares[i],
2341 UCODE_LOADER_API_VER);
2342 WL_NONE(("request fw %s\n", fw_name));
2343 status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
2344 if (status) {
683b505b
BR
2345 printf("%s: fail to load firmware %s\n",
2346 KBUILD_MODNAME, fw_name);
eb4764c3 2347 wl_release_fw(wl);
a9533e7e
HP
2348 return status;
2349 }
2350 WL_NONE(("request fw %s\n", fw_name));
2351 sprintf(fw_name, "%s_hdr-%d.fw", wl_firmwares[i],
2352 UCODE_LOADER_API_VER);
2353 status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
2354 if (status) {
683b505b
BR
2355 printf("%s: fail to load firmware %s\n",
2356 KBUILD_MODNAME, fw_name);
eb4764c3 2357 wl_release_fw(wl);
a9533e7e
HP
2358 return status;
2359 }
2360 wl->fw.hdr_num_entries[i] =
2361 wl->fw.fw_hdr[i]->size / (sizeof(struct wl_fw_hdr));
2362 WL_NONE(("request fw %s find: %d entries\n", fw_name,
2363 wl->fw.hdr_num_entries[i]));
2364 }
2365 wl->fw.fw_cnt = i;
2366 wl_ucode_data_init(wl);
2367 return 0;
2368}
2369
2370#ifdef WLC_LOW
2371void wl_ucode_free_buf(void *p)
2372{
2373 kfree(p);
2374}
2375#endif /* WLC_LOW */
2376
7cc4a4c0 2377static void wl_release_fw(wl_info_t *wl)
a9533e7e
HP
2378{
2379 int i;
eb4764c3 2380 for (i = 0; i < WL_MAX_FW; i++) {
a9533e7e
HP
2381 release_firmware(wl->fw.fw_bin[i]);
2382 release_firmware(wl->fw.fw_hdr[i]);
2383 }
2384}
2385#endif /* WLC_HIGH_ONLY */
This page took 0.16971 seconds and 5 git commands to generate.