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