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