2 * Linux cfg80211 driver - Dongle Host Driver (DHD) related
4 * $Copyright Open Broadcom Corporation$
6 * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $
9 #include <net/rtnetlink.h>
12 #include <wldev_common.h>
13 #include <wl_cfg80211.h>
14 #include <dhd_cfg80211.h>
16 #ifdef PKT_FILTER_SUPPORT
17 #include <dngl_stats.h>
21 extern struct wl_priv *wlcfg_drv_priv;
23 #ifdef PKT_FILTER_SUPPORT
24 extern uint dhd_pkt_filter_enable;
25 extern uint dhd_master_mode;
26 extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
29 static int dhd_dongle_up = FALSE;
31 #include <dngl_stats.h>
35 #include <dhd_cfg80211.h>
37 static s32 wl_dongle_up(struct net_device *ndev, u32 up);
40 * Function implementations
43 s32 dhd_cfg80211_init(struct wl_priv *wl)
45 dhd_dongle_up = FALSE;
49 s32 dhd_cfg80211_deinit(struct wl_priv *wl)
51 dhd_dongle_up = FALSE;
55 s32 dhd_cfg80211_down(struct wl_priv *wl)
57 dhd_dongle_up = FALSE;
61 s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val)
63 dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub);
65 WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode));
66 #ifdef ARP_OFFLOAD_SUPPORT
67 if (dhd->arp_version == 1) {
68 /* IF P2P is enabled, disable arpoe */
69 dhd_arp_offload_set(dhd, 0);
70 dhd_arp_offload_enable(dhd, false);
72 #endif /* ARP_OFFLOAD_SUPPORT */
77 s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl)
79 dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub);
80 dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE);
81 WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode));
83 #ifdef ARP_OFFLOAD_SUPPORT
84 if (dhd->arp_version == 1) {
85 /* IF P2P is disabled, enable arpoe back for STA mode. */
86 dhd_arp_offload_set(dhd, dhd_arp_mode);
87 dhd_arp_offload_enable(dhd, true);
89 #endif /* ARP_OFFLOAD_SUPPORT */
94 static s32 wl_dongle_up(struct net_device *ndev, u32 up)
98 err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true);
100 WL_ERR(("WLC_UP error (%d)\n", err));
104 s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock)
107 #define DHD_SDALIGN 32
109 struct net_device *ndev;
114 WL_ERR(("Dongle is already up\n"));
118 ndev = wl_to_prmry_ndev(wl);
123 err = wl_dongle_up(ndev, 0);
125 WL_ERR(("wl_dongle_up failed\n"));
126 goto default_conf_out;
128 dhd_dongle_up = true;
137 #ifdef CONFIG_NL80211_TESTMODE
138 int dhd_cfg80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
140 struct sk_buff *reply;
143 dhd_ioctl_t *ioc = data;
146 WL_TRACE(("entry: cmd = %d\n", ioc->cmd));
147 wl = wiphy_priv(wiphy);
150 DHD_OS_WAKE_LOCK(dhd);
152 /* send to dongle only if we are not waiting for reload already */
153 if (dhd->hang_was_sent) {
154 WL_ERR(("HANG was sent up earlier\n"));
155 DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhd, DHD_EVENT_TIMEOUT_MS);
156 DHD_OS_WAKE_UNLOCK(dhd);
157 return OSL_ERROR(BCME_DONGLE_DOWN);
160 /* currently there is only one wiphy for ifidx 0 */
161 err = dhd_ioctl_process(dhd, 0, ioc);
165 /* response data is in ioc->buf so return ioc here */
166 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*ioc));
167 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*ioc), ioc);
168 err = cfg80211_testmode_reply(reply);
170 DHD_OS_WAKE_UNLOCK(dhd);
173 #endif /* CONFIG_NL80211_TESTMODE */
175 /* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */
178 #if defined(COEX_DHCP)
180 /* use New SCO/eSCO smart YG suppression */
181 #define BT_DHCP_eSCO_FIX
182 /* this flag boost wifi pkt priority to max, caution: -not fair to sco */
183 #define BT_DHCP_USE_FLAGS
184 /* T1 start SCO/ESCo priority suppression */
185 #define BT_DHCP_OPPR_WIN_TIME 2500
186 /* T2 turn off SCO/SCO supperesion is (timeout) */
187 #define BT_DHCP_FLAG_FORCE_TIME 5500
189 enum wl_cfg80211_btcoex_status {
193 BT_DHCP_FLAG_FORCE_TIMEOUT
197 * get named driver variable to uint register value and return error indication
198 * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value)
201 dev_wlc_intvar_get_reg(struct net_device *dev, char *name,
202 uint reg, int *retval)
205 char buf[WLC_IOCTL_SMLEN];
210 bcm_mkiovar(name, (char *)(®), sizeof(reg),
211 (char *)(&var), sizeof(var.buf));
212 error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false);
214 *retval = dtoh32(var.val);
219 dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len)
221 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
222 char ioctlbuf_local[1024];
224 static char ioctlbuf_local[1024];
225 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
227 bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local));
229 return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true));
232 get named driver variable to uint register value and return error indication
233 calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value)
236 dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val)
240 memset(reg_addr, 0, sizeof(reg_addr));
241 memcpy((char *)®_addr[0], (char *)addr, 4);
242 memcpy((char *)®_addr[4], (char *)val, 4);
244 return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr)));
247 static bool btcoex_is_sco_active(struct net_device *dev)
255 for (i = 0; i < 12; i++) {
257 ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27);
259 WL_TRACE(("sample[%d], btc params: 27:%x\n", i, param27));
262 WL_ERR(("ioc read btc params error\n"));
266 if ((param27 & 0x6) == 2) { /* count both sco & esco */
270 if (sco_id_cnt > 2) {
271 WL_TRACE(("sco/esco detected, pkt id_cnt:%d samples:%d\n",
283 #if defined(BT_DHCP_eSCO_FIX)
284 /* Enhanced BT COEX settings for eSCO compatibility during DHCP window */
285 static int set_btc_esco_params(struct net_device *dev, bool trump_sco)
287 static bool saved_status = FALSE;
289 char buf_reg50va_dhcp_on[8] =
290 { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 };
291 char buf_reg51va_dhcp_on[8] =
292 { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
293 char buf_reg64va_dhcp_on[8] =
294 { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
295 char buf_reg65va_dhcp_on[8] =
296 { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
297 char buf_reg71va_dhcp_on[8] =
298 { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
300 static uint32 saved_reg50;
301 static uint32 saved_reg51;
302 static uint32 saved_reg64;
303 static uint32 saved_reg65;
304 static uint32 saved_reg71;
307 /* this should reduce eSCO agressive retransmit
311 /* 1st save current */
312 WL_TRACE(("Do new SCO/eSCO coex algo {save &"
314 if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) &&
315 (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) &&
316 (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) &&
317 (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) &&
318 (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) {
320 WL_TRACE(("saved bt_params[50,51,64,65,71]:"
321 "0x%x 0x%x 0x%x 0x%x 0x%x\n",
322 saved_reg50, saved_reg51,
323 saved_reg64, saved_reg65, saved_reg71));
325 WL_ERR((":%s: save btc_params failed\n",
327 saved_status = FALSE;
331 WL_TRACE(("override with [50,51,64,65,71]:"
332 "0x%x 0x%x 0x%x 0x%x 0x%x\n",
333 *(u32 *)(buf_reg50va_dhcp_on+4),
334 *(u32 *)(buf_reg51va_dhcp_on+4),
335 *(u32 *)(buf_reg64va_dhcp_on+4),
336 *(u32 *)(buf_reg65va_dhcp_on+4),
337 *(u32 *)(buf_reg71va_dhcp_on+4)));
339 dev_wlc_bufvar_set(dev, "btc_params",
340 (char *)&buf_reg50va_dhcp_on[0], 8);
341 dev_wlc_bufvar_set(dev, "btc_params",
342 (char *)&buf_reg51va_dhcp_on[0], 8);
343 dev_wlc_bufvar_set(dev, "btc_params",
344 (char *)&buf_reg64va_dhcp_on[0], 8);
345 dev_wlc_bufvar_set(dev, "btc_params",
346 (char *)&buf_reg65va_dhcp_on[0], 8);
347 dev_wlc_bufvar_set(dev, "btc_params",
348 (char *)&buf_reg71va_dhcp_on[0], 8);
351 } else if (saved_status) {
352 /* restore previously saved bt params */
353 WL_TRACE(("Do new SCO/eSCO coex algo {save &"
357 dev_wlc_intvar_set_reg(dev, "btc_params",
358 (char *)®addr, (char *)&saved_reg50);
360 dev_wlc_intvar_set_reg(dev, "btc_params",
361 (char *)®addr, (char *)&saved_reg51);
363 dev_wlc_intvar_set_reg(dev, "btc_params",
364 (char *)®addr, (char *)&saved_reg64);
366 dev_wlc_intvar_set_reg(dev, "btc_params",
367 (char *)®addr, (char *)&saved_reg65);
369 dev_wlc_intvar_set_reg(dev, "btc_params",
370 (char *)®addr, (char *)&saved_reg71);
372 WL_TRACE(("restore bt_params[50,51,64,65,71]:"
373 "0x%x 0x%x 0x%x 0x%x 0x%x\n",
374 saved_reg50, saved_reg51, saved_reg64,
375 saved_reg65, saved_reg71));
377 saved_status = FALSE;
379 WL_ERR((":%s att to restore not saved BTCOEX params\n",
385 #endif /* BT_DHCP_eSCO_FIX */
388 wl_cfg80211_bt_setflag(struct net_device *dev, bool set)
390 #if defined(BT_DHCP_USE_FLAGS)
391 char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 };
392 char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
396 #if defined(BT_DHCP_eSCO_FIX)
397 /* set = 1, save & turn on 0 - off & restore prev settings */
398 set_btc_esco_params(dev, set);
401 #if defined(BT_DHCP_USE_FLAGS)
402 WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set));
404 /* Forcing bt_flag7 */
405 dev_wlc_bufvar_set(dev, "btc_flags",
406 (char *)&buf_flag7_dhcp_on[0],
407 sizeof(buf_flag7_dhcp_on));
409 /* Restoring default bt flag7 */
410 dev_wlc_bufvar_set(dev, "btc_flags",
411 (char *)&buf_flag7_default[0],
412 sizeof(buf_flag7_default));
416 static void wl_cfg80211_bt_timerfunc(ulong data)
418 struct btcoex_info *bt_local = (struct btcoex_info *)data;
419 WL_TRACE(("Enter\n"));
420 bt_local->timer_on = 0;
421 schedule_work(&bt_local->work);
424 static void wl_cfg80211_bt_handler(struct work_struct *work)
426 struct btcoex_info *btcx_inf;
428 btcx_inf = container_of(work, struct btcoex_info, work);
430 if (btcx_inf->timer_on) {
431 btcx_inf->timer_on = 0;
432 del_timer_sync(&btcx_inf->timer);
435 switch (btcx_inf->bt_state) {
438 * provide OPPORTUNITY window to get DHCP address
440 WL_TRACE(("bt_dhcp stm: started \n"));
442 btcx_inf->bt_state = BT_DHCP_OPPR_WIN;
443 mod_timer(&btcx_inf->timer,
444 jiffies + msecs_to_jiffies(BT_DHCP_OPPR_WIN_TIME));
445 btcx_inf->timer_on = 1;
448 case BT_DHCP_OPPR_WIN:
449 if (btcx_inf->dhcp_done) {
450 WL_TRACE(("DHCP Done before T1 expiration\n"));
454 /* DHCP is not over yet, start lowering BT priority
455 * enforce btc_params + flags if necessary
457 WL_TRACE(("DHCP T1:%d expired\n", BT_DHCP_OPPR_WIN_TIME));
459 wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE);
460 btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT;
461 mod_timer(&btcx_inf->timer,
462 jiffies + msecs_to_jiffies(BT_DHCP_FLAG_FORCE_TIME));
463 btcx_inf->timer_on = 1;
466 case BT_DHCP_FLAG_FORCE_TIMEOUT:
467 if (btcx_inf->dhcp_done) {
468 WL_TRACE(("DHCP Done before T2 expiration\n"));
470 /* Noo dhcp during T1+T2, restore BT priority */
471 WL_TRACE(("DHCP wait interval T2:%d msec expired\n",
472 BT_DHCP_FLAG_FORCE_TIME));
475 /* Restoring default bt priority */
477 wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE);
479 btcx_inf->bt_state = BT_DHCP_IDLE;
480 btcx_inf->timer_on = 0;
484 WL_ERR(("error g_status=%d !!!\n", btcx_inf->bt_state));
486 wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE);
487 btcx_inf->bt_state = BT_DHCP_IDLE;
488 btcx_inf->timer_on = 0;
492 net_os_wake_unlock(btcx_inf->dev);
495 int wl_cfg80211_btcoex_init(struct wl_priv *wl)
497 struct btcoex_info *btco_inf = NULL;
499 btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL);
503 btco_inf->bt_state = BT_DHCP_IDLE;
504 btco_inf->ts_dhcp_start = 0;
505 btco_inf->ts_dhcp_ok = 0;
506 /* Set up timer for BT */
507 btco_inf->timer_ms = 10;
508 init_timer(&btco_inf->timer);
509 btco_inf->timer.data = (ulong)btco_inf;
510 btco_inf->timer.function = wl_cfg80211_bt_timerfunc;
512 btco_inf->dev = wl->wdev->netdev;
514 INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler);
516 wl->btcoex_info = btco_inf;
520 void wl_cfg80211_btcoex_deinit(struct wl_priv *wl)
522 if (!wl->btcoex_info)
525 if (wl->btcoex_info->timer_on) {
526 wl->btcoex_info->timer_on = 0;
527 del_timer_sync(&wl->btcoex_info->timer);
530 cancel_work_sync(&wl->btcoex_info->work);
532 kfree(wl->btcoex_info);
533 wl->btcoex_info = NULL;
536 int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command)
539 struct wl_priv *wl = wlcfg_drv_priv;
540 char powermode_val = 0;
541 char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 };
542 char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 };
543 char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 };
546 static uint32 saved_reg66;
547 static uint32 saved_reg41;
548 static uint32 saved_reg68;
549 static bool saved_status = FALSE;
552 char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
553 struct btcoex_info *btco_inf = wl->btcoex_info;
554 #endif /* COEX_DHCP */
556 #ifdef PKT_FILTER_SUPPORT
557 dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub);
560 /* Figure out powermode 1 or o command */
561 strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1);
563 if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) {
564 WL_TRACE_HW4(("DHCP session starts\n"));
566 #if defined(DHCP_SCAN_SUPPRESS)
567 /* Suppress scan during the DHCP */
568 wl_cfg80211_scan_suppress(dev, 1);
569 #endif /* OEM_ANDROID */
571 #ifdef PKT_FILTER_SUPPORT
572 dhd->dhcp_in_progress = 1;
574 if (dhd->early_suspended) {
575 WL_TRACE_HW4(("DHCP in progressing , disable packet filter!!!\n"));
576 dhd_enable_packet_filter(0, dhd);
580 /* Retrieve and saved orig regs value */
581 if ((saved_status == FALSE) &&
582 (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) &&
583 (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) &&
584 (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) {
586 WL_TRACE(("Saved 0x%x 0x%x 0x%x\n",
587 saved_reg66, saved_reg41, saved_reg68));
589 /* Disable PM mode during dhpc session */
591 /* Disable PM mode during dhpc session */
593 /* Start BT timer only for SCO connection */
594 if (btcoex_is_sco_active(dev)) {
596 dev_wlc_bufvar_set(dev, "btc_params",
597 (char *)&buf_reg66va_dhcp_on[0],
598 sizeof(buf_reg66va_dhcp_on));
599 /* btc_params 41 0x33 */
600 dev_wlc_bufvar_set(dev, "btc_params",
601 (char *)&buf_reg41va_dhcp_on[0],
602 sizeof(buf_reg41va_dhcp_on));
603 /* btc_params 68 0x190 */
604 dev_wlc_bufvar_set(dev, "btc_params",
605 (char *)&buf_reg68va_dhcp_on[0],
606 sizeof(buf_reg68va_dhcp_on));
609 btco_inf->bt_state = BT_DHCP_START;
610 btco_inf->timer_on = 1;
611 mod_timer(&btco_inf->timer, btco_inf->timer.expires);
612 WL_TRACE(("enable BT DHCP Timer\n"));
614 #endif /* COEX_DHCP */
616 else if (saved_status == TRUE) {
617 WL_ERR(("was called w/o DHCP OFF. Continue\n"));
620 else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) {
623 #if defined(DHCP_SCAN_SUPPRESS)
624 /* Since DHCP is complete, enable the scan back */
625 wl_cfg80211_scan_suppress(dev, 0);
626 #endif /* OEM_ANDROID */
628 #ifdef PKT_FILTER_SUPPORT
629 dhd->dhcp_in_progress = 0;
630 WL_TRACE_HW4(("DHCP is complete \n"));
632 /* Enable packet filtering */
633 if (dhd->early_suspended) {
634 WL_TRACE_HW4(("DHCP is complete , enable packet filter!!!\n"));
635 dhd_enable_packet_filter(1, dhd);
637 #endif /* PKT_FILTER_SUPPORT */
639 /* Restoring PM mode */
642 /* Stop any bt timer because DHCP session is done */
643 WL_TRACE(("disable BT DHCP Timer\n"));
644 if (btco_inf->timer_on) {
645 btco_inf->timer_on = 0;
646 del_timer_sync(&btco_inf->timer);
648 if (btco_inf->bt_state != BT_DHCP_IDLE) {
649 /* need to restore original btc flags & extra btc params */
650 WL_TRACE(("bt->bt_state:%d\n", btco_inf->bt_state));
651 /* wake up btcoex thread to restore btlags+params */
652 schedule_work(&btco_inf->work);
656 /* Restoring btc_flag paramter anyway */
657 if (saved_status == TRUE)
658 dev_wlc_bufvar_set(dev, "btc_flags",
659 (char *)&buf_flag7_default[0], sizeof(buf_flag7_default));
660 #endif /* COEX_DHCP */
662 /* Restore original values */
663 if (saved_status == TRUE) {
665 dev_wlc_intvar_set_reg(dev, "btc_params",
666 (char *)®addr, (char *)&saved_reg66);
668 dev_wlc_intvar_set_reg(dev, "btc_params",
669 (char *)®addr, (char *)&saved_reg41);
671 dev_wlc_intvar_set_reg(dev, "btc_params",
672 (char *)®addr, (char *)&saved_reg68);
674 WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n",
675 saved_reg66, saved_reg41, saved_reg68));
677 saved_status = FALSE;
681 WL_ERR(("Unkwown yet power setting, ignored\n"));
684 snprintf(command, 3, "OK");
686 return (strlen("OK"));