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