4 * Copyright (C) 1999-2017, Broadcom Corporation
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:
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.
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.
25 * <<Broadcom-WL-IPTag/Open:>>
27 * $Id: wl_roam.c 662434 2016-09-29 12:21:46Z $
31 #ifdef ROAM_CHANNEL_CACHE
34 #include <bcmwifi_channels.h>
38 #include <wl_cfg80211.h>
40 #include <wldev_common.h>
42 #define MAX_ROAM_CACHE 100
43 #define MAX_SSID_BUFSIZE 36
45 #define ROAMSCAN_MODE_NORMAL 0
46 #define ROAMSCAN_MODE_WES 1
51 char ssid
[MAX_SSID_BUFSIZE
];
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
;
59 static int roamscan_mode
= ROAMSCAN_MODE_NORMAL
;
60 #endif /* WES_SUPPORT */
62 int init_roam_cache(struct bcm_cfg80211
*cfg
, int ioctl_ver
)
65 struct net_device
*dev
= bcmcfg_to_prmry_ndev(cfg
);
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
));
79 band2G
= WL_LCHANSPEC_BAND_2G
;
80 band5G
= WL_LCHANSPEC_BAND_5G
;
81 band_bw
= WL_LCHANSPEC_BW_20
| WL_LCHANSPEC_CTL_SB_NONE
;
83 band2G
= WL_CHANSPEC_BAND_2G
;
84 band5G
= WL_CHANSPEC_BAND_5G
;
85 band_bw
= WL_CHANSPEC_BW_20
;
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 */
94 roam_band
= WLC_BAND_AUTO
;
96 roamscan_mode
= ROAMSCAN_MODE_NORMAL
;
97 #endif /* WES_SUPPORT */
103 int get_roamscan_mode(struct net_device
*dev
, int *mode
)
105 *mode
= roamscan_mode
;
110 int set_roamscan_mode(struct net_device
*dev
, int mode
)
113 roamscan_mode
= mode
;
116 error
= wldev_iovar_setint(dev
, "roamscan_mode", mode
);
118 WL_ERR(("Failed to set roamscan mode to %d, error = %d\n", mode
, error
));
124 int get_roamscan_channel_list(struct net_device
*dev
, unsigned char channels
[])
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
;
132 WL_DBG(("channel[%d] - [%02d] \n", n
, channels
[n
]));
139 int set_roamscan_channel_list(struct net_device
*dev
,
140 unsigned char n
, unsigned char channels
[], int ioctl_ver
)
144 wl_roam_channel_list_t channel_list
;
145 char iobuf
[WLC_IOCTL_SMLEN
];
146 roamscan_mode
= ROAMSCAN_MODE_WES
;
148 if (n
> MAX_ROAM_CHANNEL
)
149 n
= MAX_ROAM_CHANNEL
;
151 for (i
= 0; i
< n
; i
++) {
154 if (channels
[i
] <= CH_MAX_2G_CHANNEL
) {
155 chanspec
= band2G
| band_bw
| channels
[i
];
157 chanspec
= band5G
| band_bw
| channels
[i
];
159 roam_cache
[i
].chanspec
= chanspec
;
160 channel_list
.channels
[i
] = chanspec
;
162 WL_DBG(("channel[%d] - [%02d] \n", i
, channels
[i
]));
168 /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
169 * otherwise, it won't be updated
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
);
175 WL_DBG(("Failed to set roamscan channels, error = %d\n", error
));
177 wldev_iovar_setint(dev
, "roamscan_mode", ROAMSCAN_MODE_WES
);
181 #endif /* WES_SUPPORT */
183 void set_roam_band(int band
)
188 void reset_roam_cache(struct bcm_cfg80211
*cfg
)
190 if (!cfg
->rcc_enabled
) {
195 if (roamscan_mode
== ROAMSCAN_MODE_WES
)
197 #endif /* WES_SUPPORT */
202 void add_roam_cache(struct bcm_cfg80211
*cfg
, wl_bss_info_t
*bi
)
206 char chanbuf
[CHANSPEC_STR_LEN
];
208 if (!cfg
->rcc_enabled
) {
213 if (roamscan_mode
== ROAMSCAN_MODE_WES
)
215 #endif /* WES_SUPPORT */
217 if (n_roam_cache
>= MAX_ROAM_CACHE
)
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 */
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
);
239 static bool is_duplicated_channel(const chanspec_t
*channels
, int n_channels
, chanspec_t
new)
243 for (i
= 0; i
< n_channels
; i
++) {
244 if (channels
[i
] == new)
251 int get_roam_channel_list(int target_chan
,
252 chanspec_t
*channels
, const wlc_ssid_t
*ssid
, int ioctl_ver
)
255 char chanbuf
[CHANSPEC_STR_LEN
];
257 /* first index is filled with the given target channel */
259 channels
[0] = (target_chan
& WL_CHANSPEC_CHAN_MASK
) |
260 (target_chan
<= CH_MAX_2G_CHANNEL
? band2G
: band5G
) | band_bw
;
262 /* If target channel is not provided, set the index to 0 */
266 WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__
, target_chan
, channels
[0]));
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
));
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
)));
288 #endif /* WES_SUPPORT */
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
));
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
)));
313 void print_roam_cache(struct bcm_cfg80211
*cfg
)
317 if (!cfg
->rcc_enabled
) {
321 WL_DBG((" %d cache\n", n_roam_cache
));
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
));
330 static void add_roamcache_channel(wl_roam_channel_list_t
*channels
, chanspec_t ch
)
334 if (channels
->n
>= MAX_ROAM_CHANNEL
) /* buffer full */
337 for (i
= 0; i
< channels
->n
; i
++) {
338 if (channels
->channels
[i
] == ch
) /* already in the list */
342 channels
->channels
[i
] = ch
;
345 WL_DBG((" RCC: %02d 0x%04X\n",
346 ch
& WL_CHANSPEC_CHAN_MASK
, ch
));
349 void update_roam_cache(struct bcm_cfg80211
*cfg
, int ioctl_ver
)
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
);
357 if (!cfg
->rcc_enabled
) {
362 if (roamscan_mode
== ROAMSCAN_MODE_WES
) {
363 /* no update when ROAMSCAN_MODE_WES */
366 #endif /* WES_SUPPORT */
368 if (!wl_get_drv_status(cfg
, CONNECTED
, dev
)) {
369 WL_DBG(("Not associated\n"));
373 /* need to read out the current cache list
374 as the firmware may change dynamically
376 error
= wldev_iovar_getbuf(dev
, "roamscan_channels", 0, 0,
377 (void *)&channel_list
, sizeof(channel_list
), NULL
);
379 WL_ERR(("Failed to get roamscan channels, error = %d\n", error
));
383 error
= wldev_get_ssid(dev
, &ssid
);
385 WL_ERR(("Failed to get SSID, err=%d\n", error
));
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
));
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
);
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
);
410 WL_ERR(("Failed to update roamscan channels, error = %d\n", error
));
414 WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache
, channel_list
.n
, error
));
417 void wl_update_roamscan_cache_by_band(struct net_device
*dev
, int band
)
419 int i
, error
, ioctl_ver
, wes_mode
;
420 wl_roam_channel_list_t chanlist_before
, chanlist_after
;
421 char iobuf
[WLC_IOCTL_SMLEN
];
425 error
= wldev_iovar_getint(dev
, "roamscan_mode", &wes_mode
);
427 WL_ERR(("Failed to get roamscan mode, error = %d\n", error
));
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 */
435 chanlist_before
.n
= n_roam_cache
;
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
;
444 if (band
== WLC_BAND_AUTO
) {
447 error
= wldev_iovar_getbuf(dev
, "roamscan_channels", 0, 0,
448 (void *)&chanlist_before
, sizeof(wl_roam_channel_list_t
), NULL
);
450 WL_ERR(("Failed to get roamscan channels, error = %d\n", error
));
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
));
464 chanlist_after
.channels
[chanlist_after
.n
++] = chspec
;
469 /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
470 * otherwise, it won't be updated
472 wldev_iovar_setint(dev
, "roamscan_mode", ROAMSCAN_MODE_NORMAL
);
474 error
= wldev_iovar_setbuf(dev
, "roamscan_channels", &chanlist_after
,
475 sizeof(wl_roam_channel_list_t
), iobuf
, sizeof(iobuf
), NULL
);
477 WL_ERR(("Failed to update roamscan channels, error = %d\n", error
));
479 wldev_iovar_setint(dev
, "roamscan_mode", ROAMSCAN_MODE_WES
);
481 if (chanlist_before
.n
== chanlist_after
.n
) {
484 error
= wldev_iovar_setbuf(dev
, "roamscan_channels", &chanlist_after
,
485 sizeof(wl_roam_channel_list_t
), iobuf
, sizeof(iobuf
), NULL
);
487 WL_ERR(("Failed to update roamscan channels, error = %d\n", error
));
491 #endif /* ROAM_CHANNEL_CACHE */