/drivers/net/wireless/bcmdhd/dhd_linux.c
C | 5041 lines | 3804 code | 837 blank | 400 comment | 685 complexity | fe4e4f51f9d74a59db8f79ca55e59935 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
Large files files are truncated, but you can click here to view the full file
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-2011, 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 287541 2011-10-03 23:48:17Z $
26 */
27
28#include <typedefs.h>
29#include <linuxver.h>
30#include <osl.h>
31
32#include <linux/init.h>
33#include <linux/kernel.h>
34#include <linux/slab.h>
35#include <linux/skbuff.h>
36#include <linux/netdevice.h>
37#include <linux/inetdevice.h>
38#include <linux/rtnetlink.h>
39#include <linux/etherdevice.h>
40#include <linux/random.h>
41#include <linux/spinlock.h>
42#include <linux/ethtool.h>
43#include <linux/fcntl.h>
44#include <linux/fs.h>
45
46#include <asm/uaccess.h>
47#include <asm/unaligned.h>
48
49#include <epivers.h>
50#include <bcmutils.h>
51#include <bcmendian.h>
52
53#include <proto/ethernet.h>
54#include <dngl_stats.h>
55#include <dhd.h>
56#include <dhd_bus.h>
57#include <dhd_proto.h>
58#include <dhd_dbg.h>
59#ifdef CONFIG_HAS_WAKELOCK
60#include <linux/wakelock.h>
61#endif
62#ifdef WL_CFG80211
63#include <wl_cfg80211.h>
64#endif
65
66#include <proto/802.11_bta.h>
67#include <proto/bt_amp_hci.h>
68#include <dhd_bta.h>
69
70#ifdef WLMEDIA_HTSF
71#include <linux/time.h>
72#include <htsf.h>
73
74#define HTSF_MINLEN 200 /* min. packet length to timestamp */
75#define HTSF_BUS_DELAY 150 /* assume a fix propagation in us */
76#define TSMAX 1000 /* max no. of timing record kept */
77#define NUMBIN 34
78
79static uint32 tsidx = 0;
80static uint32 htsf_seqnum = 0;
81uint32 tsfsync;
82struct timeval tsync;
83static uint32 tsport = 5010;
84
85typedef struct histo_ {
86 uint32 bin[NUMBIN];
87} histo_t;
88
89#if !ISPOWEROF2(DHD_SDALIGN)
90#error DHD_SDALIGN is not a power of 2!
91#endif
92
93static histo_t vi_d1, vi_d2, vi_d3, vi_d4;
94#endif /* WLMEDIA_HTSF */
95
96#if defined(SOFTAP)
97extern bool ap_cfg_running;
98extern bool ap_fw_loaded;
99#endif
100
101/* enable HOSTIP cache update from the host side when an eth0:N is up */
102#define AOE_IP_ALIAS_SUPPORT 1
103
104#ifdef PROP_TXSTATUS
105#include <wlfc_proto.h>
106#include <dhd_wlfc.h>
107#endif
108
109#include <wl_android.h>
110
111#ifdef ARP_OFFLOAD_SUPPORT
112static int dhd_device_event(struct notifier_block *this,
113 unsigned long event,
114 void *ptr);
115
116static struct notifier_block dhd_notifier = {
117 .notifier_call = dhd_device_event
118};
119#endif /* ARP_OFFLOAD_SUPPORT */
120
121#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
122#include <linux/suspend.h>
123volatile bool dhd_mmc_suspend = FALSE;
124DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
125#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
126
127#if defined(OOB_INTR_ONLY)
128extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable);
129#endif /* defined(OOB_INTR_ONLY) */
130#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
131MODULE_LICENSE("GPL v2");
132#endif /* LinuxVer */
133
134#include <dhd_bus.h>
135
136#ifndef PROP_TXSTATUS
137#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen)
138#else
139#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128)
140#endif
141
142#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
143const char *
144print_tainted()
145{
146 return "";
147}
148#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */
149
150/* Linux wireless extension support */
151#if defined(CONFIG_WIRELESS_EXT)
152#include <wl_iw.h>
153extern wl_iw_extra_params_t g_wl_iw_params;
154#endif /* defined(CONFIG_WIRELESS_EXT) */
155
156#if defined(CONFIG_HAS_EARLYSUSPEND)
157#include <linux/earlysuspend.h>
158extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
159extern int dhd_get_dtim_skip(dhd_pub_t *dhd);
160#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
161
162#ifdef PKT_FILTER_SUPPORT
163extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
164extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
165#endif
166
167/* Interface control information */
168typedef struct dhd_if {
169 struct dhd_info *info; /* back pointer to dhd_info */
170 /* OS/stack specifics */
171 struct net_device *net;
172 struct net_device_stats stats;
173 int idx; /* iface idx in dongle */
174 int state; /* interface state */
175 uint subunit; /* subunit */
176 uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */
177 bool attached; /* Delayed attachment when unset */
178 bool txflowcontrol; /* Per interface flow control indicator */
179 char name[IFNAMSIZ+1]; /* linux interface name */
180 uint8 bssidx; /* bsscfg index for the interface */
181 bool set_multicast;
182} dhd_if_t;
183
184#ifdef WLMEDIA_HTSF
185typedef struct {
186 uint32 low;
187 uint32 high;
188} tsf_t;
189
190typedef struct {
191 uint32 last_cycle;
192 uint32 last_sec;
193 uint32 last_tsf;
194 uint32 coef; /* scaling factor */
195 uint32 coefdec1; /* first decimal */
196 uint32 coefdec2; /* second decimal */
197} htsf_t;
198
199typedef struct {
200 uint32 t1;
201 uint32 t2;
202 uint32 t3;
203 uint32 t4;
204} tstamp_t;
205
206static tstamp_t ts[TSMAX];
207static tstamp_t maxdelayts;
208static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0;
209
210#endif /* WLMEDIA_HTSF */
211
212/* Local private structure (extension of pub) */
213typedef struct dhd_info {
214#if defined(CONFIG_WIRELESS_EXT)
215 wl_iw_t iw; /* wireless extensions state (must be first) */
216#endif /* defined(CONFIG_WIRELESS_EXT) */
217
218 dhd_pub_t pub;
219
220 /* For supporting multiple interfaces */
221 dhd_if_t *iflist[DHD_MAX_IFS];
222
223 struct semaphore proto_sem;
224#ifdef PROP_TXSTATUS
225 spinlock_t wlfc_spinlock;
226#endif /* PROP_TXSTATUS */
227#ifdef WLMEDIA_HTSF
228 htsf_t htsf;
229#endif
230 wait_queue_head_t ioctl_resp_wait;
231 struct timer_list timer;
232 bool wd_timer_valid;
233 struct tasklet_struct tasklet;
234 spinlock_t sdlock;
235 spinlock_t txqlock;
236 spinlock_t dhd_lock;
237#ifdef DHDTHREAD
238 /* Thread based operation */
239 bool threads_only;
240 struct semaphore sdsem;
241
242 tsk_ctl_t thr_dpc_ctl;
243 tsk_ctl_t thr_wdt_ctl;
244
245#else
246 bool dhd_tasklet_create;
247#endif /* DHDTHREAD */
248 tsk_ctl_t thr_sysioc_ctl;
249
250 /* Wakelocks */
251#if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
252 struct wake_lock wl_wifi; /* Wifi wakelock */
253 struct wake_lock wl_rxwake; /* Wifi rx wakelock */
254#endif
255
256#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
257 /* net_device interface lock, prevent race conditions among net_dev interface
258 * calls and wifi_on or wifi_off
259 */
260 struct mutex dhd_net_if_mutex;
261#endif
262 spinlock_t wakelock_spinlock;
263 int wakelock_counter;
264 int wakelock_timeout_enable;
265
266 /* Thread to issue ioctl for multicast */
267 bool set_macaddress;
268 struct ether_addr macvalue;
269 wait_queue_head_t ctrl_wait;
270 atomic_t pend_8021x_cnt;
271 dhd_attach_states_t dhd_state;
272
273#ifdef CONFIG_HAS_EARLYSUSPEND
274 struct early_suspend early_suspend;
275#endif /* CONFIG_HAS_EARLYSUSPEND */
276} dhd_info_t;
277
278/* Definitions to provide path to the firmware and nvram
279 * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt"
280 */
281char firmware_path[MOD_PARAM_PATHLEN];
282char nvram_path[MOD_PARAM_PATHLEN];
283
284extern int wl_control_wl_start(struct net_device *dev);
285extern int net_os_send_hang_message(struct net_device *dev);
286#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
287struct semaphore dhd_registration_sem;
288#define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */
289#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
290
291/* Spawn a thread for system ioctls (set mac, set mcast) */
292uint dhd_sysioc = TRUE;
293module_param(dhd_sysioc, uint, 0);
294
295/* Error bits */
296module_param(dhd_msg_level, int, 0);
297
298/* load firmware and/or nvram values from the filesystem */
299module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660);
300module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
301
302/* Watchdog interval */
303uint dhd_watchdog_ms = 10;
304module_param(dhd_watchdog_ms, uint, 0);
305
306#if defined(DHD_DEBUG)
307/* Console poll interval */
308uint dhd_console_ms = 0;
309module_param(dhd_console_ms, uint, 0);
310#endif /* defined(DHD_DEBUG) */
311
312/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */
313uint dhd_arp_mode = 0xb;
314module_param(dhd_arp_mode, uint, 0);
315
316/* ARP offload enable */
317uint dhd_arp_enable = TRUE;
318module_param(dhd_arp_enable, uint, 0);
319
320/* Global Pkt filter enable control */
321uint dhd_pkt_filter_enable = TRUE;
322module_param(dhd_pkt_filter_enable, uint, 0);
323
324/* Pkt filter init setup */
325uint dhd_pkt_filter_init = 0;
326module_param(dhd_pkt_filter_init, uint, 0);
327
328/* Pkt filter mode control */
329uint dhd_master_mode = TRUE;
330module_param(dhd_master_mode, uint, 1);
331
332#ifdef DHDTHREAD
333/* Watchdog thread priority, -1 to use kernel timer */
334int dhd_watchdog_prio = 97;
335module_param(dhd_watchdog_prio, int, 0);
336
337/* DPC thread priority, -1 to use tasklet */
338int dhd_dpc_prio = 98;
339module_param(dhd_dpc_prio, int, 0);
340
341/* DPC thread priority, -1 to use tasklet */
342extern int dhd_dongle_memsize;
343module_param(dhd_dongle_memsize, int, 0);
344#endif /* DHDTHREAD */
345/* Control fw roaming */
346uint dhd_roam_disable = 0;
347
348/* Control radio state */
349uint dhd_radio_up = 1;
350
351/* Network inteface name */
352char iface_name[IFNAMSIZ];
353module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
354
355#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
356#define DAEMONIZE(a) daemonize(a); \
357 allow_signal(SIGKILL); \
358 allow_signal(SIGTERM);
359#else /* Linux 2.4 (w/o preemption patch) */
360#define RAISE_RX_SOFTIRQ() \
361 cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
362#define DAEMONIZE(a) daemonize(); \
363 do { if (a) \
364 strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
365 } while (0);
366#endif /* LINUX_VERSION_CODE */
367
368#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
369#define BLOCKABLE() (!in_atomic())
370#else
371#define BLOCKABLE() (!in_interrupt())
372#endif
373
374/* The following are specific to the SDIO dongle */
375
376/* IOCTL response timeout */
377int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
378
379/* Idle timeout for backplane clock */
380int dhd_idletime = DHD_IDLETIME_TICKS;
381module_param(dhd_idletime, int, 0);
382
383/* Use polling */
384uint dhd_poll = FALSE;
385module_param(dhd_poll, uint, 0);
386
387/* Use interrupts */
388uint dhd_intr = TRUE;
389module_param(dhd_intr, uint, 0);
390
391/* SDIO Drive Strength (in milliamps) */
392uint dhd_sdiod_drive_strength = 6;
393module_param(dhd_sdiod_drive_strength, uint, 0);
394
395/* Tx/Rx bounds */
396extern uint dhd_txbound;
397extern uint dhd_rxbound;
398module_param(dhd_txbound, uint, 0);
399module_param(dhd_rxbound, uint, 0);
400
401/* Deferred transmits */
402extern uint dhd_deferred_tx;
403module_param(dhd_deferred_tx, uint, 0);
404
405#ifdef BCMDBGFS
406extern void dhd_dbg_init(dhd_pub_t *dhdp);
407extern void dhd_dbg_remove(void);
408#endif /* BCMDBGFS */
409
410
411
412#ifdef SDTEST
413/* Echo packet generator (pkts/s) */
414uint dhd_pktgen = 0;
415module_param(dhd_pktgen, uint, 0);
416
417/* Echo packet len (0 => sawtooth, max 2040) */
418uint dhd_pktgen_len = 0;
419module_param(dhd_pktgen_len, uint, 0);
420#endif /* SDTEST */
421
422/* Version string to report */
423#ifdef DHD_DEBUG
424#ifndef SRCBASE
425#define SRCBASE "drivers/net/wireless/bcmdhd"
426#endif
427#define DHD_COMPILED "\nCompiled in " SRCBASE
428#else
429#define DHD_COMPILED
430#endif /* DHD_DEBUG */
431
432static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR
433#ifdef DHD_DEBUG
434"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__
435#endif
436;
437static void dhd_net_if_lock_local(dhd_info_t *dhd);
438static void dhd_net_if_unlock_local(dhd_info_t *dhd);
439
440#ifdef WLMEDIA_HTSF
441void htsf_update(dhd_info_t *dhd, void *data);
442tsf_t prev_tsf, cur_tsf;
443
444uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx);
445static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx);
446static void dhd_dump_latency(void);
447static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf);
448static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf);
449static void dhd_dump_htsfhisto(histo_t *his, char *s);
450#endif /* WLMEDIA_HTSF */
451
452/* Monitor interface */
453int dhd_monitor_init(void *dhd_pub);
454int dhd_monitor_uninit(void);
455
456
457#if defined(CONFIG_WIRELESS_EXT)
458struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
459#endif /* defined(CONFIG_WIRELESS_EXT) */
460
461static void dhd_dpc(ulong data);
462/* forward decl */
463extern int dhd_wait_pend8021x(struct net_device *dev);
464
465#ifdef TOE
466#ifndef BDC
467#error TOE requires BDC
468#endif /* !BDC */
469static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol);
470static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol);
471#endif /* TOE */
472
473static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
474 wl_event_msg_t *event_ptr, void **data_ptr);
475
476#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
477static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored)
478{
479 int ret = NOTIFY_DONE;
480
481 switch (action) {
482 case PM_HIBERNATION_PREPARE:
483 case PM_SUSPEND_PREPARE:
484 dhd_mmc_suspend = TRUE;
485 ret = NOTIFY_OK;
486 break;
487 case PM_POST_HIBERNATION:
488 case PM_POST_SUSPEND:
489 dhd_mmc_suspend = FALSE;
490 ret = NOTIFY_OK;
491 break;
492 }
493 smp_mb();
494 return ret;
495}
496
497static struct notifier_block dhd_sleep_pm_notifier = {
498 .notifier_call = dhd_sleep_pm_callback,
499 .priority = 0
500};
501extern int register_pm_notifier(struct notifier_block *nb);
502extern int unregister_pm_notifier(struct notifier_block *nb);
503#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
504
505static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
506{
507#ifdef PKT_FILTER_SUPPORT
508 DHD_TRACE(("%s: %d\n", __FUNCTION__, value));
509 /* 1 - Enable packet filter, only allow unicast packet to send up */
510 /* 0 - Disable packet filter */
511 if (dhd_pkt_filter_enable) {
512 int i;
513
514 for (i = 0; i < dhd->pktfilter_count; i++) {
515 dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
516 dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
517 value, dhd_master_mode);
518 }
519 }
520#endif
521}
522
523#if defined(CONFIG_HAS_EARLYSUSPEND)
524static int dhd_set_suspend(int value, dhd_pub_t *dhd)
525{
526 int power_mode = PM_FAST;
527 /* wl_pkt_filter_enable_t enable_parm; */
528 char iovbuf[32];
529 int bcn_li_dtim = 3;
530 uint roamvar = 1;
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_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__));
540
541 dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
542 sizeof(power_mode), TRUE, 0);
543
544 /* Enable packet filter, only allow unicast packet to send up */
545 dhd_set_packet_filter(1, dhd);
546
547 /* If DTIM skip is set up as default, force it to wake
548 * each third DTIM for better power savings. Note that
549 * one side effect is a chance to miss BC/MC packet.
550 */
551 bcn_li_dtim = dhd_get_dtim_skip(dhd);
552 bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
553 4, iovbuf, sizeof(iovbuf));
554 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
555
556 /* Disable firmware roaming during suspend */
557 bcm_mkiovar("roam_off", (char *)&roamvar, 4,
558 iovbuf, sizeof(iovbuf));
559 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
560 } else {
561
562 /* Kernel resumed */
563 DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__));
564
565 power_mode = PM_FAST;
566 dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
567 sizeof(power_mode), TRUE, 0);
568
569 /* disable pkt filter */
570 dhd_set_packet_filter(0, dhd);
571
572 /* restore pre-suspend setting for dtim_skip */
573 bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
574 4, iovbuf, sizeof(iovbuf));
575
576 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
577 roamvar = dhd_roam_disable;
578 bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf,
579 sizeof(iovbuf));
580 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
581 }
582 }
583
584 return 0;
585}
586
587static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
588{
589 dhd_pub_t *dhdp = &dhd->pub;
590
591 DHD_OS_WAKE_LOCK(dhdp);
592 /* Set flag when early suspend was called */
593 dhdp->in_suspend = val;
594 if (!dhdp->suspend_disable_flag)
595 dhd_set_suspend(val, dhdp);
596 DHD_OS_WAKE_UNLOCK(dhdp);
597}
598
599static void dhd_early_suspend(struct early_suspend *h)
600{
601 struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
602
603 DHD_TRACE(("%s: enter\n", __FUNCTION__));
604
605 if (dhd)
606 dhd_suspend_resume_helper(dhd, 1);
607}
608
609static void dhd_late_resume(struct early_suspend *h)
610{
611 struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
612
613 DHD_TRACE(("%s: enter\n", __FUNCTION__));
614
615 if (dhd)
616 dhd_suspend_resume_helper(dhd, 0);
617}
618#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
619
620/*
621 * Generalized timeout mechanism. Uses spin sleep with exponential back-off until
622 * the sleep time reaches one jiffy, then switches over to task delay. Usage:
623 *
624 * dhd_timeout_start(&tmo, usec);
625 * while (!dhd_timeout_expired(&tmo))
626 * if (poll_something())
627 * break;
628 * if (dhd_timeout_expired(&tmo))
629 * fatal();
630 */
631
632void
633dhd_timeout_start(dhd_timeout_t *tmo, uint usec)
634{
635 tmo->limit = usec;
636 tmo->increment = 0;
637 tmo->elapsed = 0;
638 tmo->tick = 1000000 / HZ;
639}
640
641int
642dhd_timeout_expired(dhd_timeout_t *tmo)
643{
644 /* Does nothing the first call */
645 if (tmo->increment == 0) {
646 tmo->increment = 1;
647 return 0;
648 }
649
650 if (tmo->elapsed >= tmo->limit)
651 return 1;
652
653 /* Add the delay that's about to take place */
654 tmo->elapsed += tmo->increment;
655
656 if (tmo->increment < tmo->tick) {
657 OSL_DELAY(tmo->increment);
658 tmo->increment *= 2;
659 if (tmo->increment > tmo->tick)
660 tmo->increment = tmo->tick;
661 } else {
662 wait_queue_head_t delay_wait;
663 DECLARE_WAITQUEUE(wait, current);
664 int pending;
665 init_waitqueue_head(&delay_wait);
666 add_wait_queue(&delay_wait, &wait);
667 set_current_state(TASK_INTERRUPTIBLE);
668 schedule_timeout(1);
669 pending = signal_pending(current);
670 remove_wait_queue(&delay_wait, &wait);
671 set_current_state(TASK_RUNNING);
672 if (pending)
673 return 1; /* Interrupted */
674 }
675
676 return 0;
677}
678
679int
680dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
681{
682 int i = 0;
683
684 ASSERT(dhd);
685 while (i < DHD_MAX_IFS) {
686 if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
687 return i;
688 i++;
689 }
690
691 return DHD_BAD_IF;
692}
693
694struct net_device * dhd_idx2net(struct dhd_pub *dhd_pub, int ifidx)
695{
696 struct dhd_info *dhd_info;
697
698 if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS)
699 return NULL;
700 dhd_info = dhd_pub->info;
701 if (dhd_info && dhd_info->iflist[ifidx])
702 return dhd_info->iflist[ifidx]->net;
703 return NULL;
704}
705
706int
707dhd_ifname2idx(dhd_info_t *dhd, char *name)
708{
709 int i = DHD_MAX_IFS;
710
711 ASSERT(dhd);
712
713 if (name == NULL || *name == '\0')
714 return 0;
715
716 while (--i > 0)
717 if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
718 break;
719
720 DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name));
721
722 return i; /* default - the primary interface */
723}
724
725char *
726dhd_ifname(dhd_pub_t *dhdp, int ifidx)
727{
728 dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
729
730 ASSERT(dhd);
731
732 if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
733 DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx));
734 return "<if_bad>";
735 }
736
737 if (dhd->iflist[ifidx] == NULL) {
738 DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx));
739 return "<if_null>";
740 }
741
742 if (dhd->iflist[ifidx]->net)
743 return dhd->iflist[ifidx]->net->name;
744
745 return "<if_none>";
746}
747
748uint8 *
749dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx)
750{
751 int i;
752 dhd_info_t *dhd = (dhd_info_t *)dhdp;
753
754 ASSERT(dhd);
755 for (i = 0; i < DHD_MAX_IFS; i++)
756 if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx)
757 return dhd->iflist[i]->mac_addr;
758
759 return NULL;
760}
761
762
763static void
764_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
765{
766 struct net_device *dev;
767#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
768 struct netdev_hw_addr *ha;
769#else
770 struct dev_mc_list *mclist;
771#endif
772 uint32 allmulti, cnt;
773
774 wl_ioctl_t ioc;
775 char *buf, *bufp;
776 uint buflen;
777 int ret;
778
779 ASSERT(dhd && dhd->iflist[ifidx]);
780 dev = dhd->iflist[ifidx]->net;
781#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
782 netif_addr_lock_bh(dev);
783#endif
784#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
785 cnt = netdev_mc_count(dev);
786#else
787 cnt = dev->mc_count;
788#endif
789#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
790 netif_addr_unlock_bh(dev);
791#endif
792
793 /* Determine initial value of allmulti flag */
794 allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE;
795
796 /* Send down the multicast list first. */
797
798
799 buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN);
800 if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) {
801 DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
802 dhd_ifname(&dhd->pub, ifidx), cnt));
803 return;
804 }
805
806 strcpy(bufp, "mcast_list");
807 bufp += strlen("mcast_list") + 1;
808
809 cnt = htol32(cnt);
810 memcpy(bufp, &cnt, sizeof(cnt));
811 bufp += sizeof(cnt);
812
813#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
814 netif_addr_lock_bh(dev);
815#endif
816#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
817 netdev_for_each_mc_addr(ha, dev) {
818 if (!cnt)
819 break;
820 memcpy(bufp, ha->addr, ETHER_ADDR_LEN);
821 bufp += ETHER_ADDR_LEN;
822 cnt--;
823 }
824#else
825 for (mclist = dev->mc_list; (mclist && (cnt > 0)); cnt--, mclist = mclist->next) {
826 memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN);
827 bufp += ETHER_ADDR_LEN;
828 }
829#endif
830#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
831 netif_addr_unlock_bh(dev);
832#endif
833
834 memset(&ioc, 0, sizeof(ioc));
835 ioc.cmd = WLC_SET_VAR;
836 ioc.buf = buf;
837 ioc.len = buflen;
838 ioc.set = TRUE;
839
840 ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
841 if (ret < 0) {
842 DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
843 dhd_ifname(&dhd->pub, ifidx), cnt));
844 allmulti = cnt ? TRUE : allmulti;
845 }
846
847 MFREE(dhd->pub.osh, buf, buflen);
848
849 /* Now send the allmulti setting. This is based on the setting in the
850 * net_device flags, but might be modified above to be turned on if we
851 * were trying to set some addresses and dongle rejected it...
852 */
853
854 buflen = sizeof("allmulti") + sizeof(allmulti);
855 if (!(buf = MALLOC(dhd->pub.osh, buflen))) {
856 DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx)));
857 return;
858 }
859 allmulti = htol32(allmulti);
860
861 if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) {
862 DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
863 dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen));
864 MFREE(dhd->pub.osh, buf, buflen);
865 return;
866 }
867
868
869 memset(&ioc, 0, sizeof(ioc));
870 ioc.cmd = WLC_SET_VAR;
871 ioc.buf = buf;
872 ioc.len = buflen;
873 ioc.set = TRUE;
874
875 ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
876 if (ret < 0) {
877 DHD_ERROR(("%s: set allmulti %d failed\n",
878 dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
879 }
880
881 MFREE(dhd->pub.osh, buf, buflen);
882
883 /* Finally, pick up the PROMISC flag as well, like the NIC driver does */
884
885 allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE;
886 allmulti = htol32(allmulti);
887
888 memset(&ioc, 0, sizeof(ioc));
889 ioc.cmd = WLC_SET_PROMISC;
890 ioc.buf = &allmulti;
891 ioc.len = sizeof(allmulti);
892 ioc.set = TRUE;
893
894 ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
895 if (ret < 0) {
896 DHD_ERROR(("%s: set promisc %d failed\n",
897 dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
898 }
899}
900
901static int
902_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr)
903{
904 char buf[32];
905 wl_ioctl_t ioc;
906 int ret;
907
908 if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) {
909 DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx)));
910 return -1;
911 }
912 memset(&ioc, 0, sizeof(ioc));
913 ioc.cmd = WLC_SET_VAR;
914 ioc.buf = buf;
915 ioc.len = 32;
916 ioc.set = TRUE;
917
918 ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
919 if (ret < 0) {
920 DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx)));
921 } else {
922 memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN);
923 }
924
925 return ret;
926}
927
928#ifdef SOFTAP
929extern struct net_device *ap_net_dev;
930extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */
931#endif
932
933static void
934dhd_op_if(dhd_if_t *ifp)
935{
936 dhd_info_t *dhd;
937 int ret = 0, err = 0;
938#ifdef SOFTAP
939 unsigned long flags;
940#endif
941
942 ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */
943
944 dhd = ifp->info;
945
946 DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state));
947
948#ifdef WL_CFG80211
949 if (wl_cfg80211_is_progress_ifchange())
950 return;
951
952#endif
953 switch (ifp->state) {
954 case WLC_E_IF_ADD:
955 /*
956 * Delete the existing interface before overwriting it
957 * in case we missed the WLC_E_IF_DEL event.
958 */
959 if (ifp->net != NULL) {
960 DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n",
961 __FUNCTION__, ifp->net->name));
962 netif_stop_queue(ifp->net);
963 unregister_netdev(ifp->net);
964 free_netdev(ifp->net);
965 }
966 /* Allocate etherdev, including space for private structure */
967 if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) {
968 DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
969 ret = -ENOMEM;
970 }
971 if (ret == 0) {
972 strncpy(ifp->net->name, ifp->name, IFNAMSIZ);
973 ifp->net->name[IFNAMSIZ - 1] = '\0';
974 memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
975#ifdef WL_CFG80211
976 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)
977 if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx,
978 dhd_net_attach)) {
979 ifp->state = 0;
980 return;
981 }
982#endif
983 if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) {
984 DHD_ERROR(("%s: dhd_net_attach failed, err %d\n",
985 __FUNCTION__, err));
986 ret = -EOPNOTSUPP;
987 } else {
988#if defined(SOFTAP)
989 if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
990 /* semaphore that the soft AP CODE waits on */
991 flags = dhd_os_spin_lock(&dhd->pub);
992
993 /* save ptr to wl0.1 netdev for use in wl_iw.c */
994 ap_net_dev = ifp->net;
995 /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */
996 up(&ap_eth_ctl.sema);
997 dhd_os_spin_unlock(&dhd->pub, flags);
998 }
999#endif
1000 DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n",
1001 current->pid, ifp->net->name));
1002 ifp->state = 0;
1003 }
1004 }
1005 break;
1006 case WLC_E_IF_DEL:
1007 if (ifp->net != NULL) {
1008 DHD_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n", __FUNCTION__));
1009#ifdef WL_CFG80211
1010 wl_cfg80211_ifdel_ops(ifp->net);
1011#endif
1012 netif_stop_queue(ifp->net);
1013 unregister_netdev(ifp->net);
1014 ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */
1015 }
1016 break;
1017 default:
1018 DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state));
1019 ASSERT(!ifp->state);
1020 break;
1021 }
1022
1023 if (ret < 0) {
1024 ifp->set_multicast = FALSE;
1025 if (ifp->net) {
1026 free_netdev(ifp->net);
1027 }
1028 dhd->iflist[ifp->idx] = NULL;
1029#ifdef WL_CFG80211
1030 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
1031 wl_cfg80211_notify_ifdel(ifp->net);
1032 }
1033#endif
1034#ifdef SOFTAP
1035 flags = dhd_os_spin_lock(&dhd->pub);
1036 if (ifp->net == ap_net_dev)
1037 ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */
1038 dhd_os_spin_unlock(&dhd->pub, flags);
1039#endif /* SOFTAP */
1040 MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
1041 }
1042}
1043
1044static int
1045_dhd_sysioc_thread(void *data)
1046{
1047 tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1048 dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1049
1050
1051 int i;
1052#ifdef SOFTAP
1053 bool in_ap = FALSE;
1054 unsigned long flags;
1055#endif
1056
1057 DAEMONIZE("dhd_sysioc");
1058
1059 complete(&tsk->completed);
1060
1061 while (down_interruptible(&tsk->sema) == 0) {
1062
1063 SMP_RD_BARRIER_DEPENDS();
1064 if (tsk->terminated) {
1065 break;
1066 }
1067
1068 dhd_net_if_lock_local(dhd);
1069 DHD_OS_WAKE_LOCK(&dhd->pub);
1070
1071 for (i = 0; i < DHD_MAX_IFS; i++) {
1072 if (dhd->iflist[i]) {
1073 DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i));
1074#ifdef SOFTAP
1075 flags = dhd_os_spin_lock(&dhd->pub);
1076 in_ap = (ap_net_dev != NULL);
1077 dhd_os_spin_unlock(&dhd->pub, flags);
1078#endif /* SOFTAP */
1079
1080 if (dhd->iflist[i] && dhd->iflist[i]->state)
1081 dhd_op_if(dhd->iflist[i]);
1082
1083 if (dhd->iflist[i] == NULL) {
1084 DHD_TRACE(("\n\n %s: interface %d just been removed,"
1085 "!\n\n", __FUNCTION__, i));
1086 continue;
1087 }
1088
1089#ifdef SOFTAP
1090 if (in_ap && dhd->set_macaddress) {
1091 DHD_TRACE(("attempt to set MAC for %s in AP Mode,"
1092 "blocked. \n", dhd->iflist[i]->net->name));
1093 dhd->set_macaddress = FALSE;
1094 continue;
1095 }
1096
1097 if (in_ap && dhd->iflist[i]->set_multicast) {
1098 DHD_TRACE(("attempt to set MULTICAST list for %s"
1099 "in AP Mode, blocked. \n", dhd->iflist[i]->net->name));
1100 dhd->iflist[i]->set_multicast = FALSE;
1101 continue;
1102 }
1103#endif /* SOFTAP */
1104 if (dhd->iflist[i]->set_multicast) {
1105 dhd->iflist[i]->set_multicast = FALSE;
1106 _dhd_set_multicast_list(dhd, i);
1107 }
1108 if (dhd->set_macaddress) {
1109 dhd->set_macaddress = FALSE;
1110 _dhd_set_mac_address(dhd, i, &dhd->macvalue);
1111 }
1112 }
1113 }
1114
1115 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1116 dhd_net_if_unlock_local(dhd);
1117 }
1118 DHD_TRACE(("%s: stopped\n", __FUNCTION__));
1119 complete_and_exit(&tsk->completed, 0);
1120}
1121
1122static int
1123dhd_set_mac_address(struct net_device *dev, void *addr)
1124{
1125 int ret = 0;
1126
1127 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1128 struct sockaddr *sa = (struct sockaddr *)addr;
1129 int ifidx;
1130
1131 ifidx = dhd_net2idx(dhd, dev);
1132 if (ifidx == DHD_BAD_IF)
1133 return -1;
1134
1135 ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
1136 memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN);
1137 dhd->set_macaddress = TRUE;
1138 up(&dhd->thr_sysioc_ctl.sema);
1139
1140 return ret;
1141}
1142
1143static void
1144dhd_set_multicast_list(struct net_device *dev)
1145{
1146 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1147 int ifidx;
1148
1149 ifidx = dhd_net2idx(dhd, dev);
1150 if (ifidx == DHD_BAD_IF)
1151 return;
1152
1153 ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
1154 dhd->iflist[ifidx]->set_multicast = TRUE;
1155 up(&dhd->thr_sysioc_ctl.sema);
1156}
1157
1158#ifdef PROP_TXSTATUS
1159int
1160dhd_os_wlfc_block(dhd_pub_t *pub)
1161{
1162 dhd_info_t *di = (dhd_info_t *)(pub->info);
1163 ASSERT(di != NULL);
1164 spin_lock_bh(&di->wlfc_spinlock);
1165 return 1;
1166}
1167
1168int
1169dhd_os_wlfc_unblock(dhd_pub_t *pub)
1170{
1171 dhd_info_t *di = (dhd_info_t *)(pub->info);
1172 ASSERT(di != NULL);
1173 spin_unlock_bh(&di->wlfc_spinlock);
1174 return 1;
1175}
1176
1177const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 };
1178uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
1179#define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]]
1180
1181#endif /* PROP_TXSTATUS */
1182int
1183dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
1184{
1185 int ret;
1186 dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1187 struct ether_header *eh = NULL;
1188
1189 /* Reject if down */
1190 if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
1191 /* free the packet here since the caller won't */
1192 PKTFREE(dhdp->osh, pktbuf, TRUE);
1193 return -ENODEV;
1194 }
1195
1196 /* Update multicast statistic */
1197 if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) {
1198 uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf);
1199 eh = (struct ether_header *)pktdata;
1200
1201 if (ETHER_ISMULTI(eh->ether_dhost))
1202 dhdp->tx_multicast++;
1203 if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X)
1204 atomic_inc(&dhd->pend_8021x_cnt);
1205 }
1206
1207 /* Look into the packet and update the packet priority */
1208 if (PKTPRIO(pktbuf) == 0)
1209 pktsetprio(pktbuf, FALSE);
1210
1211#ifdef PROP_TXSTATUS
1212 if (dhdp->wlfc_state) {
1213 /* store the interface ID */
1214 DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx);
1215
1216 /* store destination MAC in the tag as well */
1217 DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost);
1218
1219 /* decide which FIFO this packet belongs to */
1220 if (ETHER_ISMULTI(eh->ether_dhost))
1221 /* one additional queue index (highest AC + 1) is used for bc/mc queue */
1222 DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT);
1223 else
1224 DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf)));
1225 } else
1226#endif /* PROP_TXSTATUS */
1227 /* If the protocol uses a data header, apply it */
1228 dhd_prot_hdrpush(dhdp, ifidx, pktbuf);
1229
1230 /* Use bus module to send data frame */
1231#ifdef WLMEDIA_HTSF
1232 dhd_htsf_addtxts(dhdp, pktbuf);
1233#endif
1234#ifdef PROP_TXSTATUS
1235 if (dhdp->wlfc_state && ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode
1236 != WLFC_FCMODE_NONE) {
1237 dhd_os_wlfc_block(dhdp);
1238 ret = dhd_wlfc_enque_sendq(dhdp->wlfc_state, DHD_PKTTAG_FIFO(PKTTAG(pktbuf)),
1239 pktbuf);
1240 dhd_wlfc_commit_packets(dhdp->wlfc_state, (f_commitpkt_t)dhd_bus_txdata,
1241 dhdp->bus);
1242 if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) {
1243 ((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0;
1244 }
1245 dhd_os_wlfc_unblock(dhdp);
1246 }
1247 else
1248 /* non-proptxstatus way */
1249 ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1250#else
1251 ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1252#endif /* PROP_TXSTATUS */
1253
1254
1255 return ret;
1256}
1257
1258int
1259dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
1260{
1261 int ret;
1262 void *pktbuf;
1263 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1264 int ifidx;
1265#ifdef WLMEDIA_HTSF
1266 uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz;
1267#else
1268 uint8 htsfdlystat_sz = 0;
1269#endif
1270
1271 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1272
1273 DHD_OS_WAKE_LOCK(&dhd->pub);
1274
1275 /* Reject if down */
1276 if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) {
1277 DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n",
1278 __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
1279 netif_stop_queue(net);
1280 /* Send Event when bus down detected during data session */
1281 if (dhd->pub.busstate == DHD_BUS_DOWN) {
1282 DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__));
1283 net_os_send_hang_message(net);
1284 }
1285 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1286 return -ENODEV;
1287 }
1288
1289 ifidx = dhd_net2idx(dhd, net);
1290 if (ifidx == DHD_BAD_IF) {
1291 DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx));
1292 netif_stop_queue(net);
1293 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1294 return -ENODEV;
1295 }
1296
1297 /* Make sure there's enough room for any header */
1298
1299 if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) {
1300 struct sk_buff *skb2;
1301
1302 DHD_INFO(("%s: insufficient headroom\n",
1303 dhd_ifname(&dhd->pub, ifidx)));
1304 dhd->pub.tx_realloc++;
1305
1306 skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz);
1307
1308 dev_kfree_skb(skb);
1309 if ((skb = skb2) == NULL) {
1310 DHD_ERROR(("%s: skb_realloc_headroom failed\n",
1311 dhd_ifname(&dhd->pub, ifidx)));
1312 ret = -ENOMEM;
1313 goto done;
1314 }
1315 }
1316
1317 /* Convert to packet */
1318 if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) {
1319 DHD_ERROR(("%s: PKTFRMNATIVE failed\n",
1320 dhd_ifname(&dhd->pub, ifidx)));
1321 dev_kfree_skb_any(skb);
1322 ret = -ENOMEM;
1323 goto done;
1324 }
1325#ifdef WLMEDIA_HTSF
1326 if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) {
1327 uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf);
1328 struct ether_header *eh = (struct ether_header *)pktdata;
1329
1330 if (!ETHER_ISMULTI(eh->ether_dhost) &&
1331 (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) {
1332 eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS);
1333 }
1334 }
1335#endif
1336
1337 ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf);
1338
1339
1340done:
1341 if (ret)
1342 dhd->pub.dstats.tx_dropped++;
1343 else
1344 dhd->pub.tx_packets++;
1345
1346 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1347
1348 /* Return ok: we always eat the packet */
1349 return 0;
1350}
1351
1352void
1353dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
1354{
1355 struct net_device *net;
1356 dhd_info_t *dhd = dhdp->info;
1357 int i;
1358
1359 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1360
1361 dhdp->txoff = state;
1362 ASSERT(dhd);
1363
1364 if (ifidx == ALL_INTERFACES) {
1365 /* Flow control on all active interfaces */
1366 for (i = 0; i < DHD_MAX_IFS; i++) {
1367 if (dhd->iflist[i]) {
1368 net = dhd->iflist[i]->net;
1369 if (state == ON)
1370 netif_stop_queue(net);
1371 else
1372 netif_wake_queue(net);
1373 }
1374 }
1375 }
1376 else {
1377 if (dhd->iflist[ifidx]) {
1378 net = dhd->iflist[ifidx]->net;
1379 if (state == ON)
1380 netif_stop_queue(net);
1381 else
1382 netif_wake_queue(net);
1383 }
1384 }
1385}
1386
1387void
1388dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
1389{
1390 dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1391 struct sk_buff *skb;
1392 uchar *eth;
1393 uint len;
1394 void *data, *pnext = NULL, *save_pktbuf;
1395 int i;
1396 dhd_if_t *ifp;
1397 wl_event_msg_t event;
1398 int tout = DHD_PACKET_TIMEOUT;
1399
1400 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1401
1402 save_pktbuf = pktbuf;
1403
1404 for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) {
1405 struct ether_header *eh;
1406 struct dot11_llc_snap_header *lsh;
1407
1408 ifp = dhd->iflist[ifidx];
1409 if (ifp == NULL) {
1410 DHD_ERROR(("%s: ifp is NULL. drop packet\n",
1411 __FUNCTION__));
1412 PKTFREE(dhdp->osh, pktbuf, TRUE);
1413 continue;
1414 }
1415
1416 /* Dropping packets before registering net device to avoid kernel panic */
1417 if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED ||
1418 !dhd->pub.up) {
1419 DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n",
1420 __FUNCTION__));
1421 PKTFREE(dhdp->osh, pktbuf, TRUE);
1422 continue;
1423 }
1424
1425 pnext = PKTNEXT(dhdp->osh, pktbuf);
1426 PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
1427
1428 eh = (struct ether_header *)PKTDATA(wl->sh.osh, pktbuf);
1429 lsh = (struct dot11_llc_snap_header *)&eh[1];
1430
1431 if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) &&
1432 (PKTLEN(wl->sh.osh, pktbuf) >= RFC1042_HDR_LEN) &&
1433 bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
1434 lsh->type == HTON16(BTA_PROT_L2CAP)) {
1435 amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)
1436 ((uint8 *)eh + RFC1042_HDR_LEN);
1437 ACL_data = NULL;
1438 }
1439
1440#ifdef PROP_TXSTATUS
1441 if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) {
1442 /* WLFC may send header only packet when
1443 there is an urgent message but no packet to
1444 piggy-back on
1445 */
1446 ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++;
1447 PKTFREE(dhdp->osh, pktbuf, TRUE);
1448 continue;
1449 }
1450#endif
1451
1452 skb = PKTTONATIVE(dhdp->osh, pktbuf);
1453
1454 /* Get the protocol, maintain skb around eth_type_trans()
1455 * The main reason for this hack is for the limitation of
1456 * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len'
1457 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
1458 * coping of the packet coming from the network stack to add
1459 * BDC, Hardware header etc, during network interface registration
1460 * we set the 'net->hard_header_len' to ETH_HLEN + extra space required
1461 * for BDC, Hardware header etc. and not just the ETH_HLEN
1462 */
1463 eth = skb->data;
1464 len = skb->len;
1465
1466 ifp = dhd->iflist[ifidx];
1467 if (ifp == NULL)
1468 ifp = dhd->iflist[0];
1469
1470 ASSERT(ifp);
1471 skb->dev = ifp->net;
1472 skb->protocol = eth_type_trans(skb, skb->dev);
1473
1474 if (skb->pkt_type == PACKET_MULTICAST) {
1475 dhd->pub.rx_multicast++;
1476 }
1477
1478 skb->data = eth;
1479 skb->len = len;
1480
1481#ifdef WLMEDIA_HTSF
1482 dhd_htsf_addrxts(dhdp, pktbuf);
1483#endif
1484 /* Strip header, count, deliver upward */
1485 skb_pull(skb, ETH_HLEN);
1486
1487 /* Process special event packets and then discard them */
1488 if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) {
1489 dhd_wl_host_event(dhd, &ifidx,
1490#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
1491 skb->mac_header,
1492#else
1493 skb->mac.raw,
1494#endif
1495 &event,
1496 &data);
1497
1498 wl_event_to_host_order(&event);
1499 if (event.event_type == WLC_E_BTA_HCI_EVENT) {
1500 dhd_bta_doevt(dhdp, data, event.datalen);
1501 }
1502 tout = DHD_EVENT_TIMEOUT;
1503 }
1504
1505 ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
1506 if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
1507 ifp = dhd->iflist[ifidx];
1508
1509 if (ifp->net)
1510 ifp->net->last_rx = jiffies;
1511
1512 dhdp->dstats.rx_bytes += skb->len;
1513 dhdp->rx_packets++; /* Local count */
1514
1515 if (in_interrupt()) {
1516 netif_rx(skb);
1517 } else {
1518 /* If the receive is not processed inside an ISR,
1519 * the softirqd must be woken explicitly to service
1520 * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled
1521 * by netif_rx_ni(), but in earlier kernels, we need
1522 * to do it manually.
1523 */
1524#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1525 netif_rx_ni(skb);
1526#else
1527 ulong flags;
1528 netif_rx(skb);
1529 local_irq_save(flags);
1530 RAISE_RX_SOFTIRQ();
1531 local_irq_restore(flags);
1532#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
1533 }
1534 }
1535 DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(dhdp, tout);
1536}
1537
1538void
1539dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
1540{
1541 /* Linux version has nothing to do */
1542 return;
1543}
1544
1545void
1546dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success)
1547{
1548 uint ifidx;
1549 dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1550 struct ether_header *eh;
1551 uint16 type;
1552 uint len;
1553
1554 dhd_prot_hdrpull(dhdp, &ifidx, txp);
1555
1556 eh = (struct ether_header *)PKTDATA(dhdp->osh, txp);
1557 type = ntoh16(eh->ether_type);
1558
1559 if (type == ETHER_TYPE_802_1X)
1560 atomic_dec(&dhd->pend_8021x_cnt);
1561
1562 /* Crack open the packet and check to see if it is BT HCI ACL data packet.
1563 * If yes generate packet completion event.
1564 */
1565 len = PKTLEN(dhdp->osh, txp);
1566
1567 /* Generate ACL data tx completion event locally to avoid SDIO bus transaction */
1568 if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) {
1569 struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1];
1570
1571 if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
1572 ntoh16(lsh->type) == BTA_PROT_L2CAP) {
1573
1574 dhd_bta_tx_hcidata_complete(dhdp, txp, success);
1575 }
1576 }
1577}
1578
1579static struct net_device_stats *
1580dhd_get_stats(struct net_device *net)
1581{
1582 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1583 dhd_if_t *ifp;
1584 int ifidx;
1585
1586 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1587
1588 ifidx = dhd_net2idx(dhd, net);
1589 if (ifidx == DHD_BAD_IF)
1590 return NULL;
1591
1592 ifp = dhd->iflist[ifidx];
1593 ASSERT(dhd && ifp);
1594
1595 if (dhd->pub.up) {
1596 /* Use the protocol to get dongle stats */
1597 dhd_prot_dstats(&dhd->pub);
1598 }
1599
1600 /* Copy dongle stats to net device stats */
1601 ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
1602 ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
1603 ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
1604 ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
1605 ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
1606 ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
1607 ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
1608 ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
1609 ifp->stats.multicast = dhd->pub.dstats.multicast;
1610
1611 return &ifp->stats;
1612}
1613
1614#ifdef DHDTHREAD
1615static int
1616dhd_watchdog_thread(void *data)
1617{
1618 tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1619 dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1620 /* This thread doesn't need any user-level access,
1621 * so get rid of all our resources
1622 */
1623 if (dhd_watchdog_prio > 0) {
1624 struct sched_param param;
1625 param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)?
1626 dhd_watchdog_prio:(MAX_RT_PRIO-1);
1627 setScheduler(current, SCHED_FIFO, ¶m);
1628 }
1629
1630 DAEMONIZE("dhd_watchdog");
1631
1632 /* Run until signal received */
1633 complete(&tsk->completed);
1634
1635 while (1)
1636 if (down_interruptible (&tsk->sema) == 0) {
1637 unsigned long flags;
1638
1639 SMP_RD_BARRIER_DEPENDS();
1640 if (tsk->terminated) {
1641 break;
1642 }
1643
1644 dhd_os_sdlock(&dhd->pub);
1645 if (dhd->pub.dongle_reset == FALSE) {
1646 DHD_TIMER(("%s:\n", __FUNCTION__));
1647
1648 /* Call the bus module watchdog */
1649 dhd_bus_watchdog(&dhd->pub);
1650
1651 flags = dhd_os_spin_lock(&dhd->pub);
1652 /* Count the tick for reference */
1653 dhd->pub.tickcnt++;
1654 /* Reschedule the watchdog */
1655 if (dhd->wd_timer_valid)
1656 mod_timer(&dhd->timer,
1657 jiffies + dhd_watchdog_ms * HZ / 1000);
1658 dhd_os_spin_unlock(&dhd->pub, flags);
1659 }
1660 dhd_os_sdunlock(&dhd->pub);
1661 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1662 } else {
1663 break;
1664 }
1665
1666 complete_and_exit(&tsk->completed, 0);
1667}
1668#endif /* DHDTHREAD */
1669
1670static void dhd_watchdog(ulong data)
1671{
1672 dhd_info_t *dhd = (dhd_info_t *)data;
1673 unsigned long flags;
1674
1675 DHD_OS_WAKE_LOCK(&dhd->pub);
1676 if (dhd->pub.dongle_reset) {
1677 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1678 return;
1679 }
1680
1681#ifdef DHDTHREAD
1682 if (dhd->thr_wdt_ctl.thr_pid >= 0) {
1683 up(&dhd->thr_wdt_ctl.sema);
1684 return;
1685 }
1686#endif /* DHDTHREAD */
1687
1688 dhd_os_sdlock(&dhd->pub);
1689 /* Call the bus module watchdog */
1690 dhd_bus_watchdog(&dhd->pub);
1691
1692 flags = dhd_os_spin_lock(&dhd->pub);
1693 /* Count the tick for reference */
1694 dhd->pub.tickcnt++;
1695
1696 /* Reschedule the watchdog */
1697 if (dhd->wd_timer_valid)
1698 mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
1699 dhd_os_spin_unlock(&dhd->pub, flags);
1700 dhd_os_sdunlock(&dhd->pub);
1701 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1702}
1703
1704#ifdef DHDTHREAD
1705static int
1706dhd_dpc_thread(void *data)
1707{
1708 tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1709 dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1710
1711 /* This thread doesn't need any user-level access,
1712 * so get rid of all our resources
1713 */
1714 if (dhd_dpc_prio > 0)
1715 {
1716 struct sched_param param;
1717 param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1);
1718 setScheduler(current, SCHED_FIFO, ¶m);
1719 }
1720
1721 DAEMONIZE("dhd_dpc");
1722 /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */
1723
1724 /* signal: thread has started */
1725 complete(&tsk->completed);
1726
1727 /* Run until signal received */
1728 while (1) {
1729 if (down_interruptible(&tsk->sema) == 0) {
1730
1731 SMP_RD_BARRIER_DEPENDS();
1732 if (tsk->terminated) {
1733 break;
1734 }
1735
1736 /* Call bus dpc unless it indicated down (then clean stop) */
1737 if (dhd->pub.busstate != DHD_BUS_DOWN) {
1738 if (dhd_bus_dpc(dhd->pub.bus)) {
1739 up(&tsk->sema);
1740 }
1741 else {
1742 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1743 }
1744 } else {
1745 dhd_bus_stop(dhd->pub.bus, TRUE);
1746 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1747 }
1748 }
1749 else
1750 break;
1751 }
1752
1753 complete_and_exit(&tsk->completed, 0);
1754}
1755#endif /* DHDTHREAD */
1756
1757static void
1758dhd_dpc(ulong data)
1759{
1760 dhd_info_t *dhd;
1761
1762 dhd = (dhd_info_t *)data;
1763
1764 /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c]
1765 * down below , wake lock is set,
1766 * the tasklet is initialized in dhd_attach()
1767 */
1768 /* Call bus dpc unless it indicated down (then clean stop) */
1769 if (dhd->pub.busstate != DHD_BUS_DOWN) {
1770 if (dhd_bus_dpc(dhd->pub.bus))
1771 tasklet_schedule(&dhd->tasklet);
1772 else
1773 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1774 } else {
1775 dhd_bus_stop(dhd->pub.bus, TRUE);
1776 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1777 }
1778}
1779
1780void
1781dhd_sched_dpc(dhd_pub_t *dhdp)
1782{
1783 dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1784
1785 DHD_OS_WAKE_LOCK(dhdp);
1786#ifdef DHDTHREAD
1787 if (dhd->thr_dpc_ctl.thr_pid >= 0) {
1788 up(&dhd->thr_dpc_ctl.sema);
1789 return;
1790 }
1791#endif /* DHDTHREAD */
1792
1793 tasklet_schedule(&dhd->tasklet);
1794}
1795
1796#ifdef TOE
1797/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */
1798static int
1799dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol)
1800{
1801 wl_ioctl_t ioc;
1802 char buf[32];
1803 int ret;
1804
1805 memset(&ioc, 0, sizeof(ioc));
1806
1807 ioc.cmd = WLC_GET_VAR;
1808 ioc.buf = buf;
1809 ioc.len = (uint)sizeof(buf);
1810 ioc.set = FALSE;
1811
1812 strcpy(buf, "toe_ol");
1813 if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1814 /* Check for older dongle image that doesn't support toe_ol */
1815 if (ret == -EIO) {
1816 DHD_ERROR(("%s: toe not supported by device\n",
1817 dhd_ifname(&dhd->pub, ifidx)));
1818 return -EOPNOTSUPP;
1819 }
1820
1821 DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
1822 return ret;
1823 }
1824
1825 memcpy(toe_ol, buf, sizeof(uint32));
1826 return 0;
1827}
1828
1829/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */
1830static int
1831dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol)
1832{
1833 wl_ioctl_t ioc;
1834 char buf[32];
1835 int toe, ret;
1836
1837 memset(&ioc, 0, sizeof(ioc));
1838
1839 ioc.cmd = WLC_SET_VAR;
1840 ioc.buf = buf;
1841 ioc.len = (uint)sizeof(buf);
1842 ioc.set = TRUE;
1843
1844 /* Set toe_ol as requested */
1845
1846 strcpy(buf, "toe_ol");
1847 memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32));
1848
1849 if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1850 DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
1851 dhd_ifname(&dhd->pub, ifidx), ret));
1852 return ret;
1853 }
1854
1855 /* Enable toe globally only if any components are enabled. */
1856
1857 toe = (toe_ol != 0);
1858
1859 strcpy(buf, "toe");
1860 memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32));
1861
1862 if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1863 DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
1864 return ret;
1865 }
1866
1867 return 0;
1868}
1869#endif /* TOE */
1870
1871#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
1872static void
1873dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
1874{
1875 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1876
1877 sprintf(info->driver, "wl");
1878 sprintf(info->version, "%lu", dhd->pub.drv_version);
1879}
1880
1881struct ethtool_ops dhd_ethtool_ops = {
1882 .get_drvinfo = dhd_ethtool_get_drvinfo
1883};
1884#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
1885
1886
1887#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
1888static int
1889dhd_ethtool(dhd_info_t *dhd, void *uaddr)
1890{
1891 struct ethtool_drvinfo info;
1892 char drvname[sizeof(info.driver)];
1893 uint32 cmd;
1894#ifdef TOE
1895 struct ethtool_value edata;
1896 uint32 toe_cmpnt, csum_dir;
1897 int ret;
1898#endif
1899
1900 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1901
1902 /* all ethtool calls start with a cmd word */
1903 if (copy_from_user(&cmd, uaddr, sizeof (uint32)))
1904 return -EFAULT;
1905
1906 switch (cmd) {
1907 case ETHTOOL_GDRVINFO:
1908 /* Copy out any request driver name */
1909 if (copy_from_user(&info, uaddr, sizeof(info)))
1910 return -EFAULT;
1911 strncpy(drvname, info.driver, sizeof(info.driver));
1912 drvname[sizeof(info.driver)-1] = '\0';
1913
1914 /* clear struct for return */
1915 memset(&info, 0, sizeof(info));
1916 info.cmd = cmd;
1917
1918 /* if dhd requested, identify ourselves */
1919 if (strcmp(drvname, "?dhd") == 0) {
1920 sprintf(info.driver, "dhd");
1921 strcpy(info.version, EPI_VERSION_STR);
1922 }
1923
1924 /* otherwise, require dongle to be up */
1925 else if (!dhd->pub.up) {
1926 DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__));
1927 return -ENODEV;
1928 }
1929
1930 /* finally, report dongle driver type */
1931 else if (dhd->pub.iswl)
1932 sprintf(info.driver, "wl");
1933 else
1934 sprintf(info.driver, "xx");
1935
1936 sprintf(info.version, "%lu", dhd->pub.drv_version);
1937 if (copy_to_user(uaddr, &info, sizeof(info)))
1938 return -EFAULT;
1939 DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__,
1940 (int)sizeof(drvname), drvname, info.driver));
1941 break;
1942
1943#ifdef TOE
1944 /* Get toe…
Large files files are truncated, but you can click here to view the full file