6826a4c0d74819045e9f23a6b0325ec2a5369e00
[linux-2.6.git] / drivers / net / wireless / bcmdhd / wl_android.c
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
40 #if defined(CONFIG_WIFI_CONTROL_FUNC)
41 #include <linux/platform_device.h>
42 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
43 #include <linux/wlan_plat.h>
44 #else
45 #include <linux/wifi_tiwlan.h>
46 #endif
47 #endif /* CONFIG_WIFI_CONTROL_FUNC */
48
49 /*
50  * Android private command strings, PLEASE define new private commands here
51  * so they can be updated easily in the future (if needed)
52  */
53
54 #define CMD_START               "START"
55 #define CMD_STOP                "STOP"
56 #define CMD_SCAN_ACTIVE         "SCAN-ACTIVE"
57 #define CMD_SCAN_PASSIVE        "SCAN-PASSIVE"
58 #define CMD_RSSI                "RSSI"
59 #define CMD_LINKSPEED           "LINKSPEED"
60 #define CMD_RXFILTER_START      "RXFILTER-START"
61 #define CMD_RXFILTER_STOP       "RXFILTER-STOP"
62 #define CMD_RXFILTER_ADD        "RXFILTER-ADD"
63 #define CMD_RXFILTER_REMOVE     "RXFILTER-REMOVE"
64 #define CMD_BTCOEXSCAN_START    "BTCOEXSCAN-START"
65 #define CMD_BTCOEXSCAN_STOP     "BTCOEXSCAN-STOP"
66 #define CMD_BTCOEXMODE          "BTCOEXMODE"
67 #define CMD_SETSUSPENDOPT       "SETSUSPENDOPT"
68 #define CMD_SETFWPATH           "SETFWPATH"
69 #define CMD_SETBAND             "SETBAND"
70 #define CMD_GETBAND             "GETBAND"
71 #define CMD_COUNTRY             "COUNTRY"
72
73 typedef struct android_wifi_priv_cmd {
74         char *buf;
75         int used_len;
76         int total_len;
77 } android_wifi_priv_cmd;
78
79 /**
80  * Extern function declarations (TODO: move them to dhd_linux.h)
81  */
82 void dhd_customer_gpio_wlan_ctrl(int onoff);
83 uint dhd_dev_reset(struct net_device *dev, uint8 flag);
84 void dhd_dev_init_ioctl(struct net_device *dev);
85 int net_os_set_dtim_skip(struct net_device *dev, int val);
86 int net_os_set_suspend_disable(struct net_device *dev, int val);
87 int net_os_set_suspend(struct net_device *dev, int val);
88
89 extern bool ap_fw_loaded;
90 #ifdef CUSTOMER_HW2
91 extern char iface_name[IFNAMSIZ];
92 #endif
93
94 /**
95  * Local (static) functions and variables
96  */
97
98 /* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first
99  * time (only) in dhd_open, subsequential wifi on will be handled by
100  * wl_android_wifi_on
101  */
102 static int g_wifi_on = TRUE;
103
104 /**
105  * Local (static) function definitions
106  */
107 static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len)
108 {
109         int link_speed;
110         int bytes_written;
111         int error;
112
113         error = wldev_get_link_speed(net, &link_speed);
114         if (error)
115                 return -1;
116
117         /* Convert Kbps to Android Mbps */
118         link_speed = link_speed / 1000;
119         bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed);
120         DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command));
121         return bytes_written;
122 }
123
124 static int wl_android_get_rssi(struct net_device *net, char *command, int total_len)
125 {
126         wlc_ssid_t ssid;
127         int rssi;
128         int bytes_written;
129         int error;
130
131         error = wldev_get_rssi(net, &rssi);
132         if (error)
133                 return -1;
134
135         error = wldev_get_ssid(net, &ssid);
136         if (error)
137                 return -1;
138         memcpy(command, ssid.SSID, ssid.SSID_len);
139         bytes_written = ssid.SSID_len;
140         bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi);
141         DHD_INFO(("%s: command result is %s \n", __FUNCTION__, command));
142         return bytes_written;
143 }
144
145 static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len)
146 {
147         int suspend_flag;
148         int ret_now;
149         int ret = 0;
150
151         suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0';
152
153         if (suspend_flag != 0)
154                 suspend_flag = 1;
155         ret_now = net_os_set_suspend_disable(dev, suspend_flag);
156
157         if (ret_now != suspend_flag) {
158                 if (!(ret = net_os_set_suspend(dev, ret_now)))
159                         DHD_INFO(("%s: Suspend Flag %d -> %d\n",
160                                         __FUNCTION__, ret_now, suspend_flag));
161                 else
162                         DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret));
163         }
164         return ret;
165 }
166
167 static int wl_android_get_band(struct net_device *dev, char *command, int total_len)
168 {
169         uint band;
170         int bytes_written;
171         int error;
172
173         error = wldev_get_band(dev, &band);
174         if (error)
175                 return -1;
176         bytes_written = snprintf(command, total_len, "Band %d", band);
177         return bytes_written;
178 }
179
180 /**
181  * Global function definitions (declared in wl_android.h)
182  */
183
184 int wl_android_wifi_on(struct net_device *dev)
185 {
186         int ret = 0;
187
188         printk("%s in\n", __FUNCTION__);
189         if (!dev) {
190                 DHD_ERROR(("%s: dev is null\n", __FUNCTION__));
191                 return -EINVAL;
192         }
193
194         dhd_net_if_lock(dev);
195         if (!g_wifi_on) {
196                 dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON);
197                 sdioh_start(NULL, 0);
198                 ret = dhd_dev_reset(dev, FALSE);
199                 sdioh_start(NULL, 1);
200                 dhd_dev_init_ioctl(dev);
201                 g_wifi_on = 1;
202         }
203         dhd_net_if_unlock(dev);
204
205         return ret;
206 }
207
208 int wl_android_wifi_off(struct net_device *dev)
209 {
210         int ret = 0;
211
212         printk("%s in\n", __FUNCTION__);
213         if (!dev) {
214                 DHD_TRACE(("%s: dev is null\n", __FUNCTION__));
215                 return -EINVAL;
216         }
217
218         dhd_net_if_lock(dev);
219         if (g_wifi_on) {
220                 dhd_dev_reset(dev, 1);
221                 dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
222                 sdioh_stop(NULL);
223                 /* clean up dtim_skip setting */
224                 net_os_set_dtim_skip(dev, TRUE);
225                 g_wifi_on = 0;
226         }
227         dhd_net_if_unlock(dev);
228
229         return ret;
230 }
231
232 static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len)
233 {
234         if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN)
235                 return -1;
236         bcm_strncpy_s(fw_path, sizeof(fw_path),
237                 command + strlen(CMD_SETFWPATH) + 1, MOD_PARAM_PATHLEN - 1);
238         if (strstr(fw_path, "apsta") != NULL) {
239                 DHD_INFO(("GOT APSTA FIRMWARE\n"));
240                 ap_fw_loaded = TRUE;
241         } else {
242                 DHD_INFO(("GOT STA FIRMWARE\n"));
243                 ap_fw_loaded = FALSE;
244         }
245         return 0;
246 }
247
248 int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
249 {
250         int ret = 0;
251         char *command = NULL;
252         int bytes_written = 0;
253         android_wifi_priv_cmd *priv_cmd;
254
255         /* net_os_wake_lock(dev); */
256
257         priv_cmd = (android_wifi_priv_cmd*)ifr->ifr_data;
258         if (!priv_cmd)
259         {
260                 ret = -EINVAL;
261                 goto exit;
262         }
263         command = kmalloc(priv_cmd->total_len, GFP_KERNEL);
264         if (!command)
265         {
266                 DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__));
267                 ret = -ENOMEM;
268                 goto exit;
269         }
270         if (copy_from_user(command, priv_cmd->buf, priv_cmd->total_len)) {
271                 ret = -EFAULT;
272                 goto exit;
273         }
274
275         DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name));
276
277         if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) {
278                 DHD_INFO(("%s, Received regular START command\n", __FUNCTION__));
279                 bytes_written = wl_android_wifi_on(net);
280         }
281         else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) {
282                 bytes_written = wl_android_set_fwpath(net, command, priv_cmd->total_len);
283         }
284
285         if (!g_wifi_on) {
286                 DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n",
287                                 __FUNCTION__, command, ifr->ifr_name));
288                 ret = 0;
289                 goto exit;
290         }
291
292         if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) {
293                 bytes_written = wl_android_wifi_off(net);
294         }
295         else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) {
296                 /* TBD: SCAN-ACTIVE */
297         }
298         else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) {
299                 /* TBD: SCAN-PASSIVE */
300         }
301         else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) {
302                 bytes_written = wl_android_get_rssi(net, command, priv_cmd->total_len);
303         }
304         else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) {
305                 bytes_written = wl_android_get_link_speed(net, command, priv_cmd->total_len);
306         }
307         else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) {
308                 bytes_written = net_os_set_packet_filter(net, 1);
309         }
310         else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) {
311                 bytes_written = net_os_set_packet_filter(net, 0);
312         }
313         else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) {
314                 int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0';
315                 bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num);
316         }
317         else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) {
318                 int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0';
319                 bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num);
320         }
321         else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) {
322                 /* TBD: BTCOEXSCAN-START */
323         }
324         else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) {
325                 /* TBD: BTCOEXSCAN-STOP */
326         }
327         else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) {
328                 /* TBD: BTCOEXMODE */
329         }
330         else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) {
331                 bytes_written = wl_android_set_suspendopt(net, command, priv_cmd->total_len);
332         }
333         else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) {
334                 uint band = *(command + strlen(CMD_SETBAND) + 1) - '0';
335                 bytes_written = wldev_set_band(net, band);
336         }
337         else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) {
338                 bytes_written = wl_android_get_band(net, command, priv_cmd->total_len);
339         }
340         else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) {
341                 char *country_code = command + strlen(CMD_COUNTRY) + 1;
342                 bytes_written = wldev_set_country(net, country_code);
343         } else {
344                 DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command));
345                 snprintf(command, 3, "OK");
346                 bytes_written = strlen("OK");
347         }
348
349         if (bytes_written > 0) {
350                 bytes_written++;
351                 priv_cmd->used_len = bytes_written;
352                 if (copy_to_user(priv_cmd->buf, command, bytes_written)) {
353                         DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__));
354                         ret = -EFAULT;
355                 }
356         } else {
357                 ret = bytes_written;
358         }
359
360 exit:
361         /* net_os_wake_unlock(dev); */
362         if (command) {
363                 kfree(command);
364         }
365
366         return ret;
367 }
368
369 int wl_android_init(void)
370 {
371         int ret = 0;
372
373         dhd_msg_level = DHD_ERROR_VAL;
374 #ifdef ENABLE_INSMOD_NO_FW_LOAD
375         dhd_download_fw_on_driverload = FALSE;
376 #endif /* ENABLE_INSMOD_NO_FW_LOAD */
377 #ifdef CUSTOMER_HW2
378         if (!iface_name[0])
379                 bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ);
380 #endif /* CUSTOMER_HW2 */
381         return ret;
382 }
383
384 int wl_android_exit(void)
385 {
386         int ret = 0;
387
388         return ret;
389 }
390
391
392 /**
393  * Functions for Android WiFi card detection
394  */
395 #if defined(CONFIG_WIFI_CONTROL_FUNC)
396
397 static int g_wifidev_registered = 0;
398 static struct semaphore wifi_control_sem;
399 static struct wifi_platform_data *wifi_control_data = NULL;
400 static struct resource *wifi_irqres = NULL;
401
402 static int wifi_add_dev(void);
403 static void wifi_del_dev(void);
404
405 int wl_android_wifictrl_func_add(void)
406 {
407         int ret = 0;
408         sema_init(&wifi_control_sem, 0);
409
410         ret = wifi_add_dev();
411         if (ret) {
412                 DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__));
413                 return ret;
414         }
415         g_wifidev_registered = 1;
416
417         /* Waiting callback after platform_driver_register is done or exit with error */
418         if (down_timeout(&wifi_control_sem,  msecs_to_jiffies(1000)) != 0) {
419                 ret = -EINVAL;
420                 DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__));
421         }
422
423         return ret;
424 }
425
426 void wl_android_wifictrl_func_del(void)
427 {
428         if (g_wifidev_registered)
429         {
430                 wifi_del_dev();
431                 g_wifidev_registered = 0;
432         }
433 }
434
435 void* wl_android_prealloc(int section, unsigned long size)
436 {
437         void *alloc_ptr = NULL;
438         if (wifi_control_data && wifi_control_data->mem_prealloc) {
439                 alloc_ptr = wifi_control_data->mem_prealloc(section, size);
440                 if (alloc_ptr) {
441                         DHD_INFO(("success alloc section %d\n", section));
442                         bzero(alloc_ptr, size);
443                         return alloc_ptr;
444                 }
445         }
446
447         DHD_ERROR(("can't alloc section %d\n", section));
448         return 0;
449 }
450
451 int wifi_get_irq_number(unsigned long *irq_flags_ptr)
452 {
453         if (wifi_irqres) {
454                 *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK;
455                 return (int)wifi_irqres->start;
456         }
457 #ifdef CUSTOM_OOB_GPIO_NUM
458         return CUSTOM_OOB_GPIO_NUM;
459 #else
460         return -1;
461 #endif
462 }
463
464 int wifi_set_power(int on, unsigned long msec)
465 {
466         DHD_ERROR(("%s = %d\n", __FUNCTION__, on));
467         if (wifi_control_data && wifi_control_data->set_power) {
468                 wifi_control_data->set_power(on);
469         }
470         if (msec)
471                 mdelay(msec);
472         return 0;
473 }
474
475 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
476 int wifi_get_mac_addr(unsigned char *buf)
477 {
478         DHD_ERROR(("%s\n", __FUNCTION__));
479         if (!buf)
480                 return -EINVAL;
481         if (wifi_control_data && wifi_control_data->get_mac_addr) {
482                 return wifi_control_data->get_mac_addr(buf);
483         }
484         return -EOPNOTSUPP;
485 }
486 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */
487
488 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
489 void *wifi_get_country_code(char *ccode)
490 {
491         DHD_TRACE(("%s\n", __FUNCTION__));
492         if (!ccode)
493                 return NULL;
494         if (wifi_control_data && wifi_control_data->get_country_code) {
495                 return wifi_control_data->get_country_code(ccode);
496         }
497         return NULL;
498 }
499 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */
500
501 static int wifi_set_carddetect(int on)
502 {
503         DHD_ERROR(("%s = %d\n", __FUNCTION__, on));
504         if (wifi_control_data && wifi_control_data->set_carddetect) {
505                 wifi_control_data->set_carddetect(on);
506         }
507         return 0;
508 }
509
510 static int wifi_probe(struct platform_device *pdev)
511 {
512         struct wifi_platform_data *wifi_ctrl =
513                 (struct wifi_platform_data *)(pdev->dev.platform_data);
514
515         DHD_ERROR(("## %s\n", __FUNCTION__));
516         wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq");
517         if (wifi_irqres == NULL)
518                 wifi_irqres = platform_get_resource_byname(pdev,
519                         IORESOURCE_IRQ, "bcm4329_wlan_irq");
520         wifi_control_data = wifi_ctrl;
521
522         wifi_set_power(1, 0);   /* Power On */
523         wifi_set_carddetect(1); /* CardDetect (0->1) */
524
525         up(&wifi_control_sem);
526         return 0;
527 }
528
529 static int wifi_remove(struct platform_device *pdev)
530 {
531         struct wifi_platform_data *wifi_ctrl =
532                 (struct wifi_platform_data *)(pdev->dev.platform_data);
533
534         DHD_ERROR(("## %s\n", __FUNCTION__));
535         wifi_control_data = wifi_ctrl;
536
537         wifi_set_power(0, 0);   /* Power Off */
538         wifi_set_carddetect(0); /* CardDetect (1->0) */
539
540         up(&wifi_control_sem);
541         return 0;
542 }
543
544 static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
545 {
546         DHD_TRACE(("##> %s\n", __FUNCTION__));
547 #if defined(OOB_INTR_ONLY)
548         bcmsdh_oob_intr_set(0);
549 #endif /* (OOB_INTR_ONLY) */
550         return 0;
551 }
552
553 static int wifi_resume(struct platform_device *pdev)
554 {
555         DHD_TRACE(("##> %s\n", __FUNCTION__));
556 #if defined(OOB_INTR_ONLY)
557         bcmsdh_oob_intr_set(1);
558 #endif /* (OOB_INTR_ONLY) */
559         return 0;
560 }
561
562 static struct platform_driver wifi_device = {
563         .probe          = wifi_probe,
564         .remove         = wifi_remove,
565         .suspend        = wifi_suspend,
566         .resume         = wifi_resume,
567         .driver         = {
568         .name   = "bcmdhd_wlan",
569         }
570 };
571
572 static struct platform_driver wifi_device_legacy = {
573         .probe          = wifi_probe,
574         .remove         = wifi_remove,
575         .suspend        = wifi_suspend,
576         .resume         = wifi_resume,
577         .driver         = {
578         .name   = "bcm4329_wlan",
579         }
580 };
581
582 static int wifi_add_dev(void)
583 {
584         DHD_TRACE(("## Calling platform_driver_register\n"));
585         platform_driver_register(&wifi_device);
586         platform_driver_register(&wifi_device_legacy);
587         return 0;
588 }
589
590 static void wifi_del_dev(void)
591 {
592         DHD_TRACE(("## Unregister platform_driver_register\n"));
593         platform_driver_unregister(&wifi_device);
594         platform_driver_unregister(&wifi_device_legacy);
595 }
596 #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */