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