Merge branch develop-3.10-next
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / rockchip_wlan / rkwifi / bcmdhd / wl_cfg_btcoex.c
1 /*
2  * Linux cfg80211 driver - Dongle Host Driver (DHD) related
3  *
4  * $Copyright Open Broadcom Corporation$
5  *
6  * $Id: wl_cfg_btcoex.c 467328 2014-04-03 01:23:40Z $
7  */
8
9 #include <net/rtnetlink.h>
10
11 #include <bcmutils.h>
12 #include <wldev_common.h>
13 #include <wl_cfg80211.h>
14 #include <dhd_cfg80211.h>
15 #include <dngl_stats.h>
16 #include <dhd.h>
17 #include <dhdioctl.h>
18 #include <wlioctl.h>
19
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);
24 #endif
25
26 struct btcoex_info {
27         struct timer_list timer;
28         u32 timer_ms;
29         u32 timer_on;
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
34                                          */
35         s32 bt_state;
36         struct work_struct work;
37         struct net_device *dev;
38 };
39
40 static struct btcoex_info *btcoex_info_loc = NULL;
41
42 /* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */
43
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
52
53 enum wl_cfg80211_btcoex_status {
54         BT_DHCP_IDLE,
55         BT_DHCP_START,
56         BT_DHCP_OPPR_WIN,
57         BT_DHCP_FLAG_FORCE_TIMEOUT
58 };
59
60 /*
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, &reg_value)
63  */
64 static int
65 dev_wlc_intvar_get_reg(struct net_device *dev, char *name,
66         uint reg, int *retval)
67 {
68         union {
69                 char buf[WLC_IOCTL_SMLEN];
70                 int val;
71         } var;
72         int error;
73
74         bcm_mkiovar(name, (char *)(&reg), sizeof(reg),
75                 (char *)(&var), sizeof(var.buf));
76         error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false);
77
78         *retval = dtoh32(var.val);
79         return (error);
80 }
81
82 static int
83 dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len)
84 {
85 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
86         char ioctlbuf_local[1024];
87 #else
88         static char ioctlbuf_local[1024];
89 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
90
91         bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local));
92
93         return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true));
94 }
95 /*
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)
98 */
99 static int
100 dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val)
101 {
102         char reg_addr[8];
103
104         memset(reg_addr, 0, sizeof(reg_addr));
105         memcpy((char *)&reg_addr[0], (char *)addr, 4);
106         memcpy((char *)&reg_addr[4], (char *)val, 4);
107
108         return (dev_wlc_bufvar_set(dev, name, (char *)&reg_addr[0], sizeof(reg_addr)));
109 }
110
111 static bool btcoex_is_sco_active(struct net_device *dev)
112 {
113         int ioc_res = 0;
114         bool res = FALSE;
115         int sco_id_cnt = 0;
116         int param27;
117         int i;
118
119         for (i = 0; i < 12; i++) {
120
121                 ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, &param27);
122
123                 WL_TRACE(("sample[%d], btc params: 27:%x\n", i, param27));
124
125                 if (ioc_res < 0) {
126                         WL_ERR(("ioc read btc params error\n"));
127                         break;
128                 }
129
130                 if ((param27 & 0x6) == 2) { /* count both sco & esco  */
131                         sco_id_cnt++;
132                 }
133
134                 if (sco_id_cnt > 2) {
135                         WL_TRACE(("sco/esco detected, pkt id_cnt:%d  samples:%d\n",
136                                 sco_id_cnt, i));
137                         res = TRUE;
138                         break;
139                 }
140
141                 OSL_SLEEP(5);
142         }
143
144         return res;
145 }
146
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)
150 {
151         static bool saved_status = FALSE;
152
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 };
163         uint32 regaddr;
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;
169
170         if (trump_sco) {
171                 /* this should reduce eSCO agressive retransmit
172                  * w/o breaking it
173                  */
174
175                 /* 1st save current */
176                 WL_TRACE(("Do new SCO/eSCO coex algo {save &"
177                           "override}\n"));
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))) {
183                         saved_status = TRUE;
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));
188                 } else {
189                         WL_ERR((":%s: save btc_params failed\n",
190                                 __FUNCTION__));
191                         saved_status = FALSE;
192                         return -1;
193                 }
194
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)));
202
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);
213
214                 saved_status = TRUE;
215         } else if (saved_status) {
216                 /* restore previously saved bt params */
217                 WL_TRACE(("Do new SCO/eSCO coex algo {save &"
218                           "override}\n"));
219
220                 regaddr = 50;
221                 dev_wlc_intvar_set_reg(dev, "btc_params",
222                         (char *)&regaddr, (char *)&saved_reg50);
223                 regaddr = 51;
224                 dev_wlc_intvar_set_reg(dev, "btc_params",
225                         (char *)&regaddr, (char *)&saved_reg51);
226                 regaddr = 64;
227                 dev_wlc_intvar_set_reg(dev, "btc_params",
228                         (char *)&regaddr, (char *)&saved_reg64);
229                 regaddr = 65;
230                 dev_wlc_intvar_set_reg(dev, "btc_params",
231                         (char *)&regaddr, (char *)&saved_reg65);
232                 regaddr = 71;
233                 dev_wlc_intvar_set_reg(dev, "btc_params",
234                         (char *)&regaddr, (char *)&saved_reg71);
235
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));
240
241                 saved_status = FALSE;
242         } else {
243                 WL_ERR((":%s att to restore not saved BTCOEX params\n",
244                         __FUNCTION__));
245                 return -1;
246         }
247         return 0;
248 }
249 #endif /* BT_DHCP_eSCO_FIX */
250
251 static void
252 wl_cfg80211_bt_setflag(struct net_device *dev, bool set)
253 {
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};
257 #endif
258
259
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);
263 #endif
264
265 #if defined(BT_DHCP_USE_FLAGS)
266         WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set));
267         if (set == TRUE)
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));
272         else
273                 /* Restoring default bt flag7 */
274                 dev_wlc_bufvar_set(dev, "btc_flags",
275                         (char *)&buf_flag7_default[0],
276                         sizeof(buf_flag7_default));
277 #endif
278 }
279
280 static void wl_cfg80211_bt_timerfunc(ulong data)
281 {
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);
286 }
287
288 static void wl_cfg80211_bt_handler(struct work_struct *work)
289 {
290         struct btcoex_info *btcx_inf;
291
292         btcx_inf = container_of(work, struct btcoex_info, work);
293
294         if (btcx_inf->timer_on) {
295                 btcx_inf->timer_on = 0;
296                 del_timer_sync(&btcx_inf->timer);
297         }
298
299         switch (btcx_inf->bt_state) {
300                 case BT_DHCP_START:
301                         /* DHCP started
302                          * provide OPPORTUNITY window to get DHCP address
303                          */
304                         WL_TRACE(("bt_dhcp stm: started \n"));
305
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;
310                         break;
311
312                 case BT_DHCP_OPPR_WIN:
313                         if (btcx_inf->dhcp_done) {
314                                 WL_TRACE(("DHCP Done before T1 expiration\n"));
315                                 goto btc_coex_idle;
316                         }
317
318                         /* DHCP is not over yet, start lowering BT priority
319                          * enforce btc_params + flags if necessary
320                          */
321                         WL_TRACE(("DHCP T1:%d expired\n", BT_DHCP_OPPR_WIN_TIME));
322                         if (btcx_inf->dev)
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;
328                         break;
329
330                 case BT_DHCP_FLAG_FORCE_TIMEOUT:
331                         if (btcx_inf->dhcp_done) {
332                                 WL_TRACE(("DHCP Done before T2 expiration\n"));
333                         } else {
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));
337                         }
338
339                         /* Restoring default bt priority */
340                         if (btcx_inf->dev)
341                                 wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE);
342 btc_coex_idle:
343                         btcx_inf->bt_state = BT_DHCP_IDLE;
344                         btcx_inf->timer_on = 0;
345                         break;
346
347                 default:
348                         WL_ERR(("error g_status=%d !!!\n",      btcx_inf->bt_state));
349                         if (btcx_inf->dev)
350                                 wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE);
351                         btcx_inf->bt_state = BT_DHCP_IDLE;
352                         btcx_inf->timer_on = 0;
353                         break;
354         }
355
356         net_os_wake_unlock(btcx_inf->dev);
357 }
358
359 void* wl_cfg80211_btcoex_init(struct net_device *ndev)
360 {
361         struct btcoex_info *btco_inf = NULL;
362
363         btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL);
364         if (!btco_inf)
365                 return NULL;
366
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;
375
376         btco_inf->dev = ndev;
377
378         INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler);
379
380         btcoex_info_loc = btco_inf;
381         return btco_inf;
382 }
383
384 void wl_cfg80211_btcoex_deinit()
385 {
386         if (!btcoex_info_loc)
387                 return;
388
389         if (btcoex_info_loc->timer_on) {
390                 btcoex_info_loc->timer_on = 0;
391                 del_timer_sync(&btcoex_info_loc->timer);
392         }
393
394         cancel_work_sync(&btcoex_info_loc->work);
395
396         kfree(btcoex_info_loc);
397 }
398
399 int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, dhd_pub_t *dhd, char *command)
400 {
401
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 };
407
408         uint32 regaddr;
409         static uint32 saved_reg66;
410         static uint32 saved_reg41;
411         static uint32 saved_reg68;
412         static bool saved_status = FALSE;
413
414         char buf_flag7_default[8] =   { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
415
416         /* Figure out powermode 1 or o command */
417         strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1);
418
419         if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) {
420                 WL_TRACE_HW4(("DHCP session starts\n"));
421
422 #if defined(DHCP_SCAN_SUPPRESS)
423                 /* Suppress scan during the DHCP */
424                 wl_cfg80211_scan_suppress(dev, 1);
425 #endif /* OEM_ANDROID */
426
427 #ifdef PKT_FILTER_SUPPORT
428                 dhd->dhcp_in_progress = 1;
429
430                 if (dhd->early_suspended) {
431                         WL_TRACE_HW4(("DHCP in progressing , disable packet filter!!!\n"));
432                         dhd_enable_packet_filter(0, dhd);
433                 }
434 #endif
435
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)))   {
441                                 saved_status = TRUE;
442                                 WL_TRACE(("Saved 0x%x 0x%x 0x%x\n",
443                                         saved_reg66, saved_reg41, saved_reg68));
444
445                                 /* Disable PM mode during dhpc session */
446
447                                 /* Disable PM mode during dhpc session */
448                                 /* Start  BT timer only for SCO connection */
449                                 if (btcoex_is_sco_active(dev)) {
450                                         /* btc_params 66 */
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));
462                                         saved_status = TRUE;
463
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"));
468                                 }
469                 }
470                 else if (saved_status == TRUE) {
471                         WL_ERR(("was called w/o DHCP OFF. Continue\n"));
472                 }
473         }
474         else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) {
475
476
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 */
481
482 #ifdef PKT_FILTER_SUPPORT
483                 dhd->dhcp_in_progress = 0;
484                 WL_TRACE_HW4(("DHCP is complete \n"));
485
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);
490                 }
491 #endif /* PKT_FILTER_SUPPORT */
492
493                 /* Restoring PM mode */
494
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);
500
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);
506                         }
507                 }
508
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));
513
514                 /* Restore original values */
515                 if (saved_status == TRUE) {
516                         regaddr = 66;
517                         dev_wlc_intvar_set_reg(dev, "btc_params",
518                                 (char *)&regaddr, (char *)&saved_reg66);
519                         regaddr = 41;
520                         dev_wlc_intvar_set_reg(dev, "btc_params",
521                                 (char *)&regaddr, (char *)&saved_reg41);
522                         regaddr = 68;
523                         dev_wlc_intvar_set_reg(dev, "btc_params",
524                                 (char *)&regaddr, (char *)&saved_reg68);
525
526                         WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n",
527                                 saved_reg66, saved_reg41, saved_reg68));
528                 }
529                 saved_status = FALSE;
530
531         }
532         else {
533                 WL_ERR(("Unkwown yet power setting, ignored\n"));
534         }
535
536         snprintf(command, 3, "OK");
537
538         return (strlen("OK"));
539 }