staging: brcm80211: remove conditional SIMPLE_ISCAN code
[deliverable/linux.git] / drivers / staging / brcm80211 / brcmfmac / dhd_linux.c
CommitLineData
cf2b4488
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#ifdef CONFIG_WIFI_CONTROL_FUNC
18#include <linux/platform_device.h>
19#endif
cf2b4488
HP
20#include <linux/init.h>
21#include <linux/kernel.h>
860708d9 22#include <linux/kthread.h>
cf2b4488
HP
23#include <linux/slab.h>
24#include <linux/skbuff.h>
25#include <linux/netdevice.h>
26#include <linux/etherdevice.h>
93ad12cf 27#include <linux/mmc/sdio_func.h>
cf2b4488
HP
28#include <linux/random.h>
29#include <linux/spinlock.h>
30#include <linux/ethtool.h>
31#include <linux/fcntl.h>
32#include <linux/fs.h>
93ad12cf 33#include <linux/uaccess.h>
583c3827 34#include <net/cfg80211.h>
e51b3e52
AS
35#if defined(CONFIG_HAS_EARLYSUSPEND)
36#include <linux/earlysuspend.h>
37#endif
cc3cea5a 38#include <defs.h>
f97e956a
RV
39#include <brcmu_utils.h>
40#include <brcmu_wifi.h>
cf2b4488 41
c4daa849
RV
42#include "dngl_stats.h"
43#include "dhd.h"
44#include "dhd_bus.h"
45#include "dhd_proto.h"
46#include "dhd_dbg.h"
47#include "wl_cfg80211.h"
cf2b4488 48
c09240ac
AS
49#define EPI_VERSION_STR "4.218.248.5"
50#define ETH_P_BRCM 0x886c
cf2b4488 51
47a6d2cd
AS
52/* Global ASSERT type flag */
53u32 g_assert_type;
54
cf2b4488
HP
55#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
56#include <linux/wifi_tiwlan.h>
57
58struct semaphore wifi_control_sem;
59
60struct dhd_bus *g_bus;
61
5f782dee
JC
62static struct wifi_platform_data *wifi_control_data;
63static struct resource *wifi_irqres;
cf2b4488
HP
64
65int wifi_get_irq_number(unsigned long *irq_flags_ptr)
66{
67 if (wifi_irqres) {
68 *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK;
69 return (int)wifi_irqres->start;
70 }
71#ifdef CUSTOM_OOB_GPIO_NUM
72 return CUSTOM_OOB_GPIO_NUM;
73#else
74 return -1;
75#endif
76}
77
78int wifi_set_carddetect(int on)
79{
80 printk(KERN_ERR "%s = %d\n", __func__, on);
81 if (wifi_control_data && wifi_control_data->set_carddetect)
82 wifi_control_data->set_carddetect(on);
83 return 0;
84}
85
86int wifi_set_power(int on, unsigned long msec)
87{
88 printk(KERN_ERR "%s = %d\n", __func__, on);
89 if (wifi_control_data && wifi_control_data->set_power)
90 wifi_control_data->set_power(on);
91 if (msec)
92 mdelay(msec);
93 return 0;
94}
95
96int wifi_set_reset(int on, unsigned long msec)
97{
98 printk(KERN_ERR "%s = %d\n", __func__, on);
99 if (wifi_control_data && wifi_control_data->set_reset)
100 wifi_control_data->set_reset(on);
101 if (msec)
102 mdelay(msec);
103 return 0;
104}
105
106static int wifi_probe(struct platform_device *pdev)
107{
108 struct wifi_platform_data *wifi_ctrl =
109 (struct wifi_platform_data *)(pdev->dev.platform_data);
110
111 printk(KERN_ERR "## %s\n", __func__);
112 wifi_irqres =
113 platform_get_resource_byname(pdev, IORESOURCE_IRQ,
114 "bcm4329_wlan_irq");
115 wifi_control_data = wifi_ctrl;
116
117 wifi_set_power(1, 0); /* Power On */
118 wifi_set_carddetect(1); /* CardDetect (0->1) */
119
120 up(&wifi_control_sem);
121 return 0;
122}
123
124static int wifi_remove(struct platform_device *pdev)
125{
126 struct wifi_platform_data *wifi_ctrl =
127 (struct wifi_platform_data *)(pdev->dev.platform_data);
128
129 printk(KERN_ERR "## %s\n", __func__);
130 wifi_control_data = wifi_ctrl;
131
132 wifi_set_carddetect(0); /* CardDetect (1->0) */
133 wifi_set_power(0, 0); /* Power Off */
134
135 up(&wifi_control_sem);
136 return 0;
137}
138
139static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
140{
141 DHD_TRACE(("##> %s\n", __func__));
142 return 0;
143}
144
145static int wifi_resume(struct platform_device *pdev)
146{
147 DHD_TRACE(("##> %s\n", __func__));
148 return 0;
149}
150
151static struct platform_driver wifi_device = {
152 .probe = wifi_probe,
153 .remove = wifi_remove,
154 .suspend = wifi_suspend,
155 .resume = wifi_resume,
156 .driver = {
c836f77f 157 .name = KBUILD_MODNAME,
cf2b4488
HP
158 }
159};
160
161int wifi_add_dev(void)
162{
163 DHD_TRACE(("## Calling platform_driver_register\n"));
164 return platform_driver_register(&wifi_device);
165}
166
167void wifi_del_dev(void)
168{
169 DHD_TRACE(("## Unregister platform_driver_register\n"));
170 platform_driver_unregister(&wifi_device);
171}
172#endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
173
174#if defined(CONFIG_PM_SLEEP)
175#include <linux/suspend.h>
e6e8f894 176atomic_t dhd_mmc_suspend;
cf2b4488
HP
177DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
178#endif /* defined(CONFIG_PM_SLEEP) */
179
180#if defined(OOB_INTR_ONLY)
54ca2969 181extern void brcmf_sdbrcm_enable_oob_intr(struct dhd_bus *bus, bool enable);
cf2b4488
HP
182#endif /* defined(OOB_INTR_ONLY) */
183
184MODULE_AUTHOR("Broadcom Corporation");
185MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac driver.");
186MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac cards");
187MODULE_LICENSE("Dual BSD/GPL");
188
cf2b4488
HP
189
190/* Interface control information */
191typedef struct dhd_if {
192 struct dhd_info *info; /* back pointer to dhd_info */
193 /* OS/stack specifics */
194 struct net_device *net;
195 struct net_device_stats stats;
196 int idx; /* iface idx in dongle */
197 int state; /* interface state */
198 uint subunit; /* subunit */
b8d63078 199 u8 mac_addr[ETH_ALEN]; /* assigned MAC address */
cf2b4488
HP
200 bool attached; /* Delayed attachment when unset */
201 bool txflowcontrol; /* Per interface flow control indicator */
cbf6baac 202 char name[IFNAMSIZ]; /* linux interface name */
cf2b4488
HP
203} dhd_if_t;
204
205/* Local private structure (extension of pub) */
206typedef struct dhd_info {
cf2b4488
HP
207 dhd_pub_t pub;
208
209 /* OS/stack specifics */
210 dhd_if_t *iflist[DHD_MAX_IFS];
211
212 struct semaphore proto_sem;
213 wait_queue_head_t ioctl_resp_wait;
214 struct timer_list timer;
215 bool wd_timer_valid;
216 struct tasklet_struct tasklet;
217 spinlock_t sdlock;
cf2b4488
HP
218 /* Thread based operation */
219 bool threads_only;
220 struct semaphore sdsem;
860708d9 221 struct task_struct *watchdog_tsk;
cf2b4488 222 struct semaphore watchdog_sem;
ecd7559d 223 struct task_struct *dpc_tsk;
cf2b4488 224 struct semaphore dpc_sem;
cf2b4488
HP
225
226 /* Thread to issue ioctl for multicast */
d809dcb9 227 struct task_struct *sysioc_tsk;
cf2b4488 228 struct semaphore sysioc_sem;
cf2b4488
HP
229 bool set_multicast;
230 bool set_macaddress;
a44d4236 231 u8 macvalue[ETH_ALEN];
cf2b4488
HP
232 wait_queue_head_t ctrl_wait;
233 atomic_t pend_8021x_cnt;
234
235#ifdef CONFIG_HAS_EARLYSUSPEND
236 struct early_suspend early_suspend;
237#endif /* CONFIG_HAS_EARLYSUSPEND */
238} dhd_info_t;
239
240/* Definitions to provide path to the firmware and nvram
241 * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt"
242 */
243char firmware_path[MOD_PARAM_PATHLEN];
244char nvram_path[MOD_PARAM_PATHLEN];
245
246/* load firmware and/or nvram values from the filesystem */
247module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0);
248module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
249
f10c5b0b
AS
250/* No firmware required */
251bool dhd_no_fw_req;
252module_param(dhd_no_fw_req, bool, 0);
253
cf2b4488 254/* Error bits */
aba5e0ad 255module_param(brcmf_msg_level, int, 0);
cf2b4488
HP
256
257/* Spawn a thread for system ioctls (set mac, set mcast) */
0f0881b0 258uint dhd_sysioc = true;
cf2b4488
HP
259module_param(dhd_sysioc, uint, 0);
260
261/* Watchdog interval */
5e92aa8c
AS
262uint brcmf_watchdog_ms = 10;
263module_param(brcmf_watchdog_ms, uint, 0);
cf2b4488
HP
264
265#ifdef DHD_DEBUG
266/* Console poll interval */
5e92aa8c
AS
267uint brcmf_console_ms;
268module_param(brcmf_console_ms, uint, 0);
cf2b4488
HP
269#endif /* DHD_DEBUG */
270
271/* ARP offload agent mode : Enable ARP Host Auto-Reply
272and ARP Peer Auto-Reply */
5e92aa8c
AS
273uint brcmf_arp_mode = 0xb;
274module_param(brcmf_arp_mode, uint, 0);
cf2b4488
HP
275
276/* ARP offload enable */
5e92aa8c
AS
277uint brcmf_arp_enable = true;
278module_param(brcmf_arp_enable, uint, 0);
cf2b4488
HP
279
280/* Global Pkt filter enable control */
5e92aa8c
AS
281uint brcmf_pkt_filter_enable = true;
282module_param(brcmf_pkt_filter_enable, uint, 0);
cf2b4488
HP
283
284/* Pkt filter init setup */
5e92aa8c
AS
285uint brcmf_pkt_filter_init;
286module_param(brcmf_pkt_filter_init, uint, 0);
cf2b4488
HP
287
288/* Pkt filter mode control */
5e92aa8c
AS
289uint brcmf_master_mode = true;
290module_param(brcmf_master_mode, uint, 1);
cf2b4488
HP
291
292/* Watchdog thread priority, -1 to use kernel timer */
293int dhd_watchdog_prio = 97;
294module_param(dhd_watchdog_prio, int, 0);
295
296/* DPC thread priority, -1 to use tasklet */
297int dhd_dpc_prio = 98;
298module_param(dhd_dpc_prio, int, 0);
299
300/* DPC thread priority, -1 to use tasklet */
301extern int dhd_dongle_memsize;
302module_param(dhd_dongle_memsize, int, 0);
303
304/* Contorl fw roaming */
305#ifdef CUSTOMER_HW2
5e92aa8c 306uint brcmf_roam;
cf2b4488 307#else
5e92aa8c 308uint brcmf_roam = 1;
cf2b4488
HP
309#endif
310
311/* Control radio state */
5e92aa8c 312uint brcmf_radio_up = 1;
cf2b4488
HP
313
314/* Network inteface name */
8343f180 315char iface_name[IFNAMSIZ] = "wlan";
cf2b4488
HP
316module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
317
cf2b4488
HP
318/* The following are specific to the SDIO dongle */
319
320/* IOCTL response timeout */
321int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
322
323/* Idle timeout for backplane clock */
5e92aa8c
AS
324int brcmf_idletime = BRCMF_IDLETIME_TICKS;
325module_param(brcmf_idletime, int, 0);
cf2b4488
HP
326
327/* Use polling */
5e92aa8c
AS
328uint brcmf_poll;
329module_param(brcmf_poll, uint, 0);
cf2b4488 330
cf2b4488 331/* Use interrupts */
5e92aa8c
AS
332uint brcmf_intr = true;
333module_param(brcmf_intr, uint, 0);
cf2b4488
HP
334
335/* SDIO Drive Strength (in milliamps) */
5e92aa8c
AS
336uint brcmf_sdiod_drive_strength = 6;
337module_param(brcmf_sdiod_drive_strength, uint, 0);
cf2b4488
HP
338
339/* Tx/Rx bounds */
340extern uint dhd_txbound;
341extern uint dhd_rxbound;
342module_param(dhd_txbound, uint, 0);
343module_param(dhd_rxbound, uint, 0);
344
345/* Deferred transmits */
346extern uint dhd_deferred_tx;
347module_param(dhd_deferred_tx, uint, 0);
348
349#ifdef SDTEST
350/* Echo packet generator (pkts/s) */
5e92aa8c
AS
351uint brcmf_pktgen;
352module_param(brcmf_pktgen, uint, 0);
cf2b4488
HP
353
354/* Echo packet len (0 => sawtooth, max 2040) */
5e92aa8c
AS
355uint brcmf_pktgen_len;
356module_param(brcmf_pktgen_len, uint, 0);
cf2b4488
HP
357#endif
358
cf2b4488
HP
359/* Version string to report */
360#ifdef DHD_DEBUG
361#define DHD_COMPILED "\nCompiled in " SRCBASE
362#else
363#define DHD_COMPILED
364#endif
365
3deea904 366static void dhd_dpc(unsigned long data);
cf2b4488
HP
367/* forward decl */
368extern int dhd_wait_pend8021x(struct net_device *dev);
369
370#ifdef TOE
66cbd3ab
GKH
371static int dhd_toe_get(dhd_info_t *dhd, int idx, u32 *toe_ol);
372static int dhd_toe_set(dhd_info_t *dhd, int idx, u32 toe_ol);
cf2b4488
HP
373#endif /* TOE */
374
375static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
05dc0f43 376 brcmf_event_msg_t *event_ptr, void **data_ptr);
cf2b4488 377
cf2b4488
HP
378static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
379{
380#ifdef PKT_FILTER_SUPPORT
381 DHD_TRACE(("%s: %d\n", __func__, value));
382 /* 1 - Enable packet filter, only allow unicast packet to send up */
383 /* 0 - Disable packet filter */
5e92aa8c 384 if (brcmf_pkt_filter_enable) {
cf2b4488
HP
385 int i;
386
387 for (i = 0; i < dhd->pktfilter_count; i++) {
aba5e0ad
AS
388 brcmf_c_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
389 brcmf_c_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
5e92aa8c 390 value, brcmf_master_mode);
cf2b4488
HP
391 }
392 }
393#endif
394}
395
396#if defined(CONFIG_HAS_EARLYSUSPEND)
397static int dhd_set_suspend(int value, dhd_pub_t *dhd)
398{
399 int power_mode = PM_MAX;
400 /* wl_pkt_filter_enable_t enable_parm; */
401 char iovbuf[32];
402 int bcn_li_dtim = 3;
403#ifdef CUSTOMER_HW2
404 uint roamvar = 1;
405#endif /* CUSTOMER_HW2 */
406
407 DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n",
408 __func__, value, dhd->in_suspend));
409
410 if (dhd && dhd->up) {
411 if (value && dhd->in_suspend) {
412
413 /* Kernel suspended */
414 DHD_TRACE(("%s: force extra Suspend setting\n",
415 __func__));
416
0aa94b84 417 brcmf_proto_cdc_set_ioctl(dhd, 0, BRCMF_C_SET_PM,
cf2b4488
HP
418 (char *)&power_mode,
419 sizeof(power_mode));
420
421 /* Enable packet filter, only allow unicast
422 packet to send up */
423 dhd_set_packet_filter(1, dhd);
424
425 /* if dtim skip setup as default force it
25985edc 426 * to wake each third dtim
cf2b4488
HP
427 * for better power saving.
428 * Note that side effect is chance to miss BC/MC
429 * packet
430 */
431 if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1))
432 bcn_li_dtim = 3;
433 else
434 bcn_li_dtim = dhd->dtim_skip;
67ad48bc 435 brcmu_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
cf2b4488 436 4, iovbuf, sizeof(iovbuf));
0aa94b84
AS
437 brcmf_proto_cdc_set_ioctl(dhd, 0, BRCMF_C_SET_VAR,
438 iovbuf, sizeof(iovbuf));
cf2b4488
HP
439#ifdef CUSTOMER_HW2
440 /* Disable build-in roaming to allowed \
441 * supplicant to take of romaing
442 */
67ad48bc 443 brcmu_mkiovar("roam_off", (char *)&roamvar, 4,
cf2b4488 444 iovbuf, sizeof(iovbuf));
0aa94b84
AS
445 brcmf_proto_cdc_set_ioctl(dhd, 0, BRCMF_C_SET_VAR,
446 iovbuf, sizeof(iovbuf));
cf2b4488
HP
447#endif /* CUSTOMER_HW2 */
448 } else {
449
450 /* Kernel resumed */
451 DHD_TRACE(("%s: Remove extra suspend setting\n",
452 __func__));
453
454 power_mode = PM_FAST;
0aa94b84 455 brcmf_proto_cdc_set_ioctl(dhd, 0, BRCMF_C_SET_PM,
cf2b4488
HP
456 (char *)&power_mode,
457 sizeof(power_mode));
458
459 /* disable pkt filter */
460 dhd_set_packet_filter(0, dhd);
461
462 /* restore pre-suspend setting for dtim_skip */
67ad48bc 463 brcmu_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
cf2b4488
HP
464 4, iovbuf, sizeof(iovbuf));
465
0aa94b84
AS
466 brcmf_proto_cdc_set_ioctl(dhd, 0, BRCMF_C_SET_VAR,
467 iovbuf, sizeof(iovbuf));
cf2b4488
HP
468#ifdef CUSTOMER_HW2
469 roamvar = 0;
67ad48bc 470 brcmu_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf,
cf2b4488 471 sizeof(iovbuf));
0aa94b84
AS
472 brcmf_proto_cdc_set_ioctl(dhd, 0, BRCMF_C_SET_VAR,
473 iovbuf, sizeof(iovbuf));
cf2b4488
HP
474#endif /* CUSTOMER_HW2 */
475 }
476 }
477
478 return 0;
479}
480
481static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
482{
483 dhd_pub_t *dhdp = &dhd->pub;
484
a84bac46 485 brcmf_os_proto_block(dhdp);
cf2b4488
HP
486 /* Set flag when early suspend was called */
487 dhdp->in_suspend = val;
488 if (!dhdp->suspend_disable_flag)
489 dhd_set_suspend(val, dhdp);
a84bac46 490 brcmf_os_proto_unblock(dhdp);
cf2b4488
HP
491}
492
493static void dhd_early_suspend(struct early_suspend *h)
494{
495 struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
496
497 DHD_TRACE(("%s: enter\n", __func__));
498
499 if (dhd)
500 dhd_suspend_resume_helper(dhd, 1);
501
502}
503
504static void dhd_late_resume(struct early_suspend *h)
505{
506 struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
507
508 DHD_TRACE(("%s: enter\n", __func__));
509
510 if (dhd)
511 dhd_suspend_resume_helper(dhd, 0);
512}
513#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
514
515/*
516 * Generalized timeout mechanism. Uses spin sleep with exponential
517 * back-off until
518 * the sleep time reaches one jiffy, then switches over to task delay. Usage:
519 *
a84bac46
AS
520 * brcmf_timeout_start(&tmo, usec);
521 * while (!brcmf_timeout_expired(&tmo))
cf2b4488
HP
522 * if (poll_something())
523 * break;
a84bac46 524 * if (brcmf_timeout_expired(&tmo))
cf2b4488
HP
525 * fatal();
526 */
527
a84bac46 528void brcmf_timeout_start(dhd_timeout_t *tmo, uint usec)
cf2b4488
HP
529{
530 tmo->limit = usec;
531 tmo->increment = 0;
532 tmo->elapsed = 0;
533 tmo->tick = 1000000 / HZ;
534}
535
a84bac46 536int brcmf_timeout_expired(dhd_timeout_t *tmo)
cf2b4488
HP
537{
538 /* Does nothing the first call */
539 if (tmo->increment == 0) {
540 tmo->increment = 1;
541 return 0;
542 }
543
544 if (tmo->elapsed >= tmo->limit)
545 return 1;
546
547 /* Add the delay that's about to take place */
548 tmo->elapsed += tmo->increment;
549
550 if (tmo->increment < tmo->tick) {
7383141b 551 udelay(tmo->increment);
cf2b4488
HP
552 tmo->increment *= 2;
553 if (tmo->increment > tmo->tick)
554 tmo->increment = tmo->tick;
555 } else {
556 wait_queue_head_t delay_wait;
557 DECLARE_WAITQUEUE(wait, current);
558 int pending;
559 init_waitqueue_head(&delay_wait);
560 add_wait_queue(&delay_wait, &wait);
561 set_current_state(TASK_INTERRUPTIBLE);
562 schedule_timeout(1);
563 pending = signal_pending(current);
564 remove_wait_queue(&delay_wait, &wait);
565 set_current_state(TASK_RUNNING);
566 if (pending)
567 return 1; /* Interrupted */
568 }
569
570 return 0;
571}
572
573static int dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
574{
575 int i = 0;
576
577 ASSERT(dhd);
578 while (i < DHD_MAX_IFS) {
579 if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
580 return i;
581 i++;
582 }
583
584 return DHD_BAD_IF;
585}
586
a84bac46 587int brcmf_ifname2idx(dhd_info_t *dhd, char *name)
cf2b4488
HP
588{
589 int i = DHD_MAX_IFS;
590
591 ASSERT(dhd);
592
593 if (name == NULL || *name == '\0')
594 return 0;
595
596 while (--i > 0)
597 if (dhd->iflist[i]
598 && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
599 break;
600
601 DHD_TRACE(("%s: return idx %d for \"%s\"\n", __func__, i, name));
602
603 return i; /* default - the primary interface */
604}
605
a84bac46 606char *brcmf_ifname(dhd_pub_t *dhdp, int ifidx)
cf2b4488
HP
607{
608 dhd_info_t *dhd = (dhd_info_t *) dhdp->info;
609
610 ASSERT(dhd);
611
612 if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
613 DHD_ERROR(("%s: ifidx %d out of range\n", __func__, ifidx));
614 return "<if_bad>";
615 }
616
617 if (dhd->iflist[ifidx] == NULL) {
618 DHD_ERROR(("%s: null i/f %d\n", __func__, ifidx));
619 return "<if_null>";
620 }
621
622 if (dhd->iflist[ifidx]->net)
623 return dhd->iflist[ifidx]->net->name;
624
625 return "<if_none>";
626}
627
628static void _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
629{
630 struct net_device *dev;
631 struct netdev_hw_addr *ha;
66cbd3ab 632 u32 allmulti, cnt;
cf2b4488
HP
633
634 wl_ioctl_t ioc;
635 char *buf, *bufp;
636 uint buflen;
637 int ret;
638
639 ASSERT(dhd && dhd->iflist[ifidx]);
640 dev = dhd->iflist[ifidx]->net;
641 cnt = netdev_mc_count(dev);
642
643 /* Determine initial value of allmulti flag */
0965ae88 644 allmulti = (dev->flags & IFF_ALLMULTI) ? true : false;
cf2b4488
HP
645
646 /* Send down the multicast list first. */
647
b8d63078 648 buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETH_ALEN);
5fcc1fcb 649 bufp = buf = kmalloc(buflen, GFP_ATOMIC);
a618cc28 650 if (!bufp) {
cf2b4488 651 DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
a84bac46 652 brcmf_ifname(&dhd->pub, ifidx), cnt));
cf2b4488
HP
653 return;
654 }
655
656 strcpy(bufp, "mcast_list");
657 bufp += strlen("mcast_list") + 1;
658
628f10ba 659 cnt = cpu_to_le32(cnt);
cf2b4488
HP
660 memcpy(bufp, &cnt, sizeof(cnt));
661 bufp += sizeof(cnt);
662
663 netdev_for_each_mc_addr(ha, dev) {
664 if (!cnt)
665 break;
b8d63078
JP
666 memcpy(bufp, ha->addr, ETH_ALEN);
667 bufp += ETH_ALEN;
cf2b4488
HP
668 cnt--;
669 }
670
671 memset(&ioc, 0, sizeof(ioc));
366bbb3f 672 ioc.cmd = BRCMF_C_SET_VAR;
cf2b4488
HP
673 ioc.buf = buf;
674 ioc.len = buflen;
0f0881b0 675 ioc.set = true;
cf2b4488 676
0aa94b84 677 ret = brcmf_proto_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
cf2b4488
HP
678 if (ret < 0) {
679 DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
a84bac46 680 brcmf_ifname(&dhd->pub, ifidx), cnt));
0f0881b0 681 allmulti = cnt ? true : allmulti;
cf2b4488
HP
682 }
683
182acb3c 684 kfree(buf);
cf2b4488
HP
685
686 /* Now send the allmulti setting. This is based on the setting in the
687 * net_device flags, but might be modified above to be turned on if we
688 * were trying to set some addresses and dongle rejected it...
689 */
690
691 buflen = sizeof("allmulti") + sizeof(allmulti);
5fcc1fcb 692 buf = kmalloc(buflen, GFP_ATOMIC);
a618cc28 693 if (!buf) {
cf2b4488 694 DHD_ERROR(("%s: out of memory for allmulti\n",
a84bac46 695 brcmf_ifname(&dhd->pub, ifidx)));
cf2b4488
HP
696 return;
697 }
628f10ba 698 allmulti = cpu_to_le32(allmulti);
cf2b4488 699
67ad48bc 700 if (!brcmu_mkiovar
cf2b4488
HP
701 ("allmulti", (void *)&allmulti, sizeof(allmulti), buf, buflen)) {
702 DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d "
a84bac46 703 "buflen %u\n", brcmf_ifname(&dhd->pub, ifidx),
cf2b4488 704 (int)sizeof(allmulti), buflen));
182acb3c 705 kfree(buf);
cf2b4488
HP
706 return;
707 }
708
709 memset(&ioc, 0, sizeof(ioc));
366bbb3f 710 ioc.cmd = BRCMF_C_SET_VAR;
cf2b4488
HP
711 ioc.buf = buf;
712 ioc.len = buflen;
0f0881b0 713 ioc.set = true;
cf2b4488 714
0aa94b84 715 ret = brcmf_proto_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
cf2b4488
HP
716 if (ret < 0) {
717 DHD_ERROR(("%s: set allmulti %d failed\n",
a84bac46 718 brcmf_ifname(&dhd->pub, ifidx),
628f10ba 719 le32_to_cpu(allmulti)));
cf2b4488
HP
720 }
721
182acb3c 722 kfree(buf);
cf2b4488
HP
723
724 /* Finally, pick up the PROMISC flag as well, like the NIC
725 driver does */
726
0965ae88 727 allmulti = (dev->flags & IFF_PROMISC) ? true : false;
628f10ba 728 allmulti = cpu_to_le32(allmulti);
cf2b4488
HP
729
730 memset(&ioc, 0, sizeof(ioc));
366bbb3f 731 ioc.cmd = BRCMF_C_SET_PROMISC;
cf2b4488
HP
732 ioc.buf = &allmulti;
733 ioc.len = sizeof(allmulti);
0f0881b0 734 ioc.set = true;
cf2b4488 735
0aa94b84 736 ret = brcmf_proto_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
cf2b4488
HP
737 if (ret < 0) {
738 DHD_ERROR(("%s: set promisc %d failed\n",
a84bac46 739 brcmf_ifname(&dhd->pub, ifidx),
628f10ba 740 le32_to_cpu(allmulti)));
cf2b4488
HP
741 }
742}
743
744static int
a44d4236 745_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, u8 *addr)
cf2b4488
HP
746{
747 char buf[32];
748 wl_ioctl_t ioc;
749 int ret;
750
751 DHD_TRACE(("%s enter\n", __func__));
67ad48bc 752 if (!brcmu_mkiovar
b8d63078 753 ("cur_etheraddr", (char *)addr, ETH_ALEN, buf, 32)) {
cf2b4488 754 DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n",
a84bac46 755 brcmf_ifname(&dhd->pub, ifidx)));
cf2b4488
HP
756 return -1;
757 }
758 memset(&ioc, 0, sizeof(ioc));
366bbb3f 759 ioc.cmd = BRCMF_C_SET_VAR;
cf2b4488
HP
760 ioc.buf = buf;
761 ioc.len = 32;
0f0881b0 762 ioc.set = true;
cf2b4488 763
0aa94b84 764 ret = brcmf_proto_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
cf2b4488
HP
765 if (ret < 0) {
766 DHD_ERROR(("%s: set cur_etheraddr failed\n",
a84bac46 767 brcmf_ifname(&dhd->pub, ifidx)));
cf2b4488 768 } else {
b8d63078 769 memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETH_ALEN);
cf2b4488
HP
770 }
771
772 return ret;
773}
774
775#ifdef SOFTAP
776extern struct net_device *ap_net_dev;
777#endif
778
779static void dhd_op_if(dhd_if_t *ifp)
780{
781 dhd_info_t *dhd;
782 int ret = 0, err = 0;
783
784 ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */
785
786 dhd = ifp->info;
787
788 DHD_TRACE(("%s: idx %d, state %d\n", __func__, ifp->idx, ifp->state));
789
790 switch (ifp->state) {
05dc0f43 791 case BRCMF_E_IF_ADD:
cf2b4488
HP
792 /*
793 * Delete the existing interface before overwriting it
05dc0f43 794 * in case we missed the BRCMF_E_IF_DEL event.
cf2b4488
HP
795 */
796 if (ifp->net != NULL) {
797 DHD_ERROR(("%s: ERROR: netdev:%s already exists, "
798 "try free & unregister\n",
799 __func__, ifp->net->name));
800 netif_stop_queue(ifp->net);
801 unregister_netdev(ifp->net);
802 free_netdev(ifp->net);
803 }
804 /* Allocate etherdev, including space for private structure */
a618cc28
JC
805 ifp->net = alloc_etherdev(sizeof(dhd));
806 if (!ifp->net) {
cf2b4488
HP
807 DHD_ERROR(("%s: OOM - alloc_etherdev\n", __func__));
808 ret = -ENOMEM;
809 }
810 if (ret == 0) {
811 strcpy(ifp->net->name, ifp->name);
812 memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
a84bac46 813 err = brcmf_net_attach(&dhd->pub, ifp->idx);
a618cc28 814 if (err != 0) {
cf2b4488
HP
815 DHD_ERROR(("%s: dhd_net_attach failed, "
816 "err %d\n",
817 __func__, err));
818 ret = -EOPNOTSUPP;
819 } else {
820#ifdef SOFTAP
821 /* semaphore that the soft AP CODE
822 waits on */
823 extern struct semaphore ap_eth_sema;
824
825 /* save ptr to wl0.1 netdev for use
826 in wl_iw.c */
827 ap_net_dev = ifp->net;
828 /* signal to the SOFTAP 'sleeper' thread,
829 wl0.1 is ready */
830 up(&ap_eth_sema);
831#endif
832 DHD_TRACE(("\n ==== pid:%x, net_device for "
833 "if:%s created ===\n\n",
834 current->pid, ifp->net->name));
835 ifp->state = 0;
836 }
837 }
838 break;
05dc0f43 839 case BRCMF_E_IF_DEL:
cf2b4488
HP
840 if (ifp->net != NULL) {
841 DHD_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n",
842 __func__));
843 netif_stop_queue(ifp->net);
844 unregister_netdev(ifp->net);
845 ret = DHD_DEL_IF; /* Make sure the free_netdev()
846 is called */
847 }
848 break;
849 default:
850 DHD_ERROR(("%s: bad op %d\n", __func__, ifp->state));
851 ASSERT(!ifp->state);
852 break;
853 }
854
855 if (ret < 0) {
856 if (ifp->net)
857 free_netdev(ifp->net);
858
859 dhd->iflist[ifp->idx] = NULL;
182acb3c 860 kfree(ifp);
cf2b4488
HP
861#ifdef SOFTAP
862 if (ifp->net == ap_net_dev)
863 ap_net_dev = NULL; /* NULL SOFTAP global
864 wl0.1 as well */
865#endif /* SOFTAP */
866 }
867}
868
869static int _dhd_sysioc_thread(void *data)
870{
871 dhd_info_t *dhd = (dhd_info_t *) data;
872 int i;
873#ifdef SOFTAP
0965ae88 874 bool in_ap = false;
cf2b4488
HP
875#endif
876
af737136 877 allow_signal(SIGTERM);
878
cf2b4488 879 while (down_interruptible(&dhd->sysioc_sem) == 0) {
eeb8e46b 880 if (kthread_should_stop())
d809dcb9 881 break;
cf2b4488
HP
882 for (i = 0; i < DHD_MAX_IFS; i++) {
883 if (dhd->iflist[i]) {
884#ifdef SOFTAP
885 in_ap = (ap_net_dev != NULL);
886#endif /* SOFTAP */
887 if (dhd->iflist[i]->state)
888 dhd_op_if(dhd->iflist[i]);
889#ifdef SOFTAP
890 if (dhd->iflist[i] == NULL) {
891 DHD_TRACE(("\n\n %s: interface %d "
892 "removed!\n", __func__, i));
893 continue;
894 }
895
896 if (in_ap && dhd->set_macaddress) {
897 DHD_TRACE(("attempt to set MAC for %s "
e131d3ce 898 "in AP Mode," "blocked.\n",
cf2b4488 899 dhd->iflist[i]->net->name));
0965ae88 900 dhd->set_macaddress = false;
cf2b4488
HP
901 continue;
902 }
903
904 if (in_ap && dhd->set_multicast) {
e131d3ce 905 DHD_TRACE(("attempt to set MULTICAST list for %s" "in AP Mode, blocked.\n",
cf2b4488 906 dhd->iflist[i]->net->name));
0965ae88 907 dhd->set_multicast = false;
cf2b4488
HP
908 continue;
909 }
910#endif /* SOFTAP */
911 if (dhd->set_multicast) {
0965ae88 912 dhd->set_multicast = false;
cf2b4488
HP
913 _dhd_set_multicast_list(dhd, i);
914 }
915 if (dhd->set_macaddress) {
0965ae88 916 dhd->set_macaddress = false;
cf2b4488 917 _dhd_set_mac_address(dhd, i,
a44d4236 918 dhd->macvalue);
cf2b4488
HP
919 }
920 }
921 }
922 }
d809dcb9 923 return 0;
cf2b4488
HP
924}
925
926static int dhd_set_mac_address(struct net_device *dev, void *addr)
927{
928 int ret = 0;
929
930 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(dev);
931 struct sockaddr *sa = (struct sockaddr *)addr;
932 int ifidx;
933
934 ifidx = dhd_net2idx(dhd, dev);
935 if (ifidx == DHD_BAD_IF)
936 return -1;
937
d809dcb9 938 ASSERT(dhd->sysioc_tsk);
b8d63078 939 memcpy(&dhd->macvalue, sa->sa_data, ETH_ALEN);
0f0881b0 940 dhd->set_macaddress = true;
cf2b4488
HP
941 up(&dhd->sysioc_sem);
942
943 return ret;
944}
945
946static void dhd_set_multicast_list(struct net_device *dev)
947{
948 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(dev);
949 int ifidx;
950
951 ifidx = dhd_net2idx(dhd, dev);
952 if (ifidx == DHD_BAD_IF)
953 return;
954
d809dcb9 955 ASSERT(dhd->sysioc_tsk);
0f0881b0 956 dhd->set_multicast = true;
cf2b4488
HP
957 up(&dhd->sysioc_sem);
958}
959
a84bac46 960int brcmf_sendpkt(dhd_pub_t *dhdp, int ifidx, struct sk_buff *pktbuf)
cf2b4488
HP
961{
962 int ret;
963 dhd_info_t *dhd = (dhd_info_t *) (dhdp->info);
964
965 /* Reject if down */
966 if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN))
967 return -ENODEV;
968
969 /* Update multicast statistic */
b8d63078 970 if (pktbuf->len >= ETH_ALEN) {
54991ad6 971 u8 *pktdata = (u8 *) (pktbuf->data);
e2582ad8 972 struct ethhdr *eh = (struct ethhdr *)pktdata;
cf2b4488 973
e2582ad8 974 if (is_multicast_ether_addr(eh->h_dest))
cf2b4488 975 dhdp->tx_multicast++;
628f10ba 976 if (ntohs(eh->h_proto) == ETH_P_PAE)
cf2b4488
HP
977 atomic_inc(&dhd->pend_8021x_cnt);
978 }
979
cf2b4488 980 /* If the protocol uses a data header, apply it */
0aa94b84 981 brcmf_proto_hdrpush(dhdp, ifidx, pktbuf);
cf2b4488
HP
982
983 /* Use bus module to send data frame */
984#ifdef BCMDBUS
985 ret = dbus_send_pkt(dhdp->dbus, pktbuf, NULL /* pktinfo */);
986#else
54ca2969 987 ret = brcmf_sdbrcm_bus_txdata(dhdp->bus, pktbuf);
cf2b4488
HP
988#endif /* BCMDBUS */
989
990 return ret;
991}
992
993static int dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
994{
995 int ret;
cf2b4488
HP
996 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(net);
997 int ifidx;
998
999 DHD_TRACE(("%s: Enter\n", __func__));
1000
1001 /* Reject if down */
1002 if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) {
1003 DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d\n",
1004 __func__, dhd->pub.up, dhd->pub.busstate));
1005 netif_stop_queue(net);
1006 return -ENODEV;
1007 }
1008
1009 ifidx = dhd_net2idx(dhd, net);
1010 if (ifidx == DHD_BAD_IF) {
1011 DHD_ERROR(("%s: bad ifidx %d\n", __func__, ifidx));
1012 netif_stop_queue(net);
1013 return -ENODEV;
1014 }
1015
1016 /* Make sure there's enough room for any header */
1017 if (skb_headroom(skb) < dhd->pub.hdrlen) {
1018 struct sk_buff *skb2;
1019
1020 DHD_INFO(("%s: insufficient headroom\n",
a84bac46 1021 brcmf_ifname(&dhd->pub, ifidx)));
cf2b4488
HP
1022 dhd->pub.tx_realloc++;
1023 skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen);
1024 dev_kfree_skb(skb);
a618cc28
JC
1025 skb = skb2;
1026 if (skb == NULL) {
cf2b4488 1027 DHD_ERROR(("%s: skb_realloc_headroom failed\n",
a84bac46 1028 brcmf_ifname(&dhd->pub, ifidx)));
cf2b4488
HP
1029 ret = -ENOMEM;
1030 goto done;
1031 }
1032 }
1033
b8b5418b 1034 ret = brcmf_sendpkt(&dhd->pub, ifidx, skb);
cf2b4488
HP
1035
1036done:
1037 if (ret)
1038 dhd->pub.dstats.tx_dropped++;
1039 else
1040 dhd->pub.tx_packets++;
1041
1042 /* Return ok: we always eat the packet */
1043 return 0;
1044}
1045
a84bac46 1046void brcmf_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
cf2b4488
HP
1047{
1048 struct net_device *net;
1049 dhd_info_t *dhd = dhdp->info;
1050
1051 DHD_TRACE(("%s: Enter\n", __func__));
1052
1053 dhdp->txoff = state;
1054 ASSERT(dhd && dhd->iflist[ifidx]);
1055 net = dhd->iflist[ifidx]->net;
1056 if (state == ON)
1057 netif_stop_queue(net);
1058 else
1059 netif_wake_queue(net);
1060}
1061
b8b5418b 1062void brcmf_rx_frame(dhd_pub_t *dhdp, int ifidx, struct sk_buff *skb,
c26b1378 1063 int numpkt)
cf2b4488
HP
1064{
1065 dhd_info_t *dhd = (dhd_info_t *) dhdp->info;
580a0bd9 1066 unsigned char *eth;
cf2b4488 1067 uint len;
c26b1378
AS
1068 void *data;
1069 struct sk_buff *pnext, *save_pktbuf;
cf2b4488
HP
1070 int i;
1071 dhd_if_t *ifp;
05dc0f43 1072 brcmf_event_msg_t event;
cf2b4488
HP
1073
1074 DHD_TRACE(("%s: Enter\n", __func__));
1075
b8b5418b 1076 save_pktbuf = skb;
cf2b4488 1077
b8b5418b 1078 for (i = 0; skb && i < numpkt; i++, skb = pnext) {
cf2b4488 1079
b8b5418b
AS
1080 pnext = skb->next;
1081 skb->next = NULL;
cf2b4488
HP
1082
1083 /* Get the protocol, maintain skb around eth_type_trans()
1084 * The main reason for this hack is for the limitation of
1085 * Linux 2.4 where 'eth_type_trans' uses the
1086 * 'net->hard_header_len'
1087 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
1088 * coping of the packet coming from the network stack to add
1089 * BDC, Hardware header etc, during network interface
1090 * registration
1091 * we set the 'net->hard_header_len' to ETH_HLEN + extra space
1092 * required
1093 * for BDC, Hardware header etc. and not just the ETH_HLEN
1094 */
1095 eth = skb->data;
1096 len = skb->len;
1097
1098 ifp = dhd->iflist[ifidx];
1099 if (ifp == NULL)
1100 ifp = dhd->iflist[0];
1101
1102 ASSERT(ifp);
1103 skb->dev = ifp->net;
1104 skb->protocol = eth_type_trans(skb, skb->dev);
1105
1106 if (skb->pkt_type == PACKET_MULTICAST)
1107 dhd->pub.rx_multicast++;
1108
1109 skb->data = eth;
1110 skb->len = len;
1111
1112 /* Strip header, count, deliver upward */
1113 skb_pull(skb, ETH_HLEN);
1114
1115 /* Process special event packets and then discard them */
628f10ba 1116 if (ntohs(skb->protocol) == ETH_P_BRCM)
cf2b4488 1117 dhd_wl_host_event(dhd, &ifidx,
d4fcdc68 1118 skb_mac_header(skb),
cf2b4488
HP
1119 &event, &data);
1120
1121 ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
1122 if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
1123 ifp = dhd->iflist[ifidx];
1124
1125 if (ifp->net)
1126 ifp->net->last_rx = jiffies;
1127
1128 dhdp->dstats.rx_bytes += skb->len;
1129 dhdp->rx_packets++; /* Local count */
1130
1131 if (in_interrupt()) {
1132 netif_rx(skb);
1133 } else {
1134 /* If the receive is not processed inside an ISR,
1135 * the softirqd must be woken explicitly to service
1136 * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled
1137 * by netif_rx_ni(), but in earlier kernels, we need
1138 * to do it manually.
1139 */
1140 netif_rx_ni(skb);
1141 }
1142 }
1143}
1144
a84bac46 1145void brcmf_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
cf2b4488
HP
1146{
1147 /* Linux version has nothing to do */
1148 return;
1149}
1150
a84bac46 1151void brcmf_txcomplete(dhd_pub_t *dhdp, struct sk_buff *txp, bool success)
cf2b4488
HP
1152{
1153 uint ifidx;
1154 dhd_info_t *dhd = (dhd_info_t *) (dhdp->info);
e2582ad8 1155 struct ethhdr *eh;
7d4df48e 1156 u16 type;
cf2b4488 1157
0aa94b84 1158 brcmf_proto_hdrpull(dhdp, &ifidx, txp);
cf2b4488 1159
e2582ad8 1160 eh = (struct ethhdr *)(txp->data);
628f10ba 1161 type = ntohs(eh->h_proto);
cf2b4488 1162
fcbdbed0 1163 if (type == ETH_P_PAE)
cf2b4488
HP
1164 atomic_dec(&dhd->pend_8021x_cnt);
1165
1166}
1167
1168static struct net_device_stats *dhd_get_stats(struct net_device *net)
1169{
1170 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(net);
1171 dhd_if_t *ifp;
1172 int ifidx;
1173
1174 DHD_TRACE(("%s: Enter\n", __func__));
1175
1176 ifidx = dhd_net2idx(dhd, net);
1177 if (ifidx == DHD_BAD_IF)
1178 return NULL;
1179
1180 ifp = dhd->iflist[ifidx];
1181 ASSERT(dhd && ifp);
1182
1183 if (dhd->pub.up) {
1184 /* Use the protocol to get dongle stats */
0aa94b84 1185 brcmf_proto_dstats(&dhd->pub);
cf2b4488
HP
1186 }
1187
1188 /* Copy dongle stats to net device stats */
1189 ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
1190 ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
1191 ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
1192 ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
1193 ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
1194 ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
1195 ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
1196 ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
1197 ifp->stats.multicast = dhd->pub.dstats.multicast;
1198
1199 return &ifp->stats;
1200}
1201
1202static int dhd_watchdog_thread(void *data)
1203{
1204 dhd_info_t *dhd = (dhd_info_t *) data;
cf2b4488
HP
1205
1206 /* This thread doesn't need any user-level access,
1207 * so get rid of all our resources
1208 */
1209#ifdef DHD_SCHED
1210 if (dhd_watchdog_prio > 0) {
1211 struct sched_param param;
1212 param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO) ?
1213 dhd_watchdog_prio : (MAX_RT_PRIO - 1);
15cf23d5 1214 sched_setscheduler(current, SCHED_FIFO, &param);
cf2b4488
HP
1215 }
1216#endif /* DHD_SCHED */
1217
af737136 1218 allow_signal(SIGTERM);
cf2b4488
HP
1219 /* Run until signal received */
1220 while (1) {
860708d9
JC
1221 if (kthread_should_stop())
1222 break;
cf2b4488 1223 if (down_interruptible(&dhd->watchdog_sem) == 0) {
0965ae88 1224 if (dhd->pub.dongle_reset == false) {
cf2b4488 1225 /* Call the bus module watchdog */
54ca2969 1226 brcmf_sdbrcm_bus_watchdog(&dhd->pub);
cf2b4488
HP
1227 }
1228 /* Count the tick for reference */
1229 dhd->pub.tickcnt++;
1230 } else
1231 break;
1232 }
860708d9 1233 return 0;
cf2b4488
HP
1234}
1235
3deea904 1236static void dhd_watchdog(unsigned long data)
cf2b4488
HP
1237{
1238 dhd_info_t *dhd = (dhd_info_t *) data;
1239
860708d9 1240 if (dhd->watchdog_tsk) {
cf2b4488
HP
1241 up(&dhd->watchdog_sem);
1242
1243 /* Reschedule the watchdog */
1244 if (dhd->wd_timer_valid) {
1245 mod_timer(&dhd->timer,
5e92aa8c 1246 jiffies + brcmf_watchdog_ms * HZ / 1000);
cf2b4488
HP
1247 }
1248 return;
1249 }
1250
1251 /* Call the bus module watchdog */
54ca2969 1252 brcmf_sdbrcm_bus_watchdog(&dhd->pub);
cf2b4488
HP
1253
1254 /* Count the tick for reference */
1255 dhd->pub.tickcnt++;
1256
1257 /* Reschedule the watchdog */
1258 if (dhd->wd_timer_valid)
5e92aa8c 1259 mod_timer(&dhd->timer, jiffies + brcmf_watchdog_ms * HZ / 1000);
cf2b4488
HP
1260}
1261
1262static int dhd_dpc_thread(void *data)
1263{
1264 dhd_info_t *dhd = (dhd_info_t *) data;
1265
cf2b4488
HP
1266 /* This thread doesn't need any user-level access,
1267 * so get rid of all our resources
1268 */
1269#ifdef DHD_SCHED
1270 if (dhd_dpc_prio > 0) {
1271 struct sched_param param;
1272 param.sched_priority =
1273 (dhd_dpc_prio <
1274 MAX_RT_PRIO) ? dhd_dpc_prio : (MAX_RT_PRIO - 1);
15cf23d5 1275 sched_setscheduler(current, SCHED_FIFO, &param);
cf2b4488
HP
1276 }
1277#endif /* DHD_SCHED */
1278
af737136 1279 allow_signal(SIGTERM);
cf2b4488
HP
1280 /* Run until signal received */
1281 while (1) {
eeb8e46b 1282 if (kthread_should_stop())
ecd7559d 1283 break;
cf2b4488
HP
1284 if (down_interruptible(&dhd->dpc_sem) == 0) {
1285 /* Call bus dpc unless it indicated down
1286 (then clean stop) */
1287 if (dhd->pub.busstate != DHD_BUS_DOWN) {
cf2b4488
HP
1288 if (dhd_bus_dpc(dhd->pub.bus)) {
1289 up(&dhd->dpc_sem);
cf2b4488 1290 }
cf2b4488 1291 } else {
54ca2969 1292 brcmf_sdbrcm_bus_stop(dhd->pub.bus, true);
cf2b4488
HP
1293 }
1294 } else
1295 break;
1296 }
ecd7559d 1297 return 0;
cf2b4488
HP
1298}
1299
3deea904 1300static void dhd_dpc(unsigned long data)
cf2b4488
HP
1301{
1302 dhd_info_t *dhd;
1303
1304 dhd = (dhd_info_t *) data;
1305
1306 /* Call bus dpc unless it indicated down (then clean stop) */
1307 if (dhd->pub.busstate != DHD_BUS_DOWN) {
1308 if (dhd_bus_dpc(dhd->pub.bus))
1309 tasklet_schedule(&dhd->tasklet);
1310 } else {
54ca2969 1311 brcmf_sdbrcm_bus_stop(dhd->pub.bus, true);
cf2b4488
HP
1312 }
1313}
1314
a84bac46 1315void brcmf_sched_dpc(dhd_pub_t *dhdp)
cf2b4488
HP
1316{
1317 dhd_info_t *dhd = (dhd_info_t *) dhdp->info;
1318
ecd7559d 1319 if (dhd->dpc_tsk) {
cf2b4488
HP
1320 up(&dhd->dpc_sem);
1321 return;
1322 }
1323
1324 tasklet_schedule(&dhd->tasklet);
1325}
1326
1327#ifdef TOE
1328/* Retrieve current toe component enables, which are kept
1329 as a bitmap in toe_ol iovar */
66cbd3ab 1330static int dhd_toe_get(dhd_info_t *dhd, int ifidx, u32 *toe_ol)
cf2b4488
HP
1331{
1332 wl_ioctl_t ioc;
1333 char buf[32];
1334 int ret;
1335
1336 memset(&ioc, 0, sizeof(ioc));
1337
366bbb3f 1338 ioc.cmd = BRCMF_C_GET_VAR;
cf2b4488
HP
1339 ioc.buf = buf;
1340 ioc.len = (uint) sizeof(buf);
0965ae88 1341 ioc.set = false;
cf2b4488
HP
1342
1343 strcpy(buf, "toe_ol");
0aa94b84 1344 ret = brcmf_proto_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
a618cc28 1345 if (ret < 0) {
cf2b4488
HP
1346 /* Check for older dongle image that doesn't support toe_ol */
1347 if (ret == -EIO) {
1348 DHD_ERROR(("%s: toe not supported by device\n",
a84bac46 1349 brcmf_ifname(&dhd->pub, ifidx)));
cf2b4488
HP
1350 return -EOPNOTSUPP;
1351 }
1352
1353 DHD_INFO(("%s: could not get toe_ol: ret=%d\n",
a84bac46 1354 brcmf_ifname(&dhd->pub, ifidx), ret));
cf2b4488
HP
1355 return ret;
1356 }
1357
66cbd3ab 1358 memcpy(toe_ol, buf, sizeof(u32));
cf2b4488
HP
1359 return 0;
1360}
1361
1362/* Set current toe component enables in toe_ol iovar,
1363 and set toe global enable iovar */
66cbd3ab 1364static int dhd_toe_set(dhd_info_t *dhd, int ifidx, u32 toe_ol)
cf2b4488
HP
1365{
1366 wl_ioctl_t ioc;
1367 char buf[32];
1368 int toe, ret;
1369
1370 memset(&ioc, 0, sizeof(ioc));
1371
366bbb3f 1372 ioc.cmd = BRCMF_C_SET_VAR;
cf2b4488
HP
1373 ioc.buf = buf;
1374 ioc.len = (uint) sizeof(buf);
0f0881b0 1375 ioc.set = true;
cf2b4488
HP
1376
1377 /* Set toe_ol as requested */
1378
1379 strcpy(buf, "toe_ol");
66cbd3ab 1380 memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(u32));
cf2b4488 1381
0aa94b84 1382 ret = brcmf_proto_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
a618cc28 1383 if (ret < 0) {
cf2b4488 1384 DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
a84bac46 1385 brcmf_ifname(&dhd->pub, ifidx), ret));
cf2b4488
HP
1386 return ret;
1387 }
1388
1389 /* Enable toe globally only if any components are enabled. */
1390
1391 toe = (toe_ol != 0);
1392
1393 strcpy(buf, "toe");
66cbd3ab 1394 memcpy(&buf[sizeof("toe")], &toe, sizeof(u32));
cf2b4488 1395
0aa94b84 1396 ret = brcmf_proto_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
a618cc28 1397 if (ret < 0) {
cf2b4488 1398 DHD_ERROR(("%s: could not set toe: ret=%d\n",
a84bac46 1399 brcmf_ifname(&dhd->pub, ifidx), ret));
cf2b4488
HP
1400 return ret;
1401 }
1402
1403 return 0;
1404}
1405#endif /* TOE */
1406
1407static void dhd_ethtool_get_drvinfo(struct net_device *net,
1408 struct ethtool_drvinfo *info)
1409{
1410 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(net);
1411
f70a456a 1412 sprintf(info->driver, KBUILD_MODNAME);
cf2b4488 1413 sprintf(info->version, "%lu", dhd->pub.drv_version);
93ad12cf 1414 sprintf(info->fw_version, "%s", wl_cfg80211_get_fwname());
1415 sprintf(info->bus_info, "%s", dev_name(&wl_cfg80211_get_sdio_func()->dev));
cf2b4488
HP
1416}
1417
1418struct ethtool_ops dhd_ethtool_ops = {
1419 .get_drvinfo = dhd_ethtool_get_drvinfo
1420};
1421
1422static int dhd_ethtool(dhd_info_t *dhd, void *uaddr)
1423{
1424 struct ethtool_drvinfo info;
1425 char drvname[sizeof(info.driver)];
66cbd3ab 1426 u32 cmd;
cf2b4488
HP
1427#ifdef TOE
1428 struct ethtool_value edata;
66cbd3ab 1429 u32 toe_cmpnt, csum_dir;
cf2b4488
HP
1430 int ret;
1431#endif
1432
1433 DHD_TRACE(("%s: Enter\n", __func__));
1434
1435 /* all ethtool calls start with a cmd word */
66cbd3ab 1436 if (copy_from_user(&cmd, uaddr, sizeof(u32)))
cf2b4488
HP
1437 return -EFAULT;
1438
1439 switch (cmd) {
1440 case ETHTOOL_GDRVINFO:
1441 /* Copy out any request driver name */
1442 if (copy_from_user(&info, uaddr, sizeof(info)))
1443 return -EFAULT;
1444 strncpy(drvname, info.driver, sizeof(info.driver));
1445 drvname[sizeof(info.driver) - 1] = '\0';
1446
1447 /* clear struct for return */
1448 memset(&info, 0, sizeof(info));
1449 info.cmd = cmd;
1450
1451 /* if dhd requested, identify ourselves */
1452 if (strcmp(drvname, "?dhd") == 0) {
1453 sprintf(info.driver, "dhd");
1454 strcpy(info.version, EPI_VERSION_STR);
1455 }
1456
1457 /* otherwise, require dongle to be up */
1458 else if (!dhd->pub.up) {
1459 DHD_ERROR(("%s: dongle is not up\n", __func__));
1460 return -ENODEV;
1461 }
1462
1463 /* finally, report dongle driver type */
1464 else if (dhd->pub.iswl)
1465 sprintf(info.driver, "wl");
1466 else
1467 sprintf(info.driver, "xx");
1468
1469 sprintf(info.version, "%lu", dhd->pub.drv_version);
1470 if (copy_to_user(uaddr, &info, sizeof(info)))
1471 return -EFAULT;
1472 DHD_CTL(("%s: given %*s, returning %s\n", __func__,
1473 (int)sizeof(drvname), drvname, info.driver));
1474 break;
1475
1476#ifdef TOE
1477 /* Get toe offload components from dongle */
1478 case ETHTOOL_GRXCSUM:
1479 case ETHTOOL_GTXCSUM:
a618cc28
JC
1480 ret = dhd_toe_get(dhd, 0, &toe_cmpnt);
1481 if (ret < 0)
cf2b4488
HP
1482 return ret;
1483
1484 csum_dir =
1485 (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
1486
1487 edata.cmd = cmd;
1488 edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
1489
1490 if (copy_to_user(uaddr, &edata, sizeof(edata)))
1491 return -EFAULT;
1492 break;
1493
1494 /* Set toe offload components in dongle */
1495 case ETHTOOL_SRXCSUM:
1496 case ETHTOOL_STXCSUM:
1497 if (copy_from_user(&edata, uaddr, sizeof(edata)))
1498 return -EFAULT;
1499
1500 /* Read the current settings, update and write back */
a618cc28
JC
1501 ret = dhd_toe_get(dhd, 0, &toe_cmpnt);
1502 if (ret < 0)
cf2b4488
HP
1503 return ret;
1504
1505 csum_dir =
1506 (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
1507
1508 if (edata.data != 0)
1509 toe_cmpnt |= csum_dir;
1510 else
1511 toe_cmpnt &= ~csum_dir;
1512
a618cc28
JC
1513 ret = dhd_toe_set(dhd, 0, toe_cmpnt);
1514 if (ret < 0)
cf2b4488
HP
1515 return ret;
1516
1517 /* If setting TX checksum mode, tell Linux the new mode */
1518 if (cmd == ETHTOOL_STXCSUM) {
1519 if (edata.data)
1520 dhd->iflist[0]->net->features |=
1521 NETIF_F_IP_CSUM;
1522 else
1523 dhd->iflist[0]->net->features &=
1524 ~NETIF_F_IP_CSUM;
1525 }
1526
1527 break;
1528#endif /* TOE */
1529
1530 default:
1531 return -EOPNOTSUPP;
1532 }
1533
1534 return 0;
1535}
1536
1537static int dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd)
1538{
1539 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(net);
1540 dhd_ioctl_t ioc;
1541 int bcmerror = 0;
1542 int buflen = 0;
1543 void *buf = NULL;
1544 uint driver = 0;
1545 int ifidx;
1546 bool is_set_key_cmd;
1547
1548 ifidx = dhd_net2idx(dhd, net);
1549 DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __func__, ifidx, cmd));
1550
1551 if (ifidx == DHD_BAD_IF)
1552 return -1;
1553
cf2b4488
HP
1554 if (cmd == SIOCETHTOOL)
1555 return dhd_ethtool(dhd, (void *)ifr->ifr_data);
1556
1557 if (cmd != SIOCDEVPRIVATE)
1558 return -EOPNOTSUPP;
1559
1560 memset(&ioc, 0, sizeof(ioc));
1561
1562 /* Copy the ioc control structure part of ioctl request */
1563 if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) {
e10d82d4 1564 bcmerror = -EINVAL;
cf2b4488
HP
1565 goto done;
1566 }
1567
1568 /* Copy out any buffer passed */
1569 if (ioc.buf) {
53e974db 1570 buflen = min_t(int, ioc.len, DHD_IOCTL_MAXLEN);
cf2b4488
HP
1571 /* optimization for direct ioctl calls from kernel */
1572 /*
1573 if (segment_eq(get_fs(), KERNEL_DS)) {
1574 buf = ioc.buf;
1575 } else {
1576 */
1577 {
5fcc1fcb 1578 buf = kmalloc(buflen, GFP_ATOMIC);
a618cc28 1579 if (!buf) {
e10d82d4 1580 bcmerror = -ENOMEM;
cf2b4488
HP
1581 goto done;
1582 }
1583 if (copy_from_user(buf, ioc.buf, buflen)) {
e10d82d4 1584 bcmerror = -EINVAL;
cf2b4488
HP
1585 goto done;
1586 }
1587 }
1588 }
1589
1590 /* To differentiate between wl and dhd read 4 more byes */
1591 if ((copy_from_user(&driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t),
1592 sizeof(uint)) != 0)) {
e10d82d4 1593 bcmerror = -EINVAL;
cf2b4488
HP
1594 goto done;
1595 }
1596
1597 if (!capable(CAP_NET_ADMIN)) {
e10d82d4 1598 bcmerror = -EPERM;
cf2b4488
HP
1599 goto done;
1600 }
1601
1602 /* check for local dhd ioctl and handle it */
1603 if (driver == DHD_IOCTL_MAGIC) {
aba5e0ad 1604 bcmerror = brcmf_c_ioctl((void *)&dhd->pub, &ioc, buf, buflen);
cf2b4488
HP
1605 if (bcmerror)
1606 dhd->pub.bcmerror = bcmerror;
1607 goto done;
1608 }
1609
1610 /* send to dongle (must be up, and wl) */
1611 if ((dhd->pub.busstate != DHD_BUS_DATA)) {
1612 DHD_ERROR(("%s DONGLE_DOWN,__func__\n", __func__));
b74ac12e 1613 bcmerror = -EIO;
cf2b4488
HP
1614 goto done;
1615 }
1616
1617 if (!dhd->pub.iswl) {
b74ac12e 1618 bcmerror = -EIO;
cf2b4488
HP
1619 goto done;
1620 }
1621
366bbb3f
AS
1622 /*
1623 * Intercept BRCMF_C_SET_KEY IOCTL - serialize M4 send and
1624 * set key IOCTL to prevent M4 encryption.
cf2b4488 1625 */
366bbb3f
AS
1626 is_set_key_cmd = ((ioc.cmd == BRCMF_C_SET_KEY) ||
1627 ((ioc.cmd == BRCMF_C_SET_VAR) &&
cf2b4488 1628 !(strncmp("wsec_key", ioc.buf, 9))) ||
366bbb3f 1629 ((ioc.cmd == BRCMF_C_SET_VAR) &&
cf2b4488
HP
1630 !(strncmp("bsscfg:wsec_key", ioc.buf, 15))));
1631 if (is_set_key_cmd)
1632 dhd_wait_pend8021x(net);
1633
cf2b4488 1634 bcmerror =
0aa94b84
AS
1635 brcmf_proto_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf,
1636 buflen);
cf2b4488 1637
cf2b4488
HP
1638done:
1639 if (!bcmerror && buf && ioc.buf) {
1640 if (copy_to_user(ioc.buf, buf, buflen))
1641 bcmerror = -EFAULT;
1642 }
1643
46d994b1 1644 kfree(buf);
cf2b4488 1645
9014378b
BR
1646 if (bcmerror > 0)
1647 bcmerror = 0;
9014378b 1648
b74ac12e 1649 return bcmerror;
cf2b4488
HP
1650}
1651
1652static int dhd_stop(struct net_device *net)
1653{
1654#if !defined(IGNORE_ETH0_DOWN)
1655 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(net);
1656
1657 DHD_TRACE(("%s: Enter\n", __func__));
f10c5b0b 1658 wl_cfg80211_down();
cf2b4488
HP
1659 if (dhd->pub.up == 0)
1660 return 0;
1661
1662 /* Set state and stop OS transmissions */
1663 dhd->pub.up = 0;
1664 netif_stop_queue(net);
1665#else
1666 DHD_ERROR(("BYPASS %s:due to BRCM compilation : under investigation\n",
1667 __func__));
1668#endif /* !defined(IGNORE_ETH0_DOWN) */
1669
cf2b4488
HP
1670 return 0;
1671}
1672
1673static int dhd_open(struct net_device *net)
1674{
1675 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(net);
1676#ifdef TOE
66cbd3ab 1677 u32 toe_ol;
cf2b4488
HP
1678#endif
1679 int ifidx = dhd_net2idx(dhd, net);
3e26416e 1680 s32 ret = 0;
cf2b4488
HP
1681
1682 DHD_TRACE(("%s: ifidx %d\n", __func__, ifidx));
1683
1684 if (ifidx == 0) { /* do it only for primary eth0 */
1685
1686 /* try to bring up bus */
a84bac46 1687 ret = brcmf_bus_start(&dhd->pub);
a618cc28 1688 if (ret != 0) {
cf2b4488
HP
1689 DHD_ERROR(("%s: failed with code %d\n", __func__, ret));
1690 return -1;
1691 }
1692 atomic_set(&dhd->pend_8021x_cnt, 0);
1693
a44d4236 1694 memcpy(net->dev_addr, dhd->pub.mac, ETH_ALEN);
cf2b4488
HP
1695
1696#ifdef TOE
1697 /* Get current TOE mode from dongle */
1698 if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0
1699 && (toe_ol & TOE_TX_CSUM_OL) != 0)
1700 dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM;
1701 else
1702 dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM;
1703#endif
1704 }
1705 /* Allow transmit calls */
1706 netif_start_queue(net);
1707 dhd->pub.up = 1;
f10c5b0b
AS
1708 if (unlikely(wl_cfg80211_up())) {
1709 DHD_ERROR(("%s: failed to bring up cfg80211\n",
1710 __func__));
1711 return -1;
cf2b4488 1712 }
cf2b4488 1713
cf2b4488
HP
1714 return ret;
1715}
1716
cf2b4488 1717int
a84bac46 1718brcmf_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name,
66cbd3ab 1719 u8 *mac_addr, u32 flags, u8 bssidx)
cf2b4488
HP
1720{
1721 dhd_if_t *ifp;
1722
1723 DHD_TRACE(("%s: idx %d, handle->%p\n", __func__, ifidx, handle));
1724
1725 ASSERT(dhd && (ifidx < DHD_MAX_IFS));
1726
1727 ifp = dhd->iflist[ifidx];
a7c551bc
AS
1728 if (!ifp) {
1729 ifp = kmalloc(sizeof(dhd_if_t), GFP_ATOMIC);
1730 if (!ifp) {
1731 DHD_ERROR(("%s: OOM - dhd_if_t\n", __func__));
1732 return -ENOMEM;
1733 }
cf2b4488
HP
1734 }
1735
1736 memset(ifp, 0, sizeof(dhd_if_t));
1737 ifp->info = dhd;
1738 dhd->iflist[ifidx] = ifp;
cbf6baac 1739 strlcpy(ifp->name, name, IFNAMSIZ);
cf2b4488 1740 if (mac_addr != NULL)
b8d63078 1741 memcpy(&ifp->mac_addr, mac_addr, ETH_ALEN);
cf2b4488
HP
1742
1743 if (handle == NULL) {
05dc0f43 1744 ifp->state = BRCMF_E_IF_ADD;
cf2b4488 1745 ifp->idx = ifidx;
d809dcb9 1746 ASSERT(dhd->sysioc_tsk);
cf2b4488
HP
1747 up(&dhd->sysioc_sem);
1748 } else
1749 ifp->net = (struct net_device *)handle;
1750
1751 return 0;
1752}
1753
a84bac46 1754void brcmf_del_if(dhd_info_t *dhd, int ifidx)
cf2b4488
HP
1755{
1756 dhd_if_t *ifp;
1757
1758 DHD_TRACE(("%s: idx %d\n", __func__, ifidx));
1759
1760 ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS));
1761 ifp = dhd->iflist[ifidx];
1762 if (!ifp) {
1763 DHD_ERROR(("%s: Null interface\n", __func__));
1764 return;
1765 }
1766
05dc0f43 1767 ifp->state = BRCMF_E_IF_DEL;
cf2b4488 1768 ifp->idx = ifidx;
d809dcb9 1769 ASSERT(dhd->sysioc_tsk);
cf2b4488
HP
1770 up(&dhd->sysioc_sem);
1771}
1772
a84bac46 1773dhd_pub_t *brcmf_attach(struct dhd_bus *bus, uint bus_hdrlen)
cf2b4488
HP
1774{
1775 dhd_info_t *dhd = NULL;
1776 struct net_device *net;
1777
1778 DHD_TRACE(("%s: Enter\n", __func__));
1779 /* updates firmware nvram path if it was provided as module
1780 paramters */
1781 if ((firmware_path != NULL) && (firmware_path[0] != '\0'))
aba5e0ad 1782 strcpy(brcmf_fw_path, firmware_path);
cf2b4488 1783 if ((nvram_path != NULL) && (nvram_path[0] != '\0'))
aba5e0ad 1784 strcpy(brcmf_nv_path, nvram_path);
cf2b4488
HP
1785
1786 /* Allocate etherdev, including space for private structure */
a618cc28
JC
1787 net = alloc_etherdev(sizeof(dhd));
1788 if (!net) {
cf2b4488
HP
1789 DHD_ERROR(("%s: OOM - alloc_etherdev\n", __func__));
1790 goto fail;
1791 }
1792
1793 /* Allocate primary dhd_info */
12d0eb47 1794 dhd = kzalloc(sizeof(dhd_info_t), GFP_ATOMIC);
a618cc28 1795 if (!dhd) {
cf2b4488
HP
1796 DHD_ERROR(("%s: OOM - alloc dhd_info\n", __func__));
1797 goto fail;
1798 }
1799
cf2b4488
HP
1800 /*
1801 * Save the dhd_info into the priv
1802 */
1803 memcpy(netdev_priv(net), &dhd, sizeof(dhd));
cf2b4488
HP
1804
1805 /* Set network interface name if it was provided as module parameter */
1806 if (iface_name[0]) {
1807 int len;
1808 char ch;
1809 strncpy(net->name, iface_name, IFNAMSIZ);
1810 net->name[IFNAMSIZ - 1] = 0;
1811 len = strlen(net->name);
1812 ch = net->name[len - 1];
1813 if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2))
1814 strcat(net->name, "%d");
1815 }
1816
a84bac46 1817 if (brcmf_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) ==
cf2b4488
HP
1818 DHD_BAD_IF)
1819 goto fail;
1820
1821 net->netdev_ops = NULL;
45f4d024 1822 sema_init(&dhd->proto_sem, 1);
cf2b4488
HP
1823 /* Initialize other structure content */
1824 init_waitqueue_head(&dhd->ioctl_resp_wait);
1825 init_waitqueue_head(&dhd->ctrl_wait);
1826
1827 /* Initialize the spinlocks */
1828 spin_lock_init(&dhd->sdlock);
cf2b4488
HP
1829
1830 /* Link to info module */
1831 dhd->pub.info = dhd;
1832
1833 /* Link to bus module */
1834 dhd->pub.bus = bus;
1835 dhd->pub.hdrlen = bus_hdrlen;
1836
1837 /* Attach and link in the protocol */
0aa94b84 1838 if (brcmf_proto_attach(&dhd->pub) != 0) {
cf2b4488
HP
1839 DHD_ERROR(("dhd_prot_attach failed\n"));
1840 goto fail;
1841 }
cf2b4488 1842
cf2b4488 1843 /* Attach and link in the cfg80211 */
f10c5b0b
AS
1844 if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) {
1845 DHD_ERROR(("wl_cfg80211_attach failed\n"));
1846 goto fail;
1847 }
1848 if (!dhd_no_fw_req) {
aba5e0ad
AS
1849 strcpy(brcmf_fw_path, wl_cfg80211_get_fwname());
1850 strcpy(brcmf_nv_path, wl_cfg80211_get_nvramname());
cf2b4488 1851 }
cf2b4488
HP
1852
1853 /* Set up the watchdog timer */
1854 init_timer(&dhd->timer);
3deea904 1855 dhd->timer.data = (unsigned long) dhd;
cf2b4488
HP
1856 dhd->timer.function = dhd_watchdog;
1857
1858 /* Initialize thread based operation and lock */
45f4d024 1859 sema_init(&dhd->sdsem, 1);
cf2b4488 1860 if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0))
0f0881b0 1861 dhd->threads_only = true;
cf2b4488 1862 else
0965ae88 1863 dhd->threads_only = false;
cf2b4488
HP
1864
1865 if (dhd_dpc_prio >= 0) {
1866 /* Initialize watchdog thread */
1867 sema_init(&dhd->watchdog_sem, 0);
860708d9
JC
1868 dhd->watchdog_tsk = kthread_run(dhd_watchdog_thread, dhd,
1869 "dhd_watchdog");
1870 if (IS_ERR(dhd->watchdog_tsk)) {
1871 printk(KERN_WARNING
1872 "dhd_watchdog thread failed to start\n");
1873 dhd->watchdog_tsk = NULL;
1874 }
cf2b4488 1875 } else {
860708d9 1876 dhd->watchdog_tsk = NULL;
cf2b4488
HP
1877 }
1878
1879 /* Set up the bottom half handler */
1880 if (dhd_dpc_prio >= 0) {
1881 /* Initialize DPC thread */
1882 sema_init(&dhd->dpc_sem, 0);
ecd7559d
JC
1883 dhd->dpc_tsk = kthread_run(dhd_dpc_thread, dhd, "dhd_dpc");
1884 if (IS_ERR(dhd->dpc_tsk)) {
1885 printk(KERN_WARNING
1886 "dhd_dpc thread failed to start\n");
1887 dhd->dpc_tsk = NULL;
1888 }
cf2b4488 1889 } else {
3deea904 1890 tasklet_init(&dhd->tasklet, dhd_dpc, (unsigned long) dhd);
ecd7559d 1891 dhd->dpc_tsk = NULL;
cf2b4488
HP
1892 }
1893
1894 if (dhd_sysioc) {
1895 sema_init(&dhd->sysioc_sem, 0);
d809dcb9
JC
1896 dhd->sysioc_tsk = kthread_run(_dhd_sysioc_thread, dhd,
1897 "_dhd_sysioc");
1898 if (IS_ERR(dhd->sysioc_tsk)) {
1899 printk(KERN_WARNING
1900 "_dhd_sysioc thread failed to start\n");
1901 dhd->sysioc_tsk = NULL;
1902 }
1903 } else
1904 dhd->sysioc_tsk = NULL;
cf2b4488
HP
1905
1906 /*
1907 * Save the dhd_info into the priv
1908 */
1909 memcpy(netdev_priv(net), &dhd, sizeof(dhd));
1910
1911#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
1912 g_bus = bus;
1913#endif
1914#if defined(CONFIG_PM_SLEEP)
e6e8f894 1915 atomic_set(&dhd_mmc_suspend, false);
cf2b4488
HP
1916#endif /* defined(CONFIG_PM_SLEEP) */
1917 /* && defined(DHD_GPL) */
1918 /* Init lock suspend to prevent kernel going to suspend */
cf2b4488
HP
1919#ifdef CONFIG_HAS_EARLYSUSPEND
1920 dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20;
1921 dhd->early_suspend.suspend = dhd_early_suspend;
1922 dhd->early_suspend.resume = dhd_late_resume;
1923 register_early_suspend(&dhd->early_suspend);
1924#endif
1925
1926 return &dhd->pub;
1927
1928fail:
1929 if (net)
1930 free_netdev(net);
1931 if (dhd)
a84bac46 1932 brcmf_detach(&dhd->pub);
cf2b4488
HP
1933
1934 return NULL;
1935}
1936
a84bac46 1937int brcmf_bus_start(dhd_pub_t *dhdp)
cf2b4488
HP
1938{
1939 int ret = -1;
1940 dhd_info_t *dhd = (dhd_info_t *) dhdp->info;
1941#ifdef EMBEDDED_PLATFORM
1942 char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" +
1943 '\0' + bitvec */
1944#endif /* EMBEDDED_PLATFORM */
1945
1946 ASSERT(dhd);
1947
1948 DHD_TRACE(("%s:\n", __func__));
1949
1950 /* try to download image and nvram to the dongle */
1951 if (dhd->pub.busstate == DHD_BUS_DOWN) {
aba5e0ad
AS
1952 if (!(dhd_bus_download_firmware(dhd->pub.bus, brcmf_fw_path,
1953 brcmf_nv_path))) {
54ca2969 1954 DHD_ERROR(("%s: dhd_bus_download_firmware failed. "
cf2b4488 1955 "firmware = %s nvram = %s\n",
aba5e0ad 1956 __func__, brcmf_fw_path, brcmf_nv_path));
cf2b4488
HP
1957 return -1;
1958 }
cf2b4488
HP
1959 }
1960
1961 /* Start the watchdog timer */
1962 dhd->pub.tickcnt = 0;
a84bac46 1963 brcmf_os_wd_timer(&dhd->pub, brcmf_watchdog_ms);
cf2b4488
HP
1964
1965 /* Bring up the bus */
54ca2969 1966 ret = brcmf_sdbrcm_bus_init(&dhd->pub, true);
a618cc28 1967 if (ret != 0) {
54ca2969
RV
1968 DHD_ERROR(("%s, brcmf_sdbrcm_bus_init failed %d\n", __func__,
1969 ret));
cf2b4488
HP
1970 return ret;
1971 }
1972#if defined(OOB_INTR_ONLY)
1973 /* Host registration for OOB interrupt */
54ca2969 1974 if (brcmf_sdio_register_oob_intr(dhdp)) {
cf2b4488 1975 del_timer_sync(&dhd->timer);
0965ae88 1976 dhd->wd_timer_valid = false;
cf2b4488
HP
1977 DHD_ERROR(("%s Host failed to resgister for OOB\n", __func__));
1978 return -ENODEV;
1979 }
1980
1981 /* Enable oob at firmware */
54ca2969 1982 brcmf_sdbrcm_enable_oob_intr(dhd->pub.bus, true);
cf2b4488
HP
1983#endif /* defined(OOB_INTR_ONLY) */
1984
1985 /* If bus is not ready, can't come up */
1986 if (dhd->pub.busstate != DHD_BUS_DATA) {
1987 del_timer_sync(&dhd->timer);
0965ae88 1988 dhd->wd_timer_valid = false;
cf2b4488
HP
1989 DHD_ERROR(("%s failed bus is not ready\n", __func__));
1990 return -ENODEV;
1991 }
1992#ifdef EMBEDDED_PLATFORM
67ad48bc
RV
1993 brcmu_mkiovar("event_msgs", dhdp->eventmask, WL_EVENTING_MASK_LEN,
1994 iovbuf, sizeof(iovbuf));
0aa94b84
AS
1995 brcmf_proto_cdc_query_ioctl(dhdp, 0, BRCMF_C_GET_VAR, iovbuf,
1996 sizeof(iovbuf));
02160695 1997 memcpy(dhdp->eventmask, iovbuf, WL_EVENTING_MASK_LEN);
cf2b4488 1998
05dc0f43
AS
1999 setbit(dhdp->eventmask, BRCMF_E_SET_SSID);
2000 setbit(dhdp->eventmask, BRCMF_E_PRUNE);
2001 setbit(dhdp->eventmask, BRCMF_E_AUTH);
2002 setbit(dhdp->eventmask, BRCMF_E_REASSOC);
2003 setbit(dhdp->eventmask, BRCMF_E_REASSOC_IND);
2004 setbit(dhdp->eventmask, BRCMF_E_DEAUTH_IND);
2005 setbit(dhdp->eventmask, BRCMF_E_DISASSOC_IND);
2006 setbit(dhdp->eventmask, BRCMF_E_DISASSOC);
2007 setbit(dhdp->eventmask, BRCMF_E_JOIN);
2008 setbit(dhdp->eventmask, BRCMF_E_ASSOC_IND);
2009 setbit(dhdp->eventmask, BRCMF_E_PSK_SUP);
2010 setbit(dhdp->eventmask, BRCMF_E_LINK);
2011 setbit(dhdp->eventmask, BRCMF_E_NDIS_LINK);
2012 setbit(dhdp->eventmask, BRCMF_E_MIC_ERROR);
2013 setbit(dhdp->eventmask, BRCMF_E_PMKID_CACHE);
2014 setbit(dhdp->eventmask, BRCMF_E_TXFAIL);
2015 setbit(dhdp->eventmask, BRCMF_E_JOIN_START);
2016 setbit(dhdp->eventmask, BRCMF_E_SCAN_COMPLETE);
cf2b4488 2017#ifdef PNO_SUPPORT
05dc0f43 2018 setbit(dhdp->eventmask, BRCMF_E_PFN_NET_FOUND);
cf2b4488
HP
2019#endif /* PNO_SUPPORT */
2020
2021/* enable dongle roaming event */
2022
2023 dhdp->pktfilter_count = 1;
2024 /* Setup filter to allow only unicast */
2025 dhdp->pktfilter[0] = "100 0 0 0 0x01 0x00";
2026#endif /* EMBEDDED_PLATFORM */
2027
2028 /* Bus is ready, do any protocol initialization */
0aa94b84 2029 ret = brcmf_proto_init(&dhd->pub);
a618cc28 2030 if (ret < 0)
cf2b4488
HP
2031 return ret;
2032
2033 return 0;
2034}
2035
2036int
2037dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len,
2038 int set)
2039{
2040 char buf[strlen(name) + 1 + cmd_len];
2041 int len = sizeof(buf);
2042 wl_ioctl_t ioc;
2043 int ret;
2044
67ad48bc 2045 len = brcmu_mkiovar(name, cmd_buf, cmd_len, buf, len);
cf2b4488
HP
2046
2047 memset(&ioc, 0, sizeof(ioc));
2048
366bbb3f 2049 ioc.cmd = set ? BRCMF_C_SET_VAR : BRCMF_C_GET_VAR;
cf2b4488
HP
2050 ioc.buf = buf;
2051 ioc.len = len;
2052 ioc.set = set;
2053
0aa94b84 2054 ret = brcmf_proto_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len);
cf2b4488
HP
2055 if (!set && ret >= 0)
2056 memcpy(cmd_buf, buf, cmd_len);
2057
2058 return ret;
2059}
2060
2061static struct net_device_ops dhd_ops_pri = {
2062 .ndo_open = dhd_open,
2063 .ndo_stop = dhd_stop,
2064 .ndo_get_stats = dhd_get_stats,
2065 .ndo_do_ioctl = dhd_ioctl_entry,
2066 .ndo_start_xmit = dhd_start_xmit,
2067 .ndo_set_mac_address = dhd_set_mac_address,
2068 .ndo_set_multicast_list = dhd_set_multicast_list
2069};
2070
a84bac46 2071int brcmf_net_attach(dhd_pub_t *dhdp, int ifidx)
cf2b4488
HP
2072{
2073 dhd_info_t *dhd = (dhd_info_t *) dhdp->info;
2074 struct net_device *net;
b8d63078 2075 u8 temp_addr[ETH_ALEN] = {
cf2b4488
HP
2076 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33};
2077
2078 DHD_TRACE(("%s: ifidx %d\n", __func__, ifidx));
2079
2080 ASSERT(dhd && dhd->iflist[ifidx]);
2081
2082 net = dhd->iflist[ifidx]->net;
2083 ASSERT(net);
2084
2085 ASSERT(!net->netdev_ops);
cf2b4488
HP
2086 net->netdev_ops = &dhd_ops_pri;
2087
2088 /*
2089 * We have to use the primary MAC for virtual interfaces
2090 */
2091 if (ifidx != 0) {
2092 /* for virtual interfaces use the primary MAC */
a44d4236 2093 memcpy(temp_addr, dhd->pub.mac, ETH_ALEN);
cf2b4488
HP
2094
2095 }
2096
2097 if (ifidx == 1) {
e131d3ce 2098 DHD_TRACE(("%s ACCESS POINT MAC:\n", __func__));
cf2b4488
HP
2099 /* ACCESSPOINT INTERFACE CASE */
2100 temp_addr[0] |= 0X02; /* set bit 2 ,
2101 - Locally Administered address */
2102
2103 }
2104 net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen;
2105 net->ethtool_ops = &dhd_ethtool_ops;
2106
cf2b4488
HP
2107 dhd->pub.rxsz = net->mtu + net->hard_header_len + dhd->pub.hdrlen;
2108
b8d63078 2109 memcpy(net->dev_addr, temp_addr, ETH_ALEN);
cf2b4488
HP
2110
2111 if (register_netdev(net) != 0) {
2112 DHD_ERROR(("%s: couldn't register the net device\n",
2113 __func__));
2114 goto fail;
2115 }
2116
0bef7748 2117 DHD_INFO(("%s: Broadcom Dongle Host Driver\n", net->name));
cf2b4488
HP
2118
2119 return 0;
2120
2121fail:
2122 net->netdev_ops = NULL;
b74ac12e 2123 return -EBADE;
cf2b4488
HP
2124}
2125
2126void dhd_bus_detach(dhd_pub_t *dhdp)
2127{
2128 dhd_info_t *dhd;
2129
2130 DHD_TRACE(("%s: Enter\n", __func__));
2131
2132 if (dhdp) {
2133 dhd = (dhd_info_t *) dhdp->info;
2134 if (dhd) {
2135 /* Stop the protocol module */
0aa94b84 2136 brcmf_proto_stop(&dhd->pub);
cf2b4488
HP
2137
2138 /* Stop the bus module */
54ca2969 2139 brcmf_sdbrcm_bus_stop(dhd->pub.bus, true);
cf2b4488 2140#if defined(OOB_INTR_ONLY)
54ca2969 2141 brcmf_sdio_unregister_oob_intr();
cf2b4488
HP
2142#endif /* defined(OOB_INTR_ONLY) */
2143
2144 /* Clear the watchdog timer */
2145 del_timer_sync(&dhd->timer);
0965ae88 2146 dhd->wd_timer_valid = false;
cf2b4488
HP
2147 }
2148 }
2149}
2150
a84bac46 2151void brcmf_detach(dhd_pub_t *dhdp)
cf2b4488
HP
2152{
2153 dhd_info_t *dhd;
2154
2155 DHD_TRACE(("%s: Enter\n", __func__));
2156
2157 if (dhdp) {
2158 dhd = (dhd_info_t *) dhdp->info;
2159 if (dhd) {
2160 dhd_if_t *ifp;
2161 int i;
2162
2163#if defined(CONFIG_HAS_EARLYSUSPEND)
2164 if (dhd->early_suspend.suspend)
2165 unregister_early_suspend(&dhd->early_suspend);
2166#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
2167
2168 for (i = 1; i < DHD_MAX_IFS; i++)
2169 if (dhd->iflist[i])
a84bac46 2170 brcmf_del_if(dhd, i);
cf2b4488
HP
2171
2172 ifp = dhd->iflist[0];
2173 ASSERT(ifp);
2174 if (ifp->net->netdev_ops == &dhd_ops_pri) {
2175 dhd_stop(ifp->net);
2176 unregister_netdev(ifp->net);
2177 }
2178
860708d9 2179 if (dhd->watchdog_tsk) {
7356f429 2180 send_sig(SIGTERM, dhd->watchdog_tsk, 1);
860708d9
JC
2181 kthread_stop(dhd->watchdog_tsk);
2182 dhd->watchdog_tsk = NULL;
cf2b4488
HP
2183 }
2184
ecd7559d 2185 if (dhd->dpc_tsk) {
7356f429 2186 send_sig(SIGTERM, dhd->dpc_tsk, 1);
ecd7559d
JC
2187 kthread_stop(dhd->dpc_tsk);
2188 dhd->dpc_tsk = NULL;
eeb8e46b 2189 } else
cf2b4488
HP
2190 tasklet_kill(&dhd->tasklet);
2191
d809dcb9 2192 if (dhd->sysioc_tsk) {
7356f429 2193 send_sig(SIGTERM, dhd->sysioc_tsk, 1);
d809dcb9
JC
2194 kthread_stop(dhd->sysioc_tsk);
2195 dhd->sysioc_tsk = NULL;
cf2b4488
HP
2196 }
2197
2198 dhd_bus_detach(dhdp);
2199
2200 if (dhdp->prot)
0aa94b84 2201 brcmf_proto_detach(dhdp);
cf2b4488 2202
f10c5b0b 2203 wl_cfg80211_detach();
cf2b4488 2204
cf2b4488 2205 /* && defined(DHD_GPL) */
cf2b4488 2206 free_netdev(ifp->net);
182acb3c 2207 kfree(ifp);
2208 kfree(dhd);
cf2b4488
HP
2209 }
2210 }
2211}
2212
2213static void __exit dhd_module_cleanup(void)
2214{
2215 DHD_TRACE(("%s: Enter\n", __func__));
2216
2217 dhd_bus_unregister();
2218#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
2219 wifi_del_dev();
2220#endif
2221 /* Call customer gpio to turn off power with WL_REG_ON signal */
a84bac46 2222 brcmf_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
cf2b4488
HP
2223}
2224
2225static int __init dhd_module_init(void)
2226{
2227 int error;
2228
2229 DHD_TRACE(("%s: Enter\n", __func__));
2230
2231 /* Sanity check on the module parameters */
2232 do {
2233 /* Both watchdog and DPC as tasklets are ok */
2234 if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0))
2235 break;
2236
2237 /* If both watchdog and DPC are threads, TX must be deferred */
2238 if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)
2239 && dhd_deferred_tx)
2240 break;
2241
2242 DHD_ERROR(("Invalid module parameters.\n"));
2243 return -EINVAL;
2244 } while (0);
2245 /* Call customer gpio to turn on power with WL_REG_ON signal */
a84bac46 2246 brcmf_customer_gpio_wlan_ctrl(WLAN_POWER_ON);
cf2b4488
HP
2247
2248#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
2249 sema_init(&wifi_control_sem, 0);
2250
2251 error = wifi_add_dev();
2252 if (error) {
2253 DHD_ERROR(("%s: platform_driver_register failed\n", __func__));
f0c0dda0 2254 goto failed;
cf2b4488
HP
2255 }
2256
2257 /* Waiting callback after platform_driver_register is done or
2258 exit with error */
2259 if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) {
2260 printk(KERN_ERR "%s: platform_driver_register timeout\n",
2261 __func__);
2262 /* remove device */
2263 wifi_del_dev();
f0c0dda0 2264 goto failed;
cf2b4488
HP
2265 }
2266#endif /* #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
2267
2268 error = dhd_bus_register();
2269
49ba9d2d 2270 if (error) {
54ca2969 2271 DHD_ERROR(("%s: dhd_bus_register failed\n", __func__));
f0c0dda0 2272 goto failed;
cf2b4488
HP
2273 }
2274 return error;
2275
f0c0dda0 2276failed:
cf2b4488 2277 /* turn off power and exit */
a84bac46 2278 brcmf_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
cf2b4488
HP
2279 return -EINVAL;
2280}
2281
2282module_init(dhd_module_init);
2283module_exit(dhd_module_cleanup);
2284
2285/*
2286 * OS specific functions required to implement DHD driver in OS independent way
2287 */
a84bac46 2288int brcmf_os_proto_block(dhd_pub_t *pub)
cf2b4488
HP
2289{
2290 dhd_info_t *dhd = (dhd_info_t *) (pub->info);
2291
2292 if (dhd) {
2293 down(&dhd->proto_sem);
2294 return 1;
2295 }
2296 return 0;
2297}
2298
a84bac46 2299int brcmf_os_proto_unblock(dhd_pub_t *pub)
cf2b4488
HP
2300{
2301 dhd_info_t *dhd = (dhd_info_t *) (pub->info);
2302
2303 if (dhd) {
2304 up(&dhd->proto_sem);
2305 return 1;
2306 }
2307
2308 return 0;
2309}
2310
a84bac46 2311unsigned int brcmf_os_get_ioctl_resp_timeout(void)
cf2b4488
HP
2312{
2313 return (unsigned int)dhd_ioctl_timeout_msec;
2314}
2315
a84bac46 2316void brcmf_os_set_ioctl_resp_timeout(unsigned int timeout_msec)
cf2b4488
HP
2317{
2318 dhd_ioctl_timeout_msec = (int)timeout_msec;
2319}
2320
a84bac46 2321int brcmf_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending)
cf2b4488
HP
2322{
2323 dhd_info_t *dhd = (dhd_info_t *) (pub->info);
2324 DECLARE_WAITQUEUE(wait, current);
2325 int timeout = dhd_ioctl_timeout_msec;
2326
2327 /* Convert timeout in millsecond to jiffies */
2328 timeout = timeout * HZ / 1000;
2329
2330 /* Wait until control frame is available */
2331 add_wait_queue(&dhd->ioctl_resp_wait, &wait);
2332 set_current_state(TASK_INTERRUPTIBLE);
2333
2334 while (!(*condition) && (!signal_pending(current) && timeout))
2335 timeout = schedule_timeout(timeout);
2336
2337 if (signal_pending(current))
0f0881b0 2338 *pending = true;
cf2b4488
HP
2339
2340 set_current_state(TASK_RUNNING);
2341 remove_wait_queue(&dhd->ioctl_resp_wait, &wait);
2342
2343 return timeout;
2344}
2345
a84bac46 2346int brcmf_os_ioctl_resp_wake(dhd_pub_t *pub)
cf2b4488
HP
2347{
2348 dhd_info_t *dhd = (dhd_info_t *) (pub->info);
2349
2350 if (waitqueue_active(&dhd->ioctl_resp_wait))
2351 wake_up_interruptible(&dhd->ioctl_resp_wait);
2352
2353 return 0;
2354}
2355
a84bac46 2356void brcmf_os_wd_timer(void *bus, uint wdtick)
cf2b4488
HP
2357{
2358 dhd_pub_t *pub = bus;
5f782dee 2359 static uint save_dhd_watchdog_ms;
cf2b4488
HP
2360 dhd_info_t *dhd = (dhd_info_t *) pub->info;
2361
2362 /* don't start the wd until fw is loaded */
2363 if (pub->busstate == DHD_BUS_DOWN)
2364 return;
2365
2366 /* Totally stop the timer */
0f0881b0 2367 if (!wdtick && dhd->wd_timer_valid == true) {
cf2b4488 2368 del_timer_sync(&dhd->timer);
0965ae88 2369 dhd->wd_timer_valid = false;
cf2b4488
HP
2370 save_dhd_watchdog_ms = wdtick;
2371 return;
2372 }
2373
2374 if (wdtick) {
5e92aa8c 2375 brcmf_watchdog_ms = (uint) wdtick;
cf2b4488 2376
5e92aa8c 2377 if (save_dhd_watchdog_ms != brcmf_watchdog_ms) {
cf2b4488 2378
0f0881b0 2379 if (dhd->wd_timer_valid == true)
cf2b4488
HP
2380 /* Stop timer and restart at new value */
2381 del_timer_sync(&dhd->timer);
2382
2383 /* Create timer again when watchdog period is
2384 dynamically changed or in the first instance
2385 */
2386 dhd->timer.expires =
5e92aa8c 2387 jiffies + brcmf_watchdog_ms * HZ / 1000;
cf2b4488
HP
2388 add_timer(&dhd->timer);
2389
2390 } else {
2391 /* Re arm the timer, at last watchdog period */
2392 mod_timer(&dhd->timer,
5e92aa8c 2393 jiffies + brcmf_watchdog_ms * HZ / 1000);
cf2b4488
HP
2394 }
2395
0f0881b0 2396 dhd->wd_timer_valid = true;
cf2b4488
HP
2397 save_dhd_watchdog_ms = wdtick;
2398 }
2399}
2400
a84bac46 2401void *brcmf_os_open_image(char *filename)
cf2b4488
HP
2402{
2403 struct file *fp;
2404
f10c5b0b 2405 if (!dhd_no_fw_req)
cf2b4488 2406 return wl_cfg80211_request_fw(filename);
cf2b4488
HP
2407
2408 fp = filp_open(filename, O_RDONLY, 0);
2409 /*
2410 * 2.6.11 (FC4) supports filp_open() but later revs don't?
2411 * Alternative:
2412 * fp = open_namei(AT_FDCWD, filename, O_RD, 0);
2413 * ???
2414 */
2415 if (IS_ERR(fp))
2416 fp = NULL;
2417
2418 return fp;
2419}
2420
a84bac46 2421int brcmf_os_get_image_block(char *buf, int len, void *image)
cf2b4488
HP
2422{
2423 struct file *fp = (struct file *)image;
2424 int rdlen;
2425
f10c5b0b 2426 if (!dhd_no_fw_req)
cf2b4488 2427 return wl_cfg80211_read_fw(buf, len);
cf2b4488
HP
2428
2429 if (!image)
2430 return 0;
2431
2432 rdlen = kernel_read(fp, fp->f_pos, buf, len);
2433 if (rdlen > 0)
2434 fp->f_pos += rdlen;
2435
2436 return rdlen;
2437}
2438
a84bac46 2439void brcmf_os_close_image(void *image)
cf2b4488 2440{
f10c5b0b 2441 if (!dhd_no_fw_req)
cf2b4488 2442 return wl_cfg80211_release_fw();
cf2b4488
HP
2443 if (image)
2444 filp_close((struct file *)image, NULL);
2445}
2446
a84bac46 2447void brcmf_os_sdlock(dhd_pub_t *pub)
cf2b4488
HP
2448{
2449 dhd_info_t *dhd;
2450
2451 dhd = (dhd_info_t *) (pub->info);
2452
2453 if (dhd->threads_only)
2454 down(&dhd->sdsem);
2455 else
2456 spin_lock_bh(&dhd->sdlock);
2457}
2458
a84bac46 2459void brcmf_os_sdunlock(dhd_pub_t *pub)
cf2b4488
HP
2460{
2461 dhd_info_t *dhd;
2462
2463 dhd = (dhd_info_t *) (pub->info);
2464
2465 if (dhd->threads_only)
2466 up(&dhd->sdsem);
2467 else
2468 spin_unlock_bh(&dhd->sdlock);
2469}
2470
cf2b4488
HP
2471static int
2472dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
05dc0f43 2473 brcmf_event_msg_t *event, void **data)
cf2b4488
HP
2474{
2475 int bcmerror = 0;
2476
2477 ASSERT(dhd != NULL);
2478
aba5e0ad 2479 bcmerror = brcmf_c_host_event(dhd, ifidx, pktdata, event, data);
a1c5ad81 2480 if (bcmerror != 0)
cf2b4488
HP
2481 return bcmerror;
2482
f10c5b0b
AS
2483 ASSERT(dhd->iflist[*ifidx] != NULL);
2484 ASSERT(dhd->iflist[*ifidx]->net != NULL);
2485 if (dhd->iflist[*ifidx]->net)
2486 wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data);
cf2b4488
HP
2487
2488 return bcmerror;
2489}
2490
a84bac46 2491void brcmf_wait_for_event(dhd_pub_t *dhd, bool *lockvar)
cf2b4488
HP
2492{
2493 struct dhd_info *dhdinfo = dhd->info;
a84bac46 2494 brcmf_os_sdunlock(dhd);
cf2b4488 2495 wait_event_interruptible_timeout(dhdinfo->ctrl_wait,
0965ae88 2496 (*lockvar == false), HZ * 2);
a84bac46 2497 brcmf_os_sdlock(dhd);
cf2b4488
HP
2498 return;
2499}
2500
a84bac46 2501void brcmf_wait_event_wakeup(dhd_pub_t *dhd)
cf2b4488
HP
2502{
2503 struct dhd_info *dhdinfo = dhd->info;
2504 if (waitqueue_active(&dhdinfo->ctrl_wait))
2505 wake_up_interruptible(&dhdinfo->ctrl_wait);
2506 return;
2507}
2508
3fd79f7c 2509int dhd_dev_reset(struct net_device *dev, u8 flag)
cf2b4488
HP
2510{
2511 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2512
2513 /* Turning off watchdog */
2514 if (flag)
a84bac46 2515 brcmf_os_wd_timer(&dhd->pub, 0);
cf2b4488 2516
a84bac46 2517 brcmf_bus_devreset(&dhd->pub, flag);
cf2b4488
HP
2518
2519 /* Turning on watchdog back */
2520 if (!flag)
a84bac46 2521 brcmf_os_wd_timer(&dhd->pub, brcmf_watchdog_ms);
cf2b4488
HP
2522 DHD_ERROR(("%s: WLAN OFF DONE\n", __func__));
2523
2524 return 1;
2525}
2526
2527int net_os_set_suspend_disable(struct net_device *dev, int val)
2528{
2529 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2530 int ret = 0;
2531
2532 if (dhd) {
2533 ret = dhd->pub.suspend_disable_flag;
2534 dhd->pub.suspend_disable_flag = val;
2535 }
2536 return ret;
2537}
2538
2539int net_os_set_suspend(struct net_device *dev, int val)
2540{
2541 int ret = 0;
2542#if defined(CONFIG_HAS_EARLYSUSPEND)
2543 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2544
2545 if (dhd) {
a84bac46 2546 brcmf_os_proto_block(&dhd->pub);
cf2b4488 2547 ret = dhd_set_suspend(val, &dhd->pub);
a84bac46 2548 brcmf_os_proto_unblock(&dhd->pub);
cf2b4488
HP
2549 }
2550#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
2551 return ret;
2552}
2553
2554int net_os_set_dtim_skip(struct net_device *dev, int val)
2555{
2556 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(dev);
2557
2558 if (dhd)
2559 dhd->pub.dtim_skip = val;
2560
2561 return 0;
2562}
2563
2564int net_os_set_packet_filter(struct net_device *dev, int val)
2565{
2566 dhd_info_t *dhd = *(dhd_info_t **) netdev_priv(dev);
2567 int ret = 0;
2568
2569 /* Packet filtering is set only if we still in early-suspend and
2570 * we need either to turn it ON or turn it OFF
2571 * We can always turn it OFF in case of early-suspend, but we turn it
2572 * back ON only if suspend_disable_flag was not set
2573 */
2574 if (dhd && dhd->pub.up) {
a84bac46 2575 brcmf_os_proto_block(&dhd->pub);
cf2b4488
HP
2576 if (dhd->pub.in_suspend) {
2577 if (!val || (val && !dhd->pub.suspend_disable_flag))
2578 dhd_set_packet_filter(val, &dhd->pub);
2579 }
a84bac46 2580 brcmf_os_proto_unblock(&dhd->pub);
cf2b4488
HP
2581 }
2582 return ret;
2583}
2584
2585void dhd_dev_init_ioctl(struct net_device *dev)
2586{
2587 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2588
aba5e0ad 2589 brcmf_c_preinit_ioctls(&dhd->pub);
cf2b4488
HP
2590}
2591
2592#ifdef PNO_SUPPORT
2593/* Linux wrapper to call common dhd_pno_clean */
2594int dhd_dev_pno_reset(struct net_device *dev)
2595{
2596 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2597
2598 return dhd_pno_clean(&dhd->pub);
2599}
2600
2601/* Linux wrapper to call common dhd_pno_enable */
2602int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled)
2603{
2604 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2605
2606 return dhd_pno_enable(&dhd->pub, pfn_enabled);
2607}
2608
2609/* Linux wrapper to call common dhd_pno_set */
2610int
2611dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t *ssids_local, int nssid,
580a0bd9 2612 unsigned char scan_fr)
cf2b4488
HP
2613{
2614 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2615
2616 return dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr);
2617}
2618
2619/* Linux wrapper to get pno status */
2620int dhd_dev_get_pno_status(struct net_device *dev)
2621{
2622 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2623
2624 return dhd_pno_get_status(&dhd->pub);
2625}
2626
2627#endif /* PNO_SUPPORT */
2628
2629static int dhd_get_pend_8021x_cnt(dhd_info_t *dhd)
2630{
2631 return atomic_read(&dhd->pend_8021x_cnt);
2632}
2633
2634#define MAX_WAIT_FOR_8021X_TX 10
2635
2636int dhd_wait_pend8021x(struct net_device *dev)
2637{
2638 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2639 int timeout = 10 * HZ / 1000;
2640 int ntimes = MAX_WAIT_FOR_8021X_TX;
2641 int pend = dhd_get_pend_8021x_cnt(dhd);
2642
2643 while (ntimes && pend) {
2644 if (pend) {
2645 set_current_state(TASK_INTERRUPTIBLE);
2646 schedule_timeout(timeout);
2647 set_current_state(TASK_RUNNING);
2648 ntimes--;
2649 }
2650 pend = dhd_get_pend_8021x_cnt(dhd);
2651 }
2652 return pend;
2653}
2654
e6e8f894
SS
2655void wl_os_wd_timer(struct net_device *ndev, uint wdtick)
2656{
2657 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(ndev);
2658
a84bac46 2659 brcmf_os_wd_timer(&dhd->pub, wdtick);
e6e8f894
SS
2660}
2661
cf2b4488 2662#ifdef DHD_DEBUG
a84bac46 2663int brcmf_write_to_file(dhd_pub_t *dhd, u8 *buf, int size)
cf2b4488
HP
2664{
2665 int ret = 0;
2666 struct file *fp;
2667 mm_segment_t old_fs;
2668 loff_t pos = 0;
2669
2670 /* change to KERNEL_DS address limit */
2671 old_fs = get_fs();
2672 set_fs(KERNEL_DS);
2673
2674 /* open file to write */
2675 fp = filp_open("/tmp/mem_dump", O_WRONLY | O_CREAT, 0640);
2676 if (!fp) {
0bef7748 2677 DHD_ERROR(("%s: open file error\n", __func__));
cf2b4488
HP
2678 ret = -1;
2679 goto exit;
2680 }
2681
2682 /* Write buf to file */
2683 fp->f_op->write(fp, buf, size, &pos);
2684
2685exit:
2686 /* free buf before return */
182acb3c 2687 kfree(buf);
cf2b4488
HP
2688 /* close file before return */
2689 if (fp)
2690 filp_close(fp, current->files);
2691 /* restore previous address limit */
2692 set_fs(old_fs);
2693
2694 return ret;
2695}
2696#endif /* DHD_DEBUG */
47a6d2cd
AS
2697
2698#if defined(BCMDBG)
2699void osl_assert(char *exp, char *file, int line)
2700{
2701 char tempbuf[256];
2702 char *basename;
2703
2704 basename = strrchr(file, '/');
2705 /* skip the '/' */
2706 if (basename)
2707 basename++;
2708
2709 if (!basename)
2710 basename = file;
2711
2712 snprintf(tempbuf, 256,
2713 "assertion \"%s\" failed: file \"%s\", line %d\n", exp,
2714 basename, line);
2715
2716 /*
2717 * Print assert message and give it time to
2718 * be written to /var/log/messages
2719 */
2720 if (!in_interrupt()) {
2721 const int delay = 3;
2722 printk(KERN_ERR "%s", tempbuf);
2723 printk(KERN_ERR "panic in %d seconds\n", delay);
2724 set_current_state(TASK_INTERRUPTIBLE);
2725 schedule_timeout(delay * HZ);
2726 }
2727
2728 switch (g_assert_type) {
2729 case 0:
2730 panic(KERN_ERR "%s", tempbuf);
2731 break;
2732 case 1:
2733 printk(KERN_ERR "%s", tempbuf);
2734 BUG();
2735 break;
2736 case 2:
2737 printk(KERN_ERR "%s", tempbuf);
2738 break;
2739 default:
2740 break;
2741 }
2742}
2743#endif /* defined(BCMDBG) */
This page took 0.300613 seconds and 5 git commands to generate.