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