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