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