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