media: platform: tegra: Update OV7695 settings
[linux-3.10.git] / drivers / net / wireless / bcmdhd / dhd_linux.c
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-2013, 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 432432 2013-10-28 15:52:47Z $
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/ip.h>
46 #include <linux/device.h>
47 #include <net/addrconf.h>
48 #ifdef ENABLE_ADAPTIVE_SCHED
49 #include <linux/cpufreq.h>
50 #endif /* ENABLE_ADAPTIVE_SCHED */
51
52 #include <asm/uaccess.h>
53 #include <asm/unaligned.h>
54
55 #include <epivers.h>
56 #include <bcmutils.h>
57 #include <bcmendian.h>
58 #include <bcmdevs.h>
59
60 #include <proto/ethernet.h>
61 #include <proto/bcmip.h>
62 #include <dngl_stats.h>
63 #include <dhd.h>
64 #include <dhd_bus.h>
65 #include <dhd_proto.h>
66 #include <dhd_dbg.h>
67 #ifdef CONFIG_HAS_WAKELOCK
68 #include <linux/wakelock.h>
69 #endif
70 #ifdef WL_CFG80211
71 #include <wl_cfg80211.h>
72 #endif
73 #ifdef PNO_SUPPORT
74 #include <dhd_pno.h>
75 #endif
76
77 #ifdef WLMEDIA_HTSF
78 #include <linux/time.h>
79 #include <htsf.h>
80
81 #define HTSF_MINLEN 200    /* min. packet length to timestamp */
82 #define HTSF_BUS_DELAY 150 /* assume a fix propagation in us  */
83 #define TSMAX  1000        /* max no. of timing record kept   */
84 #define NUMBIN 34
85
86 static uint32 tsidx = 0;
87 static uint32 htsf_seqnum = 0;
88 uint32 tsfsync;
89 struct timeval tsync;
90 static uint32 tsport = 5010;
91
92 typedef struct histo_ {
93         uint32 bin[NUMBIN];
94 } histo_t;
95
96 #if !ISPOWEROF2(DHD_SDALIGN)
97 #error DHD_SDALIGN is not a power of 2!
98 #endif
99
100 static histo_t vi_d1, vi_d2, vi_d3, vi_d4;
101 #endif /* WLMEDIA_HTSF */
102
103
104 #if defined(SOFTAP)
105 extern bool ap_cfg_running;
106 extern bool ap_fw_loaded;
107 #endif
108
109
110 #ifdef ENABLE_ADAPTIVE_SCHED
111 #define DEFAULT_CPUFREQ_THRESH          1000000 /* threshold frequency : 1000000 = 1GHz */
112 #ifndef CUSTOM_CPUFREQ_THRESH
113 #define CUSTOM_CPUFREQ_THRESH   DEFAULT_CPUFREQ_THRESH
114 #endif /* CUSTOM_CPUFREQ_THRESH */
115 #endif /* ENABLE_ADAPTIVE_SCHED */
116
117 /* enable HOSTIP cache update from the host side when an eth0:N is up */
118 #define AOE_IP_ALIAS_SUPPORT 1
119
120 #ifdef BCM_FD_AGGR
121 #include <bcm_rpc.h>
122 #include <bcm_rpc_tp.h>
123 #endif
124 #ifdef PROP_TXSTATUS
125 #include <wlfc_proto.h>
126 #include <dhd_wlfc.h>
127 #endif
128
129 #include <wl_android.h>
130
131 #ifdef ARP_OFFLOAD_SUPPORT
132 void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx);
133 static int dhd_device_event(struct notifier_block *this,
134         unsigned long event,
135         void *ptr);
136
137 static struct notifier_block dhd_notifier = {
138         .notifier_call = dhd_device_event
139 };
140 #endif /* ARP_OFFLOAD_SUPPORT */
141 static int dhd_device_ipv6_event(struct notifier_block *this,
142         unsigned long event,
143         void *ptr);
144
145 static struct notifier_block dhd_notifier_ipv6 = {
146         .notifier_call = dhd_device_ipv6_event
147 };
148
149 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
150 #include <linux/suspend.h>
151 volatile bool dhd_mmc_suspend = FALSE;
152 DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
153 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
154
155 #if defined(OOB_INTR_ONLY)
156 extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable);
157 #endif 
158 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1)
159 static void dhd_hang_process(struct work_struct *work);
160 #endif 
161 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
162 MODULE_LICENSE("GPL v2");
163 #endif /* LinuxVer */
164
165 #include <dhd_bus.h>
166
167 #ifdef BCM_FD_AGGR
168 #define DBUS_RX_BUFFER_SIZE_DHD(net)    (BCM_RPC_TP_DNGL_AGG_MAX_BYTE)
169 #else
170 #ifndef PROP_TXSTATUS
171 #define DBUS_RX_BUFFER_SIZE_DHD(net)    (net->mtu + net->hard_header_len + dhd->pub.hdrlen)
172 #else
173 #define DBUS_RX_BUFFER_SIZE_DHD(net)    (net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128)
174 #endif
175 #endif /* BCM_FD_AGGR */
176
177 #ifdef PROP_TXSTATUS
178 extern bool dhd_wlfc_skip_fc(void);
179 extern void dhd_wlfc_plat_enable(void *dhd);
180 extern void dhd_wlfc_plat_deinit(void *dhd);
181 #endif /* PROP_TXSTATUS */
182
183 #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
184 const char *
185 print_tainted()
186 {
187         return "";
188 }
189 #endif  /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */
190
191 /* Linux wireless extension support */
192 #if defined(WL_WIRELESS_EXT)
193 #include <wl_iw.h>
194 extern wl_iw_extra_params_t  g_wl_iw_params;
195 #endif /* defined(WL_WIRELESS_EXT) */
196
197 #if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND)
198 #include <linux/earlysuspend.h>
199 #endif /* defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) */
200
201 extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd);
202
203 #ifdef PKT_FILTER_SUPPORT
204 extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
205 extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
206 extern void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id);
207 #endif
208
209
210 #ifdef READ_MACADDR
211 extern int dhd_read_macaddr(struct dhd_info *dhd);
212 #else
213 static inline int dhd_read_macaddr(struct dhd_info *dhd) { return 0; }
214 #endif
215 #ifdef WRITE_MACADDR
216 extern int dhd_write_macaddr(struct ether_addr *mac);
217 #else
218 static inline int dhd_write_macaddr(struct ether_addr *mac) { return 0; }
219 #endif
220 struct ipv6_addr {
221         char                    ipv6_addr[IPV6_ADDR_LEN];
222         dhd_ipv6_op_t   ipv6_oper;
223         struct list_head list;
224 };
225
226 /* Interface control information */
227 typedef struct dhd_if {
228         struct dhd_info *info;                  /* back pointer to dhd_info */
229         /* OS/stack specifics */
230         struct net_device *net;
231         struct net_device_stats stats;
232         int                     idx;                    /* iface idx in dongle */
233         dhd_if_state_t  state;                  /* interface state */
234         uint                    subunit;                /* subunit */
235         uint8                   mac_addr[ETHER_ADDR_LEN];       /* assigned MAC address */
236         bool                    attached;               /* Delayed attachment when unset */
237         bool                    txflowcontrol;  /* Per interface flow control indicator */
238         char                    name[IFNAMSIZ+1]; /* linux interface name */
239         uint8                   bssidx;                 /* bsscfg index for the interface */
240         bool                    set_multicast;
241         struct list_head ipv6_list;
242         spinlock_t              ipv6_lock;
243         bool                    event2cfg80211; /* To determine if pass event to cfg80211 */
244 } dhd_if_t;
245
246 #ifdef WLMEDIA_HTSF
247 typedef struct {
248         uint32 low;
249         uint32 high;
250 } tsf_t;
251
252 typedef struct {
253         uint32 last_cycle;
254         uint32 last_sec;
255         uint32 last_tsf;
256         uint32 coef;     /* scaling factor */
257         uint32 coefdec1; /* first decimal  */
258         uint32 coefdec2; /* second decimal */
259 } htsf_t;
260
261 typedef struct {
262         uint32 t1;
263         uint32 t2;
264         uint32 t3;
265         uint32 t4;
266 } tstamp_t;
267
268 static tstamp_t ts[TSMAX];
269 static tstamp_t maxdelayts;
270 static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0;
271
272 #endif  /* WLMEDIA_HTSF */
273
274 /* Local private structure (extension of pub) */
275 typedef struct dhd_info {
276 #if defined(WL_WIRELESS_EXT)
277         wl_iw_t         iw;             /* wireless extensions state (must be first) */
278 #endif /* defined(WL_WIRELESS_EXT) */
279
280         dhd_pub_t pub;
281
282         /* For supporting multiple interfaces */
283         dhd_if_t *iflist[DHD_MAX_IFS];
284
285         struct semaphore proto_sem;
286 #ifdef PROP_TXSTATUS
287         spinlock_t      wlfc_spinlock;
288 #endif /* PROP_TXSTATUS */
289 #ifdef WLMEDIA_HTSF
290         htsf_t  htsf;
291 #endif
292         wait_queue_head_t ioctl_resp_wait;
293         uint32  default_wd_interval;
294
295         struct timer_list timer;
296         bool wd_timer_valid;
297         struct tasklet_struct tasklet;
298         spinlock_t      sdlock;
299         spinlock_t      txqlock;
300         spinlock_t      dhd_lock;
301 #ifdef DHDTHREAD
302         /* Thread based operation */
303         bool threads_only;
304         struct semaphore sdsem;
305
306         tsk_ctl_t       thr_dpc_ctl;
307         tsk_ctl_t       thr_wdt_ctl;
308 #ifdef RXFRAME_THREAD
309         tsk_ctl_t       thr_rxf_ctl;
310         spinlock_t      rxf_lock;
311 #endif /* RXFRAME_THREAD */
312 #endif /* DHDTHREAD */
313         bool dhd_tasklet_create;
314         tsk_ctl_t       thr_sysioc_ctl;
315 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
316         struct work_struct work_hang;
317 #endif
318
319         /* Wakelocks */
320 #if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
321         struct wake_lock wl_wifi;   /* Wifi wakelock */
322         struct wake_lock wl_rxwake; /* Wifi rx wakelock */
323         struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */
324         struct wake_lock wl_wdwake; /* Wifi wd wakelock */
325 #endif
326
327 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
328         /* net_device interface lock, prevent race conditions among net_dev interface
329          * calls and wifi_on or wifi_off
330          */
331         struct mutex dhd_net_if_mutex;
332         struct mutex dhd_suspend_mutex;
333 #endif
334         spinlock_t wakelock_spinlock;
335         int wakelock_counter;
336         int wakelock_wd_counter;
337         int wakelock_rx_timeout_enable;
338         int wakelock_ctrl_timeout_enable;
339
340         /* Thread to issue ioctl for multicast */
341         unsigned char set_macaddress;
342         struct ether_addr macvalue;
343         wait_queue_head_t ctrl_wait;
344         atomic_t pend_8021x_cnt;
345         dhd_attach_states_t dhd_state;
346
347 #if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND)
348         struct early_suspend early_suspend;
349 #endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */
350
351 #ifdef ARP_OFFLOAD_SUPPORT
352         u32 pend_ipaddr;
353 #endif /* ARP_OFFLOAD_SUPPORT */
354 #ifdef BCM_FD_AGGR
355         void *rpc_th;
356         void *rpc_osh;
357         struct timer_list rpcth_timer;
358         bool rpcth_timer_active;
359         bool fdaggr;
360 #endif
361 #ifdef DHDTCPACK_SUPPRESS
362         spinlock_t      tcpack_lock;
363 #endif /* DHDTCPACK_SUPPRESS */
364 } dhd_info_t;
365
366 /* Flag to indicate if we should download firmware on driver load */
367 uint dhd_download_fw_on_driverload = TRUE;
368
369 /* Definitions to provide path to the firmware and nvram
370  * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt"
371  */
372 char firmware_path[MOD_PARAM_PATHLEN];
373 char nvram_path[MOD_PARAM_PATHLEN];
374
375 /* information string to keep firmware, chio, cheip version info visiable from log */
376 char info_string[MOD_PARAM_INFOLEN];
377 module_param_string(info_string, info_string, MOD_PARAM_INFOLEN, 0444);
378 int op_mode = 0;
379 int disable_proptx = 0;
380 module_param(op_mode, int, 0644);
381 extern int wl_control_wl_start(struct net_device *dev);
382 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
383 struct semaphore dhd_registration_sem;
384 #if defined(WL_CFG80211)
385 struct semaphore dhd_init_sem;
386 bool init_power_off = TRUE;
387 #endif
388 struct semaphore dhd_chipup_sem;
389 int dhd_registration_check = FALSE;
390
391 #define DHD_REGISTRATION_TIMEOUT  12000  /* msec : allowed time to finished dhd registration */
392 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
393
394 /* Spawn a thread for system ioctls (set mac, set mcast) */
395 uint dhd_sysioc = TRUE;
396 module_param(dhd_sysioc, uint, 0);
397
398 /* Error bits */
399 module_param(dhd_msg_level, int, 0);
400
401 #ifdef ARP_OFFLOAD_SUPPORT
402 /* ARP offload enable */
403 uint dhd_arp_enable = TRUE;
404 module_param(dhd_arp_enable, uint, 0);
405
406 /* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */
407
408 uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY;
409
410 module_param(dhd_arp_mode, uint, 0);
411 #endif /* ARP_OFFLOAD_SUPPORT */
412
413
414
415 /* Disable Prop tx */
416 module_param(disable_proptx, int, 0644);
417 /* load firmware and/or nvram values from the filesystem */
418 module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660);
419 module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
420
421
422 /* Watchdog interval */
423
424 /* extend watchdog expiration to 2 seconds when DPC is running */
425 #define WATCHDOG_EXTEND_INTERVAL (2000)
426
427 uint dhd_watchdog_ms = 10;
428 module_param(dhd_watchdog_ms, uint, 0);
429
430 #if defined(DHD_DEBUG)
431 /* Console poll interval */
432 uint dhd_console_ms = 0;
433 module_param(dhd_console_ms, uint, 0644);
434 #endif /* defined(DHD_DEBUG) */
435
436 uint dhd_slpauto = TRUE;
437 module_param(dhd_slpauto, uint, 0);
438
439 #ifdef PKT_FILTER_SUPPORT
440 /* Global Pkt filter enable control */
441 uint dhd_pkt_filter_enable = TRUE;
442 module_param(dhd_pkt_filter_enable, uint, 0);
443 #endif
444
445 /* Pkt filter init setup */
446 uint dhd_pkt_filter_init = 0;
447 module_param(dhd_pkt_filter_init, uint, 0);
448
449 /* Pkt filter mode control */
450 uint dhd_master_mode = TRUE;
451 module_param(dhd_master_mode, uint, 0);
452
453 #ifdef DHDTHREAD
454 int dhd_watchdog_prio = 0;
455 module_param(dhd_watchdog_prio, int, 0);
456
457 /* DPC thread priority */
458 int dhd_dpc_prio = CUSTOM_DPC_PRIO_SETTING;
459 module_param(dhd_dpc_prio, int, 0);
460
461 #ifdef RXFRAME_THREAD
462 /* RX frame thread priority */
463 int dhd_rxf_prio = CUSTOM_RXF_PRIO_SETTING;
464 module_param(dhd_rxf_prio, int, 0);
465 #endif /* RXFRAME_THREAD */
466
467 /* DPC thread priority, -1 to use tasklet */
468 extern int dhd_dongle_ramsize;
469 module_param(dhd_dongle_ramsize, int, 0);
470 #endif /* DHDTHREAD */
471 /* Control fw roaming */
472 uint dhd_roam_disable = 0;
473
474 /* Control radio state */
475 uint dhd_radio_up = 1;
476
477 /* Network inteface name */
478 char iface_name[IFNAMSIZ] = {'\0'};
479 module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
480
481 /* The following are specific to the SDIO dongle */
482
483 /* IOCTL response timeout */
484 int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
485
486 /* Idle timeout for backplane clock */
487 int dhd_idletime = DHD_IDLETIME_TICKS;
488 module_param(dhd_idletime, int, 0);
489
490 /* Use polling */
491 uint dhd_poll = FALSE;
492 module_param(dhd_poll, uint, 0);
493
494 /* Use interrupts */
495 uint dhd_intr = TRUE;
496 module_param(dhd_intr, uint, 0);
497
498 /* SDIO Drive Strength (in milliamps) */
499 uint dhd_sdiod_drive_strength = 6;
500 module_param(dhd_sdiod_drive_strength, uint, 0);
501
502 /* Tx/Rx bounds */
503 extern uint dhd_txbound;
504 extern uint dhd_rxbound;
505 module_param(dhd_txbound, uint, 0);
506 module_param(dhd_rxbound, uint, 0);
507
508 /* Deferred transmits */
509 extern uint dhd_deferred_tx;
510 module_param(dhd_deferred_tx, uint, 0);
511
512 #ifdef BCMDBGFS
513 extern void dhd_dbg_init(dhd_pub_t *dhdp);
514 extern void dhd_dbg_remove(void);
515 #endif /* BCMDBGFS */
516
517
518
519 #ifdef SDTEST
520 /* Echo packet generator (pkts/s) */
521 uint dhd_pktgen = 0;
522 module_param(dhd_pktgen, uint, 0);
523
524 /* Echo packet len (0 => sawtooth, max 2040) */
525 uint dhd_pktgen_len = 0;
526 module_param(dhd_pktgen_len, uint, 0);
527 #endif /* SDTEST */
528
529 /* Version string to report */
530 #ifdef DHD_DEBUG
531 #ifndef SRCBASE
532 #define SRCBASE        "drivers/net/wireless/bcmdhd"
533 #endif
534 #define DHD_COMPILED "\nCompiled in " SRCBASE
535 #else
536 #define DHD_COMPILED
537 #endif /* DHD_DEBUG */
538
539 static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR
540 #ifdef DHD_DEBUG
541 "\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__
542 #endif
543 ;
544 static void dhd_net_if_lock_local(dhd_info_t *dhd);
545 static void dhd_net_if_unlock_local(dhd_info_t *dhd);
546 static void dhd_suspend_lock(dhd_pub_t *dhdp);
547 static void dhd_suspend_unlock(dhd_pub_t *dhdp);
548
549 #ifdef WLMEDIA_HTSF
550 void htsf_update(dhd_info_t *dhd, void *data);
551 tsf_t prev_tsf, cur_tsf;
552
553 uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx);
554 static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx);
555 static void dhd_dump_latency(void);
556 static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf);
557 static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf);
558 static void dhd_dump_htsfhisto(histo_t *his, char *s);
559 #endif /* WLMEDIA_HTSF */
560
561 /* Monitor interface */
562 int dhd_monitor_init(void *dhd_pub);
563 int dhd_monitor_uninit(void);
564
565
566
567 #if defined(WL_WIRELESS_EXT)
568 struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
569 #endif /* defined(WL_WIRELESS_EXT) */
570
571 static void dhd_dpc(ulong data);
572 /* forward decl */
573 extern int dhd_wait_pend8021x(struct net_device *dev);
574 void dhd_os_wd_timer_extend(void *bus, bool extend);
575
576 #ifdef TOE
577 #ifndef BDC
578 #error TOE requires BDC
579 #endif /* !BDC */
580 static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol);
581 static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol);
582 #endif /* TOE */
583
584 static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
585                              wl_event_msg_t *event_ptr, void **data_ptr);
586
587 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \
588         KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM_SLEEP)
589 static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored)
590 {
591         int ret = NOTIFY_DONE;
592
593         switch (action) {
594         case PM_HIBERNATION_PREPARE:
595         case PM_SUSPEND_PREPARE:
596                 dhd_mmc_suspend = TRUE;
597                 ret = NOTIFY_OK;
598                 break;
599         case PM_POST_HIBERNATION:
600         case PM_POST_SUSPEND:
601                 dhd_mmc_suspend = FALSE;
602                 ret = NOTIFY_OK;
603                 break;
604         }
605         smp_mb();
606         return ret;
607 }
608
609 static struct notifier_block dhd_sleep_pm_notifier = {
610         .notifier_call = dhd_sleep_pm_callback,
611         .priority = 10
612 };
613 extern int register_pm_notifier(struct notifier_block *nb);
614 extern int unregister_pm_notifier(struct notifier_block *nb);
615 #endif /* (LINUX_VERSION >= 2.6.27 && LINUX_VERSION <= 2.6.39 && CONFIG_PM_SLEEP */
616
617 #if defined(DHDTHREAD) && defined(RXFRAME_THREAD)
618 /* Request scheduling of the bus rx frame */
619 static void dhd_sched_rxf(dhd_pub_t *dhdp, void *skb);
620 static void dhd_os_rxflock(dhd_pub_t *pub);
621 static void dhd_os_rxfunlock(dhd_pub_t *pub);
622
623 static inline int dhd_rxf_enqueue(dhd_pub_t *dhdp, void* skb)
624 {
625         uint32 store_idx;
626         uint32 sent_idx;
627
628         if (!skb) {
629                 DHD_ERROR(("dhd_rxf_enqueue: NULL skb!!!\n"));
630                 return BCME_ERROR;
631         }
632
633         dhd_os_rxflock(dhdp);
634         store_idx = dhdp->store_idx;
635         sent_idx = dhdp->sent_idx;
636         if (dhdp->skbbuf[store_idx] != NULL) {
637                 /* Make sure the previous packets are processed */
638                 /* Do I need to make this context sleep here? Definitely in Single processor case */
639                 dhd_os_rxfunlock(dhdp);
640                 DHD_ERROR(("dhd_rxf_enqueue: pktbuf not consumed %p, store idx %d sent idx %d\n",
641                         skb, store_idx, sent_idx));
642                 msleep(1);
643                 return BCME_ERROR;
644         }
645         DHD_TRACE(("dhd_rxf_enqueue: Store SKB %p. idx %d -> %d\n",
646                 skb, store_idx, (store_idx + 1) & (MAXSKBPEND - 1)));
647         dhdp->skbbuf[store_idx] = skb;
648         dhdp->store_idx = (store_idx + 1) & (MAXSKBPEND - 1);
649         dhd_os_rxfunlock(dhdp);
650
651         return BCME_OK;
652 }
653
654 static inline void* dhd_rxf_dequeue(dhd_pub_t *dhdp)
655 {
656         uint32 store_idx;
657         uint32 sent_idx;
658         void *skb;
659
660         dhd_os_rxflock(dhdp);
661
662         store_idx = dhdp->store_idx;
663         sent_idx = dhdp->sent_idx;
664         skb = dhdp->skbbuf[sent_idx];
665
666         if (skb == NULL) {
667                 dhd_os_rxfunlock(dhdp);
668                 DHD_ERROR(("dhd_rxf_dequeue: Dequeued packet is NULL, store idx %d sent idx %d\n",
669                         store_idx, sent_idx));
670                 return NULL;
671         }
672
673         dhdp->skbbuf[sent_idx] = NULL;
674         dhdp->sent_idx = (sent_idx + 1) & (MAXSKBPEND - 1);
675
676         DHD_TRACE(("dhd_rxf_dequeue: netif_rx_ni(%p), sent idx %d\n",
677                 skb, sent_idx));
678
679         dhd_os_rxfunlock(dhdp);
680
681         return skb;
682 }
683 #endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */
684
685 static int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost)
686 {
687         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
688
689         if (prepost) { /* pre process */
690                 dhd_read_macaddr(dhd);
691         } else { /* post process */
692                 dhd_write_macaddr(&dhd->pub.mac);
693         }
694
695         return 0;
696 }
697
698 #if defined(PKT_FILTER_SUPPORT) && !defined(GAN_LITE_NAT_KEEPALIVE_FILTER)
699 static bool
700 _turn_on_arp_filter(dhd_pub_t *dhd, int op_mode)
701 {
702         bool _apply = FALSE;
703         /* In case of IBSS mode, apply arp pkt filter */
704         if (op_mode & DHD_FLAG_IBSS_MODE) {
705                 _apply = TRUE;
706                 goto exit;
707         }
708         /* In case of P2P GO or GC, apply pkt filter to pass arp pkt to host */
709         if ((dhd->arp_version == 1) &&
710                 (op_mode & (DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE))) {
711                 _apply = TRUE;
712                 goto exit;
713         }
714
715 exit:
716         return _apply;
717 }
718 #endif /* PKT_FILTER_SUPPORT && !GAN_LITE_NAT_KEEPALIVE_FILTER */
719
720 #ifdef PKT_FILTER_SUPPORT
721 void
722 dhd_set_packet_filter_mode(struct net_device *dev, char *command)
723 {
724         dhd_info_t *dhdi = *(dhd_info_t **)netdev_priv(dev);
725
726         dhdi->pub.pkt_filter_mode = bcm_strtoul(command, &command, 0);
727 }
728
729 int
730 dhd_set_packet_filter_ports(struct net_device *dev, char *command)
731 {
732         int i = 0, error = BCME_OK, count = 0, get_count = 0, action = 0;
733         uint16 portnum = 0, *ports = NULL, get_ports[WL_PKT_FILTER_PORTS_MAX];
734         dhd_info_t *dhdi = *(dhd_info_t **)netdev_priv(dev);
735         dhd_pub_t *dhdp = &dhdi->pub;
736         char iovbuf[WLC_IOCTL_SMLEN];
737
738         /* get action */
739         action = bcm_strtoul(command, &command, 0);
740         if (action > PKT_FILTER_PORTS_MAX)
741                 return BCME_BADARG;
742
743         if (action == PKT_FILTER_PORTS_LOOPBACK) {
744                 /* echo the loopback value if port filter is supported else error */
745                 bcm_mkiovar("cap", NULL, 0, iovbuf, sizeof(iovbuf));
746                 error = dhd_wl_ioctl_cmd(dhdp, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0);
747                 if (error < 0) {
748                         DHD_ERROR(("%s: Get Capability failed (error=%d)\n", __FUNCTION__, error));
749                         return error;
750                 }
751
752                 if (strstr(iovbuf, "pktfltr2"))
753                         return bcm_strtoul(command, &command, 0);
754                 else {
755                         DHD_ERROR(("%s: pktfltr2 is not supported\n", __FUNCTION__));
756                         return BCME_UNSUPPORTED;
757                 }
758         }
759
760         if (action == PKT_FILTER_PORTS_CLEAR) {
761                 /* action 0 is clear all ports */
762                 dhdp->pkt_filter_ports_count = 0;
763                 bzero(dhdp->pkt_filter_ports, sizeof(dhdp->pkt_filter_ports));
764         }
765         else {
766                 portnum = bcm_strtoul(command, &command, 0);
767                 if (portnum == 0) {
768                         /* no ports to add or remove */
769                         return BCME_BADARG;
770                 }
771
772                 /* get configured ports */
773                 count = dhdp->pkt_filter_ports_count;
774                 ports = dhdp->pkt_filter_ports;
775
776                 if (action == PKT_FILTER_PORTS_ADD) {
777                         /* action 1 is add ports */
778
779                         /* copy new ports */
780                         while ((portnum != 0) && (count < WL_PKT_FILTER_PORTS_MAX)) {
781                                 for (i = 0; i < count; i++) {
782                                         /* duplicate port */
783                                         if (portnum == ports[i])
784                                                 break;
785                                 }
786                                 if (portnum != ports[i])
787                                         ports[count++] = portnum;
788                                 portnum = bcm_strtoul(command, &command, 0);
789                         }
790                 } else if ((action == PKT_FILTER_PORTS_DEL) && (count > 0)) {
791                         /* action 2 is remove ports */
792                         bcopy(dhdp->pkt_filter_ports, get_ports, count * sizeof(uint16));
793                         get_count = count;
794
795                         while (portnum != 0) {
796                                 count = 0;
797                                 for (i = 0; i < get_count; i++) {
798                                         if (portnum != get_ports[i])
799                                                 ports[count++] = get_ports[i];
800                                 }
801                                 get_count = count;
802                                 bcopy(ports, get_ports, count * sizeof(uint16));
803                                 portnum = bcm_strtoul(command, &command, 0);
804                         }
805                 }
806                 dhdp->pkt_filter_ports_count = count;
807         }
808         return error;
809 }
810
811 static void
812 dhd_enable_packet_filter_ports(dhd_pub_t *dhd, bool enable)
813 {
814         int error = 0;
815         wl_pkt_filter_ports_t *portlist = NULL;
816         const uint pkt_filter_ports_buf_len = sizeof("pkt_filter_ports") +
817                 WL_PKT_FILTER_PORTS_FIXED_LEN + (WL_PKT_FILTER_PORTS_MAX * sizeof(uint16));
818         char pkt_filter_ports_buf[pkt_filter_ports_buf_len];
819         char iovbuf[pkt_filter_ports_buf_len];
820
821         DHD_TRACE(("%s: enable %d, in_suspend %d, mode %d, port count %d\n",
822                 __FUNCTION__, enable, dhd->in_suspend,
823                 dhd->pkt_filter_mode, dhd->pkt_filter_ports_count));
824
825         bzero(pkt_filter_ports_buf, sizeof(pkt_filter_ports_buf));
826         portlist = (wl_pkt_filter_ports_t*)pkt_filter_ports_buf;
827         portlist->version = WL_PKT_FILTER_PORTS_VERSION;
828         portlist->reserved = 0;
829
830         if (enable) {
831                 if (!(dhd->pkt_filter_mode & PKT_FILTER_MODE_PORTS_ONLY))
832                         return;
833
834                 /* enable port filter */
835                 dhd_master_mode |= PKT_FILTER_MODE_PORTS_ONLY;
836                 if (dhd->pkt_filter_mode & PKT_FILTER_MODE_FORWARD_ON_MATCH)
837                         /* whitelist mode: FORWARD_ON_MATCH */
838                         dhd_master_mode |= PKT_FILTER_MODE_FORWARD_ON_MATCH;
839                 else
840                         /* blacklist mode: DISCARD_ON_MATCH */
841                         dhd_master_mode &= ~PKT_FILTER_MODE_FORWARD_ON_MATCH;
842
843                 portlist->count = dhd->pkt_filter_ports_count;
844                 bcopy(dhd->pkt_filter_ports,
845                         portlist->ports, dhd->pkt_filter_ports_count * sizeof(uint16));
846         } else {
847                 /* disable port filter */
848                 portlist->count = 0;
849                 dhd_master_mode &= ~PKT_FILTER_MODE_PORTS_ONLY;
850                 dhd_master_mode |= PKT_FILTER_MODE_FORWARD_ON_MATCH;
851         }
852
853         DHD_INFO(("%s: update: mode %d, port count %d\n",
854                 __FUNCTION__, dhd_master_mode, portlist->count));
855
856         /* update ports */
857         bcm_mkiovar("pkt_filter_ports",
858                 (char*)portlist,
859                 (WL_PKT_FILTER_PORTS_FIXED_LEN + (portlist->count * sizeof(uint16))),
860                 iovbuf, sizeof(iovbuf));
861         error = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
862         if (error < 0)
863                 DHD_ERROR(("%s: set pkt_filter_ports failed %d\n", __FUNCTION__, error));
864
865         /* update mode */
866         bcm_mkiovar("pkt_filter_mode", (char*)&dhd_master_mode,
867                 sizeof(dhd_master_mode), iovbuf, sizeof(iovbuf));
868         error = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
869         if (error < 0)
870                 DHD_ERROR(("%s: set pkt_filter_mode failed %d\n", __FUNCTION__, error));
871
872         return;
873 }
874 #endif /* PKT_FILTER_SUPPORT */
875
876 void dhd_set_packet_filter(dhd_pub_t *dhd)
877 {
878 #ifdef PKT_FILTER_SUPPORT
879         int i;
880
881         DHD_TRACE(("%s: enter\n", __FUNCTION__));
882         if (dhd_pkt_filter_enable) {
883                 for (i = 0; i < dhd->pktfilter_count; i++) {
884                         dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
885                 }
886         }
887 #endif /* PKT_FILTER_SUPPORT */
888 }
889
890 void dhd_enable_packet_filter(int value, dhd_pub_t *dhd)
891 {
892 #ifdef PKT_FILTER_SUPPORT
893         int i;
894
895         DHD_TRACE(("%s: enter, value = %d\n", __FUNCTION__, value));
896
897         dhd_enable_packet_filter_ports(dhd, value);
898
899         /* 1 - Enable packet filter, only allow unicast packet to send up */
900         /* 0 - Disable packet filter */
901         if (dhd_pkt_filter_enable && (!value ||
902             (dhd_support_sta_mode(dhd) && !dhd->dhcp_in_progress)))
903             {
904                 for (i = 0; i < dhd->pktfilter_count; i++) {
905 #ifndef GAN_LITE_NAT_KEEPALIVE_FILTER
906                         if (value && (i == DHD_ARP_FILTER_NUM) &&
907                                 !_turn_on_arp_filter(dhd, dhd->op_mode)) {
908                                 DHD_TRACE(("Do not turn on ARP white list pkt filter:"
909                                         "val %d, cnt %d, op_mode 0x%x\n",
910                                         value, i, dhd->op_mode));
911                                 continue;
912                         }
913 #endif /* !GAN_LITE_NAT_KEEPALIVE_FILTER */
914                         dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
915                                 value, dhd_master_mode);
916                 }
917         }
918 #endif /* PKT_FILTER_SUPPORT */
919 }
920
921 static int dhd_set_suspend(int value, dhd_pub_t *dhd)
922 {
923 #ifndef SUPPORT_PM2_ONLY
924         int power_mode = PM_MAX;
925 #endif /* SUPPORT_PM2_ONLY */
926         /* wl_pkt_filter_enable_t       enable_parm; */
927         char iovbuf[32];
928         int bcn_li_dtim = 0; /* Default bcn_li_dtim in resume mode is 0 */
929 #ifndef ENABLE_FW_ROAM_SUSPEND
930         uint roamvar = 1;
931 #endif /* ENABLE_FW_ROAM_SUSPEND */
932
933         if (!dhd)
934                 return -ENODEV;
935
936         DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n",
937                 __FUNCTION__, value, dhd->in_suspend));
938
939         dhd_suspend_lock(dhd);
940         if (dhd->up) {
941                 if (value && dhd->in_suspend) {
942 #ifdef PKT_FILTER_SUPPORT
943                                 dhd->early_suspended = 1;
944 #endif
945                                 /* Kernel suspended */
946                                 DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__));
947
948 #ifndef SUPPORT_PM2_ONLY
949                                 dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
950                                                  sizeof(power_mode), TRUE, 0);
951 #endif /* SUPPORT_PM2_ONLY */
952
953                                 /* Enable packet filter, only allow unicast packet to send up */
954                                 dhd_enable_packet_filter(1, dhd);
955
956
957                                 /* If DTIM skip is set up as default, force it to wake
958                                  * each third DTIM for better power savings.  Note that
959                                  * one side effect is a chance to miss BC/MC packet.
960                                  */
961                                 bcn_li_dtim = dhd_get_suspend_bcn_li_dtim(dhd);
962                                 bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
963                                         4, iovbuf, sizeof(iovbuf));
964                                 if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf),
965                                         TRUE, 0) < 0)
966                                         DHD_ERROR(("%s: set dtim failed\n", __FUNCTION__));
967
968 #ifndef ENABLE_FW_ROAM_SUSPEND
969                                 /* Disable firmware roaming during suspend */
970                                 bcm_mkiovar("roam_off", (char *)&roamvar, 4,
971                                         iovbuf, sizeof(iovbuf));
972                                 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
973 #endif /* ENABLE_FW_ROAM_SUSPEND */
974                         } else {
975 #ifdef PKT_FILTER_SUPPORT
976                                 dhd->early_suspended = 0;
977 #endif
978                                 /* Kernel resumed  */
979                                 DHD_ERROR(("%s: Remove extra suspend setting \n", __FUNCTION__));
980
981 #ifndef SUPPORT_PM2_ONLY
982                                 power_mode = PM_FAST;
983                                 dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
984                                                  sizeof(power_mode), TRUE, 0);
985 #endif /* SUPPORT_PM2_ONLY */
986 #ifdef PKT_FILTER_SUPPORT
987                                 /* disable pkt filter */
988                                 dhd_enable_packet_filter(0, dhd);
989 #endif /* PKT_FILTER_SUPPORT */
990
991                                 /* restore pre-suspend setting for dtim_skip */
992                                 bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
993                                         4, iovbuf, sizeof(iovbuf));
994
995                                 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
996 #ifndef ENABLE_FW_ROAM_SUSPEND
997                                 roamvar = dhd_roam_disable;
998                                 bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf,
999                                         sizeof(iovbuf));
1000                                 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
1001 #endif /* ENABLE_FW_ROAM_SUSPEND */
1002                         }
1003         }
1004         dhd_suspend_unlock(dhd);
1005
1006         return 0;
1007 }
1008
1009 static int dhd_suspend_resume_helper(struct dhd_info *dhd, int val, int force)
1010 {
1011         dhd_pub_t *dhdp = &dhd->pub;
1012         int ret = 0;
1013
1014         DHD_OS_WAKE_LOCK(dhdp);
1015         /* Set flag when early suspend was called */
1016         dhdp->in_suspend = val;
1017         if ((force || !dhdp->suspend_disable_flag) &&
1018                 dhd_support_sta_mode(dhdp))
1019         {
1020                 ret = dhd_set_suspend(val, dhdp);
1021         }
1022
1023         DHD_OS_WAKE_UNLOCK(dhdp);
1024         return ret;
1025 }
1026
1027 #if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND)
1028 static void dhd_early_suspend(struct early_suspend *h)
1029 {
1030         struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
1031         DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__));
1032
1033         if (dhd)
1034                 dhd_suspend_resume_helper(dhd, 1, 0);
1035 }
1036
1037 static void dhd_late_resume(struct early_suspend *h)
1038 {
1039         struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
1040         DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__));
1041
1042         if (dhd)
1043                 dhd_suspend_resume_helper(dhd, 0, 0);
1044 }
1045 #endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */
1046
1047 /*
1048  * Generalized timeout mechanism.  Uses spin sleep with exponential back-off until
1049  * the sleep time reaches one jiffy, then switches over to task delay.  Usage:
1050  *
1051  *      dhd_timeout_start(&tmo, usec);
1052  *      while (!dhd_timeout_expired(&tmo))
1053  *              if (poll_something())
1054  *                      break;
1055  *      if (dhd_timeout_expired(&tmo))
1056  *              fatal();
1057  */
1058
1059 void
1060 dhd_timeout_start(dhd_timeout_t *tmo, uint usec)
1061 {
1062         tmo->limit = usec;
1063         tmo->increment = 0;
1064         tmo->elapsed = 0;
1065         tmo->tick = jiffies_to_usecs(1);
1066 }
1067
1068 int
1069 dhd_timeout_expired(dhd_timeout_t *tmo)
1070 {
1071         /* Does nothing the first call */
1072         if (tmo->increment == 0) {
1073                 tmo->increment = 1;
1074                 return 0;
1075         }
1076
1077         if (tmo->elapsed >= tmo->limit)
1078                 return 1;
1079
1080         /* Add the delay that's about to take place */
1081         tmo->elapsed += tmo->increment;
1082
1083         if (tmo->increment < tmo->tick) {
1084                 OSL_DELAY(tmo->increment);
1085                 tmo->increment *= 2;
1086                 if (tmo->increment > tmo->tick)
1087                         tmo->increment = tmo->tick;
1088         } else {
1089                 wait_queue_head_t delay_wait;
1090                 DECLARE_WAITQUEUE(wait, current);
1091                 init_waitqueue_head(&delay_wait);
1092                 add_wait_queue(&delay_wait, &wait);
1093                 set_current_state(TASK_INTERRUPTIBLE);
1094                 schedule_timeout(1);
1095                 remove_wait_queue(&delay_wait, &wait);
1096                 set_current_state(TASK_RUNNING);
1097         }
1098
1099         return 0;
1100 }
1101
1102 int
1103 dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
1104 {
1105         int i = 0;
1106
1107         ASSERT(dhd);
1108         while (i < DHD_MAX_IFS) {
1109                 if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
1110                         return i;
1111                 i++;
1112         }
1113
1114         return DHD_BAD_IF;
1115 }
1116
1117 struct net_device * dhd_idx2net(void *pub, int ifidx)
1118 {
1119         struct dhd_pub *dhd_pub = (struct dhd_pub *)pub;
1120         struct dhd_info *dhd_info;
1121
1122         if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS)
1123                 return NULL;
1124         dhd_info = dhd_pub->info;
1125         if (dhd_info && dhd_info->iflist[ifidx])
1126                 return dhd_info->iflist[ifidx]->net;
1127         return NULL;
1128 }
1129
1130 int
1131 dhd_ifname2idx(dhd_info_t *dhd, char *name)
1132 {
1133         int i = DHD_MAX_IFS;
1134
1135         ASSERT(dhd);
1136
1137         if (name == NULL || *name == '\0')
1138                 return 0;
1139
1140         while (--i > 0)
1141                 if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
1142                                 break;
1143
1144         DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name));
1145
1146         return i;       /* default - the primary interface */
1147 }
1148
1149 char *
1150 dhd_ifname(dhd_pub_t *dhdp, int ifidx)
1151 {
1152         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1153
1154         ASSERT(dhd);
1155
1156         if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
1157                 DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx));
1158                 return "<if_bad>";
1159         }
1160
1161         if (dhd->iflist[ifidx] == NULL) {
1162                 DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx));
1163                 return "<if_null>";
1164         }
1165
1166         if (dhd->iflist[ifidx]->net)
1167                 return dhd->iflist[ifidx]->net->name;
1168
1169         return "<if_none>";
1170 }
1171
1172 uint8 *
1173 dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx)
1174 {
1175         int i;
1176         dhd_info_t *dhd = (dhd_info_t *)dhdp;
1177
1178         ASSERT(dhd);
1179         for (i = 0; i < DHD_MAX_IFS; i++)
1180         if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx)
1181                 return dhd->iflist[i]->mac_addr;
1182
1183         return NULL;
1184 }
1185
1186
1187 static void
1188 _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
1189 {
1190         struct net_device *dev;
1191 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
1192         struct netdev_hw_addr *ha;
1193 #else
1194         struct dev_mc_list *mclist;
1195 #endif
1196         uint32 allmulti, cnt;
1197
1198         wl_ioctl_t ioc;
1199         char *buf, *bufp;
1200         uint buflen;
1201         int ret;
1202
1203                         ASSERT(dhd && dhd->iflist[ifidx]);
1204                         dev = dhd->iflist[ifidx]->net;
1205                         if (!dev)
1206                                 return;
1207 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1208                         netif_addr_lock_bh(dev);
1209 #endif
1210 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
1211                         cnt = netdev_mc_count(dev);
1212 #else
1213                         cnt = dev->mc_count;
1214 #endif /* LINUX_VERSION_CODE */
1215
1216 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1217                         netif_addr_unlock_bh(dev);
1218 #endif
1219
1220                         /* Determine initial value of allmulti flag */
1221         allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE;
1222
1223         /* Send down the multicast list first. */
1224
1225
1226         buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN);
1227         if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) {
1228                 DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
1229                            dhd_ifname(&dhd->pub, ifidx), cnt));
1230                 return;
1231         }
1232
1233         strncpy(bufp, "mcast_list", buflen - 1);
1234         bufp[buflen - 1] = '\0';
1235         bufp += strlen("mcast_list") + 1;
1236
1237         cnt = htol32(cnt);
1238         memcpy(bufp, &cnt, sizeof(cnt));
1239         bufp += sizeof(cnt);
1240
1241
1242 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1243                         netif_addr_lock_bh(dev);
1244 #endif
1245 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
1246                         netdev_for_each_mc_addr(ha, dev) {
1247                                 if (!cnt)
1248                                         break;
1249                                 memcpy(bufp, ha->addr, ETHER_ADDR_LEN);
1250                                 bufp += ETHER_ADDR_LEN;
1251                                 cnt--;
1252         }
1253 #else
1254         for (mclist = dev->mc_list; (mclist && (cnt > 0));
1255                 cnt--, mclist = mclist->next) {
1256                                 memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN);
1257                                 bufp += ETHER_ADDR_LEN;
1258                         }
1259 #endif /* LINUX_VERSION_CODE */
1260
1261 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1262                         netif_addr_unlock_bh(dev);
1263 #endif
1264
1265         memset(&ioc, 0, sizeof(ioc));
1266         ioc.cmd = WLC_SET_VAR;
1267         ioc.buf = buf;
1268         ioc.len = buflen;
1269         ioc.set = TRUE;
1270
1271         ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
1272         if (ret < 0) {
1273                 DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
1274                         dhd_ifname(&dhd->pub, ifidx), cnt));
1275                 allmulti = cnt ? TRUE : allmulti;
1276         }
1277
1278         MFREE(dhd->pub.osh, buf, buflen);
1279
1280         /* Now send the allmulti setting.  This is based on the setting in the
1281          * net_device flags, but might be modified above to be turned on if we
1282          * were trying to set some addresses and dongle rejected it...
1283          */
1284
1285         buflen = sizeof("allmulti") + sizeof(allmulti);
1286         if (!(buf = MALLOC(dhd->pub.osh, buflen))) {
1287                 DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx)));
1288                 return;
1289         }
1290         allmulti = htol32(allmulti);
1291
1292         if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) {
1293                 DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
1294                            dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen));
1295                 MFREE(dhd->pub.osh, buf, buflen);
1296                 return;
1297         }
1298
1299
1300         memset(&ioc, 0, sizeof(ioc));
1301         ioc.cmd = WLC_SET_VAR;
1302         ioc.buf = buf;
1303         ioc.len = buflen;
1304         ioc.set = TRUE;
1305
1306         ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
1307         if (ret < 0) {
1308                 DHD_ERROR(("%s: set allmulti %d failed\n",
1309                            dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
1310         }
1311
1312         MFREE(dhd->pub.osh, buf, buflen);
1313
1314         /* Finally, pick up the PROMISC flag as well, like the NIC driver does */
1315
1316         allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE;
1317
1318         allmulti = htol32(allmulti);
1319
1320         memset(&ioc, 0, sizeof(ioc));
1321         ioc.cmd = WLC_SET_PROMISC;
1322         ioc.buf = &allmulti;
1323         ioc.len = sizeof(allmulti);
1324         ioc.set = TRUE;
1325
1326         ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
1327         if (ret < 0) {
1328                 DHD_ERROR(("%s: set promisc %d failed\n",
1329                            dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
1330         }
1331 }
1332
1333 int
1334 _dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr)
1335 {
1336         char buf[32];
1337         wl_ioctl_t ioc;
1338         int ret;
1339
1340         if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) {
1341                 DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx)));
1342                 return -1;
1343         }
1344         memset(&ioc, 0, sizeof(ioc));
1345         ioc.cmd = WLC_SET_VAR;
1346         ioc.buf = buf;
1347         ioc.len = 32;
1348         ioc.set = TRUE;
1349
1350         ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
1351         if (ret < 0) {
1352                 DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx)));
1353         } else {
1354                 memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN);
1355                 memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN);
1356         }
1357
1358         return ret;
1359 }
1360
1361 #ifdef SOFTAP
1362 extern struct net_device *ap_net_dev;
1363 extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */
1364 #endif
1365
1366 static void
1367 dhd_op_if(dhd_if_t *ifp)
1368 {
1369         dhd_info_t      *dhd;
1370         int ret = 0, err = 0;
1371 #ifdef SOFTAP
1372         unsigned long flags;
1373 #endif
1374
1375         if (!ifp || !ifp->info || !ifp->idx)
1376                 return;
1377         ASSERT(ifp && ifp->info && ifp->idx);   /* Virtual interfaces only */
1378         dhd = ifp->info;
1379
1380         DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state));
1381
1382 #ifdef WL_CFG80211
1383         if (wl_cfg80211_is_progress_ifchange())
1384                         return;
1385
1386 #endif
1387         switch (ifp->state) {
1388         case DHD_IF_ADD:
1389                 /*
1390                  * Delete the existing interface before overwriting it
1391                  * in case we missed the WLC_E_IF_DEL event.
1392                  */
1393                 if (ifp->net != NULL) {
1394                         DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n",
1395                          __FUNCTION__, ifp->net->name));
1396                         netif_stop_queue(ifp->net);
1397                         unregister_netdev(ifp->net);
1398                         free_netdev(ifp->net);
1399                 }
1400                 /* Allocate etherdev, including space for private structure */
1401                 if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) {
1402                         DHD_ERROR(("%s: OOM - alloc_etherdev(%d)\n", __FUNCTION__, sizeof(dhd)));
1403                         ret = -ENOMEM;
1404                 }
1405                 if (ret == 0) {
1406                         strncpy(ifp->net->name, ifp->name, IFNAMSIZ);
1407                         ifp->net->name[IFNAMSIZ - 1] = '\0';
1408                         memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
1409 #ifdef WL_CFG80211
1410                         if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)
1411                                 if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx,
1412                                         (void*)dhd_net_attach)) {
1413                                         ifp->state = DHD_IF_NONE;
1414                                         ifp->event2cfg80211 = TRUE;
1415                                         return;
1416                                 }
1417 #endif
1418                         if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) {
1419                                 DHD_ERROR(("%s: dhd_net_attach failed, err %d\n",
1420                                         __FUNCTION__, err));
1421                                 ret = -EOPNOTSUPP;
1422                         } else {
1423 #if defined(SOFTAP)
1424                 if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
1425                                  /* semaphore that the soft AP CODE waits on */
1426                                 flags = dhd_os_spin_lock(&dhd->pub);
1427
1428                                 /* save ptr to wl0.1 netdev for use in wl_iw.c  */
1429                                 ap_net_dev = ifp->net;
1430                                  /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */
1431                                 up(&ap_eth_ctl.sema);
1432                                 dhd_os_spin_unlock(&dhd->pub, flags);
1433                 }
1434 #endif
1435                                 DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n",
1436                                         current->pid, ifp->net->name));
1437                                 ifp->state = DHD_IF_NONE;
1438                         }
1439                 }
1440                 break;
1441         case DHD_IF_DEL:
1442                 /* Make sure that we don't enter again here if .. */
1443                 /* dhd_op_if is called again from some other context */
1444                 ifp->state = DHD_IF_DELETING;
1445                 if (ifp->net != NULL) {
1446                         DHD_TRACE(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__));
1447                         netif_stop_queue(ifp->net);
1448 #ifdef WL_CFG80211
1449                         if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
1450                                 wl_cfg80211_ifdel_ops(ifp->net);
1451                         }
1452 #endif
1453                         unregister_netdev(ifp->net);
1454                         ret = DHD_DEL_IF;       /* Make sure the free_netdev() is called */
1455 #ifdef WL_CFG80211
1456                         if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
1457                                 wl_cfg80211_notify_ifdel();
1458                         }
1459 #endif
1460                 }
1461                 break;
1462         case DHD_IF_DELETING:
1463                 break;
1464         default:
1465                 DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state));
1466                 ASSERT(!ifp->state);
1467                 break;
1468         }
1469
1470         if (ret < 0) {
1471                 ifp->set_multicast = FALSE;
1472                 if (ifp->net) {
1473                         free_netdev(ifp->net);
1474                         ifp->net = NULL;
1475                 }
1476                 dhd->iflist[ifp->idx] = NULL;
1477 #ifdef SOFTAP
1478                 flags = dhd_os_spin_lock(&dhd->pub);
1479                 if (ifp->net == ap_net_dev)
1480                         ap_net_dev = NULL;   /*  NULL  SOFTAP global wl0.1 as well */
1481                 dhd_os_spin_unlock(&dhd->pub, flags);
1482 #endif /*  SOFTAP */
1483                 MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
1484         }
1485 }
1486
1487 #ifdef DHDTCPACK_SUPPRESS
1488 uint dhd_use_tcpack_suppress = TRUE;
1489 module_param(dhd_use_tcpack_suppress, uint, FALSE);
1490 extern bool dhd_tcpack_suppress(dhd_pub_t *dhdp, void *pkt);
1491 #endif /* DHDTCPACK_SUPPRESS */
1492
1493 static int
1494 _dhd_sysioc_thread(void *data)
1495 {
1496         tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1497         dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1498         struct ipv6_addr *iter, *next;
1499         int i, ret;
1500 #ifdef SOFTAP
1501         bool in_ap = FALSE;
1502         unsigned long flags;
1503 #endif
1504
1505         while (down_interruptible(&tsk->sema) == 0) {
1506
1507                 SMP_RD_BARRIER_DEPENDS();
1508                 if (tsk->terminated) {
1509                         break;
1510                 }
1511
1512                 dhd_net_if_lock_local(dhd);
1513                 DHD_OS_WAKE_LOCK(&dhd->pub);
1514
1515                 for (i = 0; i < DHD_MAX_IFS; i++) {
1516                         if (dhd->iflist[i]) {
1517                                 DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i));
1518 #ifdef SOFTAP
1519                                 flags = dhd_os_spin_lock(&dhd->pub);
1520                                 in_ap = (ap_net_dev != NULL);
1521                                 dhd_os_spin_unlock(&dhd->pub, flags);
1522 #endif /* SOFTAP */
1523                                 if (dhd->iflist[i] && dhd->iflist[i]->state)
1524                                         dhd_op_if(dhd->iflist[i]);
1525
1526                                 if (dhd->iflist[i] == NULL) {
1527                                         DHD_TRACE(("\n\n %s: interface %d just been removed,"
1528                                                 "!\n\n", __FUNCTION__, i));
1529                                         continue;
1530                                 }
1531 #ifdef SOFTAP
1532                                 if (in_ap && dhd->set_macaddress == i+1)  {
1533                                         DHD_TRACE(("attempt to set MAC for %s in AP Mode,"
1534                                                 "blocked. \n", dhd->iflist[i]->net->name));
1535                                         dhd->set_macaddress = 0;
1536                                         continue;
1537                                 }
1538
1539                                 if (in_ap && dhd->iflist[i]->set_multicast)  {
1540                                         DHD_TRACE(("attempt to set MULTICAST list for %s"
1541                                          "in AP Mode, blocked. \n", dhd->iflist[i]->net->name));
1542                                         dhd->iflist[i]->set_multicast = FALSE;
1543                                         continue;
1544                                 }
1545 #endif /* SOFTAP */
1546                                 if (dhd->pub.up == 0)
1547                                         continue;
1548                                 if (dhd->iflist[i]->set_multicast) {
1549                                         dhd->iflist[i]->set_multicast = FALSE;
1550                                         _dhd_set_multicast_list(dhd, i);
1551
1552                                 }
1553                                 list_for_each_entry_safe(iter, next,
1554                                         &dhd->iflist[i]->ipv6_list, list) {
1555                                         spin_lock_bh(&dhd->iflist[i]->ipv6_lock);
1556                                         list_del(&iter->list);
1557                                         spin_unlock_bh(&dhd->iflist[i]->ipv6_lock);
1558                                         if (iter->ipv6_oper == DHD_IPV6_ADDR_ADD) {
1559                                                 ret = dhd_ndo_enable(&dhd->pub, TRUE);
1560                                                 if (ret < 0) {
1561                                                         DHD_ERROR(("%s: Enabling NDO Failed %d\n",
1562                                                                 __FUNCTION__, ret));
1563                                                         continue;
1564                                                 }
1565                                                 ret = dhd_ndo_add_ip(&dhd->pub,
1566                                                         (char*)&iter->ipv6_addr[0], i);
1567                                                 if (ret < 0) {
1568                                                         DHD_ERROR(("%s: Adding host ip fail %d\n",
1569                                                                 __FUNCTION__, ret));
1570                                                         continue;
1571                                                 }
1572                                         } else {
1573                                                 ret = dhd_ndo_remove_ip(&dhd->pub, i);
1574                                                 if (ret < 0) {
1575                                                         DHD_ERROR(("%s: Removing host ip fail %d\n",
1576                                                                 __FUNCTION__, ret));
1577                                                         continue;
1578                                                 }
1579                                         }
1580                                         NATIVE_MFREE(dhd->pub.osh, iter, sizeof(struct ipv6_addr));
1581                                 }
1582                                 if (dhd->set_macaddress == i+1) {
1583                                         dhd->set_macaddress = 0;
1584                                         if (_dhd_set_mac_address(dhd, i, &dhd->macvalue) == 0) {
1585                                                 DHD_INFO((
1586                                                         "%s: MACID is overwritten\n",
1587                                                         __FUNCTION__));
1588                                         } else {
1589                                                 DHD_ERROR((
1590                                                         "%s: _dhd_set_mac_address() failed\n",
1591                                                         __FUNCTION__));
1592                                         }
1593                                 }
1594                         }
1595                 }
1596
1597                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1598                 dhd_net_if_unlock_local(dhd);
1599         }
1600         DHD_TRACE(("%s: stopped\n", __FUNCTION__));
1601         complete_and_exit(&tsk->completed, 0);
1602 }
1603
1604 static int
1605 dhd_set_mac_address(struct net_device *dev, void *addr)
1606 {
1607         int ret = 0;
1608
1609         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1610         struct sockaddr *sa = (struct sockaddr *)addr;
1611         int ifidx;
1612
1613         ifidx = dhd_net2idx(dhd, dev);
1614         if (ifidx == DHD_BAD_IF)
1615                 return -1;
1616
1617         ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0);
1618         memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN);
1619         dhd->set_macaddress = ifidx+1;
1620         up(&dhd->thr_sysioc_ctl.sema);
1621
1622         return ret;
1623 }
1624
1625 static void
1626 dhd_set_multicast_list(struct net_device *dev)
1627 {
1628         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1629         int ifidx;
1630
1631         ifidx = dhd_net2idx(dhd, dev);
1632         if (ifidx == DHD_BAD_IF)
1633                 return;
1634
1635         ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0);
1636         dhd->iflist[ifidx]->set_multicast = TRUE;
1637         up(&dhd->thr_sysioc_ctl.sema);
1638 }
1639
1640 #ifdef PROP_TXSTATUS
1641 int
1642 dhd_os_wlfc_block(dhd_pub_t *pub)
1643 {
1644         dhd_info_t *di = (dhd_info_t *)(pub->info);
1645         ASSERT(di != NULL);
1646         spin_lock_bh(&di->wlfc_spinlock);
1647         return 1;
1648 }
1649
1650 int
1651 dhd_os_wlfc_unblock(dhd_pub_t *pub)
1652 {
1653         dhd_info_t *di = (dhd_info_t *)(pub->info);
1654
1655         ASSERT(di != NULL);
1656         spin_unlock_bh(&di->wlfc_spinlock);
1657         return 1;
1658 }
1659
1660 const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 };
1661 uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
1662 #define WME_PRIO2AC(prio)       wme_fifo2ac[prio2fifo[(prio)]]
1663
1664 #endif /* PROP_TXSTATUS */
1665 int
1666 dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
1667 {
1668         int ret = BCME_OK;
1669         dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1670         struct ether_header *eh = NULL;
1671
1672         /* Reject if down */
1673         if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
1674                 /* free the packet here since the caller won't */
1675                 PKTFREE(dhdp->osh, pktbuf, TRUE);
1676                 return -ENODEV;
1677         }
1678
1679         /* Update multicast statistic */
1680         if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_HDR_LEN) {
1681                 uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf);
1682                 eh = (struct ether_header *)pktdata;
1683
1684                 if (ETHER_ISMULTI(eh->ether_dhost))
1685                         dhdp->tx_multicast++;
1686                 if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X)
1687                         atomic_inc(&dhd->pend_8021x_cnt);
1688         } else {
1689                         PKTFREE(dhd->pub.osh, pktbuf, TRUE);
1690                         return BCME_ERROR;
1691         }
1692
1693         /* Look into the packet and update the packet priority */
1694 #ifndef PKTPRIO_OVERRIDE
1695         if (PKTPRIO(pktbuf) == 0)
1696 #endif 
1697                 pktsetprio(pktbuf, FALSE);
1698
1699 #ifdef PROP_TXSTATUS
1700         if (dhdp->wlfc_state) {
1701                 /* store the interface ID */
1702                 DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx);
1703
1704                 /* store destination MAC in the tag as well */
1705                 DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost);
1706
1707                 /* decide which FIFO this packet belongs to */
1708                 if (ETHER_ISMULTI(eh->ether_dhost))
1709                         /* one additional queue index (highest AC + 1) is used for bc/mc queue */
1710                         DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT);
1711                 else
1712                         DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf)));
1713         } else
1714 #endif /* PROP_TXSTATUS */
1715         /* If the protocol uses a data header, apply it */
1716         dhd_prot_hdrpush(dhdp, ifidx, pktbuf);
1717
1718         /* Use bus module to send data frame */
1719 #ifdef WLMEDIA_HTSF
1720         dhd_htsf_addtxts(dhdp, pktbuf);
1721 #endif
1722 #ifdef DHDTCPACK_SUPPRESS
1723         if (dhd_use_tcpack_suppress && dhd_tcpack_suppress(dhdp, pktbuf))
1724                 ret = BCME_OK;
1725         else
1726 #endif /* DHDTCPACK_SUPPRESS */
1727 #ifdef PROP_TXSTATUS
1728         {
1729         dhd_os_wlfc_block(dhdp);
1730         if (dhdp->wlfc_state && ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode
1731                 != WLFC_FCMODE_NONE) {
1732                 dhd_wlfc_commit_packets(dhdp->wlfc_state,  (f_commitpkt_t)dhd_bus_txdata,
1733                         dhdp->bus, pktbuf);
1734                 if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) {
1735                         ((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0;
1736                 }
1737                 dhd_os_wlfc_unblock(dhdp);
1738         }
1739         else {
1740                 dhd_os_wlfc_unblock(dhdp);
1741                 /* non-proptxstatus way */
1742                 ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1743         }
1744         }
1745 #else
1746         ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1747 #endif /* PROP_TXSTATUS */
1748
1749         return ret;
1750 }
1751
1752 int
1753 dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
1754 {
1755         int ret;
1756         uint datalen;
1757         void *pktbuf;
1758         dhd_info_t *dhd  =  *(dhd_info_t **)netdev_priv(net);
1759         dhd_if_t *ifp = NULL;
1760         int ifidx;
1761 #ifdef WLMEDIA_HTSF
1762         uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz;
1763 #else
1764         uint8 htsfdlystat_sz = 0;
1765 #endif
1766
1767         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1768
1769         DHD_OS_WAKE_LOCK(&dhd->pub);
1770
1771         /* Reject if down */
1772         if (dhd->pub.busstate == DHD_BUS_DOWN || dhd->pub.hang_was_sent) {
1773                 DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n",
1774                         __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
1775                 netif_stop_queue(net);
1776                 /* Send Event when bus down detected during data session */
1777                 if (dhd->pub.up) {
1778                         DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__));
1779                         net_os_send_hang_message(net);
1780                 }
1781                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1782 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20))
1783                 return -ENODEV;
1784 #else
1785                 return NETDEV_TX_BUSY;
1786 #endif
1787         }
1788
1789         ifidx = dhd_net2idx(dhd, net);
1790         if (ifidx == DHD_BAD_IF) {
1791                 DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx));
1792                 netif_stop_queue(net);
1793                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
1794 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20))
1795                 return -ENODEV;
1796 #else
1797                 return NETDEV_TX_BUSY;
1798 #endif
1799         }
1800
1801         ifp = dhd->iflist[ifidx];
1802         datalen  = PKTLEN(dhdp->osh, skb);
1803
1804         /* Make sure there's enough room for any header */
1805
1806         if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) {
1807                 struct sk_buff *skb2;
1808
1809                 DHD_INFO(("%s: insufficient headroom\n",
1810                           dhd_ifname(&dhd->pub, ifidx)));
1811                 dhd->pub.tx_realloc++;
1812
1813                 skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz);
1814
1815                 dev_kfree_skb(skb);
1816                 if ((skb = skb2) == NULL) {
1817                         DHD_ERROR(("%s: skb_realloc_headroom failed\n",
1818                                    dhd_ifname(&dhd->pub, ifidx)));
1819                         ret = -ENOMEM;
1820                         goto done;
1821                 }
1822         }
1823
1824         /* Convert to packet */
1825         if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) {
1826                 DHD_ERROR(("%s: PKTFRMNATIVE failed\n",
1827                            dhd_ifname(&dhd->pub, ifidx)));
1828                 dev_kfree_skb_any(skb);
1829                 ret = -ENOMEM;
1830                 goto done;
1831         }
1832 #ifdef WLMEDIA_HTSF
1833         if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) {
1834                 uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf);
1835                 struct ether_header *eh = (struct ether_header *)pktdata;
1836
1837                 if (!ETHER_ISMULTI(eh->ether_dhost) &&
1838                         (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) {
1839                         eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS);
1840                 }
1841         }
1842 #endif
1843
1844         ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf);
1845
1846 done:
1847         if (ret) {
1848                         ifp->stats.tx_dropped++;
1849         }
1850         else {
1851                         dhd->pub.tx_packets++;
1852                         ifp->stats.tx_packets++;
1853                         ifp->stats.tx_bytes += datalen;
1854         }
1855
1856         DHD_OS_WAKE_UNLOCK(&dhd->pub);
1857
1858         /* Return ok: we always eat the packet */
1859 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20))
1860         return 0;
1861 #else
1862         return NETDEV_TX_OK;
1863 #endif
1864 }
1865
1866 void
1867 dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
1868 {
1869         struct net_device *net;
1870         dhd_info_t *dhd = dhdp->info;
1871         int i;
1872
1873         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1874
1875         ASSERT(dhd);
1876
1877         if (ifidx == ALL_INTERFACES) {
1878                 /* Flow control on all active interfaces */
1879                 dhdp->txoff = state;
1880                 for (i = 0; i < DHD_MAX_IFS; i++) {
1881                         if (dhd->iflist[i]) {
1882                                 net = dhd->iflist[i]->net;
1883                                 if (state == ON)
1884                                         netif_stop_queue(net);
1885                                 else
1886                                         netif_wake_queue(net);
1887                         }
1888                 }
1889         }
1890         else {
1891                 if (dhd->iflist[ifidx]) {
1892                         net = dhd->iflist[ifidx]->net;
1893                         if (state == ON)
1894                                 netif_stop_queue(net);
1895                         else
1896                                 netif_wake_queue(net);
1897                 }
1898         }
1899 }
1900
1901 #ifdef DHD_RX_DUMP
1902 typedef struct {
1903         uint16 type;
1904         const char *str;
1905 } PKTTYPE_INFO;
1906
1907 static const PKTTYPE_INFO packet_type_info[] =
1908 {
1909         { ETHER_TYPE_IP, "IP" },
1910         { ETHER_TYPE_ARP, "ARP" },
1911         { ETHER_TYPE_BRCM, "BRCM" },
1912         { ETHER_TYPE_802_1X, "802.1X" },
1913         { ETHER_TYPE_WAI, "WAPI" },
1914         { 0, ""}
1915 };
1916
1917 static const char *_get_packet_type_str(uint16 type)
1918 {
1919         int i;
1920         int n = sizeof(packet_type_info)/sizeof(packet_type_info[1]) - 1;
1921
1922         for (i = 0; i < n; i++) {
1923                 if (packet_type_info[i].type == type)
1924                         return packet_type_info[i].str;
1925         }
1926
1927         return packet_type_info[n].str;
1928 }
1929 #endif /* DHD_RX_DUMP */
1930
1931 void
1932 dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
1933 {
1934         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1935         struct sk_buff *skb;
1936         uchar *eth;
1937         uint len;
1938         void *data, *pnext = NULL;
1939         int i;
1940         dhd_if_t *ifp;
1941         wl_event_msg_t event;
1942         int tout_rx = 0;
1943         int tout_ctrl = 0;
1944 #if defined(DHDTHREAD) && defined(RXFRAME_THREAD)
1945         void *skbhead = NULL;
1946         void *skbprev = NULL;
1947 #endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */
1948 #if defined(DHD_RX_DUMP) || defined(DHD_8021X_DUMP)
1949         char *dump_data;
1950         uint16 protocol;
1951 #endif /* DHD_RX_DUMP || DHD_8021X_DUMP */
1952
1953         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1954
1955         for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) {
1956
1957                 pnext = PKTNEXT(dhdp->osh, pktbuf);
1958                 PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
1959
1960                 ifp = dhd->iflist[ifidx];
1961                 if (ifp == NULL) {
1962                         DHD_ERROR(("%s: ifp is NULL. drop packet\n",
1963                                 __FUNCTION__));
1964                         PKTFREE(dhdp->osh, pktbuf, TRUE);
1965                         continue;
1966                 }
1967 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1968                 /* Dropping packets before registering net device to avoid kernel panic */
1969 #ifndef PROP_TXSTATUS_VSDB
1970                 if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) {
1971 #else
1972                 if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) {
1973 #endif /* PROP_TXSTATUS_VSDB */
1974                         DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n",
1975                         __FUNCTION__));
1976                         PKTFREE(dhdp->osh, pktbuf, TRUE);
1977                         continue;
1978                 }
1979 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
1980
1981
1982 #ifdef PROP_TXSTATUS
1983                 if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) {
1984                         /* WLFC may send header only packet when
1985                         there is an urgent message but no packet to
1986                         piggy-back on
1987                         */
1988                         ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++;
1989                         PKTFREE(dhdp->osh, pktbuf, TRUE);
1990                         continue;
1991                 }
1992 #endif
1993
1994                 skb = PKTTONATIVE(dhdp->osh, pktbuf);
1995
1996                 /* Get the protocol, maintain skb around eth_type_trans()
1997                  * The main reason for this hack is for the limitation of
1998                  * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len'
1999                  * to perform skb_pull inside vs ETH_HLEN. Since to avoid
2000                  * coping of the packet coming from the network stack to add
2001                  * BDC, Hardware header etc, during network interface registration
2002                  * we set the 'net->hard_header_len' to ETH_HLEN + extra space required
2003                  * for BDC, Hardware header etc. and not just the ETH_HLEN
2004                  */
2005                 eth = skb->data;
2006                 len = skb->len;
2007
2008 #if defined(DHD_RX_DUMP) || defined(DHD_8021X_DUMP)
2009                 dump_data = skb->data;
2010                 protocol = (dump_data[12] << 8) | dump_data[13];
2011
2012                 if (protocol == ETHER_TYPE_802_1X) {
2013                         DHD_ERROR(("ETHER_TYPE_802_1X: "
2014                                 "ver %d, type %d, replay %d\n",
2015                                 dump_data[14], dump_data[15],
2016                                 dump_data[30]));
2017                 }
2018 #endif /* DHD_RX_DUMP || DHD_8021X_DUMP */
2019 #if defined(DHD_RX_DUMP)
2020                 DHD_ERROR(("RX DUMP - %s\n", _get_packet_type_str(protocol)));
2021                 if (protocol != ETHER_TYPE_BRCM) {
2022                         if (dump_data[0] == 0xFF) {
2023                                 DHD_ERROR(("%s: BROADCAST\n", __FUNCTION__));
2024
2025                                 if ((dump_data[12] == 8) &&
2026                                         (dump_data[13] == 6)) {
2027                                         DHD_ERROR(("%s: ARP %d\n",
2028                                                 __FUNCTION__, dump_data[0x15]));
2029                                 }
2030                         } else if (dump_data[0] & 1) {
2031                                 DHD_ERROR(("%s: MULTICAST: " MACDBG "\n",
2032                                         __FUNCTION__, MAC2STRDBG(dump_data)));
2033                         }
2034 #ifdef DHD_RX_FULL_DUMP
2035                         {
2036                                 int k;
2037                                 for (k = 0; k < skb->len; k++) {
2038                                         DHD_ERROR(("%02X ", dump_data[k]));
2039                                         if ((k & 15) == 15)
2040                                                 DHD_ERROR(("\n"));
2041                                 }
2042                                 DHD_ERROR(("\n"));
2043                         }
2044 #endif /* DHD_RX_FULL_DUMP */
2045                 }
2046 #endif /* DHD_RX_DUMP */
2047
2048                 ifp = dhd->iflist[ifidx];
2049                 if (ifp == NULL)
2050                         ifp = dhd->iflist[0];
2051
2052                 ASSERT(ifp);
2053                 skb->dev = ifp->net;
2054                 skb->protocol = eth_type_trans(skb, skb->dev);
2055
2056                 if (skb->pkt_type == PACKET_MULTICAST) {
2057                         dhd->pub.rx_multicast++;
2058                 }
2059
2060                 skb->data = eth;
2061                 skb->len = len;
2062
2063 #ifdef WLMEDIA_HTSF
2064                 dhd_htsf_addrxts(dhdp, pktbuf);
2065 #endif
2066                 /* Strip header, count, deliver upward */
2067                 skb_pull(skb, ETH_HLEN);
2068
2069                 /* Process special event packets and then discard them */
2070                 memset(&event, 0, sizeof(event));
2071                 if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) {
2072                         dhd_wl_host_event(dhd, &ifidx,
2073 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
2074                         skb_mac_header(skb),
2075 #else
2076                         skb->mac.raw,
2077 #endif
2078                         &event,
2079                         &data);
2080
2081                         wl_event_to_host_order(&event);
2082                         if (!tout_ctrl)
2083                                 tout_ctrl = DHD_PACKET_TIMEOUT_MS;
2084
2085 #if defined(PNO_SUPPORT)
2086                         if (event.event_type == WLC_E_PFN_NET_FOUND) {
2087                                 /* enforce custom wake lock to garantee that Kernel not suspended */
2088                                 tout_ctrl = CUSTOM_PNO_EVENT_LOCK_xTIME * DHD_PACKET_TIMEOUT_MS;
2089                         }
2090 #endif /* PNO_SUPPORT */
2091
2092 #ifdef DHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT
2093                         PKTFREE(dhdp->osh, pktbuf, TRUE);
2094                         continue;
2095 #endif /* DHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT */
2096                 } else {
2097                         tout_rx = DHD_PACKET_TIMEOUT_MS;
2098                 }
2099
2100                 ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
2101                 if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
2102                         ifp = dhd->iflist[ifidx];
2103
2104                 if (ifp->net)
2105                         ifp->net->last_rx = jiffies;
2106
2107                 dhdp->dstats.rx_bytes += skb->len;
2108                 dhdp->rx_packets++; /* Local count */
2109                 ifp->stats.rx_bytes += skb->len;
2110                 ifp->stats.rx_packets++;
2111
2112                 if (in_interrupt()) {
2113                         netif_rx(skb);
2114                 } else {
2115                         /* If the receive is not processed inside an ISR,
2116                          * the softirqd must be woken explicitly to service
2117                          * the NET_RX_SOFTIRQ.  In 2.6 kernels, this is handled
2118                          * by netif_rx_ni(), but in earlier kernels, we need
2119                          * to do it manually.
2120                          */
2121 #if defined(DHDTHREAD) && defined(RXFRAME_THREAD)
2122                         if (!skbhead)
2123                                 skbhead = skb;
2124                         else
2125                                 PKTSETNEXT(wl->sh.osh, skbprev, skb);
2126                         skbprev = skb;
2127 #else
2128 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
2129                         netif_rx_ni(skb);
2130 #else
2131                         ulong flags;
2132                         netif_rx(skb);
2133                         local_irq_save(flags);
2134                         RAISE_RX_SOFTIRQ();
2135                         local_irq_restore(flags);
2136 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
2137 #endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */
2138                 }
2139         }
2140 #if defined(DHDTHREAD) && defined(RXFRAME_THREAD)
2141         if (skbhead)
2142                 dhd_sched_rxf(dhdp, skbhead);
2143 #endif
2144         DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx);
2145         DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl);
2146 }
2147
2148 void
2149 dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
2150 {
2151         /* Linux version has nothing to do */
2152         return;
2153 }
2154
2155 void
2156 dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success)
2157 {
2158         dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
2159         struct ether_header *eh;
2160         uint16 type;
2161
2162         dhd_prot_hdrpull(dhdp, NULL, txp, NULL, NULL);
2163
2164         eh = (struct ether_header *)PKTDATA(dhdp->osh, txp);
2165         type  = ntoh16(eh->ether_type);
2166
2167         if (type == ETHER_TYPE_802_1X)
2168                 atomic_dec(&dhd->pend_8021x_cnt);
2169
2170 }
2171
2172 static struct net_device_stats *
2173 dhd_get_stats(struct net_device *net)
2174 {
2175         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
2176         dhd_if_t *ifp;
2177         int ifidx;
2178
2179         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2180
2181         ifidx = dhd_net2idx(dhd, net);
2182         if (ifidx == DHD_BAD_IF) {
2183                 DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__));
2184                 return NULL;
2185         }
2186
2187         ifp = dhd->iflist[ifidx];
2188         ASSERT(dhd && ifp);
2189
2190         if (dhd->pub.up) {
2191                 /* Use the protocol to get dongle stats */
2192                 dhd_prot_dstats(&dhd->pub);
2193         }
2194
2195         /* Copy dongle stats to net device stats */
2196         ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
2197         ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
2198         ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
2199         ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
2200         ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
2201         ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
2202         ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
2203         ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
2204         ifp->stats.multicast = dhd->pub.dstats.multicast;
2205
2206         return &ifp->stats;
2207 }
2208
2209 #ifdef DHDTHREAD
2210 static int
2211 dhd_watchdog_thread(void *data)
2212 {
2213         tsk_ctl_t *tsk = (tsk_ctl_t *)data;
2214         dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
2215         /* This thread doesn't need any user-level access,
2216          * so get rid of all our resources
2217          */
2218         if (dhd_watchdog_prio > 0) {
2219                 struct sched_param param;
2220                 param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)?
2221                         dhd_watchdog_prio:(MAX_RT_PRIO-1);
2222                 setScheduler(current, SCHED_FIFO, &param);
2223         }
2224
2225         while (1)
2226                 if (down_interruptible (&tsk->sema) == 0) {
2227                         unsigned long flags;
2228                         unsigned long jiffies_at_start = jiffies;
2229                         unsigned long time_lapse;
2230
2231                         SMP_RD_BARRIER_DEPENDS();
2232                         if (tsk->terminated) {
2233                                 break;
2234                         }
2235
2236                         dhd_os_sdlock(&dhd->pub);
2237                         if (dhd->pub.dongle_reset == FALSE) {
2238                                 DHD_TIMER(("%s:\n", __FUNCTION__));
2239
2240                                 /* Call the bus module watchdog */
2241                                 dhd_bus_watchdog(&dhd->pub);
2242
2243                                 flags = dhd_os_spin_lock(&dhd->pub);
2244                                 /* Count the tick for reference */
2245                                 dhd->pub.tickcnt++;
2246                                 time_lapse = jiffies - jiffies_at_start;
2247
2248                                 /* Reschedule the watchdog */
2249                                 if (dhd->wd_timer_valid)
2250                                         mod_timer(&dhd->timer,
2251                                         jiffies +
2252                                         msecs_to_jiffies(dhd_watchdog_ms) -
2253                                         min(msecs_to_jiffies(dhd_watchdog_ms), time_lapse));
2254                                 dhd_os_spin_unlock(&dhd->pub, flags);
2255                         }
2256                         dhd_os_sdunlock(&dhd->pub);
2257                 } else {
2258                         break;
2259         }
2260
2261         complete_and_exit(&tsk->completed, 0);
2262 }
2263 #endif /* DHDTHREAD */
2264
2265 static void dhd_watchdog(ulong data)
2266 {
2267         dhd_info_t *dhd = (dhd_info_t *)data;
2268         unsigned long flags;
2269
2270         if (dhd->pub.dongle_reset) {
2271                 return;
2272         }
2273
2274 #ifdef DHDTHREAD
2275         if (dhd->thr_wdt_ctl.thr_pid >= 0) {
2276                 up(&dhd->thr_wdt_ctl.sema);
2277                 return;
2278         }
2279 #endif /* DHDTHREAD */
2280
2281         dhd_os_sdlock(&dhd->pub);
2282         /* Call the bus module watchdog */
2283         dhd_bus_watchdog(&dhd->pub);
2284
2285         flags = dhd_os_spin_lock(&dhd->pub);
2286         /* Count the tick for reference */
2287         dhd->pub.tickcnt++;
2288
2289         /* Reschedule the watchdog */
2290         if (dhd->wd_timer_valid)
2291                 mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms));
2292         dhd_os_spin_unlock(&dhd->pub, flags);
2293         dhd_os_sdunlock(&dhd->pub);
2294 }
2295
2296 #ifdef DHDTHREAD
2297
2298 #ifdef ENABLE_ADAPTIVE_SCHED
2299 static void
2300 dhd_sched_policy(int prio)
2301 {
2302         struct sched_param param;
2303         if (cpufreq_quick_get(0) <= CUSTOM_CPUFREQ_THRESH) {
2304                 param.sched_priority = 0;
2305                 setScheduler(current, SCHED_NORMAL, &param);
2306         } else {
2307                 param.sched_priority = (prio < MAX_RT_PRIO)? prio : (MAX_RT_PRIO-1);
2308                 setScheduler(current, SCHED_FIFO, &param);
2309         }
2310 }
2311 #endif /* ENABLE_ADAPTIVE_SCHED */
2312
2313
2314 static int
2315 dhd_dpc_thread(void *data)
2316 {
2317         tsk_ctl_t *tsk = (tsk_ctl_t *)data;
2318         dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
2319         /* This thread doesn't need any user-level access,
2320          * so get rid of all our resources
2321          */
2322         if (dhd_dpc_prio > 0)
2323         {
2324                 struct sched_param param;
2325                 param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1);
2326                 setScheduler(current, SCHED_FIFO, &param);
2327         }
2328
2329 #ifdef CUSTOM_DPC_CPUCORE
2330                 set_cpus_allowed_ptr(current, cpumask_of(CUSTOM_DPC_CPUCORE));
2331 #endif /* CUSTOM_DPC_CPUCORE */
2332
2333         /* Run until signal received */
2334         while (1) {
2335                 if (!binary_sema_down(tsk)) {
2336 #ifdef ENABLE_ADAPTIVE_SCHED
2337                         dhd_sched_policy(dhd_dpc_prio);
2338 #endif /* ENABLE_ADAPTIVE_SCHED */
2339                         SMP_RD_BARRIER_DEPENDS();
2340                         if (tsk->terminated) {
2341                                 break;
2342                         }
2343
2344                         /* Call bus dpc unless it indicated down (then clean stop) */
2345                         if (dhd->pub.busstate != DHD_BUS_DOWN) {
2346                                 dhd_os_wd_timer_extend(&dhd->pub, TRUE);
2347                                 while (dhd_bus_dpc(dhd->pub.bus)) {
2348                                         /* process all data */
2349                                 }
2350                                 dhd_os_wd_timer_extend(&dhd->pub, FALSE);
2351                                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2352                         } else {
2353                                 if (dhd->pub.up)
2354                                         dhd_bus_stop(dhd->pub.bus, TRUE);
2355                                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2356                         }
2357                 }
2358                 else
2359                         break;
2360         }
2361
2362         complete_and_exit(&tsk->completed, 0);
2363 }
2364
2365 #ifdef RXFRAME_THREAD
2366 static int
2367 dhd_rxf_thread(void *data)
2368 {
2369         tsk_ctl_t *tsk = (tsk_ctl_t *)data;
2370         dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
2371         dhd_pub_t *pub = &dhd->pub;
2372
2373         /* This thread doesn't need any user-level access,
2374          * so get rid of all our resources
2375          */
2376         if (dhd_rxf_prio > 0)
2377         {
2378                 struct sched_param param;
2379                 param.sched_priority = (dhd_rxf_prio < MAX_RT_PRIO)?dhd_rxf_prio:(MAX_RT_PRIO-1);
2380                 setScheduler(current, SCHED_FIFO, &param);
2381         }
2382
2383         DAEMONIZE("dhd_rxf");
2384         /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below  */
2385
2386         /*  signal: thread has started */
2387         complete(&tsk->completed);
2388
2389         /* Run until signal received */
2390         while (1) {
2391                 if (down_interruptible(&tsk->sema) == 0) {
2392                         void *skb;
2393 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
2394                         ulong flags;
2395 #endif
2396 #ifdef ENABLE_ADAPTIVE_SCHED
2397                         dhd_sched_policy(dhd_rxf_prio);
2398 #endif /* ENABLE_ADAPTIVE_SCHED */
2399
2400                         SMP_RD_BARRIER_DEPENDS();
2401
2402                         if (tsk->terminated) {
2403                                 break;
2404                         }
2405                         skb = dhd_rxf_dequeue(pub);
2406
2407                         if (skb == NULL) {
2408                                 continue;
2409                         }
2410                         while (skb) {
2411                                 void *skbnext = PKTNEXT(pub->osh, skb);
2412                                 PKTSETNEXT(pub->osh, skb, NULL);
2413
2414 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
2415                                 netif_rx_ni(skb);
2416 #else
2417                                 netif_rx(skb);
2418                                 local_irq_save(flags);
2419                                 RAISE_RX_SOFTIRQ();
2420                                 local_irq_restore(flags);
2421
2422 #endif
2423                                 skb = skbnext;
2424                         }
2425
2426                         DHD_OS_WAKE_UNLOCK(pub);
2427                 }
2428                 else
2429                         break;
2430         }
2431
2432         complete_and_exit(&tsk->completed, 0);
2433 }
2434 #endif /* RXFRAME_THREAD */
2435 #endif /* DHDTHREAD */
2436
2437 static void
2438 dhd_dpc(ulong data)
2439 {
2440         dhd_info_t *dhd;
2441
2442         dhd = (dhd_info_t *)data;
2443
2444         /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c]
2445          * down below , wake lock is set,
2446          * the tasklet is initialized in dhd_attach()
2447          */
2448         /* Call bus dpc unless it indicated down (then clean stop) */
2449         if (dhd->pub.busstate != DHD_BUS_DOWN) {
2450                 if (dhd_bus_dpc(dhd->pub.bus))
2451                         tasklet_schedule(&dhd->tasklet);
2452                 else
2453                         DHD_OS_WAKE_UNLOCK(&dhd->pub);
2454         } else {
2455                 dhd_bus_stop(dhd->pub.bus, TRUE);
2456                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2457         }
2458 }
2459
2460 void
2461 dhd_sched_dpc(dhd_pub_t *dhdp)
2462 {
2463         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
2464
2465         DHD_OS_WAKE_LOCK(dhdp);
2466 #ifdef DHDTHREAD
2467         if (dhd->thr_dpc_ctl.thr_pid >= 0) {
2468                 /* If the semaphore does not get up,
2469                 * wake unlock should be done here
2470                 */
2471                 if (!binary_sema_up(&dhd->thr_dpc_ctl))
2472                         DHD_OS_WAKE_UNLOCK(dhdp);
2473                 return;
2474         }
2475 #endif /* DHDTHREAD */
2476
2477         if (dhd->dhd_tasklet_create)
2478                 tasklet_schedule(&dhd->tasklet);
2479 }
2480
2481 #if defined(DHDTHREAD) && defined(RXFRAME_THREAD)
2482 static void
2483 dhd_sched_rxf(dhd_pub_t *dhdp, void *skb)
2484 {
2485         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
2486
2487         DHD_OS_WAKE_LOCK(dhdp);
2488
2489         DHD_TRACE(("dhd_sched_rxf: Enter\n"));
2490
2491         do {
2492                 if (dhd_rxf_enqueue(dhdp, skb) == BCME_OK)
2493                         break;
2494         } while (1);
2495         if (dhd->thr_rxf_ctl.thr_pid >= 0) {
2496                 up(&dhd->thr_rxf_ctl.sema);
2497         }
2498         return;
2499 }
2500 #endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */
2501
2502 #ifdef TOE
2503 /* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */
2504 static int
2505 dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol)
2506 {
2507         wl_ioctl_t ioc;
2508         char buf[32];
2509         int ret;
2510
2511         memset(&ioc, 0, sizeof(ioc));
2512
2513         ioc.cmd = WLC_GET_VAR;
2514         ioc.buf = buf;
2515         ioc.len = (uint)sizeof(buf);
2516         ioc.set = FALSE;
2517
2518         strncpy(buf, "toe_ol", sizeof(buf) - 1);
2519         buf[sizeof(buf) - 1] = '\0';
2520         if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
2521                 /* Check for older dongle image that doesn't support toe_ol */
2522                 if (ret == -EIO) {
2523                         DHD_ERROR(("%s: toe not supported by device\n",
2524                                 dhd_ifname(&dhd->pub, ifidx)));
2525                         return -EOPNOTSUPP;
2526                 }
2527
2528                 DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
2529                 return ret;
2530         }
2531
2532         memcpy(toe_ol, buf, sizeof(uint32));
2533         return 0;
2534 }
2535
2536 /* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */
2537 static int
2538 dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol)
2539 {
2540         wl_ioctl_t ioc;
2541         char buf[32];
2542         int toe, ret;
2543
2544         memset(&ioc, 0, sizeof(ioc));
2545
2546         ioc.cmd = WLC_SET_VAR;
2547         ioc.buf = buf;
2548         ioc.len = (uint)sizeof(buf);
2549         ioc.set = TRUE;
2550
2551         /* Set toe_ol as requested */
2552
2553         strncpy(buf, "toe_ol", sizeof(buf) - 1);
2554         buf[sizeof(buf) - 1] = '\0';
2555         memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32));
2556
2557         if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
2558                 DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
2559                         dhd_ifname(&dhd->pub, ifidx), ret));
2560                 return ret;
2561         }
2562
2563         /* Enable toe globally only if any components are enabled. */
2564
2565         toe = (toe_ol != 0);
2566
2567         strcpy(buf, "toe");
2568         memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32));
2569
2570         if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
2571                 DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
2572                 return ret;
2573         }
2574
2575         return 0;
2576 }
2577 #endif /* TOE */
2578
2579 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
2580 static void
2581 dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
2582 {
2583         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
2584
2585         snprintf(info->driver, sizeof(info->driver), "wl");
2586         snprintf(info->version, sizeof(info->version), "%lu", dhd->pub.drv_version);
2587 }
2588
2589 struct ethtool_ops dhd_ethtool_ops = {
2590         .get_drvinfo = dhd_ethtool_get_drvinfo
2591 };
2592 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
2593
2594
2595 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
2596 static int
2597 dhd_ethtool(dhd_info_t *dhd, void *uaddr)
2598 {
2599         struct ethtool_drvinfo info;
2600         char drvname[sizeof(info.driver)];
2601         uint32 cmd;
2602 #ifdef TOE
2603         struct ethtool_value edata;
2604         uint32 toe_cmpnt, csum_dir;
2605         int ret;
2606 #endif
2607
2608         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2609
2610         /* all ethtool calls start with a cmd word */
2611         if (copy_from_user(&cmd, uaddr, sizeof (uint32)))
2612                 return -EFAULT;
2613
2614         switch (cmd) {
2615         case ETHTOOL_GDRVINFO:
2616                 /* Copy out any request driver name */
2617                 if (copy_from_user(&info, uaddr, sizeof(info)))
2618                         return -EFAULT;
2619                 strncpy(drvname, info.driver, sizeof(info.driver));
2620                 drvname[sizeof(info.driver)-1] = '\0';
2621
2622                 /* clear struct for return */
2623                 memset(&info, 0, sizeof(info));
2624                 info.cmd = cmd;
2625
2626                 /* if dhd requested, identify ourselves */
2627                 if (strcmp(drvname, "?dhd") == 0) {
2628                         snprintf(info.driver, sizeof(info.driver), "dhd");
2629                         strncpy(info.version, EPI_VERSION_STR, sizeof(info.version) - 1);
2630                         info.version[sizeof(info.version) - 1] = '\0';
2631                 }
2632
2633                 /* otherwise, require dongle to be up */
2634                 else if (!dhd->pub.up) {
2635                         DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__));
2636                         return -ENODEV;
2637                 }
2638
2639                 /* finally, report dongle driver type */
2640                 else if (dhd->pub.iswl)
2641                         snprintf(info.driver, sizeof(info.driver), "wl");
2642                 else
2643                         snprintf(info.driver, sizeof(info.driver), "xx");
2644
2645                 snprintf(info.version, sizeof(info.version), "%lu", dhd->pub.drv_version);
2646                 if (copy_to_user(uaddr, &info, sizeof(info)))
2647                         return -EFAULT;
2648                 DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__,
2649                          (int)sizeof(drvname), drvname, info.driver));
2650                 break;
2651
2652 #ifdef TOE
2653         /* Get toe offload components from dongle */
2654         case ETHTOOL_GRXCSUM:
2655         case ETHTOOL_GTXCSUM:
2656                 if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
2657                         return ret;
2658
2659                 csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
2660
2661                 edata.cmd = cmd;
2662                 edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
2663
2664                 if (copy_to_user(uaddr, &edata, sizeof(edata)))
2665                         return -EFAULT;
2666                 break;
2667
2668         /* Set toe offload components in dongle */
2669         case ETHTOOL_SRXCSUM:
2670         case ETHTOOL_STXCSUM:
2671                 if (copy_from_user(&edata, uaddr, sizeof(edata)))
2672                         return -EFAULT;
2673
2674                 /* Read the current settings, update and write back */
2675                 if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
2676                         return ret;
2677
2678                 csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
2679
2680                 if (edata.data != 0)
2681                         toe_cmpnt |= csum_dir;
2682                 else
2683                         toe_cmpnt &= ~csum_dir;
2684
2685                 if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0)
2686                         return ret;
2687
2688                 /* If setting TX checksum mode, tell Linux the new mode */
2689                 if (cmd == ETHTOOL_STXCSUM) {
2690                         if (edata.data)
2691                                 dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM;
2692                         else
2693                                 dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM;
2694                 }
2695
2696                 break;
2697 #endif /* TOE */
2698
2699         default:
2700                 return -EOPNOTSUPP;
2701         }
2702
2703         return 0;
2704 }
2705 #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
2706
2707 static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error)
2708 {
2709         dhd_info_t *dhd;
2710
2711         if (!dhdp) {
2712                 DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__));
2713                 return FALSE;
2714         }
2715
2716         if (!dhdp->up)
2717                 return FALSE;
2718
2719         dhd = (dhd_info_t *)dhdp->info;
2720         if (dhd->thr_sysioc_ctl.thr_pid < 0) {
2721                 DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__));
2722                 return FALSE;
2723         }
2724
2725         if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) ||
2726                 ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) {
2727                 DHD_ERROR(("%s: Event HANG send up due to  re=%d te=%d e=%d s=%d\n", __FUNCTION__,
2728                         dhdp->rxcnt_timeout, dhdp->txcnt_timeout, error, dhdp->busstate));
2729                 net_os_send_hang_message(net);
2730                 return TRUE;
2731         }
2732         return FALSE;
2733 }
2734
2735 int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc)
2736 {
2737         int bcmerror = BCME_OK;
2738         int buflen = 0;
2739         void *buf = NULL;
2740         struct net_device *net;
2741
2742         net = dhd_idx2net(pub, ifidx);
2743         if (!net) {
2744                 bcmerror = BCME_BADARG;
2745                 goto done;
2746         }
2747
2748         /* Copy out any buffer passed */
2749         if (ioc->buf) {
2750                 if (ioc->len == 0) {
2751                         DHD_TRACE(("%s: ioc->len=0, returns BCME_BADARG \n", __FUNCTION__));
2752                         bcmerror = BCME_BADARG;
2753                         goto done;
2754                 }
2755                 buflen = MIN(ioc->len, DHD_IOCTL_MAXLEN);
2756                 /* optimization for direct ioctl calls from kernel */
2757                 /*
2758                 if (segment_eq(get_fs(), KERNEL_DS)) {
2759                         buf = ioc->buf;
2760                 } else {
2761                 */
2762                 {
2763                         if (!(buf = MALLOC(pub->osh, buflen + 1))) {
2764                                 bcmerror = BCME_NOMEM;
2765                                 goto done;
2766                         }
2767                         if (copy_from_user(buf, ioc->buf, buflen)) {
2768                                 bcmerror = BCME_BADADDR;
2769                                 goto done;
2770                         }
2771                         *(char *)(buf + buflen) = '\0';
2772                 }
2773         }
2774
2775         /* check for local dhd ioctl and handle it */
2776         if (ioc->driver == DHD_IOCTL_MAGIC) {
2777                 bcmerror = dhd_ioctl((void *)pub, ioc, buf, buflen);
2778                 if (bcmerror)
2779                         pub->bcmerror = bcmerror;
2780                 goto done;
2781         }
2782
2783         /* send to dongle (must be up, and wl). */
2784         if (pub->busstate != DHD_BUS_DATA) {
2785                 bcmerror = BCME_DONGLE_DOWN;
2786                 goto done;
2787         }
2788
2789         if (!pub->iswl) {
2790                 bcmerror = BCME_DONGLE_DOWN;
2791                 goto done;
2792         }
2793
2794         /*
2795          * Flush the TX queue if required for proper message serialization:
2796          * Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to
2797          * prevent M4 encryption and
2798          * intercept WLC_DISASSOC IOCTL - serialize WPS-DONE and WLC_DISASSOC IOCTL to
2799          * prevent disassoc frame being sent before WPS-DONE frame.
2800          */
2801         if (ioc->cmd == WLC_SET_KEY ||
2802             (ioc->cmd == WLC_SET_VAR && ioc->buf != NULL &&
2803              strncmp("wsec_key", ioc->buf, 9) == 0) ||
2804             (ioc->cmd == WLC_SET_VAR && ioc->buf != NULL &&
2805              strncmp("bsscfg:wsec_key", ioc->buf, 15) == 0) ||
2806             ioc->cmd == WLC_DISASSOC)
2807                 dhd_wait_pend8021x(net);
2808
2809 #ifdef WLMEDIA_HTSF
2810         if (ioc->buf) {
2811                 /*  short cut wl ioctl calls here  */
2812                 if (strcmp("htsf", ioc->buf) == 0) {
2813                         dhd_ioctl_htsf_get(dhd, 0);
2814                         return BCME_OK;
2815                 }
2816
2817                 if (strcmp("htsflate", ioc->buf) == 0) {
2818                         if (ioc->set) {
2819                                 memset(ts, 0, sizeof(tstamp_t)*TSMAX);
2820                                 memset(&maxdelayts, 0, sizeof(tstamp_t));
2821                                 maxdelay = 0;
2822                                 tspktcnt = 0;
2823                                 maxdelaypktno = 0;
2824                                 memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN);
2825                                 memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN);
2826                                 memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN);
2827                                 memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN);
2828                         } else {
2829                                 dhd_dump_latency();
2830                         }
2831                         return BCME_OK;
2832                 }
2833                 if (strcmp("htsfclear", ioc->buf) == 0) {
2834                         memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN);
2835                         memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN);
2836                         memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN);
2837                         memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN);
2838                         htsf_seqnum = 0;
2839                         return BCME_OK;
2840                 }
2841                 if (strcmp("htsfhis", ioc->buf) == 0) {
2842                         dhd_dump_htsfhisto(&vi_d1, "H to D");
2843                         dhd_dump_htsfhisto(&vi_d2, "D to D");
2844                         dhd_dump_htsfhisto(&vi_d3, "D to H");
2845                         dhd_dump_htsfhisto(&vi_d4, "H to H");
2846                         return BCME_OK;
2847                 }
2848                 if (strcmp("tsport", ioc->buf) == 0) {
2849                         if (ioc->set) {
2850                                 memcpy(&tsport, ioc->buf + 7, 4);
2851                         } else {
2852                                 DHD_ERROR(("current timestamp port: %d \n", tsport));
2853                         }
2854                         return BCME_OK;
2855                 }
2856         }
2857 #endif /* WLMEDIA_HTSF */
2858
2859         if ((ioc->cmd == WLC_SET_VAR || ioc->cmd == WLC_GET_VAR) &&
2860                 ioc->buf != NULL && strncmp("rpc_", ioc->buf, 4) == 0) {
2861 #ifdef BCM_FD_AGGR
2862                 bcmerror = dhd_fdaggr_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, buf, buflen);
2863 #else
2864                 bcmerror = BCME_UNSUPPORTED;
2865 #endif
2866                 goto done;
2867         }
2868         bcmerror = dhd_wl_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, buf, buflen);
2869
2870 done:
2871         dhd_check_hang(net, pub, bcmerror);
2872
2873         if (!bcmerror && buf && ioc->buf) {
2874                 if (copy_to_user(ioc->buf, buf, buflen))
2875                         bcmerror = -EFAULT;
2876         }
2877
2878         if (buf)
2879                 MFREE(pub->osh, buf, buflen + 1);
2880
2881         return bcmerror;
2882 }
2883
2884 static int
2885 dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd)
2886 {
2887         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
2888         dhd_ioctl_t ioc;
2889         int bcmerror = 0;
2890         int ifidx;
2891         int ret;
2892
2893         DHD_OS_WAKE_LOCK(&dhd->pub);
2894
2895         /* send to dongle only if we are not waiting for reload already */
2896         if (dhd->pub.hang_was_sent) {
2897                 DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__));
2898                 DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS);
2899                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2900                 return OSL_ERROR(BCME_DONGLE_DOWN);
2901         }
2902
2903         ifidx = dhd_net2idx(dhd, net);
2904         DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd));
2905
2906         if (ifidx == DHD_BAD_IF) {
2907                 DHD_ERROR(("%s: BAD IF\n", __FUNCTION__));
2908                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2909                 return -1;
2910         }
2911
2912 #if defined(WL_WIRELESS_EXT)
2913         /* linux wireless extensions */
2914         if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) {
2915                 /* may recurse, do NOT lock */
2916                 ret = wl_iw_ioctl(net, ifr, cmd);
2917                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2918                 return ret;
2919         }
2920 #endif /* defined(WL_WIRELESS_EXT) */
2921
2922 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
2923         if (cmd == SIOCETHTOOL) {
2924                 ret = dhd_ethtool(dhd, (void*)ifr->ifr_data);
2925                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2926                 return ret;
2927         }
2928 #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
2929
2930         if (cmd == SIOCDEVPRIVATE+1) {
2931                 ret = wl_android_priv_cmd(net, ifr, cmd);
2932                 dhd_check_hang(net, &dhd->pub, ret);
2933                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2934                 return ret;
2935         }
2936
2937         if (cmd != SIOCDEVPRIVATE) {
2938                 DHD_OS_WAKE_UNLOCK(&dhd->pub);
2939                 return -EOPNOTSUPP;
2940         }
2941
2942         memset(&ioc, 0, sizeof(ioc));
2943
2944         /* Copy the ioc control structure part of ioctl request */
2945         if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) {
2946                 bcmerror = BCME_BADADDR;
2947                 goto done;
2948         }
2949
2950         /* To differentiate between wl and dhd read 4 more byes */
2951         if ((copy_from_user(&ioc.driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t),
2952                 sizeof(uint)) != 0)) {
2953                 bcmerror = BCME_BADADDR;
2954                 goto done;
2955         }
2956
2957         if (!capable(CAP_NET_ADMIN)) {
2958                 bcmerror = BCME_EPERM;
2959                 goto done;
2960         }
2961
2962         bcmerror = dhd_ioctl_process(&dhd->pub, ifidx, &ioc);
2963
2964 done:
2965         DHD_OS_WAKE_UNLOCK(&dhd->pub);
2966
2967         return OSL_ERROR(bcmerror);
2968 }
2969
2970 #ifdef WL_CFG80211
2971 static int
2972 dhd_cleanup_virt_ifaces(dhd_info_t *dhd)
2973 {
2974         int i = 1; /* Leave ifidx 0 [Primary Interface] */
2975 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2976         int rollback_lock = FALSE;
2977 #endif
2978
2979         DHD_TRACE(("%s: Enter \n", __func__));
2980
2981 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
2982         /* release lock for unregister_netdev */
2983         if (rtnl_is_locked()) {
2984                 rtnl_unlock();
2985                 rollback_lock = TRUE;
2986         }
2987 #endif
2988
2989         for (i = 1; i < DHD_MAX_IFS; i++) {
2990                 dhd_net_if_lock_local(dhd);
2991                 if (dhd->iflist[i]) {
2992                         DHD_TRACE(("Deleting IF: %d \n", i));
2993                         if ((dhd->iflist[i]->state != DHD_IF_DEL) &&
2994                                 (dhd->iflist[i]->state != DHD_IF_DELETING)) {
2995                                 dhd->iflist[i]->state = DHD_IF_DEL;
2996                                 dhd->iflist[i]->idx = i;
2997                                 dhd_op_if(dhd->iflist[i]);
2998                         }
2999                 }
3000                 dhd_net_if_unlock_local(dhd);
3001         }
3002
3003 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3004         if (rollback_lock)
3005                 rtnl_lock();
3006 #endif
3007
3008         return 0;
3009 }
3010 #endif /* WL_CFG80211 */
3011
3012
3013 static int
3014 dhd_stop(struct net_device *net)
3015 {
3016         int ifidx = 0;
3017         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
3018         DHD_OS_WAKE_LOCK(&dhd->pub);
3019         DHD_TRACE(("%s: Enter %p\n", __FUNCTION__, net));
3020
3021
3022         if (dhd->pub.up == 0) {
3023                 goto exit;
3024         }
3025         ifidx = dhd_net2idx(dhd, net);
3026         BCM_REFERENCE(ifidx);
3027
3028         /* Set state and stop OS transmissions */
3029         netif_stop_queue(net);
3030         dhd->pub.up = 0;
3031
3032 #ifdef WL_CFG80211
3033         if (ifidx == 0) {
3034                 wl_cfg80211_down(NULL);
3035
3036                 /*
3037                  * For CFG80211: Clean up all the left over virtual interfaces
3038                  * when the primary Interface is brought down. [ifconfig wlan0 down]
3039                  */
3040                 if (!dhd_download_fw_on_driverload) {
3041                         if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) &&
3042                                 (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
3043                                 dhd_cleanup_virt_ifaces(dhd);
3044                         }
3045                 }
3046         }
3047 #endif
3048
3049 #ifdef PROP_TXSTATUS
3050         dhd_os_wlfc_block(&dhd->pub);
3051         dhd_wlfc_cleanup(&dhd->pub, NULL, 0);
3052         dhd_os_wlfc_unblock(&dhd->pub);
3053 #endif
3054         /* Stop the protocol module */
3055         dhd_prot_stop(&dhd->pub);
3056
3057         OLD_MOD_DEC_USE_COUNT;
3058 exit:
3059 #if defined(WL_CFG80211)
3060         if (ifidx == 0 && !dhd_download_fw_on_driverload)
3061                 wl_android_wifi_off(net);
3062 #endif 
3063         dhd->pub.rxcnt_timeout = 0;
3064         dhd->pub.txcnt_timeout = 0;
3065
3066         dhd->pub.hang_was_sent = 0;
3067
3068         DHD_OS_WAKE_UNLOCK(&dhd->pub);
3069         return 0;
3070 }
3071
3072 /* (USE_INITIAL_2G_SCAN || USE_INITIAL_SHORT_DWELL_TIME) */
3073
3074 static int
3075 dhd_open(struct net_device *net)
3076 {
3077         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
3078 #ifdef TOE
3079         uint32 toe_ol;
3080 #endif
3081         int ifidx;
3082         int32 ret = 0;
3083
3084
3085
3086         DHD_OS_WAKE_LOCK(&dhd->pub);
3087         /* Update FW path if it was changed */
3088         if (strlen(firmware_path) != 0) {
3089                 if (firmware_path[strlen(firmware_path)-1] == '\n')
3090                         firmware_path[strlen(firmware_path)-1] = '\0';
3091                 bzero(fw_path, MOD_PARAM_PATHLEN);
3092                 strncpy(fw_path, firmware_path, sizeof(fw_path)-1);
3093                 firmware_path[0] = '\0';
3094         }
3095
3096
3097
3098
3099         dhd->pub.dongle_trap_occured = 0;
3100         dhd->pub.hang_was_sent = 0;
3101 #if !defined(WL_CFG80211)
3102         /*
3103          * Force start if ifconfig_up gets called before START command
3104          *  We keep WEXT's wl_control_wl_start to provide backward compatibility
3105          *  This should be removed in the future
3106          */
3107         ret = wl_control_wl_start(net);
3108         if (ret != 0) {
3109                 DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret));
3110                 ret = -1;
3111                 goto exit;
3112         }
3113
3114 #endif 
3115
3116         ifidx = dhd_net2idx(dhd, net);
3117         DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
3118
3119         if (ifidx < 0) {
3120                 DHD_ERROR(("%s: Error: called with invalid IF\n", __FUNCTION__));
3121                 ret = -1;
3122                 goto exit;
3123         }
3124
3125         if (!dhd->iflist[ifidx] || dhd->iflist[ifidx]->state == DHD_IF_DEL) {
3126                 DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__));
3127                 ret = -1;
3128                 goto exit;
3129         }
3130
3131         if (ifidx == 0) {
3132                 atomic_set(&dhd->pend_8021x_cnt, 0);
3133 #if defined(WL_CFG80211)
3134                 DHD_ERROR(("\n%s\n", dhd_version));
3135 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3136                 /*
3137                  * if we are here before powering off the chip
3138                  * in init, put a request of not powering off
3139                  */
3140                 down(&dhd_init_sem);
3141                 init_power_off = FALSE;
3142                 up(&dhd_init_sem);
3143 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
3144                 if (!dhd_download_fw_on_driverload) {
3145                         ret = wl_android_wifi_on(net);
3146                         if (ret != 0) {
3147                                 DHD_ERROR(("%s : wl_android_wifi_on failed (%d)\n",
3148                                         __FUNCTION__, ret));
3149                                 ret = -1;
3150                                 goto exit;
3151                         }
3152                 }
3153 #endif 
3154
3155                 if (dhd->pub.busstate != DHD_BUS_DATA) {
3156
3157                         /* try to bring up bus */
3158                         if ((ret = dhd_bus_start(&dhd->pub)) != 0) {
3159                                 DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret));
3160                                 ret = -1;
3161                                 goto exit;
3162                         }
3163
3164                 }
3165
3166                 /* dhd_prot_init has been called in dhd_bus_start or wl_android_wifi_on */
3167                 memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
3168
3169 #ifdef TOE
3170                 /* Get current TOE mode from dongle */
3171                 if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0)
3172                         dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM;
3173                 else
3174                         dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM;
3175 #endif /* TOE */
3176
3177 #if defined(WL_CFG80211)
3178                 if (unlikely(wl_cfg80211_up(NULL))) {
3179                         DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__));
3180                         ret = -1;
3181                         goto exit;
3182                 }
3183 #endif /* WL_CFG80211 */
3184         }
3185
3186         /* Allow transmit calls */
3187         netif_start_queue(net);
3188         dhd->pub.up = 1;
3189
3190 #ifdef BCMDBGFS
3191         dhd_dbg_init(&dhd->pub);
3192 #endif
3193
3194         OLD_MOD_INC_USE_COUNT;
3195 exit:
3196         if (ret)
3197                 dhd_stop(net);
3198
3199         DHD_OS_WAKE_UNLOCK(&dhd->pub);
3200
3201
3202         return ret;
3203 }
3204
3205 int dhd_do_driver_init(struct net_device *net)
3206 {
3207         dhd_info_t *dhd = NULL;
3208
3209         if (!net) {
3210                 DHD_ERROR(("Primary Interface not initialized \n"));
3211                 return -EINVAL;
3212         }
3213
3214
3215         /*  && defined(OEM_ANDROID) && defined(BCMSDIO) */
3216         dhd = *(dhd_info_t **)netdev_priv(net);
3217
3218         /* If driver is already initialized, do nothing
3219          */
3220         if (dhd->pub.busstate == DHD_BUS_DATA) {
3221                 DHD_TRACE(("Driver already Inititalized. Nothing to do"));
3222                 return 0;
3223         }
3224
3225         if (dhd_open(net) < 0) {
3226                 DHD_ERROR(("Driver Init Failed \n"));
3227                 return -1;
3228         }
3229
3230         return 0;
3231 }
3232
3233 osl_t *
3234 dhd_osl_attach(void *pdev, uint bustype)
3235 {
3236         return osl_attach(pdev, bustype, TRUE);
3237 }
3238
3239 void
3240 dhd_osl_detach(osl_t *osh)
3241 {
3242         if (MALLOCED(osh)) {
3243                 DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh)));
3244         }
3245         osl_detach(osh);
3246 #if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3247         dhd_registration_check = FALSE;
3248         up(&dhd_registration_sem);
3249 #if     defined(BCMLXSDMMC)
3250         up(&dhd_chipup_sem);
3251 #endif
3252 #endif 
3253 }
3254
3255 int
3256 dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name,
3257         uint8 *mac_addr, uint32 flags, uint8 bssidx)
3258 {
3259         dhd_if_t *ifp;
3260
3261         DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle));
3262
3263         ASSERT(dhd && (ifidx < DHD_MAX_IFS));
3264
3265         ifp = dhd->iflist[ifidx];
3266         if (ifp != NULL) {
3267                 if (ifp->net != NULL) {
3268                         netif_stop_queue(ifp->net);
3269                         unregister_netdev(ifp->net);
3270                         free_netdev(ifp->net);
3271                 }
3272         } else
3273                 if ((ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t))) == NULL) {
3274                         DHD_ERROR(("%s: OOM - dhd_if_t(%d)\n", __FUNCTION__, sizeof(dhd_if_t)));
3275                         return -ENOMEM;
3276                 }
3277
3278         memset(ifp, 0, sizeof(dhd_if_t));
3279         ifp->event2cfg80211 = FALSE;
3280         ifp->info = dhd;
3281         dhd->iflist[ifidx] = ifp;
3282         strncpy(ifp->name, name, IFNAMSIZ);
3283         ifp->name[IFNAMSIZ] = '\0';
3284         INIT_LIST_HEAD(&ifp->ipv6_list);
3285         spin_lock_init(&ifp->ipv6_lock);
3286         if (mac_addr != NULL)
3287                 memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN);
3288
3289         if (handle == NULL) {
3290                 ifp->state = DHD_IF_ADD;
3291                 ifp->idx = ifidx;
3292                 ifp->bssidx = bssidx;
3293                 ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0);
3294                 up(&dhd->thr_sysioc_ctl.sema);
3295         } else
3296                 ifp->net = (struct net_device *)handle;
3297
3298         if (ifidx == 0) {
3299                 ifp->event2cfg80211 = TRUE;
3300         }
3301
3302         return 0;
3303 }
3304
3305 void
3306 dhd_del_if(dhd_info_t *dhd, int ifidx)
3307 {
3308         dhd_if_t *ifp;
3309
3310         DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx));
3311
3312         ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS));
3313         ifp = dhd->iflist[ifidx];
3314         if (!ifp) {
3315                 DHD_ERROR(("%s: Null interface\n", __FUNCTION__));
3316                 return;
3317         }
3318
3319         ifp->state = DHD_IF_DEL;
3320         ifp->idx = ifidx;
3321         ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0);
3322         up(&dhd->thr_sysioc_ctl.sema);
3323 }
3324
3325 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
3326 static struct net_device_ops dhd_ops_pri = {
3327         .ndo_open = dhd_open,
3328         .ndo_stop = dhd_stop,
3329         .ndo_get_stats = dhd_get_stats,
3330         .ndo_do_ioctl = dhd_ioctl_entry,
3331         .ndo_start_xmit = dhd_start_xmit,
3332         .ndo_set_mac_address = dhd_set_mac_address,
3333 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
3334         .ndo_set_rx_mode = dhd_set_multicast_list,
3335 #else
3336         .ndo_set_multicast_list = dhd_set_multicast_list,
3337 #endif
3338 };
3339
3340 static struct net_device_ops dhd_ops_virt = {
3341         .ndo_get_stats = dhd_get_stats,
3342         .ndo_do_ioctl = dhd_ioctl_entry,
3343         .ndo_start_xmit = dhd_start_xmit,
3344         .ndo_set_mac_address = dhd_set_mac_address,
3345 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
3346         .ndo_set_rx_mode = dhd_set_multicast_list,
3347 #else
3348         .ndo_set_multicast_list = dhd_set_multicast_list,
3349 #endif
3350 };
3351 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */
3352
3353 dhd_pub_t *
3354 dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev)
3355 {
3356         dhd_info_t *dhd = NULL;
3357         struct net_device *net = NULL;
3358
3359         dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT;
3360         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
3361
3362         /* updates firmware nvram path if it was provided as module parameters */
3363         if (strlen(firmware_path) != 0) {
3364                 bzero(fw_path, MOD_PARAM_PATHLEN);
3365                 strncpy(fw_path, firmware_path, sizeof(fw_path) - 1);
3366         }
3367         if (strlen(nvram_path) != 0) {
3368                 bzero(nv_path, MOD_PARAM_PATHLEN);
3369                 strncpy(nv_path, nvram_path, sizeof(nv_path) -1);
3370         }
3371
3372         /* Allocate etherdev, including space for private structure */
3373         if (!(net = alloc_etherdev(sizeof(dhd)))) {
3374                 DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
3375                 goto fail;
3376         }
3377         dhd_state |= DHD_ATTACH_STATE_NET_ALLOC;
3378
3379         SET_NETDEV_DEV(net, (struct device *)dev);
3380         /* Allocate primary dhd_info */
3381 #if defined(CONFIG_DHD_USE_STATIC_BUF)
3382         dhd = (void *)dhd_os_prealloc(osh, DHD_PREALLOC_DHD_INFO, sizeof(dhd_info_t));
3383         if (!dhd) {
3384                 DHD_INFO(("%s: OOM - Pre-alloc dhd_info\n", __FUNCTION__));
3385 #endif /* CONFIG_DHD_USE_STATIC_BUF */
3386         if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) {
3387                 DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__));
3388                 goto fail;
3389         }
3390 #if defined(CONFIG_DHD_USE_STATIC_BUF)
3391         }
3392 #endif /* CONFIG_DHD_USE_STATIC_BUF */
3393         memset(dhd, 0, sizeof(dhd_info_t));
3394
3395 #ifdef DHDTHREAD
3396         dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID;
3397         dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID;
3398 #endif /* DHDTHREAD */
3399         dhd->dhd_tasklet_create = FALSE;
3400         dhd->thr_sysioc_ctl.thr_pid = DHD_PID_KT_INVALID;
3401         dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC;
3402
3403         /*
3404          * Save the dhd_info into the priv
3405          */
3406         memcpy((void *)netdev_priv(net), &dhd, sizeof(dhd));
3407         dhd->pub.osh = osh;
3408
3409         /* Link to info module */
3410         dhd->pub.info = dhd;
3411         /* Link to bus module */
3412         dhd->pub.bus = bus;
3413         dhd->pub.hdrlen = bus_hdrlen;
3414
3415         /* Set network interface name if it was provided as module parameter */
3416         if (iface_name[0]) {
3417                 int len;
3418                 char ch;
3419                 strncpy(net->name, iface_name, IFNAMSIZ);
3420                 net->name[IFNAMSIZ - 1] = 0;
3421                 len = strlen(net->name);
3422                 ch = net->name[len - 1];
3423                 if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2))
3424                         strcat(net->name, "%d");
3425         }
3426
3427         if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF)
3428                 goto fail;
3429         dhd_state |= DHD_ATTACH_STATE_ADD_IF;
3430
3431 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
3432         net->open = NULL;
3433 #else
3434         net->netdev_ops = NULL;
3435 #endif
3436
3437         sema_init(&dhd->proto_sem, 1);
3438
3439 #ifdef PROP_TXSTATUS
3440         spin_lock_init(&dhd->wlfc_spinlock);
3441 #ifdef PROP_TXSTATUS_VSDB
3442         dhd->pub.wlfc_enabled = FALSE;
3443 #else
3444         if (!disable_proptx)
3445                 dhd->pub.wlfc_enabled = TRUE;
3446         else
3447                 dhd->pub.wlfc_enabled = FALSE;
3448 #endif /* PROP_TXSTATUS_VSDB */
3449         dhd->pub.ptx_opt_enabled = FALSE;
3450         dhd->pub.skip_fc = dhd_wlfc_skip_fc;
3451         dhd->pub.plat_enable = dhd_wlfc_plat_enable;
3452         dhd->pub.plat_deinit = dhd_wlfc_plat_deinit;
3453 #endif /* PROP_TXSTATUS */
3454
3455         /* Initialize other structure content */
3456         init_waitqueue_head(&dhd->ioctl_resp_wait);
3457         init_waitqueue_head(&dhd->ctrl_wait);
3458
3459         /* Initialize the spinlocks */
3460         spin_lock_init(&dhd->sdlock);
3461         spin_lock_init(&dhd->txqlock);
3462         spin_lock_init(&dhd->dhd_lock);
3463 #if defined(DHDTHREAD) && defined(RXFRAME_THREAD)
3464         spin_lock_init(&dhd->rxf_lock);
3465 #endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */
3466 #ifdef DHDTCPACK_SUPPRESS
3467         spin_lock_init(&dhd->tcpack_lock);
3468 #endif /* DHDTCPACK_SUPPRESS */
3469
3470         /* Initialize Wakelock stuff */
3471         spin_lock_init(&dhd->wakelock_spinlock);
3472         dhd->wakelock_counter = 0;
3473         dhd->wakelock_wd_counter = 0;
3474         dhd->wakelock_rx_timeout_enable = 0;
3475         dhd->wakelock_ctrl_timeout_enable = 0;
3476 #ifdef CONFIG_HAS_WAKELOCK
3477         wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake");
3478         wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake");
3479         wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake");
3480         wake_lock_init(&dhd->wl_wdwake, WAKE_LOCK_SUSPEND, "wlan_wd_wake");
3481 #endif /* CONFIG_HAS_WAKELOCK */
3482 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
3483         mutex_init(&dhd->dhd_net_if_mutex);
3484         mutex_init(&dhd->dhd_suspend_mutex);
3485 #endif
3486         dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT;
3487
3488         /* Attach and link in the protocol */
3489         if (dhd_prot_attach(&dhd->pub) != 0) {
3490                 DHD_ERROR(("dhd_prot_attach failed\n"));
3491                 goto fail;
3492         }
3493         dhd_state |= DHD_ATTACH_STATE_PROT_ATTACH;
3494
3495 #ifdef WL_CFG80211
3496         /* Attach and link in the cfg80211 */
3497         if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) {
3498                 DHD_ERROR(("wl_cfg80211_attach failed\n"));
3499                 goto fail;
3500         }
3501
3502         dhd_monitor_init(&dhd->pub);
3503         dhd_state |= DHD_ATTACH_STATE_CFG80211;
3504 #endif
3505 #if defined(WL_WIRELESS_EXT)
3506         /* Attach and link in the iw */
3507         if (!(dhd_state &  DHD_ATTACH_STATE_CFG80211)) {
3508                 if (wl_iw_attach(net, (void *)&dhd->pub) != 0) {
3509                 DHD_ERROR(("wl_iw_attach failed\n"));
3510                 goto fail;
3511         }
3512         dhd_state |= DHD_ATTACH_STATE_WL_ATTACH;
3513         }
3514 #endif /* defined(WL_WIRELESS_EXT) */
3515
3516
3517         /* Set up the watchdog timer */
3518         init_timer(&dhd->timer);
3519         dhd->timer.data = (ulong)dhd;
3520         dhd->timer.function = dhd_watchdog;
3521         dhd->default_wd_interval = dhd_watchdog_ms;
3522
3523 #ifdef DHDTHREAD
3524         /* Initialize thread based operation and lock */
3525         sema_init(&dhd->sdsem, 1);
3526         if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) {
3527                 dhd->threads_only = TRUE;
3528         }
3529         else {
3530                 dhd->threads_only = FALSE;
3531         }
3532
3533         if (dhd_watchdog_prio >= 0) {
3534                 /* Initialize watchdog thread */
3535                 PROC_START(dhd_watchdog_thread, dhd, &dhd->thr_wdt_ctl, 0, "dhd_watchdog_thread");
3536
3537         } else {
3538                 dhd->thr_wdt_ctl.thr_pid = -1;
3539         }
3540
3541         /* Set up the bottom half handler */
3542         if (dhd_dpc_prio >= 0) {
3543                 /* Initialize DPC thread */
3544                 PROC_START(dhd_dpc_thread, dhd, &dhd->thr_dpc_ctl, 0, "dhd_dpc");
3545         } else {
3546                 /*  use tasklet for dpc */
3547                 tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd);
3548                 dhd->thr_dpc_ctl.thr_pid = -1;
3549         }
3550 #ifdef RXFRAME_THREAD
3551         bzero(&dhd->pub.skbbuf[0], sizeof(void *) * MAXSKBPEND);
3552         /* Initialize RXF thread */
3553         PROC_START(dhd_rxf_thread, dhd, &dhd->thr_rxf_ctl, 0, "dhd_rxf");
3554 #endif
3555 #else
3556         /* Set up the bottom half handler */
3557         tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd);
3558         dhd->dhd_tasklet_create = TRUE;
3559 #endif /* DHDTHREAD */
3560
3561         if (dhd_sysioc) {
3562                 PROC_START(_dhd_sysioc_thread, dhd, &dhd->thr_sysioc_ctl, 0, "dhd_sysioc");
3563         } else {
3564                 dhd->thr_sysioc_ctl.thr_pid = -1;
3565         }
3566         dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED;
3567
3568 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1)
3569         INIT_WORK(&dhd->work_hang, dhd_hang_process);
3570 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))  */
3571
3572         /*
3573          * Save the dhd_info into the priv
3574          */
3575         memcpy(netdev_priv(net), &dhd, sizeof(dhd));
3576
3577 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \
3578         KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM_SLEEP)
3579         register_pm_notifier(&dhd_sleep_pm_notifier);
3580 #endif /* (LINUX_VERSION >= 2.6.27 && LINUX_VERSION <= 2.6.39 && CONFIG_PM_SLEEP */
3581
3582 #if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND)
3583         dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20;
3584         dhd->early_suspend.suspend = dhd_early_suspend;
3585         dhd->early_suspend.resume = dhd_late_resume;
3586         register_early_suspend(&dhd->early_suspend);
3587         dhd_state |= DHD_ATTACH_STATE_EARLYSUSPEND_DONE;
3588 #endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */
3589
3590 #ifdef ARP_OFFLOAD_SUPPORT
3591         dhd->pend_ipaddr = 0;
3592         register_inetaddr_notifier(&dhd_notifier);
3593 #endif /* ARP_OFFLOAD_SUPPORT */
3594         register_inet6addr_notifier(&dhd_notifier_ipv6);
3595
3596 #ifdef DHDTCPACK_SUPPRESS
3597         dhd->pub.tcp_ack_info_cnt = 0;
3598         bzero(dhd->pub.tcp_ack_info_tbl, sizeof(struct tcp_ack_info)*MAXTCPSTREAMS);
3599 #endif /* DHDTCPACK_SUPPRESS */
3600
3601         dhd_state |= DHD_ATTACH_STATE_DONE;
3602         dhd->dhd_state = dhd_state;
3603         return &dhd->pub;
3604
3605 fail:
3606         if (dhd_state < DHD_ATTACH_STATE_DHD_ALLOC) {
3607                 if (net) free_netdev(net);
3608         } else {
3609                 DHD_TRACE(("%s: Calling dhd_detach dhd_state 0x%x &dhd->pub %p\n",
3610                         __FUNCTION__, dhd_state, &dhd->pub));
3611                 dhd->dhd_state = dhd_state;
3612                 dhd_detach(&dhd->pub);
3613                 dhd_free(&dhd->pub);
3614         }
3615
3616         return NULL;
3617 }
3618
3619 int
3620 dhd_bus_start(dhd_pub_t *dhdp)
3621 {
3622         int ret = -1;
3623         dhd_info_t *dhd = (dhd_info_t*)dhdp->info;
3624         unsigned long flags;
3625
3626         ASSERT(dhd);
3627
3628         DHD_TRACE(("Enter %s:\n", __FUNCTION__));
3629
3630 #ifdef DHDTHREAD
3631         if (dhd->threads_only)
3632                 dhd_os_sdlock(dhdp);
3633 #endif /* DHDTHREAD */
3634
3635
3636         /* try to download image and nvram to the dongle */
3637         if  ((dhd->pub.busstate == DHD_BUS_DOWN) &&
3638                 (fw_path[0] != '\0') && (nv_path[0] != '\0')) {
3639 #ifdef SHOW_NVRAM_TYPE
3640                 {       /* Show nvram type in the kernel log */
3641                         int i;
3642                         for (i = 0; nv_path[i] != '\0'; ++i) {
3643                                 if (nv_path[i] == '.') {
3644                                         ++i;
3645                                         break;
3646                                 }
3647                         }
3648                         DHD_ERROR(("%s: nvram_type = [%s]\n", __FUNCTION__, &nv_path[i]));
3649                 }
3650 #endif /* SHOW_NVRAM_TYPE */
3651                 /* wake lock moved to dhdsdio_download_firmware */
3652                 if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh,
3653                         fw_path, nv_path))) {
3654                         DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n",
3655                                 __FUNCTION__, fw_path, nv_path));
3656 #ifdef DHDTHREAD
3657                         if (dhd->threads_only)
3658                                 dhd_os_sdunlock(dhdp);
3659 #endif /* DHDTHREAD */
3660                         return -1;
3661                 }
3662         }
3663         if (dhd->pub.busstate != DHD_BUS_LOAD) {
3664 #ifdef DHDTHREAD
3665                 if (dhd->threads_only)
3666                         dhd_os_sdunlock(dhdp);
3667 #endif /* DHDTHREAD */
3668                 return -ENETDOWN;
3669         }
3670
3671         /* Start the watchdog timer */
3672         dhd->pub.tickcnt = 0;
3673         dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms);
3674
3675         /* Bring up the bus */
3676         if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) {
3677
3678                 DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret));
3679 #ifdef DHDTHREAD
3680                 if (dhd->threads_only)
3681                         dhd_os_sdunlock(dhdp);
3682 #endif /* DHDTHREAD */
3683                 return ret;
3684         }
3685 #if defined(OOB_INTR_ONLY)
3686         /* Host registration for OOB interrupt */
3687         if (bcmsdh_register_oob_intr(dhdp)) {
3688                 /* deactivate timer and wait for the handler to finish */
3689
3690                 flags = dhd_os_spin_lock(&dhd->pub);
3691                 dhd->wd_timer_valid = FALSE;
3692                 dhd_os_spin_unlock(&dhd->pub, flags);
3693                 del_timer_sync(&dhd->timer);
3694
3695                 DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__));
3696 #ifdef DHDTHREAD
3697                 if (dhd->threads_only)
3698                         dhd_os_sdunlock(dhdp);
3699 #endif /* DHDTHREAD */
3700                 DHD_OS_WD_WAKE_UNLOCK(&dhd->pub);
3701                 return -ENODEV;
3702         }
3703
3704         /* Enable oob at firmware */
3705         dhd_enable_oob_intr(dhd->pub.bus, TRUE);
3706 #endif 
3707
3708         /* If bus is not ready, can't come up */
3709         if (dhd->pub.busstate != DHD_BUS_DATA) {
3710                 flags = dhd_os_spin_lock(&dhd->pub);
3711                 dhd->wd_timer_valid = FALSE;
3712                 dhd_os_spin_unlock(&dhd->pub, flags);
3713                 del_timer_sync(&dhd->timer);
3714                 DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__));
3715 #ifdef DHDTHREAD
3716                 if (dhd->threads_only)
3717                         dhd_os_sdunlock(dhdp);
3718 #endif /* DHDTHREAD */
3719                 DHD_OS_WD_WAKE_UNLOCK(&dhd->pub);
3720                 return -ENODEV;
3721         }
3722
3723 #ifdef DHDTHREAD
3724         if (dhd->threads_only)
3725                 dhd_os_sdunlock(dhdp);
3726 #endif /* DHDTHREAD */
3727
3728         dhd_process_cid_mac(dhdp, TRUE);
3729
3730         /* Bus is ready, do any protocol initialization */
3731         if ((ret = dhd_prot_init(&dhd->pub)) < 0)
3732                 return ret;
3733
3734         dhd_process_cid_mac(dhdp, FALSE);
3735
3736 #ifdef ARP_OFFLOAD_SUPPORT
3737         if (dhd->pend_ipaddr) {
3738 #ifdef AOE_IP_ALIAS_SUPPORT
3739                 aoe_update_host_ipv4_table(&dhd->pub, dhd->pend_ipaddr, TRUE, 0);
3740 #endif /* AOE_IP_ALIAS_SUPPORT */
3741                 dhd->pend_ipaddr = 0;
3742         }
3743 #endif /* ARP_OFFLOAD_SUPPORT */
3744
3745         return 0;
3746 }
3747 #ifdef WLTDLS
3748 int dhd_tdls_enable_disable(dhd_pub_t *dhd, bool flag)
3749 {
3750         char iovbuf[WLC_IOCTL_SMLEN];
3751         uint32 tdls = flag;
3752         int ret;
3753 #ifdef WLTDLS_AUTO_ENABLE
3754         uint32 tdls_auto_op = 1;
3755         uint32 tdls_idle_time = CUSTOM_TDLS_IDLE_MODE_SETTING;
3756 #endif /* WLTDLS_AUTO_ENABLE */
3757         if (!FW_SUPPORTED(dhd, tdls))
3758                 return BCME_ERROR;
3759
3760         bcm_mkiovar("tdls_enable", (char *)&tdls, sizeof(tdls), iovbuf, sizeof(iovbuf));
3761         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
3762                 DHD_ERROR(("%s: tdls %d failed %d\n", __FUNCTION__, tdls, ret));
3763                 goto exit;
3764         }
3765         dhd->tdls_enable = flag;
3766         if (!flag)
3767                 goto exit;
3768 #ifdef WLTDLS_AUTO_ENABLE
3769         bcm_mkiovar("tdls_auto_op", (char *)&tdls_auto_op, sizeof(tdls_auto_op),
3770                 iovbuf, sizeof(iovbuf));
3771         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
3772                 DHD_ERROR(("%s: tdls_auto_op failed %d\n", __FUNCTION__, ret));
3773                 goto exit;
3774         }
3775         bcm_mkiovar("tdls_idle_time", (char *)&tdls_idle_time, sizeof(tdls_idle_time),
3776                 iovbuf, sizeof(iovbuf));
3777         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
3778                 DHD_ERROR(("%s: tdls_idle_time failed %d\n", __FUNCTION__, ret));
3779                 goto exit;
3780         }
3781 #endif /* WLTDLS_AUTO_ENABLE */
3782 exit:
3783         return ret;
3784 }
3785 #endif /* WLTDLS */
3786
3787 bool dhd_is_concurrent_mode(dhd_pub_t *dhd)
3788 {
3789         if (!dhd)
3790                 return FALSE;
3791
3792         if (dhd->op_mode & DHD_FLAG_CONCURR_MULTI_CHAN_MODE)
3793                 return TRUE;
3794         else if ((dhd->op_mode & DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) ==
3795                 DHD_FLAG_CONCURR_SINGLE_CHAN_MODE)
3796                 return TRUE;
3797         else
3798                 return FALSE;
3799 }
3800 #if !defined(AP) && defined(WLP2P)
3801 /* From Android JerryBean release, the concurrent mode is enabled by default and the firmware
3802  * name would be fw_bcmdhd.bin. So we need to determine whether P2P is enabled in the STA
3803  * firmware and accordingly enable concurrent mode (Apply P2P settings). SoftAP firmware
3804  * would still be named as fw_bcmdhd_apsta.
3805  */
3806 uint32
3807 dhd_get_concurrent_capabilites(dhd_pub_t *dhd)
3808 {
3809         int32 ret = 0;
3810         char buf[WLC_IOCTL_SMLEN];
3811         bool mchan_supported = FALSE;
3812         /* if dhd->op_mode is already set for HOSTAP,
3813           * that means we only will use the mode as it is
3814           */
3815         if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE)
3816                 return 0;
3817         if (FW_SUPPORTED(dhd, vsdb)) {
3818                 mchan_supported = TRUE;
3819         }
3820         if (!FW_SUPPORTED(dhd, p2p)) {
3821                 DHD_TRACE(("Chip does not support p2p\n"));
3822                 return 0;
3823         }
3824         else {
3825                 /* Chip supports p2p but ensure that p2p is really implemented in firmware or not */
3826                 memset(buf, 0, sizeof(buf));
3827                 bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf));
3828                 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf),
3829                         FALSE, 0)) < 0) {
3830                         DHD_ERROR(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret));
3831                         return 0;
3832                 }
3833                 else {
3834                         if (buf[0] == 1) {
3835                                 /* By default, chip supports single chan concurrency,
3836                                 * now lets check for mchan
3837                                 */
3838                                 ret = DHD_FLAG_CONCURR_SINGLE_CHAN_MODE;
3839                                 if (mchan_supported)
3840                                         ret |= DHD_FLAG_CONCURR_MULTI_CHAN_MODE;
3841 #if defined(WL_ENABLE_P2P_IF) || defined(WL_CFG80211_P2P_DEV_IF)
3842                                 /* For customer_hw4, although ICS,
3843                                 * we still support concurrent mode
3844                                 */
3845                                 return ret;
3846 #else
3847                                 return 0;
3848 #endif 
3849                         }
3850                 }
3851         }
3852         return 0;
3853 }
3854 #endif 
3855 int
3856 dhd_preinit_ioctls(dhd_pub_t *dhd)
3857 {
3858         int ret = 0;
3859         char eventmask[WL_EVENTING_MASK_LEN];
3860         char iovbuf[WL_EVENTING_MASK_LEN + 12]; /*  Room for "event_msgs" + '\0' + bitvec  */
3861         uint32 buf_key_b4_m4 = 1;
3862 #if defined(CUSTOM_AMPDU_BA_WSIZE)
3863         uint32 ampdu_ba_wsize = 0;
3864 #endif 
3865 #ifdef DHD_ENABLE_LPC
3866         uint32 lpc = 1;
3867 #endif /* DHD_ENABLE_LPC */
3868         uint power_mode = PM_FAST;
3869         uint32 dongle_align = DHD_SDALIGN;
3870         uint32 glom = CUSTOM_GLOM_SETTING;
3871         uint bcn_timeout = 4;
3872         uint retry_max = 3;
3873 #if defined(ARP_OFFLOAD_SUPPORT)
3874         int arpoe = 1;
3875 #endif
3876         int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME;
3877         int scan_unassoc_time = DHD_SCAN_UNASSOC_ACTIVE_TIME;
3878         int scan_passive_time = DHD_SCAN_PASSIVE_TIME;
3879         char buf[WLC_IOCTL_SMLEN];
3880         char *ptr;
3881         uint32 listen_interval = CUSTOM_LISTEN_INTERVAL; /* Default Listen Interval in Beacons */
3882 #ifdef ROAM_ENABLE
3883         uint roamvar = 0;
3884         int roam_trigger[2] = {CUSTOM_ROAM_TRIGGER_SETTING, WLC_BAND_ALL};
3885         int roam_scan_period[2] = {10, WLC_BAND_ALL};
3886         int roam_delta[2] = {CUSTOM_ROAM_DELTA_SETTING, WLC_BAND_ALL};
3887 #ifdef FULL_ROAMING_SCAN_PERIOD_60_SEC
3888         int roam_fullscan_period = 60;
3889 #else /* FULL_ROAMING_SCAN_PERIOD_60_SEC */
3890         int roam_fullscan_period = 120;
3891 #endif /* FULL_ROAMING_SCAN_PERIOD_60_SEC */
3892 #else
3893 #ifdef DISABLE_BUILTIN_ROAM
3894         uint roamvar = 1;
3895 #endif /* DISABLE_BUILTIN_ROAM */
3896 #endif /* ROAM_ENABLE */
3897
3898 #if defined(SOFTAP)
3899         uint dtim = 1;
3900 #endif
3901 #if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211))
3902         uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */
3903         struct ether_addr p2p_ea;
3904 #endif
3905
3906 #if defined(AP) || defined(WLP2P)
3907         uint32 apsta = 1; /* Enable APSTA mode */
3908 #endif /* defined(AP) || defined(WLP2P) */
3909 #ifdef GET_CUSTOM_MAC_ENABLE
3910         struct ether_addr ea_addr;
3911 #endif /* GET_CUSTOM_MAC_ENABLE */
3912
3913 #ifdef DISABLE_11N
3914         uint32 nmode = 0;
3915 #endif /* DISABLE_11N */
3916 #ifdef USE_WL_TXBF
3917         uint32 txbf = 1;
3918 #endif /* USE_WL_TXBF */
3919 #ifdef USE_WL_FRAMEBURST
3920         uint32 frameburst = 1;
3921 #endif /* USE_WL_FRAMEBURST */
3922 #ifdef DHD_SET_FW_HIGHSPEED
3923         uint32 ack_ratio = 250;
3924         uint32 ack_ratio_depth = 64;
3925 #endif /* DHD_SET_FW_HIGHSPEED */
3926 #ifdef SUPPORT_2G_VHT
3927         uint32 vht_features = 0x3; /* 2G enable | rates all */
3928 #endif /* SUPPORT_2G_VHT */
3929 #ifdef PROP_TXSTATUS
3930 #ifdef PROP_TXSTATUS_VSDB
3931         /* In case the host does not support proptxstatus, hostreorder in dongle should be off */
3932         uint32 hostreorder = 0;
3933         dhd->wlfc_enabled = FALSE;
3934         /* enable WLFC only if the firmware is VSDB */
3935 #else
3936         if (!disable_proptx)
3937                 dhd->wlfc_enabled = TRUE;
3938         else
3939                 dhd->wlfc_enabled = FALSE;
3940 #endif /* PROP_TXSTATUS_VSDB */
3941 #endif /* PROP_TXSTATUS */
3942 #ifdef PKT_FILTER_SUPPORT
3943         dhd_pkt_filter_enable = TRUE;
3944 #endif
3945         dhd->suspend_bcn_li_dtim = CUSTOM_SUSPEND_BCN_LI_DTIM;
3946         DHD_TRACE(("Enter %s\n", __FUNCTION__));
3947         dhd->op_mode = 0;
3948 #ifdef GET_CUSTOM_MAC_ENABLE
3949         ret = dhd_custom_get_mac_address(ea_addr.octet);
3950         if (!ret) {
3951                 memset(buf, 0, sizeof(buf));
3952                 bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf));
3953                 ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0);
3954                 if (ret < 0) {
3955                         DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret));
3956                         return BCME_NOTUP;
3957                 }
3958                 memcpy(dhd->mac.octet, ea_addr.octet, ETHER_ADDR_LEN);
3959         } else {
3960 #endif /* GET_CUSTOM_MAC_ENABLE */
3961                 /* Get the default device MAC address directly from firmware */
3962                 memset(buf, 0, sizeof(buf));
3963                 bcm_mkiovar("cur_etheraddr", 0, 0, buf, sizeof(buf));
3964                 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf),
3965                         FALSE, 0)) < 0) {
3966                         DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret));
3967                         return BCME_NOTUP;
3968                 }
3969                 /* Update public MAC address after reading from Firmware */
3970                 memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
3971
3972 #ifdef GET_CUSTOM_MAC_ENABLE
3973         }
3974 #endif /* GET_CUSTOM_MAC_ENABLE */
3975
3976         DHD_TRACE(("Firmware = %s\n", fw_path));
3977         /* get a capabilities from firmware */
3978         memset(dhd->fw_capabilities, 0, sizeof(dhd->fw_capabilities));
3979         bcm_mkiovar("cap", 0, 0, dhd->fw_capabilities, sizeof(dhd->fw_capabilities));
3980         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, dhd->fw_capabilities,
3981                 sizeof(dhd->fw_capabilities), FALSE, 0)) < 0) {
3982                 DHD_ERROR(("%s: Get Capability failed (error=%d)\n",
3983                         __FUNCTION__, ret));
3984                 return 0;
3985         }
3986         if ((!op_mode && strstr(fw_path, "_apsta") != NULL) ||
3987                 (op_mode == DHD_FLAG_HOSTAP_MODE)) {
3988 #ifdef SET_RANDOM_MAC_SOFTAP
3989                 uint rand_mac;
3990 #endif
3991                 dhd->op_mode = DHD_FLAG_HOSTAP_MODE;
3992 #if defined(ARP_OFFLOAD_SUPPORT)
3993                         arpoe = 0;
3994 #endif
3995 #ifdef PKT_FILTER_SUPPORT
3996                         dhd_pkt_filter_enable = FALSE;
3997 #endif
3998 #ifdef SET_RANDOM_MAC_SOFTAP
3999                 SRANDOM32((uint)jiffies);
4000                 rand_mac = RANDOM32();
4001                 iovbuf[0] = 0x02;                          /* locally administered bit */
4002                 iovbuf[1] = 0x1A;
4003                 iovbuf[2] = 0x11;
4004                 iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0;
4005                 iovbuf[4] = (unsigned char)(rand_mac >> 8);
4006                 iovbuf[5] = (unsigned char)(rand_mac >> 16);
4007
4008                 bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf));
4009                 ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0);
4010                 if (ret < 0) {
4011                         DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret));
4012                 } else
4013                         memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN);
4014 #endif /* SET_RANDOM_MAC_SOFTAP */
4015 #if !defined(AP) && defined(WL_CFG80211)
4016                 /* Turn off MPC in AP mode */
4017                 bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf));
4018                 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf,
4019                         sizeof(iovbuf), TRUE, 0)) < 0) {
4020                         DHD_ERROR(("%s mpc for HostAPD failed  %d\n", __FUNCTION__, ret));
4021                 }
4022 #endif
4023         }
4024         else {
4025                 uint32 concurrent_mode = 0;
4026                 if ((!op_mode && strstr(fw_path, "_p2p") != NULL) ||
4027                         (op_mode == DHD_FLAG_P2P_MODE)) {
4028 #if defined(ARP_OFFLOAD_SUPPORT)
4029                         arpoe = 0;
4030 #endif
4031 #ifdef PKT_FILTER_SUPPORT
4032                         dhd_pkt_filter_enable = FALSE;
4033 #endif
4034                         dhd->op_mode = DHD_FLAG_P2P_MODE;
4035                 } else if (op_mode == DHD_FLAG_IBSS_MODE ||
4036                         (!op_mode && strstr(fw_path, "_ibss") != NULL)) {
4037                         dhd->op_mode = DHD_FLAG_IBSS_MODE;
4038 #ifdef PROP_TXSTATUS_VSDB
4039                         if (!disable_proptx) {
4040                                 hostreorder = 1;
4041                                 dhd->wlfc_enabled = TRUE;
4042                         }
4043 #endif /* PROP_TXSTATUS_VSDB */
4044                 } else {
4045                         dhd->op_mode = DHD_FLAG_STA_MODE;
4046                 }
4047 #if !defined(AP) && defined(WLP2P)
4048                 if (dhd->op_mode != DHD_FLAG_IBSS_MODE &&
4049                         (concurrent_mode = dhd_get_concurrent_capabilites(dhd))) {
4050 #if defined(ARP_OFFLOAD_SUPPORT)
4051                         arpoe = 1;
4052 #endif
4053                         dhd->op_mode |= concurrent_mode;
4054                 }
4055
4056                 /* Check if we are enabling p2p */
4057                 if (dhd->op_mode & DHD_FLAG_P2P_MODE) {
4058                         bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf));
4059                         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR,
4060                                 iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
4061                                 DHD_ERROR(("%s APSTA for P2P failed ret= %d\n", __FUNCTION__, ret));
4062                         }
4063
4064                         memcpy(&p2p_ea, &dhd->mac, ETHER_ADDR_LEN);
4065                         ETHER_SET_LOCALADDR(&p2p_ea);
4066                         bcm_mkiovar("p2p_da_override", (char *)&p2p_ea,
4067                                 ETHER_ADDR_LEN, iovbuf, sizeof(iovbuf));
4068                         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR,
4069                                 iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
4070                                 DHD_ERROR(("%s p2p_da_override ret= %d\n", __FUNCTION__, ret));
4071                         } else {
4072                                 DHD_INFO(("dhd_preinit_ioctls: p2p_da_override succeeded\n"));
4073                         }
4074                 }
4075 #else
4076         (void)concurrent_mode;
4077 #endif 
4078         }
4079
4080         DHD_ERROR(("Firmware up: op_mode=0x%04x, "
4081                 "Broadcom Dongle Host Driver mac="MACDBG"\n",
4082                 dhd->op_mode,
4083                 MAC2STRDBG(dhd->mac.octet)));
4084         /* Set Country code  */
4085         if (dhd->dhd_cspec.ccode[0] != 0) {
4086                 bcm_mkiovar("country", (char *)&dhd->dhd_cspec,
4087                         sizeof(wl_country_t), iovbuf, sizeof(iovbuf));
4088                 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0)
4089                         DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__));
4090         }
4091
4092         /* Set Listen Interval */
4093         bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf));
4094         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0)
4095                 DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret));
4096
4097 #if defined(ROAM_ENABLE) || defined(DISABLE_BUILTIN_ROAM)
4098         /* Disable built-in roaming to allowed ext supplicant to take care of roaming */
4099         bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf));
4100         dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
4101 #endif /* ROAM_ENABLE || DISABLE_BUILTIN_ROAM */
4102 #if defined(ROAM_ENABLE)
4103         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, roam_trigger,
4104                 sizeof(roam_trigger), TRUE, 0)) < 0)
4105                 DHD_ERROR(("%s: roam trigger set failed %d\n", __FUNCTION__, ret));
4106         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, roam_scan_period,
4107                 sizeof(roam_scan_period), TRUE, 0)) < 0)
4108                 DHD_ERROR(("%s: roam scan period set failed %d\n", __FUNCTION__, ret));
4109         if ((dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, roam_delta,
4110                 sizeof(roam_delta), TRUE, 0)) < 0)
4111                 DHD_ERROR(("%s: roam delta set failed %d\n", __FUNCTION__, ret));
4112         bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, 4, iovbuf, sizeof(iovbuf));
4113         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0)
4114                 DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret));
4115 #endif /* ROAM_ENABLE */
4116
4117 #ifdef WLTDLS
4118         dhd_tdls_enable_disable(dhd, 1);
4119 #endif /* WLTDLS */
4120
4121 #ifdef DHD_ENABLE_LPC
4122         /* Set lpc 1 */
4123         bcm_mkiovar("lpc", (char *)&lpc, 4, iovbuf, sizeof(iovbuf));
4124         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf,
4125                 sizeof(iovbuf), TRUE, 0)) < 0) {
4126                 DHD_ERROR(("%s Set lpc failed  %d\n", __FUNCTION__, ret));
4127         }
4128 #endif /* DHD_ENABLE_LPC */
4129
4130         /* Set PowerSave mode */
4131         dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0);
4132
4133         /* Match Host and Dongle rx alignment */
4134         bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf));
4135         dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
4136
4137         if (glom != DEFAULT_GLOM_VALUE) {
4138                 DHD_INFO(("%s set glom=0x%X\n", __FUNCTION__, glom));
4139                 bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf));
4140                 dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
4141         }
4142
4143         /* Setup timeout if Beacons are lost and roam is off to report link down */
4144         bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf));
4145         dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
4146         /* Setup assoc_retry_max count to reconnect target AP in dongle */
4147         bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf));
4148         dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
4149 #if defined(AP) && !defined(WLP2P)
4150         /* Turn off MPC in AP mode */
4151         bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf));
4152         dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
4153         bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf));
4154         dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
4155 #endif /* defined(AP) && !defined(WLP2P) */
4156
4157
4158 #if defined(SOFTAP)
4159         if (ap_fw_loaded == TRUE) {
4160                 dhd_wl_ioctl_cmd(dhd, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim), TRUE, 0);
4161         }
4162 #endif 
4163
4164 #if defined(KEEP_ALIVE)
4165         {
4166         /* Set Keep Alive : be sure to use FW with -keepalive */
4167         int res;
4168
4169 #if defined(SOFTAP)
4170         if (ap_fw_loaded == FALSE)
4171 #endif 
4172                 if (!(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) {
4173                         if ((res = dhd_keep_alive_onoff(dhd)) < 0)
4174                                 DHD_ERROR(("%s set keeplive failed %d\n",
4175                                 __FUNCTION__, res));
4176                 }
4177         }
4178 #endif /* defined(KEEP_ALIVE) */
4179 #ifdef USE_WL_TXBF
4180         bcm_mkiovar("txbf", (char *)&txbf, 4, iovbuf, sizeof(iovbuf));
4181         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf,
4182                 sizeof(iovbuf), TRUE, 0)) < 0) {
4183                 DHD_ERROR(("%s Set txbf failed  %d\n", __FUNCTION__, ret));
4184         }
4185 #endif /* USE_WL_TXBF */
4186 #ifdef USE_WL_FRAMEBURST
4187         /* Set frameburst to value */
4188         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_FAKEFRAG, (char *)&frameburst,
4189                 sizeof(frameburst), TRUE, 0)) < 0) {
4190                 DHD_ERROR(("%s Set frameburst failed  %d\n", __FUNCTION__, ret));
4191         }
4192 #endif /* USE_WL_FRAMEBURST */
4193 #ifdef DHD_SET_FW_HIGHSPEED
4194         /* Set ack_ratio */
4195         bcm_mkiovar("ack_ratio", (char *)&ack_ratio, 4, iovbuf, sizeof(iovbuf));
4196         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf,
4197                 sizeof(iovbuf), TRUE, 0)) < 0) {
4198                 DHD_ERROR(("%s Set ack_ratio failed  %d\n", __FUNCTION__, ret));
4199         }
4200
4201         /* Set ack_ratio_depth */
4202         bcm_mkiovar("ack_ratio_depth", (char *)&ack_ratio_depth, 4, iovbuf, sizeof(iovbuf));
4203         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf,
4204                 sizeof(iovbuf), TRUE, 0)) < 0) {
4205                 DHD_ERROR(("%s Set ack_ratio_depth failed  %d\n", __FUNCTION__, ret));
4206         }
4207 #endif /* DHD_SET_FW_HIGHSPEED */
4208 #if defined(CUSTOM_AMPDU_BA_WSIZE) || defined(CUSTOM_IBSS_AMPDU_BA_WSIZE)
4209         /* Set ampdu ba wsize to 64 or 16 */
4210 #ifdef CUSTOM_AMPDU_BA_WSIZE
4211         ampdu_ba_wsize = CUSTOM_AMPDU_BA_WSIZE;
4212 #endif
4213         if (ampdu_ba_wsize != 0) {
4214                 bcm_mkiovar("ampdu_ba_wsize", (char *)&ampdu_ba_wsize, 4, iovbuf, sizeof(iovbuf));
4215                 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf,
4216                         sizeof(iovbuf), TRUE, 0)) < 0) {
4217                         DHD_ERROR(("%s Set ampdu_ba_wsize to %d failed  %d\n",
4218                                 __FUNCTION__, CUSTOM_AMPDU_BA_WSIZE, ret));
4219                 }
4220         }
4221 #endif /* CUSTOM_AMPDU_BA_WSIZE || CUSTOM_IBSS_AMPDU_BA_WSIZE */
4222 #ifdef SUPPORT_2G_VHT
4223         bcm_mkiovar("vht_features", (char *)&vht_features, 4, iovbuf, sizeof(iovbuf));
4224         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
4225                 DHD_ERROR(("%s vht_features set failed %d\n", __FUNCTION__, ret));
4226         }
4227 #endif /* SUPPORT_2G_VHT */
4228
4229         bcm_mkiovar("buf_key_b4_m4", (char *)&buf_key_b4_m4, 4, iovbuf, sizeof(iovbuf));
4230         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf,
4231                 sizeof(iovbuf), TRUE, 0)) < 0) {
4232                 DHD_ERROR(("%s buf_key_b4_m4 set failed %d\n", __FUNCTION__, ret));
4233         }
4234
4235         /* Read event_msgs mask */
4236         bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
4237         if ((ret  = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) {
4238                 DHD_ERROR(("%s read Event mask failed %d\n", __FUNCTION__, ret));
4239                 goto done;
4240         }
4241         bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN);
4242
4243         /* Setup event_msgs */
4244         setbit(eventmask, WLC_E_SET_SSID);
4245         setbit(eventmask, WLC_E_PRUNE);
4246         setbit(eventmask, WLC_E_AUTH);
4247         setbit(eventmask, WLC_E_ASSOC);
4248         setbit(eventmask, WLC_E_REASSOC);
4249         setbit(eventmask, WLC_E_REASSOC_IND);
4250         setbit(eventmask, WLC_E_DEAUTH);
4251         setbit(eventmask, WLC_E_DEAUTH_IND);
4252         setbit(eventmask, WLC_E_DISASSOC_IND);
4253         setbit(eventmask, WLC_E_DISASSOC);
4254         setbit(eventmask, WLC_E_JOIN);
4255         setbit(eventmask, WLC_E_START);
4256         setbit(eventmask, WLC_E_ASSOC_IND);
4257         setbit(eventmask, WLC_E_PSK_SUP);
4258         setbit(eventmask, WLC_E_LINK);
4259         setbit(eventmask, WLC_E_NDIS_LINK);
4260         setbit(eventmask, WLC_E_MIC_ERROR);
4261         setbit(eventmask, WLC_E_ASSOC_REQ_IE);
4262         setbit(eventmask, WLC_E_ASSOC_RESP_IE);
4263 #ifndef WL_CFG80211
4264         setbit(eventmask, WLC_E_PMKID_CACHE);
4265         setbit(eventmask, WLC_E_TXFAIL);
4266 #endif
4267         setbit(eventmask, WLC_E_JOIN_START);
4268         setbit(eventmask, WLC_E_SCAN_COMPLETE);
4269 #ifdef WLMEDIA_HTSF
4270         setbit(eventmask, WLC_E_HTSFSYNC);
4271 #endif /* WLMEDIA_HTSF */
4272 #ifdef PNO_SUPPORT
4273         setbit(eventmask, WLC_E_PFN_NET_FOUND);
4274         setbit(eventmask, WLC_E_PFN_BEST_BATCHING);
4275         setbit(eventmask, WLC_E_PFN_BSSID_NET_FOUND);
4276         setbit(eventmask, WLC_E_PFN_BSSID_NET_LOST);
4277 #endif /* PNO_SUPPORT */
4278         /* enable dongle roaming event */
4279         setbit(eventmask, WLC_E_ROAM);
4280 #ifdef BCMCCX_S69
4281         setbit(eventmask, WLC_E_CCX_S69_RESP_RX);
4282 #endif
4283 #ifdef WLTDLS
4284         setbit(eventmask, WLC_E_TDLS_PEER_EVENT);
4285 #endif /* WLTDLS */
4286 #ifdef WL_CFG80211
4287         setbit(eventmask, WLC_E_ESCAN_RESULT);
4288         if (dhd->op_mode & DHD_FLAG_P2P_MODE) {
4289                 setbit(eventmask, WLC_E_ACTION_FRAME_RX);
4290                 setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE);
4291         }
4292 #endif /* WL_CFG80211 */
4293
4294         /* Write updated Event mask */
4295         bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
4296         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) {
4297                 DHD_ERROR(("%s Set Event mask failed %d\n", __FUNCTION__, ret));
4298                 goto done;
4299         }
4300
4301         dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time,
4302                 sizeof(scan_assoc_time), TRUE, 0);
4303         dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time,
4304                 sizeof(scan_unassoc_time), TRUE, 0);
4305         dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_PASSIVE_TIME, (char *)&scan_passive_time,
4306                 sizeof(scan_passive_time), TRUE, 0);
4307
4308 #ifdef ARP_OFFLOAD_SUPPORT
4309         /* Set and enable ARP offload feature for STA only  */
4310 #if defined(SOFTAP)
4311         if (arpoe && !ap_fw_loaded) {
4312 #else
4313         if (arpoe) {
4314 #endif 
4315                 dhd_arp_offload_enable(dhd, TRUE);
4316                 dhd_arp_offload_set(dhd, dhd_arp_mode);
4317         } else {
4318                 dhd_arp_offload_enable(dhd, FALSE);
4319                 dhd_arp_offload_set(dhd, 0);
4320         }
4321         dhd_arp_enable = arpoe;
4322 #endif /* ARP_OFFLOAD_SUPPORT */
4323
4324 #ifdef PKT_FILTER_SUPPORT
4325         /* Setup default defintions for pktfilter , enable in suspend */
4326         dhd->pktfilter_count = 6;
4327         /* Setup filter to allow only unicast */
4328         dhd->pktfilter[DHD_UNICAST_FILTER_NUM] = "100 0 0 0 0x01 0x00";
4329         dhd->pktfilter[DHD_BROADCAST_FILTER_NUM] = NULL;
4330         dhd->pktfilter[DHD_MULTICAST4_FILTER_NUM] = NULL;
4331         dhd->pktfilter[DHD_MULTICAST6_FILTER_NUM] = NULL;
4332         /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */
4333         dhd->pktfilter[DHD_MDNS_FILTER_NUM] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB";
4334         /* apply APP pktfilter */
4335         dhd->pktfilter[DHD_ARP_FILTER_NUM] = "105 0 0 12 0xFFFF 0x0806";
4336
4337
4338 #if defined(SOFTAP)
4339         if (ap_fw_loaded) {
4340                 dhd_enable_packet_filter(0, dhd);
4341         }
4342 #endif /* defined(SOFTAP) */
4343         dhd_set_packet_filter(dhd);
4344 #endif /* PKT_FILTER_SUPPORT */
4345 #ifdef DISABLE_11N
4346         bcm_mkiovar("nmode", (char *)&nmode, 4, iovbuf, sizeof(iovbuf));
4347         if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0)
4348                 DHD_ERROR(("%s wl nmode 0 failed %d\n", __FUNCTION__, ret));
4349 #else
4350 #if defined(PROP_TXSTATUS) && defined(PROP_TXSTATUS_VSDB)
4351         bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, buf, sizeof(buf));
4352         dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0);
4353 #endif 
4354 #endif /* DISABLE_11N */
4355
4356
4357
4358         /* query for 'ver' to get version info from firmware */
4359         memset(buf, 0, sizeof(buf));
4360         ptr = buf;
4361         bcm_mkiovar("ver", (char *)&buf, 4, buf, sizeof(buf));
4362         if ((ret  = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0)
4363                 DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret));
4364         else {
4365                 bcmstrtok(&ptr, "\n", 0);
4366                 /* Print fw version info */
4367                 DHD_ERROR(("Firmware version = %s\n", buf));
4368                 dhd_set_version_info(dhd, buf);
4369
4370                 /* Check and adjust IOCTL response timeout for Manufactring firmware */
4371                 if (strstr(buf, MANUFACTRING_FW) != NULL) {
4372                         dhd_os_set_ioctl_resp_timeout(20000);
4373                         DHD_ERROR(("%s : adjust IOCTL response time for Manufactring Firmware\n",
4374                         __FUNCTION__));
4375                 }
4376         }
4377
4378 #ifdef BCMSDIOH_TXGLOM
4379         if (bcmsdh_glom_enabled()) {
4380                 dhd_txglom_enable(dhd, TRUE);
4381         }
4382 #endif /* BCMSDIOH_TXGLOM */
4383
4384 #if defined(PROP_TXSTATUS)
4385 #ifdef PROP_TXSTATUS_VSDB
4386         if (dhd->op_mode == DHD_FLAG_IBSS_MODE)
4387                 dhd_wlfc_init(dhd);
4388 #else
4389         dhd_wlfc_init(dhd);
4390 #endif /* PROP_TXSTATUS_VSDB */
4391 #endif /* PROP_TXSTATUS */
4392
4393 #ifdef PNO_SUPPORT
4394         if (!dhd->pno_state) {
4395                 dhd_pno_init(dhd);
4396         }
4397 #endif
4398
4399 done:
4400         return ret;
4401 }
4402
4403
4404 int
4405 dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set)
4406 {
4407         char buf[strlen(name) + 1 + cmd_len];
4408         int len = sizeof(buf);
4409         wl_ioctl_t ioc;
4410         int ret;
4411
4412         len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len);
4413
4414         memset(&ioc, 0, sizeof(ioc));
4415
4416         ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR;
4417         ioc.buf = buf;
4418         ioc.len = len;
4419         ioc.set = set;
4420
4421         ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len);
4422         if (!set && ret >= 0)
4423                 memcpy(cmd_buf, buf, cmd_len);
4424
4425         return ret;
4426 }
4427
4428 int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx)
4429 {
4430         struct dhd_info *dhd = dhdp->info;
4431         struct net_device *dev = NULL;
4432
4433         ASSERT(dhd && dhd->iflist[ifidx]);
4434         dev = dhd->iflist[ifidx]->net;
4435         ASSERT(dev);
4436
4437         if (netif_running(dev)) {
4438                 DHD_ERROR(("%s: Must be down to change its MTU", dev->name));
4439                 return BCME_NOTDOWN;
4440         }
4441
4442 #define DHD_MIN_MTU 1500
4443 #define DHD_MAX_MTU 1752
4444
4445         if ((new_mtu < DHD_MIN_MTU) || (new_mtu > DHD_MAX_MTU)) {
4446                 DHD_ERROR(("%s: MTU size %d is invalid.\n", __FUNCTION__, new_mtu));
4447                 return BCME_BADARG;
4448         }
4449
4450         dev->mtu = new_mtu;
4451         return 0;
4452 }
4453
4454 #ifdef ARP_OFFLOAD_SUPPORT
4455 /* add or remove AOE host ip(s) (up to 8 IPs on the interface)  */
4456 void
4457 aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx)
4458 {
4459         u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */
4460         int i;
4461         int ret;
4462
4463         bzero(ipv4_buf, sizeof(ipv4_buf));
4464
4465         /* display what we've got */
4466         ret = dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx);
4467         DHD_ARPOE(("%s: hostip table read from Dongle:\n", __FUNCTION__));
4468 #ifdef AOE_DBG
4469         dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */
4470 #endif
4471         /* now we saved hoste_ip table, clr it in the dongle AOE */
4472         dhd_aoe_hostip_clr(dhd_pub, idx);
4473
4474         if (ret) {
4475                 DHD_ERROR(("%s failed\n", __FUNCTION__));
4476                 return;
4477         }
4478
4479         for (i = 0; i < MAX_IPV4_ENTRIES; i++) {
4480                 if (add && (ipv4_buf[i] == 0)) {
4481                                 ipv4_buf[i] = ipa;
4482                                 add = FALSE; /* added ipa to local table  */
4483                                 DHD_ARPOE(("%s: Saved new IP in temp arp_hostip[%d]\n",
4484                                 __FUNCTION__, i));
4485                 } else if (ipv4_buf[i] == ipa) {
4486                         ipv4_buf[i]     = 0;
4487                         DHD_ARPOE(("%s: removed IP:%x from temp table %d\n",
4488                                 __FUNCTION__, ipa, i));
4489                 }
4490
4491                 if (ipv4_buf[i] != 0) {
4492                         /* add back host_ip entries from our local cache */
4493                         dhd_arp_offload_add_ip(dhd_pub, ipv4_buf[i], idx);
4494                         DHD_ARPOE(("%s: added IP:%x to dongle arp_hostip[%d]\n\n",
4495                                 __FUNCTION__, ipv4_buf[i], i));
4496                 }
4497         }
4498 #ifdef AOE_DBG
4499         /* see the resulting hostip table */
4500         dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx);
4501         DHD_ARPOE(("%s: read back arp_hostip table:\n", __FUNCTION__));
4502         dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */
4503 #endif
4504 }
4505
4506 /*
4507  * Notification mechanism from kernel to our driver. This function is called by the Linux kernel
4508  * whenever there is an event related to an IP address.
4509  * ptr : kernel provided pointer to IP address that has changed
4510  */
4511 static int dhd_device_event(struct notifier_block *this,
4512         unsigned long event,
4513         void *ptr)
4514 {
4515         struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
4516
4517         dhd_info_t *dhd;
4518         dhd_pub_t *dhd_pub;
4519         int idx;
4520
4521         if (!dhd_arp_enable)
4522                 return NOTIFY_DONE;
4523         if (!ifa || !(ifa->ifa_dev->dev))
4524                 return NOTIFY_DONE;
4525
4526 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
4527         /* Filter notifications meant for non Broadcom devices */
4528         if ((ifa->ifa_dev->dev->netdev_ops != &dhd_ops_pri) &&
4529             (ifa->ifa_dev->dev->netdev_ops != &dhd_ops_virt)) {
4530 #if defined(WL_ENABLE_P2P_IF)
4531                 if (!wl_cfgp2p_is_ifops(ifa->ifa_dev->dev->netdev_ops))
4532 #endif /* WL_ENABLE_P2P_IF */
4533                         return NOTIFY_DONE;
4534         }
4535 #endif /* LINUX_VERSION_CODE */
4536
4537         dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev);
4538         if (!dhd)
4539                 return NOTIFY_DONE;
4540
4541         dhd_pub = &dhd->pub;
4542
4543         if (dhd_pub->arp_version == 1) {
4544                 idx = 0;
4545         }
4546         else {
4547                 for (idx = 0; idx < DHD_MAX_IFS; idx++) {
4548                         if (dhd->iflist[idx] && dhd->iflist[idx]->net == ifa->ifa_dev->dev)
4549                         break;
4550                 }
4551                 if (idx < DHD_MAX_IFS)
4552                         DHD_TRACE(("ifidx : %p %s %d\n", dhd->iflist[idx]->net,
4553                                 dhd->iflist[idx]->name, dhd->iflist[idx]->idx));
4554                 else {
4555                         DHD_ERROR(("Cannot find ifidx for(%s) set to 0\n", ifa->ifa_label));
4556                         idx = 0;
4557                 }
4558         }
4559
4560         switch (event) {
4561                 case NETDEV_UP:
4562                         DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n",
4563                                 __FUNCTION__, ifa->ifa_label, ifa->ifa_address));
4564
4565                         if (dhd->pub.busstate != DHD_BUS_DATA) {
4566                                 DHD_ERROR(("%s: bus not ready, exit\n", __FUNCTION__));
4567                                 if (dhd->pend_ipaddr) {
4568                                         DHD_ERROR(("%s: overwrite pending ipaddr: 0x%x\n",
4569                                                 __FUNCTION__, dhd->pend_ipaddr));
4570                                 }
4571                                 dhd->pend_ipaddr = ifa->ifa_address;
4572                                 break;
4573                         }
4574
4575 #ifdef AOE_IP_ALIAS_SUPPORT
4576                         DHD_ARPOE(("%s:add aliased IP to AOE hostip cache\n",
4577                                 __FUNCTION__));
4578                         aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE, idx);
4579 #endif /* AOE_IP_ALIAS_SUPPORT */
4580                         break;
4581
4582                 case NETDEV_DOWN:
4583                         DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n",
4584                                 __FUNCTION__, ifa->ifa_label, ifa->ifa_address));
4585                         dhd->pend_ipaddr = 0;
4586 #ifdef AOE_IP_ALIAS_SUPPORT
4587                         DHD_ARPOE(("%s:interface is down, AOE clr all for this if\n",
4588                                 __FUNCTION__));
4589                         aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, FALSE, idx);
4590 #else
4591                         dhd_aoe_hostip_clr(&dhd->pub, idx);
4592                         dhd_aoe_arp_clr(&dhd->pub, idx);
4593 #endif /* AOE_IP_ALIAS_SUPPORT */
4594                         break;
4595
4596                 default:
4597                         DHD_ARPOE(("%s: do noting for [%s] Event: %lu\n",
4598                                 __func__, ifa->ifa_label, event));
4599                         break;
4600         }
4601         return NOTIFY_DONE;
4602 }
4603 #endif /* ARP_OFFLOAD_SUPPORT */
4604
4605 /*
4606  * Neighbor Discovery Offload: Called when an interface
4607  * is assigned with ipv6 address.
4608  * Handles only primary interface
4609  */
4610 static int dhd_device_ipv6_event(struct notifier_block *this,
4611         unsigned long event,
4612         void *ptr)
4613 {
4614         dhd_info_t *dhd;
4615         dhd_pub_t *dhd_pub;
4616         struct ipv6_addr *_ipv6_addr = NULL;
4617         struct inet6_ifaddr *inet6_ifa = ptr;
4618         int idx = 0;
4619 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
4620         /* Filter notifications meant for non Broadcom devices */
4621         if (inet6_ifa->idev->dev->netdev_ops != &dhd_ops_pri) {
4622                         goto exit;
4623         }
4624 #endif /* LINUX_VERSION_CODE */
4625
4626         dhd = *(dhd_info_t **)netdev_priv(inet6_ifa->idev->dev);
4627         if (!dhd)
4628                 goto exit;
4629
4630         idx = dhd_net2idx(dhd, inet6_ifa->idev->dev);
4631         if (idx == DHD_BAD_IF) {
4632                 DHD_ERROR(("Cannot find ifidx"));
4633                 goto exit;
4634         }
4635         dhd_pub = &dhd->pub;
4636         if (!FW_SUPPORTED(dhd_pub, ndoe))
4637                 goto exit;
4638         if (event == NETDEV_UP || event == NETDEV_DOWN) {
4639                 _ipv6_addr = NATIVE_MALLOC(dhd_pub->osh, sizeof(struct ipv6_addr));
4640                 if (_ipv6_addr == NULL) {
4641                         DHD_ERROR(("Failed to allocate ipv6\n"));
4642                         goto exit;
4643                 }
4644                 memcpy(&_ipv6_addr->ipv6_addr[0], &inet6_ifa->addr, IPV6_ADDR_LEN);
4645                 DHD_TRACE(("IPV6 address : %pI6\n", &inet6_ifa->addr));
4646         }
4647         switch (event) {
4648                 case NETDEV_UP:
4649                         DHD_TRACE(("%s: Enable NDO and add ipv6 into table \n ", __FUNCTION__));
4650                         _ipv6_addr->ipv6_oper = DHD_IPV6_ADDR_ADD;
4651                         break;
4652                 case NETDEV_DOWN:
4653                         DHD_TRACE(("%s: clear ipv6 table \n", __FUNCTION__));
4654                         _ipv6_addr->ipv6_oper = DHD_IPV6_ADDR_DELETE;
4655                         break;
4656                 default:
4657                         DHD_ERROR(("%s: unknown notifier event \n", __FUNCTION__));
4658                         goto exit;
4659         }
4660         spin_lock_bh(&dhd->iflist[idx]->ipv6_lock);
4661         list_add_tail(&_ipv6_addr->list, &dhd->iflist[idx]->ipv6_list);
4662         spin_unlock_bh(&dhd->iflist[idx]->ipv6_lock);
4663         up(&dhd->thr_sysioc_ctl.sema);
4664 exit:
4665         return NOTIFY_DONE;
4666 }
4667
4668 int
4669 dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
4670 {
4671         dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
4672         struct net_device *net = NULL;
4673         int err = 0;
4674         uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 };
4675
4676         DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
4677
4678         ASSERT(dhd && dhd->iflist[ifidx]);
4679
4680         net = dhd->iflist[ifidx]->net;
4681         ASSERT(net);
4682
4683 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
4684         ASSERT(!net->open);
4685         net->get_stats = dhd_get_stats;
4686         net->do_ioctl = dhd_ioctl_entry;
4687         net->hard_start_xmit = dhd_start_xmit;
4688         net->set_mac_address = dhd_set_mac_address;
4689         net->set_multicast_list = dhd_set_multicast_list;
4690         net->open = net->stop = NULL;
4691 #else
4692         ASSERT(!net->netdev_ops);
4693         net->netdev_ops = &dhd_ops_virt;
4694 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
4695
4696         /* Ok, link into the network layer... */
4697         if (ifidx == 0) {
4698                 /*
4699                  * device functions for the primary interface only
4700                  */
4701 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
4702                 net->open = dhd_open;
4703                 net->stop = dhd_stop;
4704 #else
4705                 net->netdev_ops = &dhd_ops_pri;
4706 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
4707                 if (!ETHER_ISNULLADDR(dhd->pub.mac.octet))
4708                         memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
4709         } else {
4710                 /*
4711                  * We have to use the primary MAC for virtual interfaces
4712                  */
4713                 memcpy(temp_addr, dhd->iflist[ifidx]->mac_addr, ETHER_ADDR_LEN);
4714                 /*
4715                  * Android sets the locally administered bit to indicate that this is a
4716                  * portable hotspot.  This will not work in simultaneous AP/STA mode,
4717                  * nor with P2P.  Need to set the Donlge's MAC address, and then use that.
4718                  */
4719                 if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr,
4720                         ETHER_ADDR_LEN)) {
4721                         DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n",
4722                         __func__, net->name));
4723                         temp_addr[0] |= 0x02;
4724                 }
4725         }
4726
4727         net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen;
4728 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
4729         net->ethtool_ops = &dhd_ethtool_ops;
4730 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
4731
4732 #if defined(WL_WIRELESS_EXT)
4733 #if WIRELESS_EXT < 19
4734         net->get_wireless_stats = dhd_get_wireless_stats;
4735 #endif /* WIRELESS_EXT < 19 */
4736 #if WIRELESS_EXT > 12
4737         net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def;
4738 #endif /* WIRELESS_EXT > 12 */
4739 #endif /* defined(WL_WIRELESS_EXT) */
4740
4741         dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net);
4742
4743         memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN);
4744
4745         net->ifindex = 0;
4746         if ((err = register_netdev(net)) != 0) {
4747                 DHD_ERROR(("couldn't register the net device, err %d\n", err));
4748                 goto fail;
4749         }
4750         printf("Broadcom Dongle Host Driver: register interface [%s]"
4751                 " MAC: "MACDBG"\n",
4752                 net->name,
4753                 MAC2STRDBG(net->dev_addr));
4754
4755 #if defined(SOFTAP) && defined(WL_WIRELESS_EXT) && !defined(WL_CFG80211)
4756                 wl_iw_iscan_set_scan_broadcast_prep(net, 1);
4757 #endif
4758
4759 #if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
4760         if (ifidx == 0) {
4761                 dhd_registration_check = TRUE;
4762                 up(&dhd_registration_sem);
4763         }
4764 #endif 
4765         return 0;
4766
4767 fail:
4768 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
4769         net->open = NULL;
4770 #else
4771         net->netdev_ops = NULL;
4772 #endif
4773         return err;
4774 }
4775
4776 void
4777 dhd_bus_detach(dhd_pub_t *dhdp)
4778 {
4779         dhd_info_t *dhd;
4780
4781         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
4782
4783         if (dhdp) {
4784                 dhd = (dhd_info_t *)dhdp->info;
4785                 if (dhd) {
4786
4787                         /*
4788                          * In case of Android cfg80211 driver, the bus is down in dhd_stop,
4789                          *  calling stop again will cuase SD read/write errors.
4790                          */
4791                         if (dhd->pub.busstate != DHD_BUS_DOWN) {
4792                                 /* Stop the protocol module */
4793                                 dhd_prot_stop(&dhd->pub);
4794
4795                                 /* Stop the bus module */
4796                                 dhd_bus_stop(dhd->pub.bus, TRUE);
4797                         }
4798
4799 #if defined(OOB_INTR_ONLY)
4800                         bcmsdh_unregister_oob_intr();
4801 #endif 
4802                 }
4803         }
4804 }
4805
4806
4807 void dhd_detach(dhd_pub_t *dhdp)
4808 {
4809         dhd_info_t *dhd;
4810         unsigned long flags;
4811         int timer_valid = FALSE;
4812
4813         if (!dhdp)
4814                 return;
4815
4816         dhd = (dhd_info_t *)dhdp->info;
4817         if (!dhd)
4818                 return;
4819
4820         DHD_TRACE(("%s: Enter state 0x%x\n", __FUNCTION__, dhd->dhd_state));
4821 #ifdef ARP_OFFLOAD_SUPPORT
4822         unregister_inetaddr_notifier(&dhd_notifier);
4823 #endif /* ARP_OFFLOAD_SUPPORT */
4824         unregister_inet6addr_notifier(&dhd_notifier_ipv6);
4825
4826         dhd->pub.up = 0;
4827         if (!(dhd->dhd_state & DHD_ATTACH_STATE_DONE)) {
4828                 /* Give sufficient time for threads to start running in case
4829                  * dhd_attach() has failed
4830                  */
4831                 OSL_SLEEP(100);
4832         }
4833
4834         if (dhd->dhd_state & DHD_ATTACH_STATE_PROT_ATTACH) {
4835                 dhd_bus_detach(dhdp);
4836
4837                 if (dhdp->prot)
4838                         dhd_prot_detach(dhdp);
4839         }
4840 #if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND)
4841         if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) {
4842                 if (dhd->early_suspend.suspend)
4843                         unregister_early_suspend(&dhd->early_suspend);
4844         }
4845 #endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */
4846
4847 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
4848         cancel_work_sync(&dhd->work_hang);
4849 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))  */
4850
4851 #if defined(WL_WIRELESS_EXT)
4852         if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) {
4853                 /* Detatch and unlink in the iw */
4854                 wl_iw_detach();
4855         }
4856 #endif /* defined(WL_WIRELESS_EXT) */
4857
4858         if (dhd->thr_sysioc_ctl.thr_pid >= 0) {
4859                 PROC_STOP(&dhd->thr_sysioc_ctl);
4860         }
4861
4862         /* delete all interfaces, start with virtual  */
4863         if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) {
4864                 int i = 1;
4865                 dhd_if_t *ifp;
4866
4867                 /* Cleanup virtual interfaces */
4868                 for (i = 1; i < DHD_MAX_IFS; i++) {
4869                         dhd_net_if_lock_local(dhd);
4870                         if (dhd->iflist[i]) {
4871                                 dhd->iflist[i]->state = DHD_IF_DEL;
4872                                 dhd->iflist[i]->idx = i;
4873                                 dhd_op_if(dhd->iflist[i]);
4874                         }
4875
4876                         dhd_net_if_unlock_local(dhd);
4877                 }
4878                 /*  delete primary interface 0 */
4879                 ifp = dhd->iflist[0];
4880                 ASSERT(ifp);
4881                 ASSERT(ifp->net);
4882                 if (ifp && ifp->net) {
4883 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
4884                         if (ifp->net->open)
4885 #else
4886                         if (ifp->net->netdev_ops == &dhd_ops_pri)
4887 #endif
4888                         {
4889                                 unregister_netdev(ifp->net);
4890                                 free_netdev(ifp->net);
4891                                 ifp->net = NULL;
4892                                 MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
4893                                 dhd->iflist[0] = NULL;
4894                         }
4895                 }
4896         }
4897
4898         /* Clear the watchdog timer */
4899         flags = dhd_os_spin_lock(&dhd->pub);
4900         timer_valid = dhd->wd_timer_valid;
4901         dhd->wd_timer_valid = FALSE;
4902         dhd_os_spin_unlock(&dhd->pub, flags);
4903         if (timer_valid)
4904                 del_timer_sync(&dhd->timer);
4905
4906         if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) {
4907 #ifdef DHDTHREAD
4908                 if (dhd->thr_wdt_ctl.thr_pid >= 0) {
4909                         PROC_STOP(&dhd->thr_wdt_ctl);
4910                 }
4911
4912                 if (dhd->thr_dpc_ctl.thr_pid >= 0) {
4913                         PROC_STOP(&dhd->thr_dpc_ctl);
4914                 }
4915 #ifdef RXFRAME_THREAD
4916                 if (dhd->thr_rxf_ctl.thr_pid >= 0) {
4917                         PROC_STOP(&dhd->thr_rxf_ctl);
4918                 }
4919 #endif
4920                 else
4921 #endif /* DHDTHREAD */
4922                 tasklet_kill(&dhd->tasklet);
4923         }
4924 #ifdef WL_CFG80211
4925         if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
4926                 wl_cfg80211_detach(NULL);
4927                 dhd_monitor_uninit();
4928         }
4929 #endif
4930
4931 #ifdef PNO_SUPPORT
4932         if (dhdp->pno_state)
4933                 dhd_pno_deinit(dhdp);
4934 #endif
4935 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \
4936         KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM_SLEEP)
4937                 unregister_pm_notifier(&dhd_sleep_pm_notifier);
4938 #endif /* (LINUX_VERSION >= 2.6.27 && LINUX_VERSION <= 2.6.39 && CONFIG_PM_SLEEP */
4939
4940         if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) {
4941                 DHD_TRACE(("wd wakelock count:%d\n", dhd->wakelock_wd_counter));
4942 #ifdef CONFIG_HAS_WAKELOCK
4943                 dhd->wakelock_counter = 0;
4944                 dhd->wakelock_wd_counter = 0;
4945                 dhd->wakelock_rx_timeout_enable = 0;
4946                 dhd->wakelock_ctrl_timeout_enable = 0;
4947                 wake_lock_destroy(&dhd->wl_wifi);
4948                 wake_lock_destroy(&dhd->wl_rxwake);
4949                 wake_lock_destroy(&dhd->wl_ctrlwake);
4950                 wake_lock_destroy(&dhd->wl_wdwake);
4951 #endif /* CONFIG_HAS_WAKELOCK */
4952         }
4953 }
4954
4955
4956 void
4957 dhd_free(dhd_pub_t *dhdp)
4958 {
4959         dhd_info_t *dhd;
4960         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
4961
4962         if (dhdp) {
4963                 int i;
4964                 for (i = 0; i < ARRAYSIZE(dhdp->reorder_bufs); i++) {
4965                         if (dhdp->reorder_bufs[i]) {
4966                                 reorder_info_t *ptr;
4967                                 uint32 buf_size = sizeof(struct reorder_info);
4968
4969                                 ptr = dhdp->reorder_bufs[i];
4970
4971                                 buf_size += ((ptr->max_idx + 1) * sizeof(void*));
4972                                 DHD_REORDER(("free flow id buf %d, maxidx is %d, buf_size %d\n",
4973                                         i, ptr->max_idx, buf_size));
4974
4975                                 MFREE(dhdp->osh, dhdp->reorder_bufs[i], buf_size);
4976                                 dhdp->reorder_bufs[i] = NULL;
4977                         }
4978                 }
4979                 dhd = (dhd_info_t *)dhdp->info;
4980 #if defined(CONFIG_DHD_USE_STATIC_BUF)
4981                 /* If pointer is allocated by dhd_os_prealloc then avoid MFREE */
4982                 if (dhd != (dhd_info_t *)dhd_os_prealloc(NULL, DHD_PREALLOC_DHD_INFO, 0)) {
4983 #endif /* CONFIG_DHD_USE_STATIC_BUF */
4984                         if (dhd)
4985                                 MFREE(dhd->pub.osh, dhd, sizeof(*dhd));
4986 #if defined(CONFIG_DHD_USE_STATIC_BUF)
4987                 }
4988                 else {
4989                         if (dhd)
4990                                 dhd = NULL;
4991                 }
4992 #endif /* CONFIG_DHD_USE_STATIC_BUF */
4993         }
4994 }
4995
4996 static void __exit
4997 dhd_module_cleanup(void)
4998 {
4999         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
5000
5001
5002         dhd_bus_unregister();
5003
5004 #if defined(CONFIG_WIFI_CONTROL_FUNC)
5005         wl_android_wifictrl_func_del();
5006 #endif /* CONFIG_WIFI_CONTROL_FUNC */
5007         wl_android_exit();
5008
5009         /* Call customer gpio to turn off power with WL_REG_ON signal */
5010         dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
5011 }
5012
5013
5014 #if defined(CONFIG_WIFI_CONTROL_FUNC)
5015 extern bool g_wifi_poweron;
5016 #endif /* CONFIG_WIFI_CONTROL_FUNC */
5017
5018 static int __init
5019 dhd_module_init(void)
5020 {
5021         int error = 0;
5022
5023 #if 1 && defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
5024         int retry = POWERUP_MAX_RETRY;
5025         int chip_up = 0;
5026 #endif 
5027
5028         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
5029
5030         wl_android_init();
5031
5032 #if defined(DHDTHREAD)
5033         /* Sanity check on the module parameters */
5034         do {
5035                 /* Both watchdog and DPC as tasklets are ok */
5036                 if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0))
5037                         break;
5038
5039                 /* If both watchdog and DPC are threads, TX must be deferred */
5040                 if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx)
5041                         break;
5042
5043                 DHD_ERROR(("Invalid module parameters.\n"));
5044                 error = -EINVAL;
5045         } while (0);
5046 #endif 
5047         if (error)
5048                 goto fail_0;
5049
5050 #if 1 && defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
5051         do {
5052                 sema_init(&dhd_chipup_sem, 0);
5053                 error = dhd_bus_reg_sdio_notify(&dhd_chipup_sem);
5054                 if (error) {
5055                         DHD_ERROR(("n%s dhd_bus_reg_sdio_notify fail(%d)\n\n", __func__, error));
5056                         goto fail_1;
5057                 }
5058                 dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON);
5059 #if defined(CONFIG_WIFI_CONTROL_FUNC)
5060                 if (wl_android_wifictrl_func_add() < 0) {
5061                         dhd_bus_unreg_sdio_notify();
5062                         goto fail_1;
5063                 }
5064 #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */
5065                 if (down_timeout(&dhd_chipup_sem,
5066                         msecs_to_jiffies(POWERUP_WAIT_MS)) == 0) {
5067                         dhd_bus_unreg_sdio_notify();
5068                         chip_up = 1;
5069                         break;
5070                 }
5071                 DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n",
5072                         retry+1));
5073                 dhd_bus_unreg_sdio_notify();
5074 #if defined(CONFIG_WIFI_CONTROL_FUNC)
5075                 wl_android_wifictrl_func_del();
5076 #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */
5077                 dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
5078         } while (retry-- > 0);
5079
5080         if (!chip_up) {
5081                 DHD_ERROR(("\nfailed to power up wifi chip, max retry reached, exits **\n\n"));
5082                 error = -ENODEV;
5083                 goto fail_0;
5084         }
5085 #else
5086         dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON);
5087 #if defined(CONFIG_WIFI_CONTROL_FUNC)
5088         if (wl_android_wifictrl_func_add() < 0)
5089                 goto fail_1;
5090 #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */
5091 #endif 
5092
5093 #if defined(CONFIG_WIFI_CONTROL_FUNC) && defined(BCMLXSDMMC)
5094         /* If the wifi_set_power() is failed,
5095          * we need to jump error handling routines.
5096          */
5097         if (!g_wifi_poweron) {
5098                 printk("%s: wifi_set_power() failed\n", __FUNCTION__);
5099                 error = -ENODEV;
5100                 goto fail_1;
5101         }
5102 #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */
5103
5104 #if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
5105         sema_init(&dhd_registration_sem, 0);
5106 #if defined(WL_CFG80211)
5107         sema_init(&dhd_init_sem, 1);
5108 #endif
5109 #endif
5110
5111
5112         error = dhd_bus_register();
5113
5114         if (!error)
5115                 printf("\n%s\n", dhd_version);
5116         else {
5117                 DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__));
5118                 goto fail_1;
5119         }
5120
5121 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(BCMLXSDMMC)
5122         /*
5123          * Wait till MMC sdio_register_driver callback called and made driver attach.
5124          * It's needed to make sync up exit from dhd insmod  and
5125          * Kernel MMC sdio device callback registration
5126          */
5127         if ((down_timeout(&dhd_registration_sem,
5128                 msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) ||
5129                 (dhd_registration_check != TRUE)) {
5130                 error = -ENODEV;
5131                 DHD_ERROR(("%s: sdio_register_driver timeout or error \n", __FUNCTION__));
5132                 goto fail_2;
5133         }
5134 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
5135 #if defined(WL_CFG80211)
5136 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
5137         down(&dhd_init_sem);
5138         if (init_power_off)
5139 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
5140                 wl_android_post_init();
5141         /* init is done, allow open to continue */
5142 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
5143         up(&dhd_init_sem);
5144 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
5145 #endif /* defined(WL_CFG80211) */
5146
5147         return error;
5148
5149 #if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(BCMLXSDMMC)
5150 fail_2:
5151         dhd_bus_unregister();
5152 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
5153
5154 fail_1:
5155
5156 #if defined(CONFIG_WIFI_CONTROL_FUNC)
5157         wl_android_wifictrl_func_del();
5158 #endif 
5159
5160         /* Call customer gpio to turn off power with WL_REG_ON signal */
5161         dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
5162
5163 fail_0:
5164
5165         wl_android_exit();
5166
5167         return error;
5168 }
5169
5170 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
5171 #ifdef USE_LATE_INITCALL_SYNC
5172 late_initcall_sync(dhd_module_init);
5173 #else
5174 late_initcall(dhd_module_init);
5175 #endif /* USE_LATE_INITCALL_SYNC */
5176 #else
5177 module_init(dhd_module_init);
5178 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
5179
5180 module_exit(dhd_module_cleanup);
5181
5182 /*
5183  * OS specific functions required to implement DHD driver in OS independent way
5184  */
5185 int
5186 dhd_os_proto_block(dhd_pub_t *pub)
5187 {
5188         dhd_info_t * dhd = (dhd_info_t *)(pub->info);
5189
5190         if (dhd) {
5191                 down(&dhd->proto_sem);
5192                 return 1;
5193         }
5194
5195         return 0;
5196 }
5197
5198 int
5199 dhd_os_proto_unblock(dhd_pub_t *pub)
5200 {
5201         dhd_info_t * dhd = (dhd_info_t *)(pub->info);
5202
5203         if (dhd) {
5204                 up(&dhd->proto_sem);
5205                 return 1;
5206         }
5207
5208         return 0;
5209 }
5210
5211 unsigned int
5212 dhd_os_get_ioctl_resp_timeout(void)
5213 {
5214         return ((unsigned int)dhd_ioctl_timeout_msec);
5215 }
5216
5217 void
5218 dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec)
5219 {
5220         dhd_ioctl_timeout_msec = (int)timeout_msec;
5221 }
5222
5223 int
5224 dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending)
5225 {
5226         dhd_info_t * dhd = (dhd_info_t *)(pub->info);
5227         int timeout;
5228
5229         /* Convert timeout in millsecond to jiffies */
5230 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
5231         timeout = msecs_to_jiffies(dhd_ioctl_timeout_msec);
5232 #else
5233         timeout = dhd_ioctl_timeout_msec * HZ / 1000;
5234 #endif
5235
5236         timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout);
5237         return timeout;
5238 }
5239
5240 int
5241 dhd_os_ioctl_resp_wake(dhd_pub_t *pub)
5242 {
5243         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
5244
5245         if (waitqueue_active(&dhd->ioctl_resp_wait)) {
5246                 wake_up(&dhd->ioctl_resp_wait);
5247         }
5248
5249         return 0;
5250 }
5251
5252 void
5253 dhd_os_wd_timer_extend(void *bus, bool extend)
5254 {
5255         dhd_pub_t *pub = bus;
5256         dhd_info_t *dhd = (dhd_info_t *)pub->info;
5257
5258         if (extend)
5259                 dhd_os_wd_timer(bus, WATCHDOG_EXTEND_INTERVAL);
5260         else
5261                 dhd_os_wd_timer(bus, dhd->default_wd_interval);
5262 }
5263
5264
5265 void
5266 dhd_os_wd_timer(void *bus, uint wdtick)
5267 {
5268         dhd_pub_t *pub = bus;
5269         dhd_info_t *dhd = (dhd_info_t *)pub->info;
5270         unsigned long flags;
5271
5272         DHD_TRACE(("%s: Enter\n", __FUNCTION__));
5273
5274         if (!dhd) {
5275                 DHD_ERROR(("%s: dhd NULL\n", __FUNCTION__));
5276                 return;
5277         }
5278
5279         flags = dhd_os_spin_lock(pub);
5280
5281         /* don't start the wd until fw is loaded */
5282         if (pub->busstate == DHD_BUS_DOWN) {
5283                 dhd_os_spin_unlock(pub, flags);
5284                 if (!wdtick)
5285                         DHD_OS_WD_WAKE_UNLOCK(pub);
5286                 return;
5287         }
5288
5289         /* Totally stop the timer */
5290         if (!wdtick && dhd->wd_timer_valid == TRUE) {
5291                 dhd->wd_timer_valid = FALSE;
5292                 dhd_os_spin_unlock(pub, flags);
5293 #ifdef DHDTHREAD
5294                 del_timer_sync(&dhd->timer);
5295 #else
5296                 del_timer(&dhd->timer);
5297 #endif /* DHDTHREAD */
5298                 DHD_OS_WD_WAKE_UNLOCK(pub);
5299                 return;
5300         }
5301
5302         if (wdtick) {
5303                 DHD_OS_WD_WAKE_LOCK(pub);
5304                 dhd_watchdog_ms = (uint)wdtick;
5305                 /* Re arm the timer, at last watchdog period */
5306                 mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms));
5307                 dhd->wd_timer_valid = TRUE;
5308         }
5309         dhd_os_spin_unlock(pub, flags);
5310 }
5311
5312 void *
5313 dhd_os_open_image(char *filename)
5314 {
5315         struct file *fp;
5316
5317         fp = filp_open(filename, O_RDONLY, 0);
5318         /*
5319          * 2.6.11 (FC4) supports filp_open() but later revs don't?
5320          * Alternative:
5321          * fp = open_namei(AT_FDCWD, filename, O_RD, 0);
5322          * ???
5323          */
5324          if (IS_ERR(fp))
5325                  fp = NULL;
5326
5327          return fp;
5328 }
5329
5330 int
5331 dhd_os_get_image_block(char *buf, int len, void *image)
5332 {
5333         struct file *fp = (struct file *)image;
5334         int rdlen;
5335
5336         if (!image)
5337                 return 0;
5338
5339         rdlen = kernel_read(fp, fp->f_pos, buf, len);
5340         if (rdlen > 0)
5341                 fp->f_pos += rdlen;
5342
5343         return rdlen;
5344 }
5345
5346 void
5347 dhd_os_close_image(void *image)
5348 {
5349         if (image)
5350                 filp_close((struct file *)image, NULL);
5351 }
5352
5353
5354 void
5355 dhd_os_sdlock(dhd_pub_t *pub)
5356 {
5357         dhd_info_t *dhd;
5358
5359         dhd = (dhd_info_t *)(pub->info);
5360
5361 #ifdef DHDTHREAD
5362         if (dhd->threads_only)
5363                 down(&dhd->sdsem);
5364         else
5365 #endif /* DHDTHREAD */
5366         spin_lock_bh(&dhd->sdlock);
5367 }
5368
5369 void
5370 dhd_os_sdunlock(dhd_pub_t *pub)
5371 {
5372         dhd_info_t *dhd;
5373
5374         dhd = (dhd_info_t *)(pub->info);
5375
5376 #ifdef DHDTHREAD
5377         if (dhd->threads_only)
5378                 up(&dhd->sdsem);
5379         else
5380 #endif /* DHDTHREAD */
5381         spin_unlock_bh(&dhd->sdlock);
5382 }
5383
5384 void
5385 dhd_os_sdlock_txq(dhd_pub_t *pub)
5386 {
5387         dhd_info_t *dhd;
5388
5389         dhd = (dhd_info_t *)(pub->info);
5390         spin_lock_bh(&dhd->txqlock);
5391 }
5392
5393 void
5394 dhd_os_sdunlock_txq(dhd_pub_t *pub)
5395 {
5396         dhd_info_t *dhd;
5397
5398         dhd = (dhd_info_t *)(pub->info);
5399         spin_unlock_bh(&dhd->txqlock);
5400 }
5401
5402 void
5403 dhd_os_sdlock_rxq(dhd_pub_t *pub)
5404 {
5405 }
5406
5407 void
5408 dhd_os_sdunlock_rxq(dhd_pub_t *pub)
5409 {
5410 }
5411
5412 void
5413 dhd_os_sdtxlock(dhd_pub_t *pub)
5414 {
5415         dhd_os_sdlock(pub);
5416 }
5417
5418 void
5419 dhd_os_sdtxunlock(dhd_pub_t *pub)
5420 {
5421         dhd_os_sdunlock(pub);
5422 }
5423
5424 #if defined(DHDTHREAD) && defined(RXFRAME_THREAD)
5425 static void
5426 dhd_os_rxflock(dhd_pub_t *pub)
5427 {
5428         dhd_info_t *dhd;
5429
5430         dhd = (dhd_info_t *)(pub->info);
5431         spin_lock_bh(&dhd->rxf_lock);
5432
5433 }
5434
5435 static void
5436 dhd_os_rxfunlock(dhd_pub_t *pub)
5437 {
5438         dhd_info_t *dhd;
5439
5440         dhd = (dhd_info_t *)(pub->info);
5441         spin_unlock_bh(&dhd->rxf_lock);
5442 }
5443 #endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */
5444
5445 #ifdef DHDTCPACK_SUPPRESS
5446 void
5447 dhd_os_tcpacklock(dhd_pub_t *pub)
5448 {
5449         dhd_info_t *dhd;
5450
5451         dhd = (dhd_info_t *)(pub->info);
5452         spin_lock_bh(&dhd->tcpack_lock);
5453
5454 }
5455
5456 void
5457 dhd_os_tcpackunlock(dhd_pub_t *pub)
5458 {
5459         dhd_info_t *dhd;
5460
5461         dhd = (dhd_info_t *)(pub->info);
5462         spin_unlock_bh(&dhd->tcpack_lock);
5463 }
5464 #endif /* DHDTCPACK_SUPPRESS */
5465
5466 #if defined(CONFIG_DHD_USE_STATIC_BUF)
5467 uint8* dhd_os_prealloc(void *osh, int section, uint size)
5468 {
5469         return (uint8*)wl_android_prealloc(section, size);
5470 }
5471
5472 void dhd_os_prefree(void *osh, void *addr, uint size)
5473 {
5474 }
5475 #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */
5476
5477 #if defined(WL_WIRELESS_EXT)
5478 struct iw_statistics *
5479 dhd_get_wireless_stats(struct net_device *dev)
5480 {
5481         int res = 0;
5482         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5483
5484         if (!dhd->pub.up) {
5485                 return NULL;
5486         }
5487
5488         res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats);
5489
5490         if (res == 0)
5491                 return &dhd->iw.wstats;
5492         else
5493                 return NULL;
5494 }
5495 #endif /* defined(WL_WIRELESS_EXT) */
5496
5497 static int
5498 dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
5499         wl_event_msg_t *event, void **data)
5500 {
5501         int bcmerror = 0;
5502         ASSERT(dhd != NULL);
5503
5504         bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data);
5505         if (bcmerror != BCME_OK)
5506                 return (bcmerror);
5507
5508 #if defined(WL_WIRELESS_EXT)
5509         if (event->bsscfgidx == 0) {
5510                 /*
5511                  * Wireless ext is on primary interface only
5512                  */
5513
5514         ASSERT(dhd->iflist[*ifidx] != NULL);
5515         ASSERT(dhd->iflist[*ifidx]->net != NULL);
5516
5517                 if (dhd->iflist[*ifidx]->net) {
5518                 wl_iw_event(dhd->iflist[*ifidx]->net, event, *data);
5519                 }
5520         }
5521 #endif /* defined(WL_WIRELESS_EXT)  */
5522
5523 #ifdef WL_CFG80211
5524         if ((ntoh32(event->event_type) == WLC_E_IF) &&
5525                 (((dhd_if_event_t *)*data)->action == WLC_E_IF_ADD))
5526                 /* If ADD_IF has been called directly by wl utility then we
5527                  * should not report this. In case if ADD_IF was called from
5528                  * CFG stack, then too this event need not be reported back
5529                  */
5530                 return (BCME_OK);
5531         if ((wl_cfg80211_is_progress_ifchange() ||
5532                 wl_cfg80211_is_progress_ifadd()) && (*ifidx != 0)) {
5533                 /*
5534                  * If IF_ADD/CHANGE operation is going on,
5535                  *  discard any event received on the virtual I/F
5536                  */
5537                 return (BCME_OK);
5538         }
5539
5540         ASSERT(dhd->iflist[*ifidx] != NULL);
5541         ASSERT(dhd->iflist[*ifidx]->net != NULL);
5542         if (dhd->iflist[*ifidx]->event2cfg80211 && dhd->iflist[*ifidx]->net) {
5543                 wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data);
5544         }
5545 #endif /* defined(WL_CFG80211) */
5546
5547         return (bcmerror);
5548 }
5549
5550 /* send up locally generated event */
5551 void
5552 dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data)
5553 {
5554         switch (ntoh32(event->event_type)) {
5555
5556         default:
5557                 break;
5558         }
5559 }
5560
5561 void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar)
5562 {
5563 #if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
5564         struct dhd_info *dhdinfo =  dhd->info;
5565
5566 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
5567         int timeout = msecs_to_jiffies(IOCTL_RESP_TIMEOUT);
5568 #else
5569         int timeout = (IOCTL_RESP_TIMEOUT / 1000) * HZ;
5570 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
5571
5572         dhd_os_sdunlock(dhd);
5573         wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout);
5574         dhd_os_sdlock(dhd);
5575 #endif 
5576         return;
5577 }
5578
5579 void dhd_wait_event_wakeup(dhd_pub_t *dhd)
5580 {
5581 #if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
5582         struct dhd_info *dhdinfo =  dhd->info;
5583         if (waitqueue_active(&dhdinfo->ctrl_wait))
5584                 wake_up(&dhdinfo->ctrl_wait);
5585 #endif
5586         return;
5587 }
5588
5589 int
5590 dhd_dev_reset(struct net_device *dev, uint8 flag)
5591 {
5592         int ret;
5593
5594         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5595
5596         if (flag == TRUE) {
5597                 /* Issue wl down command before resetting the chip */
5598                 if (dhd_wl_ioctl_cmd(&dhd->pub, WLC_DOWN, NULL, 0, TRUE, 0) < 0) {
5599                         DHD_TRACE(("%s: wl down failed\n", __FUNCTION__));
5600                 }
5601 #if defined(PROP_TXSTATUS)
5602 #ifdef PROP_TXSTATUS_VSDB
5603                 if (dhd->pub.op_mode == DHD_FLAG_IBSS_MODE) {
5604                         dhd_wlfc_deinit(&dhd->pub);
5605                         if (dhd->pub.plat_deinit)
5606                         dhd->pub.plat_deinit((void *)&dhd->pub);
5607                 }
5608 #else
5609                 dhd_wlfc_deinit(&dhd->pub);
5610                 if (dhd->pub.plat_deinit)
5611                         dhd->pub.plat_deinit((void *)&dhd->pub);
5612 #endif /* PROP_TXSTATUS_VSDB */
5613 #endif /* PROP_TXSTATUS */
5614
5615 #ifdef PNO_SUPPORT
5616         if (dhd->pub.pno_state)
5617                 dhd_pno_deinit(&dhd->pub);
5618 #endif
5619         }
5620
5621         ret = dhd_bus_devreset(&dhd->pub, flag);
5622         if (ret) {
5623                 DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret));
5624                 return ret;
5625         }
5626
5627         return ret;
5628 }
5629
5630 int net_os_set_suspend_disable(struct net_device *dev, int val)
5631 {
5632         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5633         int ret = 0;
5634
5635         if (dhd) {
5636                 ret = dhd->pub.suspend_disable_flag;
5637                 dhd->pub.suspend_disable_flag = val;
5638         }
5639         return ret;
5640 }
5641
5642 int net_os_set_suspend(struct net_device *dev, int val, int force)
5643 {
5644         int ret = 0;
5645         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5646
5647         if (dhd) {
5648 #if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND)
5649                 ret = dhd_set_suspend(val, &dhd->pub);
5650 #else
5651                 ret = dhd_suspend_resume_helper(dhd, val, force);
5652 #endif
5653 #ifdef WL_CFG80211
5654                 wl_cfg80211_update_power_mode(dev);
5655 #endif
5656         }
5657         return ret;
5658 }
5659
5660 int net_os_set_suspend_bcn_li_dtim(struct net_device *dev, int val)
5661 {
5662         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5663
5664         if (dhd)
5665                 dhd->pub.suspend_bcn_li_dtim = val;
5666
5667         return 0;
5668 }
5669
5670 #ifdef PKT_FILTER_SUPPORT
5671 int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num)
5672 {
5673         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5674         char *filterp = NULL;
5675         int filter_id = 0;
5676         int ret = 0;
5677
5678         if (!dhd || (num == DHD_UNICAST_FILTER_NUM) ||
5679                 (num == DHD_MDNS_FILTER_NUM))
5680                 return ret;
5681         if (num >= dhd->pub.pktfilter_count)
5682                 return -EINVAL;
5683         switch (num) {
5684                 case DHD_BROADCAST_FILTER_NUM:
5685                         filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF";
5686                         filter_id = 101;
5687                         break;
5688                 case DHD_MULTICAST4_FILTER_NUM:
5689                         filterp = "102 0 0 0 0xFFFFFF 0x01005E";
5690                         filter_id = 102;
5691                         break;
5692                 case DHD_MULTICAST6_FILTER_NUM:
5693                         filterp = "103 0 0 0 0xFFFF 0x3333";
5694                         filter_id = 103;
5695                         break;
5696                 default:
5697                         return -EINVAL;
5698         }
5699
5700         /* Add filter */
5701         if (add_remove) {
5702                 dhd->pub.pktfilter[num] = filterp;
5703                 dhd_pktfilter_offload_set(&dhd->pub, dhd->pub.pktfilter[num]);
5704         } else { /* Delete filter */
5705                 if (dhd->pub.pktfilter[num] != NULL) {
5706                         dhd_pktfilter_offload_delete(&dhd->pub, filter_id);
5707                         dhd->pub.pktfilter[num] = NULL;
5708                 }
5709         }
5710         return ret;
5711 }
5712
5713 int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val)
5714
5715 {
5716         int ret = 0;
5717
5718         /* Packet filtering is set only if we still in early-suspend and
5719          * we need either to turn it ON or turn it OFF
5720          * We can always turn it OFF in case of early-suspend, but we turn it
5721          * back ON only if suspend_disable_flag was not set
5722         */
5723         if (dhdp && dhdp->up) {
5724                 if (dhdp->in_suspend) {
5725                         if (!val || (val && !dhdp->suspend_disable_flag))
5726                                 dhd_enable_packet_filter(val, dhdp);
5727                 }
5728         }
5729         return ret;
5730 }
5731
5732 /* function to enable/disable packet for Network device */
5733 int net_os_enable_packet_filter(struct net_device *dev, int val)
5734 {
5735         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5736
5737         return dhd_os_enable_packet_filter(&dhd->pub, val);
5738 }
5739 #endif /* PKT_FILTER_SUPPORT */
5740
5741 int
5742 dhd_dev_init_ioctl(struct net_device *dev)
5743 {
5744         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5745         int ret;
5746
5747         dhd_process_cid_mac(&dhd->pub, TRUE);
5748
5749         if ((ret = dhd_preinit_ioctls(&dhd->pub)) < 0)
5750                 goto done;
5751
5752         dhd_process_cid_mac(&dhd->pub, FALSE);
5753
5754 done:
5755         return ret;
5756 }
5757
5758 #ifdef PNO_SUPPORT
5759 /* Linux wrapper to call common dhd_pno_stop_for_ssid */
5760 int
5761 dhd_dev_pno_stop_for_ssid(struct net_device *dev)
5762 {
5763         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5764
5765         return (dhd_pno_stop_for_ssid(&dhd->pub));
5766 }
5767 /* Linux wrapper to call common dhd_pno_set_for_ssid */
5768 int
5769 dhd_dev_pno_set_for_ssid(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid,
5770         uint16  scan_fr, int pno_repeat, int pno_freq_expo_max, uint16 *channel_list, int nchan)
5771 {
5772         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5773
5774         return (dhd_pno_set_for_ssid(&dhd->pub, ssids_local, nssid, scan_fr,
5775                 pno_repeat, pno_freq_expo_max, channel_list, nchan));
5776 }
5777
5778 /* Linux wrapper to call common dhd_pno_enable */
5779 int
5780 dhd_dev_pno_enable(struct net_device *dev, int enable)
5781 {
5782         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5783
5784         return (dhd_pno_enable(&dhd->pub, enable));
5785 }
5786
5787 /* Linux wrapper to call common dhd_pno_set_for_hotlist */
5788 int
5789 dhd_dev_pno_set_for_hotlist(struct net_device *dev, wl_pfn_bssid_t *p_pfn_bssid,
5790         struct dhd_pno_hotlist_params *hotlist_params)
5791 {
5792         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5793         return (dhd_pno_set_for_hotlist(&dhd->pub, p_pfn_bssid, hotlist_params));
5794 }
5795 /* Linux wrapper to call common dhd_dev_pno_stop_for_batch */
5796 int
5797 dhd_dev_pno_stop_for_batch(struct net_device *dev)
5798 {
5799         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5800         return (dhd_pno_stop_for_batch(&dhd->pub));
5801 }
5802 /* Linux wrapper to call common dhd_dev_pno_set_for_batch */
5803 int
5804 dhd_dev_pno_set_for_batch(struct net_device *dev, struct dhd_pno_batch_params *batch_params)
5805 {
5806         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5807         return (dhd_pno_set_for_batch(&dhd->pub, batch_params));
5808 }
5809 /* Linux wrapper to call common dhd_dev_pno_get_for_batch */
5810 int
5811 dhd_dev_pno_get_for_batch(struct net_device *dev, char *buf, int bufsize)
5812 {
5813         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5814         return (dhd_pno_get_for_batch(&dhd->pub, buf, bufsize, PNO_STATUS_NORMAL));
5815 }
5816 #endif /* PNO_SUPPORT */
5817
5818 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1)
5819 static void dhd_hang_process(struct work_struct *work)
5820 {
5821         dhd_info_t *dhd;
5822         struct net_device *dev;
5823
5824         dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang);
5825         dev = dhd->iflist[0]->net;
5826
5827         if (dev) {
5828                 rtnl_lock();
5829                 dev_close(dev);
5830                 rtnl_unlock();
5831 #if defined(WL_WIRELESS_EXT)
5832                 wl_iw_send_priv_event(dev, "HANG");
5833 #endif
5834 #if defined(WL_CFG80211)
5835                 wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED);
5836 #endif
5837         }
5838 }
5839
5840 int dhd_os_send_hang_message(dhd_pub_t *dhdp)
5841 {
5842         int ret = 0;
5843         if (dhdp) {
5844                 if (!dhdp->hang_was_sent) {
5845                         dhdp->hang_was_sent = 1;
5846                         schedule_work(&dhdp->info->work_hang);
5847                 }
5848         }
5849         return ret;
5850 }
5851
5852 int net_os_send_hang_message(struct net_device *dev)
5853 {
5854         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5855         int ret = 0;
5856
5857         if (dhd) {
5858                 /* Report FW problem when enabled */
5859                 if (dhd->pub.hang_report) {
5860 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
5861                         ret = dhd_os_send_hang_message(&dhd->pub);
5862 #else
5863                         ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED);
5864 #endif
5865                 } else {
5866                         DHD_ERROR(("%s: FW HANG ignored (for testing purpose) and not sent up\n",
5867                                 __FUNCTION__));
5868                         /* Enforce bus down to stop any future traffic */
5869                         dhd->pub.busstate = DHD_BUS_DOWN;
5870                 }
5871         }
5872         return ret;
5873 }
5874 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && OEM_ANDROID */
5875
5876 void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify)
5877 {
5878         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5879         if (dhd && dhd->pub.up) {
5880                 memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t));
5881 #ifdef WL_CFG80211
5882                 wl_update_wiphybands(NULL, notify);
5883 #endif
5884         }
5885 }
5886
5887 void dhd_bus_band_set(struct net_device *dev, uint band)
5888 {
5889         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5890         if (dhd && dhd->pub.up) {
5891 #ifdef WL_CFG80211
5892                 wl_update_wiphybands(NULL, true);
5893 #endif
5894         }
5895 }
5896
5897 void dhd_net_if_lock(struct net_device *dev)
5898 {
5899         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5900         dhd_net_if_lock_local(dhd);
5901 }
5902
5903 void dhd_net_if_unlock(struct net_device *dev)
5904 {
5905         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5906         dhd_net_if_unlock_local(dhd);
5907 }
5908
5909 static void dhd_net_if_lock_local(dhd_info_t *dhd)
5910 {
5911 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
5912         if (dhd)
5913                 mutex_lock(&dhd->dhd_net_if_mutex);
5914 #endif
5915 }
5916
5917 static void dhd_net_if_unlock_local(dhd_info_t *dhd)
5918 {
5919 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
5920         if (dhd)
5921                 mutex_unlock(&dhd->dhd_net_if_mutex);
5922 #endif
5923 }
5924
5925 static void dhd_suspend_lock(dhd_pub_t *pub)
5926 {
5927 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
5928         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
5929         if (dhd)
5930                 mutex_lock(&dhd->dhd_suspend_mutex);
5931 #endif
5932 }
5933
5934 static void dhd_suspend_unlock(dhd_pub_t *pub)
5935 {
5936 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
5937         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
5938         if (dhd)
5939                 mutex_unlock(&dhd->dhd_suspend_mutex);
5940 #endif
5941 }
5942
5943 unsigned long dhd_os_spin_lock(dhd_pub_t *pub)
5944 {
5945         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
5946         unsigned long flags = 0;
5947
5948         if (dhd)
5949                 spin_lock_irqsave(&dhd->dhd_lock, flags);
5950
5951         return flags;
5952 }
5953
5954 void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags)
5955 {
5956         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
5957
5958         if (dhd)
5959                 spin_unlock_irqrestore(&dhd->dhd_lock, flags);
5960 }
5961
5962 static int
5963 dhd_get_pend_8021x_cnt(dhd_info_t *dhd)
5964 {
5965         return (atomic_read(&dhd->pend_8021x_cnt));
5966 }
5967
5968 #define MAX_WAIT_FOR_8021X_TX   50
5969
5970 int
5971 dhd_wait_pend8021x(struct net_device *dev)
5972 {
5973         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
5974         int timeout = msecs_to_jiffies(10);
5975         int ntimes = MAX_WAIT_FOR_8021X_TX;
5976         int pend = dhd_get_pend_8021x_cnt(dhd);
5977
5978         while (ntimes && pend) {
5979                 if (pend) {
5980                         set_current_state(TASK_INTERRUPTIBLE);
5981                         schedule_timeout(timeout);
5982                         set_current_state(TASK_RUNNING);
5983                         ntimes--;
5984                 }
5985                 pend = dhd_get_pend_8021x_cnt(dhd);
5986         }
5987         if (ntimes == 0)
5988         {
5989                 atomic_set(&dhd->pend_8021x_cnt, 0);
5990                 DHD_ERROR(("%s: TIMEOUT\n", __FUNCTION__));
5991         }
5992         return pend;
5993 }
5994
5995 #ifdef DHD_DEBUG
5996 int
5997 write_to_file(dhd_pub_t *dhd, uint8 *buf, int size)
5998 {
5999         int ret = 0;
6000         struct file *fp;
6001         mm_segment_t old_fs;
6002         loff_t pos = 0;
6003
6004         /* change to KERNEL_DS address limit */
6005         old_fs = get_fs();
6006         set_fs(KERNEL_DS);
6007
6008         /* open file to write */
6009         fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640);
6010         if (!fp) {
6011                 printf("%s: open file error\n", __FUNCTION__);
6012                 ret = -1;
6013                 goto exit;
6014         }
6015
6016         /* Write buf to file */
6017         fp->f_op->write(fp, buf, size, &pos);
6018
6019 exit:
6020         /* free buf before return */
6021         MFREE(dhd->osh, buf, size);
6022         /* close file before return */
6023         if (fp)
6024                 filp_close(fp, current->files);
6025         /* restore previous address limit */
6026         set_fs(old_fs);
6027
6028         return ret;
6029 }
6030 #endif /* DHD_DEBUG */
6031
6032 int dhd_os_wake_lock_timeout(dhd_pub_t *pub)
6033 {
6034         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
6035         unsigned long flags;
6036         int ret = 0;
6037
6038         if (dhd) {
6039                 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
6040                 ret = dhd->wakelock_rx_timeout_enable > dhd->wakelock_ctrl_timeout_enable ?
6041                         dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable;
6042 #ifdef CONFIG_HAS_WAKELOCK
6043                 if (dhd->wakelock_rx_timeout_enable)
6044                         wake_lock_timeout(&dhd->wl_rxwake,
6045                                 msecs_to_jiffies(dhd->wakelock_rx_timeout_enable));
6046                 if (dhd->wakelock_ctrl_timeout_enable)
6047                         wake_lock_timeout(&dhd->wl_ctrlwake,
6048                                 msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable));
6049 #endif
6050                 dhd->wakelock_rx_timeout_enable = 0;
6051                 dhd->wakelock_ctrl_timeout_enable = 0;
6052                 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
6053         }
6054         return ret;
6055 }
6056
6057 int net_os_wake_lock_timeout(struct net_device *dev)
6058 {
6059         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
6060         int ret = 0;
6061
6062         if (dhd)
6063                 ret = dhd_os_wake_lock_timeout(&dhd->pub);
6064         return ret;
6065 }
6066
6067 int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val)
6068 {
6069         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
6070         unsigned long flags;
6071
6072         if (dhd) {
6073                 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
6074                 if (val > dhd->wakelock_rx_timeout_enable)
6075                         dhd->wakelock_rx_timeout_enable = val;
6076                 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
6077         }
6078         return 0;
6079 }
6080
6081 int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val)
6082 {
6083         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
6084         unsigned long flags;
6085
6086         if (dhd) {
6087                 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
6088                 if (val > dhd->wakelock_ctrl_timeout_enable)
6089                         dhd->wakelock_ctrl_timeout_enable = val;
6090                 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
6091         }
6092         return 0;
6093 }
6094
6095 int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val)
6096 {
6097         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
6098         int ret = 0;
6099
6100         if (dhd)
6101                 ret = dhd_os_wake_lock_rx_timeout_enable(&dhd->pub, val);
6102         return ret;
6103 }
6104
6105 int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val)
6106 {
6107         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
6108         int ret = 0;
6109
6110         if (dhd)
6111                 ret = dhd_os_wake_lock_ctrl_timeout_enable(&dhd->pub, val);
6112         return ret;
6113 }
6114
6115 int dhd_os_wake_lock(dhd_pub_t *pub)
6116 {
6117         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
6118         unsigned long flags;
6119         int ret = 0;
6120
6121         if (dhd) {
6122                 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
6123 #ifdef CONFIG_HAS_WAKELOCK
6124                 if (!dhd->wakelock_counter)
6125                         wake_lock(&dhd->wl_wifi);
6126 #elif 1 && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
6127                 if (pm_dev)
6128                         pm_stay_awake(pm_dev);
6129 #endif
6130                 dhd->wakelock_counter++;
6131                 ret = dhd->wakelock_counter;
6132                 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
6133         }
6134         return ret;
6135 }
6136
6137 int net_os_wake_lock(struct net_device *dev)
6138 {
6139         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
6140         int ret = 0;
6141
6142         if (dhd)
6143                 ret = dhd_os_wake_lock(&dhd->pub);
6144         return ret;
6145 }
6146
6147 int dhd_os_wake_unlock(dhd_pub_t *pub)
6148 {
6149         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
6150         unsigned long flags;
6151         int ret = 0;
6152
6153         dhd_os_wake_lock_timeout(pub);
6154         if (dhd) {
6155                 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
6156                 if (dhd->wakelock_counter) {
6157                         dhd->wakelock_counter--;
6158 #ifdef CONFIG_HAS_WAKELOCK
6159                         if (!dhd->wakelock_counter)
6160                                 wake_unlock(&dhd->wl_wifi);
6161 #elif 1 && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
6162                         if (pm_dev)
6163                                 pm_relax(pm_dev);
6164 #endif
6165                         ret = dhd->wakelock_counter;
6166                 }
6167                 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
6168         }
6169         return ret;
6170 }
6171
6172 int dhd_os_check_wakelock(void *dhdp)
6173 {
6174 #if defined(CONFIG_HAS_WAKELOCK) || (1 && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, \
6175         36)))
6176         dhd_pub_t *pub = (dhd_pub_t *)dhdp;
6177         dhd_info_t *dhd;
6178
6179         if (!pub)
6180                 return 0;
6181         dhd = (dhd_info_t *)(pub->info);
6182 #endif /* CONFIG_HAS_WAKELOCK || BCMSDIO */
6183
6184 #ifdef CONFIG_HAS_WAKELOCK
6185         /* Indicate to the SD Host to avoid going to suspend if internal locks are up */
6186         if (dhd && (wake_lock_active(&dhd->wl_wifi) ||
6187                 (wake_lock_active(&dhd->wl_wdwake))))
6188                 return 1;
6189 #elif 1 && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
6190         if (dhd && (dhd->wakelock_counter > 0) && pm_dev)
6191                 return 1;
6192 #endif
6193         return 0;
6194 }
6195 int net_os_wake_unlock(struct net_device *dev)
6196 {
6197         dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
6198         int ret = 0;
6199
6200         if (dhd)
6201                 ret = dhd_os_wake_unlock(&dhd->pub);
6202         return ret;
6203 }
6204
6205 int dhd_os_wd_wake_lock(dhd_pub_t *pub)
6206 {
6207         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
6208         unsigned long flags;
6209         int ret = 0;
6210
6211         if (dhd) {
6212                 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
6213 #ifdef CONFIG_HAS_WAKELOCK
6214                 /* if wakelock_wd_counter was never used : lock it at once */
6215                 if (!dhd->wakelock_wd_counter)
6216                         wake_lock(&dhd->wl_wdwake);
6217 #endif
6218                 dhd->wakelock_wd_counter++;
6219                 ret = dhd->wakelock_wd_counter;
6220                 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
6221         }
6222         return ret;
6223 }
6224
6225 int dhd_os_wd_wake_unlock(dhd_pub_t *pub)
6226 {
6227         dhd_info_t *dhd = (dhd_info_t *)(pub->info);
6228         unsigned long flags;
6229         int ret = 0;
6230
6231         if (dhd) {
6232                 spin_lock_irqsave(&dhd->wakelock_spinlock, flags);
6233                 if (dhd->wakelock_wd_counter) {
6234                         dhd->wakelock_wd_counter = 0;
6235 #ifdef CONFIG_HAS_WAKELOCK
6236                         wake_unlock(&dhd->wl_wdwake);
6237 #endif
6238                 }
6239                 spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags);
6240         }
6241         return ret;
6242 }
6243 int dhd_os_check_if_up(void *dhdp)
6244 {
6245         dhd_pub_t *pub = (dhd_pub_t *)dhdp;
6246
6247         if (!pub)
6248                 return 0;
6249         return pub->up;
6250 }
6251 /* function to collect firmware, chip id and chip version info */
6252 void dhd_set_version_info(dhd_pub_t *dhdp, char *fw)
6253 {
6254         int i;
6255
6256         i = snprintf(info_string, sizeof(info_string),
6257                 "  Driver: %s\n  Firmware: %s ", EPI_VERSION_STR, fw);
6258
6259         if (!dhdp)
6260                 return;
6261
6262         i = snprintf(&info_string[i], sizeof(info_string) - i,
6263                 "\n  Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp),
6264                 dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp));
6265 }
6266 int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd)
6267 {
6268         int ifidx;
6269         int ret = 0;
6270         dhd_info_t *dhd = NULL;
6271
6272         if (!net || !netdev_priv(net)) {
6273                 DHD_ERROR(("%s invalid parameter\n", __FUNCTION__));
6274                 return -EINVAL;
6275         }
6276
6277         dhd = *(dhd_info_t **)netdev_priv(net);
6278         if (!dhd)
6279                 return -EINVAL;
6280
6281         ifidx = dhd_net2idx(dhd, net);
6282         if (ifidx == DHD_BAD_IF) {
6283                 DHD_ERROR(("%s bad ifidx\n", __FUNCTION__));
6284                 return -ENODEV;
6285         }
6286
6287         DHD_OS_WAKE_LOCK(&dhd->pub);
6288         ret = dhd_wl_ioctl(&dhd->pub, ifidx, ioc, ioc->buf, ioc->len);
6289         dhd_check_hang(net, &dhd->pub, ret);
6290         DHD_OS_WAKE_UNLOCK(&dhd->pub);
6291
6292         return ret;
6293 }
6294
6295 bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret)
6296 {
6297         struct net_device *net;
6298
6299         net = dhd_idx2net(dhdp, ifidx);
6300         return dhd_check_hang(net, dhdp, ret);
6301 }
6302
6303
6304 #ifdef PROP_TXSTATUS
6305 extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid,
6306         uint8 iftype, uint8* ea);
6307 extern int dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits);
6308
6309 int dhd_wlfc_interface_event(struct dhd_info *dhd,
6310         ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
6311 {
6312         if (dhd->pub.wlfc_state == NULL)
6313                 return BCME_OK;
6314
6315         return dhd_wlfc_interface_entry_update(dhd->pub.wlfc_state, action, ifid, iftype, ea);
6316 }
6317
6318 int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data)
6319 {
6320         if (dhd->pub.wlfc_state == NULL)
6321                 return BCME_OK;
6322
6323         return dhd_wlfc_FIFOcreditmap_update(dhd->pub.wlfc_state, event_data);
6324 }
6325
6326 int dhd_wlfc_event(struct dhd_info *dhd)
6327 {
6328         return dhd_wlfc_enable(&dhd->pub);
6329 }
6330
6331 void dhd_wlfc_plat_enable(void *dhd)
6332 {
6333         return;
6334 }
6335
6336 void dhd_wlfc_plat_deinit(void *dhd)
6337 {
6338         return;
6339 }
6340
6341 bool dhd_wlfc_skip_fc(void)
6342 {
6343
6344 #ifdef WL_CFG80211
6345         extern struct wl_priv *wlcfg_drv_priv;
6346
6347         /* enable flow control in vsdb mode */
6348         return !(wlcfg_drv_priv && wlcfg_drv_priv->vsdb_mode);
6349 #else
6350         return TRUE; /* skip flow control */