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