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