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