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