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