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