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