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