aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/bcmdhd/dhd_linux.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/net/wireless/bcmdhd/dhd_linux.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/net/wireless/bcmdhd/dhd_linux.c')
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_linux.c5080
1 files changed, 5080 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c
new file mode 100644
index 00000000000..80a75699e27
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/dhd_linux.c
@@ -0,0 +1,5080 @@
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 291449 2011-10-22 12:16:26Z $
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#include <linux/device.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#ifdef CONFIG_HAS_WAKELOCK
61#include <linux/wakelock.h>
62#endif
63#ifdef WL_CFG80211
64#include <wl_cfg80211.h>
65#endif
66
67#include <proto/802.11_bta.h>
68#include <proto/bt_amp_hci.h>
69#include <dhd_bta.h>
70
71#ifdef WLMEDIA_HTSF
72#include <linux/time.h>
73#include <htsf.h>
74
75#define HTSF_MINLEN 200 /* min. packet length to timestamp */
76#define HTSF_BUS_DELAY 150 /* assume a fix propagation in us */
77#define TSMAX 1000 /* max no. of timing record kept */
78#define NUMBIN 34
79
80static uint32 tsidx = 0;
81static uint32 htsf_seqnum = 0;
82uint32 tsfsync;
83struct timeval tsync;
84static uint32 tsport = 5010;
85
86typedef struct histo_ {
87 uint32 bin[NUMBIN];
88} histo_t;
89
90#if !ISPOWEROF2(DHD_SDALIGN)
91#error DHD_SDALIGN is not a power of 2!
92#endif
93
94static histo_t vi_d1, vi_d2, vi_d3, vi_d4;
95#endif /* WLMEDIA_HTSF */
96
97#if defined(SOFTAP)
98extern bool ap_cfg_running;
99extern bool ap_fw_loaded;
100#endif
101
102/* enable HOSTIP cache update from the host side when an eth0:N is up */
103#define AOE_IP_ALIAS_SUPPORT 1
104
105#ifdef PROP_TXSTATUS
106#include <wlfc_proto.h>
107#include <dhd_wlfc.h>
108#endif
109
110#include <wl_android.h>
111
112#ifdef ARP_OFFLOAD_SUPPORT
113static int dhd_device_event(struct notifier_block *this,
114 unsigned long event,
115 void *ptr);
116
117static struct notifier_block dhd_notifier = {
118 .notifier_call = dhd_device_event
119};
120#endif /* ARP_OFFLOAD_SUPPORT */
121
122#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
123#include <linux/suspend.h>
124volatile bool dhd_mmc_suspend = FALSE;
125DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
126#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
127
128#if defined(OOB_INTR_ONLY)
129extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable);
130#endif /* defined(OOB_INTR_ONLY) */
131#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
132MODULE_LICENSE("GPL v2");
133#endif /* LinuxVer */
134
135#include <dhd_bus.h>
136
137#ifndef PROP_TXSTATUS
138#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen)
139#else
140#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128)
141#endif
142
143#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
144const char *
145print_tainted()
146{
147 return "";
148}
149#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */
150
151/* Linux wireless extension support */
152#if defined(CONFIG_BCMDHD_WEXT)
153#include <wl_iw.h>
154extern wl_iw_extra_params_t g_wl_iw_params;
155#endif /* defined(CONFIG_BCMDHD_WEXT) */
156
157#if defined(CONFIG_HAS_EARLYSUSPEND)
158#include <linux/earlysuspend.h>
159extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
160extern int dhd_get_dtim_skip(dhd_pub_t *dhd);
161#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
162
163#ifdef PKT_FILTER_SUPPORT
164extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
165extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
166#endif
167
168/* Interface control information */
169typedef struct dhd_if {
170 struct dhd_info *info; /* back pointer to dhd_info */
171 /* OS/stack specifics */
172 struct net_device *net;
173 struct net_device_stats stats;
174 int idx; /* iface idx in dongle */
175 dhd_if_state_t state; /* interface state */
176 uint subunit; /* subunit */
177 uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */
178 bool attached; /* Delayed attachment when unset */
179 bool txflowcontrol; /* Per interface flow control indicator */
180 char name[IFNAMSIZ+1]; /* linux interface name */
181 uint8 bssidx; /* bsscfg index for the interface */
182 bool set_multicast;
183} dhd_if_t;
184
185#ifdef WLMEDIA_HTSF
186typedef struct {
187 uint32 low;
188 uint32 high;
189} tsf_t;
190
191typedef struct {
192 uint32 last_cycle;
193 uint32 last_sec;
194 uint32 last_tsf;
195 uint32 coef; /* scaling factor */
196 uint32 coefdec1; /* first decimal */
197 uint32 coefdec2; /* second decimal */
198} htsf_t;
199
200typedef struct {
201 uint32 t1;
202 uint32 t2;
203 uint32 t3;
204 uint32 t4;
205} tstamp_t;
206
207static tstamp_t ts[TSMAX];
208static tstamp_t maxdelayts;
209static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0;
210
211#endif /* WLMEDIA_HTSF */
212
213/* Local private structure (extension of pub) */
214typedef struct dhd_info {
215#if defined(CONFIG_BCMDHD_WEXT)
216 wl_iw_t iw; /* wireless extensions state (must be first) */
217#endif /* defined(CONFIG_BCMDHD_WEXT) */
218
219 dhd_pub_t pub;
220
221 /* For supporting multiple interfaces */
222 dhd_if_t *iflist[DHD_MAX_IFS];
223
224 struct semaphore proto_sem;
225#ifdef PROP_TXSTATUS
226 spinlock_t wlfc_spinlock;
227#endif /* PROP_TXSTATUS */
228#ifdef WLMEDIA_HTSF
229 htsf_t htsf;
230#endif
231 wait_queue_head_t ioctl_resp_wait;
232 struct timer_list timer;
233 bool wd_timer_valid;
234 struct tasklet_struct tasklet;
235 spinlock_t sdlock;
236 spinlock_t txqlock;
237 spinlock_t dhd_lock;
238#ifdef DHDTHREAD
239 /* Thread based operation */
240 bool threads_only;
241 struct semaphore sdsem;
242
243 tsk_ctl_t thr_dpc_ctl;
244 tsk_ctl_t thr_wdt_ctl;
245
246#else
247 bool dhd_tasklet_create;
248#endif /* DHDTHREAD */
249 tsk_ctl_t thr_sysioc_ctl;
250
251 /* Wakelocks */
252#if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
253 struct wake_lock wl_wifi; /* Wifi wakelock */
254 struct wake_lock wl_rxwake; /* Wifi rx wakelock */
255#endif
256
257#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
258 /* net_device interface lock, prevent race conditions among net_dev interface
259 * calls and wifi_on or wifi_off
260 */
261 struct mutex dhd_net_if_mutex;
262#endif
263 spinlock_t wakelock_spinlock;
264 int wakelock_counter;
265 int wakelock_timeout_enable;
266
267 /* Thread to issue ioctl for multicast */
268 bool set_macaddress;
269 struct ether_addr macvalue;
270 wait_queue_head_t ctrl_wait;
271 atomic_t pend_8021x_cnt;
272 dhd_attach_states_t dhd_state;
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 */
282char firmware_path[MOD_PARAM_PATHLEN];
283char nvram_path[MOD_PARAM_PATHLEN];
284
285extern int wl_control_wl_start(struct net_device *dev);
286extern int net_os_send_hang_message(struct net_device *dev);
287#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
288struct semaphore dhd_registration_sem;
289#define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */
290#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
291
292/* Spawn a thread for system ioctls (set mac, set mcast) */
293uint dhd_sysioc = TRUE;
294module_param(dhd_sysioc, uint, 0);
295
296/* Error bits */
297module_param(dhd_msg_level, int, 0);
298
299/* load firmware and/or nvram values from the filesystem */
300module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660);
301module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
302
303/* Watchdog interval */
304uint dhd_watchdog_ms = 10;
305module_param(dhd_watchdog_ms, uint, 0);
306
307#if defined(DHD_DEBUG)
308/* Console poll interval */
309uint dhd_console_ms = 0;
310module_param(dhd_console_ms, uint, 0644);
311#endif /* defined(DHD_DEBUG) */
312
313/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */
314uint dhd_arp_mode = 0xb;
315module_param(dhd_arp_mode, uint, 0);
316
317/* ARP offload enable */
318uint dhd_arp_enable = TRUE;
319module_param(dhd_arp_enable, uint, 0);
320
321/* Global Pkt filter enable control */
322uint dhd_pkt_filter_enable = TRUE;
323module_param(dhd_pkt_filter_enable, uint, 0);
324
325/* Pkt filter init setup */
326uint dhd_pkt_filter_init = 0;
327module_param(dhd_pkt_filter_init, uint, 0);
328
329/* Pkt filter mode control */
330uint dhd_master_mode = TRUE;
331module_param(dhd_master_mode, uint, 0);
332
333#ifdef DHDTHREAD
334/* Watchdog thread priority, -1 to use kernel timer */
335int dhd_watchdog_prio = 97;
336module_param(dhd_watchdog_prio, int, 0);
337
338/* DPC thread priority, -1 to use tasklet */
339int dhd_dpc_prio = 98;
340module_param(dhd_dpc_prio, int, 0);
341
342/* DPC thread priority, -1 to use tasklet */
343extern int dhd_dongle_memsize;
344module_param(dhd_dongle_memsize, int, 0);
345#endif /* DHDTHREAD */
346/* Control fw roaming */
347uint dhd_roam_disable = 0;
348
349/* Control radio state */
350uint dhd_radio_up = 1;
351
352/* Network inteface name */
353char iface_name[IFNAMSIZ] = {'\0'};
354module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
355
356#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
357#define DAEMONIZE(a) daemonize(a); \
358 allow_signal(SIGKILL); \
359 allow_signal(SIGTERM);
360#else /* Linux 2.4 (w/o preemption patch) */
361#define RAISE_RX_SOFTIRQ() \
362 cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
363#define DAEMONIZE(a) daemonize(); \
364 do { if (a) \
365 strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
366 } while (0);
367#endif /* LINUX_VERSION_CODE */
368
369#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
370#define BLOCKABLE() (!in_atomic())
371#else
372#define BLOCKABLE() (!in_interrupt())
373#endif
374
375/* The following are specific to the SDIO dongle */
376
377/* IOCTL response timeout */
378int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
379
380/* Idle timeout for backplane clock */
381int dhd_idletime = DHD_IDLETIME_TICKS;
382module_param(dhd_idletime, int, 0);
383
384/* Use polling */
385uint dhd_poll = FALSE;
386module_param(dhd_poll, uint, 0);
387
388/* Use interrupts */
389uint dhd_intr = TRUE;
390module_param(dhd_intr, uint, 0);
391
392/* SDIO Drive Strength (in milliamps) */
393uint dhd_sdiod_drive_strength = 6;
394module_param(dhd_sdiod_drive_strength, uint, 0);
395
396/* Tx/Rx bounds */
397extern uint dhd_txbound;
398extern uint dhd_rxbound;
399module_param(dhd_txbound, uint, 0);
400module_param(dhd_rxbound, uint, 0);
401
402/* Deferred transmits */
403extern uint dhd_deferred_tx;
404module_param(dhd_deferred_tx, uint, 0);
405
406#ifdef BCMDBGFS
407extern void dhd_dbg_init(dhd_pub_t *dhdp);
408extern void dhd_dbg_remove(void);
409#endif /* BCMDBGFS */
410
411
412
413#ifdef SDTEST
414/* Echo packet generator (pkts/s) */
415uint dhd_pktgen = 0;
416module_param(dhd_pktgen, uint, 0);
417
418/* Echo packet len (0 => sawtooth, max 2040) */
419uint dhd_pktgen_len = 0;
420module_param(dhd_pktgen_len, uint, 0);
421#endif /* SDTEST */
422
423/* Version string to report */
424#ifdef DHD_DEBUG
425#ifndef SRCBASE
426#define SRCBASE "drivers/net/wireless/bcmdhd"
427#endif
428#define DHD_COMPILED "\nCompiled in " SRCBASE
429#else
430#define DHD_COMPILED
431#endif /* DHD_DEBUG */
432
433static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR
434#ifdef DHD_DEBUG
435"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__
436#endif
437;
438static void dhd_net_if_lock_local(dhd_info_t *dhd);
439static void dhd_net_if_unlock_local(dhd_info_t *dhd);
440
441#ifdef WLMEDIA_HTSF
442void htsf_update(dhd_info_t *dhd, void *data);
443tsf_t prev_tsf, cur_tsf;
444
445uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx);
446static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx);
447static void dhd_dump_latency(void);
448static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf);
449static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf);
450static void dhd_dump_htsfhisto(histo_t *his, char *s);
451#endif /* WLMEDIA_HTSF */
452
453/* Monitor interface */
454int dhd_monitor_init(void *dhd_pub);
455int dhd_monitor_uninit(void);
456
457
458#if defined(CONFIG_BCMDHD_WEXT)
459struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
460#endif /* defined(CONFIG_BCMDHD_WEXT) */
461
462static void dhd_dpc(ulong data);
463/* forward decl */
464extern int dhd_wait_pend8021x(struct net_device *dev);
465
466#ifdef TOE
467#ifndef BDC
468#error TOE requires BDC
469#endif /* !BDC */
470static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol);
471static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol);
472#endif /* TOE */
473
474static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
475 wl_event_msg_t *event_ptr, void **data_ptr);
476
477#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
478static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored)
479{
480 int ret = NOTIFY_DONE;
481
482#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39))
483 switch (action) {
484 case PM_HIBERNATION_PREPARE:
485 case PM_SUSPEND_PREPARE:
486 dhd_mmc_suspend = TRUE;
487 ret = NOTIFY_OK;
488 break;
489 case PM_POST_HIBERNATION:
490 case PM_POST_SUSPEND:
491 dhd_mmc_suspend = FALSE;
492 ret = NOTIFY_OK;
493 break;
494 }
495 smp_mb();
496#endif
497 return ret;
498}
499
500static struct notifier_block dhd_sleep_pm_notifier = {
501 .notifier_call = dhd_sleep_pm_callback,
502 .priority = 0
503};
504extern int register_pm_notifier(struct notifier_block *nb);
505extern int unregister_pm_notifier(struct notifier_block *nb);
506#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
507
508static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
509{
510#ifdef PKT_FILTER_SUPPORT
511 DHD_TRACE(("%s: %d\n", __FUNCTION__, value));
512 /* 1 - Enable packet filter, only allow unicast packet to send up */
513 /* 0 - Disable packet filter */
514 if (dhd_pkt_filter_enable) {
515 int i;
516
517 for (i = 0; i < dhd->pktfilter_count; i++) {
518 dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
519 dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
520 value, dhd_master_mode);
521 }
522 }
523#endif
524}
525
526#if defined(CONFIG_HAS_EARLYSUSPEND)
527static int dhd_set_suspend(int value, dhd_pub_t *dhd)
528{
529 int power_mode = PM_MAX;
530 /* wl_pkt_filter_enable_t enable_parm; */
531 char iovbuf[32];
532 int bcn_li_dtim = 3;
533 uint roamvar = 1;
534
535 DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n",
536 __FUNCTION__, value, dhd->in_suspend));
537
538 if (dhd && dhd->up) {
539 if (value && dhd->in_suspend) {
540
541 /* Kernel suspended */
542 DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__));
543
544 dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
545 sizeof(power_mode), TRUE, 0);
546
547 /* Enable packet filter, only allow unicast packet to send up */
548 dhd_set_packet_filter(1, dhd);
549
550 /* If DTIM skip is set up as default, force it to wake
551 * each third DTIM for better power savings. Note that
552 * one side effect is a chance to miss BC/MC packet.
553 */
554 bcn_li_dtim = dhd_get_dtim_skip(dhd);
555 bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
556 4, iovbuf, sizeof(iovbuf));
557 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
558
559 /* Disable firmware roaming during suspend */
560 bcm_mkiovar("roam_off", (char *)&roamvar, 4,
561 iovbuf, sizeof(iovbuf));
562 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
563 } else {
564
565 /* Kernel resumed */
566 DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__));
567
568 power_mode = PM_FAST;
569 dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
570 sizeof(power_mode), TRUE, 0);
571
572 /* disable pkt filter */
573 dhd_set_packet_filter(0, dhd);
574
575 /* restore pre-suspend setting for dtim_skip */
576 bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
577 4, iovbuf, sizeof(iovbuf));
578
579 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
580 roamvar = dhd_roam_disable;
581 bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf,
582 sizeof(iovbuf));
583 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
584 }
585 }
586
587 return 0;
588}
589
590static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
591{
592 dhd_pub_t *dhdp = &dhd->pub;
593
594 DHD_OS_WAKE_LOCK(dhdp);
595 /* Set flag when early suspend was called */
596 dhdp->in_suspend = val;
597 if ((!dhdp->suspend_disable_flag) && (dhd_check_ap_wfd_mode_set(dhdp) == FALSE))
598 dhd_set_suspend(val, dhdp);
599 DHD_OS_WAKE_UNLOCK(dhdp);
600}
601
602static void dhd_early_suspend(struct early_suspend *h)
603{
604 struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
605
606 DHD_TRACE(("%s: enter\n", __FUNCTION__));
607
608 if (dhd)
609 dhd_suspend_resume_helper(dhd, 1);
610}
611
612static void dhd_late_resume(struct early_suspend *h)
613{
614 struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
615
616 DHD_TRACE(("%s: enter\n", __FUNCTION__));
617
618 if (dhd)
619 dhd_suspend_resume_helper(dhd, 0);
620}
621#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
622
623/*
624 * Generalized timeout mechanism. Uses spin sleep with exponential back-off until
625 * the sleep time reaches one jiffy, then switches over to task delay. Usage:
626 *
627 * dhd_timeout_start(&tmo, usec);
628 * while (!dhd_timeout_expired(&tmo))
629 * if (poll_something())
630 * break;
631 * if (dhd_timeout_expired(&tmo))
632 * fatal();
633 */
634
635void
636dhd_timeout_start(dhd_timeout_t *tmo, uint usec)
637{
638 tmo->limit = usec;
639 tmo->increment = 0;
640 tmo->elapsed = 0;
641 tmo->tick = 1000000 / HZ;
642}
643
644int
645dhd_timeout_expired(dhd_timeout_t *tmo)
646{
647 /* Does nothing the first call */
648 if (tmo->increment == 0) {
649 tmo->increment = 1;
650 return 0;
651 }
652
653 if (tmo->elapsed >= tmo->limit)
654 return 1;
655
656 /* Add the delay that's about to take place */
657 tmo->elapsed += tmo->increment;
658
659 if (tmo->increment < tmo->tick) {
660 OSL_DELAY(tmo->increment);
661 tmo->increment *= 2;
662 if (tmo->increment > tmo->tick)
663 tmo->increment = tmo->tick;
664 } else {
665 wait_queue_head_t delay_wait;
666 DECLARE_WAITQUEUE(wait, current);
667 int pending;
668 init_waitqueue_head(&delay_wait);
669 add_wait_queue(&delay_wait, &wait);
670 set_current_state(TASK_INTERRUPTIBLE);
671 schedule_timeout(1);
672 pending = signal_pending(current);
673 remove_wait_queue(&delay_wait, &wait);
674 set_current_state(TASK_RUNNING);
675 if (pending)
676 return 1; /* Interrupted */
677 }
678
679 return 0;
680}
681
682int
683dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
684{
685 int i = 0;
686
687 ASSERT(dhd);
688 while (i < DHD_MAX_IFS) {
689 if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
690 return i;
691 i++;
692 }
693
694 return DHD_BAD_IF;
695}
696
697struct net_device * dhd_idx2net(struct dhd_pub *dhd_pub, int ifidx)
698{
699 struct dhd_info *dhd_info;
700
701 if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS)
702 return NULL;
703 dhd_info = dhd_pub->info;
704 if (dhd_info && dhd_info->iflist[ifidx])
705 return dhd_info->iflist[ifidx]->net;
706 return NULL;
707}
708
709int
710dhd_ifname2idx(dhd_info_t *dhd, char *name)
711{
712 int i = DHD_MAX_IFS;
713
714 ASSERT(dhd);
715
716 if (name == NULL || *name == '\0')
717 return 0;
718
719 while (--i > 0)
720 if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
721 break;
722
723 DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name));
724
725 return i; /* default - the primary interface */
726}
727
728char *
729dhd_ifname(dhd_pub_t *dhdp, int ifidx)
730{
731 dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
732
733 ASSERT(dhd);
734
735 if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
736 DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx));
737 return "<if_bad>";
738 }
739
740 if (dhd->iflist[ifidx] == NULL) {
741 DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx));
742 return "<if_null>";
743 }
744
745 if (dhd->iflist[ifidx]->net)
746 return dhd->iflist[ifidx]->net->name;
747
748 return "<if_none>";
749}
750
751uint8 *
752dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx)
753{
754 int i;
755 dhd_info_t *dhd = (dhd_info_t *)dhdp;
756
757 ASSERT(dhd);
758 for (i = 0; i < DHD_MAX_IFS; i++)
759 if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx)
760 return dhd->iflist[i]->mac_addr;
761
762 return NULL;
763}
764
765
766static void
767_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
768{
769 struct net_device *dev;
770#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
771 struct netdev_hw_addr *ha;
772#else
773 struct dev_mc_list *mclist;
774#endif
775 uint32 allmulti, cnt;
776
777 wl_ioctl_t ioc;
778 char *buf, *bufp;
779 uint buflen;
780 int ret;
781
782 ASSERT(dhd && dhd->iflist[ifidx]);
783 dev = dhd->iflist[ifidx]->net;
784#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
785 netif_addr_lock_bh(dev);
786#endif
787#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
788 cnt = netdev_mc_count(dev);
789#else
790 cnt = dev->mc_count;
791#endif
792#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
793 netif_addr_unlock_bh(dev);
794#endif
795
796 /* Determine initial value of allmulti flag */
797 allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE;
798
799 /* Send down the multicast list first. */
800
801
802 buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN);
803 if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) {
804 DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
805 dhd_ifname(&dhd->pub, ifidx), cnt));
806 return;
807 }
808
809 strcpy(bufp, "mcast_list");
810 bufp += strlen("mcast_list") + 1;
811
812 cnt = htol32(cnt);
813 memcpy(bufp, &cnt, sizeof(cnt));
814 bufp += sizeof(cnt);
815
816#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
817 netif_addr_lock_bh(dev);
818#endif
819#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
820 netdev_for_each_mc_addr(ha, dev) {
821 if (!cnt)
822 break;
823 memcpy(bufp, ha->addr, ETHER_ADDR_LEN);
824 bufp += ETHER_ADDR_LEN;
825 cnt--;
826 }
827#else
828 for (mclist = dev->mc_list; (mclist && (cnt > 0)); cnt--, mclist = mclist->next) {
829 memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN);
830 bufp += ETHER_ADDR_LEN;
831 }
832#endif
833#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
834 netif_addr_unlock_bh(dev);
835#endif
836
837 memset(&ioc, 0, sizeof(ioc));
838 ioc.cmd = WLC_SET_VAR;
839 ioc.buf = buf;
840 ioc.len = buflen;
841 ioc.set = TRUE;
842
843 ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
844 if (ret < 0) {
845 DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
846 dhd_ifname(&dhd->pub, ifidx), cnt));
847 allmulti = cnt ? TRUE : allmulti;
848 }
849
850 MFREE(dhd->pub.osh, buf, buflen);
851
852 /* Now send the allmulti setting. This is based on the setting in the
853 * net_device flags, but might be modified above to be turned on if we
854 * were trying to set some addresses and dongle rejected it...
855 */
856
857 buflen = sizeof("allmulti") + sizeof(allmulti);
858 if (!(buf = MALLOC(dhd->pub.osh, buflen))) {
859 DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx)));
860 return;
861 }
862 allmulti = htol32(allmulti);
863
864 if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) {
865 DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
866 dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen));
867 MFREE(dhd->pub.osh, buf, buflen);
868 return;
869 }
870
871
872 memset(&ioc, 0, sizeof(ioc));
873 ioc.cmd = WLC_SET_VAR;
874 ioc.buf = buf;
875 ioc.len = buflen;
876 ioc.set = TRUE;
877
878 ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
879 if (ret < 0) {
880 DHD_ERROR(("%s: set allmulti %d failed\n",
881 dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
882 }
883
884 MFREE(dhd->pub.osh, buf, buflen);
885
886 /* Finally, pick up the PROMISC flag as well, like the NIC driver does */
887
888 allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE;
889 allmulti = htol32(allmulti);
890
891 memset(&ioc, 0, sizeof(ioc));
892 ioc.cmd = WLC_SET_PROMISC;
893 ioc.buf = &allmulti;
894 ioc.len = sizeof(allmulti);
895 ioc.set = TRUE;
896
897 ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
898 if (ret < 0) {
899 DHD_ERROR(("%s: set promisc %d failed\n",
900 dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
901 }
902}
903
904static int
905_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr)
906{
907 char buf[32];
908 wl_ioctl_t ioc;
909 int ret;
910
911 if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) {
912 DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx)));
913 return -1;
914 }
915 memset(&ioc, 0, sizeof(ioc));
916 ioc.cmd = WLC_SET_VAR;
917 ioc.buf = buf;
918 ioc.len = 32;
919 ioc.set = TRUE;
920
921 ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
922 if (ret < 0) {
923 DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx)));
924 } else {
925 memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN);
926 }
927
928 return ret;
929}
930
931#ifdef SOFTAP
932extern struct net_device *ap_net_dev;
933extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */
934#endif
935
936static void
937dhd_op_if(dhd_if_t *ifp)
938{
939 dhd_info_t *dhd;
940 int ret = 0, err = 0;
941#ifdef SOFTAP
942 unsigned long flags;
943#endif
944
945 ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */
946
947 dhd = ifp->info;
948
949 DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state));
950
951#ifdef WL_CFG80211
952 if (wl_cfg80211_is_progress_ifchange())
953 return;
954
955#endif
956 switch (ifp->state) {
957 case DHD_IF_ADD:
958 /*
959 * Delete the existing interface before overwriting it
960 * in case we missed the WLC_E_IF_DEL event.
961 */
962 if (ifp->net != NULL) {
963 DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n",
964 __FUNCTION__, ifp->net->name));
965 netif_stop_queue(ifp->net);
966 unregister_netdev(ifp->net);
967 free_netdev(ifp->net);
968 }
969 /* Allocate etherdev, including space for private structure */
970 if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) {
971 DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
972 ret = -ENOMEM;
973 }
974 if (ret == 0) {
975 strncpy(ifp->net->name, ifp->name, IFNAMSIZ);
976 ifp->net->name[IFNAMSIZ - 1] = '\0';
977 memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
978#ifdef WL_CFG80211
979 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)
980 if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx,
981 dhd_net_attach)) {
982 ifp->state = DHD_IF_NONE;
983 return;
984 }
985#endif
986 if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) {
987 DHD_ERROR(("%s: dhd_net_attach failed, err %d\n",
988 __FUNCTION__, err));
989 ret = -EOPNOTSUPP;
990 } else {
991#if defined(SOFTAP)
992 if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
993 /* semaphore that the soft AP CODE waits on */
994 flags = dhd_os_spin_lock(&dhd->pub);
995
996 /* save ptr to wl0.1 netdev for use in wl_iw.c */
997 ap_net_dev = ifp->net;
998 /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */
999 up(&ap_eth_ctl.sema);
1000 dhd_os_spin_unlock(&dhd->pub, flags);
1001 }
1002#endif
1003 DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n",
1004 current->pid, ifp->net->name));
1005 ifp->state = DHD_IF_NONE;
1006 }
1007 }
1008 break;
1009 case DHD_IF_DEL:
1010 /* Make sure that we don't enter again here if .. */
1011 /* dhd_op_if is called again from some other context */
1012 ifp->state = DHD_IF_DELETING;
1013 if (ifp->net != NULL) {
1014 DHD_TRACE(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__));
1015#ifdef WL_CFG80211
1016 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
1017 wl_cfg80211_notify_ifdel(ifp->net);
1018 }
1019#endif
1020 netif_stop_queue(ifp->net);
1021 unregister_netdev(ifp->net);
1022 ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */
1023 }
1024 break;
1025 case DHD_IF_DELETING:
1026 break;
1027 default:
1028 DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state));
1029 ASSERT(!ifp->state);
1030 break;
1031 }
1032
1033 if (ret < 0) {
1034 ifp->set_multicast = FALSE;
1035 if (ifp->net) {
1036 free_netdev(ifp->net);
1037 }
1038 dhd->iflist[ifp->idx] = NULL;
1039#ifdef SOFTAP
1040 flags = dhd_os_spin_lock(&dhd->pub);
1041 if (ifp->net == ap_net_dev)
1042 ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */
1043 dhd_os_spin_unlock(&dhd->pub, flags);
1044#endif /* SOFTAP */
1045 MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
1046 }
1047}
1048
1049static int
1050_dhd_sysioc_thread(void *data)
1051{
1052 tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1053 dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1054
1055
1056 int i;
1057#ifdef SOFTAP
1058 bool in_ap = FALSE;
1059 unsigned long flags;
1060#endif
1061
1062 DAEMONIZE("dhd_sysioc");
1063
1064 complete(&tsk->completed);
1065
1066 while (down_interruptible(&tsk->sema) == 0) {
1067
1068 SMP_RD_BARRIER_DEPENDS();
1069 if (tsk->terminated) {
1070 break;
1071 }
1072
1073 dhd_net_if_lock_local(dhd);
1074 DHD_OS_WAKE_LOCK(&dhd->pub);
1075
1076 for (i = 0; i < DHD_MAX_IFS; i++) {
1077 if (dhd->iflist[i]) {
1078 DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i));
1079#ifdef SOFTAP
1080 flags = dhd_os_spin_lock(&dhd->pub);
1081 in_ap = (ap_net_dev != NULL);
1082 dhd_os_spin_unlock(&dhd->pub, flags);
1083#endif /* SOFTAP */
1084 if (dhd->iflist[i] && dhd->iflist[i]->state)
1085 dhd_op_if(dhd->iflist[i]);
1086
1087 if (dhd->iflist[i] == NULL) {
1088 DHD_TRACE(("\n\n %s: interface %d just been removed,"
1089 "!\n\n", __FUNCTION__, i));
1090 continue;
1091 }
1092#ifdef SOFTAP
1093 if (in_ap && dhd->set_macaddress) {
1094 DHD_TRACE(("attempt to set MAC for %s in AP Mode,"
1095 "blocked. \n", dhd->iflist[i]->net->name));
1096 dhd->set_macaddress = FALSE;
1097 continue;
1098 }
1099
1100 if (in_ap && dhd->iflist[i]->set_multicast) {
1101 DHD_TRACE(("attempt to set MULTICAST list for %s"
1102 "in AP Mode, blocked. \n", dhd->iflist[i]->net->name));
1103 dhd->iflist[i]->set_multicast = FALSE;
1104 continue;
1105 }
1106#endif /* SOFTAP */
1107 if (dhd->iflist[i]->set_multicast) {
1108 dhd->iflist[i]->set_multicast = FALSE;
1109 _dhd_set_multicast_list(dhd, i);
1110 }
1111 if (dhd->set_macaddress) {
1112 dhd->set_macaddress = FALSE;
1113 _dhd_set_mac_address(dhd, i, &dhd->macvalue);
1114 }
1115 }
1116 }
1117
1118 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1119 dhd_net_if_unlock_local(dhd);
1120 }
1121 DHD_TRACE(("%s: stopped\n", __FUNCTION__));
1122 complete_and_exit(&tsk->completed, 0);
1123}
1124
1125static int
1126dhd_set_mac_address(struct net_device *dev, void *addr)
1127{
1128 int ret = 0;
1129
1130 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1131 struct sockaddr *sa = (struct sockaddr *)addr;
1132 int ifidx;
1133
1134 ifidx = dhd_net2idx(dhd, dev);
1135 if (ifidx == DHD_BAD_IF)
1136 return -1;
1137
1138 ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
1139 memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN);
1140 dhd->set_macaddress = TRUE;
1141 up(&dhd->thr_sysioc_ctl.sema);
1142
1143 return ret;
1144}
1145
1146static void
1147dhd_set_multicast_list(struct net_device *dev)
1148{
1149 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1150 int ifidx;
1151
1152 ifidx = dhd_net2idx(dhd, dev);
1153 if (ifidx == DHD_BAD_IF)
1154 return;
1155
1156 ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
1157 dhd->iflist[ifidx]->set_multicast = TRUE;
1158 up(&dhd->thr_sysioc_ctl.sema);
1159}
1160
1161#ifdef PROP_TXSTATUS
1162int
1163dhd_os_wlfc_block(dhd_pub_t *pub)
1164{
1165 dhd_info_t *di = (dhd_info_t *)(pub->info);
1166 ASSERT(di != NULL);
1167 spin_lock_bh(&di->wlfc_spinlock);
1168 return 1;
1169}
1170
1171int
1172dhd_os_wlfc_unblock(dhd_pub_t *pub)
1173{
1174 dhd_info_t *di = (dhd_info_t *)(pub->info);
1175 ASSERT(di != NULL);
1176 spin_unlock_bh(&di->wlfc_spinlock);
1177 return 1;
1178}
1179
1180const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 };
1181uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
1182#define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]]
1183
1184#endif /* PROP_TXSTATUS */
1185int
1186dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
1187{
1188 int ret;
1189 dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1190 struct ether_header *eh = NULL;
1191
1192 /* Reject if down */
1193 if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
1194 /* free the packet here since the caller won't */
1195 PKTFREE(dhdp->osh, pktbuf, TRUE);
1196 return -ENODEV;
1197 }
1198
1199 /* Update multicast statistic */
1200 if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) {
1201 uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf);
1202 eh = (struct ether_header *)pktdata;
1203
1204 if (ETHER_ISMULTI(eh->ether_dhost))
1205 dhdp->tx_multicast++;
1206 if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X)
1207 atomic_inc(&dhd->pend_8021x_cnt);
1208 }
1209
1210 /* Look into the packet and update the packet priority */
1211 if (PKTPRIO(pktbuf) == 0)
1212 pktsetprio(pktbuf, FALSE);
1213
1214#ifdef PROP_TXSTATUS
1215 if (dhdp->wlfc_state) {
1216 /* store the interface ID */
1217 DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx);
1218
1219 /* store destination MAC in the tag as well */
1220 DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost);
1221
1222 /* decide which FIFO this packet belongs to */
1223 if (ETHER_ISMULTI(eh->ether_dhost))
1224 /* one additional queue index (highest AC + 1) is used for bc/mc queue */
1225 DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT);
1226 else
1227 DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf)));
1228 } else
1229#endif /* PROP_TXSTATUS */
1230 /* If the protocol uses a data header, apply it */
1231 dhd_prot_hdrpush(dhdp, ifidx, pktbuf);
1232
1233 /* Use bus module to send data frame */
1234#ifdef WLMEDIA_HTSF
1235 dhd_htsf_addtxts(dhdp, pktbuf);
1236#endif
1237#ifdef PROP_TXSTATUS
1238 if (dhdp->wlfc_state && ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode
1239 != WLFC_FCMODE_NONE) {
1240 dhd_os_wlfc_block(dhdp);
1241 ret = dhd_wlfc_enque_sendq(dhdp->wlfc_state, DHD_PKTTAG_FIFO(PKTTAG(pktbuf)),
1242 pktbuf);
1243 dhd_wlfc_commit_packets(dhdp->wlfc_state, (f_commitpkt_t)dhd_bus_txdata,
1244 dhdp->bus);
1245 if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) {
1246 ((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0;
1247 }
1248 dhd_os_wlfc_unblock(dhdp);
1249 }
1250 else
1251 /* non-proptxstatus way */
1252 ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1253#else
1254 ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1255#endif /* PROP_TXSTATUS */
1256
1257
1258 return ret;
1259}
1260
1261int
1262dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
1263{
1264 int ret;
1265 void *pktbuf;
1266 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1267 int ifidx;
1268#ifdef WLMEDIA_HTSF
1269 uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz;
1270#else
1271 uint8 htsfdlystat_sz = 0;
1272#endif
1273
1274 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1275
1276 DHD_OS_WAKE_LOCK(&dhd->pub);
1277
1278 /* Reject if down */
1279 if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) {
1280 DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n",
1281 __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
1282 netif_stop_queue(net);
1283 /* Send Event when bus down detected during data session */
1284 if (dhd->pub.busstate == DHD_BUS_DOWN) {
1285 DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__));
1286 net_os_send_hang_message(net);
1287 }
1288 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1289 return -ENODEV;
1290 }
1291
1292 ifidx = dhd_net2idx(dhd, net);
1293 if (ifidx == DHD_BAD_IF) {
1294 DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx));
1295 netif_stop_queue(net);
1296 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1297 return -ENODEV;
1298 }
1299
1300 /* Make sure there's enough room for any header */
1301
1302 if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) {
1303 struct sk_buff *skb2;
1304
1305 DHD_INFO(("%s: insufficient headroom\n",
1306 dhd_ifname(&dhd->pub, ifidx)));
1307 dhd->pub.tx_realloc++;
1308
1309 skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz);
1310
1311 dev_kfree_skb(skb);
1312 if ((skb = skb2) == NULL) {
1313 DHD_ERROR(("%s: skb_realloc_headroom failed\n",
1314 dhd_ifname(&dhd->pub, ifidx)));
1315 ret = -ENOMEM;
1316 goto done;
1317 }
1318 }
1319
1320 /* Convert to packet */
1321 if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) {
1322 DHD_ERROR(("%s: PKTFRMNATIVE failed\n",
1323 dhd_ifname(&dhd->pub, ifidx)));
1324 dev_kfree_skb_any(skb);
1325 ret = -ENOMEM;
1326 goto done;
1327 }
1328#ifdef WLMEDIA_HTSF
1329 if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) {
1330 uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf);
1331 struct ether_header *eh = (struct ether_header *)pktdata;
1332
1333 if (!ETHER_ISMULTI(eh->ether_dhost) &&
1334 (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) {
1335 eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS);
1336 }
1337 }
1338#endif
1339
1340 ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf);
1341
1342
1343done:
1344 if (ret)
1345 dhd->pub.dstats.tx_dropped++;
1346 else
1347 dhd->pub.tx_packets++;
1348
1349 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1350
1351 /* Return ok: we always eat the packet */
1352 return 0;
1353}
1354
1355void
1356dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
1357{
1358 struct net_device *net;
1359 dhd_info_t *dhd = dhdp->info;
1360 int i;
1361
1362 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1363
1364 dhdp->txoff = state;
1365 ASSERT(dhd);
1366
1367 if (ifidx == ALL_INTERFACES) {
1368 /* Flow control on all active interfaces */
1369 for (i = 0; i < DHD_MAX_IFS; i++) {
1370 if (dhd->iflist[i]) {
1371 net = dhd->iflist[i]->net;
1372 if (state == ON)
1373 netif_stop_queue(net);
1374 else
1375 netif_wake_queue(net);
1376 }
1377 }
1378 }
1379 else {
1380 if (dhd->iflist[ifidx]) {
1381 net = dhd->iflist[ifidx]->net;
1382 if (state == ON)
1383 netif_stop_queue(net);
1384 else
1385 netif_wake_queue(net);
1386 }
1387 }
1388}
1389
1390void
1391dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
1392{
1393 dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1394 struct sk_buff *skb;
1395 uchar *eth;
1396 uint len;
1397 void *data, *pnext = NULL, *save_pktbuf;
1398 int i;
1399 dhd_if_t *ifp;
1400 wl_event_msg_t event;
1401 int tout = DHD_PACKET_TIMEOUT;
1402
1403 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1404
1405 save_pktbuf = pktbuf;
1406
1407 for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) {
1408 struct ether_header *eh;
1409 struct dot11_llc_snap_header *lsh;
1410
1411 ifp = dhd->iflist[ifidx];
1412 if (ifp == NULL) {
1413 DHD_ERROR(("%s: ifp is NULL. drop packet\n",
1414 __FUNCTION__));
1415 PKTFREE(dhdp->osh, pktbuf, TRUE);
1416 continue;
1417 }
1418
1419 /* Dropping packets before registering net device to avoid kernel panic */
1420 if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED ||
1421 !dhd->pub.up) {
1422 DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n",
1423 __FUNCTION__));
1424 PKTFREE(dhdp->osh, pktbuf, TRUE);
1425 continue;
1426 }
1427
1428 pnext = PKTNEXT(dhdp->osh, pktbuf);
1429 PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
1430
1431 eh = (struct ether_header *)PKTDATA(wl->sh.osh, pktbuf);
1432 lsh = (struct dot11_llc_snap_header *)&eh[1];
1433
1434 if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) &&
1435 (PKTLEN(wl->sh.osh, pktbuf) >= RFC1042_HDR_LEN) &&
1436 bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
1437 lsh->type == HTON16(BTA_PROT_L2CAP)) {
1438 amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)
1439 ((uint8 *)eh + RFC1042_HDR_LEN);
1440 ACL_data = NULL;
1441 }
1442
1443#ifdef PROP_TXSTATUS
1444 if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) {
1445 /* WLFC may send header only packet when
1446 there is an urgent message but no packet to
1447 piggy-back on
1448 */
1449 ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++;
1450 PKTFREE(dhdp->osh, pktbuf, TRUE);
1451 continue;
1452 }
1453#endif
1454
1455 skb = PKTTONATIVE(dhdp->osh, pktbuf);
1456
1457 /* Get the protocol, maintain skb around eth_type_trans()
1458 * The main reason for this hack is for the limitation of
1459 * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len'
1460 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
1461 * coping of the packet coming from the network stack to add
1462 * BDC, Hardware header etc, during network interface registration
1463 * we set the 'net->hard_header_len' to ETH_HLEN + extra space required
1464 * for BDC, Hardware header etc. and not just the ETH_HLEN
1465 */
1466 eth = skb->data;
1467 len = skb->len;
1468
1469 ifp = dhd->iflist[ifidx];
1470 if (ifp == NULL)
1471 ifp = dhd->iflist[0];
1472
1473 ASSERT(ifp);
1474 skb->dev = ifp->net;
1475 skb->protocol = eth_type_trans(skb, skb->dev);
1476
1477 if (skb->pkt_type == PACKET_MULTICAST) {
1478 dhd->pub.rx_multicast++;
1479 }
1480
1481 skb->data = eth;
1482 skb->len = len;
1483
1484#ifdef WLMEDIA_HTSF
1485 dhd_htsf_addrxts(dhdp, pktbuf);
1486#endif
1487 /* Strip header, count, deliver upward */
1488 skb_pull(skb, ETH_HLEN);
1489
1490 /* Process special event packets and then discard them */
1491 if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) {
1492 dhd_wl_host_event(dhd, &ifidx,
1493#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
1494 skb->mac_header,
1495#else
1496 skb->mac.raw,
1497#endif
1498 &event,
1499 &data);
1500
1501 wl_event_to_host_order(&event);
1502 if (event.event_type == WLC_E_BTA_HCI_EVENT) {
1503 dhd_bta_doevt(dhdp, data, event.datalen);
1504 }
1505 tout = DHD_EVENT_TIMEOUT;
1506 }
1507
1508 ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
1509 if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
1510 ifp = dhd->iflist[ifidx];
1511
1512 if (ifp->net)
1513 ifp->net->last_rx = jiffies;
1514
1515 dhdp->dstats.rx_bytes += skb->len;
1516 dhdp->rx_packets++; /* Local count */
1517
1518 if (in_interrupt()) {
1519 netif_rx(skb);
1520 } else {
1521 /* If the receive is not processed inside an ISR,
1522 * the softirqd must be woken explicitly to service
1523 * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled
1524 * by netif_rx_ni(), but in earlier kernels, we need
1525 * to do it manually.
1526 */
1527#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1528 netif_rx_ni(skb);
1529#else
1530 ulong flags;
1531 netif_rx(skb);
1532 local_irq_save(flags);
1533 RAISE_RX_SOFTIRQ();
1534 local_irq_restore(flags);
1535#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
1536 }
1537 }
1538 DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(dhdp, tout);
1539}
1540
1541void
1542dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
1543{
1544 /* Linux version has nothing to do */
1545 return;
1546}
1547
1548void
1549dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success)
1550{
1551 uint ifidx;
1552 dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1553 struct ether_header *eh;
1554 uint16 type;
1555 uint len;
1556
1557 dhd_prot_hdrpull(dhdp, &ifidx, txp);
1558
1559 eh = (struct ether_header *)PKTDATA(dhdp->osh, txp);
1560 type = ntoh16(eh->ether_type);
1561
1562 if (type == ETHER_TYPE_802_1X)
1563 atomic_dec(&dhd->pend_8021x_cnt);
1564
1565 /* Crack open the packet and check to see if it is BT HCI ACL data packet.
1566 * If yes generate packet completion event.
1567 */
1568 len = PKTLEN(dhdp->osh, txp);
1569
1570 /* Generate ACL data tx completion event locally to avoid SDIO bus transaction */
1571 if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) {
1572 struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1];
1573
1574 if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
1575 ntoh16(lsh->type) == BTA_PROT_L2CAP) {
1576
1577 dhd_bta_tx_hcidata_complete(dhdp, txp, success);
1578 }
1579 }
1580}
1581
1582static struct net_device_stats *
1583dhd_get_stats(struct net_device *net)
1584{
1585 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1586 dhd_if_t *ifp;
1587 int ifidx;
1588
1589 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1590
1591 ifidx = dhd_net2idx(dhd, net);
1592 if (ifidx == DHD_BAD_IF)
1593 return NULL;
1594
1595 ifp = dhd->iflist[ifidx];
1596 ASSERT(dhd && ifp);
1597
1598 if (dhd->pub.up) {
1599 /* Use the protocol to get dongle stats */
1600 dhd_prot_dstats(&dhd->pub);
1601 }
1602
1603 /* Copy dongle stats to net device stats */
1604 ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
1605 ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
1606 ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
1607 ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
1608 ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
1609 ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
1610 ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
1611 ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
1612 ifp->stats.multicast = dhd->pub.dstats.multicast;
1613
1614 return &ifp->stats;
1615}
1616
1617#ifdef DHDTHREAD
1618static int
1619dhd_watchdog_thread(void *data)
1620{
1621 tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1622 dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1623 /* This thread doesn't need any user-level access,
1624 * so get rid of all our resources
1625 */
1626 if (dhd_watchdog_prio > 0) {
1627 struct sched_param param;
1628 param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)?
1629 dhd_watchdog_prio:(MAX_RT_PRIO-1);
1630 setScheduler(current, SCHED_FIFO, &param);
1631 }
1632
1633 DAEMONIZE("dhd_watchdog");
1634
1635 /* Run until signal received */
1636 complete(&tsk->completed);
1637
1638 while (1)
1639 if (down_interruptible (&tsk->sema) == 0) {
1640 unsigned long flags;
1641
1642 SMP_RD_BARRIER_DEPENDS();
1643 if (tsk->terminated) {
1644 break;
1645 }
1646
1647 dhd_os_sdlock(&dhd->pub);
1648 if (dhd->pub.dongle_reset == FALSE) {
1649 DHD_TIMER(("%s:\n", __FUNCTION__));
1650
1651 /* Call the bus module watchdog */
1652 dhd_bus_watchdog(&dhd->pub);
1653
1654 flags = dhd_os_spin_lock(&dhd->pub);
1655 /* Count the tick for reference */
1656 dhd->pub.tickcnt++;
1657 /* Reschedule the watchdog */
1658 if (dhd->wd_timer_valid)
1659 mod_timer(&dhd->timer,
1660 jiffies + dhd_watchdog_ms * HZ / 1000);
1661 dhd_os_spin_unlock(&dhd->pub, flags);
1662 }
1663 dhd_os_sdunlock(&dhd->pub);
1664 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1665 } else {
1666 break;
1667 }
1668
1669 complete_and_exit(&tsk->completed, 0);
1670}
1671#endif /* DHDTHREAD */
1672
1673static void dhd_watchdog(ulong data)
1674{
1675 dhd_info_t *dhd = (dhd_info_t *)data;
1676 unsigned long flags;
1677
1678 DHD_OS_WAKE_LOCK(&dhd->pub);
1679 if (dhd->pub.dongle_reset) {
1680 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1681 return;
1682 }
1683
1684#ifdef DHDTHREAD
1685 if (dhd->thr_wdt_ctl.thr_pid >= 0) {
1686 up(&dhd->thr_wdt_ctl.sema);
1687 return;
1688 }
1689#endif /* DHDTHREAD */
1690
1691 dhd_os_sdlock(&dhd->pub);
1692 /* Call the bus module watchdog */
1693 dhd_bus_watchdog(&dhd->pub);
1694
1695 flags = dhd_os_spin_lock(&dhd->pub);
1696 /* Count the tick for reference */
1697 dhd->pub.tickcnt++;
1698
1699 /* Reschedule the watchdog */
1700 if (dhd->wd_timer_valid)
1701 mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
1702 dhd_os_spin_unlock(&dhd->pub, flags);
1703 dhd_os_sdunlock(&dhd->pub);
1704 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1705}
1706
1707#ifdef DHDTHREAD
1708static int
1709dhd_dpc_thread(void *data)
1710{
1711 tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1712 dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1713
1714 /* This thread doesn't need any user-level access,
1715 * so get rid of all our resources
1716 */
1717 if (dhd_dpc_prio > 0)
1718 {
1719 struct sched_param param;
1720 param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1);
1721 setScheduler(current, SCHED_FIFO, &param);
1722 }
1723
1724 DAEMONIZE("dhd_dpc");
1725 /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */
1726
1727 /* signal: thread has started */
1728 complete(&tsk->completed);
1729
1730 /* Run until signal received */
1731 while (1) {
1732 if (down_interruptible(&tsk->sema) == 0) {
1733
1734 SMP_RD_BARRIER_DEPENDS();
1735 if (tsk->terminated) {
1736 break;
1737 }
1738
1739 /* Call bus dpc unless it indicated down (then clean stop) */
1740 if (dhd->pub.busstate != DHD_BUS_DOWN) {
1741 if (dhd_bus_dpc(dhd->pub.bus)) {
1742 up(&tsk->sema);
1743 }
1744 else {
1745 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1746 }
1747 } else {
1748 if (dhd->pub.up)
1749 dhd_bus_stop(dhd->pub.bus, TRUE);
1750 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1751 }
1752 }
1753 else
1754 break;
1755 }
1756
1757 complete_and_exit(&tsk->completed, 0);
1758}
1759#endif /* DHDTHREAD */
1760
1761static void
1762dhd_dpc(ulong data)
1763{
1764 dhd_info_t *dhd;
1765
1766 dhd = (dhd_info_t *)data;
1767
1768 /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c]
1769 * down below , wake lock is set,
1770 * the tasklet is initialized in dhd_attach()
1771 */
1772 /* Call bus dpc unless it indicated down (then clean stop) */
1773 if (dhd->pub.busstate != DHD_BUS_DOWN) {
1774 if (dhd_bus_dpc(dhd->pub.bus))
1775 tasklet_schedule(&dhd->tasklet);
1776 else
1777 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1778 } else {
1779 dhd_bus_stop(dhd->pub.bus, TRUE);
1780 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1781 }
1782}
1783
1784void
1785dhd_sched_dpc(dhd_pub_t *dhdp)
1786{
1787 dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1788
1789 DHD_OS_WAKE_LOCK(dhdp);
1790#ifdef DHDTHREAD
1791 if (dhd->thr_dpc_ctl.thr_pid >= 0) {
1792 up(&dhd->thr_dpc_ctl.sema);
1793 return;
1794 }
1795#endif /* DHDTHREAD */
1796
1797 tasklet_schedule(&dhd->tasklet);
1798}
1799
1800#ifdef TOE
1801/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */
1802static int
1803dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol)
1804{
1805 wl_ioctl_t ioc;
1806 char buf[32];
1807 int ret;
1808
1809 memset(&ioc, 0, sizeof(ioc));
1810
1811 ioc.cmd = WLC_GET_VAR;
1812 ioc.buf = buf;
1813 ioc.len = (uint)sizeof(buf);
1814 ioc.set = FALSE;
1815
1816 strcpy(buf, "toe_ol");
1817 if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1818 /* Check for older dongle image that doesn't support toe_ol */
1819 if (ret == -EIO) {
1820 DHD_ERROR(("%s: toe not supported by device\n",
1821 dhd_ifname(&dhd->pub, ifidx)));
1822 return -EOPNOTSUPP;
1823 }
1824
1825 DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
1826 return ret;
1827 }
1828
1829 memcpy(toe_ol, buf, sizeof(uint32));
1830 return 0;
1831}
1832
1833/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */
1834static int
1835dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol)
1836{
1837 wl_ioctl_t ioc;
1838 char buf[32];
1839 int toe, ret;
1840
1841 memset(&ioc, 0, sizeof(ioc));
1842
1843 ioc.cmd = WLC_SET_VAR;
1844 ioc.buf = buf;
1845 ioc.len = (uint)sizeof(buf);
1846 ioc.set = TRUE;
1847
1848 /* Set toe_ol as requested */
1849
1850 strcpy(buf, "toe_ol");
1851 memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32));
1852
1853 if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1854 DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
1855 dhd_ifname(&dhd->pub, ifidx), ret));
1856 return ret;
1857 }
1858
1859 /* Enable toe globally only if any components are enabled. */
1860
1861 toe = (toe_ol != 0);
1862
1863 strcpy(buf, "toe");
1864 memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32));
1865
1866 if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1867 DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
1868 return ret;
1869 }
1870
1871 return 0;
1872}
1873#endif /* TOE */
1874
1875#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
1876static void
1877dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
1878{
1879 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1880
1881 sprintf(info->driver, "wl");
1882 sprintf(info->version, "%lu", dhd->pub.drv_version);
1883}
1884
1885struct ethtool_ops dhd_ethtool_ops = {
1886 .get_drvinfo = dhd_ethtool_get_drvinfo
1887};
1888#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
1889
1890
1891#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
1892static int
1893dhd_ethtool(dhd_info_t *dhd, void *uaddr)
1894{
1895 struct ethtool_drvinfo info;
1896 char drvname[sizeof(info.driver)];
1897 uint32 cmd;
1898#ifdef TOE
1899 struct ethtool_value edata;
1900 uint32 toe_cmpnt, csum_dir;
1901 int ret;
1902#endif
1903
1904 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1905
1906 /* all ethtool calls start with a cmd word */
1907 if (copy_from_user(&cmd, uaddr, sizeof (uint32)))
1908 return -EFAULT;
1909
1910 switch (cmd) {
1911 case ETHTOOL_GDRVINFO:
1912 /* Copy out any request driver name */
1913 if (copy_from_user(&info, uaddr, sizeof(info)))
1914 return -EFAULT;
1915 strncpy(drvname, info.driver, sizeof(info.driver));
1916 drvname[sizeof(info.driver)-1] = '\0';
1917
1918 /* clear struct for return */
1919 memset(&info, 0, sizeof(info));
1920 info.cmd = cmd;
1921
1922 /* if dhd requested, identify ourselves */
1923 if (strcmp(drvname, "?dhd") == 0) {
1924 sprintf(info.driver, "dhd");
1925 strcpy(info.version, EPI_VERSION_STR);
1926 }
1927
1928 /* otherwise, require dongle to be up */
1929 else if (!dhd->pub.up) {
1930 DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__));
1931 return -ENODEV;
1932 }
1933
1934 /* finally, report dongle driver type */
1935 else if (dhd->pub.iswl)
1936 sprintf(info.driver, "wl");
1937 else
1938 sprintf(info.driver, "xx");
1939
1940 sprintf(info.version, "%lu", dhd->pub.drv_version);
1941 if (copy_to_user(uaddr, &info, sizeof(info)))
1942 return -EFAULT;
1943 DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__,
1944 (int)sizeof(drvname), drvname, info.driver));
1945 break;
1946
1947#ifdef TOE
1948 /* Get toe offload components from dongle */
1949 case ETHTOOL_GRXCSUM:
1950 case ETHTOOL_GTXCSUM:
1951 if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
1952 return ret;
1953
1954 csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
1955
1956 edata.cmd = cmd;
1957 edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
1958
1959 if (copy_to_user(uaddr, &edata, sizeof(edata)))
1960 return -EFAULT;
1961 break;
1962
1963 /* Set toe offload components in dongle */
1964 case ETHTOOL_SRXCSUM:
1965 case ETHTOOL_STXCSUM:
1966 if (copy_from_user(&edata, uaddr, sizeof(edata)))
1967 return -EFAULT;
1968
1969 /* Read the current settings, update and write back */
1970 if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
1971 return ret;
1972
1973 csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
1974
1975 if (edata.data != 0)
1976 toe_cmpnt |= csum_dir;
1977 else
1978 toe_cmpnt &= ~csum_dir;
1979
1980 if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0)
1981 return ret;
1982
1983 /* If setting TX checksum mode, tell Linux the new mode */
1984 if (cmd == ETHTOOL_STXCSUM) {
1985 if (edata.data)
1986 dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM;
1987 else
1988 dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM;
1989 }
1990
1991 break;
1992#endif /* TOE */
1993
1994 default:
1995 return -EOPNOTSUPP;
1996 }
1997
1998 return 0;
1999}
2000#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
2001
2002static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error)
2003{
2004 if (!dhdp)
2005 return FALSE;
2006 if ((error == -ETIMEDOUT) || ((dhdp->busstate == DHD_BUS_DOWN) &&
2007 (!dhdp->dongle_reset))) {
2008 DHD_ERROR(("%s: Event HANG send up due to re=%d te=%d e=%d s=%d\n", __FUNCTION__,
2009 dhdp->rxcnt_timeout, dhdp->txcnt_timeout, error, dhdp->busstate));
2010 net_os_send_hang_message(net);
2011 return TRUE;
2012 }
2013 return FALSE;
2014}
2015
2016static int
2017dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd)
2018{
2019 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
2020 dhd_ioctl_t ioc;
2021 int bcmerror = 0;
2022 int buflen = 0;
2023 void *buf = NULL;
2024 uint driver = 0;
2025 int ifidx;
2026 int ret;
2027
2028 DHD_OS_WAKE_LOCK(&dhd->pub);
2029
2030 /* send to dongle only if we are not waiting for reload already */
2031 if (dhd->pub.hang_was_sent) {
2032 DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__));
2033 DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT);
2034 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2035 return OSL_ERROR(BCME_DONGLE_DOWN);
2036 }
2037
2038 ifidx = dhd_net2idx(dhd, net);
2039 DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd));
2040
2041 if (ifidx == DHD_BAD_IF) {
2042 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2043 return -1;
2044 }
2045
2046#if defined(CONFIG_BCMDHD_WEXT)
2047 /* linux wireless extensions */
2048 if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) {
2049 /* may recurse, do NOT lock */
2050 ret = wl_iw_ioctl(net, ifr, cmd);
2051 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2052 return ret;
2053 }
2054#endif /* defined(CONFIG_BCMDHD_WEXT) */
2055
2056#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
2057 if (cmd == SIOCETHTOOL) {
2058 ret = dhd_ethtool(dhd, (void*)ifr->ifr_data);
2059 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2060 return ret;
2061 }
2062#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
2063
2064 if (cmd == SIOCDEVPRIVATE+1) {
2065 ret = wl_android_priv_cmd(net, ifr, cmd);
2066 dhd_check_hang(net, &dhd->pub, ret);
2067 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2068 return ret;
2069 }
2070
2071 if (cmd != SIOCDEVPRIVATE) {
2072 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2073 return -EOPNOTSUPP;
2074 }
2075
2076 memset(&ioc, 0, sizeof(ioc));
2077
2078 /* Copy the ioc control structure part of ioctl request */
2079 if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) {
2080 bcmerror = -BCME_BADADDR;
2081 goto done;
2082 }
2083
2084 /* Copy out any buffer passed */
2085 if (ioc.buf) {
2086 buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN);
2087 /* optimization for direct ioctl calls from kernel */
2088 /*
2089 if (segment_eq(get_fs(), KERNEL_DS)) {
2090 buf = ioc.buf;
2091 } else {
2092 */
2093 {
2094 if (!(buf = (char*)MALLOC(dhd->pub.osh, buflen))) {
2095 bcmerror = -BCME_NOMEM;
2096 goto done;
2097 }
2098 if (copy_from_user(buf, ioc.buf, buflen)) {
2099 bcmerror = -BCME_BADADDR;
2100 goto done;
2101 }
2102 }
2103 }
2104
2105 /* To differentiate between wl and dhd read 4 more byes */
2106 if ((copy_from_user(&driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t),
2107 sizeof(uint)) != 0)) {
2108 bcmerror = -BCME_BADADDR;
2109 goto done;
2110 }
2111
2112 if (!capable(CAP_NET_ADMIN)) {
2113 bcmerror = -BCME_EPERM;
2114 goto done;
2115 }
2116
2117 /* check for local dhd ioctl and handle it */
2118 if (driver == DHD_IOCTL_MAGIC) {
2119 bcmerror = dhd_ioctl((void *)&dhd->pub, &ioc, buf, buflen);
2120 if (bcmerror)
2121 dhd->pub.bcmerror = bcmerror;
2122 goto done;
2123 }
2124
2125 /* send to dongle (must be up, and wl). */
2126 if (dhd->pub.busstate != DHD_BUS_DATA) {
2127 bcmerror = BCME_DONGLE_DOWN;
2128 goto done;
2129 }
2130
2131 if (!dhd->pub.iswl) {
2132 bcmerror = BCME_DONGLE_DOWN;
2133 goto done;
2134 }
2135
2136 /*
2137 * Flush the TX queue if required for proper message serialization:
2138 * Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to
2139 * prevent M4 encryption and
2140 * intercept WLC_DISASSOC IOCTL - serialize WPS-DONE and WLC_DISASSOC IOCTL to
2141 * prevent disassoc frame being sent before WPS-DONE frame.
2142 */
2143 if (ioc.cmd == WLC_SET_KEY ||
2144 (ioc.cmd == WLC_SET_VAR && ioc.buf != NULL &&
2145 strncmp("wsec_key", ioc.buf, 9) == 0) ||
2146 (ioc.cmd == WLC_SET_VAR && ioc.buf != NULL &&
2147 strncmp("bsscfg:wsec_key", ioc.buf, 15) == 0) ||
2148 ioc.cmd == WLC_DISASSOC)
2149 dhd_wait_pend8021x(net);
2150
2151#ifdef WLMEDIA_HTSF
2152 if (ioc.buf) {
2153 /* short cut wl ioctl calls here */
2154 if (strcmp("htsf", ioc.buf) == 0) {
2155 dhd_ioctl_htsf_get(dhd, 0);
2156 return BCME_OK;
2157 }
2158
2159 if (strcmp("htsflate", ioc.buf) == 0) {
2160 if (ioc.set) {
2161 memset(ts, 0, sizeof(tstamp_t)*TSMAX);
2162 memset(&maxdelayts, 0, sizeof(tstamp_t));
2163 maxdelay = 0;
2164 tspktcnt = 0;
2165 maxdelaypktno = 0;
2166 memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN);
2167 memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN);
2168 memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN);
2169 memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN);
2170 } else {
2171 dhd_dump_latency();
2172 }
2173 return BCME_OK;
2174 }
2175 if (strcmp("htsfclear", ioc.buf) == 0) {
2176 memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN);
2177 memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN);
2178 memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN);
2179 memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN);
2180 htsf_seqnum = 0;
2181 return BCME_OK;
2182 }
2183 if (strcmp("htsfhis", ioc.buf) == 0) {
2184 dhd_dump_htsfhisto(&vi_d1, "H to D");
2185 dhd_dump_htsfhisto(&vi_d2, "D to D");
2186 dhd_dump_htsfhisto(&vi_d3, "D to H");
2187 dhd_dump_htsfhisto(&vi_d4, "H to H");
2188 return BCME_OK;
2189 }
2190 if (strcmp("tsport", ioc.buf) == 0) {
2191 if (ioc.set) {
2192 memcpy(&tsport, ioc.buf + 7, 4);
2193 } else {
2194 DHD_ERROR(("current timestamp port: %d \n", tsport));
2195 }
2196 return BCME_OK;
2197 }
2198 }
2199#endif /* WLMEDIA_HTSF */
2200
2201 bcmerror = dhd_wl_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen);
2202
2203done:
2204 dhd_check_hang(net, &dhd->pub, bcmerror);
2205
2206 if (!bcmerror && buf && ioc.buf) {
2207 if (copy_to_user(ioc.buf, buf, buflen))
2208 bcmerror = -EFAULT;
2209 }
2210
2211 if (buf)
2212 MFREE(dhd->pub.osh, buf, buflen);
2213
2214 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2215
2216 return OSL_ERROR(bcmerror);
2217}
2218
2219#ifdef WL_CFG80211
2220static int
2221dhd_cleanup_virt_ifaces(dhd_info_t *dhd)
2222{
2223 int i = 1; /* Leave ifidx 0 [Primary Interface] */
2224#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2225 int rollback_lock = FALSE;
2226#endif
2227
2228 DHD_TRACE(("%s: Enter \n", __func__));
2229
2230#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2231 /* release lock for unregister_netdev */
2232 if (rtnl_is_locked()) {
2233 rtnl_unlock();
2234 rollback_lock = TRUE;
2235 }
2236#endif
2237
2238 for (i = 1; i < DHD_MAX_IFS; i++) {
2239 if (dhd->iflist[i]) {
2240 DHD_TRACE(("Deleting IF: %d \n", i));
2241 if ((dhd->iflist[i]->state != DHD_IF_DEL) &&
2242 (dhd->iflist[i]->state != DHD_IF_DELETING)) {
2243 dhd->iflist[i]->state = DHD_IF_DEL;
2244 dhd->iflist[i]->idx = i;
2245 dhd_op_if(dhd->iflist[i]);
2246 }
2247 }
2248 }
2249
2250#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2251 if (rollback_lock)
2252 rtnl_lock();
2253#endif
2254
2255 return 0;
2256}
2257#endif /* WL_CFG80211 */
2258
2259static int
2260dhd_stop(struct net_device *net)
2261{
2262 int ifidx;
2263 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
2264 DHD_OS_WAKE_LOCK(&dhd->pub);
2265 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2266 if (dhd->pub.up == 0) {
2267 goto exit;
2268 }
2269 ifidx = dhd_net2idx(dhd, net);
2270
2271#ifdef WL_CFG80211
2272 if (ifidx == 0) {
2273 wl_cfg80211_down();
2274
2275 /*
2276 * For CFG80211: Clean up all the left over virtual interfaces
2277 * when the primary Interface is brought down. [ifconfig wlan0 down]
2278 */
2279 if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) &&
2280 (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
2281 dhd_cleanup_virt_ifaces(dhd);
2282 }
2283 }
2284#endif
2285
2286#ifdef PROP_TXSTATUS
2287 dhd_wlfc_cleanup(&dhd->pub);
2288#endif
2289 /* Set state and stop OS transmissions */
2290 dhd->pub.up = 0;
2291 netif_stop_queue(net);
2292
2293 /* Stop the protocol module */
2294 dhd_prot_stop(&dhd->pub);
2295
2296#if defined(WL_CFG80211)
2297 if (ifidx == 0 && !dhd_download_fw_on_driverload)
2298 wl_android_wifi_off(net);
2299#endif
2300 dhd->pub.hang_was_sent = 0;
2301 dhd->pub.rxcnt_timeout = 0;
2302 dhd->pub.txcnt_timeout = 0;
2303 OLD_MOD_DEC_USE_COUNT;
2304exit:
2305 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2306 return 0;
2307}
2308
2309static int
2310dhd_open(struct net_device *net)
2311{
2312 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
2313
2314#ifdef TOE
2315 uint32 toe_ol;
2316#endif
2317 int ifidx;
2318 int32 ret = 0;
2319
2320 DHD_OS_WAKE_LOCK(&dhd->pub);
2321 /* Update FW path if it was changed */
2322 if ((firmware_path != NULL) && (firmware_path[0] != '\0')) {
2323 if (firmware_path[strlen(firmware_path)-1] == '\n')
2324 firmware_path[strlen(firmware_path)-1] = '\0';
2325 strcpy(fw_path, firmware_path);
2326 firmware_path[0] = '\0';
2327 }
2328
2329#if !defined(WL_CFG80211)
2330 /*
2331 * Force start if ifconfig_up gets called before START command
2332 * We keep WEXT's wl_control_wl_start to provide backward compatibility
2333 * This should be removed in the future
2334 */
2335 wl_control_wl_start(net);
2336#endif
2337
2338 ifidx = dhd_net2idx(dhd, net);
2339 DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
2340
2341 if (ifidx < 0) {
2342 DHD_ERROR(("%s: Error: called with invalid IF\n", __FUNCTION__));
2343 ret = -1;
2344 goto exit;
2345 }
2346
2347 if (!dhd->iflist[ifidx] || dhd->iflist[ifidx]->state == DHD_IF_DEL) {
2348 DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__));
2349 ret = -1;
2350 goto exit;
2351 }
2352
2353 if (ifidx == 0) {
2354 atomic_set(&dhd->pend_8021x_cnt, 0);
2355#if defined(WL_CFG80211)
2356 DHD_ERROR(("\n%s\n", dhd_version));
2357 if (!dhd_download_fw_on_driverload)
2358 wl_android_wifi_on(net);
2359#endif /* defined(WL_CFG80211) */
2360
2361 if (dhd->pub.busstate != DHD_BUS_DATA) {
2362 int ret;
2363
2364 /* try to bring up bus */
2365 if ((ret = dhd_bus_start(&dhd->pub)) != 0) {
2366 DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret));
2367 ret = -1;
2368 goto exit;
2369 }
2370
2371 }
2372
2373 /* dhd_prot_init has been called in dhd_bus_start or wl_android_wifi_on */
2374 memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
2375
2376#ifdef TOE
2377 /* Get current TOE mode from dongle */
2378 if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0)
2379 dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM;
2380 else
2381 dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM;
2382#endif /* TOE */
2383
2384#if defined(WL_CFG80211)
2385 if (unlikely(wl_cfg80211_up())) {
2386 DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__));
2387 ret = -1;
2388 goto exit;
2389 }
2390#endif /* WL_CFG80211 */
2391 }
2392
2393 /* Allow transmit calls */
2394 netif_start_queue(net);
2395 dhd->pub.up = 1;
2396
2397#ifdef BCMDBGFS
2398 dhd_dbg_init(&dhd->pub);
2399#endif
2400
2401 OLD_MOD_INC_USE_COUNT;
2402exit:
2403 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2404 return ret;
2405}
2406
2407osl_t *
2408dhd_osl_attach(void *pdev, uint bustype)
2409{
2410 return osl_attach(pdev, bustype, TRUE);
2411}
2412
2413void
2414dhd_osl_detach(osl_t *osh)
2415{
2416 if (MALLOCED(osh)) {
2417 DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh)));
2418 }
2419 osl_detach(osh);
2420#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2421 up(&dhd_registration_sem);
2422#endif
2423}
2424
2425int
2426dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name,
2427 uint8 *mac_addr, uint32 flags, uint8 bssidx)
2428{
2429 dhd_if_t *ifp;
2430
2431 DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle));
2432
2433 ASSERT(dhd && (ifidx < DHD_MAX_IFS));
2434
2435 ifp = dhd->iflist[ifidx];
2436 if (ifp != NULL) {
2437 if (ifp->net != NULL) {
2438 netif_stop_queue(ifp->net);
2439 unregister_netdev(ifp->net);
2440 free_netdev(ifp->net);
2441 }
2442 } else
2443 if ((ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t))) == NULL) {
2444 DHD_ERROR(("%s: OOM - dhd_if_t\n", __FUNCTION__));
2445 return -ENOMEM;
2446 }
2447
2448 memset(ifp, 0, sizeof(dhd_if_t));
2449 ifp->info = dhd;
2450 dhd->iflist[ifidx] = ifp;
2451 strncpy(ifp->name, name, IFNAMSIZ);
2452 ifp->name[IFNAMSIZ] = '\0';
2453 if (mac_addr != NULL)
2454 memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN);
2455
2456 if (handle == NULL) {
2457 ifp->state = DHD_IF_ADD;
2458 ifp->idx = ifidx;
2459 ifp->bssidx = bssidx;
2460 ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
2461 up(&dhd->thr_sysioc_ctl.sema);
2462 } else
2463 ifp->net = (struct net_device *)handle;
2464
2465 return 0;
2466}
2467
2468void
2469dhd_del_if(dhd_info_t *dhd, int ifidx)
2470{
2471 dhd_if_t *ifp;
2472
2473 DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx));
2474
2475 ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS));
2476 ifp = dhd->iflist[ifidx];
2477 if (!ifp) {
2478 DHD_ERROR(("%s: Null interface\n", __FUNCTION__));
2479 return;
2480 }
2481
2482 ifp->state = DHD_IF_DEL;
2483 ifp->idx = ifidx;
2484 ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
2485 up(&dhd->thr_sysioc_ctl.sema);
2486}
2487
2488dhd_pub_t *
2489dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev)
2490{
2491 dhd_info_t *dhd = NULL;
2492 struct net_device *net = NULL;
2493
2494 dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT;
2495 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2496
2497 /* updates firmware nvram path if it was provided as module parameters */
2498 if ((firmware_path != NULL) && (firmware_path[0] != '\0'))
2499 strcpy(fw_path, firmware_path);
2500 if ((nvram_path != NULL) && (nvram_path[0] != '\0'))
2501 strcpy(nv_path, nvram_path);
2502
2503 /* Allocate etherdev, including space for private structure */
2504 if (!(net = alloc_etherdev(sizeof(dhd)))) {
2505 DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
2506 goto fail;
2507 }
2508 dhd_state |= DHD_ATTACH_STATE_NET_ALLOC;
2509
2510 SET_NETDEV_DEV(net, (struct device *)dev);
2511 /* Allocate primary dhd_info */
2512 if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) {
2513 DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__));
2514 goto fail;
2515 }
2516 memset(dhd, 0, sizeof(dhd_info_t));
2517
2518#ifdef DHDTHREAD
2519 dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID;
2520 dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID;
2521#else
2522 dhd->dhd_tasklet_create = FALSE;
2523#endif /* DHDTHREAD */
2524 dhd->thr_sysioc_ctl.thr_pid = DHD_PID_KT_INVALID;
2525 dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC;
2526
2527 /*
2528 * Save the dhd_info into the priv
2529 */
2530 memcpy((void *)netdev_priv(net), &dhd, sizeof(dhd));
2531 dhd->pub.osh = osh;
2532
2533 /* Link to info module */
2534 dhd->pub.info = dhd;
2535 /* Link to bus module */
2536 dhd->pub.bus = bus;
2537 dhd->pub.hdrlen = bus_hdrlen;
2538
2539 /* Set network interface name if it was provided as module parameter */
2540 if (iface_name[0]) {
2541 int len;
2542 char ch;
2543 strncpy(net->name, iface_name, IFNAMSIZ);
2544 net->name[IFNAMSIZ - 1] = 0;
2545 len = strlen(net->name);
2546 ch = net->name[len - 1];
2547 if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2))
2548 strcat(net->name, "%d");
2549 }
2550
2551 if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF)
2552 goto fail;
2553 dhd_state |= DHD_ATTACH_STATE_ADD_IF;
2554
2555#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
2556 net->open = NULL;
2557#else
2558 net->netdev_ops = NULL;
2559#endif
2560
2561 sema_init(&dhd->proto_sem, 1);
2562
2563#ifdef PROP_TXSTATUS
2564 spin_lock_init(&dhd->wlfc_spinlock);
2565 dhd->pub.wlfc_enabled = TRUE;
2566#endif /* PROP_TXSTATUS */
2567
2568 /* Initialize other structure content */
2569 init_waitqueue_head(&dhd->ioctl_resp_wait);
2570 init_waitqueue_head(&dhd->ctrl_wait);
2571
2572 /* Initialize the spinlocks */
2573 spin_lock_init(&dhd->sdlock);
2574 spin_lock_init(&dhd->txqlock);
2575 spin_lock_init(&dhd->dhd_lock);
2576
2577 /* Initialize Wakelock stuff */
2578 spin_lock_init(&dhd->wakelock_spinlock);
2579 dhd->wakelock_counter = 0;
2580 dhd->wakelock_timeout_enable = 0;
2581#ifdef CONFIG_HAS_WAKELOCK
2582 wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake");
2583 wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake");
2584#endif
2585#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
2586 mutex_init(&dhd->dhd_net_if_mutex);
2587#endif
2588 dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT;
2589
2590 /* Attach and link in the protocol */
2591 if (dhd_prot_attach(&dhd->pub) != 0) {
2592 DHD_ERROR(("dhd_prot_attach failed\n"));
2593 goto fail;
2594 }
2595 dhd_state |= DHD_ATTACH_STATE_PROT_ATTACH;
2596
2597#ifdef WL_CFG80211
2598 /* Attach and link in the cfg80211 */
2599 if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) {
2600 DHD_ERROR(("wl_cfg80211_attach failed\n"));
2601 goto fail;
2602 }
2603
2604 dhd_monitor_init(&dhd->pub);
2605 dhd_state |= DHD_ATTACH_STATE_CFG80211;
2606#endif
2607#if defined(CONFIG_BCMDHD_WEXT)
2608 /* Attach and link in the iw */
2609 if (!(dhd_state & DHD_ATTACH_STATE_CFG80211)) {
2610 if (wl_iw_attach(net, (void *)&dhd->pub) != 0) {
2611 DHD_ERROR(("wl_iw_attach failed\n"));
2612 goto fail;
2613 }
2614 dhd_state |= DHD_ATTACH_STATE_WL_ATTACH;
2615 }
2616#endif /* defined(CONFIG_BCMDHD_WEXT) */
2617
2618
2619 /* Set up the watchdog timer */
2620 init_timer(&dhd->timer);
2621 dhd->timer.data = (ulong)dhd;
2622 dhd->timer.function = dhd_watchdog;
2623
2624#ifdef DHDTHREAD
2625 /* Initialize thread based operation and lock */
2626 sema_init(&dhd->sdsem, 1);
2627 if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) {
2628 dhd->threads_only = TRUE;
2629 }
2630 else {
2631 dhd->threads_only = FALSE;
2632 }
2633
2634 if (dhd_dpc_prio >= 0) {
2635 /* Initialize watchdog thread */
2636 PROC_START(dhd_watchdog_thread, dhd, &dhd->thr_wdt_ctl, 0);
2637 } else {
2638 dhd->thr_wdt_ctl.thr_pid = -1;
2639 }
2640
2641 /* Set up the bottom half handler */
2642 if (dhd_dpc_prio >= 0) {
2643 /* Initialize DPC thread */
2644 PROC_START(dhd_dpc_thread, dhd, &dhd->thr_dpc_ctl, 0);
2645 } else {
2646 /* use tasklet for dpc */
2647 tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd);
2648 dhd->thr_dpc_ctl.thr_pid = -1;
2649 }
2650#else
2651 /* Set up the bottom half handler */
2652 tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd);
2653 dhd->dhd_tasklet_create = TRUE;
2654#endif /* DHDTHREAD */
2655
2656 if (dhd_sysioc) {
2657 PROC_START(_dhd_sysioc_thread, dhd, &dhd->thr_sysioc_ctl, 0);
2658 } else {
2659 dhd->thr_sysioc_ctl.thr_pid = -1;
2660 }
2661 dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED;
2662
2663 /*
2664 * Save the dhd_info into the priv
2665 */
2666 memcpy(netdev_priv(net), &dhd, sizeof(dhd));
2667
2668#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
2669 register_pm_notifier(&dhd_sleep_pm_notifier);
2670#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
2671
2672#ifdef CONFIG_HAS_EARLYSUSPEND
2673 dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20;
2674 dhd->early_suspend.suspend = dhd_early_suspend;
2675 dhd->early_suspend.resume = dhd_late_resume;
2676 register_early_suspend(&dhd->early_suspend);
2677 dhd_state |= DHD_ATTACH_STATE_EARLYSUSPEND_DONE;
2678#endif
2679
2680#ifdef ARP_OFFLOAD_SUPPORT
2681 register_inetaddr_notifier(&dhd_notifier);
2682#endif /* ARP_OFFLOAD_SUPPORT */
2683
2684 dhd_state |= DHD_ATTACH_STATE_DONE;
2685 dhd->dhd_state = dhd_state;
2686 return &dhd->pub;
2687
2688fail:
2689 if (dhd_state < DHD_ATTACH_STATE_DHD_ALLOC) {
2690 if (net) free_netdev(net);
2691 } else {
2692 DHD_TRACE(("%s: Calling dhd_detach dhd_state 0x%x &dhd->pub %p\n",
2693 __FUNCTION__, dhd_state, &dhd->pub));
2694 dhd->dhd_state = dhd_state;
2695 dhd_detach(&dhd->pub);
2696 dhd_free(&dhd->pub);
2697 }
2698
2699 return NULL;
2700}
2701
2702int
2703dhd_bus_start(dhd_pub_t *dhdp)
2704{
2705 int ret = -1;
2706 dhd_info_t *dhd = (dhd_info_t*)dhdp->info;
2707 unsigned long flags;
2708
2709 ASSERT(dhd);
2710
2711 DHD_TRACE(("Enter %s:\n", __FUNCTION__));
2712
2713#ifdef DHDTHREAD
2714 dhd_os_sdlock(dhdp);
2715#endif /* DHDTHREAD */
2716
2717 /* try to download image and nvram to the dongle */
2718 if ((dhd->pub.busstate == DHD_BUS_DOWN) &&
2719 (fw_path != NULL) && (fw_path[0] != '\0') &&
2720 (nv_path != NULL) && (nv_path[0] != '\0')) {
2721 /* wake lock moved to dhdsdio_download_firmware */
2722 if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh,
2723 fw_path, nv_path))) {
2724 DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n",
2725 __FUNCTION__, fw_path, nv_path));
2726#ifdef DHDTHREAD
2727 dhd_os_sdunlock(dhdp);
2728#endif /* DHDTHREAD */
2729 return -1;
2730 }
2731 }
2732 if (dhd->pub.busstate != DHD_BUS_LOAD) {
2733#ifdef DHDTHREAD
2734 dhd_os_sdunlock(dhdp);
2735#endif /* DHDTHREAD */
2736 return -ENETDOWN;
2737 }
2738
2739 /* Start the watchdog timer */
2740 dhd->pub.tickcnt = 0;
2741 dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms);
2742
2743 /* Bring up the bus */
2744 if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) {
2745
2746 DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret));
2747#ifdef DHDTHREAD
2748 dhd_os_sdunlock(dhdp);
2749#endif /* DHDTHREAD */
2750 return ret;
2751 }
2752#if defined(OOB_INTR_ONLY)
2753 /* Host registration for OOB interrupt */
2754 if (bcmsdh_register_oob_intr(dhdp)) {
2755 /* deactivate timer and wait for the handler to finish */
2756
2757 flags = dhd_os_spin_lock(&dhd->pub);
2758 dhd->wd_timer_valid = FALSE;
2759 dhd_os_spin_unlock(&dhd->pub, flags);
2760 del_timer_sync(&dhd->timer);
2761
2762 DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__));
2763#ifdef DHDTHREAD
2764 dhd_os_sdunlock(dhdp);
2765#endif /* DHDTHREAD */
2766 return -ENODEV;
2767 }
2768
2769 /* Enable oob at firmware */
2770 dhd_enable_oob_intr(dhd->pub.bus, TRUE);
2771#endif /* defined(OOB_INTR_ONLY) */
2772
2773 /* If bus is not ready, can't come up */
2774 if (dhd->pub.busstate != DHD_BUS_DATA) {
2775 flags = dhd_os_spin_lock(&dhd->pub);
2776 dhd->wd_timer_valid = FALSE;
2777 dhd_os_spin_unlock(&dhd->pub, flags);
2778 del_timer_sync(&dhd->timer);
2779 DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__));
2780#ifdef DHDTHREAD
2781 dhd_os_sdunlock(dhdp);
2782#endif /* DHDTHREAD */
2783 return -ENODEV;
2784 }
2785
2786#ifdef DHDTHREAD
2787 dhd_os_sdunlock(dhdp);
2788#endif /* DHDTHREAD */
2789
2790#ifdef READ_MACADDR
2791 dhd_read_macaddr(dhd);
2792#endif
2793
2794 /* Bus is ready, do any protocol initialization */
2795 if ((ret = dhd_prot_init(&dhd->pub)) < 0)
2796 return ret;
2797
2798#ifdef WRITE_MACADDR
2799 dhd_write_macaddr(dhd->pub.mac.octet);
2800#endif
2801
2802 return 0;
2803}
2804
2805int
2806dhd_preinit_ioctls(dhd_pub_t *dhd)
2807{
2808 int ret = 0;
2809 char eventmask[WL_EVENTING_MASK_LEN];
2810 char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */
2811
2812 uint up = 0;
2813 uint power_mode = PM_FAST;
2814 uint32 dongle_align = DHD_SDALIGN;
2815 uint32 glom = 0;
2816 uint bcn_timeout = 4;
2817 uint retry_max = 3;
2818#if defined(ARP_OFFLOAD_SUPPORT)
2819 int arpoe = 1;
2820#endif
2821 int scan_assoc_time = 40;
2822 int scan_unassoc_time = 40;
2823 int scan_passive_time = 130;
2824 char buf[WLC_IOCTL_SMLEN];
2825 char *ptr;
2826 uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */
2827#if defined(SOFTAP)
2828 uint dtim = 1;
2829#endif
2830#if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211))
2831 uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */
2832#endif
2833
2834#if defined(AP) || defined(WLP2P)
2835 uint32 apsta = 1; /* Enable APSTA mode */
2836#endif /* defined(AP) || defined(WLP2P) */
2837#ifdef GET_CUSTOM_MAC_ENABLE
2838 struct ether_addr ea_addr;
2839#endif /* GET_CUSTOM_MAC_ENABLE */
2840
2841 DHD_TRACE(("Enter %s\n", __FUNCTION__));
2842 dhd->op_mode = 0;
2843#ifdef GET_CUSTOM_MAC_ENABLE
2844 ret = dhd_custom_get_mac_address(ea_addr.octet);
2845 if (!ret) {
2846 memset(buf, 0, sizeof(buf));
2847 bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf));
2848 ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0);
2849 if (ret < 0) {
2850 DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret));
2851 return BCME_NOTUP;
2852 }
2853 } else {
2854#endif /* GET_CUSTOM_MAC_ENABLE */
2855 /* Get the default device MAC address directly from firmware */
2856 memset(buf, 0, sizeof(buf));
2857 bcm_mkiovar("cur_etheraddr", 0, 0, buf, sizeof(buf));
2858 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf),
2859 FALSE, 0)) < 0) {
2860 DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret));
2861 return BCME_NOTUP;
2862 }
2863 /* Update public MAC address after reading from Firmware */
2864 memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
2865#ifdef GET_CUSTOM_MAC_ENABLE
2866 }
2867#endif /* GET_CUSTOM_MAC_ENABLE */
2868
2869#ifdef SET_RANDOM_MAC_SOFTAP
2870 if (strstr(fw_path, "_apsta") != NULL) {
2871 uint rand_mac;
2872
2873 srandom32((uint)jiffies);
2874 rand_mac = random32();
2875 iovbuf[0] = 0x02; /* locally administered bit */
2876 iovbuf[1] = 0x1A;
2877 iovbuf[2] = 0x11;
2878 iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0;
2879 iovbuf[4] = (unsigned char)(rand_mac >> 8);
2880 iovbuf[5] = (unsigned char)(rand_mac >> 16);
2881
2882 bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf));
2883 ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0);
2884 if (ret < 0) {
2885 DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret));
2886 } else
2887 memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN);
2888 }
2889#endif /* SET_RANDOM_MAC_SOFTAP */
2890
2891 DHD_TRACE(("Firmware = %s\n", fw_path));
2892#if !defined(AP) && defined(WLP2P)
2893 /* Check if firmware with WFD support used */
2894 if (strstr(fw_path, "_p2p") != NULL) {
2895 bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf));
2896 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR,
2897 iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
2898 DHD_ERROR(("%s APSTA for WFD failed ret= %d\n", __FUNCTION__, ret));
2899 } else {
2900 dhd->op_mode |= WFD_MASK;
2901#if defined(ARP_OFFLOAD_SUPPORT)
2902 arpoe = 0;
2903#endif /* (ARP_OFFLOAD_SUPPORT) */
2904 dhd_pkt_filter_enable = FALSE;
2905 }
2906 }
2907#endif
2908
2909#if !defined(AP) && defined(WL_CFG80211)
2910 /* Check if firmware with HostAPD support used */
2911 if (strstr(fw_path, "_apsta") != NULL) {
2912 /* Turn off MPC in AP mode */
2913 bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf));
2914 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf,
2915 sizeof(iovbuf), TRUE, 0)) < 0) {
2916 DHD_ERROR(("%s mpc for HostAPD failed %d\n", __FUNCTION__, ret));
2917 } else {
2918 dhd->op_mode |= HOSTAPD_MASK;
2919#if defined(ARP_OFFLOAD_SUPPORT)
2920 arpoe = 0;
2921#endif /* (ARP_OFFLOAD_SUPPORT) */
2922 dhd_pkt_filter_enable = FALSE;
2923 }
2924 }
2925#endif
2926
2927 if ((dhd->op_mode != WFD_MASK) && (dhd->op_mode != HOSTAPD_MASK)) {
2928 /* STA only operation mode */
2929 dhd->op_mode |= STA_MASK;
2930 dhd_pkt_filter_enable = TRUE;
2931 }
2932
2933 DHD_ERROR(("Firmware up: fw_path=%s op_mode=%d, "
2934 "Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
2935 fw_path,
2936 dhd->op_mode,
2937 dhd->mac.octet[0], dhd->mac.octet[1], dhd->mac.octet[2],
2938 dhd->mac.octet[3], dhd->mac.octet[4], dhd->mac.octet[5]));
2939
2940 /* Set Country code */
2941 if (dhd->dhd_cspec.ccode[0] != 0) {
2942 bcm_mkiovar("country", (char *)&dhd->dhd_cspec,
2943 sizeof(wl_country_t), iovbuf, sizeof(iovbuf));
2944 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0)
2945 DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__));
2946 }
2947
2948 /* Set Listen Interval */
2949 bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf));
2950 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0)
2951 DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret));
2952
2953 /* Set PowerSave mode */
2954 dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0);
2955
2956 /* Match Host and Dongle rx alignment */
2957 bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf));
2958 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
2959
2960 /* disable glom option per default */
2961 bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf));
2962 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
2963
2964 /* Setup timeout if Beacons are lost and roam is off to report link down */
2965 bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf));
2966 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
2967 /* Setup assoc_retry_max count to reconnect target AP in dongle */
2968 bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf));
2969 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
2970#if defined(AP) && !defined(WLP2P)
2971 /* Turn off MPC in AP mode */
2972 bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf));
2973 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
2974 bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf));
2975 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
2976#endif /* defined(AP) && !defined(WLP2P) */
2977
2978#if defined(SOFTAP)
2979 if (ap_fw_loaded == TRUE) {
2980 dhd_wl_ioctl_cmd(dhd, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim), TRUE, 0);
2981 }
2982#endif
2983
2984#if defined(KEEP_ALIVE)
2985 {
2986 /* Set Keep Alive : be sure to use FW with -keepalive */
2987 int res;
2988
2989#if defined(SOFTAP)
2990 if (ap_fw_loaded == FALSE)
2991#endif
2992 if ((res = dhd_keep_alive_onoff(dhd)) < 0)
2993 DHD_ERROR(("%s set keeplive failed %d\n",
2994 __FUNCTION__, res));
2995 }
2996#endif /* defined(KEEP_ALIVE) */
2997
2998 /* Read event_msgs mask */
2999 bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
3000 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) {
3001 DHD_ERROR(("%s read Event mask failed %d\n", __FUNCTION__, ret));
3002 goto done;
3003 }
3004 bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN);
3005
3006 /* Setup event_msgs */
3007 setbit(eventmask, WLC_E_SET_SSID);
3008 setbit(eventmask, WLC_E_PRUNE);
3009 setbit(eventmask, WLC_E_AUTH);
3010 setbit(eventmask, WLC_E_REASSOC);
3011 setbit(eventmask, WLC_E_REASSOC_IND);
3012 setbit(eventmask, WLC_E_DEAUTH);
3013 setbit(eventmask, WLC_E_DEAUTH_IND);
3014 setbit(eventmask, WLC_E_DISASSOC_IND);
3015 setbit(eventmask, WLC_E_DISASSOC);
3016 setbit(eventmask, WLC_E_JOIN);
3017 setbit(eventmask, WLC_E_ASSOC_IND);
3018 setbit(eventmask, WLC_E_PSK_SUP);
3019 setbit(eventmask, WLC_E_LINK);
3020 setbit(eventmask, WLC_E_NDIS_LINK);
3021 setbit(eventmask, WLC_E_MIC_ERROR);
3022 setbit(eventmask, WLC_E_PMKID_CACHE);
3023 setbit(eventmask, WLC_E_JOIN_START);
3024 setbit(eventmask, WLC_E_SCAN_COMPLETE);
3025#ifdef WLMEDIA_HTSF
3026 setbit(eventmask, WLC_E_HTSFSYNC);
3027#endif /* WLMEDIA_HTSF */
3028#ifdef PNO_SUPPORT
3029 setbit(eventmask, WLC_E_PFN_NET_FOUND);
3030#endif /* PNO_SUPPORT */
3031 /* enable dongle roaming event */
3032 setbit(eventmask, WLC_E_ROAM);
3033#ifdef WL_CFG80211
3034 setbit(eventmask, WLC_E_ESCAN_RESULT);
3035 if ((dhd->op_mode & WFD_MASK) == WFD_MASK) {
3036 setbit(eventmask, WLC_E_ACTION_FRAME_RX);
3037 setbit(eventmask, WLC_E_ACTION_FRAME_COMPLETE);
3038 setbit(eventmask, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE);
3039 setbit(eventmask, WLC_E_P2P_PROBREQ_MSG);
3040 setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE);
3041 }
3042#endif /* WL_CFG80211 */
3043
3044 /* Write updated Event mask */
3045 bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
3046 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
3047 DHD_ERROR(("%s Set Event mask failed %d\n", __FUNCTION__, ret));
3048 goto done;
3049 }
3050
3051 dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time,
3052 sizeof(scan_assoc_time), TRUE, 0);
3053 dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time,
3054 sizeof(scan_unassoc_time), TRUE, 0);
3055 dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_PASSIVE_TIME, (char *)&scan_passive_time,
3056 sizeof(scan_passive_time), TRUE, 0);
3057
3058#ifdef ARP_OFFLOAD_SUPPORT
3059 /* Set and enable ARP offload feature for STA only */
3060#if defined(SOFTAP)
3061 if (arpoe && !ap_fw_loaded) {
3062#else
3063 if (arpoe) {
3064#endif
3065 dhd_arp_offload_set(dhd, dhd_arp_mode);
3066 dhd_arp_offload_enable(dhd, arpoe);
3067 } else {
3068 dhd_arp_offload_set(dhd, 0);
3069 dhd_arp_offload_enable(dhd, FALSE);
3070 }
3071#endif /* ARP_OFFLOAD_SUPPORT */
3072
3073#ifdef PKT_FILTER_SUPPORT
3074 /* Setup defintions for pktfilter , enable in suspend */
3075 dhd->pktfilter_count = 4;
3076 /* Setup filter to allow only unicast */
3077 dhd->pktfilter[0] = "100 0 0 0 0x01 0x00";
3078 dhd->pktfilter[1] = NULL;
3079 dhd->pktfilter[2] = NULL;
3080 dhd->pktfilter[3] = NULL;
3081#if defined(SOFTAP)
3082 if (ap_fw_loaded) {
3083 int i;
3084 for (i = 0; i < dhd->pktfilter_count; i++) {
3085 dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
3086 0, dhd_master_mode);
3087 }
3088 }
3089#endif /* defined(SOFTAP) */
3090#endif /* PKT_FILTER_SUPPORT */
3091
3092 /* Force STA UP */
3093 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0)) < 0) {
3094 DHD_ERROR(("%s Setting WL UP failed %d\n", __FUNCTION__, ret));
3095 goto done;
3096 }
3097
3098 /* query for 'ver' to get version info from firmware */
3099 memset(buf, 0, sizeof(buf));
3100 ptr = buf;
3101 bcm_mkiovar("ver", (char *)&buf, 4, buf, sizeof(buf));
3102 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0)
3103 DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret));
3104 else {
3105 bcmstrtok(&ptr, "\n", 0);
3106 /* Print fw version info */
3107 DHD_ERROR(("Firmware version = %s\n", buf));
3108 DHD_BLOG(buf, strlen(buf) + 1);
3109 DHD_BLOG(dhd_version, strlen(dhd_version) + 1);
3110 }
3111
3112done:
3113 return ret;
3114}
3115
3116
3117int
3118dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set)
3119{
3120 char buf[strlen(name) + 1 + cmd_len];
3121 int len = sizeof(buf);
3122 wl_ioctl_t ioc;
3123 int ret;
3124
3125 len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len);
3126
3127 memset(&ioc, 0, sizeof(ioc));
3128
3129 ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR;
3130 ioc.buf = buf;
3131 ioc.len = len;
3132 ioc.set = TRUE;
3133
3134 ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len);
3135 if (!set && ret >= 0)
3136 memcpy(cmd_buf, buf, cmd_len);
3137
3138 return ret;
3139}
3140
3141#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
3142static struct net_device_ops dhd_ops_pri = {
3143 .ndo_open = dhd_open,
3144 .ndo_stop = dhd_stop,
3145 .ndo_get_stats = dhd_get_stats,
3146 .ndo_do_ioctl = dhd_ioctl_entry,
3147 .ndo_start_xmit = dhd_start_xmit,
3148 .ndo_set_mac_address = dhd_set_mac_address,
3149 .ndo_set_multicast_list = dhd_set_multicast_list,
3150};
3151
3152static struct net_device_ops dhd_ops_virt = {
3153 .ndo_get_stats = dhd_get_stats,
3154 .ndo_do_ioctl = dhd_ioctl_entry,
3155 .ndo_start_xmit = dhd_start_xmit,
3156 .ndo_set_mac_address = dhd_set_mac_address,
3157 .ndo_set_multicast_list = dhd_set_multicast_list,
3158};
3159#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */
3160
3161int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx)
3162{
3163 struct dhd_info *dhd = dhdp->info;
3164 struct net_device *dev = NULL;
3165
3166 ASSERT(dhd && dhd->iflist[ifidx]);
3167 dev = dhd->iflist[ifidx]->net;
3168 ASSERT(dev);
3169
3170 if (netif_running(dev)) {
3171 DHD_ERROR(("%s: Must be down to change its MTU", dev->name));
3172 return BCME_NOTDOWN;
3173 }
3174
3175#define DHD_MIN_MTU 1500
3176#define DHD_MAX_MTU 1752
3177
3178 if ((new_mtu < DHD_MIN_MTU) || (new_mtu > DHD_MAX_MTU)) {
3179 DHD_ERROR(("%s: MTU size %d is invalid.\n", __FUNCTION__, new_mtu));
3180 return BCME_BADARG;
3181 }
3182
3183 dev->mtu = new_mtu;
3184 return 0;
3185}
3186
3187#ifdef ARP_OFFLOAD_SUPPORT
3188/* add or remove AOE host ip(s) (up to 8 IPs on the interface) */
3189void
3190aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add)
3191{
3192 u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */
3193 int i;
3194 int ret;
3195
3196 bzero(ipv4_buf, sizeof(ipv4_buf));
3197
3198 /* display what we've got */
3199 ret = dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf));
3200 DHD_ARPOE(("%s: hostip table read from Dongle:\n", __FUNCTION__));
3201#ifdef AOE_DBG
3202 dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */
3203#endif
3204 /* now we saved hoste_ip table, clr it in the dongle AOE */
3205 dhd_aoe_hostip_clr(dhd_pub);
3206
3207 if (ret) {
3208 DHD_ERROR(("%s failed\n", __FUNCTION__));
3209 return;
3210 }
3211
3212 for (i = 0; i < MAX_IPV4_ENTRIES; i++) {
3213 if (add && (ipv4_buf[i] == 0)) {
3214 ipv4_buf[i] = ipa;
3215 add = FALSE; /* added ipa to local table */
3216 DHD_ARPOE(("%s: Saved new IP in temp arp_hostip[%d]\n",
3217 __FUNCTION__, i));
3218 } else if (ipv4_buf[i] == ipa) {
3219 ipv4_buf[i] = 0;
3220 DHD_ARPOE(("%s: removed IP:%x from temp table %d\n",
3221 __FUNCTION__, ipa, i));
3222 }
3223
3224 if (ipv4_buf[i] != 0) {
3225 /* add back host_ip entries from our local cache */
3226 dhd_arp_offload_add_ip(dhd_pub, ipv4_buf[i]);
3227 DHD_ARPOE(("%s: added IP:%x to dongle arp_hostip[%d]\n\n",
3228 __FUNCTION__, ipv4_buf[i], i));
3229 }
3230 }
3231#ifdef AOE_DBG
3232 /* see the resulting hostip table */
3233 dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf));
3234 DHD_ARPOE(("%s: read back arp_hostip table:\n", __FUNCTION__));
3235 dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */
3236#endif
3237}
3238
3239static int dhd_device_event(struct notifier_block *this,
3240 unsigned long event,
3241 void *ptr)
3242{
3243 struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
3244
3245 dhd_info_t *dhd;
3246 dhd_pub_t *dhd_pub;
3247
3248 if (!ifa)
3249 return NOTIFY_DONE;
3250
3251 dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev);
3252 dhd_pub = &dhd->pub;
3253
3254#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
3255 if (ifa->ifa_dev->dev->netdev_ops == &dhd_ops_pri) {
3256#else
3257 if (ifa->ifa_dev->dev) {
3258#endif
3259 switch (event) {
3260 case NETDEV_UP:
3261 DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n",
3262 __FUNCTION__, ifa->ifa_label, ifa->ifa_address));
3263
3264 /* firmware not downloaded, do nothing */
3265 if (dhd->pub.busstate == DHD_BUS_DOWN) {
3266 DHD_ERROR(("%s: bus is down, exit\n", __FUNCTION__));
3267 break;
3268 }
3269
3270#ifdef AOE_IP_ALIAS_SUPPORT
3271 if (ifa->ifa_label[strlen(ifa->ifa_label)-2] == 0x3a) {
3272 DHD_ARPOE(("%s:add aliased IP to AOE hostip cache\n",
3273 __FUNCTION__));
3274 aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE);
3275 }
3276 else
3277 aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE);
3278#endif
3279 break;
3280
3281 case NETDEV_DOWN:
3282 DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n",
3283 __FUNCTION__, ifa->ifa_label, ifa->ifa_address));
3284
3285#ifdef AOE_IP_ALIAS_SUPPORT
3286 if (!(ifa->ifa_label[strlen(ifa->ifa_label)-2] == 0x3a)) {
3287 DHD_ARPOE(("%s: primary interface is down, AOE clr all\n",
3288 __FUNCTION__));
3289 dhd_aoe_hostip_clr(&dhd->pub);
3290 dhd_aoe_arp_clr(&dhd->pub);
3291 } else
3292 aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, FALSE);
3293#else
3294 dhd_aoe_hostip_clr(&dhd->pub);
3295 dhd_aoe_arp_clr(&dhd->pub);
3296#endif
3297 break;
3298
3299 default:
3300 DHD_ARPOE(("%s: do noting for [%s] Event: %lu\n",
3301 __func__, ifa->ifa_label, event));
3302 break;
3303 }
3304 }
3305 return NOTIFY_DONE;
3306}
3307#endif /* ARP_OFFLOAD_SUPPORT */
3308
3309int
3310dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
3311{
3312 dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
3313 struct net_device *net = NULL;
3314 int err = 0;
3315 uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 };
3316
3317 DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
3318
3319 ASSERT(dhd && dhd->iflist[ifidx]);
3320
3321 net = dhd->iflist[ifidx]->net;
3322 ASSERT(net);
3323
3324#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
3325 ASSERT(!net->open);
3326 net->get_stats = dhd_get_stats;
3327 net->do_ioctl = dhd_ioctl_entry;
3328 net->hard_start_xmit = dhd_start_xmit;
3329 net->set_mac_address = dhd_set_mac_address;
3330 net->set_multicast_list = dhd_set_multicast_list;
3331 net->open = net->stop = NULL;
3332#else
3333 ASSERT(!net->netdev_ops);
3334 net->netdev_ops = &dhd_ops_virt;
3335#endif
3336
3337 /* Ok, link into the network layer... */
3338 if (ifidx == 0) {
3339 /*
3340 * device functions for the primary interface only
3341 */
3342#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
3343 net->open = dhd_open;
3344 net->stop = dhd_stop;
3345#else
3346 net->netdev_ops = &dhd_ops_pri;
3347#endif
3348 } else {
3349 /*
3350 * We have to use the primary MAC for virtual interfaces
3351 */
3352 memcpy(temp_addr, dhd->iflist[ifidx]->mac_addr, ETHER_ADDR_LEN);
3353 /*
3354 * Android sets the locally administered bit to indicate that this is a
3355 * portable hotspot. This will not work in simultaneous AP/STA mode,
3356 * nor with P2P. Need to set the Donlge's MAC address, and then use that.
3357 */
3358 if (ifidx > 0) {
3359 DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n",
3360 __func__, net->name));
3361 temp_addr[0] |= 0x02;
3362 }
3363 }
3364
3365 net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen;
3366#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
3367 net->ethtool_ops = &dhd_ethtool_ops;
3368#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
3369
3370#if defined(CONFIG_BCMDHD_WEXT)
3371#if WIRELESS_EXT < 19
3372 net->get_wireless_stats = dhd_get_wireless_stats;
3373#endif /* WIRELESS_EXT < 19 */
3374#if WIRELESS_EXT > 12
3375 net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def;
3376#endif /* WIRELESS_EXT > 12 */
3377#endif /* defined(CONFIG_BCMDHD_WEXT) */
3378
3379 dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net);
3380
3381 memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN);
3382
3383 if ((err = register_netdev(net)) != 0) {
3384 DHD_ERROR(("couldn't register the net device, err %d\n", err));
3385 goto fail;
3386 }
3387 printf("Broadcom Dongle Host Driver: register interface [%s]"
3388 " MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
3389 net->name,
3390 net->dev_addr[0], net->dev_addr[1], net->dev_addr[2],
3391 net->dev_addr[3], net->dev_addr[4], net->dev_addr[5]);
3392
3393#if defined(SOFTAP) && defined(CONFIG_BCMDHD_WEXT) && !defined(WL_CFG80211)
3394 wl_iw_iscan_set_scan_broadcast_prep(net, 1);
3395#endif
3396
3397
3398#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3399 if (ifidx == 0) {
3400 up(&dhd_registration_sem);
3401 }
3402#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
3403 return 0;
3404
3405fail:
3406#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
3407 net->open = NULL;
3408#else
3409 net->netdev_ops = NULL;
3410#endif
3411 return err;
3412}
3413
3414void
3415dhd_bus_detach(dhd_pub_t *dhdp)
3416{
3417 dhd_info_t *dhd;
3418
3419 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
3420
3421 if (dhdp) {
3422 dhd = (dhd_info_t *)dhdp->info;
3423 if (dhd) {
3424
3425 /*
3426 * In case of Android cfg80211 driver, the bus is down in dhd_stop,
3427 * calling stop again will cuase SD read/write errors.
3428 */
3429 if (dhd->pub.busstate != DHD_BUS_DOWN) {
3430 /* Stop the protocol module */
3431 dhd_prot_stop(&dhd->pub);
3432
3433 /* Stop the bus module */
3434 dhd_bus_stop(dhd->pub.bus, TRUE);
3435 }
3436
3437#if defined(OOB_INTR_ONLY)
3438 bcmsdh_unregister_oob_intr();
3439#endif /* defined(OOB_INTR_ONLY) */
3440 }
3441 }
3442}
3443
3444
3445void dhd_detach(dhd_pub_t *dhdp)
3446{
3447 dhd_info_t *dhd;
3448 unsigned long flags;
3449 int timer_valid = FALSE;
3450
3451 if (!dhdp)
3452 return;
3453
3454 dhd = (dhd_info_t *)dhdp->info;
3455 if (!dhd)
3456 return;
3457
3458 DHD_TRACE(("%s: Enter state 0x%x\n", __FUNCTION__, dhd->dhd_state));
3459
3460 if (!(dhd->dhd_state & DHD_ATTACH_STATE_DONE)) {
3461 /* Give sufficient time for threads to start running in case
3462 * dhd_attach() has failed
3463 */
3464 osl_delay(1000*100);
3465 }
3466
3467#ifdef ARP_OFFLOAD_SUPPORT
3468 unregister_inetaddr_notifier(&dhd_notifier);
3469#endif /* ARP_OFFLOAD_SUPPORT */
3470
3471#if defined(CONFIG_HAS_EARLYSUSPEND)
3472 if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) {
3473 if (dhd->early_suspend.suspend)
3474 unregister_early_suspend(&dhd->early_suspend);
3475 }
3476#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
3477
3478#if defined(CONFIG_BCMDHD_WEXT)
3479 if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) {
3480 /* Detatch and unlink in the iw */
3481 wl_iw_detach();
3482 }
3483#endif /* defined(CONFIG_BCMDHD_WEXT) */
3484
3485 if (&dhd->thr_sysioc_ctl.thr_pid >= 0) {
3486 PROC_STOP(&dhd->thr_sysioc_ctl);
3487 }
3488
3489 /* delete all interfaces, start with virtual */
3490 if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) {
3491 int i = 1;
3492 dhd_if_t *ifp;
3493
3494 /* Cleanup virtual interfaces */
3495 for (i = 1; i < DHD_MAX_IFS; i++)
3496 if (dhd->iflist[i]) {
3497 dhd->iflist[i]->state = DHD_IF_DEL;
3498 dhd->iflist[i]->idx = i;
3499 dhd_op_if(dhd->iflist[i]);
3500 }
3501
3502 /* delete primary interface 0 */
3503 ifp = dhd->iflist[0];
3504 ASSERT(ifp);
3505
3506#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
3507 if (ifp->net->open)
3508#else
3509 if (ifp->net->netdev_ops == &dhd_ops_pri)
3510#endif
3511 {
3512 if (ifp->net) {
3513 unregister_netdev(ifp->net);
3514 free_netdev(ifp->net);
3515 ifp->net = NULL;
3516 }
3517 MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
3518 dhd->iflist[0] = NULL;
3519 }
3520 }
3521
3522 /* Clear the watchdog timer */
3523 flags = dhd_os_spin_lock(&dhd->pub);
3524 timer_valid = dhd->wd_timer_valid;
3525 dhd->wd_timer_valid = FALSE;
3526 dhd_os_spin_unlock(&dhd->pub, flags);
3527 if (timer_valid)
3528 del_timer_sync(&dhd->timer);
3529
3530 if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) {
3531#ifdef DHDTHREAD
3532 if (dhd->thr_wdt_ctl.thr_pid >= 0) {
3533 PROC_STOP(&dhd->thr_wdt_ctl);
3534 }
3535
3536 if (dhd->thr_dpc_ctl.thr_pid >= 0) {
3537 PROC_STOP(&dhd->thr_dpc_ctl);
3538 }
3539 else
3540#endif /* DHDTHREAD */
3541 tasklet_kill(&dhd->tasklet);
3542 }
3543 if (dhd->dhd_state & DHD_ATTACH_STATE_PROT_ATTACH) {
3544 dhd_bus_detach(dhdp);
3545
3546 if (dhdp->prot)
3547 dhd_prot_detach(dhdp);
3548 }
3549
3550#ifdef WL_CFG80211
3551 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
3552 wl_cfg80211_detach();
3553 dhd_monitor_uninit();
3554 }
3555#endif
3556
3557#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
3558 unregister_pm_notifier(&dhd_sleep_pm_notifier);
3559#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
3560
3561 if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) {
3562#ifdef CONFIG_HAS_WAKELOCK
3563 wake_lock_destroy(&dhd->wl_wifi);
3564 wake_lock_destroy(&dhd->wl_rxwake);
3565#endif
3566 }
3567}
3568
3569
3570void
3571dhd_free(dhd_pub_t *dhdp)
3572{
3573 dhd_info_t *dhd;
3574 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
3575
3576 if (dhdp) {
3577 dhd = (dhd_info_t *)dhdp->info;
3578 if (dhd)
3579 MFREE(dhd->pub.osh, dhd, sizeof(*dhd));
3580 }
3581}
3582
3583static void __exit
3584dhd_module_cleanup(void)
3585{
3586 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
3587
3588 dhd_bus_unregister();
3589
3590#if defined(CONFIG_WIFI_CONTROL_FUNC)
3591 wl_android_wifictrl_func_del();
3592#endif /* CONFIG_WIFI_CONTROL_FUNC */
3593 wl_android_exit();
3594
3595 /* Call customer gpio to turn off power with WL_REG_ON signal */
3596 dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
3597}
3598
3599
3600static int __init
3601dhd_module_init(void)
3602{
3603 int error = 0;
3604
3605 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
3606
3607 wl_android_init();
3608
3609#ifdef DHDTHREAD
3610 /* Sanity check on the module parameters */
3611 do {
3612 /* Both watchdog and DPC as tasklets are ok */
3613 if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0))
3614 break;
3615
3616 /* If both watchdog and DPC are threads, TX must be deferred */
3617 if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx)
3618 break;
3619
3620 DHD_ERROR(("Invalid module parameters.\n"));
3621 return -EINVAL;
3622 } while (0);
3623#endif /* DHDTHREAD */
3624
3625 /* Call customer gpio to turn on power with WL_REG_ON signal */
3626 dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON);
3627
3628#if defined(CONFIG_WIFI_CONTROL_FUNC)
3629 if (wl_android_wifictrl_func_add() < 0)
3630 goto fail_1;
3631#endif
3632
3633#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3634 sema_init(&dhd_registration_sem, 0);
3635#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
3636 error = dhd_bus_register();
3637
3638 if (!error)
3639 printf("\n%s\n", dhd_version);
3640 else {
3641 DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__));
3642 goto fail_1;
3643 }
3644
3645#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3646 /*
3647 * Wait till MMC sdio_register_driver callback called and made driver attach.
3648 * It's needed to make sync up exit from dhd insmod and
3649 * Kernel MMC sdio device callback registration
3650 */
3651 if (down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) {
3652 error = -EINVAL;
3653 DHD_ERROR(("%s: sdio_register_driver timeout\n", __FUNCTION__));
3654 goto fail_2;
3655 }
3656#endif
3657#if defined(WL_CFG80211)
3658 error = wl_android_post_init();
3659#endif
3660
3661 return error;
3662#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && 1
3663fail_2:
3664 dhd_bus_unregister();
3665#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
3666fail_1:
3667#if defined(CONFIG_WIFI_CONTROL_FUNC)
3668 wl_android_wifictrl_func_del();
3669#endif
3670
3671 /* Call customer gpio to turn off power with WL_REG_ON signal */
3672 dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
3673
3674 return error;
3675}
3676
3677late_initcall(dhd_module_init);
3678module_exit(dhd_module_cleanup);
3679
3680/*
3681 * OS specific functions required to implement DHD driver in OS independent way
3682 */
3683int
3684dhd_os_proto_block(dhd_pub_t *pub)
3685{
3686 dhd_info_t * dhd = (dhd_info_t *)(pub->info);
3687
3688 if (dhd) {
3689 down(&dhd->proto_sem);
3690 return 1;
3691 }
3692
3693 return 0;
3694}
3695
3696int
3697dhd_os_proto_unblock(dhd_pub_t *pub)
3698{
3699 dhd_info_t * dhd = (dhd_info_t *)(pub->info);
3700
3701 if (dhd) {
3702 up(&dhd->proto_sem);
3703 return 1;
3704 }
3705
3706 return 0;
3707}
3708
3709unsigned int
3710dhd_os_get_ioctl_resp_timeout(void)
3711{
3712 return ((unsigned int)dhd_ioctl_timeout_msec);
3713}
3714
3715void
3716dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec)
3717{
3718 dhd_ioctl_timeout_msec = (int)timeout_msec;
3719}
3720
3721int
3722dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending)
3723{
3724 dhd_info_t * dhd = (dhd_info_t *)(pub->info);
3725 DECLARE_WAITQUEUE(wait, current);
3726 int timeout = dhd_ioctl_timeout_msec;
3727
3728 /* Convert timeout in millsecond to jiffies */
3729#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3730 timeout = msecs_to_jiffies(timeout);
3731#else
3732 timeout = timeout * HZ / 1000;
3733#endif
3734
3735 /* Wait until control frame is available */
3736 add_wait_queue(&dhd->ioctl_resp_wait, &wait);
3737 set_current_state(TASK_INTERRUPTIBLE);
3738
3739 /* Memory barrier to support multi-processing
3740 * As the variable "condition", which points to dhd->rxlen (dhd_bus_rxctl[dhd_sdio.c])
3741 * Can be changed by another processor.
3742 */
3743 smp_mb();
3744 while (!(*condition) && (!signal_pending(current) && timeout)) {
3745 timeout = schedule_timeout(timeout);
3746 smp_mb();
3747 }
3748
3749 if (signal_pending(current))
3750 *pending = TRUE;
3751
3752 set_current_state(TASK_RUNNING);
3753 remove_wait_queue(&dhd->ioctl_resp_wait, &wait);
3754
3755 return timeout;
3756}
3757
3758int
3759dhd_os_ioctl_resp_wake(dhd_pub_t *pub)
3760{
3761 dhd_info_t *dhd = (dhd_info_t *)(pub->info);
3762
3763 if (waitqueue_active(&dhd->ioctl_resp_wait)) {
3764 wake_up_interruptible(&dhd->ioctl_resp_wait);
3765 }
3766
3767 return 0;
3768}
3769
3770void
3771dhd_os_wd_timer(void *bus, uint wdtick)
3772{
3773 dhd_pub_t *pub = bus;
3774 dhd_info_t *dhd = (dhd_info_t *)pub->info;
3775 unsigned long flags;
3776
3777 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
3778
3779 flags = dhd_os_spin_lock(pub);
3780
3781 /* don't start the wd until fw is loaded */
3782 if (pub->busstate == DHD_BUS_DOWN) {
3783 dhd_os_spin_unlock(pub, flags);
3784 return;
3785 }
3786
3787 /* Totally stop the timer */
3788 if (!wdtick && dhd->wd_timer_valid == TRUE) {
3789 dhd->wd_timer_valid = FALSE;
3790 dhd_os_spin_unlock(pub, flags);
3791#ifdef DHDTHREAD
3792 del_timer_sync(&dhd->timer);
3793#else
3794 del_timer(&dhd->timer);
3795#endif /* DHDTHREAD */
3796 return;
3797 }
3798
3799 if (wdtick) {
3800 dhd_watchdog_ms = (uint)wdtick;
3801 /* Re arm the timer, at last watchdog period */
3802 mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
3803 dhd->wd_timer_valid = TRUE;
3804 }
3805 dhd_os_spin_unlock(pub, flags);
3806}
3807
3808void *
3809dhd_os_open_image(char *filename)
3810{
3811 struct file *fp;
3812
3813 fp = filp_open(filename, O_RDONLY, 0);
3814 /*
3815 * 2.6.11 (FC4) supports filp_open() but later revs don't?
3816 * Alternative:
3817 * fp = open_namei(AT_FDCWD, filename, O_RD, 0);
3818 * ???
3819 */
3820 if (IS_ERR(fp))
3821 fp = NULL;
3822
3823 return fp;
3824}
3825
3826int
3827dhd_os_get_image_block(char *buf, int len, void *image)
3828{
3829 struct file *fp = (struct file *)image;
3830 int rdlen;
3831
3832 if (!image)
3833 return 0;
3834
3835 rdlen = kernel_read(fp, fp->f_pos, buf, len);
3836 if (rdlen > 0)
3837 fp->f_pos += rdlen;
3838
3839 return rdlen;
3840}
3841
3842void
3843dhd_os_close_image(void *image)
3844{
3845 if (image)
3846 filp_close((struct file *)image, NULL);
3847}
3848
3849
3850void
3851dhd_os_sdlock(dhd_pub_t *pub)
3852{
3853 dhd_info_t *dhd;
3854
3855 dhd = (dhd_info_t *)(pub->info);
3856
3857#ifdef DHDTHREAD
3858 if (dhd->threads_only)
3859 down(&dhd->sdsem);
3860 else
3861#endif /* DHDTHREAD */
3862 spin_lock_bh(&dhd->sdlock);
3863}
3864
3865void
3866dhd_os_sdunlock(dhd_pub_t *pub)
3867{
3868 dhd_info_t *dhd;
3869
3870 dhd = (dhd_info_t *)(pub->info);
3871
3872#ifdef DHDTHREAD
3873 if (dhd->threads_only)
3874 up(&dhd->sdsem);
3875 else
3876#endif /* DHDTHREAD */
3877 spin_unlock_bh(&dhd->sdlock);
3878}
3879
3880void
3881dhd_os_sdlock_txq(dhd_pub_t *pub)
3882{
3883 dhd_info_t *dhd;
3884
3885 dhd = (dhd_info_t *)(pub->info);
3886 spin_lock_bh(&dhd->txqlock);
3887}
3888
3889void
3890dhd_os_sdunlock_txq(dhd_pub_t *pub)
3891{
3892 dhd_info_t *dhd;
3893
3894 dhd = (dhd_info_t *)(pub->info);
3895 spin_unlock_bh(&dhd->txqlock);
3896}
3897
3898void
3899dhd_os_sdlock_rxq(dhd_pub_t *pub)
3900{
3901}
3902
3903void
3904dhd_os_sdunlock_rxq(dhd_pub_t *pub)
3905{
3906}
3907
3908void
3909dhd_os_sdtxlock(dhd_pub_t *pub)
3910{
3911 dhd_os_sdlock(pub);
3912}
3913
3914void
3915dhd_os_sdtxunlock(dhd_pub_t *pub)
3916{
3917 dhd_os_sdunlock(pub);
3918}
3919
3920#if defined(DHD_USE_STATIC_BUF)
3921uint8* dhd_os_prealloc(void *osh, int section, uint size)
3922{
3923 return (uint8*)wl_android_prealloc(section, size);
3924}
3925
3926void dhd_os_prefree(void *osh, void *addr, uint size)
3927{
3928}
3929#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */
3930
3931#if defined(CONFIG_BCMDHD_WEXT)
3932struct iw_statistics *
3933dhd_get_wireless_stats(struct net_device *dev)
3934{
3935 int res = 0;
3936 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
3937
3938 if (!dhd->pub.up) {
3939 return NULL;
3940 }
3941
3942 res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats);
3943
3944 if (res == 0)
3945 return &dhd->iw.wstats;
3946 else
3947 return NULL;
3948}
3949#endif /* defined(CONFIG_BCMDHD_WEXT) */
3950
3951static int
3952dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
3953 wl_event_msg_t *event, void **data)
3954{
3955 int bcmerror = 0;
3956 ASSERT(dhd != NULL);
3957
3958 bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data);
3959 if (bcmerror != BCME_OK)
3960 return (bcmerror);
3961
3962#if defined(CONFIG_BCMDHD_WEXT)
3963 if (event->bsscfgidx == 0) {
3964 /*
3965 * Wireless ext is on primary interface only
3966 */
3967
3968 ASSERT(dhd->iflist[*ifidx] != NULL);
3969 ASSERT(dhd->iflist[*ifidx]->net != NULL);
3970
3971 if (dhd->iflist[*ifidx]->net) {
3972 wl_iw_event(dhd->iflist[*ifidx]->net, event, *data);
3973 }
3974 }
3975#endif /* defined(CONFIG_BCMDHD_WEXT) */
3976
3977#ifdef WL_CFG80211
3978
3979 if ((wl_cfg80211_is_progress_ifchange() ||
3980 wl_cfg80211_is_progress_ifadd()) && (*ifidx != 0)) {
3981 /*
3982 * If IF_ADD/CHANGE operation is going on,
3983 * discard any event received on the virtual I/F
3984 */
3985 return (BCME_OK);
3986 }
3987
3988 ASSERT(dhd->iflist[*ifidx] != NULL);
3989 ASSERT(dhd->iflist[*ifidx]->net != NULL);
3990 if (dhd->iflist[*ifidx]->net) {
3991 wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data);
3992 }
3993#endif /* defined(WL_CFG80211) */
3994
3995 return (bcmerror);
3996}
3997
3998/* send up locally generated event */
3999void
4000dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data)
4001{
4002 switch (ntoh32(event->event_type)) {
4003 /* Send up locally generated AMP HCI Events */
4004 case WLC_E_BTA_HCI_EVENT: {
4005 struct sk_buff *p, *skb;
4006 bcm_event_t *msg;
4007 wl_event_msg_t *p_bcm_event;
4008 char *ptr;
4009 uint32 len;
4010 uint32 pktlen;
4011 dhd_if_t *ifp;
4012 dhd_info_t *dhd;
4013 uchar *eth;
4014 int ifidx;
4015
4016 len = ntoh32(event->datalen);
4017 pktlen = sizeof(bcm_event_t) + len + 2;
4018 dhd = dhdp->info;
4019 ifidx = dhd_ifname2idx(dhd, event->ifname);
4020
4021 if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) {
4022 ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32)));
4023
4024 msg = (bcm_event_t *) PKTDATA(dhdp->osh, p);
4025
4026 bcopy(&dhdp->mac, &msg->eth.ether_dhost, ETHER_ADDR_LEN);
4027 bcopy(&dhdp->mac, &msg->eth.ether_shost, ETHER_ADDR_LEN);
4028 ETHER_TOGGLE_LOCALADDR(&msg->eth.ether_shost);
4029
4030 msg->eth.ether_type = hton16(ETHER_TYPE_BRCM);
4031
4032 /* BCM Vendor specific header... */
4033 msg->bcm_hdr.subtype = hton16(BCMILCP_SUBTYPE_VENDOR_LONG);
4034 msg->bcm_hdr.version = BCMILCP_BCM_SUBTYPEHDR_VERSION;
4035 bcopy(BRCM_OUI, &msg->bcm_hdr.oui[0], DOT11_OUI_LEN);
4036
4037 /* vendor spec header length + pvt data length (private indication
4038 * hdr + actual message itself)
4039 */
4040 msg->bcm_hdr.length = hton16(BCMILCP_BCM_SUBTYPEHDR_MINLENGTH +
4041 BCM_MSG_LEN + sizeof(wl_event_msg_t) + (uint16)len);
4042 msg->bcm_hdr.usr_subtype = hton16(BCMILCP_BCM_SUBTYPE_EVENT);
4043
4044 PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2));
4045
4046 /* copy wl_event_msg_t into sk_buf */
4047
4048 /* pointer to wl_event_msg_t in sk_buf */
4049 p_bcm_event = &msg->event;
4050 bcopy(event, p_bcm_event, sizeof(wl_event_msg_t));
4051
4052 /* copy hci event into sk_buf */
4053 bcopy(data, (p_bcm_event + 1), len);
4054
4055 msg->bcm_hdr.length = hton16(sizeof(wl_event_msg_t) +
4056 ntoh16(msg->bcm_hdr.length));
4057 PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2));
4058
4059 ptr = (char *)(msg + 1);
4060 /* Last 2 bytes of the message are 0x00 0x00 to signal that there
4061 * are no ethertypes which are following this
4062 */
4063 ptr[len+0] = 0x00;
4064 ptr[len+1] = 0x00;
4065
4066 skb = PKTTONATIVE(dhdp->osh, p);
4067 eth = skb->data;
4068 len = skb->len;
4069
4070 ifp = dhd->iflist[ifidx];
4071 if (ifp == NULL)
4072 ifp = dhd->iflist[0];
4073
4074 ASSERT(ifp);
4075 skb->dev = ifp->net;
4076 skb->protocol = eth_type_trans(skb, skb->dev);
4077
4078 skb->data = eth;
4079 skb->len = len;
4080
4081 /* Strip header, count, deliver upward */
4082 skb_pull(skb, ETH_HLEN);
4083
4084 /* Send the packet */
4085 if (in_interrupt()) {
4086 netif_rx(skb);
4087 } else {
4088 netif_rx_ni(skb);
4089 }
4090 }
4091 else {
4092 /* Could not allocate a sk_buf */
4093 DHD_ERROR(("%s: unable to alloc sk_buf", __FUNCTION__));
4094 }
4095 break;
4096 } /* case WLC_E_BTA_HCI_EVENT */
4097
4098 default:
4099 break;
4100 }
4101}
4102
4103void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar)
4104{
4105#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
4106 struct dhd_info *dhdinfo = dhd->info;
4107 dhd_os_sdunlock(dhd);
4108 wait_event_interruptible_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), HZ * 2);
4109 dhd_os_sdlock(dhd);
4110#endif
4111 return;
4112}
4113
4114void dhd_wait_event_wakeup(dhd_pub_t *dhd)
4115{
4116#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
4117 struct dhd_info *dhdinfo = dhd->info;
4118 if (waitqueue_active(&dhdinfo->ctrl_wait))
4119 wake_up_interruptible(&dhdinfo->ctrl_wait);
4120#endif
4121 return;
4122}
4123
4124int
4125dhd_dev_reset(struct net_device *dev, uint8 flag)
4126{
4127 int ret;
4128
4129 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4130
4131 ret = dhd_bus_devreset(&dhd->pub, flag);
4132 if (ret) {
4133 DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret));
4134 return ret;
4135 }
4136
4137 return ret;
4138}
4139
4140int net_os_set_suspend_disable(struct net_device *dev, int val)
4141{
4142 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4143 int ret = 0;
4144
4145 if (dhd) {
4146 ret = dhd->pub.suspend_disable_flag;
4147 dhd->pub.suspend_disable_flag = val;
4148 }
4149 return ret;
4150}
4151
4152int net_os_set_suspend(struct net_device *dev, int val)
4153{
4154 int ret = 0;
4155#if defined(CONFIG_HAS_EARLYSUSPEND)
4156 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4157
4158 if (dhd) {
4159 ret = dhd_set_suspend(val, &dhd->pub);
4160 }
4161#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
4162 return ret;
4163}
4164
4165int net_os_set_dtim_skip(struct net_device *dev, int val)
4166{
4167 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4168
4169 if (dhd)
4170 dhd->pub.dtim_skip = val;
4171
4172 return 0;
4173}
4174
4175int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num)
4176{
4177 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4178 char *filterp = NULL;
4179 int ret = 0;
4180
4181 if (!dhd || (num == DHD_UNICAST_FILTER_NUM))
4182 return ret;
4183 if (num >= dhd->pub.pktfilter_count)
4184 return -EINVAL;
4185 if (add_remove) {
4186 switch (num) {
4187 case DHD_BROADCAST_FILTER_NUM:
4188 filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF";
4189 break;
4190 case DHD_MULTICAST4_FILTER_NUM:
4191 filterp = "102 0 0 0 0xFFFFFF 0x01005E";
4192 break;
4193 case DHD_MULTICAST6_FILTER_NUM:
4194 filterp = "103 0 0 0 0xFFFF 0x3333";
4195 break;
4196 default:
4197 return -EINVAL;
4198 }
4199 }
4200 dhd->pub.pktfilter[num] = filterp;
4201 return ret;
4202}
4203
4204int net_os_set_packet_filter(struct net_device *dev, int val)
4205{
4206 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4207 int ret = 0;
4208
4209 /* Packet filtering is set only if we still in early-suspend and
4210 * we need either to turn it ON or turn it OFF
4211 * We can always turn it OFF in case of early-suspend, but we turn it
4212 * back ON only if suspend_disable_flag was not set
4213 */
4214 if (dhd && dhd->pub.up) {
4215 if (dhd->pub.in_suspend) {
4216 if (!val || (val && !dhd->pub.suspend_disable_flag))
4217 dhd_set_packet_filter(val, &dhd->pub);
4218 }
4219 }
4220 return ret;
4221}
4222
4223
4224void
4225dhd_dev_init_ioctl(struct net_device *dev)
4226{
4227 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4228
4229 dhd_preinit_ioctls(&dhd->pub);
4230}
4231
4232#ifdef PNO_SUPPORT
4233/* Linux wrapper to call common dhd_pno_clean */
4234int
4235dhd_dev_pno_reset(struct net_device *dev)
4236{
4237 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4238
4239 return (dhd_pno_clean(&dhd->pub));
4240}
4241
4242
4243/* Linux wrapper to call common dhd_pno_enable */
4244int
4245dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled)
4246{
4247 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4248
4249 return (dhd_pno_enable(&dhd->pub, pfn_enabled));
4250}
4251
4252
4253/* Linux wrapper to call common dhd_pno_set */
4254int
4255dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid,
4256 ushort scan_fr, int pno_repeat, int pno_freq_expo_max)
4257{
4258 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4259
4260 return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max));
4261}
4262
4263/* Linux wrapper to get pno status */
4264int
4265dhd_dev_get_pno_status(struct net_device *dev)
4266{
4267 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4268
4269 return (dhd_pno_get_status(&dhd->pub));
4270}
4271
4272#endif /* PNO_SUPPORT */
4273
4274int net_os_send_hang_message(struct net_device *dev)
4275{
4276 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4277 int ret = 0;
4278
4279 if (dhd) {
4280 if (!dhd->pub.hang_was_sent) {
4281 dhd->pub.hang_was_sent = 1;
4282#if defined(CONFIG_BCMDHD_WEXT)
4283 ret = wl_iw_send_priv_event(dev, "HANG");
4284#endif
4285#if defined(WL_CFG80211)
4286 ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED);
4287#endif
4288 }
4289 }
4290 return ret;
4291}
4292
4293void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec)
4294{
4295 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4296
4297 if (dhd && dhd->pub.up)
4298 memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t));
4299}
4300
4301
4302void dhd_net_if_lock(struct net_device *dev)
4303{
4304 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4305 dhd_net_if_lock_local(dhd);
4306}
4307
4308void dhd_net_if_unlock(struct net_device *dev)
4309{
4310 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4311 dhd_net_if_unlock_local(dhd);
4312}
4313
4314static void dhd_net_if_lock_local(dhd_info_t *dhd)
4315{
4316#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
4317 if (dhd)
4318 mutex_lock(&dhd->dhd_net_if_mutex);
4319#endif
4320}
4321
4322static void dhd_net_if_unlock_local(dhd_info_t *dhd)
4323{
4324#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
4325 if (dhd)
4326 mutex_unlock(&dhd->dhd_net_if_mutex);
4327#endif
4328}
4329
4330unsigned long dhd_os_spin_lock(dhd_pub_t *pub)
4331{
4332 dhd_info_t *dhd = (dhd_info_t *)(pub->info);
4333 unsigned long flags = 0;
4334
4335 if (dhd)
4336 spin_lock_irqsave(&dhd->dhd_lock, flags);
4337
4338 return flags;
4339}
4340
4341void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags)
4342{
4343 dhd_info_t *dhd = (dhd_info_t *)(pub->info);
4344
4345 if (dhd)
4346 spin_unlock_irqrestore(&dhd->dhd_lock, flags);
4347}
4348
4349static int
4350dhd_get_pend_8021x_cnt(dhd_info_t *dhd)
4351{
4352 return (atomic_read(&dhd->pend_8021x_cnt));
4353}
4354
4355#define MAX_WAIT_FOR_8021X_TX 10
4356
4357int
4358dhd_wait_pend8021x(struct net_device *dev)
4359{
4360 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4361 int timeout = 10 * HZ / 1000;
4362 int ntimes = MAX_WAIT_FOR_8021X_TX;
4363 int pend = dhd_get_pend_8021x_cnt(dhd);
4364
4365 while (ntimes && pend) {
4366 if (pend) {
4367 set_current_state(TASK_INTERRUPTIBLE);
4368 schedule_timeout(timeout);
4369 set_current_state(TASK_RUNNING);
4370 ntimes--;
4371 }
4372 pend = dhd_get_pend_8021x_cnt(dhd);
4373 }
4374 return pend;
4375}
4376
4377#ifdef DHD_DEBUG
4378int
4379write_to_file(dhd_pub_t *dhd, uint8 *buf, int size)
4380{
4381 int ret = 0;
4382 struct file *fp;
4383 mm_segment_t old_fs;
4384 loff_t pos = 0;
4385
4386 /* change to KERNEL_DS address limit */
4387 old_fs = get_fs();
4388 set_fs(KERNEL_DS);
4389
4390 /* open file to write */
4391 fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640);
4392 if (!fp) {
4393 printf("%s: open file error\n", __FUNCTION__);
4394 ret = -1;
4395 goto exit;
4396 }
4397
4398 /* Write buf to file */
4399 fp->f_op->write(fp, buf, size, &pos);
4400
4401exit:
4402 /* free buf before return */
4403 MFREE(dhd->osh, buf, size);
4404 /* close file before return */
4405 if (fp)
4406 filp_close(fp, current->files);
4407 /* restore previous address limit */
4408 set_fs(old_fs);
4409
4410 return ret;
4411}
4412#endif /* DHD_DEBUG */
4413
4414int dhd_os_wake_lock_timeout(dhd_pub_t *pub)
4415{
4416 dhd_info_t *dhd = (dhd_info_t *)(pub->info);
4417 unsigned long flags;
4418 int ret = 0;
4419
4420 if (dhd) {
4421 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
4422 ret = dhd->wakelock_timeout_enable;
4423#ifdef CONFIG_HAS_WAKELOCK
4424 if (dhd->wakelock_timeout_enable)
4425 wake_lock_timeout(&dhd->wl_rxwake,
4426 dhd->wakelock_timeout_enable * HZ);
4427#endif
4428 dhd->wakelock_timeout_enable = 0;
4429 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
4430 }
4431 return ret;
4432}
4433
4434int net_os_wake_lock_timeout(struct net_device *dev)
4435{
4436 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4437 int ret = 0;
4438
4439 if (dhd)
4440 ret = dhd_os_wake_lock_timeout(&dhd->pub);
4441 return ret;
4442}
4443
4444int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub, int val)
4445{
4446 dhd_info_t *dhd = (dhd_info_t *)(pub->info);
4447 unsigned long flags;
4448
4449 if (dhd) {
4450 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
4451 if (val > dhd->wakelock_timeout_enable)
4452 dhd->wakelock_timeout_enable = val;
4453 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
4454 }
4455 return 0;
4456}
4457
4458int net_os_wake_lock_timeout_enable(struct net_device *dev, int val)
4459{
4460 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4461 int ret = 0;
4462
4463 if (dhd)
4464 ret = dhd_os_wake_lock_timeout_enable(&dhd->pub, val);
4465 return ret;
4466}
4467
4468int dhd_os_wake_lock(dhd_pub_t *pub)
4469{
4470 dhd_info_t *dhd = (dhd_info_t *)(pub->info);
4471 unsigned long flags;
4472 int ret = 0;
4473
4474 if (dhd) {
4475 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
4476#ifdef CONFIG_HAS_WAKELOCK
4477 if (!dhd->wakelock_counter)
4478 wake_lock(&dhd->wl_wifi);
4479#endif
4480 dhd->wakelock_counter++;
4481 ret = dhd->wakelock_counter;
4482 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
4483 }
4484 return ret;
4485}
4486
4487int net_os_wake_lock(struct net_device *dev)
4488{
4489 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4490 int ret = 0;
4491
4492 if (dhd)
4493 ret = dhd_os_wake_lock(&dhd->pub);
4494 return ret;
4495}
4496
4497int dhd_os_wake_unlock(dhd_pub_t *pub)
4498{
4499 dhd_info_t *dhd = (dhd_info_t *)(pub->info);
4500 unsigned long flags;
4501 int ret = 0;
4502
4503 dhd_os_wake_lock_timeout(pub);
4504 if (dhd) {
4505 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
4506 if (dhd->wakelock_counter) {
4507 dhd->wakelock_counter--;
4508#ifdef CONFIG_HAS_WAKELOCK
4509 if (!dhd->wakelock_counter)
4510 wake_unlock(&dhd->wl_wifi);
4511#endif
4512 ret = dhd->wakelock_counter;
4513 }
4514 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
4515 }
4516 return ret;
4517}
4518
4519int dhd_os_check_wakelock(void *dhdp)
4520{
4521#ifdef CONFIG_HAS_WAKELOCK
4522 dhd_pub_t *pub = (dhd_pub_t *)dhdp;
4523 dhd_info_t *dhd;
4524
4525 if (!pub)
4526 return 0;
4527 dhd = (dhd_info_t *)(pub->info);
4528
4529 if (dhd && wake_lock_active(&dhd->wl_wifi))
4530 return 1;
4531#endif
4532 return 0;
4533}
4534
4535int dhd_os_check_if_up(void *dhdp)
4536{
4537 dhd_pub_t *pub = (dhd_pub_t *)dhdp;
4538
4539 if (!pub)
4540 return 0;
4541 return pub->up;
4542}
4543
4544int net_os_wake_unlock(struct net_device *dev)
4545{
4546 dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
4547 int ret = 0;
4548
4549 if (dhd)
4550 ret = dhd_os_wake_unlock(&dhd->pub);
4551 return ret;
4552}
4553
4554int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd)
4555{
4556 int ifidx;
4557 int ret = 0;
4558 dhd_info_t *dhd = NULL;
4559
4560 if (!net || !netdev_priv(net)) {
4561 DHD_ERROR(("%s invalid parameter\n", __FUNCTION__));
4562 return -EINVAL;
4563 }
4564
4565 dhd = *(dhd_info_t **)netdev_priv(net);
4566 ifidx = dhd_net2idx(dhd, net);
4567 if (ifidx == DHD_BAD_IF) {
4568 DHD_ERROR(("%s bad ifidx\n", __FUNCTION__));
4569 return -ENODEV;
4570 }
4571
4572 DHD_OS_WAKE_LOCK(&dhd->pub);
4573 ret = dhd_wl_ioctl(&dhd->pub, ifidx, ioc, ioc->buf, ioc->len);
4574 dhd_check_hang(net, &dhd->pub, ret);
4575 DHD_OS_WAKE_UNLOCK(&dhd->pub);
4576
4577 return ret;
4578}
4579
4580bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret)
4581{
4582 struct net_device *net;
4583
4584 net = dhd_idx2net(dhdp, ifidx);
4585 return dhd_check_hang(net, dhdp, ret);
4586}
4587
4588#ifdef PROP_TXSTATUS
4589extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid,
4590 uint8 iftype, uint8* ea);
4591extern int dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits);
4592
4593int dhd_wlfc_interface_event(struct dhd_info *dhd, uint8 action, uint8 ifid, uint8 iftype,
4594 uint8* ea)
4595{
4596 if (dhd->pub.wlfc_state == NULL)
4597 return BCME_OK;
4598
4599 return dhd_wlfc_interface_entry_update(dhd->pub.wlfc_state, action, ifid, iftype, ea);
4600}
4601
4602int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data)
4603{
4604 if (dhd->pub.wlfc_state == NULL)
4605 return BCME_OK;
4606
4607 return dhd_wlfc_FIFOcreditmap_update(dhd->pub.wlfc_state, event_data);
4608}
4609
4610int dhd_wlfc_event(struct dhd_info *dhd)
4611{
4612 return dhd_wlfc_enable(&dhd->pub);
4613}
4614#endif /* PROP_TXSTATUS */
4615
4616#ifdef BCMDBGFS
4617
4618#include <linux/debugfs.h>
4619
4620extern uint32 dhd_readregl(void *bp, uint32 addr);
4621extern uint32 dhd_writeregl(void *bp, uint32 addr, uint32 data);
4622
4623typedef struct dhd_dbgfs {
4624 struct dentry *debugfs_dir;
4625 struct dentry *debugfs_mem;
4626 dhd_pub_t *dhdp;
4627 uint32 size;
4628} dhd_dbgfs_t;
4629
4630dhd_dbgfs_t g_dbgfs;
4631
4632static int
4633dhd_dbg_state_open(struct inode *inode, struct file *file)
4634{
4635 file->private_data = inode->i_private;
4636 return 0;
4637}
4638
4639static ssize_t
4640dhd_dbg_state_read(struct file *file, char __user *ubuf,
4641 size_t count, loff_t *ppos)
4642{
4643 ssize_t rval;
4644 uint32 tmp;
4645 loff_t pos = *ppos;
4646 size_t ret;
4647
4648 if (pos < 0)
4649 return -EINVAL;
4650 if (pos >= g_dbgfs.size || !count)
4651 return 0;
4652 if (count > g_dbgfs.size - pos)
4653 count = g_dbgfs.size - pos;
4654
4655 /* Basically enforce aligned 4 byte reads. It's up to the user to work out the details */
4656 tmp = dhd_readregl(g_dbgfs.dhdp->bus, file->f_pos & (~3));
4657
4658 ret = copy_to_user(ubuf, &tmp, 4);
4659 if (ret == count)
4660 return -EFAULT;
4661
4662 count -= ret;
4663 *ppos = pos + count;
4664 rval = count;
4665
4666 return rval;
4667}
4668
4669
4670static ssize_t
4671dhd_debugfs_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
4672{
4673 loff_t pos = *ppos;
4674 size_t ret;
4675 uint32 buf;
4676
4677 if (pos < 0)
4678 return -EINVAL;
4679 if (pos >= g_dbgfs.size || !count)
4680 return 0;
4681 if (count > g_dbgfs.size - pos)
4682 count = g_dbgfs.size - pos;
4683
4684 ret = copy_from_user(&buf, ubuf, sizeof(uint32));
4685 if (ret == count)
4686 return -EFAULT;
4687
4688 /* Basically enforce aligned 4 byte writes. It's up to the user to work out the details */
4689 dhd_writeregl(g_dbgfs.dhdp->bus, file->f_pos & (~3), buf);
4690
4691 return count;
4692}
4693
4694
4695loff_t
4696dhd_debugfs_lseek(struct file *file, loff_t off, int whence)
4697{
4698 loff_t pos = -1;
4699
4700 switch (whence) {
4701 case 0:
4702 pos = off;
4703 break;
4704 case 1:
4705 pos = file->f_pos + off;
4706 break;
4707 case 2:
4708 pos = g_dbgfs.size - off;
4709 }
4710 return (pos < 0 || pos > g_dbgfs.size) ? -EINVAL : (file->f_pos = pos);
4711}
4712
4713static const struct file_operations dhd_dbg_state_ops = {
4714 .read = dhd_dbg_state_read,
4715 .write = dhd_debugfs_write,
4716 .open = dhd_dbg_state_open,
4717 .llseek = dhd_debugfs_lseek
4718};
4719
4720static void dhd_dbg_create(void)
4721{
4722 if (g_dbgfs.debugfs_dir) {
4723 g_dbgfs.debugfs_mem = debugfs_create_file("mem", 0644, g_dbgfs.debugfs_dir,
4724 NULL, &dhd_dbg_state_ops);
4725 }
4726}
4727
4728void dhd_dbg_init(dhd_pub_t *dhdp)
4729{
4730 int err;
4731
4732 g_dbgfs.dhdp = dhdp;
4733 g_dbgfs.size = 0x20000000; /* Allow access to various cores regs */
4734
4735 g_dbgfs.debugfs_dir = debugfs_create_dir("dhd", 0);
4736 if (IS_ERR(g_dbgfs.debugfs_dir)) {
4737 err = PTR_ERR(g_dbgfs.debugfs_dir);
4738 g_dbgfs.debugfs_dir = NULL;
4739 return;
4740 }
4741
4742 dhd_dbg_create();
4743
4744 return;
4745}
4746
4747void dhd_dbg_remove(void)
4748{
4749 debugfs_remove(g_dbgfs.debugfs_mem);
4750 debugfs_remove(g_dbgfs.debugfs_dir);
4751
4752 bzero((unsigned char *) &g_dbgfs, sizeof(g_dbgfs));
4753
4754}
4755#endif /* ifdef BCMDBGFS */
4756
4757#ifdef WLMEDIA_HTSF
4758
4759static
4760void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf)
4761{
4762 dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
4763 struct sk_buff *skb;
4764 uint32 htsf = 0;
4765 uint16 dport = 0, oldmagic = 0xACAC;
4766 char *p1;
4767 htsfts_t ts;
4768
4769 /* timestamp packet */
4770
4771 p1 = (char*) PKTDATA(dhdp->osh, pktbuf);
4772
4773 if (PKTLEN(dhdp->osh, pktbuf) > HTSF_MINLEN) {
4774/* memcpy(&proto, p1+26, 4); */
4775 memcpy(&dport, p1+40, 2);
4776/* proto = ((ntoh32(proto))>> 16) & 0xFF; */
4777 dport = ntoh16(dport);
4778 }
4779
4780 /* timestamp only if icmp or udb iperf with port 5555 */
4781/* if (proto == 17 && dport == tsport) { */
4782 if (dport >= tsport && dport <= tsport + 20) {
4783
4784 skb = (struct sk_buff *) pktbuf;
4785
4786 htsf = dhd_get_htsf(dhd, 0);
4787 memset(skb->data + 44, 0, 2); /* clear checksum */
4788 memcpy(skb->data+82, &oldmagic, 2);
4789 memcpy(skb->data+84, &htsf, 4);
4790
4791 memset(&ts, 0, sizeof(htsfts_t));
4792 ts.magic = HTSFMAGIC;
4793 ts.prio = PKTPRIO(pktbuf);
4794 ts.seqnum = htsf_seqnum++;
4795 ts.c10 = get_cycles();
4796 ts.t10 = htsf;
4797 ts.endmagic = HTSFENDMAGIC;
4798
4799 memcpy(skb->data + HTSF_HOSTOFFSET, &ts, sizeof(ts));
4800 }
4801}
4802
4803static void dhd_dump_htsfhisto(histo_t *his, char *s)
4804{
4805 int pktcnt = 0, curval = 0, i;
4806 for (i = 0; i < (NUMBIN-2); i++) {
4807 curval += 500;
4808 printf("%d ", his->bin[i]);
4809 pktcnt += his->bin[i];
4810 }
4811 printf(" max: %d TotPkt: %d neg: %d [%s]\n", his->bin[NUMBIN-2], pktcnt,
4812 his->bin[NUMBIN-1], s);
4813}
4814
4815static
4816void sorttobin(int value, histo_t *histo)
4817{
4818 int i, binval = 0;
4819
4820 if (value < 0) {
4821 histo->bin[NUMBIN-1]++;
4822 return;
4823 }
4824 if (value > histo->bin[NUMBIN-2]) /* store the max value */
4825 histo->bin[NUMBIN-2] = value;
4826
4827 for (i = 0; i < (NUMBIN-2); i++) {
4828 binval += 500; /* 500m s bins */
4829 if (value <= binval) {
4830 histo->bin[i]++;
4831 return;
4832 }
4833 }
4834 histo->bin[NUMBIN-3]++;
4835}
4836
4837static
4838void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf)
4839{
4840 dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
4841 struct sk_buff *skb;
4842 char *p1;
4843 uint16 old_magic;
4844 int d1, d2, d3, end2end;
4845 htsfts_t *htsf_ts;
4846 uint32 htsf;
4847
4848 skb = PKTTONATIVE(dhdp->osh, pktbuf);
4849 p1 = (char*)PKTDATA(dhdp->osh, pktbuf);
4850
4851 if (PKTLEN(osh, pktbuf) > HTSF_MINLEN) {
4852 memcpy(&old_magic, p1+78, 2);
4853 htsf_ts = (htsfts_t*) (p1 + HTSF_HOSTOFFSET - 4);
4854 }
4855 else
4856 return;
4857
4858 if (htsf_ts->magic == HTSFMAGIC) {
4859 htsf_ts->tE0 = dhd_get_htsf(dhd, 0);
4860 htsf_ts->cE0 = get_cycles();
4861 }
4862
4863 if (old_magic == 0xACAC) {
4864
4865 tspktcnt++;
4866 htsf = dhd_get_htsf(dhd, 0);
4867 memcpy(skb->data+92, &htsf, sizeof(uint32));
4868
4869 memcpy(&ts[tsidx].t1, skb->data+80, 16);
4870
4871 d1 = ts[tsidx].t2 - ts[tsidx].t1;
4872 d2 = ts[tsidx].t3 - ts[tsidx].t2;
4873 d3 = ts[tsidx].t4 - ts[tsidx].t3;
4874 end2end = ts[tsidx].t4 - ts[tsidx].t1;
4875
4876 sorttobin(d1, &vi_d1);
4877 sorttobin(d2, &vi_d2);
4878 sorttobin(d3, &vi_d3);
4879 sorttobin(end2end, &vi_d4);
4880
4881 if (end2end > 0 && end2end > maxdelay) {
4882 maxdelay = end2end;
4883 maxdelaypktno = tspktcnt;
4884 memcpy(&maxdelayts, &ts[tsidx], 16);
4885 }
4886 if (++tsidx >= TSMAX)
4887 tsidx = 0;
4888 }
4889}
4890
4891uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx)
4892{
4893 uint32 htsf = 0, cur_cycle, delta, delta_us;
4894 uint32 factor, baseval, baseval2;
4895 cycles_t t;
4896
4897 t = get_cycles();
4898 cur_cycle = t;
4899
4900 if (cur_cycle > dhd->htsf.last_cycle)
4901 delta = cur_cycle - dhd->htsf.last_cycle;
4902 else {
4903 delta = cur_cycle + (0xFFFFFFFF - dhd->htsf.last_cycle);
4904 }
4905
4906 delta = delta >> 4;
4907
4908 if (dhd->htsf.coef) {
4909 /* times ten to get the first digit */
4910 factor = (dhd->htsf.coef*10 + dhd->htsf.coefdec1);
4911 baseval = (delta*10)/factor;
4912 baseval2 = (delta*10)/(factor+1);
4913 delta_us = (baseval - (((baseval - baseval2) * dhd->htsf.coefdec2)) / 10);
4914 htsf = (delta_us << 4) + dhd->htsf.last_tsf + HTSF_BUS_DELAY;
4915 }
4916 else {
4917 DHD_ERROR(("-------dhd->htsf.coef = 0 -------\n"));
4918 }
4919
4920 return htsf;
4921}
4922
4923static void dhd_dump_latency(void)
4924{
4925 int i, max = 0;
4926 int d1, d2, d3, d4, d5;
4927
4928 printf("T1 T2 T3 T4 d1 d2 t4-t1 i \n");
4929 for (i = 0; i < TSMAX; i++) {
4930 d1 = ts[i].t2 - ts[i].t1;
4931 d2 = ts[i].t3 - ts[i].t2;
4932 d3 = ts[i].t4 - ts[i].t3;
4933 d4 = ts[i].t4 - ts[i].t1;
4934 d5 = ts[max].t4-ts[max].t1;
4935 if (d4 > d5 && d4 > 0) {
4936 max = i;
4937 }
4938 printf("%08X %08X %08X %08X \t%d %d %d %d i=%d\n",
4939 ts[i].t1, ts[i].t2, ts[i].t3, ts[i].t4,
4940 d1, d2, d3, d4, i);
4941 }
4942
4943 printf("current idx = %d \n", tsidx);
4944
4945 printf("Highest latency %d pkt no.%d total=%d\n", maxdelay, maxdelaypktno, tspktcnt);
4946 printf("%08X %08X %08X %08X \t%d %d %d %d\n",
4947 maxdelayts.t1, maxdelayts.t2, maxdelayts.t3, maxdelayts.t4,
4948 maxdelayts.t2 - maxdelayts.t1,
4949 maxdelayts.t3 - maxdelayts.t2,
4950 maxdelayts.t4 - maxdelayts.t3,
4951 maxdelayts.t4 - maxdelayts.t1);
4952}
4953
4954
4955static int
4956dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx)
4957{
4958 wl_ioctl_t ioc;
4959 char buf[32];
4960 int ret;
4961 uint32 s1, s2;
4962
4963 struct tsf {
4964 uint32 low;
4965 uint32 high;
4966 } tsf_buf;
4967
4968 memset(&ioc, 0, sizeof(ioc));
4969 memset(&tsf_buf, 0, sizeof(tsf_buf));
4970
4971 ioc.cmd = WLC_GET_VAR;
4972 ioc.buf = buf;
4973 ioc.len = (uint)sizeof(buf);
4974 ioc.set = FALSE;
4975
4976 strcpy(buf, "tsf");
4977 s1 = dhd_get_htsf(dhd, 0);
4978 if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
4979 if (ret == -EIO) {
4980 DHD_ERROR(("%s: tsf is not supported by device\n",
4981 dhd_ifname(&dhd->pub, ifidx)));
4982 return -EOPNOTSUPP;
4983 }
4984 return ret;
4985 }
4986 s2 = dhd_get_htsf(dhd, 0);
4987
4988 memcpy(&tsf_buf, buf, sizeof(tsf_buf));
4989 printf(" TSF_h=%04X lo=%08X Calc:htsf=%08X, coef=%d.%d%d delta=%d ",
4990 tsf_buf.high, tsf_buf.low, s2, dhd->htsf.coef, dhd->htsf.coefdec1,
4991 dhd->htsf.coefdec2, s2-tsf_buf.low);
4992 printf("lasttsf=%08X lastcycle=%08X\n", dhd->htsf.last_tsf, dhd->htsf.last_cycle);
4993 return 0;
4994}
4995
4996void htsf_update(dhd_info_t *dhd, void *data)
4997{
4998 static ulong cur_cycle = 0, prev_cycle = 0;
4999 uint32 htsf, tsf_delta = 0;
5000 uint32 hfactor = 0, cyc_delta, dec1 = 0, dec2, dec3, tmp;
5001 ulong b, a;
5002 cycles_t t;
5003
5004 /* cycles_t in inlcude/mips/timex.h */
5005
5006 t = get_cycles();
5007
5008 prev_cycle = cur_cycle;
5009 cur_cycle = t;
5010
5011 if (cur_cycle > prev_cycle)
5012 cyc_delta = cur_cycle - prev_cycle;
5013 else {
5014 b = cur_cycle;
5015 a = prev_cycle;
5016 cyc_delta = cur_cycle + (0xFFFFFFFF - prev_cycle);
5017 }
5018
5019 if (data == NULL)
5020 printf(" tsf update ata point er is null \n");
5021
5022 memcpy(&prev_tsf, &cur_tsf, sizeof(tsf_t));
5023 memcpy(&cur_tsf, data, sizeof(tsf_t));
5024
5025 if (cur_tsf.low == 0) {
5026 DHD_INFO((" ---- 0 TSF, do not update, return\n"));
5027 return;
5028 }
5029
5030 if (cur_tsf.low > prev_tsf.low)
5031 tsf_delta = (cur_tsf.low - prev_tsf.low);
5032 else {
5033 DHD_INFO((" ---- tsf low is smaller cur_tsf= %08X, prev_tsf=%08X, \n",
5034 cur_tsf.low, prev_tsf.low));
5035 if (cur_tsf.high > prev_tsf.high) {
5036 tsf_delta = cur_tsf.low + (0xFFFFFFFF - prev_tsf.low);
5037 DHD_INFO((" ---- Wrap around tsf coutner adjusted TSF=%08X\n", tsf_delta));
5038 }
5039 else
5040 return; /* do not update */
5041 }
5042
5043 if (tsf_delta) {
5044 hfactor = cyc_delta / tsf_delta;
5045 tmp = (cyc_delta - (hfactor * tsf_delta))*10;
5046 dec1 = tmp/tsf_delta;
5047 dec2 = ((tmp - dec1*tsf_delta)*10) / tsf_delta;
5048 tmp = (tmp - (dec1*tsf_delta))*10;
5049 dec3 = ((tmp - dec2*tsf_delta)*10) / tsf_delta;
5050
5051 if (dec3 > 4) {
5052 if (dec2 == 9) {
5053 dec2 = 0;
5054 if (dec1 == 9) {
5055 dec1 = 0;
5056 hfactor++;
5057 }
5058 else {
5059 dec1++;
5060 }
5061 }
5062 else
5063 dec2++;
5064 }
5065 }
5066
5067 if (hfactor) {
5068 htsf = ((cyc_delta * 10) / (hfactor*10+dec1)) + prev_tsf.low;
5069 dhd->htsf.coef = hfactor;
5070 dhd->htsf.last_cycle = cur_cycle;
5071 dhd->htsf.last_tsf = cur_tsf.low;
5072 dhd->htsf.coefdec1 = dec1;
5073 dhd->htsf.coefdec2 = dec2;
5074 }
5075 else {
5076 htsf = prev_tsf.low;
5077 }
5078}
5079
5080#endif /* WLMEDIA_HTSF */