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