From cde9070d9970eef1f7ec2360586c802a16230ad8 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 10 May 2024 07:43:50 +0000 Subject: [PATCH] rtl88x2CE_WiFi_linux driver --- kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/wl_roam.c | 519 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 513 insertions(+), 6 deletions(-) diff --git a/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/wl_roam.c b/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/wl_roam.c index aa5f6ff..e3be4aa 100644 --- a/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/wl_roam.c +++ b/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/wl_roam.c @@ -1,15 +1,16 @@ -/* SPDX-License-Identifier: GPL-2.0 */ /* * Linux roam cache * - * Copyright (C) 1999-2019, Broadcom Corporation - * + * Portions of this code are copyright (c) 2022 Cypress Semiconductor Corporation + * + * Copyright (C) 1999-2017, 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 @@ -17,7 +18,7 @@ * 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. @@ -25,5 +26,511 @@ * * <<Broadcom-WL-IPTag/Open:>> * - * $Id: wl_roam.c 589977 2015-10-01 07:03:40Z $ + * $Id: wl_roam.c 798173 2019-01-07 09:23:21Z $ */ + +#include <typedefs.h> +#include <osl.h> +#include <bcmwifi_channels.h> +#include <wlioctl.h> +#include <bcmutils.h> +#ifdef WL_CFG80211 +#include <wl_cfg80211.h> +#endif // endif +#include <wldev_common.h> +#include <bcmstdlib_s.h> + +#ifdef ESCAN_CHANNEL_CACHE +#define MAX_ROAM_CACHE 200 +#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; + +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, band6G, band_bw; + +#ifdef WES_SUPPORT +static int roamscan_mode = ROAMSCAN_MODE_NORMAL; +#endif /* WES_SUPPORT */ + +#ifdef ROAM_CHANNEL_CACHE +int init_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver) +{ + int err; + struct net_device *dev = bcmcfg_to_prmry_ndev(cfg); + s32 mode; + + /* Check support in firmware */ + err = wldev_iovar_getint(dev, "roamscan_mode", &mode); + if (err && (err == BCME_UNSUPPORTED)) { + /* If firmware doesn't support, return error. Else proceed */ + WL_ERR(("roamscan_mode iovar failed. %d\n", err)); + return err; + } + +#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; + band6G = WL_CHANSPEC_BAND_6G; + band_bw = WL_CHANSPEC_BW_20; + } +#else + band2G = WL_CHANSPEC_BAND_2G; + band5G = WL_CHANSPEC_BAND_5G; + band6G = WL_CHANSPEC_BAND_6G; + band_bw = WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE; +#endif /* D11AC_IOTYPES */ + + n_roam_cache = 0; + roam_band = WLC_BAND_AUTO; +#ifdef WES_SUPPORT + roamscan_mode = ROAMSCAN_MODE_NORMAL; +#endif /* WES_SUPPORT */ + + return 0; +} +#endif /* ROAM_CHANNEL_CACHE */ + +#ifdef 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_channels) +{ + int n = 0; + int max_channel_number = MIN(n_channels, n_roam_cache); + + if (roamscan_mode == ROAMSCAN_MODE_WES) { + for (n = 0; n < max_channel_number; 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; + wl_roam_channel_list_t channel_list; + char iobuf[WLC_IOCTL_SMLEN]; + roamscan_mode = ROAMSCAN_MODE_WES; + + if (n > MAX_ROAM_CHANNEL) + n = MAX_ROAM_CHANNEL; + + 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 + */ + error = wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL); + if (error) { + WL_ERR(("Failed to set roamscan mode to %d, error = %d\n", + ROAMSCAN_MODE_NORMAL, error)); + return error; + } + error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list, + sizeof(channel_list), iobuf, sizeof(iobuf), NULL); + if (error) { + WL_ERR(("Failed to set roamscan channels, error = %d\n", error)); + return error; + } + error = wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES); + if (error) { + WL_ERR(("Failed to set roamscan mode to %d, error = %d\n", + ROAMSCAN_MODE_WES, error)); + } + + return error; +} +#endif /* WES_SUPPORT */ + +#ifdef ESCAN_CHANNEL_CACHE +void set_roam_band(int band) +{ + roam_band = band; +} + +void reset_roam_cache(struct bcm_cfg80211 *cfg) +{ + if (!cfg->rcc_enabled) { + return; + } + +#ifdef WES_SUPPORT + if (roamscan_mode == ROAMSCAN_MODE_WES) + return; +#endif /* WES_SUPPORT */ + + n_roam_cache = 0; +} + +void add_roam_cache(struct bcm_cfg80211 *cfg, wl_bss_info_t *bi) +{ + int i; + uint8 channel; + char chanbuf[CHANSPEC_STR_LEN]; + + if (!cfg->rcc_enabled) { + return; + } + +#ifdef WES_SUPPORT + if (roamscan_mode == ROAMSCAN_MODE_WES) + return; +#endif /* WES_SUPPORT */ + + 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 = wf_chspec_ctlchan(bi->chanspec); + WL_DBG(("CHSPEC = %s, CTL %d\n", wf_chspec_ntoa_ex(bi->chanspec, chanbuf), channel)); + roam_cache[n_roam_cache].chanspec = + (CHSPEC_IS6G(wl_chspec_driver_to_host(bi->chanspec))? + band6G : (channel <= CH_MAX_2G_CHANNEL ? band2G : band5G)) | band_bw | channel; + (void)memcpy_s(roam_cache[n_roam_cache].ssid, bi->SSID_len, 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, int n_channels, const wlc_ssid_t *ssid, int ioctl_ver, + struct ieee80211_channel *chan) +{ + int i, n = 1; + char chanbuf[CHANSPEC_STR_LEN]; + + /* first index is filled with the given target channel */ + if (target_chan) { + channels[0] = (target_chan & WL_CHANSPEC_CHAN_MASK) | + (chan->center_freq > FREQ_START_6G_CHANNEL ? + band6G : (target_chan <= CH_MAX_2G_CHANNEL ? band2G : band5G)) | band_bw; + } else { + /* If target channel is not provided, set the index to 0 */ + n = 0; + } + + WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__, target_chan, channels[0])); + +#ifdef 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 is_6G = CHSPEC_IS6G(ch); + bool band_match = ((roam_band == WLC_BAND_AUTO) || + ((roam_band == WLC_BAND_2G) && is_2G) || + ((roam_band == WLC_BAND_5G) && is_5G) || + ((roam_band == WLC_BAND_6G) && is_6G)); + + ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw; + ch = CHSPEC_CHANNEL(ch) | + (is_6G ? band6G : (is_2G ? band2G : band5G)) | band_bw; + if (band_match && !is_duplicated_channel(channels, n, ch)) { + WL_DBG(("%s: Chanspec = %s\n", __FUNCTION__, + wf_chspec_ntoa_ex(ch, chanbuf))); + channels[n++] = ch; + if (n >= n_channels) { + WL_ERR(("Too many roam scan channels\n")); + return n; + } + } + } + + 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 is_6G = CHSPEC_IS6G(ch); + bool band_match = ((roam_band == WLC_BAND_AUTO) || + ((roam_band == WLC_BAND_2G) && is_2G) || + ((roam_band == WLC_BAND_5G) && is_5G) || + ((roam_band == WLC_BAND_6G) && is_6G)); + + ch = CHSPEC_CHANNEL(ch) | (is_6G ? band6G : (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: Chanspec = %s\n", __FUNCTION__, + wf_chspec_ntoa_ex(ch, chanbuf))); + channels[n++] = ch; + if (n >= n_channels) { + WL_ERR(("Too many roam scan channels\n")); + return n; + } + } + } + + return n; +} +#endif /* ESCAN_CHANNEL_CACHE */ + +#ifdef ROAM_CHANNEL_CACHE +void print_roam_cache(struct bcm_cfg80211 *cfg) +{ + int i; + + if (!cfg->rcc_enabled) { + return; + } + + 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(wl_roam_channel_list_t *channels, chanspec_t ch) +{ + int i; + + if (channels->n >= MAX_ROAM_CHANNEL) /* 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; + wl_roam_channel_list_t channel_list; + char iobuf[WLC_IOCTL_SMLEN]; + struct net_device *dev = bcmcfg_to_prmry_ndev(cfg); + wlc_ssid_t ssid; + + if (!cfg->rcc_enabled) { + return; + } + +#ifdef WES_SUPPORT + if (roamscan_mode == ROAMSCAN_MODE_WES) { + /* no update when ROAMSCAN_MODE_WES */ + return; + } +#endif /* WES_SUPPORT */ + + 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); + if (error) { + WL_ERR(("Failed to get roamscan channels, error = %d\n", error)); + return; + } + + 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 is_6G = CHSPEC_IS6G(ch); + bool band_match = ((roam_band == WLC_BAND_AUTO) || + ((roam_band == WLC_BAND_2G) && is_2G) || + ((roam_band == WLC_BAND_5G) && is_5G) || + ((roam_band == WLC_BAND_6G) && is_6G)); + + 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_6G ? band6G : (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)); + } + } + + WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache, channel_list.n, error)); +} + +void wl_update_roamscan_cache_by_band(struct net_device *dev, int band) +{ + int i, error, ioctl_ver, wes_mode; + wl_roam_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); + bool is_6G = CHSPEC_IS6G(ch); + chanlist_before.channels[n] = CHSPEC_CHANNEL(ch) | + (is_6G ? band6G : (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(wl_roam_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 is_6G = CHSPEC_IS6G(chspec); + bool band_match = ((band == WLC_BAND_AUTO) || + ((band == WLC_BAND_2G) && is_2G) || + ((band == WLC_BAND_5G) && is_5G) || + ((band == WLC_BAND_6G) && is_6G)); + 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(wl_roam_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(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL); + if (error) { + WL_ERR(("Failed to update roamscan channels, error = %d\n", error)); + } + } +} +#endif /* ROAM_CHANNEL_CACHE */ +#endif /* ESCAN_CHANNEL_CACHE */ -- Gitblit v1.6.2