Commit | Line | Data |
---|---|---|
d2839953 RC |
1 | /* |
2 | * Linux roam cache | |
3 | * | |
965f77c4 | 4 | * Copyright (C) 1999-2019, Broadcom. |
d2839953 RC |
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 | * 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 | * | |
965f77c4 | 27 | * $Id: wl_roam.c 798173 2019-01-07 09:23:21Z $ |
d2839953 RC |
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> | |
965f77c4 | 39 | #include <bcmstdlib_s.h> |
d2839953 RC |
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 | |
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 | |
99 | void set_roam_band(int band) | |
100 | { | |
101 | roam_band = band; | |
102 | } | |
103 | ||
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 | ||
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 | for (i = 0; i < n_roam_cache; i++) { | |
127 | if ((roam_cache[i].ssid_len == bi->SSID_len) && | |
128 | (roam_cache[i].chanspec == bi->chanspec) && | |
129 | (memcmp(roam_cache[i].ssid, bi->SSID, bi->SSID_len) == 0)) { | |
130 | /* identical one found, just return */ | |
131 | return; | |
132 | } | |
133 | } | |
134 | ||
135 | roam_cache[n_roam_cache].ssid_len = bi->SSID_len; | |
136 | channel = wf_chspec_ctlchan(bi->chanspec); | |
137 | WL_DBG(("CHSPEC = %s, CTL %d\n", wf_chspec_ntoa_ex(bi->chanspec, chanbuf), channel)); | |
138 | roam_cache[n_roam_cache].chanspec = | |
139 | (channel <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw | channel; | |
965f77c4 | 140 | (void)memcpy_s(roam_cache[n_roam_cache].ssid, bi->SSID_len, bi->SSID, bi->SSID_len); |
d2839953 RC |
141 | |
142 | n_roam_cache++; | |
143 | } | |
144 | ||
145 | static bool is_duplicated_channel(const chanspec_t *channels, int n_channels, chanspec_t new) | |
146 | { | |
147 | int i; | |
148 | ||
149 | for (i = 0; i < n_channels; i++) { | |
150 | if (channels[i] == new) | |
151 | return TRUE; | |
152 | } | |
153 | ||
154 | return FALSE; | |
155 | } | |
156 | ||
157 | int get_roam_channel_list(int target_chan, | |
158 | chanspec_t *channels, int n_channels, const wlc_ssid_t *ssid, int ioctl_ver) | |
159 | { | |
160 | int i, n = 1; | |
161 | char chanbuf[CHANSPEC_STR_LEN]; | |
162 | ||
163 | /* first index is filled with the given target channel */ | |
164 | if (target_chan) { | |
165 | channels[0] = (target_chan & WL_CHANSPEC_CHAN_MASK) | | |
166 | (target_chan <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw; | |
167 | } else { | |
168 | /* If target channel is not provided, set the index to 0 */ | |
169 | n = 0; | |
170 | } | |
171 | ||
172 | WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__, target_chan, channels[0])); | |
173 | ||
174 | for (i = 0; i < n_roam_cache; i++) { | |
175 | chanspec_t ch = roam_cache[i].chanspec; | |
176 | bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch); | |
177 | bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch); | |
178 | bool band_match = ((roam_band == WLC_BAND_AUTO) || | |
179 | ((roam_band == WLC_BAND_2G) && is_2G) || | |
180 | ((roam_band == WLC_BAND_5G) && is_5G)); | |
181 | ||
182 | ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw; | |
183 | if ((roam_cache[i].ssid_len == ssid->SSID_len) && | |
184 | band_match && !is_duplicated_channel(channels, n, ch) && | |
185 | (memcmp(roam_cache[i].ssid, ssid->SSID, ssid->SSID_len) == 0)) { | |
186 | /* match found, add it */ | |
187 | WL_DBG(("%s: Chanspec = %s\n", __FUNCTION__, | |
188 | wf_chspec_ntoa_ex(ch, chanbuf))); | |
189 | channels[n++] = ch; | |
190 | if (n >= n_channels) { | |
191 | WL_ERR(("Too many roam scan channels\n")); | |
192 | return n; | |
193 | } | |
194 | } | |
195 | } | |
196 | ||
197 | return n; | |
198 | } | |
199 | #endif /* ESCAN_CHANNEL_CACHE */ | |
200 | ||
201 | #ifdef ROAM_CHANNEL_CACHE | |
202 | void print_roam_cache(struct bcm_cfg80211 *cfg) | |
203 | { | |
204 | int i; | |
205 | ||
206 | if (!cfg->rcc_enabled) { | |
207 | return; | |
208 | } | |
209 | ||
210 | WL_DBG((" %d cache\n", n_roam_cache)); | |
211 | ||
212 | for (i = 0; i < n_roam_cache; i++) { | |
213 | roam_cache[i].ssid[roam_cache[i].ssid_len] = 0; | |
214 | WL_DBG(("0x%02X %02d %s\n", roam_cache[i].chanspec, | |
215 | roam_cache[i].ssid_len, roam_cache[i].ssid)); | |
216 | } | |
217 | } | |
218 | ||
219 | static void add_roamcache_channel(wl_roam_channel_list_t *channels, chanspec_t ch) | |
220 | { | |
221 | int i; | |
222 | ||
223 | if (channels->n >= MAX_ROAM_CHANNEL) /* buffer full */ | |
224 | return; | |
225 | ||
226 | for (i = 0; i < channels->n; i++) { | |
227 | if (channels->channels[i] == ch) /* already in the list */ | |
228 | return; | |
229 | } | |
230 | ||
231 | channels->channels[i] = ch; | |
232 | channels->n++; | |
233 | ||
234 | WL_DBG((" RCC: %02d 0x%04X\n", | |
235 | ch & WL_CHANSPEC_CHAN_MASK, ch)); | |
236 | } | |
237 | ||
238 | void update_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver) | |
239 | { | |
240 | int error, i, prev_channels; | |
241 | wl_roam_channel_list_t channel_list; | |
242 | char iobuf[WLC_IOCTL_SMLEN]; | |
243 | struct net_device *dev = bcmcfg_to_prmry_ndev(cfg); | |
244 | wlc_ssid_t ssid; | |
245 | ||
246 | if (!cfg->rcc_enabled) { | |
247 | return; | |
248 | } | |
249 | ||
250 | if (!wl_get_drv_status(cfg, CONNECTED, dev)) { | |
251 | WL_DBG(("Not associated\n")); | |
252 | return; | |
253 | } | |
254 | ||
255 | /* need to read out the current cache list | |
256 | as the firmware may change dynamically | |
257 | */ | |
258 | error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0, | |
259 | (void *)&channel_list, sizeof(channel_list), NULL); | |
260 | if (error) { | |
261 | WL_ERR(("Failed to get roamscan channels, error = %d\n", error)); | |
262 | return; | |
263 | } | |
264 | ||
265 | error = wldev_get_ssid(dev, &ssid); | |
266 | if (error) { | |
267 | WL_ERR(("Failed to get SSID, err=%d\n", error)); | |
268 | return; | |
269 | } | |
270 | ||
271 | prev_channels = channel_list.n; | |
272 | for (i = 0; i < n_roam_cache; i++) { | |
273 | chanspec_t ch = roam_cache[i].chanspec; | |
274 | bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch); | |
275 | bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch); | |
276 | bool band_match = ((roam_band == WLC_BAND_AUTO) || | |
277 | ((roam_band == WLC_BAND_2G) && is_2G) || | |
278 | ((roam_band == WLC_BAND_5G) && is_5G)); | |
279 | ||
280 | if ((roam_cache[i].ssid_len == ssid.SSID_len) && | |
281 | band_match && (memcmp(roam_cache[i].ssid, ssid.SSID, ssid.SSID_len) == 0)) { | |
282 | /* match found, add it */ | |
283 | ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw; | |
284 | add_roamcache_channel(&channel_list, ch); | |
285 | } | |
286 | } | |
287 | if (prev_channels != channel_list.n) { | |
288 | /* channel list updated */ | |
289 | error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list, | |
290 | sizeof(channel_list), iobuf, sizeof(iobuf), NULL); | |
291 | if (error) { | |
292 | WL_ERR(("Failed to update roamscan channels, error = %d\n", error)); | |
293 | } | |
294 | } | |
295 | ||
296 | WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache, channel_list.n, error)); | |
297 | } | |
298 | ||
299 | void wl_update_roamscan_cache_by_band(struct net_device *dev, int band) | |
300 | { | |
301 | int i, error, ioctl_ver, wes_mode; | |
302 | wl_roam_channel_list_t chanlist_before, chanlist_after; | |
303 | char iobuf[WLC_IOCTL_SMLEN]; | |
304 | ||
305 | roam_band = band; | |
306 | ||
307 | error = wldev_iovar_getint(dev, "roamscan_mode", &wes_mode); | |
308 | if (error) { | |
309 | WL_ERR(("Failed to get roamscan mode, error = %d\n", error)); | |
310 | return; | |
311 | } | |
312 | ||
313 | ioctl_ver = wl_cfg80211_get_ioctl_version(); | |
314 | /* in case of WES mode, update channel list by band based on the cache in DHD */ | |
315 | if (wes_mode) { | |
316 | int n = 0; | |
317 | chanlist_before.n = n_roam_cache; | |
318 | ||
319 | for (n = 0; n < n_roam_cache; n++) { | |
320 | chanspec_t ch = roam_cache[n].chanspec; | |
321 | bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch); | |
322 | chanlist_before.channels[n] = CHSPEC_CHANNEL(ch) | | |
323 | (is_2G ? band2G : band5G) | band_bw; | |
324 | } | |
325 | } else { | |
326 | if (band == WLC_BAND_AUTO) { | |
327 | return; | |
328 | } | |
329 | error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0, | |
330 | (void *)&chanlist_before, sizeof(wl_roam_channel_list_t), NULL); | |
331 | if (error) { | |
332 | WL_ERR(("Failed to get roamscan channels, error = %d\n", error)); | |
333 | return; | |
334 | } | |
335 | } | |
336 | chanlist_after.n = 0; | |
337 | /* filtering by the given band */ | |
338 | for (i = 0; i < chanlist_before.n; i++) { | |
339 | chanspec_t chspec = chanlist_before.channels[i]; | |
340 | bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(chspec) : CHSPEC_IS2G(chspec); | |
341 | bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(chspec) : CHSPEC_IS5G(chspec); | |
342 | bool band_match = ((band == WLC_BAND_AUTO) || | |
343 | ((band == WLC_BAND_2G) && is_2G) || | |
344 | ((band == WLC_BAND_5G) && is_5G)); | |
345 | if (band_match) { | |
346 | chanlist_after.channels[chanlist_after.n++] = chspec; | |
347 | } | |
348 | } | |
349 | ||
350 | if (wes_mode) { | |
351 | /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels, | |
352 | * otherwise, it won't be updated | |
353 | */ | |
354 | wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL); | |
355 | ||
356 | error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after, | |
357 | sizeof(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL); | |
358 | if (error) { | |
359 | WL_ERR(("Failed to update roamscan channels, error = %d\n", error)); | |
360 | } | |
361 | wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES); | |
362 | } else { | |
363 | if (chanlist_before.n == chanlist_after.n) { | |
364 | return; | |
365 | } | |
366 | error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after, | |
367 | sizeof(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL); | |
368 | if (error) { | |
369 | WL_ERR(("Failed to update roamscan channels, error = %d\n", error)); | |
370 | } | |
371 | } | |
372 | } | |
373 | #endif /* ROAM_CHANNEL_CACHE */ | |
374 | #endif /* ESCAN_CHANNEL_CACHE */ |