input: touch: raydium:fix warnings during shutdown
[linux-3.10.git] / drivers / net / wireless / bcmdhd / wl_android.c
1 /*
2  * Linux cfg80211 driver - Android related functions
3  *
4  * Copyright (C) 1999-2015, Broadcom Corporation
5  * 
6  * Portions contributed by Nvidia
7  * Copyright (C) 2015 NVIDIA Corporation. All rights reserved.
8  *
9  *      Unless you and Broadcom execute a separate written software license
10  * agreement governing use of this software, this software is licensed to you
11  * under the terms of the GNU General Public License version 2 (the "GPL"),
12  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
13  * following added to such license:
14  * 
15  *      As a special exception, the copyright holders of this software give you
16  * permission to link this software with independent modules, and to copy and
17  * distribute the resulting executable under terms of your choice, provided that
18  * you also meet, for each linked independent module, the terms and conditions of
19  * the license of that module.  An independent module is a module which is not
20  * derived from this software.  The special exception does not apply to any
21  * modifications of the software.
22  * 
23  *      Notwithstanding the above, under no circumstances may you combine this
24  * software in any way with any other Broadcom software provided under a license
25  * other than the GPL, without Broadcom's express prior written consent.
26  *
27  * $Id: wl_android.c 531815 2015-02-04 05:53:26Z $
28  */
29
30 #include <linux/module.h>
31 #include <linux/netdevice.h>
32 #include <net/netlink.h>
33 #ifdef CONFIG_COMPAT
34 #include <linux/compat.h>
35 #endif
36
37 #include <wl_android.h>
38 #include <wldev_common.h>
39 #include <wlioctl.h>
40 #include <bcmutils.h>
41 #include <linux_osl.h>
42 #include <dhd_dbg.h>
43 #include <dngl_stats.h>
44 #include <dhd.h>
45 #include <proto/bcmip.h>
46 #include "dynamic.h"
47 #ifdef PNO_SUPPORT
48 #include <dhd_pno.h>
49 #endif
50 #ifdef BCMSDIO
51 #include <bcmsdbus.h>
52 #endif
53 #ifdef WL_CFG80211
54 #include <wl_cfg80211.h>
55 #endif
56 #ifdef WL_NAN
57 #include <wl_cfgnan.h>
58 #endif /* WL_NAN */
59
60 #ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
61 #include "dhd_custom_sysfs_tegra.h"
62 #endif
63
64 #include <proto/802.1d.h>
65 /*
66  * Android private command strings, PLEASE define new private commands here
67  * so they can be updated easily in the future (if needed)
68  */
69
70 #define CMD_START               "START"
71 #define CMD_STOP                "STOP"
72 #define CMD_SCAN_ACTIVE         "SCAN-ACTIVE"
73 #define CMD_SCAN_PASSIVE        "SCAN-PASSIVE"
74 #define CMD_RSSI                "RSSI"
75 #define CMD_LINKSPEED           "LINKSPEED"
76 #ifdef PKT_FILTER_SUPPORT
77 #define CMD_RXFILTER_START      "RXFILTER-START"
78 #define CMD_RXFILTER_STOP       "RXFILTER-STOP"
79 #define CMD_RXFILTER_ADD        "RXFILTER-ADD"
80 #define CMD_RXFILTER_REMOVE     "RXFILTER-REMOVE"
81 #define CMD_PKT_FILTER_MODE             "PKT_FILTER_MODE"
82 #define CMD_PKT_FILTER_PORTS    "PKT_FILTER_PORTS"
83 #endif /* PKT_FILTER_SUPPORT */
84 #define CMD_BTCOEXSCAN_START    "BTCOEXSCAN-START"
85 #define CMD_BTCOEXSCAN_STOP     "BTCOEXSCAN-STOP"
86 #define CMD_BTCOEXMODE          "BTCOEXMODE"
87 #define CMD_SETSUSPENDOPT       "SETSUSPENDOPT"
88 #define CMD_SETSUSPENDMODE      "SETSUSPENDMODE"
89 #define CMD_P2P_DEV_ADDR        "P2P_DEV_ADDR"
90 #define CMD_SETFWPATH           "SETFWPATH"
91 #define CMD_SETBAND             "SETBAND"
92 #define CMD_GETBAND             "GETBAND"
93 #define CMD_COUNTRY             "COUNTRY"
94 #define CMD_NV_COUNTRY         "NV_COUNTRY"
95 #define CMD_P2P_SET_NOA         "P2P_SET_NOA"
96 #if !defined WL_ENABLE_P2P_IF
97 #define CMD_P2P_GET_NOA                 "P2P_GET_NOA"
98 #endif /* WL_ENABLE_P2P_IF */
99 #define CMD_P2P_SD_OFFLOAD              "P2P_SD_"
100 #define CMD_P2P_SET_PS          "P2P_SET_PS"
101 #define CMD_SET_AP_WPS_P2P_IE           "SET_AP_WPS_P2P_IE"
102 #define CMD_SETROAMMODE         "SETROAMMODE"
103 #define CMD_SETIBSSBEACONOUIDATA        "SETIBSSBEACONOUIDATA"
104 #define CMD_MIRACAST            "MIRACAST"
105 #define CMD_NAN         "NAN_"
106
107 #if defined(WL_SUPPORT_AUTO_CHANNEL)
108 #define CMD_GET_BEST_CHANNELS   "GET_BEST_CHANNELS"
109 #endif /* WL_SUPPORT_AUTO_CHANNEL */
110
111 #define CMD_SETMIRACAST         "SETMIRACAST"
112 #define CMD_ASSOCRESPIE         "ASSOCRESPIE"
113 #define CMD_RXRATESTATS         "RXRATESTATS"
114 #define CMD_MAXLINKSPEED        "MAXLINKSPEED"
115 #define CMD_AMPDU_SEND_DELBA    "AMPDU_SEND_DELBA"
116
117 /* Commands for iovar settings */
118 #define CMD_SETIOVAR           "SETIOVAR"
119 #define CMD_GETIOVAR           "GETIOVAR"
120
121 #define CMD_80211_MODE    "MODE"  /* 802.11 mode a/b/g/n/ac */
122 #define CMD_CHANSPEC      "CHANSPEC"
123 #define CMD_DATARATE      "DATARATE"
124 #define CMD_ASSOC_CLIENTS "ASSOCLIST"
125 #define CMD_SET_CSA       "SETCSA"
126 #define CMD_KEEP_ALIVE    "KEEPALIVE"
127 #define CMD_MKEEP_ALIVE   "MKEEP_ALIVE"
128
129 #ifdef PNO_SUPPORT
130 #define CMD_PNOSSIDCLR_SET      "PNOSSIDCLR"
131 #define CMD_PNOSETUP_SET        "PNOSETUP "
132 #define CMD_PNOENABLE_SET       "PNOFORCE"
133 #define CMD_PNODEBUG_SET        "PNODEBUG"
134 #define CMD_WLS_BATCHING        "WLS_BATCHING"
135 #endif /* PNO_SUPPORT */
136
137 #define CMD_OKC_SET_PMK         "SET_PMK"
138 #define CMD_OKC_ENABLE          "OKC_ENABLE"
139
140 #define CMD_HAPD_MAC_FILTER     "HAPD_MAC_FILTER"
141
142 #define CMD_AUTOSLEEP           "AUTOSLEEP"
143
144
145 #define CMD_ROAM_OFFLOAD                        "SETROAMOFFLOAD"
146 #define CMD_ROAM_OFFLOAD_APLIST         "SETROAMOFFLAPLIST"
147 #define CMD_GET_LINK_STATUS                     "GETLINKSTATUS"
148
149 #ifdef P2PRESP_WFDIE_SRC
150 #define CMD_P2P_SET_WFDIE_RESP      "P2P_SET_WFDIE_RESP"
151 #define CMD_P2P_GET_WFDIE_RESP      "P2P_GET_WFDIE_RESP"
152 #endif /* P2PRESP_WFDIE_SRC */
153
154 #ifdef WLWFDS
155 #define CMD_ADD_WFDS_HASH       "ADD_WFDS_HASH"
156 #define CMD_DEL_WFDS_HASH       "DEL_WFDS_HASH"
157 #endif /* WLWFDS */
158 /* related with CMD_GET_LINK_STATUS */
159 #define WL_ANDROID_LINK_VHT                                     0x01
160 #define WL_ANDROID_LINK_MIMO                                    0x02
161 #define WL_ANDROID_LINK_AP_VHT_SUPPORT          0x04
162 #define WL_ANDROID_LINK_AP_MIMO_SUPPORT 0x08
163
164 /* miracast related definition */
165 #define MIRACAST_MODE_OFF       0
166 #define MIRACAST_MODE_SOURCE    1
167 #define MIRACAST_MODE_SINK      2
168
169 #ifndef MIRACAST_AMPDU_SIZE
170 #define MIRACAST_AMPDU_SIZE     8
171 #endif
172
173 #ifndef MIRACAST_MCHAN_ALGO
174 #define MIRACAST_MCHAN_ALGO     1
175 #endif
176
177 #ifndef MIRACAST_MCHAN_BW
178 #define MIRACAST_MCHAN_BW       25
179 #endif
180
181 #ifdef CONNECTION_STATISTICS
182 #define CMD_GET_CONNECTION_STATS        "GET_CONNECTION_STATS"
183
184 struct connection_stats {
185         u32 txframe;
186         u32 txbyte;
187         u32 txerror;
188         u32 rxframe;
189         u32 rxbyte;
190         u32 txfail;
191         u32 txretry;
192         u32 txretrie;
193         u32 txrts;
194         u32 txnocts;
195         u32 txexptime;
196         u32 txrate;
197         u8      chan_idle;
198 };
199 #endif /* CONNECTION_STATISTICS */
200
201 static LIST_HEAD(miracast_resume_list);
202 static u8 miracast_cur_mode;
203
204 struct io_cfg {
205         s8 *iovar;
206         s32 param;
207         u32 ioctl;
208         void *arg;
209         u32 len;
210         struct list_head list;
211 };
212
213 typedef struct _android_wifi_priv_cmd {
214         char *buf;
215         int used_len;
216         int total_len;
217 } android_wifi_priv_cmd;
218
219 #ifdef CONFIG_COMPAT
220 typedef struct _compat_android_wifi_priv_cmd {
221         compat_caddr_t buf;
222         int used_len;
223         int total_len;
224 } compat_android_wifi_priv_cmd;
225 #endif /* CONFIG_COMPAT */
226
227 #if defined(BCMFW_ROAM_ENABLE)
228 #define CMD_SET_ROAMPREF        "SET_ROAMPREF"
229
230 #define MAX_NUM_SUITES          10
231 #define WIDTH_AKM_SUITE         8
232 #define JOIN_PREF_RSSI_LEN              0x02
233 #define JOIN_PREF_RSSI_SIZE             4       /* RSSI pref header size in bytes */
234 #define JOIN_PREF_WPA_HDR_SIZE          4 /* WPA pref header size in bytes */
235 #define JOIN_PREF_WPA_TUPLE_SIZE        12      /* Tuple size in bytes */
236 #define JOIN_PREF_MAX_WPA_TUPLES        16
237 #define MAX_BUF_SIZE            (JOIN_PREF_RSSI_SIZE + JOIN_PREF_WPA_HDR_SIZE + \
238                                            (JOIN_PREF_WPA_TUPLE_SIZE * JOIN_PREF_MAX_WPA_TUPLES))
239 #endif /* BCMFW_ROAM_ENABLE */
240
241
242 /**
243  * Extern function declarations (TODO: move them to dhd_linux.h)
244  */
245 int dhd_net_bus_devreset(struct net_device *dev, uint8 flag);
246 int dhd_dev_init_ioctl(struct net_device *dev);
247 #ifdef WL_CFG80211
248 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr);
249 int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, dhd_pub_t *dhd, char *command);
250 #else
251 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr)
252 { return 0; }
253 int wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len)
254 { return 0; }
255 int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len)
256 { return 0; }
257 int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len)
258 { return 0; }
259 #endif /* WK_CFG80211 */
260
261 extern int dhd_set_slpauto_mode(struct net_device *dev, s32 val);
262
263
264 #ifdef ENABLE_4335BT_WAR
265 extern int bcm_bt_lock(int cookie);
266 extern void bcm_bt_unlock(int cookie);
267 static int lock_cookie_wifi = 'W' | 'i'<<8 | 'F'<<16 | 'i'<<24; /* cookie is "WiFi" */
268 #endif /* ENABLE_4335BT_WAR */
269
270 extern bool ap_fw_loaded;
271 #if defined(CUSTOMER_HW2)
272 extern char iface_name[IFNAMSIZ];
273 #endif 
274
275 /**
276  * Local (static) functions and variables
277  */
278
279 /* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first
280  * time (only) in dhd_open, subsequential wifi on will be handled by
281  * wl_android_wifi_on
282  */
283 static int g_wifi_on = TRUE;
284
285 /**
286  * Local (static) function definitions
287  */
288
289 #ifdef WLWFDS
290 static int wl_android_set_wfds_hash(
291         struct net_device *dev, char *command, int total_len, bool enable)
292 {
293         int error = 0;
294         wl_p2p_wfds_hash_t *wfds_hash = NULL;
295         char *smbuf = NULL;
296         smbuf = kmalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
297
298         if (smbuf == NULL) {
299                 DHD_ERROR(("%s: failed to allocated memory %d bytes\n",
300                         __FUNCTION__, WLC_IOCTL_MAXLEN));
301                         goto set_wfds_hash_out;
302         }
303
304         if (enable) {
305                 wfds_hash = (wl_p2p_wfds_hash_t *)(command + strlen(CMD_ADD_WFDS_HASH) + 1);
306                 error = wldev_iovar_setbuf(dev, "p2p_add_wfds_hash", wfds_hash,
307                         sizeof(wl_p2p_wfds_hash_t), smbuf, WLC_IOCTL_MAXLEN, NULL);
308         }
309         else {
310                 wfds_hash = (wl_p2p_wfds_hash_t *)(command + strlen(CMD_DEL_WFDS_HASH) + 1);
311                 error = wldev_iovar_setbuf(dev, "p2p_del_wfds_hash", wfds_hash,
312                         sizeof(wl_p2p_wfds_hash_t), smbuf, WLC_IOCTL_MAXLEN, NULL);
313         }
314
315         if (error) {
316                 DHD_ERROR(("%s: failed to %s, error=%d\n", __FUNCTION__, command, error));
317         }
318
319 set_wfds_hash_out:
320         if (smbuf)
321                 kfree(smbuf);
322
323         if (error)
324                 return -1;
325         else
326                 return 0;
327 }
328 #endif /* WLWFDS */
329
330 static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len)
331 {
332         int link_speed;
333         int bytes_written;
334         int error;
335
336         error = wldev_get_link_speed(net, &link_speed);
337         if (error)
338                 return -1;
339
340         /* Convert Kbps to Android Mbps */
341         link_speed = link_speed / 1000;
342         bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed);
343         DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command));
344         return bytes_written;
345 }
346
347 static int wl_android_get_rssi(struct net_device *net, char *command, int total_len)
348 {
349         wlc_ssid_t ssid = {0};
350         int bytes_written = 0;
351         int error = 0;
352         scb_val_t scbval;
353         char *delim = NULL;
354
355
356         delim = strchr(command, ' ');
357         /* For Ap mode rssi command would be
358          * driver rssi <sta_mac_addr>
359          * for STA/GC mode
360          * driver rssi
361          */
362         if (delim) {
363                 /* Ap/GO mode
364                  * driver rssi <sta_mac_addr>
365                  */
366                 DHD_TRACE(("%s: cmd:%s\n", __FUNCTION__, delim));
367                 /* skip space from delim after finding char */
368                 delim++;
369                 if (!(bcm_ether_atoe((delim), &scbval.ea))) {
370
371                         DHD_ERROR(("%s:address err\n", __FUNCTION__));
372                         return -1;
373                 }
374                 scbval.val = htod32(0);
375                 DHD_TRACE(("%s: address:"MACDBG, __FUNCTION__, MAC2STRDBG(scbval.ea.octet)));
376         }
377         else {
378                 memset(&scbval, 0, sizeof(scb_val_t));
379         }
380
381         error = wldev_get_rssi(net, &scbval);
382         if (error)
383                 return -1;
384
385         error = wldev_get_ssid(net, &ssid);
386         if (error)
387                 return -1;
388         if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) {
389                 DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__));
390         } else {
391                 memcpy(command, ssid.SSID, ssid.SSID_len);
392                 bytes_written = ssid.SSID_len;
393         }
394         bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", scbval.val);
395         DHD_TRACE(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written));
396         return bytes_written;
397 }
398
399 static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len)
400 {
401         int suspend_flag;
402         int ret_now;
403         int ret = 0;
404
405                 suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0';
406
407                 if (suspend_flag != 0)
408                         suspend_flag = 1;
409                 ret_now = net_os_set_suspend_disable(dev, suspend_flag);
410
411                 if (ret_now != suspend_flag) {
412                         if (!(ret = net_os_set_suspend(dev, ret_now, 1)))
413                                 DHD_INFO(("%s: Suspend Flag %d -> %d\n",
414                                         __FUNCTION__, ret_now, suspend_flag));
415                         else
416                                 DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret));
417                 }
418         return ret;
419 }
420
421 static int wl_android_set_suspendmode(struct net_device *dev, char *command, int total_len)
422 {
423         int ret = 0;
424
425 #if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND)
426         int suspend_flag;
427
428         suspend_flag = *(command + strlen(CMD_SETSUSPENDMODE) + 1) - '0';
429         if (suspend_flag != 0)
430                 suspend_flag = 1;
431
432         if (!(ret = net_os_set_suspend(dev, suspend_flag, 0)))
433                 DHD_INFO(("%s: Suspend Mode %d\n", __FUNCTION__, suspend_flag));
434         else
435                 DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret));
436 #endif
437
438         return ret;
439 }
440
441 int wl_android_get_80211_mode(struct net_device *dev, char *command, int total_len)
442 {
443         uint8 mode[4];
444         int  error = 0;
445         int bytes_written = 0;
446
447         error = wldev_get_mode(dev, mode);
448         if (error)
449                 return -1;
450
451         DHD_INFO(("%s: mode:%s\n", __FUNCTION__, mode));
452         bytes_written = snprintf(command, total_len, "%s %s", CMD_80211_MODE, mode);
453         DHD_INFO(("%s: command:%s EXIT\n", __FUNCTION__, command));
454         return bytes_written;
455
456 }
457
458 extern chanspec_t
459 wl_chspec_driver_to_host(chanspec_t chanspec);
460 int wl_android_get_chanspec(struct net_device *dev, char *command, int total_len)
461 {
462         int error = 0;
463         int bytes_written = 0;
464         int chsp = {0};
465         uint16 band = 0;
466         uint16 bw = 0;
467         uint16 channel = 0;
468         u32 sb = 0;
469         chanspec_t chanspec;
470
471         /* command is
472          * driver chanspec
473          */
474         error = wldev_iovar_getint(dev, "chanspec", &chsp);
475         if (error)
476                 return -1;
477
478         chanspec = wl_chspec_driver_to_host(chsp);
479         DHD_INFO(("%s:return value of chanspec:%x\n", __FUNCTION__, chanspec));
480
481         channel = chanspec & WL_CHANSPEC_CHAN_MASK;
482         band = chanspec & WL_CHANSPEC_BAND_MASK;
483         bw = chanspec & WL_CHANSPEC_BW_MASK;
484
485         DHD_INFO(("%s:channel:%d band:%d bandwidth:%d\n", __FUNCTION__, channel, band, bw));
486
487         if (bw == WL_CHANSPEC_BW_80)
488                 bw = WL_CH_BANDWIDTH_80MHZ;
489         else if (bw == WL_CHANSPEC_BW_40)
490                 bw = WL_CH_BANDWIDTH_40MHZ;
491         else if (bw == WL_CHANSPEC_BW_20)
492                 bw = WL_CH_BANDWIDTH_20MHZ;
493         else
494                 bw = WL_CH_BANDWIDTH_20MHZ;
495
496         if (bw == WL_CH_BANDWIDTH_40MHZ) {
497                 if (CHSPEC_SB_UPPER(chanspec)) {
498                         channel += CH_10MHZ_APART;
499                 } else {
500                         channel -= CH_10MHZ_APART;
501                 }
502         }
503         else if (bw == WL_CH_BANDWIDTH_80MHZ) {
504                 sb = chanspec & WL_CHANSPEC_CTL_SB_MASK;
505                 if (sb == WL_CHANSPEC_CTL_SB_LL) {
506                         channel -= (CH_10MHZ_APART + CH_20MHZ_APART);
507                 } else if (sb == WL_CHANSPEC_CTL_SB_LU) {
508                         channel -= CH_10MHZ_APART;
509                 } else if (sb == WL_CHANSPEC_CTL_SB_UL) {
510                         channel += CH_10MHZ_APART;
511                 } else {
512                         /* WL_CHANSPEC_CTL_SB_UU */
513                         channel += (CH_10MHZ_APART + CH_20MHZ_APART);
514                 }
515         }
516         bytes_written = snprintf(command, total_len, "%s channel %d band %s bw %d", CMD_CHANSPEC,
517                 channel, band == WL_CHANSPEC_BAND_5G ? "5G":"2G", bw);
518
519         DHD_INFO(("%s: command:%s EXIT\n", __FUNCTION__, command));
520         return bytes_written;
521
522 }
523
524 /* returns current datarate datarate returned from firmware are in 500kbps */
525 int wl_android_get_datarate(struct net_device *dev, char *command, int total_len)
526 {
527         int  error = 0;
528         int datarate = 0;
529         int bytes_written = 0;
530
531         error = wldev_get_datarate(dev, &datarate);
532         if (error)
533                 return -1;
534
535         DHD_INFO(("%s:datarate:%d\n", __FUNCTION__, datarate));
536
537         bytes_written = snprintf(command, total_len, "%s %d", CMD_DATARATE, (datarate/2));
538         return bytes_written;
539 }
540 int wl_android_get_assoclist(struct net_device *dev, char *command, int total_len)
541 {
542         int  error = 0;
543         int bytes_written = 0;
544         uint i;
545         char mac_buf[MAX_NUM_OF_ASSOCLIST *
546                 sizeof(struct ether_addr) + sizeof(uint)] = {0};
547         struct maclist *assoc_maclist = (struct maclist *)mac_buf;
548
549         DHD_TRACE(("%s: ENTER\n", __FUNCTION__));
550
551         assoc_maclist->count = htod32(MAX_NUM_OF_ASSOCLIST);
552
553         error = wldev_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, sizeof(mac_buf), false);
554         if (error)
555                 return -1;
556
557         assoc_maclist->count = dtoh32(assoc_maclist->count);
558         bytes_written = snprintf(command, total_len, "%s listcount: %d Stations:",
559                 CMD_ASSOC_CLIENTS, assoc_maclist->count);
560
561         for (i = 0; i < assoc_maclist->count; i++) {
562                 bytes_written += snprintf(command + bytes_written, total_len, " " MACDBG,
563                         MAC2STRDBG(assoc_maclist->ea[i].octet));
564         }
565         return bytes_written;
566
567 }
568 extern chanspec_t
569 wl_chspec_host_to_driver(chanspec_t chanspec);
570 static int wl_android_set_csa(struct net_device *dev, char *command, int total_len)
571 {
572         int error = 0;
573         char smbuf[WLC_IOCTL_SMLEN];
574         wl_chan_switch_t csa_arg;
575         char buf[32];
576         u32 chnsp = 0;
577         int err = 0;
578
579         DHD_INFO(("%s: command:%s\n", __FUNCTION__, command));
580
581         command = (command + strlen(CMD_SET_CSA));
582         /* Order is mode, count channel */
583         if (!*++command) {
584                 DHD_ERROR(("%s:error missing arguments\n", __FUNCTION__));
585                 return -1;
586         }
587         csa_arg.mode = bcm_atoi(command);
588         if (csa_arg.mode != 0 && csa_arg.mode != 1) {
589                 DHD_ERROR(("Invalid mode\n"));
590                 return -1;
591         } if (!*++command) {
592                 DHD_ERROR(("%s:error missing count\n", __FUNCTION__));
593                 return -1;
594         }
595         command++;
596         csa_arg.count = bcm_atoi(command);
597         if (!*++command) {
598                 DHD_ERROR(("%s:error missing channel\n", __FUNCTION__));
599                 return -1;
600         }
601         csa_arg.reg = 0;
602         csa_arg.chspec = 0;
603         command += 2;
604         if (sizeof(buf) > strlen(command))
605                 bcm_strncpy_s(buf, sizeof(buf), command, strlen(command));
606         else {
607                 DHD_ERROR(("%s:command is not valid\n", __FUNCTION__));
608                 return -1;
609         }
610         chnsp = wf_chspec_aton(buf);
611         if (chnsp == 0) {
612                 DHD_ERROR(("%s:chsp is not correct\n", __FUNCTION__));
613                 return -1;
614         }
615         chnsp = wl_chspec_host_to_driver(chnsp);
616         csa_arg.chspec = chnsp;
617
618         if (chnsp & WL_CHANSPEC_BAND_5G) {
619                 u32 chanspec = chnsp;
620                 err = wldev_iovar_getint(dev, "per_chan_info", &chanspec);
621                 if (!err) {
622                         if ((chanspec & WL_CHAN_RADAR) || (chanspec & WL_CHAN_PASSIVE)) {
623                                 DHD_ERROR(("Channel is radar sensitive\n"));
624                                 return -1;
625                         }
626                         if (chanspec == 0) {
627                                 DHD_ERROR(("Invalid hw channel\n"));
628                                 return -1;
629                         }
630                 } else  {
631                         DHD_ERROR(("does not support per_chan_info\n"));
632                         return -1;
633                 }
634                 DHD_INFO(("non radar sensitivity\n"));
635         }
636         error = wldev_iovar_setbuf(dev, "csa", &csa_arg, sizeof(csa_arg),
637                 smbuf, sizeof(smbuf), NULL);
638         if (error) {
639                 DHD_ERROR(("%s:set csa failed:%d\n", __FUNCTION__, error));
640                 return -1;
641         }
642         return 0;
643 }
644 static int wl_android_get_band(struct net_device *dev, char *command, int total_len)
645 {
646         uint band;
647         int bytes_written;
648         int error;
649
650         error = wldev_get_band(dev, &band);
651         if (error)
652                 return -1;
653         bytes_written = snprintf(command, total_len, "Band %d", band);
654         return bytes_written;
655 }
656
657
658 #ifdef PNO_SUPPORT
659 #define PNO_PARAM_SIZE 50
660 #define VALUE_SIZE 50
661 #define LIMIT_STR_FMT  ("%50s %50s")
662 static int
663 wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len)
664 {
665         int err = BCME_OK;
666         uint i, tokens;
667         char *pos, *pos2, *token, *token2, *delim;
668         char param[PNO_PARAM_SIZE+1], value[VALUE_SIZE+1];
669         struct dhd_pno_batch_params batch_params;
670         DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
671         if (total_len < strlen(CMD_WLS_BATCHING)) {
672                 DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len));
673                 err = BCME_ERROR;
674                 goto exit;
675         }
676         pos = command + strlen(CMD_WLS_BATCHING) + 1;
677         memset(&batch_params, 0, sizeof(struct dhd_pno_batch_params));
678
679         if (!strncmp(pos, PNO_BATCHING_SET, strlen(PNO_BATCHING_SET))) {
680                 pos += strlen(PNO_BATCHING_SET) + 1;
681                 while ((token = strsep(&pos, PNO_PARAMS_DELIMETER)) != NULL) {
682                         memset(param, 0, sizeof(param));
683                         memset(value, 0, sizeof(value));
684                         if (token == NULL || !*token)
685                                 break;
686                         if (*token == '\0')
687                                 continue;
688                         delim = strchr(token, PNO_PARAM_VALUE_DELLIMETER);
689                         if (delim != NULL)
690                                 *delim = ' ';
691
692                         tokens = sscanf(token, LIMIT_STR_FMT, param, value);
693                         if (!strncmp(param, PNO_PARAM_SCANFREQ, strlen(PNO_PARAM_SCANFREQ))) {
694                                 batch_params.scan_fr = simple_strtol(value, NULL, 0);
695                                 DHD_PNO(("scan_freq : %d\n", batch_params.scan_fr));
696                         } else if (!strncmp(param, PNO_PARAM_BESTN, strlen(PNO_PARAM_BESTN))) {
697                                 batch_params.bestn = simple_strtol(value, NULL, 0);
698                                 DHD_PNO(("bestn : %d\n", batch_params.bestn));
699                         } else if (!strncmp(param, PNO_PARAM_MSCAN, strlen(PNO_PARAM_MSCAN))) {
700                                 batch_params.mscan = simple_strtol(value, NULL, 0);
701                                 DHD_PNO(("mscan : %d\n", batch_params.mscan));
702                         } else if (!strncmp(param, PNO_PARAM_CHANNEL, strlen(PNO_PARAM_CHANNEL))) {
703                                 i = 0;
704                                 pos2 = value;
705                                 tokens = sscanf(value, "<%s>", value);
706                                 if (tokens != 1) {
707                                         err = BCME_ERROR;
708                                         DHD_ERROR(("%s : invalid format for channel"
709                                         " <> params\n", __FUNCTION__));
710                                         goto exit;
711                                 }
712                                         while ((token2 = strsep(&pos2,
713                                         PNO_PARAM_CHANNEL_DELIMETER)) != NULL) {
714                                         if (token2 == NULL || !*token2)
715                                                 break;
716                                         if (*token2 == '\0')
717                                                 continue;
718                                         if (*token2 == 'A' || *token2 == 'B') {
719                                                 batch_params.band = (*token2 == 'A')?
720                                                         WLC_BAND_5G : WLC_BAND_2G;
721                                                 DHD_PNO(("band : %s\n",
722                                                         (*token2 == 'A')? "A" : "B"));
723                                         } else {
724                                                 batch_params.chan_list[i++] =
725                                                 simple_strtol(token2, NULL, 0);
726                                                 batch_params.nchan++;
727                                                 DHD_PNO(("channel :%d\n",
728                                                 batch_params.chan_list[i-1]));
729                                         }
730                                  }
731                         } else if (!strncmp(param, PNO_PARAM_RTT, strlen(PNO_PARAM_RTT))) {
732                                 batch_params.rtt = simple_strtol(value, NULL, 0);
733                                 DHD_PNO(("rtt : %d\n", batch_params.rtt));
734                         } else {
735                                 DHD_ERROR(("%s : unknown param: %s\n", __FUNCTION__, param));
736                                 err = BCME_ERROR;
737                                 goto exit;
738                         }
739                 }
740                 err = dhd_dev_pno_set_for_batch(dev, &batch_params);
741                 if (err < 0) {
742                         DHD_ERROR(("failed to configure batch scan\n"));
743                 } else {
744                         memset(command, 0, total_len);
745                         err = sprintf(command, "%d", err);
746                 }
747         } else if (!strncmp(pos, PNO_BATCHING_GET, strlen(PNO_BATCHING_GET))) {
748                 err = dhd_dev_pno_get_for_batch(dev, command, total_len);
749                 if (err < 0) {
750                         DHD_ERROR(("failed to getting batching results\n"));
751                 } else {
752                         err = strlen(command);
753                 }
754         } else if (!strncmp(pos, PNO_BATCHING_STOP, strlen(PNO_BATCHING_STOP))) {
755                 err = dhd_dev_pno_stop_for_batch(dev);
756                 if (err < 0) {
757                         DHD_ERROR(("failed to stop batching scan\n"));
758                 } else {
759                         memset(command, 0, total_len);
760                         err = sprintf(command, "OK");
761                 }
762         } else {
763                 DHD_ERROR(("%s : unknown command\n", __FUNCTION__));
764                 err = BCME_ERROR;
765                 goto exit;
766         }
767 exit:
768         return err;
769 }
770 #ifndef WL_SCHED_SCAN
771 static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len)
772 {
773         wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT];
774         int res = -1;
775         int nssid = 0;
776         cmd_tlv_t *cmd_tlv_temp;
777         char *str_ptr;
778         int tlv_size_left;
779         int pno_time = 0;
780         int pno_repeat = 0;
781         int pno_freq_expo_max = 0;
782
783 #ifdef PNO_SET_DEBUG
784         int i;
785         char pno_in_example[] = {
786                 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ',
787                 'S', '1', '2', '0',
788                 'S',
789                 0x05,
790                 'd', 'l', 'i', 'n', 'k',
791                 'S',
792                 0x04,
793                 'G', 'O', 'O', 'G',
794                 'T',
795                 '0', 'B',
796                 'R',
797                 '2',
798                 'M',
799                 '2',
800                 0x00
801                 };
802 #endif /* PNO_SET_DEBUG */
803         DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
804
805         if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) {
806                 DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len));
807                 goto exit_proc;
808         }
809 #ifdef PNO_SET_DEBUG
810         memcpy(command, pno_in_example, sizeof(pno_in_example));
811         total_len = sizeof(pno_in_example);
812 #endif
813         str_ptr = command + strlen(CMD_PNOSETUP_SET);
814         tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET);
815
816         cmd_tlv_temp = (cmd_tlv_t *)str_ptr;
817         memset(ssids_local, 0, sizeof(ssids_local));
818
819         if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) &&
820                 (cmd_tlv_temp->version == PNO_TLV_VERSION) &&
821                 (cmd_tlv_temp->subtype == PNO_TLV_SUBTYPE_LEGACY_PNO)) {
822
823                 str_ptr += sizeof(cmd_tlv_t);
824                 tlv_size_left -= sizeof(cmd_tlv_t);
825
826                 if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local,
827                         MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) {
828                         DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid));
829                         goto exit_proc;
830                 } else {
831                         if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) {
832                                 DHD_ERROR(("%s scan duration corrupted field size %d\n",
833                                         __FUNCTION__, tlv_size_left));
834                                 goto exit_proc;
835                         }
836                         str_ptr++;
837                         pno_time = simple_strtoul(str_ptr, &str_ptr, 16);
838                         DHD_PNO(("%s: pno_time=%d\n", __FUNCTION__, pno_time));
839
840                         if (str_ptr[0] != 0) {
841                                 if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) {
842                                         DHD_ERROR(("%s pno repeat : corrupted field\n",
843                                                 __FUNCTION__));
844                                         goto exit_proc;
845                                 }
846                                 str_ptr++;
847                                 pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16);
848                                 DHD_PNO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat));
849                                 if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) {
850                                         DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n",
851                                                 __FUNCTION__));
852                                         goto exit_proc;
853                                 }
854                                 str_ptr++;
855                                 pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16);
856                                 DHD_PNO(("%s: pno_freq_expo_max=%d\n",
857                                         __FUNCTION__, pno_freq_expo_max));
858                         }
859                 }
860         } else {
861                 DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__));
862                 goto exit_proc;
863         }
864
865         res = dhd_dev_pno_set_for_ssid(dev, ssids_local, nssid, pno_time, pno_repeat,
866                 pno_freq_expo_max, NULL, 0);
867 exit_proc:
868         return res;
869 }
870 #endif /* !WL_SCHED_SCAN */
871 #endif /* PNO_SUPPORT  */
872
873 static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len)
874 {
875         int ret;
876         int bytes_written = 0;
877
878         ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command);
879         if (ret)
880                 return 0;
881         bytes_written = sizeof(struct ether_addr);
882         return bytes_written;
883 }
884
885
886 int
887 wl_android_set_ap_mac_list(struct net_device *dev, int macmode, struct maclist *maclist)
888 {
889         int i, j, match;
890         int ret = 0;
891         char mac_buf[MAX_NUM_OF_ASSOCLIST *
892                 sizeof(struct ether_addr) + sizeof(uint)] = {0};
893         struct maclist *assoc_maclist = (struct maclist *)mac_buf;
894
895         /* set filtering mode */
896         if ((ret = wldev_ioctl(dev, WLC_SET_MACMODE, &macmode, sizeof(macmode), true)) != 0) {
897                 DHD_ERROR(("%s : WLC_SET_MACMODE error=%d\n", __FUNCTION__, ret));
898                 return ret;
899         }
900         if (macmode != MACLIST_MODE_DISABLED) {
901                 /* set the MAC filter list */
902                 if ((ret = wldev_ioctl(dev, WLC_SET_MACLIST, maclist,
903                         sizeof(int) + sizeof(struct ether_addr) * maclist->count, true)) != 0) {
904                         DHD_ERROR(("%s : WLC_SET_MACLIST error=%d\n", __FUNCTION__, ret));
905                         return ret;
906                 }
907                 /* get the current list of associated STAs */
908                 assoc_maclist->count = MAX_NUM_OF_ASSOCLIST;
909                 if ((ret = wldev_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist,
910                         sizeof(mac_buf), false)) != 0) {
911                         DHD_ERROR(("%s : WLC_GET_ASSOCLIST error=%d\n", __FUNCTION__, ret));
912                         return ret;
913                 }
914                 /* do we have any STA associated?  */
915                 if (assoc_maclist->count) {
916                         /* iterate each associated STA */
917                         for (i = 0; i < assoc_maclist->count; i++) {
918                                 match = 0;
919                                 /* compare with each entry */
920                                 for (j = 0; j < maclist->count; j++) {
921                                         DHD_INFO(("%s : associated="MACDBG " list="MACDBG "\n",
922                                         __FUNCTION__, MAC2STRDBG(assoc_maclist->ea[i].octet),
923                                         MAC2STRDBG(maclist->ea[j].octet)));
924                                         if (memcmp(assoc_maclist->ea[i].octet,
925                                                 maclist->ea[j].octet, ETHER_ADDR_LEN) == 0) {
926                                                 match = 1;
927                                                 break;
928                                         }
929                                 }
930                                 /* do conditional deauth */
931                                 /*   "if not in the allow list" or "if in the deny list" */
932                                 if ((macmode == MACLIST_MODE_ALLOW && !match) ||
933                                         (macmode == MACLIST_MODE_DENY && match)) {
934                                         scb_val_t scbval;
935
936                                         scbval.val = htod32(1);
937                                         memcpy(&scbval.ea, &assoc_maclist->ea[i],
938                                                 ETHER_ADDR_LEN);
939                                         if ((ret = wldev_ioctl(dev,
940                                                 WLC_SCB_DEAUTHENTICATE_FOR_REASON,
941                                                 &scbval, sizeof(scb_val_t), true)) != 0)
942                                                 DHD_ERROR(("%s WLC_SCB_DEAUTHENTICATE error=%d\n",
943                                                         __FUNCTION__, ret));
944                                 }
945                         }
946                 }
947         }
948         return ret;
949 }
950
951 /*
952  * HAPD_MAC_FILTER mac_mode mac_cnt mac_addr1 mac_addr2
953  *
954  */
955 static int
956 wl_android_set_mac_address_filter(struct net_device *dev, const char* str)
957 {
958         int i;
959         int ret = 0;
960         int macnum = 0;
961         int macmode = MACLIST_MODE_DISABLED;
962         struct maclist *list;
963         char eabuf[ETHER_ADDR_STR_LEN];
964         char *token;
965
966         /* string should look like below (macmode/macnum/maclist) */
967         /*   1 2 00:11:22:33:44:55 00:11:22:33:44:ff  */
968
969         /* get the MAC filter mode */
970         token = strsep((char**)&str, " ");
971         if (!token) {
972                 return -1;
973         }
974         macmode = bcm_atoi(token);
975
976         if (macmode < MACLIST_MODE_DISABLED || macmode > MACLIST_MODE_ALLOW) {
977                 DHD_ERROR(("%s : invalid macmode %d\n", __FUNCTION__, macmode));
978                 return -1;
979         }
980
981         token = strsep((char**)&str, " ");
982         if (!token) {
983                 return -1;
984         }
985         macnum = bcm_atoi(token);
986         if (macnum < 0 || macnum > MAX_NUM_MAC_FILT) {
987                 DHD_ERROR(("%s : invalid number of MAC address entries %d\n",
988                         __FUNCTION__, macnum));
989                 return -1;
990         }
991         /* allocate memory for the MAC list */
992         list = (struct maclist*)kmalloc(sizeof(int) +
993                 sizeof(struct ether_addr) * macnum, GFP_KERNEL);
994         if (!list) {
995                 DHD_ERROR(("%s : failed to allocate memory\n", __FUNCTION__));
996                 return -1;
997         }
998         /* prepare the MAC list */
999         list->count = htod32(macnum);
1000         bzero((char *)eabuf, ETHER_ADDR_STR_LEN);
1001         for (i = 0; i < list->count; i++) {
1002                 strncpy(eabuf, strsep((char**)&str, " "), ETHER_ADDR_STR_LEN - 1);
1003                 if (!(ret = bcm_ether_atoe(eabuf, &list->ea[i]))) {
1004                         DHD_ERROR(("%s : mac parsing err index=%d, addr=%s\n",
1005                                 __FUNCTION__, i, eabuf));
1006                         list->count--;
1007                         break;
1008                 }
1009                 DHD_INFO(("%s : %d/%d MACADDR=%s", __FUNCTION__, i, list->count, eabuf));
1010         }
1011         /* set the list */
1012         if ((ret = wl_android_set_ap_mac_list(dev, macmode, list)) != 0)
1013                 DHD_ERROR(("%s : Setting MAC list failed error=%d\n", __FUNCTION__, ret));
1014
1015         kfree(list);
1016
1017         return 0;
1018 }
1019
1020 /**
1021  * Global function definitions (declared in wl_android.h)
1022  */
1023
1024 int wl_android_wifi_on(struct net_device *dev)
1025 {
1026         int ret = 0;
1027 #ifdef CONFIG_MACH_UNIVERSAL5433
1028         int retry;
1029         /* Do not retry old revision Helsinki Prime */
1030         if (!check_rev()) {
1031                 retry = 1;
1032         } else {
1033                 retry = POWERUP_MAX_RETRY;
1034         }
1035 #else
1036         int retry = POWERUP_MAX_RETRY;
1037 #endif /* CONFIG_MACH_UNIVERSAL5433 */
1038
1039         DHD_ERROR(("%s in\n", __FUNCTION__));
1040         if (!dev) {
1041                 DHD_ERROR(("%s: dev is null\n", __FUNCTION__));
1042                 return -EINVAL;
1043         }
1044
1045         dhd_net_if_lock(dev);
1046         if (!g_wifi_on) {
1047                 do {
1048                         dhd_net_wifi_platform_set_power(dev, TRUE, WIFI_TURNON_DELAY);
1049 #ifdef BCMSDIO
1050                         ret = dhd_net_bus_resume(dev, 0);
1051 #endif /* BCMSDIO */
1052 #ifdef BCMPCIE
1053                         ret = dhd_net_bus_devreset(dev, FALSE);
1054 #endif /* BCMPCIE */
1055                         if (ret == 0)
1056                                 break;
1057                         DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n",
1058                                 retry));
1059 #ifdef BCMPCIE
1060                         dhd_net_bus_devreset(dev, TRUE);
1061 #endif /* BCMPCIE */
1062                         dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY);
1063                 } while (retry-- > 0);
1064                 if (ret != 0) {
1065                         DHD_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n"));
1066                         goto exit;
1067                 }
1068 #ifdef BCMSDIO
1069                 ret = dhd_net_bus_devreset(dev, FALSE);
1070                 dhd_net_bus_resume(dev, 1);
1071 #endif /* BCMSDIO */
1072
1073 #ifndef BCMPCIE
1074                 if (!ret) {
1075                         if (dhd_dev_init_ioctl(dev) < 0)
1076                                 ret = -EFAULT;
1077                 }
1078 #endif /* !BCMPCIE */
1079                 g_wifi_on = TRUE;
1080         }
1081
1082 #ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
1083         if (!ret)
1084                 tegra_sysfs_on();
1085 #endif
1086
1087 exit:
1088         dhd_net_if_unlock(dev);
1089
1090         return ret;
1091 }
1092
1093 int wl_android_wifi_off(struct net_device *dev)
1094 {
1095         int ret = 0;
1096
1097         DHD_ERROR(("%s in\n", __FUNCTION__));
1098         if (!dev) {
1099                 DHD_TRACE(("%s: dev is null\n", __FUNCTION__));
1100                 return -EINVAL;
1101         }
1102 #ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
1103         tegra_sysfs_off();
1104 #endif
1105
1106         dhd_net_if_lock(dev);
1107         if (g_wifi_on) {
1108 #if defined(BCMSDIO) || defined(BCMPCIE)
1109                 ret = dhd_net_bus_devreset(dev, TRUE);
1110 #ifdef BCMSDIO
1111                 dhd_net_bus_suspend(dev);
1112 #endif /* BCMSDIO */
1113 #endif /* BCMSDIO || BCMPCIE */
1114                 dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY);
1115                 g_wifi_on = FALSE;
1116         }
1117         dhd_net_if_unlock(dev);
1118
1119         return ret;
1120 }
1121
1122 static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len)
1123 {
1124         if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN)
1125                 return -1;
1126         return dhd_net_set_fw_path(net, command + strlen(CMD_SETFWPATH) + 1);
1127 }
1128
1129 #ifdef CONNECTION_STATISTICS
1130 static int
1131 wl_chanim_stats(struct net_device *dev, u8 *chan_idle)
1132 {
1133         int err;
1134         wl_chanim_stats_t *list;
1135         /* Parameter _and_ returned buffer of chanim_stats. */
1136         wl_chanim_stats_t param;
1137         u8 result[WLC_IOCTL_SMLEN];
1138         chanim_stats_t *stats;
1139
1140         memset(&param, 0, sizeof(param));
1141         memset(result, 0, sizeof(result));
1142
1143         param.buflen = htod32(sizeof(wl_chanim_stats_t));
1144         param.count = htod32(WL_CHANIM_COUNT_ONE);
1145
1146         if ((err = wldev_iovar_getbuf(dev, "chanim_stats", (char*)&param, sizeof(wl_chanim_stats_t),
1147                 (char*)result, sizeof(result), 0)) < 0) {
1148                 WL_ERR(("Failed to get chanim results %d \n", err));
1149                 return err;
1150         }
1151
1152         list = (wl_chanim_stats_t*)result;
1153
1154         list->buflen = dtoh32(list->buflen);
1155         list->version = dtoh32(list->version);
1156         list->count = dtoh32(list->count);
1157
1158         if (list->buflen == 0) {
1159                 list->version = 0;
1160                 list->count = 0;
1161         } else if (list->version != WL_CHANIM_STATS_VERSION) {
1162                 WL_ERR(("Sorry, firmware has wl_chanim_stats version %d "
1163                         "but driver supports only version %d.\n",
1164                                 list->version, WL_CHANIM_STATS_VERSION));
1165                 list->buflen = 0;
1166                 list->count = 0;
1167         }
1168
1169         stats = list->stats;
1170         stats->glitchcnt = dtoh32(stats->glitchcnt);
1171         stats->badplcp = dtoh32(stats->badplcp);
1172         stats->chanspec = dtoh16(stats->chanspec);
1173         stats->timestamp = dtoh32(stats->timestamp);
1174         stats->chan_idle = dtoh32(stats->chan_idle);
1175
1176         WL_INFORM(("chanspec: 0x%4x glitch: %d badplcp: %d idle: %d timestamp: %d\n",
1177                 stats->chanspec, stats->glitchcnt, stats->badplcp, stats->chan_idle,
1178                 stats->timestamp));
1179
1180         *chan_idle = stats->chan_idle;
1181
1182         return (err);
1183 }
1184
1185 static int
1186 wl_android_get_connection_stats(struct net_device *dev, char *command, int total_len)
1187 {
1188         wl_cnt_t* cnt = NULL;
1189 #ifndef DISABLE_IF_COUNTERS
1190         wl_if_stats_t* if_stats = NULL;
1191 #endif /* DISABLE_IF_COUNTERS */
1192
1193         int link_speed = 0;
1194         struct connection_stats *output;
1195         unsigned int bufsize = 0;
1196         int bytes_written = -1;
1197         int ret = 0;
1198
1199         WL_INFORM(("%s: enter Get Connection Stats\n", __FUNCTION__));
1200
1201         if (total_len <= 0) {
1202                 WL_ERR(("%s: invalid buffer size %d\n", __FUNCTION__, total_len));
1203                 goto error;
1204         }
1205
1206         bufsize = total_len;
1207         if (bufsize < sizeof(struct connection_stats)) {
1208                 WL_ERR(("%s: not enough buffer size, provided=%u, requires=%zu\n",
1209                         __FUNCTION__, bufsize,
1210                         sizeof(struct connection_stats)));
1211                 goto error;
1212         }
1213
1214         output = (struct connection_stats *)command;
1215
1216 #ifndef DISABLE_IF_COUNTERS
1217         if ((if_stats = kmalloc(sizeof(*if_stats), GFP_KERNEL)) == NULL) {
1218                 WL_ERR(("%s(%d): kmalloc failed\n", __FUNCTION__, __LINE__));
1219                 goto error;
1220         }
1221         memset(if_stats, 0, sizeof(*if_stats));
1222
1223         ret = wldev_iovar_getbuf(dev, "if_counters", NULL, 0,
1224                 (char *)if_stats, sizeof(*if_stats), NULL);
1225         if (ret) {
1226                 WL_ERR(("%s: if_counters not supported ret=%d\n",
1227                         __FUNCTION__, ret));
1228
1229                 /* In case if_stats IOVAR is not supported, get information from counters. */
1230 #endif /* DISABLE_IF_COUNTERS */
1231                 if ((cnt = kmalloc(sizeof(*cnt), GFP_KERNEL)) == NULL) {
1232                         WL_ERR(("%s(%d): kmalloc failed\n", __FUNCTION__, __LINE__));
1233                         goto error;
1234                 }
1235                 memset(cnt, 0, sizeof(*cnt));
1236
1237                 ret = wldev_iovar_getbuf(dev, "counters", NULL, 0,
1238                         (char *)cnt, sizeof(wl_cnt_t), NULL);
1239                 if (ret) {
1240                         WL_ERR(("%s: wldev_iovar_getbuf() failed, ret=%d\n",
1241                                 __FUNCTION__, ret));
1242                         goto error;
1243                 }
1244
1245                 if (dtoh16(cnt->version) > WL_CNT_T_VERSION) {
1246                         WL_ERR(("%s: incorrect version of wl_cnt_t, expected=%u got=%u\n",
1247                                 __FUNCTION__,  WL_CNT_T_VERSION, cnt->version));
1248                         goto error;
1249                 }
1250
1251                 output->txframe   = dtoh32(cnt->txframe);
1252                 output->txbyte    = dtoh32(cnt->txbyte);
1253                 output->txerror   = dtoh32(cnt->txerror);
1254                 output->rxframe   = dtoh32(cnt->rxframe);
1255                 output->rxbyte    = dtoh32(cnt->rxbyte);
1256                 output->txfail    = dtoh32(cnt->txfail);
1257                 output->txretry   = dtoh32(cnt->txretry);
1258                 output->txretrie  = dtoh32(cnt->txretrie);
1259                 output->txrts     = dtoh32(cnt->txrts);
1260                 output->txnocts   = dtoh32(cnt->txnocts);
1261                 output->txexptime = dtoh32(cnt->txexptime);
1262 #ifndef DISABLE_IF_COUNTERS
1263         } else {
1264                 /* Populate from if_stats. */
1265                 if (dtoh16(if_stats->version) > WL_IF_STATS_T_VERSION) {
1266                         WL_ERR(("%s: incorrect version of wl_if_stats_t, expected=%u got=%u\n",
1267                                 __FUNCTION__,  WL_IF_STATS_T_VERSION, if_stats->version));
1268                         goto error;
1269                 }
1270
1271                 output->txframe   = (uint32)dtoh64(if_stats->txframe);
1272                 output->txbyte    = (uint32)dtoh64(if_stats->txbyte);
1273                 output->txerror   = (uint32)dtoh64(if_stats->txerror);
1274                 output->rxframe   = (uint32)dtoh64(if_stats->rxframe);
1275                 output->rxbyte    = (uint32)dtoh64(if_stats->rxbyte);
1276                 output->txfail    = (uint32)dtoh64(if_stats->txfail);
1277                 output->txretry   = (uint32)dtoh64(if_stats->txretry);
1278                 output->txretrie  = (uint32)dtoh64(if_stats->txretrie);
1279                 /* Unavailable */
1280                 output->txrts     = 0;
1281                 output->txnocts   = 0;
1282                 output->txexptime = 0;
1283         }
1284 #endif /* DISABLE_IF_COUNTERS */
1285
1286         /* link_speed is in kbps */
1287         ret = wldev_get_link_speed(dev, &link_speed);
1288         if (ret || link_speed < 0) {
1289                 WL_ERR(("%s: wldev_get_link_speed() failed, ret=%d, speed=%d\n",
1290                         __FUNCTION__, ret, link_speed));
1291                 goto error;
1292         }
1293
1294         output->txrate    = link_speed;
1295
1296         /* Channel idle ratio. */
1297         if (wl_chanim_stats(dev, &(output->chan_idle)) < 0) {
1298                 output->chan_idle = 0;
1299         };
1300
1301         bytes_written = sizeof(struct connection_stats);
1302
1303 error:
1304 #ifndef DISABLE_IF_COUNTERS
1305         if (if_stats) {
1306                 kfree(if_stats);
1307         }
1308 #endif /* DISABLE_IF_COUNTERS */
1309         if (cnt) {
1310                 kfree(cnt);
1311         }
1312
1313         return bytes_written;
1314 }
1315 #endif /* CONNECTION_STATISTICS */
1316
1317 static int
1318 wl_android_set_pmk(struct net_device *dev, char *command, int total_len)
1319 {
1320         uchar pmk[33];
1321         int error = 0;
1322         char smbuf[WLC_IOCTL_SMLEN];
1323 #ifdef OKC_DEBUG
1324         int i = 0;
1325 #endif
1326
1327         bzero(pmk, sizeof(pmk));
1328         memcpy((char *)pmk, command + strlen("SET_PMK "), 32);
1329         error = wldev_iovar_setbuf(dev, "okc_info_pmk", pmk, 32, smbuf, sizeof(smbuf), NULL);
1330         if (error) {
1331                 DHD_ERROR(("Failed to set PMK for OKC, error = %d\n", error));
1332         }
1333 #ifdef OKC_DEBUG
1334         DHD_ERROR(("PMK is "));
1335         for (i = 0; i < 32; i++)
1336                 DHD_ERROR(("%02X ", pmk[i]));
1337
1338         DHD_ERROR(("\n"));
1339 #endif
1340         return error;
1341 }
1342
1343 static int
1344 wl_android_okc_enable(struct net_device *dev, char *command, int total_len)
1345 {
1346         int error = 0;
1347         char okc_enable = 0;
1348
1349         okc_enable = command[strlen(CMD_OKC_ENABLE) + 1] - '0';
1350         error = wldev_iovar_setint(dev, "okc_enable", okc_enable);
1351         if (error) {
1352                 DHD_ERROR(("Failed to %s OKC, error = %d\n",
1353                         okc_enable ? "enable" : "disable", error));
1354         }
1355
1356         wldev_iovar_setint(dev, "ccx_enable", 0);
1357
1358         return error;
1359 }
1360
1361
1362
1363 int wl_android_set_roam_mode(struct net_device *dev, char *command, int total_len)
1364 {
1365         int error = 0;
1366         int mode = 0;
1367
1368         if (sscanf(command, "%*s %d", &mode) != 1) {
1369                 DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__));
1370                 return -1;
1371         }
1372
1373         error = wldev_iovar_setint(dev, "roam_off", mode);
1374         if (error) {
1375                 DHD_ERROR(("%s: Failed to set roaming Mode %d, error = %d\n",
1376                 __FUNCTION__, mode, error));
1377                 return -1;
1378         }
1379         else
1380                 DHD_ERROR(("%s: succeeded to set roaming Mode %d, error = %d\n",
1381                 __FUNCTION__, mode, error));
1382         return 0;
1383 }
1384
1385 int wl_android_set_slpauto(struct net_device *dev, char *command, int total_len)
1386 {
1387         int error = 0;
1388         char slpauto_enable = 0;
1389
1390         slpauto_enable = command[strlen(CMD_AUTOSLEEP) + 1] - '0';
1391         error = dhd_set_slpauto_mode(dev, slpauto_enable);
1392         if (error) {
1393                 DHD_ERROR(("Failed to %s auto sleep, error = %d\n",
1394                         slpauto_enable ? "enable" : "disable", error));
1395         }
1396
1397         return error;
1398 }
1399
1400 int wl_android_set_ibss_beacon_ouidata(struct net_device *dev, char *command, int total_len)
1401 {
1402         char ie_buf[VNDR_IE_MAX_LEN];
1403         char *ioctl_buf = NULL;
1404         char hex[] = "XX";
1405         char *pcmd = NULL;
1406         int ielen = 0, datalen = 0, idx = 0, tot_len = 0;
1407         vndr_ie_setbuf_t *vndr_ie = NULL;
1408         s32 iecount;
1409         uint32 pktflag;
1410         u16 kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
1411         s32 err = BCME_OK;
1412
1413         /* Check the VSIE (Vendor Specific IE) which was added.
1414          *  If exist then send IOVAR to delete it
1415          */
1416         if (wl_cfg80211_ibss_vsie_delete(dev) != BCME_OK) {
1417                 return -EINVAL;
1418         }
1419
1420         pcmd = command + strlen(CMD_SETIBSSBEACONOUIDATA) + 1;
1421         for (idx = 0; idx < DOT11_OUI_LEN; idx++) {
1422                 hex[0] = *pcmd++;
1423                 hex[1] = *pcmd++;
1424                 ie_buf[idx] =  (uint8)simple_strtoul(hex, NULL, 16);
1425         }
1426         pcmd++;
1427         while ((*pcmd != '\0') && (idx < VNDR_IE_MAX_LEN)) {
1428                 hex[0] = *pcmd++;
1429                 hex[1] = *pcmd++;
1430                 ie_buf[idx++] =  (uint8)simple_strtoul(hex, NULL, 16);
1431                 datalen++;
1432         }
1433         tot_len = sizeof(vndr_ie_setbuf_t) + (datalen - 1);
1434         vndr_ie = (vndr_ie_setbuf_t *) kzalloc(tot_len, kflags);
1435         if (!vndr_ie) {
1436                 WL_ERR(("IE memory alloc failed\n"));
1437                 return -ENOMEM;
1438         }
1439         /* Copy the vndr_ie SET command ("add"/"del") to the buffer */
1440         strncpy(vndr_ie->cmd, "add", VNDR_IE_CMD_LEN - 1);
1441         vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
1442
1443         /* Set the IE count - the buffer contains only 1 IE */
1444         iecount = htod32(1);
1445         memcpy((void *)&vndr_ie->vndr_ie_buffer.iecount, &iecount, sizeof(s32));
1446
1447         /* Set packet flag to indicate that BEACON's will contain this IE */
1448         pktflag = htod32(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG);
1449         memcpy((void *)&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
1450                 sizeof(u32));
1451         /* Set the IE ID */
1452         vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = (uchar) DOT11_MNG_PROPR_ID;
1453
1454         memcpy(&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui, &ie_buf,
1455                 DOT11_OUI_LEN);
1456         memcpy(&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data,
1457                 &ie_buf[DOT11_OUI_LEN], datalen);
1458
1459         ielen = DOT11_OUI_LEN + datalen;
1460         vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = (uchar) ielen;
1461
1462         ioctl_buf = kmalloc(WLC_IOCTL_MEDLEN, GFP_KERNEL);
1463         if (!ioctl_buf) {
1464                 WL_ERR(("ioctl memory alloc failed\n"));
1465                 if (vndr_ie) {
1466                         kfree(vndr_ie);
1467                 }
1468                 return -ENOMEM;
1469         }
1470         memset(ioctl_buf, 0, WLC_IOCTL_MEDLEN); /* init the buffer */
1471         err = wldev_iovar_setbuf(dev, "ie", vndr_ie, tot_len, ioctl_buf, WLC_IOCTL_MEDLEN, NULL);
1472
1473
1474         if (err != BCME_OK) {
1475                 err = -EINVAL;
1476                 if (vndr_ie) {
1477                         kfree(vndr_ie);
1478                 }
1479         }
1480         else {
1481                 /* do NOT free 'vndr_ie' for the next process */
1482                 wl_cfg80211_ibss_vsie_set_buffer(vndr_ie, tot_len);
1483         }
1484
1485         if (ioctl_buf) {
1486                 kfree(ioctl_buf);
1487         }
1488
1489         return err;
1490 }
1491
1492 #if defined(BCMFW_ROAM_ENABLE)
1493 static int
1494 wl_android_set_roampref(struct net_device *dev, char *command, int total_len)
1495 {
1496         int error = 0;
1497         char smbuf[WLC_IOCTL_SMLEN];
1498         uint8 buf[MAX_BUF_SIZE];
1499         uint8 *pref = buf;
1500         char *pcmd;
1501         int num_ucipher_suites = 0;
1502         int num_akm_suites = 0;
1503         wpa_suite_t ucipher_suites[MAX_NUM_SUITES];
1504         wpa_suite_t akm_suites[MAX_NUM_SUITES];
1505         int num_tuples = 0;
1506         int total_bytes = 0;
1507         int total_len_left;
1508         int i, j;
1509         char hex[] = "XX";
1510
1511         pcmd = command + strlen(CMD_SET_ROAMPREF) + 1;
1512         total_len_left = total_len - strlen(CMD_SET_ROAMPREF) + 1;
1513
1514         num_akm_suites = simple_strtoul(pcmd, NULL, 16);
1515         /* Increment for number of AKM suites field + space */
1516         pcmd += 3;
1517         total_len_left -= 3;
1518
1519         /* check to make sure pcmd does not overrun */
1520         if (total_len_left < (num_akm_suites * WIDTH_AKM_SUITE))
1521                 return -1;
1522
1523         memset(buf, 0, sizeof(buf));
1524         memset(akm_suites, 0, sizeof(akm_suites));
1525         memset(ucipher_suites, 0, sizeof(ucipher_suites));
1526
1527         /* Save the AKM suites passed in the command */
1528         for (i = 0; i < num_akm_suites; i++) {
1529                 /* Store the MSB first, as required by join_pref */
1530                 for (j = 0; j < 4; j++) {
1531                         hex[0] = *pcmd++;
1532                         hex[1] = *pcmd++;
1533                         buf[j] = (uint8)simple_strtoul(hex, NULL, 16);
1534                 }
1535                 memcpy((uint8 *)&akm_suites[i], buf, sizeof(uint32));
1536         }
1537
1538         total_len_left -= (num_akm_suites * WIDTH_AKM_SUITE);
1539         num_ucipher_suites = simple_strtoul(pcmd, NULL, 16);
1540         /* Increment for number of cipher suites field + space */
1541         pcmd += 3;
1542         total_len_left -= 3;
1543
1544         if (total_len_left < (num_ucipher_suites * WIDTH_AKM_SUITE))
1545                 return -1;
1546
1547         /* Save the cipher suites passed in the command */
1548         for (i = 0; i < num_ucipher_suites; i++) {
1549                 /* Store the MSB first, as required by join_pref */
1550                 for (j = 0; j < 4; j++) {
1551                         hex[0] = *pcmd++;
1552                         hex[1] = *pcmd++;
1553                         buf[j] = (uint8)simple_strtoul(hex, NULL, 16);
1554                 }
1555                 memcpy((uint8 *)&ucipher_suites[i], buf, sizeof(uint32));
1556         }
1557
1558         /* Join preference for RSSI
1559          * Type   : 1 byte (0x01)
1560          * Length : 1 byte (0x02)
1561          * Value  : 2 bytes     (reserved)
1562          */
1563         *pref++ = WL_JOIN_PREF_RSSI;
1564         *pref++ = JOIN_PREF_RSSI_LEN;
1565         *pref++ = 0;
1566         *pref++ = 0;
1567
1568         /* Join preference for WPA
1569          * Type   : 1 byte (0x02)
1570          * Length : 1 byte (not used)
1571          * Value  : (variable length)
1572          *              reserved: 1 byte
1573          *      count   : 1 byte (no of tuples)
1574          *              Tuple1  : 12 bytes
1575          *                      akm[4]
1576          *                      ucipher[4]
1577          *                      mcipher[4]
1578          *              Tuple2  : 12 bytes
1579          *              Tuplen  : 12 bytes
1580          */
1581         num_tuples = num_akm_suites * num_ucipher_suites;
1582         if (num_tuples != 0) {
1583                 if (num_tuples <= JOIN_PREF_MAX_WPA_TUPLES) {
1584                         *pref++ = WL_JOIN_PREF_WPA;
1585                         *pref++ = 0;
1586                         *pref++ = 0;
1587                         *pref++ = (uint8)num_tuples;
1588                         total_bytes = JOIN_PREF_RSSI_SIZE + JOIN_PREF_WPA_HDR_SIZE +
1589                                 (JOIN_PREF_WPA_TUPLE_SIZE * num_tuples);
1590                 } else {
1591                         DHD_ERROR(("%s: Too many wpa configs for join_pref \n", __FUNCTION__));
1592                         return -1;
1593                 }
1594         } else {
1595                 /* No WPA config, configure only RSSI preference */
1596                 total_bytes = JOIN_PREF_RSSI_SIZE;
1597         }
1598
1599         /* akm-ucipher-mcipher tuples in the format required for join_pref */
1600         for (i = 0; i < num_ucipher_suites; i++) {
1601                 for (j = 0; j < num_akm_suites; j++) {
1602                         memcpy(pref, (uint8 *)&akm_suites[j], WPA_SUITE_LEN);
1603                         pref += WPA_SUITE_LEN;
1604                         memcpy(pref, (uint8 *)&ucipher_suites[i], WPA_SUITE_LEN);
1605                         pref += WPA_SUITE_LEN;
1606                         /* Set to 0 to match any available multicast cipher */
1607                         memset(pref, 0, WPA_SUITE_LEN);
1608                         pref += WPA_SUITE_LEN;
1609                 }
1610         }
1611
1612         prhex("join pref", (uint8 *)buf, total_bytes);
1613         error = wldev_iovar_setbuf(dev, "join_pref", buf, total_bytes, smbuf, sizeof(smbuf), NULL);
1614         if (error) {
1615                 DHD_ERROR(("Failed to set join_pref, error = %d\n", error));
1616         }
1617         return error;
1618 }
1619 #endif /* defined(BCMFW_ROAM_ENABLE */
1620
1621 static int
1622 wl_android_iolist_add(struct net_device *dev, struct list_head *head, struct io_cfg *config)
1623 {
1624         struct io_cfg *resume_cfg;
1625         s32 ret;
1626
1627         resume_cfg = kzalloc(sizeof(struct io_cfg), GFP_KERNEL);
1628         if (!resume_cfg)
1629                 return -ENOMEM;
1630
1631         if (config->iovar) {
1632                 ret = wldev_iovar_getint(dev, config->iovar, &resume_cfg->param);
1633                 if (ret) {
1634                         DHD_ERROR(("%s: Failed to get current %s value\n",
1635                                 __FUNCTION__, config->iovar));
1636                         goto error;
1637                 }
1638
1639                 ret = wldev_iovar_setint(dev, config->iovar, config->param);
1640                 if (ret) {
1641                         DHD_ERROR(("%s: Failed to set %s to %d\n", __FUNCTION__,
1642                                 config->iovar, config->param));
1643                         goto error;
1644                 }
1645
1646                 resume_cfg->iovar = config->iovar;
1647         } else {
1648                 resume_cfg->arg = kzalloc(config->len, GFP_KERNEL);
1649                 if (!resume_cfg->arg) {
1650                         ret = -ENOMEM;
1651                         goto error;
1652                 }
1653                 ret = wldev_ioctl(dev, config->ioctl, resume_cfg->arg, config->len, false);
1654                 if (ret) {
1655                         DHD_ERROR(("%s: Failed to get ioctl %d\n", __FUNCTION__,
1656                                 config->ioctl));
1657                         goto error;
1658                 }
1659                 ret = wldev_ioctl(dev, config->ioctl + 1, config->arg, config->len, true);
1660                 if (ret) {
1661                         DHD_ERROR(("%s: Failed to set %s to %d\n", __FUNCTION__,
1662                                 config->iovar, config->param));
1663                         goto error;
1664                 }
1665                 if (config->ioctl + 1 == WLC_SET_PM)
1666                         wl_cfg80211_update_power_mode(dev);
1667                 resume_cfg->ioctl = config->ioctl;
1668                 resume_cfg->len = config->len;
1669         }
1670
1671         list_add(&resume_cfg->list, head);
1672
1673         return 0;
1674 error:
1675         kfree(resume_cfg->arg);
1676         kfree(resume_cfg);
1677         return ret;
1678 }
1679
1680 static void
1681 wl_android_iolist_resume(struct net_device *dev, struct list_head *head)
1682 {
1683         struct io_cfg *config;
1684         struct list_head *cur, *q;
1685         s32 ret = 0;
1686
1687         list_for_each_safe(cur, q, head) {
1688                 config = list_entry(cur, struct io_cfg, list);
1689                 if (config->iovar) {
1690                         if (!ret)
1691                                 ret = wldev_iovar_setint(dev, config->iovar,
1692                                         config->param);
1693                 } else {
1694                         if (!ret)
1695                                 ret = wldev_ioctl(dev, config->ioctl + 1,
1696                                         config->arg, config->len, true);
1697                         if (config->ioctl + 1 == WLC_SET_PM)
1698                                 wl_cfg80211_update_power_mode(dev);
1699                         kfree(config->arg);
1700                 }
1701                 list_del(cur);
1702                 kfree(config);
1703         }
1704 }
1705
1706 static int
1707 wl_android_set_miracast(struct net_device *dev, char *command, int total_len)
1708 {
1709         int mode, val;
1710         int ret = 0;
1711         struct io_cfg config;
1712
1713         if (sscanf(command, "%*s %d", &mode) != 1) {
1714                 DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__));
1715                 return -1;
1716         }
1717
1718         DHD_INFO(("%s: enter miracast mode %d\n", __FUNCTION__, mode));
1719
1720         if (miracast_cur_mode == mode) {
1721                 return 0;
1722         }
1723
1724         wl_android_iolist_resume(dev, &miracast_resume_list);
1725         miracast_cur_mode = MIRACAST_MODE_OFF;
1726
1727         switch (mode) {
1728         case MIRACAST_MODE_SOURCE:
1729                 /* setting mchan_algo to platform specific value */
1730                 config.iovar = "mchan_algo";
1731
1732                 ret = wldev_ioctl(dev, WLC_GET_BCNPRD, &val, sizeof(int), false);
1733                 if (!ret && val > 100) {
1734                         config.param = 0;
1735                         DHD_ERROR(("%s: Connected station's beacon interval: "
1736                                 "%d and set mchan_algo to %d \n",
1737                                 __FUNCTION__, val, config.param));
1738                 } else {
1739                         config.param = MIRACAST_MCHAN_ALGO;
1740                 }
1741                 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
1742                 if (ret) {
1743                         goto resume;
1744                 }
1745
1746                 /* setting mchan_bw to platform specific value */
1747                 config.iovar = "mchan_bw";
1748                 config.param = MIRACAST_MCHAN_BW;
1749                 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
1750                 if (ret) {
1751                         goto resume;
1752                 }
1753
1754                 /* setting apmdu to platform specific value */
1755                 config.iovar = "ampdu_mpdu";
1756                 config.param = MIRACAST_AMPDU_SIZE;
1757                 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
1758                 if (ret) {
1759                         goto resume;
1760                 }
1761                 /* FALLTROUGH */
1762                 /* Source mode shares most configurations with sink mode.
1763                  * Fall through here to avoid code duplication
1764                  */
1765         case MIRACAST_MODE_SINK:
1766                 /* disable internal roaming */
1767                 config.iovar = "roam_off";
1768                 config.param = 1;
1769                 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
1770                 if (ret) {
1771                         goto resume;
1772                 }
1773
1774                 /* tunr off pm */
1775                 ret = wldev_ioctl(dev, WLC_GET_PM, &val, sizeof(val), false);
1776                 if (ret) {
1777                         goto resume;
1778                 }
1779
1780                 if (val != PM_OFF) {
1781                         val = PM_OFF;
1782                         config.iovar = NULL;
1783                         config.ioctl = WLC_GET_PM;
1784                         config.arg = &val;
1785                         config.len = sizeof(int);
1786                         ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
1787                         if (ret) {
1788                                 goto resume;
1789                         }
1790                 }
1791                 break;
1792         case MIRACAST_MODE_OFF:
1793         default:
1794                 break;
1795         }
1796         miracast_cur_mode = mode;
1797
1798         return 0;
1799
1800 resume:
1801         DHD_ERROR(("%s: turnoff miracast mode because of err%d\n", __FUNCTION__, ret));
1802         wl_android_iolist_resume(dev, &miracast_resume_list);
1803         return ret;
1804 }
1805
1806 int
1807 wl_android_ampdu_send_delba(struct net_device *dev, char *command)
1808 {
1809         int error = 0;
1810         struct ampdu_ea_tid aet;
1811         char smbuf[WLC_IOCTL_SMLEN];
1812
1813         DHD_INFO(("%s, %s\n", __FUNCTION__, command));
1814
1815         /* get tid */
1816         aet.tid = bcm_strtoul(command, &command, 10);
1817         if (aet.tid > MAXPRIO) {
1818                 DHD_ERROR(("%s: error: invalid tid %d\n", __FUNCTION__, aet.tid));
1819                 return BCME_BADARG;
1820         }
1821         command++;
1822
1823         /* get mac address, here 17 is strlen("xx:xx:xx:xx:xx:xx") */
1824         if ((strlen(command) < 17) || !bcm_ether_atoe(command, &aet.ea)) {
1825                 DHD_ERROR(("%s: error: invalid MAC address %s\n", __FUNCTION__, command));
1826                 return BCME_BADARG;
1827         }
1828         /* skip mac address */
1829         command += strlen("xx:xx:xx:xx:xx:xx") + 1;
1830
1831         /* get initiator */
1832         aet.initiator = bcm_strtoul(command, &command, 10);
1833         if (aet.initiator > 1) {
1834                 DHD_ERROR(("%s: error: inivalid initiator %d\n", __FUNCTION__, aet.initiator));
1835                 return BCME_BADARG;
1836         }
1837
1838         error = wldev_iovar_setbuf(dev, "ampdu_send_delba", &aet, sizeof(aet),
1839                 smbuf, sizeof(smbuf), NULL);
1840         if (error) {
1841                 DHD_ERROR(("%s: Failed to send delba, error = %d\n", __FUNCTION__, error));
1842         }
1843
1844         return error;
1845 }
1846
1847 static int wl_android_mkeep_alive(struct net_device *dev, char *command, int total_len)
1848 {
1849         char *pcmd = command;
1850         char *str = NULL;
1851         wl_mkeep_alive_pkt_t *mkeep_alive_pkt = NULL;
1852         char *ioctl_buf = NULL;
1853         u16 kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
1854         s32 err = BCME_OK;
1855         uint32 len;
1856         char *endptr;
1857         int i = 0;
1858
1859         len = sizeof(wl_mkeep_alive_pkt_t);
1860         mkeep_alive_pkt = (wl_mkeep_alive_pkt_t *)kzalloc(len, kflags);
1861         if (!mkeep_alive_pkt) {
1862                 WL_ERR(("%s: mkeep_alive pkt alloc failed\n", __func__));
1863                 return -ENOMEM;
1864         }
1865
1866         ioctl_buf = kzalloc(WLC_IOCTL_MEDLEN, GFP_KERNEL);
1867         if (!ioctl_buf) {
1868                 WL_ERR(("ioctl memory alloc failed\n"));
1869                 if (mkeep_alive_pkt) {
1870                         kfree(mkeep_alive_pkt);
1871                 }
1872                 return -ENOMEM;
1873         }
1874         memset(ioctl_buf, 0, WLC_IOCTL_MEDLEN);
1875
1876         /* drop command */
1877         str = bcmstrtok(&pcmd, " ", NULL);
1878
1879         /* get index */
1880         str = bcmstrtok(&pcmd, " ", NULL);
1881         if (!str) {
1882                 WL_ERR(("Invalid index parameter %s\n", str));
1883                 err = -EINVAL;
1884                 goto exit;
1885         }
1886         mkeep_alive_pkt->keep_alive_id = bcm_strtoul(str, &endptr, 0);
1887         if (*endptr != '\0') {
1888                 WL_ERR(("Invalid index parameter %s\n", str));
1889                 err = -EINVAL;
1890                 goto exit;
1891         }
1892
1893         /* get period */
1894         str = bcmstrtok(&pcmd, " ", NULL);
1895         if (!str) {
1896                 WL_ERR(("Invalid period parameter %s\n", str));
1897                 err = -EINVAL;
1898                 goto exit;
1899         }
1900         mkeep_alive_pkt->period_msec = bcm_strtoul(str, &endptr, 0);
1901         if (*endptr != '\0') {
1902                 WL_ERR(("Invalid period parameter %s\n", str));
1903                 err = -EINVAL;
1904                 goto exit;
1905         }
1906
1907         mkeep_alive_pkt->version = htod16(WL_MKEEP_ALIVE_VERSION);
1908         mkeep_alive_pkt->length = htod16(WL_MKEEP_ALIVE_FIXED_LEN);
1909
1910         /*get packet*/
1911         str = bcmstrtok(&pcmd, " ", NULL);
1912         if (str) {
1913                 if (strncmp(str, "0x", 2) != 0 &&
1914                                 strncmp(str, "0X", 2) != 0) {
1915                         WL_ERR(("Packet invalid format. Needs to start with 0x\n"));
1916                         err = -EINVAL;
1917                         goto exit;
1918                 }
1919                 str = str + 2; /* Skip past 0x */
1920                 if (strlen(str) % 2 != 0) {
1921                         WL_ERR(("Packet invalid format. Needs to be of even length\n"));
1922                         err = -EINVAL;
1923                         goto exit;
1924                 }
1925                 for (i = 0; *str != '\0'; i++) {
1926                         char num[3];
1927                         strncpy(num, str, 2);
1928                         num[2] = '\0';
1929                         mkeep_alive_pkt->data[i] = (uint8)bcm_strtoul(num, NULL, 16);
1930                         str += 2;
1931                 }
1932                 mkeep_alive_pkt->len_bytes = i;
1933         }
1934
1935         err = wldev_iovar_setbuf(dev, "mkeep_alive", mkeep_alive_pkt,
1936                 len + i, ioctl_buf, WLC_IOCTL_MEDLEN, NULL);
1937         if (err != BCME_OK) {
1938                 WL_ERR(("%s: Fail to set iovar %d\n", __func__, err));
1939                 err = -EINVAL;
1940         }
1941
1942 exit:
1943         if (mkeep_alive_pkt)
1944                 kfree(mkeep_alive_pkt);
1945
1946         if (ioctl_buf)
1947                 kfree(ioctl_buf);
1948
1949         return err;
1950
1951
1952 }
1953
1954 #define NETLINK_OXYGEN     30
1955 #define AIBSS_BEACON_TIMEOUT    10
1956
1957 static struct sock *nl_sk = NULL;
1958
1959 static void wl_netlink_recv(struct sk_buff *skb)
1960 {
1961         WL_ERR(("netlink_recv called\n"));
1962 }
1963
1964 static int wl_netlink_init(void)
1965 {
1966 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
1967         struct netlink_kernel_cfg cfg = {
1968                 .input  = wl_netlink_recv,
1969         };
1970 #endif
1971
1972         if (nl_sk != NULL) {
1973                 WL_ERR(("nl_sk already exist\n"));
1974                 return BCME_ERROR;
1975         }
1976
1977 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
1978         nl_sk = netlink_kernel_create(&init_net, NETLINK_OXYGEN,
1979                 0, wl_netlink_recv, NULL, THIS_MODULE);
1980 #elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0))
1981         nl_sk = netlink_kernel_create(&init_net, NETLINK_OXYGEN, THIS_MODULE, &cfg);
1982 #else
1983         nl_sk = netlink_kernel_create(&init_net, NETLINK_OXYGEN, &cfg);
1984 #endif
1985
1986         if (nl_sk == NULL) {
1987                 WL_ERR(("nl_sk is not ready\n"));
1988                 return BCME_ERROR;
1989         }
1990
1991         return BCME_OK;
1992 }
1993
1994 static void wl_netlink_deinit(void)
1995 {
1996         if (nl_sk) {
1997                 netlink_kernel_release(nl_sk);
1998                 nl_sk = NULL;
1999         }
2000 }
2001
2002 s32
2003 wl_netlink_send_msg(int pid, int type, int seq, void *data, size_t size)
2004 {
2005         struct sk_buff *skb = NULL;
2006         struct nlmsghdr *nlh = NULL;
2007         int ret = -1;
2008
2009         if (nl_sk == NULL) {
2010                 WL_ERR(("nl_sk was not initialized\n"));
2011                 goto nlmsg_failure;
2012         }
2013
2014         skb = alloc_skb(NLMSG_SPACE(size), GFP_ATOMIC);
2015         if (skb == NULL) {
2016                 WL_ERR(("failed to allocate memory\n"));
2017                 goto nlmsg_failure;
2018         }
2019
2020         nlh = nlmsg_put(skb, 0, 0, 0, size, 0);
2021         if (nlh == NULL) {
2022                 WL_ERR(("failed to build nlmsg, skb_tailroom:%d, nlmsg_total_size:%d\n",
2023                         skb_tailroom(skb), nlmsg_total_size(size)));
2024                 dev_kfree_skb(skb);
2025                 goto nlmsg_failure;
2026         }
2027
2028         memcpy(nlmsg_data(nlh), data, size);
2029         nlh->nlmsg_seq = seq;
2030         nlh->nlmsg_type = type;
2031
2032         /* netlink_unicast() takes ownership of the skb and frees it itself. */
2033         ret = netlink_unicast(nl_sk, skb, pid, 0);
2034         WL_DBG(("netlink_unicast() pid=%d, ret=%d\n", pid, ret));
2035
2036 nlmsg_failure:
2037         return ret;
2038 }
2039
2040
2041 int wl_keep_alive_set(struct net_device *dev, char* extra, int total_len)
2042 {
2043         char                            buf[256];
2044         const char                      *str;
2045         wl_mkeep_alive_pkt_t    mkeep_alive_pkt;
2046         wl_mkeep_alive_pkt_t    *mkeep_alive_pktp;
2047         int                                     buf_len;
2048         int                                     str_len;
2049         int res                                 = -1;
2050         uint period_msec = 0;
2051
2052         if (extra == NULL)
2053         {
2054                  DHD_ERROR(("%s: extra is NULL\n", __FUNCTION__));
2055                  return -1;
2056         }
2057         if (sscanf(extra, "%d", &period_msec) != 1)
2058         {
2059                  DHD_ERROR(("%s: sscanf error. check period_msec value\n", __FUNCTION__));
2060                  return -EINVAL;
2061         }
2062         DHD_ERROR(("%s: period_msec is %d\n", __FUNCTION__, period_msec));
2063
2064         memset(&mkeep_alive_pkt, 0, sizeof(wl_mkeep_alive_pkt_t));
2065
2066         str = "mkeep_alive";
2067         str_len = strlen(str);
2068         strncpy(buf, str, str_len);
2069         buf[ str_len ] = '\0';
2070         mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) (buf + str_len + 1);
2071         mkeep_alive_pkt.period_msec = period_msec;
2072         buf_len = str_len + 1;
2073         mkeep_alive_pkt.version = htod16(WL_MKEEP_ALIVE_VERSION);
2074         mkeep_alive_pkt.length = htod16(WL_MKEEP_ALIVE_FIXED_LEN);
2075
2076         /* Setup keep alive zero for null packet generation */
2077         mkeep_alive_pkt.keep_alive_id = 0;
2078         mkeep_alive_pkt.len_bytes = 0;
2079         buf_len += WL_MKEEP_ALIVE_FIXED_LEN;
2080         /* Keep-alive attributes are set in local       variable (mkeep_alive_pkt), and
2081          * then memcpy'ed into buffer (mkeep_alive_pktp) since there is no
2082          * guarantee that the buffer is properly aligned.
2083          */
2084         memcpy((char *)mkeep_alive_pktp, &mkeep_alive_pkt, WL_MKEEP_ALIVE_FIXED_LEN);
2085
2086         if ((res = wldev_ioctl(dev, WLC_SET_VAR, buf, buf_len, TRUE)) < 0)
2087         {
2088                 DHD_ERROR(("%s:keep_alive set failed. res[%d]\n", __FUNCTION__, res));
2089         }
2090         else
2091         {
2092                 DHD_ERROR(("%s:keep_alive set ok. res[%d]\n", __FUNCTION__, res));
2093         }
2094
2095         return res;
2096 }
2097
2098 static int wl_android_get_iovar(struct net_device *dev, char *command,
2099                                         int total_len)
2100 {
2101         int skip = strlen(CMD_GETIOVAR) + 1;
2102         char iovbuf[WLC_IOCTL_SMLEN];
2103         s32 param = -1;
2104         int bytes_written = 0;
2105
2106         if (strlen(command) < skip ) {
2107                 DHD_ERROR(("%s: Invalid command length", __func__));
2108                 return BCME_BADARG;
2109         }
2110
2111         DHD_INFO(("%s: command buffer %s command length:%zd\n",
2112                         __func__, (command + skip), strlen(command + skip)));
2113
2114         /* Initiate get_iovar command */
2115         memset(iovbuf, 0, sizeof(iovbuf));
2116         bytes_written = wldev_iovar_getbuf(dev, (command + skip), &param,
2117                                 sizeof(param), iovbuf, sizeof(iovbuf), NULL);
2118
2119         /* Check for errors, log the error and reset bytes written value */
2120         if (bytes_written < 0) {
2121                 DHD_ERROR(("%s: get iovar failed (error=%d)\n",
2122                                 __func__, bytes_written));
2123                 bytes_written = 0;
2124         } else {
2125                 snprintf(command, total_len, iovbuf);
2126                 bytes_written = snprintf(command, total_len,
2127                                         "%d:%s", dtoh32(param), iovbuf);
2128                 DHD_INFO(("%s: param:%d iovbuf:%s strlen(iovbuf):%zd"
2129                                 " bytes_written:%d\n", __func__, param, iovbuf,
2130                                 strlen(iovbuf), bytes_written));
2131         }
2132
2133         return bytes_written;
2134 }
2135
2136 static int wl_android_set_iovar(struct net_device *dev, char *command,
2137                                         int total_len)
2138 {
2139         int skip = strlen(CMD_GETIOVAR) + 1;
2140         char iovbuf[WLC_IOCTL_SMLEN];
2141         char iovar[WLC_IOCTL_SMLEN];
2142         s32 param = -1;
2143         int bytes_written = 0;
2144         int ret = -1;
2145
2146         if (strlen(command) < skip ) {
2147                 DHD_ERROR(("Short command length"));
2148                 return BCME_BADARG;
2149         }
2150
2151         DHD_INFO(("%s: command buffer:%s command length:%zd\n",
2152                         __func__, (command + skip), strlen(command + skip)));
2153
2154         /* Parse command and get iovbuf and param values */
2155         memset(iovbuf, 0, sizeof(iovbuf));
2156         memset(iovar, 0, sizeof(iovbuf));
2157         ret = sscanf((command + skip), "%s %d:%s", iovar, &param, iovbuf);
2158         if (ret < 3) {
2159                 DHD_ERROR(("%s: Failed to get Parameter %d\n", __func__, ret));
2160                 return BCME_BADARG;
2161         }
2162         DHD_INFO(("%s: iovar:%s param:%d iovbuf:%s strlen(iovbuf):%zd\n", __func__,
2163                         iovar, param, iovbuf, strlen(iovbuf)));
2164
2165         bytes_written = wldev_iovar_setbuf(dev, iovar, &param,
2166                                 sizeof(param), iovbuf, sizeof(iovbuf), NULL);
2167         if (bytes_written < 0)
2168                 DHD_ERROR(("%s: set iovar failed (error=%d)\n",
2169                                 __func__, bytes_written));
2170
2171         return bytes_written;
2172 }
2173
2174 static const char *
2175 get_string_by_separator(char *result, int result_len, const char *src, char separator)
2176 {
2177         char *end = result + result_len - 1;
2178         while ((result != end) && (*src != separator) && (*src)) {
2179                 *result++ = *src++;
2180         }
2181         *result = 0;
2182         if (*src == separator)
2183                 ++src;
2184         return src;
2185 }
2186
2187 int
2188 wl_android_set_roam_offload_bssid_list(struct net_device *dev, const char *cmd)
2189 {
2190         char sbuf[32];
2191         int i, cnt, size, err, ioctl_buf_len;
2192         roamoffl_bssid_list_t *bssid_list;
2193         const char *str = cmd;
2194         char *ioctl_buf;
2195
2196         str = get_string_by_separator(sbuf, 32, str, ',');
2197         cnt = bcm_atoi(sbuf);
2198         cnt = MIN(cnt, MAX_ROAMOFFL_BSSID_NUM);
2199         size = sizeof(int) + sizeof(struct ether_addr) * cnt;
2200         WL_ERR(("ROAM OFFLOAD BSSID LIST %d BSSIDs, size %d\n", cnt, size));
2201         bssid_list = kmalloc(size, GFP_KERNEL);
2202         if (bssid_list == NULL) {
2203                 WL_ERR(("%s: memory alloc for bssid list(%d) failed\n",
2204                         __FUNCTION__, size));
2205                 return -ENOMEM;
2206         }
2207         ioctl_buf_len = size + 64;
2208         ioctl_buf = kmalloc(ioctl_buf_len, GFP_KERNEL);
2209         if (ioctl_buf == NULL) {
2210                 WL_ERR(("%s: memory alloc for ioctl_buf(%d) failed\n",
2211                         __FUNCTION__, ioctl_buf_len));
2212                 kfree(bssid_list);
2213                 return -ENOMEM;
2214         }
2215
2216         for (i = 0; i < cnt; i++) {
2217                 str = get_string_by_separator(sbuf, 32, str, ',');
2218                 if (bcm_ether_atoe(sbuf, &bssid_list->bssid[i]) == 0) {
2219                         DHD_ERROR(("%s: Invalid station MAC Address!!!\n", __FUNCTION__));
2220                         kfree(bssid_list);
2221                         kfree(ioctl_buf);
2222                         return -1;
2223                 }
2224         }
2225
2226         bssid_list->cnt = cnt;
2227         err = wldev_iovar_setbuf(dev, "roamoffl_bssid_list",
2228                 bssid_list, size, ioctl_buf, ioctl_buf_len, NULL);
2229         kfree(bssid_list);
2230         kfree(ioctl_buf);
2231
2232         return err;
2233 }
2234
2235 #ifdef P2PRESP_WFDIE_SRC
2236 static int wl_android_get_wfdie_resp(struct net_device *dev, char *command, int total_len)
2237 {
2238         int error = 0;
2239         int bytes_written = 0;
2240         int only_resp_wfdsrc = 0;
2241
2242         error = wldev_iovar_getint(dev, "p2p_only_resp_wfdsrc", &only_resp_wfdsrc);
2243         if (error) {
2244                 DHD_ERROR(("%s: Failed to get the mode for only_resp_wfdsrc, error = %d\n",
2245                         __FUNCTION__, error));
2246                 return -1;
2247         }
2248
2249         bytes_written = snprintf(command, total_len, "%s %d",
2250                 CMD_P2P_GET_WFDIE_RESP, only_resp_wfdsrc);
2251
2252         return bytes_written;
2253 }
2254
2255 static int wl_android_set_wfdie_resp(struct net_device *dev, int only_resp_wfdsrc)
2256 {
2257         int error = 0;
2258
2259         error = wldev_iovar_setint(dev, "p2p_only_resp_wfdsrc", only_resp_wfdsrc);
2260         if (error) {
2261                 DHD_ERROR(("%s: Failed to set only_resp_wfdsrc %d, error = %d\n",
2262                         __FUNCTION__, only_resp_wfdsrc, error));
2263                 return -1;
2264         }
2265
2266         return 0;
2267 }
2268 #endif /* P2PRESP_WFDIE_SRC */
2269
2270 static int wl_android_get_link_status(struct net_device *dev, char *command,
2271         int total_len)
2272 {
2273         int bytes_written, error, result = 0, single_stream, stf = -1, i, nss = 0, mcs_map;
2274         uint32 rspec;
2275         uint encode, rate, txexp;
2276         struct wl_bss_info *bi;
2277         int datalen = sizeof(uint32) + sizeof(wl_bss_info_t);
2278         char buf[datalen];
2279
2280         /* get BSS information */
2281         *(u32 *) buf = htod32(datalen);
2282         error = wldev_ioctl(dev, WLC_GET_BSS_INFO, (void *)buf, datalen, false);
2283         if (unlikely(error)) {
2284                 WL_ERR(("Could not get bss info %d\n", error));
2285                 return -1;
2286         }
2287
2288         bi = (struct wl_bss_info *) (buf + sizeof(uint32));
2289
2290         for (i = 0; i < ETHER_ADDR_LEN; i++) {
2291                 if (bi->BSSID.octet[i] > 0) {
2292                         break;
2293                 }
2294         }
2295
2296         if (i == ETHER_ADDR_LEN) {
2297                 WL_DBG(("No BSSID\n"));
2298                 return -1;
2299         }
2300
2301         /* check VHT capability at beacon */
2302         if (bi->vht_cap) {
2303                 if (CHSPEC_IS5G(bi->chanspec)) {
2304                         result |= WL_ANDROID_LINK_AP_VHT_SUPPORT;
2305                 }
2306         }
2307
2308         /* get a rspec (radio spectrum) rate */
2309         error = wldev_iovar_getint(dev, "nrate", &rspec);
2310         if (unlikely(error) || rspec == 0) {
2311                 WL_ERR(("get link status error (%d)\n", error));
2312                 return -1;
2313         }
2314
2315         encode = (rspec & WL_RSPEC_ENCODING_MASK);
2316         rate = (rspec & WL_RSPEC_RATE_MASK);
2317         txexp = (rspec & WL_RSPEC_TXEXP_MASK) >> WL_RSPEC_TXEXP_SHIFT;
2318
2319         switch (encode) {
2320         case WL_RSPEC_ENCODE_HT:
2321                 /* check Rx MCS Map for HT */
2322                 for (i = 0; i < MAX_STREAMS_SUPPORTED; i++) {
2323                         int8 bitmap = 0xFF;
2324                         if (i == MAX_STREAMS_SUPPORTED-1) {
2325                                 bitmap = 0x7F;
2326                         }
2327                         if (bi->basic_mcs[i] & bitmap) {
2328                                 nss++;
2329                         }
2330                 }
2331                 break;
2332         case WL_RSPEC_ENCODE_VHT:
2333                 /* check Rx MCS Map for VHT */
2334                 for (i = 1; i <= VHT_CAP_MCS_MAP_NSS_MAX; i++) {
2335                         mcs_map = VHT_MCS_MAP_GET_MCS_PER_SS(i, dtoh16(bi->vht_rxmcsmap));
2336                         if (mcs_map != VHT_CAP_MCS_MAP_NONE) {
2337                                 nss++;
2338                         }
2339                 }
2340                 break;
2341         }
2342
2343         /* check MIMO capability with nss in beacon */
2344         if (nss > 1) {
2345                 result |= WL_ANDROID_LINK_AP_MIMO_SUPPORT;
2346         }
2347
2348         single_stream = (encode == WL_RSPEC_ENCODE_RATE) ||
2349                 ((encode == WL_RSPEC_ENCODE_HT) && rate < 8) ||
2350                 ((encode == WL_RSPEC_ENCODE_VHT) &&
2351                 ((rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT) == 1);
2352
2353         if (txexp == 0) {
2354                 if ((rspec & WL_RSPEC_STBC) && single_stream) {
2355                         stf = OLD_NRATE_STF_STBC;
2356                 } else {
2357                         stf = (single_stream) ? OLD_NRATE_STF_SISO : OLD_NRATE_STF_SDM;
2358                 }
2359         } else if (txexp == 1 && single_stream) {
2360                 stf = OLD_NRATE_STF_CDD;
2361         }
2362
2363         /* check 11ac (VHT) */
2364         if (encode == WL_RSPEC_ENCODE_VHT) {
2365                 if (CHSPEC_IS5G(bi->chanspec)) {
2366                         result |= WL_ANDROID_LINK_VHT;
2367                 }
2368         }
2369
2370         /* check MIMO */
2371         if (result & WL_ANDROID_LINK_AP_MIMO_SUPPORT) {
2372                 switch (stf) {
2373                 case OLD_NRATE_STF_SISO:
2374                         break;
2375                 case OLD_NRATE_STF_CDD:
2376                 case OLD_NRATE_STF_STBC:
2377                         result |= WL_ANDROID_LINK_MIMO;
2378                         break;
2379                 case OLD_NRATE_STF_SDM:
2380                         if (!single_stream) {
2381                                 result |= WL_ANDROID_LINK_MIMO;
2382                         }
2383                         break;
2384                 }
2385         }
2386
2387         WL_DBG(("%s:result=%d, stf=%d, single_stream=%d, mcs map=%d\n",
2388                 __FUNCTION__, result, stf, single_stream, nss));
2389
2390         bytes_written = sprintf(command, "%s %d", CMD_GET_LINK_STATUS, result);
2391
2392         return bytes_written;
2393 }
2394
2395
2396 int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
2397 {
2398 #define PRIVATE_COMMAND_MAX_LEN 8192
2399         int ret = 0;
2400         char *command = NULL;
2401         int bytes_written = 0;
2402         android_wifi_priv_cmd priv_cmd;
2403
2404         net_os_wake_lock(net);
2405
2406         if (!ifr->ifr_data) {
2407                 ret = -EINVAL;
2408                 goto exit;
2409         }
2410
2411 #ifdef CONFIG_COMPAT
2412         if (is_compat_task()) {
2413                 compat_android_wifi_priv_cmd compat_priv_cmd;
2414                 if (copy_from_user(&compat_priv_cmd, ifr->ifr_data,
2415                         sizeof(compat_android_wifi_priv_cmd))) {
2416                         ret = -EFAULT;
2417                         goto exit;
2418
2419                 }
2420                 priv_cmd.buf = compat_ptr(compat_priv_cmd.buf);
2421                 priv_cmd.used_len = compat_priv_cmd.used_len;
2422                 priv_cmd.total_len = compat_priv_cmd.total_len;
2423         } else
2424 #endif /* CONFIG_COMPAT */
2425         {
2426                 if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) {
2427                         ret = -EFAULT;
2428                         goto exit;
2429                 }
2430         }
2431         if ((priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) || (priv_cmd.total_len < 0)) {
2432                 DHD_ERROR(("%s: too long priavte command\n", __FUNCTION__));
2433                 ret = -EINVAL;
2434                 goto exit;
2435         }
2436         command = kmalloc((priv_cmd.total_len + 1), GFP_KERNEL);
2437         if (!command)
2438         {
2439                 DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__));
2440                 ret = -ENOMEM;
2441                 goto exit;
2442         }
2443         if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) {
2444                 ret = -EFAULT;
2445                 goto exit;
2446         }
2447         command[priv_cmd.total_len] = '\0';
2448
2449         DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name));
2450
2451         if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) {
2452                 DHD_INFO(("%s, Received regular START command\n", __FUNCTION__));
2453                 bytes_written = wl_android_wifi_on(net);
2454         }
2455         else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) {
2456                 bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len);
2457         }
2458
2459         if (!g_wifi_on) {
2460                 DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n",
2461                         __FUNCTION__, command, ifr->ifr_name));
2462                 ret = 0;
2463                 goto exit;
2464         }
2465
2466         if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) {
2467                 bytes_written = wl_android_wifi_off(net);
2468         }
2469         else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) {
2470                 /* TBD: SCAN-ACTIVE */
2471         }
2472         else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) {
2473                 /* TBD: SCAN-PASSIVE */
2474         }
2475         else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) {
2476                 bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len);
2477         }
2478         else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) {
2479                 bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len);
2480         }
2481 #ifdef PKT_FILTER_SUPPORT
2482         else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) {
2483                 bytes_written = net_os_enable_packet_filter(net, 1);
2484         }
2485         else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) {
2486                 bytes_written = net_os_enable_packet_filter(net, 0);
2487         }
2488         else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) {
2489                 int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0';
2490                 bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num);
2491         }
2492         else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) {
2493                 int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0';
2494                 bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num);
2495         }
2496         else if (strnicmp(command, CMD_PKT_FILTER_MODE, strlen(CMD_PKT_FILTER_MODE)) == 0) {
2497                 dhd_set_packet_filter_mode(net, &command[strlen(CMD_PKT_FILTER_MODE) + 1]);
2498         } else if (strnicmp(command, CMD_PKT_FILTER_PORTS, strlen(CMD_PKT_FILTER_PORTS)) == 0) {
2499                 bytes_written = dhd_set_packet_filter_ports(net,
2500                         &command[strlen(CMD_PKT_FILTER_PORTS) + 1]);
2501                 ret = bytes_written;
2502         }
2503 #endif /* PKT_FILTER_SUPPORT */
2504         else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) {
2505                 /* TBD: BTCOEXSCAN-START */
2506         }
2507         else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) {
2508                 /* TBD: BTCOEXSCAN-STOP */
2509         }
2510         else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) {
2511 #ifdef WL_CFG80211
2512                 void *dhdp = wl_cfg80211_get_dhdp();
2513                 bytes_written = wl_cfg80211_set_btcoex_dhcp(net, dhdp, command);
2514 #else
2515 #ifdef PKT_FILTER_SUPPORT
2516                 uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0';
2517
2518                 if (mode == 1)
2519                         net_os_enable_packet_filter(net, 0); /* DHCP starts */
2520                 else
2521                         net_os_enable_packet_filter(net, 1); /* DHCP ends */
2522 #endif /* PKT_FILTER_SUPPORT */
2523 #endif /* WL_CFG80211 */
2524         }
2525         else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) {
2526                 bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len);
2527         }
2528         else if (strnicmp(command, CMD_SETSUSPENDMODE, strlen(CMD_SETSUSPENDMODE)) == 0) {
2529                 bytes_written = wl_android_set_suspendmode(net, command, priv_cmd.total_len);
2530         }
2531         else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) {
2532                 uint band = *(command + strlen(CMD_SETBAND) + 1) - '0';
2533                 bytes_written = wldev_set_band(net, band);
2534         }
2535         else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) {
2536                 bytes_written = wl_android_get_band(net, command, priv_cmd.total_len);
2537         }
2538 #ifdef WL_CFG80211
2539         /* CUSTOMER_SET_COUNTRY feature is define for only GGSM model */
2540         else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) {
2541                 /* We use only NV_COUNTRY command to set the country code
2542                  * Do not allow default COUNTRY command as third party apps
2543                  * can manipulate WiFi country code */
2544                 bytes_written = 0;
2545         } else if (strnicmp(command, CMD_NV_COUNTRY, strlen(CMD_NV_COUNTRY)) == 0) {
2546                 char *country_code = command + strlen(CMD_NV_COUNTRY) + 1;
2547                 bytes_written = wldev_set_country(net, country_code, true, false);
2548         }
2549 #endif /* WL_CFG80211 */
2550         else if (strnicmp(command, CMD_SET_CSA, strlen(CMD_SET_CSA)) == 0) {
2551                 bytes_written = wl_android_set_csa(net, command, priv_cmd.total_len);
2552         } else if (strnicmp(command, CMD_80211_MODE, strlen(CMD_80211_MODE)) == 0) {
2553                 bytes_written = wl_android_get_80211_mode(net, command, priv_cmd.total_len);
2554         } else if (strnicmp(command, CMD_CHANSPEC, strlen(CMD_CHANSPEC)) == 0) {
2555                 bytes_written = wl_android_get_chanspec(net, command, priv_cmd.total_len);
2556         } else if (strnicmp(command, CMD_DATARATE, strlen(CMD_DATARATE)) == 0) {
2557                 bytes_written = wl_android_get_datarate(net, command, priv_cmd.total_len);
2558         } else if (strnicmp(command, CMD_ASSOC_CLIENTS, strlen(CMD_ASSOC_CLIENTS)) == 0) {
2559                 bytes_written = wl_android_get_assoclist(net, command, priv_cmd.total_len);
2560         }
2561
2562
2563 #ifdef PNO_SUPPORT
2564         else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) {
2565                 bytes_written = dhd_dev_pno_stop_for_ssid(net);
2566         }
2567 #ifndef WL_SCHED_SCAN
2568         else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) {
2569                 bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len);
2570         }
2571 #endif /* !WL_SCHED_SCAN */
2572         else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) {
2573                 int enable = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0';
2574                 bytes_written = (enable)? 0 : dhd_dev_pno_stop_for_ssid(net);
2575         }
2576         else if (strnicmp(command, CMD_WLS_BATCHING, strlen(CMD_WLS_BATCHING)) == 0) {
2577                 bytes_written = wls_parse_batching_cmd(net, command, priv_cmd.total_len);
2578         }
2579 #endif /* PNO_SUPPORT */
2580         else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) {
2581                 bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len);
2582         }
2583         else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) {
2584                 int skip = strlen(CMD_P2P_SET_NOA) + 1;
2585                 bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip,
2586                         priv_cmd.total_len - skip);
2587         }
2588 #ifdef WL_NAN
2589         else if (strnicmp(command, CMD_NAN, strlen(CMD_NAN)) == 0) {
2590                 bytes_written = wl_cfg80211_nan_cmd_handler(net, command,
2591                         priv_cmd.total_len);
2592         }
2593 #endif /* WL_NAN */
2594 #if !defined WL_ENABLE_P2P_IF
2595         else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) {
2596                 bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len);
2597         }
2598 #endif /* WL_ENABLE_P2P_IF */
2599         else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) {
2600                 int skip = strlen(CMD_P2P_SET_PS) + 1;
2601                 bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip,
2602                         priv_cmd.total_len - skip);
2603         }
2604 #ifdef WL_CFG80211
2605         else if (strnicmp(command, CMD_SET_AP_WPS_P2P_IE,
2606                 strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) {
2607                 int skip = strlen(CMD_SET_AP_WPS_P2P_IE) + 3;
2608                 bytes_written = wl_cfg80211_set_wps_p2p_ie(net, command + skip,
2609                         priv_cmd.total_len - skip, *(command + skip - 2) - '0');
2610         }
2611 #endif /* WL_CFG80211 */
2612         else if (strnicmp(command, CMD_AMPDU_SEND_DELBA, strlen(CMD_AMPDU_SEND_DELBA)) == 0)
2613                 bytes_written = wl_android_ampdu_send_delba(net,
2614                         &command[strlen(CMD_AMPDU_SEND_DELBA) + 1]);
2615         else if (strnicmp(command, CMD_OKC_SET_PMK, strlen(CMD_OKC_SET_PMK)) == 0)
2616                 bytes_written = wl_android_set_pmk(net, command, priv_cmd.total_len);
2617         else if (strnicmp(command, CMD_OKC_ENABLE, strlen(CMD_OKC_ENABLE)) == 0)
2618                 bytes_written = wl_android_okc_enable(net, command, priv_cmd.total_len);
2619 #if defined(WL_SUPPORT_AUTO_CHANNEL)
2620         else if (strnicmp(command, CMD_GET_BEST_CHANNELS,
2621                 strlen(CMD_GET_BEST_CHANNELS)) == 0) {
2622                 bytes_written = wl_cfg80211_get_best_channels(net, command,
2623                         priv_cmd.total_len);
2624         }
2625 #endif /* WL_SUPPORT_AUTO_CHANNEL */
2626         else if (strnicmp(command, CMD_HAPD_MAC_FILTER, strlen(CMD_HAPD_MAC_FILTER)) == 0) {
2627                 int skip = strlen(CMD_HAPD_MAC_FILTER) + 1;
2628                 wl_android_set_mac_address_filter(net, (const char*)command+skip);
2629         }
2630         else if (strnicmp(command, CMD_SETROAMMODE, strlen(CMD_SETROAMMODE)) == 0)
2631                 bytes_written = wl_android_set_roam_mode(net, command, priv_cmd.total_len);
2632 #if defined(BCMFW_ROAM_ENABLE)
2633         else if (strnicmp(command, CMD_SET_ROAMPREF, strlen(CMD_SET_ROAMPREF)) == 0) {
2634                 bytes_written = wl_android_set_roampref(net, command, priv_cmd.total_len);
2635         }
2636 #endif /* BCMFW_ROAM_ENABLE */
2637         else if (strnicmp(command, CMD_MIRACAST, strlen(CMD_MIRACAST)) == 0)
2638                 bytes_written = wl_android_set_miracast(net, command, priv_cmd.total_len);
2639         else if (strnicmp(command, CMD_SETMIRACAST, strlen(CMD_SETMIRACAST)) == 0)
2640                 bytes_written = wldev_miracast_tuning(net, command, priv_cmd.total_len);
2641         else if (strnicmp(command, CMD_ASSOCRESPIE, strlen(CMD_ASSOCRESPIE)) == 0)
2642                 bytes_written = wldev_get_assoc_resp_ie(net, command, priv_cmd.total_len);
2643         else if (strnicmp(command, CMD_RXRATESTATS, strlen(CMD_RXRATESTATS)) == 0)
2644                 bytes_written = wldev_get_rx_rate_stats(net, command, priv_cmd.total_len);
2645         else if (strnicmp(command, CMD_SETIBSSBEACONOUIDATA, strlen(CMD_SETIBSSBEACONOUIDATA)) == 0)
2646                 bytes_written = wl_android_set_ibss_beacon_ouidata(net,
2647                 command, priv_cmd.total_len);
2648         else if (strnicmp(command, CMD_KEEP_ALIVE, strlen(CMD_KEEP_ALIVE)) == 0) {
2649                 int skip = strlen(CMD_KEEP_ALIVE) + 1;
2650                 bytes_written = wl_keep_alive_set(net, command + skip, priv_cmd.total_len - skip);
2651         } else if (strnicmp(command, CMD_MKEEP_ALIVE,
2652                 strlen(CMD_MKEEP_ALIVE)) == 0) {
2653                 DHD_ERROR(("%s: CMD_MKEEP_ALIVE\n", __func__));
2654                 bytes_written = wl_android_mkeep_alive(net, command, priv_cmd.total_len);
2655         } else if (strnicmp(command, CMD_MAXLINKSPEED, strlen(CMD_MAXLINKSPEED))== 0)
2656                 bytes_written = wldev_get_max_linkspeed(net, command, priv_cmd.total_len);
2657         else if (strnicmp(command, CMD_ROAM_OFFLOAD, strlen(CMD_ROAM_OFFLOAD)) == 0) {
2658                 int enable = *(command + strlen(CMD_ROAM_OFFLOAD) + 1) - '0';
2659                 bytes_written = wl_cfg80211_enable_roam_offload(net, enable);
2660         }
2661         else if (strnicmp(command, CMD_GETIOVAR, strlen(CMD_GETIOVAR)) == 0)
2662                 bytes_written = wl_android_get_iovar(net, command, priv_cmd.total_len);
2663         else if (strnicmp(command, CMD_SETIOVAR, strlen(CMD_GETIOVAR)) == 0)
2664                 bytes_written = wl_android_set_iovar(net, command, priv_cmd.total_len);
2665         else if (strnicmp(command, CMD_AUTOSLEEP, strlen(CMD_AUTOSLEEP)) == 0) {
2666                 bytes_written = wl_android_set_slpauto(net, command,
2667                         priv_cmd.total_len);
2668         }
2669         else if (strnicmp(command, CMD_ROAM_OFFLOAD_APLIST, strlen(CMD_ROAM_OFFLOAD_APLIST)) == 0) {
2670                 bytes_written = wl_android_set_roam_offload_bssid_list(net,
2671                         command + strlen(CMD_ROAM_OFFLOAD_APLIST) + 1);
2672         }
2673 #ifdef P2PRESP_WFDIE_SRC
2674         else if (strnicmp(command, CMD_P2P_SET_WFDIE_RESP,
2675                 strlen(CMD_P2P_SET_WFDIE_RESP)) == 0) {
2676                 int mode = *(command + strlen(CMD_P2P_SET_WFDIE_RESP) + 1) - '0';
2677                 bytes_written = wl_android_set_wfdie_resp(net, mode);
2678         } else if (strnicmp(command, CMD_P2P_GET_WFDIE_RESP,
2679                 strlen(CMD_P2P_GET_WFDIE_RESP)) == 0) {
2680                 bytes_written = wl_android_get_wfdie_resp(net, command, priv_cmd.total_len);
2681         }
2682 #endif /* P2PRESP_WFDIE_SRC */
2683         else if (strnicmp(command, CMD_GET_LINK_STATUS, strlen(CMD_GET_LINK_STATUS)) == 0) {
2684                 bytes_written = wl_android_get_link_status(net, command, priv_cmd.total_len);
2685         }
2686 #ifdef CONNECTION_STATISTICS
2687         else if (strnicmp(command, CMD_GET_CONNECTION_STATS,
2688                 strlen(CMD_GET_CONNECTION_STATS)) == 0) {
2689                 bytes_written = wl_android_get_connection_stats(net, command,
2690                         priv_cmd.total_len);
2691         }
2692 #endif
2693 #ifdef WLWFDS
2694         else if (strnicmp(command, CMD_ADD_WFDS_HASH, strlen(CMD_ADD_WFDS_HASH)) == 0) {
2695                 bytes_written = wl_android_set_wfds_hash(net, command, priv_cmd.total_len, 1);
2696         }
2697         else if (strnicmp(command, CMD_DEL_WFDS_HASH, strlen(CMD_DEL_WFDS_HASH)) == 0) {
2698                 bytes_written = wl_android_set_wfds_hash(net, command, priv_cmd.total_len, 0);
2699         }
2700 #endif /* WLWFDS */
2701         else {
2702                 DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command));
2703                 snprintf(command, 3, "OK");
2704                 bytes_written = strlen("OK");
2705         }
2706
2707         if (bytes_written >= 0) {
2708                 if ((bytes_written == 0) && (priv_cmd.total_len > 0))
2709                         command[0] = '\0';
2710                 if (bytes_written >= priv_cmd.total_len) {
2711                         DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written));
2712                         bytes_written = priv_cmd.total_len;
2713                 } else {
2714                         bytes_written++;
2715                 }
2716                 priv_cmd.used_len = bytes_written;
2717                 if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
2718                         DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__));
2719                         ret = -EFAULT;
2720                 }
2721         }
2722         else {
2723                 ret = bytes_written;
2724         }
2725
2726 exit:
2727         net_os_wake_unlock(net);
2728         if (command) {
2729                 kfree(command);
2730         }
2731
2732         return ret;
2733 }
2734
2735 int wl_android_init(void)
2736 {
2737         int ret = 0;
2738
2739 #ifdef ENABLE_INSMOD_NO_FW_LOAD
2740         dhd_download_fw_on_driverload = FALSE;
2741 #endif /* ENABLE_INSMOD_NO_FW_LOAD */
2742 #if defined(CUSTOMER_HW2)
2743         if (!iface_name[0]) {
2744                 memset(iface_name, 0, IFNAMSIZ);
2745                 bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ);
2746         }
2747 #endif 
2748
2749         wl_netlink_init();
2750
2751         return ret;
2752 }
2753
2754 int wl_android_exit(void)
2755 {
2756         int ret = 0;
2757         struct io_cfg *cur, *q;
2758
2759         wl_netlink_deinit();
2760
2761         list_for_each_entry_safe(cur, q, &miracast_resume_list, list) {
2762                 list_del(&cur->list);
2763                 kfree(cur);
2764         }
2765
2766         return ret;
2767 }
2768
2769 void wl_android_post_init(void)
2770 {
2771
2772 #ifdef ENABLE_4335BT_WAR
2773         bcm_bt_unlock(lock_cookie_wifi);
2774         printk("%s: btlock released\n", __FUNCTION__);
2775 #endif /* ENABLE_4335BT_WAR */
2776
2777         if (!dhd_download_fw_on_driverload)
2778                 g_wifi_on = FALSE;
2779 }