1 /*
2 * Linux cfg80211 driver - Android related functions
3 *
4 * Copyright (C) 1999-2013, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: wl_android.c 420671 2013-08-28 11:37:19Z $
25 */
26
27 #include <linux/module.h>
28 #include <linux/netdevice.h>
29 #include <linux/of_gpio.h>
30 #include <linux/regulator/consumer.h>
31
32 #include <wl_android.h>
33 #include <wldev_common.h>
34 #include <wlioctl.h>
35 #include <bcmutils.h>
36 #include <linux_osl.h>
37 #include <dhd_dbg.h>
38 #include <dngl_stats.h>
39 #include <dhd.h>
40 #ifdef PNO_SUPPORT
41 #include <dhd_pno.h>
42 #endif
43 #include <bcmsdbus.h>
44 #ifdef WL_CFG80211
45 #include <wl_cfg80211.h>
46 #endif
47 #if defined(CONFIG_WIFI_CONTROL_FUNC)
48 #include <linux/platform_device.h>
49 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
50 #include <linux/wlan_plat.h>
51 #else
52 #include <linux/wifi_tiwlan.h>
53 #endif
54 #endif /* CONFIG_WIFI_CONTROL_FUNC */
55
56 /*
57 * Android private command strings, PLEASE define new private commands here
58 * so they can be updated easily in the future (if needed)
59 */
60
61 #define CMD_START "START"
62 #define CMD_STOP "STOP"
63 #define CMD_SCAN_ACTIVE "SCAN-ACTIVE"
64 #define CMD_SCAN_PASSIVE "SCAN-PASSIVE"
65 #define CMD_RSSI "RSSI"
66 #define CMD_LINKSPEED "LINKSPEED"
67 #define CMD_RXFILTER_START "RXFILTER-START"
68 #define CMD_RXFILTER_STOP "RXFILTER-STOP"
69 #define CMD_RXFILTER_ADD "RXFILTER-ADD"
70 #define CMD_RXFILTER_REMOVE "RXFILTER-REMOVE"
71 #define CMD_BTCOEXSCAN_START "BTCOEXSCAN-START"
72 #define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP"
73 #define CMD_BTCOEXMODE "BTCOEXMODE"
74 #define CMD_SETSUSPENDOPT "SETSUSPENDOPT"
75 #define CMD_SETSUSPENDMODE "SETSUSPENDMODE"
76 #define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR"
77 #define CMD_SETFWPATH "SETFWPATH"
78 #define CMD_SETBAND "SETBAND"
79 #define CMD_GETBAND "GETBAND"
80 #define CMD_COUNTRY "COUNTRY"
81 #define CMD_P2P_SET_NOA "P2P_SET_NOA"
82 #if !defined WL_ENABLE_P2P_IF
83 #define CMD_P2P_GET_NOA "P2P_GET_NOA"
84 #endif /* WL_ENABLE_P2P_IF */
85 #define CMD_P2P_SD_OFFLOAD "P2P_SD_"
86 #define CMD_P2P_SET_PS "P2P_SET_PS"
87 #define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE"
88 #define CMD_SETROAMMODE "SETROAMMODE"
89 #define CMD_MIRACAST "MIRACAST"
90
91
92
93 /* CCX Private Commands */
94
95 #ifdef PNO_SUPPORT
96 #define CMD_PNOSSIDCLR_SET "PNOSSIDCLR"
97 #define CMD_PNOSETUP_SET "PNOSETUP "
98 #define CMD_PNOENABLE_SET "PNOFORCE"
99 #define CMD_PNODEBUG_SET "PNODEBUG"
100 #define CMD_WLS_BATCHING "WLS_BATCHING"
101 #endif /* PNO_SUPPORT */
102
103 #define CMD_OKC_SET_PMK "SET_PMK"
104 #define CMD_OKC_ENABLE "OKC_ENABLE"
105
106
107 /* miracast related definition */
108 #define MIRACAST_MODE_OFF 0
109 #define MIRACAST_MODE_SOURCE 1
110 #define MIRACAST_MODE_SINK 2
111
112 #ifndef MIRACAST_AMPDU_SIZE
113 #define MIRACAST_AMPDU_SIZE 8
114 #endif
115
116 #ifndef MIRACAST_MCHAN_ALGO
117 #define MIRACAST_MCHAN_ALGO 1
118 #endif
119
120 #ifndef MIRACAST_MCHAN_BW
121 #define MIRACAST_MCHAN_BW 25
122 #endif
123
124 static LIST_HEAD(miracast_resume_list);
125 static u8 miracast_cur_mode;
126
127 struct io_cfg {
128 s8 *iovar;
129 s32 param;
130 u32 ioctl;
131 void *arg;
132 u32 len;
133 struct list_head list;
134 };
135
136 typedef struct android_wifi_priv_cmd {
137 char *buf;
138 int used_len;
139 int total_len;
140 } android_wifi_priv_cmd;
141
142
143 /**
144 * Extern function declarations (TODO: move them to dhd_linux.h)
145 */
146 void dhd_customer_gpio_wlan_ctrl(int onoff);
147 int dhd_dev_reset(struct net_device *dev, uint8 flag);
148 int dhd_dev_init_ioctl(struct net_device *dev);
149 #ifdef WL_CFG80211
150 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr);
151 int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command);
152 int wl_cfg80211_get_ioctl_version(void);
153 #else
wl_cfg80211_get_p2p_dev_addr(struct net_device * net,struct ether_addr * p2pdev_addr)154 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr)
155 { return 0; }
wl_cfg80211_set_p2p_noa(struct net_device * net,char * buf,int len)156 int wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len)
157 { return 0; }
wl_cfg80211_get_p2p_noa(struct net_device * net,char * buf,int len)158 int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len)
159 { return 0; }
wl_cfg80211_set_p2p_ps(struct net_device * net,char * buf,int len)160 int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len)
161 { return 0; }
162 #endif /* WK_CFG80211 */
163 extern int dhd_os_check_if_up(void *dhdp);
164 #ifdef BCMLXSDMMC
165 extern void *bcmsdh_get_drvdata(void);
166 #endif /* BCMLXSDMMC */
167
168
169 #ifdef ENABLE_4335BT_WAR
170 extern int bcm_bt_lock(int cookie);
171 extern void bcm_bt_unlock(int cookie);
172 static int lock_cookie_wifi = 'W' | 'i'<<8 | 'F'<<16 | 'i'<<24; /* cookie is "WiFi" */
173 #endif /* ENABLE_4335BT_WAR */
174
175 extern bool ap_fw_loaded;
176 #if defined(CUSTOMER_HW2)
177 extern char iface_name[IFNAMSIZ];
178 #endif
179
180 /**
181 * Local (static) functions and variables
182 */
183
184 /* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first
185 * time (only) in dhd_open, subsequential wifi on will be handled by
186 * wl_android_wifi_on
187 */
188 static int g_wifi_on = TRUE;
189
190 /**
191 * Local (static) function definitions
192 */
wl_android_get_link_speed(struct net_device * net,char * command,int total_len)193 static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len)
194 {
195 int link_speed;
196 int bytes_written;
197 int error;
198
199 error = wldev_get_link_speed(net, &link_speed);
200 if (error)
201 return -1;
202
203 /* Convert Kbps to Android Mbps */
204 link_speed = link_speed / 1000;
205 bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed);
206 DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command));
207 return bytes_written;
208 }
209
wl_android_get_rssi(struct net_device * net,char * command,int total_len)210 static int wl_android_get_rssi(struct net_device *net, char *command, int total_len)
211 {
212 wlc_ssid_t ssid = {0};
213 int rssi;
214 int bytes_written = 0;
215 int error;
216
217 error = wldev_get_rssi(net, &rssi);
218 if (error)
219 return -1;
220
221 error = wldev_get_ssid(net, &ssid);
222 if (error)
223 return -1;
224 if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) {
225 DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__));
226 } else {
227 memcpy(command, ssid.SSID, ssid.SSID_len);
228 bytes_written = ssid.SSID_len;
229 }
230 bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi);
231 DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written));
232 return bytes_written;
233 }
234
wl_android_set_suspendopt(struct net_device * dev,char * command,int total_len)235 static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len)
236 {
237 int suspend_flag;
238 int ret_now;
239 int ret = 0;
240
241 suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0';
242
243 if (suspend_flag != 0)
244 suspend_flag = 1;
245 ret_now = net_os_set_suspend_disable(dev, suspend_flag);
246
247 if (ret_now != suspend_flag) {
248 if (!(ret = net_os_set_suspend(dev, ret_now, 1)))
249 DHD_INFO(("%s: Suspend Flag %d -> %d\n",
250 __FUNCTION__, ret_now, suspend_flag));
251 else
252 DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret));
253 }
254 return ret;
255 }
256
wl_android_set_suspendmode(struct net_device * dev,char * command,int total_len)257 static int wl_android_set_suspendmode(struct net_device *dev, char *command, int total_len)
258 {
259 int ret = 0;
260
261 #if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND)
262 int suspend_flag;
263
264 suspend_flag = *(command + strlen(CMD_SETSUSPENDMODE) + 1) - '0';
265 if (suspend_flag != 0)
266 suspend_flag = 1;
267
268 if (!(ret = net_os_set_suspend(dev, suspend_flag, 0)))
269 DHD_INFO(("%s: Suspend Mode %d\n", __FUNCTION__, suspend_flag));
270 else
271 DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret));
272 #endif
273
274 return ret;
275 }
276
wl_android_get_band(struct net_device * dev,char * command,int total_len)277 static int wl_android_get_band(struct net_device *dev, char *command, int total_len)
278 {
279 uint band;
280 int bytes_written;
281 int error;
282
283 error = wldev_get_band(dev, &band);
284 if (error)
285 return -1;
286 bytes_written = snprintf(command, total_len, "Band %d", band);
287 return bytes_written;
288 }
289
290
291 #ifdef PNO_SUPPORT
292 #define PARAM_SIZE 50
293 #define VALUE_SIZE 50
294 static int
wls_parse_batching_cmd(struct net_device * dev,char * command,int total_len)295 wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len)
296 {
297 int err = BCME_OK;
298 uint i, tokens;
299 char *pos, *pos2, *token, *token2, *delim;
300 char param[PARAM_SIZE], value[VALUE_SIZE];
301 struct dhd_pno_batch_params batch_params;
302 DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
303 if (total_len < strlen(CMD_WLS_BATCHING)) {
304 DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len));
305 err = BCME_ERROR;
306 goto exit;
307 }
308 pos = command + strlen(CMD_WLS_BATCHING) + 1;
309 memset(&batch_params, 0, sizeof(struct dhd_pno_batch_params));
310
311 if (!strncmp(pos, PNO_BATCHING_SET, strlen(PNO_BATCHING_SET))) {
312 pos += strlen(PNO_BATCHING_SET) + 1;
313 while ((token = strsep(&pos, PNO_PARAMS_DELIMETER)) != NULL) {
314 memset(param, 0, sizeof(param));
315 memset(value, 0, sizeof(value));
316 if (token == NULL || !*token)
317 break;
318 if (*token == '\0')
319 continue;
320 delim = strchr(token, PNO_PARAM_VALUE_DELLIMETER);
321 if (delim != NULL)
322 *delim = ' ';
323
324 tokens = sscanf(token, "%s %s", param, value);
325 if (!strncmp(param, PNO_PARAM_SCANFREQ, strlen(PNO_PARAM_MSCAN))) {
326 batch_params.scan_fr = simple_strtol(value, NULL, 0);
327 DHD_PNO(("scan_freq : %d\n", batch_params.scan_fr));
328 } else if (!strncmp(param, PNO_PARAM_BESTN, strlen(PNO_PARAM_MSCAN))) {
329 batch_params.bestn = simple_strtol(value, NULL, 0);
330 DHD_PNO(("bestn : %d\n", batch_params.bestn));
331 } else if (!strncmp(param, PNO_PARAM_MSCAN, strlen(PNO_PARAM_MSCAN))) {
332 batch_params.mscan = simple_strtol(value, NULL, 0);
333 DHD_PNO(("mscan : %d\n", batch_params.mscan));
334 } else if (!strncmp(param, PNO_PARAM_CHANNEL, strlen(PNO_PARAM_MSCAN))) {
335 i = 0;
336 pos2 = value;
337 tokens = sscanf(value, "<%s>", value);
338 if (tokens != 1) {
339 err = BCME_ERROR;
340 DHD_ERROR(("%s : invalid format for channel"
341 " <> params\n", __FUNCTION__));
342 goto exit;
343 }
344 while ((token2 = strsep(&pos2,
345 PNO_PARAM_CHANNEL_DELIMETER)) != NULL) {
346 if (token2 == NULL || !*token2)
347 break;
348 if (*token2 == '\0')
349 continue;
350 if (*token2 == 'A' || *token2 == 'B') {
351 batch_params.band = (*token2 == 'A')?
352 WLC_BAND_5G : WLC_BAND_2G;
353 DHD_PNO(("band : %s\n",
354 (*token2 == 'A')? "A" : "B"));
355 } else {
356 batch_params.chan_list[i++] =
357 simple_strtol(token2, NULL, 0);
358 batch_params.nchan++;
359 DHD_PNO(("channel :%d\n",
360 batch_params.chan_list[i-1]));
361 }
362 }
363 } else if (!strncmp(param, PNO_PARAM_RTT, strlen(PNO_PARAM_MSCAN))) {
364 batch_params.rtt = simple_strtol(value, NULL, 0);
365 DHD_PNO(("rtt : %d\n", batch_params.rtt));
366 } else {
367 DHD_ERROR(("%s : unknown param: %s\n", __FUNCTION__, param));
368 err = BCME_ERROR;
369 goto exit;
370 }
371 }
372 err = dhd_dev_pno_set_for_batch(dev, &batch_params);
373 if (err < 0) {
374 DHD_ERROR(("failed to configure batch scan\n"));
375 } else {
376 memset(command, 0, total_len);
377 err = sprintf(command, "%d", err);
378 }
379 } else if (!strncmp(pos, PNO_BATCHING_GET, strlen(PNO_BATCHING_GET))) {
380 err = dhd_dev_pno_get_for_batch(dev, command, total_len);
381 if (err < 0) {
382 DHD_ERROR(("failed to getting batching results\n"));
383 }
384 } else if (!strncmp(pos, PNO_BATCHING_STOP, strlen(PNO_BATCHING_STOP))) {
385 err = dhd_dev_pno_stop_for_batch(dev);
386 if (err < 0) {
387 DHD_ERROR(("failed to stop batching scan\n"));
388 } else {
389 memset(command, 0, total_len);
390 err = sprintf(command, "OK");
391 }
392 } else {
393 DHD_ERROR(("%s : unknown command\n", __FUNCTION__));
394 err = BCME_ERROR;
395 goto exit;
396 }
397 exit:
398 return err;
399 }
400
401 #ifndef WL_SCHED_SCAN
wl_android_set_pno_setup(struct net_device * dev,char * command,int total_len)402 static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len)
403 {
404 wlc_ssid_ext_t ssids_local[MAX_PFN_LIST_COUNT];
405 int res = -1;
406 int nssid = 0;
407 cmd_tlv_t *cmd_tlv_temp;
408 char *str_ptr;
409 int tlv_size_left;
410 int pno_time = 0;
411 int pno_repeat = 0;
412 int pno_freq_expo_max = 0;
413
414 #ifdef PNO_SET_DEBUG
415 int i;
416 char pno_in_example[] = {
417 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ',
418 'S', '1', '2', '0',
419 'S',
420 0x05,
421 'd', 'l', 'i', 'n', 'k',
422 'S',
423 0x04,
424 'G', 'O', 'O', 'G',
425 'T',
426 '0', 'B',
427 'R',
428 '2',
429 'M',
430 '2',
431 0x00
432 };
433 #endif /* PNO_SET_DEBUG */
434 DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
435
436 if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) {
437 DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len));
438 goto exit_proc;
439 }
440 #ifdef PNO_SET_DEBUG
441 memcpy(command, pno_in_example, sizeof(pno_in_example));
442 total_len = sizeof(pno_in_example);
443 #endif
444 str_ptr = command + strlen(CMD_PNOSETUP_SET);
445 tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET);
446
447 cmd_tlv_temp = (cmd_tlv_t *)str_ptr;
448 memset(ssids_local, 0, sizeof(ssids_local));
449
450 if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) &&
451 (cmd_tlv_temp->version == PNO_TLV_VERSION) &&
452 (cmd_tlv_temp->subtype == PNO_TLV_SUBTYPE_LEGACY_PNO)) {
453
454 str_ptr += sizeof(cmd_tlv_t);
455 tlv_size_left -= sizeof(cmd_tlv_t);
456
457 if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local,
458 MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) {
459 DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid));
460 goto exit_proc;
461 } else {
462 if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) {
463 DHD_ERROR(("%s scan duration corrupted field size %d\n",
464 __FUNCTION__, tlv_size_left));
465 goto exit_proc;
466 }
467 str_ptr++;
468 pno_time = simple_strtoul(str_ptr, &str_ptr, 16);
469 DHD_PNO(("%s: pno_time=%d\n", __FUNCTION__, pno_time));
470
471 if (str_ptr[0] != 0) {
472 if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) {
473 DHD_ERROR(("%s pno repeat : corrupted field\n",
474 __FUNCTION__));
475 goto exit_proc;
476 }
477 str_ptr++;
478 pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16);
479 DHD_PNO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat));
480 if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) {
481 DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n",
482 __FUNCTION__));
483 goto exit_proc;
484 }
485 str_ptr++;
486 pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16);
487 DHD_PNO(("%s: pno_freq_expo_max=%d\n",
488 __FUNCTION__, pno_freq_expo_max));
489 }
490 }
491 } else {
492 DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__));
493 goto exit_proc;
494 }
495
496 res = dhd_dev_pno_set_for_ssid(dev, ssids_local, nssid, pno_time, pno_repeat,
497 pno_freq_expo_max, NULL, 0);
498 exit_proc:
499 return res;
500 }
501 #endif /* !WL_SCHED_SCAN */
502 #endif /* PNO_SUPPORT */
503
wl_android_get_p2p_dev_addr(struct net_device * ndev,char * command,int total_len)504 static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len)
505 {
506 int ret;
507 int bytes_written = 0;
508
509 ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command);
510 if (ret)
511 return 0;
512 bytes_written = sizeof(struct ether_addr);
513 return bytes_written;
514 }
515
516
517 /**
518 * Global function definitions (declared in wl_android.h)
519 */
520
wl_android_wifi_on(struct net_device * dev)521 int wl_android_wifi_on(struct net_device *dev)
522 {
523 int ret = 0;
524 int retry = POWERUP_MAX_RETRY;
525
526 printk("%s in\n", __FUNCTION__);
527 if (!dev) {
528 DHD_ERROR(("%s: dev is null\n", __FUNCTION__));
529 return -EINVAL;
530 }
531
532 dhd_net_if_lock(dev);
533 if (!g_wifi_on) {
534 do {
535 dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON);
536 ret = sdioh_start(NULL, 0);
537 if (ret == 0)
538 break;
539 DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n",
540 retry+1));
541 dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
542 } while (retry-- >= 0);
543 if (ret != 0) {
544 DHD_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n"));
545 goto exit;
546 }
547 ret = dhd_dev_reset(dev, FALSE);
548 sdioh_start(NULL, 1);
549 if (!ret) {
550 if (dhd_dev_init_ioctl(dev) < 0)
551 ret = -EFAULT;
552 }
553 g_wifi_on = TRUE;
554 }
555
556 exit:
557 dhd_net_if_unlock(dev);
558
559 return ret;
560 }
561
wl_android_wifi_off(struct net_device * dev)562 int wl_android_wifi_off(struct net_device *dev)
563 {
564 int ret = 0;
565
566 printk("%s in\n", __FUNCTION__);
567 if (!dev) {
568 DHD_TRACE(("%s: dev is null\n", __FUNCTION__));
569 return -EINVAL;
570 }
571
572 dhd_net_if_lock(dev);
573 if (g_wifi_on) {
574 ret = dhd_dev_reset(dev, TRUE);
575 sdioh_stop(NULL);
576 dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
577 g_wifi_on = FALSE;
578 }
579 dhd_net_if_unlock(dev);
580
581 return ret;
582 }
583
wl_android_set_fwpath(struct net_device * net,char * command,int total_len)584 static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len)
585 {
586 if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN)
587 return -1;
588 bcm_strncpy_s(fw_path, sizeof(fw_path),
589 command + strlen(CMD_SETFWPATH) + 1, MOD_PARAM_PATHLEN - 1);
590 if (strstr(fw_path, "apsta") != NULL) {
591 DHD_INFO(("GOT APSTA FIRMWARE\n"));
592 ap_fw_loaded = TRUE;
593 } else {
594 DHD_INFO(("GOT STA FIRMWARE\n"));
595 ap_fw_loaded = FALSE;
596 }
597 return 0;
598 }
599
600
601 static int
wl_android_set_pmk(struct net_device * dev,char * command,int total_len)602 wl_android_set_pmk(struct net_device *dev, char *command, int total_len)
603 {
604 uchar pmk[33];
605 int error = 0;
606 char smbuf[WLC_IOCTL_SMLEN];
607 #ifdef OKC_DEBUG
608 int i = 0;
609 #endif
610
611 bzero(pmk, sizeof(pmk));
612 memcpy((char *)pmk, command + strlen("SET_PMK "), 32);
613 error = wldev_iovar_setbuf(dev, "okc_info_pmk", pmk, 32, smbuf, sizeof(smbuf), NULL);
614 if (error) {
615 DHD_ERROR(("Failed to set PMK for OKC, error = %d\n", error));
616 }
617 #ifdef OKC_DEBUG
618 DHD_ERROR(("PMK is "));
619 for (i = 0; i < 32; i++)
620 DHD_ERROR(("%02X ", pmk[i]));
621
622 DHD_ERROR(("\n"));
623 #endif
624 return error;
625 }
626
627 static int
wl_android_okc_enable(struct net_device * dev,char * command,int total_len)628 wl_android_okc_enable(struct net_device *dev, char *command, int total_len)
629 {
630 int error = 0;
631 char okc_enable = 0;
632
633 okc_enable = command[strlen(CMD_OKC_ENABLE) + 1] - '0';
634 error = wldev_iovar_setint(dev, "okc_enable", okc_enable);
635 if (error) {
636 DHD_ERROR(("Failed to %s OKC, error = %d\n",
637 okc_enable ? "enable" : "disable", error));
638 }
639
640 wldev_iovar_setint(dev, "ccx_enable", 0);
641
642 return error;
643 }
644
645
646
wl_android_set_roam_mode(struct net_device * dev,char * command,int total_len)647 int wl_android_set_roam_mode(struct net_device *dev, char *command, int total_len)
648 {
649 int error = 0;
650 int mode = 0;
651
652 if (sscanf(command, "%*s %d", &mode) != 1) {
653 DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__));
654 return -1;
655 }
656
657 error = wldev_iovar_setint(dev, "roam_off", mode);
658 if (error) {
659 DHD_ERROR(("%s: Failed to set roaming Mode %d, error = %d\n",
660 __FUNCTION__, mode, error));
661 return -1;
662 }
663 else
664 DHD_ERROR(("%s: succeeded to set roaming Mode %d, error = %d\n",
665 __FUNCTION__, mode, error));
666 return 0;
667 }
668
669 static int
wl_android_iolist_add(struct net_device * dev,struct list_head * head,struct io_cfg * config)670 wl_android_iolist_add(struct net_device *dev, struct list_head *head, struct io_cfg *config)
671 {
672 struct io_cfg *resume_cfg;
673 s32 ret;
674
675 resume_cfg = kzalloc(sizeof(struct io_cfg), GFP_KERNEL);
676 if (!resume_cfg)
677 return -ENOMEM;
678
679 if (config->iovar) {
680 ret = wldev_iovar_getint(dev, config->iovar, &resume_cfg->param);
681 if (ret) {
682 DHD_ERROR(("%s: Failed to get current %s value\n",
683 __FUNCTION__, config->iovar));
684 goto error;
685 }
686
687 ret = wldev_iovar_setint(dev, config->iovar, config->param);
688 if (ret) {
689 DHD_ERROR(("%s: Failed to set %s to %d\n", __FUNCTION__,
690 config->iovar, config->param));
691 goto error;
692 }
693
694 resume_cfg->iovar = config->iovar;
695 } else {
696 resume_cfg->arg = kzalloc(config->len, GFP_KERNEL);
697 if (!resume_cfg->arg) {
698 ret = -ENOMEM;
699 goto error;
700 }
701 ret = wldev_ioctl(dev, config->ioctl, resume_cfg->arg, config->len, false);
702 if (ret) {
703 DHD_ERROR(("%s: Failed to get ioctl %d\n", __FUNCTION__,
704 config->ioctl));
705 goto error;
706 }
707 ret = wldev_ioctl(dev, config->ioctl + 1, config->arg, config->len, true);
708 if (ret) {
709 DHD_ERROR(("%s: Failed to set %s to %d\n", __FUNCTION__,
710 config->iovar, config->param));
711 goto error;
712 }
713 if (config->ioctl + 1 == WLC_SET_PM)
714 wl_cfg80211_update_power_mode(dev);
715 resume_cfg->ioctl = config->ioctl;
716 resume_cfg->len = config->len;
717 }
718
719 list_add(&resume_cfg->list, head);
720
721 return 0;
722 error:
723 kfree(resume_cfg->arg);
724 kfree(resume_cfg);
725 return ret;
726 }
727
728 static void
wl_android_iolist_resume(struct net_device * dev,struct list_head * head)729 wl_android_iolist_resume(struct net_device *dev, struct list_head *head)
730 {
731 struct io_cfg *config;
732 struct list_head *cur, *q;
733 s32 ret = 0;
734
735 list_for_each_safe(cur, q, head) {
736 config = list_entry(cur, struct io_cfg, list);
737 if (config->iovar) {
738 if (!ret)
739 ret = wldev_iovar_setint(dev, config->iovar,
740 config->param);
741 } else {
742 if (!ret)
743 ret = wldev_ioctl(dev, config->ioctl + 1,
744 config->arg, config->len, true);
745 if (config->ioctl + 1 == WLC_SET_PM)
746 wl_cfg80211_update_power_mode(dev);
747 kfree(config->arg);
748 }
749 list_del(cur);
750 kfree(config);
751 }
752 }
753
754 static int
wl_android_set_miracast(struct net_device * dev,char * command,int total_len)755 wl_android_set_miracast(struct net_device *dev, char *command, int total_len)
756 {
757 int mode, val;
758 int ret = 0;
759 struct io_cfg config;
760
761 if (sscanf(command, "%*s %d", &mode) != 1) {
762 DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__));
763 return -1;
764 }
765
766 DHD_INFO(("%s: enter miracast mode %d\n", __FUNCTION__, mode));
767
768 if (miracast_cur_mode == mode)
769 return 0;
770
771 wl_android_iolist_resume(dev, &miracast_resume_list);
772 miracast_cur_mode = MIRACAST_MODE_OFF;
773
774 switch (mode) {
775 case MIRACAST_MODE_SOURCE:
776 /* setting mchan_algo to platform specific value */
777 config.iovar = "mchan_algo";
778 config.param = MIRACAST_MCHAN_ALGO;
779 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
780 if (ret)
781 goto resume;
782
783 /* setting mchan_bw to platform specific value */
784 config.iovar = "mchan_bw";
785 config.param = MIRACAST_MCHAN_BW;
786 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
787 if (ret)
788 goto resume;
789
790 /* setting apmdu to platform specific value */
791 config.iovar = "ampdu_mpdu";
792 config.param = MIRACAST_AMPDU_SIZE;
793 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
794 if (ret)
795 goto resume;
796 /* FALLTROUGH */
797 /* Source mode shares most configurations with sink mode.
798 * Fall through here to avoid code duplication
799 */
800 case MIRACAST_MODE_SINK:
801 /* disable internal roaming */
802 config.iovar = "roam_off";
803 config.param = 1;
804 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
805 if (ret)
806 goto resume;
807 /* tunr off pm */
808 val = 0;
809 config.iovar = NULL;
810 config.ioctl = WLC_GET_PM;
811 config.arg = &val;
812 config.len = sizeof(int);
813 ret = wl_android_iolist_add(dev, &miracast_resume_list, &config);
814 if (ret)
815 goto resume;
816
817 break;
818 case MIRACAST_MODE_OFF:
819 default:
820 break;
821 }
822 miracast_cur_mode = mode;
823
824 return 0;
825
826 resume:
827 DHD_ERROR(("%s: turnoff miracast mode because of err %d\n", __FUNCTION__, ret));
828 wl_android_iolist_resume(dev, &miracast_resume_list);
829 return ret;
830 }
831
wl_android_priv_cmd(struct net_device * net,struct ifreq * ifr,int cmd)832 int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
833 {
834 #define PRIVATE_COMMAND_MAX_LEN 8192
835 int ret = 0;
836 char *command = NULL;
837 int bytes_written = 0;
838 android_wifi_priv_cmd priv_cmd;
839
840 net_os_wake_lock(net);
841
842 if (!ifr->ifr_data) {
843 ret = -EINVAL;
844 goto exit;
845 }
846 if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) {
847 ret = -EFAULT;
848 goto exit;
849 }
850 if (priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN)
851 {
852 DHD_ERROR(("%s: too long priavte command\n", __FUNCTION__));
853 ret = -EINVAL;
854 goto exit;
855 }
856 command = kmalloc((priv_cmd.total_len + 1), GFP_KERNEL);
857 if (!command)
858 {
859 DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__));
860 ret = -ENOMEM;
861 goto exit;
862 }
863 if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) {
864 ret = -EFAULT;
865 goto exit;
866 }
867 command[priv_cmd.total_len] = '\0';
868
869 DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name));
870
871 if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) {
872 DHD_INFO(("%s, Received regular START command\n", __FUNCTION__));
873 bytes_written = wl_android_wifi_on(net);
874 }
875 else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) {
876 bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len);
877 }
878
879 if (!g_wifi_on) {
880 DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n",
881 __FUNCTION__, command, ifr->ifr_name));
882 ret = 0;
883 goto exit;
884 }
885
886 if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) {
887 bytes_written = wl_android_wifi_off(net);
888 }
889 else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) {
890 /* TBD: SCAN-ACTIVE */
891 }
892 else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) {
893 /* TBD: SCAN-PASSIVE */
894 }
895 else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) {
896 bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len);
897 }
898 else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) {
899 bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len);
900 }
901 #ifdef PKT_FILTER_SUPPORT
902 else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) {
903 bytes_written = net_os_enable_packet_filter(net, 1);
904 }
905 else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) {
906 bytes_written = net_os_enable_packet_filter(net, 0);
907 }
908 else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) {
909 int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0';
910 bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num);
911 }
912 else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) {
913 int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0';
914 bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num);
915 }
916 #endif /* PKT_FILTER_SUPPORT */
917 else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) {
918 /* TBD: BTCOEXSCAN-START */
919 }
920 else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) {
921 /* TBD: BTCOEXSCAN-STOP */
922 }
923 else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) {
924 #ifdef WL_CFG80211
925 bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command);
926 #else
927 #ifdef PKT_FILTER_SUPPORT
928 uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0';
929
930 if (mode == 1)
931 net_os_enable_packet_filter(net, 0); /* DHCP starts */
932 else
933 net_os_enable_packet_filter(net, 1); /* DHCP ends */
934 #endif /* PKT_FILTER_SUPPORT */
935 #endif /* WL_CFG80211 */
936 }
937 else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) {
938 bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len);
939 }
940 else if (strnicmp(command, CMD_SETSUSPENDMODE, strlen(CMD_SETSUSPENDMODE)) == 0) {
941 bytes_written = wl_android_set_suspendmode(net, command, priv_cmd.total_len);
942 }
943 else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) {
944 uint band = *(command + strlen(CMD_SETBAND) + 1) - '0';
945 bytes_written = wldev_set_band(net, band);
946 }
947 else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) {
948 bytes_written = wl_android_get_band(net, command, priv_cmd.total_len);
949 }
950 #ifdef WL_CFG80211
951 /* CUSTOMER_SET_COUNTRY feature is define for only GGSM model */
952 else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) {
953 char *country_code = command + strlen(CMD_COUNTRY) + 1;
954 bytes_written = wldev_set_country(net, country_code, true, true);
955 }
956 #endif /* WL_CFG80211 */
957
958
959 #ifdef PNO_SUPPORT
960 #ifndef WL_SCHED_SCAN
961 else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) {
962 bytes_written = dhd_dev_pno_stop_for_ssid(net);
963 }
964 else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) {
965 bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len);
966 }
967 else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) {
968 int enable = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0';
969 bytes_written = (enable)? 0 : dhd_dev_pno_stop_for_ssid(net);
970 }
971 #endif
972 else if (strnicmp(command, CMD_WLS_BATCHING, strlen(CMD_WLS_BATCHING)) == 0) {
973 bytes_written = wls_parse_batching_cmd(net, command, priv_cmd.total_len);
974 }
975 #endif /* PNO_SUPPORT */
976 else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) {
977 bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len);
978 }
979 else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) {
980 int skip = strlen(CMD_P2P_SET_NOA) + 1;
981 bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip,
982 priv_cmd.total_len - skip);
983 }
984 #if !defined WL_ENABLE_P2P_IF
985 else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) {
986 bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len);
987 }
988 #endif /* WL_ENABLE_P2P_IF */
989 else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) {
990 int skip = strlen(CMD_P2P_SET_PS) + 1;
991 bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip,
992 priv_cmd.total_len - skip);
993 }
994 #ifdef WL_CFG80211
995 else if (strnicmp(command, CMD_SET_AP_WPS_P2P_IE,
996 strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) {
997 int skip = strlen(CMD_SET_AP_WPS_P2P_IE) + 3;
998 bytes_written = wl_cfg80211_set_wps_p2p_ie(net, command + skip,
999 priv_cmd.total_len - skip, *(command + skip - 2) - '0');
1000 }
1001 #endif /* WL_CFG80211 */
1002 else if (strnicmp(command, CMD_OKC_SET_PMK, strlen(CMD_OKC_SET_PMK)) == 0)
1003 bytes_written = wl_android_set_pmk(net, command, priv_cmd.total_len);
1004 else if (strnicmp(command, CMD_OKC_ENABLE, strlen(CMD_OKC_ENABLE)) == 0)
1005 bytes_written = wl_android_okc_enable(net, command, priv_cmd.total_len);
1006 else if (strnicmp(command, CMD_SETROAMMODE, strlen(CMD_SETROAMMODE)) == 0)
1007 bytes_written = wl_android_set_roam_mode(net, command, priv_cmd.total_len);
1008 else if (strnicmp(command, CMD_MIRACAST, strlen(CMD_MIRACAST)) == 0)
1009 bytes_written = wl_android_set_miracast(net, command, priv_cmd.total_len);
1010 else {
1011 DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command));
1012 snprintf(command, 3, "OK");
1013 bytes_written = strlen("OK");
1014 }
1015
1016 if (bytes_written >= 0) {
1017 if ((bytes_written == 0) && (priv_cmd.total_len > 0))
1018 command[0] = '\0';
1019 if (bytes_written >= priv_cmd.total_len) {
1020 DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written));
1021 bytes_written = priv_cmd.total_len;
1022 } else {
1023 bytes_written++;
1024 }
1025 priv_cmd.used_len = bytes_written;
1026 if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
1027 DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__));
1028 ret = -EFAULT;
1029 }
1030 }
1031 else {
1032 ret = bytes_written;
1033 }
1034
1035 exit:
1036 net_os_wake_unlock(net);
1037 if (command) {
1038 kfree(command);
1039 }
1040
1041 return ret;
1042 }
1043
wl_android_init(void)1044 int wl_android_init(void)
1045 {
1046 int ret = 0;
1047
1048 #ifdef ENABLE_INSMOD_NO_FW_LOAD
1049 dhd_download_fw_on_driverload = FALSE;
1050 #endif /* ENABLE_INSMOD_NO_FW_LOAD */
1051 #if defined(CUSTOMER_HW2)
1052 if (!iface_name[0]) {
1053 memset(iface_name, 0, IFNAMSIZ);
1054 bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ);
1055 }
1056 #endif
1057
1058
1059 return ret;
1060 }
1061
wl_android_exit(void)1062 int wl_android_exit(void)
1063 {
1064 int ret = 0;
1065
1066
1067 return ret;
1068 }
1069
wl_android_post_init(void)1070 void wl_android_post_init(void)
1071 {
1072
1073 #ifdef ENABLE_4335BT_WAR
1074 bcm_bt_unlock(lock_cookie_wifi);
1075 printk("%s: btlock released\n", __FUNCTION__);
1076 #endif /* ENABLE_4335BT_WAR */
1077
1078 if (!dhd_download_fw_on_driverload) {
1079 /* Call customer gpio to turn off power with WL_REG_ON signal */
1080 dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
1081 g_wifi_on = FALSE;
1082 }
1083 }
1084
1085
1086 /**
1087 * Functions for Android WiFi card detection
1088 */
1089 #if defined(CONFIG_WIFI_CONTROL_FUNC)
1090
1091 bool g_wifi_poweron = FALSE;
1092 static int g_wifidev_registered = 0;
1093 static struct semaphore wifi_control_sem;
1094 static struct wifi_platform_data *wifi_control_data = NULL;
1095 static struct resource *wifi_irqres = NULL;
1096 static struct regulator *wifi_regulator = NULL;
1097
1098 static int wifi_add_dev(void);
1099 static void wifi_del_dev(void);
1100
wl_android_wifictrl_func_add(void)1101 int wl_android_wifictrl_func_add(void)
1102 {
1103 int ret = 0;
1104 sema_init(&wifi_control_sem, 0);
1105
1106 ret = wifi_add_dev();
1107 if (ret) {
1108 DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__));
1109 return ret;
1110 }
1111 g_wifidev_registered = 1;
1112
1113 /* Waiting callback after platform_driver_register is done or exit with error */
1114 if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) {
1115 ret = -EINVAL;
1116 DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__));
1117 }
1118
1119 return ret;
1120 }
1121
wl_android_wifictrl_func_del(void)1122 void wl_android_wifictrl_func_del(void)
1123 {
1124 if (g_wifidev_registered)
1125 {
1126 wifi_del_dev();
1127 g_wifidev_registered = 0;
1128 }
1129 }
1130
wl_android_prealloc(int section,unsigned long size)1131 void* wl_android_prealloc(int section, unsigned long size)
1132 {
1133 void *alloc_ptr = NULL;
1134 if (wifi_control_data && wifi_control_data->mem_prealloc) {
1135 alloc_ptr = wifi_control_data->mem_prealloc(section, size);
1136 if (alloc_ptr) {
1137 DHD_INFO(("success alloc section %d\n", section));
1138 if (size != 0L)
1139 bzero(alloc_ptr, size);
1140 return alloc_ptr;
1141 }
1142 }
1143
1144 DHD_ERROR(("can't alloc section %d\n", section));
1145 return NULL;
1146 }
1147
wifi_get_irq_number(unsigned long * irq_flags_ptr)1148 int wifi_get_irq_number(unsigned long *irq_flags_ptr)
1149 {
1150 if (wifi_irqres) {
1151 *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK;
1152 return (int)wifi_irqres->start;
1153 }
1154 #ifdef CUSTOM_OOB_GPIO_NUM
1155 return CUSTOM_OOB_GPIO_NUM;
1156 #else
1157 return -1;
1158 #endif
1159 }
1160
wifi_set_power(int on,unsigned long msec)1161 int wifi_set_power(int on, unsigned long msec)
1162 {
1163 int ret = 0;
1164 DHD_ERROR(("%s = %d\n", __FUNCTION__, on));
1165 if (wifi_regulator && on)
1166 ret = regulator_enable(wifi_regulator);
1167 if (wifi_control_data && wifi_control_data->set_power) {
1168 #ifdef ENABLE_4335BT_WAR
1169 if (on) {
1170 printk("WiFi: trying to acquire BT lock\n");
1171 if (bcm_bt_lock(lock_cookie_wifi) != 0)
1172 printk("** WiFi: timeout in acquiring bt lock**\n");
1173 printk("%s: btlock acquired\n", __FUNCTION__);
1174 }
1175 else {
1176 /* For a exceptional case, release btlock */
1177 bcm_bt_unlock(lock_cookie_wifi);
1178 }
1179 #endif /* ENABLE_4335BT_WAR */
1180 ret = wifi_control_data->set_power(on);
1181 }
1182
1183 if (wifi_regulator && !on)
1184 ret = regulator_disable(wifi_regulator);
1185
1186 if (msec && !ret)
1187 OSL_SLEEP(msec);
1188 return ret;
1189 }
1190
1191 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
wifi_get_mac_addr(unsigned char * buf)1192 int wifi_get_mac_addr(unsigned char *buf)
1193 {
1194 DHD_ERROR(("%s\n", __FUNCTION__));
1195 if (!buf)
1196 return -EINVAL;
1197 if (wifi_control_data && wifi_control_data->get_mac_addr) {
1198 return wifi_control_data->get_mac_addr(buf);
1199 }
1200 return -EOPNOTSUPP;
1201 }
1202 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */
1203
1204 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
wifi_get_country_code(char * ccode)1205 void *wifi_get_country_code(char *ccode)
1206 {
1207 DHD_TRACE(("%s\n", __FUNCTION__));
1208 if (!ccode)
1209 return NULL;
1210 if (wifi_control_data && wifi_control_data->get_country_code) {
1211 return wifi_control_data->get_country_code(ccode);
1212 }
1213 return NULL;
1214 }
1215 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */
1216
wifi_set_carddetect(int on)1217 static int wifi_set_carddetect(int on)
1218 {
1219 DHD_ERROR(("%s = %d\n", __FUNCTION__, on));
1220 if (wifi_control_data && wifi_control_data->set_carddetect) {
1221 wifi_control_data->set_carddetect(on);
1222 }
1223 return 0;
1224 }
1225
get_wifi_irqres_from_of(struct platform_device * pdev)1226 static struct resource *get_wifi_irqres_from_of(struct platform_device *pdev)
1227 {
1228 static struct resource gpio_wifi_irqres;
1229 int irq;
1230 int gpio = of_get_gpio(pdev->dev.of_node, 0);
1231 if (gpio < 0)
1232 return NULL;
1233 irq = gpio_to_irq(gpio);
1234 if (irq < 0)
1235 return NULL;
1236
1237 gpio_wifi_irqres.name = "bcmdhd_wlan_irq";
1238 gpio_wifi_irqres.start = irq;
1239 gpio_wifi_irqres.end = irq;
1240 gpio_wifi_irqres.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL |
1241 IORESOURCE_IRQ_SHAREABLE;
1242
1243 return &gpio_wifi_irqres;
1244 }
1245
wifi_probe(struct platform_device * pdev)1246 static int wifi_probe(struct platform_device *pdev)
1247 {
1248 int err;
1249 struct regulator *regulator;
1250 struct wifi_platform_data *wifi_ctrl =
1251 (struct wifi_platform_data *)(pdev->dev.platform_data);
1252
1253 if (!wifi_ctrl) {
1254 regulator = regulator_get(&pdev->dev, "wlreg_on");
1255 if (IS_ERR(regulator))
1256 return PTR_ERR(regulator);
1257 wifi_regulator = regulator;
1258 }
1259
1260 wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq");
1261 if (wifi_irqres == NULL)
1262 wifi_irqres = platform_get_resource_byname(pdev,
1263 IORESOURCE_IRQ, "bcm4329_wlan_irq");
1264 if (wifi_irqres == NULL)
1265 wifi_irqres = get_wifi_irqres_from_of(pdev);
1266 wifi_control_data = wifi_ctrl;
1267 err = wifi_set_power(1, 0); /* Power On */
1268 if (unlikely(err)) {
1269 DHD_ERROR(("%s: set_power failed. err=%d\n", __FUNCTION__, err));
1270 wifi_set_power(0, WIFI_TURNOFF_DELAY);
1271 /* WL_REG_ON state unknown, Power off forcely */
1272 } else {
1273 wifi_set_carddetect(1); /* CardDetect (0->1) */
1274 g_wifi_poweron = TRUE;
1275 }
1276
1277 up(&wifi_control_sem);
1278 return 0;
1279 }
1280
wifi_remove(struct platform_device * pdev)1281 static int wifi_remove(struct platform_device *pdev)
1282 {
1283 struct wifi_platform_data *wifi_ctrl =
1284 (struct wifi_platform_data *)(pdev->dev.platform_data);
1285 struct io_cfg *cur, *q;
1286
1287 DHD_ERROR(("## %s\n", __FUNCTION__));
1288 wifi_control_data = wifi_ctrl;
1289
1290 if (g_wifi_poweron) {
1291 wifi_set_power(0, WIFI_TURNOFF_DELAY); /* Power Off */
1292 wifi_set_carddetect(0); /* CardDetect (1->0) */
1293 g_wifi_poweron = FALSE;
1294 list_for_each_entry_safe(cur, q, &miracast_resume_list, list) {
1295 list_del(&cur->list);
1296 kfree(cur);
1297 }
1298 }
1299 if (wifi_regulator) {
1300 regulator_put(wifi_regulator);
1301 wifi_regulator = NULL;
1302 }
1303
1304 up(&wifi_control_sem);
1305 return 0;
1306 }
1307
wifi_suspend(struct platform_device * pdev,pm_message_t state)1308 static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
1309 {
1310 DHD_TRACE(("##> %s\n", __FUNCTION__));
1311 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1
1312 bcmsdh_oob_intr_set(0);
1313 #endif /* (OOB_INTR_ONLY) */
1314 return 0;
1315 }
1316
wifi_resume(struct platform_device * pdev)1317 static int wifi_resume(struct platform_device *pdev)
1318 {
1319 DHD_TRACE(("##> %s\n", __FUNCTION__));
1320 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1
1321 if (dhd_os_check_if_up(bcmsdh_get_drvdata()))
1322 bcmsdh_oob_intr_set(1);
1323 #endif /* (OOB_INTR_ONLY) */
1324 return 0;
1325 }
1326
1327 static const struct of_device_id wifi_device_dt_match[] = {
1328 { .compatible = "android,bcmdhd_wlan", },
1329 {},
1330 };
1331 MODULE_DEVICE_TABLE(of, wifi_device_dt_match);
1332
1333 static struct platform_driver wifi_device = {
1334 .probe = wifi_probe,
1335 .remove = wifi_remove,
1336 .suspend = wifi_suspend,
1337 .resume = wifi_resume,
1338 .driver = {
1339 .name = "bcmdhd_wlan",
1340 .of_match_table = wifi_device_dt_match,
1341 }
1342 };
1343
1344 static struct platform_driver wifi_device_legacy = {
1345 .probe = wifi_probe,
1346 .remove = wifi_remove,
1347 .suspend = wifi_suspend,
1348 .resume = wifi_resume,
1349 .driver = {
1350 .name = "bcm4329_wlan",
1351 }
1352 };
1353
wifi_add_dev(void)1354 static int wifi_add_dev(void)
1355 {
1356 int ret = 0;
1357 DHD_TRACE(("## Calling platform_driver_register\n"));
1358 ret = platform_driver_register(&wifi_device);
1359 if (ret)
1360 return ret;
1361
1362 ret = platform_driver_register(&wifi_device_legacy);
1363 return ret;
1364 }
1365
wifi_del_dev(void)1366 static void wifi_del_dev(void)
1367 {
1368 DHD_TRACE(("## Unregister platform_driver_register\n"));
1369 platform_driver_unregister(&wifi_device);
1370 platform_driver_unregister(&wifi_device_legacy);
1371 }
1372 #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */
1373