diff options
Diffstat (limited to 'drivers/net/wireless/bcmdhd/wl_android.c')
-rw-r--r-- | drivers/net/wireless/bcmdhd/wl_android.c | 840 |
1 files changed, 840 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c new file mode 100644 index 00000000000..9ca3d602023 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_android.c | |||
@@ -0,0 +1,840 @@ | |||
1 | /* | ||
2 | * Linux cfg80211 driver - Android related functions | ||
3 | * | ||
4 | * Copyright (C) 1999-2011, 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,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/netdevice.h> | ||
29 | |||
30 | #include <wl_android.h> | ||
31 | #include <wldev_common.h> | ||
32 | #include <wlioctl.h> | ||
33 | #include <bcmutils.h> | ||
34 | #include <linux_osl.h> | ||
35 | #include <dhd_dbg.h> | ||
36 | #include <dngl_stats.h> | ||
37 | #include <dhd.h> | ||
38 | #include <bcmsdbus.h> | ||
39 | #ifdef WL_CFG80211 | ||
40 | #include <wl_cfg80211.h> | ||
41 | #endif | ||
42 | #if defined(CONFIG_WIFI_CONTROL_FUNC) | ||
43 | #include <linux/platform_device.h> | ||
44 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) | ||
45 | #include <linux/wlan_plat.h> | ||
46 | #else | ||
47 | #include <linux/wifi_tiwlan.h> | ||
48 | #endif | ||
49 | #endif /* CONFIG_WIFI_CONTROL_FUNC */ | ||
50 | |||
51 | /* | ||
52 | * Android private command strings, PLEASE define new private commands here | ||
53 | * so they can be updated easily in the future (if needed) | ||
54 | */ | ||
55 | |||
56 | #define CMD_START "START" | ||
57 | #define CMD_STOP "STOP" | ||
58 | #define CMD_SCAN_ACTIVE "SCAN-ACTIVE" | ||
59 | #define CMD_SCAN_PASSIVE "SCAN-PASSIVE" | ||
60 | #define CMD_RSSI "RSSI" | ||
61 | #define CMD_LINKSPEED "LINKSPEED" | ||
62 | #define CMD_RXFILTER_START "RXFILTER-START" | ||
63 | #define CMD_RXFILTER_STOP "RXFILTER-STOP" | ||
64 | #define CMD_RXFILTER_ADD "RXFILTER-ADD" | ||
65 | #define CMD_RXFILTER_REMOVE "RXFILTER-REMOVE" | ||
66 | #define CMD_BTCOEXSCAN_START "BTCOEXSCAN-START" | ||
67 | #define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" | ||
68 | #define CMD_BTCOEXMODE "BTCOEXMODE" | ||
69 | #define CMD_SETSUSPENDOPT "SETSUSPENDOPT" | ||
70 | #define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR" | ||
71 | #define CMD_SETFWPATH "SETFWPATH" | ||
72 | #define CMD_SETBAND "SETBAND" | ||
73 | #define CMD_GETBAND "GETBAND" | ||
74 | #define CMD_COUNTRY "COUNTRY" | ||
75 | #define CMD_P2P_SET_NOA "P2P_SET_NOA" | ||
76 | #define CMD_P2P_GET_NOA "P2P_GET_NOA" | ||
77 | #define CMD_P2P_SET_PS "P2P_SET_PS" | ||
78 | #define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" | ||
79 | |||
80 | |||
81 | #ifdef PNO_SUPPORT | ||
82 | #define CMD_PNOSSIDCLR_SET "PNOSSIDCLR" | ||
83 | #define CMD_PNOSETUP_SET "PNOSETUP " | ||
84 | #define CMD_PNOENABLE_SET "PNOFORCE" | ||
85 | #define CMD_PNODEBUG_SET "PNODEBUG" | ||
86 | |||
87 | #define PNO_TLV_PREFIX 'S' | ||
88 | #define PNO_TLV_VERSION '1' | ||
89 | #define PNO_TLV_SUBVERSION '2' | ||
90 | #define PNO_TLV_RESERVED '0' | ||
91 | #define PNO_TLV_TYPE_SSID_IE 'S' | ||
92 | #define PNO_TLV_TYPE_TIME 'T' | ||
93 | #define PNO_TLV_FREQ_REPEAT 'R' | ||
94 | #define PNO_TLV_FREQ_EXPO_MAX 'M' | ||
95 | |||
96 | typedef struct cmd_tlv { | ||
97 | char prefix; | ||
98 | char version; | ||
99 | char subver; | ||
100 | char reserved; | ||
101 | } cmd_tlv_t; | ||
102 | #endif /* PNO_SUPPORT */ | ||
103 | |||
104 | typedef struct android_wifi_priv_cmd { | ||
105 | char *buf; | ||
106 | int used_len; | ||
107 | int total_len; | ||
108 | } android_wifi_priv_cmd; | ||
109 | |||
110 | /** | ||
111 | * Extern function declarations (TODO: move them to dhd_linux.h) | ||
112 | */ | ||
113 | void dhd_customer_gpio_wlan_ctrl(int onoff); | ||
114 | uint dhd_dev_reset(struct net_device *dev, uint8 flag); | ||
115 | void dhd_dev_init_ioctl(struct net_device *dev); | ||
116 | #ifdef WL_CFG80211 | ||
117 | int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); | ||
118 | int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); | ||
119 | #else | ||
120 | int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) | ||
121 | { return 0; } | ||
122 | int wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) | ||
123 | { return 0; } | ||
124 | int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) | ||
125 | { return 0; } | ||
126 | int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) | ||
127 | { return 0; } | ||
128 | #endif | ||
129 | extern int dhd_os_check_if_up(void *dhdp); | ||
130 | extern void *bcmsdh_get_drvdata(void); | ||
131 | |||
132 | extern bool ap_fw_loaded; | ||
133 | #ifdef CUSTOMER_HW2 | ||
134 | extern char iface_name[IFNAMSIZ]; | ||
135 | #endif | ||
136 | |||
137 | /** | ||
138 | * Local (static) functions and variables | ||
139 | */ | ||
140 | |||
141 | /* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first | ||
142 | * time (only) in dhd_open, subsequential wifi on will be handled by | ||
143 | * wl_android_wifi_on | ||
144 | */ | ||
145 | static int g_wifi_on = TRUE; | ||
146 | |||
147 | /** | ||
148 | * Local (static) function definitions | ||
149 | */ | ||
150 | static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len) | ||
151 | { | ||
152 | int link_speed; | ||
153 | int bytes_written; | ||
154 | int error; | ||
155 | |||
156 | error = wldev_get_link_speed(net, &link_speed); | ||
157 | if (error) | ||
158 | return -1; | ||
159 | |||
160 | /* Convert Kbps to Android Mbps */ | ||
161 | link_speed = link_speed / 1000; | ||
162 | bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed); | ||
163 | DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command)); | ||
164 | return bytes_written; | ||
165 | } | ||
166 | |||
167 | static int wl_android_get_rssi(struct net_device *net, char *command, int total_len) | ||
168 | { | ||
169 | wlc_ssid_t ssid = {0}; | ||
170 | int rssi; | ||
171 | int bytes_written = 0; | ||
172 | int error; | ||
173 | |||
174 | error = wldev_get_rssi(net, &rssi); | ||
175 | if (error) | ||
176 | return -1; | ||
177 | |||
178 | error = wldev_get_ssid(net, &ssid); | ||
179 | if (error) | ||
180 | return -1; | ||
181 | if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) { | ||
182 | DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__)); | ||
183 | } else { | ||
184 | memcpy(command, ssid.SSID, ssid.SSID_len); | ||
185 | bytes_written = ssid.SSID_len; | ||
186 | } | ||
187 | bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi); | ||
188 | DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written)); | ||
189 | return bytes_written; | ||
190 | } | ||
191 | |||
192 | static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len) | ||
193 | { | ||
194 | int suspend_flag; | ||
195 | int ret_now; | ||
196 | int ret = 0; | ||
197 | |||
198 | suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0'; | ||
199 | |||
200 | if (suspend_flag != 0) | ||
201 | suspend_flag = 1; | ||
202 | ret_now = net_os_set_suspend_disable(dev, suspend_flag); | ||
203 | |||
204 | if (ret_now != suspend_flag) { | ||
205 | if (!(ret = net_os_set_suspend(dev, ret_now))) | ||
206 | DHD_INFO(("%s: Suspend Flag %d -> %d\n", | ||
207 | __FUNCTION__, ret_now, suspend_flag)); | ||
208 | else | ||
209 | DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); | ||
210 | } | ||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | static int wl_android_get_band(struct net_device *dev, char *command, int total_len) | ||
215 | { | ||
216 | uint band; | ||
217 | int bytes_written; | ||
218 | int error; | ||
219 | |||
220 | error = wldev_get_band(dev, &band); | ||
221 | if (error) | ||
222 | return -1; | ||
223 | bytes_written = snprintf(command, total_len, "Band %d", band); | ||
224 | return bytes_written; | ||
225 | } | ||
226 | |||
227 | #ifdef PNO_SUPPORT | ||
228 | static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) | ||
229 | { | ||
230 | wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; | ||
231 | int res = -1; | ||
232 | int nssid = 0; | ||
233 | cmd_tlv_t *cmd_tlv_temp; | ||
234 | char *str_ptr; | ||
235 | int tlv_size_left; | ||
236 | int pno_time = 0; | ||
237 | int pno_repeat = 0; | ||
238 | int pno_freq_expo_max = 0; | ||
239 | |||
240 | #ifdef PNO_SET_DEBUG | ||
241 | int i; | ||
242 | char pno_in_example[] = { | ||
243 | 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', | ||
244 | 'S', '1', '2', '0', | ||
245 | 'S', | ||
246 | 0x05, | ||
247 | 'd', 'l', 'i', 'n', 'k', | ||
248 | 'S', | ||
249 | 0x04, | ||
250 | 'G', 'O', 'O', 'G', | ||
251 | 'T', | ||
252 | '0', 'B', | ||
253 | 'R', | ||
254 | '2', | ||
255 | 'M', | ||
256 | '2', | ||
257 | 0x00 | ||
258 | }; | ||
259 | #endif /* PNO_SET_DEBUG */ | ||
260 | |||
261 | DHD_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); | ||
262 | |||
263 | if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { | ||
264 | DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); | ||
265 | goto exit_proc; | ||
266 | } | ||
267 | |||
268 | #ifdef PNO_SET_DEBUG | ||
269 | memcpy(command, pno_in_example, sizeof(pno_in_example)); | ||
270 | for (i = 0; i < sizeof(pno_in_example); i++) | ||
271 | printf("%02X ", command[i]); | ||
272 | printf("\n"); | ||
273 | total_len = sizeof(pno_in_example); | ||
274 | #endif | ||
275 | |||
276 | str_ptr = command + strlen(CMD_PNOSETUP_SET); | ||
277 | tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET); | ||
278 | |||
279 | cmd_tlv_temp = (cmd_tlv_t *)str_ptr; | ||
280 | memset(ssids_local, 0, sizeof(ssids_local)); | ||
281 | |||
282 | if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && | ||
283 | (cmd_tlv_temp->version == PNO_TLV_VERSION) && | ||
284 | (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) { | ||
285 | |||
286 | str_ptr += sizeof(cmd_tlv_t); | ||
287 | tlv_size_left -= sizeof(cmd_tlv_t); | ||
288 | |||
289 | if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, | ||
290 | MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) { | ||
291 | DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); | ||
292 | goto exit_proc; | ||
293 | } else { | ||
294 | if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { | ||
295 | DHD_ERROR(("%s scan duration corrupted field size %d\n", | ||
296 | __FUNCTION__, tlv_size_left)); | ||
297 | goto exit_proc; | ||
298 | } | ||
299 | str_ptr++; | ||
300 | pno_time = simple_strtoul(str_ptr, &str_ptr, 16); | ||
301 | DHD_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); | ||
302 | |||
303 | if (str_ptr[0] != 0) { | ||
304 | if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { | ||
305 | DHD_ERROR(("%s pno repeat : corrupted field\n", | ||
306 | __FUNCTION__)); | ||
307 | goto exit_proc; | ||
308 | } | ||
309 | str_ptr++; | ||
310 | pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); | ||
311 | DHD_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); | ||
312 | if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { | ||
313 | DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", | ||
314 | __FUNCTION__)); | ||
315 | goto exit_proc; | ||
316 | } | ||
317 | str_ptr++; | ||
318 | pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); | ||
319 | DHD_INFO(("%s: pno_freq_expo_max=%d\n", | ||
320 | __FUNCTION__, pno_freq_expo_max)); | ||
321 | } | ||
322 | } | ||
323 | } else { | ||
324 | DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); | ||
325 | goto exit_proc; | ||
326 | } | ||
327 | |||
328 | res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); | ||
329 | |||
330 | exit_proc: | ||
331 | return res; | ||
332 | } | ||
333 | #endif /* PNO_SUPPORT */ | ||
334 | |||
335 | static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) | ||
336 | { | ||
337 | int ret; | ||
338 | int bytes_written = 0; | ||
339 | |||
340 | ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command); | ||
341 | if (ret) | ||
342 | return 0; | ||
343 | bytes_written = sizeof(struct ether_addr); | ||
344 | return bytes_written; | ||
345 | } | ||
346 | |||
347 | /** | ||
348 | * Global function definitions (declared in wl_android.h) | ||
349 | */ | ||
350 | |||
351 | int wl_android_wifi_on(struct net_device *dev) | ||
352 | { | ||
353 | int ret = 0; | ||
354 | |||
355 | printk("%s in\n", __FUNCTION__); | ||
356 | if (!dev) { | ||
357 | DHD_ERROR(("%s: dev is null\n", __FUNCTION__)); | ||
358 | return -EINVAL; | ||
359 | } | ||
360 | |||
361 | dhd_net_if_lock(dev); | ||
362 | if (!g_wifi_on) { | ||
363 | dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); | ||
364 | sdioh_start(NULL, 0); | ||
365 | ret = dhd_dev_reset(dev, FALSE); | ||
366 | sdioh_start(NULL, 1); | ||
367 | if (!ret) | ||
368 | dhd_dev_init_ioctl(dev); | ||
369 | g_wifi_on = 1; | ||
370 | } | ||
371 | dhd_net_if_unlock(dev); | ||
372 | |||
373 | return ret; | ||
374 | } | ||
375 | |||
376 | int wl_android_wifi_off(struct net_device *dev) | ||
377 | { | ||
378 | int ret = 0; | ||
379 | |||
380 | printk("%s in\n", __FUNCTION__); | ||
381 | if (!dev) { | ||
382 | DHD_TRACE(("%s: dev is null\n", __FUNCTION__)); | ||
383 | return -EINVAL; | ||
384 | } | ||
385 | |||
386 | dhd_net_if_lock(dev); | ||
387 | if (g_wifi_on) { | ||
388 | ret = dhd_dev_reset(dev, TRUE); | ||
389 | sdioh_stop(NULL); | ||
390 | dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); | ||
391 | g_wifi_on = 0; | ||
392 | } | ||
393 | dhd_net_if_unlock(dev); | ||
394 | |||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len) | ||
399 | { | ||
400 | if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN) | ||
401 | return -1; | ||
402 | bcm_strncpy_s(fw_path, sizeof(fw_path), | ||
403 | command + strlen(CMD_SETFWPATH) + 1, MOD_PARAM_PATHLEN - 1); | ||
404 | if (strstr(fw_path, "apsta") != NULL) { | ||
405 | DHD_INFO(("GOT APSTA FIRMWARE\n")); | ||
406 | ap_fw_loaded = TRUE; | ||
407 | } else { | ||
408 | DHD_INFO(("GOT STA FIRMWARE\n")); | ||
409 | ap_fw_loaded = FALSE; | ||
410 | } | ||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) | ||
415 | { | ||
416 | int ret = 0; | ||
417 | char *command = NULL; | ||
418 | int bytes_written = 0; | ||
419 | android_wifi_priv_cmd priv_cmd; | ||
420 | |||
421 | net_os_wake_lock(net); | ||
422 | |||
423 | if (!ifr->ifr_data) { | ||
424 | ret = -EINVAL; | ||
425 | goto exit; | ||
426 | } | ||
427 | if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { | ||
428 | ret = -EFAULT; | ||
429 | goto exit; | ||
430 | } | ||
431 | command = kmalloc(priv_cmd.total_len, GFP_KERNEL); | ||
432 | if (!command) | ||
433 | { | ||
434 | DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__)); | ||
435 | ret = -ENOMEM; | ||
436 | goto exit; | ||
437 | } | ||
438 | if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) { | ||
439 | ret = -EFAULT; | ||
440 | goto exit; | ||
441 | } | ||
442 | |||
443 | DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name)); | ||
444 | |||
445 | if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) { | ||
446 | DHD_INFO(("%s, Received regular START command\n", __FUNCTION__)); | ||
447 | bytes_written = wl_android_wifi_on(net); | ||
448 | } | ||
449 | else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) { | ||
450 | bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len); | ||
451 | } | ||
452 | |||
453 | if (!g_wifi_on) { | ||
454 | DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n", | ||
455 | __FUNCTION__, command, ifr->ifr_name)); | ||
456 | ret = 0; | ||
457 | goto exit; | ||
458 | } | ||
459 | |||
460 | if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) { | ||
461 | bytes_written = wl_android_wifi_off(net); | ||
462 | } | ||
463 | else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) { | ||
464 | /* TBD: SCAN-ACTIVE */ | ||
465 | } | ||
466 | else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) { | ||
467 | /* TBD: SCAN-PASSIVE */ | ||
468 | } | ||
469 | else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) { | ||
470 | bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len); | ||
471 | } | ||
472 | else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) { | ||
473 | bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len); | ||
474 | } | ||
475 | else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) { | ||
476 | bytes_written = net_os_set_packet_filter(net, 1); | ||
477 | } | ||
478 | else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) { | ||
479 | bytes_written = net_os_set_packet_filter(net, 0); | ||
480 | } | ||
481 | else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) { | ||
482 | int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0'; | ||
483 | bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num); | ||
484 | } | ||
485 | else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) { | ||
486 | int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0'; | ||
487 | bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num); | ||
488 | } | ||
489 | else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) { | ||
490 | /* TBD: BTCOEXSCAN-START */ | ||
491 | } | ||
492 | else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) { | ||
493 | /* TBD: BTCOEXSCAN-STOP */ | ||
494 | } | ||
495 | else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) { | ||
496 | uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0'; | ||
497 | |||
498 | if (mode == 1) | ||
499 | net_os_set_packet_filter(net, 0); /* DHCP starts */ | ||
500 | else | ||
501 | net_os_set_packet_filter(net, 1); /* DHCP ends */ | ||
502 | #ifdef WL_CFG80211 | ||
503 | bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command); | ||
504 | #endif | ||
505 | } | ||
506 | else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { | ||
507 | bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); | ||
508 | } | ||
509 | else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { | ||
510 | uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; | ||
511 | bytes_written = wldev_set_band(net, band); | ||
512 | } | ||
513 | else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) { | ||
514 | bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); | ||
515 | } | ||
516 | else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { | ||
517 | char *country_code = command + strlen(CMD_COUNTRY) + 1; | ||
518 | bytes_written = wldev_set_country(net, country_code); | ||
519 | } | ||
520 | #ifdef PNO_SUPPORT | ||
521 | else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { | ||
522 | bytes_written = dhd_dev_pno_reset(net); | ||
523 | } | ||
524 | else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) { | ||
525 | bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len); | ||
526 | } | ||
527 | else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) { | ||
528 | uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0'; | ||
529 | bytes_written = dhd_dev_pno_enable(net, pfn_enabled); | ||
530 | } | ||
531 | #endif | ||
532 | else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { | ||
533 | bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); | ||
534 | } | ||
535 | else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) { | ||
536 | int skip = strlen(CMD_P2P_SET_NOA) + 1; | ||
537 | bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, | ||
538 | priv_cmd.total_len - skip); | ||
539 | } | ||
540 | else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) { | ||
541 | bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len); | ||
542 | } | ||
543 | else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) { | ||
544 | int skip = strlen(CMD_P2P_SET_PS) + 1; | ||
545 | bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip, | ||
546 | priv_cmd.total_len - skip); | ||
547 | } | ||
548 | #ifdef WL_CFG80211 | ||
549 | else if (strnicmp(command, CMD_SET_AP_WPS_P2P_IE, | ||
550 | strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) { | ||
551 | int skip = strlen(CMD_SET_AP_WPS_P2P_IE) + 3; | ||
552 | bytes_written = wl_cfg80211_set_wps_p2p_ie(net, command + skip, | ||
553 | priv_cmd.total_len - skip, *(command + skip - 2) - '0'); | ||
554 | } | ||
555 | #endif /* WL_CFG80211 */ | ||
556 | else { | ||
557 | DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command)); | ||
558 | snprintf(command, 3, "OK"); | ||
559 | bytes_written = strlen("OK"); | ||
560 | } | ||
561 | |||
562 | if (bytes_written > 0) { | ||
563 | if (bytes_written > priv_cmd.total_len) { | ||
564 | DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written)); | ||
565 | bytes_written = priv_cmd.total_len; | ||
566 | } else { | ||
567 | bytes_written++; | ||
568 | } | ||
569 | priv_cmd.used_len = bytes_written; | ||
570 | if (copy_to_user(priv_cmd.buf, command, bytes_written)) { | ||
571 | DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__)); | ||
572 | ret = -EFAULT; | ||
573 | } | ||
574 | } else { | ||
575 | ret = bytes_written; | ||
576 | } | ||
577 | |||
578 | exit: | ||
579 | net_os_wake_unlock(net); | ||
580 | if (command) { | ||
581 | kfree(command); | ||
582 | } | ||
583 | |||
584 | return ret; | ||
585 | } | ||
586 | |||
587 | int wl_android_init(void) | ||
588 | { | ||
589 | int ret = 0; | ||
590 | |||
591 | dhd_msg_level = DHD_ERROR_VAL; | ||
592 | #ifdef ENABLE_INSMOD_NO_FW_LOAD | ||
593 | dhd_download_fw_on_driverload = FALSE; | ||
594 | #endif /* ENABLE_INSMOD_NO_FW_LOAD */ | ||
595 | #ifdef CUSTOMER_HW2 | ||
596 | if (!iface_name[0]) { | ||
597 | memset(iface_name, 0, IFNAMSIZ); | ||
598 | bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); | ||
599 | } | ||
600 | #endif /* CUSTOMER_HW2 */ | ||
601 | return ret; | ||
602 | } | ||
603 | |||
604 | int wl_android_exit(void) | ||
605 | { | ||
606 | int ret = 0; | ||
607 | |||
608 | return ret; | ||
609 | } | ||
610 | |||
611 | int wl_android_post_init(void) | ||
612 | { | ||
613 | struct net_device *ndev; | ||
614 | int ret = 0; | ||
615 | char buf[IFNAMSIZ]; | ||
616 | if (!dhd_download_fw_on_driverload) { | ||
617 | /* Call customer gpio to turn off power with WL_REG_ON signal */ | ||
618 | dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); | ||
619 | g_wifi_on = 0; | ||
620 | } else { | ||
621 | memset(buf, 0, IFNAMSIZ); | ||
622 | #ifdef CUSTOMER_HW2 | ||
623 | snprintf(buf, IFNAMSIZ, "%s%d", iface_name, 0); | ||
624 | #else | ||
625 | snprintf(buf, IFNAMSIZ, "%s%d", "eth", 0); | ||
626 | #endif | ||
627 | if ((ndev = dev_get_by_name (&init_net, buf)) != NULL) { | ||
628 | dhd_dev_init_ioctl(ndev); | ||
629 | dev_put(ndev); | ||
630 | } | ||
631 | } | ||
632 | return ret; | ||
633 | } | ||
634 | |||
635 | /** | ||
636 | * Functions for Android WiFi card detection | ||
637 | */ | ||
638 | #if defined(CONFIG_WIFI_CONTROL_FUNC) | ||
639 | |||
640 | static int g_wifidev_registered = 0; | ||
641 | static struct semaphore wifi_control_sem; | ||
642 | static struct wifi_platform_data *wifi_control_data = NULL; | ||
643 | static struct resource *wifi_irqres = NULL; | ||
644 | |||
645 | static int wifi_add_dev(void); | ||
646 | static void wifi_del_dev(void); | ||
647 | |||
648 | int wl_android_wifictrl_func_add(void) | ||
649 | { | ||
650 | int ret = 0; | ||
651 | sema_init(&wifi_control_sem, 0); | ||
652 | |||
653 | ret = wifi_add_dev(); | ||
654 | if (ret) { | ||
655 | DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__)); | ||
656 | return ret; | ||
657 | } | ||
658 | g_wifidev_registered = 1; | ||
659 | |||
660 | /* Waiting callback after platform_driver_register is done or exit with error */ | ||
661 | if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) { | ||
662 | ret = -EINVAL; | ||
663 | DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__)); | ||
664 | } | ||
665 | |||
666 | return ret; | ||
667 | } | ||
668 | |||
669 | void wl_android_wifictrl_func_del(void) | ||
670 | { | ||
671 | if (g_wifidev_registered) | ||
672 | { | ||
673 | wifi_del_dev(); | ||
674 | g_wifidev_registered = 0; | ||
675 | } | ||
676 | } | ||
677 | |||
678 | void* wl_android_prealloc(int section, unsigned long size) | ||
679 | { | ||
680 | void *alloc_ptr = NULL; | ||
681 | if (wifi_control_data && wifi_control_data->mem_prealloc) { | ||
682 | alloc_ptr = wifi_control_data->mem_prealloc(section, size); | ||
683 | if (alloc_ptr) { | ||
684 | DHD_INFO(("success alloc section %d\n", section)); | ||
685 | bzero(alloc_ptr, size); | ||
686 | return alloc_ptr; | ||
687 | } | ||
688 | } | ||
689 | |||
690 | DHD_ERROR(("can't alloc section %d\n", section)); | ||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | int wifi_get_irq_number(unsigned long *irq_flags_ptr) | ||
695 | { | ||
696 | if (wifi_irqres) { | ||
697 | *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK; | ||
698 | return (int)wifi_irqres->start; | ||
699 | } | ||
700 | #ifdef CUSTOM_OOB_GPIO_NUM | ||
701 | return CUSTOM_OOB_GPIO_NUM; | ||
702 | #else | ||
703 | return -1; | ||
704 | #endif | ||
705 | } | ||
706 | |||
707 | int wifi_set_power(int on, unsigned long msec) | ||
708 | { | ||
709 | DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); | ||
710 | if (wifi_control_data && wifi_control_data->set_power) { | ||
711 | wifi_control_data->set_power(on); | ||
712 | } | ||
713 | if (msec) | ||
714 | msleep(msec); | ||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) | ||
719 | int wifi_get_mac_addr(unsigned char *buf) | ||
720 | { | ||
721 | DHD_ERROR(("%s\n", __FUNCTION__)); | ||
722 | if (!buf) | ||
723 | return -EINVAL; | ||
724 | if (wifi_control_data && wifi_control_data->get_mac_addr) { | ||
725 | return wifi_control_data->get_mac_addr(buf); | ||
726 | } | ||
727 | return -EOPNOTSUPP; | ||
728 | } | ||
729 | #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */ | ||
730 | |||
731 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) | ||
732 | void *wifi_get_country_code(char *ccode) | ||
733 | { | ||
734 | DHD_TRACE(("%s\n", __FUNCTION__)); | ||
735 | if (!ccode) | ||
736 | return NULL; | ||
737 | if (wifi_control_data && wifi_control_data->get_country_code) { | ||
738 | return wifi_control_data->get_country_code(ccode); | ||
739 | } | ||
740 | return NULL; | ||
741 | } | ||
742 | #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */ | ||
743 | |||
744 | static int wifi_set_carddetect(int on) | ||
745 | { | ||
746 | DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); | ||
747 | if (wifi_control_data && wifi_control_data->set_carddetect) { | ||
748 | wifi_control_data->set_carddetect(on); | ||
749 | } | ||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | static int wifi_probe(struct platform_device *pdev) | ||
754 | { | ||
755 | struct wifi_platform_data *wifi_ctrl = | ||
756 | (struct wifi_platform_data *)(pdev->dev.platform_data); | ||
757 | |||
758 | DHD_ERROR(("## %s\n", __FUNCTION__)); | ||
759 | wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq"); | ||
760 | if (wifi_irqres == NULL) | ||
761 | wifi_irqres = platform_get_resource_byname(pdev, | ||
762 | IORESOURCE_IRQ, "bcm4329_wlan_irq"); | ||
763 | wifi_control_data = wifi_ctrl; | ||
764 | |||
765 | wifi_set_power(1, 0); /* Power On */ | ||
766 | wifi_set_carddetect(1); /* CardDetect (0->1) */ | ||
767 | |||
768 | up(&wifi_control_sem); | ||
769 | return 0; | ||
770 | } | ||
771 | |||
772 | static int wifi_remove(struct platform_device *pdev) | ||
773 | { | ||
774 | struct wifi_platform_data *wifi_ctrl = | ||
775 | (struct wifi_platform_data *)(pdev->dev.platform_data); | ||
776 | |||
777 | DHD_ERROR(("## %s\n", __FUNCTION__)); | ||
778 | wifi_control_data = wifi_ctrl; | ||
779 | |||
780 | wifi_set_power(0, 0); /* Power Off */ | ||
781 | wifi_set_carddetect(0); /* CardDetect (1->0) */ | ||
782 | |||
783 | up(&wifi_control_sem); | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | static int wifi_suspend(struct platform_device *pdev, pm_message_t state) | ||
788 | { | ||
789 | DHD_TRACE(("##> %s\n", __FUNCTION__)); | ||
790 | #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) | ||
791 | bcmsdh_oob_intr_set(0); | ||
792 | #endif | ||
793 | return 0; | ||
794 | } | ||
795 | |||
796 | static int wifi_resume(struct platform_device *pdev) | ||
797 | { | ||
798 | DHD_TRACE(("##> %s\n", __FUNCTION__)); | ||
799 | #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) | ||
800 | if (dhd_os_check_if_up(bcmsdh_get_drvdata())) | ||
801 | bcmsdh_oob_intr_set(1); | ||
802 | #endif | ||
803 | return 0; | ||
804 | } | ||
805 | |||
806 | static struct platform_driver wifi_device = { | ||
807 | .probe = wifi_probe, | ||
808 | .remove = wifi_remove, | ||
809 | .suspend = wifi_suspend, | ||
810 | .resume = wifi_resume, | ||
811 | .driver = { | ||
812 | .name = "bcmdhd_wlan", | ||
813 | } | ||
814 | }; | ||
815 | |||
816 | static struct platform_driver wifi_device_legacy = { | ||
817 | .probe = wifi_probe, | ||
818 | .remove = wifi_remove, | ||
819 | .suspend = wifi_suspend, | ||
820 | .resume = wifi_resume, | ||
821 | .driver = { | ||
822 | .name = "bcm4329_wlan", | ||
823 | } | ||
824 | }; | ||
825 | |||
826 | static int wifi_add_dev(void) | ||
827 | { | ||
828 | DHD_TRACE(("## Calling platform_driver_register\n")); | ||
829 | platform_driver_register(&wifi_device); | ||
830 | platform_driver_register(&wifi_device_legacy); | ||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | static void wifi_del_dev(void) | ||
835 | { | ||
836 | DHD_TRACE(("## Unregister platform_driver_register\n")); | ||
837 | platform_driver_unregister(&wifi_device); | ||
838 | platform_driver_unregister(&wifi_device_legacy); | ||
839 | } | ||
840 | #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ | ||