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