• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Linux roam cache
3  *
4  * Copyright (C) 1999-2019, 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
16  * of the license of that module.  An independent module is a module which is
17  * not derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *      Notwithstanding the above, under no circumstances may you combine this
21  * software in any way with any other Broadcom software provided under a license
22  * other than the GPL, without Broadcom's express prior written consent.
23  *
24  *
25  * <<Broadcom-WL-IPTag/Open:>>
26  *
27  * $Id: wl_roam.c 798173 2019-01-07 09:23:21Z $
28  */
29 
30 #include <typedefs.h>
31 #include <osl.h>
32 #include <bcmwifi_channels.h>
33 #include <wlioctl.h>
34 #include <bcmutils.h>
35 #ifdef WL_CFG80211
36 #include <wl_cfg80211.h>
37 #endif // endif
38 #include <wldev_common.h>
39 #include <bcmstdlib_s.h>
40 
41 #ifdef ESCAN_CHANNEL_CACHE
42 #define MAX_ROAM_CACHE 200
43 #define MAX_SSID_BUFSIZE 36
44 
45 #define ROAMSCAN_MODE_NORMAL 0
46 #define ROAMSCAN_MODE_WES 1
47 
48 typedef struct {
49     chanspec_t chanspec;
50     int ssid_len;
51     char ssid[MAX_SSID_BUFSIZE];
52 } roam_channel_cache;
53 
54 static int n_roam_cache = 0;
55 static int roam_band = WLC_BAND_AUTO;
56 static roam_channel_cache roam_cache[MAX_ROAM_CACHE];
57 static uint band2G, band5G, band_bw;
58 
59 #ifdef ROAM_CHANNEL_CACHE
init_roam_cache(struct bcm_cfg80211 * cfg,int ioctl_ver)60 int init_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
61 {
62     int err;
63     struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
64     s32 mode;
65 
66     /* Check support in firmware */
67     err = wldev_iovar_getint(dev, "roamscan_mode", &mode);
68     if (err && (err == BCME_UNSUPPORTED)) {
69         /* If firmware doesn't support, return error. Else proceed */
70         WL_ERR(("roamscan_mode iovar failed. %d\n", err));
71         return err;
72     }
73 
74 #ifdef D11AC_IOTYPES
75     if (ioctl_ver == 1) {
76         /* legacy chanspec */
77         band2G = WL_LCHANSPEC_BAND_2G;
78         band5G = WL_LCHANSPEC_BAND_5G;
79         band_bw = WL_LCHANSPEC_BW_20 | WL_LCHANSPEC_CTL_SB_NONE;
80     } else {
81         band2G = WL_CHANSPEC_BAND_2G;
82         band5G = WL_CHANSPEC_BAND_5G;
83         band_bw = WL_CHANSPEC_BW_20;
84     }
85 #else
86     band2G = WL_CHANSPEC_BAND_2G;
87     band5G = WL_CHANSPEC_BAND_5G;
88     band_bw = WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
89 #endif /* D11AC_IOTYPES */
90 
91     n_roam_cache = 0;
92     roam_band = WLC_BAND_AUTO;
93 
94     return 0;
95 }
96 #endif /* ROAM_CHANNEL_CACHE */
97 
98 #ifdef ESCAN_CHANNEL_CACHE
set_roam_band(int band)99 void set_roam_band(int band)
100 {
101     roam_band = band;
102 }
103 
reset_roam_cache(struct bcm_cfg80211 * cfg)104 void reset_roam_cache(struct bcm_cfg80211 *cfg)
105 {
106     if (!cfg->rcc_enabled) {
107         return;
108     }
109 
110     n_roam_cache = 0;
111 }
112 
add_roam_cache(struct bcm_cfg80211 * cfg,wl_bss_info_t * bi)113 void add_roam_cache(struct bcm_cfg80211 *cfg, wl_bss_info_t *bi)
114 {
115     int i;
116     uint8 channel;
117     char chanbuf[CHANSPEC_STR_LEN];
118 
119     if (!cfg->rcc_enabled) {
120         return;
121     }
122 
123     if (n_roam_cache >= MAX_ROAM_CACHE) {
124         return;
125     }
126 
127     for (i = 0; i < n_roam_cache; i++) {
128         if ((roam_cache[i].ssid_len == bi->SSID_len) &&
129             (roam_cache[i].chanspec == bi->chanspec) &&
130             (memcmp(roam_cache[i].ssid, bi->SSID, bi->SSID_len) == 0)) {
131             /* identical one found, just return */
132             return;
133         }
134     }
135 
136     roam_cache[n_roam_cache].ssid_len = bi->SSID_len;
137     channel = wf_chspec_ctlchan(bi->chanspec);
138     WL_DBG(("CHSPEC  = %s, CTL %d\n", wf_chspec_ntoa_ex(bi->chanspec, chanbuf),
139             channel));
140     roam_cache[n_roam_cache].chanspec =
141         (channel <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw | channel;
142     (void)memcpy_s(roam_cache[n_roam_cache].ssid, bi->SSID_len, bi->SSID,
143                    bi->SSID_len);
144 
145     n_roam_cache++;
146 }
147 
is_duplicated_channel(const chanspec_t * channels,int n_channels,chanspec_t new)148 static bool is_duplicated_channel(const chanspec_t *channels, int n_channels,
149                                   chanspec_t new)
150 {
151     int i;
152 
153     for (i = 0; i < n_channels; i++) {
154         if (channels[i] == new) {
155             return TRUE;
156         }
157     }
158 
159     return FALSE;
160 }
161 
get_roam_channel_list(int target_chan,chanspec_t * channels,int n_channels,const wlc_ssid_t * ssid,int ioctl_ver)162 int get_roam_channel_list(int target_chan, chanspec_t *channels, int n_channels,
163                           const wlc_ssid_t *ssid, int ioctl_ver)
164 {
165     int i, n = 1;
166     char chanbuf[CHANSPEC_STR_LEN];
167 
168     /* first index is filled with the given target channel */
169     if (target_chan) {
170         channels[0] = (target_chan & WL_CHANSPEC_CHAN_MASK) |
171                       (target_chan <= CH_MAX_2G_CHANNEL ? band2G : band5G) |
172                       band_bw;
173     } else {
174         /* If target channel is not provided, set the index to 0 */
175         n = 0;
176     }
177 
178     WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__, target_chan, channels[0]));
179 
180     for (i = 0; i < n_roam_cache; i++) {
181         chanspec_t ch = roam_cache[i].chanspec;
182         bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
183         bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
184         bool band_match = ((roam_band == WLC_BAND_AUTO) ||
185                            ((roam_band == WLC_BAND_2G) && is_2G) ||
186                            ((roam_band == WLC_BAND_5G) && is_5G));
187 
188         ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
189         if ((roam_cache[i].ssid_len == ssid->SSID_len) && band_match &&
190             !is_duplicated_channel(channels, n, ch) &&
191             (memcmp(roam_cache[i].ssid, ssid->SSID, ssid->SSID_len) == 0)) {
192             /* match found, add it */
193             WL_DBG(("%s: Chanspec = %s\n", __FUNCTION__,
194                     wf_chspec_ntoa_ex(ch, chanbuf)));
195             channels[n++] = ch;
196             if (n >= n_channels) {
197                 WL_ERR(("Too many roam scan channels\n"));
198                 return n;
199             }
200         }
201     }
202 
203     return n;
204 }
205 #endif /* ESCAN_CHANNEL_CACHE */
206 
207 #ifdef ROAM_CHANNEL_CACHE
print_roam_cache(struct bcm_cfg80211 * cfg)208 void print_roam_cache(struct bcm_cfg80211 *cfg)
209 {
210     int i;
211 
212     if (!cfg->rcc_enabled) {
213         return;
214     }
215 
216     WL_DBG((" %d cache\n", n_roam_cache));
217 
218     for (i = 0; i < n_roam_cache; i++) {
219         roam_cache[i].ssid[roam_cache[i].ssid_len] = 0;
220         WL_DBG(("0x%02X %02d %s\n", roam_cache[i].chanspec,
221                 roam_cache[i].ssid_len, roam_cache[i].ssid));
222     }
223 }
224 
add_roamcache_channel(wl_roam_channel_list_t * channels,chanspec_t ch)225 static void add_roamcache_channel(wl_roam_channel_list_t *channels,
226                                   chanspec_t ch)
227 {
228     int i;
229 
230     if (channels->n >= MAX_ROAM_CHANNEL) { /* buffer full */
231         return;
232     }
233 
234     for (i = 0; i < channels->n; i++) {
235         if (channels->channels[i] == ch) { /* already in the list */
236             return;
237         }
238     }
239 
240     channels->channels[i] = ch;
241     channels->n++;
242 
243     WL_DBG((" RCC: %02d 0x%04X\n", ch & WL_CHANSPEC_CHAN_MASK, ch));
244 }
245 
update_roam_cache(struct bcm_cfg80211 * cfg,int ioctl_ver)246 void update_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
247 {
248     int error, i, prev_channels;
249     wl_roam_channel_list_t channel_list;
250     char iobuf[WLC_IOCTL_SMLEN];
251     struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
252     wlc_ssid_t ssid;
253 
254     if (!cfg->rcc_enabled) {
255         return;
256     }
257 
258     if (!wl_get_drv_status(cfg, CONNECTED, dev)) {
259         WL_DBG(("Not associated\n"));
260         return;
261     }
262 
263     /* need to read out the current cache list
264        as the firmware may change dynamically
265     */
266     error =
267         wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
268                            (void *)&channel_list, sizeof(channel_list), NULL);
269     if (error) {
270         WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
271         return;
272     }
273 
274     error = wldev_get_ssid(dev, &ssid);
275     if (error) {
276         WL_ERR(("Failed to get SSID, err=%d\n", error));
277         return;
278     }
279 
280     prev_channels = channel_list.n;
281     for (i = 0; i < n_roam_cache; i++) {
282         chanspec_t ch = roam_cache[i].chanspec;
283         bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
284         bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
285         bool band_match = ((roam_band == WLC_BAND_AUTO) ||
286                            ((roam_band == WLC_BAND_2G) && is_2G) ||
287                            ((roam_band == WLC_BAND_5G) && is_5G));
288 
289         if ((roam_cache[i].ssid_len == ssid.SSID_len) && band_match &&
290             (memcmp(roam_cache[i].ssid, ssid.SSID, ssid.SSID_len) == 0)) {
291             /* match found, add it */
292             ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
293             add_roamcache_channel(&channel_list, ch);
294         }
295     }
296     if (prev_channels != channel_list.n) {
297         /* channel list updated */
298         error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
299                                    sizeof(channel_list), iobuf, sizeof(iobuf),
300                                    NULL);
301         if (error) {
302             WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
303         }
304     }
305 
306     WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache, channel_list.n,
307             error));
308 }
309 
wl_update_roamscan_cache_by_band(struct net_device * dev,int band)310 void wl_update_roamscan_cache_by_band(struct net_device *dev, int band)
311 {
312     int i, error, ioctl_ver, wes_mode;
313     wl_roam_channel_list_t chanlist_before, chanlist_after;
314     char iobuf[WLC_IOCTL_SMLEN];
315 
316     roam_band = band;
317 
318     error = wldev_iovar_getint(dev, "roamscan_mode", &wes_mode);
319     if (error) {
320         WL_ERR(("Failed to get roamscan mode, error = %d\n", error));
321         return;
322     }
323 
324     ioctl_ver = wl_cfg80211_get_ioctl_version();
325     /* in case of WES mode, update channel list by band based on the cache in
326      * DHD */
327     if (wes_mode) {
328         int n = 0;
329         chanlist_before.n = n_roam_cache;
330 
331         for (n = 0; n < n_roam_cache; n++) {
332             chanspec_t ch = roam_cache[n].chanspec;
333             bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
334             chanlist_before.channels[n] =
335                 CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
336         }
337     } else {
338         if (band == WLC_BAND_AUTO) {
339             return;
340         }
341         error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
342                                    (void *)&chanlist_before,
343                                    sizeof(wl_roam_channel_list_t), NULL);
344         if (error) {
345             WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
346             return;
347         }
348     }
349     chanlist_after.n = 0;
350     /* filtering by the given band */
351     for (i = 0; i < chanlist_before.n; i++) {
352         chanspec_t chspec = chanlist_before.channels[i];
353         bool is_2G =
354             ioctl_ver == 1 ? LCHSPEC_IS2G(chspec) : CHSPEC_IS2G(chspec);
355         bool is_5G =
356             ioctl_ver == 1 ? LCHSPEC_IS5G(chspec) : CHSPEC_IS5G(chspec);
357         bool band_match =
358             ((band == WLC_BAND_AUTO) || ((band == WLC_BAND_2G) && is_2G) ||
359              ((band == WLC_BAND_5G) && is_5G));
360         if (band_match) {
361             chanlist_after.channels[chanlist_after.n++] = chspec;
362         }
363     }
364 
365     if (wes_mode) {
366         /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
367          * otherwise, it won't be updated
368          */
369         wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
370 
371         error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
372                                    sizeof(wl_roam_channel_list_t), iobuf,
373                                    sizeof(iobuf), NULL);
374         if (error) {
375             WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
376         }
377         wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
378     } else {
379         if (chanlist_before.n == chanlist_after.n) {
380             return;
381         }
382         error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
383                                    sizeof(wl_roam_channel_list_t), iobuf,
384                                    sizeof(iobuf), NULL);
385         if (error) {
386             WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
387         }
388     }
389 }
390 #endif /* ROAM_CHANNEL_CACHE */
391 #endif /* ESCAN_CHANNEL_CACHE */
392