xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/wl_android_ext.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun 
2*4882a593Smuzhiyun #include <linux/module.h>
3*4882a593Smuzhiyun #include <linux/netdevice.h>
4*4882a593Smuzhiyun #include <net/netlink.h>
5*4882a593Smuzhiyun #include <typedefs.h>
6*4882a593Smuzhiyun #include <linuxver.h>
7*4882a593Smuzhiyun #include <osl.h>
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <bcmutils.h>
10*4882a593Smuzhiyun #include <bcmendian.h>
11*4882a593Smuzhiyun #include <ethernet.h>
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <wl_android.h>
14*4882a593Smuzhiyun #include <linux/if_arp.h>
15*4882a593Smuzhiyun #include <asm/uaccess.h>
16*4882a593Smuzhiyun #include <linux/wireless.h>
17*4882a593Smuzhiyun #if defined(WL_WIRELESS_EXT)
18*4882a593Smuzhiyun #include <wl_iw.h>
19*4882a593Smuzhiyun #endif /* WL_WIRELESS_EXT */
20*4882a593Smuzhiyun #include <wldev_common.h>
21*4882a593Smuzhiyun #include <wlioctl.h>
22*4882a593Smuzhiyun #include <bcmutils.h>
23*4882a593Smuzhiyun #include <linux_osl.h>
24*4882a593Smuzhiyun #include <dhd_dbg.h>
25*4882a593Smuzhiyun #include <dngl_stats.h>
26*4882a593Smuzhiyun #include <dhd.h>
27*4882a593Smuzhiyun #include <dhd_config.h>
28*4882a593Smuzhiyun #ifdef WL_CFG80211
29*4882a593Smuzhiyun #include <wl_cfg80211.h>
30*4882a593Smuzhiyun #endif /* WL_CFG80211 */
31*4882a593Smuzhiyun #ifdef WL_ESCAN
32*4882a593Smuzhiyun #include <wl_escan.h>
33*4882a593Smuzhiyun #endif /* WL_ESCAN */
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun uint android_msg_level = ANDROID_ERROR_LEVEL | ANDROID_MSG_LEVEL;
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #define AEXT_ERROR(name, arg1, args...) \
38*4882a593Smuzhiyun 	do { \
39*4882a593Smuzhiyun 		if (android_msg_level & ANDROID_ERROR_LEVEL) { \
40*4882a593Smuzhiyun 			printf("[%s] AEXT-ERROR) %s : " arg1, name, __func__, ## args); \
41*4882a593Smuzhiyun 		} \
42*4882a593Smuzhiyun 	} while (0)
43*4882a593Smuzhiyun #define AEXT_TRACE(name, arg1, args...) \
44*4882a593Smuzhiyun 	do { \
45*4882a593Smuzhiyun 		if (android_msg_level & ANDROID_TRACE_LEVEL) { \
46*4882a593Smuzhiyun 			printf("[%s] AEXT-TRACE) %s : " arg1, name, __func__, ## args); \
47*4882a593Smuzhiyun 		} \
48*4882a593Smuzhiyun 	} while (0)
49*4882a593Smuzhiyun #define AEXT_INFO(name, arg1, args...) \
50*4882a593Smuzhiyun 	do { \
51*4882a593Smuzhiyun 		if (android_msg_level & ANDROID_INFO_LEVEL) { \
52*4882a593Smuzhiyun 			printf("[%s] AEXT-INFO) %s : " arg1, name, __func__, ## args); \
53*4882a593Smuzhiyun 		} \
54*4882a593Smuzhiyun 	} while (0)
55*4882a593Smuzhiyun #define AEXT_DBG(name, arg1, args...) \
56*4882a593Smuzhiyun 	do { \
57*4882a593Smuzhiyun 		if (android_msg_level & ANDROID_DBG_LEVEL) { \
58*4882a593Smuzhiyun 			printf("[%s] AEXT-DBG) %s : " arg1, name, __func__, ## args); \
59*4882a593Smuzhiyun 		} \
60*4882a593Smuzhiyun 	} while (0)
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun #ifndef WL_CFG80211
63*4882a593Smuzhiyun #define htod32(i) i
64*4882a593Smuzhiyun #define htod16(i) i
65*4882a593Smuzhiyun #define dtoh32(i) i
66*4882a593Smuzhiyun #define dtoh16(i) i
67*4882a593Smuzhiyun #define htodchanspec(i) i
68*4882a593Smuzhiyun #define dtohchanspec(i) i
69*4882a593Smuzhiyun #define IEEE80211_BAND_2GHZ 0
70*4882a593Smuzhiyun #define IEEE80211_BAND_5GHZ 1
71*4882a593Smuzhiyun #define WL_SCAN_JOIN_PROBE_INTERVAL_MS 		20
72*4882a593Smuzhiyun #define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 	320
73*4882a593Smuzhiyun #define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 	400
74*4882a593Smuzhiyun #endif /* WL_CFG80211 */
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun #ifndef IW_CUSTOM_MAX
77*4882a593Smuzhiyun #define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */
78*4882a593Smuzhiyun #endif /* IW_CUSTOM_MAX */
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun #define CMD_CHANNEL				"CHANNEL"
81*4882a593Smuzhiyun #define CMD_CHANNELS			"CHANNELS"
82*4882a593Smuzhiyun #define CMD_ROAM_TRIGGER		"ROAM_TRIGGER"
83*4882a593Smuzhiyun #define CMD_PM					"PM"
84*4882a593Smuzhiyun #define CMD_MONITOR				"MONITOR"
85*4882a593Smuzhiyun #ifdef BTC_WAR
86*4882a593Smuzhiyun #define CMD_BTC_WAR			"BTC_WAR"
87*4882a593Smuzhiyun #endif /* BTC_WAR */
88*4882a593Smuzhiyun #define CMD_SET_SUSPEND_BCN_LI_DTIM		"SET_SUSPEND_BCN_LI_DTIM"
89*4882a593Smuzhiyun #define CMD_WLMSGLEVEL			"WLMSGLEVEL"
90*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
91*4882a593Smuzhiyun #define CMD_IAPSTA_INIT			"IAPSTA_INIT"
92*4882a593Smuzhiyun #define CMD_IAPSTA_CONFIG		"IAPSTA_CONFIG"
93*4882a593Smuzhiyun #define CMD_IAPSTA_ENABLE		"IAPSTA_ENABLE"
94*4882a593Smuzhiyun #define CMD_IAPSTA_DISABLE		"IAPSTA_DISABLE"
95*4882a593Smuzhiyun #define CMD_ISAM_INIT			"ISAM_INIT"
96*4882a593Smuzhiyun #define CMD_ISAM_CONFIG			"ISAM_CONFIG"
97*4882a593Smuzhiyun #define CMD_ISAM_ENABLE			"ISAM_ENABLE"
98*4882a593Smuzhiyun #define CMD_ISAM_DISABLE		"ISAM_DISABLE"
99*4882a593Smuzhiyun #define CMD_ISAM_STATUS			"ISAM_STATUS"
100*4882a593Smuzhiyun #define CMD_ISAM_PEER_PATH		"ISAM_PEER_PATH"
101*4882a593Smuzhiyun #define CMD_ISAM_PARAM			"ISAM_PARAM"
102*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
103*4882a593Smuzhiyun #define CMD_AUTOCHANNEL		"AUTOCHANNEL"
104*4882a593Smuzhiyun #define CMD_WL		"WL"
105*4882a593Smuzhiyun #define CMD_CONF	"CONF"
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun #if defined(PKT_STATICS) && defined(BCMSDIO)
108*4882a593Smuzhiyun #define CMD_DUMP_PKT_STATICS			"DUMP_PKT_STATICS"
109*4882a593Smuzhiyun #define CMD_CLEAR_PKT_STATICS			"CLEAR_PKT_STATICS"
110*4882a593Smuzhiyun extern void dhd_bus_dump_txpktstatics(dhd_pub_t *dhdp);
111*4882a593Smuzhiyun extern void dhd_bus_clear_txpktstatics(dhd_pub_t *dhdp);
112*4882a593Smuzhiyun #endif /* PKT_STATICS && BCMSDIO */
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun #ifdef IDHCP
115*4882a593Smuzhiyun typedef struct dhcpc_parameter {
116*4882a593Smuzhiyun 	uint32 ip_addr;
117*4882a593Smuzhiyun 	uint32 ip_serv;
118*4882a593Smuzhiyun 	uint32 lease_time;
119*4882a593Smuzhiyun } dhcpc_para_t;
120*4882a593Smuzhiyun #endif /* IDHCP */
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun #ifdef WL_EXT_WOWL
123*4882a593Smuzhiyun #define WL_WOWL_TCPFIN	(1 << 26)
124*4882a593Smuzhiyun typedef struct wl_wowl_pattern2 {
125*4882a593Smuzhiyun 	char cmd[4];
126*4882a593Smuzhiyun 	wl_wowl_pattern_t wowl_pattern;
127*4882a593Smuzhiyun } wl_wowl_pattern2_t;
128*4882a593Smuzhiyun #endif /* WL_EXT_WOWL */
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun #ifdef WL_EXT_TCPKA
131*4882a593Smuzhiyun typedef struct tcpka_conn {
132*4882a593Smuzhiyun 	uint32 sess_id;
133*4882a593Smuzhiyun 	struct ether_addr dst_mac;	/* Destinition Mac */
134*4882a593Smuzhiyun 	struct ipv4_addr  src_ip;	/* Sorce IP */
135*4882a593Smuzhiyun 	struct ipv4_addr  dst_ip;	/* Destinition IP */
136*4882a593Smuzhiyun 	uint16 ipid;	/* Ip Identification */
137*4882a593Smuzhiyun 	uint16 srcport;	/* Source Port Address */
138*4882a593Smuzhiyun 	uint16 dstport;	/* Destination Port Address */
139*4882a593Smuzhiyun 	uint32 seq;		/* TCP Sequence Number */
140*4882a593Smuzhiyun 	uint32 ack;		/* TCP Ack Number */
141*4882a593Smuzhiyun 	uint16 tcpwin;	/* TCP window */
142*4882a593Smuzhiyun 	uint32 tsval;	/* Timestamp Value */
143*4882a593Smuzhiyun 	uint32 tsecr;	/* Timestamp Echo Reply */
144*4882a593Smuzhiyun 	uint32 len;		/* last packet payload len */
145*4882a593Smuzhiyun 	uint32 ka_payload_len;	/* keep alive payload length */
146*4882a593Smuzhiyun 	uint8  ka_payload[1];	/* keep alive payload */
147*4882a593Smuzhiyun } tcpka_conn_t;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun typedef struct tcpka_conn_sess {
150*4882a593Smuzhiyun 	uint32 sess_id;	/* session id */
151*4882a593Smuzhiyun 	uint32 flag;	/* enable/disable flag */
152*4882a593Smuzhiyun 	wl_mtcpkeep_alive_timers_pkt_t  tcpka_timers;
153*4882a593Smuzhiyun } tcpka_conn_sess_t;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun typedef struct tcpka_conn_info {
156*4882a593Smuzhiyun 	uint32 ipid;
157*4882a593Smuzhiyun 	uint32 seq;
158*4882a593Smuzhiyun 	uint32 ack;
159*4882a593Smuzhiyun } tcpka_conn_sess_info_t;
160*4882a593Smuzhiyun #endif /* WL_EXT_TCPKA */
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun typedef struct auth_name_map_t {
163*4882a593Smuzhiyun 	uint auth;
164*4882a593Smuzhiyun 	uint wpa_auth;
165*4882a593Smuzhiyun 	char *auth_name;
166*4882a593Smuzhiyun } auth_name_map_t;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun const auth_name_map_t auth_name_map[] = {
169*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA_AUTH_DISABLED,	"open"},
170*4882a593Smuzhiyun 	{WL_AUTH_SHARED_KEY,	WPA_AUTH_DISABLED,	"shared"},
171*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA_AUTH_PSK,		"wpa/psk"},
172*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA2_AUTH_PSK,		"wpa2/psk"},
173*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK,	"wpa2/psk/sha256"},
174*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA2_AUTH_FT|WPA2_AUTH_PSK,			"wpa2/psk/ft"},
175*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA2_AUTH_UNSPECIFIED,				"wpa2/eap"},
176*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA2_AUTH_FT|WPA2_AUTH_UNSPECIFIED,	"wpa2/eap/ft"},
177*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA3_AUTH_SAE_PSK,	"wpa3/psk"},
178*4882a593Smuzhiyun 	{WL_AUTH_SAE_KEY,		WPA3_AUTH_SAE_PSK,	"wpa3sae/psk"},
179*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA3_AUTH_SAE_PSK|WPA2_AUTH_PSK,	"wpa3/psk"},
180*4882a593Smuzhiyun 	{WL_AUTH_SAE_KEY,		WPA3_AUTH_SAE_PSK|WPA2_AUTH_PSK,	"wpa3sae/psk"},
181*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	0x20,	"wpa3/psk"},
182*4882a593Smuzhiyun 	{WL_AUTH_SAE_KEY,		0x20,	"wpa3sae/psk"},
183*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA3_AUTH_SAE_PSK|WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK,	"wpa3/psk/sha256"},
184*4882a593Smuzhiyun 	{WL_AUTH_SAE_KEY,		WPA3_AUTH_SAE_PSK|WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK,	"wpa3sae/psk/sha256"},
185*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	0x20|WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK,	"wpa3/psk/sha256"},
186*4882a593Smuzhiyun 	{WL_AUTH_SAE_KEY,		0x20|WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK,	"wpa3sae/psk/sha256"},
187*4882a593Smuzhiyun 	{WL_AUTH_OPEN_SYSTEM,	WPA3_AUTH_OWE,	"owe"},
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun typedef struct wsec_name_map_t {
191*4882a593Smuzhiyun 	uint wsec;
192*4882a593Smuzhiyun 	char *wsec_name;
193*4882a593Smuzhiyun } wsec_name_map_t;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun const wsec_name_map_t wsec_name_map[] = {
196*4882a593Smuzhiyun 	{WSEC_NONE,		"none"},
197*4882a593Smuzhiyun 	{WEP_ENABLED,	"wep"},
198*4882a593Smuzhiyun 	{TKIP_ENABLED,	"tkip"},
199*4882a593Smuzhiyun 	{AES_ENABLED,	"aes"},
200*4882a593Smuzhiyun 	{TKIP_ENABLED|AES_ENABLED,	"tkip/aes"},
201*4882a593Smuzhiyun };
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun static int wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len);
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun int
wl_ext_ioctl(struct net_device * dev,u32 cmd,void * arg,u32 len,u32 set)206*4882a593Smuzhiyun wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	int ret;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	ret = wldev_ioctl(dev, cmd, arg, len, set);
211*4882a593Smuzhiyun 	if (ret)
212*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "cmd=%d, ret=%d\n", cmd, ret);
213*4882a593Smuzhiyun 	return ret;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun int
wl_ext_iovar_getint(struct net_device * dev,s8 * iovar,s32 * val)217*4882a593Smuzhiyun wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun 	int ret;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	ret = wldev_iovar_getint(dev, iovar, val);
222*4882a593Smuzhiyun 	if (ret)
223*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar, ret);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	return ret;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun int
wl_ext_iovar_setint(struct net_device * dev,s8 * iovar,s32 val)229*4882a593Smuzhiyun wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	int ret;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	ret = wldev_iovar_setint(dev, iovar, val);
234*4882a593Smuzhiyun 	if (ret)
235*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar, ret);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	return ret;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun int
wl_ext_iovar_getbuf(struct net_device * dev,s8 * iovar_name,void * param,s32 paramlen,void * buf,s32 buflen,struct mutex * buf_sync)241*4882a593Smuzhiyun wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name,
242*4882a593Smuzhiyun 	void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun 	int ret;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
247*4882a593Smuzhiyun 	if (ret != 0)
248*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	return ret;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun int
wl_ext_iovar_setbuf(struct net_device * dev,s8 * iovar_name,void * param,s32 paramlen,void * buf,s32 buflen,struct mutex * buf_sync)254*4882a593Smuzhiyun wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name,
255*4882a593Smuzhiyun 	void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	int ret;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
260*4882a593Smuzhiyun 	if (ret != 0)
261*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	return ret;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun int
wl_ext_iovar_setbuf_bsscfg(struct net_device * dev,s8 * iovar_name,void * param,s32 paramlen,void * buf,s32 buflen,s32 bsscfg_idx,struct mutex * buf_sync)267*4882a593Smuzhiyun wl_ext_iovar_setbuf_bsscfg(struct net_device *dev, s8 *iovar_name,
268*4882a593Smuzhiyun 	void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx,
269*4882a593Smuzhiyun 	struct mutex* buf_sync)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	int ret;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen,
274*4882a593Smuzhiyun 		buf, buflen, bsscfg_idx, buf_sync);
275*4882a593Smuzhiyun 	if (ret < 0)
276*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	return ret;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun static chanspec_t
wl_ext_chspec_to_legacy(chanspec_t chspec)282*4882a593Smuzhiyun wl_ext_chspec_to_legacy(chanspec_t chspec)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun 	chanspec_t lchspec;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	if (wf_chspec_malformed(chspec)) {
287*4882a593Smuzhiyun 		AEXT_ERROR("wlan", "input chanspec (0x%04X) malformed\n", chspec);
288*4882a593Smuzhiyun 		return INVCHANSPEC;
289*4882a593Smuzhiyun 	}
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	/* get the channel number */
292*4882a593Smuzhiyun 	lchspec = CHSPEC_CHANNEL(chspec);
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	/* convert the band */
295*4882a593Smuzhiyun 	if (CHSPEC_IS2G(chspec)) {
296*4882a593Smuzhiyun 		lchspec |= WL_LCHANSPEC_BAND_2G;
297*4882a593Smuzhiyun 	} else {
298*4882a593Smuzhiyun 		lchspec |= WL_LCHANSPEC_BAND_5G;
299*4882a593Smuzhiyun 	}
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	/* convert the bw and sideband */
302*4882a593Smuzhiyun 	if (CHSPEC_IS20(chspec)) {
303*4882a593Smuzhiyun 		lchspec |= WL_LCHANSPEC_BW_20;
304*4882a593Smuzhiyun 		lchspec |= WL_LCHANSPEC_CTL_SB_NONE;
305*4882a593Smuzhiyun 	} else if (CHSPEC_IS40(chspec)) {
306*4882a593Smuzhiyun 		lchspec |= WL_LCHANSPEC_BW_40;
307*4882a593Smuzhiyun 		if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) {
308*4882a593Smuzhiyun 			lchspec |= WL_LCHANSPEC_CTL_SB_LOWER;
309*4882a593Smuzhiyun 		} else {
310*4882a593Smuzhiyun 			lchspec |= WL_LCHANSPEC_CTL_SB_UPPER;
311*4882a593Smuzhiyun 		}
312*4882a593Smuzhiyun 	} else {
313*4882a593Smuzhiyun 		/* cannot express the bandwidth */
314*4882a593Smuzhiyun 		char chanbuf[CHANSPEC_STR_LEN];
315*4882a593Smuzhiyun 		AEXT_ERROR("wlan", "unable to convert chanspec %s (0x%04X) "
316*4882a593Smuzhiyun 			"to pre-11ac format\n",
317*4882a593Smuzhiyun 			wf_chspec_ntoa(chspec, chanbuf), chspec);
318*4882a593Smuzhiyun 		return INVCHANSPEC;
319*4882a593Smuzhiyun 	}
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	return lchspec;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun chanspec_t
wl_ext_chspec_host_to_driver(struct dhd_pub * dhd,chanspec_t chanspec)325*4882a593Smuzhiyun wl_ext_chspec_host_to_driver(struct dhd_pub *dhd, chanspec_t chanspec)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	if (dhd->conf->ioctl_ver == 1) {
328*4882a593Smuzhiyun 		chanspec = wl_ext_chspec_to_legacy(chanspec);
329*4882a593Smuzhiyun 		if (chanspec == INVCHANSPEC) {
330*4882a593Smuzhiyun 			return chanspec;
331*4882a593Smuzhiyun 		}
332*4882a593Smuzhiyun 	}
333*4882a593Smuzhiyun 	chanspec = htodchanspec(chanspec);
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	return chanspec;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun static void
wl_ext_ch_to_chanspec(struct dhd_pub * dhd,int ch,struct wl_join_params * join_params,size_t * join_params_size)339*4882a593Smuzhiyun wl_ext_ch_to_chanspec(struct dhd_pub *dhd, int ch,
340*4882a593Smuzhiyun 	struct wl_join_params *join_params, size_t *join_params_size)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun 	chanspec_t chanspec = 0;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	if (ch != 0) {
345*4882a593Smuzhiyun 		join_params->params.chanspec_num = 1;
346*4882a593Smuzhiyun 		join_params->params.chanspec_list[0] = ch;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 		if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL)
349*4882a593Smuzhiyun 			chanspec |= WL_CHANSPEC_BAND_2G;
350*4882a593Smuzhiyun 		else
351*4882a593Smuzhiyun 			chanspec |= WL_CHANSPEC_BAND_5G;
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 		chanspec |= WL_CHANSPEC_BW_20;
354*4882a593Smuzhiyun 		chanspec |= WL_CHANSPEC_CTL_SB_NONE;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 		*join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE +
357*4882a593Smuzhiyun 			join_params->params.chanspec_num * sizeof(chanspec_t);
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 		join_params->params.chanspec_list[0]  &= WL_CHANSPEC_CHAN_MASK;
360*4882a593Smuzhiyun 		join_params->params.chanspec_list[0] |= chanspec;
361*4882a593Smuzhiyun 		join_params->params.chanspec_list[0] =
362*4882a593Smuzhiyun 			wl_ext_chspec_host_to_driver(dhd,
363*4882a593Smuzhiyun 				join_params->params.chanspec_list[0]);
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 		join_params->params.chanspec_num =
366*4882a593Smuzhiyun 			htod32(join_params->params.chanspec_num);
367*4882a593Smuzhiyun 	}
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun #if defined(WL_EXT_IAPSTA) || defined(WL_CFG80211) || defined(WL_ESCAN)
371*4882a593Smuzhiyun static chanspec_t
wl_ext_chspec_from_legacy(chanspec_t legacy_chspec)372*4882a593Smuzhiyun wl_ext_chspec_from_legacy(chanspec_t legacy_chspec)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	chanspec_t chspec;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	/* get the channel number */
377*4882a593Smuzhiyun 	chspec = LCHSPEC_CHANNEL(legacy_chspec);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	/* convert the band */
380*4882a593Smuzhiyun 	if (LCHSPEC_IS2G(legacy_chspec)) {
381*4882a593Smuzhiyun 		chspec |= WL_CHANSPEC_BAND_2G;
382*4882a593Smuzhiyun 	} else {
383*4882a593Smuzhiyun 		chspec |= WL_CHANSPEC_BAND_5G;
384*4882a593Smuzhiyun 	}
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	/* convert the bw and sideband */
387*4882a593Smuzhiyun 	if (LCHSPEC_IS20(legacy_chspec)) {
388*4882a593Smuzhiyun 		chspec |= WL_CHANSPEC_BW_20;
389*4882a593Smuzhiyun 	} else {
390*4882a593Smuzhiyun 		chspec |= WL_CHANSPEC_BW_40;
391*4882a593Smuzhiyun 		if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) {
392*4882a593Smuzhiyun 			chspec |= WL_CHANSPEC_CTL_SB_L;
393*4882a593Smuzhiyun 		} else {
394*4882a593Smuzhiyun 			chspec |= WL_CHANSPEC_CTL_SB_U;
395*4882a593Smuzhiyun 		}
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	if (wf_chspec_malformed(chspec)) {
399*4882a593Smuzhiyun 		AEXT_ERROR("wlan", "output chanspec (0x%04X) malformed\n", chspec);
400*4882a593Smuzhiyun 		return INVCHANSPEC;
401*4882a593Smuzhiyun 	}
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	return chspec;
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun chanspec_t
wl_ext_chspec_driver_to_host(struct dhd_pub * dhd,chanspec_t chanspec)407*4882a593Smuzhiyun wl_ext_chspec_driver_to_host(struct dhd_pub *dhd, chanspec_t chanspec)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	chanspec = dtohchanspec(chanspec);
410*4882a593Smuzhiyun 	if (dhd->conf->ioctl_ver == 1) {
411*4882a593Smuzhiyun 		chanspec = wl_ext_chspec_from_legacy(chanspec);
412*4882a593Smuzhiyun 	}
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	return chanspec;
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA || WL_CFG80211 || WL_ESCAN */
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun chanspec_band_t
wl_ext_wlcband_to_chanspec_band(int band)419*4882a593Smuzhiyun wl_ext_wlcband_to_chanspec_band(int band)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	chanspec_band_t chanspec_band = INVCHANSPEC;
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	switch (band) {
424*4882a593Smuzhiyun #ifdef WL_6G_BAND
425*4882a593Smuzhiyun 		case WLC_BAND_6G:
426*4882a593Smuzhiyun 			chanspec_band = WL_CHANSPEC_BAND_6G;
427*4882a593Smuzhiyun 			break;
428*4882a593Smuzhiyun #endif /* WL_6G_BAND */
429*4882a593Smuzhiyun 		case WLC_BAND_5G:
430*4882a593Smuzhiyun 			chanspec_band = WL_CHANSPEC_BAND_5G;
431*4882a593Smuzhiyun 			break;
432*4882a593Smuzhiyun 		case WLC_BAND_2G:
433*4882a593Smuzhiyun 			chanspec_band = WL_CHANSPEC_BAND_2G;
434*4882a593Smuzhiyun 			break;
435*4882a593Smuzhiyun 		default:
436*4882a593Smuzhiyun 			AEXT_ERROR("wlan", "Invalid Frequency Band\n");
437*4882a593Smuzhiyun 			break;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun 	return chanspec_band;
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun bool
wl_ext_check_scan(struct net_device * dev,dhd_pub_t * dhdp)443*4882a593Smuzhiyun wl_ext_check_scan(struct net_device *dev, dhd_pub_t *dhdp)
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun #ifdef WL_CFG80211
446*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
447*4882a593Smuzhiyun #endif /* WL_CFG80211 */
448*4882a593Smuzhiyun #ifdef WL_ESCAN
449*4882a593Smuzhiyun 	struct wl_escan_info *escan = dhdp->escan;
450*4882a593Smuzhiyun #endif /* WL_ESCAN */
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun #ifdef WL_CFG80211
453*4882a593Smuzhiyun 	if (wl_get_drv_status_all(cfg, SCANNING)) {
454*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "cfg80211 scanning...\n");
455*4882a593Smuzhiyun 		return TRUE;
456*4882a593Smuzhiyun 	}
457*4882a593Smuzhiyun #endif /* WL_CFG80211 */
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun #ifdef WL_ESCAN
460*4882a593Smuzhiyun 	if (escan->escan_state == ESCAN_STATE_SCANING) {
461*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "escan scanning...\n");
462*4882a593Smuzhiyun 		return TRUE;
463*4882a593Smuzhiyun 	}
464*4882a593Smuzhiyun #endif /* WL_ESCAN */
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	return FALSE;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun #if defined(WL_CFG80211) || defined(WL_ESCAN)
470*4882a593Smuzhiyun void
wl_ext_user_sync(struct dhd_pub * dhd,int ifidx,bool lock)471*4882a593Smuzhiyun wl_ext_user_sync(struct dhd_pub *dhd, int ifidx, bool lock)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun 	struct net_device *dev = dhd_idx2net(dhd, ifidx);
474*4882a593Smuzhiyun #ifdef WL_CFG80211
475*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
476*4882a593Smuzhiyun #endif /* WL_CFG80211 */
477*4882a593Smuzhiyun #ifdef WL_ESCAN
478*4882a593Smuzhiyun 	struct wl_escan_info *escan = dhd->escan;
479*4882a593Smuzhiyun #endif /* WL_ESCAN */
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	AEXT_INFO(dev->name, "lock=%d\n", lock);
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	if (lock) {
484*4882a593Smuzhiyun #if defined(WL_CFG80211)
485*4882a593Smuzhiyun 		mutex_lock(&cfg->usr_sync);
486*4882a593Smuzhiyun #endif
487*4882a593Smuzhiyun #if defined(WL_ESCAN)
488*4882a593Smuzhiyun 		mutex_lock(&escan->usr_sync);
489*4882a593Smuzhiyun #endif
490*4882a593Smuzhiyun 	} else {
491*4882a593Smuzhiyun #if defined(WL_CFG80211)
492*4882a593Smuzhiyun 		mutex_unlock(&cfg->usr_sync);
493*4882a593Smuzhiyun #endif
494*4882a593Smuzhiyun #if defined(WL_ESCAN)
495*4882a593Smuzhiyun 		mutex_unlock(&escan->usr_sync);
496*4882a593Smuzhiyun #endif
497*4882a593Smuzhiyun 	}
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun #endif /* WL_CFG80211 && WL_ESCAN */
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun static bool
wl_ext_event_complete(struct dhd_pub * dhd,int ifidx)502*4882a593Smuzhiyun wl_ext_event_complete(struct dhd_pub *dhd, int ifidx)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun 	struct net_device *dev = dhd_idx2net(dhd, ifidx);
505*4882a593Smuzhiyun #ifdef WL_CFG80211
506*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
507*4882a593Smuzhiyun #endif /* WL_CFG80211 */
508*4882a593Smuzhiyun #ifdef WL_ESCAN
509*4882a593Smuzhiyun 	struct wl_escan_info *escan = dhd->escan;
510*4882a593Smuzhiyun #endif /* WL_ESCAN */
511*4882a593Smuzhiyun 	bool complete = TRUE;
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun #ifdef WL_CFG80211
514*4882a593Smuzhiyun 	if (wl_get_drv_status_all(cfg, SCANNING)) {
515*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "SCANNING\n");
516*4882a593Smuzhiyun 		complete = FALSE;
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun 	if (wl_get_drv_status_all(cfg, CONNECTING)) {
519*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "CFG80211 CONNECTING\n");
520*4882a593Smuzhiyun 		complete = FALSE;
521*4882a593Smuzhiyun 	}
522*4882a593Smuzhiyun 	if (wl_get_drv_status_all(cfg, DISCONNECTING)) {
523*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "DISCONNECTING\n");
524*4882a593Smuzhiyun 		complete = FALSE;
525*4882a593Smuzhiyun 	}
526*4882a593Smuzhiyun #endif /* WL_CFG80211 */
527*4882a593Smuzhiyun #ifdef WL_ESCAN
528*4882a593Smuzhiyun 	if (escan->escan_state == ESCAN_STATE_SCANING) {
529*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "ESCAN_STATE_SCANING\n");
530*4882a593Smuzhiyun 		complete = FALSE;
531*4882a593Smuzhiyun 	}
532*4882a593Smuzhiyun #endif /* WL_ESCAN */
533*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
534*4882a593Smuzhiyun 	if (wl_ext_sta_connecting(dev)) {
535*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "CONNECTING\n");
536*4882a593Smuzhiyun 		complete = FALSE;
537*4882a593Smuzhiyun 	}
538*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	return complete;
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun void
wl_ext_wait_event_complete(struct dhd_pub * dhd,int ifidx)544*4882a593Smuzhiyun wl_ext_wait_event_complete(struct dhd_pub *dhd, int ifidx)
545*4882a593Smuzhiyun {
546*4882a593Smuzhiyun 	struct net_device *net;
547*4882a593Smuzhiyun 	s32 timeout = -1;
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	timeout = wait_event_interruptible_timeout(dhd->conf->event_complete,
550*4882a593Smuzhiyun 		wl_ext_event_complete(dhd, ifidx), msecs_to_jiffies(10000));
551*4882a593Smuzhiyun 	if (timeout <= 0 || !wl_ext_event_complete(dhd, ifidx)) {
552*4882a593Smuzhiyun 		wl_ext_event_complete(dhd, ifidx);
553*4882a593Smuzhiyun 		net = dhd_idx2net(dhd, ifidx);
554*4882a593Smuzhiyun 		AEXT_ERROR(net->name, "timeout\n");
555*4882a593Smuzhiyun 	}
556*4882a593Smuzhiyun }
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun void
wl_ext_bss_iovar_war(struct net_device * ndev,s32 * val)559*4882a593Smuzhiyun wl_ext_bss_iovar_war(struct net_device *ndev, s32 *val)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun 	dhd_pub_t *dhd = dhd_get_pub(ndev);
562*4882a593Smuzhiyun 	uint chip;
563*4882a593Smuzhiyun 	bool need_war = false;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	chip = dhd_conf_get_chip(dhd);
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	if (chip == BCM43362_CHIP_ID || chip == BCM4330_CHIP_ID ||
568*4882a593Smuzhiyun 		chip == BCM4354_CHIP_ID || chip == BCM4356_CHIP_ID ||
569*4882a593Smuzhiyun 		chip == BCM4371_CHIP_ID ||
570*4882a593Smuzhiyun 		chip == BCM43430_CHIP_ID ||
571*4882a593Smuzhiyun 		chip == BCM4345_CHIP_ID || chip == BCM43454_CHIP_ID ||
572*4882a593Smuzhiyun 		chip == BCM4359_CHIP_ID ||
573*4882a593Smuzhiyun 		chip == BCM43143_CHIP_ID || chip == BCM43242_CHIP_ID ||
574*4882a593Smuzhiyun 		chip == BCM43569_CHIP_ID) {
575*4882a593Smuzhiyun 		need_war = true;
576*4882a593Smuzhiyun 	}
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	if (need_war) {
579*4882a593Smuzhiyun 		/* Few firmware branches have issues in bss iovar handling and
580*4882a593Smuzhiyun 		 * that can't be changed since they are in production.
581*4882a593Smuzhiyun 		 */
582*4882a593Smuzhiyun 		if (*val == WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE) {
583*4882a593Smuzhiyun 			*val = WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE;
584*4882a593Smuzhiyun 		} else if (*val == WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE) {
585*4882a593Smuzhiyun 			*val = WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE;
586*4882a593Smuzhiyun 		} else {
587*4882a593Smuzhiyun 			/* Ignore for other bss enums */
588*4882a593Smuzhiyun 			return;
589*4882a593Smuzhiyun 		}
590*4882a593Smuzhiyun 		AEXT_TRACE(ndev->name, "wl bss %d\n", *val);
591*4882a593Smuzhiyun 	}
592*4882a593Smuzhiyun }
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun int
wl_ext_set_chanspec(struct net_device * dev,struct wl_chan_info * chan_info,chanspec_t * ret_chspec)595*4882a593Smuzhiyun wl_ext_set_chanspec(struct net_device *dev, struct wl_chan_info *chan_info,
596*4882a593Smuzhiyun 	chanspec_t *ret_chspec)
597*4882a593Smuzhiyun {
598*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
599*4882a593Smuzhiyun 	s32 _chan = chan_info->chan;
600*4882a593Smuzhiyun 	chanspec_t chspec = 0;
601*4882a593Smuzhiyun 	chanspec_t fw_chspec = 0;
602*4882a593Smuzhiyun 	u32 bw = WL_CHANSPEC_BW_20;
603*4882a593Smuzhiyun 	s32 err = BCME_OK;
604*4882a593Smuzhiyun 	s32 bw_cap = 0;
605*4882a593Smuzhiyun 	s8 iovar_buf[WLC_IOCTL_SMLEN];
606*4882a593Smuzhiyun 	struct {
607*4882a593Smuzhiyun 		u32 band;
608*4882a593Smuzhiyun 		u32 bw_cap;
609*4882a593Smuzhiyun 	} param = {0, 0};
610*4882a593Smuzhiyun 	chanspec_band_t chanspec_band = 0;
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	if ((chan_info->band != WLC_BAND_2G) && (chan_info->band != WLC_BAND_5G) &&
613*4882a593Smuzhiyun 			(chan_info->band != WLC_BAND_6G)) {
614*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "bad band %d\n", chan_info->band);
615*4882a593Smuzhiyun 		return BCME_BADBAND;
616*4882a593Smuzhiyun 	}
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 	param.band = chan_info->band;
619*4882a593Smuzhiyun 	err = wldev_iovar_getbuf(dev, "bw_cap", &param, sizeof(param),
620*4882a593Smuzhiyun 		iovar_buf, WLC_IOCTL_SMLEN, NULL);
621*4882a593Smuzhiyun 	if (err) {
622*4882a593Smuzhiyun 		if (err != BCME_UNSUPPORTED) {
623*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "bw_cap failed, %d\n", err);
624*4882a593Smuzhiyun 			return err;
625*4882a593Smuzhiyun 		} else {
626*4882a593Smuzhiyun 			err = wl_ext_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
627*4882a593Smuzhiyun 			if (bw_cap != WLC_N_BW_20ALL)
628*4882a593Smuzhiyun 				bw = WL_CHANSPEC_BW_40;
629*4882a593Smuzhiyun 		}
630*4882a593Smuzhiyun 	} else {
631*4882a593Smuzhiyun 		if (WL_BW_CAP_80MHZ(iovar_buf[0]))
632*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_80;
633*4882a593Smuzhiyun 		else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
634*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_40;
635*4882a593Smuzhiyun 		else
636*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_20;
637*4882a593Smuzhiyun 	}
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun set_channel:
640*4882a593Smuzhiyun 	chanspec_band = wl_ext_wlcband_to_chanspec_band(chan_info->band);
641*4882a593Smuzhiyun 	chspec = wf_create_chspec_from_primary(chan_info->chan, bw, chanspec_band);
642*4882a593Smuzhiyun 	if (wf_chspec_valid(chspec)) {
643*4882a593Smuzhiyun 		fw_chspec = wl_ext_chspec_host_to_driver(dhd, chspec);
644*4882a593Smuzhiyun 		if (fw_chspec != INVCHANSPEC) {
645*4882a593Smuzhiyun 			if ((err = wl_ext_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) {
646*4882a593Smuzhiyun 				if (bw == WL_CHANSPEC_BW_80)
647*4882a593Smuzhiyun 					goto change_bw;
648*4882a593Smuzhiyun 				err = wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);
649*4882a593Smuzhiyun 				WL_MSG(dev->name, "channel %s-%d\n", CHSPEC2BANDSTR(chspec), _chan);
650*4882a593Smuzhiyun 			} else if (err) {
651*4882a593Smuzhiyun 				AEXT_ERROR(dev->name, "failed to set chanspec error %d\n", err);
652*4882a593Smuzhiyun 			} else
653*4882a593Smuzhiyun 				WL_MSG(dev->name, "channel %s-%d(0x%x %sMHz)\n",
654*4882a593Smuzhiyun 					CHSPEC2BANDSTR(chspec), chan_info->chan, chspec,
655*4882a593Smuzhiyun 					CHSPEC_IS20(chspec)?"20":
656*4882a593Smuzhiyun 					CHSPEC_IS40(chspec)?"40":
657*4882a593Smuzhiyun 					CHSPEC_IS80(chspec)?"80":"160");
658*4882a593Smuzhiyun 		} else {
659*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "failed to convert host chanspec to fw chanspec\n");
660*4882a593Smuzhiyun 			err = BCME_ERROR;
661*4882a593Smuzhiyun 		}
662*4882a593Smuzhiyun 	} else {
663*4882a593Smuzhiyun change_bw:
664*4882a593Smuzhiyun 		if (bw == WL_CHANSPEC_BW_80)
665*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_40;
666*4882a593Smuzhiyun 		else if (bw == WL_CHANSPEC_BW_40)
667*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_20;
668*4882a593Smuzhiyun 		else
669*4882a593Smuzhiyun 			bw = 0;
670*4882a593Smuzhiyun 		if (bw)
671*4882a593Smuzhiyun 			goto set_channel;
672*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "Invalid chanspec 0x%x\n", chspec);
673*4882a593Smuzhiyun 		err = BCME_ERROR;
674*4882a593Smuzhiyun 	}
675*4882a593Smuzhiyun 	*ret_chspec = fw_chspec;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	return err;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun static int
wl_ext_channel(struct net_device * dev,char * command,int total_len)681*4882a593Smuzhiyun wl_ext_channel(struct net_device *dev, char* command, int total_len)
682*4882a593Smuzhiyun {
683*4882a593Smuzhiyun 	struct wl_chan_info chan_info;
684*4882a593Smuzhiyun 	int ret;
685*4882a593Smuzhiyun 	char band[16]="";
686*4882a593Smuzhiyun 	int channel = 0;
687*4882a593Smuzhiyun 	channel_info_t ci;
688*4882a593Smuzhiyun 	int bytes_written = 0;
689*4882a593Smuzhiyun 	chanspec_t fw_chspec;
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "cmd %s", command);
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	sscanf(command, "%*s %d %s", &channel, band);
694*4882a593Smuzhiyun 	if (strnicmp(band, "band=auto", strlen("band=auto")) == 0) {
695*4882a593Smuzhiyun 		chan_info.band = WLC_BAND_AUTO;
696*4882a593Smuzhiyun 	}
697*4882a593Smuzhiyun #ifdef WL_6G_BAND
698*4882a593Smuzhiyun 	else if (strnicmp(band, "band=6g", strlen("band=6g")) == 0) {
699*4882a593Smuzhiyun 		chan_info.band = WLC_BAND_6G;
700*4882a593Smuzhiyun 	}
701*4882a593Smuzhiyun #endif /* WL_6G_BAND */
702*4882a593Smuzhiyun 	else if (strnicmp(band, "band=5g", strlen("band=5g")) == 0) {
703*4882a593Smuzhiyun 		chan_info.band = WLC_BAND_5G;
704*4882a593Smuzhiyun 	}
705*4882a593Smuzhiyun 	else if (strnicmp(band, "band=2g", strlen("band=2g")) == 0) {
706*4882a593Smuzhiyun 		chan_info.band = WLC_BAND_2G;
707*4882a593Smuzhiyun 	}
708*4882a593Smuzhiyun 	else if (channel <= CH_MAX_2G_CHANNEL)
709*4882a593Smuzhiyun 		chan_info.band = WLC_BAND_2G;
710*4882a593Smuzhiyun 	else
711*4882a593Smuzhiyun 		chan_info.band = WLC_BAND_5G;
712*4882a593Smuzhiyun 
713*4882a593Smuzhiyun 	if (channel > 0) {
714*4882a593Smuzhiyun 		chan_info.chan = channel;
715*4882a593Smuzhiyun 		ret = wl_ext_set_chanspec(dev, &chan_info, &fw_chspec);
716*4882a593Smuzhiyun 	} else {
717*4882a593Smuzhiyun 		if (!(ret = wl_ext_ioctl(dev, WLC_GET_CHANNEL, &ci,
718*4882a593Smuzhiyun 				sizeof(channel_info_t), FALSE))) {
719*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "hw_channel %d\n", ci.hw_channel);
720*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "target_channel %d\n", ci.target_channel);
721*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "scan_channel %d\n", ci.scan_channel);
722*4882a593Smuzhiyun 			bytes_written = snprintf(command, sizeof(channel_info_t)+2,
723*4882a593Smuzhiyun 				"channel %d", ci.hw_channel);
724*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "command result is %s\n", command);
725*4882a593Smuzhiyun 			ret = bytes_written;
726*4882a593Smuzhiyun 		}
727*4882a593Smuzhiyun 	}
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	return ret;
730*4882a593Smuzhiyun }
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun static int
wl_ext_channels(struct net_device * dev,char * command,int total_len)733*4882a593Smuzhiyun wl_ext_channels(struct net_device *dev, char* command, int total_len)
734*4882a593Smuzhiyun {
735*4882a593Smuzhiyun 	int ret, i;
736*4882a593Smuzhiyun 	int bytes_written = -1;
737*4882a593Smuzhiyun 	u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
738*4882a593Smuzhiyun 	wl_uint32_list_t *list;
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "cmd %s", command);
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun 	memset(valid_chan_list, 0, sizeof(valid_chan_list));
743*4882a593Smuzhiyun 	list = (wl_uint32_list_t *)(void *) valid_chan_list;
744*4882a593Smuzhiyun 	list->count = htod32(WL_NUMCHANNELS);
745*4882a593Smuzhiyun 	ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
746*4882a593Smuzhiyun 		sizeof(valid_chan_list), 0);
747*4882a593Smuzhiyun 	if (ret<0) {
748*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "get channels failed with %d\n", ret);
749*4882a593Smuzhiyun 	} else {
750*4882a593Smuzhiyun 		bytes_written = snprintf(command, total_len, "channels");
751*4882a593Smuzhiyun 		for (i = 0; i < dtoh32(list->count); i++) {
752*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len, " %d",
753*4882a593Smuzhiyun 				dtoh32(list->element[i]));
754*4882a593Smuzhiyun 		}
755*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "command result is %s\n", command);
756*4882a593Smuzhiyun 		ret = bytes_written;
757*4882a593Smuzhiyun 	}
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	return ret;
760*4882a593Smuzhiyun }
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun static int
wl_ext_roam_trigger(struct net_device * dev,char * command,int total_len)763*4882a593Smuzhiyun wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len)
764*4882a593Smuzhiyun {
765*4882a593Smuzhiyun 	int ret = 0;
766*4882a593Smuzhiyun 	int roam_trigger[2] = {0, 0};
767*4882a593Smuzhiyun 	int trigger[2]= {0, 0};
768*4882a593Smuzhiyun 	int bytes_written=-1;
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 	sscanf(command, "%*s %10d", &roam_trigger[0]);
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun 	if (roam_trigger[0]) {
773*4882a593Smuzhiyun 		roam_trigger[1] = WLC_BAND_ALL;
774*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger,
775*4882a593Smuzhiyun 			sizeof(roam_trigger), 1);
776*4882a593Smuzhiyun 	} else {
777*4882a593Smuzhiyun 		roam_trigger[1] = WLC_BAND_2G;
778*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger,
779*4882a593Smuzhiyun 			sizeof(roam_trigger), 0);
780*4882a593Smuzhiyun 		if (!ret)
781*4882a593Smuzhiyun 			trigger[0] = roam_trigger[0];
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 		roam_trigger[1] = WLC_BAND_5G;
784*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_GET_ROAM_TRIGGER, &roam_trigger,
785*4882a593Smuzhiyun 			sizeof(roam_trigger), 0);
786*4882a593Smuzhiyun 		if (!ret)
787*4882a593Smuzhiyun 			trigger[1] = roam_trigger[0];
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "roam_trigger %d %d\n", trigger[0], trigger[1]);
790*4882a593Smuzhiyun 		bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]);
791*4882a593Smuzhiyun 		ret = bytes_written;
792*4882a593Smuzhiyun 	}
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	return ret;
795*4882a593Smuzhiyun }
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun static int
wl_ext_pm(struct net_device * dev,char * command,int total_len)798*4882a593Smuzhiyun wl_ext_pm(struct net_device *dev, char *command, int total_len)
799*4882a593Smuzhiyun {
800*4882a593Smuzhiyun 	int pm=-1, ret = -1;
801*4882a593Smuzhiyun 	char *pm_local;
802*4882a593Smuzhiyun 	int bytes_written=-1;
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "cmd %s", command);
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 	sscanf(command, "%*s %d", &pm);
807*4882a593Smuzhiyun 
808*4882a593Smuzhiyun 	if (pm >= 0) {
809*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), 1);
810*4882a593Smuzhiyun 	} else {
811*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), 0);
812*4882a593Smuzhiyun 		if (!ret) {
813*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "PM = %d", pm);
814*4882a593Smuzhiyun 			if (pm == PM_OFF)
815*4882a593Smuzhiyun 				pm_local = "PM_OFF";
816*4882a593Smuzhiyun 			else if(pm == PM_MAX)
817*4882a593Smuzhiyun 				pm_local = "PM_MAX";
818*4882a593Smuzhiyun 			else if(pm == PM_FAST)
819*4882a593Smuzhiyun 				pm_local = "PM_FAST";
820*4882a593Smuzhiyun 			else {
821*4882a593Smuzhiyun 				pm = 0;
822*4882a593Smuzhiyun 				pm_local = "Invalid";
823*4882a593Smuzhiyun 			}
824*4882a593Smuzhiyun 			bytes_written = snprintf(command, total_len, "PM %s", pm_local);
825*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "command result is %s\n", command);
826*4882a593Smuzhiyun 			ret = bytes_written;
827*4882a593Smuzhiyun 		}
828*4882a593Smuzhiyun 	}
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	return ret;
831*4882a593Smuzhiyun }
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun static int
wl_ext_monitor(struct net_device * dev,char * command,int total_len)834*4882a593Smuzhiyun wl_ext_monitor(struct net_device *dev, char *command, int total_len)
835*4882a593Smuzhiyun {
836*4882a593Smuzhiyun 	int val = -1, ret = -1;
837*4882a593Smuzhiyun 	int bytes_written=-1;
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun 	sscanf(command, "%*s %d", &val);
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun 	if (val >=0) {
842*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(val), 1);
843*4882a593Smuzhiyun 	} else {
844*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), 0);
845*4882a593Smuzhiyun 		if (!ret) {
846*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "monitor = %d\n", val);
847*4882a593Smuzhiyun 			bytes_written = snprintf(command, total_len, "monitor %d", val);
848*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "command result is %s\n", command);
849*4882a593Smuzhiyun 			ret = bytes_written;
850*4882a593Smuzhiyun 		}
851*4882a593Smuzhiyun 	}
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 	return ret;
854*4882a593Smuzhiyun }
855*4882a593Smuzhiyun 
856*4882a593Smuzhiyun #ifdef BTC_WAR
857*4882a593Smuzhiyun extern int btc_war;
858*4882a593Smuzhiyun static int
wl_ext_btc_war(struct net_device * dev,char * command,int total_len)859*4882a593Smuzhiyun wl_ext_btc_war(struct net_device *dev, char *command, int total_len)
860*4882a593Smuzhiyun {
861*4882a593Smuzhiyun 	int user_btc_war = 0;
862*4882a593Smuzhiyun 	bool enable = FALSE;
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun 	sscanf(command, "%*s %d", &user_btc_war);
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "btc_war=%d, user_btc_war=%d\n",
867*4882a593Smuzhiyun 		btc_war, user_btc_war);
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 	if (btc_war >= 0) {
870*4882a593Smuzhiyun 		btc_war = user_btc_war;
871*4882a593Smuzhiyun 		if (btc_war > 0)
872*4882a593Smuzhiyun 			enable = TRUE;
873*4882a593Smuzhiyun 		wl_ext_btc_config(dev, enable);
874*4882a593Smuzhiyun 	}
875*4882a593Smuzhiyun 
876*4882a593Smuzhiyun 	return 0;
877*4882a593Smuzhiyun }
878*4882a593Smuzhiyun #endif /* BTC_WAR */
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun s32
wl_ext_connect(struct net_device * dev,struct wl_conn_info * conn_info)881*4882a593Smuzhiyun wl_ext_connect(struct net_device *dev, struct wl_conn_info *conn_info)
882*4882a593Smuzhiyun {
883*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
884*4882a593Smuzhiyun 	wl_extjoin_params_t *ext_join_params = NULL;
885*4882a593Smuzhiyun 	struct wl_join_params join_params;
886*4882a593Smuzhiyun 	size_t join_params_size;
887*4882a593Smuzhiyun 	s32 err = 0;
888*4882a593Smuzhiyun 	u32 chan_cnt = 0;
889*4882a593Smuzhiyun 	s8 *iovar_buf = NULL;
890*4882a593Smuzhiyun 	char sec[64];
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun 	if (dhd->conf->chip == BCM43362_CHIP_ID)
893*4882a593Smuzhiyun 		goto set_ssid;
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	if (conn_info->channel) {
896*4882a593Smuzhiyun 		chan_cnt = 1;
897*4882a593Smuzhiyun 	}
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	iovar_buf = kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
900*4882a593Smuzhiyun 	if (iovar_buf == NULL) {
901*4882a593Smuzhiyun 		err = -ENOMEM;
902*4882a593Smuzhiyun 		goto exit;
903*4882a593Smuzhiyun 	}
904*4882a593Smuzhiyun 
905*4882a593Smuzhiyun 	/*
906*4882a593Smuzhiyun 	 *	Join with specific BSSID and cached SSID
907*4882a593Smuzhiyun 	 *	If SSID is zero join based on BSSID only
908*4882a593Smuzhiyun 	 */
909*4882a593Smuzhiyun 	join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE +
910*4882a593Smuzhiyun 		chan_cnt * sizeof(chanspec_t);
911*4882a593Smuzhiyun 	ext_join_params =  (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL);
912*4882a593Smuzhiyun 	if (ext_join_params == NULL) {
913*4882a593Smuzhiyun 		err = -ENOMEM;
914*4882a593Smuzhiyun 		goto exit;
915*4882a593Smuzhiyun 	}
916*4882a593Smuzhiyun 	ext_join_params->ssid.SSID_len = min((uint32)sizeof(ext_join_params->ssid.SSID),
917*4882a593Smuzhiyun 		conn_info->ssid.SSID_len);
918*4882a593Smuzhiyun 	memcpy(&ext_join_params->ssid.SSID, conn_info->ssid.SSID, ext_join_params->ssid.SSID_len);
919*4882a593Smuzhiyun 	ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len);
920*4882a593Smuzhiyun 	/* increate dwell time to receive probe response or detect Beacon
921*4882a593Smuzhiyun 	* from target AP at a noisy air only during connect command
922*4882a593Smuzhiyun 	*/
923*4882a593Smuzhiyun 	ext_join_params->scan.active_time = chan_cnt ? WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS : -1;
924*4882a593Smuzhiyun 	ext_join_params->scan.passive_time = chan_cnt ? WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS : -1;
925*4882a593Smuzhiyun 	/* Set up join scan parameters */
926*4882a593Smuzhiyun 	ext_join_params->scan.scan_type = -1;
927*4882a593Smuzhiyun 	ext_join_params->scan.nprobes = chan_cnt ?
928*4882a593Smuzhiyun 		(ext_join_params->scan.active_time/WL_SCAN_JOIN_PROBE_INTERVAL_MS) : -1;
929*4882a593Smuzhiyun 	ext_join_params->scan.home_time = -1;
930*4882a593Smuzhiyun 
931*4882a593Smuzhiyun 	if (memcmp(&ether_null, &conn_info->bssid, ETHER_ADDR_LEN))
932*4882a593Smuzhiyun 		memcpy(&ext_join_params->assoc.bssid, &conn_info->bssid, ETH_ALEN);
933*4882a593Smuzhiyun 	else
934*4882a593Smuzhiyun 		memcpy(&ext_join_params->assoc.bssid, &ether_bcast, ETH_ALEN);
935*4882a593Smuzhiyun 	ext_join_params->assoc.chanspec_num = chan_cnt;
936*4882a593Smuzhiyun 	if (chan_cnt) {
937*4882a593Smuzhiyun 		u16 band, bw, ctl_sb;
938*4882a593Smuzhiyun 		chanspec_t chspec;
939*4882a593Smuzhiyun 		band = (conn_info->channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G
940*4882a593Smuzhiyun 			: WL_CHANSPEC_BAND_5G;
941*4882a593Smuzhiyun 		bw = WL_CHANSPEC_BW_20;
942*4882a593Smuzhiyun 		ctl_sb = WL_CHANSPEC_CTL_SB_NONE;
943*4882a593Smuzhiyun 		chspec = (conn_info->channel | band | bw | ctl_sb);
944*4882a593Smuzhiyun 		ext_join_params->assoc.chanspec_list[0]  &= WL_CHANSPEC_CHAN_MASK;
945*4882a593Smuzhiyun 		ext_join_params->assoc.chanspec_list[0] |= chspec;
946*4882a593Smuzhiyun 		ext_join_params->assoc.chanspec_list[0] =
947*4882a593Smuzhiyun 			wl_ext_chspec_host_to_driver(dhd,
948*4882a593Smuzhiyun 				ext_join_params->assoc.chanspec_list[0]);
949*4882a593Smuzhiyun 	}
950*4882a593Smuzhiyun 	ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num);
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 	wl_ext_get_sec(dev, 0, sec, sizeof(sec), TRUE);
953*4882a593Smuzhiyun 	WL_MSG(dev->name,
954*4882a593Smuzhiyun 		"Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
955*4882a593Smuzhiyun 		&ext_join_params->assoc.bssid, conn_info->channel,
956*4882a593Smuzhiyun 		ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len, sec);
957*4882a593Smuzhiyun 	err = wl_ext_iovar_setbuf_bsscfg(dev, "join", ext_join_params,
958*4882a593Smuzhiyun 		join_params_size, iovar_buf, WLC_IOCTL_MAXLEN, conn_info->bssidx, NULL);
959*4882a593Smuzhiyun 
960*4882a593Smuzhiyun 	if (err) {
961*4882a593Smuzhiyun 		if (err == BCME_UNSUPPORTED) {
962*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "join iovar is not supported\n");
963*4882a593Smuzhiyun 			goto set_ssid;
964*4882a593Smuzhiyun 		} else {
965*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "error (%d)\n", err);
966*4882a593Smuzhiyun 			goto exit;
967*4882a593Smuzhiyun 		}
968*4882a593Smuzhiyun 	} else
969*4882a593Smuzhiyun 		goto exit;
970*4882a593Smuzhiyun 
971*4882a593Smuzhiyun set_ssid:
972*4882a593Smuzhiyun 	memset(&join_params, 0, sizeof(join_params));
973*4882a593Smuzhiyun 	join_params_size = sizeof(join_params.ssid);
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun 	join_params.ssid.SSID_len = min((uint32)sizeof(join_params.ssid.SSID),
976*4882a593Smuzhiyun 		conn_info->ssid.SSID_len);
977*4882a593Smuzhiyun 	memcpy(&join_params.ssid.SSID, conn_info->ssid.SSID, join_params.ssid.SSID_len);
978*4882a593Smuzhiyun 	join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len);
979*4882a593Smuzhiyun 	if (memcmp(&ether_null, &conn_info->bssid, ETHER_ADDR_LEN))
980*4882a593Smuzhiyun 		memcpy(&join_params.params.bssid, &conn_info->bssid, ETH_ALEN);
981*4882a593Smuzhiyun 	else
982*4882a593Smuzhiyun 		memcpy(&join_params.params.bssid, &ether_bcast, ETH_ALEN);
983*4882a593Smuzhiyun 
984*4882a593Smuzhiyun 	wl_ext_ch_to_chanspec(dhd, conn_info->channel, &join_params, &join_params_size);
985*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "join_param_size %zu\n", join_params_size);
986*4882a593Smuzhiyun 
987*4882a593Smuzhiyun 	if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
988*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "ssid \"%s\", len (%d)\n", join_params.ssid.SSID,
989*4882a593Smuzhiyun 			join_params.ssid.SSID_len);
990*4882a593Smuzhiyun 	}
991*4882a593Smuzhiyun 	wl_ext_get_sec(dev, 0, sec, sizeof(sec), TRUE);
992*4882a593Smuzhiyun 	WL_MSG(dev->name,
993*4882a593Smuzhiyun 		"Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
994*4882a593Smuzhiyun 		&join_params.params.bssid, conn_info->channel,
995*4882a593Smuzhiyun 		join_params.ssid.SSID, join_params.ssid.SSID_len, sec);
996*4882a593Smuzhiyun 	err = wl_ext_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, 1);
997*4882a593Smuzhiyun 
998*4882a593Smuzhiyun exit:
999*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
1000*4882a593Smuzhiyun 	if (!err)
1001*4882a593Smuzhiyun 		wl_ext_add_remove_pm_enable_work(dev, TRUE);
1002*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
1003*4882a593Smuzhiyun 	if (iovar_buf)
1004*4882a593Smuzhiyun 		kfree(iovar_buf);
1005*4882a593Smuzhiyun 	if (ext_join_params)
1006*4882a593Smuzhiyun 		kfree(ext_join_params);
1007*4882a593Smuzhiyun 	return err;
1008*4882a593Smuzhiyun 
1009*4882a593Smuzhiyun }
1010*4882a593Smuzhiyun 
1011*4882a593Smuzhiyun void
wl_ext_get_sec(struct net_device * dev,int ifmode,char * sec,int total_len,bool dump)1012*4882a593Smuzhiyun wl_ext_get_sec(struct net_device *dev, int ifmode, char *sec, int total_len, bool dump)
1013*4882a593Smuzhiyun {
1014*4882a593Smuzhiyun 	int auth=0, wpa_auth=0, wsec=0, mfp=0, i;
1015*4882a593Smuzhiyun 	int bytes_written=0;
1016*4882a593Smuzhiyun 	bool match = FALSE;
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	memset(sec, 0, total_len);
1019*4882a593Smuzhiyun 	wl_ext_iovar_getint(dev, "auth", &auth);
1020*4882a593Smuzhiyun 	wl_ext_iovar_getint(dev, "wpa_auth", &wpa_auth);
1021*4882a593Smuzhiyun 	wl_ext_iovar_getint(dev, "wsec", &wsec);
1022*4882a593Smuzhiyun 	wldev_iovar_getint(dev, "mfp", &mfp);
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
1025*4882a593Smuzhiyun 	if (ifmode == IMESH_MODE) {
1026*4882a593Smuzhiyun 		if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_DISABLED) {
1027*4882a593Smuzhiyun 			bytes_written += snprintf(sec+bytes_written, total_len, "open");
1028*4882a593Smuzhiyun 		} else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA2_AUTH_PSK) {
1029*4882a593Smuzhiyun 			bytes_written += snprintf(sec+bytes_written, total_len, "sae");
1030*4882a593Smuzhiyun 		} else {
1031*4882a593Smuzhiyun 			bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
1032*4882a593Smuzhiyun 				auth, wpa_auth);
1033*4882a593Smuzhiyun 		}
1034*4882a593Smuzhiyun 	} else
1035*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
1036*4882a593Smuzhiyun 	{
1037*4882a593Smuzhiyun 		match = FALSE;
1038*4882a593Smuzhiyun 		for (i=0; i<sizeof(auth_name_map)/sizeof(auth_name_map[0]); i++) {
1039*4882a593Smuzhiyun 			const auth_name_map_t* row = &auth_name_map[i];
1040*4882a593Smuzhiyun 			if (row->auth == auth && row->wpa_auth == wpa_auth) {
1041*4882a593Smuzhiyun 				bytes_written += snprintf(sec+bytes_written, total_len, "%s",
1042*4882a593Smuzhiyun 					row->auth_name);
1043*4882a593Smuzhiyun 				match = TRUE;
1044*4882a593Smuzhiyun 				break;
1045*4882a593Smuzhiyun 			}
1046*4882a593Smuzhiyun 		}
1047*4882a593Smuzhiyun 		if (!match) {
1048*4882a593Smuzhiyun 			bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
1049*4882a593Smuzhiyun 				auth, wpa_auth);
1050*4882a593Smuzhiyun 		}
1051*4882a593Smuzhiyun 	}
1052*4882a593Smuzhiyun 
1053*4882a593Smuzhiyun 	if (mfp == WL_MFP_NONE) {
1054*4882a593Smuzhiyun 		bytes_written += snprintf(sec+bytes_written, total_len, "/mfpn");
1055*4882a593Smuzhiyun 	} else if (mfp == WL_MFP_CAPABLE) {
1056*4882a593Smuzhiyun 		bytes_written += snprintf(sec+bytes_written, total_len, "/mfpc");
1057*4882a593Smuzhiyun 	} else if (mfp == WL_MFP_REQUIRED) {
1058*4882a593Smuzhiyun 		bytes_written += snprintf(sec+bytes_written, total_len, "/mfpr");
1059*4882a593Smuzhiyun 	} else {
1060*4882a593Smuzhiyun 		bytes_written += snprintf(sec+bytes_written, total_len, "/%d", mfp);
1061*4882a593Smuzhiyun 	}
1062*4882a593Smuzhiyun 
1063*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
1064*4882a593Smuzhiyun 	if (ifmode == IMESH_MODE) {
1065*4882a593Smuzhiyun 		if (wsec == WSEC_NONE) {
1066*4882a593Smuzhiyun 			bytes_written += snprintf(sec+bytes_written, total_len, "/none");
1067*4882a593Smuzhiyun 		} else {
1068*4882a593Smuzhiyun 			bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
1069*4882a593Smuzhiyun 		}
1070*4882a593Smuzhiyun 	} else
1071*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
1072*4882a593Smuzhiyun 	{
1073*4882a593Smuzhiyun 		match = FALSE;
1074*4882a593Smuzhiyun 		for (i=0; i<sizeof(wsec_name_map)/sizeof(wsec_name_map[0]); i++) {
1075*4882a593Smuzhiyun 			const wsec_name_map_t* row = &wsec_name_map[i];
1076*4882a593Smuzhiyun 			if (row->wsec == (wsec&0x7)) {
1077*4882a593Smuzhiyun 				bytes_written += snprintf(sec+bytes_written, total_len, "/%s",
1078*4882a593Smuzhiyun 					row->wsec_name);
1079*4882a593Smuzhiyun 				match = TRUE;
1080*4882a593Smuzhiyun 				break;
1081*4882a593Smuzhiyun 			}
1082*4882a593Smuzhiyun 		}
1083*4882a593Smuzhiyun 		if (!match) {
1084*4882a593Smuzhiyun 			bytes_written += snprintf(sec+bytes_written, total_len, "/0x%x", wsec);
1085*4882a593Smuzhiyun 		}
1086*4882a593Smuzhiyun 	}
1087*4882a593Smuzhiyun 	if (dump) {
1088*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "auth/wpa_auth/mfp/wsec = %d/0x%x/%d/0x%x\n",
1089*4882a593Smuzhiyun 			auth, wpa_auth, mfp, wsec);
1090*4882a593Smuzhiyun 	}
1091*4882a593Smuzhiyun }
1092*4882a593Smuzhiyun 
1093*4882a593Smuzhiyun bool
wl_ext_dfs_chan(struct wl_chan_info * chan_info)1094*4882a593Smuzhiyun wl_ext_dfs_chan(struct wl_chan_info *chan_info)
1095*4882a593Smuzhiyun {
1096*4882a593Smuzhiyun 	if (chan_info->band == WLC_BAND_5G && chan_info->chan >= 52 && chan_info->chan <= 144)
1097*4882a593Smuzhiyun 		return TRUE;
1098*4882a593Smuzhiyun 	return FALSE;
1099*4882a593Smuzhiyun }
1100*4882a593Smuzhiyun 
1101*4882a593Smuzhiyun bool
wl_ext_passive_chan(struct net_device * dev,struct wl_chan_info * chan_info)1102*4882a593Smuzhiyun wl_ext_passive_chan(struct net_device *dev, struct wl_chan_info *chan_info)
1103*4882a593Smuzhiyun {
1104*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
1105*4882a593Smuzhiyun 	u32 chanspec;
1106*4882a593Smuzhiyun 	s32 ret = BCME_OK;
1107*4882a593Smuzhiyun 
1108*4882a593Smuzhiyun 	chanspec = wf_create_chspec_from_primary(chan_info->chan,
1109*4882a593Smuzhiyun 		WL_CHANSPEC_BW_20, wl_ext_wlcband_to_chanspec_band(chan_info->band));
1110*4882a593Smuzhiyun 
1111*4882a593Smuzhiyun 	chanspec = wl_ext_chspec_host_to_driver(dhd, chanspec);
1112*4882a593Smuzhiyun 
1113*4882a593Smuzhiyun 	ret = wldev_iovar_getint(dev, "per_chan_info", &chanspec);
1114*4882a593Smuzhiyun 	if (!ret) {
1115*4882a593Smuzhiyun 		if (chanspec & WL_CHAN_PASSIVE)
1116*4882a593Smuzhiyun 			return TRUE;
1117*4882a593Smuzhiyun 	} else {
1118*4882a593Smuzhiyun 		if (chan_info->band == WLC_BAND_5G && chan_info->chan >= 52 && chan_info->chan <= 144)
1119*4882a593Smuzhiyun 			return TRUE;
1120*4882a593Smuzhiyun 	}
1121*4882a593Smuzhiyun 
1122*4882a593Smuzhiyun 	return FALSE;
1123*4882a593Smuzhiyun }
1124*4882a593Smuzhiyun 
1125*4882a593Smuzhiyun uint16
wl_ext_get_default_chan(struct net_device * dev,uint16 * chan_2g,uint16 * chan_5g,bool nodfs)1126*4882a593Smuzhiyun wl_ext_get_default_chan(struct net_device *dev,
1127*4882a593Smuzhiyun 	uint16 *chan_2g, uint16 *chan_5g, bool nodfs)
1128*4882a593Smuzhiyun {
1129*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
1130*4882a593Smuzhiyun 	struct wl_chan_info chan_info;
1131*4882a593Smuzhiyun 	uint16 chan_tmp = 0, chan = 0;
1132*4882a593Smuzhiyun 	wl_uint32_list_t *list;
1133*4882a593Smuzhiyun 	u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
1134*4882a593Smuzhiyun 	s32 ret = BCME_OK;
1135*4882a593Smuzhiyun 	int i;
1136*4882a593Smuzhiyun 
1137*4882a593Smuzhiyun 	*chan_2g = 0;
1138*4882a593Smuzhiyun 	*chan_5g = 0;
1139*4882a593Smuzhiyun 	memset(valid_chan_list, 0, sizeof(valid_chan_list));
1140*4882a593Smuzhiyun 	list = (wl_uint32_list_t *)(void *) valid_chan_list;
1141*4882a593Smuzhiyun 	list->count = htod32(WL_NUMCHANNELS);
1142*4882a593Smuzhiyun 	ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
1143*4882a593Smuzhiyun 		sizeof(valid_chan_list), 0);
1144*4882a593Smuzhiyun 	if (ret == 0) {
1145*4882a593Smuzhiyun 		for (i=0; i<dtoh32(list->count); i++) {
1146*4882a593Smuzhiyun 			chan_tmp = dtoh32(list->element[i]);
1147*4882a593Smuzhiyun 			if (!dhd_conf_match_channel(dhd, chan_tmp))
1148*4882a593Smuzhiyun 				continue;
1149*4882a593Smuzhiyun 			if (chan_tmp <= 13 && !*chan_2g) {
1150*4882a593Smuzhiyun 				*chan_2g = chan_tmp;
1151*4882a593Smuzhiyun 			} else if (chan_tmp >= 36 && chan_tmp <= 161 && !*chan_5g) {
1152*4882a593Smuzhiyun 				chan_info.band = WLC_BAND_5G;
1153*4882a593Smuzhiyun 				chan_info.chan = chan_tmp;
1154*4882a593Smuzhiyun 				if (wl_ext_dfs_chan(&chan_info) && nodfs)
1155*4882a593Smuzhiyun 					continue;
1156*4882a593Smuzhiyun 				else if (wl_ext_passive_chan(dev, &chan_info))
1157*4882a593Smuzhiyun 					continue;
1158*4882a593Smuzhiyun 				else
1159*4882a593Smuzhiyun 					*chan_5g = chan_tmp;
1160*4882a593Smuzhiyun 			}
1161*4882a593Smuzhiyun 		}
1162*4882a593Smuzhiyun 	}
1163*4882a593Smuzhiyun 
1164*4882a593Smuzhiyun 	return chan;
1165*4882a593Smuzhiyun }
1166*4882a593Smuzhiyun 
1167*4882a593Smuzhiyun int
wl_ext_set_scan_time(struct net_device * dev,int scan_time,uint32 scan_get,uint32 scan_set)1168*4882a593Smuzhiyun wl_ext_set_scan_time(struct net_device *dev, int scan_time,
1169*4882a593Smuzhiyun 	uint32 scan_get, uint32 scan_set)
1170*4882a593Smuzhiyun {
1171*4882a593Smuzhiyun 	int ret, cur_scan_time;
1172*4882a593Smuzhiyun 
1173*4882a593Smuzhiyun 	ret = wl_ext_ioctl(dev, scan_get, &cur_scan_time, sizeof(cur_scan_time), 0);
1174*4882a593Smuzhiyun 	if (ret)
1175*4882a593Smuzhiyun 		return 0;
1176*4882a593Smuzhiyun 
1177*4882a593Smuzhiyun 	if (scan_time != cur_scan_time)
1178*4882a593Smuzhiyun 		wl_ext_ioctl(dev, scan_set, &scan_time, sizeof(scan_time), 1);
1179*4882a593Smuzhiyun 
1180*4882a593Smuzhiyun 	return cur_scan_time;
1181*4882a593Smuzhiyun }
1182*4882a593Smuzhiyun 
1183*4882a593Smuzhiyun static int
wl_ext_wlmsglevel(struct net_device * dev,char * command,int total_len)1184*4882a593Smuzhiyun wl_ext_wlmsglevel(struct net_device *dev, char *command, int total_len)
1185*4882a593Smuzhiyun {
1186*4882a593Smuzhiyun 	int val = -1, ret = 0;
1187*4882a593Smuzhiyun 	int bytes_written = 0;
1188*4882a593Smuzhiyun 
1189*4882a593Smuzhiyun 	sscanf(command, "%*s %x", &val);
1190*4882a593Smuzhiyun 
1191*4882a593Smuzhiyun 	if (val >=0) {
1192*4882a593Smuzhiyun 		if (val & DHD_ANDROID_VAL) {
1193*4882a593Smuzhiyun 			android_msg_level = (uint)(val & 0xFFFF);
1194*4882a593Smuzhiyun 			WL_MSG(dev->name, "android_msg_level=0x%x\n", android_msg_level);
1195*4882a593Smuzhiyun 		}
1196*4882a593Smuzhiyun #if defined(WL_WIRELESS_EXT)
1197*4882a593Smuzhiyun 		else if (val & DHD_IW_VAL) {
1198*4882a593Smuzhiyun 			iw_msg_level = (uint)(val & 0xFFFF);
1199*4882a593Smuzhiyun 			WL_MSG(dev->name, "iw_msg_level=0x%x\n", iw_msg_level);
1200*4882a593Smuzhiyun 		}
1201*4882a593Smuzhiyun #endif
1202*4882a593Smuzhiyun #ifdef WL_CFG80211
1203*4882a593Smuzhiyun 		else if (val & DHD_CFG_VAL) {
1204*4882a593Smuzhiyun 			wl_cfg80211_enable_trace((u32)(val & 0xFFFF));
1205*4882a593Smuzhiyun 		}
1206*4882a593Smuzhiyun #endif
1207*4882a593Smuzhiyun 		else if (val & DHD_CONFIG_VAL) {
1208*4882a593Smuzhiyun 			config_msg_level = (uint)(val & 0xFFFF);
1209*4882a593Smuzhiyun 			WL_MSG(dev->name, "config_msg_level=0x%x\n", config_msg_level);
1210*4882a593Smuzhiyun 		}
1211*4882a593Smuzhiyun 		else if (val & DHD_DUMP_VAL) {
1212*4882a593Smuzhiyun 			dump_msg_level = (uint)(val & 0xFFFF);
1213*4882a593Smuzhiyun 			WL_MSG(dev->name, "dump_msg_level=0x%x\n", dump_msg_level);
1214*4882a593Smuzhiyun 		}
1215*4882a593Smuzhiyun 	}
1216*4882a593Smuzhiyun 	else {
1217*4882a593Smuzhiyun 		bytes_written += snprintf(command+bytes_written, total_len,
1218*4882a593Smuzhiyun 			"android_msg_level=0x%x", android_msg_level);
1219*4882a593Smuzhiyun #if defined(WL_WIRELESS_EXT)
1220*4882a593Smuzhiyun 		bytes_written += snprintf(command+bytes_written, total_len,
1221*4882a593Smuzhiyun 			"\niw_msg_level=0x%x", iw_msg_level);
1222*4882a593Smuzhiyun #endif
1223*4882a593Smuzhiyun #ifdef WL_CFG80211
1224*4882a593Smuzhiyun 		bytes_written += snprintf(command+bytes_written, total_len,
1225*4882a593Smuzhiyun 			"\nwl_dbg_level=0x%x", wl_dbg_level);
1226*4882a593Smuzhiyun #endif
1227*4882a593Smuzhiyun 		bytes_written += snprintf(command+bytes_written, total_len,
1228*4882a593Smuzhiyun 			"\nconfig_msg_level=0x%x", config_msg_level);
1229*4882a593Smuzhiyun 		bytes_written += snprintf(command+bytes_written, total_len,
1230*4882a593Smuzhiyun 			"\ndump_msg_level=0x%x", dump_msg_level);
1231*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "%s\n", command);
1232*4882a593Smuzhiyun 		ret = bytes_written;
1233*4882a593Smuzhiyun 	}
1234*4882a593Smuzhiyun 
1235*4882a593Smuzhiyun 	return ret;
1236*4882a593Smuzhiyun }
1237*4882a593Smuzhiyun 
1238*4882a593Smuzhiyun #ifdef WLEASYMESH
1239*4882a593Smuzhiyun #define CMD_EASYMESH "EASYMESH"
1240*4882a593Smuzhiyun //Set map 4 and dwds 1 on wlan0 interface
1241*4882a593Smuzhiyun #define EASYMESH_SLAVE		"slave"
1242*4882a593Smuzhiyun #define EASYMESH_MASTER		"master"
1243*4882a593Smuzhiyun 
1244*4882a593Smuzhiyun static int
wl_ext_easymesh(struct net_device * dev,char * command,int total_len)1245*4882a593Smuzhiyun wl_ext_easymesh(struct net_device *dev, char* command, int total_len)
1246*4882a593Smuzhiyun {
1247*4882a593Smuzhiyun 	int ret = 0, wlc_down = 1, wlc_up = 1, map = 4, dwds = 1;
1248*4882a593Smuzhiyun 
1249*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
1250*4882a593Smuzhiyun 	if (strncmp(command, EASYMESH_SLAVE, strlen(EASYMESH_SLAVE)) == 0) {
1251*4882a593Smuzhiyun 		WL_MSG(dev->name, "try to set map %d, dwds %d\n", map, dwds);
1252*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_DOWN, &wlc_down, sizeof(wlc_down), 1);
1253*4882a593Smuzhiyun 		if (ret)
1254*4882a593Smuzhiyun 			goto exit;
1255*4882a593Smuzhiyun 		wl_ext_iovar_setint(dev, "map", map);
1256*4882a593Smuzhiyun 		wl_ext_iovar_setint(dev, "dwds", dwds);
1257*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_UP, &wlc_up, sizeof(wlc_up), 1);
1258*4882a593Smuzhiyun 		if (ret)
1259*4882a593Smuzhiyun 			goto exit;
1260*4882a593Smuzhiyun 	}
1261*4882a593Smuzhiyun 	else if (strncmp(command, EASYMESH_MASTER, strlen(EASYMESH_MASTER)) == 0) {
1262*4882a593Smuzhiyun 		map = dwds = 0;
1263*4882a593Smuzhiyun 		WL_MSG(dev->name, "try to set map %d, dwds %d\n", map, dwds);
1264*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_DOWN, &wlc_down, sizeof(wlc_down), 1);
1265*4882a593Smuzhiyun 		if (ret) {
1266*4882a593Smuzhiyun 			goto exit;
1267*4882a593Smuzhiyun 		}
1268*4882a593Smuzhiyun 		wl_ext_iovar_setint(dev, "map", map);
1269*4882a593Smuzhiyun 		wl_ext_iovar_setint(dev, "dwds", dwds);
1270*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_UP, &wlc_up, sizeof(wlc_up), 1);
1271*4882a593Smuzhiyun 		if (ret) {
1272*4882a593Smuzhiyun 			goto exit;
1273*4882a593Smuzhiyun 		}
1274*4882a593Smuzhiyun 	}
1275*4882a593Smuzhiyun 
1276*4882a593Smuzhiyun exit:
1277*4882a593Smuzhiyun 	return ret;
1278*4882a593Smuzhiyun }
1279*4882a593Smuzhiyun #endif /* WLEASYMESH */
1280*4882a593Smuzhiyun 
1281*4882a593Smuzhiyun int
wl_ext_add_del_ie(struct net_device * dev,uint pktflag,char * ie_data,const char * add_del_cmd)1282*4882a593Smuzhiyun wl_ext_add_del_ie(struct net_device *dev, uint pktflag, char *ie_data, const char* add_del_cmd)
1283*4882a593Smuzhiyun {
1284*4882a593Smuzhiyun 	vndr_ie_setbuf_t *vndr_ie = NULL;
1285*4882a593Smuzhiyun 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
1286*4882a593Smuzhiyun 	int ie_data_len = 0, tot_len = 0, iecount;
1287*4882a593Smuzhiyun 	int err = -1;
1288*4882a593Smuzhiyun 
1289*4882a593Smuzhiyun 	if (!strlen(ie_data)) {
1290*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "wrong ie %s\n", ie_data);
1291*4882a593Smuzhiyun 		goto exit;
1292*4882a593Smuzhiyun 	}
1293*4882a593Smuzhiyun 
1294*4882a593Smuzhiyun 	tot_len = (int)(sizeof(vndr_ie_setbuf_t) + ((strlen(ie_data)-2)/2));
1295*4882a593Smuzhiyun 	vndr_ie = (vndr_ie_setbuf_t *) kzalloc(tot_len, GFP_KERNEL);
1296*4882a593Smuzhiyun 	if (!vndr_ie) {
1297*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "IE memory alloc failed\n");
1298*4882a593Smuzhiyun 		err = -ENOMEM;
1299*4882a593Smuzhiyun 		goto exit;
1300*4882a593Smuzhiyun 	}
1301*4882a593Smuzhiyun 
1302*4882a593Smuzhiyun 	/* Copy the vndr_ie SET command ("add"/"del") to the buffer */
1303*4882a593Smuzhiyun 	strncpy(vndr_ie->cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1);
1304*4882a593Smuzhiyun 	vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
1305*4882a593Smuzhiyun 
1306*4882a593Smuzhiyun 	/* Set the IE count - the buffer contains only 1 IE */
1307*4882a593Smuzhiyun 	iecount = htod32(1);
1308*4882a593Smuzhiyun 	memcpy((void *)&vndr_ie->vndr_ie_buffer.iecount, &iecount, sizeof(s32));
1309*4882a593Smuzhiyun 
1310*4882a593Smuzhiyun 	/* Set packet flag to indicate that BEACON's will contain this IE */
1311*4882a593Smuzhiyun 	pktflag = htod32(pktflag);
1312*4882a593Smuzhiyun 	memcpy((void *)&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
1313*4882a593Smuzhiyun 		sizeof(u32));
1314*4882a593Smuzhiyun 
1315*4882a593Smuzhiyun 	/* Set the IE ID */
1316*4882a593Smuzhiyun 	vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = (uchar)DOT11_MNG_VS_ID;
1317*4882a593Smuzhiyun 
1318*4882a593Smuzhiyun 	/* Set the IE LEN */
1319*4882a593Smuzhiyun 	vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = (strlen(ie_data)-2)/2;
1320*4882a593Smuzhiyun 
1321*4882a593Smuzhiyun 	/* Set the IE OUI and DATA */
1322*4882a593Smuzhiyun 	ie_data_len = wl_pattern_atoh(ie_data,
1323*4882a593Smuzhiyun 		(char *)vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui);
1324*4882a593Smuzhiyun 	if (ie_data_len <= 0) {
1325*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "wrong ie_data_len %d\n", (int)strlen(ie_data)-2);
1326*4882a593Smuzhiyun 		goto exit;
1327*4882a593Smuzhiyun 	}
1328*4882a593Smuzhiyun 
1329*4882a593Smuzhiyun 	err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, tot_len, iovar_buf,
1330*4882a593Smuzhiyun 		sizeof(iovar_buf), NULL);
1331*4882a593Smuzhiyun 
1332*4882a593Smuzhiyun exit:
1333*4882a593Smuzhiyun 	if (vndr_ie) {
1334*4882a593Smuzhiyun 		kfree(vndr_ie);
1335*4882a593Smuzhiyun 	}
1336*4882a593Smuzhiyun 	return err;
1337*4882a593Smuzhiyun }
1338*4882a593Smuzhiyun 
1339*4882a593Smuzhiyun #ifdef IDHCP
1340*4882a593Smuzhiyun /*
1341*4882a593Smuzhiyun terence 20190409:
1342*4882a593Smuzhiyun dhd_priv wl dhcpc_dump
1343*4882a593Smuzhiyun dhd_priv wl dhcpc_param <client ip> <server ip> <lease time>
1344*4882a593Smuzhiyun */
1345*4882a593Smuzhiyun static int
wl_ext_dhcpc_dump(struct net_device * dev,char * data,char * command,int total_len)1346*4882a593Smuzhiyun wl_ext_dhcpc_dump(struct net_device *dev, char *data, char *command,
1347*4882a593Smuzhiyun 	int total_len)
1348*4882a593Smuzhiyun {
1349*4882a593Smuzhiyun 	int ret = 0;
1350*4882a593Smuzhiyun 	int bytes_written = 0;
1351*4882a593Smuzhiyun 	uint32 ip_addr;
1352*4882a593Smuzhiyun 	char buf[20]="";
1353*4882a593Smuzhiyun 
1354*4882a593Smuzhiyun 	if (!data) {
1355*4882a593Smuzhiyun 		ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr);
1356*4882a593Smuzhiyun 		if (!ret) {
1357*4882a593Smuzhiyun 			bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
1358*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len,
1359*4882a593Smuzhiyun 				"ipaddr %s ", buf);
1360*4882a593Smuzhiyun 		}
1361*4882a593Smuzhiyun 
1362*4882a593Smuzhiyun 		ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr);
1363*4882a593Smuzhiyun 		if (!ret) {
1364*4882a593Smuzhiyun 			bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
1365*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len,
1366*4882a593Smuzhiyun 				"mask %s ", buf);
1367*4882a593Smuzhiyun 		}
1368*4882a593Smuzhiyun 
1369*4882a593Smuzhiyun 		ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr);
1370*4882a593Smuzhiyun 		if (!ret) {
1371*4882a593Smuzhiyun 			bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
1372*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len,
1373*4882a593Smuzhiyun 				"gw %s ", buf);
1374*4882a593Smuzhiyun 		}
1375*4882a593Smuzhiyun 
1376*4882a593Smuzhiyun 		ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr);
1377*4882a593Smuzhiyun 		if (!ret) {
1378*4882a593Smuzhiyun 			bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
1379*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len,
1380*4882a593Smuzhiyun 				"dnsserv %s ", buf);
1381*4882a593Smuzhiyun 		}
1382*4882a593Smuzhiyun 
1383*4882a593Smuzhiyun 		if (!bytes_written)
1384*4882a593Smuzhiyun 			bytes_written = -1;
1385*4882a593Smuzhiyun 
1386*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "command result is %s\n", command);
1387*4882a593Smuzhiyun 	}
1388*4882a593Smuzhiyun 
1389*4882a593Smuzhiyun 	return bytes_written;
1390*4882a593Smuzhiyun }
1391*4882a593Smuzhiyun 
1392*4882a593Smuzhiyun int
wl_ext_dhcpc_param(struct net_device * dev,char * data,char * command,int total_len)1393*4882a593Smuzhiyun wl_ext_dhcpc_param(struct net_device *dev, char *data, char *command,
1394*4882a593Smuzhiyun 	int total_len)
1395*4882a593Smuzhiyun {
1396*4882a593Smuzhiyun 	int ret = -1, bytes_written = 0;
1397*4882a593Smuzhiyun 	char ip_addr_str[20]="", ip_serv_str[20]="";
1398*4882a593Smuzhiyun 	struct dhcpc_parameter dhcpc_param;
1399*4882a593Smuzhiyun 	uint32 ip_addr, ip_serv, lease_time;
1400*4882a593Smuzhiyun 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
1401*4882a593Smuzhiyun 
1402*4882a593Smuzhiyun 	if (data) {
1403*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "cmd %s", command);
1404*4882a593Smuzhiyun 		sscanf(data, "%s %s %d", ip_addr_str, ip_serv_str, &lease_time);
1405*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "ip_addr = %s, ip_serv = %s, lease_time = %d",
1406*4882a593Smuzhiyun 			ip_addr_str, ip_serv_str, lease_time);
1407*4882a593Smuzhiyun 
1408*4882a593Smuzhiyun 		memset(&dhcpc_param, 0, sizeof(struct dhcpc_parameter));
1409*4882a593Smuzhiyun 		if (!bcm_atoipv4(ip_addr_str, (struct ipv4_addr *)&ip_addr)) {
1410*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "wrong ip_addr_str %s\n", ip_addr_str);
1411*4882a593Smuzhiyun 			ret = -1;
1412*4882a593Smuzhiyun 			goto exit;
1413*4882a593Smuzhiyun 		}
1414*4882a593Smuzhiyun 		dhcpc_param.ip_addr = ip_addr;
1415*4882a593Smuzhiyun 
1416*4882a593Smuzhiyun 		if (!bcm_atoipv4(ip_addr_str, (struct ipv4_addr *)&ip_serv)) {
1417*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "wrong ip_addr_str %s\n", ip_addr_str);
1418*4882a593Smuzhiyun 			ret = -1;
1419*4882a593Smuzhiyun 			goto exit;
1420*4882a593Smuzhiyun 		}
1421*4882a593Smuzhiyun 		dhcpc_param.ip_serv = ip_serv;
1422*4882a593Smuzhiyun 		dhcpc_param.lease_time = lease_time;
1423*4882a593Smuzhiyun 		ret = wl_ext_iovar_setbuf(dev, "dhcpc_param", &dhcpc_param,
1424*4882a593Smuzhiyun 			sizeof(struct dhcpc_parameter), iovar_buf, sizeof(iovar_buf), NULL);
1425*4882a593Smuzhiyun 	} else {
1426*4882a593Smuzhiyun 		ret = wl_ext_iovar_getbuf(dev, "dhcpc_param", &dhcpc_param,
1427*4882a593Smuzhiyun 			sizeof(struct dhcpc_parameter), iovar_buf, WLC_IOCTL_SMLEN, NULL);
1428*4882a593Smuzhiyun 		if (!ret) {
1429*4882a593Smuzhiyun 			bcm_ip_ntoa((struct ipv4_addr *)&dhcpc_param.ip_addr, ip_addr_str);
1430*4882a593Smuzhiyun 			bytes_written += snprintf(command + bytes_written, total_len,
1431*4882a593Smuzhiyun 				"ip_addr %s\n", ip_addr_str);
1432*4882a593Smuzhiyun 			bcm_ip_ntoa((struct ipv4_addr *)&dhcpc_param.ip_serv, ip_serv_str);
1433*4882a593Smuzhiyun 			bytes_written += snprintf(command + bytes_written, total_len,
1434*4882a593Smuzhiyun 				"ip_serv %s\n", ip_serv_str);
1435*4882a593Smuzhiyun 			bytes_written += snprintf(command + bytes_written, total_len,
1436*4882a593Smuzhiyun 				"lease_time %d\n", dhcpc_param.lease_time);
1437*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "command result is %s\n", command);
1438*4882a593Smuzhiyun 			ret = bytes_written;
1439*4882a593Smuzhiyun 		}
1440*4882a593Smuzhiyun 	}
1441*4882a593Smuzhiyun 
1442*4882a593Smuzhiyun 	exit:
1443*4882a593Smuzhiyun 		return ret;
1444*4882a593Smuzhiyun }
1445*4882a593Smuzhiyun #endif /* IDHCP */
1446*4882a593Smuzhiyun 
1447*4882a593Smuzhiyun int
wl_ext_mkeep_alive(struct net_device * dev,char * data,char * command,int total_len)1448*4882a593Smuzhiyun wl_ext_mkeep_alive(struct net_device *dev, char *data, char *command,
1449*4882a593Smuzhiyun 	int total_len)
1450*4882a593Smuzhiyun {
1451*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
1452*4882a593Smuzhiyun 	wl_mkeep_alive_pkt_v1_t *mkeep_alive_pktp;
1453*4882a593Smuzhiyun 	int ret = -1, i, ifidx, id, period=-1;
1454*4882a593Smuzhiyun 	char *packet = NULL, *buf = NULL;
1455*4882a593Smuzhiyun 	int bytes_written = 0;
1456*4882a593Smuzhiyun 
1457*4882a593Smuzhiyun 	if (data) {
1458*4882a593Smuzhiyun 		buf = kmalloc(total_len, GFP_KERNEL);
1459*4882a593Smuzhiyun 		if (buf == NULL) {
1460*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
1461*4882a593Smuzhiyun 			goto exit;
1462*4882a593Smuzhiyun 		}
1463*4882a593Smuzhiyun 		packet = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
1464*4882a593Smuzhiyun 		if (packet == NULL) {
1465*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
1466*4882a593Smuzhiyun 			goto exit;
1467*4882a593Smuzhiyun 		}
1468*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "cmd %s", command);
1469*4882a593Smuzhiyun 		sscanf(data, "%d %d %s", &id, &period, packet);
1470*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "id=%d, period=%d, packet=%s", id, period, packet);
1471*4882a593Smuzhiyun 		if (period >= 0) {
1472*4882a593Smuzhiyun 			ifidx = dhd_net2idx(dhd->info, dev);
1473*4882a593Smuzhiyun 			ret = dhd_conf_mkeep_alive(dhd, ifidx, id, period, packet, FALSE);
1474*4882a593Smuzhiyun 		} else {
1475*4882a593Smuzhiyun 			if (id < 0)
1476*4882a593Smuzhiyun 				id = 0;
1477*4882a593Smuzhiyun 			ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf,
1478*4882a593Smuzhiyun 				total_len, NULL);
1479*4882a593Smuzhiyun 			if (!ret) {
1480*4882a593Smuzhiyun 				mkeep_alive_pktp = (wl_mkeep_alive_pkt_v1_t *) buf;
1481*4882a593Smuzhiyun 				bytes_written += snprintf(command+bytes_written, total_len,
1482*4882a593Smuzhiyun 					"Id            :%d\n"
1483*4882a593Smuzhiyun 					"Period (msec) :%d\n"
1484*4882a593Smuzhiyun 					"Length        :%d\n"
1485*4882a593Smuzhiyun 					"Packet        :0x",
1486*4882a593Smuzhiyun 					mkeep_alive_pktp->keep_alive_id,
1487*4882a593Smuzhiyun 					dtoh32(mkeep_alive_pktp->period_msec),
1488*4882a593Smuzhiyun 					dtoh16(mkeep_alive_pktp->len_bytes));
1489*4882a593Smuzhiyun 				for (i=0; i<mkeep_alive_pktp->len_bytes; i++) {
1490*4882a593Smuzhiyun 					bytes_written += snprintf(command+bytes_written, total_len,
1491*4882a593Smuzhiyun 						"%02x", mkeep_alive_pktp->data[i]);
1492*4882a593Smuzhiyun 				}
1493*4882a593Smuzhiyun 				AEXT_TRACE(dev->name, "command result is %s\n", command);
1494*4882a593Smuzhiyun 				ret = bytes_written;
1495*4882a593Smuzhiyun 			}
1496*4882a593Smuzhiyun 		}
1497*4882a593Smuzhiyun 	}
1498*4882a593Smuzhiyun 
1499*4882a593Smuzhiyun exit:
1500*4882a593Smuzhiyun 	if (buf)
1501*4882a593Smuzhiyun 		kfree(buf);
1502*4882a593Smuzhiyun 	if (packet)
1503*4882a593Smuzhiyun 		kfree(packet);
1504*4882a593Smuzhiyun 	return ret;
1505*4882a593Smuzhiyun }
1506*4882a593Smuzhiyun 
1507*4882a593Smuzhiyun #ifdef WL_EXT_TCPKA
1508*4882a593Smuzhiyun static int
wl_ext_tcpka_conn_add(struct net_device * dev,char * data,char * command,int total_len)1509*4882a593Smuzhiyun wl_ext_tcpka_conn_add(struct net_device *dev, char *data, char *command,
1510*4882a593Smuzhiyun 	int total_len)
1511*4882a593Smuzhiyun {
1512*4882a593Smuzhiyun 	int ret = 0;
1513*4882a593Smuzhiyun 	s8 iovar_buf[WLC_IOCTL_SMLEN];
1514*4882a593Smuzhiyun 	tcpka_conn_t *tcpka = NULL;
1515*4882a593Smuzhiyun 	uint32 sess_id = 0, ipid = 0, srcport = 0, dstport = 0, seq = 0, ack = 0,
1516*4882a593Smuzhiyun 		tcpwin = 0, tsval = 0, tsecr = 0, len = 0, ka_payload_len = 0;
1517*4882a593Smuzhiyun 	char dst_mac[ETHER_ADDR_STR_LEN], src_ip[IPV4_ADDR_STR_LEN],
1518*4882a593Smuzhiyun 		dst_ip[IPV4_ADDR_STR_LEN], ka_payload[32];
1519*4882a593Smuzhiyun 
1520*4882a593Smuzhiyun 	if (data) {
1521*4882a593Smuzhiyun 		memset(dst_mac, 0, sizeof(dst_mac));
1522*4882a593Smuzhiyun 		memset(src_ip, 0, sizeof(src_ip));
1523*4882a593Smuzhiyun 		memset(dst_ip, 0, sizeof(dst_ip));
1524*4882a593Smuzhiyun 		memset(ka_payload, 0, sizeof(ka_payload));
1525*4882a593Smuzhiyun 		sscanf(data, "%d %s %s %s %d %d %d %u %u %d %u %u %u %32s",
1526*4882a593Smuzhiyun 			&sess_id, dst_mac, src_ip, dst_ip, &ipid, &srcport, &dstport, &seq,
1527*4882a593Smuzhiyun 			&ack, &tcpwin, &tsval, &tsecr, &len, ka_payload);
1528*4882a593Smuzhiyun 
1529*4882a593Smuzhiyun 		ka_payload_len = strlen(ka_payload) / 2;
1530*4882a593Smuzhiyun 		tcpka = kmalloc(sizeof(struct tcpka_conn) + ka_payload_len, GFP_KERNEL);
1531*4882a593Smuzhiyun 		if (tcpka == NULL) {
1532*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
1533*4882a593Smuzhiyun 				sizeof(struct tcpka_conn) + ka_payload_len);
1534*4882a593Smuzhiyun 			ret = -1;
1535*4882a593Smuzhiyun 			goto exit;
1536*4882a593Smuzhiyun 		}
1537*4882a593Smuzhiyun 		memset(tcpka, 0, sizeof(struct tcpka_conn) + ka_payload_len);
1538*4882a593Smuzhiyun 
1539*4882a593Smuzhiyun 		tcpka->sess_id = sess_id;
1540*4882a593Smuzhiyun 		if (!(ret = bcm_ether_atoe(dst_mac, &tcpka->dst_mac))) {
1541*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "mac parsing err addr=%s\n", dst_mac);
1542*4882a593Smuzhiyun 			ret = -1;
1543*4882a593Smuzhiyun 			goto exit;
1544*4882a593Smuzhiyun 		}
1545*4882a593Smuzhiyun 		if (!bcm_atoipv4(src_ip, &tcpka->src_ip)) {
1546*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "src_ip parsing err ip=%s\n", src_ip);
1547*4882a593Smuzhiyun 			ret = -1;
1548*4882a593Smuzhiyun 			goto exit;
1549*4882a593Smuzhiyun 		}
1550*4882a593Smuzhiyun 		if (!bcm_atoipv4(dst_ip, &tcpka->dst_ip)) {
1551*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "dst_ip parsing err ip=%s\n", dst_ip);
1552*4882a593Smuzhiyun 			ret = -1;
1553*4882a593Smuzhiyun 			goto exit;
1554*4882a593Smuzhiyun 		}
1555*4882a593Smuzhiyun 		tcpka->ipid = ipid;
1556*4882a593Smuzhiyun 		tcpka->srcport = srcport;
1557*4882a593Smuzhiyun 		tcpka->dstport = dstport;
1558*4882a593Smuzhiyun 		tcpka->seq = seq;
1559*4882a593Smuzhiyun 		tcpka->ack = ack;
1560*4882a593Smuzhiyun 		tcpka->tcpwin = tcpwin;
1561*4882a593Smuzhiyun 		tcpka->tsval = tsval;
1562*4882a593Smuzhiyun 		tcpka->tsecr = tsecr;
1563*4882a593Smuzhiyun 		tcpka->len = len;
1564*4882a593Smuzhiyun 		ka_payload_len = wl_pattern_atoh(ka_payload, (char *)tcpka->ka_payload);
1565*4882a593Smuzhiyun 		if (ka_payload_len == -1) {
1566*4882a593Smuzhiyun 			AEXT_ERROR(dev->name,"rejecting ka_payload=%s\n", ka_payload);
1567*4882a593Smuzhiyun 			ret = -1;
1568*4882a593Smuzhiyun 			goto exit;
1569*4882a593Smuzhiyun 		}
1570*4882a593Smuzhiyun 		tcpka->ka_payload_len = ka_payload_len;
1571*4882a593Smuzhiyun 
1572*4882a593Smuzhiyun 		AEXT_INFO(dev->name,
1573*4882a593Smuzhiyun 			"tcpka_conn_add %d %pM %pM %pM %d %d %d %u %u %d %u %u %u %u \"%s\"\n",
1574*4882a593Smuzhiyun 			tcpka->sess_id, &tcpka->dst_mac, &tcpka->src_ip, &tcpka->dst_ip,
1575*4882a593Smuzhiyun 			tcpka->ipid, tcpka->srcport, tcpka->dstport, tcpka->seq,
1576*4882a593Smuzhiyun 			tcpka->ack, tcpka->tcpwin, tcpka->tsval, tcpka->tsecr,
1577*4882a593Smuzhiyun 			tcpka->len, tcpka->ka_payload_len, tcpka->ka_payload);
1578*4882a593Smuzhiyun 
1579*4882a593Smuzhiyun 		ret = wl_ext_iovar_setbuf(dev, "tcpka_conn_add", (char *)tcpka,
1580*4882a593Smuzhiyun 			(sizeof(tcpka_conn_t) + tcpka->ka_payload_len - 1),
1581*4882a593Smuzhiyun 			iovar_buf, sizeof(iovar_buf), NULL);
1582*4882a593Smuzhiyun 	}
1583*4882a593Smuzhiyun 
1584*4882a593Smuzhiyun exit:
1585*4882a593Smuzhiyun 	if (tcpka)
1586*4882a593Smuzhiyun 		kfree(tcpka);
1587*4882a593Smuzhiyun 	return ret;
1588*4882a593Smuzhiyun }
1589*4882a593Smuzhiyun 
1590*4882a593Smuzhiyun static int
wl_ext_tcpka_conn_enable(struct net_device * dev,char * data,char * command,int total_len)1591*4882a593Smuzhiyun wl_ext_tcpka_conn_enable(struct net_device *dev, char *data, char *command,
1592*4882a593Smuzhiyun 	int total_len)
1593*4882a593Smuzhiyun {
1594*4882a593Smuzhiyun 	s8 iovar_buf[WLC_IOCTL_SMLEN];
1595*4882a593Smuzhiyun 	tcpka_conn_sess_t tcpka_conn;
1596*4882a593Smuzhiyun 	int ret = 0;
1597*4882a593Smuzhiyun 	uint32 sess_id = 0, flag, interval = 0, retry_interval = 0, retry_count = 0;
1598*4882a593Smuzhiyun 
1599*4882a593Smuzhiyun 	if (data) {
1600*4882a593Smuzhiyun 		sscanf(data, "%d %d %d %d %d",
1601*4882a593Smuzhiyun 			&sess_id, &flag, &interval, &retry_interval, &retry_count);
1602*4882a593Smuzhiyun 		tcpka_conn.sess_id = sess_id;
1603*4882a593Smuzhiyun 		tcpka_conn.flag = flag;
1604*4882a593Smuzhiyun 		if (tcpka_conn.flag) {
1605*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.interval = interval;
1606*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.retry_interval = retry_interval;
1607*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.retry_count = retry_count;
1608*4882a593Smuzhiyun 		} else {
1609*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.interval = 0;
1610*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.retry_interval = 0;
1611*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.retry_count = 0;
1612*4882a593Smuzhiyun 		}
1613*4882a593Smuzhiyun 
1614*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "tcpka_conn_enable %d %d %d %d %d\n",
1615*4882a593Smuzhiyun 			tcpka_conn.sess_id, tcpka_conn.flag,
1616*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.interval,
1617*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.retry_interval,
1618*4882a593Smuzhiyun 			tcpka_conn.tcpka_timers.retry_count);
1619*4882a593Smuzhiyun 
1620*4882a593Smuzhiyun 		ret = wl_ext_iovar_setbuf(dev, "tcpka_conn_enable", (char *)&tcpka_conn,
1621*4882a593Smuzhiyun 			sizeof(tcpka_conn_sess_t), iovar_buf, sizeof(iovar_buf), NULL);
1622*4882a593Smuzhiyun 	}
1623*4882a593Smuzhiyun 
1624*4882a593Smuzhiyun 	return ret;
1625*4882a593Smuzhiyun }
1626*4882a593Smuzhiyun 
1627*4882a593Smuzhiyun static int
wl_ext_tcpka_conn_info(struct net_device * dev,char * data,char * command,int total_len)1628*4882a593Smuzhiyun wl_ext_tcpka_conn_info(struct net_device *dev, char *data, char *command,
1629*4882a593Smuzhiyun 	int total_len)
1630*4882a593Smuzhiyun {
1631*4882a593Smuzhiyun 	s8 iovar_buf[WLC_IOCTL_SMLEN];
1632*4882a593Smuzhiyun 	tcpka_conn_sess_info_t *info = NULL;
1633*4882a593Smuzhiyun 	uint32 sess_id = 0;
1634*4882a593Smuzhiyun 	int ret = 0, bytes_written = 0;
1635*4882a593Smuzhiyun 
1636*4882a593Smuzhiyun 	if (data) {
1637*4882a593Smuzhiyun 		sscanf(data, "%d", &sess_id);
1638*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "tcpka_conn_sess_info %d\n", sess_id);
1639*4882a593Smuzhiyun 		ret = wl_ext_iovar_getbuf(dev, "tcpka_conn_sess_info", (char *)&sess_id,
1640*4882a593Smuzhiyun 			sizeof(uint32), iovar_buf, sizeof(iovar_buf), NULL);
1641*4882a593Smuzhiyun 		if (!ret) {
1642*4882a593Smuzhiyun 			info = (tcpka_conn_sess_info_t *) iovar_buf;
1643*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len,
1644*4882a593Smuzhiyun 				"id   :%d\n"
1645*4882a593Smuzhiyun 				"ipid :%d\n"
1646*4882a593Smuzhiyun 				"seq  :%u\n"
1647*4882a593Smuzhiyun 				"ack  :%u",
1648*4882a593Smuzhiyun 				sess_id, info->ipid, info->seq, info->ack);
1649*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "%s\n", command);
1650*4882a593Smuzhiyun 			ret = bytes_written;
1651*4882a593Smuzhiyun 		}
1652*4882a593Smuzhiyun 	}
1653*4882a593Smuzhiyun 
1654*4882a593Smuzhiyun 	return ret;
1655*4882a593Smuzhiyun }
1656*4882a593Smuzhiyun #endif /* WL_EXT_TCPKA */
1657*4882a593Smuzhiyun 
1658*4882a593Smuzhiyun static int
wl_ext_rsdb_mode(struct net_device * dev,char * data,char * command,int total_len)1659*4882a593Smuzhiyun wl_ext_rsdb_mode(struct net_device *dev, char *data, char *command,
1660*4882a593Smuzhiyun 	int total_len)
1661*4882a593Smuzhiyun {
1662*4882a593Smuzhiyun 	s8 iovar_buf[WLC_IOCTL_SMLEN];
1663*4882a593Smuzhiyun 	wl_config_t rsdb_mode_cfg = {1, 0}, *rsdb_p;
1664*4882a593Smuzhiyun 	int ret = 0;
1665*4882a593Smuzhiyun 
1666*4882a593Smuzhiyun 	if (data) {
1667*4882a593Smuzhiyun 		rsdb_mode_cfg.config = (int)simple_strtol(data, NULL, 0);
1668*4882a593Smuzhiyun 		ret = wl_ext_iovar_setbuf(dev, "rsdb_mode", (char *)&rsdb_mode_cfg,
1669*4882a593Smuzhiyun 			sizeof(rsdb_mode_cfg), iovar_buf, WLC_IOCTL_SMLEN, NULL);
1670*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "rsdb_mode %d\n", rsdb_mode_cfg.config);
1671*4882a593Smuzhiyun 	} else {
1672*4882a593Smuzhiyun 		ret = wl_ext_iovar_getbuf(dev, "rsdb_mode", NULL, 0,
1673*4882a593Smuzhiyun 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
1674*4882a593Smuzhiyun 		if (!ret) {
1675*4882a593Smuzhiyun 			rsdb_p = (wl_config_t *) iovar_buf;
1676*4882a593Smuzhiyun 			ret = snprintf(command, total_len, "%d", rsdb_p->config);
1677*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "command result is %s\n", command);
1678*4882a593Smuzhiyun 		}
1679*4882a593Smuzhiyun 	}
1680*4882a593Smuzhiyun 
1681*4882a593Smuzhiyun 	return ret;
1682*4882a593Smuzhiyun }
1683*4882a593Smuzhiyun 
1684*4882a593Smuzhiyun static int
wl_ext_recal(struct net_device * dev,char * data,char * command,int total_len)1685*4882a593Smuzhiyun wl_ext_recal(struct net_device *dev, char *data, char *command,
1686*4882a593Smuzhiyun 	int total_len)
1687*4882a593Smuzhiyun {
1688*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
1689*4882a593Smuzhiyun 	int ret = 0, i, nchan, nssid = 0;
1690*4882a593Smuzhiyun 	int params_size = WL_SCAN_PARAMS_V1_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
1691*4882a593Smuzhiyun 	wl_scan_params_v1_t *params = NULL;
1692*4882a593Smuzhiyun 	char *p;
1693*4882a593Smuzhiyun 
1694*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "Enter\n");
1695*4882a593Smuzhiyun 
1696*4882a593Smuzhiyun 	if (data) {
1697*4882a593Smuzhiyun 		params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
1698*4882a593Smuzhiyun 		params = (wl_scan_params_v1_t *) kzalloc(params_size, GFP_KERNEL);
1699*4882a593Smuzhiyun 		if (params == NULL) {
1700*4882a593Smuzhiyun 			ret = -ENOMEM;
1701*4882a593Smuzhiyun 			goto exit;
1702*4882a593Smuzhiyun 		}
1703*4882a593Smuzhiyun 		memset(params, 0, params_size);
1704*4882a593Smuzhiyun 
1705*4882a593Smuzhiyun 		memcpy(&params->bssid, &ether_bcast, ETHER_ADDR_LEN);
1706*4882a593Smuzhiyun 		params->bss_type = DOT11_BSSTYPE_ANY;
1707*4882a593Smuzhiyun 		params->scan_type = 0;
1708*4882a593Smuzhiyun 		params->nprobes = -1;
1709*4882a593Smuzhiyun 		params->active_time = -1;
1710*4882a593Smuzhiyun 		params->passive_time = -1;
1711*4882a593Smuzhiyun 		params->home_time = -1;
1712*4882a593Smuzhiyun 		params->channel_num = 0;
1713*4882a593Smuzhiyun 
1714*4882a593Smuzhiyun 		params->scan_type |= WL_SCANFLAGS_PASSIVE;
1715*4882a593Smuzhiyun 		nchan = 2;
1716*4882a593Smuzhiyun 		params->channel_list[0] = wf_channel2chspec(1, WL_CHANSPEC_BW_20);
1717*4882a593Smuzhiyun 		params->channel_list[1] = wf_channel2chspec(2, WL_CHANSPEC_BW_20);
1718*4882a593Smuzhiyun 
1719*4882a593Smuzhiyun 		params->nprobes = htod32(params->nprobes);
1720*4882a593Smuzhiyun 		params->active_time = htod32(params->active_time);
1721*4882a593Smuzhiyun 		params->passive_time = htod32(params->passive_time);
1722*4882a593Smuzhiyun 		params->home_time = htod32(params->home_time);
1723*4882a593Smuzhiyun 
1724*4882a593Smuzhiyun 		for (i = 0; i < nchan; i++) {
1725*4882a593Smuzhiyun 			wl_ext_chspec_host_to_driver(dhd, params->channel_list[i]);
1726*4882a593Smuzhiyun 		}
1727*4882a593Smuzhiyun 
1728*4882a593Smuzhiyun 		p = (char*)params->channel_list + nchan * sizeof(uint16);
1729*4882a593Smuzhiyun 
1730*4882a593Smuzhiyun 		params->channel_num = htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) |
1731*4882a593Smuzhiyun 		                             (nchan & WL_SCAN_PARAMS_COUNT_MASK));
1732*4882a593Smuzhiyun 		params_size = p - (char*)params + nssid * sizeof(wlc_ssid_t);
1733*4882a593Smuzhiyun 
1734*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "recal\n");
1735*4882a593Smuzhiyun 		ret = wl_ext_ioctl(dev, WLC_SCAN, params, params_size, 1);
1736*4882a593Smuzhiyun 	}
1737*4882a593Smuzhiyun 
1738*4882a593Smuzhiyun exit:
1739*4882a593Smuzhiyun 	if (params)
1740*4882a593Smuzhiyun 		kfree(params);
1741*4882a593Smuzhiyun 	return ret;
1742*4882a593Smuzhiyun }
1743*4882a593Smuzhiyun 
1744*4882a593Smuzhiyun static s32
wl_ext_add_remove_eventmsg(struct net_device * ndev,u16 event,bool add)1745*4882a593Smuzhiyun wl_ext_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add)
1746*4882a593Smuzhiyun {
1747*4882a593Smuzhiyun 	s8 iovbuf[WL_EVENTING_MASK_LEN + 12];
1748*4882a593Smuzhiyun 	s8 eventmask[WL_EVENTING_MASK_LEN];
1749*4882a593Smuzhiyun 	s32 err = 0;
1750*4882a593Smuzhiyun 
1751*4882a593Smuzhiyun 	if (!ndev)
1752*4882a593Smuzhiyun 		return -ENODEV;
1753*4882a593Smuzhiyun 
1754*4882a593Smuzhiyun 	/* Setup event_msgs */
1755*4882a593Smuzhiyun 	err = wldev_iovar_getbuf(ndev, "event_msgs", NULL, 0, iovbuf, sizeof(iovbuf), NULL);
1756*4882a593Smuzhiyun 	if (unlikely(err)) {
1757*4882a593Smuzhiyun 		AEXT_ERROR(ndev->name, "Get event_msgs error (%d)\n", err);
1758*4882a593Smuzhiyun 		goto eventmsg_out;
1759*4882a593Smuzhiyun 	}
1760*4882a593Smuzhiyun 	memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN);
1761*4882a593Smuzhiyun 	if (add) {
1762*4882a593Smuzhiyun 		setbit(eventmask, event);
1763*4882a593Smuzhiyun 	} else {
1764*4882a593Smuzhiyun 		clrbit(eventmask, event);
1765*4882a593Smuzhiyun 	}
1766*4882a593Smuzhiyun 	err = wldev_iovar_setbuf(ndev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf,
1767*4882a593Smuzhiyun 			sizeof(iovbuf), NULL);
1768*4882a593Smuzhiyun 	if (unlikely(err)) {
1769*4882a593Smuzhiyun 		AEXT_ERROR(ndev->name, "Set event_msgs error (%d)\n", err);
1770*4882a593Smuzhiyun 		goto eventmsg_out;
1771*4882a593Smuzhiyun 	}
1772*4882a593Smuzhiyun 
1773*4882a593Smuzhiyun eventmsg_out:
1774*4882a593Smuzhiyun 	return err;
1775*4882a593Smuzhiyun }
1776*4882a593Smuzhiyun 
1777*4882a593Smuzhiyun static int
wl_ext_event_msg(struct net_device * dev,char * data,char * command,int total_len)1778*4882a593Smuzhiyun wl_ext_event_msg(struct net_device *dev, char *data,
1779*4882a593Smuzhiyun 	char *command, int total_len)
1780*4882a593Smuzhiyun {
1781*4882a593Smuzhiyun 	s8 iovbuf[WL_EVENTING_MASK_LEN + 12];
1782*4882a593Smuzhiyun 	s8 eventmask[WL_EVENTING_MASK_LEN];
1783*4882a593Smuzhiyun 	int i, bytes_written = 0, add = -1;
1784*4882a593Smuzhiyun 	uint event;
1785*4882a593Smuzhiyun 	char *vbuf;
1786*4882a593Smuzhiyun 	bool skipzeros;
1787*4882a593Smuzhiyun 
1788*4882a593Smuzhiyun 	/* dhd_priv wl event_msg [offset] [1/0, 1 for add, 0 for remove] */
1789*4882a593Smuzhiyun 	/* dhd_priv wl event_msg 40 1 */
1790*4882a593Smuzhiyun 	if (data) {
1791*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "data = %s\n", data);
1792*4882a593Smuzhiyun 		sscanf(data, "%d %d", &event, &add);
1793*4882a593Smuzhiyun 		/* Setup event_msgs */
1794*4882a593Smuzhiyun 		bytes_written = wldev_iovar_getbuf(dev, "event_msgs", NULL, 0, iovbuf,
1795*4882a593Smuzhiyun 			sizeof(iovbuf), NULL);
1796*4882a593Smuzhiyun 		if (unlikely(bytes_written)) {
1797*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "Get event_msgs error (%d)\n", bytes_written);
1798*4882a593Smuzhiyun 			goto eventmsg_out;
1799*4882a593Smuzhiyun 		}
1800*4882a593Smuzhiyun 		memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN);
1801*4882a593Smuzhiyun 		if (add == -1) {
1802*4882a593Smuzhiyun 			if (isset(eventmask, event))
1803*4882a593Smuzhiyun 				bytes_written += snprintf(command+bytes_written, total_len, "1");
1804*4882a593Smuzhiyun 			else
1805*4882a593Smuzhiyun 				bytes_written += snprintf(command+bytes_written, total_len, "0");
1806*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "%s\n", command);
1807*4882a593Smuzhiyun 			goto eventmsg_out;
1808*4882a593Smuzhiyun 		}
1809*4882a593Smuzhiyun 		bytes_written = wl_ext_add_remove_eventmsg(dev, event, add);
1810*4882a593Smuzhiyun 	}
1811*4882a593Smuzhiyun 	else {
1812*4882a593Smuzhiyun 		/* Setup event_msgs */
1813*4882a593Smuzhiyun 		bytes_written = wldev_iovar_getbuf(dev, "event_msgs", NULL, 0, iovbuf,
1814*4882a593Smuzhiyun 			sizeof(iovbuf), NULL);
1815*4882a593Smuzhiyun 		if (bytes_written) {
1816*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "Get event_msgs error (%d)\n", bytes_written);
1817*4882a593Smuzhiyun 			goto eventmsg_out;
1818*4882a593Smuzhiyun 		}
1819*4882a593Smuzhiyun 		vbuf = (char *)iovbuf;
1820*4882a593Smuzhiyun 		bytes_written += snprintf(command+bytes_written, total_len, "0x");
1821*4882a593Smuzhiyun 		for (i = (sizeof(eventmask) - 1); i >= 0; i--) {
1822*4882a593Smuzhiyun 			if (vbuf[i] || (i == 0))
1823*4882a593Smuzhiyun 				skipzeros = FALSE;
1824*4882a593Smuzhiyun 			if (skipzeros)
1825*4882a593Smuzhiyun 				continue;
1826*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len,
1827*4882a593Smuzhiyun 				"%02x", vbuf[i] & 0xff);
1828*4882a593Smuzhiyun 		}
1829*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "%s\n", command);
1830*4882a593Smuzhiyun 	}
1831*4882a593Smuzhiyun 
1832*4882a593Smuzhiyun eventmsg_out:
1833*4882a593Smuzhiyun 	return bytes_written;
1834*4882a593Smuzhiyun }
1835*4882a593Smuzhiyun 
1836*4882a593Smuzhiyun #ifdef PKT_FILTER_SUPPORT
1837*4882a593Smuzhiyun extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
1838*4882a593Smuzhiyun extern void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id);
1839*4882a593Smuzhiyun extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
1840*4882a593Smuzhiyun static int
wl_ext_pkt_filter_add(struct net_device * dev,char * data,char * command,int total_len)1841*4882a593Smuzhiyun wl_ext_pkt_filter_add(struct net_device *dev, char *data, char *command,
1842*4882a593Smuzhiyun 	int total_len)
1843*4882a593Smuzhiyun {
1844*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
1845*4882a593Smuzhiyun 	int i, filter_id, new_id = 0, cnt;
1846*4882a593Smuzhiyun 	conf_pkt_filter_add_t *filter_add = &dhd->conf->pkt_filter_add;
1847*4882a593Smuzhiyun 	char **pktfilter = dhd->pktfilter;
1848*4882a593Smuzhiyun 	int err = 0;
1849*4882a593Smuzhiyun 
1850*4882a593Smuzhiyun 	if (data) {
1851*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "data = %s\n", data);
1852*4882a593Smuzhiyun 
1853*4882a593Smuzhiyun 		new_id = simple_strtol(data, NULL, 10);
1854*4882a593Smuzhiyun 		if (new_id <= 0) {
1855*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "wrong id %d\n", new_id);
1856*4882a593Smuzhiyun 			return -1;
1857*4882a593Smuzhiyun 		}
1858*4882a593Smuzhiyun 
1859*4882a593Smuzhiyun 		cnt = dhd->pktfilter_count;
1860*4882a593Smuzhiyun 		for (i=0; i<cnt; i++) {
1861*4882a593Smuzhiyun 			if (!pktfilter[i])
1862*4882a593Smuzhiyun 				continue;
1863*4882a593Smuzhiyun 			filter_id = simple_strtol(pktfilter[i], NULL, 10);
1864*4882a593Smuzhiyun 			if (new_id == filter_id) {
1865*4882a593Smuzhiyun 				AEXT_ERROR(dev->name, "filter id %d already in list\n", filter_id);
1866*4882a593Smuzhiyun 				return -1;
1867*4882a593Smuzhiyun 			}
1868*4882a593Smuzhiyun 		}
1869*4882a593Smuzhiyun 
1870*4882a593Smuzhiyun 		cnt = filter_add->count;
1871*4882a593Smuzhiyun 		if (cnt >= DHD_CONF_FILTER_MAX) {
1872*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "not enough filter\n");
1873*4882a593Smuzhiyun 			return -1;
1874*4882a593Smuzhiyun 		}
1875*4882a593Smuzhiyun 		for (i=0; i<cnt; i++) {
1876*4882a593Smuzhiyun 			filter_id = simple_strtol(filter_add->filter[i], NULL, 10);
1877*4882a593Smuzhiyun 			if (new_id == filter_id) {
1878*4882a593Smuzhiyun 				AEXT_ERROR(dev->name, "filter id %d already in list\n", filter_id);
1879*4882a593Smuzhiyun 				return -1;
1880*4882a593Smuzhiyun 			}
1881*4882a593Smuzhiyun 		}
1882*4882a593Smuzhiyun 
1883*4882a593Smuzhiyun 		strcpy(&filter_add->filter[cnt][0], data);
1884*4882a593Smuzhiyun 		dhd->pktfilter[dhd->pktfilter_count] = filter_add->filter[cnt];
1885*4882a593Smuzhiyun 		filter_add->count++;
1886*4882a593Smuzhiyun 		dhd->pktfilter_count++;
1887*4882a593Smuzhiyun 
1888*4882a593Smuzhiyun 		dhd_pktfilter_offload_set(dhd, data);
1889*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "filter id %d added\n", new_id);
1890*4882a593Smuzhiyun 	}
1891*4882a593Smuzhiyun 
1892*4882a593Smuzhiyun 	return err;
1893*4882a593Smuzhiyun }
1894*4882a593Smuzhiyun 
1895*4882a593Smuzhiyun static int
wl_ext_pkt_filter_delete(struct net_device * dev,char * data,char * command,int total_len)1896*4882a593Smuzhiyun wl_ext_pkt_filter_delete(struct net_device *dev, char *data, char *command,
1897*4882a593Smuzhiyun 	int total_len)
1898*4882a593Smuzhiyun {
1899*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
1900*4882a593Smuzhiyun 	int i, j, filter_id, cnt;
1901*4882a593Smuzhiyun 	char **pktfilter = dhd->pktfilter;
1902*4882a593Smuzhiyun 	conf_pkt_filter_add_t *filter_add = &dhd->conf->pkt_filter_add;
1903*4882a593Smuzhiyun 	bool in_filter = FALSE;
1904*4882a593Smuzhiyun 	int id, err = 0;
1905*4882a593Smuzhiyun 
1906*4882a593Smuzhiyun 	if (data) {
1907*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "data = %s\n", data);
1908*4882a593Smuzhiyun 		id = (int)simple_strtol(data, NULL, 0);
1909*4882a593Smuzhiyun 
1910*4882a593Smuzhiyun 		cnt = filter_add->count;
1911*4882a593Smuzhiyun 		for (i=0; i<cnt; i++) {
1912*4882a593Smuzhiyun 			filter_id = simple_strtol(filter_add->filter[i], NULL, 10);
1913*4882a593Smuzhiyun 			if (id == filter_id) {
1914*4882a593Smuzhiyun 				in_filter = TRUE;
1915*4882a593Smuzhiyun 				memset(filter_add->filter[i], 0, PKT_FILTER_LEN);
1916*4882a593Smuzhiyun 				for (j=i; j<(cnt-1); j++) {
1917*4882a593Smuzhiyun 					strcpy(filter_add->filter[j], filter_add->filter[j+1]);
1918*4882a593Smuzhiyun 					memset(filter_add->filter[j+1], 0, PKT_FILTER_LEN);
1919*4882a593Smuzhiyun 				}
1920*4882a593Smuzhiyun 				cnt--;
1921*4882a593Smuzhiyun 				filter_add->count--;
1922*4882a593Smuzhiyun 				dhd->pktfilter_count--;
1923*4882a593Smuzhiyun 			}
1924*4882a593Smuzhiyun 		}
1925*4882a593Smuzhiyun 
1926*4882a593Smuzhiyun 		cnt = dhd->pktfilter_count;
1927*4882a593Smuzhiyun 		for (i=0; i<cnt; i++) {
1928*4882a593Smuzhiyun 			if (!pktfilter[i])
1929*4882a593Smuzhiyun 				continue;
1930*4882a593Smuzhiyun 			filter_id = simple_strtol(pktfilter[i], NULL, 10);
1931*4882a593Smuzhiyun 			if (id == filter_id) {
1932*4882a593Smuzhiyun 				in_filter = TRUE;
1933*4882a593Smuzhiyun 				memset(pktfilter[i], 0, strlen(pktfilter[i]));
1934*4882a593Smuzhiyun 			}
1935*4882a593Smuzhiyun 		}
1936*4882a593Smuzhiyun 
1937*4882a593Smuzhiyun 		if (in_filter) {
1938*4882a593Smuzhiyun 			dhd_pktfilter_offload_delete(dhd, id);
1939*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "filter id %d deleted\n", id);
1940*4882a593Smuzhiyun 		} else {
1941*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "filter id %d not in list\n", id);
1942*4882a593Smuzhiyun 			err = -1;
1943*4882a593Smuzhiyun 		}
1944*4882a593Smuzhiyun 	}
1945*4882a593Smuzhiyun 
1946*4882a593Smuzhiyun 	return err;
1947*4882a593Smuzhiyun }
1948*4882a593Smuzhiyun 
1949*4882a593Smuzhiyun static int
wl_ext_pkt_filter_enable(struct net_device * dev,char * data,char * command,int total_len)1950*4882a593Smuzhiyun wl_ext_pkt_filter_enable(struct net_device *dev, char *data, char *command,
1951*4882a593Smuzhiyun 	int total_len)
1952*4882a593Smuzhiyun {
1953*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
1954*4882a593Smuzhiyun 	int err = 0, id, enable;
1955*4882a593Smuzhiyun 	int i, filter_id, cnt;
1956*4882a593Smuzhiyun 	char **pktfilter = dhd->pktfilter;
1957*4882a593Smuzhiyun 	bool in_filter = FALSE;
1958*4882a593Smuzhiyun 
1959*4882a593Smuzhiyun 	/* dhd_priv wl pkt_filter_enable [id] [1/0] */
1960*4882a593Smuzhiyun 	/* dhd_priv wl pkt_filter_enable 141 1 */
1961*4882a593Smuzhiyun 	if (data) {
1962*4882a593Smuzhiyun 		sscanf(data, "%d %d", &id, &enable);
1963*4882a593Smuzhiyun 
1964*4882a593Smuzhiyun 		cnt = dhd->pktfilter_count;
1965*4882a593Smuzhiyun 		for (i=0; i<cnt; i++) {
1966*4882a593Smuzhiyun 			if (!pktfilter[i])
1967*4882a593Smuzhiyun 				continue;
1968*4882a593Smuzhiyun 			filter_id = simple_strtol(pktfilter[i], NULL, 10);
1969*4882a593Smuzhiyun 			if (id == filter_id) {
1970*4882a593Smuzhiyun 				in_filter = TRUE;
1971*4882a593Smuzhiyun 				break;
1972*4882a593Smuzhiyun 			}
1973*4882a593Smuzhiyun 		}
1974*4882a593Smuzhiyun 
1975*4882a593Smuzhiyun 		if (in_filter) {
1976*4882a593Smuzhiyun 			dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
1977*4882a593Smuzhiyun 				enable, dhd_master_mode);
1978*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "filter id %d %s\n", id, enable?"enabled":"disabled");
1979*4882a593Smuzhiyun 		} else {
1980*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "filter id %d not in list\n", id);
1981*4882a593Smuzhiyun 			err = -1;
1982*4882a593Smuzhiyun 		}
1983*4882a593Smuzhiyun 	}
1984*4882a593Smuzhiyun 
1985*4882a593Smuzhiyun 	return err;
1986*4882a593Smuzhiyun }
1987*4882a593Smuzhiyun #endif /* PKT_FILTER_SUPPORT */
1988*4882a593Smuzhiyun 
1989*4882a593Smuzhiyun #ifdef SENDPROB
1990*4882a593Smuzhiyun static int
wl_ext_send_probreq(struct net_device * dev,char * data,char * command,int total_len)1991*4882a593Smuzhiyun wl_ext_send_probreq(struct net_device *dev, char *data, char *command,
1992*4882a593Smuzhiyun 	int total_len)
1993*4882a593Smuzhiyun {
1994*4882a593Smuzhiyun 	int err = 0;
1995*4882a593Smuzhiyun 	char addr_str[16], addr[6];
1996*4882a593Smuzhiyun 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
1997*4882a593Smuzhiyun 	char ie_data[WLC_IOCTL_SMLEN] = "\0";
1998*4882a593Smuzhiyun 	wl_probe_params_t params;
1999*4882a593Smuzhiyun 
2000*4882a593Smuzhiyun 	/* dhd_priv wl send_probreq [dest. addr] [OUI+VAL] */
2001*4882a593Smuzhiyun 	/* dhd_priv wl send_probreq 0x00904c010203 0x00904c01020304050607 */
2002*4882a593Smuzhiyun 	if (data) {
2003*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "data = %s\n", data);
2004*4882a593Smuzhiyun 		sscanf(data, "%s %s", addr_str, ie_data);
2005*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "addr=%s, ie=%s\n", addr_str, ie_data);
2006*4882a593Smuzhiyun 
2007*4882a593Smuzhiyun 		if (strlen(addr_str) != 14) {
2008*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "wrong addr %s\n", addr_str);
2009*4882a593Smuzhiyun 			goto exit;
2010*4882a593Smuzhiyun 		}
2011*4882a593Smuzhiyun 		wl_pattern_atoh(addr_str, (char *) addr);
2012*4882a593Smuzhiyun 		memset(&params, 0, sizeof(params));
2013*4882a593Smuzhiyun 		memcpy(&params.bssid, addr, ETHER_ADDR_LEN);
2014*4882a593Smuzhiyun 		memcpy(&params.mac, addr, ETHER_ADDR_LEN);
2015*4882a593Smuzhiyun 
2016*4882a593Smuzhiyun 		err = wl_ext_add_del_ie(dev, VNDR_IE_PRBREQ_FLAG, ie_data, "add");
2017*4882a593Smuzhiyun 		if (err)
2018*4882a593Smuzhiyun 			goto exit;
2019*4882a593Smuzhiyun 		err = wl_ext_iovar_setbuf(dev, "sendprb", (char *)&params, sizeof(params),
2020*4882a593Smuzhiyun 			iovar_buf, sizeof(iovar_buf), NULL);
2021*4882a593Smuzhiyun 		OSL_SLEEP(100);
2022*4882a593Smuzhiyun 		wl_ext_add_del_ie(dev, VNDR_IE_PRBREQ_FLAG, ie_data, "del");
2023*4882a593Smuzhiyun 	}
2024*4882a593Smuzhiyun 
2025*4882a593Smuzhiyun exit:
2026*4882a593Smuzhiyun     return err;
2027*4882a593Smuzhiyun }
2028*4882a593Smuzhiyun 
2029*4882a593Smuzhiyun static int
wl_ext_send_probresp(struct net_device * dev,char * data,char * command,int total_len)2030*4882a593Smuzhiyun wl_ext_send_probresp(struct net_device *dev, char *data, char *command,
2031*4882a593Smuzhiyun 	int total_len)
2032*4882a593Smuzhiyun {
2033*4882a593Smuzhiyun 	int err = 0;
2034*4882a593Smuzhiyun 	char addr_str[16], addr[6];
2035*4882a593Smuzhiyun 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
2036*4882a593Smuzhiyun 	char ie_data[WLC_IOCTL_SMLEN] = "\0";
2037*4882a593Smuzhiyun 
2038*4882a593Smuzhiyun 	/* dhd_priv wl send_probresp [dest. addr] [OUI+VAL] */
2039*4882a593Smuzhiyun 	/* dhd_priv wl send_probresp 0x00904c010203 0x00904c01020304050607 */
2040*4882a593Smuzhiyun 	if (data) {
2041*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "data = %s\n", data);
2042*4882a593Smuzhiyun 		sscanf(data, "%s %s", addr_str, ie_data);
2043*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "addr=%s, ie=%s\n", addr_str, ie_data);
2044*4882a593Smuzhiyun 
2045*4882a593Smuzhiyun 		if (strlen(addr_str) != 14) {
2046*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "wrong addr %s\n", addr_str);
2047*4882a593Smuzhiyun 			goto exit;
2048*4882a593Smuzhiyun 		}
2049*4882a593Smuzhiyun 		wl_pattern_atoh(addr_str, (char *) addr);
2050*4882a593Smuzhiyun 
2051*4882a593Smuzhiyun 		err = wl_ext_add_del_ie(dev, VNDR_IE_PRBRSP_FLAG, ie_data, "add");
2052*4882a593Smuzhiyun 		if (err)
2053*4882a593Smuzhiyun 			goto exit;
2054*4882a593Smuzhiyun 		err = wl_ext_iovar_setbuf(dev, "send_probresp", addr, sizeof(addr),
2055*4882a593Smuzhiyun 			iovar_buf, sizeof(iovar_buf), NULL);
2056*4882a593Smuzhiyun 		OSL_SLEEP(100);
2057*4882a593Smuzhiyun 		wl_ext_add_del_ie(dev, VNDR_IE_PRBRSP_FLAG, ie_data, "del");
2058*4882a593Smuzhiyun 	}
2059*4882a593Smuzhiyun 
2060*4882a593Smuzhiyun exit:
2061*4882a593Smuzhiyun     return err;
2062*4882a593Smuzhiyun }
2063*4882a593Smuzhiyun 
2064*4882a593Smuzhiyun static int
wl_ext_recv_probreq(struct net_device * dev,char * data,char * command,int total_len)2065*4882a593Smuzhiyun wl_ext_recv_probreq(struct net_device *dev, char *data, char *command,
2066*4882a593Smuzhiyun 	int total_len)
2067*4882a593Smuzhiyun {
2068*4882a593Smuzhiyun 	int err = 0, enable = 0;
2069*4882a593Smuzhiyun 	char cmd[32];
2070*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
2071*4882a593Smuzhiyun 
2072*4882a593Smuzhiyun 	/* enable:
2073*4882a593Smuzhiyun 	    1. dhd_priv wl 86 0
2074*4882a593Smuzhiyun 	    2. dhd_priv wl event_msg 44 1
2075*4882a593Smuzhiyun 	    disable:
2076*4882a593Smuzhiyun 	    1. dhd_priv wl 86 2;
2077*4882a593Smuzhiyun 	    2. dhd_priv wl event_msg 44 0
2078*4882a593Smuzhiyun 	*/
2079*4882a593Smuzhiyun 	if (data) {
2080*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "data = %s\n", data);
2081*4882a593Smuzhiyun 		sscanf(data, "%d", &enable);
2082*4882a593Smuzhiyun 		if (enable) {
2083*4882a593Smuzhiyun 			strcpy(cmd, "wl 86 0");
2084*4882a593Smuzhiyun 			err = wl_ext_wl_iovar(dev, cmd, total_len);
2085*4882a593Smuzhiyun 			if (err)
2086*4882a593Smuzhiyun 				goto exit;
2087*4882a593Smuzhiyun 			strcpy(cmd, "wl event_msg 44 1");
2088*4882a593Smuzhiyun 			err = wl_ext_wl_iovar(dev, cmd, total_len);
2089*4882a593Smuzhiyun 			if (err)
2090*4882a593Smuzhiyun 				goto exit;
2091*4882a593Smuzhiyun 			dhd->recv_probereq = TRUE;
2092*4882a593Smuzhiyun 		} else {
2093*4882a593Smuzhiyun 			if (dhd->conf->pm) {
2094*4882a593Smuzhiyun 				strcpy(cmd, "wl 86 2");
2095*4882a593Smuzhiyun 				wl_ext_wl_iovar(dev, cmd, total_len);
2096*4882a593Smuzhiyun 			}
2097*4882a593Smuzhiyun 			strcpy(cmd, "wl event_msg 44 0");
2098*4882a593Smuzhiyun 			wl_ext_wl_iovar(dev, cmd, total_len);
2099*4882a593Smuzhiyun 			dhd->recv_probereq = FALSE;
2100*4882a593Smuzhiyun 		}
2101*4882a593Smuzhiyun 	}
2102*4882a593Smuzhiyun 
2103*4882a593Smuzhiyun exit:
2104*4882a593Smuzhiyun     return err;
2105*4882a593Smuzhiyun }
2106*4882a593Smuzhiyun 
2107*4882a593Smuzhiyun static int
wl_ext_recv_probresp(struct net_device * dev,char * data,char * command,int total_len)2108*4882a593Smuzhiyun wl_ext_recv_probresp(struct net_device *dev, char *data, char *command,
2109*4882a593Smuzhiyun 	int total_len)
2110*4882a593Smuzhiyun {
2111*4882a593Smuzhiyun 	int err = 0, enable = 0;
2112*4882a593Smuzhiyun 	char cmd[64];
2113*4882a593Smuzhiyun 
2114*4882a593Smuzhiyun 	/* enable:
2115*4882a593Smuzhiyun 	    1. dhd_priv wl pkt_filter_add 150 0 0 0 0xFF 0x50
2116*4882a593Smuzhiyun 	    2. dhd_priv wl pkt_filter_enable 150 1
2117*4882a593Smuzhiyun 	    3. dhd_priv wl mpc 0
2118*4882a593Smuzhiyun 	    4. dhd_priv wl 108 1
2119*4882a593Smuzhiyun 	    disable:
2120*4882a593Smuzhiyun 	    1. dhd_priv wl 108 0
2121*4882a593Smuzhiyun 	    2. dhd_priv wl mpc 1
2122*4882a593Smuzhiyun 	    3. dhd_priv wl pkt_filter_disable 150 0
2123*4882a593Smuzhiyun 	    4. dhd_priv pkt_filter_delete 150
2124*4882a593Smuzhiyun 	*/
2125*4882a593Smuzhiyun 	if (data) {
2126*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "data = %s\n", data);
2127*4882a593Smuzhiyun 		sscanf(data, "%d", &enable);
2128*4882a593Smuzhiyun 		if (enable) {
2129*4882a593Smuzhiyun 			strcpy(cmd, "wl pkt_filter_add 150 0 0 0 0xFF 0x50");
2130*4882a593Smuzhiyun 			err = wl_ext_wl_iovar(dev, cmd, total_len);
2131*4882a593Smuzhiyun 			if (err)
2132*4882a593Smuzhiyun 				goto exit;
2133*4882a593Smuzhiyun 			strcpy(cmd, "wl pkt_filter_enable 150 1");
2134*4882a593Smuzhiyun 			err = wl_ext_wl_iovar(dev, cmd, total_len);
2135*4882a593Smuzhiyun 			if (err)
2136*4882a593Smuzhiyun 				goto exit;
2137*4882a593Smuzhiyun 			strcpy(cmd, "wl mpc 0");
2138*4882a593Smuzhiyun 			err = wl_ext_wl_iovar(dev, cmd, total_len);
2139*4882a593Smuzhiyun 			if (err)
2140*4882a593Smuzhiyun 				goto exit;
2141*4882a593Smuzhiyun 			strcpy(cmd, "wl 108 1");
2142*4882a593Smuzhiyun 			err= wl_ext_wl_iovar(dev, cmd, total_len);
2143*4882a593Smuzhiyun 		} else {
2144*4882a593Smuzhiyun 			strcpy(cmd, "wl 108 0");
2145*4882a593Smuzhiyun 			wl_ext_wl_iovar(dev, cmd, total_len);
2146*4882a593Smuzhiyun 			strcpy(cmd, "wl mpc 1");
2147*4882a593Smuzhiyun 			wl_ext_wl_iovar(dev, cmd, total_len);
2148*4882a593Smuzhiyun 			strcpy(cmd, "wl pkt_filter_enable 150 0");
2149*4882a593Smuzhiyun 			wl_ext_wl_iovar(dev, cmd, total_len);
2150*4882a593Smuzhiyun 			strcpy(cmd, "wl pkt_filter_delete 150");
2151*4882a593Smuzhiyun 			wl_ext_wl_iovar(dev, cmd, total_len);
2152*4882a593Smuzhiyun 		}
2153*4882a593Smuzhiyun 	}
2154*4882a593Smuzhiyun 
2155*4882a593Smuzhiyun exit:
2156*4882a593Smuzhiyun     return err;
2157*4882a593Smuzhiyun }
2158*4882a593Smuzhiyun #endif /* SENDPROB */
2159*4882a593Smuzhiyun 
2160*4882a593Smuzhiyun #if defined(USE_IW)
2161*4882a593Smuzhiyun static int
wl_ext_gtk_key_info(struct net_device * dev,char * data,char * command,int total_len)2162*4882a593Smuzhiyun wl_ext_gtk_key_info(struct net_device *dev, char *data, char *command, int total_len)
2163*4882a593Smuzhiyun {
2164*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
2165*4882a593Smuzhiyun 	int err = 0;
2166*4882a593Smuzhiyun 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
2167*4882a593Smuzhiyun 	gtk_keyinfo_t keyinfo;
2168*4882a593Smuzhiyun 	bcol_gtk_para_t bcol_keyinfo;
2169*4882a593Smuzhiyun 
2170*4882a593Smuzhiyun 	/* wl gtk_key_info [kck kek replay_ctr] */
2171*4882a593Smuzhiyun 	/* wl gtk_key_info 001122..FF001122..FF00000000000001 */
2172*4882a593Smuzhiyun 	if (data) {
2173*4882a593Smuzhiyun 		if (!dhd->conf->rekey_offload) {
2174*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "rekey_offload disabled\n");
2175*4882a593Smuzhiyun 			return BCME_UNSUPPORTED;
2176*4882a593Smuzhiyun 		}
2177*4882a593Smuzhiyun 
2178*4882a593Smuzhiyun 		memset(&bcol_keyinfo, 0, sizeof(bcol_keyinfo));
2179*4882a593Smuzhiyun 		bcol_keyinfo.enable = 1;
2180*4882a593Smuzhiyun 		bcol_keyinfo.ptk_len = 64;
2181*4882a593Smuzhiyun 		memcpy(&bcol_keyinfo.ptk, data, RSN_KCK_LENGTH+RSN_KEK_LENGTH);
2182*4882a593Smuzhiyun 		err = wl_ext_iovar_setbuf(dev, "bcol_gtk_rekey_ptk", &bcol_keyinfo,
2183*4882a593Smuzhiyun 			sizeof(bcol_keyinfo), iovar_buf, sizeof(iovar_buf), NULL);
2184*4882a593Smuzhiyun 		if (!err) {
2185*4882a593Smuzhiyun 			goto exit;
2186*4882a593Smuzhiyun 		}
2187*4882a593Smuzhiyun 
2188*4882a593Smuzhiyun 		memset(&keyinfo, 0, sizeof(keyinfo));
2189*4882a593Smuzhiyun 		memcpy(&keyinfo, data, RSN_KCK_LENGTH+RSN_KEK_LENGTH+RSN_REPLAY_LEN);
2190*4882a593Smuzhiyun 		err = wl_ext_iovar_setbuf(dev, "gtk_key_info", &keyinfo, sizeof(keyinfo),
2191*4882a593Smuzhiyun 			iovar_buf, sizeof(iovar_buf), NULL);
2192*4882a593Smuzhiyun 		if (err) {
2193*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "failed to set gtk_key_info\n");
2194*4882a593Smuzhiyun 			return err;
2195*4882a593Smuzhiyun 		}
2196*4882a593Smuzhiyun 	}
2197*4882a593Smuzhiyun 
2198*4882a593Smuzhiyun exit:
2199*4882a593Smuzhiyun 	if (android_msg_level & ANDROID_INFO_LEVEL) {
2200*4882a593Smuzhiyun 		prhex("kck", (uchar *)keyinfo.KCK, RSN_KCK_LENGTH);
2201*4882a593Smuzhiyun 		prhex("kek", (uchar *)keyinfo.KEK, RSN_KEK_LENGTH);
2202*4882a593Smuzhiyun 		prhex("replay_ctr", (uchar *)keyinfo.ReplayCounter, RSN_REPLAY_LEN);
2203*4882a593Smuzhiyun 	}
2204*4882a593Smuzhiyun     return err;
2205*4882a593Smuzhiyun }
2206*4882a593Smuzhiyun #endif /* USE_IW */
2207*4882a593Smuzhiyun 
2208*4882a593Smuzhiyun #ifdef WL_EXT_WOWL
2209*4882a593Smuzhiyun static int
wl_ext_wowl_pattern(struct net_device * dev,char * data,char * command,int total_len)2210*4882a593Smuzhiyun wl_ext_wowl_pattern(struct net_device *dev, char *data, char *command,
2211*4882a593Smuzhiyun 	int total_len)
2212*4882a593Smuzhiyun {
2213*4882a593Smuzhiyun 	s8 iovar_buf[WLC_IOCTL_SMLEN];
2214*4882a593Smuzhiyun 	uint buf_len = 0;
2215*4882a593Smuzhiyun 	int	offset;
2216*4882a593Smuzhiyun 	char mask[128]="\0", pattern[128]="\0", add[4]="\0",
2217*4882a593Smuzhiyun 		mask_tmp[128], *pmask_tmp;
2218*4882a593Smuzhiyun 	uint32 masksize, patternsize, pad_len = 0;
2219*4882a593Smuzhiyun 	wl_wowl_pattern2_t *wowl_pattern2 = NULL;
2220*4882a593Smuzhiyun 	wl_wowl_pattern_t *wowl_pattern = NULL;
2221*4882a593Smuzhiyun 	char *mask_and_pattern;
2222*4882a593Smuzhiyun 	wl_wowl_pattern_list_t *list;
2223*4882a593Smuzhiyun 	uint8 *ptr;
2224*4882a593Smuzhiyun 	int ret = 0, i, j, v;
2225*4882a593Smuzhiyun 
2226*4882a593Smuzhiyun 	if (data) {
2227*4882a593Smuzhiyun 		sscanf(data, "%s %d %s %s", add, &offset, mask_tmp, pattern);
2228*4882a593Smuzhiyun 		if (strcmp(add, "add") != 0 && strcmp(add, "clr") != 0) {
2229*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "first arg should be add or clr\n");
2230*4882a593Smuzhiyun 			goto exit;
2231*4882a593Smuzhiyun 		}
2232*4882a593Smuzhiyun 		if (!strcmp(add, "clr")) {
2233*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "wowl_pattern clr\n");
2234*4882a593Smuzhiyun 			ret = wl_ext_iovar_setbuf(dev, "wowl_pattern", add,
2235*4882a593Smuzhiyun 				sizeof(add), iovar_buf, sizeof(iovar_buf), NULL);
2236*4882a593Smuzhiyun 			goto exit;
2237*4882a593Smuzhiyun 		}
2238*4882a593Smuzhiyun 		masksize = strlen(mask_tmp) -2;
2239*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "0 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
2240*4882a593Smuzhiyun 
2241*4882a593Smuzhiyun 		// add pading
2242*4882a593Smuzhiyun 		if (masksize % 16)
2243*4882a593Smuzhiyun 			pad_len = (16 - masksize % 16);
2244*4882a593Smuzhiyun 		for (i=0; i<pad_len; i++)
2245*4882a593Smuzhiyun 			strcat(mask_tmp, "0");
2246*4882a593Smuzhiyun 		masksize += pad_len;
2247*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "1 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
2248*4882a593Smuzhiyun 
2249*4882a593Smuzhiyun 		// translate 0x00 to 0, others to 1
2250*4882a593Smuzhiyun 		j = 0;
2251*4882a593Smuzhiyun 		pmask_tmp = &mask_tmp[2];
2252*4882a593Smuzhiyun 		for (i=0; i<masksize/2; i++) {
2253*4882a593Smuzhiyun 			if(strncmp(&pmask_tmp[i*2], "00", 2))
2254*4882a593Smuzhiyun 				pmask_tmp[j] = '1';
2255*4882a593Smuzhiyun 			else
2256*4882a593Smuzhiyun 				pmask_tmp[j] = '0';
2257*4882a593Smuzhiyun 			j++;
2258*4882a593Smuzhiyun 		}
2259*4882a593Smuzhiyun 		pmask_tmp[j] = '\0';
2260*4882a593Smuzhiyun 		masksize = masksize / 2;
2261*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "2 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
2262*4882a593Smuzhiyun 
2263*4882a593Smuzhiyun 		// reorder per 8bits
2264*4882a593Smuzhiyun 		pmask_tmp = &mask_tmp[2];
2265*4882a593Smuzhiyun 		for (i=0; i<masksize/8; i++) {
2266*4882a593Smuzhiyun 			char c;
2267*4882a593Smuzhiyun 			for (j=0; j<4; j++) {
2268*4882a593Smuzhiyun 				c = pmask_tmp[i*8+j];
2269*4882a593Smuzhiyun 				pmask_tmp[i*8+j] = pmask_tmp[(i+1)*8-j-1];
2270*4882a593Smuzhiyun 				pmask_tmp[(i+1)*8-j-1] = c;
2271*4882a593Smuzhiyun 			}
2272*4882a593Smuzhiyun 		}
2273*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "3 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
2274*4882a593Smuzhiyun 
2275*4882a593Smuzhiyun 		// translate 8bits to 1byte
2276*4882a593Smuzhiyun 		j = 0; v = 0;
2277*4882a593Smuzhiyun 		pmask_tmp = &mask_tmp[2];
2278*4882a593Smuzhiyun 		strcpy(mask, "0x");
2279*4882a593Smuzhiyun 		for (i=0; i<masksize; i++) {
2280*4882a593Smuzhiyun 			v = (v<<1) | (pmask_tmp[i]=='1');
2281*4882a593Smuzhiyun 			if (((i+1)%4) == 0) {
2282*4882a593Smuzhiyun 				if (v < 10)
2283*4882a593Smuzhiyun 					mask[j+2] = v + '0';
2284*4882a593Smuzhiyun 				else
2285*4882a593Smuzhiyun 					mask[j+2] = (v-10) + 'a';
2286*4882a593Smuzhiyun 				j++;
2287*4882a593Smuzhiyun 				v = 0;
2288*4882a593Smuzhiyun 			}
2289*4882a593Smuzhiyun 		}
2290*4882a593Smuzhiyun 		mask[j+2] = '\0';
2291*4882a593Smuzhiyun 		masksize = j/2;
2292*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "4 mask=%s, masksize=%d\n", mask, masksize);
2293*4882a593Smuzhiyun 
2294*4882a593Smuzhiyun 		patternsize = (strlen(pattern)-2)/2;
2295*4882a593Smuzhiyun 		buf_len = sizeof(wl_wowl_pattern2_t) + patternsize + masksize;
2296*4882a593Smuzhiyun 		wowl_pattern2 = kmalloc(buf_len, GFP_KERNEL);
2297*4882a593Smuzhiyun 		if (wowl_pattern2 == NULL) {
2298*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", buf_len);
2299*4882a593Smuzhiyun 			goto exit;
2300*4882a593Smuzhiyun 		}
2301*4882a593Smuzhiyun 		memset(wowl_pattern2, 0, sizeof(wl_wowl_pattern2_t));
2302*4882a593Smuzhiyun 
2303*4882a593Smuzhiyun 		strncpy(wowl_pattern2->cmd, add, sizeof(add));
2304*4882a593Smuzhiyun 		wowl_pattern2->wowl_pattern.type = 0;
2305*4882a593Smuzhiyun 		wowl_pattern2->wowl_pattern.offset = offset;
2306*4882a593Smuzhiyun 		mask_and_pattern = (char*)wowl_pattern2 + sizeof(wl_wowl_pattern2_t);
2307*4882a593Smuzhiyun 
2308*4882a593Smuzhiyun 		wowl_pattern2->wowl_pattern.masksize = masksize;
2309*4882a593Smuzhiyun 		ret = wl_pattern_atoh(mask, mask_and_pattern);
2310*4882a593Smuzhiyun 		if (ret == -1) {
2311*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "rejecting mask=%s\n", mask);
2312*4882a593Smuzhiyun 			goto exit;
2313*4882a593Smuzhiyun 		}
2314*4882a593Smuzhiyun 
2315*4882a593Smuzhiyun 		mask_and_pattern += wowl_pattern2->wowl_pattern.masksize;
2316*4882a593Smuzhiyun 		wowl_pattern2->wowl_pattern.patternoffset = sizeof(wl_wowl_pattern_t) +
2317*4882a593Smuzhiyun 			wowl_pattern2->wowl_pattern.masksize;
2318*4882a593Smuzhiyun 
2319*4882a593Smuzhiyun 		wowl_pattern2->wowl_pattern.patternsize = patternsize;
2320*4882a593Smuzhiyun 		ret = wl_pattern_atoh(pattern, mask_and_pattern);
2321*4882a593Smuzhiyun 		if (ret == -1) {
2322*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "rejecting pattern=%s\n", pattern);
2323*4882a593Smuzhiyun 			goto exit;
2324*4882a593Smuzhiyun 		}
2325*4882a593Smuzhiyun 
2326*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "%s %d %s %s\n", add, offset, mask, pattern);
2327*4882a593Smuzhiyun 
2328*4882a593Smuzhiyun 		ret = wl_ext_iovar_setbuf(dev, "wowl_pattern", (char *)wowl_pattern2,
2329*4882a593Smuzhiyun 			buf_len, iovar_buf, sizeof(iovar_buf), NULL);
2330*4882a593Smuzhiyun 	}
2331*4882a593Smuzhiyun 	else {
2332*4882a593Smuzhiyun 		ret = wl_ext_iovar_getbuf(dev, "wowl_pattern", NULL, 0,
2333*4882a593Smuzhiyun 			iovar_buf, sizeof(iovar_buf), NULL);
2334*4882a593Smuzhiyun 		if (!ret) {
2335*4882a593Smuzhiyun 			list = (wl_wowl_pattern_list_t *)iovar_buf;
2336*4882a593Smuzhiyun 			ret = snprintf(command, total_len, "#of patterns :%d\n", list->count);
2337*4882a593Smuzhiyun 			ptr = (uint8 *)list->pattern;
2338*4882a593Smuzhiyun 			for (i=0; i<list->count; i++) {
2339*4882a593Smuzhiyun 				uint8 *pattern;
2340*4882a593Smuzhiyun 				wowl_pattern = (wl_wowl_pattern_t *)ptr;
2341*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len,
2342*4882a593Smuzhiyun 					"Pattern %d:\n"
2343*4882a593Smuzhiyun 					"ID         :0x%x\n"
2344*4882a593Smuzhiyun 					"Offset     :%d\n"
2345*4882a593Smuzhiyun 					"Masksize   :%d\n"
2346*4882a593Smuzhiyun 					"Mask       :0x",
2347*4882a593Smuzhiyun 					i+1, (uint32)wowl_pattern->id, wowl_pattern->offset,
2348*4882a593Smuzhiyun 					wowl_pattern->masksize);
2349*4882a593Smuzhiyun 				pattern = ((uint8 *)wowl_pattern + sizeof(wl_wowl_pattern_t));
2350*4882a593Smuzhiyun 				for (j = 0; j < wowl_pattern->masksize; j++) {
2351*4882a593Smuzhiyun 					ret += snprintf(command+ret, total_len, "%02x", pattern[j]);
2352*4882a593Smuzhiyun 				}
2353*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, "\n");
2354*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len,
2355*4882a593Smuzhiyun 					"PatternSize:%d\n"
2356*4882a593Smuzhiyun 					"Pattern    :0x",
2357*4882a593Smuzhiyun 					wowl_pattern->patternsize);
2358*4882a593Smuzhiyun 
2359*4882a593Smuzhiyun 				pattern = ((uint8*)wowl_pattern + wowl_pattern->patternoffset);
2360*4882a593Smuzhiyun 				for (j=0; j<wowl_pattern->patternsize; j++)
2361*4882a593Smuzhiyun 					ret += snprintf(command+ret, total_len, "%02x", pattern[j]);
2362*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, "\n");
2363*4882a593Smuzhiyun 				ptr += (wowl_pattern->masksize + wowl_pattern->patternsize +
2364*4882a593Smuzhiyun 				        sizeof(wl_wowl_pattern_t));
2365*4882a593Smuzhiyun 			}
2366*4882a593Smuzhiyun 
2367*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "%s\n", command);
2368*4882a593Smuzhiyun 		}
2369*4882a593Smuzhiyun 	}
2370*4882a593Smuzhiyun 
2371*4882a593Smuzhiyun exit:
2372*4882a593Smuzhiyun 	if (wowl_pattern2)
2373*4882a593Smuzhiyun 		kfree(wowl_pattern2);
2374*4882a593Smuzhiyun 	return ret;
2375*4882a593Smuzhiyun }
2376*4882a593Smuzhiyun 
2377*4882a593Smuzhiyun static int
wl_ext_wowl_wakeind(struct net_device * dev,char * data,char * command,int total_len)2378*4882a593Smuzhiyun wl_ext_wowl_wakeind(struct net_device *dev, char *data, char *command,
2379*4882a593Smuzhiyun 	int total_len)
2380*4882a593Smuzhiyun {
2381*4882a593Smuzhiyun 	s8 iovar_buf[WLC_IOCTL_SMLEN];
2382*4882a593Smuzhiyun 	wl_wowl_wakeind_t *wake = NULL;
2383*4882a593Smuzhiyun 	int ret = -1;
2384*4882a593Smuzhiyun 	char clr[6]="\0";
2385*4882a593Smuzhiyun 
2386*4882a593Smuzhiyun 	if (data) {
2387*4882a593Smuzhiyun 		sscanf(data, "%s", clr);
2388*4882a593Smuzhiyun 		if (!strcmp(clr, "clear")) {
2389*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "wowl_wakeind clear\n");
2390*4882a593Smuzhiyun 			ret = wl_ext_iovar_setbuf(dev, "wowl_wakeind", clr, sizeof(clr),
2391*4882a593Smuzhiyun 				iovar_buf, sizeof(iovar_buf), NULL);
2392*4882a593Smuzhiyun 		} else {
2393*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "first arg should be clear\n");
2394*4882a593Smuzhiyun 		}
2395*4882a593Smuzhiyun 	} else {
2396*4882a593Smuzhiyun 		ret = wl_ext_iovar_getbuf(dev, "wowl_wakeind", NULL, 0,
2397*4882a593Smuzhiyun 			iovar_buf, sizeof(iovar_buf), NULL);
2398*4882a593Smuzhiyun 		if (!ret) {
2399*4882a593Smuzhiyun 			wake = (wl_wowl_wakeind_t *) iovar_buf;
2400*4882a593Smuzhiyun 			ret = snprintf(command, total_len, "wakeind=0x%x", wake->ucode_wakeind);
2401*4882a593Smuzhiyun 			if (wake->ucode_wakeind & WL_WOWL_MAGIC)
2402*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, " (MAGIC packet)");
2403*4882a593Smuzhiyun 			if (wake->ucode_wakeind & WL_WOWL_NET)
2404*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, " (Netpattern)");
2405*4882a593Smuzhiyun 			if (wake->ucode_wakeind & WL_WOWL_DIS)
2406*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, " (Disassoc/Deauth)");
2407*4882a593Smuzhiyun 			if (wake->ucode_wakeind & WL_WOWL_BCN)
2408*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, " (Loss of beacon)");
2409*4882a593Smuzhiyun 			if (wake->ucode_wakeind & WL_WOWL_TCPKEEP_TIME)
2410*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, " (TCPKA timeout)");
2411*4882a593Smuzhiyun 			if (wake->ucode_wakeind & WL_WOWL_TCPKEEP_DATA)
2412*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, " (TCPKA data)");
2413*4882a593Smuzhiyun 			if (wake->ucode_wakeind & WL_WOWL_TCPFIN)
2414*4882a593Smuzhiyun 				ret += snprintf(command+ret, total_len, " (TCP FIN)");
2415*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "%s\n", command);
2416*4882a593Smuzhiyun 		}
2417*4882a593Smuzhiyun 	}
2418*4882a593Smuzhiyun 
2419*4882a593Smuzhiyun 	return ret;
2420*4882a593Smuzhiyun }
2421*4882a593Smuzhiyun #endif /* WL_EXT_WOWL */
2422*4882a593Smuzhiyun 
2423*4882a593Smuzhiyun #ifdef WL_GPIO_NOTIFY
2424*4882a593Smuzhiyun typedef struct notify_payload {
2425*4882a593Smuzhiyun 	int index;
2426*4882a593Smuzhiyun 	int len;
2427*4882a593Smuzhiyun 	char payload[128];
2428*4882a593Smuzhiyun } notify_payload_t;
2429*4882a593Smuzhiyun 
2430*4882a593Smuzhiyun static int
wl_ext_gpio_notify(struct net_device * dev,char * data,char * command,int total_len)2431*4882a593Smuzhiyun wl_ext_gpio_notify(struct net_device *dev, char *data, char *command,
2432*4882a593Smuzhiyun 	int total_len)
2433*4882a593Smuzhiyun {
2434*4882a593Smuzhiyun 	s8 iovar_buf[WLC_IOCTL_SMLEN];
2435*4882a593Smuzhiyun 	notify_payload_t notify, *pnotify = NULL;
2436*4882a593Smuzhiyun 	int i, ret = 0, bytes_written = 0;
2437*4882a593Smuzhiyun 	char frame_str[WLC_IOCTL_SMLEN+3];
2438*4882a593Smuzhiyun 
2439*4882a593Smuzhiyun 	if (data) {
2440*4882a593Smuzhiyun 		memset(&notify, 0, sizeof(notify));
2441*4882a593Smuzhiyun 		memset(frame_str, 0, sizeof(frame_str));
2442*4882a593Smuzhiyun 		sscanf(data, "%d %s", &notify.index, frame_str);
2443*4882a593Smuzhiyun 
2444*4882a593Smuzhiyun 		if (notify.index < 0)
2445*4882a593Smuzhiyun 			notify.index = 0;
2446*4882a593Smuzhiyun 
2447*4882a593Smuzhiyun 		if (strlen(frame_str)) {
2448*4882a593Smuzhiyun 			notify.len = wl_pattern_atoh(frame_str, notify.payload);
2449*4882a593Smuzhiyun 			if (notify.len == -1) {
2450*4882a593Smuzhiyun 				AEXT_ERROR(dev->name, "rejecting pattern=%s\n", frame_str);
2451*4882a593Smuzhiyun 				goto exit;
2452*4882a593Smuzhiyun 			}
2453*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "index=%d, len=%d\n", notify.index, notify.len);
2454*4882a593Smuzhiyun 			if (android_msg_level & ANDROID_INFO_LEVEL)
2455*4882a593Smuzhiyun 				prhex("payload", (uchar *)notify.payload, notify.len);
2456*4882a593Smuzhiyun 			ret = wl_ext_iovar_setbuf(dev, "bcol_gpio_noti", (char *)&notify,
2457*4882a593Smuzhiyun 				sizeof(notify), iovar_buf, WLC_IOCTL_SMLEN, NULL);
2458*4882a593Smuzhiyun 		} else {
2459*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "index=%d\n", notify.index);
2460*4882a593Smuzhiyun 			ret = wl_ext_iovar_getbuf(dev, "bcol_gpio_noti", &notify.index,
2461*4882a593Smuzhiyun 				sizeof(notify.index), iovar_buf, sizeof(iovar_buf), NULL);
2462*4882a593Smuzhiyun 			if (!ret) {
2463*4882a593Smuzhiyun 				pnotify = (notify_payload_t *)iovar_buf;
2464*4882a593Smuzhiyun 				bytes_written += snprintf(command+bytes_written, total_len,
2465*4882a593Smuzhiyun 					"Id            :%d\n"
2466*4882a593Smuzhiyun 					"Packet        :0x",
2467*4882a593Smuzhiyun 					pnotify->index);
2468*4882a593Smuzhiyun 				for (i=0; i<pnotify->len; i++) {
2469*4882a593Smuzhiyun 					bytes_written += snprintf(command+bytes_written, total_len,
2470*4882a593Smuzhiyun 						"%02x", pnotify->payload[i]);
2471*4882a593Smuzhiyun 				}
2472*4882a593Smuzhiyun 				AEXT_TRACE(dev->name, "command result is\n%s\n", command);
2473*4882a593Smuzhiyun 				ret = bytes_written;
2474*4882a593Smuzhiyun 			}
2475*4882a593Smuzhiyun 		}
2476*4882a593Smuzhiyun 	}
2477*4882a593Smuzhiyun 
2478*4882a593Smuzhiyun exit:
2479*4882a593Smuzhiyun 	return ret;
2480*4882a593Smuzhiyun }
2481*4882a593Smuzhiyun #endif /* WL_GPIO_NOTIFY */
2482*4882a593Smuzhiyun 
2483*4882a593Smuzhiyun #ifdef CSI_SUPPORT
2484*4882a593Smuzhiyun typedef struct csi_config {
2485*4882a593Smuzhiyun 	/* Peer device mac address. */
2486*4882a593Smuzhiyun 	struct ether_addr addr;
2487*4882a593Smuzhiyun 	/* BW to be used in the measurements. This needs to be supported both by the */
2488*4882a593Smuzhiyun 	/* device itself and the peer. */
2489*4882a593Smuzhiyun 	uint32 bw;
2490*4882a593Smuzhiyun 	/* Time interval between measurements (units: 1 ms). */
2491*4882a593Smuzhiyun 	uint32 period;
2492*4882a593Smuzhiyun 	/* CSI method */
2493*4882a593Smuzhiyun 	uint32 method;
2494*4882a593Smuzhiyun } csi_config_t;
2495*4882a593Smuzhiyun 
2496*4882a593Smuzhiyun typedef struct csi_list {
2497*4882a593Smuzhiyun 	uint32 cnt;
2498*4882a593Smuzhiyun 	csi_config_t configs[1];
2499*4882a593Smuzhiyun } csi_list_t;
2500*4882a593Smuzhiyun 
2501*4882a593Smuzhiyun static int
wl_ext_csi(struct net_device * dev,char * data,char * command,int total_len)2502*4882a593Smuzhiyun wl_ext_csi(struct net_device *dev, char *data, char *command, int total_len)
2503*4882a593Smuzhiyun {
2504*4882a593Smuzhiyun 	csi_config_t csi, *csip;
2505*4882a593Smuzhiyun 	csi_list_t *csi_list;
2506*4882a593Smuzhiyun 	int ret = -1, period=-1, i;
2507*4882a593Smuzhiyun 	char mac[32], *buf = NULL;
2508*4882a593Smuzhiyun 	struct ether_addr ea;
2509*4882a593Smuzhiyun 	int bytes_written = 0;
2510*4882a593Smuzhiyun 
2511*4882a593Smuzhiyun 	buf = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
2512*4882a593Smuzhiyun 	if (buf == NULL) {
2513*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
2514*4882a593Smuzhiyun 		goto exit;
2515*4882a593Smuzhiyun 	}
2516*4882a593Smuzhiyun 	memset(buf, 0, WLC_IOCTL_SMLEN);
2517*4882a593Smuzhiyun 
2518*4882a593Smuzhiyun 	if (data) {
2519*4882a593Smuzhiyun 		sscanf(data, "%s %d", mac, &period);
2520*4882a593Smuzhiyun 		ret = bcm_ether_atoe(mac, &ea);
2521*4882a593Smuzhiyun 		if (!ret) {
2522*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "rejecting mac=%s, ret=%d\n", mac, ret);
2523*4882a593Smuzhiyun 			goto exit;
2524*4882a593Smuzhiyun 		}
2525*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "mac=%pM, period=%d", &ea, period);
2526*4882a593Smuzhiyun 		if (period > 0) {
2527*4882a593Smuzhiyun 			memset(&csi, 0, sizeof(csi_config_t));
2528*4882a593Smuzhiyun 			bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
2529*4882a593Smuzhiyun 			csi.period = period;
2530*4882a593Smuzhiyun 			ret = wl_ext_iovar_setbuf(dev, "csi", (char *)&csi, sizeof(csi),
2531*4882a593Smuzhiyun 				buf, WLC_IOCTL_SMLEN, NULL);
2532*4882a593Smuzhiyun 		} else if (period == 0) {
2533*4882a593Smuzhiyun 			memset(&csi, 0, sizeof(csi_config_t));
2534*4882a593Smuzhiyun 			bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
2535*4882a593Smuzhiyun 			ret = wl_ext_iovar_setbuf(dev, "csi_del", (char *)&csi, sizeof(csi),
2536*4882a593Smuzhiyun 				buf, WLC_IOCTL_SMLEN, NULL);
2537*4882a593Smuzhiyun 		} else {
2538*4882a593Smuzhiyun 			ret = wl_ext_iovar_getbuf(dev, "csi", &ea, ETHER_ADDR_LEN, buf,
2539*4882a593Smuzhiyun 				WLC_IOCTL_SMLEN, NULL);
2540*4882a593Smuzhiyun 			if (!ret) {
2541*4882a593Smuzhiyun 				csip = (csi_config_t *) buf;
2542*4882a593Smuzhiyun 					/* Dump all lists */
2543*4882a593Smuzhiyun 				bytes_written += snprintf(command+bytes_written, total_len,
2544*4882a593Smuzhiyun 					"Mac    :%pM\n"
2545*4882a593Smuzhiyun 					"Period :%d\n"
2546*4882a593Smuzhiyun 					"BW     :%d\n"
2547*4882a593Smuzhiyun 					"Method :%d\n",
2548*4882a593Smuzhiyun 					&csip->addr, csip->period, csip->bw, csip->method);
2549*4882a593Smuzhiyun 				AEXT_TRACE(dev->name, "command result is %s\n", command);
2550*4882a593Smuzhiyun 				ret = bytes_written;
2551*4882a593Smuzhiyun 			}
2552*4882a593Smuzhiyun 		}
2553*4882a593Smuzhiyun 	}
2554*4882a593Smuzhiyun 	else {
2555*4882a593Smuzhiyun 		ret = wl_ext_iovar_getbuf(dev, "csi_list", NULL, 0, buf, WLC_IOCTL_SMLEN, NULL);
2556*4882a593Smuzhiyun 		if (!ret) {
2557*4882a593Smuzhiyun 			csi_list = (csi_list_t *)buf;
2558*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len,
2559*4882a593Smuzhiyun 				"Total number :%d\n", csi_list->cnt);
2560*4882a593Smuzhiyun 			for (i=0; i<csi_list->cnt; i++) {
2561*4882a593Smuzhiyun 				csip = &csi_list->configs[i];
2562*4882a593Smuzhiyun 				bytes_written += snprintf(command+bytes_written, total_len,
2563*4882a593Smuzhiyun 					"Idx    :%d\n"
2564*4882a593Smuzhiyun 					"Mac    :%pM\n"
2565*4882a593Smuzhiyun 					"Period :%d\n"
2566*4882a593Smuzhiyun 					"BW     :%d\n"
2567*4882a593Smuzhiyun 					"Method :%d\n\n",
2568*4882a593Smuzhiyun 					i+1, &csip->addr, csip->period, csip->bw, csip->method);
2569*4882a593Smuzhiyun 			}
2570*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "command result is %s\n", command);
2571*4882a593Smuzhiyun 			ret = bytes_written;
2572*4882a593Smuzhiyun 		}
2573*4882a593Smuzhiyun 	}
2574*4882a593Smuzhiyun 
2575*4882a593Smuzhiyun exit:
2576*4882a593Smuzhiyun 	if (buf)
2577*4882a593Smuzhiyun 		kfree(buf);
2578*4882a593Smuzhiyun 	return ret;
2579*4882a593Smuzhiyun }
2580*4882a593Smuzhiyun #endif /* CSI_SUPPORT */
2581*4882a593Smuzhiyun 
2582*4882a593Smuzhiyun static int
wl_ext_get_country(struct net_device * dev,char * data,char * command,int total_len)2583*4882a593Smuzhiyun wl_ext_get_country(struct net_device *dev, char *data, char *command,
2584*4882a593Smuzhiyun 	int total_len)
2585*4882a593Smuzhiyun {
2586*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
2587*4882a593Smuzhiyun 	wl_country_t cspec = {{0}, 0, {0}};
2588*4882a593Smuzhiyun 	int bytes_written = 0, ret = 0;
2589*4882a593Smuzhiyun 
2590*4882a593Smuzhiyun 	if (data) {
2591*4882a593Smuzhiyun 		char *country_code = data;
2592*4882a593Smuzhiyun 		char *rev_info_delim = country_code + 2; /* 2 bytes of country code */
2593*4882a593Smuzhiyun 		int revinfo = 0;
2594*4882a593Smuzhiyun 		if ((rev_info_delim) &&
2595*4882a593Smuzhiyun 			(strnicmp(rev_info_delim, "/", strlen("/")) == 0) && (rev_info_delim + 1)) {
2596*4882a593Smuzhiyun 			revinfo  = bcm_atoi(rev_info_delim + 1);
2597*4882a593Smuzhiyun 		}
2598*4882a593Smuzhiyun #ifdef WL_CFG80211
2599*4882a593Smuzhiyun 		bytes_written = wl_cfg80211_set_country_code(dev, country_code,
2600*4882a593Smuzhiyun 			true, true, revinfo);
2601*4882a593Smuzhiyun #else
2602*4882a593Smuzhiyun 		bytes_written = wldev_set_country(dev, country_code, true, true, revinfo);
2603*4882a593Smuzhiyun #endif /* WL_CFG80211 */
2604*4882a593Smuzhiyun 	} else {
2605*4882a593Smuzhiyun 		ret = dhd_conf_get_country(dhd, &cspec);
2606*4882a593Smuzhiyun 		if (!ret) {
2607*4882a593Smuzhiyun 			bytes_written += snprintf(command+bytes_written, total_len,
2608*4882a593Smuzhiyun 				"%s/%d", cspec.ccode, cspec.rev);
2609*4882a593Smuzhiyun 		}
2610*4882a593Smuzhiyun 		if (!bytes_written)
2611*4882a593Smuzhiyun 			bytes_written = -1;
2612*4882a593Smuzhiyun 		AEXT_TRACE(dev->name, "command result is %s\n", command);
2613*4882a593Smuzhiyun 	}
2614*4882a593Smuzhiyun 
2615*4882a593Smuzhiyun 	return bytes_written;
2616*4882a593Smuzhiyun }
2617*4882a593Smuzhiyun 
2618*4882a593Smuzhiyun static int
wl_ext_disable_5g_band(struct net_device * dev,char * data,char * command,int total_len)2619*4882a593Smuzhiyun wl_ext_disable_5g_band(struct net_device *dev, char *data, char *command,
2620*4882a593Smuzhiyun 	int total_len)
2621*4882a593Smuzhiyun {
2622*4882a593Smuzhiyun #ifdef WL_CFG80211
2623*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
2624*4882a593Smuzhiyun #endif
2625*4882a593Smuzhiyun 	int ret = -1;
2626*4882a593Smuzhiyun 	int val;
2627*4882a593Smuzhiyun 
2628*4882a593Smuzhiyun 	if (data) {
2629*4882a593Smuzhiyun 		val = (int)simple_strtol(data, NULL, 0);
2630*4882a593Smuzhiyun 		ret = wl_ext_iovar_setint(dev, "disable_5g_band", val);
2631*4882a593Smuzhiyun #ifdef WL_CFG80211
2632*4882a593Smuzhiyun 		if (!ret)
2633*4882a593Smuzhiyun 			wl_update_wiphybands(cfg, true);
2634*4882a593Smuzhiyun #endif
2635*4882a593Smuzhiyun 	} else {
2636*4882a593Smuzhiyun 		ret = wl_ext_iovar_getint(dev, "disable_5g_band", &val);
2637*4882a593Smuzhiyun 		if (!ret) {
2638*4882a593Smuzhiyun 			ret = snprintf(command, total_len, "%d", val);
2639*4882a593Smuzhiyun 			AEXT_TRACE(dev->name, "command result is %s\n", command);
2640*4882a593Smuzhiyun 		}
2641*4882a593Smuzhiyun 	}
2642*4882a593Smuzhiyun 
2643*4882a593Smuzhiyun 	return ret;
2644*4882a593Smuzhiyun }
2645*4882a593Smuzhiyun 
2646*4882a593Smuzhiyun typedef int (wl_ext_tpl_parse_t)(struct net_device *dev, char *data, char *command,
2647*4882a593Smuzhiyun 	int total_len);
2648*4882a593Smuzhiyun 
2649*4882a593Smuzhiyun typedef struct wl_ext_iovar_tpl_t {
2650*4882a593Smuzhiyun 	int get;
2651*4882a593Smuzhiyun 	int set;
2652*4882a593Smuzhiyun 	char *name;
2653*4882a593Smuzhiyun 	wl_ext_tpl_parse_t *parse;
2654*4882a593Smuzhiyun } wl_ext_iovar_tpl_t;
2655*4882a593Smuzhiyun 
2656*4882a593Smuzhiyun const wl_ext_iovar_tpl_t wl_ext_iovar_tpl_list[] = {
2657*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"event_msg",	wl_ext_event_msg},
2658*4882a593Smuzhiyun #if defined(USE_IW)
2659*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"gtk_key_info",	wl_ext_gtk_key_info},
2660*4882a593Smuzhiyun #endif /* USE_IW */
2661*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"recal",		wl_ext_recal},
2662*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"rsdb_mode",	wl_ext_rsdb_mode},
2663*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"mkeep_alive",	wl_ext_mkeep_alive},
2664*4882a593Smuzhiyun #ifdef PKT_FILTER_SUPPORT
2665*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"pkt_filter_add",		wl_ext_pkt_filter_add},
2666*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"pkt_filter_delete",	wl_ext_pkt_filter_delete},
2667*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"pkt_filter_enable",	wl_ext_pkt_filter_enable},
2668*4882a593Smuzhiyun #endif /* PKT_FILTER_SUPPORT */
2669*4882a593Smuzhiyun #if defined(WL_EXT_IAPSTA) && defined(WLMESH)
2670*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"mesh_peer_status",	wl_ext_mesh_peer_status},
2671*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA && WLMESH */
2672*4882a593Smuzhiyun #ifdef SENDPROB
2673*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"send_probreq",		wl_ext_send_probreq},
2674*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"send_probresp",	wl_ext_send_probresp},
2675*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"recv_probreq",		wl_ext_recv_probreq},
2676*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"recv_probresp",	wl_ext_recv_probresp},
2677*4882a593Smuzhiyun #endif /* SENDPROB */
2678*4882a593Smuzhiyun #ifdef WL_EXT_TCPKA
2679*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"tcpka_conn_add",		wl_ext_tcpka_conn_add},
2680*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"tcpka_conn_enable",	wl_ext_tcpka_conn_enable},
2681*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"tcpka_conn_sess_info",	wl_ext_tcpka_conn_info},
2682*4882a593Smuzhiyun #endif /* WL_EXT_TCPKA */
2683*4882a593Smuzhiyun #ifdef WL_EXT_WOWL
2684*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"wowl_pattern",		wl_ext_wowl_pattern},
2685*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"wowl_wakeind",		wl_ext_wowl_wakeind},
2686*4882a593Smuzhiyun #endif /* WL_EXT_WOWL */
2687*4882a593Smuzhiyun #ifdef IDHCP
2688*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"dhcpc_dump",		wl_ext_dhcpc_dump},
2689*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"dhcpc_param",		wl_ext_dhcpc_param},
2690*4882a593Smuzhiyun #endif /* IDHCP */
2691*4882a593Smuzhiyun #ifdef WL_GPIO_NOTIFY
2692*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"bcol_gpio_noti",	wl_ext_gpio_notify},
2693*4882a593Smuzhiyun #endif /* WL_GPIO_NOTIFY */
2694*4882a593Smuzhiyun #ifdef CSI_SUPPORT
2695*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"csi",				wl_ext_csi},
2696*4882a593Smuzhiyun #endif /* CSI_SUPPORT */
2697*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"country",			wl_ext_get_country},
2698*4882a593Smuzhiyun 	{WLC_GET_VAR,	WLC_SET_VAR,	"disable_5g_band",	wl_ext_disable_5g_band},
2699*4882a593Smuzhiyun };
2700*4882a593Smuzhiyun 
2701*4882a593Smuzhiyun /*
2702*4882a593Smuzhiyun Ex: dhd_priv wl [cmd] [val]
2703*4882a593Smuzhiyun   dhd_priv wl 85
2704*4882a593Smuzhiyun   dhd_priv wl 86 1
2705*4882a593Smuzhiyun   dhd_priv wl mpc
2706*4882a593Smuzhiyun   dhd_priv wl mpc 1
2707*4882a593Smuzhiyun */
2708*4882a593Smuzhiyun static int
wl_ext_wl_iovar(struct net_device * dev,char * command,int total_len)2709*4882a593Smuzhiyun wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len)
2710*4882a593Smuzhiyun {
2711*4882a593Smuzhiyun 	int cmd, val, ret = -1, i;
2712*4882a593Smuzhiyun 	char name[32], *pch, *pick_tmp, *data;
2713*4882a593Smuzhiyun 	int bytes_written=-1;
2714*4882a593Smuzhiyun 	const wl_ext_iovar_tpl_t *tpl = wl_ext_iovar_tpl_list;
2715*4882a593Smuzhiyun 	int tpl_count = ARRAY_SIZE(wl_ext_iovar_tpl_list);
2716*4882a593Smuzhiyun 	char *pEnd;
2717*4882a593Smuzhiyun 
2718*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "cmd %s\n", command);
2719*4882a593Smuzhiyun 	pick_tmp = command;
2720*4882a593Smuzhiyun 
2721*4882a593Smuzhiyun 	pch = bcmstrtok(&pick_tmp, " ", 0); // pick wl
2722*4882a593Smuzhiyun 	if (!pch || strncmp(pch, "wl", 2))
2723*4882a593Smuzhiyun 		goto exit;
2724*4882a593Smuzhiyun 
2725*4882a593Smuzhiyun 	pch = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
2726*4882a593Smuzhiyun 	if (!pch)
2727*4882a593Smuzhiyun 		goto exit;
2728*4882a593Smuzhiyun 
2729*4882a593Smuzhiyun 	memset(name, 0 , sizeof (name));
2730*4882a593Smuzhiyun 	cmd = bcm_strtoul(pch, &pEnd, 0);
2731*4882a593Smuzhiyun 	if (cmd == 0 || strlen(pEnd)) {
2732*4882a593Smuzhiyun 		strcpy(name, pch);
2733*4882a593Smuzhiyun 	}
2734*4882a593Smuzhiyun 	data = bcmstrtok(&pick_tmp, "", 0); // pick data
2735*4882a593Smuzhiyun 	if (data && (cmd == 0|| strlen(pEnd))) {
2736*4882a593Smuzhiyun 		cmd = WLC_SET_VAR;
2737*4882a593Smuzhiyun 	} else if (cmd == 0|| strlen(pEnd)) {
2738*4882a593Smuzhiyun 		cmd = WLC_GET_VAR;
2739*4882a593Smuzhiyun 	}
2740*4882a593Smuzhiyun 
2741*4882a593Smuzhiyun 	/* look for a matching code in the table */
2742*4882a593Smuzhiyun 	for (i = 0; i < tpl_count; i++, tpl++) {
2743*4882a593Smuzhiyun 		if ((tpl->get == cmd || tpl->set == cmd) && !strcmp(tpl->name, name))
2744*4882a593Smuzhiyun 			break;
2745*4882a593Smuzhiyun 	}
2746*4882a593Smuzhiyun 	if (i < tpl_count && tpl->parse) {
2747*4882a593Smuzhiyun 		ret = tpl->parse(dev, data, command, total_len);
2748*4882a593Smuzhiyun 	} else {
2749*4882a593Smuzhiyun 		if (cmd == WLC_SET_VAR) {
2750*4882a593Smuzhiyun 			val = (int)simple_strtol(data, NULL, 0);
2751*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "set %s %d\n", name, val);
2752*4882a593Smuzhiyun 			ret = wl_ext_iovar_setint(dev, name, val);
2753*4882a593Smuzhiyun 		} else if (cmd == WLC_GET_VAR) {
2754*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "get %s\n", name);
2755*4882a593Smuzhiyun 			ret = wl_ext_iovar_getint(dev, name, &val);
2756*4882a593Smuzhiyun 			if (!ret) {
2757*4882a593Smuzhiyun 				bytes_written = snprintf(command, total_len, "%d", val);
2758*4882a593Smuzhiyun 				AEXT_INFO(dev->name, "command result is %s\n", command);
2759*4882a593Smuzhiyun 				ret = bytes_written;
2760*4882a593Smuzhiyun 			}
2761*4882a593Smuzhiyun 		} else if (data) {
2762*4882a593Smuzhiyun 			val = (int)simple_strtol(data, NULL, 0);
2763*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "set %d %d\n", cmd, val);
2764*4882a593Smuzhiyun 			ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE);
2765*4882a593Smuzhiyun 		} else {
2766*4882a593Smuzhiyun 			AEXT_INFO(dev->name, "get %d\n", cmd);
2767*4882a593Smuzhiyun 			ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE);
2768*4882a593Smuzhiyun 			if (!ret) {
2769*4882a593Smuzhiyun 				bytes_written = snprintf(command, total_len, "%d", val);
2770*4882a593Smuzhiyun 				AEXT_INFO(dev->name, "command result is %s\n", command);
2771*4882a593Smuzhiyun 				ret = bytes_written;
2772*4882a593Smuzhiyun 			}
2773*4882a593Smuzhiyun 		}
2774*4882a593Smuzhiyun 	}
2775*4882a593Smuzhiyun 
2776*4882a593Smuzhiyun exit:
2777*4882a593Smuzhiyun 	return ret;
2778*4882a593Smuzhiyun }
2779*4882a593Smuzhiyun 
2780*4882a593Smuzhiyun int
wl_ext_conf_iovar(struct net_device * dev,char * command,int total_len)2781*4882a593Smuzhiyun wl_ext_conf_iovar(struct net_device *dev, char *command, int total_len)
2782*4882a593Smuzhiyun {
2783*4882a593Smuzhiyun 	int ret = 0;
2784*4882a593Smuzhiyun 	char name[32], *pch, *pick_tmp, *data;
2785*4882a593Smuzhiyun 	int bytes_written=-1;
2786*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
2787*4882a593Smuzhiyun 
2788*4882a593Smuzhiyun 	AEXT_TRACE(dev->name, "cmd %s\n", command);
2789*4882a593Smuzhiyun 	pick_tmp = command;
2790*4882a593Smuzhiyun 
2791*4882a593Smuzhiyun 	pch = bcmstrtok(&pick_tmp, " ", 0); // pick conf
2792*4882a593Smuzhiyun 	if (!pch || strncmp(pch, "conf", 4))
2793*4882a593Smuzhiyun 		goto exit;
2794*4882a593Smuzhiyun 
2795*4882a593Smuzhiyun 	pch = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
2796*4882a593Smuzhiyun 	if (!pch)
2797*4882a593Smuzhiyun 		goto exit;
2798*4882a593Smuzhiyun 
2799*4882a593Smuzhiyun 	strncpy(name, pch, sizeof(name));
2800*4882a593Smuzhiyun 
2801*4882a593Smuzhiyun 	data = bcmstrtok(&pick_tmp, "", 0); // pick data
2802*4882a593Smuzhiyun 
2803*4882a593Smuzhiyun 	if (!strcmp(name, "pm")) {
2804*4882a593Smuzhiyun 		if (data) {
2805*4882a593Smuzhiyun 			dhd->conf->pm = simple_strtol(data, NULL, 0);
2806*4882a593Smuzhiyun 			ret = 0;
2807*4882a593Smuzhiyun 		} else {
2808*4882a593Smuzhiyun 			bytes_written = snprintf(command, total_len, "%d", dhd->conf->pm);
2809*4882a593Smuzhiyun 			ret = bytes_written;
2810*4882a593Smuzhiyun 		}
2811*4882a593Smuzhiyun 	} else {
2812*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "no config parameter found\n");
2813*4882a593Smuzhiyun 	}
2814*4882a593Smuzhiyun 
2815*4882a593Smuzhiyun exit:
2816*4882a593Smuzhiyun 	return ret;
2817*4882a593Smuzhiyun }
2818*4882a593Smuzhiyun 
2819*4882a593Smuzhiyun int
wl_android_ext_priv_cmd(struct net_device * net,char * command,int total_len,int * bytes_written)2820*4882a593Smuzhiyun wl_android_ext_priv_cmd(struct net_device *net, char *command,
2821*4882a593Smuzhiyun 	int total_len, int *bytes_written)
2822*4882a593Smuzhiyun {
2823*4882a593Smuzhiyun 	int ret = 0;
2824*4882a593Smuzhiyun 
2825*4882a593Smuzhiyun 	if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) {
2826*4882a593Smuzhiyun 		*bytes_written = wl_ext_channels(net, command, total_len);
2827*4882a593Smuzhiyun 	}
2828*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) {
2829*4882a593Smuzhiyun 		*bytes_written = wl_ext_channel(net, command, total_len);
2830*4882a593Smuzhiyun 	}
2831*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) {
2832*4882a593Smuzhiyun 		*bytes_written = wl_ext_roam_trigger(net, command, total_len);
2833*4882a593Smuzhiyun 	}
2834*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) {
2835*4882a593Smuzhiyun 		*bytes_written = wl_ext_pm(net, command, total_len);
2836*4882a593Smuzhiyun 	}
2837*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) {
2838*4882a593Smuzhiyun 		*bytes_written = wl_ext_monitor(net, command, total_len);
2839*4882a593Smuzhiyun 	}
2840*4882a593Smuzhiyun #ifdef BTC_WAR
2841*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_BTC_WAR, strlen(CMD_BTC_WAR)) == 0) {
2842*4882a593Smuzhiyun 		*bytes_written = wl_ext_btc_war(net, command, total_len);
2843*4882a593Smuzhiyun 	}
2844*4882a593Smuzhiyun #endif /* BTC_WAR */
2845*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) {
2846*4882a593Smuzhiyun 		int bcn_li_dtim;
2847*4882a593Smuzhiyun 		bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10);
2848*4882a593Smuzhiyun 		*bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim);
2849*4882a593Smuzhiyun 	}
2850*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
2851*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0 ||
2852*4882a593Smuzhiyun 			strnicmp(command, CMD_ISAM_INIT, strlen(CMD_ISAM_INIT)) == 0) {
2853*4882a593Smuzhiyun 		*bytes_written = wl_ext_isam_init(net, command, total_len);
2854*4882a593Smuzhiyun 	}
2855*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_IAPSTA_CONFIG, strlen(CMD_IAPSTA_CONFIG)) == 0 ||
2856*4882a593Smuzhiyun 			strnicmp(command, CMD_ISAM_CONFIG, strlen(CMD_ISAM_CONFIG)) == 0) {
2857*4882a593Smuzhiyun 		*bytes_written = wl_ext_iapsta_config(net, command, total_len);
2858*4882a593Smuzhiyun 	}
2859*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_IAPSTA_ENABLE, strlen(CMD_IAPSTA_ENABLE)) == 0 ||
2860*4882a593Smuzhiyun 			strnicmp(command, CMD_ISAM_ENABLE, strlen(CMD_ISAM_ENABLE)) == 0) {
2861*4882a593Smuzhiyun 		*bytes_written = wl_ext_iapsta_enable(net, command, total_len);
2862*4882a593Smuzhiyun 	}
2863*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_IAPSTA_DISABLE, strlen(CMD_IAPSTA_DISABLE)) == 0 ||
2864*4882a593Smuzhiyun 			strnicmp(command, CMD_ISAM_DISABLE, strlen(CMD_ISAM_DISABLE)) == 0) {
2865*4882a593Smuzhiyun 		*bytes_written = wl_ext_iapsta_disable(net, command, total_len);
2866*4882a593Smuzhiyun 	}
2867*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_ISAM_STATUS, strlen(CMD_ISAM_STATUS)) == 0) {
2868*4882a593Smuzhiyun 		*bytes_written = wl_ext_isam_status(net, command, total_len);
2869*4882a593Smuzhiyun 	}
2870*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_ISAM_PARAM, strlen(CMD_ISAM_PARAM)) == 0) {
2871*4882a593Smuzhiyun 		*bytes_written = wl_ext_isam_param(net, command, total_len);
2872*4882a593Smuzhiyun 	}
2873*4882a593Smuzhiyun #if defined(WLMESH) && defined(WL_ESCAN)
2874*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_ISAM_PEER_PATH, strlen(CMD_ISAM_PEER_PATH)) == 0) {
2875*4882a593Smuzhiyun 		*bytes_written = wl_ext_isam_peer_path(net, command, total_len);
2876*4882a593Smuzhiyun 	}
2877*4882a593Smuzhiyun #endif /* WLMESH && WL_ESCAN */
2878*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
2879*4882a593Smuzhiyun #ifdef WL_CFG80211
2880*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
2881*4882a593Smuzhiyun 		*bytes_written = wl_cfg80211_autochannel(net, command, total_len);
2882*4882a593Smuzhiyun 	}
2883*4882a593Smuzhiyun #endif /* WL_CFG80211 */
2884*4882a593Smuzhiyun #if defined(WL_WIRELESS_EXT) && defined(WL_ESCAN)
2885*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
2886*4882a593Smuzhiyun 		*bytes_written = wl_iw_autochannel(net, command, total_len);
2887*4882a593Smuzhiyun 	}
2888*4882a593Smuzhiyun #endif /* WL_WIRELESS_EXT && WL_ESCAN */
2889*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_WLMSGLEVEL, strlen(CMD_WLMSGLEVEL)) == 0) {
2890*4882a593Smuzhiyun 		*bytes_written = wl_ext_wlmsglevel(net, command, total_len);
2891*4882a593Smuzhiyun 	}
2892*4882a593Smuzhiyun #ifdef WLEASYMESH
2893*4882a593Smuzhiyun     else if (strnicmp(command, CMD_EASYMESH, strlen(CMD_EASYMESH)) == 0) {
2894*4882a593Smuzhiyun 		int skip = strlen(CMD_EASYMESH) + 1;
2895*4882a593Smuzhiyun 		*bytes_written = wl_ext_easymesh(net, command+skip, total_len);
2896*4882a593Smuzhiyun     }
2897*4882a593Smuzhiyun #endif /* WLEASYMESH */
2898*4882a593Smuzhiyun #if defined(PKT_STATICS) && defined(BCMSDIO)
2899*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_DUMP_PKT_STATICS, strlen(CMD_DUMP_PKT_STATICS)) == 0) {
2900*4882a593Smuzhiyun 		struct dhd_pub *dhd = dhd_get_pub(net);
2901*4882a593Smuzhiyun 		dhd_bus_dump_txpktstatics(dhd);
2902*4882a593Smuzhiyun 	}
2903*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_CLEAR_PKT_STATICS, strlen(CMD_CLEAR_PKT_STATICS)) == 0) {
2904*4882a593Smuzhiyun 		struct dhd_pub *dhd = dhd_get_pub(net);
2905*4882a593Smuzhiyun 		dhd_bus_clear_txpktstatics(dhd);
2906*4882a593Smuzhiyun 	}
2907*4882a593Smuzhiyun #endif /* PKT_STATICS && BCMSDIO */
2908*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) {
2909*4882a593Smuzhiyun 		*bytes_written = wl_ext_wl_iovar(net, command, total_len);
2910*4882a593Smuzhiyun 	}
2911*4882a593Smuzhiyun 	else if (strnicmp(command, CMD_CONF, strlen(CMD_CONF)) == 0) {
2912*4882a593Smuzhiyun 		*bytes_written = wl_ext_conf_iovar(net, command, total_len);
2913*4882a593Smuzhiyun 	}
2914*4882a593Smuzhiyun 	else
2915*4882a593Smuzhiyun 		ret = -1;
2916*4882a593Smuzhiyun 
2917*4882a593Smuzhiyun 	return ret;
2918*4882a593Smuzhiyun }
2919*4882a593Smuzhiyun 
2920*4882a593Smuzhiyun #define CH_MIN_5G_CHANNEL 34
2921*4882a593Smuzhiyun int
wl_construct_ctl_chanspec_list(struct net_device * dev,wl_uint32_list_t * chan_list)2922*4882a593Smuzhiyun wl_construct_ctl_chanspec_list(struct net_device *dev, wl_uint32_list_t *chan_list)
2923*4882a593Smuzhiyun {
2924*4882a593Smuzhiyun 	void *list;
2925*4882a593Smuzhiyun 	u32 i, channel;
2926*4882a593Smuzhiyun 	chanspec_t chspec = 0;
2927*4882a593Smuzhiyun 	s32 err = BCME_OK;
2928*4882a593Smuzhiyun 	bool legacy_chan_info = FALSE;
2929*4882a593Smuzhiyun 	u16 list_count;
2930*4882a593Smuzhiyun 
2931*4882a593Smuzhiyun #define LOCAL_BUF_LEN 4096
2932*4882a593Smuzhiyun 	list = kmalloc(LOCAL_BUF_LEN, GFP_KERNEL);
2933*4882a593Smuzhiyun 	if (list == NULL) {
2934*4882a593Smuzhiyun 		WL_ERR(("failed to allocate local buf\n"));
2935*4882a593Smuzhiyun 		return -ENOMEM;
2936*4882a593Smuzhiyun 	}
2937*4882a593Smuzhiyun 
2938*4882a593Smuzhiyun 	err = wldev_iovar_getbuf(dev, "chan_info_list", NULL,
2939*4882a593Smuzhiyun 		0, list, LOCAL_BUF_LEN, NULL);
2940*4882a593Smuzhiyun 	if (err == BCME_UNSUPPORTED) {
2941*4882a593Smuzhiyun 		err = wl_ext_iovar_getbuf(dev, "chanspecs", NULL,
2942*4882a593Smuzhiyun 			0, list, LOCAL_BUF_LEN, NULL);
2943*4882a593Smuzhiyun 		if (err != BCME_OK) {
2944*4882a593Smuzhiyun 			WL_ERR(("get chanspecs err(%d)\n", err));
2945*4882a593Smuzhiyun 			kfree(list);
2946*4882a593Smuzhiyun 			return err;
2947*4882a593Smuzhiyun 		}
2948*4882a593Smuzhiyun 		/* Update indicating legacy chan info usage */
2949*4882a593Smuzhiyun 		legacy_chan_info = TRUE;
2950*4882a593Smuzhiyun 	} else if (err != BCME_OK) {
2951*4882a593Smuzhiyun 		WL_ERR(("get chan_info_list err(%d)\n", err));
2952*4882a593Smuzhiyun 		kfree(list);
2953*4882a593Smuzhiyun 		return err;
2954*4882a593Smuzhiyun 	}
2955*4882a593Smuzhiyun 
2956*4882a593Smuzhiyun 	list_count = legacy_chan_info ? ((wl_uint32_list_t *)list)->count :
2957*4882a593Smuzhiyun 		((wl_chanspec_list_v1_t *)list)->count;
2958*4882a593Smuzhiyun 	for (i = 0; i < dtoh32(list_count); i++) {
2959*4882a593Smuzhiyun 		if (legacy_chan_info) {
2960*4882a593Smuzhiyun 			chspec = (chanspec_t)dtoh32(((wl_uint32_list_t *)list)->element[i]);
2961*4882a593Smuzhiyun 		} else {
2962*4882a593Smuzhiyun 			chspec = (chanspec_t)dtoh32
2963*4882a593Smuzhiyun 			(((wl_chanspec_list_v1_t *)list)->chspecs[i].chanspec);
2964*4882a593Smuzhiyun 		}
2965*4882a593Smuzhiyun 		chspec = wl_chspec_driver_to_host(chspec);
2966*4882a593Smuzhiyun 		channel = wf_chspec_ctlchan(chspec);
2967*4882a593Smuzhiyun 
2968*4882a593Smuzhiyun 		if (!CHSPEC_IS20(chspec)) {
2969*4882a593Smuzhiyun 			continue;
2970*4882a593Smuzhiyun 		}
2971*4882a593Smuzhiyun 		if (CHSPEC_IS2G(chspec) && (channel >= CH_MIN_2G_CHANNEL) &&
2972*4882a593Smuzhiyun 				(channel <= CH_MAX_2G_CHANNEL)) {
2973*4882a593Smuzhiyun 			chan_list->element[chan_list->count] = chspec;
2974*4882a593Smuzhiyun 			chan_list->count++;
2975*4882a593Smuzhiyun 		}
2976*4882a593Smuzhiyun #ifdef WL_6G_BAND
2977*4882a593Smuzhiyun 		else if (CHSPEC_IS6G(chspec) && (channel >= CH_MIN_6G_CHANNEL) &&
2978*4882a593Smuzhiyun 				(channel <= CH_MAX_6G_CHANNEL)) {
2979*4882a593Smuzhiyun 			if (channel == 2)
2980*4882a593Smuzhiyun 				continue;
2981*4882a593Smuzhiyun 			chan_list->element[chan_list->count] = chspec;
2982*4882a593Smuzhiyun 			chan_list->count++;
2983*4882a593Smuzhiyun 		}
2984*4882a593Smuzhiyun #endif /* WL_6G_BAND */
2985*4882a593Smuzhiyun 		else if (CHSPEC_IS5G(chspec) && (channel >= CH_MIN_5G_CHANNEL) &&
2986*4882a593Smuzhiyun 				(channel <= 165)) {
2987*4882a593Smuzhiyun 			chan_list->element[chan_list->count] = chspec;
2988*4882a593Smuzhiyun 			chan_list->count++;
2989*4882a593Smuzhiyun 		} else {
2990*4882a593Smuzhiyun 			continue;
2991*4882a593Smuzhiyun 		}
2992*4882a593Smuzhiyun 	}
2993*4882a593Smuzhiyun 
2994*4882a593Smuzhiyun 	kfree(list);
2995*4882a593Smuzhiyun #undef LOCAL_BUF_LEN
2996*4882a593Smuzhiyun 	return err;
2997*4882a593Smuzhiyun }
2998*4882a593Smuzhiyun 
2999*4882a593Smuzhiyun #if defined(WL_CFG80211) || defined(WL_ESCAN)
3000*4882a593Smuzhiyun int
wl_ext_get_distance(struct net_device * net,u32 band)3001*4882a593Smuzhiyun wl_ext_get_distance(struct net_device *net, u32 band)
3002*4882a593Smuzhiyun {
3003*4882a593Smuzhiyun 	u32 bw = WL_CHANSPEC_BW_20;
3004*4882a593Smuzhiyun 	s32 bw_cap = 0, distance = 0;
3005*4882a593Smuzhiyun 	struct {
3006*4882a593Smuzhiyun 		u32 band;
3007*4882a593Smuzhiyun 		u32 bw_cap;
3008*4882a593Smuzhiyun 	} param = {0, 0};
3009*4882a593Smuzhiyun 	char buf[WLC_IOCTL_SMLEN]="\0";
3010*4882a593Smuzhiyun 	s32 err = BCME_OK;
3011*4882a593Smuzhiyun 
3012*4882a593Smuzhiyun 	param.band = band;
3013*4882a593Smuzhiyun 	err = wldev_iovar_getbuf(net, "bw_cap", &param, sizeof(param), buf,
3014*4882a593Smuzhiyun 		sizeof(buf), NULL);
3015*4882a593Smuzhiyun 	if (err) {
3016*4882a593Smuzhiyun 		if (err != BCME_UNSUPPORTED) {
3017*4882a593Smuzhiyun 			AEXT_TRACE(net->name, "bw_cap failed, %d\n", err);
3018*4882a593Smuzhiyun 			return err;
3019*4882a593Smuzhiyun 		} else {
3020*4882a593Smuzhiyun 			err = wl_ext_iovar_getint(net, "mimo_bw_cap", &bw_cap);
3021*4882a593Smuzhiyun 			if (bw_cap != WLC_N_BW_20ALL)
3022*4882a593Smuzhiyun 				bw = WL_CHANSPEC_BW_40;
3023*4882a593Smuzhiyun 		}
3024*4882a593Smuzhiyun 	} else {
3025*4882a593Smuzhiyun 		if (WL_BW_CAP_80MHZ(buf[0]))
3026*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_80;
3027*4882a593Smuzhiyun 		else if (WL_BW_CAP_40MHZ(buf[0]))
3028*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_40;
3029*4882a593Smuzhiyun 		else
3030*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_20;
3031*4882a593Smuzhiyun 	}
3032*4882a593Smuzhiyun 
3033*4882a593Smuzhiyun 	if (bw == WL_CHANSPEC_BW_20)
3034*4882a593Smuzhiyun 		distance = 2;
3035*4882a593Smuzhiyun 	else if (bw == WL_CHANSPEC_BW_40)
3036*4882a593Smuzhiyun 		distance = 4;
3037*4882a593Smuzhiyun 	else if (bw == WL_CHANSPEC_BW_80)
3038*4882a593Smuzhiyun 		distance = 8;
3039*4882a593Smuzhiyun 	else
3040*4882a593Smuzhiyun 		distance = 16;
3041*4882a593Smuzhiyun 	AEXT_INFO(net->name, "bw=0x%x, distance=%d\n", bw, distance);
3042*4882a593Smuzhiyun 
3043*4882a593Smuzhiyun 	return distance;
3044*4882a593Smuzhiyun }
3045*4882a593Smuzhiyun 
3046*4882a593Smuzhiyun int
wl_ext_get_best_channel(struct net_device * net,wl_bss_cache_ctrl_t * bss_cache_ctrl,int * best_2g_ch,int * best_5g_ch,int * best_6g_ch)3047*4882a593Smuzhiyun wl_ext_get_best_channel(struct net_device *net,
3048*4882a593Smuzhiyun #if defined(BSSCACHE)
3049*4882a593Smuzhiyun 	wl_bss_cache_ctrl_t *bss_cache_ctrl,
3050*4882a593Smuzhiyun #else
3051*4882a593Smuzhiyun 	wl_scan_results_v109_t *bss_list,
3052*4882a593Smuzhiyun #endif /* BSSCACHE */
3053*4882a593Smuzhiyun 	int *best_2g_ch, int *best_5g_ch, int *best_6g_ch)
3054*4882a593Smuzhiyun {
3055*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(net);
3056*4882a593Smuzhiyun 	struct wl_bss_info *bi = NULL;	/* must be initialized */
3057*4882a593Smuzhiyun 	struct wl_chan_info chan_info;
3058*4882a593Smuzhiyun 	s32 i, j;
3059*4882a593Smuzhiyun #if defined(BSSCACHE)
3060*4882a593Smuzhiyun 	wl_bss_cache_t *node;
3061*4882a593Smuzhiyun #endif /* BSSCACHE */
3062*4882a593Smuzhiyun 	int b_band[CH_MAX_2G_CHANNEL]={0}, a_band1[4]={0}, a_band4[5]={0};
3063*4882a593Smuzhiyun #ifdef WL_6G_BAND
3064*4882a593Smuzhiyun 	int six_g_band5[24]={0}, six_g_band6[5]={0}, six_g_band7[18]={0}, six_g_band8[13]={0};
3065*4882a593Smuzhiyun 	s32 distance_6g;
3066*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3067*4882a593Smuzhiyun 	s32 cen_ch, distance, distance_2g, distance_5g, chanspec, min_ap=999;
3068*4882a593Smuzhiyun 	wl_uint32_list_t *list = NULL;
3069*4882a593Smuzhiyun 	int ret;
3070*4882a593Smuzhiyun 	chanspec_t chspec;
3071*4882a593Smuzhiyun 	u32 channel;
3072*4882a593Smuzhiyun 
3073*4882a593Smuzhiyun 	memset(b_band, -1, sizeof(b_band));
3074*4882a593Smuzhiyun 	memset(a_band1, -1, sizeof(a_band1));
3075*4882a593Smuzhiyun 	memset(a_band4, -1, sizeof(a_band4));
3076*4882a593Smuzhiyun #ifdef WL_6G_BAND
3077*4882a593Smuzhiyun 	memset(six_g_band5, -1, sizeof(six_g_band5));
3078*4882a593Smuzhiyun 	memset(six_g_band6, -1, sizeof(six_g_band6));
3079*4882a593Smuzhiyun 	memset(six_g_band7, -1, sizeof(six_g_band7));
3080*4882a593Smuzhiyun 	memset(six_g_band8, -1, sizeof(six_g_band8));
3081*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3082*4882a593Smuzhiyun 
3083*4882a593Smuzhiyun 	list = kzalloc(sizeof(u32)*(MAX_CTRL_CHANSPECS + 1), GFP_KERNEL);
3084*4882a593Smuzhiyun 	if (list == NULL) {
3085*4882a593Smuzhiyun 		AEXT_ERROR(net->name, "kzalloc failed\n");
3086*4882a593Smuzhiyun 		ret = -ENOMEM;
3087*4882a593Smuzhiyun 		goto exit;
3088*4882a593Smuzhiyun 	}
3089*4882a593Smuzhiyun 
3090*4882a593Smuzhiyun 	ret = wl_construct_ctl_chanspec_list(net, list);
3091*4882a593Smuzhiyun 	if (ret < 0) {
3092*4882a593Smuzhiyun 		AEXT_ERROR(net->name, "get channels failed with %d\n", ret);
3093*4882a593Smuzhiyun 		goto exit;
3094*4882a593Smuzhiyun 	} else {
3095*4882a593Smuzhiyun 		for (i = 0; i < list->count; i++) {
3096*4882a593Smuzhiyun 			chspec = list->element[i];
3097*4882a593Smuzhiyun 			channel = wf_chspec_ctlchan(chspec);
3098*4882a593Smuzhiyun 			chan_info.band = CHSPEC2WLC_BAND(chspec);
3099*4882a593Smuzhiyun 			chan_info.chan = channel;
3100*4882a593Smuzhiyun 			if (wl_ext_passive_chan(net, &chan_info)) {
3101*4882a593Smuzhiyun 				continue;
3102*4882a593Smuzhiyun 			}
3103*4882a593Smuzhiyun 			if (CHSPEC_IS2G(chspec) && (channel >= CH_MIN_2G_CHANNEL) &&
3104*4882a593Smuzhiyun 					(channel <= CH_MAX_2G_CHANNEL)) {
3105*4882a593Smuzhiyun 				b_band[channel-1] = 0;
3106*4882a593Smuzhiyun 			}
3107*4882a593Smuzhiyun #ifdef WL_6G_BAND
3108*4882a593Smuzhiyun 			else if (CHSPEC_IS6G(chspec) && (channel >= CH_MIN_6G_CHANNEL) &&
3109*4882a593Smuzhiyun 					(channel <= CH_MAX_6G_CHANNEL)) {
3110*4882a593Smuzhiyun 				if (channel <= 93)
3111*4882a593Smuzhiyun 					six_g_band5[(channel-1)/4] = 0;
3112*4882a593Smuzhiyun 				else if (channel >= 97 && channel <= 109)
3113*4882a593Smuzhiyun 					six_g_band6[(channel-97)/4] = 0;
3114*4882a593Smuzhiyun 				else if (channel >= 117 && channel <= 181)
3115*4882a593Smuzhiyun 					six_g_band7[(channel-117)/4] = 0;
3116*4882a593Smuzhiyun 				else if (channel >= 189 && channel <= 221)
3117*4882a593Smuzhiyun 					six_g_band8[(channel-189)/4] = 0;
3118*4882a593Smuzhiyun 			}
3119*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3120*4882a593Smuzhiyun 			else if (CHSPEC_IS5G(chspec) && channel >= CH_MIN_5G_CHANNEL) {
3121*4882a593Smuzhiyun 				if (channel <= 48)
3122*4882a593Smuzhiyun 					a_band1[(channel-36)/4] = 0;
3123*4882a593Smuzhiyun 				else if (channel >= 149 && channel <= 161)
3124*4882a593Smuzhiyun 					a_band4[(channel-149)/4] = 0;
3125*4882a593Smuzhiyun 			}
3126*4882a593Smuzhiyun 		}
3127*4882a593Smuzhiyun 	}
3128*4882a593Smuzhiyun 
3129*4882a593Smuzhiyun 	distance_2g = wl_ext_get_distance(net, WLC_BAND_2G);
3130*4882a593Smuzhiyun 	distance_5g = wl_ext_get_distance(net, WLC_BAND_5G);
3131*4882a593Smuzhiyun #ifdef WL_6G_BAND
3132*4882a593Smuzhiyun 	distance_6g = wl_ext_get_distance(net, WLC_BAND_6G);
3133*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3134*4882a593Smuzhiyun 
3135*4882a593Smuzhiyun #if defined(BSSCACHE)
3136*4882a593Smuzhiyun 	node = bss_cache_ctrl->m_cache_head;
3137*4882a593Smuzhiyun 	for (i=0; node && i<256; i++)
3138*4882a593Smuzhiyun #else
3139*4882a593Smuzhiyun 	for (i=0; i < bss_list->count; i++)
3140*4882a593Smuzhiyun #endif /* BSSCACHE */
3141*4882a593Smuzhiyun 	{
3142*4882a593Smuzhiyun #if defined(BSSCACHE)
3143*4882a593Smuzhiyun 		bi = node->results.bss_info;
3144*4882a593Smuzhiyun #else
3145*4882a593Smuzhiyun 		bi = bi ? (wl_bss_info_v109_t *)((uintptr)bi + dtoh32(bi->length)) : bss_list->bss_info;
3146*4882a593Smuzhiyun #endif /* BSSCACHE */
3147*4882a593Smuzhiyun 		chanspec = wl_ext_chspec_driver_to_host(dhd, bi->chanspec);
3148*4882a593Smuzhiyun 		cen_ch = CHSPEC_CHANNEL(bi->chanspec);
3149*4882a593Smuzhiyun 		distance = 0;
3150*4882a593Smuzhiyun 		if (CHSPEC_IS20(chanspec))
3151*4882a593Smuzhiyun 			distance += 2;
3152*4882a593Smuzhiyun 		else if (CHSPEC_IS40(chanspec))
3153*4882a593Smuzhiyun 			distance += 4;
3154*4882a593Smuzhiyun 		else if (CHSPEC_IS80(chanspec))
3155*4882a593Smuzhiyun 			distance += 8;
3156*4882a593Smuzhiyun 		else
3157*4882a593Smuzhiyun 			distance += 16;
3158*4882a593Smuzhiyun 
3159*4882a593Smuzhiyun 		if (CHSPEC_IS2G(chanspec)) {
3160*4882a593Smuzhiyun 			distance += distance_2g;
3161*4882a593Smuzhiyun 			for (j=0; j<ARRAYSIZE(b_band); j++) {
3162*4882a593Smuzhiyun 				if (b_band[j] >= 0 && abs(cen_ch-(1+j)) <= distance)
3163*4882a593Smuzhiyun 					b_band[j] += 1;
3164*4882a593Smuzhiyun 			}
3165*4882a593Smuzhiyun 		}
3166*4882a593Smuzhiyun #ifdef WL_6G_BAND
3167*4882a593Smuzhiyun 		else if (CHSPEC_IS6G(chanspec)) {
3168*4882a593Smuzhiyun 			distance += distance_6g;
3169*4882a593Smuzhiyun 			if (cen_ch <= 93) {
3170*4882a593Smuzhiyun 				for (j=0; j<ARRAYSIZE(six_g_band5); j++) {
3171*4882a593Smuzhiyun 					if (six_g_band5[j] >= 0 && abs(cen_ch-(93+j*4)) <= distance)
3172*4882a593Smuzhiyun 						six_g_band5[j] += 1;
3173*4882a593Smuzhiyun 				}
3174*4882a593Smuzhiyun 			}
3175*4882a593Smuzhiyun 			else if (channel >= 97 && channel <= 109) {
3176*4882a593Smuzhiyun 				for (j=0; j<ARRAYSIZE(six_g_band6); j++) {
3177*4882a593Smuzhiyun 					if (six_g_band6[j] >= 0 && abs(cen_ch-(97+j*4)) <= distance)
3178*4882a593Smuzhiyun 						six_g_band6[j] += 1;
3179*4882a593Smuzhiyun 				}
3180*4882a593Smuzhiyun 			}
3181*4882a593Smuzhiyun 			else if (channel >= 117 && channel <= 181) {
3182*4882a593Smuzhiyun 				for (j=0; j<ARRAYSIZE(six_g_band7); j++) {
3183*4882a593Smuzhiyun 					if (six_g_band7[j] >= 0 && abs(cen_ch-(117+j*4)) <= distance)
3184*4882a593Smuzhiyun 						six_g_band7[j] += 1;
3185*4882a593Smuzhiyun 				}
3186*4882a593Smuzhiyun 			}
3187*4882a593Smuzhiyun 			else if (channel >= 189 && channel <= 221) {
3188*4882a593Smuzhiyun 				for (j=0; j<ARRAYSIZE(six_g_band8); j++) {
3189*4882a593Smuzhiyun 					if (six_g_band8[j] >= 0 && abs(cen_ch-(189+j*4)) <= distance)
3190*4882a593Smuzhiyun 						six_g_band8[j] += 1;
3191*4882a593Smuzhiyun 				}
3192*4882a593Smuzhiyun 			}
3193*4882a593Smuzhiyun 		}
3194*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3195*4882a593Smuzhiyun 		else {
3196*4882a593Smuzhiyun 			distance += distance_5g;
3197*4882a593Smuzhiyun 			if (cen_ch <= 48) {
3198*4882a593Smuzhiyun 				for (j=0; j<ARRAYSIZE(a_band1); j++) {
3199*4882a593Smuzhiyun 					if (a_band1[j] >= 0 && abs(cen_ch-(36+j*4)) <= distance)
3200*4882a593Smuzhiyun 						a_band1[j] += 1;
3201*4882a593Smuzhiyun 				}
3202*4882a593Smuzhiyun 			}
3203*4882a593Smuzhiyun 			else if (cen_ch >= 149) {
3204*4882a593Smuzhiyun 				for (j=0; j<ARRAYSIZE(a_band4); j++) {
3205*4882a593Smuzhiyun 					if (a_band4[j] >= 0 && abs(cen_ch-(149+j*4)) <= distance)
3206*4882a593Smuzhiyun 						a_band4[j] += 1;
3207*4882a593Smuzhiyun 				}
3208*4882a593Smuzhiyun 			}
3209*4882a593Smuzhiyun 		}
3210*4882a593Smuzhiyun #if defined(BSSCACHE)
3211*4882a593Smuzhiyun 		node = node->next;
3212*4882a593Smuzhiyun #endif /* BSSCACHE */
3213*4882a593Smuzhiyun 	}
3214*4882a593Smuzhiyun 
3215*4882a593Smuzhiyun 	*best_2g_ch = 0;
3216*4882a593Smuzhiyun 	min_ap = 999;
3217*4882a593Smuzhiyun 	for (i=0; i<CH_MAX_2G_CHANNEL; i++) {
3218*4882a593Smuzhiyun 		if(b_band[i] < min_ap && b_band[i] >= 0) {
3219*4882a593Smuzhiyun 			min_ap = b_band[i];
3220*4882a593Smuzhiyun 			*best_2g_ch = i+1;
3221*4882a593Smuzhiyun 		}
3222*4882a593Smuzhiyun 	}
3223*4882a593Smuzhiyun 	*best_5g_ch = 0;
3224*4882a593Smuzhiyun 	min_ap = 999;
3225*4882a593Smuzhiyun 	for (i=0; i<ARRAYSIZE(a_band1); i++) {
3226*4882a593Smuzhiyun 		if(a_band1[i] < min_ap && a_band1[i] >= 0) {
3227*4882a593Smuzhiyun 			min_ap = a_band1[i];
3228*4882a593Smuzhiyun 			*best_5g_ch = i*4 + 36;
3229*4882a593Smuzhiyun 		}
3230*4882a593Smuzhiyun 	}
3231*4882a593Smuzhiyun 	for (i=0; i<ARRAYSIZE(a_band4); i++) {
3232*4882a593Smuzhiyun 		if(a_band4[i] < min_ap && a_band4[i] >= 0) {
3233*4882a593Smuzhiyun 			min_ap = a_band4[i];
3234*4882a593Smuzhiyun 			*best_5g_ch = i*4 + 149;
3235*4882a593Smuzhiyun 		}
3236*4882a593Smuzhiyun 	}
3237*4882a593Smuzhiyun #ifdef WL_6G_BAND
3238*4882a593Smuzhiyun 	*best_6g_ch = 0;
3239*4882a593Smuzhiyun 	min_ap = 999;
3240*4882a593Smuzhiyun 	for (i=0; i<ARRAYSIZE(six_g_band5); i++) {
3241*4882a593Smuzhiyun 		if(six_g_band5[i] < min_ap && six_g_band5[i] >= 0) {
3242*4882a593Smuzhiyun 			min_ap = six_g_band5[i];
3243*4882a593Smuzhiyun 			*best_6g_ch = i*4 + 1;
3244*4882a593Smuzhiyun 		}
3245*4882a593Smuzhiyun 	}
3246*4882a593Smuzhiyun 	for (i=0; i<ARRAYSIZE(six_g_band6); i++) {
3247*4882a593Smuzhiyun 		if(six_g_band6[i] < min_ap && six_g_band6[i] >= 0) {
3248*4882a593Smuzhiyun 			min_ap = six_g_band6[i];
3249*4882a593Smuzhiyun 			*best_6g_ch = i*4 + 97;
3250*4882a593Smuzhiyun 		}
3251*4882a593Smuzhiyun 	}
3252*4882a593Smuzhiyun 	for (i=0; i<ARRAYSIZE(six_g_band7); i++) {
3253*4882a593Smuzhiyun 		if(six_g_band7[i] < min_ap && six_g_band7[i] >= 0) {
3254*4882a593Smuzhiyun 			min_ap = six_g_band7[i];
3255*4882a593Smuzhiyun 			*best_6g_ch = i*4 + 117;
3256*4882a593Smuzhiyun 		}
3257*4882a593Smuzhiyun 	}
3258*4882a593Smuzhiyun 	for (i=0; i<ARRAYSIZE(six_g_band8); i++) {
3259*4882a593Smuzhiyun 		if(six_g_band8[i] < min_ap && six_g_band8[i] >= 0) {
3260*4882a593Smuzhiyun 			min_ap = six_g_band8[i];
3261*4882a593Smuzhiyun 			*best_6g_ch = i*4 + 189;
3262*4882a593Smuzhiyun 		}
3263*4882a593Smuzhiyun 	}
3264*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3265*4882a593Smuzhiyun 
3266*4882a593Smuzhiyun 	if (android_msg_level & ANDROID_INFO_LEVEL) {
3267*4882a593Smuzhiyun 		struct bcmstrbuf strbuf;
3268*4882a593Smuzhiyun 		char *tmp_buf = NULL;
3269*4882a593Smuzhiyun 		tmp_buf = kmalloc(WLC_IOCTL_MEDLEN, GFP_KERNEL);
3270*4882a593Smuzhiyun 		if (tmp_buf == NULL) {
3271*4882a593Smuzhiyun 			AEXT_ERROR(net->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
3272*4882a593Smuzhiyun 			goto exit;
3273*4882a593Smuzhiyun 		}
3274*4882a593Smuzhiyun 		bcm_binit(&strbuf, tmp_buf, WLC_IOCTL_MEDLEN);
3275*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "2g: ");
3276*4882a593Smuzhiyun 		for (j=0; j<ARRAYSIZE(b_band); j++)
3277*4882a593Smuzhiyun 			bcm_bprintf(&strbuf, "%d/%d, ", b_band[j], 1+j);
3278*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "\n");
3279*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "5g band 1: ");
3280*4882a593Smuzhiyun 		for (j=0; j<ARRAYSIZE(a_band1); j++)
3281*4882a593Smuzhiyun 			bcm_bprintf(&strbuf, "%d/%d, ", a_band1[j], 36+j*4);
3282*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "\n");
3283*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "5g band 4: ");
3284*4882a593Smuzhiyun 		for (j=0; j<ARRAYSIZE(a_band4); j++)
3285*4882a593Smuzhiyun 			bcm_bprintf(&strbuf, "%d/%d, ", a_band4[j], 149+j*4);
3286*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "\n");
3287*4882a593Smuzhiyun #ifdef WL_6G_BAND
3288*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "6g band 5: ");
3289*4882a593Smuzhiyun 		for (j=0; j<ARRAYSIZE(six_g_band5); j++)
3290*4882a593Smuzhiyun 			bcm_bprintf(&strbuf, "%d/%d, ", six_g_band5[j], 1+j*4);
3291*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "\n");
3292*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "6g band 6: ");
3293*4882a593Smuzhiyun 		for (j=0; j<ARRAYSIZE(six_g_band6); j++)
3294*4882a593Smuzhiyun 			bcm_bprintf(&strbuf, "%d/%d, ", six_g_band6[j], 97+j*4);
3295*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "\n");
3296*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "6g band 7: ");
3297*4882a593Smuzhiyun 		for (j=0; j<ARRAYSIZE(six_g_band7); j++)
3298*4882a593Smuzhiyun 			bcm_bprintf(&strbuf, "%d/%d, ", six_g_band7[j], 117+j*4);
3299*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "\n");
3300*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "6g band 8: ");
3301*4882a593Smuzhiyun 		for (j=0; j<ARRAYSIZE(six_g_band8); j++)
3302*4882a593Smuzhiyun 			bcm_bprintf(&strbuf, "%d/%d, ", six_g_band8[j], 189+j*4);
3303*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "\n");
3304*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3305*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "best_2g_ch=%d, best_5g_ch=%d",
3306*4882a593Smuzhiyun 			*best_2g_ch, *best_5g_ch);
3307*4882a593Smuzhiyun #ifdef WL_6G_BAND
3308*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, ", best_6g_ch=%d", *best_6g_ch);
3309*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3310*4882a593Smuzhiyun 		bcm_bprintf(&strbuf, "\n");
3311*4882a593Smuzhiyun 		AEXT_INFO(net->name, "\n%s", strbuf.origbuf);
3312*4882a593Smuzhiyun 		if (tmp_buf) {
3313*4882a593Smuzhiyun 			kfree(tmp_buf);
3314*4882a593Smuzhiyun 		}
3315*4882a593Smuzhiyun 	}
3316*4882a593Smuzhiyun 
3317*4882a593Smuzhiyun exit:
3318*4882a593Smuzhiyun 	if (list)
3319*4882a593Smuzhiyun 		kfree(list);
3320*4882a593Smuzhiyun 	return ret;
3321*4882a593Smuzhiyun }
3322*4882a593Smuzhiyun #endif /* WL_CFG80211 || WL_ESCAN */
3323*4882a593Smuzhiyun 
3324*4882a593Smuzhiyun #ifdef WL_CFG80211
3325*4882a593Smuzhiyun #define APCS_MAX_RETRY		10
3326*4882a593Smuzhiyun static int
wl_ext_fw_apcs(struct net_device * dev,uint32 band)3327*4882a593Smuzhiyun wl_ext_fw_apcs(struct net_device *dev, uint32 band)
3328*4882a593Smuzhiyun {
3329*4882a593Smuzhiyun 	int channel = 0, chosen = 0, retry = 0, ret = 0, spect = 0;
3330*4882a593Smuzhiyun 	u8 *reqbuf = NULL;
3331*4882a593Smuzhiyun 	uint32 buf_size;
3332*4882a593Smuzhiyun 	chanspec_band_t acs_band = WLC_BAND_INVALID;
3333*4882a593Smuzhiyun 
3334*4882a593Smuzhiyun 	ret = wldev_ioctl_get(dev, WLC_GET_SPECT_MANAGMENT, &spect, sizeof(spect));
3335*4882a593Smuzhiyun 	if (ret) {
3336*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "ACS: error getting the spect, ret=%d\n", ret);
3337*4882a593Smuzhiyun 		goto done;
3338*4882a593Smuzhiyun 	}
3339*4882a593Smuzhiyun 
3340*4882a593Smuzhiyun 	if (spect > 0) {
3341*4882a593Smuzhiyun 		ret = wl_android_set_spect(dev, 0);
3342*4882a593Smuzhiyun 		if (ret < 0) {
3343*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "ACS: error while setting spect, ret=%d\n", ret);
3344*4882a593Smuzhiyun 			goto done;
3345*4882a593Smuzhiyun 		}
3346*4882a593Smuzhiyun 	}
3347*4882a593Smuzhiyun 
3348*4882a593Smuzhiyun 	reqbuf = kmalloc(CHANSPEC_BUF_SIZE, GFP_KERNEL);
3349*4882a593Smuzhiyun 	if (reqbuf == NULL) {
3350*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "failed to allocate chanspec buffer\n");
3351*4882a593Smuzhiyun 		goto done;
3352*4882a593Smuzhiyun 	}
3353*4882a593Smuzhiyun 	memset(reqbuf, 0, CHANSPEC_BUF_SIZE);
3354*4882a593Smuzhiyun 
3355*4882a593Smuzhiyun 	acs_band = wl_ext_wlcband_to_chanspec_band(band);
3356*4882a593Smuzhiyun 	if (acs_band == INVCHANSPEC) {
3357*4882a593Smuzhiyun 		acs_band = WL_CHANSPEC_BAND_2G;
3358*4882a593Smuzhiyun 	}
3359*4882a593Smuzhiyun 
3360*4882a593Smuzhiyun 	if ((ret = wl_android_get_band_chanspecs(dev, reqbuf, CHANSPEC_BUF_SIZE,
3361*4882a593Smuzhiyun 			acs_band, true)) < 0) {
3362*4882a593Smuzhiyun 		WL_ERR(("ACS chanspec retrieval failed!\n"));
3363*4882a593Smuzhiyun 		goto done;
3364*4882a593Smuzhiyun 	}
3365*4882a593Smuzhiyun 
3366*4882a593Smuzhiyun 	AEXT_INFO(dev->name, "ACS chanspec band 0x%x\n", acs_band);
3367*4882a593Smuzhiyun 
3368*4882a593Smuzhiyun 	buf_size = CHANSPEC_BUF_SIZE;
3369*4882a593Smuzhiyun 	ret = wldev_ioctl_set(dev, WLC_START_CHANNEL_SEL, (void *)reqbuf,
3370*4882a593Smuzhiyun 		buf_size);
3371*4882a593Smuzhiyun 	if (ret < 0) {
3372*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "can't start auto channel scan, err = %d\n", ret);
3373*4882a593Smuzhiyun 		channel = 0;
3374*4882a593Smuzhiyun 		goto done;
3375*4882a593Smuzhiyun 	}
3376*4882a593Smuzhiyun 
3377*4882a593Smuzhiyun 	/* Wait for auto channel selection, max 3000 ms */
3378*4882a593Smuzhiyun 	if ((band == WLC_BAND_2G) || (band == WLC_BAND_5G) || (band == WLC_BAND_6G)) {
3379*4882a593Smuzhiyun 		OSL_SLEEP(500);
3380*4882a593Smuzhiyun 	} else {
3381*4882a593Smuzhiyun 		/*
3382*4882a593Smuzhiyun 		 * Full channel scan at the minimum takes 1.2secs
3383*4882a593Smuzhiyun 		 * even with parallel scan. max wait time: 3500ms
3384*4882a593Smuzhiyun 		 */
3385*4882a593Smuzhiyun 		OSL_SLEEP(1000);
3386*4882a593Smuzhiyun 	}
3387*4882a593Smuzhiyun 
3388*4882a593Smuzhiyun 	retry = APCS_MAX_RETRY;
3389*4882a593Smuzhiyun 	while (retry--) {
3390*4882a593Smuzhiyun 		ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &chosen,
3391*4882a593Smuzhiyun 			sizeof(chosen));
3392*4882a593Smuzhiyun 		if (ret < 0) {
3393*4882a593Smuzhiyun 			chosen = 0;
3394*4882a593Smuzhiyun 		} else {
3395*4882a593Smuzhiyun 			chosen = dtoh32(chosen);
3396*4882a593Smuzhiyun 		}
3397*4882a593Smuzhiyun 
3398*4882a593Smuzhiyun 		if (wf_chspec_valid((chanspec_t)chosen)) {
3399*4882a593Smuzhiyun 			channel = wf_chspec_ctlchan((chanspec_t)chosen);
3400*4882a593Smuzhiyun 			acs_band = CHSPEC_BAND((chanspec_t)chosen);
3401*4882a593Smuzhiyun 			WL_MSG(dev->name, "selected channel = %d(band %d)\n",
3402*4882a593Smuzhiyun 				channel, CHSPEC2WLC_BAND((chanspec_t)chosen));
3403*4882a593Smuzhiyun 			break;
3404*4882a593Smuzhiyun 		}
3405*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "%d tried, ret = %d, chosen = 0x%x\n",
3406*4882a593Smuzhiyun 			(APCS_MAX_RETRY - retry), ret, chosen);
3407*4882a593Smuzhiyun 		OSL_SLEEP(250);
3408*4882a593Smuzhiyun 	}
3409*4882a593Smuzhiyun 
3410*4882a593Smuzhiyun done:
3411*4882a593Smuzhiyun 	if (spect > 0) {
3412*4882a593Smuzhiyun 		if ((ret = wl_android_set_spect(dev, spect) < 0)) {
3413*4882a593Smuzhiyun 			AEXT_ERROR(dev->name, "ACS: error while setting spect\n");
3414*4882a593Smuzhiyun 		}
3415*4882a593Smuzhiyun 	}
3416*4882a593Smuzhiyun 
3417*4882a593Smuzhiyun 	if (reqbuf) {
3418*4882a593Smuzhiyun 		kfree(reqbuf);
3419*4882a593Smuzhiyun 	}
3420*4882a593Smuzhiyun 
3421*4882a593Smuzhiyun 	return chosen;
3422*4882a593Smuzhiyun }
3423*4882a593Smuzhiyun #endif /* WL_CFG80211 */
3424*4882a593Smuzhiyun 
3425*4882a593Smuzhiyun #ifdef WL_ESCAN
3426*4882a593Smuzhiyun int
wl_ext_drv_scan(struct net_device * dev,uint32 band,bool fast_scan)3427*4882a593Smuzhiyun wl_ext_drv_scan(struct net_device *dev, uint32 band, bool fast_scan)
3428*4882a593Smuzhiyun {
3429*4882a593Smuzhiyun 	int ret = -1, i, cnt = 0;
3430*4882a593Smuzhiyun 	int retry = 0, retry_max, retry_interval = 250, up = 1;
3431*4882a593Smuzhiyun 	wl_scan_info_t *scan_info = NULL;
3432*4882a593Smuzhiyun 
3433*4882a593Smuzhiyun 	scan_info = kmalloc(sizeof(wl_scan_info_t), GFP_KERNEL);
3434*4882a593Smuzhiyun 	if (scan_info == NULL) {
3435*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "kzalloc failed\n");
3436*4882a593Smuzhiyun 		ret = -ENOMEM;
3437*4882a593Smuzhiyun 		goto exit;
3438*4882a593Smuzhiyun 	}
3439*4882a593Smuzhiyun 
3440*4882a593Smuzhiyun 	retry_max = WL_ESCAN_TIMER_INTERVAL_MS/retry_interval;
3441*4882a593Smuzhiyun 	ret = wldev_ioctl_get(dev, WLC_GET_UP, &up, sizeof(s32));
3442*4882a593Smuzhiyun 	if (ret < 0 || up == 0) {
3443*4882a593Smuzhiyun 		ret = wldev_ioctl_set(dev, WLC_UP, &up, sizeof(s32));
3444*4882a593Smuzhiyun 	}
3445*4882a593Smuzhiyun 	memset(scan_info, 0, sizeof(wl_scan_info_t));
3446*4882a593Smuzhiyun 	if (band == WLC_BAND_2G || band == WLC_BAND_AUTO) {
3447*4882a593Smuzhiyun 		for (i=0; i<13; i++) {
3448*4882a593Smuzhiyun 			scan_info->channels.channel[i+cnt] = wf_create_chspec_from_primary(i+1,
3449*4882a593Smuzhiyun 				WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_2G);
3450*4882a593Smuzhiyun 		}
3451*4882a593Smuzhiyun 		cnt += 13;
3452*4882a593Smuzhiyun 	}
3453*4882a593Smuzhiyun 	if (band == WLC_BAND_5G || band == WLC_BAND_AUTO) {
3454*4882a593Smuzhiyun 		for (i=0; i<4; i++) {
3455*4882a593Smuzhiyun 			scan_info->channels.channel[i+cnt] = wf_create_chspec_from_primary(36+i*4,
3456*4882a593Smuzhiyun 				WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_5G);
3457*4882a593Smuzhiyun 		}
3458*4882a593Smuzhiyun 		cnt += 4;
3459*4882a593Smuzhiyun 		for (i=0; i<4; i++) {
3460*4882a593Smuzhiyun 			scan_info->channels.channel[i+cnt] = wf_create_chspec_from_primary(149+i*4,
3461*4882a593Smuzhiyun 				WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_5G);
3462*4882a593Smuzhiyun 		}
3463*4882a593Smuzhiyun 		cnt += 4;
3464*4882a593Smuzhiyun 	}
3465*4882a593Smuzhiyun #ifdef WL_6G_BAND
3466*4882a593Smuzhiyun 	if (band == WLC_BAND_6G || band == WLC_BAND_AUTO) {
3467*4882a593Smuzhiyun 		for (i=0; i<59; i++) {
3468*4882a593Smuzhiyun 			scan_info->channels.channel[i+cnt] = wf_create_chspec_from_primary(1+i*4,
3469*4882a593Smuzhiyun 				WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_6G);
3470*4882a593Smuzhiyun 		}
3471*4882a593Smuzhiyun 		cnt += 59;
3472*4882a593Smuzhiyun 	}
3473*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3474*4882a593Smuzhiyun 	if (band == WLC_BAND_2G)
3475*4882a593Smuzhiyun 		fast_scan = FALSE;
3476*4882a593Smuzhiyun 	scan_info->channels.count = cnt;
3477*4882a593Smuzhiyun 	if (fast_scan)
3478*4882a593Smuzhiyun 		scan_info->scan_time = 40;
3479*4882a593Smuzhiyun 	scan_info->bcast_ssid = TRUE;
3480*4882a593Smuzhiyun 	retry = retry_max;
3481*4882a593Smuzhiyun 	while (retry--) {
3482*4882a593Smuzhiyun 		ret = wl_escan_set_scan(dev, scan_info);
3483*4882a593Smuzhiyun 		if (!ret)
3484*4882a593Smuzhiyun 			break;
3485*4882a593Smuzhiyun 		OSL_SLEEP(retry_interval);
3486*4882a593Smuzhiyun 	}
3487*4882a593Smuzhiyun 	if (retry == 0) {
3488*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "scan retry failed %d\n", retry_max);
3489*4882a593Smuzhiyun 		ret = -1;
3490*4882a593Smuzhiyun 	}
3491*4882a593Smuzhiyun 
3492*4882a593Smuzhiyun exit:
3493*4882a593Smuzhiyun 	if (scan_info)
3494*4882a593Smuzhiyun 		kfree(scan_info);
3495*4882a593Smuzhiyun 	return ret;
3496*4882a593Smuzhiyun }
3497*4882a593Smuzhiyun 
3498*4882a593Smuzhiyun static int
wl_ext_drv_apcs(struct net_device * dev,uint32 band)3499*4882a593Smuzhiyun wl_ext_drv_apcs(struct net_device *dev, uint32 band)
3500*4882a593Smuzhiyun {
3501*4882a593Smuzhiyun 	int ret = 0, chanspec = 0;
3502*4882a593Smuzhiyun 	struct dhd_pub *dhd = dhd_get_pub(dev);
3503*4882a593Smuzhiyun 	struct wl_escan_info *escan = NULL;
3504*4882a593Smuzhiyun 	int retry = 0, retry_max, retry_interval = 250;
3505*4882a593Smuzhiyun 
3506*4882a593Smuzhiyun 	escan = dhd->escan;
3507*4882a593Smuzhiyun 	WL_MSG(dev->name, "ACS_SCAN\n");
3508*4882a593Smuzhiyun 	escan->autochannel = 1;
3509*4882a593Smuzhiyun 	ret = wl_ext_drv_scan(dev, band, TRUE);
3510*4882a593Smuzhiyun 	if (ret < 0)
3511*4882a593Smuzhiyun 		goto done;
3512*4882a593Smuzhiyun 	retry_max = WL_ESCAN_TIMER_INTERVAL_MS/retry_interval;
3513*4882a593Smuzhiyun 	retry = retry_max;
3514*4882a593Smuzhiyun 	while (retry--) {
3515*4882a593Smuzhiyun 		if (escan->escan_state == ESCAN_STATE_IDLE) {
3516*4882a593Smuzhiyun 			if (band == WLC_BAND_5G) {
3517*4882a593Smuzhiyun 				chanspec = wf_create_chspec_from_primary(wf_chspec_primary20_chan(escan->best_5g_ch),
3518*4882a593Smuzhiyun 					WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_5G);
3519*4882a593Smuzhiyun 			}
3520*4882a593Smuzhiyun #ifdef WL_6G_BAND
3521*4882a593Smuzhiyun 			else if (band == WLC_BAND_6G) {
3522*4882a593Smuzhiyun 				chanspec = wf_create_chspec_from_primary(wf_chspec_primary20_chan(escan->best_6g_ch),
3523*4882a593Smuzhiyun 					WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_6G);
3524*4882a593Smuzhiyun 			}
3525*4882a593Smuzhiyun #endif /* WL_6G_BAND */
3526*4882a593Smuzhiyun 			else {
3527*4882a593Smuzhiyun 				chanspec = wf_create_chspec_from_primary(wf_chspec_primary20_chan(escan->best_2g_ch),
3528*4882a593Smuzhiyun 					WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_2G);
3529*4882a593Smuzhiyun 			}
3530*4882a593Smuzhiyun 			WL_MSG(dev->name, "selected channel = %d(0x%x)\n",
3531*4882a593Smuzhiyun 				wf_chspec_ctlchan(chanspec), chanspec);
3532*4882a593Smuzhiyun 			goto done;
3533*4882a593Smuzhiyun 		}
3534*4882a593Smuzhiyun 		AEXT_INFO(dev->name, "escan_state=%d, %d tried, ret = %d\n",
3535*4882a593Smuzhiyun 			escan->escan_state, (retry_max - retry), ret);
3536*4882a593Smuzhiyun 		OSL_SLEEP(retry_interval);
3537*4882a593Smuzhiyun 	}
3538*4882a593Smuzhiyun 
3539*4882a593Smuzhiyun done:
3540*4882a593Smuzhiyun 	escan->autochannel = 0;
3541*4882a593Smuzhiyun 
3542*4882a593Smuzhiyun 	return chanspec;
3543*4882a593Smuzhiyun }
3544*4882a593Smuzhiyun #endif /* WL_ESCAN */
3545*4882a593Smuzhiyun 
3546*4882a593Smuzhiyun int
wl_ext_autochannel(struct net_device * dev,uint acs,uint32 band)3547*4882a593Smuzhiyun wl_ext_autochannel(struct net_device *dev, uint acs, uint32 band)
3548*4882a593Smuzhiyun {
3549*4882a593Smuzhiyun 	int chosen = 0;
3550*4882a593Smuzhiyun 	uint16 chan_2g, chan_5g, channel;
3551*4882a593Smuzhiyun 
3552*4882a593Smuzhiyun 	AEXT_INFO(dev->name, "acs=0x%x, band=%s\n", acs, WLCBAND2STR(band));
3553*4882a593Smuzhiyun 
3554*4882a593Smuzhiyun #ifdef WL_CFG80211
3555*4882a593Smuzhiyun 	if (acs & ACS_FW_BIT) {
3556*4882a593Smuzhiyun 		int ret = 0;
3557*4882a593Smuzhiyun 		ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &channel, sizeof(channel));
3558*4882a593Smuzhiyun 		chosen = 0;
3559*4882a593Smuzhiyun 		if (ret != BCME_UNSUPPORTED)
3560*4882a593Smuzhiyun 			chosen = wl_ext_fw_apcs(dev, band);
3561*4882a593Smuzhiyun 		if (chosen)
3562*4882a593Smuzhiyun 			return chosen;
3563*4882a593Smuzhiyun 	}
3564*4882a593Smuzhiyun #endif
3565*4882a593Smuzhiyun 
3566*4882a593Smuzhiyun #ifdef WL_ESCAN
3567*4882a593Smuzhiyun 	if (acs & ACS_DRV_BIT)
3568*4882a593Smuzhiyun 		chosen = wl_ext_drv_apcs(dev, band);
3569*4882a593Smuzhiyun #endif /* WL_ESCAN */
3570*4882a593Smuzhiyun 
3571*4882a593Smuzhiyun 	if (chosen == 0) {
3572*4882a593Smuzhiyun 		wl_ext_get_default_chan(dev, &chan_2g, &chan_5g, TRUE);
3573*4882a593Smuzhiyun 		if (band == WLC_BAND_5G) {
3574*4882a593Smuzhiyun 			chosen = wf_create_chspec_from_primary(wf_chspec_primary20_chan(chan_5g),
3575*4882a593Smuzhiyun 				WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_5G);
3576*4882a593Smuzhiyun 			channel = chan_5g;
3577*4882a593Smuzhiyun 		} else {
3578*4882a593Smuzhiyun 			chosen = wf_create_chspec_from_primary(wf_chspec_primary20_chan(chan_2g),
3579*4882a593Smuzhiyun 				WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_2G);
3580*4882a593Smuzhiyun 			channel = chan_2g;
3581*4882a593Smuzhiyun 		}
3582*4882a593Smuzhiyun 		AEXT_ERROR(dev->name, "ACS failed. Fall back to default channel (%s-%d) \n",
3583*4882a593Smuzhiyun 			CHSPEC2BANDSTR(chosen), channel);
3584*4882a593Smuzhiyun 	}
3585*4882a593Smuzhiyun 
3586*4882a593Smuzhiyun 	return chosen;
3587*4882a593Smuzhiyun }
3588*4882a593Smuzhiyun 
3589*4882a593Smuzhiyun #if defined(RSSIAVG)
3590*4882a593Smuzhiyun void
wl_free_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl)3591*4882a593Smuzhiyun wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
3592*4882a593Smuzhiyun {
3593*4882a593Smuzhiyun 	wl_rssi_cache_t *node, *cur, **rssi_head;
3594*4882a593Smuzhiyun 	int i=0;
3595*4882a593Smuzhiyun 
3596*4882a593Smuzhiyun 	rssi_head = &rssi_cache_ctrl->m_cache_head;
3597*4882a593Smuzhiyun 	node = *rssi_head;
3598*4882a593Smuzhiyun 
3599*4882a593Smuzhiyun 	for (;node;) {
3600*4882a593Smuzhiyun 		AEXT_INFO("wlan", "Free %d with BSSID %pM\n", i, &node->BSSID);
3601*4882a593Smuzhiyun 		cur = node;
3602*4882a593Smuzhiyun 		node = cur->next;
3603*4882a593Smuzhiyun 		kfree(cur);
3604*4882a593Smuzhiyun 		i++;
3605*4882a593Smuzhiyun 	}
3606*4882a593Smuzhiyun 	*rssi_head = NULL;
3607*4882a593Smuzhiyun }
3608*4882a593Smuzhiyun 
3609*4882a593Smuzhiyun void
wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl)3610*4882a593Smuzhiyun wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
3611*4882a593Smuzhiyun {
3612*4882a593Smuzhiyun 	wl_rssi_cache_t *node, *prev, **rssi_head;
3613*4882a593Smuzhiyun 	int i = -1, tmp = 0;
3614*4882a593Smuzhiyun 	struct osl_timespec now;
3615*4882a593Smuzhiyun 
3616*4882a593Smuzhiyun 	osl_do_gettimeofday(&now);
3617*4882a593Smuzhiyun 
3618*4882a593Smuzhiyun 	rssi_head = &rssi_cache_ctrl->m_cache_head;
3619*4882a593Smuzhiyun 	node = *rssi_head;
3620*4882a593Smuzhiyun 	prev = node;
3621*4882a593Smuzhiyun 	for (;node;) {
3622*4882a593Smuzhiyun 		i++;
3623*4882a593Smuzhiyun 		if (now.tv_sec > node->tv.tv_sec) {
3624*4882a593Smuzhiyun 			if (node == *rssi_head) {
3625*4882a593Smuzhiyun 				tmp = 1;
3626*4882a593Smuzhiyun 				*rssi_head = node->next;
3627*4882a593Smuzhiyun 			} else {
3628*4882a593Smuzhiyun 				tmp = 0;
3629*4882a593Smuzhiyun 				prev->next = node->next;
3630*4882a593Smuzhiyun 			}
3631*4882a593Smuzhiyun 			AEXT_INFO("wlan", "Del %d with BSSID %pM\n", i, &node->BSSID);
3632*4882a593Smuzhiyun 			kfree(node);
3633*4882a593Smuzhiyun 			if (tmp == 1) {
3634*4882a593Smuzhiyun 				node = *rssi_head;
3635*4882a593Smuzhiyun 				prev = node;
3636*4882a593Smuzhiyun 			} else {
3637*4882a593Smuzhiyun 				node = prev->next;
3638*4882a593Smuzhiyun 			}
3639*4882a593Smuzhiyun 			continue;
3640*4882a593Smuzhiyun 		}
3641*4882a593Smuzhiyun 		prev = node;
3642*4882a593Smuzhiyun 		node = node->next;
3643*4882a593Smuzhiyun 	}
3644*4882a593Smuzhiyun }
3645*4882a593Smuzhiyun 
3646*4882a593Smuzhiyun void
wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl,u8 * bssid)3647*4882a593Smuzhiyun wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
3648*4882a593Smuzhiyun 	u8 *bssid)
3649*4882a593Smuzhiyun {
3650*4882a593Smuzhiyun 	wl_rssi_cache_t *node, *prev, **rssi_head;
3651*4882a593Smuzhiyun 	int i = -1, tmp = 0;
3652*4882a593Smuzhiyun 
3653*4882a593Smuzhiyun 	rssi_head = &rssi_cache_ctrl->m_cache_head;
3654*4882a593Smuzhiyun 	node = *rssi_head;
3655*4882a593Smuzhiyun 	prev = node;
3656*4882a593Smuzhiyun 	for (;node;) {
3657*4882a593Smuzhiyun 		i++;
3658*4882a593Smuzhiyun 		if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) {
3659*4882a593Smuzhiyun 			if (node == *rssi_head) {
3660*4882a593Smuzhiyun 				tmp = 1;
3661*4882a593Smuzhiyun 				*rssi_head = node->next;
3662*4882a593Smuzhiyun 			} else {
3663*4882a593Smuzhiyun 				tmp = 0;
3664*4882a593Smuzhiyun 				prev->next = node->next;
3665*4882a593Smuzhiyun 			}
3666*4882a593Smuzhiyun 			AEXT_INFO("wlan", "Del %d with BSSID %pM\n", i, &node->BSSID);
3667*4882a593Smuzhiyun 			kfree(node);
3668*4882a593Smuzhiyun 			if (tmp == 1) {
3669*4882a593Smuzhiyun 				node = *rssi_head;
3670*4882a593Smuzhiyun 				prev = node;
3671*4882a593Smuzhiyun 			} else {
3672*4882a593Smuzhiyun 				node = prev->next;
3673*4882a593Smuzhiyun 			}
3674*4882a593Smuzhiyun 			continue;
3675*4882a593Smuzhiyun 		}
3676*4882a593Smuzhiyun 		prev = node;
3677*4882a593Smuzhiyun 		node = node->next;
3678*4882a593Smuzhiyun 	}
3679*4882a593Smuzhiyun }
3680*4882a593Smuzhiyun 
3681*4882a593Smuzhiyun void
wl_reset_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl)3682*4882a593Smuzhiyun wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
3683*4882a593Smuzhiyun {
3684*4882a593Smuzhiyun 	wl_rssi_cache_t *node, **rssi_head;
3685*4882a593Smuzhiyun 
3686*4882a593Smuzhiyun 	rssi_head = &rssi_cache_ctrl->m_cache_head;
3687*4882a593Smuzhiyun 
3688*4882a593Smuzhiyun 	/* reset dirty */
3689*4882a593Smuzhiyun 	node = *rssi_head;
3690*4882a593Smuzhiyun 	for (;node;) {
3691*4882a593Smuzhiyun 		node->dirty += 1;
3692*4882a593Smuzhiyun 		node = node->next;
3693*4882a593Smuzhiyun 	}
3694*4882a593Smuzhiyun }
3695*4882a593Smuzhiyun 
3696*4882a593Smuzhiyun int
wl_update_connected_rssi_cache(struct net_device * net,wl_rssi_cache_ctrl_t * rssi_cache_ctrl,int * rssi_avg)3697*4882a593Smuzhiyun wl_update_connected_rssi_cache(struct net_device *net,
3698*4882a593Smuzhiyun 	wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg)
3699*4882a593Smuzhiyun {
3700*4882a593Smuzhiyun 	wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
3701*4882a593Smuzhiyun 	int j, k=0;
3702*4882a593Smuzhiyun 	int rssi, error=0;
3703*4882a593Smuzhiyun 	struct ether_addr bssid;
3704*4882a593Smuzhiyun 	struct osl_timespec now, timeout;
3705*4882a593Smuzhiyun 	scb_val_t scbval;
3706*4882a593Smuzhiyun 
3707*4882a593Smuzhiyun 	if (!g_wifi_on)
3708*4882a593Smuzhiyun 		return 0;
3709*4882a593Smuzhiyun 
3710*4882a593Smuzhiyun 	error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
3711*4882a593Smuzhiyun 	if (error == BCME_NOTASSOCIATED) {
3712*4882a593Smuzhiyun 		AEXT_INFO("wlan", "Not Associated! res:%d\n", error);
3713*4882a593Smuzhiyun 		return 0;
3714*4882a593Smuzhiyun 	}
3715*4882a593Smuzhiyun 	if (error) {
3716*4882a593Smuzhiyun 		AEXT_ERROR(net->name, "Could not get bssid (%d)\n", error);
3717*4882a593Smuzhiyun 	}
3718*4882a593Smuzhiyun 	error = wldev_get_rssi(net, &scbval);
3719*4882a593Smuzhiyun 	if (error) {
3720*4882a593Smuzhiyun 		AEXT_ERROR(net->name, "Could not get rssi (%d)\n", error);
3721*4882a593Smuzhiyun 		return error;
3722*4882a593Smuzhiyun 	}
3723*4882a593Smuzhiyun 	rssi = dtoh32(scbval.val);
3724*4882a593Smuzhiyun 
3725*4882a593Smuzhiyun 	osl_do_gettimeofday(&now);
3726*4882a593Smuzhiyun 	timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
3727*4882a593Smuzhiyun 	if (timeout.tv_sec < now.tv_sec) {
3728*4882a593Smuzhiyun 		/*
3729*4882a593Smuzhiyun 		 * Integer overflow - assume long enough timeout to be assumed
3730*4882a593Smuzhiyun 		 * to be infinite, i.e., the timeout would never happen.
3731*4882a593Smuzhiyun 		 */
3732*4882a593Smuzhiyun 		AEXT_TRACE(net->name,
3733*4882a593Smuzhiyun 			"Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
3734*4882a593Smuzhiyun 			RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
3735*4882a593Smuzhiyun 	}
3736*4882a593Smuzhiyun 
3737*4882a593Smuzhiyun 	/* update RSSI */
3738*4882a593Smuzhiyun 	rssi_head = &rssi_cache_ctrl->m_cache_head;
3739*4882a593Smuzhiyun 	node = *rssi_head;
3740*4882a593Smuzhiyun 	prev = NULL;
3741*4882a593Smuzhiyun 	for (;node;) {
3742*4882a593Smuzhiyun 		if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) {
3743*4882a593Smuzhiyun 			AEXT_INFO("wlan", "Update %d with BSSID %pM, RSSI=%d\n", k, &bssid, rssi);
3744*4882a593Smuzhiyun 			for (j=0; j<RSSIAVG_LEN-1; j++)
3745*4882a593Smuzhiyun 				node->RSSI[j] = node->RSSI[j+1];
3746*4882a593Smuzhiyun 			node->RSSI[j] = rssi;
3747*4882a593Smuzhiyun 			node->dirty = 0;
3748*4882a593Smuzhiyun 			node->tv = timeout;
3749*4882a593Smuzhiyun 			goto exit;
3750*4882a593Smuzhiyun 		}
3751*4882a593Smuzhiyun 		prev = node;
3752*4882a593Smuzhiyun 		node = node->next;
3753*4882a593Smuzhiyun 		k++;
3754*4882a593Smuzhiyun 	}
3755*4882a593Smuzhiyun 
3756*4882a593Smuzhiyun 	leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
3757*4882a593Smuzhiyun 	if (!leaf) {
3758*4882a593Smuzhiyun 		AEXT_ERROR(net->name, "Memory alloc failure %d\n", (int)sizeof(wl_rssi_cache_t));
3759*4882a593Smuzhiyun 		return 0;
3760*4882a593Smuzhiyun 	}
3761*4882a593Smuzhiyun 	AEXT_INFO(net->name, "Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n",
3762*4882a593Smuzhiyun 		k, &bssid, rssi);
3763*4882a593Smuzhiyun 
3764*4882a593Smuzhiyun 	leaf->next = NULL;
3765*4882a593Smuzhiyun 	leaf->dirty = 0;
3766*4882a593Smuzhiyun 	leaf->tv = timeout;
3767*4882a593Smuzhiyun 	memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN);
3768*4882a593Smuzhiyun 	for (j=0; j<RSSIAVG_LEN; j++)
3769*4882a593Smuzhiyun 		leaf->RSSI[j] = rssi;
3770*4882a593Smuzhiyun 
3771*4882a593Smuzhiyun 	if (!prev)
3772*4882a593Smuzhiyun 		*rssi_head = leaf;
3773*4882a593Smuzhiyun 	else
3774*4882a593Smuzhiyun 		prev->next = leaf;
3775*4882a593Smuzhiyun 
3776*4882a593Smuzhiyun exit:
3777*4882a593Smuzhiyun 	*rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid);
3778*4882a593Smuzhiyun 
3779*4882a593Smuzhiyun 	return error;
3780*4882a593Smuzhiyun }
3781*4882a593Smuzhiyun 
3782*4882a593Smuzhiyun void
wl_update_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl,wl_scan_results_v109_t * ss_list)3783*4882a593Smuzhiyun wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
3784*4882a593Smuzhiyun 	wl_scan_results_v109_t *ss_list)
3785*4882a593Smuzhiyun {
3786*4882a593Smuzhiyun 	wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
3787*4882a593Smuzhiyun 	wl_bss_info_v109_t *bi = NULL;
3788*4882a593Smuzhiyun 	int i, j, k;
3789*4882a593Smuzhiyun 	struct osl_timespec now, timeout;
3790*4882a593Smuzhiyun 
3791*4882a593Smuzhiyun 	if (!ss_list->count)
3792*4882a593Smuzhiyun 		return;
3793*4882a593Smuzhiyun 
3794*4882a593Smuzhiyun 	osl_do_gettimeofday(&now);
3795*4882a593Smuzhiyun 	timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
3796*4882a593Smuzhiyun 	if (timeout.tv_sec < now.tv_sec) {
3797*4882a593Smuzhiyun 		/*
3798*4882a593Smuzhiyun 		 * Integer overflow - assume long enough timeout to be assumed
3799*4882a593Smuzhiyun 		 * to be infinite, i.e., the timeout would never happen.
3800*4882a593Smuzhiyun 		 */
3801*4882a593Smuzhiyun 		AEXT_TRACE("wlan",
3802*4882a593Smuzhiyun 			"Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
3803*4882a593Smuzhiyun 			RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
3804*4882a593Smuzhiyun 	}
3805*4882a593Smuzhiyun 
3806*4882a593Smuzhiyun 	rssi_head = &rssi_cache_ctrl->m_cache_head;
3807*4882a593Smuzhiyun 
3808*4882a593Smuzhiyun 	/* update RSSI */
3809*4882a593Smuzhiyun 	for (i = 0; i < ss_list->count; i++) {
3810*4882a593Smuzhiyun 		node = *rssi_head;
3811*4882a593Smuzhiyun 		prev = NULL;
3812*4882a593Smuzhiyun 		k = 0;
3813*4882a593Smuzhiyun 		bi = bi ? (wl_bss_info_v109_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
3814*4882a593Smuzhiyun 		for (;node;) {
3815*4882a593Smuzhiyun 			if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
3816*4882a593Smuzhiyun 				AEXT_INFO("wlan", "Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
3817*4882a593Smuzhiyun 					k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
3818*4882a593Smuzhiyun 				for (j=0; j<RSSIAVG_LEN-1; j++)
3819*4882a593Smuzhiyun 					node->RSSI[j] = node->RSSI[j+1];
3820*4882a593Smuzhiyun 				node->RSSI[j] = dtoh16(bi->RSSI);
3821*4882a593Smuzhiyun 				node->dirty = 0;
3822*4882a593Smuzhiyun 				node->tv = timeout;
3823*4882a593Smuzhiyun 				break;
3824*4882a593Smuzhiyun 			}
3825*4882a593Smuzhiyun 			prev = node;
3826*4882a593Smuzhiyun 			node = node->next;
3827*4882a593Smuzhiyun 			k++;
3828*4882a593Smuzhiyun 		}
3829*4882a593Smuzhiyun 
3830*4882a593Smuzhiyun 		if (node)
3831*4882a593Smuzhiyun 			continue;
3832*4882a593Smuzhiyun 
3833*4882a593Smuzhiyun 		leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
3834*4882a593Smuzhiyun 		if (!leaf) {
3835*4882a593Smuzhiyun 			AEXT_ERROR("wlan", "Memory alloc failure %d\n",
3836*4882a593Smuzhiyun 				(int)sizeof(wl_rssi_cache_t));
3837*4882a593Smuzhiyun 			return;
3838*4882a593Smuzhiyun 		}
3839*4882a593Smuzhiyun 		AEXT_INFO("wlan", "Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n",
3840*4882a593Smuzhiyun 			k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
3841*4882a593Smuzhiyun 
3842*4882a593Smuzhiyun 		leaf->next = NULL;
3843*4882a593Smuzhiyun 		leaf->dirty = 0;
3844*4882a593Smuzhiyun 		leaf->tv = timeout;
3845*4882a593Smuzhiyun 		memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN);
3846*4882a593Smuzhiyun 		for (j=0; j<RSSIAVG_LEN; j++)
3847*4882a593Smuzhiyun 			leaf->RSSI[j] = dtoh16(bi->RSSI);
3848*4882a593Smuzhiyun 
3849*4882a593Smuzhiyun 		if (!prev)
3850*4882a593Smuzhiyun 			*rssi_head = leaf;
3851*4882a593Smuzhiyun 		else
3852*4882a593Smuzhiyun 			prev->next = leaf;
3853*4882a593Smuzhiyun 	}
3854*4882a593Smuzhiyun }
3855*4882a593Smuzhiyun 
3856*4882a593Smuzhiyun int16
wl_get_avg_rssi(wl_rssi_cache_ctrl_t * rssi_cache_ctrl,void * addr)3857*4882a593Smuzhiyun wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr)
3858*4882a593Smuzhiyun {
3859*4882a593Smuzhiyun 	wl_rssi_cache_t *node, **rssi_head;
3860*4882a593Smuzhiyun 	int j, rssi_sum, rssi=RSSI_MINVAL;
3861*4882a593Smuzhiyun 
3862*4882a593Smuzhiyun 	rssi_head = &rssi_cache_ctrl->m_cache_head;
3863*4882a593Smuzhiyun 
3864*4882a593Smuzhiyun 	node = *rssi_head;
3865*4882a593Smuzhiyun 	for (;node;) {
3866*4882a593Smuzhiyun 		if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) {
3867*4882a593Smuzhiyun 			rssi_sum = 0;
3868*4882a593Smuzhiyun 			rssi = 0;
3869*4882a593Smuzhiyun 			for (j=0; j<RSSIAVG_LEN; j++)
3870*4882a593Smuzhiyun 				rssi_sum += node->RSSI[RSSIAVG_LEN-j-1];
3871*4882a593Smuzhiyun 			rssi = rssi_sum / j;
3872*4882a593Smuzhiyun 			break;
3873*4882a593Smuzhiyun 		}
3874*4882a593Smuzhiyun 		node = node->next;
3875*4882a593Smuzhiyun 	}
3876*4882a593Smuzhiyun 	rssi = MIN(rssi, RSSI_MAXVAL);
3877*4882a593Smuzhiyun 	if (rssi == RSSI_MINVAL) {
3878*4882a593Smuzhiyun 		AEXT_ERROR("wlan", "BSSID %pM does not in RSSI cache\n", addr);
3879*4882a593Smuzhiyun 	}
3880*4882a593Smuzhiyun 	return (int16)rssi;
3881*4882a593Smuzhiyun }
3882*4882a593Smuzhiyun #endif /* RSSIAVG */
3883*4882a593Smuzhiyun 
3884*4882a593Smuzhiyun #if defined(RSSIOFFSET)
3885*4882a593Smuzhiyun int
wl_update_rssi_offset(struct net_device * net,int rssi)3886*4882a593Smuzhiyun wl_update_rssi_offset(struct net_device *net, int rssi)
3887*4882a593Smuzhiyun {
3888*4882a593Smuzhiyun #if defined(RSSIOFFSET_NEW)
3889*4882a593Smuzhiyun 	int j;
3890*4882a593Smuzhiyun #endif /* RSSIOFFSET_NEW */
3891*4882a593Smuzhiyun 
3892*4882a593Smuzhiyun 	if (!g_wifi_on)
3893*4882a593Smuzhiyun 		return rssi;
3894*4882a593Smuzhiyun 
3895*4882a593Smuzhiyun #if defined(RSSIOFFSET_NEW)
3896*4882a593Smuzhiyun 	for (j=0; j<RSSI_OFFSET; j++) {
3897*4882a593Smuzhiyun 		if (rssi - (RSSI_OFFSET_MINVAL+RSSI_OFFSET_INTVAL*(j+1)) < 0)
3898*4882a593Smuzhiyun 			break;
3899*4882a593Smuzhiyun 	}
3900*4882a593Smuzhiyun 	rssi += j;
3901*4882a593Smuzhiyun #else
3902*4882a593Smuzhiyun 	rssi += RSSI_OFFSET;
3903*4882a593Smuzhiyun #endif /* RSSIOFFSET_NEW */
3904*4882a593Smuzhiyun 	return MIN(rssi, RSSI_MAXVAL);
3905*4882a593Smuzhiyun }
3906*4882a593Smuzhiyun #endif /* RSSIOFFSET */
3907*4882a593Smuzhiyun 
3908*4882a593Smuzhiyun #if defined(BSSCACHE)
3909*4882a593Smuzhiyun void
wl_free_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl)3910*4882a593Smuzhiyun wl_free_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
3911*4882a593Smuzhiyun {
3912*4882a593Smuzhiyun 	wl_bss_cache_t *node, *cur, **bss_head;
3913*4882a593Smuzhiyun 	int i=0;
3914*4882a593Smuzhiyun 
3915*4882a593Smuzhiyun 	AEXT_TRACE("wlan", "called\n");
3916*4882a593Smuzhiyun 
3917*4882a593Smuzhiyun 	bss_head = &bss_cache_ctrl->m_cache_head;
3918*4882a593Smuzhiyun 	node = *bss_head;
3919*4882a593Smuzhiyun 
3920*4882a593Smuzhiyun 	for (;node;) {
3921*4882a593Smuzhiyun 		AEXT_TRACE("wlan", "Free %d with BSSID %pM\n",
3922*4882a593Smuzhiyun 			i, &node->results.bss_info->BSSID);
3923*4882a593Smuzhiyun 		cur = node;
3924*4882a593Smuzhiyun 		node = cur->next;
3925*4882a593Smuzhiyun 		kfree(cur);
3926*4882a593Smuzhiyun 		i++;
3927*4882a593Smuzhiyun 	}
3928*4882a593Smuzhiyun 	*bss_head = NULL;
3929*4882a593Smuzhiyun }
3930*4882a593Smuzhiyun 
3931*4882a593Smuzhiyun void
wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl)3932*4882a593Smuzhiyun wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
3933*4882a593Smuzhiyun {
3934*4882a593Smuzhiyun 	wl_bss_cache_t *node, *prev, **bss_head;
3935*4882a593Smuzhiyun 	int i = -1, tmp = 0;
3936*4882a593Smuzhiyun 	struct osl_timespec now;
3937*4882a593Smuzhiyun 
3938*4882a593Smuzhiyun 	osl_do_gettimeofday(&now);
3939*4882a593Smuzhiyun 
3940*4882a593Smuzhiyun 	bss_head = &bss_cache_ctrl->m_cache_head;
3941*4882a593Smuzhiyun 	node = *bss_head;
3942*4882a593Smuzhiyun 	prev = node;
3943*4882a593Smuzhiyun 	for (;node;) {
3944*4882a593Smuzhiyun 		i++;
3945*4882a593Smuzhiyun 		if (now.tv_sec > node->tv.tv_sec || node->dirty > BSSCACHE_DIRTY) {
3946*4882a593Smuzhiyun 			if (node == *bss_head) {
3947*4882a593Smuzhiyun 				tmp = 1;
3948*4882a593Smuzhiyun 				*bss_head = node->next;
3949*4882a593Smuzhiyun 			} else {
3950*4882a593Smuzhiyun 				tmp = 0;
3951*4882a593Smuzhiyun 				prev->next = node->next;
3952*4882a593Smuzhiyun 			}
3953*4882a593Smuzhiyun 			AEXT_TRACE("wlan", "Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
3954*4882a593Smuzhiyun 				i, &node->results.bss_info->BSSID,
3955*4882a593Smuzhiyun 				dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID);
3956*4882a593Smuzhiyun 			kfree(node);
3957*4882a593Smuzhiyun 			if (tmp == 1) {
3958*4882a593Smuzhiyun 				node = *bss_head;
3959*4882a593Smuzhiyun 				prev = node;
3960*4882a593Smuzhiyun 			} else {
3961*4882a593Smuzhiyun 				node = prev->next;
3962*4882a593Smuzhiyun 			}
3963*4882a593Smuzhiyun 			continue;
3964*4882a593Smuzhiyun 		}
3965*4882a593Smuzhiyun 		prev = node;
3966*4882a593Smuzhiyun 		node = node->next;
3967*4882a593Smuzhiyun 	}
3968*4882a593Smuzhiyun }
3969*4882a593Smuzhiyun 
3970*4882a593Smuzhiyun void
wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl,u8 * bssid)3971*4882a593Smuzhiyun wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
3972*4882a593Smuzhiyun 	u8 *bssid)
3973*4882a593Smuzhiyun {
3974*4882a593Smuzhiyun 	wl_bss_cache_t *node, *prev, **bss_head;
3975*4882a593Smuzhiyun 	int i = -1, tmp = 0;
3976*4882a593Smuzhiyun 
3977*4882a593Smuzhiyun 	bss_head = &bss_cache_ctrl->m_cache_head;
3978*4882a593Smuzhiyun 	node = *bss_head;
3979*4882a593Smuzhiyun 	prev = node;
3980*4882a593Smuzhiyun 	for (;node;) {
3981*4882a593Smuzhiyun 		i++;
3982*4882a593Smuzhiyun 		if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) {
3983*4882a593Smuzhiyun 			if (node == *bss_head) {
3984*4882a593Smuzhiyun 				tmp = 1;
3985*4882a593Smuzhiyun 				*bss_head = node->next;
3986*4882a593Smuzhiyun 			} else {
3987*4882a593Smuzhiyun 				tmp = 0;
3988*4882a593Smuzhiyun 				prev->next = node->next;
3989*4882a593Smuzhiyun 			}
3990*4882a593Smuzhiyun 			AEXT_TRACE("wlan", "Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
3991*4882a593Smuzhiyun 				i, &node->results.bss_info->BSSID,
3992*4882a593Smuzhiyun 				dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID);
3993*4882a593Smuzhiyun 			kfree(node);
3994*4882a593Smuzhiyun 			if (tmp == 1) {
3995*4882a593Smuzhiyun 				node = *bss_head;
3996*4882a593Smuzhiyun 				prev = node;
3997*4882a593Smuzhiyun 			} else {
3998*4882a593Smuzhiyun 				node = prev->next;
3999*4882a593Smuzhiyun 			}
4000*4882a593Smuzhiyun 			continue;
4001*4882a593Smuzhiyun 		}
4002*4882a593Smuzhiyun 		prev = node;
4003*4882a593Smuzhiyun 		node = node->next;
4004*4882a593Smuzhiyun 	}
4005*4882a593Smuzhiyun }
4006*4882a593Smuzhiyun 
4007*4882a593Smuzhiyun int
wl_bss_cache_size(wl_bss_cache_ctrl_t * bss_cache_ctrl)4008*4882a593Smuzhiyun wl_bss_cache_size(wl_bss_cache_ctrl_t *bss_cache_ctrl)
4009*4882a593Smuzhiyun {
4010*4882a593Smuzhiyun 	wl_bss_cache_t *node, **bss_head;
4011*4882a593Smuzhiyun 	int bss_num = 0;
4012*4882a593Smuzhiyun 
4013*4882a593Smuzhiyun 	bss_head = &bss_cache_ctrl->m_cache_head;
4014*4882a593Smuzhiyun 
4015*4882a593Smuzhiyun 	node = *bss_head;
4016*4882a593Smuzhiyun 	for (;node;) {
4017*4882a593Smuzhiyun 		if (node->dirty > 1) {
4018*4882a593Smuzhiyun 			bss_num++;
4019*4882a593Smuzhiyun 		}
4020*4882a593Smuzhiyun 		node = node->next;
4021*4882a593Smuzhiyun 	}
4022*4882a593Smuzhiyun 	return bss_num;
4023*4882a593Smuzhiyun }
4024*4882a593Smuzhiyun 
4025*4882a593Smuzhiyun void
wl_reset_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl)4026*4882a593Smuzhiyun wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
4027*4882a593Smuzhiyun {
4028*4882a593Smuzhiyun 	wl_bss_cache_t *node, **bss_head;
4029*4882a593Smuzhiyun 
4030*4882a593Smuzhiyun 	bss_head = &bss_cache_ctrl->m_cache_head;
4031*4882a593Smuzhiyun 
4032*4882a593Smuzhiyun 	/* reset dirty */
4033*4882a593Smuzhiyun 	node = *bss_head;
4034*4882a593Smuzhiyun 	for (;node;) {
4035*4882a593Smuzhiyun 		node->dirty += 1;
4036*4882a593Smuzhiyun 		node = node->next;
4037*4882a593Smuzhiyun 	}
4038*4882a593Smuzhiyun }
4039*4882a593Smuzhiyun 
4040*4882a593Smuzhiyun static void
wl_bss_cache_dump(wl_rssi_cache_ctrl_t * rssi_cache_ctrl,wl_bss_cache_t * node)4041*4882a593Smuzhiyun wl_bss_cache_dump(
4042*4882a593Smuzhiyun #if defined(RSSIAVG)
4043*4882a593Smuzhiyun 	wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
4044*4882a593Smuzhiyun #endif /* RSSIAVG */
4045*4882a593Smuzhiyun 	wl_bss_cache_t *node)
4046*4882a593Smuzhiyun {
4047*4882a593Smuzhiyun 	int k = 0;
4048*4882a593Smuzhiyun 	int16 rssi;
4049*4882a593Smuzhiyun 
4050*4882a593Smuzhiyun 	for (;node;) {
4051*4882a593Smuzhiyun #if defined(RSSIAVG)
4052*4882a593Smuzhiyun 		rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
4053*4882a593Smuzhiyun #else
4054*4882a593Smuzhiyun 		rssi = dtoh16(node->results.bss_info->RSSI);
4055*4882a593Smuzhiyun #endif /* RSSIAVG */
4056*4882a593Smuzhiyun 		k++;
4057*4882a593Smuzhiyun 		AEXT_TRACE("wlan", "dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
4058*4882a593Smuzhiyun 			k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID);
4059*4882a593Smuzhiyun 		node = node->next;
4060*4882a593Smuzhiyun 	}
4061*4882a593Smuzhiyun }
4062*4882a593Smuzhiyun 
4063*4882a593Smuzhiyun #if defined(SORT_BSS_CHANNEL)
4064*4882a593Smuzhiyun static wl_bss_cache_t *
wl_bss_cache_sort_channel(wl_bss_cache_t ** bss_head,wl_bss_cache_t * leaf)4065*4882a593Smuzhiyun wl_bss_cache_sort_channel(wl_bss_cache_t **bss_head, wl_bss_cache_t *leaf)
4066*4882a593Smuzhiyun {
4067*4882a593Smuzhiyun 	wl_bss_cache_t *node, *prev;
4068*4882a593Smuzhiyun 	uint16 channel, channel_node;
4069*4882a593Smuzhiyun 
4070*4882a593Smuzhiyun 	node = *bss_head;
4071*4882a593Smuzhiyun 	channel = wf_chspec_ctlchan(leaf->results.bss_info->chanspec);
4072*4882a593Smuzhiyun 	for (;node;) {
4073*4882a593Smuzhiyun 		channel_node = wf_chspec_ctlchan(node->results.bss_info->chanspec);
4074*4882a593Smuzhiyun 		if (channel_node > channel) {
4075*4882a593Smuzhiyun 			leaf->next = node;
4076*4882a593Smuzhiyun 			if (node == *bss_head)
4077*4882a593Smuzhiyun 				*bss_head = leaf;
4078*4882a593Smuzhiyun 			else
4079*4882a593Smuzhiyun 				prev->next = leaf;
4080*4882a593Smuzhiyun 			break;
4081*4882a593Smuzhiyun 		}
4082*4882a593Smuzhiyun 		prev = node;
4083*4882a593Smuzhiyun 		node = node->next;
4084*4882a593Smuzhiyun 	}
4085*4882a593Smuzhiyun 	if (node == NULL)
4086*4882a593Smuzhiyun 		prev->next = leaf;
4087*4882a593Smuzhiyun 
4088*4882a593Smuzhiyun 	return *bss_head;
4089*4882a593Smuzhiyun }
4090*4882a593Smuzhiyun #endif /* SORT_BSS_CHANNEL */
4091*4882a593Smuzhiyun 
4092*4882a593Smuzhiyun #if defined(SORT_BSS_RSSI)
4093*4882a593Smuzhiyun static wl_bss_cache_t *
wl_bss_cache_sort_rssi(wl_bss_cache_t ** bss_head,wl_bss_cache_t * leaf,wl_rssi_cache_ctrl_t * rssi_cache_ctrl)4094*4882a593Smuzhiyun wl_bss_cache_sort_rssi(wl_bss_cache_t **bss_head, wl_bss_cache_t *leaf
4095*4882a593Smuzhiyun #if defined(RSSIAVG)
4096*4882a593Smuzhiyun , wl_rssi_cache_ctrl_t *rssi_cache_ctrl
4097*4882a593Smuzhiyun #endif /* RSSIAVG */
4098*4882a593Smuzhiyun )
4099*4882a593Smuzhiyun {
4100*4882a593Smuzhiyun 	wl_bss_cache_t *node, *prev;
4101*4882a593Smuzhiyun 	int16 rssi, rssi_node;
4102*4882a593Smuzhiyun 
4103*4882a593Smuzhiyun 	node = *bss_head;
4104*4882a593Smuzhiyun #if defined(RSSIAVG)
4105*4882a593Smuzhiyun 	rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID);
4106*4882a593Smuzhiyun #else
4107*4882a593Smuzhiyun 	rssi = dtoh16(leaf->results.bss_info->RSSI);
4108*4882a593Smuzhiyun #endif /* RSSIAVG */
4109*4882a593Smuzhiyun 	for (;node;) {
4110*4882a593Smuzhiyun #if defined(RSSIAVG)
4111*4882a593Smuzhiyun 		rssi_node = wl_get_avg_rssi(rssi_cache_ctrl,
4112*4882a593Smuzhiyun 			&node->results.bss_info->BSSID);
4113*4882a593Smuzhiyun #else
4114*4882a593Smuzhiyun 		rssi_node = dtoh16(node->results.bss_info->RSSI);
4115*4882a593Smuzhiyun #endif /* RSSIAVG */
4116*4882a593Smuzhiyun 		if (rssi > rssi_node) {
4117*4882a593Smuzhiyun 			leaf->next = node;
4118*4882a593Smuzhiyun 			if (node == *bss_head)
4119*4882a593Smuzhiyun 				*bss_head = leaf;
4120*4882a593Smuzhiyun 			else
4121*4882a593Smuzhiyun 				prev->next = leaf;
4122*4882a593Smuzhiyun 			break;
4123*4882a593Smuzhiyun 		}
4124*4882a593Smuzhiyun 		prev = node;
4125*4882a593Smuzhiyun 		node = node->next;
4126*4882a593Smuzhiyun 	}
4127*4882a593Smuzhiyun 	if (node == NULL)
4128*4882a593Smuzhiyun 		prev->next = leaf;
4129*4882a593Smuzhiyun 
4130*4882a593Smuzhiyun 	return *bss_head;
4131*4882a593Smuzhiyun }
4132*4882a593Smuzhiyun #endif /* SORT_BSS_BY_RSSI */
4133*4882a593Smuzhiyun 
4134*4882a593Smuzhiyun void
wl_update_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl,wl_rssi_cache_ctrl_t * rssi_cache_ctrl,wl_scan_results_v109_t * ss_list)4135*4882a593Smuzhiyun wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
4136*4882a593Smuzhiyun #if defined(RSSIAVG)
4137*4882a593Smuzhiyun 	wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
4138*4882a593Smuzhiyun #endif /* RSSIAVG */
4139*4882a593Smuzhiyun 	wl_scan_results_v109_t *ss_list)
4140*4882a593Smuzhiyun {
4141*4882a593Smuzhiyun 	wl_bss_cache_t *node, *node_target = NULL, *prev, *leaf, **bss_head;
4142*4882a593Smuzhiyun 	wl_bss_cache_t *node_rssi_prev = NULL, *node_rssi = NULL;
4143*4882a593Smuzhiyun 	wl_bss_info_v109_t *bi = NULL;
4144*4882a593Smuzhiyun 	int i, k=0, bss_num = 0;
4145*4882a593Smuzhiyun 	struct osl_timespec now, timeout;
4146*4882a593Smuzhiyun 	int16 rssi_min;
4147*4882a593Smuzhiyun 	bool rssi_replace = FALSE;
4148*4882a593Smuzhiyun 
4149*4882a593Smuzhiyun 	if (!ss_list->count)
4150*4882a593Smuzhiyun 		return;
4151*4882a593Smuzhiyun 
4152*4882a593Smuzhiyun 	osl_do_gettimeofday(&now);
4153*4882a593Smuzhiyun 	timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT;
4154*4882a593Smuzhiyun 	if (timeout.tv_sec < now.tv_sec) {
4155*4882a593Smuzhiyun 		/*
4156*4882a593Smuzhiyun 		 * Integer overflow - assume long enough timeout to be assumed
4157*4882a593Smuzhiyun 		 * to be infinite, i.e., the timeout would never happen.
4158*4882a593Smuzhiyun 		 */
4159*4882a593Smuzhiyun 		AEXT_TRACE("wlan",
4160*4882a593Smuzhiyun 			"Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
4161*4882a593Smuzhiyun 			BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
4162*4882a593Smuzhiyun 	}
4163*4882a593Smuzhiyun 
4164*4882a593Smuzhiyun 	bss_head = &bss_cache_ctrl->m_cache_head;
4165*4882a593Smuzhiyun 
4166*4882a593Smuzhiyun 	// get the num of bss cache
4167*4882a593Smuzhiyun 	node = *bss_head;
4168*4882a593Smuzhiyun 	for (;node;) {
4169*4882a593Smuzhiyun 		node = node->next;
4170*4882a593Smuzhiyun 		bss_num++;
4171*4882a593Smuzhiyun 	}
4172*4882a593Smuzhiyun 
4173*4882a593Smuzhiyun 	for (i=0; i < ss_list->count; i++) {
4174*4882a593Smuzhiyun 		node = *bss_head;
4175*4882a593Smuzhiyun 		prev = NULL;
4176*4882a593Smuzhiyun 		node_target = NULL;
4177*4882a593Smuzhiyun 		node_rssi_prev = NULL;
4178*4882a593Smuzhiyun 		bi = bi ? (wl_bss_info_v109_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
4179*4882a593Smuzhiyun 
4180*4882a593Smuzhiyun 		// find the bss with same BSSID
4181*4882a593Smuzhiyun 		for (;node;) {
4182*4882a593Smuzhiyun 			if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
4183*4882a593Smuzhiyun 				if (node == *bss_head)
4184*4882a593Smuzhiyun 					*bss_head = node->next;
4185*4882a593Smuzhiyun 				else {
4186*4882a593Smuzhiyun 					prev->next = node->next;
4187*4882a593Smuzhiyun 				}
4188*4882a593Smuzhiyun 				break;
4189*4882a593Smuzhiyun 			}
4190*4882a593Smuzhiyun 			prev = node;
4191*4882a593Smuzhiyun 			node = node->next;
4192*4882a593Smuzhiyun 		}
4193*4882a593Smuzhiyun 		if (node)
4194*4882a593Smuzhiyun 			node_target = node;
4195*4882a593Smuzhiyun 
4196*4882a593Smuzhiyun 		// find the bss with lowest RSSI
4197*4882a593Smuzhiyun 		if (!node_target && bss_num >= BSSCACHE_MAXCNT) {
4198*4882a593Smuzhiyun 			node = *bss_head;
4199*4882a593Smuzhiyun 			prev = NULL;
4200*4882a593Smuzhiyun 			rssi_min = dtoh16(bi->RSSI);
4201*4882a593Smuzhiyun 			for (;node;) {
4202*4882a593Smuzhiyun 				if (dtoh16(node->results.bss_info->RSSI) < rssi_min) {
4203*4882a593Smuzhiyun 					node_rssi = node;
4204*4882a593Smuzhiyun 					node_rssi_prev = prev;
4205*4882a593Smuzhiyun 					rssi_min = dtoh16(node->results.bss_info->RSSI);
4206*4882a593Smuzhiyun 				}
4207*4882a593Smuzhiyun 				prev = node;
4208*4882a593Smuzhiyun 				node = node->next;
4209*4882a593Smuzhiyun 			}
4210*4882a593Smuzhiyun 			if (dtoh16(bi->RSSI) > rssi_min) {
4211*4882a593Smuzhiyun 				rssi_replace = TRUE;
4212*4882a593Smuzhiyun 				node_target = node_rssi;
4213*4882a593Smuzhiyun 				if (node_rssi == *bss_head)
4214*4882a593Smuzhiyun 					*bss_head = node_rssi->next;
4215*4882a593Smuzhiyun 				else if (node_rssi) {
4216*4882a593Smuzhiyun 					node_rssi_prev->next = node_rssi->next;
4217*4882a593Smuzhiyun 				}
4218*4882a593Smuzhiyun 			}
4219*4882a593Smuzhiyun 		}
4220*4882a593Smuzhiyun 
4221*4882a593Smuzhiyun 		k++;
4222*4882a593Smuzhiyun 		if (bss_num < BSSCACHE_MAXCNT) {
4223*4882a593Smuzhiyun 			bss_num++;
4224*4882a593Smuzhiyun 			AEXT_TRACE("wlan",
4225*4882a593Smuzhiyun 				"Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
4226*4882a593Smuzhiyun 				k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
4227*4882a593Smuzhiyun 		} else if (node_target) {
4228*4882a593Smuzhiyun 			if (rssi_replace) {
4229*4882a593Smuzhiyun 				AEXT_TRACE("wlan",
4230*4882a593Smuzhiyun 					"Replace %d with cached BSSID %pM(%3d) => %pM(%3d), "\
4231*4882a593Smuzhiyun 					"SSID \"%s\" => \"%s\"\n",
4232*4882a593Smuzhiyun 					k, &node_target->results.bss_info->BSSID,
4233*4882a593Smuzhiyun 					dtoh16(node_target->results.bss_info->RSSI),
4234*4882a593Smuzhiyun 					&bi->BSSID, dtoh16(bi->RSSI),
4235*4882a593Smuzhiyun 					node_target->results.bss_info->SSID, bi->SSID);
4236*4882a593Smuzhiyun 			} else {
4237*4882a593Smuzhiyun 				AEXT_TRACE("wlan",
4238*4882a593Smuzhiyun 					"Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
4239*4882a593Smuzhiyun 					k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
4240*4882a593Smuzhiyun 			}
4241*4882a593Smuzhiyun 			kfree(node_target);
4242*4882a593Smuzhiyun 			node_target = NULL;
4243*4882a593Smuzhiyun 		} else {
4244*4882a593Smuzhiyun 			AEXT_TRACE("wlan", "Skip %d BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
4245*4882a593Smuzhiyun 				k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
4246*4882a593Smuzhiyun 			continue;
4247*4882a593Smuzhiyun 		}
4248*4882a593Smuzhiyun 
4249*4882a593Smuzhiyun 		leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL);
4250*4882a593Smuzhiyun 		if (!leaf) {
4251*4882a593Smuzhiyun 			AEXT_ERROR("wlan", "Memory alloc failure %d\n",
4252*4882a593Smuzhiyun 				dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t));
4253*4882a593Smuzhiyun 			return;
4254*4882a593Smuzhiyun 		}
4255*4882a593Smuzhiyun 
4256*4882a593Smuzhiyun 		memcpy(leaf->results.bss_info, bi, dtoh32(bi->length));
4257*4882a593Smuzhiyun 		leaf->next = NULL;
4258*4882a593Smuzhiyun 		leaf->dirty = 0;
4259*4882a593Smuzhiyun 		leaf->tv = timeout;
4260*4882a593Smuzhiyun 		leaf->results.count = 1;
4261*4882a593Smuzhiyun 		leaf->results.version = ss_list->version;
4262*4882a593Smuzhiyun 
4263*4882a593Smuzhiyun 		if (*bss_head == NULL)
4264*4882a593Smuzhiyun 			*bss_head = leaf;
4265*4882a593Smuzhiyun 		else {
4266*4882a593Smuzhiyun #if defined(SORT_BSS_CHANNEL)
4267*4882a593Smuzhiyun 			*bss_head = wl_bss_cache_sort_channel(bss_head, leaf);
4268*4882a593Smuzhiyun #elif defined(SORT_BSS_RSSI)
4269*4882a593Smuzhiyun 			*bss_head = wl_bss_cache_sort_rssi(bss_head, leaf
4270*4882a593Smuzhiyun #if defined(RSSIAVG)
4271*4882a593Smuzhiyun 				, rssi_cache_ctrl
4272*4882a593Smuzhiyun #endif /* RSSIAVG */
4273*4882a593Smuzhiyun 				);
4274*4882a593Smuzhiyun #else
4275*4882a593Smuzhiyun 			leaf->next = *bss_head;
4276*4882a593Smuzhiyun 			*bss_head = leaf;
4277*4882a593Smuzhiyun #endif /* SORT_BSS_BY_RSSI */
4278*4882a593Smuzhiyun 		}
4279*4882a593Smuzhiyun 	}
4280*4882a593Smuzhiyun 	wl_bss_cache_dump(
4281*4882a593Smuzhiyun #if defined(RSSIAVG)
4282*4882a593Smuzhiyun 		rssi_cache_ctrl,
4283*4882a593Smuzhiyun #endif /* RSSIAVG */
4284*4882a593Smuzhiyun 		*bss_head);
4285*4882a593Smuzhiyun }
4286*4882a593Smuzhiyun 
4287*4882a593Smuzhiyun void
wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t * bss_cache_ctrl)4288*4882a593Smuzhiyun wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl)
4289*4882a593Smuzhiyun {
4290*4882a593Smuzhiyun 	AEXT_TRACE("wlan", "Enter\n");
4291*4882a593Smuzhiyun 	wl_free_bss_cache(bss_cache_ctrl);
4292*4882a593Smuzhiyun }
4293*4882a593Smuzhiyun #endif /* BSSCACHE */
4294