4 * Copyright (C) 1999-2019, Broadcom.
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 798173 2019-01-07 09:23:21Z $
32 #include <bcmwifi_channels.h>
36 #include <wl_cfg80211.h>
38 #include <wldev_common.h>
39 #include <bcmstdlib_s.h>
41 #ifdef ESCAN_CHANNEL_CACHE
42 #define MAX_ROAM_CACHE 200
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 #ifdef ROAM_CHANNEL_CACHE
60 int init_roam_cache(struct bcm_cfg80211
*cfg
, int ioctl_ver
)
63 struct net_device
*dev
= bcmcfg_to_prmry_ndev(cfg
);
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
));
77 band2G
= WL_LCHANSPEC_BAND_2G
;
78 band5G
= WL_LCHANSPEC_BAND_5G
;
79 band_bw
= WL_LCHANSPEC_BW_20
| WL_LCHANSPEC_CTL_SB_NONE
;
81 band2G
= WL_CHANSPEC_BAND_2G
;
82 band5G
= WL_CHANSPEC_BAND_5G
;
83 band_bw
= WL_CHANSPEC_BW_20
;
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 */
92 roam_band
= WLC_BAND_AUTO
;
96 #endif /* ROAM_CHANNEL_CACHE */
98 #ifdef ESCAN_CHANNEL_CACHE
99 void set_roam_band(int band
)
104 void reset_roam_cache(struct bcm_cfg80211
*cfg
)
106 if (!cfg
->rcc_enabled
) {
113 void add_roam_cache(struct bcm_cfg80211
*cfg
, wl_bss_info_t
*bi
)
117 char chanbuf
[CHANSPEC_STR_LEN
];
119 if (!cfg
->rcc_enabled
) {
123 if (n_roam_cache
>= MAX_ROAM_CACHE
)
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 */
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
;
140 (void)memcpy_s(roam_cache
[n_roam_cache
].ssid
, bi
->SSID_len
, bi
->SSID
, bi
->SSID_len
);
145 static bool is_duplicated_channel(const chanspec_t
*channels
, int n_channels
, chanspec_t
new)
149 for (i
= 0; i
< n_channels
; i
++) {
150 if (channels
[i
] == new)
157 int get_roam_channel_list(int target_chan
,
158 chanspec_t
*channels
, int n_channels
, const wlc_ssid_t
*ssid
, int ioctl_ver
)
161 char chanbuf
[CHANSPEC_STR_LEN
];
163 /* first index is filled with the given target channel */
165 channels
[0] = (target_chan
& WL_CHANSPEC_CHAN_MASK
) |
166 (target_chan
<= CH_MAX_2G_CHANNEL
? band2G
: band5G
) | band_bw
;
168 /* If target channel is not provided, set the index to 0 */
172 WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__
, target_chan
, channels
[0]));
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
));
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
)));
190 if (n
>= n_channels
) {
191 WL_ERR(("Too many roam scan channels\n"));
199 #endif /* ESCAN_CHANNEL_CACHE */
201 #ifdef ROAM_CHANNEL_CACHE
202 void print_roam_cache(struct bcm_cfg80211
*cfg
)
206 if (!cfg
->rcc_enabled
) {
210 WL_DBG((" %d cache\n", n_roam_cache
));
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
));
219 static void add_roamcache_channel(wl_roam_channel_list_t
*channels
, chanspec_t ch
)
223 if (channels
->n
>= MAX_ROAM_CHANNEL
) /* buffer full */
226 for (i
= 0; i
< channels
->n
; i
++) {
227 if (channels
->channels
[i
] == ch
) /* already in the list */
231 channels
->channels
[i
] = ch
;
234 WL_DBG((" RCC: %02d 0x%04X\n",
235 ch
& WL_CHANSPEC_CHAN_MASK
, ch
));
238 void update_roam_cache(struct bcm_cfg80211
*cfg
, int ioctl_ver
)
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
);
246 if (!cfg
->rcc_enabled
) {
250 if (!wl_get_drv_status(cfg
, CONNECTED
, dev
)) {
251 WL_DBG(("Not associated\n"));
255 /* need to read out the current cache list
256 as the firmware may change dynamically
258 error
= wldev_iovar_getbuf(dev
, "roamscan_channels", 0, 0,
259 (void *)&channel_list
, sizeof(channel_list
), NULL
);
261 WL_ERR(("Failed to get roamscan channels, error = %d\n", error
));
265 error
= wldev_get_ssid(dev
, &ssid
);
267 WL_ERR(("Failed to get SSID, err=%d\n", error
));
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
));
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
);
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
);
292 WL_ERR(("Failed to update roamscan channels, error = %d\n", error
));
296 WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache
, channel_list
.n
, error
));
299 void wl_update_roamscan_cache_by_band(struct net_device
*dev
, int band
)
301 int i
, error
, ioctl_ver
, wes_mode
;
302 wl_roam_channel_list_t chanlist_before
, chanlist_after
;
303 char iobuf
[WLC_IOCTL_SMLEN
];
307 error
= wldev_iovar_getint(dev
, "roamscan_mode", &wes_mode
);
309 WL_ERR(("Failed to get roamscan mode, error = %d\n", error
));
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 */
317 chanlist_before
.n
= n_roam_cache
;
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
;
326 if (band
== WLC_BAND_AUTO
) {
329 error
= wldev_iovar_getbuf(dev
, "roamscan_channels", 0, 0,
330 (void *)&chanlist_before
, sizeof(wl_roam_channel_list_t
), NULL
);
332 WL_ERR(("Failed to get roamscan channels, error = %d\n", error
));
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
));
346 chanlist_after
.channels
[chanlist_after
.n
++] = chspec
;
351 /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
352 * otherwise, it won't be updated
354 wldev_iovar_setint(dev
, "roamscan_mode", ROAMSCAN_MODE_NORMAL
);
356 error
= wldev_iovar_setbuf(dev
, "roamscan_channels", &chanlist_after
,
357 sizeof(wl_roam_channel_list_t
), iobuf
, sizeof(iobuf
), NULL
);
359 WL_ERR(("Failed to update roamscan channels, error = %d\n", error
));
361 wldev_iovar_setint(dev
, "roamscan_mode", ROAMSCAN_MODE_WES
);
363 if (chanlist_before
.n
== chanlist_after
.n
) {
366 error
= wldev_iovar_setbuf(dev
, "roamscan_channels", &chanlist_after
,
367 sizeof(wl_roam_channel_list_t
), iobuf
, sizeof(iobuf
), NULL
);
369 WL_ERR(("Failed to update roamscan channels, error = %d\n", error
));
373 #endif /* ROAM_CHANNEL_CACHE */
374 #endif /* ESCAN_CHANNEL_CACHE */