PageRenderTime 127ms CodeModel.GetById 53ms app.highlight 70ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/net/wireless/bcmdhd_34/dhd_cfg80211.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 673 lines | 487 code | 109 blank | 77 comment | 54 complexity | 49cf8de1abd55063c742f3f5724cae5a MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1/*
  2 * Linux cfg80211 driver - Dongle Host Driver (DHD) related
  3 *
  4 * Copyright (C) 1999-2012, 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_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $
 25 */
 26
 27#include <net/rtnetlink.h>
 28
 29#include <bcmutils.h>
 30#include <wldev_common.h>
 31#include <wl_cfg80211.h>
 32#include <dhd_cfg80211.h>
 33
 34#ifdef PKT_FILTER_SUPPORT
 35#include <dngl_stats.h>
 36#include <dhd.h>
 37#endif
 38
 39extern struct wl_priv *wlcfg_drv_priv;
 40
 41#ifdef PKT_FILTER_SUPPORT
 42extern uint dhd_pkt_filter_enable;
 43extern uint dhd_master_mode;
 44extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
 45#endif
 46
 47static int dhd_dongle_up = FALSE;
 48
 49#include <dngl_stats.h>
 50#include <dhd.h>
 51#include <dhdioctl.h>
 52#include <wlioctl.h>
 53#include <dhd_cfg80211.h>
 54
 55static s32 wl_dongle_up(struct net_device *ndev, u32 up);
 56
 57/**
 58 * Function implementations
 59 */
 60
 61s32 dhd_cfg80211_init(struct wl_priv *wl)
 62{
 63	dhd_dongle_up = FALSE;
 64	return 0;
 65}
 66
 67s32 dhd_cfg80211_deinit(struct wl_priv *wl)
 68{
 69	dhd_dongle_up = FALSE;
 70	return 0;
 71}
 72
 73s32 dhd_cfg80211_down(struct wl_priv *wl)
 74{
 75	dhd_dongle_up = FALSE;
 76	return 0;
 77}
 78
 79s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val)
 80{
 81	dhd_pub_t *dhd =  (dhd_pub_t *)(wl->pub);
 82	dhd->op_mode |= val;
 83	WL_ERR(("Set : op_mode=%d\n", dhd->op_mode));
 84
 85#ifdef ARP_OFFLOAD_SUPPORT
 86	/* IF P2P is enabled, disable arpoe */
 87	dhd_arp_offload_set(dhd, 0);
 88	dhd_arp_offload_enable(dhd, false);
 89#endif /* ARP_OFFLOAD_SUPPORT */
 90
 91	return 0;
 92}
 93
 94s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl)
 95{
 96	dhd_pub_t *dhd =  (dhd_pub_t *)(wl->pub);
 97	dhd->op_mode &= ~CONCURENT_MASK;
 98	WL_ERR(("Clean : op_mode=%d\n", dhd->op_mode));
 99
100#ifdef ARP_OFFLOAD_SUPPORT
101	/* IF P2P is disabled, enable arpoe back for STA mode. */
102	dhd_arp_offload_set(dhd, dhd_arp_mode);
103	dhd_arp_offload_enable(dhd, true);
104#endif /* ARP_OFFLOAD_SUPPORT */
105
106	return 0;
107}
108
109static s32 wl_dongle_up(struct net_device *ndev, u32 up)
110{
111	s32 err = 0;
112
113	err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true);
114	if (unlikely(err)) {
115		WL_ERR(("WLC_UP error (%d)\n", err));
116	}
117	return err;
118}
119s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock)
120{
121#ifndef DHD_SDALIGN
122#define DHD_SDALIGN	32
123#endif
124	struct net_device *ndev;
125	s32 err = 0;
126
127	WL_TRACE(("In\n"));
128	if (dhd_dongle_up) {
129		WL_ERR(("Dongle is already up\n"));
130		return err;
131	}
132
133	ndev = wl_to_prmry_ndev(wl);
134
135	if (need_lock)
136		rtnl_lock();
137
138	err = wl_dongle_up(ndev, 0);
139	if (unlikely(err)) {
140		WL_ERR(("wl_dongle_up failed\n"));
141		goto default_conf_out;
142	}
143	dhd_dongle_up = true;
144
145default_conf_out:
146	if (need_lock)
147		rtnl_unlock();
148	return err;
149
150}
151
152
153/* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */
154#define COEX_DHCP
155
156#if defined(COEX_DHCP)
157
158/* use New SCO/eSCO smart YG suppression */
159#define BT_DHCP_eSCO_FIX
160/* this flag boost wifi pkt priority to max, caution: -not fair to sco */
161#define BT_DHCP_USE_FLAGS
162/* T1 start SCO/ESCo priority suppression */
163#define BT_DHCP_OPPR_WIN_TIME	2500
164/* T2 turn off SCO/SCO supperesion is (timeout) */
165#define BT_DHCP_FLAG_FORCE_TIME 5500
166
167enum wl_cfg80211_btcoex_status {
168	BT_DHCP_IDLE,
169	BT_DHCP_START,
170	BT_DHCP_OPPR_WIN,
171	BT_DHCP_FLAG_FORCE_TIMEOUT
172};
173
174/*
175 * get named driver variable to uint register value and return error indication
176 * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, &reg_value)
177 */
178static int
179dev_wlc_intvar_get_reg(struct net_device *dev, char *name,
180	uint reg, int *retval)
181{
182	union {
183		char buf[WLC_IOCTL_SMLEN];
184		int val;
185	} var;
186	int error;
187
188	bcm_mkiovar(name, (char *)(&reg), sizeof(reg),
189		(char *)(&var), sizeof(var.buf));
190	error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false);
191
192	*retval = dtoh32(var.val);
193	return (error);
194}
195
196static int
197dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len)
198{
199#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
200	char ioctlbuf_local[1024];
201#else
202	static char ioctlbuf_local[1024];
203#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
204
205	bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local));
206
207	return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true));
208}
209/*
210get named driver variable to uint register value and return error indication
211calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value)
212*/
213static int
214dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val)
215{
216	char reg_addr[8];
217
218	memset(reg_addr, 0, sizeof(reg_addr));
219	memcpy((char *)&reg_addr[0], (char *)addr, 4);
220	memcpy((char *)&reg_addr[4], (char *)val, 4);
221
222	return (dev_wlc_bufvar_set(dev, name, (char *)&reg_addr[0], sizeof(reg_addr)));
223}
224
225static bool btcoex_is_sco_active(struct net_device *dev)
226{
227	int ioc_res = 0;
228	bool res = FALSE;
229	int sco_id_cnt = 0;
230	int param27;
231	int i;
232
233	for (i = 0; i < 12; i++) {
234
235		ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, &param27);
236
237		WL_TRACE(("%s, sample[%d], btc params: 27:%x\n",
238			__FUNCTION__, i, param27));
239
240		if (ioc_res < 0) {
241			WL_ERR(("%s ioc read btc params error\n", __FUNCTION__));
242			break;
243		}
244
245		if ((param27 & 0x6) == 2) { /* count both sco & esco  */
246			sco_id_cnt++;
247		}
248
249		if (sco_id_cnt > 2) {
250			WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d  samples:%d\n",
251				__FUNCTION__, sco_id_cnt, i));
252			res = TRUE;
253			break;
254		}
255
256		msleep(5);
257	}
258
259	return res;
260}
261
262#if defined(BT_DHCP_eSCO_FIX)
263/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */
264static int set_btc_esco_params(struct net_device *dev, bool trump_sco)
265{
266	static bool saved_status = FALSE;
267
268	char buf_reg50va_dhcp_on[8] =
269		{ 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 };
270	char buf_reg51va_dhcp_on[8] =
271		{ 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
272	char buf_reg64va_dhcp_on[8] =
273		{ 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
274	char buf_reg65va_dhcp_on[8] =
275		{ 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
276	char buf_reg71va_dhcp_on[8] =
277		{ 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
278	uint32 regaddr;
279	static uint32 saved_reg50;
280	static uint32 saved_reg51;
281	static uint32 saved_reg64;
282	static uint32 saved_reg65;
283	static uint32 saved_reg71;
284
285	if (trump_sco) {
286		/* this should reduce eSCO agressive retransmit
287		 * w/o breaking it
288		 */
289
290		/* 1st save current */
291		WL_TRACE(("Do new SCO/eSCO coex algo {save &"
292			  "override}\n"));
293		if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) &&
294			(!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) &&
295			(!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) &&
296			(!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) &&
297			(!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) {
298			saved_status = TRUE;
299			WL_TRACE(("%s saved bt_params[50,51,64,65,71]:"
300				  "0x%x 0x%x 0x%x 0x%x 0x%x\n",
301				  __FUNCTION__, saved_reg50, saved_reg51,
302				  saved_reg64, saved_reg65, saved_reg71));
303		} else {
304			WL_ERR((":%s: save btc_params failed\n",
305				__FUNCTION__));
306			saved_status = FALSE;
307			return -1;
308		}
309
310		WL_TRACE(("override with [50,51,64,65,71]:"
311			  "0x%x 0x%x 0x%x 0x%x 0x%x\n",
312			  *(u32 *)(buf_reg50va_dhcp_on+4),
313			  *(u32 *)(buf_reg51va_dhcp_on+4),
314			  *(u32 *)(buf_reg64va_dhcp_on+4),
315			  *(u32 *)(buf_reg65va_dhcp_on+4),
316			  *(u32 *)(buf_reg71va_dhcp_on+4)));
317
318		dev_wlc_bufvar_set(dev, "btc_params",
319			(char *)&buf_reg50va_dhcp_on[0], 8);
320		dev_wlc_bufvar_set(dev, "btc_params",
321			(char *)&buf_reg51va_dhcp_on[0], 8);
322		dev_wlc_bufvar_set(dev, "btc_params",
323			(char *)&buf_reg64va_dhcp_on[0], 8);
324		dev_wlc_bufvar_set(dev, "btc_params",
325			(char *)&buf_reg65va_dhcp_on[0], 8);
326		dev_wlc_bufvar_set(dev, "btc_params",
327			(char *)&buf_reg71va_dhcp_on[0], 8);
328
329		saved_status = TRUE;
330	} else if (saved_status) {
331		/* restore previously saved bt params */
332		WL_TRACE(("Do new SCO/eSCO coex algo {save &"
333			  "override}\n"));
334
335		regaddr = 50;
336		dev_wlc_intvar_set_reg(dev, "btc_params",
337			(char *)&regaddr, (char *)&saved_reg50);
338		regaddr = 51;
339		dev_wlc_intvar_set_reg(dev, "btc_params",
340			(char *)&regaddr, (char *)&saved_reg51);
341		regaddr = 64;
342		dev_wlc_intvar_set_reg(dev, "btc_params",
343			(char *)&regaddr, (char *)&saved_reg64);
344		regaddr = 65;
345		dev_wlc_intvar_set_reg(dev, "btc_params",
346			(char *)&regaddr, (char *)&saved_reg65);
347		regaddr = 71;
348		dev_wlc_intvar_set_reg(dev, "btc_params",
349			(char *)&regaddr, (char *)&saved_reg71);
350
351		WL_TRACE(("restore bt_params[50,51,64,65,71]:"
352			"0x%x 0x%x 0x%x 0x%x 0x%x\n",
353			saved_reg50, saved_reg51, saved_reg64,
354			saved_reg65, saved_reg71));
355
356		saved_status = FALSE;
357	} else {
358		WL_ERR((":%s att to restore not saved BTCOEX params\n",
359			__FUNCTION__));
360		return -1;
361	}
362	return 0;
363}
364#endif /* BT_DHCP_eSCO_FIX */
365
366static void
367wl_cfg80211_bt_setflag(struct net_device *dev, bool set)
368{
369#if defined(BT_DHCP_USE_FLAGS)
370	char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 };
371	char buf_flag7_default[8]   = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
372#endif
373
374
375#if defined(BT_DHCP_eSCO_FIX)
376	/* set = 1, save & turn on  0 - off & restore prev settings */
377	set_btc_esco_params(dev, set);
378#endif
379
380#if defined(BT_DHCP_USE_FLAGS)
381	WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set));
382	if (set == TRUE)
383		/* Forcing bt_flag7  */
384		dev_wlc_bufvar_set(dev, "btc_flags",
385			(char *)&buf_flag7_dhcp_on[0],
386			sizeof(buf_flag7_dhcp_on));
387	else
388		/* Restoring default bt flag7 */
389		dev_wlc_bufvar_set(dev, "btc_flags",
390			(char *)&buf_flag7_default[0],
391			sizeof(buf_flag7_default));
392#endif
393}
394
395static void wl_cfg80211_bt_timerfunc(ulong data)
396{
397	struct btcoex_info *bt_local = (struct btcoex_info *)data;
398	WL_TRACE(("%s\n", __FUNCTION__));
399	bt_local->timer_on = 0;
400	schedule_work(&bt_local->work);
401}
402
403static void wl_cfg80211_bt_handler(struct work_struct *work)
404{
405	struct btcoex_info *btcx_inf;
406
407	btcx_inf = container_of(work, struct btcoex_info, work);
408
409	if (btcx_inf->timer_on) {
410		btcx_inf->timer_on = 0;
411		del_timer_sync(&btcx_inf->timer);
412	}
413
414	switch (btcx_inf->bt_state) {
415		case BT_DHCP_START:
416			/* DHCP started
417			 * provide OPPORTUNITY window to get DHCP address
418			 */
419			WL_TRACE(("%s bt_dhcp stm: started \n",
420				__FUNCTION__));
421			btcx_inf->bt_state = BT_DHCP_OPPR_WIN;
422			mod_timer(&btcx_inf->timer,
423				jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000);
424			btcx_inf->timer_on = 1;
425			break;
426
427		case BT_DHCP_OPPR_WIN:
428			if (btcx_inf->dhcp_done) {
429				WL_TRACE(("%s DHCP Done before T1 expiration\n",
430					__FUNCTION__));
431				goto btc_coex_idle;
432			}
433
434			/* DHCP is not over yet, start lowering BT priority
435			 * enforce btc_params + flags if necessary
436			 */
437			WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__,
438				BT_DHCP_OPPR_WIN_TIME));
439			if (btcx_inf->dev)
440				wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE);
441			btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT;
442			mod_timer(&btcx_inf->timer,
443				jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000);
444			btcx_inf->timer_on = 1;
445			break;
446
447		case BT_DHCP_FLAG_FORCE_TIMEOUT:
448			if (btcx_inf->dhcp_done) {
449				WL_TRACE(("%s DHCP Done before T2 expiration\n",
450					__FUNCTION__));
451			} else {
452				/* Noo dhcp during T1+T2, restore BT priority */
453				WL_TRACE(("%s DHCP wait interval T2:%d"
454					  "msec expired\n", __FUNCTION__,
455					  BT_DHCP_FLAG_FORCE_TIME));
456			}
457
458			/* Restoring default bt priority */
459			if (btcx_inf->dev)
460				wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE);
461btc_coex_idle:
462			btcx_inf->bt_state = BT_DHCP_IDLE;
463			btcx_inf->timer_on = 0;
464			break;
465
466		default:
467			WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__,
468				btcx_inf->bt_state));
469			if (btcx_inf->dev)
470				wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE);
471			btcx_inf->bt_state = BT_DHCP_IDLE;
472			btcx_inf->timer_on = 0;
473			break;
474	}
475
476	net_os_wake_unlock(btcx_inf->dev);
477}
478
479int wl_cfg80211_btcoex_init(struct wl_priv *wl)
480{
481	struct btcoex_info *btco_inf = NULL;
482
483	btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL);
484	if (!btco_inf)
485		return -ENOMEM;
486
487	btco_inf->bt_state = BT_DHCP_IDLE;
488	btco_inf->ts_dhcp_start = 0;
489	btco_inf->ts_dhcp_ok = 0;
490	/* Set up timer for BT  */
491	btco_inf->timer_ms = 10;
492	init_timer(&btco_inf->timer);
493	btco_inf->timer.data = (ulong)btco_inf;
494	btco_inf->timer.function = wl_cfg80211_bt_timerfunc;
495
496	btco_inf->dev = wl->wdev->netdev;
497
498	INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler);
499
500	wl->btcoex_info = btco_inf;
501	return 0;
502}
503
504void wl_cfg80211_btcoex_deinit(struct wl_priv *wl)
505{
506	if (!wl->btcoex_info)
507		return;
508
509	if (!wl->btcoex_info->timer_on) {
510		wl->btcoex_info->timer_on = 0;
511		del_timer_sync(&wl->btcoex_info->timer);
512	}
513
514	cancel_work_sync(&wl->btcoex_info->work);
515
516	kfree(wl->btcoex_info);
517	wl->btcoex_info = NULL;
518}
519#endif 
520
521int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command)
522{
523
524	struct wl_priv *wl = wlcfg_drv_priv;
525	char powermode_val = 0;
526	char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 };
527	char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 };
528	char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 };
529
530	uint32 regaddr;
531	static uint32 saved_reg66;
532	static uint32 saved_reg41;
533	static uint32 saved_reg68;
534	static bool saved_status = FALSE;
535
536#ifdef COEX_DHCP
537	char buf_flag7_default[8] =   { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
538	struct btcoex_info *btco_inf = wl->btcoex_info;
539#endif /* COEX_DHCP */
540
541#ifdef PKT_FILTER_SUPPORT
542	dhd_pub_t *dhd =  (dhd_pub_t *)(wl->pub);
543	int i;
544#endif
545
546	/* Figure out powermode 1 or o command */
547	strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1);
548
549	if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) {
550
551		WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__));
552
553#ifdef PKT_FILTER_SUPPORT
554		dhd->dhcp_in_progress = 1;
555
556		/* Disable packet filtering */
557		if (dhd_pkt_filter_enable && dhd->early_suspended) {
558			WL_TRACE(("DHCP in progressing , disable packet filter!!!\n"));
559			for (i = 0; i < dhd->pktfilter_count; i++) {
560				dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
561				 0, dhd_master_mode);
562			}
563		}
564#endif
565
566		/* Retrieve and saved orig regs value */
567		if ((saved_status == FALSE) &&
568			(!dev_wlc_intvar_get_reg(dev, "btc_params", 66,  &saved_reg66)) &&
569			(!dev_wlc_intvar_get_reg(dev, "btc_params", 41,  &saved_reg41)) &&
570			(!dev_wlc_intvar_get_reg(dev, "btc_params", 68,  &saved_reg68)))   {
571				saved_status = TRUE;
572				WL_TRACE(("Saved 0x%x 0x%x 0x%x\n",
573					saved_reg66, saved_reg41, saved_reg68));
574
575				/* Disable PM mode during dhpc session */
576
577				/* Disable PM mode during dhpc session */
578#ifdef COEX_DHCP
579				/* Start  BT timer only for SCO connection */
580				if (btcoex_is_sco_active(dev)) {
581					/* btc_params 66 */
582					dev_wlc_bufvar_set(dev, "btc_params",
583						(char *)&buf_reg66va_dhcp_on[0],
584						sizeof(buf_reg66va_dhcp_on));
585					/* btc_params 41 0x33 */
586					dev_wlc_bufvar_set(dev, "btc_params",
587						(char *)&buf_reg41va_dhcp_on[0],
588						sizeof(buf_reg41va_dhcp_on));
589					/* btc_params 68 0x190 */
590					dev_wlc_bufvar_set(dev, "btc_params",
591						(char *)&buf_reg68va_dhcp_on[0],
592						sizeof(buf_reg68va_dhcp_on));
593					saved_status = TRUE;
594
595					btco_inf->bt_state = BT_DHCP_START;
596					btco_inf->timer_on = 1;
597					mod_timer(&btco_inf->timer, btco_inf->timer.expires);
598					WL_TRACE(("%s enable BT DHCP Timer\n",
599					__FUNCTION__));
600				}
601#endif /* COEX_DHCP */
602		}
603		else if (saved_status == TRUE) {
604			WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__));
605		}
606	}
607	else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) {
608
609
610#ifdef PKT_FILTER_SUPPORT
611		dhd->dhcp_in_progress = 0;
612
613		/* Enable packet filtering */
614		if (dhd_pkt_filter_enable && dhd->early_suspended) {
615			WL_TRACE(("DHCP is complete , enable packet filter!!!\n"));
616			for (i = 0; i < dhd->pktfilter_count; i++) {
617				dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
618				 1, dhd_master_mode);
619			}
620		}
621#endif
622
623		/* Restoring PM mode */
624
625#ifdef COEX_DHCP
626		/* Stop any bt timer because DHCP session is done */
627		WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__));
628		if (btco_inf->timer_on) {
629			btco_inf->timer_on = 0;
630			del_timer_sync(&btco_inf->timer);
631
632			if (btco_inf->bt_state != BT_DHCP_IDLE) {
633			/* need to restore original btc flags & extra btc params */
634				WL_TRACE(("%s bt->bt_state:%d\n",
635					__FUNCTION__, btco_inf->bt_state));
636				/* wake up btcoex thread to restore btlags+params  */
637				schedule_work(&btco_inf->work);
638			}
639		}
640
641		/* Restoring btc_flag paramter anyway */
642		if (saved_status == TRUE)
643			dev_wlc_bufvar_set(dev, "btc_flags",
644				(char *)&buf_flag7_default[0], sizeof(buf_flag7_default));
645#endif /* COEX_DHCP */
646
647		/* Restore original values */
648		if (saved_status == TRUE) {
649			regaddr = 66;
650			dev_wlc_intvar_set_reg(dev, "btc_params",
651				(char *)&regaddr, (char *)&saved_reg66);
652			regaddr = 41;
653			dev_wlc_intvar_set_reg(dev, "btc_params",
654				(char *)&regaddr, (char *)&saved_reg41);
655			regaddr = 68;
656			dev_wlc_intvar_set_reg(dev, "btc_params",
657				(char *)&regaddr, (char *)&saved_reg68);
658
659			WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n",
660				saved_reg66, saved_reg41, saved_reg68));
661		}
662		saved_status = FALSE;
663
664	}
665	else {
666		WL_ERR(("%s Unkwown yet power setting, ignored\n",
667			__FUNCTION__));
668	}
669
670	snprintf(command, 3, "OK");
671
672	return (strlen("OK"));
673}