/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

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