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