xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/wl_roam.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Linux roam cache
3  *
4  * Copyright (C) 2020, Broadcom.
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *
21  * <<Broadcom-WL-IPTag/Dual:>>
22  */
23 
24 #include <typedefs.h>
25 #include <osl.h>
26 #include <bcmwifi_channels.h>
27 #include <wlioctl.h>
28 #include <bcmutils.h>
29 #ifdef WL_CFG80211
30 #include <wl_cfg80211.h>
31 #endif
32 #include <wldev_common.h>
33 #if defined(__linux__)
34 #include <bcmstdlib_s.h>
35 #endif /* defined(__linux__) */
36 
37 #ifdef ESCAN_CHANNEL_CACHE
38 #define MAX_ROAM_CACHE		200
39 #define MAX_SSID_BUFSIZE	36
40 
41 typedef struct {
42 	chanspec_t chanspec;
43 	int ssid_len;
44 	char ssid[MAX_SSID_BUFSIZE];
45 } roam_channel_cache;
46 
47 static int n_roam_cache = 0;
48 static int roam_band = WLC_BAND_AUTO;
49 static roam_channel_cache roam_cache[MAX_ROAM_CACHE];
50 static uint band_bw;
51 
add_roamcache_channel(wl_roam_channel_list_t * channels,chanspec_t ch)52 static void add_roamcache_channel(wl_roam_channel_list_t *channels, chanspec_t ch)
53 {
54 	int i;
55 
56 	if (channels->n >= MAX_ROAM_CHANNEL) /* buffer full */
57 		return;
58 
59 	for (i = 0; i < channels->n; i++) {
60 		if (channels->channels[i] == ch) /* already in the list */
61 			return;
62 	}
63 
64 	channels->channels[i] = ch;
65 	channels->n++;
66 
67 	WL_DBG((" RCC: %02d 0x%04X\n",
68 		ch & WL_CHANSPEC_CHAN_MASK, ch));
69 }
70 
update_roam_cache(struct bcm_cfg80211 * cfg,int ioctl_ver)71 void update_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
72 {
73 	int error, i, prev_channels;
74 	wl_roam_channel_list_t channel_list;
75 	char iobuf[WLC_IOCTL_SMLEN];
76 	struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
77 	wlc_ssid_t ssid;
78 
79 	if (!cfg->rcc_enabled) {
80 		return;
81 	}
82 
83 #ifdef WES_SUPPORT
84 	if (cfg->roamscan_mode == ROAMSCAN_MODE_WES) {
85 		/* no update when ROAMSCAN_MODE_WES */
86 		return;
87 	}
88 #endif /* WES_SUPPORT */
89 
90 	if (!wl_get_drv_status(cfg, CONNECTED, dev)) {
91 		WL_DBG(("Not associated\n"));
92 		return;
93 	}
94 
95 	/* need to read out the current cache list
96 	   as the firmware may change dynamically
97 	*/
98 	error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
99 		(void *)&channel_list, sizeof(channel_list), NULL);
100 	if (error) {
101 		WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
102 		return;
103 	}
104 
105 	error = wldev_get_ssid(dev, &ssid);
106 	if (error) {
107 		WL_ERR(("Failed to get SSID, err=%d\n", error));
108 		return;
109 	}
110 
111 	prev_channels = channel_list.n;
112 	for (i = 0; i < n_roam_cache; i++) {
113 		chanspec_t ch = roam_cache[i].chanspec;
114 		bool band_match = ((roam_band == WLC_BAND_AUTO) ||
115 #ifdef WL_6G_BAND
116 			((roam_band == WLC_BAND_6G) && (CHSPEC_IS6G(ch))) ||
117 #endif /* WL_6G_BAND */
118 			((roam_band == WLC_BAND_2G) && (CHSPEC_IS2G(ch))) ||
119 			((roam_band == WLC_BAND_5G) && (CHSPEC_IS5G(ch))));
120 
121 		if ((roam_cache[i].ssid_len == ssid.SSID_len) &&
122 			band_match && (memcmp(roam_cache[i].ssid, ssid.SSID, ssid.SSID_len) == 0)) {
123 			/* match found, add it */
124 			ch = wf_chspec_ctlchan(ch) | CHSPEC_BAND(ch) | band_bw;
125 			add_roamcache_channel(&channel_list, ch);
126 		}
127 	}
128 	if (prev_channels != channel_list.n) {
129 		/* channel list updated */
130 		error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
131 			sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
132 		if (error) {
133 			WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
134 		}
135 	}
136 
137 	WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache, channel_list.n, error));
138 }
139 
set_roam_band(int band)140 void set_roam_band(int band)
141 {
142 	roam_band = band;
143 }
144 
reset_roam_cache(struct bcm_cfg80211 * cfg)145 void reset_roam_cache(struct bcm_cfg80211 *cfg)
146 {
147 	if (!cfg->rcc_enabled) {
148 		return;
149 	}
150 
151 #ifdef WES_SUPPORT
152 	if (cfg->roamscan_mode == ROAMSCAN_MODE_WES)
153 		return;
154 #endif /* WES_SUPPORT */
155 
156 	n_roam_cache = 0;
157 }
158 
159 static void
add_roam_cache_list(uint8 * SSID,uint32 SSID_len,chanspec_t chanspec)160 add_roam_cache_list(uint8 *SSID, uint32 SSID_len, chanspec_t chanspec)
161 {
162 	int i;
163 	uint8 channel;
164 	char chanbuf[CHANSPEC_STR_LEN];
165 
166 	if (n_roam_cache >= MAX_ROAM_CACHE) {
167 		return;
168 	}
169 
170 	for (i = 0; i < n_roam_cache; i++) {
171 		if ((roam_cache[i].ssid_len == SSID_len) &&
172 			(roam_cache[i].chanspec == chanspec) &&
173 			(memcmp(roam_cache[i].ssid, SSID, SSID_len) == 0)) {
174 			/* identical one found, just return */
175 			return;
176 		}
177 	}
178 
179 	roam_cache[n_roam_cache].ssid_len = SSID_len;
180 	channel = wf_chspec_ctlchan(chanspec);
181 	WL_DBG(("CHSPEC  = %s, CTL %d SSID %s\n",
182 		wf_chspec_ntoa_ex(chanspec, chanbuf), channel, SSID));
183 	roam_cache[n_roam_cache].chanspec = CHSPEC_BAND(chanspec) | band_bw | channel;
184 	(void)memcpy_s(roam_cache[n_roam_cache].ssid, SSID_len, SSID, SSID_len);
185 
186 	n_roam_cache++;
187 }
188 
189 void
add_roam_cache(struct bcm_cfg80211 * cfg,wl_bss_info_t * bi)190 add_roam_cache(struct bcm_cfg80211 *cfg, wl_bss_info_t *bi)
191 {
192 	if (!cfg->rcc_enabled) {
193 		return;
194 	}
195 
196 #ifdef WES_SUPPORT
197 	if (cfg->roamscan_mode == ROAMSCAN_MODE_WES) {
198 		return;
199 	}
200 #endif /* WES_SUPPORT */
201 
202 	add_roam_cache_list(bi->SSID, bi->SSID_len, bi->chanspec);
203 }
204 
is_duplicated_channel(const chanspec_t * channels,int n_channels,chanspec_t new)205 static bool is_duplicated_channel(const chanspec_t *channels, int n_channels, chanspec_t new)
206 {
207 	int i;
208 
209 	for (i = 0; i < n_channels; i++) {
210 		if (channels[i] == new)
211 			return TRUE;
212 	}
213 
214 	return FALSE;
215 }
216 
get_roam_channel_list(struct bcm_cfg80211 * cfg,chanspec_t target_chan,chanspec_t * channels,int n_channels,const wlc_ssid_t * ssid,int ioctl_ver)217 int get_roam_channel_list(struct bcm_cfg80211 *cfg, chanspec_t target_chan,
218 	chanspec_t *channels, int n_channels, const wlc_ssid_t *ssid, int ioctl_ver)
219 {
220 	int i, n = 0;
221 	char chanbuf[CHANSPEC_STR_LEN];
222 
223 	/* first index is filled with the given target channel */
224 	if ((target_chan != INVCHANSPEC) && (target_chan != 0)) {
225 		channels[0] = target_chan;
226 		n++;
227 	}
228 
229 	WL_SCAN(("0x%04X\n", channels[0]));
230 
231 #ifdef WES_SUPPORT
232 	if (cfg->roamscan_mode == ROAMSCAN_MODE_WES) {
233 		for (i = 0; i < n_roam_cache; i++) {
234 			chanspec_t ch = roam_cache[i].chanspec;
235 			bool band_match = ((roam_band == WLC_BAND_AUTO) ||
236 #ifdef WL_6G_BAND
237 				((roam_band == WLC_BAND_6G) && (CHSPEC_IS6G(ch))) ||
238 #endif /* WL_6G_BAND */
239 				((roam_band == WLC_BAND_2G) && (CHSPEC_IS2G(ch))) ||
240 				((roam_band == WLC_BAND_5G) && (CHSPEC_IS5G(ch))));
241 
242 			ch = wf_chspec_ctlchan(ch) | CHSPEC_BAND(ch) | band_bw;
243 
244 			if (band_match && !is_duplicated_channel(channels, n, ch)) {
245 				WL_SCAN(("Chanspec = %s\n",
246 					wf_chspec_ntoa_ex(ch, chanbuf)));
247 				channels[n++] = ch;
248 				if (n >= n_channels) {
249 					WL_ERR(("Too many roam scan channels\n"));
250 					return n;
251 				}
252 			}
253 		}
254 
255 		return n;
256 	}
257 #endif /* WES_SUPPORT */
258 
259 	for (i = 0; i < n_roam_cache; i++) {
260 		chanspec_t ch = roam_cache[i].chanspec;
261 		bool band_match = ((roam_band == WLC_BAND_AUTO) ||
262 #ifdef WL_6G_BAND
263 			((roam_band == WLC_BAND_6G) && (CHSPEC_IS6G(ch))) ||
264 #endif /* WL_6G_BAND */
265 			((roam_band == WLC_BAND_2G) && (CHSPEC_IS2G(ch))) ||
266 			((roam_band == WLC_BAND_5G) && (CHSPEC_IS5G(ch))));
267 
268 		ch = wf_chspec_ctlchan(ch) | CHSPEC_BAND(ch) | band_bw;
269 		if ((roam_cache[i].ssid_len == ssid->SSID_len) &&
270 			band_match && !is_duplicated_channel(channels, n, ch) &&
271 			(memcmp(roam_cache[i].ssid, ssid->SSID, ssid->SSID_len) == 0)) {
272 			/* match found, add it */
273 			WL_SCAN(("Chanspec = %s\n",
274 				wf_chspec_ntoa_ex(ch, chanbuf)));
275 			channels[n++] = ch;
276 			if (n >= n_channels) {
277 				WL_ERR(("Too many roam scan channels\n"));
278 				return n;
279 			}
280 		}
281 	}
282 
283 	return n;
284 }
285 
286 #ifdef WES_SUPPORT
get_roamscan_mode(struct net_device * dev,int * mode)287 int get_roamscan_mode(struct net_device *dev, int *mode)
288 {
289 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
290 	*mode = cfg->roamscan_mode;
291 
292 	return 0;
293 }
294 
set_roamscan_mode(struct net_device * dev,int mode)295 int set_roamscan_mode(struct net_device *dev, int mode)
296 {
297 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
298 	int error = 0;
299 	cfg->roamscan_mode = mode;
300 	n_roam_cache = 0;
301 
302 	error = wldev_iovar_setint(dev, "roamscan_mode", mode);
303 	if (error) {
304 		WL_ERR(("Failed to set roamscan mode to %d, error = %d\n", mode, error));
305 	}
306 
307 	return error;
308 }
309 
310 int
get_roamscan_chanspec_list(struct net_device * dev,chanspec_t * chanspecs)311 get_roamscan_chanspec_list(struct net_device *dev, chanspec_t *chanspecs)
312 {
313 	int i = 0;
314 	int error = BCME_OK;
315 	wl_roam_channel_list_t channel_list;
316 
317 	/* Get Current RCC List */
318 	error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
319 		(void *)&channel_list, sizeof(channel_list), NULL);
320 	if (error) {
321 		WL_ERR(("Failed to get roamscan channels, err = %d\n", error));
322 		return error;
323 	}
324 	if (channel_list.n > MAX_ROAM_CHANNEL) {
325 		WL_ERR(("Invalid roamscan channels count(%d)\n", channel_list.n));
326 		return BCME_ERROR;
327 	}
328 
329 	for (i = 0; i < channel_list.n; i++) {
330 		chanspecs[i] = channel_list.channels[i];
331 		WL_DBG(("%02d: chanspec %04x\n", i, chanspecs[i]));
332 	}
333 
334 	return i;
335 }
336 
337 int
set_roamscan_chanspec_list(struct net_device * dev,uint nchan,chanspec_t * chanspecs)338 set_roamscan_chanspec_list(struct net_device *dev, uint nchan, chanspec_t *chanspecs)
339 {
340 	int i;
341 	int error;
342 	wl_roam_channel_list_t channel_list;
343 	char iobuf[WLC_IOCTL_SMLEN];
344 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
345 	cfg->roamscan_mode = ROAMSCAN_MODE_WES;
346 
347 	if (nchan > MAX_ROAM_CHANNEL) {
348 		nchan = MAX_ROAM_CHANNEL;
349 	}
350 
351 	for (i = 0; i < nchan; i++) {
352 		roam_cache[i].chanspec = chanspecs[i];
353 		channel_list.channels[i] = chanspecs[i];
354 
355 		WL_DBG(("%02d/%d: chan: 0x%04x\n", i, nchan, chanspecs[i]));
356 	}
357 
358 	n_roam_cache = nchan;
359 	channel_list.n = nchan;
360 
361 	/* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
362 	 * otherwise, it won't be updated
363 	 */
364 	error = wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
365 	if (error) {
366 		WL_ERR(("Failed to set roamscan mode to %d, error = %d\n",
367 			ROAMSCAN_MODE_NORMAL, error));
368 		return error;
369 	}
370 	error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
371 		sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
372 	if (error) {
373 		WL_ERR(("Failed to set roamscan channels, error = %d\n", error));
374 		return error;
375 	}
376 	error = wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
377 	if (error) {
378 		WL_ERR(("Failed to set roamscan mode to %d, error = %d\n",
379 			ROAMSCAN_MODE_WES, error));
380 	}
381 
382 	return error;
383 }
384 
385 int
add_roamscan_chanspec_list(struct net_device * dev,uint nchan,chanspec_t * chanspecs)386 add_roamscan_chanspec_list(struct net_device *dev, uint nchan, chanspec_t *chanspecs)
387 {
388 	int i, error = BCME_OK;
389 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
390 	wlc_ssid_t ssid;
391 
392 	if (!cfg->rcc_enabled) {
393 		return BCME_ERROR;
394 	}
395 
396 	if (cfg->roamscan_mode == ROAMSCAN_MODE_WES) {
397 		WL_ERR(("Failed to add roamscan channels, WES mode %d\n",
398 			cfg->roamscan_mode));
399 		return BCME_ERROR;
400 	}
401 
402 	if (nchan > MAX_ROAM_CHANNEL) {
403 		WL_ERR(("Failed Over MAX channel list(%d)\n", nchan));
404 		return BCME_BADARG;
405 	}
406 
407 	error = wldev_get_ssid(dev, &ssid);
408 	if (error) {
409 		WL_ERR(("Failed to get SSID, err=%d\n", error));
410 		return error;
411 	}
412 
413 	WL_DBG(("Add Roam scan channel count %d\n", nchan));
414 
415 	for (i = 0; i < nchan; i++) {
416 		if (chanspecs[i] == 0) {
417 			continue;
418 		}
419 		add_roam_cache_list(ssid.SSID, ssid.SSID_len, chanspecs[i]);
420 		WL_DBG(("channel[%d] - 0x%04x SSID %s\n", i, chanspecs[i], ssid.SSID));
421 	}
422 
423 	update_roam_cache(cfg, ioctl_version);
424 
425 	return error;
426 }
427 #endif /* WES_SUPPORT */
428 
429 #ifdef ROAM_CHANNEL_CACHE
init_roam_cache(struct bcm_cfg80211 * cfg,int ioctl_ver)430 int init_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
431 {
432 	int err;
433 	struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
434 	s32 mode;
435 
436 	/* Check support in firmware */
437 	err = wldev_iovar_getint(dev, "roamscan_mode", &mode);
438 	if (err && (err == BCME_UNSUPPORTED)) {
439 		/* If firmware doesn't support, return error. Else proceed */
440 		WL_ERR(("roamscan_mode iovar failed. %d\n", err));
441 		return err;
442 	}
443 
444 #ifdef D11AC_IOTYPES
445 	band_bw = WL_CHANSPEC_BW_20;
446 #else
447 	band_bw = WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
448 #endif /* D11AC_IOTYPES */
449 
450 	n_roam_cache = 0;
451 	roam_band = WLC_BAND_AUTO;
452 	cfg->roamscan_mode = ROAMSCAN_MODE_NORMAL;
453 
454 	return 0;
455 }
456 
print_roam_cache(struct bcm_cfg80211 * cfg)457 void print_roam_cache(struct bcm_cfg80211 *cfg)
458 {
459 	int i;
460 
461 	if (!cfg->rcc_enabled) {
462 		return;
463 	}
464 
465 	WL_DBG((" %d cache\n", n_roam_cache));
466 
467 	for (i = 0; i < n_roam_cache; i++) {
468 		roam_cache[i].ssid[roam_cache[i].ssid_len] = 0;
469 		WL_DBG(("0x%02X %02d %s\n", roam_cache[i].chanspec,
470 			roam_cache[i].ssid_len, roam_cache[i].ssid));
471 	}
472 }
473 
wl_update_roamscan_cache_by_band(struct net_device * dev,int band)474 void wl_update_roamscan_cache_by_band(struct net_device *dev, int band)
475 {
476 	int i, error, roamscan_mode;
477 	wl_roam_channel_list_t chanlist_before, chanlist_after;
478 	char iobuf[WLC_IOCTL_SMLEN];
479 
480 	roam_band = band;
481 
482 	error = wldev_iovar_getint(dev, "roamscan_mode", &roamscan_mode);
483 	if (error) {
484 		WL_ERR(("Failed to get roamscan mode, error = %d\n", error));
485 		return;
486 	}
487 
488 	/* in case of WES mode, update channel list by band based on the cache in DHD */
489 	if (roamscan_mode) {
490 		int n = 0;
491 		chanlist_before.n = n_roam_cache;
492 
493 		for (n = 0; n < n_roam_cache; n++) {
494 			chanspec_t ch = roam_cache[n].chanspec;
495 			chanlist_before.channels[n] = wf_chspec_ctlchan(ch) |
496 				CHSPEC_BAND(ch) | band_bw;
497 		}
498 	} else {
499 		if (band == WLC_BAND_AUTO) {
500 			return;
501 		}
502 		error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
503 				(void *)&chanlist_before, sizeof(wl_roam_channel_list_t), NULL);
504 		if (error) {
505 			WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
506 			return;
507 		}
508 	}
509 	chanlist_after.n = 0;
510 	/* filtering by the given band */
511 	for (i = 0; i < chanlist_before.n; i++) {
512 		chanspec_t chspec = chanlist_before.channels[i];
513 		bool band_match = ((band == WLC_BAND_AUTO) ||
514 #ifdef WL_6G_BAND
515 				((band == WLC_BAND_6G) && (CHSPEC_IS6G(chspec))) ||
516 #endif /* WL_6G_BAND */
517 				((band == WLC_BAND_2G) && (CHSPEC_IS2G(chspec))) ||
518 				((band == WLC_BAND_5G) && (CHSPEC_IS5G(chspec))));
519 		if (band_match) {
520 			chanlist_after.channels[chanlist_after.n++] = chspec;
521 		}
522 	}
523 
524 	if (roamscan_mode) {
525 		/* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
526 		 * otherwise, it won't be updated
527 		 */
528 		wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
529 
530 		error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
531 				sizeof(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL);
532 		if (error) {
533 			WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
534 		}
535 		wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
536 	} else {
537 		if (chanlist_before.n == chanlist_after.n) {
538 			return;
539 		}
540 		error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
541 				sizeof(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL);
542 		if (error) {
543 			WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
544 		}
545 	}
546 }
547 #endif /* ROAM_CHANNEL_CACHE */
548 #endif /* ESCAN_CHANNEL_CACHE */
549