123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- /*
- * Linux roam cache
- *
- * Copyright (C) 1999-2015, Broadcom Corporation
- *
- * Unless you and Broadcom execute a separate written software license
- * agreement governing use of this software, this software is licensed to you
- * under the terms of the GNU General Public License version 2 (the "GPL"),
- * available at http://www.broadcom.com/licenses/GPLv2.php, with the
- * following added to such license:
- *
- * As a special exception, the copyright holders of this software give you
- * permission to link this software with independent modules, and to copy and
- * distribute the resulting executable under terms of your choice, provided that
- * you also meet, for each linked independent module, the terms and conditions of
- * the license of that module. An independent module is a module which is not
- * derived from this software. The special exception does not apply to any
- * modifications of the software.
- *
- * Notwithstanding the above, under no circumstances may you combine this
- * software in any way with any other Broadcom software provided under a license
- * other than the GPL, without Broadcom's express prior written consent.
- *
- * $Id: wl_roam.c 599101 2015-11-12 11:51:02Z $
- */
- #include <typedefs.h>
- #include <osl.h>
- #include <bcmwifi_channels.h>
- #include <wlioctl.h>
- #include <bcmutils.h>
- #ifdef WL_CFG80211
- #include <wl_cfg80211.h>
- #endif
- #include <wldev_common.h>
- #define MAX_ROAM_CACHE 100
- #define MAX_CHANNEL_LIST 20
- #define MAX_SSID_BUFSIZE 36
- #define ROAMSCAN_MODE_NORMAL 0
- #define ROAMSCAN_MODE_WES 1
- typedef struct {
- chanspec_t chanspec;
- int ssid_len;
- char ssid[MAX_SSID_BUFSIZE];
- } roam_channel_cache;
- typedef struct {
- int n;
- chanspec_t channels[MAX_CHANNEL_LIST];
- } channel_list_t;
- static int n_roam_cache = 0;
- static int roam_band = WLC_BAND_AUTO;
- static roam_channel_cache roam_cache[MAX_ROAM_CACHE];
- static uint band2G, band5G, band_bw;
- #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
- static int roamscan_mode = ROAMSCAN_MODE_NORMAL;
- #endif
- void init_roam(int ioctl_ver)
- {
- #ifdef D11AC_IOTYPES
- if (ioctl_ver == 1) {
- /* legacy chanspec */
- band2G = WL_LCHANSPEC_BAND_2G;
- band5G = WL_LCHANSPEC_BAND_5G;
- band_bw = WL_LCHANSPEC_BW_20 | WL_LCHANSPEC_CTL_SB_NONE;
- } else {
- band2G = WL_CHANSPEC_BAND_2G;
- band5G = WL_CHANSPEC_BAND_5G;
- band_bw = WL_CHANSPEC_BW_20;
- }
- #else
- band2G = WL_CHANSPEC_BAND_2G;
- band5G = WL_CHANSPEC_BAND_5G;
- band_bw = WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
- #endif /* D11AC_IOTYPES */
- n_roam_cache = 0;
- roam_band = WLC_BAND_AUTO;
- #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
- roamscan_mode = ROAMSCAN_MODE_NORMAL;
- #endif
- }
- #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
- int get_roamscan_mode(struct net_device *dev, int *mode)
- {
- *mode = roamscan_mode;
- return 0;
- }
- int set_roamscan_mode(struct net_device *dev, int mode)
- {
- int error = 0;
- roamscan_mode = mode;
- n_roam_cache = 0;
- error = wldev_iovar_setint(dev, "roamscan_mode", mode);
- if (error) {
- WL_ERR(("Failed to set roamscan mode to %d, error = %d\n", mode, error));
- }
- return error;
- }
- int get_roamscan_channel_list(struct net_device *dev, unsigned char channels[])
- {
- int n = 0;
- if (roamscan_mode == ROAMSCAN_MODE_WES) {
- for (n = 0; n < n_roam_cache; n++) {
- channels[n] = roam_cache[n].chanspec & WL_CHANSPEC_CHAN_MASK;
- WL_DBG(("channel[%d] - [%02d] \n", n, channels[n]));
- }
- }
- return n;
- }
- int set_roamscan_channel_list(struct net_device *dev,
- unsigned char n, unsigned char channels[], int ioctl_ver)
- {
- int i;
- int error;
- channel_list_t channel_list;
- char iobuf[WLC_IOCTL_SMLEN];
- roamscan_mode = ROAMSCAN_MODE_WES;
- if (n > MAX_CHANNEL_LIST)
- n = MAX_CHANNEL_LIST;
- for (i = 0; i < n; i++) {
- chanspec_t chanspec;
- if (channels[i] <= CH_MAX_2G_CHANNEL) {
- chanspec = band2G | band_bw | channels[i];
- } else {
- chanspec = band5G | band_bw | channels[i];
- }
- roam_cache[i].chanspec = chanspec;
- channel_list.channels[i] = chanspec;
- WL_DBG(("channel[%d] - [%02d] \n", i, channels[i]));
- }
- n_roam_cache = n;
- channel_list.n = n;
- /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
- * otherwise, it won't be updated
- */
- wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
- error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
- sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
- if (error) {
- WL_DBG(("Failed to set roamscan channels, error = %d\n", error));
- }
- wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
- return error;
- }
- #endif /* WES_SUPPORT */
- void set_roam_band(int band)
- {
- roam_band = band;
- }
- void reset_roam_cache(void)
- {
- #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
- if (roamscan_mode == ROAMSCAN_MODE_WES)
- return;
- #endif
- n_roam_cache = 0;
- }
- void add_roam_cache(wl_bss_info_t *bi)
- {
- int i;
- uint8 channel;
- #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
- if (roamscan_mode == ROAMSCAN_MODE_WES)
- return;
- #endif
- if (n_roam_cache >= MAX_ROAM_CACHE)
- return;
- for (i = 0; i < n_roam_cache; i++) {
- if ((roam_cache[i].ssid_len == bi->SSID_len) &&
- (roam_cache[i].chanspec == bi->chanspec) &&
- (memcmp(roam_cache[i].ssid, bi->SSID, bi->SSID_len) == 0)) {
- /* identical one found, just return */
- return;
- }
- }
- roam_cache[n_roam_cache].ssid_len = bi->SSID_len;
- channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
- WL_DBG(("CHSPEC 0x%X %d, CTL %d\n",
- bi->chanspec, CHSPEC_CHANNEL(bi->chanspec), bi->ctl_ch));
- roam_cache[n_roam_cache].chanspec =
- (channel <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw | channel;
- memcpy(roam_cache[n_roam_cache].ssid, bi->SSID, bi->SSID_len);
- n_roam_cache++;
- }
- static bool is_duplicated_channel(const chanspec_t *channels, int n_channels, chanspec_t new)
- {
- int i;
- for (i = 0; i < n_channels; i++) {
- if (channels[i] == new)
- return TRUE;
- }
- return FALSE;
- }
- int get_roam_channel_list(int target_chan,
- chanspec_t *channels, const wlc_ssid_t *ssid, int ioctl_ver)
- {
- int i, n = 1;
- /* first index is filled with the given target channel */
- channels[0] = (target_chan & WL_CHANSPEC_CHAN_MASK) |
- (target_chan <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw;
- WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__, target_chan, channels[0]));
- #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
- if (roamscan_mode == ROAMSCAN_MODE_WES) {
- for (i = 0; i < n_roam_cache; i++) {
- chanspec_t ch = roam_cache[i].chanspec;
- bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
- bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
- bool band_match = ((roam_band == WLC_BAND_AUTO) ||
- ((roam_band == WLC_BAND_2G) && is_2G) ||
- ((roam_band == WLC_BAND_5G) && is_5G));
- ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
- if (band_match && !is_duplicated_channel(channels, n, ch)) {
- WL_DBG((" %s: %03d(0x%X)\n", __FUNCTION__, CHSPEC_CHANNEL(ch), ch));
- channels[n++] = ch;
- }
- }
- return n;
- }
- #endif /* WES_SUPPORT */
- for (i = 0; i < n_roam_cache; i++) {
- chanspec_t ch = roam_cache[i].chanspec;
- bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
- bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
- bool band_match = ((roam_band == WLC_BAND_AUTO) ||
- ((roam_band == WLC_BAND_2G) && is_2G) ||
- ((roam_band == WLC_BAND_5G) && is_5G));
- ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
- if ((roam_cache[i].ssid_len == ssid->SSID_len) &&
- band_match && !is_duplicated_channel(channels, n, ch) &&
- (memcmp(roam_cache[i].ssid, ssid->SSID, ssid->SSID_len) == 0)) {
- /* match found, add it */
- WL_DBG((" %s: %03d(0x%04X)\n", __FUNCTION__,
- CHSPEC_CHANNEL(ch), ch));
- channels[n++] = ch;
- }
- }
- return n;
- }
- void print_roam_cache(void)
- {
- int i;
- WL_DBG((" %d cache\n", n_roam_cache));
- for (i = 0; i < n_roam_cache; i++) {
- roam_cache[i].ssid[roam_cache[i].ssid_len] = 0;
- WL_DBG(("0x%02X %02d %s\n", roam_cache[i].chanspec,
- roam_cache[i].ssid_len, roam_cache[i].ssid));
- }
- }
- static void add_roamcache_channel(channel_list_t *channels, chanspec_t ch)
- {
- int i;
- if (channels->n >= MAX_CHANNEL_LIST) /* buffer full */
- return;
- for (i = 0; i < channels->n; i++) {
- if (channels->channels[i] == ch) /* already in the list */
- return;
- }
- channels->channels[i] = ch;
- channels->n++;
- WL_DBG((" RCC: %02d 0x%04X\n",
- ch & WL_CHANSPEC_CHAN_MASK, ch));
- }
- void update_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
- {
- int error, i, prev_channels;
- channel_list_t channel_list;
- char iobuf[WLC_IOCTL_SMLEN];
- struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
- wlc_ssid_t ssid;
- #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
- if (roamscan_mode == ROAMSCAN_MODE_WES) {
- /* no update when ROAMSCAN_MODE_WES */
- return;
- }
- #endif
- if (!wl_get_drv_status(cfg, CONNECTED, dev)) {
- WL_DBG(("Not associated\n"));
- return;
- }
- /* need to read out the current cache list
- as the firmware may change dynamically
- */
- error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
- (void *)&channel_list, sizeof(channel_list), NULL);
- WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache, channel_list.n, error));
- error = wldev_get_ssid(dev, &ssid);
- if (error) {
- WL_ERR(("Failed to get SSID, err=%d\n", error));
- return;
- }
- prev_channels = channel_list.n;
- for (i = 0; i < n_roam_cache; i++) {
- chanspec_t ch = roam_cache[i].chanspec;
- bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
- bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
- bool band_match = ((roam_band == WLC_BAND_AUTO) ||
- ((roam_band == WLC_BAND_2G) && is_2G) ||
- ((roam_band == WLC_BAND_5G) && is_5G));
- if ((roam_cache[i].ssid_len == ssid.SSID_len) &&
- band_match && (memcmp(roam_cache[i].ssid, ssid.SSID, ssid.SSID_len) == 0)) {
- /* match found, add it */
- ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
- add_roamcache_channel(&channel_list, ch);
- }
- }
- if (prev_channels != channel_list.n) {
- /* channel list updated */
- error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
- sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
- if (error) {
- WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
- }
- }
- }
- void wl_update_roamscan_cache_by_band(struct net_device *dev, int band)
- {
- int i, error, ioctl_ver, wes_mode;
- channel_list_t chanlist_before, chanlist_after;
- char iobuf[WLC_IOCTL_SMLEN];
- roam_band = band;
- error = wldev_iovar_getint(dev, "roamscan_mode", &wes_mode);
- if (error) {
- WL_ERR(("Failed to get roamscan mode, error = %d\n", error));
- return;
- }
- ioctl_ver = wl_cfg80211_get_ioctl_version();
- /* in case of WES mode, update channel list by band based on the cache in DHD */
- if (wes_mode) {
- int n = 0;
- chanlist_before.n = n_roam_cache;
- for (n = 0; n < n_roam_cache; n++) {
- chanspec_t ch = roam_cache[n].chanspec;
- bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
- chanlist_before.channels[n] = CHSPEC_CHANNEL(ch) |
- (is_2G ? band2G : band5G) | band_bw;
- }
- } else {
- if (band == WLC_BAND_AUTO) {
- return;
- }
- error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
- (void *)&chanlist_before, sizeof(channel_list_t), NULL);
- if (error) {
- WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
- return;
- }
- }
- chanlist_after.n = 0;
- /* filtering by the given band */
- for (i = 0; i < chanlist_before.n; i++) {
- chanspec_t chspec = chanlist_before.channels[i];
- bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(chspec) : CHSPEC_IS2G(chspec);
- bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(chspec) : CHSPEC_IS5G(chspec);
- bool band_match = ((band == WLC_BAND_AUTO) ||
- ((band == WLC_BAND_2G) && is_2G) ||
- ((band == WLC_BAND_5G) && is_5G));
- if (band_match) {
- chanlist_after.channels[chanlist_after.n++] = chspec;
- }
- }
- if (wes_mode) {
- /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
- * otherwise, it won't be updated
- */
- wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
- error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
- sizeof(channel_list_t), iobuf, sizeof(iobuf), NULL);
- if (error) {
- WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
- }
- wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
- } else {
- if (chanlist_before.n == chanlist_after.n) {
- return;
- }
- error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
- sizeof(channel_list_t), iobuf, sizeof(iobuf), NULL);
- if (error) {
- WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
- }
- }
- }
|