876fc080ac1dcd0165d2a33ed1457725f6923c25
[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_P2P_DEV_ADDR        "P2P_DEV_ADDR"
69 #define CMD_SETFWPATH           "SETFWPATH"
70 #define CMD_SETBAND             "SETBAND"
71 #define CMD_GETBAND             "GETBAND"
72 #define CMD_COUNTRY             "COUNTRY"
73
74 #ifdef PNO_SUPPORT
75 #define CMD_PNOSSIDCLR_SET      "PNOSSIDCLR"
76 #define CMD_PNOSETUP_SET        "PNOSETUP "
77 #define CMD_PNOENABLE_SET       "PNOFORCE"
78 #define CMD_PNODEBUG_SET        "PNODEBUG"
79
80 #define PNO_TLV_PREFIX                  'S'
81 #define PNO_TLV_VERSION                 '1'
82 #define PNO_TLV_SUBVERSION              '2'
83 #define PNO_TLV_RESERVED                '0'
84 #define PNO_TLV_TYPE_SSID_IE            'S'
85 #define PNO_TLV_TYPE_TIME               'T'
86 #define PNO_TLV_FREQ_REPEAT             'R'
87 #define PNO_TLV_FREQ_EXPO_MAX           'M'
88
89 typedef struct cmd_tlv {
90         char prefix;
91         char version;
92         char subver;
93         char reserved;
94 } cmd_tlv_t;
95 #endif
96
97 typedef struct android_wifi_priv_cmd {
98         char *buf;
99         int used_len;
100         int total_len;
101 } android_wifi_priv_cmd;
102
103 /**
104  * Extern function declarations (TODO: move them to dhd_linux.h)
105  */
106 void dhd_customer_gpio_wlan_ctrl(int onoff);
107 uint dhd_dev_reset(struct net_device *dev, uint8 flag);
108 void dhd_dev_init_ioctl(struct net_device *dev);
109 #ifdef WL_CFG80211
110 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr);
111 #else
112 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) { return 0; }
113 #endif
114
115 extern bool ap_fw_loaded;
116 #ifdef CUSTOMER_HW2
117 extern char iface_name[IFNAMSIZ];
118 #endif
119
120 /**
121  * Local (static) functions and variables
122  */
123
124 /* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first
125  * time (only) in dhd_open, subsequential wifi on will be handled by
126  * wl_android_wifi_on
127  */
128 static int g_wifi_on = TRUE;
129
130 /**
131  * Local (static) function definitions
132  */
133 static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len)
134 {
135         int link_speed;
136         int bytes_written;
137         int error;
138
139         error = wldev_get_link_speed(net, &link_speed);
140         if (error)
141                 return -1;
142
143         /* Convert Kbps to Android Mbps */
144         link_speed = link_speed / 1000;
145         bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed);
146         DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command));
147         return bytes_written;
148 }
149
150 static int wl_android_get_rssi(struct net_device *net, char *command, int total_len)
151 {
152         wlc_ssid_t ssid;
153         int rssi;
154         int bytes_written;
155         int error;
156
157         error = wldev_get_rssi(net, &rssi);
158         if (error)
159                 return -1;
160
161         error = wldev_get_ssid(net, &ssid);
162         if (error)
163                 return -1;
164         memcpy(command, ssid.SSID, ssid.SSID_len);
165         bytes_written = ssid.SSID_len;
166         bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi);
167         DHD_INFO(("%s: command result is %s \n", __FUNCTION__, command));
168         return bytes_written;
169 }
170
171 static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len)
172 {
173         int suspend_flag;
174         int ret_now;
175         int ret = 0;
176
177         suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0';
178
179         if (suspend_flag != 0)
180                 suspend_flag = 1;
181         ret_now = net_os_set_suspend_disable(dev, suspend_flag);
182
183         if (ret_now != suspend_flag) {
184                 if (!(ret = net_os_set_suspend(dev, ret_now)))
185                         DHD_INFO(("%s: Suspend Flag %d -> %d\n",
186                                         __FUNCTION__, ret_now, suspend_flag));
187                 else
188                         DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret));
189         }
190         return ret;
191 }
192
193 static int wl_android_get_band(struct net_device *dev, char *command, int total_len)
194 {
195         uint band;
196         int bytes_written;
197         int error;
198
199         error = wldev_get_band(dev, &band);
200         if (error)
201                 return -1;
202         bytes_written = snprintf(command, total_len, "Band %d", band);
203         return bytes_written;
204 }
205
206 #ifdef PNO_SUPPORT
207 static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len)
208 {
209         wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT];
210         int res = -1;
211         int nssid = 0;
212         cmd_tlv_t *cmd_tlv_temp;
213         char *str_ptr;
214         int tlv_size_left;
215         int pno_time = 0;
216         int pno_repeat = 0;
217         int pno_freq_expo_max = 0;
218
219         DHD_ERROR(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
220
221         if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) {
222                 DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len));
223                 goto exit_proc;
224         }
225
226         str_ptr = command + strlen(CMD_PNOSETUP_SET);
227         tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET);
228
229         cmd_tlv_temp = (cmd_tlv_t *)str_ptr;
230         memset(ssids_local, 0, sizeof(ssids_local));
231
232         if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) &&
233                 (cmd_tlv_temp->version == PNO_TLV_VERSION) &&
234                 (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) {
235
236                 str_ptr += sizeof(cmd_tlv_t);
237                 tlv_size_left -= sizeof(cmd_tlv_t);
238
239                 if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local,
240                         MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) {
241                         DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid));
242                         goto exit_proc;
243                 } else {
244                         if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) {
245                                 DHD_ERROR(("%s scan duration corrupted field size %d\n",
246                                         __FUNCTION__, tlv_size_left));
247                                 goto exit_proc;
248                         }
249                         str_ptr++;
250                         pno_time = simple_strtoul(str_ptr, &str_ptr, 16);
251                         DHD_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time));
252
253                         if (str_ptr[0] != 0) {
254                                 if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) {
255                                         DHD_ERROR(("%s pno repeat : corrupted field\n",
256                                                 __FUNCTION__));
257                                         goto exit_proc;
258                                 }
259                                 str_ptr++;
260                                 pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16);
261                                 DHD_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat));
262                                 if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) {
263                                         DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n",
264                                                 __FUNCTION__));
265                                         goto exit_proc;
266                                 }
267                                 str_ptr++;
268                                 pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16);
269                                 DHD_INFO(("%s: pno_freq_expo_max=%d\n",
270                                         __FUNCTION__, pno_freq_expo_max));
271                         }
272                 }
273         } else {
274                 DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__));
275                 goto exit_proc;
276         }
277
278         res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max);
279
280 exit_proc:
281         return res;
282 }
283 #endif
284
285 static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len)
286 {
287         int ret;
288         int bytes_written = 0;
289
290         ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command);
291         if (ret)
292                 return 0;
293         bytes_written = sizeof(struct ether_addr);
294         return bytes_written;
295 }
296
297 /**
298  * Global function definitions (declared in wl_android.h)
299  */
300
301 int wl_android_wifi_on(struct net_device *dev)
302 {
303         int ret = 0;
304
305         printk("%s in\n", __FUNCTION__);
306         if (!dev) {
307                 DHD_ERROR(("%s: dev is null\n", __FUNCTION__));
308                 return -EINVAL;
309         }
310
311         dhd_net_if_lock(dev);
312         if (!g_wifi_on) {
313                 dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON);
314                 sdioh_start(NULL, 0);
315                 ret = dhd_dev_reset(dev, FALSE);
316                 sdioh_start(NULL, 1);
317                 dhd_dev_init_ioctl(dev);
318                 g_wifi_on = 1;
319         }
320         dhd_net_if_unlock(dev);
321
322         return ret;
323 }
324
325 int wl_android_wifi_off(struct net_device *dev)
326 {
327         int ret = 0;
328
329         printk("%s in\n", __FUNCTION__);
330         if (!dev) {
331                 DHD_TRACE(("%s: dev is null\n", __FUNCTION__));
332                 return -EINVAL;
333         }
334
335         dhd_net_if_lock(dev);
336         if (g_wifi_on) {
337                 dhd_dev_reset(dev, 1);
338                 dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
339                 sdioh_stop(NULL);
340                 /* clean up dtim_skip setting */
341                 net_os_set_dtim_skip(dev, TRUE);
342                 g_wifi_on = 0;
343         }
344         dhd_net_if_unlock(dev);
345
346         return ret;
347 }
348
349 static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len)
350 {
351         if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN)
352                 return -1;
353         bcm_strncpy_s(fw_path, sizeof(fw_path),
354                 command + strlen(CMD_SETFWPATH) + 1, MOD_PARAM_PATHLEN - 1);
355         if (strstr(fw_path, "apsta") != NULL) {
356                 DHD_INFO(("GOT APSTA FIRMWARE\n"));
357                 ap_fw_loaded = TRUE;
358         } else {
359                 DHD_INFO(("GOT STA FIRMWARE\n"));
360                 ap_fw_loaded = FALSE;
361         }
362         return 0;
363 }
364
365 int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
366 {
367         int ret = 0;
368         char *command = NULL;
369         int bytes_written = 0;
370         android_wifi_priv_cmd *priv_cmd;
371
372         net_os_wake_lock(net);
373
374         priv_cmd = (android_wifi_priv_cmd*)ifr->ifr_data;
375         if (!priv_cmd)
376         {
377                 ret = -EINVAL;
378                 goto exit;
379         }
380         command = kmalloc(priv_cmd->total_len, GFP_KERNEL);
381         if (!command)
382         {
383                 DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__));
384                 ret = -ENOMEM;
385                 goto exit;
386         }
387         if (copy_from_user(command, priv_cmd->buf, priv_cmd->total_len)) {
388                 ret = -EFAULT;
389                 goto exit;
390         }
391
392         DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name));
393
394         if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) {
395                 DHD_INFO(("%s, Received regular START command\n", __FUNCTION__));
396                 bytes_written = wl_android_wifi_on(net);
397         }
398         else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) {
399                 bytes_written = wl_android_set_fwpath(net, command, priv_cmd->total_len);
400         }
401
402         if (!g_wifi_on) {
403                 DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n",
404                                 __FUNCTION__, command, ifr->ifr_name));
405                 ret = 0;
406                 goto exit;
407         }
408
409         if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) {
410                 bytes_written = wl_android_wifi_off(net);
411         }
412         else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) {
413                 /* TBD: SCAN-ACTIVE */
414         }
415         else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) {
416                 /* TBD: SCAN-PASSIVE */
417         }
418         else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) {
419                 bytes_written = wl_android_get_rssi(net, command, priv_cmd->total_len);
420         }
421         else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) {
422                 bytes_written = wl_android_get_link_speed(net, command, priv_cmd->total_len);
423         }
424         else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) {
425                 bytes_written = net_os_set_packet_filter(net, 1);
426         }
427         else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) {
428                 bytes_written = net_os_set_packet_filter(net, 0);
429         }
430         else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) {
431                 int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0';
432                 bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num);
433         }
434         else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) {
435                 int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0';
436                 bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num);
437         }
438         else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) {
439                 /* TBD: BTCOEXSCAN-START */
440         }
441         else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) {
442                 /* TBD: BTCOEXSCAN-STOP */
443         }
444         else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) {
445                 /* TBD: BTCOEXMODE */
446         }
447         else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) {
448                 bytes_written = wl_android_set_suspendopt(net, command, priv_cmd->total_len);
449         }
450         else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) {
451                 uint band = *(command + strlen(CMD_SETBAND) + 1) - '0';
452                 bytes_written = wldev_set_band(net, band);
453         }
454         else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) {
455                 bytes_written = wl_android_get_band(net, command, priv_cmd->total_len);
456         }
457         else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) {
458                 char *country_code = command + strlen(CMD_COUNTRY) + 1;
459                 bytes_written = wldev_set_country(net, country_code);
460         }
461 #ifdef PNO_SUPPORT
462         else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) {
463                 bytes_written = dhd_dev_pno_reset(net);
464         }
465         else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) {
466                 bytes_written = wl_android_set_pno_setup(net, command, priv_cmd->total_len);
467         }
468         else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) {
469                 uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0';
470                 bytes_written = dhd_dev_pno_enable(net, pfn_enabled);
471         }
472 #endif
473         else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) {
474                 bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd->total_len);
475         } else {
476                 DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command));
477                 snprintf(command, 3, "OK");
478                 bytes_written = strlen("OK");
479         }
480
481         if (bytes_written > 0) {
482                 bytes_written++;
483                 priv_cmd->used_len = bytes_written;
484                 if (copy_to_user(priv_cmd->buf, command, bytes_written)) {
485                         DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__));
486                         ret = -EFAULT;
487                 }
488         } else {
489                 ret = bytes_written;
490         }
491
492 exit:
493         net_os_wake_unlock(net);
494         if (command) {
495                 kfree(command);
496         }
497
498         return ret;
499 }
500
501 int wl_android_init(void)
502 {
503         int ret = 0;
504
505         dhd_msg_level = DHD_ERROR_VAL;
506 #ifdef ENABLE_INSMOD_NO_FW_LOAD
507         dhd_download_fw_on_driverload = FALSE;
508 #endif /* ENABLE_INSMOD_NO_FW_LOAD */
509 #ifdef CUSTOMER_HW2
510         if (!iface_name[0])
511                 bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ);
512 #endif /* CUSTOMER_HW2 */
513         return ret;
514 }
515
516 int wl_android_exit(void)
517 {
518         int ret = 0;
519
520         return ret;
521 }
522
523
524 /**
525  * Functions for Android WiFi card detection
526  */
527 #if defined(CONFIG_WIFI_CONTROL_FUNC)
528
529 static int g_wifidev_registered = 0;
530 static struct semaphore wifi_control_sem;
531 static struct wifi_platform_data *wifi_control_data = NULL;
532 static struct resource *wifi_irqres = NULL;
533
534 static int wifi_add_dev(void);
535 static void wifi_del_dev(void);
536
537 int wl_android_wifictrl_func_add(void)
538 {
539         int ret = 0;
540         sema_init(&wifi_control_sem, 0);
541
542         ret = wifi_add_dev();
543         if (ret) {
544                 DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__));
545                 return ret;
546         }
547         g_wifidev_registered = 1;
548
549         /* Waiting callback after platform_driver_register is done or exit with error */
550         if (down_timeout(&wifi_control_sem,  msecs_to_jiffies(1000)) != 0) {
551                 ret = -EINVAL;
552                 DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__));
553         }
554
555         return ret;
556 }
557
558 void wl_android_wifictrl_func_del(void)
559 {
560         if (g_wifidev_registered)
561         {
562                 wifi_del_dev();
563                 g_wifidev_registered = 0;
564         }
565 }
566
567 void* wl_android_prealloc(int section, unsigned long size)
568 {
569         void *alloc_ptr = NULL;
570         if (wifi_control_data && wifi_control_data->mem_prealloc) {
571                 alloc_ptr = wifi_control_data->mem_prealloc(section, size);
572                 if (alloc_ptr) {
573                         DHD_INFO(("success alloc section %d\n", section));
574                         bzero(alloc_ptr, size);
575                         return alloc_ptr;
576                 }
577         }
578
579         DHD_ERROR(("can't alloc section %d\n", section));
580         return 0;
581 }
582
583 int wifi_get_irq_number(unsigned long *irq_flags_ptr)
584 {
585         if (wifi_irqres) {
586                 *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK;
587                 return (int)wifi_irqres->start;
588         }
589 #ifdef CUSTOM_OOB_GPIO_NUM
590         return CUSTOM_OOB_GPIO_NUM;
591 #else
592         return -1;
593 #endif
594 }
595
596 int wifi_set_power(int on, unsigned long msec)
597 {
598         DHD_ERROR(("%s = %d\n", __FUNCTION__, on));
599         if (wifi_control_data && wifi_control_data->set_power) {
600                 wifi_control_data->set_power(on);
601         }
602         if (msec)
603                 mdelay(msec);
604         return 0;
605 }
606
607 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
608 int wifi_get_mac_addr(unsigned char *buf)
609 {
610         DHD_ERROR(("%s\n", __FUNCTION__));
611         if (!buf)
612                 return -EINVAL;
613         if (wifi_control_data && wifi_control_data->get_mac_addr) {
614                 return wifi_control_data->get_mac_addr(buf);
615         }
616         return -EOPNOTSUPP;
617 }
618 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */
619
620 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
621 void *wifi_get_country_code(char *ccode)
622 {
623         DHD_TRACE(("%s\n", __FUNCTION__));
624         if (!ccode)
625                 return NULL;
626         if (wifi_control_data && wifi_control_data->get_country_code) {
627                 return wifi_control_data->get_country_code(ccode);
628         }
629         return NULL;
630 }
631 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */
632
633 static int wifi_set_carddetect(int on)
634 {
635         DHD_ERROR(("%s = %d\n", __FUNCTION__, on));
636         if (wifi_control_data && wifi_control_data->set_carddetect) {
637                 wifi_control_data->set_carddetect(on);
638         }
639         return 0;
640 }
641
642 static int wifi_probe(struct platform_device *pdev)
643 {
644         struct wifi_platform_data *wifi_ctrl =
645                 (struct wifi_platform_data *)(pdev->dev.platform_data);
646
647         DHD_ERROR(("## %s\n", __FUNCTION__));
648         wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq");
649         if (wifi_irqres == NULL)
650                 wifi_irqres = platform_get_resource_byname(pdev,
651                         IORESOURCE_IRQ, "bcm4329_wlan_irq");
652         wifi_control_data = wifi_ctrl;
653
654         wifi_set_power(1, 0);   /* Power On */
655         wifi_set_carddetect(1); /* CardDetect (0->1) */
656
657         up(&wifi_control_sem);
658         return 0;
659 }
660
661 static int wifi_remove(struct platform_device *pdev)
662 {
663         struct wifi_platform_data *wifi_ctrl =
664                 (struct wifi_platform_data *)(pdev->dev.platform_data);
665
666         DHD_ERROR(("## %s\n", __FUNCTION__));
667         wifi_control_data = wifi_ctrl;
668
669         wifi_set_power(0, 0);   /* Power Off */
670         wifi_set_carddetect(0); /* CardDetect (1->0) */
671
672         up(&wifi_control_sem);
673         return 0;
674 }
675
676 static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
677 {
678         DHD_TRACE(("##> %s\n", __FUNCTION__));
679 #if defined(OOB_INTR_ONLY)
680         bcmsdh_oob_intr_set(0);
681 #endif /* (OOB_INTR_ONLY) */
682         return 0;
683 }
684
685 static int wifi_resume(struct platform_device *pdev)
686 {
687         DHD_TRACE(("##> %s\n", __FUNCTION__));
688 #if defined(OOB_INTR_ONLY)
689         bcmsdh_oob_intr_set(1);
690 #endif /* (OOB_INTR_ONLY) */
691         return 0;
692 }
693
694 static struct platform_driver wifi_device = {
695         .probe          = wifi_probe,
696         .remove         = wifi_remove,
697         .suspend        = wifi_suspend,
698         .resume         = wifi_resume,
699         .driver         = {
700         .name   = "bcmdhd_wlan",
701         }
702 };
703
704 static struct platform_driver wifi_device_legacy = {
705         .probe          = wifi_probe,
706         .remove         = wifi_remove,
707         .suspend        = wifi_suspend,
708         .resume         = wifi_resume,
709         .driver         = {
710         .name   = "bcm4329_wlan",
711         }
712 };
713
714 static int wifi_add_dev(void)
715 {
716         DHD_TRACE(("## Calling platform_driver_register\n"));
717         platform_driver_register(&wifi_device);
718         platform_driver_register(&wifi_device_legacy);
719         return 0;
720 }
721
722 static void wifi_del_dev(void)
723 {
724         DHD_TRACE(("## Unregister platform_driver_register\n"));
725         platform_driver_unregister(&wifi_device);
726         platform_driver_unregister(&wifi_device_legacy);
727 }
728 #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */