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