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