745a479244e9f7ae1cb90dda21933185e6e760f7
[GitHub/LineageOS/android_kernel_samsung_universal7580.git] / drivers / net / wireless / bcmdhd_1_77 / wl_roam.c
1 /*
2 * Linux roam cache
3 *
4 * Copyright (C) 1999-2017, Broadcom Corporation
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 662434 2016-09-29 12:21:46Z $
28 */
29
30
31 #ifdef ROAM_CHANNEL_CACHE
32 #include <typedefs.h>
33 #include <osl.h>
34 #include <bcmwifi_channels.h>
35 #include <wlioctl.h>
36 #include <bcmutils.h>
37 #ifdef WL_CFG80211
38 #include <wl_cfg80211.h>
39 #endif
40 #include <wldev_common.h>
41
42 #define MAX_ROAM_CACHE 100
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 #ifdef WES_SUPPORT
59 static int roamscan_mode = ROAMSCAN_MODE_NORMAL;
60 #endif /* WES_SUPPORT */
61
62 int init_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
63 {
64 int err;
65 struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
66 s32 mode;
67
68 /* Check support in firmware */
69 err = wldev_iovar_getint(dev, "roamscan_mode", &mode);
70 if (err && (err == BCME_UNSUPPORTED)) {
71 /* If firmware doesn't support, return error. Else proceed */
72 WL_ERR(("roamscan_mode iovar failed. %d\n", err));
73 return err;
74 }
75
76 #ifdef D11AC_IOTYPES
77 if (ioctl_ver == 1) {
78 /* legacy chanspec */
79 band2G = WL_LCHANSPEC_BAND_2G;
80 band5G = WL_LCHANSPEC_BAND_5G;
81 band_bw = WL_LCHANSPEC_BW_20 | WL_LCHANSPEC_CTL_SB_NONE;
82 } else {
83 band2G = WL_CHANSPEC_BAND_2G;
84 band5G = WL_CHANSPEC_BAND_5G;
85 band_bw = WL_CHANSPEC_BW_20;
86 }
87 #else
88 band2G = WL_CHANSPEC_BAND_2G;
89 band5G = WL_CHANSPEC_BAND_5G;
90 band_bw = WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
91 #endif /* D11AC_IOTYPES */
92
93 n_roam_cache = 0;
94 roam_band = WLC_BAND_AUTO;
95 #ifdef WES_SUPPORT
96 roamscan_mode = ROAMSCAN_MODE_NORMAL;
97 #endif /* WES_SUPPORT */
98
99 return 0;
100 }
101
102 #ifdef WES_SUPPORT
103 int get_roamscan_mode(struct net_device *dev, int *mode)
104 {
105 *mode = roamscan_mode;
106
107 return 0;
108 }
109
110 int set_roamscan_mode(struct net_device *dev, int mode)
111 {
112 int error = 0;
113 roamscan_mode = mode;
114 n_roam_cache = 0;
115
116 error = wldev_iovar_setint(dev, "roamscan_mode", mode);
117 if (error) {
118 WL_ERR(("Failed to set roamscan mode to %d, error = %d\n", mode, error));
119 }
120
121 return error;
122 }
123
124 int get_roamscan_channel_list(struct net_device *dev, unsigned char channels[])
125 {
126 int n = 0;
127
128 if (roamscan_mode == ROAMSCAN_MODE_WES) {
129 for (n = 0; n < n_roam_cache; n++) {
130 channels[n] = roam_cache[n].chanspec & WL_CHANSPEC_CHAN_MASK;
131
132 WL_DBG(("channel[%d] - [%02d] \n", n, channels[n]));
133 }
134 }
135
136 return n;
137 }
138
139 int set_roamscan_channel_list(struct net_device *dev,
140 unsigned char n, unsigned char channels[], int ioctl_ver)
141 {
142 int i;
143 int error;
144 wl_roam_channel_list_t channel_list;
145 char iobuf[WLC_IOCTL_SMLEN];
146 roamscan_mode = ROAMSCAN_MODE_WES;
147
148 if (n > MAX_ROAM_CHANNEL)
149 n = MAX_ROAM_CHANNEL;
150
151 for (i = 0; i < n; i++) {
152 chanspec_t chanspec;
153
154 if (channels[i] <= CH_MAX_2G_CHANNEL) {
155 chanspec = band2G | band_bw | channels[i];
156 } else {
157 chanspec = band5G | band_bw | channels[i];
158 }
159 roam_cache[i].chanspec = chanspec;
160 channel_list.channels[i] = chanspec;
161
162 WL_DBG(("channel[%d] - [%02d] \n", i, channels[i]));
163 }
164
165 n_roam_cache = n;
166 channel_list.n = n;
167
168 /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
169 * otherwise, it won't be updated
170 */
171 wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
172 error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
173 sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
174 if (error) {
175 WL_DBG(("Failed to set roamscan channels, error = %d\n", error));
176 }
177 wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
178
179 return error;
180 }
181 #endif /* WES_SUPPORT */
182
183 void set_roam_band(int band)
184 {
185 roam_band = band;
186 }
187
188 void reset_roam_cache(struct bcm_cfg80211 *cfg)
189 {
190 if (!cfg->rcc_enabled) {
191 return;
192 }
193
194 #ifdef WES_SUPPORT
195 if (roamscan_mode == ROAMSCAN_MODE_WES)
196 return;
197 #endif /* WES_SUPPORT */
198
199 n_roam_cache = 0;
200 }
201
202 void add_roam_cache(struct bcm_cfg80211 *cfg, wl_bss_info_t *bi)
203 {
204 int i;
205 uint8 channel;
206 char chanbuf[CHANSPEC_STR_LEN];
207
208 if (!cfg->rcc_enabled) {
209 return;
210 }
211
212 #ifdef WES_SUPPORT
213 if (roamscan_mode == ROAMSCAN_MODE_WES)
214 return;
215 #endif /* WES_SUPPORT */
216
217 if (n_roam_cache >= MAX_ROAM_CACHE)
218 return;
219
220 for (i = 0; i < n_roam_cache; i++) {
221 if ((roam_cache[i].ssid_len == bi->SSID_len) &&
222 (roam_cache[i].chanspec == bi->chanspec) &&
223 (memcmp(roam_cache[i].ssid, bi->SSID, bi->SSID_len) == 0)) {
224 /* identical one found, just return */
225 return;
226 }
227 }
228
229 roam_cache[n_roam_cache].ssid_len = bi->SSID_len;
230 channel = wf_chspec_ctlchan(bi->chanspec);
231 WL_DBG(("CHSPEC = %s, CTL %d\n", wf_chspec_ntoa_ex(bi->chanspec, chanbuf), channel));
232 roam_cache[n_roam_cache].chanspec =
233 (channel <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw | channel;
234 memcpy(roam_cache[n_roam_cache].ssid, bi->SSID, bi->SSID_len);
235
236 n_roam_cache++;
237 }
238
239 static bool is_duplicated_channel(const chanspec_t *channels, int n_channels, chanspec_t new)
240 {
241 int i;
242
243 for (i = 0; i < n_channels; i++) {
244 if (channels[i] == new)
245 return TRUE;
246 }
247
248 return FALSE;
249 }
250
251 int get_roam_channel_list(int target_chan,
252 chanspec_t *channels, const wlc_ssid_t *ssid, int ioctl_ver)
253 {
254 int i, n = 1;
255 char chanbuf[CHANSPEC_STR_LEN];
256
257 /* first index is filled with the given target channel */
258 if (target_chan) {
259 channels[0] = (target_chan & WL_CHANSPEC_CHAN_MASK) |
260 (target_chan <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw;
261 } else {
262 /* If target channel is not provided, set the index to 0 */
263 n = 0;
264 }
265
266 WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__, target_chan, channels[0]));
267
268 #ifdef WES_SUPPORT
269 if (roamscan_mode == ROAMSCAN_MODE_WES) {
270 for (i = 0; i < n_roam_cache; i++) {
271 chanspec_t ch = roam_cache[i].chanspec;
272 bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
273 bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
274 bool band_match = ((roam_band == WLC_BAND_AUTO) ||
275 ((roam_band == WLC_BAND_2G) && is_2G) ||
276 ((roam_band == WLC_BAND_5G) && is_5G));
277
278 ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
279 if (band_match && !is_duplicated_channel(channels, n, ch)) {
280 WL_DBG(("%s: Chanspec = %s\n", __FUNCTION__,
281 wf_chspec_ntoa_ex(ch, chanbuf)));
282 channels[n++] = ch;
283 }
284 }
285
286 return n;
287 }
288 #endif /* WES_SUPPORT */
289
290 for (i = 0; i < n_roam_cache; i++) {
291 chanspec_t ch = roam_cache[i].chanspec;
292 bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
293 bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
294 bool band_match = ((roam_band == WLC_BAND_AUTO) ||
295 ((roam_band == WLC_BAND_2G) && is_2G) ||
296 ((roam_band == WLC_BAND_5G) && is_5G));
297
298 ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
299 if ((roam_cache[i].ssid_len == ssid->SSID_len) &&
300 band_match && !is_duplicated_channel(channels, n, ch) &&
301 (memcmp(roam_cache[i].ssid, ssid->SSID, ssid->SSID_len) == 0)) {
302 /* match found, add it */
303 WL_DBG(("%s: Chanspec = %s\n", __FUNCTION__,
304 wf_chspec_ntoa_ex(ch, chanbuf)));
305 channels[n++] = ch;
306 }
307 }
308
309 return n;
310 }
311
312
313 void print_roam_cache(struct bcm_cfg80211 *cfg)
314 {
315 int i;
316
317 if (!cfg->rcc_enabled) {
318 return;
319 }
320
321 WL_DBG((" %d cache\n", n_roam_cache));
322
323 for (i = 0; i < n_roam_cache; i++) {
324 roam_cache[i].ssid[roam_cache[i].ssid_len] = 0;
325 WL_DBG(("0x%02X %02d %s\n", roam_cache[i].chanspec,
326 roam_cache[i].ssid_len, roam_cache[i].ssid));
327 }
328 }
329
330 static void add_roamcache_channel(wl_roam_channel_list_t *channels, chanspec_t ch)
331 {
332 int i;
333
334 if (channels->n >= MAX_ROAM_CHANNEL) /* buffer full */
335 return;
336
337 for (i = 0; i < channels->n; i++) {
338 if (channels->channels[i] == ch) /* already in the list */
339 return;
340 }
341
342 channels->channels[i] = ch;
343 channels->n++;
344
345 WL_DBG((" RCC: %02d 0x%04X\n",
346 ch & WL_CHANSPEC_CHAN_MASK, ch));
347 }
348
349 void update_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
350 {
351 int error, i, prev_channels;
352 wl_roam_channel_list_t channel_list;
353 char iobuf[WLC_IOCTL_SMLEN];
354 struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
355 wlc_ssid_t ssid;
356
357 if (!cfg->rcc_enabled) {
358 return;
359 }
360
361 #ifdef WES_SUPPORT
362 if (roamscan_mode == ROAMSCAN_MODE_WES) {
363 /* no update when ROAMSCAN_MODE_WES */
364 return;
365 }
366 #endif /* WES_SUPPORT */
367
368 if (!wl_get_drv_status(cfg, CONNECTED, dev)) {
369 WL_DBG(("Not associated\n"));
370 return;
371 }
372
373 /* need to read out the current cache list
374 as the firmware may change dynamically
375 */
376 error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
377 (void *)&channel_list, sizeof(channel_list), NULL);
378 if (error) {
379 WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
380 return;
381 }
382
383 error = wldev_get_ssid(dev, &ssid);
384 if (error) {
385 WL_ERR(("Failed to get SSID, err=%d\n", error));
386 return;
387 }
388
389 prev_channels = channel_list.n;
390 for (i = 0; i < n_roam_cache; i++) {
391 chanspec_t ch = roam_cache[i].chanspec;
392 bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
393 bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
394 bool band_match = ((roam_band == WLC_BAND_AUTO) ||
395 ((roam_band == WLC_BAND_2G) && is_2G) ||
396 ((roam_band == WLC_BAND_5G) && is_5G));
397
398 if ((roam_cache[i].ssid_len == ssid.SSID_len) &&
399 band_match && (memcmp(roam_cache[i].ssid, ssid.SSID, ssid.SSID_len) == 0)) {
400 /* match found, add it */
401 ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
402 add_roamcache_channel(&channel_list, ch);
403 }
404 }
405 if (prev_channels != channel_list.n) {
406 /* channel list updated */
407 error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
408 sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
409 if (error) {
410 WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
411 }
412 }
413
414 WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache, channel_list.n, error));
415 }
416
417 void wl_update_roamscan_cache_by_band(struct net_device *dev, int band)
418 {
419 int i, error, ioctl_ver, wes_mode;
420 wl_roam_channel_list_t chanlist_before, chanlist_after;
421 char iobuf[WLC_IOCTL_SMLEN];
422
423 roam_band = band;
424
425 error = wldev_iovar_getint(dev, "roamscan_mode", &wes_mode);
426 if (error) {
427 WL_ERR(("Failed to get roamscan mode, error = %d\n", error));
428 return;
429 }
430
431 ioctl_ver = wl_cfg80211_get_ioctl_version();
432 /* in case of WES mode, update channel list by band based on the cache in DHD */
433 if (wes_mode) {
434 int n = 0;
435 chanlist_before.n = n_roam_cache;
436
437 for (n = 0; n < n_roam_cache; n++) {
438 chanspec_t ch = roam_cache[n].chanspec;
439 bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
440 chanlist_before.channels[n] = CHSPEC_CHANNEL(ch) |
441 (is_2G ? band2G : band5G) | band_bw;
442 }
443 } else {
444 if (band == WLC_BAND_AUTO) {
445 return;
446 }
447 error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
448 (void *)&chanlist_before, sizeof(wl_roam_channel_list_t), NULL);
449 if (error) {
450 WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
451 return;
452 }
453 }
454 chanlist_after.n = 0;
455 /* filtering by the given band */
456 for (i = 0; i < chanlist_before.n; i++) {
457 chanspec_t chspec = chanlist_before.channels[i];
458 bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(chspec) : CHSPEC_IS2G(chspec);
459 bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(chspec) : CHSPEC_IS5G(chspec);
460 bool band_match = ((band == WLC_BAND_AUTO) ||
461 ((band == WLC_BAND_2G) && is_2G) ||
462 ((band == WLC_BAND_5G) && is_5G));
463 if (band_match) {
464 chanlist_after.channels[chanlist_after.n++] = chspec;
465 }
466 }
467
468 if (wes_mode) {
469 /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
470 * otherwise, it won't be updated
471 */
472 wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
473
474 error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
475 sizeof(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL);
476 if (error) {
477 WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
478 }
479 wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
480 } else {
481 if (chanlist_before.n == chanlist_after.n) {
482 return;
483 }
484 error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
485 sizeof(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL);
486 if (error) {
487 WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
488 }
489 }
490 }
491 #endif /* ROAM_CHANNEL_CACHE */