PageRenderTime 45ms CodeModel.GetById 16ms app.highlight 23ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/net/wireless/bcmdhd/wl_android.c

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