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