2 * Linux cfg80211 driver - Dongle Host Driver (DHD) related
4 * $Copyright Open Broadcom Corporation$
6 * $Id: wl_cfg_btcoex.c 467328 2014-04-03 01:23:40Z $
9 #include <net/rtnetlink.h>
12 #include <wldev_common.h>
13 #include <wl_cfg80211.h>
14 #include <dhd_cfg80211.h>
15 #include <dngl_stats.h>
20 #ifdef PKT_FILTER_SUPPORT
21 extern uint dhd_pkt_filter_enable;
22 extern uint dhd_master_mode;
23 extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
27 struct timer_list timer;
30 u32 ts_dhcp_start; /* ms ts ecord time stats */
31 u32 ts_dhcp_ok; /* ms ts ecord time stats */
32 bool dhcp_done; /* flag, indicates that host done with
33 * dhcp before t1/t2 expiration
36 struct work_struct work;
37 struct net_device *dev;
40 static struct btcoex_info *btcoex_info_loc = NULL;
42 /* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */
44 /* use New SCO/eSCO smart YG suppression */
45 #define BT_DHCP_eSCO_FIX
46 /* this flag boost wifi pkt priority to max, caution: -not fair to sco */
47 #define BT_DHCP_USE_FLAGS
48 /* T1 start SCO/ESCo priority suppression */
49 #define BT_DHCP_OPPR_WIN_TIME 2500
50 /* T2 turn off SCO/SCO supperesion is (timeout) */
51 #define BT_DHCP_FLAG_FORCE_TIME 5500
53 enum wl_cfg80211_btcoex_status {
57 BT_DHCP_FLAG_FORCE_TIMEOUT
61 * get named driver variable to uint register value and return error indication
62 * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value)
65 dev_wlc_intvar_get_reg(struct net_device *dev, char *name,
66 uint reg, int *retval)
69 char buf[WLC_IOCTL_SMLEN];
74 bcm_mkiovar(name, (char *)(®), sizeof(reg),
75 (char *)(&var), sizeof(var.buf));
76 error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false);
78 *retval = dtoh32(var.val);
83 dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len)
85 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
86 char ioctlbuf_local[1024];
88 static char ioctlbuf_local[1024];
89 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
91 bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local));
93 return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true));
96 get named driver variable to uint register value and return error indication
97 calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value)
100 dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val)
104 memset(reg_addr, 0, sizeof(reg_addr));
105 memcpy((char *)®_addr[0], (char *)addr, 4);
106 memcpy((char *)®_addr[4], (char *)val, 4);
108 return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr)));
111 static bool btcoex_is_sco_active(struct net_device *dev)
119 for (i = 0; i < 12; i++) {
121 ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27);
123 WL_TRACE(("sample[%d], btc params: 27:%x\n", i, param27));
126 WL_ERR(("ioc read btc params error\n"));
130 if ((param27 & 0x6) == 2) { /* count both sco & esco */
134 if (sco_id_cnt > 2) {
135 WL_TRACE(("sco/esco detected, pkt id_cnt:%d samples:%d\n",
147 #if defined(BT_DHCP_eSCO_FIX)
148 /* Enhanced BT COEX settings for eSCO compatibility during DHCP window */
149 static int set_btc_esco_params(struct net_device *dev, bool trump_sco)
151 static bool saved_status = FALSE;
153 char buf_reg50va_dhcp_on[8] =
154 { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 };
155 char buf_reg51va_dhcp_on[8] =
156 { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
157 char buf_reg64va_dhcp_on[8] =
158 { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
159 char buf_reg65va_dhcp_on[8] =
160 { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
161 char buf_reg71va_dhcp_on[8] =
162 { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
164 static uint32 saved_reg50;
165 static uint32 saved_reg51;
166 static uint32 saved_reg64;
167 static uint32 saved_reg65;
168 static uint32 saved_reg71;
171 /* this should reduce eSCO agressive retransmit
175 /* 1st save current */
176 WL_TRACE(("Do new SCO/eSCO coex algo {save &"
178 if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) &&
179 (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) &&
180 (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) &&
181 (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) &&
182 (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) {
184 WL_TRACE(("saved bt_params[50,51,64,65,71]:"
185 "0x%x 0x%x 0x%x 0x%x 0x%x\n",
186 saved_reg50, saved_reg51,
187 saved_reg64, saved_reg65, saved_reg71));
189 WL_ERR((":%s: save btc_params failed\n",
191 saved_status = FALSE;
195 WL_TRACE(("override with [50,51,64,65,71]:"
196 "0x%x 0x%x 0x%x 0x%x 0x%x\n",
197 *(u32 *)(buf_reg50va_dhcp_on+4),
198 *(u32 *)(buf_reg51va_dhcp_on+4),
199 *(u32 *)(buf_reg64va_dhcp_on+4),
200 *(u32 *)(buf_reg65va_dhcp_on+4),
201 *(u32 *)(buf_reg71va_dhcp_on+4)));
203 dev_wlc_bufvar_set(dev, "btc_params",
204 (char *)&buf_reg50va_dhcp_on[0], 8);
205 dev_wlc_bufvar_set(dev, "btc_params",
206 (char *)&buf_reg51va_dhcp_on[0], 8);
207 dev_wlc_bufvar_set(dev, "btc_params",
208 (char *)&buf_reg64va_dhcp_on[0], 8);
209 dev_wlc_bufvar_set(dev, "btc_params",
210 (char *)&buf_reg65va_dhcp_on[0], 8);
211 dev_wlc_bufvar_set(dev, "btc_params",
212 (char *)&buf_reg71va_dhcp_on[0], 8);
215 } else if (saved_status) {
216 /* restore previously saved bt params */
217 WL_TRACE(("Do new SCO/eSCO coex algo {save &"
221 dev_wlc_intvar_set_reg(dev, "btc_params",
222 (char *)®addr, (char *)&saved_reg50);
224 dev_wlc_intvar_set_reg(dev, "btc_params",
225 (char *)®addr, (char *)&saved_reg51);
227 dev_wlc_intvar_set_reg(dev, "btc_params",
228 (char *)®addr, (char *)&saved_reg64);
230 dev_wlc_intvar_set_reg(dev, "btc_params",
231 (char *)®addr, (char *)&saved_reg65);
233 dev_wlc_intvar_set_reg(dev, "btc_params",
234 (char *)®addr, (char *)&saved_reg71);
236 WL_TRACE(("restore bt_params[50,51,64,65,71]:"
237 "0x%x 0x%x 0x%x 0x%x 0x%x\n",
238 saved_reg50, saved_reg51, saved_reg64,
239 saved_reg65, saved_reg71));
241 saved_status = FALSE;
243 WL_ERR((":%s att to restore not saved BTCOEX params\n",
249 #endif /* BT_DHCP_eSCO_FIX */
252 wl_cfg80211_bt_setflag(struct net_device *dev, bool set)
254 #if defined(BT_DHCP_USE_FLAGS)
255 char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 };
256 char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
260 #if defined(BT_DHCP_eSCO_FIX)
261 /* set = 1, save & turn on 0 - off & restore prev settings */
262 set_btc_esco_params(dev, set);
265 #if defined(BT_DHCP_USE_FLAGS)
266 WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set));
268 /* Forcing bt_flag7 */
269 dev_wlc_bufvar_set(dev, "btc_flags",
270 (char *)&buf_flag7_dhcp_on[0],
271 sizeof(buf_flag7_dhcp_on));
273 /* Restoring default bt flag7 */
274 dev_wlc_bufvar_set(dev, "btc_flags",
275 (char *)&buf_flag7_default[0],
276 sizeof(buf_flag7_default));
280 static void wl_cfg80211_bt_timerfunc(ulong data)
282 struct btcoex_info *bt_local = (struct btcoex_info *)data;
283 WL_TRACE(("Enter\n"));
284 bt_local->timer_on = 0;
285 schedule_work(&bt_local->work);
288 static void wl_cfg80211_bt_handler(struct work_struct *work)
290 struct btcoex_info *btcx_inf;
292 btcx_inf = container_of(work, struct btcoex_info, work);
294 if (btcx_inf->timer_on) {
295 btcx_inf->timer_on = 0;
296 del_timer_sync(&btcx_inf->timer);
299 switch (btcx_inf->bt_state) {
302 * provide OPPORTUNITY window to get DHCP address
304 WL_TRACE(("bt_dhcp stm: started \n"));
306 btcx_inf->bt_state = BT_DHCP_OPPR_WIN;
307 mod_timer(&btcx_inf->timer,
308 jiffies + msecs_to_jiffies(BT_DHCP_OPPR_WIN_TIME));
309 btcx_inf->timer_on = 1;
312 case BT_DHCP_OPPR_WIN:
313 if (btcx_inf->dhcp_done) {
314 WL_TRACE(("DHCP Done before T1 expiration\n"));
318 /* DHCP is not over yet, start lowering BT priority
319 * enforce btc_params + flags if necessary
321 WL_TRACE(("DHCP T1:%d expired\n", BT_DHCP_OPPR_WIN_TIME));
323 wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE);
324 btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT;
325 mod_timer(&btcx_inf->timer,
326 jiffies + msecs_to_jiffies(BT_DHCP_FLAG_FORCE_TIME));
327 btcx_inf->timer_on = 1;
330 case BT_DHCP_FLAG_FORCE_TIMEOUT:
331 if (btcx_inf->dhcp_done) {
332 WL_TRACE(("DHCP Done before T2 expiration\n"));
334 /* Noo dhcp during T1+T2, restore BT priority */
335 WL_TRACE(("DHCP wait interval T2:%d msec expired\n",
336 BT_DHCP_FLAG_FORCE_TIME));
339 /* Restoring default bt priority */
341 wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE);
343 btcx_inf->bt_state = BT_DHCP_IDLE;
344 btcx_inf->timer_on = 0;
348 WL_ERR(("error g_status=%d !!!\n", btcx_inf->bt_state));
350 wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE);
351 btcx_inf->bt_state = BT_DHCP_IDLE;
352 btcx_inf->timer_on = 0;
356 net_os_wake_unlock(btcx_inf->dev);
359 void* wl_cfg80211_btcoex_init(struct net_device *ndev)
361 struct btcoex_info *btco_inf = NULL;
363 btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL);
367 btco_inf->bt_state = BT_DHCP_IDLE;
368 btco_inf->ts_dhcp_start = 0;
369 btco_inf->ts_dhcp_ok = 0;
370 /* Set up timer for BT */
371 btco_inf->timer_ms = 10;
372 init_timer(&btco_inf->timer);
373 btco_inf->timer.data = (ulong)btco_inf;
374 btco_inf->timer.function = wl_cfg80211_bt_timerfunc;
376 btco_inf->dev = ndev;
378 INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler);
380 btcoex_info_loc = btco_inf;
384 void wl_cfg80211_btcoex_deinit()
386 if (!btcoex_info_loc)
389 if (btcoex_info_loc->timer_on) {
390 btcoex_info_loc->timer_on = 0;
391 del_timer_sync(&btcoex_info_loc->timer);
394 cancel_work_sync(&btcoex_info_loc->work);
396 kfree(btcoex_info_loc);
399 int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, dhd_pub_t *dhd, char *command)
402 struct btcoex_info *btco_inf = btcoex_info_loc;
403 char powermode_val = 0;
404 char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 };
405 char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 };
406 char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 };
409 static uint32 saved_reg66;
410 static uint32 saved_reg41;
411 static uint32 saved_reg68;
412 static bool saved_status = FALSE;
414 char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
416 /* Figure out powermode 1 or o command */
417 strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1);
419 if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) {
420 WL_TRACE_HW4(("DHCP session starts\n"));
422 #if defined(DHCP_SCAN_SUPPRESS)
423 /* Suppress scan during the DHCP */
424 wl_cfg80211_scan_suppress(dev, 1);
425 #endif /* OEM_ANDROID */
427 #ifdef PKT_FILTER_SUPPORT
428 dhd->dhcp_in_progress = 1;
430 if (dhd->early_suspended) {
431 WL_TRACE_HW4(("DHCP in progressing , disable packet filter!!!\n"));
432 dhd_enable_packet_filter(0, dhd);
436 /* Retrieve and saved orig regs value */
437 if ((saved_status == FALSE) &&
438 (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) &&
439 (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) &&
440 (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) {
442 WL_TRACE(("Saved 0x%x 0x%x 0x%x\n",
443 saved_reg66, saved_reg41, saved_reg68));
445 /* Disable PM mode during dhpc session */
447 /* Disable PM mode during dhpc session */
448 /* Start BT timer only for SCO connection */
449 if (btcoex_is_sco_active(dev)) {
451 dev_wlc_bufvar_set(dev, "btc_params",
452 (char *)&buf_reg66va_dhcp_on[0],
453 sizeof(buf_reg66va_dhcp_on));
454 /* btc_params 41 0x33 */
455 dev_wlc_bufvar_set(dev, "btc_params",
456 (char *)&buf_reg41va_dhcp_on[0],
457 sizeof(buf_reg41va_dhcp_on));
458 /* btc_params 68 0x190 */
459 dev_wlc_bufvar_set(dev, "btc_params",
460 (char *)&buf_reg68va_dhcp_on[0],
461 sizeof(buf_reg68va_dhcp_on));
464 btco_inf->bt_state = BT_DHCP_START;
465 btco_inf->timer_on = 1;
466 mod_timer(&btco_inf->timer, btco_inf->timer.expires);
467 WL_TRACE(("enable BT DHCP Timer\n"));
470 else if (saved_status == TRUE) {
471 WL_ERR(("was called w/o DHCP OFF. Continue\n"));
474 else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) {
477 #if defined(DHCP_SCAN_SUPPRESS)
478 /* Since DHCP is complete, enable the scan back */
479 wl_cfg80211_scan_suppress(dev, 0);
480 #endif /* OEM_ANDROID */
482 #ifdef PKT_FILTER_SUPPORT
483 dhd->dhcp_in_progress = 0;
484 WL_TRACE_HW4(("DHCP is complete \n"));
486 /* Enable packet filtering */
487 if (dhd->early_suspended) {
488 WL_TRACE_HW4(("DHCP is complete , enable packet filter!!!\n"));
489 dhd_enable_packet_filter(1, dhd);
491 #endif /* PKT_FILTER_SUPPORT */
493 /* Restoring PM mode */
495 /* Stop any bt timer because DHCP session is done */
496 WL_TRACE(("disable BT DHCP Timer\n"));
497 if (btco_inf->timer_on) {
498 btco_inf->timer_on = 0;
499 del_timer_sync(&btco_inf->timer);
501 if (btco_inf->bt_state != BT_DHCP_IDLE) {
502 /* need to restore original btc flags & extra btc params */
503 WL_TRACE(("bt->bt_state:%d\n", btco_inf->bt_state));
504 /* wake up btcoex thread to restore btlags+params */
505 schedule_work(&btco_inf->work);
509 /* Restoring btc_flag paramter anyway */
510 if (saved_status == TRUE)
511 dev_wlc_bufvar_set(dev, "btc_flags",
512 (char *)&buf_flag7_default[0], sizeof(buf_flag7_default));
514 /* Restore original values */
515 if (saved_status == TRUE) {
517 dev_wlc_intvar_set_reg(dev, "btc_params",
518 (char *)®addr, (char *)&saved_reg66);
520 dev_wlc_intvar_set_reg(dev, "btc_params",
521 (char *)®addr, (char *)&saved_reg41);
523 dev_wlc_intvar_set_reg(dev, "btc_params",
524 (char *)®addr, (char *)&saved_reg68);
526 WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n",
527 saved_reg66, saved_reg41, saved_reg68));
529 saved_status = FALSE;
533 WL_ERR(("Unkwown yet power setting, ignored\n"));
536 snprintf(command, 3, "OK");
538 return (strlen("OK"));