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