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