Add channal get and android wifi dormancy mechanism to bcm4329.(V2.00)
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / bcm4329 / dhd_linux.c
1 /*
2  * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
3  * Basically selected code segments from usb-cdc.c and usb-rndis.c
4  *
5  * Copyright (C) 1999-2010, Broadcom Corporation
6  * 
7  *      Unless you and Broadcom execute a separate written software license
8  * agreement governing use of this software, this software is licensed to you
9  * under the terms of the GNU General Public License version 2 (the "GPL"),
10  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
11  * following added to such license:
12  * 
13  *      As a special exception, the copyright holders of this software give you
14  * permission to link this software with independent modules, and to copy and
15  * distribute the resulting executable under terms of your choice, provided that
16  * you also meet, for each linked independent module, the terms and conditions of
17  * the license of that module.  An independent module is a module which is not
18  * derived from this software.  The special exception does not apply to any
19  * modifications of the software.
20  * 
21  *      Notwithstanding the above, under no circumstances may you combine this
22  * software in any way with any other Broadcom software provided under a license
23  * other than the GPL, without Broadcom's express prior written consent.
24  *
25  * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104.4.35 2010/11/17 03:13:21 Exp $
26  */
27
28 #ifdef CONFIG_WIFI_CONTROL_FUNC
29 #include <linux/platform_device.h>
30 #endif
31 #include <typedefs.h>
32 #include <linuxver.h>
33 #include <osl.h>
34
35 #include <linux/init.h>
36 #include <linux/kernel.h>
37 #include <linux/slab.h>
38 #include <linux/skbuff.h>
39 #include <linux/netdevice.h>
40 #include <linux/etherdevice.h>
41 #include <linux/random.h>
42 #include <linux/spinlock.h>
43 #include <linux/ethtool.h>
44 #include <linux/fcntl.h>
45 #include <linux/fs.h>
46
47 #include <asm/uaccess.h>
48 #include <asm/unaligned.h>
49
50 #include <wifi_version.h>
51 #include <epivers.h>
52 #include <bcmutils.h>
53 #include <bcmendian.h>
54
55 #include <proto/ethernet.h>
56 #include <dngl_stats.h>
57 #include <dhd.h>
58 #include <dhd_bus.h>
59 #include <dhd_proto.h>
60 #include <dhd_dbg.h>
61 #include <wl_iw.h>
62 #ifdef CONFIG_HAS_WAKELOCK
63 #include <linux/wakelock.h>
64 #endif
65 #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
66 //#include <linux/wlan_plat.h>
67 #include <mach/board.h>
68
69 struct semaphore wifi_control_sem;
70
71 struct dhd_bus *g_bus;
72
73 extern void bcm4329_power_save_exit(void);
74 extern void bcm4329_power_save_init(void);
75
76 static struct wifi_platform_data *wifi_control_data = NULL;
77 static struct resource *wifi_irqres = NULL;
78
79 int wifi_get_irq_number(unsigned long *irq_flags_ptr)
80 {
81         if (wifi_irqres) {
82                 *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK;
83                 return (int)wifi_irqres->start;
84         }
85 #ifdef CUSTOM_OOB_GPIO_NUM
86         return CUSTOM_OOB_GPIO_NUM;
87 #else
88         return -1;
89 #endif
90 }
91
92 int wifi_set_carddetect(int on)
93 {
94         printk("%s = %d\n", __FUNCTION__, on);
95         if (wifi_control_data && wifi_control_data->set_carddetect) {
96                 wifi_control_data->set_carddetect(on);
97         }
98         return 0;
99 }
100
101 int wifi_set_power(int on, unsigned long msec)
102 {
103         printk("%s = %d\n", __FUNCTION__, on);
104         if (wifi_control_data && wifi_control_data->set_power) {
105                 wifi_control_data->set_power(on);
106         }
107         if (msec)
108                 mdelay(msec);
109         return 0;
110 }
111
112 int wifi_set_reset(int on, unsigned long msec)
113 {
114         DHD_TRACE(("%s = %d\n", __FUNCTION__, on));
115         if (wifi_control_data && wifi_control_data->set_reset) {
116                 wifi_control_data->set_reset(on);
117         }
118         if (msec)
119                 mdelay(msec);
120         return 0;
121 }
122
123 int wifi_get_mac_addr(unsigned char *buf)
124 {
125         DHD_TRACE(("%s\n", __FUNCTION__));
126         if (!buf)
127                 return -EINVAL;
128         if (wifi_control_data && wifi_control_data->get_mac_addr) {
129                 return wifi_control_data->get_mac_addr(buf);
130         }
131         return -EOPNOTSUPP;
132 }
133
134 static int wifi_probe(struct platform_device *pdev)
135 {
136         struct wifi_platform_data *wifi_ctrl =
137                 (struct wifi_platform_data *)(pdev->dev.platform_data);
138
139         DHD_TRACE(("## %s\n", __FUNCTION__));
140         wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcm4329_wlan_irq");
141         wifi_control_data = wifi_ctrl;
142
143         wifi_set_power(1, 0);   /* Power On */
144         wifi_set_carddetect(1); /* CardDetect (0->1) */
145
146         up(&wifi_control_sem);
147         return 0;
148 }
149
150 static int wifi_remove(struct platform_device *pdev)
151 {
152         struct wifi_platform_data *wifi_ctrl =
153                 (struct wifi_platform_data *)(pdev->dev.platform_data);
154
155         DHD_TRACE(("## %s\n", __FUNCTION__));
156         wifi_control_data = wifi_ctrl;
157
158         wifi_set_power(0, 0);   /* Power Off */
159         wifi_set_carddetect(0); /* CardDetect (1->0) */
160
161         up(&wifi_control_sem);
162         return 0;
163 }
164 static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
165 {
166         DHD_TRACE(("##> %s\n", __FUNCTION__));
167         return 0;
168 }
169 static int wifi_resume(struct platform_device *pdev)
170 {
171         DHD_TRACE(("##> %s\n", __FUNCTION__));
172         return 0;
173 }
174
175 static struct platform_driver wifi_device = {
176         .probe          = wifi_probe,
177         .remove         = wifi_remove,
178         .suspend        = wifi_suspend,
179         .resume         = wifi_resume,
180         .driver         = {
181         .name   = "bcm4329_wlan",
182         }
183 };
184
185 int wifi_add_dev(void)
186 {
187         DHD_TRACE(("## Calling platform_driver_register\n"));
188         return platform_driver_register(&wifi_device);
189 }
190
191 void wifi_del_dev(void)
192 {
193         DHD_TRACE(("## Unregister platform_driver_register\n"));
194         platform_driver_unregister(&wifi_device);
195 }
196 #endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
197
198
199 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
200 #include <linux/suspend.h>
201 volatile bool dhd_mmc_suspend = FALSE;
202 DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
203 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
204
205 #if defined(OOB_INTR_ONLY)
206 extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable);
207 #endif /* defined(OOB_INTR_ONLY) */
208 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
209 MODULE_LICENSE("GPL v2");
210 #endif /* LinuxVer */
211
212 #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
213 const char *
214 print_tainted()
215 {
216         return "";
217 }
218 #endif  /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */
219
220 /* Linux wireless extension support */
221 #if defined(CONFIG_WIRELESS_EXT)
222 #include <wl_iw.h>
223 #endif /* defined(CONFIG_WIRELESS_EXT) */
224
225 extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
226
227 #if defined(CONFIG_HAS_EARLYSUSPEND)
228 #include <linux/earlysuspend.h>
229 #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
230
231 #ifdef PKT_FILTER_SUPPORT
232 extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
233 extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
234 #endif
235
236 /* Interface control information */
237 typedef struct dhd_if {
238         struct dhd_info *info;                  /* back pointer to dhd_info */
239         /* OS/stack specifics */
240         struct net_device *net;
241         struct net_device_stats stats;
242         int                     idx;                    /* iface idx in dongle */
243         int                     state;                  /* interface state */
244         uint                    subunit;                /* subunit */
245         uint8                   mac_addr[ETHER_ADDR_LEN];       /* assigned MAC address */
246         bool                    attached;               /* Delayed attachment when unset */
247         bool                    txflowcontrol;  /* Per interface flow control indicator */
248         char                    name[IFNAMSIZ+1]; /* linux interface name */
249 } dhd_if_t;
250
251 /* Local private structure (extension of pub) */
252 typedef struct dhd_info {
253 #if defined(CONFIG_WIRELESS_EXT)
254         wl_iw_t         iw;             /* wireless extensions state (must be first) */
255 #endif /* defined(CONFIG_WIRELESS_EXT) */
256
257         dhd_pub_t pub;
258
259         /* OS/stack specifics */
260         dhd_if_t *iflist[DHD_MAX_IFS];
261
262         struct semaphore proto_sem;
263         wait_queue_head_t ioctl_resp_wait;
264         struct timer_list timer;
265         bool wd_timer_valid;
266         struct tasklet_struct tasklet;
267         spinlock_t      sdlock;
268         spinlock_t      txqlock;
269         spinlock_t      dhd_lock;
270
271         /* Thread based operation */
272         bool threads_only;
273         struct semaphore sdsem;
274         long watchdog_pid;
275         struct semaphore watchdog_sem;
276         struct completion watchdog_exited;
277         long dpc_pid;
278         struct semaphore dpc_sem;
279         struct completion dpc_exited;
280
281         /* Wakelocks */
282 #ifdef CONFIG_HAS_WAKELOCK
283         struct wake_lock wl_wifi;   /* Wifi wakelock */
284         struct wake_lock wl_rxwake; /* Wifi rx wakelock */
285 #endif
286         spinlock_t wl_lock;
287         int wl_count;
288         int wl_packet;
289
290         int hang_was_sent; /* flag that message was send at least once */
291 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
292         struct mutex wl_start_lock; /* mutex when START called to prevent any other Linux calls */
293 #endif
294         /* Thread to issue ioctl for multicast */
295         long sysioc_pid;
296         struct semaphore sysioc_sem;
297         struct completion sysioc_exited;
298         bool set_multicast;
299         bool set_macaddress;
300         struct ether_addr macvalue;
301         wait_queue_head_t ctrl_wait;
302         atomic_t pend_8021x_cnt;
303
304 #ifdef CONFIG_HAS_EARLYSUSPEND
305         struct early_suspend early_suspend;
306 #endif /* CONFIG_HAS_EARLYSUSPEND */
307 } dhd_info_t;
308
309 /* Definitions to provide path to the firmware and nvram
310  * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt"
311  */
312 char firmware_path[MOD_PARAM_PATHLEN];
313 char nvram_path[MOD_PARAM_PATHLEN];
314
315 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
316 struct semaphore dhd_registration_sem;
317 #define DHD_REGISTRATION_TIMEOUT  12000  /* msec : allowed time to finished dhd registration */
318 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
319 /* load firmware and/or nvram values from the filesystem */
320 module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0);
321 module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
322
323 /* Error bits */
324 module_param(dhd_msg_level, int, 0);
325
326 /* Spawn a thread for system ioctls (set mac, set mcast) */
327 uint dhd_sysioc = TRUE;
328 module_param(dhd_sysioc, uint, 0);
329
330 /* Watchdog interval */
331 uint dhd_watchdog_ms = 10;
332 module_param(dhd_watchdog_ms, uint, 0);
333
334 #ifdef DHD_DEBUG
335 /* Console poll interval */
336 uint dhd_console_ms = 0;
337 module_param(dhd_console_ms, uint, 0);
338 #endif /* DHD_DEBUG */
339
340 /* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */
341 uint dhd_arp_mode = 0xb;
342 module_param(dhd_arp_mode, uint, 0);
343
344 /* ARP offload enable */
345 uint dhd_arp_enable = TRUE;
346 module_param(dhd_arp_enable, uint, 0);
347
348 /* Global Pkt filter enable control */
349 uint dhd_pkt_filter_enable = TRUE;
350 module_param(dhd_pkt_filter_enable, uint, 0);
351
352 /*  Pkt filter init setup */
353 uint dhd_pkt_filter_init = 0;
354 module_param(dhd_pkt_filter_init, uint, 0);
355
356 /* Pkt filter mode control */
357 uint dhd_master_mode = TRUE;
358 module_param(dhd_master_mode, uint, 1);
359
360 /* Watchdog thread priority, -1 to use kernel timer */
361 int dhd_watchdog_prio = 97;
362 module_param(dhd_watchdog_prio, int, 0);
363
364 /* DPC thread priority, -1 to use tasklet */
365 int dhd_dpc_prio = 98;
366 module_param(dhd_dpc_prio, int, 0);
367
368 /* DPC thread priority, -1 to use tasklet */
369 extern int dhd_dongle_memsize;
370 module_param(dhd_dongle_memsize, int, 0);
371
372 /* Control fw roaming */
373 #ifdef CUSTOMER_HW2
374 uint dhd_roam = 0;
375 #else
376 uint dhd_roam = 1;
377 #endif
378
379 /* Control radio state */
380 uint dhd_radio_up = 1;
381
382 /* Network inteface name */
383 char iface_name[IFNAMSIZ] = "wlan0";
384 module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
385
386 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
387 #define DAEMONIZE(a) daemonize(a); \
388         allow_signal(SIGKILL); \
389         allow_signal(SIGTERM);
390 #else /* Linux 2.4 (w/o preemption patch) */
391 #define RAISE_RX_SOFTIRQ() \
392         cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
393 #define DAEMONIZE(a) daemonize(); \
394         do { if (a) \
395                 strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
396         } while (0);
397 #endif /* LINUX_VERSION_CODE  */
398
399 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
400 #define BLOCKABLE()     (!in_atomic())
401 #else
402 #define BLOCKABLE()     (!in_interrupt())
403 #endif
404
405 /* The following are specific to the SDIO dongle */
406
407 /* IOCTL response timeout */
408 int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
409
410 /* Idle timeout for backplane clock */
411 int dhd_idletime = DHD_IDLETIME_TICKS;
412 module_param(dhd_idletime, int, 0);
413
414 /* Use polling */
415 uint dhd_poll = FALSE;
416 module_param(dhd_poll, uint, 0);
417
418 /* Use interrupts */
419 uint dhd_intr = TRUE;
420 module_param(dhd_intr, uint, 0);
421
422 /* SDIO Drive Strength (in milliamps) */
423 uint dhd_sdiod_drive_strength = 6;
424 module_param(dhd_sdiod_drive_strength, uint, 0);
425
426 /* Tx/Rx bounds */
427 extern uint dhd_txbound;
428 extern uint dhd_rxbound;
429 module_param(dhd_txbound, uint, 0);
430 module_param(dhd_rxbound, uint, 0);
431
432 /* Deferred transmits */
433 extern uint dhd_deferred_tx;
434 module_param(dhd_deferred_tx, uint, 0);
435
436
437
438 #ifdef SDTEST
439 /* Echo packet generator (pkts/s) */
440 uint dhd_pktgen = 0;
441 module_param(dhd_pktgen, uint, 0);
442
443 /* Echo packet len (0 => sawtooth, max 2040) */
444 uint dhd_pktgen_len = 0;
445 module_param(dhd_pktgen_len, uint, 0);
446 #endif
447
448 /* Version string to report */
449 #ifdef DHD_DEBUG
450 #ifndef SRCBASE
451 #define SRCBASE        "drivers/net/wireless/bcm4329"
452 #endif
453 #define DHD_COMPILED "\nCompiled in " SRCBASE
454 #else
455 #define DHD_COMPILED
456 #endif
457
458 static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR
459 #ifdef DHD_DEBUG
460 "\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__
461 #endif
462 ;
463
464
465 #if defined(CONFIG_WIRELESS_EXT)
466 struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
467 #endif /* defined(CONFIG_WIRELESS_EXT) */
468
469 static void dhd_dpc(ulong data);
470 /* forward decl */
471 extern int dhd_wait_pend8021x(struct net_device *dev);
472
473 #ifdef TOE
474 #ifndef BDC
475 #error TOE requires BDC
476 #endif /* !BDC */
477 static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol);
478 static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol);
479 #endif /* TOE */
480
481 static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
482                              wl_event_msg_t *event_ptr, void **data_ptr);
483
484 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
485 static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored)
486 {
487         int ret = NOTIFY_DONE;
488
489         switch (action) {
490         case PM_HIBERNATION_PREPARE:
491         case PM_SUSPEND_PREPARE:
492                 dhd_mmc_suspend = TRUE;
493                 ret = NOTIFY_OK;
494                 break;
495         case PM_POST_HIBERNATION:
496         case PM_POST_SUSPEND:
497                 dhd_mmc_suspend = FALSE;
498                 ret = NOTIFY_OK;
499                 break;
500         }
501         smp_mb();
502         return ret;
503 }
504
505 static struct notifier_block dhd_sleep_pm_notifier = {
506         .notifier_call = dhd_sleep_pm_callback,
507         .priority = 0
508 };
509 extern int register_pm_notifier(struct notifier_block *nb);
510 extern int unregister_pm_notifier(struct notifier_block *nb);
511 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
512
513 static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
514 {
515 #ifdef PKT_FILTER_SUPPORT
516         DHD_TRACE(("%s: %d\n", __FUNCTION__, value));
517         /* 1 - Enable packet filter, only allow unicast packet to send up */
518         /* 0 - Disable packet filter */
519         if (dhd_pkt_filter_enable) {
520                 int i;
521
522                 for (i = 0; i < dhd->pktfilter_count; i++) {
523                         dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
524                         dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
525                                         value, dhd_master_mode);
526                 }
527         }
528 #endif
529 }
530
531
532
533 #if defined(CONFIG_HAS_EARLYSUSPEND)
534 static int dhd_set_suspend(int value, dhd_pub_t *dhd)
535 {
536         int power_mode = PM_MAX;
537         /* wl_pkt_filter_enable_t       enable_parm; */
538         char iovbuf[32];
539         int bcn_li_dtim = 3;
540 #ifdef CUSTOMER_HW2
541         uint roamvar = 1;
542 #endif /* CUSTOMER_HW2 */
543
544         DHD_TRACE(("%s: enter, value = %d in_suspend = %d\n",
545                         __FUNCTION__, value, dhd->in_suspend));
546
547         if (dhd && dhd->up) {
548                 if (value && dhd->in_suspend) {
549
550                         /* Kernel suspended */
551                         DHD_TRACE(("%s: force extra Suspend setting \n", __FUNCTION__));
552
553                         dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM,
554                                 (char *)&power_mode, sizeof(power_mode));
555
556                         /* Enable packet filter, only allow unicast packet to send up */
557                         dhd_set_packet_filter(1, dhd);
558
559                         /* if dtim skip setup as default force it to wake each thrid dtim
560                          *  for better power saving.
561                          *  Note that side effect is chance to miss BC/MC packet
562                         */
563                         bcn_li_dtim = dhd_get_dtim_skip(dhd);
564                         bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
565                                 4, iovbuf, sizeof(iovbuf));
566                         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
567 #ifdef CUSTOMER_HW2
568                         /* Disable build-in roaming during suspend */
569                         bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf));
570                         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
571 #endif /* CUSTOMER_HW2 */
572
573                 } else {
574
575                         /* Kernel resumed  */
576                         DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__));
577
578                         power_mode = PM_FAST;
579                         dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode,
580                                 sizeof(power_mode));
581
582                         /* disable pkt filter */
583                         dhd_set_packet_filter(0, dhd);
584
585                         /* restore pre-suspend setting for dtim_skip */
586                         bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
587                                 4, iovbuf, sizeof(iovbuf));
588
589                         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
590 #ifdef CUSTOMER_HW2
591                         roamvar = dhd_roam;
592                         bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf));
593                         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
594 #endif /* CUSTOMER_HW2 */
595                 }
596         }
597
598         return 0;
599 }
600
601 static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
602 {
603         dhd_pub_t *dhdp = &dhd->pub;
604
605         dhd_os_wake_lock(dhdp);
606         dhd_os_proto_block(dhdp);
607         /* Set flag when early suspend was called */
608         dhdp->in_suspend = val;
609         if (!dhdp->suspend_disable_flag)
610                 dhd_set_suspend(val, dhdp);
611         dhd_os_proto_unblock(dhdp);
612         dhd_os_wake_unlock(dhdp);
613 }
614
615 static void dhd_early_suspend(struct early_suspend *h)
616 {
617         struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
618
619         DHD_TRACE(("%s: enter\n", __FUNCTION__));
620
621         if (dhd)
622                 dhd_suspend_resume_helper(dhd, 1);
623 }
624
625 static void dhd_late_resume(struct early_suspend *h)
626 {
627         struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
628
629         DHD_TRACE(("%s: enter\n", __FUNCTION__));
630
631         if (dhd)
632                 dhd_suspend_resume_helper(dhd, 0);
633 }
634 #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
635
636 /*
637  * Generalized timeout mechanism.  Uses spin sleep with exponential back-off until
638  * the sleep time reaches one jiffy, then switches over to task delay.  Usage:
639  *
640  *      dhd_timeout_start(&tmo, usec);
641  *      while (!dhd_timeout_expired(&tmo))
642  *              if (poll_something())
643  *                      break;
644  *      if (dhd_timeout_expired(&tmo))
645  *              fatal();
646  */
647
648 void
649 dhd_timeout_start(dhd_timeout_t *tmo, uint usec)
650 {
651         tmo->limit = usec;
652         tmo->increment = 0;
653         tmo->elapsed = 0;
654         tmo->tick = 1000000 / HZ;
655 }
656
657 int
658 dhd_timeout_expired(dhd_timeout_t *tmo)
659 {
660         /* Does nothing the first call */
661         if (tmo->increment == 0) {
662                 tmo->increment = 1;
663                 return 0;
664         }
665
666         if (tmo->elapsed >= tmo->limit)
667                 return 1;
668
669         /* Add the delay that's about to take place */
670         tmo->elapsed += tmo->increment;
671
672         if (tmo->increment < tmo->tick) {
673                 OSL_DELAY(tmo->increment);
674                 tmo->increment *= 2;
675                 if (tmo->increment > tmo->tick)
676                         tmo->increment = tmo->tick;
677         } else {
678                 wait_queue_head_t delay_wait;
679                 DECLARE_WAITQUEUE(wait, current);
680                 int pending;
681                 init_waitqueue_head(&delay_wait);
682                 add_wait_queue(&delay_wait, &wait);
683                 set_current_state(TASK_INTERRUPTIBLE);
684                 schedule_timeout(1);
685                 pending = signal_pending(current);
686                 remove_wait_queue(&delay_wait, &wait);
687                 set_current_state(TASK_RUNNING);
688                 if (pending)
689                         return 1;       /* Interrupted */
690         }
691
692         return 0;
693 }
694
695 static int
696 dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
697 {
698         int i = 0;
699
700         ASSERT(dhd);
701         while (i < DHD_MAX_IFS) {
702                 if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
703                         return i;
704                 i++;
705         }
706
707         return DHD_BAD_IF;
708 }
709
710 int
711 dhd_ifname2idx(dhd_info_t *dhd, char *name)
712 {
713         int i = DHD_MAX_IFS;
714
715         ASSERT(dhd);
716
717         if (name == NULL || *name == '\0')
718                 return 0;
719
720         while (--i > 0)
721                 if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
722                                 break;
723
724         DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name));
725
726         return i;       /* default - the primary interface */
727 }
728
729 char *
730 dhd_ifname(dhd_pub_t *dhdp, int ifidx)
731 {
732         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
733
734         ASSERT(dhd);
735
736         if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
737                 DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx));
738                 return "<if_bad>";
739         }
740
741         if (dhd->iflist[ifidx] == NULL) {
742                 DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx));
743                 return "<if_null>";
744         }
745
746         if (dhd->iflist[ifidx]->net)
747                 return dhd->iflist[ifidx]->net->name;
748
749         return "<if_none>";
750 }
751
752 static void
753 _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
754 {
755         struct net_device *dev;
756 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
757         struct netdev_hw_addr *ha;
758 #else
759         struct dev_mc_list *mclist;
760 #endif
761         uint32 allmulti, cnt;
762
763         wl_ioctl_t ioc;
764         char *buf, *bufp;
765         uint buflen;
766         int ret;
767
768         ASSERT(dhd && dhd->iflist[ifidx]);
769         dev = dhd->iflist[ifidx]->net;
770
771         netif_addr_lock_bh(dev);
772 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
773         cnt = netdev_mc_count(dev);
774 #else
775         cnt = dev->mc_count;
776 #endif
777         netif_addr_unlock_bh(dev);
778
779         /* Determine initial value of allmulti flag */
780         allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE;
781
782         /* Send down the multicast list first. */
783         buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN);
784         if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) {
785                 DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
786                            dhd_ifname(&dhd->pub, ifidx), cnt));
787                 return;
788         }
789
790         strcpy(bufp, "mcast_list");
791         bufp += strlen("mcast_list") + 1;
792
793         cnt = htol32(cnt);
794         memcpy(bufp, &cnt, sizeof(cnt));
795         bufp += sizeof(cnt);
796
797         netif_addr_lock_bh(dev);
798 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
799         netdev_for_each_mc_addr(ha, dev) {
800                 if (!cnt)
801                         break;
802                 memcpy(bufp, ha->addr, ETHER_ADDR_LEN);
803                 bufp += ETHER_ADDR_LEN;
804                 cnt--;
805         }
806 #else
807         for (mclist = dev->mc_list;(mclist && (cnt > 0)); cnt--, mclist = mclist->next) {
808                 memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN);
809                 bufp += ETHER_ADDR_LEN;
810         }
811 #endif
812         netif_addr_unlock_bh(dev);
813
814         memset(&ioc, 0, sizeof(ioc));
815         ioc.cmd = WLC_SET_VAR;
816         ioc.buf = buf;
817         ioc.len = buflen;
818         ioc.set = TRUE;
819
820         ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
821         if (ret < 0) {
822                 DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
823                         dhd_ifname(&dhd->pub, ifidx), cnt));
824                 allmulti = cnt ? TRUE : allmulti;
825         }
826
827         MFREE(dhd->pub.osh, buf, buflen);
828
829         /* Now send the allmulti setting.  This is based on the setting in the
830          * net_device flags, but might be modified above to be turned on if we
831          * were trying to set some addresses and dongle rejected it...
832          */
833
834         buflen = sizeof("allmulti") + sizeof(allmulti);
835         if (!(buf = MALLOC(dhd->pub.osh, buflen))) {
836                 DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx)));
837                 return;
838         }
839         allmulti = htol32(allmulti);
840
841         if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) {
842                 DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
843                            dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen));
844                 MFREE(dhd->pub.osh, buf, buflen);
845                 return;
846         }
847
848
849         memset(&ioc, 0, sizeof(ioc));
850         ioc.cmd = WLC_SET_VAR;
851         ioc.buf = buf;
852         ioc.len = buflen;
853         ioc.set = TRUE;
854
855         ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
856         if (ret < 0) {
857                 DHD_ERROR(("%s: set allmulti %d failed\n",
858                            dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
859         }
860
861         MFREE(dhd->pub.osh, buf, buflen);
862
863         /* Finally, pick up the PROMISC flag as well, like the NIC driver does */
864
865         allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE;
866         allmulti = htol32(allmulti);
867
868         memset(&ioc, 0, sizeof(ioc));
869         ioc.cmd = WLC_SET_PROMISC;
870         ioc.buf = &allmulti;
871         ioc.len = sizeof(allmulti);
872         ioc.set = TRUE;
873
874         ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
875         if (ret < 0) {
876                 DHD_ERROR(("%s: set promisc %d failed\n",
877                            dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
878         }
879 }
880
881 static int
882 _dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr)
883 {
884         char buf[32];
885         wl_ioctl_t ioc;
886         int ret;
887
888         DHD_TRACE(("%s enter\n", __FUNCTION__));
889         if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) {
890                 DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx)));
891                 return -1;
892         }
893         memset(&ioc, 0, sizeof(ioc));
894         ioc.cmd = WLC_SET_VAR;
895         ioc.buf = buf;
896         ioc.len = 32;
897         ioc.set = TRUE;
898
899         ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
900         if (ret < 0) {
901                 DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx)));
902         } else {
903                 memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN);
904         }
905
906         return ret;
907 }
908
909 #ifdef SOFTAP
910 extern struct net_device *ap_net_dev;
911 /* semaphore that the soft AP CODE waits on */
912 extern struct semaphore ap_eth_sema;
913 #endif
914
915 static void
916 dhd_op_if(dhd_if_t *ifp)
917 {
918         dhd_info_t *dhd;
919         int ret = 0, err = 0;
920 #ifdef SOFTAP
921         unsigned long flags;
922 #endif
923
924         ASSERT(ifp && ifp->info && ifp->idx);   /* Virtual interfaces only */
925
926         dhd = ifp->info;
927
928         DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state));
929
930         switch (ifp->state) {
931         case WLC_E_IF_ADD:
932                 /*
933                  * Delete the existing interface before overwriting it
934                  * in case we missed the WLC_E_IF_DEL event.
935                  */
936                 if (ifp->net != NULL) {
937                         DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n",
938                          __FUNCTION__, ifp->net->name));
939                         netif_stop_queue(ifp->net);
940                         unregister_netdev(ifp->net);
941                         free_netdev(ifp->net);
942                 }
943                 /* Allocate etherdev, including space for private structure */
944                 if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) {
945                         DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
946                         ret = -ENOMEM;
947                 }
948                 if (ret == 0) {
949                         strcpy(ifp->net->name, ifp->name);
950                         memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
951                         if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) {
952                                 DHD_ERROR(("%s: dhd_net_attach failed, err %d\n",
953                                         __FUNCTION__, err));
954                                 ret = -EOPNOTSUPP;
955                         } else {
956 #ifdef SOFTAP
957                                 flags = dhd_os_spin_lock(&dhd->pub);
958                                 /* save ptr to wl0.1 netdev for use in wl_iw.c  */
959                                 ap_net_dev = ifp->net;
960                                  /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */
961                                 up(&ap_eth_sema);
962                                 dhd_os_spin_unlock(&dhd->pub, flags);
963 #endif
964                                 DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n",
965                                         current->pid, ifp->net->name));
966                                 ifp->state = 0;
967                         }
968                 }
969                 break;
970         case WLC_E_IF_DEL:
971                 if (ifp->net != NULL) {
972                         DHD_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n", __FUNCTION__));
973                         netif_stop_queue(ifp->net);
974                         unregister_netdev(ifp->net);
975                         ret = DHD_DEL_IF;       /* Make sure the free_netdev() is called */
976                 }
977                 break;
978         default:
979                 DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state));
980                 ASSERT(!ifp->state);
981                 break;
982         }
983
984         if (ret < 0) {
985                 if (ifp->net) {
986                         free_netdev(ifp->net);
987                 }
988                 dhd->iflist[ifp->idx] = NULL;
989                 MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
990 #ifdef SOFTAP
991                 flags = dhd_os_spin_lock(&dhd->pub);
992                 if (ifp->net == ap_net_dev)
993                         ap_net_dev = NULL;     /* NULL SOFTAP global as well */
994                 dhd_os_spin_unlock(&dhd->pub, flags);
995 #endif /*  SOFTAP */
996         }
997 }
998
999 static int
1000 _dhd_sysioc_thread(void *data)
1001 {
1002         dhd_info_t *dhd = (dhd_info_t *)data;
1003         int i;
1004 #ifdef SOFTAP
1005         bool in_ap = FALSE;
1006         unsigned long flags;
1007 #endif
1008
1009         DAEMONIZE("dhd_sysioc");
1010
1011         while (down_interruptible(&dhd->sysioc_sem) == 0) {
1012                 dhd_os_start_lock(&dhd->pub);
1013                 dhd_os_wake_lock(&dhd->pub);
1014                 for (i = 0; i < DHD_MAX_IFS; i++) {
1015                         if (dhd->iflist[i]) {
1016                                 DHD_TRACE(("%s: interface %d\n",__FUNCTION__, i));
1017 #ifdef SOFTAP
1018                                 flags = dhd_os_spin_lock(&dhd->pub);
1019                                 in_ap = (ap_net_dev != NULL);
1020                                 dhd_os_spin_unlock(&dhd->pub, flags);
1021 #endif /* SOFTAP */
1022                                 if (dhd->iflist[i]->state)
1023                                         dhd_op_if(dhd->iflist[i]);
1024 #ifdef SOFTAP
1025                                 if (dhd->iflist[i] == NULL) {
1026                                         DHD_TRACE(("%s: interface %d just been removed!\n\n", __FUNCTION__, i));
1027                                         continue;
1028                                 }
1029
1030                                 if (in_ap && dhd->set_macaddress) {
1031                                         DHD_TRACE(("attempt to set MAC for %s in AP Mode blocked.\n", dhd->iflist[i]->net->name));
1032                                         dhd->set_macaddress = FALSE;
1033                                         continue;
1034                                 }
1035
1036                                 if (in_ap && dhd->set_multicast)  {
1037                                         DHD_TRACE(("attempt to set MULTICAST list for %s in AP Mode blocked.\n", dhd->iflist[i]->net->name));
1038                                         dhd->set_multicast = FALSE;
1039                                         continue;
1040                                 }
1041 #endif /* SOFTAP */
1042                                 if (dhd->set_multicast) {
1043                                         dhd->set_multicast = FALSE;
1044                                         _dhd_set_multicast_list(dhd, i);
1045                                 }
1046                                 if (dhd->set_macaddress) {
1047                                         dhd->set_macaddress = FALSE;
1048                                         _dhd_set_mac_address(dhd, i, &dhd->macvalue);
1049                                 }
1050                         }
1051                 }
1052                 dhd_os_wake_unlock(&dhd->pub);
1053                 dhd_os_start_unlock(&dhd->pub);
1054         }
1055         DHD_TRACE(("%s: stopped\n",__FUNCTION__));
1056         complete_and_exit(&dhd->sysioc_exited, 0);
1057 }
1058
1059 static int
1060 dhd_set_mac_address(struct net_device *dev, void *addr)
1061 {
1062         int ret = 0;
1063
1064         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1065         struct sockaddr *sa = (struct sockaddr *)addr;
1066         int ifidx;
1067
1068         DHD_TRACE(("%s: Enter\n",__FUNCTION__));
1069         ifidx = dhd_net2idx(dhd, dev);
1070         if (ifidx == DHD_BAD_IF)
1071                 return -1;
1072
1073         ASSERT(dhd->sysioc_pid >= 0);
1074         memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN);
1075         dhd->set_macaddress = TRUE;
1076         up(&dhd->sysioc_sem);
1077
1078         return ret;
1079 }
1080
1081 static void
1082 dhd_set_multicast_list(struct net_device *dev)
1083 {
1084         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1085         int ifidx;
1086
1087         DHD_TRACE(("%s: Enter\n",__FUNCTION__));
1088         ifidx = dhd_net2idx(dhd, dev);
1089         if (ifidx == DHD_BAD_IF)
1090                 return;
1091
1092         ASSERT(dhd->sysioc_pid >= 0);
1093         dhd->set_multicast = TRUE;
1094         up(&dhd->sysioc_sem);
1095 }
1096
1097 int
1098 dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
1099 {
1100         int ret;
1101         dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1102
1103         /* Reject if down */
1104         if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
1105                 return -ENODEV;
1106         }
1107
1108         /* Update multicast statistic */
1109         if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) {
1110                 uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf);
1111                 struct ether_header *eh = (struct ether_header *)pktdata;
1112
1113                 if (ETHER_ISMULTI(eh->ether_dhost))
1114                         dhdp->tx_multicast++;
1115                 if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X)
1116                         atomic_inc(&dhd->pend_8021x_cnt);
1117         }
1118
1119         /* Look into the packet and update the packet priority */
1120         if ((PKTPRIO(pktbuf) == 0))
1121                 pktsetprio(pktbuf, FALSE);
1122
1123         /* If the protocol uses a data header, apply it */
1124         dhd_prot_hdrpush(dhdp, ifidx, pktbuf);
1125
1126         /* Use bus module to send data frame */
1127 #ifdef BCMDBUS
1128         ret = dbus_send_pkt(dhdp->dbus, pktbuf, NULL /* pktinfo */);
1129 #else
1130         ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1131 #endif /* BCMDBUS */
1132
1133         return ret;
1134 }
1135
1136 static int
1137 dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
1138 {
1139         int ret;
1140         void *pktbuf;
1141         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1142         int ifidx;
1143
1144         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1145
1146         dhd_os_wake_lock(&dhd->pub);
1147
1148         /* Reject if down */
1149         if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) {
1150                 DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d\n",
1151                          __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
1152                 netif_stop_queue(net);
1153                 /* Send Event when bus down detected during data session */
1154                 if (dhd->pub.busstate == DHD_BUS_DOWN)  {
1155                         DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
1156                         net_os_send_hang_message(net);
1157                 }
1158                 dhd_os_wake_unlock(&dhd->pub);
1159                 return -ENODEV;
1160         }
1161
1162         ifidx = dhd_net2idx(dhd, net);
1163         if (ifidx == DHD_BAD_IF) {
1164                 DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx));
1165                 netif_stop_queue(net);
1166                 dhd_os_wake_unlock(&dhd->pub);
1167                 return -ENODEV;
1168         }
1169
1170         /* Make sure there's enough room for any header */
1171         if (skb_headroom(skb) < dhd->pub.hdrlen) {
1172                 struct sk_buff *skb2;
1173
1174                 DHD_INFO(("%s: insufficient headroom\n",
1175                           dhd_ifname(&dhd->pub, ifidx)));
1176                 dhd->pub.tx_realloc++;
1177                 skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen);
1178                 dev_kfree_skb(skb);
1179                 if ((skb = skb2) == NULL) {
1180                         DHD_ERROR(("%s: skb_realloc_headroom failed\n",
1181                                    dhd_ifname(&dhd->pub, ifidx)));
1182                         ret = -ENOMEM;
1183                         goto done;
1184                 }
1185         }
1186
1187         /* Convert to packet */
1188         if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) {
1189                 DHD_ERROR(("%s: PKTFRMNATIVE failed\n",
1190                            dhd_ifname(&dhd->pub, ifidx)));
1191                 dev_kfree_skb_any(skb);
1192                 ret = -ENOMEM;
1193                 goto done;
1194         }
1195
1196         ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf);
1197
1198 done:
1199         if (ret)
1200                 dhd->pub.dstats.tx_dropped++;
1201         else
1202                 dhd->pub.tx_packets++;
1203
1204         dhd_os_wake_unlock(&dhd->pub);
1205
1206         /* Return ok: we always eat the packet */
1207         return 0;
1208 }
1209
1210 void
1211 dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
1212 {
1213         struct net_device *net;
1214         dhd_info_t *dhd = dhdp->info;
1215
1216         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1217
1218         dhdp->txoff = state;
1219         ASSERT(dhd && dhd->iflist[ifidx]);
1220         net = dhd->iflist[ifidx]->net;
1221         if (state == ON)
1222                 netif_stop_queue(net);
1223         else
1224                 netif_wake_queue(net);
1225 }
1226
1227 void
1228 dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt)
1229 {
1230         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1231         struct sk_buff *skb;
1232         uchar *eth;
1233         uint len;
1234         void * data, *pnext, *save_pktbuf;
1235         int i;
1236         dhd_if_t *ifp;
1237         wl_event_msg_t event;
1238
1239         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1240
1241         save_pktbuf = pktbuf;
1242
1243         for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) {
1244
1245                 pnext = PKTNEXT(dhdp->osh, pktbuf);
1246                 PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
1247
1248
1249                 skb = PKTTONATIVE(dhdp->osh, pktbuf);
1250
1251                 /* Get the protocol, maintain skb around eth_type_trans()
1252                  * The main reason for this hack is for the limitation of
1253                  * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len'
1254                  * to perform skb_pull inside vs ETH_HLEN. Since to avoid
1255                  * coping of the packet coming from the network stack to add
1256                  * BDC, Hardware header etc, during network interface registration
1257                  * we set the 'net->hard_header_len' to ETH_HLEN + extra space required
1258                  * for BDC, Hardware header etc. and not just the ETH_HLEN
1259                  */
1260                 eth = skb->data;
1261                 len = skb->len;
1262
1263                 ifp = dhd->iflist[ifidx];
1264                 if (ifp == NULL)
1265                         ifp = dhd->iflist[0];
1266
1267                 ASSERT(ifp);
1268                 skb->dev = ifp->net;
1269                 skb->protocol = eth_type_trans(skb, skb->dev);
1270
1271                 if (skb->pkt_type == PACKET_MULTICAST) {
1272                         dhd->pub.rx_multicast++;
1273                 }
1274
1275                 skb->data = eth;
1276                 skb->len = len;
1277
1278                 /* Strip header, count, deliver upward */
1279                 skb_pull(skb, ETH_HLEN);
1280
1281                 /* Process special event packets and then discard them */
1282                 if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM)
1283                         dhd_wl_host_event(dhd, &ifidx,
1284 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
1285                         skb->mac_header,
1286 #else
1287                         skb->mac.raw,
1288 #endif
1289                         &event,
1290                         &data);
1291
1292                 ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
1293                 if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
1294                         ifp = dhd->iflist[ifidx];
1295
1296                 if (ifp->net)
1297                         ifp->net->last_rx = jiffies;
1298
1299                 dhdp->dstats.rx_bytes += skb->len;
1300                 dhdp->rx_packets++; /* Local count */
1301
1302                 if (in_interrupt()) {
1303                         netif_rx(skb);
1304                 } else {
1305                         /* If the receive is not processed inside an ISR,
1306                          * the softirqd must be woken explicitly to service
1307                          * the NET_RX_SOFTIRQ.  In 2.6 kernels, this is handled
1308                          * by netif_rx_ni(), but in earlier kernels, we need
1309                          * to do it manually.
1310                          */
1311 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1312                         netif_rx_ni(skb);
1313 #else
1314                         ulong flags;
1315                         netif_rx(skb);
1316                         local_irq_save(flags);
1317                         RAISE_RX_SOFTIRQ();
1318                         local_irq_restore(flags);
1319 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
1320                 }
1321         }
1322         dhd_os_wake_lock_timeout_enable(dhdp);
1323 }
1324
1325 void
1326 dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
1327 {
1328         /* Linux version has nothing to do */
1329         return;
1330 }
1331
1332 void
1333 dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success)
1334 {
1335         uint ifidx;
1336         dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1337         struct ether_header *eh;
1338         uint16 type;
1339
1340         dhd_prot_hdrpull(dhdp, &ifidx, txp);
1341
1342         eh = (struct ether_header *)PKTDATA(dhdp->osh, txp);
1343         type  = ntoh16(eh->ether_type);
1344
1345         if (type == ETHER_TYPE_802_1X)
1346                 atomic_dec(&dhd->pend_8021x_cnt);
1347
1348 }
1349
1350 static struct net_device_stats *
1351 dhd_get_stats(struct net_device *net)
1352 {
1353         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1354         dhd_if_t *ifp;
1355         int ifidx;
1356
1357         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1358
1359         ifidx = dhd_net2idx(dhd, net);
1360         if (ifidx == DHD_BAD_IF)
1361                 return NULL;
1362
1363         ifp = dhd->iflist[ifidx];
1364         ASSERT(dhd && ifp);
1365
1366         if (dhd->pub.up) {
1367                 /* Use the protocol to get dongle stats */
1368                 dhd_prot_dstats(&dhd->pub);
1369         }
1370
1371         /* Copy dongle stats to net device stats */
1372         ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
1373         ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
1374         ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
1375         ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
1376         ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
1377         ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
1378         ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
1379         ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
1380         ifp->stats.multicast = dhd->pub.dstats.multicast;
1381
1382         return &ifp->stats;
1383 }
1384
1385 static int
1386 dhd_watchdog_thread(void *data)
1387 {
1388         dhd_info_t *dhd = (dhd_info_t *)data;
1389
1390         /* This thread doesn't need any user-level access,
1391          * so get rid of all our resources
1392          */
1393 #ifdef DHD_SCHED
1394         if (dhd_watchdog_prio > 0) {
1395                 struct sched_param param;
1396                 param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)?
1397                         dhd_watchdog_prio:(MAX_RT_PRIO-1);
1398                 setScheduler(current, SCHED_FIFO, &param);
1399         }
1400 #endif /* DHD_SCHED */
1401
1402         DAEMONIZE("dhd_watchdog");
1403
1404         /* Run until signal received */
1405         while (1) {
1406                 if (down_interruptible (&dhd->watchdog_sem) == 0) {
1407                         dhd_os_sdlock(&dhd->pub);
1408                         if (dhd->pub.dongle_reset == FALSE) {
1409                                 DHD_TIMER(("%s:\n", __FUNCTION__));
1410                                 /* Call the bus module watchdog */
1411                                 dhd_bus_watchdog(&dhd->pub);
1412
1413                                 /* Count the tick for reference */
1414                                 dhd->pub.tickcnt++;
1415
1416                                 /* Reschedule the watchdog */
1417                                 if (dhd->wd_timer_valid)
1418                                         mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
1419                         }
1420                         dhd_os_sdunlock(&dhd->pub);
1421                         dhd_os_wake_unlock(&dhd->pub);
1422                 } else {
1423                         break;
1424                 }
1425         }
1426
1427         complete_and_exit(&dhd->watchdog_exited, 0);
1428 }
1429
1430 static void
1431 dhd_watchdog(ulong data)
1432 {
1433         dhd_info_t *dhd = (dhd_info_t *)data;
1434
1435         dhd_os_wake_lock(&dhd->pub);
1436         if (dhd->pub.dongle_reset) {
1437                 dhd_os_wake_unlock(&dhd->pub);
1438                 return;
1439         }
1440
1441         if (dhd->watchdog_pid >= 0) {
1442                 up(&dhd->watchdog_sem);
1443                 return;
1444         }
1445
1446         dhd_os_sdlock(&dhd->pub);
1447         /* Call the bus module watchdog */
1448         dhd_bus_watchdog(&dhd->pub);
1449
1450         /* Count the tick for reference */
1451         dhd->pub.tickcnt++;
1452
1453         /* Reschedule the watchdog */
1454         if (dhd->wd_timer_valid)
1455                 mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
1456         dhd_os_sdunlock(&dhd->pub);
1457         dhd_os_wake_unlock(&dhd->pub);
1458 }
1459
1460 static int
1461 dhd_dpc_thread(void *data)
1462 {
1463         dhd_info_t *dhd = (dhd_info_t *)data;
1464
1465         /* This thread doesn't need any user-level access,
1466          * so get rid of all our resources
1467          */
1468 #ifdef DHD_SCHED
1469         if (dhd_dpc_prio > 0)
1470         {
1471                 struct sched_param param;
1472                 param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1);
1473                 setScheduler(current, SCHED_FIFO, &param);
1474         }
1475 #endif /* DHD_SCHED */
1476
1477         DAEMONIZE("dhd_dpc");
1478
1479         /* Run until signal received */
1480         while (1) {
1481                 if (down_interruptible(&dhd->dpc_sem) == 0) {
1482                         /* Call bus dpc unless it indicated down (then clean stop) */
1483                         if (dhd->pub.busstate != DHD_BUS_DOWN) {
1484                                 if (dhd_bus_dpc(dhd->pub.bus)) {
1485                                         up(&dhd->dpc_sem);
1486                                 }
1487                                 else {
1488                                         dhd_os_wake_unlock(&dhd->pub);
1489                                 }
1490                         } else {
1491                                 dhd_bus_stop(dhd->pub.bus, TRUE);
1492                                 dhd_os_wake_unlock(&dhd->pub);
1493                         }
1494                 }
1495                 else
1496                         break;
1497         }
1498
1499         complete_and_exit(&dhd->dpc_exited, 0);
1500 }
1501
1502 static void
1503 dhd_dpc(ulong data)
1504 {
1505         dhd_info_t *dhd;
1506
1507         dhd = (dhd_info_t *)data;
1508
1509         /* Call bus dpc unless it indicated down (then clean stop) */
1510         if (dhd->pub.busstate != DHD_BUS_DOWN) {
1511                 if (dhd_bus_dpc(dhd->pub.bus))
1512                         tasklet_schedule(&dhd->tasklet);
1513         } else {
1514                 dhd_bus_stop(dhd->pub.bus, TRUE);
1515         }
1516 }
1517
1518 void
1519 dhd_sched_dpc(dhd_pub_t *dhdp)
1520 {
1521         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1522
1523         dhd_os_wake_lock(dhdp);
1524         if (dhd->dpc_pid >= 0) {
1525                 up(&dhd->dpc_sem);
1526                 return;
1527         }
1528
1529         tasklet_schedule(&dhd->tasklet);
1530 }
1531
1532 #ifdef TOE
1533 /* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */
1534 static int
1535 dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol)
1536 {
1537         wl_ioctl_t ioc;
1538         char buf[32];
1539         int ret;
1540
1541         memset(&ioc, 0, sizeof(ioc));
1542
1543         ioc.cmd = WLC_GET_VAR;
1544         ioc.buf = buf;
1545         ioc.len = (uint)sizeof(buf);
1546         ioc.set = FALSE;
1547
1548         strcpy(buf, "toe_ol");
1549         if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1550                 /* Check for older dongle image that doesn't support toe_ol */
1551                 if (ret == -EIO) {
1552                         DHD_ERROR(("%s: toe not supported by device\n",
1553                                 dhd_ifname(&dhd->pub, ifidx)));
1554                         return -EOPNOTSUPP;
1555                 }
1556
1557                 DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
1558                 return ret;
1559         }
1560
1561         memcpy(toe_ol, buf, sizeof(uint32));
1562         return 0;
1563 }
1564
1565 /* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */
1566 static int
1567 dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol)
1568 {
1569         wl_ioctl_t ioc;
1570         char buf[32];
1571         int toe, ret;
1572
1573         memset(&ioc, 0, sizeof(ioc));
1574
1575         ioc.cmd = WLC_SET_VAR;
1576         ioc.buf = buf;
1577         ioc.len = (uint)sizeof(buf);
1578         ioc.set = TRUE;
1579
1580         /* Set toe_ol as requested */
1581
1582         strcpy(buf, "toe_ol");
1583         memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32));
1584
1585         if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1586                 DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
1587                         dhd_ifname(&dhd->pub, ifidx), ret));
1588                 return ret;
1589         }
1590
1591         /* Enable toe globally only if any components are enabled. */
1592
1593         toe = (toe_ol != 0);
1594
1595         strcpy(buf, "toe");
1596         memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32));
1597
1598         if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1599                 DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
1600                 return ret;
1601         }
1602
1603         return 0;
1604 }
1605 #endif /* TOE */
1606
1607 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
1608 static void dhd_ethtool_get_drvinfo(struct net_device *net,
1609                                     struct ethtool_drvinfo *info)
1610 {
1611         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1612
1613         sprintf(info->driver, "wl");
1614         sprintf(info->version, "%lu", dhd->pub.drv_version);
1615 }
1616
1617 struct ethtool_ops dhd_ethtool_ops = {
1618         .get_drvinfo = dhd_ethtool_get_drvinfo
1619 };
1620 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
1621
1622
1623 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
1624 static int
1625 dhd_ethtool(dhd_info_t *dhd, void *uaddr)
1626 {
1627         struct ethtool_drvinfo info;
1628         char drvname[sizeof(info.driver)];
1629         uint32 cmd;
1630 #ifdef TOE
1631         struct ethtool_value edata;
1632         uint32 toe_cmpnt, csum_dir;
1633         int ret;
1634 #endif
1635
1636         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1637
1638         /* all ethtool calls start with a cmd word */
1639         if (copy_from_user(&cmd, uaddr, sizeof (uint32)))
1640                 return -EFAULT;
1641
1642         switch (cmd) {
1643         case ETHTOOL_GDRVINFO:
1644                 /* Copy out any request driver name */
1645                 if (copy_from_user(&info, uaddr, sizeof(info)))
1646                         return -EFAULT;
1647                 strncpy(drvname, info.driver, sizeof(info.driver));
1648                 drvname[sizeof(info.driver)-1] = '\0';
1649
1650                 /* clear struct for return */
1651                 memset(&info, 0, sizeof(info));
1652                 info.cmd = cmd;
1653
1654                 /* if dhd requested, identify ourselves */
1655                 if (strcmp(drvname, "?dhd") == 0) {
1656                         sprintf(info.driver, "dhd");
1657                         strcpy(info.version, EPI_VERSION_STR);
1658                 }
1659
1660                 /* otherwise, require dongle to be up */
1661                 else if (!dhd->pub.up) {
1662                         DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__));
1663                         return -ENODEV;
1664                 }
1665
1666                 /* finally, report dongle driver type */
1667                 else if (dhd->pub.iswl)
1668                         sprintf(info.driver, "wl");
1669                 else
1670                         sprintf(info.driver, "xx");
1671
1672                 sprintf(info.version, "%lu", dhd->pub.drv_version);
1673                 if (copy_to_user(uaddr, &info, sizeof(info)))
1674                         return -EFAULT;
1675                 DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__,
1676                          (int)sizeof(drvname), drvname, info.driver));
1677                 break;
1678
1679 #ifdef TOE
1680         /* Get toe offload components from dongle */
1681         case ETHTOOL_GRXCSUM:
1682         case ETHTOOL_GTXCSUM:
1683                 if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
1684                         return ret;
1685
1686                 csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
1687
1688                 edata.cmd = cmd;
1689                 edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
1690
1691                 if (copy_to_user(uaddr, &edata, sizeof(edata)))
1692                         return -EFAULT;
1693                 break;
1694
1695         /* Set toe offload components in dongle */
1696         case ETHTOOL_SRXCSUM:
1697         case ETHTOOL_STXCSUM:
1698                 if (copy_from_user(&edata, uaddr, sizeof(edata)))
1699                         return -EFAULT;
1700
1701                 /* Read the current settings, update and write back */
1702                 if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
1703                         return ret;
1704
1705                 csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
1706
1707                 if (edata.data != 0)
1708                         toe_cmpnt |= csum_dir;
1709                 else
1710                         toe_cmpnt &= ~csum_dir;
1711
1712                 if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0)
1713                         return ret;
1714
1715                 /* If setting TX checksum mode, tell Linux the new mode */
1716                 if (cmd == ETHTOOL_STXCSUM) {
1717                         if (edata.data)
1718                                 dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM;
1719                         else
1720                                 dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM;
1721                 }
1722
1723                 break;
1724 #endif /* TOE */
1725
1726         default:
1727                 return -EOPNOTSUPP;
1728         }
1729
1730         return 0;
1731 }
1732 #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
1733
1734 static int
1735 dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd)
1736 {
1737         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1738         dhd_ioctl_t ioc;
1739         int bcmerror = 0;
1740         int buflen = 0;
1741         void *buf = NULL;
1742         uint driver = 0;
1743         int ifidx;
1744         bool is_set_key_cmd;
1745         int ret;
1746
1747         dhd_os_wake_lock(&dhd->pub);
1748
1749         ifidx = dhd_net2idx(dhd, net);
1750         DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd));
1751
1752         if (ifidx == DHD_BAD_IF) {
1753                 dhd_os_wake_unlock(&dhd->pub);
1754                 return -1;
1755         }
1756
1757 #if defined(CONFIG_WIRELESS_EXT)
1758         /* linux wireless extensions */
1759         if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) {
1760                 /* may recurse, do NOT lock */
1761                 ret = wl_iw_ioctl(net, ifr, cmd);
1762                 dhd_os_wake_unlock(&dhd->pub);
1763                 return ret;
1764         }
1765 #endif /* defined(CONFIG_WIRELESS_EXT) */
1766
1767 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
1768         if (cmd == SIOCETHTOOL) {
1769                 ret = dhd_ethtool(dhd, (void*)ifr->ifr_data);
1770                 dhd_os_wake_unlock(&dhd->pub);
1771                 return ret;
1772         }
1773 #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
1774
1775         if (cmd != SIOCDEVPRIVATE) {
1776                 dhd_os_wake_unlock(&dhd->pub);
1777                 return -EOPNOTSUPP;
1778         }
1779
1780         memset(&ioc, 0, sizeof(ioc));
1781
1782         /* Copy the ioc control structure part of ioctl request */
1783         if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) {
1784                 bcmerror = -BCME_BADADDR;
1785                 goto done;
1786         }
1787
1788         /* Copy out any buffer passed */
1789         if (ioc.buf) {
1790                 buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN);
1791                 /* optimization for direct ioctl calls from kernel */
1792                 /*
1793                 if (segment_eq(get_fs(), KERNEL_DS)) {
1794                         buf = ioc.buf;
1795                 } else {
1796                 */
1797                 {
1798                         if (!(buf = (char*)MALLOC(dhd->pub.osh, buflen))) {
1799                                 bcmerror = -BCME_NOMEM;
1800                                 goto done;
1801                         }
1802                         if (copy_from_user(buf, ioc.buf, buflen)) {
1803                                 bcmerror = -BCME_BADADDR;
1804                                 goto done;
1805                         }
1806                 }
1807         }
1808
1809         /* To differentiate between wl and dhd read 4 more byes */
1810         if ((copy_from_user(&driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t),
1811                 sizeof(uint)) != 0)) {
1812                 bcmerror = -BCME_BADADDR;
1813                 goto done;
1814         }
1815
1816         if (!capable(CAP_NET_ADMIN)) {
1817                 bcmerror = -BCME_EPERM;
1818                 goto done;
1819         }
1820
1821         /* check for local dhd ioctl and handle it */
1822         if (driver == DHD_IOCTL_MAGIC) {
1823                 bcmerror = dhd_ioctl((void *)&dhd->pub, &ioc, buf, buflen);
1824                 if (bcmerror)
1825                         dhd->pub.bcmerror = bcmerror;
1826                 goto done;
1827         }
1828
1829         /* send to dongle (must be up, and wl) */
1830         if (dhd->pub.busstate != DHD_BUS_DATA) {
1831                 DHD_ERROR(("%s DONGLE_DOWN\n", __FUNCTION__));
1832                 bcmerror = BCME_DONGLE_DOWN;
1833                 goto done;
1834         }
1835
1836         if (!dhd->pub.iswl) {
1837                 bcmerror = BCME_DONGLE_DOWN;
1838                 goto done;
1839         }
1840
1841         /* Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to
1842          * prevent M4 encryption.
1843          */
1844         is_set_key_cmd = ((ioc.cmd == WLC_SET_KEY) ||
1845                          ((ioc.cmd == WLC_SET_VAR) &&
1846                                 !(strncmp("wsec_key", ioc.buf, 9))) ||
1847                          ((ioc.cmd == WLC_SET_VAR) &&
1848                                 !(strncmp("bsscfg:wsec_key", ioc.buf, 15))));
1849         if (is_set_key_cmd) {
1850                 dhd_wait_pend8021x(net);
1851         }
1852
1853         bcmerror = dhd_prot_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen);
1854
1855 done:
1856         if ((bcmerror == -ETIMEDOUT) || ((dhd->pub.busstate == DHD_BUS_DOWN) &&
1857                         (!dhd->pub.dongle_reset))) {
1858                 DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
1859                 net_os_send_hang_message(net);
1860         }
1861
1862         if (!bcmerror && buf && ioc.buf) {
1863                 if (copy_to_user(ioc.buf, buf, buflen))
1864                         bcmerror = -EFAULT;
1865         }
1866
1867         if (buf)
1868                 MFREE(dhd->pub.osh, buf, buflen);
1869
1870         dhd_os_wake_unlock(&dhd->pub);
1871
1872         return OSL_ERROR(bcmerror);
1873 }
1874
1875 static int
1876 dhd_stop(struct net_device *net)
1877 {
1878 #if !defined(IGNORE_ETH0_DOWN)
1879         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1880
1881         DHD_TRACE(("%s: Enter %s\n", __FUNCTION__, net->name));
1882         if (dhd->pub.up == 0) {
1883                 return 0;
1884         }
1885
1886         /* Set state and stop OS transmissions */
1887         dhd->pub.up = 0;
1888         netif_stop_queue(net);
1889 #else
1890         DHD_ERROR(("BYPASS %s:due to BRCM compilation : under investigation ...\n", __FUNCTION__));
1891 #endif /* !defined(IGNORE_ETH0_DOWN) */
1892
1893         OLD_MOD_DEC_USE_COUNT;
1894         return 0;
1895 }
1896
1897 static int
1898 dhd_open(struct net_device *net)
1899 {
1900         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1901 #ifdef TOE
1902         uint32 toe_ol;
1903 #endif
1904         int ifidx;
1905
1906         /*  Force start if ifconfig_up gets called before START command */
1907         wl_control_wl_start(net);
1908
1909         ifidx = dhd_net2idx(dhd, net);
1910         if (ifidx == DHD_BAD_IF)
1911                 return -1;
1912         DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
1913
1914         if ((dhd->iflist[ifidx]) && (dhd->iflist[ifidx]->state == WLC_E_IF_DEL)) {
1915                 DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__));
1916                 return -1;
1917         }
1918
1919         if (ifidx == 0) { /* do it only for primary eth0 */
1920
1921                 atomic_set(&dhd->pend_8021x_cnt, 0);
1922
1923         memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
1924
1925 #ifdef TOE
1926         /* Get current TOE mode from dongle */
1927         if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0)
1928                 dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM;
1929         else
1930                 dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM;
1931 #endif
1932         }
1933         /* Allow transmit calls */
1934         netif_start_queue(net);
1935         dhd->pub.up = 1;
1936
1937         OLD_MOD_INC_USE_COUNT;
1938         return 0;
1939 }
1940
1941 osl_t *
1942 dhd_osl_attach(void *pdev, uint bustype)
1943 {
1944         return osl_attach(pdev, bustype, TRUE);
1945 }
1946
1947 void
1948 dhd_osl_detach(osl_t *osh)
1949 {
1950         if (MALLOCED(osh)) {
1951                 DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh)));
1952         }
1953         osl_detach(osh);
1954 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && 1
1955         up(&dhd_registration_sem);
1956 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
1957 }
1958
1959 int
1960 dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name,
1961         uint8 *mac_addr, uint32 flags, uint8 bssidx)
1962 {
1963         dhd_if_t *ifp;
1964
1965         DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle));
1966
1967         ASSERT(dhd && (ifidx < DHD_MAX_IFS));
1968
1969         ifp = dhd->iflist[ifidx];
1970         if (!ifp && !(ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t)))) {
1971                 DHD_ERROR(("%s: OOM - dhd_if_t\n", __FUNCTION__));
1972                 return -ENOMEM;
1973         }
1974
1975         memset(ifp, 0, sizeof(dhd_if_t));
1976         ifp->info = dhd;
1977         dhd->iflist[ifidx] = ifp;
1978         strncpy(ifp->name, name, IFNAMSIZ);
1979         ifp->name[IFNAMSIZ] = '\0';
1980         if (mac_addr != NULL)
1981                 memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN);
1982
1983         if (handle == NULL) {
1984                 ifp->state = WLC_E_IF_ADD;
1985                 ifp->idx = ifidx;
1986                 ASSERT(dhd->sysioc_pid >= 0);
1987                 up(&dhd->sysioc_sem);
1988         } else
1989                 ifp->net = (struct net_device *)handle;
1990
1991         return 0;
1992 }
1993
1994 void
1995 dhd_del_if(dhd_info_t *dhd, int ifidx)
1996 {
1997         dhd_if_t *ifp;
1998
1999         DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx));
2000
2001         ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS));
2002         ifp = dhd->iflist[ifidx];
2003         if (!ifp) {
2004                 DHD_ERROR(("%s: Null interface\n", __FUNCTION__));
2005                 return;
2006         }
2007
2008         ifp->state = WLC_E_IF_DEL;
2009         ifp->idx = ifidx;
2010         ASSERT(dhd->sysioc_pid >= 0);
2011         up(&dhd->sysioc_sem);
2012 }
2013
2014 dhd_pub_t *
2015 dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
2016 {
2017         dhd_info_t *dhd = NULL;
2018         struct net_device *net;
2019
2020         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2021         /* updates firmware nvram path if it was provided as module paramters */
2022         if ((firmware_path != NULL) && (firmware_path[0] != '\0'))
2023                 strcpy(fw_path, firmware_path);
2024         if ((nvram_path != NULL) && (nvram_path[0] != '\0'))
2025                 strcpy(nv_path, nvram_path);
2026
2027         /* Allocate etherdev, including space for private structure */
2028         if (!(net = alloc_etherdev(sizeof(dhd)))) {
2029                 DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
2030                 goto fail;
2031         }
2032
2033         /* Allocate primary dhd_info */
2034         if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) {
2035                 DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__));
2036                 goto fail;
2037         }
2038
2039         memset(dhd, 0, sizeof(dhd_info_t));
2040
2041         /*
2042          * Save the dhd_info into the priv
2043          */
2044         memcpy(netdev_priv(net), &dhd, sizeof(dhd));
2045         dhd->pub.osh = osh;
2046
2047         /* Set network interface name if it was provided as module parameter */
2048         if (iface_name[0]) {
2049                 int len;
2050                 char ch;
2051                 strncpy(net->name, iface_name, IFNAMSIZ);
2052                 net->name[IFNAMSIZ - 1] = 0;
2053                 len = strlen(net->name);
2054                 ch = net->name[len - 1];
2055                 if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2))
2056                         strcat(net->name, "%d");
2057         }
2058
2059         if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF)
2060                 goto fail;
2061
2062 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
2063         net->open = NULL;
2064 #else
2065         net->netdev_ops = NULL;
2066 #endif
2067
2068         init_MUTEX(&dhd->proto_sem);
2069         /* Initialize other structure content */
2070         init_waitqueue_head(&dhd->ioctl_resp_wait);
2071         init_waitqueue_head(&dhd->ctrl_wait);
2072
2073         /* Initialize the spinlocks */
2074         spin_lock_init(&dhd->sdlock);
2075         spin_lock_init(&dhd->txqlock);
2076         spin_lock_init(&dhd->dhd_lock);
2077
2078         /* Initialize Wakelock stuff */
2079         spin_lock_init(&dhd->wl_lock);
2080         dhd->wl_count = 0;
2081         dhd->wl_packet = 0;
2082 #ifdef CONFIG_HAS_WAKELOCK
2083         wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake");
2084         wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake");
2085 #endif
2086 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
2087         mutex_init(&dhd->wl_start_lock);
2088 #endif
2089         /* Link to info module */
2090         dhd->pub.info = dhd;
2091
2092         /* Link to bus module */
2093         dhd->pub.bus = bus;
2094         dhd->pub.hdrlen = bus_hdrlen;
2095
2096         /* Attach and link in the protocol */
2097         if (dhd_prot_attach(&dhd->pub) != 0) {
2098                 DHD_ERROR(("dhd_prot_attach failed\n"));
2099                 goto fail;
2100         }
2101 #if defined(CONFIG_WIRELESS_EXT)
2102         /* Attach and link in the iw */
2103         if (wl_iw_attach(net, (void *)&dhd->pub) != 0) {
2104                 DHD_ERROR(("wl_iw_attach failed\n"));
2105                 goto fail;
2106         }
2107 #endif /* defined(CONFIG_WIRELESS_EXT) */
2108
2109         /* Set up the watchdog timer */
2110         init_timer(&dhd->timer);
2111         dhd->timer.data = (ulong)dhd;
2112         dhd->timer.function = dhd_watchdog;
2113
2114         /* Initialize thread based operation and lock */
2115         init_MUTEX(&dhd->sdsem);
2116         if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) {
2117                 dhd->threads_only = TRUE;
2118         }
2119         else {
2120                 dhd->threads_only = FALSE;
2121         }
2122
2123         if (dhd_dpc_prio >= 0) {
2124                 /* Initialize watchdog thread */
2125                 sema_init(&dhd->watchdog_sem, 0);
2126                 init_completion(&dhd->watchdog_exited);
2127                 dhd->watchdog_pid = kernel_thread(dhd_watchdog_thread, dhd, 0);
2128         } else {
2129                 dhd->watchdog_pid = -1;
2130         }
2131
2132         /* Set up the bottom half handler */
2133         if (dhd_dpc_prio >= 0) {
2134                 /* Initialize DPC thread */
2135                 sema_init(&dhd->dpc_sem, 0);
2136                 init_completion(&dhd->dpc_exited);
2137                 dhd->dpc_pid = kernel_thread(dhd_dpc_thread, dhd, 0);
2138         } else {
2139                 tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd);
2140                 dhd->dpc_pid = -1;
2141         }
2142
2143         if (dhd_sysioc) {
2144                 sema_init(&dhd->sysioc_sem, 0);
2145                 init_completion(&dhd->sysioc_exited);
2146                 dhd->sysioc_pid = kernel_thread(_dhd_sysioc_thread, dhd, 0);
2147         } else {
2148                 dhd->sysioc_pid = -1;
2149         }
2150
2151         /*
2152          * Save the dhd_info into the priv
2153          */
2154         memcpy(netdev_priv(net), &dhd, sizeof(dhd));
2155
2156 #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
2157         g_bus = bus;
2158 #endif
2159 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
2160         register_pm_notifier(&dhd_sleep_pm_notifier);
2161 #endif /*  (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
2162
2163 #ifdef CONFIG_HAS_EARLYSUSPEND
2164         dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20;
2165         dhd->early_suspend.suspend = dhd_early_suspend;
2166         dhd->early_suspend.resume = dhd_late_resume;
2167         register_early_suspend(&dhd->early_suspend);
2168 #endif
2169
2170         return &dhd->pub;
2171
2172 fail:
2173         if (net)
2174                 free_netdev(net);
2175         if (dhd)
2176                 dhd_detach(&dhd->pub);
2177
2178         return NULL;
2179 }
2180
2181
2182 int
2183 dhd_bus_start(dhd_pub_t *dhdp)
2184 {
2185         int ret = -1;
2186         dhd_info_t *dhd = (dhd_info_t*)dhdp->info;
2187 #ifdef EMBEDDED_PLATFORM
2188         char iovbuf[WL_EVENTING_MASK_LEN + 12]; /*  Room for "event_msgs" + '\0' + bitvec  */
2189 #endif /* EMBEDDED_PLATFORM */
2190
2191         ASSERT(dhd);
2192
2193         DHD_TRACE(("%s: \n", __FUNCTION__));
2194
2195         /* try to download image and nvram to the dongle */
2196         if  (dhd->pub.busstate == DHD_BUS_DOWN) {
2197                 if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh,
2198                                                 fw_path, nv_path))) {
2199                         DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n",
2200                                    __FUNCTION__, fw_path, nv_path));
2201                         return -1;
2202                 }
2203         }
2204
2205         /* Start the watchdog timer */
2206         dhd->pub.tickcnt = 0;
2207         dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms);
2208
2209         /* Bring up the bus */
2210         if ((ret = dhd_bus_init(&dhd->pub, TRUE)) != 0) {
2211                 DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret));
2212                 return ret;
2213         }
2214 #if defined(OOB_INTR_ONLY)
2215         /* Host registration for OOB interrupt */
2216         if (bcmsdh_register_oob_intr(dhdp)) {
2217                 dhd->wd_timer_valid = FALSE;
2218                 del_timer_sync(&dhd->timer);
2219                 DHD_ERROR(("%s Host failed to resgister for OOB\n", __FUNCTION__));
2220                 return -ENODEV;
2221         }
2222
2223         /* Enable oob at firmware */
2224         dhd_enable_oob_intr(dhd->pub.bus, TRUE);
2225 #endif /* defined(OOB_INTR_ONLY) */
2226
2227         /* If bus is not ready, can't come up */
2228         if (dhd->pub.busstate != DHD_BUS_DATA) {
2229                 dhd->wd_timer_valid = FALSE;
2230                 del_timer_sync(&dhd->timer);
2231                 DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__));
2232                 return -ENODEV;
2233         }
2234
2235 #ifdef EMBEDDED_PLATFORM
2236         bcm_mkiovar("event_msgs", dhdp->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
2237         dhdcdc_query_ioctl(dhdp, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf));
2238         bcopy(iovbuf, dhdp->eventmask, WL_EVENTING_MASK_LEN);
2239
2240         setbit(dhdp->eventmask, WLC_E_SET_SSID);
2241         setbit(dhdp->eventmask, WLC_E_PRUNE);
2242         setbit(dhdp->eventmask, WLC_E_AUTH);
2243         setbit(dhdp->eventmask, WLC_E_REASSOC);
2244         setbit(dhdp->eventmask, WLC_E_REASSOC_IND);
2245         setbit(dhdp->eventmask, WLC_E_DEAUTH_IND);
2246         setbit(dhdp->eventmask, WLC_E_DISASSOC_IND);
2247         setbit(dhdp->eventmask, WLC_E_DISASSOC);
2248         setbit(dhdp->eventmask, WLC_E_JOIN);
2249         setbit(dhdp->eventmask, WLC_E_ASSOC_IND);
2250         setbit(dhdp->eventmask, WLC_E_PSK_SUP);
2251         setbit(dhdp->eventmask, WLC_E_LINK);
2252         setbit(dhdp->eventmask, WLC_E_NDIS_LINK);
2253         setbit(dhdp->eventmask, WLC_E_MIC_ERROR);
2254         setbit(dhdp->eventmask, WLC_E_PMKID_CACHE);
2255         setbit(dhdp->eventmask, WLC_E_TXFAIL);
2256         setbit(dhdp->eventmask, WLC_E_JOIN_START);
2257         setbit(dhdp->eventmask, WLC_E_SCAN_COMPLETE);
2258         setbit(dhdp->eventmask, WLC_E_RELOAD);
2259 #ifdef PNO_SUPPORT
2260         setbit(dhdp->eventmask, WLC_E_PFN_NET_FOUND);
2261 #endif /* PNO_SUPPORT */
2262
2263 /* enable dongle roaming event */
2264         setbit(dhdp->eventmask, WLC_E_ROAM);
2265
2266         dhdp->pktfilter_count = 1;
2267         /* Setup filter to allow only unicast */
2268         dhdp->pktfilter[0] = "100 0 0 0 0x01 0x00";
2269 #endif /* EMBEDDED_PLATFORM */
2270
2271         /* Bus is ready, do any protocol initialization */
2272         if ((ret = dhd_prot_init(&dhd->pub)) < 0)
2273                 return ret;
2274
2275         return 0;
2276 }
2277
2278 int
2279 dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set)
2280 {
2281         char buf[strlen(name) + 1 + cmd_len];
2282         int len = sizeof(buf);
2283         wl_ioctl_t ioc;
2284         int ret;
2285
2286         len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len);
2287
2288         memset(&ioc, 0, sizeof(ioc));
2289
2290         ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR;
2291         ioc.buf = buf;
2292         ioc.len = len;
2293         ioc.set = set;
2294
2295         ret = dhd_prot_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len);
2296         if (!set && ret >= 0)
2297                 memcpy(cmd_buf, buf, cmd_len);
2298
2299         return ret;
2300 }
2301
2302 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
2303 static struct net_device_ops dhd_ops_pri = {
2304         .ndo_open = dhd_open,
2305         .ndo_stop = dhd_stop,
2306         .ndo_get_stats = dhd_get_stats,
2307         .ndo_do_ioctl = dhd_ioctl_entry,
2308         .ndo_start_xmit = dhd_start_xmit,
2309         .ndo_set_mac_address = dhd_set_mac_address,
2310         .ndo_set_multicast_list = dhd_set_multicast_list,
2311 };
2312
2313 static struct net_device_ops dhd_ops_virt = {
2314         .ndo_get_stats = dhd_get_stats,
2315         .ndo_do_ioctl = dhd_ioctl_entry,
2316         .ndo_start_xmit = dhd_start_xmit,
2317         .ndo_set_mac_address = dhd_set_mac_address,
2318         .ndo_set_multicast_list = dhd_set_multicast_list,
2319 };
2320 #endif
2321
2322 int
2323 dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
2324 {
2325         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
2326         struct net_device *net;
2327         uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 };
2328
2329         DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
2330
2331         ASSERT(dhd && dhd->iflist[ifidx]);
2332         net = dhd->iflist[ifidx]->net;
2333
2334         ASSERT(net);
2335 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
2336         ASSERT(!net->open);
2337         net->get_stats = dhd_get_stats;
2338         net->do_ioctl = dhd_ioctl_entry;
2339         net->hard_start_xmit = dhd_start_xmit;
2340         net->set_mac_address = dhd_set_mac_address;
2341         net->set_multicast_list = dhd_set_multicast_list;
2342         net->open = net->stop = NULL;
2343 #else
2344         ASSERT(!net->netdev_ops);
2345         net->netdev_ops = &dhd_ops_virt;
2346 #endif
2347
2348 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
2349                 net->open = dhd_open;
2350                 net->stop = dhd_stop;
2351 #else
2352                 net->netdev_ops = &dhd_ops_pri;
2353 #endif
2354
2355         /*
2356          * We have to use the primary MAC for virtual interfaces
2357          */
2358         if (ifidx != 0) {
2359                 /* for virtual interfaces use the primary MAC  */
2360                 memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
2361         }
2362
2363         if (ifidx == 1) {
2364                 DHD_TRACE(("%s ACCESS POINT MAC: \n", __FUNCTION__));
2365                 /*  ACCESSPOINT INTERFACE CASE */
2366                 temp_addr[0] |= 0x02;  /* set bit 2 , - Locally Administered address  */
2367         }
2368         net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen;
2369 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
2370         net->ethtool_ops = &dhd_ethtool_ops;
2371 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
2372
2373 #if defined(CONFIG_WIRELESS_EXT)
2374 #if WIRELESS_EXT < 19
2375         net->get_wireless_stats = dhd_get_wireless_stats;
2376 #endif /* WIRELESS_EXT < 19 */
2377 #if WIRELESS_EXT > 12
2378         net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def;
2379 #endif /* WIRELESS_EXT > 12 */
2380 #endif /* defined(CONFIG_WIRELESS_EXT) */
2381
2382         dhd->pub.rxsz = net->mtu + net->hard_header_len + dhd->pub.hdrlen;
2383
2384         memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN);
2385
2386         if (register_netdev(net) != 0) {
2387                 DHD_ERROR(("%s: couldn't register the net device\n", __FUNCTION__));
2388                 goto fail;
2389         }
2390
2391         printf("%s: Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", net->name,
2392                dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2],
2393                dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]);
2394
2395 #if defined(CONFIG_WIRELESS_EXT)
2396 #if defined(CONFIG_FIRST_SCAN)
2397 #ifdef SOFTAP
2398         if (ifidx == 0)
2399                 /* Don't call for SOFTAP Interface in SOFTAP MODE */
2400                 wl_iw_iscan_set_scan_broadcast_prep(net, 1);
2401 #else
2402                 wl_iw_iscan_set_scan_broadcast_prep(net, 1);
2403 #endif /* SOFTAP */
2404 #endif /* CONFIG_FIRST_SCAN */
2405 #endif /* CONFIG_WIRELESS_EXT */
2406
2407 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2408         up(&dhd_registration_sem);
2409 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
2410         return 0;
2411
2412 fail:
2413 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
2414         net->open = NULL;
2415 #else
2416         net->netdev_ops = NULL;
2417 #endif
2418         return BCME_ERROR;
2419 }
2420
2421 void
2422 dhd_bus_detach(dhd_pub_t *dhdp)
2423 {
2424         dhd_info_t *dhd;
2425
2426         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2427
2428         if (dhdp) {
2429                 dhd = (dhd_info_t *)dhdp->info;
2430                 if (dhd) {
2431                         /* Stop the protocol module */
2432                         dhd_prot_stop(&dhd->pub);
2433
2434                         /* Stop the bus module */
2435                         dhd_bus_stop(dhd->pub.bus, TRUE);
2436 #if defined(OOB_INTR_ONLY)
2437                         bcmsdh_unregister_oob_intr();
2438 #endif /* defined(OOB_INTR_ONLY) */
2439
2440                         /* Clear the watchdog timer */
2441                         dhd->wd_timer_valid = FALSE;
2442                         del_timer_sync(&dhd->timer);
2443                 }
2444         }
2445 }
2446
2447 void
2448 dhd_detach(dhd_pub_t *dhdp)
2449 {
2450         dhd_info_t *dhd;
2451
2452         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2453
2454         if (dhdp) {
2455                 dhd = (dhd_info_t *)dhdp->info;
2456                 if (dhd) {
2457                         dhd_if_t *ifp;
2458                         int i;
2459
2460 #if defined(CONFIG_HAS_EARLYSUSPEND)
2461                         if (dhd->early_suspend.suspend)
2462                                 unregister_early_suspend(&dhd->early_suspend);
2463 #endif  /* defined(CONFIG_HAS_EARLYSUSPEND) */
2464 #if defined(CONFIG_WIRELESS_EXT)
2465                         /* Attach and link in the iw */
2466                         wl_iw_detach();
2467 #endif
2468                         if (dhd->sysioc_pid >= 0) {
2469                                 KILL_PROC(dhd->sysioc_pid, SIGTERM);
2470                                 wait_for_completion(&dhd->sysioc_exited);
2471                         }
2472
2473                         for (i = 1; i < DHD_MAX_IFS; i++)
2474                                 if (dhd->iflist[i]) {
2475                                         dhd->iflist[i]->state = WLC_E_IF_DEL;
2476                                         dhd->iflist[i]->idx = i;
2477                                         dhd_op_if(dhd->iflist[i]);
2478                                 }
2479
2480                         ifp = dhd->iflist[0];
2481                         ASSERT(ifp);
2482 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
2483                         if (ifp->net->open) {
2484 #else
2485                         if (ifp->net->netdev_ops == &dhd_ops_pri) {
2486 #endif
2487                                 dhd_stop(ifp->net);
2488                                 unregister_netdev(ifp->net);
2489                         }
2490
2491                         if (dhd->watchdog_pid >= 0)
2492                         {
2493                                 KILL_PROC(dhd->watchdog_pid, SIGTERM);
2494                                 wait_for_completion(&dhd->watchdog_exited);
2495                         }
2496
2497                         if (dhd->dpc_pid >= 0)
2498                         {
2499                                 KILL_PROC(dhd->dpc_pid, SIGTERM);
2500                                 wait_for_completion(&dhd->dpc_exited);
2501                         }
2502                         else
2503                                 tasklet_kill(&dhd->tasklet);
2504
2505                         dhd_bus_detach(dhdp);
2506
2507                         if (dhdp->prot)
2508                                 dhd_prot_detach(dhdp);
2509
2510 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
2511                         unregister_pm_notifier(&dhd_sleep_pm_notifier);
2512 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
2513                         free_netdev(ifp->net);
2514 #ifdef CONFIG_HAS_WAKELOCK
2515                         wake_lock_destroy(&dhd->wl_wifi);
2516                         wake_lock_destroy(&dhd->wl_rxwake);
2517 #endif
2518                         MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
2519                         MFREE(dhd->pub.osh, dhd, sizeof(*dhd));
2520                 }
2521         }
2522 }
2523
2524 void
2525 rockchip_wifi_exit_module(void)
2526 {
2527         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2528
2529         dhd_bus_unregister();
2530 #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
2531         wifi_del_dev();
2532 #endif
2533         /* Call customer gpio to turn off power with WL_REG_ON signal */
2534         dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
2535         bcm4329_power_save_exit();
2536 }
2537
2538 int
2539 rockchip_wifi_init_module(void)
2540 {
2541         int error;
2542
2543         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2544
2545         printk("BCM4329 Wi-Fi driver (Powered by Rockchip,Ver %s) init.\n", BCM4329_DRV_VERSION);
2546         /* Sanity check on the module parameters */
2547         do {
2548                 /* Both watchdog and DPC as tasklets are ok */
2549                 if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0))
2550                         break;
2551
2552                 /* If both watchdog and DPC are threads, TX must be deferred */
2553                 if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx)
2554                         break;
2555
2556                 DHD_ERROR(("Invalid module parameters.\n"));
2557                 return -EINVAL;
2558         } while (0);
2559
2560         /* Call customer gpio to turn on power with WL_REG_ON signal */
2561         dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON);
2562
2563 #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
2564         sema_init(&wifi_control_sem, 0);
2565
2566         error = wifi_add_dev();
2567         if (error) {
2568                 DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__));
2569                 goto fail_0;
2570         }
2571
2572         /* Waiting callback after platform_driver_register is done or exit with error */
2573         if (down_timeout(&wifi_control_sem,  msecs_to_jiffies(5000)) != 0) {
2574                 error = -EINVAL;
2575                 DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__));
2576                 goto fail_1;
2577         }
2578 #endif /* #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
2579
2580 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2581         sema_init(&dhd_registration_sem, 0);
2582 #endif 
2583
2584         error = dhd_bus_register();
2585
2586         if (!error)
2587                 printf("\n%s\n", dhd_version);
2588         else {
2589                 DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__));
2590                 goto fail_1;
2591         }
2592 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2593         /*
2594          * Wait till MMC sdio_register_driver callback called and made driver attach.
2595          * It's needed to make sync up exit from dhd insmod  and
2596          * Kernel MMC sdio device callback registration
2597          */
2598         if (down_timeout(&dhd_registration_sem,  msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) {
2599                 error = -EINVAL;
2600                 DHD_ERROR(("%s: sdio_register_driver timeout\n", __FUNCTION__));
2601                 goto fail_2;
2602         }
2603 #endif
2604         bcm4329_power_save_init();
2605
2606         return error;
2607 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2608 fail_2:
2609         dhd_bus_unregister();
2610 #endif
2611 fail_1:
2612 #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
2613         wifi_del_dev();
2614 fail_0:
2615 #endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
2616
2617         /* Call customer gpio to turn off power with WL_REG_ON signal */
2618         dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
2619
2620         return error;
2621 }
2622
2623 //module_init(dhd_module_init);
2624 //module_exit(dhd_module_cleanup);
2625 int mv88w8686_if_sdio_init_module(void)
2626 {
2627         return rockchip_wifi_init_module();
2628 }
2629
2630 void mv88w8686_if_sdio_exit_module(void)
2631 {
2632        rockchip_wifi_exit_module();
2633 }
2634
2635 EXPORT_SYMBOL(rockchip_wifi_init_module);
2636 EXPORT_SYMBOL(rockchip_wifi_exit_module);
2637 EXPORT_SYMBOL(mv88w8686_if_sdio_init_module);
2638 EXPORT_SYMBOL(mv88w8686_if_sdio_exit_module);
2639
2640
2641 /*
2642  * OS specific functions required to implement DHD driver in OS independent way
2643  */
2644 int
2645 dhd_os_proto_block(dhd_pub_t *pub)
2646 {
2647         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
2648
2649         if (dhd) {
2650                 down(&dhd->proto_sem);
2651                 return 1;
2652         }
2653
2654         return 0;
2655 }
2656
2657 int
2658 dhd_os_proto_unblock(dhd_pub_t *pub)
2659 {
2660         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
2661
2662         if (dhd) {
2663                 up(&dhd->proto_sem);
2664                 return 1;
2665         }
2666
2667         return 0;
2668 }
2669
2670 unsigned int
2671 dhd_os_get_ioctl_resp_timeout(void)
2672 {
2673         return ((unsigned int)dhd_ioctl_timeout_msec);
2674 }
2675
2676 void
2677 dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec)
2678 {
2679         dhd_ioctl_timeout_msec = (int)timeout_msec;
2680 }
2681
2682 int
2683 dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending)
2684 {
2685         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
2686         DECLARE_WAITQUEUE(wait, current);
2687         int timeout = dhd_ioctl_timeout_msec;
2688
2689         /* Convert timeout in millsecond to jiffies */
2690         /* timeout = timeout * HZ / 1000; */
2691         timeout = msecs_to_jiffies(timeout);
2692
2693         /* Wait until control frame is available */
2694         add_wait_queue(&dhd->ioctl_resp_wait, &wait);
2695         set_current_state(TASK_INTERRUPTIBLE);
2696         smp_mb();
2697         while (!(*condition) && (!signal_pending(current) && timeout)) {
2698                 timeout = schedule_timeout(timeout);
2699                 smp_mb();
2700         }
2701
2702         if (signal_pending(current))
2703                 *pending = TRUE;
2704
2705         set_current_state(TASK_RUNNING);
2706         remove_wait_queue(&dhd->ioctl_resp_wait, &wait);
2707
2708         return timeout;
2709 }
2710
2711 int
2712 dhd_os_ioctl_resp_wake(dhd_pub_t *pub)
2713 {
2714         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
2715
2716         if (waitqueue_active(&dhd->ioctl_resp_wait)) {
2717                 wake_up_interruptible(&dhd->ioctl_resp_wait);
2718         }
2719
2720         return 0;
2721 }
2722
2723 void
2724 dhd_os_wd_timer(void *bus, uint wdtick)
2725 {
2726         dhd_pub_t *pub = bus;
2727         dhd_info_t *dhd = (dhd_info_t *)pub->info;
2728         unsigned long flags;
2729         int del_timer_flag = FALSE;
2730
2731         flags = dhd_os_spin_lock(pub);
2732
2733         /* don't start the wd until fw is loaded */
2734         if (pub->busstate != DHD_BUS_DOWN) {
2735                 if (wdtick) {
2736                         dhd_watchdog_ms = (uint)wdtick;
2737                         dhd->wd_timer_valid = TRUE;
2738                         /* Re arm the timer, at last watchdog period */
2739                         mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
2740                 } else if (dhd->wd_timer_valid == TRUE) {
2741                         /* Totally stop the timer */
2742                         dhd->wd_timer_valid = FALSE;
2743                         del_timer_flag = TRUE;
2744                 }
2745         }
2746         dhd_os_spin_unlock(pub, flags);
2747         if (del_timer_flag) {
2748                 del_timer_sync(&dhd->timer);
2749         }
2750 }
2751
2752 void *
2753 dhd_os_open_image(char *filename)
2754 {
2755         struct file *fp;
2756
2757         fp = filp_open(filename, O_RDONLY, 0);
2758         /*
2759          * 2.6.11 (FC4) supports filp_open() but later revs don't?
2760          * Alternative:
2761          * fp = open_namei(AT_FDCWD, filename, O_RD, 0);
2762          * ???
2763          */
2764          if (IS_ERR(fp))
2765                  fp = NULL;
2766
2767          return fp;
2768 }
2769
2770 int
2771 dhd_os_get_image_block(char *buf, int len, void *image)
2772 {
2773         struct file *fp = (struct file *)image;
2774         int rdlen;
2775
2776         if (!image)
2777                 return 0;
2778
2779         rdlen = kernel_read(fp, fp->f_pos, buf, len);
2780         if (rdlen > 0)
2781                 fp->f_pos += rdlen;
2782
2783         return rdlen;
2784 }
2785
2786 void
2787 dhd_os_close_image(void *image)
2788 {
2789         if (image)
2790                 filp_close((struct file *)image, NULL);
2791 }
2792
2793
2794 void
2795 dhd_os_sdlock(dhd_pub_t *pub)
2796 {
2797         dhd_info_t *dhd;
2798
2799         dhd = (dhd_info_t *)(pub->info);
2800
2801         if (dhd->threads_only)
2802                 down(&dhd->sdsem);
2803         else
2804                 spin_lock_bh(&dhd->sdlock);
2805 }
2806
2807 void
2808 dhd_os_sdunlock(dhd_pub_t *pub)
2809 {
2810         dhd_info_t *dhd;
2811
2812         dhd = (dhd_info_t *)(pub->info);
2813
2814         if (dhd->threads_only)
2815                 up(&dhd->sdsem);
2816         else
2817                 spin_unlock_bh(&dhd->sdlock);
2818 }
2819
2820 void
2821 dhd_os_sdlock_txq(dhd_pub_t *pub)
2822 {
2823         dhd_info_t *dhd;
2824
2825         dhd = (dhd_info_t *)(pub->info);
2826         spin_lock_bh(&dhd->txqlock);
2827 }
2828
2829 void
2830 dhd_os_sdunlock_txq(dhd_pub_t *pub)
2831 {
2832         dhd_info_t *dhd;
2833
2834         dhd = (dhd_info_t *)(pub->info);
2835         spin_unlock_bh(&dhd->txqlock);
2836 }
2837 void
2838 dhd_os_sdlock_rxq(dhd_pub_t *pub)
2839 {
2840 }
2841 void
2842 dhd_os_sdunlock_rxq(dhd_pub_t *pub)
2843 {
2844 }
2845
2846 void
2847 dhd_os_sdtxlock(dhd_pub_t *pub)
2848 {
2849         dhd_os_sdlock(pub);
2850 }
2851
2852 void
2853 dhd_os_sdtxunlock(dhd_pub_t *pub)
2854 {
2855         dhd_os_sdunlock(pub);
2856 }
2857
2858 #ifdef DHD_USE_STATIC_BUF
2859 void * dhd_os_prealloc(int section, unsigned long size)
2860 {
2861 #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
2862         void *alloc_ptr = NULL;
2863         if (wifi_control_data && wifi_control_data->mem_prealloc)
2864         {
2865                 alloc_ptr = wifi_control_data->mem_prealloc(section, size);
2866                 if (alloc_ptr)
2867                 {
2868                         DHD_INFO(("success alloc section %d\n", section));
2869                         bzero(alloc_ptr, size);
2870                         return alloc_ptr;
2871                 }
2872         }
2873
2874         DHD_ERROR(("can't alloc section %d\n", section));
2875         return 0;
2876 #else
2877 return MALLOC(0, size);
2878 #endif /* #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
2879 }
2880 #endif /* DHD_USE_STATIC_BUF */
2881 #if defined(CONFIG_WIRELESS_EXT)
2882 struct iw_statistics *
2883 dhd_get_wireless_stats(struct net_device *dev)
2884 {
2885         int res = 0;
2886         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2887
2888         res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats);
2889
2890         if (res == 0)
2891                 return &dhd->iw.wstats;
2892         else
2893                 return NULL;
2894 }
2895 #endif /* defined(CONFIG_WIRELESS_EXT) */
2896
2897 static int
2898 dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
2899         wl_event_msg_t *event, void **data)
2900 {
2901         int bcmerror = 0;
2902
2903         ASSERT(dhd != NULL);
2904
2905         bcmerror = wl_host_event(dhd, ifidx, pktdata, event, data);
2906         if (bcmerror != BCME_OK)
2907                 return (bcmerror);
2908
2909 #if defined(CONFIG_WIRELESS_EXT)
2910         ASSERT(dhd->iflist[*ifidx] != NULL);
2911
2912         if (ntoh32(event->event_type) == WLC_E_IF) {
2913                 DHD_INFO(("<0> interface:%d OP:%d don't pass to wext,"
2914                         "net_device might not be created yet\n",
2915                                 *ifidx, ntoh32(event->event_type)));
2916                 return bcmerror;
2917         }
2918
2919         ASSERT(dhd->iflist[*ifidx]->net != NULL);
2920
2921         if (dhd->iflist[*ifidx]->net)
2922                 wl_iw_event(dhd->iflist[*ifidx]->net, event, *data);
2923 #endif /* defined(CONFIG_WIRELESS_EXT) */
2924
2925         return (bcmerror);
2926 }
2927
2928 /* send up locally generated event */
2929 void
2930 dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data)
2931 {
2932         switch (ntoh32(event->event_type)) {
2933         default:
2934                 break;
2935         }
2936 }
2937
2938 void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar)
2939 {
2940 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
2941         struct dhd_info *dhdinfo =  dhd->info;
2942         dhd_os_sdunlock(dhd);
2943         wait_event_interruptible_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), HZ * 2);
2944         dhd_os_sdlock(dhd);
2945 #endif
2946         return;
2947 }
2948
2949 void dhd_wait_event_wakeup(dhd_pub_t *dhd)
2950 {
2951 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
2952         struct dhd_info *dhdinfo =  dhd->info;
2953         if (waitqueue_active(&dhdinfo->ctrl_wait))
2954                 wake_up_interruptible(&dhdinfo->ctrl_wait);
2955 #endif
2956         return;
2957 }
2958
2959 int
2960 dhd_dev_reset(struct net_device *dev, uint8 flag)
2961 {
2962         int ret;
2963
2964         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2965
2966         ret = dhd_bus_devreset(&dhd->pub, flag);
2967         if (ret) {
2968                 DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret));
2969                 return ret;
2970         }
2971         DHD_ERROR(("%s: WLAN %s DONE\n", __FUNCTION__, flag ? "OFF" : "ON"));
2972
2973         return ret;
2974 }
2975
2976 int net_os_set_suspend_disable(struct net_device *dev, int val)
2977 {
2978         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2979         int ret = 0;
2980
2981         if (dhd) {
2982                 ret = dhd->pub.suspend_disable_flag;
2983                 dhd->pub.suspend_disable_flag = val;
2984         }
2985         return ret;
2986 }
2987
2988 int net_os_set_suspend(struct net_device *dev, int val)
2989 {
2990         int ret = 0;
2991 #if defined(CONFIG_HAS_EARLYSUSPEND)
2992         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
2993
2994         if (dhd) {
2995                 dhd_os_proto_block(&dhd->pub);
2996                 ret = dhd_set_suspend(val, &dhd->pub);
2997                 dhd_os_proto_unblock(&dhd->pub);
2998         }
2999 #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
3000         return ret;
3001 }
3002
3003 int net_os_set_dtim_skip(struct net_device *dev, int val)
3004 {
3005         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3006
3007         if (dhd)
3008                 dhd->pub.dtim_skip = val;
3009
3010         return 0;
3011 }
3012
3013 int net_os_set_packet_filter(struct net_device *dev, int val)
3014 {
3015         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3016         int ret = 0;
3017
3018         /* Packet filtering is set only if we still in early-suspend and
3019          * we need either to turn it ON or turn it OFF
3020          * We can always turn it OFF in case of early-suspend, but we turn it
3021          * back ON only if suspend_disable_flag was not set
3022         */
3023         if (dhd && dhd->pub.up) {
3024                 dhd_os_proto_block(&dhd->pub);
3025                 if (dhd->pub.in_suspend) {
3026                         if (!val || (val && !dhd->pub.suspend_disable_flag))
3027                                 dhd_set_packet_filter(val, &dhd->pub);
3028                 }
3029                 dhd_os_proto_unblock(&dhd->pub);
3030         }
3031         return ret;
3032 }
3033
3034
3035 void
3036 dhd_dev_init_ioctl(struct net_device *dev)
3037 {
3038         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3039
3040         dhd_preinit_ioctls(&dhd->pub);
3041 }
3042
3043 #ifdef PNO_SUPPORT
3044 /* Linux wrapper to call common dhd_pno_clean */
3045 int
3046 dhd_dev_pno_reset(struct net_device *dev)
3047 {
3048         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3049
3050         return (dhd_pno_clean(&dhd->pub));
3051 }
3052
3053
3054 /* Linux wrapper to call common dhd_pno_enable */
3055 int
3056 dhd_dev_pno_enable(struct net_device *dev,  int pfn_enabled)
3057 {
3058         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3059
3060         return (dhd_pno_enable(&dhd->pub, pfn_enabled));
3061 }
3062
3063
3064 /* Linux wrapper to call common dhd_pno_set */
3065 int
3066 dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ushort  scan_fr)
3067 {
3068         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3069
3070         return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr));
3071 }
3072
3073 /* Linux wrapper to get  pno status */
3074 int
3075 dhd_dev_get_pno_status(struct net_device *dev)
3076 {
3077         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3078
3079         return (dhd_pno_get_status(&dhd->pub));
3080 }
3081
3082 #endif /* PNO_SUPPORT */
3083
3084 int net_os_send_hang_message(struct net_device *dev)
3085 {
3086         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3087         int ret = 0;
3088
3089         if (dhd) {
3090                 if (!dhd->hang_was_sent) {
3091                         dhd->hang_was_sent = 1;
3092                         ret = wl_iw_send_priv_event(dev, "HANG");
3093                 }
3094         }
3095         return ret;
3096 }
3097
3098 void dhd_bus_country_set(struct net_device *dev, char *country_code)
3099 {
3100         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3101
3102         if (dhd && dhd->pub.up)
3103                 strncpy(dhd->pub.country_code, country_code, WLC_CNTRY_BUF_SZ);
3104 }
3105
3106 char *dhd_bus_country_get(struct net_device *dev)
3107 {
3108         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3109
3110         if (dhd && (dhd->pub.country_code[0] != 0))
3111                 return dhd->pub.country_code;
3112         return NULL;
3113 }
3114
3115 void dhd_os_start_lock(dhd_pub_t *pub)
3116 {
3117 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
3118         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3119
3120         if (dhd)
3121                 mutex_lock(&dhd->wl_start_lock);
3122 #endif
3123 }
3124
3125 void dhd_os_start_unlock(dhd_pub_t *pub)
3126 {
3127 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
3128         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3129
3130         if (dhd)
3131                 mutex_unlock(&dhd->wl_start_lock);
3132 #endif
3133 }
3134
3135 static int
3136 dhd_get_pend_8021x_cnt(dhd_info_t *dhd)
3137 {
3138         return (atomic_read(&dhd->pend_8021x_cnt));
3139 }
3140
3141 #define MAX_WAIT_FOR_8021X_TX   10
3142
3143 int
3144 dhd_wait_pend8021x(struct net_device *dev)
3145 {
3146         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3147         int timeout = 10 * HZ / 1000;
3148         int ntimes = MAX_WAIT_FOR_8021X_TX;
3149         int pend = dhd_get_pend_8021x_cnt(dhd);
3150
3151         while (ntimes && pend) {
3152                 if (pend) {
3153                         set_current_state(TASK_INTERRUPTIBLE);
3154                         schedule_timeout(timeout);
3155                         set_current_state(TASK_RUNNING);
3156                         ntimes--;
3157                 }
3158                 pend = dhd_get_pend_8021x_cnt(dhd);
3159         }
3160         return pend;
3161 }
3162
3163 #ifdef DHD_DEBUG
3164 int
3165 write_to_file(dhd_pub_t *dhd, uint8 *buf, int size)
3166 {
3167         int ret = 0;
3168         struct file *fp;
3169         mm_segment_t old_fs;
3170         loff_t pos = 0;
3171
3172         /* change to KERNEL_DS address limit */
3173         old_fs = get_fs();
3174         set_fs(KERNEL_DS);
3175
3176         /* open file to write */
3177         fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640);
3178         if (!fp) {
3179                 printf("%s: open file error\n", __FUNCTION__);
3180                 ret = -1;
3181                 goto exit;
3182         }
3183
3184         /* Write buf to file */
3185         fp->f_op->write(fp, buf, size, &pos);
3186
3187 exit:
3188         /* free buf before return */
3189         MFREE(dhd->osh, buf, size);
3190         /* close file before return */
3191         if (fp)
3192                 filp_close(fp, current->files);
3193         /* restore previous address limit */
3194         set_fs(old_fs);
3195
3196         return ret;
3197 }
3198 #endif /* DHD_DEBUG */
3199
3200 int dhd_os_wake_lock_timeout(dhd_pub_t *pub)
3201 {
3202         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3203         unsigned long flags;
3204         int ret = 0;
3205
3206         if (dhd) {
3207                 spin_lock_irqsave(&dhd->wl_lock, flags);
3208                 ret = dhd->wl_packet;
3209 #ifdef CONFIG_HAS_WAKELOCK
3210                 if (dhd->wl_packet)
3211                         wake_lock_timeout(&dhd->wl_rxwake, HZ);
3212 #endif
3213                 dhd->wl_packet = 0;
3214                 spin_unlock_irqrestore(&dhd->wl_lock, flags);
3215         }
3216         /* printk("%s: %d\n", __FUNCTION__, ret); */
3217         return ret;
3218 }
3219
3220 int net_os_wake_lock_timeout(struct net_device *dev)
3221 {
3222         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3223         int ret = 0;
3224
3225         if (dhd)
3226                 ret = dhd_os_wake_lock_timeout(&dhd->pub);
3227         return ret;
3228 }
3229
3230 int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub)
3231 {
3232         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3233         unsigned long flags;
3234
3235         if (dhd) {
3236                 spin_lock_irqsave(&dhd->wl_lock, flags);
3237                 dhd->wl_packet = 1;
3238                 spin_unlock_irqrestore(&dhd->wl_lock, flags);
3239         }
3240         /* printk("%s\n",__func__); */
3241         return 0;
3242 }
3243
3244 int net_os_wake_lock_timeout_enable(struct net_device *dev)
3245 {
3246         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3247         int ret = 0;
3248
3249         if (dhd)
3250                 ret = dhd_os_wake_lock_timeout_enable(&dhd->pub);
3251         return ret;
3252 }
3253
3254 int dhd_os_wake_lock(dhd_pub_t *pub)
3255 {
3256         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3257         unsigned long flags;
3258         int ret = 0;
3259
3260         if (dhd) {
3261                 spin_lock_irqsave(&dhd->wl_lock, flags);
3262 #ifdef CONFIG_HAS_WAKELOCK
3263                 if (!dhd->wl_count)
3264                         wake_lock(&dhd->wl_wifi);
3265 #endif
3266                 dhd->wl_count++;
3267                 ret = dhd->wl_count;
3268                 spin_unlock_irqrestore(&dhd->wl_lock, flags);
3269         }
3270         /* printk("%s: %d\n", __FUNCTION__, ret); */
3271         return ret;
3272 }
3273
3274 int net_os_wake_lock(struct net_device *dev)
3275 {
3276         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3277         int ret = 0;
3278
3279         if (dhd)
3280                 ret = dhd_os_wake_lock(&dhd->pub);
3281         return ret;
3282 }
3283
3284 int dhd_os_wake_unlock(dhd_pub_t *pub)
3285 {
3286         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3287         unsigned long flags;
3288         int ret = 0;
3289
3290         dhd_os_wake_lock_timeout(pub);
3291         if (dhd) {
3292                 spin_lock_irqsave(&dhd->wl_lock, flags);
3293                 if (dhd->wl_count) {
3294                         dhd->wl_count--;
3295 #ifdef CONFIG_HAS_WAKELOCK
3296                         if (!dhd->wl_count)
3297                                 wake_unlock(&dhd->wl_wifi);
3298 #endif
3299                         ret = dhd->wl_count;
3300                 }
3301                 spin_unlock_irqrestore(&dhd->wl_lock, flags);
3302         }
3303         /* printk("%s: %d\n", __FUNCTION__, ret); */
3304         return ret;
3305 }
3306
3307 int net_os_wake_unlock(struct net_device *dev)
3308 {
3309         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3310         int ret = 0;
3311
3312         if (dhd)
3313                 ret = dhd_os_wake_unlock(&dhd->pub);
3314         return ret;
3315 }
3316
3317 unsigned long dhd_os_spin_lock(dhd_pub_t *pub)
3318 {
3319         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3320         unsigned long flags = 0;
3321
3322         if (dhd)
3323                 spin_lock_irqsave(&dhd->dhd_lock, flags);
3324
3325         return flags;
3326 }
3327
3328 void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags)
3329 {
3330         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3331
3332         if (dhd)
3333                 spin_unlock_irqrestore(&dhd->dhd_lock, flags);
3334 }