mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-28 09:53:15 -04:00
wifi: nxp: add nxpwifi driver for IW61x
Add support for the NXP IW61x wireless devices. The nxpwifi driver implements a full-MAC design and integrates with cfg80211 for configuration and control, supporting both station (STA) and access point (AP) modes. The driver provides a firmware-based command/event interface using TLV messages, with the core handling command processing, event dispatching, and device lifecycle management. A SDIO transport layer is implemented to support IW61x devices. Key features include: - 802.11n/ac/ax (HT/VHT/HE) capability support - Scan, association, and connection management - Data path handling for TX/RX, including aggregation and reorder - WMM QoS support and traffic prioritization - 802.11h (DFS/TPC) support for regulatory compliance - cfg80211 integration for STA and AP operations - Debugfs and ethtool support - Wake-on-LAN support The driver translates cfg80211 configuration into firmware commands and implements required data path processing in software where needed. Signed-off-by: Jeff Chen <jeff.chen_1@nxp.com>
This commit is contained in:
@@ -19536,6 +19536,13 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
|
||||
F: drivers/ptp/ptp_netc.c
|
||||
|
||||
NXP NXPWIFI WIRELESS DRIVER
|
||||
M: Jeff Chen <jeff.chen_1@nxp.com>
|
||||
R: Francesco Dolcini <francesco@dolcini.it>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/nxp/nxpwifi
|
||||
|
||||
NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
|
||||
M: Woodrow Douglass <wdouglass@carnegierobotics.com>
|
||||
S: Maintained
|
||||
|
||||
@@ -27,6 +27,7 @@ source "drivers/net/wireless/intersil/Kconfig"
|
||||
source "drivers/net/wireless/marvell/Kconfig"
|
||||
source "drivers/net/wireless/mediatek/Kconfig"
|
||||
source "drivers/net/wireless/microchip/Kconfig"
|
||||
source "drivers/net/wireless/nxp/Kconfig"
|
||||
source "drivers/net/wireless/purelifi/Kconfig"
|
||||
source "drivers/net/wireless/ralink/Kconfig"
|
||||
source "drivers/net/wireless/realtek/Kconfig"
|
||||
|
||||
@@ -12,6 +12,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
|
||||
obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
|
||||
obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
|
||||
obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
|
||||
obj-$(CONFIG_WLAN_VENDOR_NXP) += nxp/
|
||||
obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
|
||||
obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
|
||||
obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
|
||||
|
||||
17
drivers/net/wireless/nxp/Kconfig
Normal file
17
drivers/net/wireless/nxp/Kconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config WLAN_VENDOR_NXP
|
||||
bool "NXP devices"
|
||||
default y
|
||||
help
|
||||
If you have a wireless card belonging to this class, say Y.
|
||||
|
||||
Note that the answer to this question doesn't directly affect the
|
||||
kernel: saying N will just cause the configurator to skip all the
|
||||
questions about these cards. If you say Y, you will be asked for
|
||||
your specific card in the following questions.
|
||||
|
||||
if WLAN_VENDOR_NXP
|
||||
|
||||
source "drivers/net/wireless/nxp/nxpwifi/Kconfig"
|
||||
|
||||
endif # WLAN_VENDOR_NXP
|
||||
3
drivers/net/wireless/nxp/Makefile
Normal file
3
drivers/net/wireless/nxp/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_NXPWIFI) += nxpwifi/
|
||||
280
drivers/net/wireless/nxp/nxpwifi/11ac.c
Normal file
280
drivers/net/wireless/nxp/nxpwifi/11ac.c
Normal file
@@ -0,0 +1,280 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi 802.11ac helpers
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "11ac.h"
|
||||
|
||||
/* Map VHT MCS/NSS to highest data rate (Mbps), long GI. */
|
||||
static const u16 max_rate_lgi_80MHZ[8][3] = {
|
||||
{0x124, 0x15F, 0x186}, /* NSS = 1 */
|
||||
{0x249, 0x2BE, 0x30C}, /* NSS = 2 */
|
||||
{0x36D, 0x41D, 0x492}, /* NSS = 3 */
|
||||
{0x492, 0x57C, 0x618}, /* NSS = 4 */
|
||||
{0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */
|
||||
{0x6DB, 0x83A, 0x0}, /* NSS = 6 */
|
||||
{0x7FF, 0x999, 0xAAA}, /* NSS = 7 */
|
||||
{0x924, 0xAF8, 0xC30} /* NSS = 8 */
|
||||
};
|
||||
|
||||
static const u16 max_rate_lgi_160MHZ[8][3] = {
|
||||
{0x249, 0x2BE, 0x30C}, /* NSS = 1 */
|
||||
{0x492, 0x57C, 0x618}, /* NSS = 2 */
|
||||
{0x6DB, 0x83A, 0x0}, /* NSS = 3 */
|
||||
{0x924, 0xAF8, 0xC30}, /* NSS = 4 */
|
||||
{0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */
|
||||
{0xDB6, 0x1074, 0x1248}, /* NSS = 6 */
|
||||
{0xFFF, 0x1332, 0x1554}, /* NSS = 7 */
|
||||
{0x1248, 0x15F0, 0x1860} /* NSS = 8 */
|
||||
};
|
||||
|
||||
/* Convert 2-bit MCS map to highest long-GI VHT data rate. */
|
||||
static u16
|
||||
nxpwifi_convert_mcsmap_to_maxrate(struct nxpwifi_private *priv,
|
||||
u16 bands, u16 mcs_map)
|
||||
{
|
||||
u8 i, nss, mcs;
|
||||
u16 max_rate = 0;
|
||||
u32 usr_vht_cap_info = 0;
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (bands & BAND_AAC)
|
||||
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
|
||||
else
|
||||
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
|
||||
|
||||
/* Find max supported NSS. */
|
||||
nss = 1;
|
||||
for (i = 1; i <= 8; i++) {
|
||||
mcs = GET_VHTNSSMCS(mcs_map, i);
|
||||
if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED)
|
||||
nss = i;
|
||||
}
|
||||
mcs = GET_VHTNSSMCS(mcs_map, nss);
|
||||
|
||||
/* If not supported, fall back to 0-9. */
|
||||
if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED)
|
||||
mcs = IEEE80211_VHT_MCS_SUPPORT_0_9;
|
||||
|
||||
if (u32_get_bits(usr_vht_cap_info, IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK)) {
|
||||
/* Support 160 MHz. */
|
||||
max_rate = max_rate_lgi_160MHZ[nss - 1][mcs];
|
||||
if (!max_rate)
|
||||
/* MCS9 not supported in NSS6. */
|
||||
max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1];
|
||||
} else {
|
||||
max_rate = max_rate_lgi_80MHZ[nss - 1][mcs];
|
||||
if (!max_rate)
|
||||
/* MCS9 not supported in NSS3. */
|
||||
max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1];
|
||||
}
|
||||
|
||||
return max_rate;
|
||||
}
|
||||
|
||||
static void
|
||||
nxpwifi_fill_vht_cap_info(struct nxpwifi_private *priv,
|
||||
struct ieee80211_vht_cap *vht_cap, u16 bands)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (bands & BAND_A)
|
||||
vht_cap->vht_cap_info =
|
||||
cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a);
|
||||
else
|
||||
vht_cap->vht_cap_info =
|
||||
cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg);
|
||||
}
|
||||
|
||||
void
|
||||
nxpwifi_fill_vht_cap_tlv(struct nxpwifi_private *priv,
|
||||
struct ieee80211_vht_cap *vht_cap, u16 bands)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u16 mcs_map_user, mcs_map_resp, mcs_map_result;
|
||||
u16 mcs_user, mcs_resp, nss, tmp;
|
||||
|
||||
/* Fill VHT capability info. */
|
||||
nxpwifi_fill_vht_cap_info(priv, vht_cap, bands);
|
||||
|
||||
/* RX MCS set: min(user, AP). */
|
||||
mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
|
||||
mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map);
|
||||
mcs_map_result = 0;
|
||||
|
||||
for (nss = 1; nss <= 8; nss++) {
|
||||
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
|
||||
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
|
||||
|
||||
if (mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED ||
|
||||
mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)
|
||||
SET_VHTNSSMCS(mcs_map_result, nss,
|
||||
IEEE80211_VHT_MCS_NOT_SUPPORTED);
|
||||
else
|
||||
SET_VHTNSSMCS(mcs_map_result, nss,
|
||||
min(mcs_user, mcs_resp));
|
||||
}
|
||||
|
||||
vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result);
|
||||
|
||||
tmp = nxpwifi_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
|
||||
vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp);
|
||||
|
||||
/* TX MCS set: min(user, AP). */
|
||||
mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support);
|
||||
mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
|
||||
mcs_map_result = 0;
|
||||
|
||||
for (nss = 1; nss <= 8; nss++) {
|
||||
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
|
||||
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
|
||||
if (mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED ||
|
||||
mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)
|
||||
SET_VHTNSSMCS(mcs_map_result, nss,
|
||||
IEEE80211_VHT_MCS_NOT_SUPPORTED);
|
||||
else
|
||||
SET_VHTNSSMCS(mcs_map_result, nss,
|
||||
min(mcs_user, mcs_resp));
|
||||
}
|
||||
|
||||
vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result);
|
||||
|
||||
tmp = nxpwifi_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
|
||||
vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp);
|
||||
}
|
||||
|
||||
int nxpwifi_cmd_append_11ac_tlv(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc,
|
||||
u8 **buffer)
|
||||
{
|
||||
struct nxpwifi_ie_types_vhtcap *vht_cap;
|
||||
struct nxpwifi_ie_types_oper_mode_ntf *oper_ntf;
|
||||
struct ieee_types_oper_mode_ntf *ieee_oper_ntf;
|
||||
struct nxpwifi_ie_types_vht_oper *vht_op;
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u8 supp_chwd_set;
|
||||
u32 usr_vht_cap_info;
|
||||
int ret_len = 0;
|
||||
|
||||
if (bss_desc->bss_band & BAND_A)
|
||||
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
|
||||
else
|
||||
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
|
||||
|
||||
/* VHT Capabilities element. */
|
||||
if (bss_desc->bcn_vht_cap) {
|
||||
vht_cap = (struct nxpwifi_ie_types_vhtcap *)*buffer;
|
||||
memset(vht_cap, 0, sizeof(*vht_cap));
|
||||
vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
|
||||
vht_cap->header.len =
|
||||
cpu_to_le16(sizeof(struct ieee80211_vht_cap));
|
||||
memcpy((u8 *)vht_cap + sizeof(struct nxpwifi_ie_types_header),
|
||||
(u8 *)bss_desc->bcn_vht_cap,
|
||||
le16_to_cpu(vht_cap->header.len));
|
||||
|
||||
nxpwifi_fill_vht_cap_tlv(priv, &vht_cap->vht_cap,
|
||||
bss_desc->bss_band);
|
||||
*buffer += sizeof(*vht_cap);
|
||||
ret_len += sizeof(*vht_cap);
|
||||
}
|
||||
|
||||
/* VHT Operation element. */
|
||||
if (bss_desc->bcn_vht_oper) {
|
||||
if (priv->bss_mode == NL80211_IFTYPE_STATION) {
|
||||
vht_op = (struct nxpwifi_ie_types_vht_oper *)*buffer;
|
||||
memset(vht_op, 0, sizeof(*vht_op));
|
||||
vht_op->header.type =
|
||||
cpu_to_le16(WLAN_EID_VHT_OPERATION);
|
||||
vht_op->header.len = cpu_to_le16(sizeof(*vht_op) -
|
||||
sizeof(struct nxpwifi_ie_types_header));
|
||||
memcpy((u8 *)vht_op +
|
||||
sizeof(struct nxpwifi_ie_types_header),
|
||||
(u8 *)bss_desc->bcn_vht_oper,
|
||||
le16_to_cpu(vht_op->header.len));
|
||||
|
||||
/* Negotiate channel width; keep peer's center freq. */
|
||||
supp_chwd_set = u32_get_bits(usr_vht_cap_info,
|
||||
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
|
||||
|
||||
switch (supp_chwd_set) {
|
||||
case 0:
|
||||
vht_op->chan_width =
|
||||
min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ,
|
||||
bss_desc->bcn_vht_oper->chan_width);
|
||||
break;
|
||||
case 1:
|
||||
vht_op->chan_width =
|
||||
min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ,
|
||||
bss_desc->bcn_vht_oper->chan_width);
|
||||
break;
|
||||
case 2:
|
||||
vht_op->chan_width =
|
||||
min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ,
|
||||
bss_desc->bcn_vht_oper->chan_width);
|
||||
break;
|
||||
default:
|
||||
vht_op->chan_width =
|
||||
IEEE80211_VHT_CHANWIDTH_USE_HT;
|
||||
break;
|
||||
}
|
||||
|
||||
*buffer += sizeof(*vht_op);
|
||||
ret_len += sizeof(*vht_op);
|
||||
}
|
||||
}
|
||||
|
||||
/* Operating Mode Notification element. */
|
||||
if (bss_desc->oper_mode) {
|
||||
ieee_oper_ntf = bss_desc->oper_mode;
|
||||
oper_ntf = (void *)*buffer;
|
||||
memset(oper_ntf, 0, sizeof(*oper_ntf));
|
||||
oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF);
|
||||
oper_ntf->header.len = cpu_to_le16(sizeof(u8));
|
||||
oper_ntf->oper_mode = ieee_oper_ntf->oper_mode;
|
||||
*buffer += sizeof(*oper_ntf);
|
||||
ret_len += sizeof(*oper_ntf);
|
||||
}
|
||||
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
int nxpwifi_cmd_11ac_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_11ac_vht_cfg *cfg)
|
||||
{
|
||||
struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_11AC_CFG);
|
||||
cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) +
|
||||
S_DS_GEN);
|
||||
vhtcfg->action = cpu_to_le16(cmd_action);
|
||||
vhtcfg->band_config = cfg->band_config;
|
||||
vhtcfg->misc_config = cfg->misc_config;
|
||||
vhtcfg->cap_info = cpu_to_le32(cfg->cap_info);
|
||||
vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set);
|
||||
vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize BlockAck parameters for 11ac. */
|
||||
void nxpwifi_set_11ac_ba_params(struct nxpwifi_private *priv)
|
||||
{
|
||||
priv->add_ba_param.timeout = NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT;
|
||||
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
|
||||
priv->add_ba_param.tx_win_size =
|
||||
NXPWIFI_11AC_UAP_AMPDU_DEF_TXWINSIZE;
|
||||
priv->add_ba_param.rx_win_size =
|
||||
NXPWIFI_11AC_UAP_AMPDU_DEF_RXWINSIZE;
|
||||
} else {
|
||||
priv->add_ba_param.tx_win_size =
|
||||
NXPWIFI_11AC_STA_AMPDU_DEF_TXWINSIZE;
|
||||
priv->add_ba_param.rx_win_size =
|
||||
NXPWIFI_11AC_STA_AMPDU_DEF_RXWINSIZE;
|
||||
}
|
||||
}
|
||||
33
drivers/net/wireless/nxp/nxpwifi/11ac.h
Normal file
33
drivers/net/wireless/nxp/nxpwifi/11ac.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* nxpwifi: 802.11ac (VHT) definitions
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_11AC_H_
|
||||
#define _NXPWIFI_11AC_H_
|
||||
|
||||
#define VHT_CFG_2GHZ BIT(0)
|
||||
#define VHT_CFG_5GHZ BIT(1)
|
||||
|
||||
enum vht_cfg_misc_config {
|
||||
VHT_CAP_TX_OPERATION = 1,
|
||||
VHT_CAP_ASSOCIATION,
|
||||
VHT_CAP_UAP_ONLY
|
||||
};
|
||||
|
||||
#define DEFAULT_VHT_MCS_SET 0xfffe
|
||||
#define DISABLE_VHT_MCS_SET 0xffff
|
||||
|
||||
#define VHT_BW_80_160_80P80 BIT(2)
|
||||
|
||||
int nxpwifi_cmd_append_11ac_tlv(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc,
|
||||
u8 **buffer);
|
||||
int nxpwifi_cmd_11ac_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_11ac_vht_cfg *cfg);
|
||||
void nxpwifi_fill_vht_cap_tlv(struct nxpwifi_private *priv,
|
||||
struct ieee80211_vht_cap *vht_cap, u16 bands);
|
||||
#endif /* _NXPWIFI_11AC_H_ */
|
||||
594
drivers/net/wireless/nxp/nxpwifi/11ax.c
Normal file
594
drivers/net/wireless/nxp/nxpwifi/11ax.c
Normal file
@@ -0,0 +1,594 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* nxpwifi: 802.11ax (HE) support
|
||||
* Copyright (C) 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "11ax.h"
|
||||
|
||||
void nxpwifi_update_11ax_cap(struct nxpwifi_adapter *adapter,
|
||||
struct hw_spec_extension *hw_he_cap)
|
||||
{
|
||||
struct nxpwifi_private *priv;
|
||||
struct nxpwifi_ie_types_he_cap *he_cap = NULL;
|
||||
struct nxpwifi_ie_types_he_cap *user_he_cap = NULL;
|
||||
u8 header_len = sizeof(struct nxpwifi_ie_types_header);
|
||||
u16 data_len = le16_to_cpu(hw_he_cap->header.len);
|
||||
bool he_cap_2g = false;
|
||||
int i;
|
||||
|
||||
if ((data_len + header_len) > sizeof(adapter->hw_he_cap)) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"hw_he_cap too big, len=%d\n",
|
||||
data_len);
|
||||
return;
|
||||
}
|
||||
|
||||
he_cap = (struct nxpwifi_ie_types_he_cap *)hw_he_cap;
|
||||
|
||||
if (he_cap->he_phy_cap[0] &
|
||||
(AX_2G_40MHZ_SUPPORT | AX_2G_20MHZ_SUPPORT)) {
|
||||
adapter->hw_2g_he_cap_len = data_len + header_len;
|
||||
memcpy(adapter->hw_2g_he_cap, (u8 *)hw_he_cap,
|
||||
adapter->hw_2g_he_cap_len);
|
||||
adapter->fw_bands |= BAND_GAX;
|
||||
he_cap_2g = true;
|
||||
nxpwifi_dbg_dump(adapter, CMD_D, "2.4G HE capability element ",
|
||||
adapter->hw_2g_he_cap,
|
||||
adapter->hw_2g_he_cap_len);
|
||||
} else {
|
||||
adapter->hw_he_cap_len = data_len + header_len;
|
||||
memcpy(adapter->hw_he_cap, (u8 *)hw_he_cap,
|
||||
adapter->hw_he_cap_len);
|
||||
adapter->fw_bands |= BAND_AAX;
|
||||
nxpwifi_dbg_dump(adapter, CMD_D, "5G HE capability element ",
|
||||
adapter->hw_he_cap,
|
||||
adapter->hw_he_cap_len);
|
||||
}
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
|
||||
if (he_cap_2g) {
|
||||
priv->user_2g_he_cap_len = adapter->hw_2g_he_cap_len;
|
||||
memcpy(priv->user_2g_he_cap, adapter->hw_2g_he_cap,
|
||||
sizeof(adapter->hw_2g_he_cap));
|
||||
user_he_cap = (struct nxpwifi_ie_types_he_cap *)
|
||||
priv->user_2g_he_cap;
|
||||
} else {
|
||||
priv->user_he_cap_len = adapter->hw_he_cap_len;
|
||||
memcpy(priv->user_he_cap, adapter->hw_he_cap,
|
||||
sizeof(adapter->hw_he_cap));
|
||||
user_he_cap = (struct nxpwifi_ie_types_he_cap *)
|
||||
priv->user_he_cap;
|
||||
}
|
||||
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
|
||||
user_he_cap->he_mac_cap[0] &=
|
||||
~HE_MAC_CAP_TWT_RESP_SUPPORT;
|
||||
else
|
||||
user_he_cap->he_mac_cap[0] &=
|
||||
~HE_MAC_CAP_TWT_REQ_SUPPORT;
|
||||
}
|
||||
|
||||
adapter->is_hw_11ax_capable = true;
|
||||
}
|
||||
|
||||
bool nxpwifi_11ax_bandconfig_allowed(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc)
|
||||
{
|
||||
u16 bss_band = bss_desc->bss_band;
|
||||
|
||||
if (bss_desc->disable_11n)
|
||||
return false;
|
||||
|
||||
if (bss_band & BAND_G)
|
||||
return (priv->config_bands & BAND_GAX);
|
||||
else if (bss_band & BAND_A)
|
||||
return (priv->config_bands & BAND_AAX);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int nxpwifi_fill_he_cap_tlv(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ie_types_he_cap *he_cap,
|
||||
u16 bands)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct nxpwifi_ie_types_he_cap *hw_he_cap = NULL;
|
||||
u16 rx_nss, tx_nss;
|
||||
u8 nss;
|
||||
u16 cfg_value;
|
||||
u16 hw_value;
|
||||
int ret_len;
|
||||
|
||||
if (bands & BAND_A) {
|
||||
memcpy(he_cap, priv->user_he_cap, priv->user_he_cap_len);
|
||||
hw_he_cap = (struct nxpwifi_ie_types_he_cap *)adapter->hw_he_cap;
|
||||
ret_len = priv->user_he_cap_len;
|
||||
} else {
|
||||
memcpy(he_cap, priv->user_2g_he_cap, priv->user_2g_he_cap_len);
|
||||
hw_he_cap = (struct nxpwifi_ie_types_he_cap *)adapter->hw_2g_he_cap;
|
||||
ret_len = priv->user_2g_he_cap_len;
|
||||
}
|
||||
|
||||
if (bands & BAND_A) {
|
||||
rx_nss = GET_RXMCSSUPP(adapter->user_htstream >> 8);
|
||||
tx_nss = GET_TXMCSSUPP(adapter->user_htstream >> 8) & 0x0f;
|
||||
} else {
|
||||
rx_nss = GET_RXMCSSUPP(adapter->user_htstream);
|
||||
tx_nss = GET_TXMCSSUPP(adapter->user_htstream) & 0x0f;
|
||||
}
|
||||
|
||||
for (nss = 1; nss <= 8; nss++) {
|
||||
cfg_value = nxpwifi_get_he_nss_mcs(he_cap->rx_mcs_80, nss);
|
||||
hw_value = nxpwifi_get_he_nss_mcs(hw_he_cap->rx_mcs_80, nss);
|
||||
if (rx_nss != 0 && nss > rx_nss)
|
||||
cfg_value = NO_NSS_SUPPORT;
|
||||
if (hw_value == NO_NSS_SUPPORT || cfg_value == NO_NSS_SUPPORT)
|
||||
nxpwifi_set_he_nss_mcs(&he_cap->rx_mcs_80, nss,
|
||||
NO_NSS_SUPPORT);
|
||||
else
|
||||
nxpwifi_set_he_nss_mcs(&he_cap->rx_mcs_80, nss,
|
||||
min(cfg_value, hw_value));
|
||||
}
|
||||
|
||||
for (nss = 1; nss <= 8; nss++) {
|
||||
cfg_value = nxpwifi_get_he_nss_mcs(he_cap->tx_mcs_80, nss);
|
||||
hw_value = nxpwifi_get_he_nss_mcs(hw_he_cap->tx_mcs_80, nss);
|
||||
if (tx_nss != 0 && nss > tx_nss)
|
||||
cfg_value = NO_NSS_SUPPORT;
|
||||
if (hw_value == NO_NSS_SUPPORT || cfg_value == NO_NSS_SUPPORT)
|
||||
nxpwifi_set_he_nss_mcs(&he_cap->tx_mcs_80, nss,
|
||||
NO_NSS_SUPPORT);
|
||||
else
|
||||
nxpwifi_set_he_nss_mcs(&he_cap->tx_mcs_80, nss,
|
||||
min(cfg_value, hw_value));
|
||||
}
|
||||
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
int nxpwifi_cmd_append_11ax_tlv(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc,
|
||||
u8 **buffer)
|
||||
{
|
||||
struct nxpwifi_ie_types_he_cap *he_cap = NULL;
|
||||
int ret_len;
|
||||
|
||||
if (!bss_desc->bcn_he_cap)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
he_cap = (struct nxpwifi_ie_types_he_cap *)*buffer;
|
||||
ret_len = nxpwifi_fill_he_cap_tlv(priv, he_cap, bss_desc->bss_band);
|
||||
*buffer += ret_len;
|
||||
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
int nxpwifi_cmd_11ax_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_11ax_he_cfg *ax_cfg)
|
||||
{
|
||||
struct host_cmd_11ax_cfg *he_cfg = &cmd->params.ax_cfg;
|
||||
u16 cmd_size;
|
||||
struct nxpwifi_ie_types_header *header;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_11AX_CFG);
|
||||
cmd_size = sizeof(struct host_cmd_11ax_cfg) + S_DS_GEN;
|
||||
|
||||
he_cfg->action = cpu_to_le16(cmd_action);
|
||||
he_cfg->band_config = ax_cfg->band;
|
||||
|
||||
if (ax_cfg->he_cap_cfg.len &&
|
||||
ax_cfg->he_cap_cfg.ext_id == WLAN_EID_EXT_HE_CAPABILITY) {
|
||||
header = (struct nxpwifi_ie_types_header *)he_cfg->tlv;
|
||||
header->type = cpu_to_le16(ax_cfg->he_cap_cfg.id);
|
||||
header->len = cpu_to_le16(ax_cfg->he_cap_cfg.len);
|
||||
memcpy(he_cfg->tlv + sizeof(*header),
|
||||
&ax_cfg->he_cap_cfg.ext_id,
|
||||
ax_cfg->he_cap_cfg.len);
|
||||
cmd_size += (sizeof(*header) + ax_cfg->he_cap_cfg.len);
|
||||
}
|
||||
|
||||
cmd->size = cpu_to_le16(cmd_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxpwifi_ret_11ax_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp,
|
||||
struct nxpwifi_11ax_he_cfg *ax_cfg)
|
||||
{
|
||||
struct host_cmd_11ax_cfg *he_cfg = &resp->params.ax_cfg;
|
||||
struct nxpwifi_ie_types_header *header;
|
||||
u16 left_len, tlv_type, tlv_len;
|
||||
u8 ext_id;
|
||||
struct nxpwifi_11ax_he_cap_cfg *he_cap = &ax_cfg->he_cap_cfg;
|
||||
|
||||
left_len = le16_to_cpu(resp->size) - sizeof(*he_cfg) - S_DS_GEN;
|
||||
header = (struct nxpwifi_ie_types_header *)he_cfg->tlv;
|
||||
|
||||
while (left_len > sizeof(*header)) {
|
||||
tlv_type = le16_to_cpu(header->type);
|
||||
tlv_len = le16_to_cpu(header->len);
|
||||
|
||||
if (tlv_type == TLV_TYPE_EXTENSION_ID) {
|
||||
ext_id = *((u8 *)header + sizeof(*header) + 1);
|
||||
if (ext_id == WLAN_EID_EXT_HE_CAPABILITY) {
|
||||
he_cap->id = tlv_type;
|
||||
he_cap->len = tlv_len;
|
||||
memcpy((u8 *)&he_cap->ext_id,
|
||||
(u8 *)header + sizeof(*header) + 1,
|
||||
tlv_len);
|
||||
if (he_cfg->band_config & BIT(1)) {
|
||||
memcpy(priv->user_he_cap,
|
||||
(u8 *)header,
|
||||
sizeof(*header) + tlv_len);
|
||||
priv->user_he_cap_len =
|
||||
sizeof(*header) + tlv_len;
|
||||
} else {
|
||||
memcpy(priv->user_2g_he_cap,
|
||||
(u8 *)header,
|
||||
sizeof(*header) + tlv_len);
|
||||
priv->user_2g_he_cap_len =
|
||||
sizeof(*header) + tlv_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
left_len -= (sizeof(*header) + tlv_len);
|
||||
header = (struct nxpwifi_ie_types_header *)((u8 *)header +
|
||||
sizeof(*header) +
|
||||
tlv_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxpwifi_cmd_11ax_cmd(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_11ax_cmd_cfg *ax_cmd)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct host_cmd_11ax_cmd *he_cmd = &cmd->params.ax_cmd;
|
||||
u16 cmd_size;
|
||||
struct nxpwifi_11ax_sr_cmd *sr_cmd;
|
||||
struct nxpwifi_ie_types_data *tlv;
|
||||
struct nxpwifi_11ax_beam_cmd *beam_cmd;
|
||||
struct nxpwifi_11ax_htc_cmd *htc_cmd;
|
||||
struct nxpwifi_11ax_txomi_cmd *txmoi_cmd;
|
||||
struct nxpwifi_11ax_toltime_cmd *toltime_cmd;
|
||||
struct nxpwifi_11ax_txop_cmd *txop_cmd;
|
||||
struct nxpwifi_11ax_set_bsrp_cmd *set_bsrp_cmd;
|
||||
struct nxpwifi_11ax_llde_cmd *llde_cmd;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_11AX_CMD);
|
||||
cmd_size = sizeof(struct host_cmd_11ax_cmd) + S_DS_GEN;
|
||||
|
||||
he_cmd->action = cpu_to_le16(cmd_action);
|
||||
he_cmd->sub_id = cpu_to_le16(ax_cmd->sub_id);
|
||||
|
||||
switch (ax_cmd->sub_command) {
|
||||
case NXPWIFI_11AXCMD_SR_SUBID:
|
||||
sr_cmd = (struct nxpwifi_11ax_sr_cmd *)&ax_cmd->param;
|
||||
|
||||
tlv = (struct nxpwifi_ie_types_data *)he_cmd->val;
|
||||
tlv->header.type = cpu_to_le16(sr_cmd->type);
|
||||
tlv->header.len = cpu_to_le16(sr_cmd->len);
|
||||
memcpy(tlv->data, sr_cmd->param.obss_pd_offset.offset,
|
||||
sr_cmd->len);
|
||||
cmd_size += (sizeof(tlv->header) + sr_cmd->len);
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_BEAM_SUBID:
|
||||
beam_cmd = (struct nxpwifi_11ax_beam_cmd *)&ax_cmd->param;
|
||||
|
||||
he_cmd->val[0] = beam_cmd->value;
|
||||
cmd_size += sizeof(*beam_cmd);
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_HTC_SUBID:
|
||||
htc_cmd = (struct nxpwifi_11ax_htc_cmd *)&ax_cmd->param;
|
||||
|
||||
he_cmd->val[0] = htc_cmd->value;
|
||||
cmd_size += sizeof(*htc_cmd);
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_TXOMI_SUBID:
|
||||
txmoi_cmd = (struct nxpwifi_11ax_txomi_cmd *)&ax_cmd->param;
|
||||
|
||||
memcpy((void *)he_cmd->val, txmoi_cmd, sizeof(*txmoi_cmd));
|
||||
cmd_size += sizeof(*txmoi_cmd);
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_OBSS_TOLTIME_SUBID:
|
||||
toltime_cmd = (struct nxpwifi_11ax_toltime_cmd *)&ax_cmd->param;
|
||||
|
||||
memcpy(he_cmd->val, &toltime_cmd->tol_time,
|
||||
sizeof(toltime_cmd->tol_time));
|
||||
cmd_size += sizeof(*toltime_cmd);
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_TXOPRTS_SUBID:
|
||||
txop_cmd = (struct nxpwifi_11ax_txop_cmd *)&ax_cmd->param;
|
||||
|
||||
memcpy(he_cmd->val, &txop_cmd->rts_thres,
|
||||
sizeof(txop_cmd->rts_thres));
|
||||
cmd_size += sizeof(*txop_cmd);
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_SET_BSRP_SUBID:
|
||||
set_bsrp_cmd = (struct nxpwifi_11ax_set_bsrp_cmd *)&ax_cmd->param;
|
||||
|
||||
he_cmd->val[0] = set_bsrp_cmd->value;
|
||||
cmd_size += sizeof(*set_bsrp_cmd);
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_LLDE_SUBID:
|
||||
llde_cmd = (struct nxpwifi_11ax_llde_cmd *)&ax_cmd->param;
|
||||
|
||||
memcpy((void *)he_cmd->val, llde_cmd, sizeof(*llde_cmd));
|
||||
cmd_size += sizeof(*llde_cmd);
|
||||
break;
|
||||
default:
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"%s: Unknown sub command: %d\n",
|
||||
__func__, ax_cmd->sub_command);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd->size = cpu_to_le16(cmd_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxpwifi_ret_11ax_cmd(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp,
|
||||
struct nxpwifi_11ax_cmd_cfg *ax_cmd)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct host_cmd_11ax_cmd *he_cmd = &resp->params.ax_cmd;
|
||||
struct nxpwifi_ie_types_data *tlv;
|
||||
|
||||
ax_cmd->sub_id = le16_to_cpu(he_cmd->sub_id);
|
||||
|
||||
switch (ax_cmd->sub_command) {
|
||||
case NXPWIFI_11AXCMD_SR_SUBID:
|
||||
tlv = (struct nxpwifi_ie_types_data *)he_cmd->val;
|
||||
memcpy(ax_cmd->param.sr_cfg.param.obss_pd_offset.offset,
|
||||
tlv->data,
|
||||
ax_cmd->param.sr_cfg.len);
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_BEAM_SUBID:
|
||||
ax_cmd->param.beam_cfg.value = *he_cmd->val;
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_HTC_SUBID:
|
||||
ax_cmd->param.htc_cfg.value = *he_cmd->val;
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_TXOMI_SUBID:
|
||||
memcpy(&ax_cmd->param.txomi_cfg,
|
||||
he_cmd->val, sizeof(ax_cmd->param.txomi_cfg));
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_OBSS_TOLTIME_SUBID:
|
||||
memcpy(&ax_cmd->param.toltime_cfg.tol_time,
|
||||
he_cmd->val, sizeof(ax_cmd->param.toltime_cfg));
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_TXOPRTS_SUBID:
|
||||
memcpy(&ax_cmd->param.txop_cfg.rts_thres,
|
||||
he_cmd->val, sizeof(ax_cmd->param.txop_cfg));
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_SET_BSRP_SUBID:
|
||||
ax_cmd->param.setbsrp_cfg.value = *he_cmd->val;
|
||||
break;
|
||||
case NXPWIFI_11AXCMD_LLDE_SUBID:
|
||||
memcpy(&ax_cmd->param.llde_cfg,
|
||||
he_cmd->val, sizeof(ax_cmd->param.llde_cfg));
|
||||
break;
|
||||
default:
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"%s: Unknown sub command: %d\n",
|
||||
__func__, ax_cmd->sub_command);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 nxpwifi_is_ap_11ax_twt_supported(struct nxpwifi_bssdescriptor *bss_desc)
|
||||
{
|
||||
struct element *ext_cap;
|
||||
|
||||
if (!bss_desc->bcn_he_cap)
|
||||
return false;
|
||||
if (!(bss_desc->bcn_he_cap->mac_cap_info[0] & HE_MAC_CAP_TWT_RESP_SUPPORT))
|
||||
return false;
|
||||
if (!bss_desc->bcn_ext_cap)
|
||||
return false;
|
||||
ext_cap = (struct element *)bss_desc->bcn_ext_cap;
|
||||
|
||||
if (!(ext_cap->data[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nxpwifi_is_11ax_twt_supported(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc)
|
||||
{
|
||||
struct nxpwifi_ie_types_he_cap *user_he_cap;
|
||||
struct nxpwifi_ie_types_he_cap *hw_he_cap;
|
||||
|
||||
if (bss_desc && (!nxpwifi_is_ap_11ax_twt_supported(bss_desc))) {
|
||||
nxpwifi_dbg(priv->adapter, MSG,
|
||||
"AP don't support twt feature\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bss_desc->bss_band & BAND_A) {
|
||||
hw_he_cap = (struct nxpwifi_ie_types_he_cap *)
|
||||
priv->adapter->hw_he_cap;
|
||||
user_he_cap = (struct nxpwifi_ie_types_he_cap *)
|
||||
priv->user_he_cap;
|
||||
} else {
|
||||
hw_he_cap = (struct nxpwifi_ie_types_he_cap *)
|
||||
priv->adapter->hw_2g_he_cap;
|
||||
user_he_cap = (struct nxpwifi_ie_types_he_cap *)
|
||||
priv->user_2g_he_cap;
|
||||
}
|
||||
|
||||
if (!(hw_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) {
|
||||
nxpwifi_dbg(priv->adapter, MSG,
|
||||
"FW don't support TWT\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(user_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) {
|
||||
nxpwifi_dbg(priv->adapter, MSG,
|
||||
"USER HE_MAC_CAP don't support TWT\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 nxpwifi_is_sta_11ax_twt_req_supported(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_ie_types_he_cap *user_he_cap;
|
||||
u8 ret = 0;
|
||||
|
||||
if (ISSUPP_11AXENABLED(priv->adapter->fw_cap_ext) &&
|
||||
(priv->config_bands & BAND_GAX || priv->config_bands & BAND_AAX)) {
|
||||
if (priv->config_bands & BAND_AAX)
|
||||
user_he_cap = (struct nxpwifi_ie_types_he_cap *)priv->user_he_cap;
|
||||
else
|
||||
user_he_cap = (struct nxpwifi_ie_types_he_cap *)priv->user_2g_he_cap;
|
||||
ret = user_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nxpwifi_cmd_twt_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_twt_cfg *twt_cfg)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct host_cmd_twt_cfg *twt_cfg_cmd = &cmd->params.twt_cfg;
|
||||
struct nxpwifi_twt_setup *twt_setup;
|
||||
struct nxpwifi_twt_teardown *twt_teardown;
|
||||
struct nxpwifi_twt_report *twt_report;
|
||||
struct nxpwifi_twt_information *twt_information;
|
||||
struct nxpwifi_btwt_ap_config *btwt_ap_config;
|
||||
u8 i;
|
||||
u16 cmd_size;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_TWT_CFG);
|
||||
cmd_size = sizeof(struct host_cmd_twt_cfg) + S_DS_GEN;
|
||||
|
||||
twt_cfg_cmd->action = cpu_to_le16(cmd_action);
|
||||
twt_cfg_cmd->sub_id = cpu_to_le16(twt_cfg->sub_id);
|
||||
|
||||
switch (twt_cfg->sub_id) {
|
||||
case NXPWIFI_11AX_TWT_SETUP_SUBID:
|
||||
twt_setup = (struct nxpwifi_twt_setup *)
|
||||
twt_cfg_cmd->val;
|
||||
|
||||
memset(twt_setup, 0x00, sizeof(struct nxpwifi_twt_setup));
|
||||
twt_setup->implicit = twt_cfg->param.twt_setup.implicit;
|
||||
twt_setup->announced = twt_cfg->param.twt_setup.announced;
|
||||
twt_setup->trigger_enabled = twt_cfg->param.twt_setup.trigger_enabled;
|
||||
twt_setup->twt_info_disabled = twt_cfg->param.twt_setup.twt_info_disabled;
|
||||
twt_setup->negotiation_type = twt_cfg->param.twt_setup.negotiation_type;
|
||||
twt_setup->twt_wakeup_duration =
|
||||
twt_cfg->param.twt_setup.twt_wakeup_duration;
|
||||
twt_setup->flow_identifier = twt_cfg->param.twt_setup.flow_identifier;
|
||||
twt_setup->hard_constraint = twt_cfg->param.twt_setup.hard_constraint;
|
||||
twt_setup->twt_exponent = twt_cfg->param.twt_setup.twt_exponent;
|
||||
twt_setup->twt_mantissa = twt_cfg->param.twt_setup.twt_mantissa;
|
||||
twt_setup->twt_request = twt_cfg->param.twt_setup.twt_request;
|
||||
twt_setup->bcn_miss_threshold = twt_cfg->param.twt_setup.bcn_miss_threshold;
|
||||
cmd_size += sizeof(struct nxpwifi_twt_setup);
|
||||
break;
|
||||
case NXPWIFI_11AX_TWT_TEARDOWN_SUBID:
|
||||
twt_teardown = (struct nxpwifi_twt_teardown *)
|
||||
twt_cfg_cmd->val;
|
||||
memset(twt_teardown, 0x00,
|
||||
sizeof(struct nxpwifi_twt_teardown));
|
||||
twt_teardown->flow_identifier =
|
||||
twt_cfg->param.twt_teardown.flow_identifier;
|
||||
twt_teardown->negotiation_type =
|
||||
twt_cfg->param.twt_teardown.negotiation_type;
|
||||
twt_teardown->teardown_all_twt =
|
||||
twt_cfg->param.twt_teardown.teardown_all_twt;
|
||||
cmd_size += sizeof(struct nxpwifi_twt_teardown);
|
||||
break;
|
||||
case NXPWIFI_11AX_TWT_REPORT_SUBID:
|
||||
twt_report = (struct nxpwifi_twt_report *)
|
||||
twt_cfg_cmd->val;
|
||||
memset(twt_report, 0x00, sizeof(struct nxpwifi_twt_report));
|
||||
twt_report->type = twt_cfg->param.twt_report.type;
|
||||
cmd_size += sizeof(struct nxpwifi_twt_report);
|
||||
break;
|
||||
case NXPWIFI_11AX_TWT_INFORMATION_SUBID:
|
||||
twt_information = (struct nxpwifi_twt_information *)
|
||||
twt_cfg_cmd->val;
|
||||
memset(twt_information, 0x00,
|
||||
sizeof(struct nxpwifi_twt_information));
|
||||
twt_information->flow_identifier =
|
||||
twt_cfg->param.twt_information.flow_identifier;
|
||||
twt_information->suspend_duration =
|
||||
twt_cfg->param.twt_information.suspend_duration;
|
||||
cmd_size += sizeof(struct nxpwifi_twt_information);
|
||||
break;
|
||||
case NXPWIFI_11AX_BTWT_AP_CONFIG_SUBID:
|
||||
btwt_ap_config = (struct nxpwifi_btwt_ap_config *)
|
||||
twt_cfg_cmd->val;
|
||||
memset(btwt_ap_config, 0x00,
|
||||
sizeof(struct nxpwifi_btwt_ap_config));
|
||||
btwt_ap_config->ap_bcast_bet_sta_wait =
|
||||
twt_cfg->param.btwt_ap_config.ap_bcast_bet_sta_wait;
|
||||
btwt_ap_config->ap_bcast_offset =
|
||||
twt_cfg->param.btwt_ap_config.ap_bcast_offset;
|
||||
btwt_ap_config->bcast_twtli =
|
||||
twt_cfg->param.btwt_ap_config.bcast_twtli;
|
||||
btwt_ap_config->count =
|
||||
twt_cfg->param.btwt_ap_config.count;
|
||||
for (i = 0; i < BTWT_AGREEMENT_MAX; i++) {
|
||||
btwt_ap_config->btwt_sets[i].btwt_id =
|
||||
twt_cfg->param.btwt_ap_config.btwt_sets[i].btwt_id;
|
||||
btwt_ap_config->btwt_sets[i].ap_bcast_mantissa =
|
||||
twt_cfg->param.btwt_ap_config.btwt_sets[i].ap_bcast_mantissa;
|
||||
btwt_ap_config->btwt_sets[i].ap_bcast_exponent =
|
||||
twt_cfg->param.btwt_ap_config.btwt_sets[i].ap_bcast_exponent;
|
||||
btwt_ap_config->btwt_sets[i].nominalwake =
|
||||
twt_cfg->param.btwt_ap_config.btwt_sets[i].nominalwake;
|
||||
}
|
||||
|
||||
cmd_size += sizeof(struct nxpwifi_btwt_ap_config);
|
||||
break;
|
||||
default:
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"Unknown sub id: %d\n", twt_cfg->sub_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd->size = cpu_to_le16(cmd_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxpwifi_ret_twt_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp,
|
||||
struct nxpwifi_twt_cfg *twt_cfg)
|
||||
{
|
||||
struct host_cmd_twt_cfg *twt_cfg_cmd = &resp->params.twt_cfg;
|
||||
u16 action;
|
||||
|
||||
action = le16_to_cpu(twt_cfg_cmd->action);
|
||||
twt_cfg->sub_id = le16_to_cpu(twt_cfg_cmd->sub_id);
|
||||
|
||||
if (action == HOST_ACT_GEN_GET &&
|
||||
twt_cfg->sub_id == NXPWIFI_11AX_TWT_REPORT_SUBID) {
|
||||
struct nxpwifi_twt_report *twt_report =
|
||||
(struct nxpwifi_twt_report *)twt_cfg_cmd->val;
|
||||
|
||||
memcpy(&twt_cfg->param.twt_report, twt_report, sizeof(struct nxpwifi_twt_report));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
73
drivers/net/wireless/nxp/nxpwifi/11ax.h
Normal file
73
drivers/net/wireless/nxp/nxpwifi/11ax.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* nxpwifi: 802.11ax support
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_11AX_H_
|
||||
#define _NXPWIFI_11AX_H_
|
||||
|
||||
/* device support 2.4G 40MHZ */
|
||||
#define AX_2G_40MHZ_SUPPORT BIT(1)
|
||||
/* device support 2.4G 242 tone RUs */
|
||||
#define AX_2G_20MHZ_SUPPORT BIT(5)
|
||||
|
||||
/* Get HE MCS map code for n spatial streams (0..3). */
|
||||
static inline u16
|
||||
nxpwifi_get_he_nss_mcs(__le16 mcs_map_set, int nss) {
|
||||
return ((le16_to_cpu(mcs_map_set) >> (2 * (nss - 1))) & 0x3);
|
||||
}
|
||||
|
||||
static inline void
|
||||
nxpwifi_set_he_nss_mcs(__le16 *mcs_map_set, int nss, int value) {
|
||||
u16 temp;
|
||||
|
||||
temp = le16_to_cpu(*mcs_map_set);
|
||||
temp |= ((value & 0x3) << (2 * (nss - 1)));
|
||||
*mcs_map_set = cpu_to_le16(temp);
|
||||
}
|
||||
|
||||
bool nxpwifi_is_11ax_twt_supported(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc);
|
||||
|
||||
void nxpwifi_update_11ax_cap(struct nxpwifi_adapter *adapter,
|
||||
struct hw_spec_extension *hw_he_cap);
|
||||
|
||||
bool nxpwifi_11ax_bandconfig_allowed(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc);
|
||||
|
||||
int nxpwifi_cmd_append_11ax_tlv(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc,
|
||||
u8 **buffer);
|
||||
|
||||
int nxpwifi_fill_he_cap_tlv(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ie_types_he_cap *he_cap,
|
||||
u16 bands);
|
||||
int nxpwifi_cmd_11ax_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_11ax_he_cfg *ax_cfg);
|
||||
|
||||
int nxpwifi_ret_11ax_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp,
|
||||
struct nxpwifi_11ax_he_cfg *ax_cfg);
|
||||
|
||||
int nxpwifi_cmd_11ax_cmd(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_11ax_cmd_cfg *ax_cmd);
|
||||
|
||||
int nxpwifi_ret_11ax_cmd(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp,
|
||||
struct nxpwifi_11ax_cmd_cfg *ax_cmd);
|
||||
|
||||
int nxpwifi_cmd_twt_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_twt_cfg *twt_cfg);
|
||||
|
||||
int nxpwifi_ret_twt_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp,
|
||||
struct nxpwifi_twt_cfg *twt_cfg);
|
||||
|
||||
u8 nxpwifi_is_sta_11ax_twt_req_supported(struct nxpwifi_private *priv);
|
||||
|
||||
#endif /* _NXPWIFI_11AX_H_ */
|
||||
339
drivers/net/wireless/nxp/nxpwifi/11h.c
Normal file
339
drivers/net/wireless/nxp/nxpwifi/11h.c
Normal file
@@ -0,0 +1,339 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi: 802.11h helpers
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
#include "fw.h"
|
||||
#include "cfg80211.h"
|
||||
|
||||
void nxpwifi_init_11h_params(struct nxpwifi_private *priv)
|
||||
{
|
||||
priv->state_11h.is_11h_enabled = true;
|
||||
priv->state_11h.is_11h_active = false;
|
||||
}
|
||||
|
||||
int nxpwifi_is_11h_active(struct nxpwifi_private *priv)
|
||||
{
|
||||
return priv->state_11h.is_11h_active;
|
||||
}
|
||||
|
||||
/* appends 11h info to a buffer while joining an infrastructure BSS */
|
||||
static void
|
||||
nxpwifi_11h_process_infra_join(struct nxpwifi_private *priv, u8 **buffer,
|
||||
struct nxpwifi_bssdescriptor *bss_desc)
|
||||
{
|
||||
struct nxpwifi_ie_types_header *ie_header;
|
||||
struct nxpwifi_ie_types_pwr_capability *cap;
|
||||
struct nxpwifi_ie_types_local_pwr_constraint *constraint;
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 radio_type;
|
||||
int i;
|
||||
|
||||
if (!buffer || !(*buffer))
|
||||
return;
|
||||
|
||||
radio_type = nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
|
||||
sband = priv->wdev.wiphy->bands[radio_type];
|
||||
|
||||
cap = (struct nxpwifi_ie_types_pwr_capability *)*buffer;
|
||||
cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY);
|
||||
cap->header.len = cpu_to_le16(2);
|
||||
cap->min_pwr = 0;
|
||||
cap->max_pwr = 0;
|
||||
*buffer += sizeof(*cap);
|
||||
|
||||
constraint = (struct nxpwifi_ie_types_local_pwr_constraint *)*buffer;
|
||||
constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT);
|
||||
constraint->header.len = cpu_to_le16(2);
|
||||
constraint->chan = bss_desc->channel;
|
||||
constraint->constraint = bss_desc->local_constraint;
|
||||
*buffer += sizeof(*constraint);
|
||||
|
||||
ie_header = (struct nxpwifi_ie_types_header *)*buffer;
|
||||
ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
|
||||
ie_header->len = cpu_to_le16(2 * sband->n_channels + 2);
|
||||
*buffer += sizeof(*ie_header);
|
||||
*(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS;
|
||||
*(*buffer)++ = 2 * sband->n_channels;
|
||||
for (i = 0; i < sband->n_channels; i++) {
|
||||
u32 center_freq;
|
||||
|
||||
center_freq = sband->channels[i].center_freq;
|
||||
*(*buffer)++ = ieee80211_frequency_to_channel(center_freq);
|
||||
*(*buffer)++ = 1; /* one channel in the subband */
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable or disable the 11h extensions in the firmware */
|
||||
int nxpwifi_11h_activate(struct nxpwifi_private *priv, bool flag)
|
||||
{
|
||||
u32 enable = flag;
|
||||
|
||||
/* enable master mode radar detection on AP interface */
|
||||
if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) && enable)
|
||||
enable |= NXPWIFI_MASTER_RADAR_DET_MASK;
|
||||
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
|
||||
HOST_ACT_GEN_SET, DOT11H_I, &enable, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process TLV buffer for a pending BSS join. Enable 11h in firmware when the
|
||||
* network advertises spectrum management, and add required TLVs based on the
|
||||
* BSS's 11h capability.
|
||||
*/
|
||||
void nxpwifi_11h_process_join(struct nxpwifi_private *priv, u8 **buffer,
|
||||
struct nxpwifi_bssdescriptor *bss_desc)
|
||||
{
|
||||
if (bss_desc->sensed_11h) {
|
||||
/* Activate 11h functions in firmware, turns on capability bit */
|
||||
nxpwifi_11h_activate(priv, true);
|
||||
priv->state_11h.is_11h_active = true;
|
||||
bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT;
|
||||
nxpwifi_11h_process_infra_join(priv, buffer, bss_desc);
|
||||
} else {
|
||||
/* Deactivate 11h functions in the firmware */
|
||||
nxpwifi_11h_activate(priv, false);
|
||||
priv->state_11h.is_11h_active = false;
|
||||
bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DFS CAC work function. This delayed work emits CAC finished event for cfg80211
|
||||
* if CAC was started earlier
|
||||
*/
|
||||
void nxpwifi_dfs_cac_work(struct wiphy *wiphy, struct wiphy_work *work)
|
||||
{
|
||||
struct cfg80211_chan_def chandef;
|
||||
struct wiphy_delayed_work *delayed_work =
|
||||
container_of(work, struct wiphy_delayed_work, work);
|
||||
struct nxpwifi_private *priv = container_of(delayed_work,
|
||||
struct nxpwifi_private,
|
||||
dfs_cac_work);
|
||||
|
||||
chandef = priv->dfs_chandef;
|
||||
if (priv->wdev.links[0].cac_started) {
|
||||
nxpwifi_dbg(priv->adapter, MSG,
|
||||
"CAC timer finished; No radar detected\n");
|
||||
cfg80211_cac_event(priv->netdev, &chandef,
|
||||
NL80211_RADAR_CAC_FINISHED,
|
||||
GFP_KERNEL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* prepares channel report request command to FW for starting radar detection */
|
||||
int nxpwifi_cmd_issue_chan_report_request(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd,
|
||||
void *data_buf)
|
||||
{
|
||||
struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req;
|
||||
struct nxpwifi_radar_params *radar_params = (void *)data_buf;
|
||||
u16 size;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_CHAN_REPORT_REQUEST);
|
||||
size = S_DS_GEN;
|
||||
|
||||
cr_req->chan_desc.start_freq = cpu_to_le16(NXPWIFI_A_BAND_START_FREQ);
|
||||
nxpwifi_convert_chan_to_band_cfg(priv,
|
||||
&cr_req->chan_desc.band_cfg,
|
||||
radar_params->chandef);
|
||||
cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value;
|
||||
cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms);
|
||||
size += sizeof(*cr_req);
|
||||
|
||||
if (radar_params->cac_time_ms) {
|
||||
struct nxpwifi_ie_types_chan_rpt_data *rpt;
|
||||
|
||||
rpt = (struct nxpwifi_ie_types_chan_rpt_data *)((u8 *)cmd + size);
|
||||
rpt->header.type = cpu_to_le16(TLV_TYPE_CHANRPT_11H_BASIC);
|
||||
rpt->header.len = cpu_to_le16(sizeof(u8));
|
||||
rpt->meas_rpt_map = 1 << MEAS_RPT_MAP_RADAR_SHIFT_BIT;
|
||||
size += sizeof(*rpt);
|
||||
|
||||
nxpwifi_dbg(priv->adapter, MSG,
|
||||
"11h: issuing DFS Radar check for channel=%d\n",
|
||||
radar_params->chandef->chan->hw_value);
|
||||
} else {
|
||||
nxpwifi_dbg(priv->adapter, MSG, "cancelling CAC\n");
|
||||
}
|
||||
|
||||
cmd->size = cpu_to_le16(size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxpwifi_stop_radar_detection(struct nxpwifi_private *priv,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct nxpwifi_radar_params radar_params;
|
||||
|
||||
memset(&radar_params, 0, sizeof(struct nxpwifi_radar_params));
|
||||
radar_params.chandef = chandef;
|
||||
radar_params.cac_time_ms = 0;
|
||||
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REPORT_REQUEST,
|
||||
HOST_ACT_GEN_SET, 0, &radar_params, true);
|
||||
}
|
||||
|
||||
/* Abort ongoing CAC when stopping AP operations or during unload */
|
||||
void nxpwifi_abort_cac(struct nxpwifi_private *priv)
|
||||
{
|
||||
if (priv->wdev.links[0].cac_started) {
|
||||
if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef))
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"failed to stop CAC in FW\n");
|
||||
nxpwifi_dbg(priv->adapter, MSG,
|
||||
"Aborting delayed work for CAC.\n");
|
||||
wiphy_delayed_work_cancel(priv->adapter->wiphy, &priv->dfs_cac_work);
|
||||
cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
|
||||
NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handles channel report event from FW during CAC period. If radar is detected
|
||||
* during CAC, driver indicates the same to cfg80211 and also cancels ongoing
|
||||
* delayed work
|
||||
*/
|
||||
int nxpwifi_11h_handle_chanrpt_ready(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct host_cmd_ds_chan_rpt_event *rpt_event;
|
||||
struct nxpwifi_ie_types_chan_rpt_data *rpt;
|
||||
u16 event_len, tlv_len;
|
||||
|
||||
rpt_event = (void *)(skb->data + sizeof(u32));
|
||||
event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event) +
|
||||
sizeof(u32));
|
||||
|
||||
if (le32_to_cpu(rpt_event->result) != HOST_RESULT_OK) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"Error in channel report event\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (event_len >= sizeof(struct nxpwifi_ie_types_header)) {
|
||||
rpt = (void *)&rpt_event->tlvbuf;
|
||||
tlv_len = le16_to_cpu(rpt->header.len);
|
||||
|
||||
switch (le16_to_cpu(rpt->header.type)) {
|
||||
case TLV_TYPE_CHANRPT_11H_BASIC:
|
||||
if (rpt->meas_rpt_map & MEAS_RPT_MAP_RADAR_MASK) {
|
||||
nxpwifi_dbg(priv->adapter, MSG,
|
||||
"RADAR Detected on channel %d!\n",
|
||||
priv->dfs_chandef.chan->hw_value);
|
||||
|
||||
wiphy_delayed_work_cancel(priv->adapter->wiphy,
|
||||
&priv->dfs_cac_work);
|
||||
cfg80211_cac_event(priv->netdev,
|
||||
&priv->dfs_chandef,
|
||||
NL80211_RADAR_CAC_ABORTED,
|
||||
GFP_KERNEL, 0);
|
||||
cfg80211_radar_event(priv->adapter->wiphy,
|
||||
&priv->dfs_chandef,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
event_len -= (tlv_len + sizeof(rpt->header));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handler for radar detected event from FW */
|
||||
int nxpwifi_11h_handle_radar_detected(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_radar_det_event *rdr_event;
|
||||
|
||||
rdr_event = (void *)(skb->data + sizeof(u32));
|
||||
|
||||
nxpwifi_dbg(priv->adapter, MSG,
|
||||
"radar detected; indicating kernel\n");
|
||||
|
||||
if (priv->wdev.links[0].cac_started) {
|
||||
if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef))
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"Failed to stop CAC in FW\n");
|
||||
wiphy_delayed_work_cancel(priv->adapter->wiphy, &priv->dfs_cac_work);
|
||||
cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
|
||||
NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0);
|
||||
}
|
||||
cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef,
|
||||
GFP_KERNEL);
|
||||
nxpwifi_dbg(priv->adapter, MSG, "regdomain: %d\n",
|
||||
rdr_event->reg_domain);
|
||||
nxpwifi_dbg(priv->adapter, MSG, "radar detection type: %d\n",
|
||||
rdr_event->det_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* work function for channel switch handling. takes care of updating new channel
|
||||
* definitin to bss config structure, restart AP and indicate channel switch
|
||||
* success to cfg80211
|
||||
*/
|
||||
void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *work)
|
||||
{
|
||||
struct nxpwifi_uap_bss_param *bss_cfg;
|
||||
struct wiphy_delayed_work *delayed_work =
|
||||
container_of(work, struct wiphy_delayed_work, work);
|
||||
struct nxpwifi_private *priv = container_of(delayed_work,
|
||||
struct nxpwifi_private,
|
||||
dfs_chan_sw_work);
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (nxpwifi_del_mgmt_ies(priv))
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"Failed to delete mgmt IEs!\n");
|
||||
|
||||
bss_cfg = &priv->bss_cfg;
|
||||
if (!bss_cfg->beacon_period) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"channel switch: AP already stopped\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP,
|
||||
HOST_ACT_GEN_SET, 0, NULL, true)) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"channel switch: Failed to stop the BSS\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nxpwifi_cfg80211_change_beacon(adapter->wiphy, priv->netdev,
|
||||
&priv->ap_update_info)) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"channel switch: Failed to set beacon\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nxpwifi_uap_set_channel(priv, bss_cfg, priv->dfs_chandef);
|
||||
|
||||
if (nxpwifi_config_start_uap(priv, bss_cfg)) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"Failed to start AP after channel switch\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nxpwifi_dbg(adapter, MSG,
|
||||
"indicating channel switch completion to kernel\n");
|
||||
|
||||
cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef, 0);
|
||||
|
||||
if (priv->uap_stop_tx) {
|
||||
netif_carrier_on(priv->netdev);
|
||||
nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter);
|
||||
priv->uap_stop_tx = false;
|
||||
}
|
||||
}
|
||||
837
drivers/net/wireless/nxp/nxpwifi/11n.c
Normal file
837
drivers/net/wireless/nxp/nxpwifi/11n.c
Normal file
@@ -0,0 +1,837 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi 802.11n helpers
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
#include "wmm.h"
|
||||
#include "11n.h"
|
||||
#include "11ax.h"
|
||||
|
||||
/*
|
||||
* Fills HT capability information field, AMPDU Parameters field, HT extended
|
||||
* capability field, and supported MCS set fields.
|
||||
*
|
||||
* HT capability information field, AMPDU Parameters field, supported MCS set
|
||||
* fields are retrieved from cfg80211 stack
|
||||
*
|
||||
* RD responder bit to set to clear in the extended capability header.
|
||||
*/
|
||||
int nxpwifi_fill_cap_info(struct nxpwifi_private *priv, u8 radio_type,
|
||||
struct ieee80211_ht_cap *ht_cap)
|
||||
{
|
||||
u16 ht_cap_info;
|
||||
u16 bcn_ht_cap = le16_to_cpu(ht_cap->cap_info);
|
||||
u16 ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
|
||||
struct ieee80211_supported_band *sband =
|
||||
priv->wdev.wiphy->bands[radio_type];
|
||||
|
||||
if (WARN_ON_ONCE(!sband)) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR, "Invalid radio type!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ht_cap->ampdu_params_info =
|
||||
(AMPDU_FACTOR_64K & IEEE80211_HT_AMPDU_PARM_FACTOR) |
|
||||
((priv->adapter->hw_mpdu_density <<
|
||||
IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &
|
||||
IEEE80211_HT_AMPDU_PARM_DENSITY);
|
||||
|
||||
memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,
|
||||
sizeof(sband->ht_cap.mcs));
|
||||
|
||||
if (priv->bss_mode == NL80211_IFTYPE_STATION ||
|
||||
(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
|
||||
priv->adapter->sec_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_NONE))
|
||||
/* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
|
||||
SETHT_MCS32(ht_cap->mcs.rx_mask);
|
||||
|
||||
/* Clear RD responder bit */
|
||||
ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;
|
||||
|
||||
ht_cap_info = sband->ht_cap.cap;
|
||||
if (bcn_ht_cap) {
|
||||
if (!(bcn_ht_cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
|
||||
ht_cap_info &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
if (!(bcn_ht_cap & IEEE80211_HT_CAP_SGI_40))
|
||||
ht_cap_info &= ~IEEE80211_HT_CAP_SGI_40;
|
||||
if (!(bcn_ht_cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
|
||||
ht_cap_info &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT;
|
||||
}
|
||||
ht_cap->cap_info = cpu_to_le16(ht_cap_info);
|
||||
ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
|
||||
|
||||
if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
|
||||
ht_cap->tx_BF_cap_info = cpu_to_le32(NXPWIFI_DEF_11N_TX_BF_CAP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return BA stream entry that matches the requested status. */
|
||||
static struct nxpwifi_tx_ba_stream_tbl *
|
||||
nxpwifi_get_ba_status(struct nxpwifi_private *priv, int tid,
|
||||
enum nxpwifi_ba_status ba_status)
|
||||
{
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl, *found = NULL;
|
||||
|
||||
guard(rcu)();
|
||||
list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], list) {
|
||||
if (tx_ba_tsr_tbl->ba_status == ba_status) {
|
||||
found = tx_ba_tsr_tbl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Handle DELBA command response (recreate or continue ADDBA as needed). */
|
||||
int nxpwifi_ret_11n_delba(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp)
|
||||
{
|
||||
int tid;
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl;
|
||||
struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba;
|
||||
u16 del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set);
|
||||
|
||||
tid = del_ba_param_set >> DELBA_TID_POS;
|
||||
if (del_ba->del_result == BA_RESULT_SUCCESS) {
|
||||
nxpwifi_del_ba_tbl(priv, tid, del_ba->peer_mac_addr,
|
||||
TYPE_DELBA_SENT,
|
||||
INITIATOR_BIT(del_ba_param_set));
|
||||
|
||||
tx_ba_tbl = nxpwifi_get_ba_status(priv, tid, BA_SETUP_INPROGRESS);
|
||||
if (tx_ba_tbl)
|
||||
nxpwifi_send_addba(priv, tx_ba_tbl->tid,
|
||||
tx_ba_tbl->ra);
|
||||
} else {
|
||||
/*
|
||||
* In case of failure, recreate the deleted stream in case
|
||||
* we initiated the DELBA
|
||||
*/
|
||||
if (!INITIATOR_BIT(del_ba_param_set))
|
||||
return 0;
|
||||
|
||||
nxpwifi_create_ba_tbl(priv, del_ba->peer_mac_addr, tid,
|
||||
BA_SETUP_INPROGRESS);
|
||||
|
||||
tx_ba_tbl = nxpwifi_get_ba_status(priv, tid, BA_SETUP_INPROGRESS);
|
||||
|
||||
if (tx_ba_tbl)
|
||||
nxpwifi_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra,
|
||||
TYPE_DELBA_SENT, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle ADDBA response; delete BA stream on failure. */
|
||||
int nxpwifi_ret_11n_addba_req(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp)
|
||||
{
|
||||
int tid, tid_down;
|
||||
struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl;
|
||||
struct nxpwifi_ra_list_tbl *ra_list;
|
||||
u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
|
||||
|
||||
add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
|
||||
& SSN_MASK);
|
||||
|
||||
tid = u16_get_bits(block_ack_param_set, IEEE80211_ADDBA_PARAM_TID_MASK);
|
||||
|
||||
tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
|
||||
ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down,
|
||||
add_ba_rsp->peer_mac_addr);
|
||||
if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
|
||||
if (ra_list) {
|
||||
ra_list->ba_status = BA_SETUP_NONE;
|
||||
ra_list->amsdu_in_ampdu = false;
|
||||
}
|
||||
nxpwifi_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
|
||||
TYPE_DELBA_SENT, true);
|
||||
if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
|
||||
priv->aggr_prio_tbl[tid].ampdu_ap =
|
||||
BA_STREAM_NOT_ALLOWED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
guard(rcu)();
|
||||
tx_ba_tbl = nxpwifi_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr);
|
||||
if (tx_ba_tbl) {
|
||||
nxpwifi_dbg(priv->adapter, EVENT, "info: BA stream complete\n");
|
||||
tx_ba_tbl->ba_status = BA_SETUP_COMPLETE;
|
||||
if ((block_ack_param_set & IEEE80211_ADDBA_PARAM_AMSDU_MASK) &&
|
||||
priv->add_ba_param.tx_amsdu &&
|
||||
priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
|
||||
tx_ba_tbl->amsdu = true;
|
||||
else
|
||||
tx_ba_tbl->amsdu = false;
|
||||
if (ra_list) {
|
||||
ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu;
|
||||
ra_list->ba_status = BA_SETUP_COMPLETE;
|
||||
}
|
||||
} else {
|
||||
nxpwifi_dbg(priv->adapter, ERROR, "BA stream not created\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconfigure Tx buffer command.
|
||||
* Set command ID/action/size; set Tx buffer size on SET; ensure little-endian.
|
||||
*/
|
||||
int nxpwifi_cmd_recfg_tx_buf(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, int cmd_action,
|
||||
u16 *buf_size)
|
||||
{
|
||||
struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;
|
||||
u16 action = (u16)cmd_action;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_RECONFIGURE_TX_BUFF);
|
||||
cmd->size =
|
||||
cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN);
|
||||
tx_buf->action = cpu_to_le16(action);
|
||||
switch (action) {
|
||||
case HOST_ACT_GEN_SET:
|
||||
nxpwifi_dbg(priv->adapter, CMD,
|
||||
"cmd: set tx_buf=%d\n", *buf_size);
|
||||
tx_buf->buff_size = cpu_to_le16(*buf_size);
|
||||
break;
|
||||
case HOST_ACT_GEN_GET:
|
||||
default:
|
||||
tx_buf->buff_size = 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* AMSDU aggregation control command.
|
||||
* Set ID/action/size; set AMSDU params on SET; ensure little-endian.
|
||||
*/
|
||||
int nxpwifi_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
|
||||
int cmd_action,
|
||||
struct nxpwifi_ds_11n_amsdu_aggr_ctrl *aa_ctrl)
|
||||
{
|
||||
struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
|
||||
&cmd->params.amsdu_aggr_ctrl;
|
||||
u16 action = (u16)cmd_action;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_AMSDU_AGGR_CTRL);
|
||||
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl)
|
||||
+ S_DS_GEN);
|
||||
amsdu_ctrl->action = cpu_to_le16(action);
|
||||
switch (action) {
|
||||
case HOST_ACT_GEN_SET:
|
||||
amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable);
|
||||
amsdu_ctrl->curr_buf_size = 0;
|
||||
break;
|
||||
case HOST_ACT_GEN_GET:
|
||||
default:
|
||||
amsdu_ctrl->curr_buf_size = 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 11n configuration command.
|
||||
* Set action, HT Tx capability/info, and misc config when 11ac HW is present.
|
||||
*/
|
||||
int nxpwifi_cmd_11n_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_ds_11n_tx_cfg *txcfg)
|
||||
{
|
||||
struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_11N_CFG);
|
||||
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN);
|
||||
htcfg->action = cpu_to_le16(cmd_action);
|
||||
htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap);
|
||||
htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo);
|
||||
|
||||
if (priv->adapter->is_hw_11ac_capable)
|
||||
htcfg->misc_config = cpu_to_le16(txcfg->misc_config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Append 11n TLVs to the caller-owned buffer.
|
||||
* Caller allocates space; no size checks here.
|
||||
* May add: HT Cap, HT Operation + channel list, 20/40 BSS Coexistence,
|
||||
* and Extended Capabilities (HS2/TWT bits when applicable).
|
||||
*/
|
||||
int
|
||||
nxpwifi_cmd_append_11n_tlv(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc,
|
||||
u8 **buffer)
|
||||
{
|
||||
struct nxpwifi_ie_types_htcap *ht_cap;
|
||||
struct nxpwifi_ie_types_chan_list_param_set *chan_list;
|
||||
struct nxpwifi_chan_scan_param_set *chan_param;
|
||||
struct nxpwifi_ie_types_2040bssco *bss_co_2040;
|
||||
struct nxpwifi_ie_types_extcap *ext_cap;
|
||||
int ret_len = 0;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct element *hdr;
|
||||
u8 radio_type;
|
||||
|
||||
if (!buffer || !*buffer)
|
||||
return ret_len;
|
||||
|
||||
radio_type = nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
|
||||
sband = priv->wdev.wiphy->bands[radio_type];
|
||||
|
||||
if (bss_desc->bcn_ht_cap) {
|
||||
ht_cap = (struct nxpwifi_ie_types_htcap *)*buffer;
|
||||
memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap));
|
||||
ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
|
||||
ht_cap->header.len =
|
||||
cpu_to_le16(sizeof(struct ieee80211_ht_cap));
|
||||
memcpy((u8 *)ht_cap + sizeof(struct nxpwifi_ie_types_header),
|
||||
(u8 *)bss_desc->bcn_ht_cap,
|
||||
le16_to_cpu(ht_cap->header.len));
|
||||
|
||||
nxpwifi_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
|
||||
/* Update HT40 capability from current channel. */
|
||||
if (bss_desc->bcn_ht_oper) {
|
||||
u8 ht_param = bss_desc->bcn_ht_oper->ht_param;
|
||||
u8 radio =
|
||||
nxpwifi_band_to_radio_type(bss_desc->bss_band);
|
||||
int freq =
|
||||
ieee80211_channel_to_frequency(bss_desc->channel,
|
||||
radio);
|
||||
struct ieee80211_channel *chan =
|
||||
ieee80211_get_channel(priv->adapter->wiphy, freq);
|
||||
|
||||
switch (ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) {
|
||||
ht_cap->ht_cap.cap_info &=
|
||||
cpu_to_le16
|
||||
(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
|
||||
ht_cap->ht_cap.cap_info &=
|
||||
cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
|
||||
}
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) {
|
||||
ht_cap->ht_cap.cap_info &=
|
||||
cpu_to_le16
|
||||
(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
|
||||
ht_cap->ht_cap.cap_info &=
|
||||
cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*buffer += sizeof(struct nxpwifi_ie_types_htcap);
|
||||
ret_len += sizeof(struct nxpwifi_ie_types_htcap);
|
||||
}
|
||||
|
||||
if (bss_desc->bcn_ht_oper) {
|
||||
chan_list =
|
||||
(struct nxpwifi_ie_types_chan_list_param_set *)*buffer;
|
||||
chan_param = chan_list->chan_scan_param;
|
||||
memset(chan_list, 0, struct_size(chan_list, chan_scan_param, 1));
|
||||
chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
|
||||
chan_list->header.len = cpu_to_le16(sizeof(*chan_param));
|
||||
chan_param->chan_number = bss_desc->bcn_ht_oper->primary_chan;
|
||||
chan_param->band_cfg =
|
||||
nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
|
||||
|
||||
if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
|
||||
bss_desc->bcn_vht_oper &&
|
||||
bss_desc->bcn_vht_oper->chan_width ==
|
||||
IEEE80211_VHT_CHANWIDTH_80MHZ) {
|
||||
SET_SECONDARYCHAN(chan_param->band_cfg,
|
||||
(bss_desc->bcn_ht_oper->ht_param &
|
||||
IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
|
||||
chan_param->band_cfg |=
|
||||
((CHAN_BW_80MHZ <<
|
||||
BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
|
||||
BAND_CFG_CHAN_WIDTH_MASK);
|
||||
} else if (sband->ht_cap.cap &
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
|
||||
bss_desc->bcn_ht_oper->ht_param &
|
||||
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) {
|
||||
SET_SECONDARYCHAN(chan_param->band_cfg,
|
||||
(bss_desc->bcn_ht_oper->ht_param &
|
||||
IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
|
||||
chan_param->band_cfg |=
|
||||
((CHAN_BW_40MHZ <<
|
||||
BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
|
||||
BAND_CFG_CHAN_WIDTH_MASK);
|
||||
}
|
||||
|
||||
*buffer += struct_size(chan_list, chan_scan_param, 1);
|
||||
ret_len += struct_size(chan_list, chan_scan_param, 1);
|
||||
}
|
||||
|
||||
if (bss_desc->bcn_bss_co_2040) {
|
||||
bss_co_2040 = (struct nxpwifi_ie_types_2040bssco *)*buffer;
|
||||
memset(bss_co_2040, 0,
|
||||
sizeof(struct nxpwifi_ie_types_2040bssco));
|
||||
bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040);
|
||||
bss_co_2040->header.len =
|
||||
cpu_to_le16(sizeof(bss_co_2040->bss_co_2040));
|
||||
|
||||
memcpy((u8 *)bss_co_2040 +
|
||||
sizeof(struct nxpwifi_ie_types_header),
|
||||
bss_desc->bcn_bss_co_2040 +
|
||||
sizeof(struct element),
|
||||
le16_to_cpu(bss_co_2040->header.len));
|
||||
|
||||
*buffer += sizeof(struct nxpwifi_ie_types_2040bssco);
|
||||
ret_len += sizeof(struct nxpwifi_ie_types_2040bssco);
|
||||
}
|
||||
|
||||
if (bss_desc->bcn_ext_cap) {
|
||||
u8 *ext_capab;
|
||||
|
||||
hdr = (void *)bss_desc->bcn_ext_cap;
|
||||
|
||||
ext_capab = (u8 *)cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, priv->gen_ie_buf,
|
||||
priv->gen_ie_buf_len);
|
||||
if (ext_capab) {
|
||||
ext_capab += 2;
|
||||
} else {
|
||||
ext_cap = (struct nxpwifi_ie_types_extcap *)*buffer;
|
||||
memset(ext_cap, 0, sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen);
|
||||
ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
|
||||
ext_cap->header.len = cpu_to_le16(hdr->datalen);
|
||||
ext_capab = ext_cap->ext_capab;
|
||||
*buffer += sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen;
|
||||
ret_len += sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen;
|
||||
}
|
||||
|
||||
if (hdr->datalen > 3 &&
|
||||
ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
|
||||
priv->hs2_enabled = true;
|
||||
else
|
||||
priv->hs2_enabled = false;
|
||||
|
||||
if (nxpwifi_is_11ax_twt_supported(priv, bss_desc))
|
||||
ext_capab[9] |=
|
||||
WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT;
|
||||
}
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
/* Check if pointer is a valid Tx BA stream entry. */
|
||||
static bool
|
||||
nxpwifi_is_tx_ba_stream_ptr_valid(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_tbl_ptr)
|
||||
{
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl;
|
||||
bool ret = false;
|
||||
int tid;
|
||||
|
||||
tid = tx_tbl_ptr->tid;
|
||||
guard(rcu)();
|
||||
list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], list) {
|
||||
if (tx_ba_tsr_tbl == tx_tbl_ptr) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Delete a Tx BA stream entry (after validating pointer). */
|
||||
void
|
||||
nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_tx_ba_stream_tbl *tbl)
|
||||
{
|
||||
if (!tbl && nxpwifi_is_tx_ba_stream_ptr_valid(priv, tbl))
|
||||
return;
|
||||
|
||||
nxpwifi_dbg(priv->adapter, INFO,
|
||||
"info: tx_ba_tsr_tbl %p\n", tbl);
|
||||
|
||||
list_del_rcu(&tbl->list);
|
||||
kfree_rcu(tbl, rcu);
|
||||
}
|
||||
|
||||
/* Delete all entries in Tx BA stream table. */
|
||||
void nxpwifi_11n_delete_all_tx_ba_stream_tbl(struct nxpwifi_private *priv)
|
||||
{
|
||||
int i;
|
||||
struct nxpwifi_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node;
|
||||
|
||||
for (i = 0; i < MAX_NUM_TID; i++) {
|
||||
spin_lock_bh(&priv->tx_ba_stream_tbl_lock[i]);
|
||||
list_for_each_entry_safe(del_tbl_ptr, tmp_node,
|
||||
&priv->tx_ba_stream_tbl_ptr[i], list)
|
||||
nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr);
|
||||
spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[i]);
|
||||
|
||||
INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr[i]);
|
||||
|
||||
priv->aggr_prio_tbl[i].ampdu_ap =
|
||||
priv->aggr_prio_tbl[i].ampdu_user;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return BA stream entry for given RA/TID. */
|
||||
struct nxpwifi_tx_ba_stream_tbl *
|
||||
nxpwifi_get_ba_tbl(struct nxpwifi_private *priv, int tid, u8 *ra)
|
||||
{
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl = NULL;
|
||||
|
||||
list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], list) {
|
||||
if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) &&
|
||||
tx_ba_tsr_tbl->tid == tid)
|
||||
return tx_ba_tsr_tbl;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create Tx BA stream entry for given RA/TID. */
|
||||
void nxpwifi_create_ba_tbl(struct nxpwifi_private *priv, u8 *ra, int tid,
|
||||
enum nxpwifi_ba_status ba_status)
|
||||
{
|
||||
struct nxpwifi_tx_ba_stream_tbl *new_node;
|
||||
struct nxpwifi_ra_list_tbl *ra_list;
|
||||
int tid_down;
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl;
|
||||
|
||||
guard(rcu)();
|
||||
tx_ba_tbl = nxpwifi_get_ba_tbl(priv, tid, ra);
|
||||
|
||||
if (!tx_ba_tbl) {
|
||||
new_node = kzalloc_obj(*new_node, GFP_ATOMIC);
|
||||
if (!new_node)
|
||||
return;
|
||||
|
||||
tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
|
||||
ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, ra);
|
||||
if (ra_list) {
|
||||
ra_list->ba_status = ba_status;
|
||||
ra_list->amsdu_in_ampdu = false;
|
||||
}
|
||||
INIT_LIST_HEAD(&new_node->list);
|
||||
|
||||
new_node->tid = tid;
|
||||
new_node->ba_status = ba_status;
|
||||
memcpy(new_node->ra, ra, ETH_ALEN);
|
||||
|
||||
spin_lock_bh(&priv->tx_ba_stream_tbl_lock[tid]);
|
||||
list_add_tail_rcu(&new_node->list, &priv->tx_ba_stream_tbl_ptr[tid]);
|
||||
spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[tid]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send ADDBA request to the given TID/RA. */
|
||||
int nxpwifi_send_addba(struct nxpwifi_private *priv, int tid, u8 *peer_mac)
|
||||
{
|
||||
struct host_cmd_ds_11n_addba_req add_ba_req;
|
||||
u32 tx_win_size = priv->add_ba_param.tx_win_size;
|
||||
static u8 dialog_tok;
|
||||
u16 block_ack_param_set;
|
||||
|
||||
nxpwifi_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid);
|
||||
|
||||
memset(&add_ba_req, 0, sizeof(add_ba_req));
|
||||
|
||||
block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
|
||||
tx_win_size << BLOCKACKPARAM_WINSIZE_POS |
|
||||
IMMEDIATE_BLOCK_ACK);
|
||||
|
||||
/* enable AMSDU inside AMPDU */
|
||||
if (priv->add_ba_param.tx_amsdu &&
|
||||
priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
|
||||
block_ack_param_set |= IEEE80211_ADDBA_PARAM_AMSDU_MASK;
|
||||
|
||||
add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set);
|
||||
add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);
|
||||
|
||||
++dialog_tok;
|
||||
|
||||
if (dialog_tok == 0)
|
||||
dialog_tok = 1;
|
||||
|
||||
add_ba_req.dialog_token = dialog_tok;
|
||||
memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN);
|
||||
|
||||
/* We don't wait for the response of this command */
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_REQ,
|
||||
0, 0, &add_ba_req, false);
|
||||
}
|
||||
|
||||
/* Send DELBA request to the given TID/RA. */
|
||||
int nxpwifi_send_delba(struct nxpwifi_private *priv, int tid, u8 *peer_mac,
|
||||
int initiator)
|
||||
{
|
||||
struct host_cmd_ds_11n_delba delba;
|
||||
u16 del_ba_param_set;
|
||||
|
||||
memset(&delba, 0, sizeof(delba));
|
||||
|
||||
del_ba_param_set = tid << DELBA_TID_POS;
|
||||
|
||||
if (initiator)
|
||||
del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK;
|
||||
else
|
||||
del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK;
|
||||
|
||||
delba.del_ba_param_set = cpu_to_le16(del_ba_param_set);
|
||||
memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN);
|
||||
|
||||
/* We don't wait for the response of this command */
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA,
|
||||
HOST_ACT_GEN_SET, 0, &delba, false);
|
||||
}
|
||||
|
||||
/* Send DELBA to specific TID. */
|
||||
void nxpwifi_11n_delba(struct nxpwifi_private *priv, int tid)
|
||||
{
|
||||
struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr;
|
||||
u8 ta[ETH_ALEN];
|
||||
bool found = false;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr[tid], list) {
|
||||
if (rx_reor_tbl_ptr->tid == tid) {
|
||||
memcpy(ta, rx_reor_tbl_ptr->ta, ETH_ALEN);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (found) {
|
||||
nxpwifi_dbg(priv->adapter, INFO,
|
||||
"Send delba to tid=%d, %pM\n", tid, ta);
|
||||
nxpwifi_send_delba(priv, tid, ta, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle DELBA event; remove BA stream. */
|
||||
void nxpwifi_11n_delete_ba_stream(struct nxpwifi_private *priv, u8 *del_ba)
|
||||
{
|
||||
struct host_cmd_ds_11n_delba *cmd_del_ba =
|
||||
(struct host_cmd_ds_11n_delba *)del_ba;
|
||||
u16 del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set);
|
||||
int tid;
|
||||
|
||||
tid = del_ba_param_set >> DELBA_TID_POS;
|
||||
|
||||
nxpwifi_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr,
|
||||
TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set));
|
||||
}
|
||||
|
||||
/* Retrieve Rx reordering table. */
|
||||
int nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ds_rx_reorder_tbl *buf)
|
||||
{
|
||||
int i, j;
|
||||
struct nxpwifi_ds_rx_reorder_tbl *rx_reo_tbl = buf;
|
||||
struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr;
|
||||
int count = 0;
|
||||
|
||||
guard(rcu)();
|
||||
for (j = 0; j < MAX_NUM_TID; j++) {
|
||||
list_for_each_entry_rcu(rx_reorder_tbl_ptr,
|
||||
&priv->rx_reorder_tbl_ptr[j],
|
||||
list) {
|
||||
rx_reo_tbl->tid = (u16)rx_reorder_tbl_ptr->tid;
|
||||
memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN);
|
||||
rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win;
|
||||
rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size;
|
||||
for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) {
|
||||
if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
|
||||
rx_reo_tbl->buffer[i] = true;
|
||||
else
|
||||
rx_reo_tbl->buffer[i] = false;
|
||||
}
|
||||
rx_reo_tbl++;
|
||||
count++;
|
||||
|
||||
if (count >= NXPWIFI_MAX_RX_BASTREAM_SUPPORTED)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Retrieve Tx BA stream table. */
|
||||
int nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ds_tx_ba_stream_tbl *buf)
|
||||
{
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl;
|
||||
struct nxpwifi_ds_tx_ba_stream_tbl *rx_reo_tbl = buf;
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
guard(rcu)();
|
||||
for (i = 0; i < MAX_NUM_TID; i++) {
|
||||
list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[i], list) {
|
||||
rx_reo_tbl->tid = (u16)tx_ba_tsr_tbl->tid;
|
||||
nxpwifi_dbg(priv->adapter, DATA, "data: %s tid=%d\n",
|
||||
__func__, rx_reo_tbl->tid);
|
||||
memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN);
|
||||
rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu;
|
||||
rx_reo_tbl++;
|
||||
count++;
|
||||
if (count >= NXPWIFI_MAX_TX_BASTREAM_SUPPORTED)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Delete Tx BA stream entry by RA. */
|
||||
void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra)
|
||||
{
|
||||
struct nxpwifi_tx_ba_stream_tbl *tbl;
|
||||
int i;
|
||||
|
||||
if (!ra)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_NUM_TID; i++) {
|
||||
spin_lock_bh(&priv->tx_ba_stream_tbl_lock[i]);
|
||||
list_for_each_entry_rcu(tbl, &priv->tx_ba_stream_tbl_ptr[i], list)
|
||||
if (!memcmp(tbl->ra, ra, ETH_ALEN))
|
||||
nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, tbl);
|
||||
|
||||
spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize BlockAck parameters. */
|
||||
void nxpwifi_set_ba_params(struct nxpwifi_private *priv)
|
||||
{
|
||||
priv->add_ba_param.timeout = NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT;
|
||||
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
|
||||
priv->add_ba_param.tx_win_size =
|
||||
NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE;
|
||||
priv->add_ba_param.rx_win_size =
|
||||
NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE;
|
||||
} else {
|
||||
priv->add_ba_param.tx_win_size =
|
||||
NXPWIFI_STA_AMPDU_DEF_TXWINSIZE;
|
||||
priv->add_ba_param.rx_win_size =
|
||||
NXPWIFI_STA_AMPDU_DEF_RXWINSIZE;
|
||||
}
|
||||
|
||||
priv->add_ba_param.tx_amsdu = true;
|
||||
priv->add_ba_param.rx_amsdu = true;
|
||||
}
|
||||
|
||||
u8 nxpwifi_get_sec_chan_offset(int chan)
|
||||
{
|
||||
u8 sec_offset;
|
||||
|
||||
switch (chan) {
|
||||
case 36:
|
||||
case 44:
|
||||
case 52:
|
||||
case 60:
|
||||
case 100:
|
||||
case 108:
|
||||
case 116:
|
||||
case 124:
|
||||
case 132:
|
||||
case 140:
|
||||
case 149:
|
||||
case 157:
|
||||
case 173:
|
||||
sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
||||
break;
|
||||
case 40:
|
||||
case 48:
|
||||
case 56:
|
||||
case 64:
|
||||
case 104:
|
||||
case 112:
|
||||
case 120:
|
||||
case 128:
|
||||
case 136:
|
||||
case 144:
|
||||
case 153:
|
||||
case 161:
|
||||
case 169:
|
||||
case 177:
|
||||
sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
||||
break;
|
||||
case 165:
|
||||
default:
|
||||
sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
return sec_offset;
|
||||
}
|
||||
|
||||
/* Send DELBA to entries in the Tx BA stream table. */
|
||||
static void
|
||||
nxpwifi_send_delba_txbastream_tbl(struct nxpwifi_private *priv, u8 tid)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr;
|
||||
|
||||
guard(rcu)();
|
||||
list_for_each_entry_rcu(tx_ba_stream_tbl_ptr,
|
||||
&priv->tx_ba_stream_tbl_ptr[tid], list) {
|
||||
if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) {
|
||||
if (tid == tx_ba_stream_tbl_ptr->tid) {
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"Tx:Send delba to tid=%d, %pM\n", tid,
|
||||
tx_ba_stream_tbl_ptr->ra);
|
||||
nxpwifi_send_delba(priv,
|
||||
tx_ba_stream_tbl_ptr->tid,
|
||||
tx_ba_stream_tbl_ptr->ra, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update tx_win_size for all interfaces and send DELBA when it changes.
|
||||
*/
|
||||
void nxpwifi_update_ampdu_txwinsize(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
u8 i, j;
|
||||
u32 tx_win_size;
|
||||
struct nxpwifi_private *priv;
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
tx_win_size = priv->add_ba_param.tx_win_size;
|
||||
|
||||
if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
|
||||
priv->add_ba_param.tx_win_size =
|
||||
NXPWIFI_STA_AMPDU_DEF_TXWINSIZE;
|
||||
|
||||
if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
|
||||
priv->add_ba_param.tx_win_size =
|
||||
NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE;
|
||||
|
||||
if (adapter->coex_win_size) {
|
||||
if (adapter->coex_tx_win_size)
|
||||
priv->add_ba_param.tx_win_size =
|
||||
adapter->coex_tx_win_size;
|
||||
}
|
||||
|
||||
if (tx_win_size != priv->add_ba_param.tx_win_size) {
|
||||
if (!priv->media_connected)
|
||||
continue;
|
||||
for (j = 0; j < MAX_NUM_TID; j++)
|
||||
nxpwifi_send_delba_txbastream_tbl(priv, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
158
drivers/net/wireless/nxp/nxpwifi/11n.h
Normal file
158
drivers/net/wireless/nxp/nxpwifi/11n.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* nxpwifi: 802.11n support
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_11N_H_
|
||||
#define _NXPWIFI_11N_H_
|
||||
|
||||
#include "11n_aggr.h"
|
||||
#include "11n_rxreorder.h"
|
||||
#include "wmm.h"
|
||||
|
||||
int nxpwifi_ret_11n_delba(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp);
|
||||
int nxpwifi_ret_11n_addba_req(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp);
|
||||
int nxpwifi_cmd_11n_cfg(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct nxpwifi_ds_11n_tx_cfg *txcfg);
|
||||
int nxpwifi_cmd_append_11n_tlv(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc,
|
||||
u8 **buffer);
|
||||
int nxpwifi_fill_cap_info(struct nxpwifi_private *priv, u8 radio_type,
|
||||
struct ieee80211_ht_cap *ht_cap);
|
||||
int nxpwifi_set_get_11n_htcap_cfg(struct nxpwifi_private *priv,
|
||||
u16 action, int *htcap_cfg);
|
||||
void nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_tx_ba_stream_tbl
|
||||
*tx_tbl);
|
||||
void nxpwifi_11n_delete_all_tx_ba_stream_tbl(struct nxpwifi_private *priv);
|
||||
struct nxpwifi_tx_ba_stream_tbl *nxpwifi_get_ba_tbl(struct nxpwifi_private
|
||||
*priv, int tid, u8 *ra);
|
||||
void nxpwifi_create_ba_tbl(struct nxpwifi_private *priv, u8 *ra, int tid,
|
||||
enum nxpwifi_ba_status ba_status);
|
||||
int nxpwifi_send_addba(struct nxpwifi_private *priv, int tid, u8 *peer_mac);
|
||||
int nxpwifi_send_delba(struct nxpwifi_private *priv, int tid, u8 *peer_mac,
|
||||
int initiator);
|
||||
void nxpwifi_11n_delete_ba_stream(struct nxpwifi_private *priv, u8 *del_ba);
|
||||
int nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ds_rx_reorder_tbl *buf);
|
||||
int nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ds_tx_ba_stream_tbl *buf);
|
||||
int nxpwifi_cmd_recfg_tx_buf(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd,
|
||||
int cmd_action, u16 *buf_size);
|
||||
int nxpwifi_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
|
||||
int cmd_action,
|
||||
struct nxpwifi_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
|
||||
void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra);
|
||||
u8 nxpwifi_get_sec_chan_offset(int chan);
|
||||
|
||||
static inline bool
|
||||
nxpwifi_is_station_ampdu_allowed(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ra_list_tbl *ptr, int tid)
|
||||
{
|
||||
struct nxpwifi_sta_node *node;
|
||||
|
||||
guard(rcu)();
|
||||
node = nxpwifi_get_sta_entry(priv, ptr->ra);
|
||||
if (unlikely(!node))
|
||||
return false;
|
||||
|
||||
if (node->ampdu_sta[tid] == BA_STREAM_NOT_ALLOWED)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check if AMPDU is allowed for the given TID. */
|
||||
static inline bool
|
||||
nxpwifi_is_ampdu_allowed(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ra_list_tbl *ptr, int tid)
|
||||
{
|
||||
if (is_broadcast_ether_addr(ptr->ra))
|
||||
return false;
|
||||
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP)
|
||||
return nxpwifi_is_station_ampdu_allowed(priv, ptr, tid);
|
||||
|
||||
return priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
/* Check if AMSDU is allowed for the given TID. */
|
||||
static inline bool
|
||||
nxpwifi_is_amsdu_allowed(struct nxpwifi_private *priv, int tid)
|
||||
{
|
||||
bool amsdu_enabled = priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED;
|
||||
bool rate_ok = priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03);
|
||||
|
||||
return amsdu_enabled && rate_ok;
|
||||
}
|
||||
|
||||
/* Check if there is available space for a new BA stream. */
|
||||
static inline bool
|
||||
nxpwifi_space_avail_for_new_ba_stream(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
struct nxpwifi_private *priv;
|
||||
u8 i, j;
|
||||
size_t ba_stream_num = 0;
|
||||
size_t ba_stream_max = NXPWIFI_MAX_TX_BASTREAM_SUPPORTED;
|
||||
|
||||
if (adapter->fw_api_ver == NXPWIFI_FW_V15) {
|
||||
ba_stream_max = GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap);
|
||||
if (!ba_stream_max)
|
||||
ba_stream_max = NXPWIFI_MAX_TX_BASTREAM_SUPPORTED;
|
||||
}
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
for (j = 0; j < MAX_NUM_TID; j++)
|
||||
ba_stream_num += list_count_nodes(&priv->tx_ba_stream_tbl_ptr[j]);
|
||||
}
|
||||
|
||||
return ba_stream_num < ba_stream_max;
|
||||
}
|
||||
|
||||
/* Find the Tx BA stream to delete and return its TID and RA. */
|
||||
static inline bool
|
||||
nxpwifi_find_stream_to_delete(struct nxpwifi_private *priv, int ptr_tid,
|
||||
int *ptid, u8 *ra)
|
||||
{
|
||||
int search_tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user;
|
||||
bool found = false;
|
||||
struct nxpwifi_tx_ba_stream_tbl *tx_tbl;
|
||||
int candidate_tid;
|
||||
|
||||
spin_lock_bh(&priv->tx_ba_stream_tbl_lock[ptr_tid]);
|
||||
|
||||
list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr[ptr_tid], list) {
|
||||
candidate_tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user;
|
||||
|
||||
if (search_tid > candidate_tid) {
|
||||
search_tid = candidate_tid;
|
||||
*ptid = tx_tbl->tid;
|
||||
memcpy(ra, tx_tbl->ra, ETH_ALEN);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[ptr_tid]);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Check whether the associated station is 11n enabled. */
|
||||
static inline int nxpwifi_is_sta_11n_enabled(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_sta_node *node)
|
||||
{
|
||||
if (!node || (priv->bss_role == NXPWIFI_BSS_ROLE_UAP &&
|
||||
!priv->ap_11n_enabled))
|
||||
return 0;
|
||||
|
||||
return node->is_11n_enabled;
|
||||
}
|
||||
|
||||
#endif /* !_NXPWIFI_11N_H_ */
|
||||
251
drivers/net/wireless/nxp/nxpwifi/11n_aggr.c
Normal file
251
drivers/net/wireless/nxp/nxpwifi/11n_aggr.c
Normal file
@@ -0,0 +1,251 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi: 802.11n Aggregation
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "wmm.h"
|
||||
#include "11n.h"
|
||||
#include "11n_aggr.h"
|
||||
|
||||
/*
|
||||
* Build an AMSDU subframe for aggregation, with fields
|
||||
* (DA | SA | Length | SNAP header | MSDU), and compute padding
|
||||
* to align the subframe to a 4-byte boundary.
|
||||
*/
|
||||
static int nxpwifi_11n_form_amsdu_pkt(struct sk_buff *skb_aggr,
|
||||
struct sk_buff *skb_src, int *pad)
|
||||
|
||||
{
|
||||
int dt_offset;
|
||||
struct rfc_1042_hdr snap = {
|
||||
0xaa, /* LLC DSAP */
|
||||
0xaa, /* LLC SSAP */
|
||||
0x03, /* LLC CTRL */
|
||||
{0x00, 0x00, 0x00}, /* SNAP OUI */
|
||||
0x0000 /* SNAP type */
|
||||
/* This field will be overwritten later with ethertype */
|
||||
};
|
||||
struct tx_packet_hdr *tx_header;
|
||||
|
||||
tx_header = skb_put(skb_aggr, sizeof(*tx_header));
|
||||
|
||||
/* Copy DA and SA */
|
||||
dt_offset = 2 * ETH_ALEN;
|
||||
memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
|
||||
|
||||
/* Copy SNAP header */
|
||||
snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto;
|
||||
|
||||
dt_offset += sizeof(__be16);
|
||||
|
||||
memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
|
||||
|
||||
skb_pull(skb_src, dt_offset);
|
||||
|
||||
/* Update Length field */
|
||||
tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN);
|
||||
|
||||
/* Add payload */
|
||||
skb_put_data(skb_aggr, skb_src->data, skb_src->len);
|
||||
|
||||
/* Add padding for new MSDU to start from 4 byte boundary */
|
||||
*pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4;
|
||||
|
||||
return skb_aggr->len + *pad;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds TxPD to AMSDU header. Each AMSDU packet will contain one TxPD at the
|
||||
* beginning, followed by multiple AMSDU subframes
|
||||
*/
|
||||
static void
|
||||
nxpwifi_11n_form_amsdu_txpd(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct txpd *local_tx_pd;
|
||||
|
||||
skb_push(skb, sizeof(*local_tx_pd));
|
||||
|
||||
local_tx_pd = (struct txpd *)skb->data;
|
||||
memset(local_tx_pd, 0, sizeof(struct txpd));
|
||||
|
||||
/* Original priority has been overwritten */
|
||||
local_tx_pd->priority = (u8)skb->priority;
|
||||
local_tx_pd->pkt_delay_2ms =
|
||||
nxpwifi_wmm_compute_drv_pkt_delay(priv, skb);
|
||||
local_tx_pd->bss_num = priv->bss_num;
|
||||
local_tx_pd->bss_type = priv->bss_type;
|
||||
/* Always zero as the data is followed by struct txpd */
|
||||
local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
|
||||
local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU);
|
||||
local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
|
||||
sizeof(*local_tx_pd));
|
||||
|
||||
if (local_tx_pd->tx_control == 0)
|
||||
/* TxCtrl set by user or default */
|
||||
local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
|
||||
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA &&
|
||||
priv->adapter->pps_uapsd_mode) {
|
||||
if (nxpwifi_check_last_packet_indication(priv)) {
|
||||
priv->adapter->tx_lock_flag = true;
|
||||
local_tx_pd->flags =
|
||||
NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an aggregated MSDU packet by encapsulating buffers from the RA
|
||||
* list as AMSDU subframes and concatenating them. A TxPD is prepended
|
||||
* before transmission to form the final AMSDU packet.
|
||||
*/
|
||||
int
|
||||
nxpwifi_11n_aggregate_pkt(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ra_list_tbl *pra_list,
|
||||
int ptrindex)
|
||||
__releases(&priv->wmm.ra_list_spinlock)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct sk_buff *skb_aggr, *skb_src;
|
||||
struct nxpwifi_txinfo *tx_info_aggr, *tx_info_src;
|
||||
int pad = 0, aggr_num = 0, ret;
|
||||
struct nxpwifi_tx_param tx_param;
|
||||
struct txpd *ptx_pd = NULL;
|
||||
int headroom = adapter->intf_hdr_len;
|
||||
|
||||
skb_src = skb_peek(&pra_list->skb_head);
|
||||
if (!skb_src) {
|
||||
spin_unlock_bh(&priv->wmm.ra_list_spinlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tx_info_src = NXPWIFI_SKB_TXCB(skb_src);
|
||||
skb_aggr = nxpwifi_alloc_dma_align_buf(adapter->tx_buf_size,
|
||||
GFP_ATOMIC);
|
||||
if (!skb_aggr) {
|
||||
spin_unlock_bh(&priv->wmm.ra_list_spinlock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* skb_aggr->data already 64 byte align, just reserve bus interface
|
||||
* header and txpd.
|
||||
*/
|
||||
skb_reserve(skb_aggr, headroom + sizeof(struct txpd));
|
||||
tx_info_aggr = NXPWIFI_SKB_TXCB(skb_aggr);
|
||||
|
||||
memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
|
||||
tx_info_aggr->bss_type = tx_info_src->bss_type;
|
||||
tx_info_aggr->bss_num = tx_info_src->bss_num;
|
||||
|
||||
tx_info_aggr->flags |= NXPWIFI_BUF_FLAG_AGGR_PKT;
|
||||
skb_aggr->priority = skb_src->priority;
|
||||
skb_aggr->tstamp = skb_src->tstamp;
|
||||
|
||||
do {
|
||||
/* Check if AMSDU can accommodate this MSDU */
|
||||
if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) >
|
||||
adapter->tx_buf_size)
|
||||
break;
|
||||
|
||||
skb_src = skb_dequeue(&pra_list->skb_head);
|
||||
pra_list->total_pkt_count--;
|
||||
atomic_dec(&priv->wmm.tx_pkts_queued);
|
||||
aggr_num++;
|
||||
spin_unlock_bh(&priv->wmm.ra_list_spinlock);
|
||||
nxpwifi_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad);
|
||||
|
||||
nxpwifi_write_data_complete(adapter, skb_src, 0, 0);
|
||||
|
||||
spin_lock_bh(&priv->wmm.ra_list_spinlock);
|
||||
|
||||
if (!nxpwifi_is_ralist_valid(priv, pra_list, ptrindex)) {
|
||||
spin_unlock_bh(&priv->wmm.ra_list_spinlock);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (skb_tailroom(skb_aggr) < pad) {
|
||||
pad = 0;
|
||||
break;
|
||||
}
|
||||
skb_put(skb_aggr, pad);
|
||||
|
||||
skb_src = skb_peek(&pra_list->skb_head);
|
||||
|
||||
} while (skb_src);
|
||||
|
||||
spin_unlock_bh(&priv->wmm.ra_list_spinlock);
|
||||
|
||||
/* Last AMSDU packet does not need padding */
|
||||
skb_trim(skb_aggr, skb_aggr->len - pad);
|
||||
|
||||
/* Form AMSDU */
|
||||
nxpwifi_11n_form_amsdu_txpd(priv, skb_aggr);
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
|
||||
ptx_pd = (struct txpd *)skb_aggr->data;
|
||||
|
||||
skb_push(skb_aggr, headroom);
|
||||
tx_info_aggr->aggr_num = aggr_num * 2;
|
||||
if (adapter->data_sent || adapter->tx_lock_flag) {
|
||||
atomic_add(aggr_num * 2, &adapter->tx_queued);
|
||||
skb_queue_tail(&adapter->tx_data_q, skb_aggr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (skb_src)
|
||||
tx_param.next_pkt_len = skb_src->len + sizeof(struct txpd);
|
||||
else
|
||||
tx_param.next_pkt_len = 0;
|
||||
|
||||
ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA,
|
||||
skb_aggr, &tx_param);
|
||||
|
||||
switch (ret) {
|
||||
case -EBUSY:
|
||||
spin_lock_bh(&priv->wmm.ra_list_spinlock);
|
||||
if (!nxpwifi_is_ralist_valid(priv, pra_list, ptrindex)) {
|
||||
spin_unlock_bh(&priv->wmm.ra_list_spinlock);
|
||||
nxpwifi_write_data_complete(adapter, skb_aggr, 1, -1);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA &&
|
||||
adapter->pps_uapsd_mode && adapter->tx_lock_flag) {
|
||||
priv->adapter->tx_lock_flag = false;
|
||||
if (ptx_pd)
|
||||
ptx_pd->flags = 0;
|
||||
}
|
||||
|
||||
skb_queue_tail(&pra_list->skb_head, skb_aggr);
|
||||
|
||||
pra_list->total_pkt_count++;
|
||||
|
||||
atomic_inc(&priv->wmm.tx_pkts_queued);
|
||||
|
||||
tx_info_aggr->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
|
||||
spin_unlock_bh(&priv->wmm.ra_list_spinlock);
|
||||
nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
|
||||
break;
|
||||
case -EINPROGRESS:
|
||||
break;
|
||||
case 0:
|
||||
nxpwifi_write_data_complete(adapter, skb_aggr, 1, ret);
|
||||
break;
|
||||
default:
|
||||
nxpwifi_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n",
|
||||
__func__, ret);
|
||||
adapter->dbg.num_tx_host_to_card_failure++;
|
||||
nxpwifi_write_data_complete(adapter, skb_aggr, 1, ret);
|
||||
break;
|
||||
}
|
||||
if (ret != -EBUSY)
|
||||
nxpwifi_rotate_priolists(priv, pra_list, ptrindex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
drivers/net/wireless/nxp/nxpwifi/11n_aggr.h
Normal file
21
drivers/net/wireless/nxp/nxpwifi/11n_aggr.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* NXP Wireless LAN device driver: 802.11n Aggregation
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_11N_AGGR_H_
|
||||
#define _NXPWIFI_11N_AGGR_H_
|
||||
|
||||
#define PKT_TYPE_AMSDU 0xE6
|
||||
#define MIN_NUM_AMSDU 2
|
||||
|
||||
int nxpwifi_11n_deaggregate_pkt(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb);
|
||||
int nxpwifi_11n_aggregate_pkt(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ra_list_tbl *ptr,
|
||||
int ptr_index)
|
||||
__releases(&priv->wmm.ra_list_spinlock);
|
||||
|
||||
#endif /* !_NXPWIFI_11N_AGGR_H_ */
|
||||
826
drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c
Normal file
826
drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c
Normal file
@@ -0,0 +1,826 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi: 802.11n RX Re-ordering
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
#include "wmm.h"
|
||||
#include "11n.h"
|
||||
#include "11n_rxreorder.h"
|
||||
/* Dispatch A-MSDU to stack. */
|
||||
static int nxpwifi_11n_dispatch_amsdu_pkt(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
|
||||
int ret;
|
||||
|
||||
if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
|
||||
struct sk_buff_head list;
|
||||
struct sk_buff *rx_skb;
|
||||
|
||||
__skb_queue_head_init(&list);
|
||||
|
||||
skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
|
||||
skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
|
||||
|
||||
ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
|
||||
priv->wdev.iftype, 0, NULL, NULL, false);
|
||||
|
||||
while (!skb_queue_empty(&list)) {
|
||||
rx_skb = __skb_dequeue(&list);
|
||||
|
||||
if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
|
||||
ret = nxpwifi_uap_recv_packet(priv, rx_skb);
|
||||
else
|
||||
ret = nxpwifi_recv_packet(priv, rx_skb);
|
||||
if (ret)
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"Rx of A-MSDU failed");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Process RX packet and forward to stack. */
|
||||
static int nxpwifi_11n_dispatch_pkt(struct nxpwifi_private *priv,
|
||||
struct sk_buff *payload)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!payload) {
|
||||
nxpwifi_dbg(priv->adapter, INFO, "info: fw drop data\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = nxpwifi_11n_dispatch_amsdu_pkt(priv, payload);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
|
||||
return nxpwifi_handle_uap_rx_forward(priv, payload);
|
||||
|
||||
return nxpwifi_process_rx_packet(priv, payload);
|
||||
}
|
||||
|
||||
/* Dispatch packets up to start_win. */
|
||||
static void
|
||||
nxpwifi_11n_dispatch_pkt_until_start_win(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_rx_reorder_tbl *tbl,
|
||||
int start_win)
|
||||
{
|
||||
struct sk_buff_head list;
|
||||
struct sk_buff *skb;
|
||||
int pkt_to_send, i, tid;
|
||||
|
||||
tid = tbl->tid;
|
||||
__skb_queue_head_init(&list);
|
||||
spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]);
|
||||
|
||||
pkt_to_send = (start_win > tbl->start_win) ?
|
||||
min((start_win - tbl->start_win), tbl->win_size) :
|
||||
tbl->win_size;
|
||||
|
||||
for (i = 0; i < pkt_to_send; ++i) {
|
||||
if (tbl->rx_reorder_ptr[i]) {
|
||||
skb = tbl->rx_reorder_ptr[i];
|
||||
__skb_queue_tail(&list, skb);
|
||||
tbl->rx_reorder_ptr[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Simulate circular buffer via rotation. */
|
||||
for (i = 0; i < tbl->win_size - pkt_to_send; ++i) {
|
||||
tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i];
|
||||
tbl->rx_reorder_ptr[pkt_to_send + i] = NULL;
|
||||
}
|
||||
|
||||
tbl->start_win = start_win;
|
||||
spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]);
|
||||
|
||||
while ((skb = __skb_dequeue(&list)))
|
||||
nxpwifi_11n_dispatch_pkt(priv, skb);
|
||||
}
|
||||
|
||||
/* Dispatch packets until a hole is found. */
|
||||
static void
|
||||
nxpwifi_11n_scan_and_dispatch(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_rx_reorder_tbl *tbl)
|
||||
{
|
||||
struct sk_buff_head list;
|
||||
struct sk_buff *skb;
|
||||
int i, j, xchg, tid;
|
||||
|
||||
tid = tbl->tid;
|
||||
__skb_queue_head_init(&list);
|
||||
spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]);
|
||||
|
||||
for (i = 0; i < tbl->win_size; ++i) {
|
||||
if (!tbl->rx_reorder_ptr[i])
|
||||
break;
|
||||
skb = tbl->rx_reorder_ptr[i];
|
||||
__skb_queue_tail(&list, skb);
|
||||
tbl->rx_reorder_ptr[i] = NULL;
|
||||
}
|
||||
|
||||
/* Simulate circular buffer via rotation. */
|
||||
if (i > 0) {
|
||||
xchg = tbl->win_size - i;
|
||||
for (j = 0; j < xchg; ++j) {
|
||||
tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j];
|
||||
tbl->rx_reorder_ptr[i + j] = NULL;
|
||||
}
|
||||
}
|
||||
tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1);
|
||||
|
||||
spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]);
|
||||
|
||||
while ((skb = __skb_dequeue(&list)))
|
||||
nxpwifi_11n_dispatch_pkt(priv, skb);
|
||||
}
|
||||
|
||||
/* Delete RX reorder entry and flush pending packets. */
|
||||
static void
|
||||
nxpwifi_del_rx_reorder_entry(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_rx_reorder_tbl *tbl)
|
||||
{
|
||||
int start_win, tid;
|
||||
|
||||
if (!tbl)
|
||||
return;
|
||||
|
||||
tid = tbl->tid;
|
||||
|
||||
atomic_set(&priv->adapter->rx_ba_teardown_pending, 1);
|
||||
flush_workqueue(priv->adapter->rx_workqueue);
|
||||
|
||||
start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
|
||||
nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
|
||||
|
||||
timer_delete_sync(&tbl->timer_context.timer);
|
||||
tbl->timer_context.timer_is_set = false;
|
||||
|
||||
spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]);
|
||||
list_del_rcu(&tbl->list);
|
||||
spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]);
|
||||
|
||||
kfree(tbl->rx_reorder_ptr);
|
||||
kfree_rcu(tbl, rcu);
|
||||
|
||||
atomic_set(&priv->adapter->rx_ba_teardown_pending, 0);
|
||||
}
|
||||
|
||||
/* Lookup RX reorder entry by TID/TA. */
|
||||
struct nxpwifi_rx_reorder_tbl *
|
||||
nxpwifi_11n_get_rx_reorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta)
|
||||
{
|
||||
struct nxpwifi_rx_reorder_tbl *tbl, *found = NULL;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[tid], list) {
|
||||
if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) {
|
||||
found = tbl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Delete RX reorder entries by TA. */
|
||||
void nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private *priv, u8 *ta)
|
||||
{
|
||||
struct nxpwifi_rx_reorder_tbl *tbl, *tmp;
|
||||
LIST_HEAD(to_delete);
|
||||
int i;
|
||||
|
||||
if (!ta)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_NUM_TID; i++) {
|
||||
guard(rcu)();
|
||||
list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[i], list) {
|
||||
if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
|
||||
INIT_LIST_HEAD(&tbl->tmp_list);
|
||||
list_add_tail(&tbl->tmp_list, &to_delete);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(tbl, tmp, &to_delete, tmp_list)
|
||||
nxpwifi_del_rx_reorder_entry(priv, tbl);
|
||||
|
||||
INIT_LIST_HEAD(&to_delete);
|
||||
}
|
||||
}
|
||||
|
||||
/* Find last buffered sequence index. */
|
||||
static int
|
||||
nxpwifi_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
|
||||
{
|
||||
struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
|
||||
int i;
|
||||
|
||||
guard(rcu)();
|
||||
for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
|
||||
if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Flush and dispatch buffered packets on timer. */
|
||||
static void
|
||||
nxpwifi_flush_data(struct timer_list *t)
|
||||
{
|
||||
struct reorder_tmr_cnxt *ctx =
|
||||
timer_container_of(ctx, t, timer);
|
||||
int start_win, seq_num;
|
||||
|
||||
ctx->timer_is_set = false;
|
||||
seq_num = nxpwifi_11n_find_last_seq_num(ctx);
|
||||
|
||||
if (seq_num < 0)
|
||||
return;
|
||||
|
||||
nxpwifi_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num);
|
||||
start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
|
||||
nxpwifi_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
|
||||
start_win);
|
||||
}
|
||||
|
||||
/* Create RX reorder entry (TID/TA, SSN, winsize, timer). */
|
||||
static void
|
||||
nxpwifi_11n_create_rx_reorder_tbl(struct nxpwifi_private *priv, u8 *ta,
|
||||
int tid, int win_size, int seq_num)
|
||||
{
|
||||
int i;
|
||||
struct nxpwifi_rx_reorder_tbl *tbl, *new_node;
|
||||
u16 last_seq = 0;
|
||||
struct nxpwifi_sta_node *node;
|
||||
|
||||
/* Existing TID/TA: flush and move window to SSN. */
|
||||
tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta);
|
||||
if (tbl) {
|
||||
nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
|
||||
return;
|
||||
}
|
||||
/* if !tbl then create one */
|
||||
new_node = kzalloc_obj(*new_node, GFP_KERNEL);
|
||||
if (!new_node)
|
||||
return;
|
||||
|
||||
INIT_LIST_HEAD(&new_node->list);
|
||||
new_node->tid = tid;
|
||||
memcpy(new_node->ta, ta, ETH_ALEN);
|
||||
new_node->start_win = seq_num;
|
||||
new_node->init_win = seq_num;
|
||||
new_node->flags = 0;
|
||||
|
||||
if (nxpwifi_queuing_ra_based(priv)) {
|
||||
if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) {
|
||||
guard(rcu)();
|
||||
node = nxpwifi_get_sta_entry(priv, ta);
|
||||
if (node)
|
||||
last_seq = node->rx_seq[tid];
|
||||
}
|
||||
} else {
|
||||
guard(rcu)();
|
||||
node = nxpwifi_get_sta_entry(priv, ta);
|
||||
if (node)
|
||||
last_seq = node->rx_seq[tid];
|
||||
else
|
||||
last_seq = priv->rx_seq[tid];
|
||||
}
|
||||
|
||||
nxpwifi_dbg(priv->adapter, INFO,
|
||||
"info: last_seq=%d start_win=%d\n",
|
||||
last_seq, new_node->start_win);
|
||||
|
||||
if (last_seq != NXPWIFI_DEF_11N_RX_SEQ_NUM &&
|
||||
last_seq >= new_node->start_win) {
|
||||
new_node->start_win = last_seq + 1;
|
||||
new_node->flags |= RXREOR_INIT_WINDOW_SHIFT;
|
||||
}
|
||||
|
||||
new_node->win_size = win_size;
|
||||
|
||||
new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!new_node->rx_reorder_ptr) {
|
||||
kfree(new_node);
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"%s: failed to alloc reorder_ptr\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
new_node->timer_context.ptr = new_node;
|
||||
new_node->timer_context.priv = priv;
|
||||
new_node->timer_context.timer_is_set = false;
|
||||
|
||||
timer_setup(&new_node->timer_context.timer, nxpwifi_flush_data, 0);
|
||||
|
||||
for (i = 0; i < win_size; ++i)
|
||||
new_node->rx_reorder_ptr[i] = NULL;
|
||||
|
||||
spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]);
|
||||
list_add_tail_rcu(&new_node->list, &priv->rx_reorder_tbl_ptr[tid]);
|
||||
spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]);
|
||||
}
|
||||
|
||||
static void
|
||||
nxpwifi_11n_rxreorder_timer_restart(struct nxpwifi_rx_reorder_tbl *tbl)
|
||||
{
|
||||
u32 min_flush_time;
|
||||
|
||||
if (tbl->win_size >= NXPWIFI_BA_WIN_SIZE_32)
|
||||
min_flush_time = MIN_FLUSH_TIMER_15_MS;
|
||||
else
|
||||
min_flush_time = MIN_FLUSH_TIMER_MS;
|
||||
|
||||
mod_timer(&tbl->timer_context.timer,
|
||||
jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
|
||||
|
||||
tbl->timer_context.timer_is_set = true;
|
||||
}
|
||||
|
||||
/* Prepare ADDBA request. */
|
||||
int nxpwifi_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf)
|
||||
{
|
||||
struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_11N_ADDBA_REQ);
|
||||
cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN);
|
||||
memcpy(add_ba_req, data_buf, sizeof(*add_ba_req));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepare ADDBA response and create RX reorder table. */
|
||||
int nxpwifi_cmd_11n_addba_rsp_gen(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd,
|
||||
struct host_cmd_ds_11n_addba_req
|
||||
*cmd_addba_req)
|
||||
{
|
||||
struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
|
||||
u32 rx_win_size = priv->add_ba_param.rx_win_size;
|
||||
u8 tid;
|
||||
int win_size;
|
||||
u16 block_ack_param_set;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_11N_ADDBA_RSP);
|
||||
cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN);
|
||||
|
||||
memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr,
|
||||
ETH_ALEN);
|
||||
add_ba_rsp->dialog_token = cmd_addba_req->dialog_token;
|
||||
add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo;
|
||||
add_ba_rsp->ssn = cmd_addba_req->ssn;
|
||||
|
||||
block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set);
|
||||
tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
|
||||
>> BLOCKACKPARAM_TID_POS;
|
||||
add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT);
|
||||
block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
|
||||
|
||||
/* If we don't support AMSDU inside AMPDU, reset the bit */
|
||||
if (!priv->add_ba_param.rx_amsdu ||
|
||||
priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)
|
||||
block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_AMSDU_MASK;
|
||||
block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
|
||||
add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
|
||||
win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
|
||||
& IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
|
||||
>> BLOCKACKPARAM_WINSIZE_POS;
|
||||
cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set);
|
||||
|
||||
nxpwifi_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr,
|
||||
tid, win_size,
|
||||
le16_to_cpu(cmd_addba_req->ssn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepare DELBA command. */
|
||||
int nxpwifi_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf)
|
||||
{
|
||||
struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_11N_DELBA);
|
||||
cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN);
|
||||
memcpy(del_ba, data_buf, sizeof(*del_ba));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decide and perform RX reordering for a packet. */
|
||||
int nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private *priv,
|
||||
u16 seq_num, u16 tid,
|
||||
u8 *ta, u8 pkt_type, void *payload)
|
||||
{
|
||||
struct nxpwifi_rx_reorder_tbl *tbl;
|
||||
int prev_start_win, start_win, end_win, win_size;
|
||||
u16 pkt_index;
|
||||
bool init_window_shift = false;
|
||||
int ret = 0;
|
||||
|
||||
tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta);
|
||||
if (!tbl) {
|
||||
if (pkt_type != PKT_TYPE_BAR)
|
||||
nxpwifi_11n_dispatch_pkt(priv, payload);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pkt_type == PKT_TYPE_AMSDU && !tbl->amsdu) {
|
||||
nxpwifi_11n_dispatch_pkt(priv, payload);
|
||||
return ret;
|
||||
}
|
||||
|
||||
start_win = tbl->start_win;
|
||||
prev_start_win = start_win;
|
||||
win_size = tbl->win_size;
|
||||
end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
|
||||
if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
|
||||
init_window_shift = true;
|
||||
tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
|
||||
}
|
||||
|
||||
if (tbl->flags & RXREOR_FORCE_NO_DROP) {
|
||||
nxpwifi_dbg(priv->adapter, INFO,
|
||||
"RXREOR_FORCE_NO_DROP when HS is activated\n");
|
||||
tbl->flags &= ~RXREOR_FORCE_NO_DROP;
|
||||
} else if (init_window_shift && seq_num < start_win &&
|
||||
seq_num >= tbl->init_win) {
|
||||
nxpwifi_dbg(priv->adapter, INFO,
|
||||
"Sender TID sequence number reset %d->%d for SSN %d\n",
|
||||
start_win, seq_num, tbl->init_win);
|
||||
start_win = seq_num;
|
||||
tbl->start_win = start_win;
|
||||
end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
|
||||
} else {
|
||||
/* Drop packet if seq_num < start_win. */
|
||||
if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
|
||||
if (seq_num >= ((start_win + TWOPOW11) &
|
||||
(MAX_TID_VALUE - 1)) &&
|
||||
seq_num < start_win) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
} else if ((seq_num < start_win) ||
|
||||
(seq_num >= (start_win + TWOPOW11))) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust seq_num for BAR (WinStart = seq_num). */
|
||||
if (pkt_type == PKT_TYPE_BAR)
|
||||
seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1);
|
||||
|
||||
if ((end_win < start_win &&
|
||||
seq_num < start_win && seq_num > end_win) ||
|
||||
(end_win > start_win && (seq_num > end_win ||
|
||||
seq_num < start_win))) {
|
||||
end_win = seq_num;
|
||||
if (((end_win - win_size) + 1) >= 0)
|
||||
start_win = (end_win - win_size) + 1;
|
||||
else
|
||||
start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1;
|
||||
nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
|
||||
}
|
||||
|
||||
if (pkt_type != PKT_TYPE_BAR) {
|
||||
if (seq_num >= start_win)
|
||||
pkt_index = seq_num - start_win;
|
||||
else
|
||||
pkt_index = (seq_num + MAX_TID_VALUE) - start_win;
|
||||
|
||||
if (tbl->rx_reorder_ptr[pkt_index]) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
tbl->rx_reorder_ptr[pkt_index] = payload;
|
||||
}
|
||||
|
||||
/* Dispatch sequentially until a hole; update start_win. */
|
||||
nxpwifi_11n_scan_and_dispatch(priv, tbl);
|
||||
|
||||
done:
|
||||
if (!tbl->timer_context.timer_is_set ||
|
||||
prev_start_win != tbl->start_win)
|
||||
nxpwifi_11n_rxreorder_timer_restart(tbl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Delete BA entry for TID/TA. */
|
||||
void
|
||||
nxpwifi_del_ba_tbl(struct nxpwifi_private *priv, int tid, u8 *peer_mac,
|
||||
u8 type, int initiator)
|
||||
{
|
||||
struct nxpwifi_rx_reorder_tbl *tbl;
|
||||
struct nxpwifi_tx_ba_stream_tbl *ptx_tbl;
|
||||
struct nxpwifi_ra_list_tbl *ra_list;
|
||||
u8 cleanup_rx_reorder_tbl;
|
||||
int tid_down;
|
||||
|
||||
if (type == TYPE_DELBA_RECEIVE)
|
||||
cleanup_rx_reorder_tbl = (initiator) ? true : false;
|
||||
else
|
||||
cleanup_rx_reorder_tbl = (initiator) ? false : true;
|
||||
|
||||
nxpwifi_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n",
|
||||
peer_mac, tid, initiator);
|
||||
|
||||
if (cleanup_rx_reorder_tbl) {
|
||||
tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, peer_mac);
|
||||
if (!tbl) {
|
||||
nxpwifi_dbg(priv->adapter, EVENT,
|
||||
"event: TID, TA not found in table\n");
|
||||
return;
|
||||
}
|
||||
nxpwifi_del_rx_reorder_entry(priv, tbl);
|
||||
} else {
|
||||
guard(rcu)();
|
||||
ptx_tbl = nxpwifi_get_ba_tbl(priv, tid, peer_mac);
|
||||
|
||||
if (!ptx_tbl) {
|
||||
nxpwifi_dbg(priv->adapter, EVENT,
|
||||
"event: TID, RA not found in table\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
|
||||
ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, peer_mac);
|
||||
if (ra_list) {
|
||||
ra_list->amsdu_in_ampdu = false;
|
||||
ra_list->ba_status = BA_SETUP_NONE;
|
||||
}
|
||||
spin_lock_bh(&priv->tx_ba_stream_tbl_lock[tid]);
|
||||
nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
|
||||
spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[tid]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle ADDBA response. */
|
||||
int nxpwifi_ret_11n_addba_resp(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp)
|
||||
{
|
||||
struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
|
||||
int tid, win_size;
|
||||
struct nxpwifi_rx_reorder_tbl *tbl;
|
||||
u16 block_ack_param_set;
|
||||
|
||||
block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
|
||||
|
||||
tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
|
||||
>> BLOCKACKPARAM_TID_POS;
|
||||
/* Check if we had rejected the ADDBA, if yes then do not create the stream */
|
||||
if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n",
|
||||
add_ba_rsp->peer_mac_addr, tid);
|
||||
|
||||
tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid,
|
||||
add_ba_rsp->peer_mac_addr);
|
||||
if (tbl)
|
||||
nxpwifi_del_rx_reorder_entry(priv, tbl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
|
||||
>> BLOCKACKPARAM_WINSIZE_POS;
|
||||
|
||||
tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid,
|
||||
add_ba_rsp->peer_mac_addr);
|
||||
if (tbl) {
|
||||
if ((block_ack_param_set & IEEE80211_ADDBA_PARAM_AMSDU_MASK) &&
|
||||
priv->add_ba_param.rx_amsdu &&
|
||||
priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
|
||||
tbl->amsdu = true;
|
||||
else
|
||||
tbl->amsdu = false;
|
||||
}
|
||||
|
||||
nxpwifi_dbg(priv->adapter, CMD,
|
||||
"cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
|
||||
add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle BA stream timeout: send DELBA. */
|
||||
void nxpwifi_11n_ba_stream_timeout(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_11n_batimeout *event)
|
||||
{
|
||||
struct host_cmd_ds_11n_delba delba;
|
||||
|
||||
memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba));
|
||||
memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN);
|
||||
|
||||
delba.del_ba_param_set |=
|
||||
cpu_to_le16((u16)event->tid << DELBA_TID_POS);
|
||||
delba.del_ba_param_set |=
|
||||
cpu_to_le16((u16)event->origninator << DELBA_INITIATOR_POS);
|
||||
delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT);
|
||||
nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA, 0, 0, &delba, false);
|
||||
}
|
||||
|
||||
/* Cleanup all RX reorder entries. */
|
||||
void nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_rx_reorder_tbl *del_tbl_ptr, *tmp_node;
|
||||
LIST_HEAD(to_delete_list);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_NUM_TID; i++) {
|
||||
spin_lock_bh(&priv->rx_reorder_tbl_lock[i]);
|
||||
list_splice_init(&priv->rx_reorder_tbl_ptr[i], &to_delete_list);
|
||||
spin_unlock_bh(&priv->rx_reorder_tbl_lock[i]);
|
||||
|
||||
list_for_each_entry_safe(del_tbl_ptr, tmp_node, &to_delete_list, list)
|
||||
nxpwifi_del_rx_reorder_entry(priv, del_tbl_ptr);
|
||||
|
||||
INIT_LIST_HEAD(&to_delete_list);
|
||||
}
|
||||
|
||||
nxpwifi_reset_11n_rx_seq_num(priv);
|
||||
}
|
||||
|
||||
/* Update flags for all RX reorder tables. */
|
||||
void nxpwifi_update_rxreor_flags(struct nxpwifi_adapter *adapter, u8 flags)
|
||||
{
|
||||
struct nxpwifi_private *priv;
|
||||
struct nxpwifi_rx_reorder_tbl *tbl;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
|
||||
for (j = 0; j < MAX_NUM_TID; j++) {
|
||||
spin_lock_bh(&priv->rx_reorder_tbl_lock[j]);
|
||||
list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[j], list)
|
||||
tbl->flags = flags;
|
||||
spin_unlock_bh(&priv->rx_reorder_tbl_lock[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update RX window size based on coex flag. */
|
||||
static void nxpwifi_update_ampdu_rxwinsize(struct nxpwifi_adapter *adapter,
|
||||
bool coex_flag)
|
||||
{
|
||||
u8 i, j;
|
||||
u32 rx_win_size;
|
||||
struct nxpwifi_private *priv;
|
||||
|
||||
nxpwifi_dbg(adapter, INFO, "Update rxwinsize %d\n", coex_flag);
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
rx_win_size = priv->add_ba_param.rx_win_size;
|
||||
if (coex_flag) {
|
||||
if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
|
||||
priv->add_ba_param.rx_win_size =
|
||||
NXPWIFI_STA_COEX_AMPDU_DEF_RXWINSIZE;
|
||||
if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
|
||||
priv->add_ba_param.rx_win_size =
|
||||
NXPWIFI_UAP_COEX_AMPDU_DEF_RXWINSIZE;
|
||||
} else {
|
||||
if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
|
||||
priv->add_ba_param.rx_win_size =
|
||||
NXPWIFI_STA_AMPDU_DEF_RXWINSIZE;
|
||||
if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
|
||||
priv->add_ba_param.rx_win_size =
|
||||
NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE;
|
||||
}
|
||||
|
||||
if (adapter->coex_win_size && adapter->coex_rx_win_size)
|
||||
priv->add_ba_param.rx_win_size =
|
||||
adapter->coex_rx_win_size;
|
||||
|
||||
if (rx_win_size != priv->add_ba_param.rx_win_size) {
|
||||
if (!priv->media_connected)
|
||||
continue;
|
||||
for (j = 0; j < MAX_NUM_TID; j++)
|
||||
nxpwifi_11n_delba(priv, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check coex for RX BA. */
|
||||
void nxpwifi_coex_ampdu_rxwinsize(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
u8 i;
|
||||
struct nxpwifi_private *priv;
|
||||
u8 count = 0;
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) {
|
||||
if (priv->media_connected)
|
||||
count++;
|
||||
}
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
|
||||
if (priv->bss_started)
|
||||
count++;
|
||||
}
|
||||
if (count >= NXPWIFI_BSS_COEX_COUNT)
|
||||
break;
|
||||
}
|
||||
if (count >= NXPWIFI_BSS_COEX_COUNT)
|
||||
nxpwifi_update_ampdu_rxwinsize(adapter, true);
|
||||
else
|
||||
nxpwifi_update_ampdu_rxwinsize(adapter, false);
|
||||
}
|
||||
|
||||
/* Handle RXBA sync event. */
|
||||
void nxpwifi_11n_rxba_sync_event(struct nxpwifi_private *priv,
|
||||
u8 *event_buf, u16 len)
|
||||
{
|
||||
struct nxpwifi_ie_types_rxba_sync *tlv_rxba = (void *)event_buf;
|
||||
u16 tlv_type, tlv_len;
|
||||
struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr;
|
||||
u8 i, j;
|
||||
u16 seq_num, tlv_seq_num, tlv_bitmap_len;
|
||||
int tlv_buf_left = len;
|
||||
int ret;
|
||||
u8 *tmp;
|
||||
|
||||
nxpwifi_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:",
|
||||
event_buf, len);
|
||||
while (tlv_buf_left > sizeof(*tlv_rxba)) {
|
||||
tlv_type = le16_to_cpu(tlv_rxba->header.type);
|
||||
tlv_len = le16_to_cpu(tlv_rxba->header.len);
|
||||
if (size_add(sizeof(tlv_rxba->header), tlv_len) > tlv_buf_left) {
|
||||
nxpwifi_dbg(priv->adapter, WARN,
|
||||
"TLV size (%zu) overflows event_buf buf_left=%d\n",
|
||||
size_add(sizeof(tlv_rxba->header), tlv_len),
|
||||
tlv_buf_left);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tlv_type != TLV_TYPE_RXBA_SYNC) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"Wrong TLV id=0x%x\n", tlv_type);
|
||||
return;
|
||||
}
|
||||
|
||||
tlv_seq_num = le16_to_cpu(tlv_rxba->seq_num);
|
||||
tlv_bitmap_len = le16_to_cpu(tlv_rxba->bitmap_len);
|
||||
if (size_add(sizeof(*tlv_rxba), tlv_bitmap_len) > tlv_buf_left) {
|
||||
nxpwifi_dbg(priv->adapter, WARN,
|
||||
"TLV size (%zu) overflows event_buf buf_left=%d\n",
|
||||
size_add(sizeof(*tlv_rxba), tlv_bitmap_len),
|
||||
tlv_buf_left);
|
||||
return;
|
||||
}
|
||||
|
||||
nxpwifi_dbg(priv->adapter, INFO,
|
||||
"%pM tid=%d seq_num=%d bitmap_len=%d\n",
|
||||
tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num,
|
||||
tlv_bitmap_len);
|
||||
|
||||
rx_reor_tbl_ptr =
|
||||
nxpwifi_11n_get_rx_reorder_tbl(priv, tlv_rxba->tid,
|
||||
tlv_rxba->mac);
|
||||
if (!rx_reor_tbl_ptr) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"Can not find rx_reorder_tbl!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < tlv_bitmap_len; i++) {
|
||||
for (j = 0 ; j < 8; j++) {
|
||||
if (tlv_rxba->bitmap[i] & (1 << j)) {
|
||||
seq_num = (MAX_TID_VALUE - 1) &
|
||||
(tlv_seq_num + i * 8 + j);
|
||||
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"drop packet,seq=%d\n",
|
||||
seq_num);
|
||||
|
||||
ret = nxpwifi_11n_rx_reorder_pkt
|
||||
(priv, seq_num, tlv_rxba->tid,
|
||||
tlv_rxba->mac, 0, NULL);
|
||||
|
||||
if (ret)
|
||||
nxpwifi_dbg(priv->adapter,
|
||||
ERROR,
|
||||
"Fail to drop packet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tlv_buf_left -= (sizeof(tlv_rxba->header) + tlv_len);
|
||||
tmp = (u8 *)tlv_rxba + sizeof(tlv_rxba->header) + tlv_len;
|
||||
tlv_rxba = (struct nxpwifi_ie_types_rxba_sync *)tmp;
|
||||
}
|
||||
}
|
||||
71
drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h
Normal file
71
drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* NXP Wireless LAN device driver: 802.11n RX Re-ordering
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_11N_RXREORDER_H_
|
||||
#define _NXPWIFI_11N_RXREORDER_H_
|
||||
|
||||
#define MIN_FLUSH_TIMER_MS 50
|
||||
#define MIN_FLUSH_TIMER_15_MS 15
|
||||
#define NXPWIFI_BA_WIN_SIZE_32 32
|
||||
|
||||
#define PKT_TYPE_BAR 0xE7
|
||||
#define MAX_TID_VALUE (2 << 11)
|
||||
#define TWOPOW11 (2 << 10)
|
||||
|
||||
#define BLOCKACKPARAM_TID_POS 2
|
||||
#define BLOCKACKPARAM_WINSIZE_POS 6
|
||||
#define DELBA_TID_POS 12
|
||||
#define DELBA_INITIATOR_POS 11
|
||||
#define TYPE_DELBA_SENT 1
|
||||
#define TYPE_DELBA_RECEIVE 2
|
||||
#define IMMEDIATE_BLOCK_ACK 0x2
|
||||
|
||||
#define ADDBA_RSP_STATUS_ACCEPT 0
|
||||
|
||||
#define NXPWIFI_DEF_11N_RX_SEQ_NUM 0xffff
|
||||
#define BA_SETUP_MAX_PACKET_THRESHOLD 16
|
||||
#define BA_SETUP_PACKET_OFFSET 16
|
||||
|
||||
enum nxpwifi_rxreor_flags {
|
||||
RXREOR_FORCE_NO_DROP = 1 << 0,
|
||||
RXREOR_INIT_WINDOW_SHIFT = 1 << 1,
|
||||
};
|
||||
|
||||
static inline void nxpwifi_reset_11n_rx_seq_num(struct nxpwifi_private *priv)
|
||||
{
|
||||
memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq));
|
||||
}
|
||||
|
||||
int nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private *priv,
|
||||
u16 seq_num,
|
||||
u16 tid, u8 *ta,
|
||||
u8 pkttype, void *payload);
|
||||
void nxpwifi_del_ba_tbl(struct nxpwifi_private *priv, int tid,
|
||||
u8 *peer_mac, u8 type, int initiator);
|
||||
void nxpwifi_11n_ba_stream_timeout(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_11n_batimeout *event);
|
||||
int nxpwifi_ret_11n_addba_resp(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command
|
||||
*resp);
|
||||
int nxpwifi_cmd_11n_delba(struct host_cmd_ds_command *cmd,
|
||||
void *data_buf);
|
||||
int nxpwifi_cmd_11n_addba_rsp_gen(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd,
|
||||
struct host_cmd_ds_11n_addba_req
|
||||
*cmd_addba_req);
|
||||
int nxpwifi_cmd_11n_addba_req(struct host_cmd_ds_command *cmd,
|
||||
void *data_buf);
|
||||
void nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private *priv);
|
||||
struct nxpwifi_rx_reorder_tbl *
|
||||
nxpwifi_11n_get_rxreorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta);
|
||||
struct nxpwifi_rx_reorder_tbl *
|
||||
nxpwifi_11n_get_rx_reorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta);
|
||||
void nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private *priv, u8 *ta);
|
||||
void nxpwifi_update_rxreor_flags(struct nxpwifi_adapter *adapter, u8 flags);
|
||||
void nxpwifi_11n_rxba_sync_event(struct nxpwifi_private *priv,
|
||||
u8 *event_buf, u16 len);
|
||||
#endif /* _NXPWIFI_11N_RXREORDER_H_ */
|
||||
22
drivers/net/wireless/nxp/nxpwifi/Kconfig
Normal file
22
drivers/net/wireless/nxp/nxpwifi/Kconfig
Normal file
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config NXPWIFI
|
||||
tristate "NXP WiFi Driver"
|
||||
depends on CFG80211
|
||||
help
|
||||
This adds support for wireless adapters based on NXP
|
||||
802.11n/ac chipsets.
|
||||
|
||||
If you choose to build it as a module, it will be called
|
||||
nxpwifi.
|
||||
|
||||
config NXPWIFI_SDIO
|
||||
tristate "NXP WiFi Driver for IW61x"
|
||||
depends on NXPWIFI && MMC
|
||||
select FW_LOADER
|
||||
select WANT_DEV_COREDUMP
|
||||
help
|
||||
This adds support for wireless adapters based on NXP
|
||||
IW61x interface.
|
||||
|
||||
If you choose to build it as a module, it will be called
|
||||
nxpwifi_sdio.
|
||||
39
drivers/net/wireless/nxp/nxpwifi/Makefile
Normal file
39
drivers/net/wireless/nxp/nxpwifi/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Copyright 2011-2020 NXP
|
||||
#
|
||||
|
||||
|
||||
nxpwifi-y += main.o
|
||||
nxpwifi-y += init.o
|
||||
nxpwifi-y += cfp.o
|
||||
nxpwifi-y += cmdevt.o
|
||||
nxpwifi-y += util.o
|
||||
nxpwifi-y += txrx.o
|
||||
nxpwifi-y += wmm.o
|
||||
nxpwifi-y += 11n.o
|
||||
nxpwifi-y += 11ac.o
|
||||
nxpwifi-y += 11ax.o
|
||||
nxpwifi-y += 11n_aggr.o
|
||||
nxpwifi-y += 11n_rxreorder.o
|
||||
nxpwifi-y += scan.o
|
||||
nxpwifi-y += join.o
|
||||
nxpwifi-y += sta_cfg.o
|
||||
nxpwifi-y += sta_cmd.o
|
||||
nxpwifi-y += uap_cmd.o
|
||||
nxpwifi-y += ie.o
|
||||
nxpwifi-y += sta_event.o
|
||||
nxpwifi-y += uap_event.o
|
||||
nxpwifi-y += sta_tx.o
|
||||
nxpwifi-y += sta_rx.o
|
||||
nxpwifi-y += uap_txrx.o
|
||||
nxpwifi-y += cfg80211.o
|
||||
nxpwifi-y += ethtool.o
|
||||
nxpwifi-y += 11h.o
|
||||
nxpwifi-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
obj-$(CONFIG_NXPWIFI) += nxpwifi.o
|
||||
|
||||
nxpwifi_sdio-y += sdio.o
|
||||
obj-$(CONFIG_NXPWIFI_SDIO) += nxpwifi_sdio.o
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN
|
||||
1019
drivers/net/wireless/nxp/nxpwifi/cfg.h
Normal file
1019
drivers/net/wireless/nxp/nxpwifi/cfg.h
Normal file
File diff suppressed because it is too large
Load Diff
3931
drivers/net/wireless/nxp/nxpwifi/cfg80211.c
Normal file
3931
drivers/net/wireless/nxp/nxpwifi/cfg80211.c
Normal file
File diff suppressed because it is too large
Load Diff
18
drivers/net/wireless/nxp/nxpwifi/cfg80211.h
Normal file
18
drivers/net/wireless/nxp/nxpwifi/cfg80211.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* nxpwifi: cfg80211 support
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef __NXPWIFI_CFG80211__
|
||||
#define __NXPWIFI_CFG80211__
|
||||
|
||||
#include "main.h"
|
||||
|
||||
int nxpwifi_register_cfg80211(struct nxpwifi_adapter *adapter);
|
||||
|
||||
int nxpwifi_cfg80211_change_beacon(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_ap_update *params);
|
||||
#endif
|
||||
478
drivers/net/wireless/nxp/nxpwifi/cfp.c
Normal file
478
drivers/net/wireless/nxp/nxpwifi/cfp.c
Normal file
@@ -0,0 +1,478 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi: Channel, Frequency and Power
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "cfg80211.h"
|
||||
|
||||
/* 100mW */
|
||||
#define NXPWIFI_TX_PWR_DEFAULT 20
|
||||
/* 100mW */
|
||||
#define NXPWIFI_TX_PWR_US_DEFAULT 20
|
||||
/* 50mW */
|
||||
#define NXPWIFI_TX_PWR_JP_DEFAULT 16
|
||||
/* 100mW */
|
||||
#define NXPWIFI_TX_PWR_FR_100MW 20
|
||||
/* 10mW */
|
||||
#define NXPWIFI_TX_PWR_FR_10MW 10
|
||||
/* 100mW */
|
||||
#define NXPWIFI_TX_PWR_EMEA_DEFAULT 20
|
||||
|
||||
static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
|
||||
0xb0, 0x48, 0x60, 0x6c, 0 };
|
||||
static u16 nxpwifi_data_rates[NXPWIFI_SUPPORTED_RATES_EXT] = { 0x02, 0x04,
|
||||
0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18,
|
||||
0x24, 0x30, 0x48, 0x60, 0x6C, 0x90,
|
||||
0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68,
|
||||
0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51,
|
||||
0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 };
|
||||
|
||||
static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 };
|
||||
|
||||
static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
|
||||
0x30, 0x48, 0x60, 0x6c, 0 };
|
||||
|
||||
static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
|
||||
0x12, 0x16, 0x18, 0x24, 0x30, 0x48,
|
||||
0x60, 0x6c, 0 };
|
||||
|
||||
/* mcs_rate: first 8 entries for 1x1; all 16 for 2x2. */
|
||||
static const u16 mcs_rate[4][16] = {
|
||||
/* LGI 40M */
|
||||
{ 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
|
||||
0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
|
||||
|
||||
/* SGI 40M */
|
||||
{ 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
|
||||
0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
|
||||
|
||||
/* LGI 20M */
|
||||
{ 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
|
||||
0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
|
||||
|
||||
/* SGI 20M */
|
||||
{ 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
|
||||
0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
|
||||
};
|
||||
|
||||
/* AC rates */
|
||||
static const u16 ac_mcs_rate_nss1[8][10] = {
|
||||
/* LG 160M */
|
||||
{ 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
|
||||
0x492, 0x57C, 0x618 },
|
||||
|
||||
/* SG 160M */
|
||||
{ 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
|
||||
0x514, 0x618, 0x6C6 },
|
||||
|
||||
/* LG 80M */
|
||||
{ 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
|
||||
0x249, 0x2BE, 0x30C },
|
||||
|
||||
/* SG 80M */
|
||||
{ 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
|
||||
0x28A, 0x30C, 0x363 },
|
||||
|
||||
/* LG 40M */
|
||||
{ 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
|
||||
0x10E, 0x144, 0x168 },
|
||||
|
||||
/* SG 40M */
|
||||
{ 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
|
||||
0x12C, 0x168, 0x190 },
|
||||
|
||||
/* LG 20M */
|
||||
{ 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
|
||||
|
||||
/* SG 20M */
|
||||
{ 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
|
||||
};
|
||||
|
||||
/* NSS2 note: the value in the table is 2 multiplier of the actual rate */
|
||||
static const u16 ac_mcs_rate_nss2[8][10] = {
|
||||
/* LG 160M */
|
||||
{ 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
|
||||
0x924, 0xAF8, 0xC30 },
|
||||
|
||||
/* SG 160M */
|
||||
{ 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
|
||||
0xA28, 0xC30, 0xD8B },
|
||||
|
||||
/* LG 80M */
|
||||
{ 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
|
||||
0x492, 0x57C, 0x618 },
|
||||
|
||||
/* SG 80M */
|
||||
{ 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
|
||||
0x514, 0x618, 0x6C6 },
|
||||
|
||||
/* LG 40M */
|
||||
{ 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
|
||||
0x21C, 0x288, 0x2D0 },
|
||||
|
||||
/* SG 40M */
|
||||
{ 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
|
||||
0x258, 0x2D0, 0x320 },
|
||||
|
||||
/* LG 20M */
|
||||
{ 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
|
||||
0x138, 0x00 },
|
||||
|
||||
/* SG 20M */
|
||||
{ 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
|
||||
0x15B, 0x00 },
|
||||
};
|
||||
|
||||
struct region_code_mapping {
|
||||
u8 code;
|
||||
u8 region[IEEE80211_COUNTRY_STRING_LEN];
|
||||
};
|
||||
|
||||
static struct region_code_mapping region_code_mapping_t[] = {
|
||||
{ 0x10, "US " }, /* US FCC */
|
||||
{ 0x20, "CA " }, /* IC Canada */
|
||||
{ 0x30, "FR " }, /* France */
|
||||
{ 0x31, "ES " }, /* Spain */
|
||||
{ 0x32, "FR " }, /* France */
|
||||
{ 0x40, "JP " }, /* Japan */
|
||||
{ 0x41, "JP " }, /* Japan */
|
||||
{ 0x50, "CN " }, /* China */
|
||||
};
|
||||
|
||||
/* Convert 11d country code to region string. */
|
||||
u8 *nxpwifi_11d_code_2_region(u8 code)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
/* Look for code in mapping table */
|
||||
for (i = 0; i < ARRAY_SIZE(region_code_mapping_t); i++)
|
||||
if (region_code_mapping_t[i].code == code)
|
||||
return region_code_mapping_t[i].region;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Map supported rate index to AC/VHT data rate. */
|
||||
u32 nxpwifi_index_to_acs_data_rate(struct nxpwifi_private *priv,
|
||||
u8 index, u8 ht_info)
|
||||
{
|
||||
u32 rate = 0;
|
||||
u8 mcs_index = 0;
|
||||
u8 bw = 0;
|
||||
u8 gi = 0;
|
||||
|
||||
if ((ht_info & 0x3) == NXPWIFI_RATE_FORMAT_VHT) {
|
||||
mcs_index = min(index & 0xF, 9);
|
||||
|
||||
/* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */
|
||||
bw = (ht_info & 0xC) >> 2;
|
||||
|
||||
/* LGI: gi =0, SGI: gi = 1 */
|
||||
gi = (ht_info & 0x10) >> 4;
|
||||
|
||||
if ((index >> 4) == 1) /* NSS = 2 */
|
||||
rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index];
|
||||
else /* NSS = 1 */
|
||||
rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index];
|
||||
} else if ((ht_info & 0x3) == NXPWIFI_RATE_FORMAT_HT) {
|
||||
/* 20M: bw=0, 40M: bw=1 */
|
||||
bw = (ht_info & 0xC) >> 2;
|
||||
|
||||
/* LGI: gi =0, SGI: gi = 1 */
|
||||
gi = (ht_info & 0x10) >> 4;
|
||||
|
||||
if (index == NXPWIFI_RATE_BITMAP_MCS0) {
|
||||
if (gi == 1)
|
||||
rate = 0x0D; /* MCS 32 SGI rate */
|
||||
else
|
||||
rate = 0x0C; /* MCS 32 LGI rate */
|
||||
} else if (index < 16) {
|
||||
if (bw == 1 || bw == 0)
|
||||
rate = mcs_rate[2 * (1 - bw) + gi][index];
|
||||
else
|
||||
rate = nxpwifi_data_rates[0];
|
||||
} else {
|
||||
rate = nxpwifi_data_rates[0];
|
||||
}
|
||||
} else {
|
||||
/* 11n non-HT rates */
|
||||
if (index >= NXPWIFI_SUPPORTED_RATES_EXT)
|
||||
index = 0;
|
||||
rate = nxpwifi_data_rates[index];
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
/* Map supported rate index to data rate. */
|
||||
u32 nxpwifi_index_to_data_rate(struct nxpwifi_private *priv,
|
||||
u8 index, u8 ht_info)
|
||||
{
|
||||
u32 mcs_num_supp =
|
||||
(priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
|
||||
u32 rate;
|
||||
|
||||
if (priv->adapter->is_hw_11ac_capable)
|
||||
return nxpwifi_index_to_acs_data_rate(priv, index, ht_info);
|
||||
|
||||
if (ht_info & BIT(0)) {
|
||||
if (index == NXPWIFI_RATE_BITMAP_MCS0) {
|
||||
if (ht_info & BIT(2))
|
||||
rate = 0x0D; /* MCS 32 SGI rate */
|
||||
else
|
||||
rate = 0x0C; /* MCS 32 LGI rate */
|
||||
} else if (index < mcs_num_supp) {
|
||||
if (ht_info & BIT(1)) {
|
||||
if (ht_info & BIT(2))
|
||||
/* SGI, 40M */
|
||||
rate = mcs_rate[1][index];
|
||||
else
|
||||
/* LGI, 40M */
|
||||
rate = mcs_rate[0][index];
|
||||
} else {
|
||||
if (ht_info & BIT(2))
|
||||
/* SGI, 20M */
|
||||
rate = mcs_rate[3][index];
|
||||
else
|
||||
/* LGI, 20M */
|
||||
rate = mcs_rate[2][index];
|
||||
}
|
||||
} else {
|
||||
rate = nxpwifi_data_rates[0];
|
||||
}
|
||||
} else {
|
||||
if (index >= NXPWIFI_SUPPORTED_RATES_EXT)
|
||||
index = 0;
|
||||
rate = nxpwifi_data_rates[index];
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
/* Return current active data rates (depends on connection). */
|
||||
u32 nxpwifi_get_active_data_rates(struct nxpwifi_private *priv, u8 *rates)
|
||||
{
|
||||
if (!priv->media_connected)
|
||||
return nxpwifi_get_supported_rates(priv, rates);
|
||||
else
|
||||
return nxpwifi_copy_rates(rates, 0,
|
||||
priv->curr_bss_params.data_rates,
|
||||
priv->curr_bss_params.num_of_rates);
|
||||
}
|
||||
|
||||
/* Find Channel/Frequency/Power by band and channel or frequency. */
|
||||
struct nxpwifi_chan_freq_power *
|
||||
nxpwifi_get_cfp(struct nxpwifi_private *priv, u8 band, u16 channel, u32 freq)
|
||||
{
|
||||
struct nxpwifi_chan_freq_power *cfp = NULL;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_channel *ch = NULL;
|
||||
int i;
|
||||
|
||||
if (!channel && !freq)
|
||||
return cfp;
|
||||
|
||||
if (nxpwifi_band_to_radio_type(band) == HOST_SCAN_RADIO_TYPE_BG)
|
||||
sband = priv->wdev.wiphy->bands[NL80211_BAND_2GHZ];
|
||||
else
|
||||
sband = priv->wdev.wiphy->bands[NL80211_BAND_5GHZ];
|
||||
|
||||
if (!sband) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"%s: cannot find cfp by band %d\n",
|
||||
__func__, band);
|
||||
return cfp;
|
||||
}
|
||||
|
||||
for (i = 0; i < sband->n_channels; i++) {
|
||||
ch = &sband->channels[i];
|
||||
|
||||
if (ch->flags & IEEE80211_CHAN_DISABLED)
|
||||
continue;
|
||||
|
||||
if (freq) {
|
||||
if (ch->center_freq == freq)
|
||||
break;
|
||||
} else {
|
||||
/* Find by valid channel. */
|
||||
if (ch->hw_value == channel ||
|
||||
channel == FIRST_VALID_CHANNEL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == sband->n_channels) {
|
||||
nxpwifi_dbg(priv->adapter, WARN,
|
||||
"%s: cannot find cfp by band %d\t"
|
||||
"& channel=%d freq=%d\n",
|
||||
__func__, band, channel, freq);
|
||||
} else {
|
||||
if (!ch)
|
||||
return cfp;
|
||||
|
||||
priv->cfp.channel = ch->hw_value;
|
||||
priv->cfp.freq = ch->center_freq;
|
||||
priv->cfp.max_tx_power = ch->max_power;
|
||||
cfp = &priv->cfp;
|
||||
}
|
||||
|
||||
return cfp;
|
||||
}
|
||||
|
||||
/* Return true if data rate is set to auto. */
|
||||
u8
|
||||
nxpwifi_is_rate_auto(struct nxpwifi_private *priv)
|
||||
{
|
||||
u32 i;
|
||||
int rate_num = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++)
|
||||
if (priv->bitmap_rates[i])
|
||||
rate_num++;
|
||||
|
||||
if (rate_num > 1)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Extract supported rates from cfg80211_scan_request bitmask. */
|
||||
u32 nxpwifi_get_rates_from_cfg80211(struct nxpwifi_private *priv,
|
||||
u8 *rates, u8 radio_type)
|
||||
{
|
||||
struct wiphy *wiphy = priv->adapter->wiphy;
|
||||
struct cfg80211_scan_request *request = priv->scan_request;
|
||||
u32 num_rates, rate_mask;
|
||||
struct ieee80211_supported_band *sband;
|
||||
int i;
|
||||
|
||||
if (radio_type) {
|
||||
sband = wiphy->bands[NL80211_BAND_5GHZ];
|
||||
if (WARN_ON_ONCE(!sband))
|
||||
return 0;
|
||||
rate_mask = request->rates[NL80211_BAND_5GHZ];
|
||||
} else {
|
||||
sband = wiphy->bands[NL80211_BAND_2GHZ];
|
||||
if (WARN_ON_ONCE(!sband))
|
||||
return 0;
|
||||
rate_mask = request->rates[NL80211_BAND_2GHZ];
|
||||
}
|
||||
|
||||
num_rates = 0;
|
||||
for (i = 0; i < sband->n_bitrates; i++) {
|
||||
if ((BIT(i) & rate_mask) == 0)
|
||||
continue; /* skip rate */
|
||||
rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
|
||||
}
|
||||
|
||||
return num_rates;
|
||||
}
|
||||
|
||||
/* Convert config_bands to B/G/A band */
|
||||
static u16 nxpwifi_convert_config_bands(u16 config_bands)
|
||||
{
|
||||
u16 bands = 0;
|
||||
|
||||
if (config_bands & BAND_B)
|
||||
bands |= BAND_B;
|
||||
if (config_bands & BAND_G || config_bands & BAND_GN ||
|
||||
config_bands & BAND_GAC || config_bands & BAND_GAX)
|
||||
bands |= BAND_G;
|
||||
if (config_bands & BAND_A || config_bands & BAND_AN ||
|
||||
config_bands & BAND_AAC || config_bands & BAND_AAX)
|
||||
bands |= BAND_A;
|
||||
|
||||
return bands;
|
||||
}
|
||||
|
||||
/* Get supported rates in infrastructure (STA/P2P client) mode. */
|
||||
u32 nxpwifi_get_supported_rates(struct nxpwifi_private *priv, u8 *rates)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u32 k = 0;
|
||||
u16 bands = 0;
|
||||
|
||||
bands = nxpwifi_convert_config_bands(adapter->fw_bands);
|
||||
|
||||
if (priv->bss_mode == NL80211_IFTYPE_STATION) {
|
||||
if (bands == BAND_B) {
|
||||
/* B only */
|
||||
nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
|
||||
"supported_rates_b\n",
|
||||
priv->config_bands);
|
||||
k = nxpwifi_copy_rates(rates, k, supported_rates_b,
|
||||
sizeof(supported_rates_b));
|
||||
} else if (bands == BAND_G) {
|
||||
/* G only */
|
||||
nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
|
||||
"supported_rates_g\n",
|
||||
priv->config_bands);
|
||||
k = nxpwifi_copy_rates(rates, k, supported_rates_g,
|
||||
sizeof(supported_rates_g));
|
||||
} else if (bands & (BAND_B | BAND_G)) {
|
||||
/* BG only */
|
||||
nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
|
||||
"supported_rates_bg\n",
|
||||
priv->config_bands);
|
||||
k = nxpwifi_copy_rates(rates, k, supported_rates_bg,
|
||||
sizeof(supported_rates_bg));
|
||||
} else if (bands & BAND_A) {
|
||||
/* support A */
|
||||
nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
|
||||
"supported_rates_a\n",
|
||||
priv->config_bands);
|
||||
k = nxpwifi_copy_rates(rates, k, supported_rates_a,
|
||||
sizeof(supported_rates_a));
|
||||
}
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
u8 nxpwifi_adjust_data_rate(struct nxpwifi_private *priv,
|
||||
u8 rx_rate, u8 rate_info)
|
||||
{
|
||||
u8 rate_index = 0;
|
||||
|
||||
/* HT40 */
|
||||
if ((rate_info & BIT(0)) && (rate_info & BIT(1)))
|
||||
rate_index = NXPWIFI_RATE_INDEX_MCS0 +
|
||||
NXPWIFI_BW20_MCS_NUM + rx_rate;
|
||||
else if (rate_info & BIT(0)) /* HT20 */
|
||||
rate_index = NXPWIFI_RATE_INDEX_MCS0 + rx_rate;
|
||||
else
|
||||
rate_index = (rx_rate > NXPWIFI_RATE_INDEX_OFDM0) ?
|
||||
rx_rate - 1 : rx_rate;
|
||||
|
||||
if (rate_index >= NXPWIFI_MAX_AC_RX_RATES)
|
||||
rate_index = NXPWIFI_MAX_AC_RX_RATES - 1;
|
||||
|
||||
return rate_index;
|
||||
}
|
||||
|
||||
/* Check if the given region code is a valid NXP-defined region code.
|
||||
* Valid codes are defined by the FW v18 region enum in IEEE_types.h:
|
||||
* 0x00 (World), 0x10 (US/FCC), 0x20 (Canada/IC), 0x30 (ETSI),
|
||||
* 0x31 (Spain), 0x32 (France), 0x40 (Japan), 0x41 (Japan1), 0x50 (China)
|
||||
*/
|
||||
bool nxpwifi_is_valid_region_code(enum nxpwifi_region_code code)
|
||||
{
|
||||
switch (code) {
|
||||
case NXPWIFI_REGION_WORLD:
|
||||
case NXPWIFI_REGION_FCC:
|
||||
case NXPWIFI_REGION_IC:
|
||||
case NXPWIFI_REGION_ETSI:
|
||||
case NXPWIFI_REGION_SPAIN:
|
||||
case NXPWIFI_REGION_FRANCE:
|
||||
case NXPWIFI_REGION_JAPAN:
|
||||
case NXPWIFI_REGION_JAPAN1:
|
||||
case NXPWIFI_REGION_CHINA:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
1310
drivers/net/wireless/nxp/nxpwifi/cmdevt.c
Normal file
1310
drivers/net/wireless/nxp/nxpwifi/cmdevt.c
Normal file
File diff suppressed because it is too large
Load Diff
122
drivers/net/wireless/nxp/nxpwifi/cmdevt.h
Normal file
122
drivers/net/wireless/nxp/nxpwifi/cmdevt.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* nxpwifi: commands and events
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_CMD_EVT_H_
|
||||
#define _NXPWIFI_CMD_EVT_H_
|
||||
|
||||
struct nxpwifi_cmd_entry {
|
||||
u16 cmd_no;
|
||||
int (*prepare_cmd)(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd,
|
||||
u16 cmd_no, void *data_buf,
|
||||
u16 cmd_action, u32 cmd_type);
|
||||
int (*cmd_resp)(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp,
|
||||
u16 cmdresp_no,
|
||||
void *data_buf);
|
||||
};
|
||||
|
||||
struct nxpwifi_evt_entry {
|
||||
u32 event_cause;
|
||||
int (*event_handler)(struct nxpwifi_private *priv);
|
||||
};
|
||||
|
||||
static inline int
|
||||
nxpwifi_cmd_fill_head_only(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd,
|
||||
u16 cmd_no, void *data_buf,
|
||||
u16 cmd_action, u32 cmd_type)
|
||||
{
|
||||
cmd->command = cpu_to_le16(cmd_no);
|
||||
cmd->size = cpu_to_le16(S_DS_GEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxpwifi_send_cmd(struct nxpwifi_private *priv, u16 cmd_no,
|
||||
u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync);
|
||||
int nxpwifi_sta_prepare_cmd(struct nxpwifi_private *priv,
|
||||
struct cmd_ctrl_node *cmd_node,
|
||||
u16 cmd_action, u32 cmd_oid);
|
||||
int nxpwifi_sta_init_cmd(struct nxpwifi_private *priv, u8 first_sta, bool init);
|
||||
int nxpwifi_uap_prepare_cmd(struct nxpwifi_private *priv,
|
||||
struct cmd_ctrl_node *cmd_node,
|
||||
u16 cmd_action, u32 type);
|
||||
int nxpwifi_set_secure_params(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_config,
|
||||
struct cfg80211_ap_settings *params);
|
||||
void nxpwifi_set_ht_params(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_cfg,
|
||||
struct cfg80211_ap_settings *params);
|
||||
void nxpwifi_set_vht_params(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_cfg,
|
||||
struct cfg80211_ap_settings *params);
|
||||
void nxpwifi_set_tpc_params(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_cfg,
|
||||
struct cfg80211_ap_settings *params);
|
||||
void nxpwifi_set_uap_rates(struct nxpwifi_uap_bss_param *bss_cfg,
|
||||
struct cfg80211_ap_settings *params);
|
||||
void nxpwifi_set_vht_width(struct nxpwifi_private *priv,
|
||||
enum nl80211_chan_width width,
|
||||
bool ap_11ac_disable);
|
||||
bool nxpwifi_check_11ax_capability(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_cfg,
|
||||
struct cfg80211_ap_settings *params);
|
||||
int nxpwifi_set_11ax_status(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_cfg,
|
||||
struct cfg80211_ap_settings *params);
|
||||
void nxpwifi_set_sys_config_invalid_data(struct nxpwifi_uap_bss_param *config);
|
||||
void nxpwifi_set_wmm_params(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_cfg,
|
||||
struct cfg80211_ap_settings *params);
|
||||
void nxpwifi_config_uap_11d(struct nxpwifi_private *priv,
|
||||
struct cfg80211_beacon_data *beacon_data);
|
||||
void nxpwifi_uap_set_channel(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_cfg,
|
||||
struct cfg80211_chan_def chandef);
|
||||
int nxpwifi_config_start_uap(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *bss_cfg);
|
||||
int nxpwifi_process_event(struct nxpwifi_adapter *adapter);
|
||||
int nxpwifi_process_sta_event(struct nxpwifi_private *priv);
|
||||
int nxpwifi_process_uap_event(struct nxpwifi_private *priv);
|
||||
void nxpwifi_reset_connect_state(struct nxpwifi_private *priv, u16 reason,
|
||||
bool from_ap);
|
||||
void nxpwifi_process_multi_chan_event(struct nxpwifi_private *priv,
|
||||
struct sk_buff *event_skb);
|
||||
void nxpwifi_process_tx_pause_event(struct nxpwifi_private *priv,
|
||||
struct sk_buff *event);
|
||||
void nxpwifi_bt_coex_wlan_param_update_event(struct nxpwifi_private *priv,
|
||||
struct sk_buff *event_skb);
|
||||
int nxpwifi_mgmt_frame_reg(struct nxpwifi_private *priv, u32 mask);
|
||||
int nxpwifi_set_uap_sys_cfg(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_uap_bss_param *cfg);
|
||||
int nxpwifi_set_rts(struct nxpwifi_private *priv, u32 rts_thr);
|
||||
int nxpwifi_set_frag(struct nxpwifi_private *priv, u32 frag_thr);
|
||||
int nxpwifi_set_bss_mode(struct nxpwifi_private *priv);
|
||||
int nxpwifi_config_monitor_mode(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_802_11_net_monitor *cfg);
|
||||
int nxpwifi_apply_regdomain(struct nxpwifi_private *priv);
|
||||
int nxpwifi_get_tx_pwr(struct nxpwifi_private *priv);
|
||||
int nxpwifi_get_rssi_info(struct nxpwifi_private *priv);
|
||||
int nxpwifi_get_802_11_snmp_mib(struct nxpwifi_private *priv, u16 oid, void *value);
|
||||
int nxpwifi_set_rf_antenna(struct nxpwifi_private *priv, void *antcfg);
|
||||
int nxpwifi_get_rf_antenna(struct nxpwifi_private *priv, u32 *tx_ant, u32 *rx_ant);
|
||||
int nxpwifi_ap_stop_bss(struct nxpwifi_private *priv);
|
||||
int nxpwifi_ap_sys_reset(struct nxpwifi_private *priv);
|
||||
int nxpwifi_cfg80211_deinit_p2p(struct nxpwifi_private *priv);
|
||||
int nxpwifi_ap_get_sta_list(struct nxpwifi_private *priv);
|
||||
int nxpwifi_set_tx_rate(struct nxpwifi_private *priv, void *bitmap_rates);
|
||||
int nxpwifi_802_11_subscribe_event(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ds_misc_subsc_evt *subsc_evt);
|
||||
int nxpwifi_uap_sta_deauth(struct nxpwifi_private *priv, u8 *mac);
|
||||
int nxpwifi_bg_scan_config(struct nxpwifi_private *priv, void *bg_scan_cfg);
|
||||
int nxpwifi_mef_cfg(struct nxpwifi_private *priv, void *mef_cfg);
|
||||
int nxpwifi_coalesce_cfg(struct nxpwifi_private *priv, void *coalesce_cfg);
|
||||
int nxpwifi_add_new_station(struct nxpwifi_private *priv, void *add_sta);
|
||||
int nxpwifi_hostcmd(struct nxpwifi_private *priv, struct nxpwifi_ds_misc_cmd *hostcmd);
|
||||
int nxpwifi_chan_report_request(struct nxpwifi_private *priv, void *radar_params);
|
||||
#endif /* !_NXPWIFI_CMD_EVT_H_ */
|
||||
1094
drivers/net/wireless/nxp/nxpwifi/debugfs.c
Normal file
1094
drivers/net/wireless/nxp/nxpwifi/debugfs.c
Normal file
File diff suppressed because it is too large
Load Diff
58
drivers/net/wireless/nxp/nxpwifi/ethtool.c
Normal file
58
drivers/net/wireless/nxp/nxpwifi/ethtool.c
Normal file
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi: ethtool
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
static void nxpwifi_ethtool_get_wol(struct net_device *dev,
|
||||
struct ethtool_wolinfo *wol)
|
||||
{
|
||||
struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
|
||||
u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions);
|
||||
|
||||
wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_PHY;
|
||||
|
||||
if (conditions == HS_CFG_COND_DEF)
|
||||
return;
|
||||
|
||||
if (conditions & HS_CFG_COND_UNICAST_DATA)
|
||||
wol->wolopts |= WAKE_UCAST;
|
||||
if (conditions & HS_CFG_COND_MULTICAST_DATA)
|
||||
wol->wolopts |= WAKE_MCAST;
|
||||
if (conditions & HS_CFG_COND_BROADCAST_DATA)
|
||||
wol->wolopts |= WAKE_BCAST;
|
||||
if (conditions & HS_CFG_COND_MAC_EVENT)
|
||||
wol->wolopts |= WAKE_PHY;
|
||||
}
|
||||
|
||||
static int nxpwifi_ethtool_set_wol(struct net_device *dev,
|
||||
struct ethtool_wolinfo *wol)
|
||||
{
|
||||
struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
|
||||
u32 conditions = 0;
|
||||
|
||||
if (wol->wolopts & ~(WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_PHY))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (wol->wolopts & WAKE_UCAST)
|
||||
conditions |= HS_CFG_COND_UNICAST_DATA;
|
||||
if (wol->wolopts & WAKE_MCAST)
|
||||
conditions |= HS_CFG_COND_MULTICAST_DATA;
|
||||
if (wol->wolopts & WAKE_BCAST)
|
||||
conditions |= HS_CFG_COND_BROADCAST_DATA;
|
||||
if (wol->wolopts & WAKE_PHY)
|
||||
conditions |= HS_CFG_COND_MAC_EVENT;
|
||||
if (wol->wolopts == 0)
|
||||
conditions |= HS_CFG_COND_DEF;
|
||||
priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct ethtool_ops nxpwifi_ethtool_ops = {
|
||||
.get_wol = nxpwifi_ethtool_get_wol,
|
||||
.set_wol = nxpwifi_ethtool_set_wol,
|
||||
};
|
||||
2475
drivers/net/wireless/nxp/nxpwifi/fw.h
Normal file
2475
drivers/net/wireless/nxp/nxpwifi/fw.h
Normal file
File diff suppressed because it is too large
Load Diff
480
drivers/net/wireless/nxp/nxpwifi/ie.c
Normal file
480
drivers/net/wireless/nxp/nxpwifi/ie.c
Normal file
@@ -0,0 +1,480 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi: management element handling - set/delete elements.
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
|
||||
/* Return true if the IE index is used by another interface. */
|
||||
static bool
|
||||
nxpwifi_ie_index_used_by_other_intf(struct nxpwifi_private *priv, u16 idx)
|
||||
{
|
||||
int i;
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct nxpwifi_ie *ie;
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
if (adapter->priv[i] != priv) {
|
||||
ie = &adapter->priv[i]->mgmt_ie[idx];
|
||||
if (ie->mgmt_subtype_mask && ie->ie_length)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Pick an unused IE index for a new element. */
|
||||
static int
|
||||
nxpwifi_ie_get_autoidx(struct nxpwifi_private *priv, u16 subtype_mask,
|
||||
struct nxpwifi_ie *ie, u16 *index)
|
||||
{
|
||||
u16 mask, len, i;
|
||||
|
||||
for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
|
||||
mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
|
||||
len = le16_to_cpu(ie->ie_length);
|
||||
|
||||
if (mask == NXPWIFI_AUTO_IDX_MASK)
|
||||
continue;
|
||||
|
||||
if (mask == subtype_mask) {
|
||||
if (len > IEEE_MAX_IE_SIZE)
|
||||
continue;
|
||||
|
||||
*index = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!priv->mgmt_ie[i].ie_length) {
|
||||
if (nxpwifi_ie_index_used_by_other_intf(priv, i))
|
||||
continue;
|
||||
|
||||
*index = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Build IE list and resolve AUTO index before sending to FW. */
|
||||
static int
|
||||
nxpwifi_update_autoindex_ies(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ie_list *ie_list)
|
||||
{
|
||||
u16 travel_len, index, mask;
|
||||
s16 input_len, tlv_len;
|
||||
struct nxpwifi_ie *ie;
|
||||
u8 *tmp;
|
||||
|
||||
input_len = le16_to_cpu(ie_list->len);
|
||||
travel_len = sizeof(struct nxpwifi_ie_types_header);
|
||||
|
||||
ie_list->len = 0;
|
||||
|
||||
while (input_len >= sizeof(struct nxpwifi_ie_types_header)) {
|
||||
ie = (struct nxpwifi_ie *)(((u8 *)ie_list) + travel_len);
|
||||
tlv_len = le16_to_cpu(ie->ie_length);
|
||||
travel_len += tlv_len + NXPWIFI_IE_HDR_SIZE;
|
||||
|
||||
if (input_len < tlv_len + NXPWIFI_IE_HDR_SIZE)
|
||||
return -EINVAL;
|
||||
index = le16_to_cpu(ie->ie_index);
|
||||
mask = le16_to_cpu(ie->mgmt_subtype_mask);
|
||||
|
||||
if (index == NXPWIFI_AUTO_IDX_MASK) {
|
||||
/* automatic addition */
|
||||
if (nxpwifi_ie_get_autoidx(priv, mask, ie, &index))
|
||||
return -ENOENT;
|
||||
if (index == NXPWIFI_AUTO_IDX_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
|
||||
memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
|
||||
priv->mgmt_ie[index].ie_length = ie->ie_length;
|
||||
priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
|
||||
priv->mgmt_ie[index].mgmt_subtype_mask =
|
||||
cpu_to_le16(mask);
|
||||
|
||||
ie->ie_index = cpu_to_le16(index);
|
||||
} else {
|
||||
if (mask != NXPWIFI_DELETE_MASK)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Check if this index is being used on any
|
||||
* other interface.
|
||||
*/
|
||||
if (nxpwifi_ie_index_used_by_other_intf(priv, index))
|
||||
return -EPERM;
|
||||
|
||||
ie->ie_length = 0;
|
||||
memcpy(&priv->mgmt_ie[index], ie,
|
||||
sizeof(struct nxpwifi_ie));
|
||||
}
|
||||
|
||||
le16_unaligned_add_cpu
|
||||
(&ie_list->len,
|
||||
le16_to_cpu(priv->mgmt_ie[index].ie_length) +
|
||||
NXPWIFI_IE_HDR_SIZE);
|
||||
input_len -= tlv_len + NXPWIFI_IE_HDR_SIZE;
|
||||
}
|
||||
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP)
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_UAP_SYS_CONFIG,
|
||||
HOST_ACT_GEN_SET,
|
||||
UAP_CUSTOM_IE_I, ie_list, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pack beacon/probe/assoc IEs into one list and update auto-assigned indices. */
|
||||
static int
|
||||
nxpwifi_update_uap_custom_ie(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ie *beacon_ie, u16 *beacon_idx,
|
||||
struct nxpwifi_ie *pr_ie, u16 *probe_idx,
|
||||
struct nxpwifi_ie *ar_ie, u16 *assoc_idx)
|
||||
{
|
||||
struct nxpwifi_ie_list *ap_custom_ie;
|
||||
u8 *pos;
|
||||
u16 len;
|
||||
int ret;
|
||||
|
||||
ap_custom_ie = kzalloc_obj(*ap_custom_ie, GFP_KERNEL);
|
||||
if (!ap_custom_ie)
|
||||
return -ENOMEM;
|
||||
|
||||
ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
|
||||
pos = (u8 *)ap_custom_ie->ie_list;
|
||||
|
||||
if (beacon_ie) {
|
||||
len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
|
||||
le16_to_cpu(beacon_ie->ie_length);
|
||||
memcpy(pos, beacon_ie, len);
|
||||
pos += len;
|
||||
le16_unaligned_add_cpu(&ap_custom_ie->len, len);
|
||||
}
|
||||
if (pr_ie) {
|
||||
len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
|
||||
le16_to_cpu(pr_ie->ie_length);
|
||||
memcpy(pos, pr_ie, len);
|
||||
pos += len;
|
||||
le16_unaligned_add_cpu(&ap_custom_ie->len, len);
|
||||
}
|
||||
if (ar_ie) {
|
||||
len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
|
||||
le16_to_cpu(ar_ie->ie_length);
|
||||
memcpy(pos, ar_ie, len);
|
||||
pos += len;
|
||||
le16_unaligned_add_cpu(&ap_custom_ie->len, len);
|
||||
}
|
||||
|
||||
ret = nxpwifi_update_autoindex_ies(priv, ap_custom_ie);
|
||||
|
||||
pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
|
||||
if (beacon_ie && *beacon_idx == NXPWIFI_AUTO_IDX_MASK) {
|
||||
/* save beacon element index after auto-indexing */
|
||||
*beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
|
||||
len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
|
||||
le16_to_cpu(beacon_ie->ie_length);
|
||||
pos += len;
|
||||
}
|
||||
if (pr_ie && le16_to_cpu(pr_ie->ie_index) == NXPWIFI_AUTO_IDX_MASK) {
|
||||
/* save probe resp element index after auto-indexing */
|
||||
*probe_idx = *((u16 *)pos);
|
||||
len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
|
||||
le16_to_cpu(pr_ie->ie_length);
|
||||
pos += len;
|
||||
}
|
||||
if (ar_ie && le16_to_cpu(ar_ie->ie_index) == NXPWIFI_AUTO_IDX_MASK)
|
||||
/* save assoc resp element index after auto-indexing */
|
||||
*assoc_idx = *((u16 *)pos);
|
||||
|
||||
kfree(ap_custom_ie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Append vendor IE (if present) into nxpwifi_ie, allocating as needed. */
|
||||
static int nxpwifi_update_vs_ie(const u8 *ies, int ies_len,
|
||||
struct nxpwifi_ie **ie_ptr, u16 mask,
|
||||
unsigned int oui, u8 oui_type)
|
||||
{
|
||||
struct element *vs_ie;
|
||||
struct nxpwifi_ie *ie = *ie_ptr;
|
||||
const u8 *vendor_ie;
|
||||
|
||||
vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
|
||||
if (vendor_ie) {
|
||||
if (!*ie_ptr) {
|
||||
*ie_ptr = kzalloc_obj(struct nxpwifi_ie, GFP_KERNEL);
|
||||
if (!*ie_ptr)
|
||||
return -ENOMEM;
|
||||
ie = *ie_ptr;
|
||||
}
|
||||
|
||||
vs_ie = (struct element *)vendor_ie;
|
||||
if (le16_to_cpu(ie->ie_length) + vs_ie->datalen + 2 >
|
||||
IEEE_MAX_IE_SIZE)
|
||||
return -EINVAL;
|
||||
memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
|
||||
vs_ie, vs_ie->datalen + 2);
|
||||
le16_unaligned_add_cpu(&ie->ie_length, vs_ie->datalen + 2);
|
||||
ie->mgmt_subtype_mask = cpu_to_le16(mask);
|
||||
ie->ie_index = cpu_to_le16(NXPWIFI_AUTO_IDX_MASK);
|
||||
}
|
||||
|
||||
*ie_ptr = ie;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse beacon/probe/assoc IEs from cfg80211 and push them to FW. */
|
||||
static int nxpwifi_set_mgmt_beacon_data_ies(struct nxpwifi_private *priv,
|
||||
struct cfg80211_beacon_data *data)
|
||||
{
|
||||
struct nxpwifi_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
|
||||
u16 beacon_idx = NXPWIFI_AUTO_IDX_MASK, pr_idx = NXPWIFI_AUTO_IDX_MASK;
|
||||
u16 ar_idx = NXPWIFI_AUTO_IDX_MASK;
|
||||
int ret = 0;
|
||||
|
||||
if (data->beacon_ies && data->beacon_ies_len) {
|
||||
nxpwifi_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
|
||||
&beacon_ie, MGMT_MASK_BEACON,
|
||||
WLAN_OUI_MICROSOFT,
|
||||
WLAN_OUI_TYPE_MICROSOFT_WPS);
|
||||
nxpwifi_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
|
||||
&beacon_ie, MGMT_MASK_BEACON,
|
||||
WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
|
||||
}
|
||||
|
||||
if (data->proberesp_ies && data->proberesp_ies_len) {
|
||||
nxpwifi_update_vs_ie(data->proberesp_ies,
|
||||
data->proberesp_ies_len, &pr_ie,
|
||||
MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
|
||||
WLAN_OUI_TYPE_MICROSOFT_WPS);
|
||||
nxpwifi_update_vs_ie(data->proberesp_ies,
|
||||
data->proberesp_ies_len, &pr_ie,
|
||||
MGMT_MASK_PROBE_RESP,
|
||||
WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
|
||||
}
|
||||
|
||||
if (data->assocresp_ies && data->assocresp_ies_len) {
|
||||
nxpwifi_update_vs_ie(data->assocresp_ies,
|
||||
data->assocresp_ies_len, &ar_ie,
|
||||
MGMT_MASK_ASSOC_RESP |
|
||||
MGMT_MASK_REASSOC_RESP,
|
||||
WLAN_OUI_MICROSOFT,
|
||||
WLAN_OUI_TYPE_MICROSOFT_WPS);
|
||||
nxpwifi_update_vs_ie(data->assocresp_ies,
|
||||
data->assocresp_ies_len, &ar_ie,
|
||||
MGMT_MASK_ASSOC_RESP |
|
||||
MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
|
||||
WLAN_OUI_TYPE_WFA_P2P);
|
||||
}
|
||||
|
||||
if (beacon_ie || pr_ie || ar_ie) {
|
||||
ret = nxpwifi_update_uap_custom_ie(priv, beacon_ie,
|
||||
&beacon_idx, pr_ie,
|
||||
&pr_idx, ar_ie, &ar_idx);
|
||||
if (ret)
|
||||
goto done;
|
||||
}
|
||||
|
||||
priv->beacon_idx = beacon_idx;
|
||||
priv->proberesp_idx = pr_idx;
|
||||
priv->assocresp_idx = ar_idx;
|
||||
|
||||
done:
|
||||
kfree(beacon_ie);
|
||||
kfree(pr_ie);
|
||||
kfree(ar_ie);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse head/tail IEs from cfg80211_beacon_data and send them to FW. */
|
||||
static int nxpwifi_uap_parse_tail_ies(struct nxpwifi_private *priv,
|
||||
struct cfg80211_beacon_data *info)
|
||||
{
|
||||
struct nxpwifi_ie *gen_ie;
|
||||
struct element *hdr;
|
||||
struct ieee80211_vendor_ie *vendorhdr;
|
||||
u16 gen_idx = NXPWIFI_AUTO_IDX_MASK, ie_len = 0;
|
||||
int left_len, parsed_len = 0;
|
||||
unsigned int token_len;
|
||||
int ret = 0;
|
||||
|
||||
if (!info->tail || !info->tail_len)
|
||||
return 0;
|
||||
|
||||
gen_ie = kzalloc_obj(*gen_ie, GFP_KERNEL);
|
||||
if (!gen_ie)
|
||||
return -ENOMEM;
|
||||
|
||||
left_len = info->tail_len;
|
||||
|
||||
/* Skip IEs generated by FW from bss configuration to avoid duplicates. */
|
||||
while (left_len > sizeof(struct element)) {
|
||||
hdr = (void *)(info->tail + parsed_len);
|
||||
token_len = hdr->datalen + sizeof(struct element);
|
||||
if (token_len > left_len) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch (hdr->id) {
|
||||
case WLAN_EID_SSID:
|
||||
case WLAN_EID_SUPP_RATES:
|
||||
case WLAN_EID_COUNTRY:
|
||||
case WLAN_EID_PWR_CONSTRAINT:
|
||||
case WLAN_EID_ERP_INFO:
|
||||
case WLAN_EID_EXT_SUPP_RATES:
|
||||
case WLAN_EID_HT_CAPABILITY:
|
||||
case WLAN_EID_HT_OPERATION:
|
||||
case WLAN_EID_VHT_CAPABILITY:
|
||||
break;
|
||||
case WLAN_EID_VENDOR_SPECIFIC:
|
||||
/* Skip only Microsoft WMM element */
|
||||
if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
||||
WLAN_OUI_TYPE_MICROSOFT_WMM,
|
||||
(const u8 *)hdr,
|
||||
token_len))
|
||||
break;
|
||||
fallthrough;
|
||||
default:
|
||||
if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len);
|
||||
ie_len += token_len;
|
||||
break;
|
||||
}
|
||||
left_len -= token_len;
|
||||
parsed_len += token_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse only WPA vendor element from tail, WMM element is configured by
|
||||
* bss_config command
|
||||
*/
|
||||
vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
||||
WLAN_OUI_TYPE_MICROSOFT_WPA,
|
||||
info->tail, info->tail_len);
|
||||
if (vendorhdr) {
|
||||
token_len = vendorhdr->len + sizeof(struct element);
|
||||
if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len);
|
||||
ie_len += token_len;
|
||||
}
|
||||
|
||||
if (!ie_len)
|
||||
goto done;
|
||||
|
||||
gen_ie->ie_index = cpu_to_le16(gen_idx);
|
||||
gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
|
||||
MGMT_MASK_PROBE_RESP |
|
||||
MGMT_MASK_ASSOC_RESP);
|
||||
gen_ie->ie_length = cpu_to_le16(ie_len);
|
||||
|
||||
ret = nxpwifi_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
priv->gen_idx = gen_idx;
|
||||
|
||||
done:
|
||||
kfree(gen_ie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse head/tail/beacon/probe/assoc IEs and program the FW. */
|
||||
int nxpwifi_set_mgmt_ies(struct nxpwifi_private *priv,
|
||||
struct cfg80211_beacon_data *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nxpwifi_uap_parse_tail_ies(priv, info);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return nxpwifi_set_mgmt_beacon_data_ies(priv, info);
|
||||
}
|
||||
|
||||
/* Remove previously set management IEs. */
|
||||
int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_ie *beacon_ie = NULL, *pr_ie = NULL;
|
||||
struct nxpwifi_ie *ar_ie = NULL, *gen_ie = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (priv->gen_idx != NXPWIFI_AUTO_IDX_MASK) {
|
||||
gen_ie = kmalloc_obj(*gen_ie, GFP_KERNEL);
|
||||
if (!gen_ie)
|
||||
return -ENOMEM;
|
||||
|
||||
gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
|
||||
gen_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
|
||||
gen_ie->ie_length = 0;
|
||||
ret = nxpwifi_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
|
||||
NULL, &priv->proberesp_idx,
|
||||
NULL, &priv->assocresp_idx);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
priv->gen_idx = NXPWIFI_AUTO_IDX_MASK;
|
||||
}
|
||||
|
||||
if (priv->beacon_idx != NXPWIFI_AUTO_IDX_MASK) {
|
||||
beacon_ie = kmalloc_obj(*beacon_ie, GFP_KERNEL);
|
||||
if (!beacon_ie) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
|
||||
beacon_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
|
||||
beacon_ie->ie_length = 0;
|
||||
}
|
||||
if (priv->proberesp_idx != NXPWIFI_AUTO_IDX_MASK) {
|
||||
pr_ie = kmalloc_obj(*pr_ie, GFP_KERNEL);
|
||||
if (!pr_ie) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
|
||||
pr_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
|
||||
pr_ie->ie_length = 0;
|
||||
}
|
||||
if (priv->assocresp_idx != NXPWIFI_AUTO_IDX_MASK) {
|
||||
ar_ie = kmalloc_obj(*ar_ie, GFP_KERNEL);
|
||||
if (!ar_ie) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
|
||||
ar_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
|
||||
ar_ie->ie_length = 0;
|
||||
}
|
||||
|
||||
if (beacon_ie || pr_ie || ar_ie)
|
||||
ret = nxpwifi_update_uap_custom_ie(priv,
|
||||
beacon_ie, &priv->beacon_idx,
|
||||
pr_ie, &priv->proberesp_idx,
|
||||
ar_ie, &priv->assocresp_idx);
|
||||
|
||||
done:
|
||||
kfree(gen_ie);
|
||||
kfree(beacon_ie);
|
||||
kfree(pr_ie);
|
||||
kfree(ar_ie);
|
||||
|
||||
return ret;
|
||||
}
|
||||
607
drivers/net/wireless/nxp/nxpwifi/init.c
Normal file
607
drivers/net/wireless/nxp/nxpwifi/init.c
Normal file
@@ -0,0 +1,607 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi: HW/FW initialization
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
#include "wmm.h"
|
||||
#include "11n.h"
|
||||
|
||||
/* Add a BSS priority node to the adapter list. */
|
||||
static int nxpwifi_add_bss_prio_tbl(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct nxpwifi_bss_prio_node *bss_prio;
|
||||
struct nxpwifi_bss_prio_tbl *tbl = adapter->bss_prio_tbl;
|
||||
|
||||
bss_prio = kzalloc_obj(*bss_prio, GFP_KERNEL);
|
||||
if (!bss_prio)
|
||||
return -ENOMEM;
|
||||
|
||||
bss_prio->priv = priv;
|
||||
INIT_LIST_HEAD(&bss_prio->list);
|
||||
|
||||
spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock);
|
||||
list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head);
|
||||
spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wakeup_timer_fn(struct timer_list *t)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = timer_container_of(adapter, t, wakeup_timer);
|
||||
|
||||
nxpwifi_dbg(adapter, ERROR, "Firmware wakeup failed\n");
|
||||
adapter->hw_status = NXPWIFI_HW_STATUS_RESET;
|
||||
nxpwifi_cancel_all_pending_cmd(adapter);
|
||||
|
||||
if (adapter->if_ops.card_reset)
|
||||
adapter->if_ops.card_reset(adapter);
|
||||
}
|
||||
|
||||
/* Initialize priv defaults and lists. */
|
||||
int nxpwifi_init_priv(struct nxpwifi_private *priv)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
priv->media_connected = false;
|
||||
eth_broadcast_addr(priv->curr_addr);
|
||||
priv->port_open = false;
|
||||
priv->usb_port = NXPWIFI_USB_EP_DATA;
|
||||
priv->pkt_tx_ctrl = 0;
|
||||
priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
|
||||
priv->data_rate = 0; /* Initially indicate the rate as auto */
|
||||
priv->is_data_rate_auto = true;
|
||||
priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
|
||||
priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
|
||||
|
||||
priv->auth_flag = 0;
|
||||
priv->auth_alg = WLAN_AUTH_NONE;
|
||||
|
||||
priv->sec_info.wep_enabled = 0;
|
||||
priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
|
||||
priv->sec_info.encryption_mode = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++)
|
||||
memset(&priv->wep_key[i], 0, sizeof(struct nxpwifi_wep_key));
|
||||
priv->wep_key_curr_index = 0;
|
||||
priv->curr_pkt_filter = HOST_ACT_MAC_DYNAMIC_BW_ENABLE |
|
||||
HOST_ACT_MAC_RX_ON | HOST_ACT_MAC_TX_ON |
|
||||
HOST_ACT_MAC_ETHERNETII_ENABLE;
|
||||
|
||||
priv->beacon_period = 100; /* beacon interval */
|
||||
priv->attempted_bss_desc = NULL;
|
||||
memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params));
|
||||
priv->listen_interval = NXPWIFI_DEFAULT_LISTEN_INTERVAL;
|
||||
|
||||
memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid));
|
||||
memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid));
|
||||
memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf));
|
||||
priv->assoc_rsp_size = 0;
|
||||
priv->atim_window = 0;
|
||||
priv->tx_power_level = 0;
|
||||
priv->max_tx_power_level = 0;
|
||||
priv->min_tx_power_level = 0;
|
||||
priv->tx_ant = 0;
|
||||
priv->rx_ant = 0;
|
||||
priv->tx_rate = 0;
|
||||
priv->rxpd_htinfo = 0;
|
||||
priv->rxpd_rate = 0;
|
||||
priv->rate_bitmap = 0;
|
||||
priv->data_rssi_last = 0;
|
||||
priv->data_rssi_avg = 0;
|
||||
priv->data_nf_avg = 0;
|
||||
priv->data_nf_last = 0;
|
||||
priv->bcn_rssi_last = 0;
|
||||
priv->bcn_rssi_avg = 0;
|
||||
priv->bcn_nf_avg = 0;
|
||||
priv->bcn_nf_last = 0;
|
||||
memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie));
|
||||
memset(&priv->aes_key, 0, sizeof(priv->aes_key));
|
||||
priv->wpa_ie_len = 0;
|
||||
priv->wpa_is_gtk_set = false;
|
||||
|
||||
memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf));
|
||||
priv->assoc_tlv_buf_len = 0;
|
||||
memset(&priv->wps, 0, sizeof(priv->wps));
|
||||
memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf));
|
||||
priv->gen_ie_buf_len = 0;
|
||||
memset(priv->vs_ie, 0, sizeof(priv->vs_ie));
|
||||
|
||||
priv->wmm_required = true;
|
||||
priv->wmm_enabled = false;
|
||||
priv->wmm_qosinfo = 0;
|
||||
priv->curr_bcn_buf = NULL;
|
||||
priv->curr_bcn_size = 0;
|
||||
priv->wps_ie = NULL;
|
||||
priv->wps_ie_len = 0;
|
||||
priv->ap_11n_enabled = 0;
|
||||
memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg));
|
||||
|
||||
priv->scan_block = false;
|
||||
|
||||
priv->csa_chan = 0;
|
||||
priv->csa_expire_time = 0;
|
||||
priv->del_list_idx = 0;
|
||||
priv->hs2_enabled = false;
|
||||
nxpwifi_wmm_init_tos_to_tid_inv(priv);
|
||||
|
||||
nxpwifi_init_11h_params(priv);
|
||||
|
||||
return nxpwifi_add_bss_prio_tbl(priv);
|
||||
}
|
||||
|
||||
/* Allocate command buffer and sleep-confirm skb. */
|
||||
static int nxpwifi_allocate_adapter(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Allocate command buffer */
|
||||
ret = nxpwifi_alloc_cmd_buffer(adapter);
|
||||
if (ret) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"%s: failed to alloc cmd buffer\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
adapter->sleep_cfm =
|
||||
dev_alloc_skb(sizeof(struct nxpwifi_opt_sleep_confirm)
|
||||
+ INTF_HEADER_LEN);
|
||||
|
||||
if (!adapter->sleep_cfm) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"%s: failed to alloc sleep cfm\t"
|
||||
" cmd buffer\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize adapter defaults and WMM parameters. */
|
||||
static void nxpwifi_init_adapter(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
struct nxpwifi_opt_sleep_confirm *sleep_cfm_buf = NULL;
|
||||
|
||||
skb_put(adapter->sleep_cfm, sizeof(struct nxpwifi_opt_sleep_confirm));
|
||||
|
||||
adapter->cmd_sent = false;
|
||||
adapter->data_sent = true;
|
||||
|
||||
adapter->intf_hdr_len = INTF_HEADER_LEN;
|
||||
|
||||
adapter->cmd_resp_received = false;
|
||||
adapter->event_received = false;
|
||||
adapter->data_received = false;
|
||||
adapter->assoc_resp_received = false;
|
||||
adapter->priv_link_lost = NULL;
|
||||
adapter->host_mlme_link_lost = false;
|
||||
|
||||
clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags);
|
||||
|
||||
adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING;
|
||||
|
||||
adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM;
|
||||
adapter->ps_state = PS_STATE_AWAKE;
|
||||
adapter->need_to_wakeup = false;
|
||||
|
||||
adapter->scan_mode = HOST_BSS_MODE_ANY;
|
||||
adapter->specific_scan_time = NXPWIFI_SPECIFIC_SCAN_CHAN_TIME;
|
||||
adapter->active_scan_time = NXPWIFI_ACTIVE_SCAN_CHAN_TIME;
|
||||
adapter->passive_scan_time = NXPWIFI_PASSIVE_SCAN_CHAN_TIME;
|
||||
adapter->scan_chan_gap_time = NXPWIFI_DEF_SCAN_CHAN_GAP_TIME;
|
||||
|
||||
adapter->scan_probes = 1;
|
||||
|
||||
adapter->multiple_dtim = 1;
|
||||
|
||||
/* default value in firmware will be used */
|
||||
adapter->local_listen_interval = 0;
|
||||
|
||||
adapter->is_deep_sleep = false;
|
||||
|
||||
adapter->delay_null_pkt = false;
|
||||
adapter->delay_to_ps = 1000;
|
||||
adapter->enhanced_ps_mode = PS_MODE_AUTO;
|
||||
|
||||
/* Disable NULL Pkg generation by default */
|
||||
adapter->gen_null_pkt = false;
|
||||
/* Disable pps/uapsd mode by default */
|
||||
adapter->pps_uapsd_mode = false;
|
||||
adapter->pm_wakeup_card_req = false;
|
||||
|
||||
adapter->pm_wakeup_fw_try = false;
|
||||
|
||||
adapter->curr_tx_buf_size = NXPWIFI_TX_DATA_BUF_SIZE_2K;
|
||||
|
||||
clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags);
|
||||
adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF);
|
||||
adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF;
|
||||
adapter->hs_cfg.gap = HS_CFG_GAP_DEF;
|
||||
adapter->hs_activated = false;
|
||||
|
||||
memset(adapter->event_body, 0, sizeof(adapter->event_body));
|
||||
adapter->hw_dot_11n_dev_cap = 0;
|
||||
adapter->hw_dev_mcs_support = 0;
|
||||
adapter->sec_chan_offset = 0;
|
||||
|
||||
nxpwifi_wmm_init(adapter);
|
||||
atomic_set(&adapter->tx_hw_pending, 0);
|
||||
|
||||
sleep_cfm_buf = (struct nxpwifi_opt_sleep_confirm *)
|
||||
adapter->sleep_cfm->data;
|
||||
memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
|
||||
sleep_cfm_buf->command = cpu_to_le16(HOST_CMD_802_11_PS_MODE_ENH);
|
||||
sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len);
|
||||
sleep_cfm_buf->result = 0;
|
||||
sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
|
||||
sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
|
||||
|
||||
memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period));
|
||||
adapter->tx_lock_flag = false;
|
||||
adapter->null_pkt_interval = 0;
|
||||
adapter->fw_bands = 0;
|
||||
adapter->fw_release_number = 0;
|
||||
adapter->fw_cap_info = 0;
|
||||
memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf));
|
||||
adapter->event_cause = 0;
|
||||
adapter->region_code = 0;
|
||||
adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT;
|
||||
memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter));
|
||||
adapter->arp_filter_size = 0;
|
||||
adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
|
||||
adapter->key_api_major_ver = 0;
|
||||
adapter->key_api_minor_ver = 0;
|
||||
eth_broadcast_addr(adapter->perm_addr);
|
||||
adapter->iface_limit.sta_intf = NXPWIFI_MAX_STA_NUM;
|
||||
adapter->iface_limit.uap_intf = NXPWIFI_MAX_UAP_NUM;
|
||||
adapter->active_scan_triggered = false;
|
||||
timer_setup(&adapter->wakeup_timer, wakeup_timer_fn, 0);
|
||||
adapter->devdump_len = 0;
|
||||
memset(&adapter->vdll_ctrl, 0, sizeof(adapter->vdll_ctrl));
|
||||
adapter->vdll_ctrl.skb = dev_alloc_skb(NXPWIFI_SIZE_OF_CMD_BUFFER);
|
||||
atomic_set(&adapter->iface_changing, 0);
|
||||
}
|
||||
|
||||
/* Update trans_start for each Tx queue. */
|
||||
void nxpwifi_set_trans_start(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->num_tx_queues; i++)
|
||||
txq_trans_cond_update(netdev_get_tx_queue(dev, i));
|
||||
|
||||
netif_trans_update(dev);
|
||||
}
|
||||
|
||||
/* Wake all netdev Tx queues. */
|
||||
void nxpwifi_wake_up_net_dev_queue(struct net_device *netdev,
|
||||
struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
spin_lock_bh(&adapter->queue_lock);
|
||||
netif_tx_wake_all_queues(netdev);
|
||||
spin_unlock_bh(&adapter->queue_lock);
|
||||
}
|
||||
|
||||
/* Stop all netdev Tx queues. */
|
||||
void nxpwifi_stop_net_dev_queue(struct net_device *netdev,
|
||||
struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
spin_lock_bh(&adapter->queue_lock);
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
spin_unlock_bh(&adapter->queue_lock);
|
||||
}
|
||||
|
||||
/* Invalidate list heads. */
|
||||
static void nxpwifi_invalidate_lists(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
struct nxpwifi_private *priv;
|
||||
s32 i, j;
|
||||
|
||||
list_del(&adapter->cmd_free_q);
|
||||
list_del(&adapter->cmd_pending_q);
|
||||
list_del(&adapter->scan_pending_q);
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++)
|
||||
list_del(&adapter->bss_prio_tbl[i].bss_prio_head);
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
for (j = 0; j < MAX_NUM_TID; ++j) {
|
||||
list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
|
||||
list_del(&priv->tx_ba_stream_tbl_ptr[j]);
|
||||
list_del(&priv->rx_reorder_tbl_ptr[j]);
|
||||
}
|
||||
list_del(&priv->sta_list);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cancel pending work, stop timers, and free adapter buffers. */
|
||||
static void
|
||||
nxpwifi_adapter_cleanup(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
timer_delete(&adapter->wakeup_timer);
|
||||
nxpwifi_cancel_all_pending_cmd(adapter);
|
||||
wake_up_interruptible(&adapter->cmd_wait_q.wait);
|
||||
wake_up_interruptible(&adapter->hs_activate_wait_q);
|
||||
if (adapter->vdll_ctrl.vdll_mem) {
|
||||
vfree(adapter->vdll_ctrl.vdll_mem);
|
||||
adapter->vdll_ctrl.vdll_mem = NULL;
|
||||
adapter->vdll_ctrl.vdll_len = 0;
|
||||
}
|
||||
if (adapter->vdll_ctrl.skb) {
|
||||
dev_kfree_skb_any(adapter->vdll_ctrl.skb);
|
||||
adapter->vdll_ctrl.skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void nxpwifi_free_cmd_buffers(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
nxpwifi_invalidate_lists(adapter);
|
||||
|
||||
/* Free command buffer */
|
||||
nxpwifi_dbg(adapter, INFO, "info: free cmd buffer\n");
|
||||
nxpwifi_free_cmd_buffer(adapter);
|
||||
|
||||
if (adapter->sleep_cfm)
|
||||
dev_kfree_skb_any(adapter->sleep_cfm);
|
||||
}
|
||||
|
||||
/* Initialize locks and list heads. */
|
||||
void nxpwifi_init_lock_list(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
struct nxpwifi_private *priv;
|
||||
s32 i, j;
|
||||
|
||||
spin_lock_init(&adapter->int_lock);
|
||||
spin_lock_init(&adapter->nxpwifi_cmd_lock);
|
||||
spin_lock_init(&adapter->queue_lock);
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
spin_lock_init(&priv->wmm.ra_list_spinlock);
|
||||
spin_lock_init(&priv->curr_bcn_buf_lock);
|
||||
spin_lock_init(&priv->sta_list_spinlock);
|
||||
}
|
||||
|
||||
/* Initialize cmd_free_q */
|
||||
INIT_LIST_HEAD(&adapter->cmd_free_q);
|
||||
/* Initialize cmd_pending_q */
|
||||
INIT_LIST_HEAD(&adapter->cmd_pending_q);
|
||||
/* Initialize scan_pending_q */
|
||||
INIT_LIST_HEAD(&adapter->scan_pending_q);
|
||||
|
||||
spin_lock_init(&adapter->cmd_free_q_lock);
|
||||
spin_lock_init(&adapter->cmd_pending_q_lock);
|
||||
spin_lock_init(&adapter->scan_pending_q_lock);
|
||||
|
||||
skb_queue_head_init(&adapter->rx_mlme_q);
|
||||
skb_queue_head_init(&adapter->rx_data_q);
|
||||
skb_queue_head_init(&adapter->tx_data_q);
|
||||
|
||||
for (i = 0; i < adapter->priv_num; ++i) {
|
||||
INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
|
||||
spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock);
|
||||
}
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
for (j = 0; j < MAX_NUM_TID; ++j) {
|
||||
INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
|
||||
INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr[j]);
|
||||
INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr[j]);
|
||||
spin_lock_init(&priv->tx_ba_stream_tbl_lock[j]);
|
||||
spin_lock_init(&priv->rx_reorder_tbl_lock[j]);
|
||||
}
|
||||
INIT_LIST_HEAD(&priv->sta_list);
|
||||
skb_queue_head_init(&priv->bypass_txq);
|
||||
|
||||
spin_lock_init(&priv->ack_status_lock);
|
||||
xa_init_flags(&priv->ack_status_frames, XA_FLAGS_ALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
/* Init firmware: alloc resources, init adapter/privs, send STA init. */
|
||||
int nxpwifi_init_fw(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
int ret;
|
||||
struct nxpwifi_private *priv;
|
||||
u8 i;
|
||||
bool first_sta = true;
|
||||
|
||||
adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING;
|
||||
|
||||
/* Allocate memory for member of adapter structure */
|
||||
ret = nxpwifi_allocate_adapter(adapter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initialize adapter structure */
|
||||
nxpwifi_init_adapter(adapter);
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
|
||||
/* Initialize private structure */
|
||||
ret = nxpwifi_init_priv(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
ret = nxpwifi_sta_init_cmd(adapter->priv[i],
|
||||
first_sta, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
first_sta = false;
|
||||
}
|
||||
spin_lock_bh(&adapter->cmd_pending_q_lock);
|
||||
WARN_ON(!list_empty(&adapter->cmd_pending_q));
|
||||
spin_unlock_bh(&adapter->cmd_pending_q_lock);
|
||||
adapter->hw_status = NXPWIFI_HW_STATUS_READY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove all BSS priority nodes for this priv. */
|
||||
static void nxpwifi_delete_bss_prio_tbl(struct nxpwifi_private *priv)
|
||||
{
|
||||
int i;
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct nxpwifi_bss_prio_node *bssprio_node, *tmp_node;
|
||||
struct list_head *head;
|
||||
spinlock_t *lock; /* bss priority lock */
|
||||
|
||||
for (i = 0; i < adapter->priv_num; ++i) {
|
||||
head = &adapter->bss_prio_tbl[i].bss_prio_head;
|
||||
lock = &adapter->bss_prio_tbl[i].bss_prio_lock;
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: delete BSS priority table,\t"
|
||||
"bss_type = %d, bss_num = %d, i = %d,\t"
|
||||
"head = %p\n",
|
||||
priv->bss_type, priv->bss_num, i, head);
|
||||
|
||||
{
|
||||
spin_lock_bh(lock);
|
||||
list_for_each_entry_safe(bssprio_node, tmp_node, head,
|
||||
list) {
|
||||
if (bssprio_node->priv == priv) {
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: Delete\t"
|
||||
"node %p, next = %p\n",
|
||||
bssprio_node, tmp_node);
|
||||
list_del(&bssprio_node->list);
|
||||
kfree(bssprio_node);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free per-priv resources and BSS priority entries. */
|
||||
void nxpwifi_free_priv(struct nxpwifi_private *priv)
|
||||
{
|
||||
nxpwifi_clean_txrx(priv);
|
||||
nxpwifi_delete_bss_prio_tbl(priv);
|
||||
nxpwifi_free_curr_bcn(priv);
|
||||
}
|
||||
|
||||
/* Shutdown driver: stop work, drain queues, free resources. */
|
||||
void
|
||||
nxpwifi_shutdown_drv(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
struct nxpwifi_private *priv;
|
||||
s32 i;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* nxpwifi already shutdown */
|
||||
if (adapter->hw_status == NXPWIFI_HW_STATUS_NOT_READY)
|
||||
return;
|
||||
|
||||
/* cancel current command */
|
||||
if (adapter->curr_cmd) {
|
||||
nxpwifi_dbg(adapter, WARN,
|
||||
"curr_cmd is still in processing\n");
|
||||
timer_delete_sync(&adapter->cmd_timer);
|
||||
nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd);
|
||||
adapter->curr_cmd = NULL;
|
||||
}
|
||||
|
||||
/* shut down nxpwifi */
|
||||
nxpwifi_dbg(adapter, MSG,
|
||||
"info: shutdown nxpwifi...\n");
|
||||
|
||||
/* Clean up Tx/Rx queues and delete BSS priority table */
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
|
||||
nxpwifi_abort_cac(priv);
|
||||
nxpwifi_free_priv(priv);
|
||||
}
|
||||
|
||||
atomic_set(&adapter->tx_queued, 0);
|
||||
while ((skb = skb_dequeue(&adapter->tx_data_q)))
|
||||
nxpwifi_write_data_complete(adapter, skb, 0, 0);
|
||||
|
||||
while ((skb = skb_dequeue(&adapter->rx_mlme_q)))
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
while ((skb = skb_dequeue(&adapter->rx_data_q))) {
|
||||
struct nxpwifi_rxinfo *rx_info = NXPWIFI_SKB_RXCB(skb);
|
||||
|
||||
atomic_dec(&adapter->rx_pending);
|
||||
priv = adapter->priv[rx_info->bss_num];
|
||||
if (priv)
|
||||
priv->stats.rx_dropped++;
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
nxpwifi_adapter_cleanup(adapter);
|
||||
|
||||
adapter->hw_status = NXPWIFI_HW_STATUS_NOT_READY;
|
||||
}
|
||||
|
||||
/* Download FW if needed; check winner and wait until ready. */
|
||||
int nxpwifi_dnld_fw(struct nxpwifi_adapter *adapter,
|
||||
struct nxpwifi_fw_image *pmfw)
|
||||
{
|
||||
int ret;
|
||||
u32 poll_num = 1;
|
||||
|
||||
/* check if firmware is already running */
|
||||
ret = adapter->if_ops.check_fw_status(adapter, poll_num);
|
||||
if (!ret) {
|
||||
nxpwifi_dbg(adapter, MSG,
|
||||
"WLAN FW already running! Skip FW dnld\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check if we are the winner for downloading FW */
|
||||
if (adapter->if_ops.check_winner_status) {
|
||||
adapter->winner = 0;
|
||||
ret = adapter->if_ops.check_winner_status(adapter);
|
||||
|
||||
poll_num = MAX_FIRMWARE_POLL_TRIES;
|
||||
if (ret) {
|
||||
nxpwifi_dbg(adapter, MSG,
|
||||
"WLAN read winner status failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!adapter->winner) {
|
||||
nxpwifi_dbg(adapter, MSG,
|
||||
"WLAN is not the winner! Skip FW dnld\n");
|
||||
goto poll_fw;
|
||||
}
|
||||
}
|
||||
|
||||
if (pmfw) {
|
||||
/* Download firmware with helper */
|
||||
ret = adapter->if_ops.prog_fw(adapter, pmfw);
|
||||
if (ret) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"prog_fw failed ret=%#x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
poll_fw:
|
||||
/* Check if the firmware is downloaded successfully or not */
|
||||
ret = adapter->if_ops.check_fw_status(adapter, poll_num);
|
||||
if (ret)
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"FW failed to be active in time\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nxpwifi_dnld_fw);
|
||||
787
drivers/net/wireless/nxp/nxpwifi/join.c
Normal file
787
drivers/net/wireless/nxp/nxpwifi/join.c
Normal file
@@ -0,0 +1,787 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* nxpwifi: association and ad-hoc start/join
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
#include "wmm.h"
|
||||
#include "11n.h"
|
||||
#include "11ac.h"
|
||||
#include "11ax.h"
|
||||
|
||||
#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9)))
|
||||
|
||||
/* Append generic IE as pass-through TLV for join */
|
||||
static int
|
||||
nxpwifi_cmd_append_generic_ie(struct nxpwifi_private *priv, u8 **buffer)
|
||||
{
|
||||
int ret_len = 0;
|
||||
struct nxpwifi_ie_types_header ie_header;
|
||||
|
||||
/* Null Checks */
|
||||
if (!buffer)
|
||||
return 0;
|
||||
if (!(*buffer))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If there is a generic element buffer setup, append it to the return
|
||||
* parameter buffer pointer.
|
||||
*/
|
||||
if (priv->gen_ie_buf_len) {
|
||||
nxpwifi_dbg(priv->adapter, INFO,
|
||||
"info: %s: append generic element len %d to %p\n",
|
||||
__func__, priv->gen_ie_buf_len, *buffer);
|
||||
|
||||
/* Wrap the generic element buffer with a pass through TLV type */
|
||||
ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
|
||||
ie_header.len = cpu_to_le16(priv->gen_ie_buf_len);
|
||||
memcpy(*buffer, &ie_header, sizeof(ie_header));
|
||||
|
||||
/*
|
||||
* Increment the return size and the return buffer pointer
|
||||
* param
|
||||
*/
|
||||
*buffer += sizeof(ie_header);
|
||||
ret_len += sizeof(ie_header);
|
||||
|
||||
/*
|
||||
* Copy the generic element buffer to the output buffer, advance
|
||||
* pointer
|
||||
*/
|
||||
memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len);
|
||||
|
||||
/*
|
||||
* Increment the return size and the return buffer pointer
|
||||
* param
|
||||
*/
|
||||
*buffer += priv->gen_ie_buf_len;
|
||||
ret_len += priv->gen_ie_buf_len;
|
||||
|
||||
/* Reset the generic element buffer */
|
||||
priv->gen_ie_buf_len = 0;
|
||||
}
|
||||
|
||||
/* return the length appended to the buffer */
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
/* Append TSF timestamp (AP TSF and local RX TSF) for reassoc */
|
||||
static int
|
||||
nxpwifi_cmd_append_tsf_tlv(struct nxpwifi_private *priv, u8 **buffer,
|
||||
struct nxpwifi_bssdescriptor *bss_desc)
|
||||
{
|
||||
struct nxpwifi_ie_types_tsf_timestamp tsf_tlv;
|
||||
__le64 tsf_val;
|
||||
|
||||
/* Null Checks */
|
||||
if (!buffer)
|
||||
return 0;
|
||||
if (!*buffer)
|
||||
return 0;
|
||||
|
||||
memset(&tsf_tlv, 0x00, sizeof(struct nxpwifi_ie_types_tsf_timestamp));
|
||||
|
||||
tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP);
|
||||
tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val));
|
||||
|
||||
memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header));
|
||||
*buffer += sizeof(tsf_tlv.header);
|
||||
|
||||
/* TSF at the time when beacon/probe_response was received */
|
||||
tsf_val = cpu_to_le64(bss_desc->fw_tsf);
|
||||
memcpy(*buffer, &tsf_val, sizeof(tsf_val));
|
||||
*buffer += sizeof(tsf_val);
|
||||
|
||||
tsf_val = cpu_to_le64(bss_desc->timestamp);
|
||||
|
||||
nxpwifi_dbg(priv->adapter, INFO,
|
||||
"info: %s: TSF offset calc: %016llx - %016llx\n",
|
||||
__func__, bss_desc->timestamp, bss_desc->fw_tsf);
|
||||
|
||||
memcpy(*buffer, &tsf_val, sizeof(tsf_val));
|
||||
*buffer += sizeof(tsf_val);
|
||||
|
||||
return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val));
|
||||
}
|
||||
|
||||
/* Compute intersection of two rate sets; rate1 updated in-place */
|
||||
static int nxpwifi_get_common_rates(struct nxpwifi_private *priv, u8 *rate1,
|
||||
u32 rate1_size, u8 *rate2, u32 rate2_size)
|
||||
{
|
||||
int ret;
|
||||
u8 *ptr = rate1, *tmp;
|
||||
u32 i, j;
|
||||
|
||||
tmp = kmemdup(rate1, rate1_size, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(rate1, 0, rate1_size);
|
||||
|
||||
for (i = 0; i < rate2_size && rate2[i]; i++) {
|
||||
for (j = 0; j < rate1_size && tmp[j]; j++) {
|
||||
/*
|
||||
* Check common rate, excluding the bit for
|
||||
* basic rate
|
||||
*/
|
||||
if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) {
|
||||
*rate1++ = tmp[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nxpwifi_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n",
|
||||
priv->data_rate);
|
||||
|
||||
if (!priv->is_data_rate_auto) {
|
||||
while (*ptr) {
|
||||
if ((*ptr & 0x7f) == priv->data_rate) {
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"previously set fixed data rate %#x\t"
|
||||
"is not compatible with the network\n",
|
||||
priv->data_rate);
|
||||
|
||||
ret = -EPERM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
done:
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Build common rates from BSS descriptor into out_rates */
|
||||
static int
|
||||
nxpwifi_setup_rates_from_bssdesc(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc,
|
||||
u8 *out_rates, u32 *out_rates_size)
|
||||
{
|
||||
u8 card_rates[NXPWIFI_SUPPORTED_RATES];
|
||||
u32 card_rates_size;
|
||||
int ret;
|
||||
|
||||
/* Copy AP supported rates */
|
||||
memcpy(out_rates, bss_desc->supported_rates, NXPWIFI_SUPPORTED_RATES);
|
||||
/* Get the STA supported rates */
|
||||
card_rates_size = nxpwifi_get_active_data_rates(priv, card_rates);
|
||||
/* Get the common rates between AP and STA supported rates */
|
||||
ret = nxpwifi_get_common_rates(priv, out_rates, NXPWIFI_SUPPORTED_RATES,
|
||||
card_rates, card_rates_size);
|
||||
if (ret) {
|
||||
*out_rates_size = 0;
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"%s: cannot get common rates\n",
|
||||
__func__);
|
||||
} else {
|
||||
*out_rates_size =
|
||||
min_t(size_t, strlen(out_rates), NXPWIFI_SUPPORTED_RATES);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Append WPS IE as pass-through TLV for join */
|
||||
static int
|
||||
nxpwifi_cmd_append_wps_ie(struct nxpwifi_private *priv, u8 **buffer)
|
||||
{
|
||||
int ret_len = 0;
|
||||
struct nxpwifi_ie_types_header ie_header;
|
||||
|
||||
if (!buffer || !*buffer)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If there is a wps element buffer setup, append it to the return
|
||||
* parameter buffer pointer.
|
||||
*/
|
||||
if (priv->wps_ie_len) {
|
||||
nxpwifi_dbg(priv->adapter, CMD,
|
||||
"cmd: append wps element %d to %p\n",
|
||||
priv->wps_ie_len, *buffer);
|
||||
|
||||
/* Wrap the generic element buffer with a pass through TLV type */
|
||||
ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
|
||||
ie_header.len = cpu_to_le16(priv->wps_ie_len);
|
||||
memcpy(*buffer, &ie_header, sizeof(ie_header));
|
||||
*buffer += sizeof(ie_header);
|
||||
ret_len += sizeof(ie_header);
|
||||
|
||||
memcpy(*buffer, priv->wps_ie, priv->wps_ie_len);
|
||||
*buffer += priv->wps_ie_len;
|
||||
ret_len += priv->wps_ie_len;
|
||||
}
|
||||
|
||||
kfree(priv->wps_ie);
|
||||
priv->wps_ie_len = 0;
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
/* Append WPA/WPA2 RSN IE TLV */
|
||||
static int nxpwifi_append_rsn_ie_wpa_wpa2(struct nxpwifi_private *priv,
|
||||
u8 **buffer)
|
||||
{
|
||||
struct nxpwifi_ie_types_rsn_param_set *rsn_ie_tlv;
|
||||
int rsn_ie_len;
|
||||
|
||||
if (!buffer || !(*buffer))
|
||||
return 0;
|
||||
|
||||
rsn_ie_tlv = (struct nxpwifi_ie_types_rsn_param_set *)(*buffer);
|
||||
rsn_ie_tlv->header.type = cpu_to_le16((u16)priv->wpa_ie[0]);
|
||||
rsn_ie_tlv->header.type =
|
||||
cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF);
|
||||
rsn_ie_tlv->header.len = cpu_to_le16((u16)priv->wpa_ie[1]);
|
||||
rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len)
|
||||
& 0x00FF);
|
||||
if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2))
|
||||
memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2],
|
||||
le16_to_cpu(rsn_ie_tlv->header.len));
|
||||
else
|
||||
return -ENOMEM;
|
||||
|
||||
rsn_ie_len = sizeof(rsn_ie_tlv->header) +
|
||||
le16_to_cpu(rsn_ie_tlv->header.len);
|
||||
*buffer += rsn_ie_len;
|
||||
|
||||
return rsn_ie_len;
|
||||
}
|
||||
|
||||
/* Build 802.11 association command and required TLVs */
|
||||
int nxpwifi_cmd_802_11_associate(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *cmd,
|
||||
struct nxpwifi_bssdescriptor *bss_desc)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate;
|
||||
struct nxpwifi_ie_types_host_mlme *host_mlme_tlv;
|
||||
struct nxpwifi_ie_types_ssid_param_set *ssid_tlv;
|
||||
struct nxpwifi_ie_types_phy_param_set *phy_tlv;
|
||||
struct nxpwifi_ie_types_ss_param_set *ss_tlv;
|
||||
struct nxpwifi_ie_types_rates_param_set *rates_tlv;
|
||||
struct nxpwifi_ie_types_auth_type *auth_tlv;
|
||||
struct nxpwifi_ie_types_sae_pwe_mode *sae_pwe_tlv;
|
||||
struct nxpwifi_ie_types_chan_list_param_set *chan_tlv;
|
||||
u8 rates[NXPWIFI_SUPPORTED_RATES];
|
||||
u32 rates_size;
|
||||
u16 tmp_cap;
|
||||
u8 *pos;
|
||||
int rsn_ie_len = 0;
|
||||
int ret;
|
||||
|
||||
pos = (u8 *)assoc;
|
||||
|
||||
cmd->command = cpu_to_le16(HOST_CMD_802_11_ASSOCIATE);
|
||||
|
||||
/* Save so we know which BSS Desc to use in the response handler */
|
||||
priv->attempted_bss_desc = bss_desc;
|
||||
|
||||
memcpy(assoc->peer_sta_addr,
|
||||
bss_desc->mac_address, sizeof(assoc->peer_sta_addr));
|
||||
pos += sizeof(assoc->peer_sta_addr);
|
||||
|
||||
/* Set the listen interval */
|
||||
assoc->listen_interval = cpu_to_le16(priv->listen_interval);
|
||||
/* Set the beacon period */
|
||||
assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period);
|
||||
|
||||
pos += sizeof(assoc->cap_info_bitmap);
|
||||
pos += sizeof(assoc->listen_interval);
|
||||
pos += sizeof(assoc->beacon_period);
|
||||
pos += sizeof(assoc->dtim_period);
|
||||
|
||||
host_mlme_tlv = (struct nxpwifi_ie_types_host_mlme *)pos;
|
||||
host_mlme_tlv->header.type = cpu_to_le16(TLV_TYPE_HOST_MLME);
|
||||
host_mlme_tlv->header.len = cpu_to_le16(sizeof(host_mlme_tlv->host_mlme));
|
||||
host_mlme_tlv->host_mlme = 1;
|
||||
pos += sizeof(host_mlme_tlv->header) + sizeof(host_mlme_tlv->host_mlme);
|
||||
|
||||
ssid_tlv = (struct nxpwifi_ie_types_ssid_param_set *)pos;
|
||||
ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID);
|
||||
ssid_tlv->header.len = cpu_to_le16((u16)bss_desc->ssid.ssid_len);
|
||||
memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid,
|
||||
le16_to_cpu(ssid_tlv->header.len));
|
||||
pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len);
|
||||
|
||||
phy_tlv = (struct nxpwifi_ie_types_phy_param_set *)pos;
|
||||
phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS);
|
||||
phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set));
|
||||
memcpy(&phy_tlv->fh_ds.ds_param_set,
|
||||
&bss_desc->phy_param_set.ds_param_set.current_chan,
|
||||
sizeof(phy_tlv->fh_ds.ds_param_set));
|
||||
pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len);
|
||||
|
||||
ss_tlv = (struct nxpwifi_ie_types_ss_param_set *)pos;
|
||||
ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS);
|
||||
ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set));
|
||||
pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len);
|
||||
|
||||
/* Get the common rates supported between the driver and the BSS Desc */
|
||||
ret = nxpwifi_setup_rates_from_bssdesc(priv, bss_desc,
|
||||
rates, &rates_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Save the data rates into Current BSS state structure */
|
||||
priv->curr_bss_params.num_of_rates = rates_size;
|
||||
memcpy(&priv->curr_bss_params.data_rates, rates, rates_size);
|
||||
|
||||
/* Setup the Rates TLV in the association command */
|
||||
rates_tlv = (struct nxpwifi_ie_types_rates_param_set *)pos;
|
||||
rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
|
||||
rates_tlv->header.len = cpu_to_le16((u16)rates_size);
|
||||
memcpy(rates_tlv->rates, rates, rates_size);
|
||||
pos += sizeof(rates_tlv->header) + rates_size;
|
||||
nxpwifi_dbg(adapter, INFO, "info: ASSOC_CMD: rates size = %d\n",
|
||||
rates_size);
|
||||
|
||||
/* Add the Authentication type */
|
||||
auth_tlv = (struct nxpwifi_ie_types_auth_type *)pos;
|
||||
auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
|
||||
auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type));
|
||||
if (priv->sec_info.wep_enabled)
|
||||
auth_tlv->auth_type =
|
||||
cpu_to_le16((u16)priv->sec_info.authentication_mode);
|
||||
else
|
||||
auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM);
|
||||
|
||||
pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len);
|
||||
|
||||
if (priv->sec_info.authentication_mode == WLAN_AUTH_SAE) {
|
||||
auth_tlv->auth_type = cpu_to_le16(NXPWIFI_AUTHTYPE_SAE);
|
||||
if (bss_desc->bcn_rsnx_ie &&
|
||||
bss_desc->bcn_rsnx_ie->datalen &&
|
||||
(bss_desc->bcn_rsnx_ie->data[0] &
|
||||
WLAN_RSNX_CAPA_SAE_H2E)) {
|
||||
sae_pwe_tlv =
|
||||
(struct nxpwifi_ie_types_sae_pwe_mode *)pos;
|
||||
sae_pwe_tlv->header.type =
|
||||
cpu_to_le16(TLV_TYPE_SAE_PWE_MODE);
|
||||
sae_pwe_tlv->header.len =
|
||||
cpu_to_le16(sizeof(sae_pwe_tlv->pwe[0]));
|
||||
sae_pwe_tlv->pwe[0] = bss_desc->bcn_rsnx_ie->data[0];
|
||||
pos += sizeof(sae_pwe_tlv->header) +
|
||||
sizeof(sae_pwe_tlv->pwe[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_SUPPORT_MULTI_BANDS(adapter) &&
|
||||
!(ISSUPP_11NENABLED(adapter->fw_cap_info) &&
|
||||
!bss_desc->disable_11n &&
|
||||
(priv->config_bands & BAND_GN ||
|
||||
priv->config_bands & BAND_AN) &&
|
||||
bss_desc->bcn_ht_cap)) {
|
||||
/*
|
||||
* Append a channel TLV for the channel the attempted AP was
|
||||
* found on
|
||||
*/
|
||||
chan_tlv = (struct nxpwifi_ie_types_chan_list_param_set *)pos;
|
||||
chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
|
||||
chan_tlv->header.len =
|
||||
cpu_to_le16(sizeof(struct nxpwifi_chan_scan_param_set));
|
||||
|
||||
memset(chan_tlv->chan_scan_param, 0x00,
|
||||
sizeof(struct nxpwifi_chan_scan_param_set));
|
||||
chan_tlv->chan_scan_param[0].chan_number =
|
||||
(bss_desc->phy_param_set.ds_param_set.current_chan);
|
||||
nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Chan = %d\n",
|
||||
chan_tlv->chan_scan_param[0].chan_number);
|
||||
|
||||
chan_tlv->chan_scan_param[0].band_cfg =
|
||||
nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
|
||||
|
||||
nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Band = %d\n",
|
||||
chan_tlv->chan_scan_param[0].band_cfg);
|
||||
pos += sizeof(chan_tlv->header) +
|
||||
sizeof(struct nxpwifi_chan_scan_param_set);
|
||||
}
|
||||
|
||||
if (!priv->wps.session_enable) {
|
||||
if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
|
||||
rsn_ie_len = nxpwifi_append_rsn_ie_wpa_wpa2(priv, &pos);
|
||||
|
||||
if (rsn_ie_len == -ENOMEM)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (ISSUPP_11NENABLED(adapter->fw_cap_info) &&
|
||||
!bss_desc->disable_11n &&
|
||||
(priv->config_bands & BAND_GN ||
|
||||
priv->config_bands & BAND_AN))
|
||||
nxpwifi_cmd_append_11n_tlv(priv, bss_desc, &pos);
|
||||
|
||||
if (ISSUPP_11ACENABLED(adapter->fw_cap_info) &&
|
||||
!bss_desc->disable_11n && !bss_desc->disable_11ac &&
|
||||
(priv->config_bands & BAND_GAC ||
|
||||
priv->config_bands & BAND_AAC))
|
||||
nxpwifi_cmd_append_11ac_tlv(priv, bss_desc, &pos);
|
||||
|
||||
if (ISSUPP_11AXENABLED(adapter->fw_cap_ext) &&
|
||||
nxpwifi_11ax_bandconfig_allowed(priv, bss_desc))
|
||||
nxpwifi_cmd_append_11ax_tlv(priv, bss_desc, &pos);
|
||||
|
||||
/* Append vendor specific element TLV */
|
||||
nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_ASSOC, &pos);
|
||||
|
||||
nxpwifi_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie,
|
||||
bss_desc->bcn_ht_cap);
|
||||
|
||||
if (priv->wps.session_enable && priv->wps_ie_len)
|
||||
nxpwifi_cmd_append_wps_ie(priv, &pos);
|
||||
|
||||
nxpwifi_cmd_append_generic_ie(priv, &pos);
|
||||
|
||||
nxpwifi_cmd_append_tsf_tlv(priv, &pos, bss_desc);
|
||||
|
||||
nxpwifi_11h_process_join(priv, &pos, bss_desc);
|
||||
|
||||
cmd->size = cpu_to_le16((u16)(pos - (u8 *)assoc) + S_DS_GEN);
|
||||
|
||||
/* Set the Capability info at last */
|
||||
tmp_cap = bss_desc->cap_info_bitmap;
|
||||
|
||||
if (priv->config_bands == BAND_B)
|
||||
tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
|
||||
|
||||
tmp_cap &= CAPINFO_MASK;
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n",
|
||||
tmp_cap, CAPINFO_MASK);
|
||||
assoc->cap_info_bitmap = cpu_to_le16(tmp_cap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *assoc_failure_reason_to_str(u16 cap_info)
|
||||
{
|
||||
switch (cap_info) {
|
||||
case CONNECT_ERR_AUTH_ERR_STA_FAILURE:
|
||||
return "CONNECT_ERR_AUTH_ERR_STA_FAILURE";
|
||||
case CONNECT_ERR_AUTH_MSG_UNHANDLED:
|
||||
return "CONNECT_ERR_AUTH_MSG_UNHANDLED";
|
||||
case CONNECT_ERR_ASSOC_ERR_TIMEOUT:
|
||||
return "CONNECT_ERR_ASSOC_ERR_TIMEOUT";
|
||||
case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED:
|
||||
return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED";
|
||||
case CONNECT_ERR_STA_FAILURE:
|
||||
return "CONNECT_ERR_STA_FAILURE";
|
||||
}
|
||||
|
||||
return "Unknown connect failure";
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle association command response.
|
||||
* Parse cap_info/status_code/AID and copy IEs, update connection state,
|
||||
* WMM and HT/HE parameters, queues and filters. On failure, return the
|
||||
* IEEE status code or a mapped timeout error.
|
||||
*/
|
||||
int nxpwifi_ret_802_11_associate(struct nxpwifi_private *priv,
|
||||
struct host_cmd_ds_command *resp)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
int ret = 0;
|
||||
struct ieee_types_assoc_rsp *assoc_rsp;
|
||||
struct nxpwifi_bssdescriptor *bss_desc;
|
||||
bool enable_data = true;
|
||||
u16 cap_info, status_code, aid;
|
||||
const u8 *ie_ptr;
|
||||
struct ieee80211_ht_operation *assoc_resp_ht_oper;
|
||||
struct ieee80211_mgmt *hdr;
|
||||
|
||||
if (!priv->attempted_bss_desc) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"%s: failed, association terminated by host\n",
|
||||
__func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
hdr = (struct ieee80211_mgmt *)&resp->params;
|
||||
if (!memcmp(hdr->bssid, priv->attempted_bss_desc->mac_address,
|
||||
ETH_ALEN))
|
||||
assoc_rsp = (struct ieee_types_assoc_rsp *)&hdr->u.assoc_resp;
|
||||
else
|
||||
assoc_rsp = (struct ieee_types_assoc_rsp *)&resp->params;
|
||||
|
||||
cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap);
|
||||
status_code = le16_to_cpu(assoc_rsp->status_code);
|
||||
aid = le16_to_cpu(assoc_rsp->a_id);
|
||||
|
||||
if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"invalid AID value 0x%x; bits 15:14 not set\n", aid);
|
||||
|
||||
aid &= ~(BIT(15) | BIT(14));
|
||||
|
||||
priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN,
|
||||
sizeof(priv->assoc_rsp_buf));
|
||||
|
||||
assoc_rsp->a_id = cpu_to_le16(aid);
|
||||
memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
|
||||
|
||||
if (status_code) {
|
||||
adapter->dbg.num_cmd_assoc_failure++;
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"ASSOC_RESP: failed,\t"
|
||||
"status code=%d err=%#x a_id=%#x\n",
|
||||
status_code, cap_info,
|
||||
le16_to_cpu(assoc_rsp->a_id));
|
||||
|
||||
nxpwifi_dbg(adapter, ERROR, "assoc failure: reason %s\n",
|
||||
assoc_failure_reason_to_str(cap_info));
|
||||
if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) {
|
||||
if (status_code == NXPWIFI_ASSOC_CMD_FAILURE_AUTH) {
|
||||
ret = WLAN_STATUS_AUTH_TIMEOUT;
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"ASSOC_RESP: AUTH timeout\n");
|
||||
} else {
|
||||
ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"ASSOC_RESP: UNSPECIFIED failure\n");
|
||||
}
|
||||
|
||||
priv->assoc_rsp_size = 0;
|
||||
} else {
|
||||
ret = status_code;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Send a Media Connected event, according to the Spec */
|
||||
priv->media_connected = true;
|
||||
|
||||
adapter->ps_state = PS_STATE_AWAKE;
|
||||
adapter->pps_uapsd_mode = false;
|
||||
adapter->tx_lock_flag = false;
|
||||
|
||||
/* Set the attempted BSSID Index to current */
|
||||
bss_desc = priv->attempted_bss_desc;
|
||||
|
||||
nxpwifi_dbg(adapter, INFO, "info: ASSOC_RESP: %s\n",
|
||||
bss_desc->ssid.ssid);
|
||||
|
||||
/* Make a copy of current BSSID descriptor */
|
||||
memcpy(&priv->curr_bss_params.bss_descriptor,
|
||||
bss_desc, sizeof(struct nxpwifi_bssdescriptor));
|
||||
|
||||
/* Update curr_bss_params */
|
||||
priv->curr_bss_params.bss_descriptor.channel =
|
||||
bss_desc->phy_param_set.ds_param_set.current_chan;
|
||||
|
||||
priv->curr_bss_params.band = (u8)bss_desc->bss_band;
|
||||
|
||||
if (bss_desc->wmm_ie.element_id == WLAN_EID_VENDOR_SPECIFIC)
|
||||
priv->curr_bss_params.wmm_enabled = true;
|
||||
else
|
||||
priv->curr_bss_params.wmm_enabled = false;
|
||||
|
||||
if ((priv->wmm_required || bss_desc->bcn_ht_cap) &&
|
||||
priv->curr_bss_params.wmm_enabled)
|
||||
priv->wmm_enabled = true;
|
||||
else
|
||||
priv->wmm_enabled = false;
|
||||
|
||||
priv->curr_bss_params.wmm_uapsd_enabled = false;
|
||||
|
||||
priv->curr_bss_params.wmm_uapsd_enabled = priv->wmm_enabled &&
|
||||
(bss_desc->wmm_ie.qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD);
|
||||
|
||||
/* Store the bandwidth information from assoc response */
|
||||
ie_ptr = cfg80211_find_ie(WLAN_EID_HT_OPERATION, assoc_rsp->ie_buffer,
|
||||
priv->assoc_rsp_size
|
||||
- sizeof(struct ieee_types_assoc_rsp));
|
||||
if (ie_ptr) {
|
||||
assoc_resp_ht_oper = (struct ieee80211_ht_operation *)(ie_ptr
|
||||
+ sizeof(struct element));
|
||||
priv->assoc_resp_ht_param = assoc_resp_ht_oper->ht_param;
|
||||
priv->ht_param_present = true;
|
||||
} else {
|
||||
priv->ht_param_present = false;
|
||||
}
|
||||
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: ASSOC_RESP: curr_pkt_filter is %#x\n",
|
||||
priv->curr_pkt_filter);
|
||||
if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
|
||||
priv->wpa_is_gtk_set = false;
|
||||
|
||||
if (priv->wmm_enabled) {
|
||||
/* Don't re-enable carrier until we get the WMM_GET_STATUS event */
|
||||
enable_data = false;
|
||||
} else {
|
||||
/* Since WMM is not enabled, setup the queues with the defaults */
|
||||
nxpwifi_wmm_setup_queue_priorities(priv, NULL);
|
||||
nxpwifi_wmm_setup_ac_downgrade(priv);
|
||||
}
|
||||
|
||||
if (enable_data)
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: post association, re-enabling data flow\n");
|
||||
|
||||
/* Reset SNR/NF/RSSI values */
|
||||
priv->data_rssi_last = 0;
|
||||
priv->data_nf_last = 0;
|
||||
priv->data_rssi_avg = 0;
|
||||
priv->data_nf_avg = 0;
|
||||
priv->bcn_rssi_last = 0;
|
||||
priv->bcn_nf_last = 0;
|
||||
priv->bcn_rssi_avg = 0;
|
||||
priv->bcn_nf_avg = 0;
|
||||
priv->rxpd_rate = 0;
|
||||
priv->rxpd_htinfo = 0;
|
||||
|
||||
nxpwifi_save_curr_bcn(priv);
|
||||
|
||||
adapter->dbg.num_cmd_assoc_success++;
|
||||
|
||||
nxpwifi_dbg(adapter, MSG, "assoc: associated with %pM\n",
|
||||
priv->attempted_bss_desc->mac_address);
|
||||
|
||||
/* Add the ra_list here for infra mode as there will be only 1 ra always */
|
||||
nxpwifi_ralist_add(priv,
|
||||
priv->curr_bss_params.bss_descriptor.mac_address);
|
||||
|
||||
netif_carrier_on(priv->netdev);
|
||||
nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter);
|
||||
|
||||
if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
|
||||
priv->scan_block = true;
|
||||
else
|
||||
priv->port_open = true;
|
||||
|
||||
done:
|
||||
/* Need to indicate IOCTL complete */
|
||||
if (adapter->curr_cmd->wait_q_enabled) {
|
||||
if (ret)
|
||||
adapter->cmd_wait_q.status = -1;
|
||||
else
|
||||
adapter->cmd_wait_q.status = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Associate to the specified BSS (STA only) */
|
||||
int nxpwifi_associate(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_bssdescriptor *bss_desc)
|
||||
{
|
||||
/*
|
||||
* Return error if the adapter is not STA role or table entry
|
||||
* is not marked as infra.
|
||||
*/
|
||||
if ((GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_STA) ||
|
||||
bss_desc->bss_mode != NL80211_IFTYPE_STATION)
|
||||
return -EINVAL;
|
||||
|
||||
if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
|
||||
!bss_desc->disable_11n && !bss_desc->disable_11ac &&
|
||||
priv->config_bands & BAND_AAC)
|
||||
nxpwifi_set_11ac_ba_params(priv);
|
||||
else
|
||||
nxpwifi_set_ba_params(priv);
|
||||
|
||||
/*
|
||||
* Clear any past association response stored for application
|
||||
* retrieval
|
||||
*/
|
||||
priv->assoc_rsp_size = 0;
|
||||
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_802_11_ASSOCIATE,
|
||||
HOST_ACT_GEN_SET, 0, bss_desc, true);
|
||||
}
|
||||
|
||||
/* Send deauth to disconnect from an infrastructure BSS */
|
||||
static int nxpwifi_deauthenticate_infra(struct nxpwifi_private *priv, u8 *mac)
|
||||
{
|
||||
u8 mac_address[ETH_ALEN];
|
||||
int ret;
|
||||
|
||||
if (!mac || is_zero_ether_addr(mac))
|
||||
memcpy(mac_address,
|
||||
priv->curr_bss_params.bss_descriptor.mac_address,
|
||||
ETH_ALEN);
|
||||
else
|
||||
memcpy(mac_address, mac, ETH_ALEN);
|
||||
|
||||
ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_DEAUTHENTICATE,
|
||||
HOST_ACT_GEN_SET, 0, mac_address, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disconnect from the current BSS (STA/P2P/AP) */
|
||||
int nxpwifi_deauthenticate(struct nxpwifi_private *priv, u8 *mac)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!priv->media_connected)
|
||||
return 0;
|
||||
|
||||
priv->auth_flag = 0;
|
||||
priv->auth_alg = WLAN_AUTH_NONE;
|
||||
priv->host_mlme_reg = false;
|
||||
priv->mgmt_frame_mask = 0;
|
||||
|
||||
ret = nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG,
|
||||
HOST_ACT_GEN_SET, 0,
|
||||
&priv->mgmt_frame_mask, false);
|
||||
if (ret) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"could not unregister mgmt frame rx\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (priv->bss_mode) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
ret = nxpwifi_deauthenticate_infra(priv, mac);
|
||||
if (ret)
|
||||
cfg80211_disconnected(priv->netdev, 0, NULL, 0,
|
||||
true, GFP_KERNEL);
|
||||
break;
|
||||
case NL80211_IFTYPE_AP:
|
||||
ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP,
|
||||
HOST_ACT_GEN_SET, 0, NULL, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Deauthenticate/disconnect from all BSS. */
|
||||
void nxpwifi_deauthenticate_all(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
struct nxpwifi_private *priv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < adapter->priv_num; i++) {
|
||||
priv = adapter->priv[i];
|
||||
nxpwifi_deauthenticate(priv, NULL);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nxpwifi_deauthenticate_all);
|
||||
|
||||
/* Convert band to radio type used in channel TLV. */
|
||||
u8 nxpwifi_band_to_radio_type(u16 config_bands)
|
||||
{
|
||||
if (config_bands & BAND_A || config_bands & BAND_AN ||
|
||||
config_bands & BAND_AAC || config_bands & BAND_AAX)
|
||||
return HOST_SCAN_RADIO_TYPE_A;
|
||||
|
||||
return HOST_SCAN_RADIO_TYPE_BG;
|
||||
}
|
||||
1673
drivers/net/wireless/nxp/nxpwifi/main.c
Normal file
1673
drivers/net/wireless/nxp/nxpwifi/main.c
Normal file
File diff suppressed because it is too large
Load Diff
1429
drivers/net/wireless/nxp/nxpwifi/main.h
Normal file
1429
drivers/net/wireless/nxp/nxpwifi/main.h
Normal file
File diff suppressed because it is too large
Load Diff
2695
drivers/net/wireless/nxp/nxpwifi/scan.c
Normal file
2695
drivers/net/wireless/nxp/nxpwifi/scan.c
Normal file
File diff suppressed because it is too large
Load Diff
2327
drivers/net/wireless/nxp/nxpwifi/sdio.c
Normal file
2327
drivers/net/wireless/nxp/nxpwifi/sdio.c
Normal file
File diff suppressed because it is too large
Load Diff
340
drivers/net/wireless/nxp/nxpwifi/sdio.h
Normal file
340
drivers/net/wireless/nxp/nxpwifi/sdio.h
Normal file
@@ -0,0 +1,340 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* NXP Wireless LAN device driver: SDIO specific definitions
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_SDIO_H
|
||||
#define _NXPWIFI_SDIO_H
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#define IW61X_SDIO_FW_NAME "nxp/sd_w61x_v1.bin.se"
|
||||
|
||||
#define BLOCK_MODE 1
|
||||
#define BYTE_MODE 0
|
||||
|
||||
#define NXPWIFI_SDIO_IO_PORT_MASK 0xfffff
|
||||
|
||||
#define NXPWIFI_SDIO_BYTE_MODE_MASK 0x80000000
|
||||
|
||||
#define NXPWIFI_MAX_FUNC2_REG_NUM 13
|
||||
#define NXPWIFI_SDIO_SCRATCH_SIZE 10
|
||||
|
||||
#define SDIO_MPA_ADDR_BASE 0x1000
|
||||
|
||||
#define CMD_PORT_UPLD_INT_MASK (0x1U << 6)
|
||||
#define CMD_PORT_DNLD_INT_MASK (0x1U << 7)
|
||||
#define HOST_TERM_CMD53 (0x1U << 2)
|
||||
#define REG_PORT 0
|
||||
#define MEM_PORT 0x10000
|
||||
|
||||
#define CMD53_NEW_MODE (0x1U << 0)
|
||||
#define CMD_PORT_RD_LEN_EN (0x1U << 2)
|
||||
#define CMD_PORT_AUTO_EN (0x1U << 0)
|
||||
#define CMD_PORT_SLCT 0x8000
|
||||
#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
|
||||
#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
|
||||
|
||||
#define NXPWIFI_MP_AGGR_BSIZE_32K (32768)
|
||||
/* we leave one block of 256 bytes for DMA alignment*/
|
||||
#define NXPWIFI_MP_AGGR_BSIZE_MAX (65280)
|
||||
|
||||
/* Misc. Config Register : Auto Re-enable interrupts */
|
||||
#define AUTO_RE_ENABLE_INT BIT(4)
|
||||
|
||||
/* Host Control Registers : Configuration */
|
||||
#define CONFIGURATION_REG 0x00
|
||||
/* Host Control Registers : Host power up */
|
||||
#define HOST_POWER_UP (0x1U << 1)
|
||||
|
||||
/* Host Control Registers : Upload host interrupt mask */
|
||||
#define UP_LD_HOST_INT_MASK (0x1U)
|
||||
/* Host Control Registers : Download host interrupt mask */
|
||||
#define DN_LD_HOST_INT_MASK (0x2U)
|
||||
|
||||
/* Host Control Registers : Upload host interrupt status */
|
||||
#define UP_LD_HOST_INT_STATUS (0x1U)
|
||||
/* Host Control Registers : Download host interrupt status */
|
||||
#define DN_LD_HOST_INT_STATUS (0x2U)
|
||||
|
||||
/* Host Control Registers : Host interrupt status */
|
||||
#define CARD_INT_STATUS_REG 0x28
|
||||
|
||||
/* Card Control Registers : Card I/O ready */
|
||||
#define CARD_IO_READY (0x1U << 3)
|
||||
/* Card Control Registers : Download card ready */
|
||||
#define DN_LD_CARD_RDY (0x1U << 0)
|
||||
|
||||
/* Max retry number of CMD53 write */
|
||||
#define MAX_WRITE_IOMEM_RETRY 2
|
||||
|
||||
/* SDIO Tx aggregation in progress ? */
|
||||
#define MP_TX_AGGR_IN_PROGRESS(a) ((a)->mpa_tx.pkt_cnt > 0)
|
||||
|
||||
/* SDIO Tx aggregation buffer room for next packet ? */
|
||||
#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ({ \
|
||||
typeof(a) (_a) = a; \
|
||||
(((_a)->mpa_tx.buf_len + (len)) <= (_a)->mpa_tx.buf_size); \
|
||||
})
|
||||
|
||||
/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */
|
||||
#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \
|
||||
typeof(a) (_a) = (a); \
|
||||
typeof(pkt_len) (_pkt_len) = pkt_len; \
|
||||
typeof(port) (_port) = port; \
|
||||
memmove(&(_a)->mpa_tx.buf[(_a)->mpa_tx.buf_len], \
|
||||
payload, (_pkt_len)); \
|
||||
(_a)->mpa_tx.buf_len += (_pkt_len); \
|
||||
if (!(_a)->mpa_tx.pkt_cnt) \
|
||||
(_a)->mpa_tx.start_port = (_port); \
|
||||
if ((_a)->mpa_tx.start_port <= (_port)) \
|
||||
(_a)->mpa_tx.ports |= (1 << ((_a)->mpa_tx.pkt_cnt)); \
|
||||
else \
|
||||
(_a)->mpa_tx.ports |= (1 << ((_a)->mpa_tx.pkt_cnt + 1 + \
|
||||
((_a)->max_ports - \
|
||||
(_a)->mp_end_port))); \
|
||||
(_a)->mpa_tx.pkt_cnt++; \
|
||||
} while (0)
|
||||
|
||||
/* SDIO Tx aggregation limit ? */
|
||||
#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) ({ \
|
||||
typeof(a) (_a) = a; \
|
||||
((_a)->mpa_tx.pkt_cnt == (_a)->mpa_tx.pkt_aggr_limit); \
|
||||
})
|
||||
|
||||
/* Reset SDIO Tx aggregation buffer parameters */
|
||||
#define MP_TX_AGGR_BUF_RESET(a) do { \
|
||||
typeof(a) (_a) = (a); \
|
||||
(_a)->mpa_tx.pkt_cnt = 0; \
|
||||
(_a)->mpa_tx.buf_len = 0; \
|
||||
(_a)->mpa_tx.ports = 0; \
|
||||
(_a)->mpa_tx.start_port = 0; \
|
||||
} while (0)
|
||||
|
||||
/* SDIO Rx aggregation limit ? */
|
||||
#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) ({ \
|
||||
typeof(a) (_a) = a; \
|
||||
((_a)->mpa_rx.pkt_cnt == (_a)->mpa_rx.pkt_aggr_limit); \
|
||||
})
|
||||
|
||||
/* SDIO Rx aggregation in progress ? */
|
||||
#define MP_RX_AGGR_IN_PROGRESS(a) ((a)->mpa_rx.pkt_cnt > 0)
|
||||
|
||||
/* SDIO Rx aggregation buffer room for next packet ? */
|
||||
#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) ({ \
|
||||
typeof(a) (_a) = a; \
|
||||
((((_a)->mpa_rx.buf_len + (rx_len))) <= (_a)->mpa_rx.buf_size); \
|
||||
})
|
||||
|
||||
/* Reset SDIO Rx aggregation buffer parameters */
|
||||
#define MP_RX_AGGR_BUF_RESET(a) do { \
|
||||
typeof(a) (_a) = (a); \
|
||||
(_a)->mpa_rx.pkt_cnt = 0; \
|
||||
(_a)->mpa_rx.buf_len = 0; \
|
||||
(_a)->mpa_rx.ports = 0; \
|
||||
(_a)->mpa_rx.start_port = 0; \
|
||||
} while (0)
|
||||
|
||||
/* data structure for SDIO MPA TX */
|
||||
struct nxpwifi_sdio_mpa_tx {
|
||||
/* multiport tx aggregation buffer pointer */
|
||||
u8 *buf;
|
||||
u32 buf_len;
|
||||
u32 pkt_cnt;
|
||||
u32 ports;
|
||||
u16 start_port;
|
||||
u8 enabled;
|
||||
u32 buf_size;
|
||||
u32 pkt_aggr_limit;
|
||||
};
|
||||
|
||||
struct nxpwifi_sdio_mpa_rx {
|
||||
u8 *buf;
|
||||
u32 buf_len;
|
||||
u32 pkt_cnt;
|
||||
u32 ports;
|
||||
u16 start_port;
|
||||
u32 *len_arr;
|
||||
u8 enabled;
|
||||
u32 buf_size;
|
||||
u32 pkt_aggr_limit;
|
||||
};
|
||||
|
||||
int nxpwifi_bus_register(void);
|
||||
void nxpwifi_bus_unregister(void);
|
||||
|
||||
struct nxpwifi_sdio_card_reg {
|
||||
u8 start_rd_port;
|
||||
u8 start_wr_port;
|
||||
u8 base_0_reg;
|
||||
u8 base_1_reg;
|
||||
u8 poll_reg;
|
||||
u8 host_int_enable;
|
||||
u8 host_int_rsr_reg;
|
||||
u8 host_int_status_reg;
|
||||
u8 host_int_mask_reg;
|
||||
u8 host_strap_reg;
|
||||
u8 host_strap_mask;
|
||||
u8 host_strap_value;
|
||||
u8 status_reg_0;
|
||||
u8 status_reg_1;
|
||||
u8 sdio_int_mask;
|
||||
u32 data_port_mask;
|
||||
u8 io_port_0_reg;
|
||||
u8 io_port_1_reg;
|
||||
u8 io_port_2_reg;
|
||||
u8 max_mp_regs;
|
||||
u8 rd_bitmap_l;
|
||||
u8 rd_bitmap_u;
|
||||
u8 rd_bitmap_1l;
|
||||
u8 rd_bitmap_1u;
|
||||
u8 wr_bitmap_l;
|
||||
u8 wr_bitmap_u;
|
||||
u8 wr_bitmap_1l;
|
||||
u8 wr_bitmap_1u;
|
||||
u8 rd_len_p0_l;
|
||||
u8 rd_len_p0_u;
|
||||
u8 card_misc_cfg_reg;
|
||||
u8 card_cfg_2_1_reg;
|
||||
u8 cmd_rd_len_0;
|
||||
u8 cmd_rd_len_1;
|
||||
u8 cmd_rd_len_2;
|
||||
u8 cmd_rd_len_3;
|
||||
u8 cmd_cfg_0;
|
||||
u8 cmd_cfg_1;
|
||||
u8 cmd_cfg_2;
|
||||
u8 cmd_cfg_3;
|
||||
u8 fw_dump_host_ready;
|
||||
u8 fw_dump_ctrl;
|
||||
u8 fw_dump_start;
|
||||
u8 fw_dump_end;
|
||||
u8 func1_dump_reg_start;
|
||||
u8 func1_dump_reg_end;
|
||||
u8 func1_scratch_reg;
|
||||
u8 func1_spec_reg_num;
|
||||
u8 func1_spec_reg_table[NXPWIFI_MAX_FUNC2_REG_NUM];
|
||||
};
|
||||
|
||||
struct sdio_mmc_card {
|
||||
struct sdio_func *func;
|
||||
struct nxpwifi_adapter *adapter;
|
||||
|
||||
struct completion fw_done;
|
||||
const char *firmware;
|
||||
const char *firmware_sdiouart;
|
||||
const struct nxpwifi_sdio_card_reg *reg;
|
||||
u8 max_ports;
|
||||
u8 mp_agg_pkt_limit;
|
||||
u16 tx_buf_size;
|
||||
u32 mp_tx_agg_buf_size;
|
||||
u32 mp_rx_agg_buf_size;
|
||||
|
||||
u32 mp_rd_bitmap;
|
||||
u32 mp_wr_bitmap;
|
||||
|
||||
u16 mp_end_port;
|
||||
u32 mp_data_port_mask;
|
||||
|
||||
u8 curr_rd_port;
|
||||
u8 curr_wr_port;
|
||||
|
||||
u8 *mp_regs;
|
||||
bool can_dump_fw;
|
||||
bool fw_dump_enh;
|
||||
bool can_ext_scan;
|
||||
|
||||
struct nxpwifi_sdio_mpa_tx mpa_tx;
|
||||
struct nxpwifi_sdio_mpa_rx mpa_rx;
|
||||
|
||||
struct work_struct work;
|
||||
unsigned long work_flags;
|
||||
};
|
||||
|
||||
struct nxpwifi_sdio_device {
|
||||
const char *firmware;
|
||||
const char *firmware_sdiouart;
|
||||
const struct nxpwifi_sdio_card_reg *reg;
|
||||
u8 max_ports;
|
||||
u8 mp_agg_pkt_limit;
|
||||
u16 tx_buf_size;
|
||||
u32 mp_tx_agg_buf_size;
|
||||
u32 mp_rx_agg_buf_size;
|
||||
bool can_dump_fw;
|
||||
bool fw_dump_enh;
|
||||
bool can_ext_scan;
|
||||
};
|
||||
|
||||
/* .cmdrsp_complete handler
|
||||
*/
|
||||
static inline int nxpwifi_sdio_cmdrsp_complete(struct nxpwifi_adapter *adapter,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* .event_complete handler
|
||||
*/
|
||||
static inline int nxpwifi_sdio_event_complete(struct nxpwifi_adapter *adapter,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card)
|
||||
{
|
||||
u8 tmp;
|
||||
|
||||
if (card->curr_rd_port < card->mpa_rx.start_port) {
|
||||
tmp = card->mp_end_port >> 1;
|
||||
|
||||
if (((card->max_ports - card->mpa_rx.start_port) +
|
||||
card->curr_rd_port) >= tmp)
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((card->curr_rd_port - card->mpa_rx.start_port) >=
|
||||
(card->mp_end_port >> 1))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card)
|
||||
{
|
||||
u16 tmp;
|
||||
|
||||
if (card->curr_wr_port < card->mpa_tx.start_port) {
|
||||
tmp = card->mp_end_port >> 1;
|
||||
|
||||
if (((card->max_ports - card->mpa_tx.start_port) +
|
||||
card->curr_wr_port) >= tmp)
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((card->curr_wr_port - card->mpa_tx.start_port) >=
|
||||
(card->mp_end_port >> 1))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
|
||||
static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
|
||||
u16 rx_len, u8 port)
|
||||
{
|
||||
card->mpa_rx.buf_len += rx_len;
|
||||
|
||||
if (!card->mpa_rx.pkt_cnt)
|
||||
card->mpa_rx.start_port = port;
|
||||
|
||||
card->mpa_rx.ports |= (1 << port);
|
||||
card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len;
|
||||
card->mpa_rx.pkt_cnt++;
|
||||
}
|
||||
#endif /* _NXPWIFI_SDIO_H */
|
||||
1165
drivers/net/wireless/nxp/nxpwifi/sta_cfg.c
Normal file
1165
drivers/net/wireless/nxp/nxpwifi/sta_cfg.c
Normal file
File diff suppressed because it is too large
Load Diff
3383
drivers/net/wireless/nxp/nxpwifi/sta_cmd.c
Normal file
3383
drivers/net/wireless/nxp/nxpwifi/sta_cmd.c
Normal file
File diff suppressed because it is too large
Load Diff
862
drivers/net/wireless/nxp/nxpwifi/sta_event.c
Normal file
862
drivers/net/wireless/nxp/nxpwifi/sta_event.c
Normal file
@@ -0,0 +1,862 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* NXP Wireless LAN device driver: station event handling
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
#include "wmm.h"
|
||||
#include "11n.h"
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_link_lost(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
adapter->dbg.num_event_link_lost++;
|
||||
if (priv->media_connected) {
|
||||
adapter->priv_link_lost = priv;
|
||||
adapter->host_mlme_link_lost = true;
|
||||
nxpwifi_queue_wiphy_work(adapter,
|
||||
&adapter->host_mlme_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_link_sensed(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
netif_carrier_on(priv->netdev);
|
||||
nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_deauthenticated(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (priv->wps.session_enable) {
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: receive deauth event in wps session\n");
|
||||
} else {
|
||||
adapter->dbg.num_event_deauth++;
|
||||
if (priv->media_connected) {
|
||||
priv->last_deauth_reason =
|
||||
get_unaligned_le16(priv->adapter->event_body);
|
||||
nxpwifi_queue_wiphy_work(priv->adapter,
|
||||
&priv->reset_conn_state_work);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_disassociated(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (priv->wps.session_enable) {
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: receive disassoc event in wps session\n");
|
||||
} else {
|
||||
adapter->dbg.num_event_disassoc++;
|
||||
if (priv->media_connected) {
|
||||
priv->last_deauth_reason =
|
||||
get_unaligned_le16(priv->adapter->event_body);
|
||||
nxpwifi_queue_wiphy_work(priv->adapter,
|
||||
&priv->reset_conn_state_work);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_ps_awake(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (!adapter->pps_uapsd_mode &&
|
||||
priv->port_open &&
|
||||
priv->media_connected && adapter->sleep_period.period) {
|
||||
adapter->pps_uapsd_mode = true;
|
||||
nxpwifi_dbg(adapter, EVENT,
|
||||
"event: PPS/UAPSD mode activated\n");
|
||||
}
|
||||
adapter->tx_lock_flag = false;
|
||||
if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) {
|
||||
if (nxpwifi_check_last_packet_indication(priv)) {
|
||||
if (adapter->data_sent) {
|
||||
adapter->ps_state = PS_STATE_AWAKE;
|
||||
adapter->pm_wakeup_card_req = false;
|
||||
adapter->pm_wakeup_fw_try = false;
|
||||
timer_delete(&adapter->wakeup_timer);
|
||||
} else {
|
||||
if (!nxpwifi_send_null_packet
|
||||
(priv,
|
||||
NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET |
|
||||
NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET))
|
||||
adapter->ps_state = PS_STATE_SLEEP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
adapter->ps_state = PS_STATE_AWAKE;
|
||||
adapter->pm_wakeup_card_req = false;
|
||||
adapter->pm_wakeup_fw_try = false;
|
||||
timer_delete(&adapter->wakeup_timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_ps_sleep(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
adapter->ps_state = PS_STATE_PRE_SLEEP;
|
||||
nxpwifi_check_ps_cond(adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_mic_err_multicast(struct nxpwifi_private *priv)
|
||||
{
|
||||
cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid,
|
||||
NL80211_KEYTYPE_GROUP,
|
||||
-1, NULL, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_mic_err_unicast(struct nxpwifi_private *priv)
|
||||
{
|
||||
cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid,
|
||||
NL80211_KEYTYPE_PAIRWISE,
|
||||
-1, NULL, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_deep_sleep_awake(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
adapter->if_ops.wakeup_complete(adapter);
|
||||
if (adapter->is_deep_sleep)
|
||||
adapter->is_deep_sleep = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_wmm_status_change(struct nxpwifi_private *priv)
|
||||
{
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_WMM_GET_STATUS,
|
||||
0, 0, NULL, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_bs_scan_report(struct nxpwifi_private *priv)
|
||||
{
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_QUERY,
|
||||
HOST_ACT_GEN_GET, 0, NULL, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_rssi_low(struct nxpwifi_private *priv)
|
||||
{
|
||||
cfg80211_cqm_rssi_notify(priv->netdev,
|
||||
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
|
||||
0, GFP_KERNEL);
|
||||
priv->subsc_evt_rssi_state = RSSI_LOW_RECVD;
|
||||
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_RSSI_INFO,
|
||||
HOST_ACT_GEN_GET, 0, NULL, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_rssi_high(struct nxpwifi_private *priv)
|
||||
{
|
||||
cfg80211_cqm_rssi_notify(priv->netdev,
|
||||
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
|
||||
0, GFP_KERNEL);
|
||||
priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD;
|
||||
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_RSSI_INFO,
|
||||
HOST_ACT_GEN_GET, 0, NULL, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_port_release(struct nxpwifi_private *priv)
|
||||
{
|
||||
priv->port_open = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_addba(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_RSP,
|
||||
HOST_ACT_GEN_SET, 0,
|
||||
adapter->event_body, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_delba(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_11n_delete_ba_stream(priv, adapter->event_body);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_bs_stream_timeout(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct host_cmd_ds_11n_batimeout *event =
|
||||
(struct host_cmd_ds_11n_batimeout *)adapter->event_body;
|
||||
|
||||
nxpwifi_11n_ba_stream_timeout(priv, event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_amsdu_aggr_ctrl(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u16 ctrl;
|
||||
|
||||
ctrl = get_unaligned_le16(adapter->event_body);
|
||||
adapter->tx_buf_size = min_t(u16, adapter->curr_tx_buf_size, ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_hs_act_req(struct nxpwifi_private *priv)
|
||||
{
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_802_11_HS_CFG_ENH,
|
||||
0, 0, NULL, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_channel_switch_ann(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_bssdescriptor *bss_desc;
|
||||
|
||||
bss_desc = &priv->curr_bss_params.bss_descriptor;
|
||||
priv->csa_expire_time = jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME);
|
||||
priv->csa_chan = bss_desc->channel;
|
||||
return nxpwifi_send_cmd(priv, HOST_CMD_802_11_DEAUTHENTICATE,
|
||||
HOST_ACT_GEN_SET, 0,
|
||||
bss_desc->mac_address, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_radar_detected(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
return nxpwifi_11h_handle_radar_detected(priv, adapter->event_skb);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_channel_report_rdy(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
return nxpwifi_11h_handle_chanrpt_ready(priv, adapter->event_skb);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_tx_data_pause(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_process_tx_pause_event(priv, adapter->event_skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_ext_scan_report(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
void *buf = adapter->event_skb->data;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* We intend to skip this event during suspend, but handle
|
||||
* it in interface disabled case
|
||||
*/
|
||||
if (adapter->ext_scan && (!priv->scan_aborting ||
|
||||
!netif_running(priv->netdev)))
|
||||
ret = nxpwifi_handle_event_ext_scan_report(priv, buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_rxba_sync(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_11n_rxba_sync_event(priv, adapter->event_body,
|
||||
adapter->event_skb->len -
|
||||
sizeof(adapter->event_cause));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_remain_on_chan_expired(struct nxpwifi_private *priv)
|
||||
{
|
||||
if (priv->auth_flag & HOST_MLME_AUTH_PENDING) {
|
||||
priv->auth_flag = 0;
|
||||
priv->auth_alg = WLAN_AUTH_NONE;
|
||||
} else {
|
||||
cfg80211_remain_on_channel_expired(&priv->wdev,
|
||||
priv->roc_cfg.cookie,
|
||||
&priv->roc_cfg.chan,
|
||||
GFP_ATOMIC);
|
||||
}
|
||||
|
||||
memset(&priv->roc_cfg, 0x00, sizeof(struct nxpwifi_roc_cfg));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_bg_scan_stopped(struct nxpwifi_private *priv)
|
||||
{
|
||||
cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
|
||||
if (priv->sched_scanning)
|
||||
priv->sched_scanning = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_multi_chan_info(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_process_multi_chan_event(priv, adapter->event_skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_tx_status_report(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_parse_tx_status_event(priv, adapter->event_body);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_bt_coex_wlan_para_change(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (!adapter->ignore_btcoex_events)
|
||||
nxpwifi_bt_coex_wlan_param_update_event(priv,
|
||||
adapter->event_skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_sta_event_vdll_ind(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
return nxpwifi_process_vdll_event(priv, adapter->event_skb);
|
||||
}
|
||||
|
||||
static const struct nxpwifi_evt_entry evt_table_sta[] = {
|
||||
{.event_cause = EVENT_LINK_LOST,
|
||||
.event_handler = nxpwifi_sta_event_link_lost},
|
||||
{.event_cause = EVENT_LINK_SENSED,
|
||||
.event_handler = nxpwifi_sta_event_link_sensed},
|
||||
{.event_cause = EVENT_DEAUTHENTICATED,
|
||||
.event_handler = nxpwifi_sta_event_deauthenticated},
|
||||
{.event_cause = EVENT_DISASSOCIATED,
|
||||
.event_handler = nxpwifi_sta_event_disassociated},
|
||||
{.event_cause = EVENT_PS_AWAKE,
|
||||
.event_handler = nxpwifi_sta_event_ps_awake},
|
||||
{.event_cause = EVENT_PS_SLEEP,
|
||||
.event_handler = nxpwifi_sta_event_ps_sleep},
|
||||
{.event_cause = EVENT_MIC_ERR_MULTICAST,
|
||||
.event_handler = nxpwifi_sta_event_mic_err_multicast},
|
||||
{.event_cause = EVENT_MIC_ERR_UNICAST,
|
||||
.event_handler = nxpwifi_sta_event_mic_err_unicast},
|
||||
{.event_cause = EVENT_DEEP_SLEEP_AWAKE,
|
||||
.event_handler = nxpwifi_sta_event_deep_sleep_awake},
|
||||
{.event_cause = EVENT_WMM_STATUS_CHANGE,
|
||||
.event_handler = nxpwifi_sta_event_wmm_status_change},
|
||||
{.event_cause = EVENT_BG_SCAN_REPORT,
|
||||
.event_handler = nxpwifi_sta_event_bs_scan_report},
|
||||
{.event_cause = EVENT_RSSI_LOW,
|
||||
.event_handler = nxpwifi_sta_event_rssi_low},
|
||||
{.event_cause = EVENT_RSSI_HIGH,
|
||||
.event_handler = nxpwifi_sta_event_rssi_high},
|
||||
{.event_cause = EVENT_PORT_RELEASE,
|
||||
.event_handler = nxpwifi_sta_event_port_release},
|
||||
{.event_cause = EVENT_ADDBA,
|
||||
.event_handler = nxpwifi_sta_event_addba},
|
||||
{.event_cause = EVENT_DELBA,
|
||||
.event_handler = nxpwifi_sta_event_delba},
|
||||
{.event_cause = EVENT_BA_STREAM_TIEMOUT,
|
||||
.event_handler = nxpwifi_sta_event_bs_stream_timeout},
|
||||
{.event_cause = EVENT_AMSDU_AGGR_CTRL,
|
||||
.event_handler = nxpwifi_sta_event_amsdu_aggr_ctrl},
|
||||
{.event_cause = EVENT_HS_ACT_REQ,
|
||||
.event_handler = nxpwifi_sta_event_hs_act_req},
|
||||
{.event_cause = EVENT_CHANNEL_SWITCH_ANN,
|
||||
.event_handler = nxpwifi_sta_event_channel_switch_ann},
|
||||
{.event_cause = EVENT_RADAR_DETECTED,
|
||||
.event_handler = nxpwifi_sta_event_radar_detected},
|
||||
{.event_cause = EVENT_CHANNEL_REPORT_RDY,
|
||||
.event_handler = nxpwifi_sta_event_channel_report_rdy},
|
||||
{.event_cause = EVENT_TX_DATA_PAUSE,
|
||||
.event_handler = nxpwifi_sta_event_tx_data_pause},
|
||||
{.event_cause = EVENT_EXT_SCAN_REPORT,
|
||||
.event_handler = nxpwifi_sta_event_ext_scan_report},
|
||||
{.event_cause = EVENT_RXBA_SYNC,
|
||||
.event_handler = nxpwifi_sta_event_rxba_sync},
|
||||
{.event_cause = EVENT_REMAIN_ON_CHAN_EXPIRED,
|
||||
.event_handler = nxpwifi_sta_event_remain_on_chan_expired},
|
||||
{.event_cause = EVENT_BG_SCAN_STOPPED,
|
||||
.event_handler = nxpwifi_sta_event_bg_scan_stopped},
|
||||
{.event_cause = EVENT_MULTI_CHAN_INFO,
|
||||
.event_handler = nxpwifi_sta_event_multi_chan_info},
|
||||
{.event_cause = EVENT_TX_STATUS_REPORT,
|
||||
.event_handler = nxpwifi_sta_event_tx_status_report},
|
||||
{.event_cause = EVENT_BT_COEX_WLAN_PARA_CHANGE,
|
||||
.event_handler = nxpwifi_sta_event_bt_coex_wlan_para_change},
|
||||
{.event_cause = EVENT_VDLL_IND,
|
||||
.event_handler = nxpwifi_sta_event_vdll_ind},
|
||||
{.event_cause = EVENT_DUMMY_HOST_WAKEUP_SIGNAL,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_MIB_CHANGED,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_INIT_DONE,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_SNR_LOW,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_MAX_FAIL,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_SNR_HIGH,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_DATA_RSSI_LOW,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_DATA_SNR_LOW,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_DATA_RSSI_HIGH,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_DATA_SNR_HIGH,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_LINK_QUALITY,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_PRE_BEACON_LOST,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_WEP_ICV_ERR,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_BW_CHANGE,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_HOSTWAKE_STAIE,
|
||||
.event_handler = NULL},
|
||||
{.event_cause = EVENT_UNKNOWN_DEBUG,
|
||||
.event_handler = NULL},
|
||||
};
|
||||
|
||||
static void nxpwifi_process_uap_tx_pause(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ie_types_header *tlv)
|
||||
{
|
||||
struct nxpwifi_tx_pause_tlv *tp;
|
||||
struct nxpwifi_sta_node *sta_ptr;
|
||||
|
||||
tp = (void *)tlv;
|
||||
nxpwifi_dbg(priv->adapter, EVENT,
|
||||
"uap tx_pause: %pM pause=%d, pkts=%d\n",
|
||||
tp->peermac, tp->tx_pause,
|
||||
tp->pkt_cnt);
|
||||
|
||||
if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) {
|
||||
if (tp->tx_pause)
|
||||
priv->port_open = false;
|
||||
else
|
||||
priv->port_open = true;
|
||||
} else if (is_multicast_ether_addr(tp->peermac)) {
|
||||
nxpwifi_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause);
|
||||
} else {
|
||||
rcu_read_lock();
|
||||
sta_ptr = nxpwifi_get_sta_entry(priv, tp->peermac);
|
||||
if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) {
|
||||
sta_ptr->tx_pause = tp->tx_pause;
|
||||
nxpwifi_update_ralist_tx_pause(priv, tp->peermac,
|
||||
tp->tx_pause);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static void nxpwifi_process_sta_tx_pause(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ie_types_header *tlv)
|
||||
{
|
||||
struct nxpwifi_tx_pause_tlv *tp;
|
||||
|
||||
tp = (void *)tlv;
|
||||
nxpwifi_dbg(priv->adapter, EVENT,
|
||||
"sta tx_pause: %pM pause=%d, pkts=%d\n",
|
||||
tp->peermac, tp->tx_pause,
|
||||
tp->pkt_cnt);
|
||||
|
||||
if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) {
|
||||
if (tp->tx_pause)
|
||||
priv->port_open = false;
|
||||
else
|
||||
priv->port_open = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset connection state after a firmware-triggered disconnect.
|
||||
* Clears link state, queues, RSSI/SNR and security settings,
|
||||
* saves previous SSID/BSSID for possible reassociation,
|
||||
* and notifies cfg80211.
|
||||
*/
|
||||
void nxpwifi_reset_connect_state(struct nxpwifi_private *priv, u16 reason_code,
|
||||
bool from_ap)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (!priv->media_connected)
|
||||
return;
|
||||
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: handles disconnect event\n");
|
||||
|
||||
priv->media_connected = false;
|
||||
|
||||
priv->auth_flag = 0;
|
||||
priv->auth_alg = WLAN_AUTH_NONE;
|
||||
|
||||
priv->scan_block = false;
|
||||
priv->port_open = false;
|
||||
|
||||
/* Free Tx and Rx packets, report disconnect to upper layer */
|
||||
nxpwifi_clean_txrx(priv);
|
||||
|
||||
/* Reset SNR/NF/RSSI values */
|
||||
priv->data_rssi_last = 0;
|
||||
priv->data_nf_last = 0;
|
||||
priv->data_rssi_avg = 0;
|
||||
priv->data_nf_avg = 0;
|
||||
priv->bcn_rssi_last = 0;
|
||||
priv->bcn_nf_last = 0;
|
||||
priv->bcn_rssi_avg = 0;
|
||||
priv->bcn_nf_avg = 0;
|
||||
priv->rxpd_rate = 0;
|
||||
priv->rxpd_htinfo = 0;
|
||||
priv->sec_info.wpa_enabled = false;
|
||||
priv->sec_info.wpa2_enabled = false;
|
||||
priv->wpa_ie_len = 0;
|
||||
|
||||
priv->sec_info.encryption_mode = 0;
|
||||
|
||||
/* Enable auto data rate */
|
||||
priv->is_data_rate_auto = true;
|
||||
priv->data_rate = 0;
|
||||
|
||||
priv->assoc_resp_ht_param = 0;
|
||||
priv->ht_param_present = false;
|
||||
|
||||
if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA ||
|
||||
GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) && priv->hist_data)
|
||||
nxpwifi_hist_data_reset(priv);
|
||||
|
||||
/*
|
||||
* Memorize the previous SSID and BSSID so
|
||||
* it could be used for re-assoc
|
||||
*/
|
||||
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: previous SSID=%s, SSID len=%u\n",
|
||||
priv->prev_ssid.ssid, priv->prev_ssid.ssid_len);
|
||||
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: current SSID=%s, SSID len=%u\n",
|
||||
priv->curr_bss_params.bss_descriptor.ssid.ssid,
|
||||
priv->curr_bss_params.bss_descriptor.ssid.ssid_len);
|
||||
|
||||
memcpy(&priv->prev_ssid,
|
||||
&priv->curr_bss_params.bss_descriptor.ssid,
|
||||
sizeof(struct cfg80211_ssid));
|
||||
|
||||
memcpy(priv->prev_bssid,
|
||||
priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN);
|
||||
|
||||
/* Need to erase the current SSID and BSSID info */
|
||||
memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params));
|
||||
|
||||
adapter->tx_lock_flag = false;
|
||||
adapter->pps_uapsd_mode = false;
|
||||
|
||||
if (test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags) &&
|
||||
adapter->curr_cmd)
|
||||
return;
|
||||
|
||||
priv->media_connected = false;
|
||||
nxpwifi_dbg(adapter, MSG,
|
||||
"info: successfully disconnected from %pM: reason code %d\n",
|
||||
priv->cfg_bssid, reason_code);
|
||||
|
||||
if (priv->bss_mode == NL80211_IFTYPE_STATION) {
|
||||
if (adapter->host_mlme_link_lost)
|
||||
nxpwifi_host_mlme_disconnect(adapter->priv_link_lost,
|
||||
reason_code, NULL);
|
||||
else
|
||||
cfg80211_disconnected(priv->netdev, reason_code, NULL,
|
||||
0, !from_ap, GFP_KERNEL);
|
||||
}
|
||||
eth_zero_addr(priv->cfg_bssid);
|
||||
|
||||
nxpwifi_stop_net_dev_queue(priv->netdev, adapter);
|
||||
netif_carrier_off(priv->netdev);
|
||||
|
||||
if (!ISSUPP_FIRMWARE_SUPPLICANT(priv->adapter->fw_cap_info))
|
||||
return;
|
||||
|
||||
nxpwifi_send_cmd(priv, HOST_CMD_GTK_REKEY_OFFLOAD_CFG,
|
||||
HOST_ACT_GEN_REMOVE, 0, NULL, false);
|
||||
}
|
||||
|
||||
void nxpwifi_reset_conn_state_work(struct wiphy *wiphy, struct wiphy_work *work)
|
||||
{
|
||||
struct nxpwifi_private *priv = container_of(work,
|
||||
struct nxpwifi_private,
|
||||
reset_conn_state_work);
|
||||
|
||||
nxpwifi_reset_connect_state(priv, priv->last_deauth_reason, true);
|
||||
}
|
||||
|
||||
void nxpwifi_process_multi_chan_event(struct nxpwifi_private *priv,
|
||||
struct sk_buff *event_skb)
|
||||
{
|
||||
struct nxpwifi_ie_types_multi_chan_info *chan_info;
|
||||
struct nxpwifi_ie_types_mc_group_info *grp_info;
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct nxpwifi_ie_types_header *tlv;
|
||||
u16 tlv_buf_left, tlv_type, tlv_len;
|
||||
int intf_num, bss_type, bss_num, i;
|
||||
struct nxpwifi_private *intf_priv;
|
||||
|
||||
tlv_buf_left = event_skb->len - sizeof(u32);
|
||||
chan_info = (void *)event_skb->data + sizeof(u32);
|
||||
|
||||
if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO ||
|
||||
tlv_buf_left < sizeof(struct nxpwifi_ie_types_multi_chan_info)) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"unknown TLV in chan_info event\n");
|
||||
return;
|
||||
}
|
||||
|
||||
adapter->usb_mc_status = le16_to_cpu(chan_info->status);
|
||||
nxpwifi_dbg(adapter, EVENT, "multi chan operation %s\n",
|
||||
adapter->usb_mc_status ? "started" : "over");
|
||||
|
||||
tlv_buf_left -= sizeof(struct nxpwifi_ie_types_multi_chan_info);
|
||||
tlv = (struct nxpwifi_ie_types_header *)chan_info->tlv_buffer;
|
||||
|
||||
while (tlv_buf_left >= (int)sizeof(struct nxpwifi_ie_types_header)) {
|
||||
tlv_type = le16_to_cpu(tlv->type);
|
||||
tlv_len = le16_to_cpu(tlv->len);
|
||||
if ((sizeof(struct nxpwifi_ie_types_header) + tlv_len) >
|
||||
tlv_buf_left) {
|
||||
nxpwifi_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t"
|
||||
"tlvBufLeft=%d\n", tlv_len, tlv_buf_left);
|
||||
break;
|
||||
}
|
||||
if (tlv_type != TLV_TYPE_MC_GROUP_INFO) {
|
||||
nxpwifi_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n",
|
||||
tlv_type);
|
||||
break;
|
||||
}
|
||||
|
||||
grp_info = (struct nxpwifi_ie_types_mc_group_info *)tlv;
|
||||
intf_num = grp_info->intf_num;
|
||||
for (i = 0; i < intf_num; i++) {
|
||||
bss_type = grp_info->bss_type_numlist[i] >> 4;
|
||||
bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK;
|
||||
intf_priv = nxpwifi_get_priv_by_id(adapter, bss_num,
|
||||
bss_type);
|
||||
if (!intf_priv) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"Invalid bss_type bss_num\t"
|
||||
"in multi channel event\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
tlv_buf_left -= sizeof(struct nxpwifi_ie_types_header) +
|
||||
tlv_len;
|
||||
tlv = (void *)((u8 *)tlv + tlv_len +
|
||||
sizeof(struct nxpwifi_ie_types_header));
|
||||
}
|
||||
}
|
||||
|
||||
void nxpwifi_process_tx_pause_event(struct nxpwifi_private *priv,
|
||||
struct sk_buff *event_skb)
|
||||
{
|
||||
struct nxpwifi_ie_types_header *tlv;
|
||||
u16 tlv_type, tlv_len;
|
||||
int tlv_buf_left;
|
||||
|
||||
if (!priv->media_connected) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"tx_pause event while disconnected; bss_role=%d\n",
|
||||
priv->bss_role);
|
||||
return;
|
||||
}
|
||||
|
||||
tlv_buf_left = event_skb->len - sizeof(u32);
|
||||
tlv = (void *)event_skb->data + sizeof(u32);
|
||||
|
||||
while (tlv_buf_left >= (int)sizeof(struct nxpwifi_ie_types_header)) {
|
||||
tlv_type = le16_to_cpu(tlv->type);
|
||||
tlv_len = le16_to_cpu(tlv->len);
|
||||
if ((sizeof(struct nxpwifi_ie_types_header) + tlv_len) >
|
||||
tlv_buf_left) {
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"wrong tlv: tlvLen=%d, tlvBufLeft=%d\n",
|
||||
tlv_len, tlv_buf_left);
|
||||
break;
|
||||
}
|
||||
if (tlv_type == TLV_TYPE_TX_PAUSE) {
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
|
||||
nxpwifi_process_sta_tx_pause(priv, tlv);
|
||||
else
|
||||
nxpwifi_process_uap_tx_pause(priv, tlv);
|
||||
}
|
||||
|
||||
tlv_buf_left -= sizeof(struct nxpwifi_ie_types_header) +
|
||||
tlv_len;
|
||||
tlv = (void *)((u8 *)tlv + tlv_len +
|
||||
sizeof(struct nxpwifi_ie_types_header));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle BT coexistence event. Parse TLVs and update
|
||||
* coexistence aggregation window and scan timing parameters.
|
||||
*/
|
||||
void nxpwifi_bt_coex_wlan_param_update_event(struct nxpwifi_private *priv,
|
||||
struct sk_buff *event_skb)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct nxpwifi_ie_types_header *tlv;
|
||||
struct nxpwifi_ie_types_btcoex_aggr_win_size *winsizetlv;
|
||||
struct nxpwifi_ie_types_btcoex_scan_time *scantlv;
|
||||
s32 len = event_skb->len - sizeof(u32);
|
||||
u8 *cur_ptr = event_skb->data + sizeof(u32);
|
||||
u16 tlv_type, tlv_len;
|
||||
|
||||
while (len >= sizeof(struct nxpwifi_ie_types_header)) {
|
||||
tlv = (struct nxpwifi_ie_types_header *)cur_ptr;
|
||||
tlv_len = le16_to_cpu(tlv->len);
|
||||
tlv_type = le16_to_cpu(tlv->type);
|
||||
|
||||
if ((tlv_len + sizeof(struct nxpwifi_ie_types_header)) > len)
|
||||
break;
|
||||
switch (tlv_type) {
|
||||
case TLV_BTCOEX_WL_AGGR_WINSIZE:
|
||||
winsizetlv =
|
||||
(struct nxpwifi_ie_types_btcoex_aggr_win_size *)tlv;
|
||||
adapter->coex_win_size = winsizetlv->coex_win_size;
|
||||
adapter->coex_tx_win_size =
|
||||
winsizetlv->tx_win_size;
|
||||
adapter->coex_rx_win_size =
|
||||
winsizetlv->rx_win_size;
|
||||
nxpwifi_coex_ampdu_rxwinsize(adapter);
|
||||
nxpwifi_update_ampdu_txwinsize(adapter);
|
||||
break;
|
||||
|
||||
case TLV_BTCOEX_WL_SCANTIME:
|
||||
scantlv =
|
||||
(struct nxpwifi_ie_types_btcoex_scan_time *)tlv;
|
||||
adapter->coex_scan = scantlv->coex_scan;
|
||||
adapter->coex_min_scan_time = le16_to_cpu(scantlv->min_scan_time);
|
||||
adapter->coex_max_scan_time = le16_to_cpu(scantlv->max_scan_time);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
len -= tlv_len + sizeof(struct nxpwifi_ie_types_header);
|
||||
cur_ptr += tlv_len +
|
||||
sizeof(struct nxpwifi_ie_types_header);
|
||||
}
|
||||
|
||||
nxpwifi_dbg(adapter, INFO, "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n",
|
||||
adapter->coex_scan, adapter->coex_min_scan_time,
|
||||
adapter->coex_win_size, adapter->coex_tx_win_size,
|
||||
adapter->coex_rx_win_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispatch station firmware event based on event_cause.
|
||||
* Looks up the handler in the station event table and invokes it.
|
||||
*/
|
||||
int nxpwifi_process_sta_event(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u32 eventcause = adapter->event_cause;
|
||||
int evt, ret = 0;
|
||||
|
||||
for (evt = 0; evt < ARRAY_SIZE(evt_table_sta); evt++) {
|
||||
if (eventcause == evt_table_sta[evt].event_cause) {
|
||||
if (evt_table_sta[evt].event_handler)
|
||||
ret = evt_table_sta[evt].event_handler(priv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (evt == ARRAY_SIZE(evt_table_sta))
|
||||
nxpwifi_dbg(adapter, EVENT,
|
||||
"%s: unknown event id: %#x\n",
|
||||
__func__, eventcause);
|
||||
else
|
||||
nxpwifi_dbg(adapter, EVENT,
|
||||
"%s: event id: %#x\n",
|
||||
__func__, eventcause);
|
||||
|
||||
return ret;
|
||||
}
|
||||
242
drivers/net/wireless/nxp/nxpwifi/sta_rx.c
Normal file
242
drivers/net/wireless/nxp/nxpwifi/sta_rx.c
Normal file
@@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* NXP Wireless LAN device driver: station RX data handling
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include <uapi/linux/ipv6.h>
|
||||
#include <net/ndisc.h>
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "11n_aggr.h"
|
||||
#include "11n_rxreorder.h"
|
||||
|
||||
/*
|
||||
* Drop gratuitous IPv4 ARP and IPv6 neighbour advertisements when
|
||||
* source and destination addresses are identical.
|
||||
*/
|
||||
static bool
|
||||
nxpwifi_discard_gratuitous_arp(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
const struct nxpwifi_arp_eth_header *arp;
|
||||
struct ethhdr *eth;
|
||||
struct ipv6hdr *ipv6;
|
||||
struct icmp6hdr *icmpv6;
|
||||
|
||||
eth = (struct ethhdr *)skb->data;
|
||||
switch (ntohs(eth->h_proto)) {
|
||||
case ETH_P_ARP:
|
||||
arp = (void *)(skb->data + sizeof(struct ethhdr));
|
||||
if (arp->hdr.ar_op == htons(ARPOP_REPLY) ||
|
||||
arp->hdr.ar_op == htons(ARPOP_REQUEST)) {
|
||||
if (!memcmp(arp->ar_sip, arp->ar_tip, 4))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ETH_P_IPV6:
|
||||
ipv6 = (void *)(skb->data + sizeof(struct ethhdr));
|
||||
icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) +
|
||||
sizeof(struct ipv6hdr));
|
||||
if (icmpv6->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
|
||||
if (!memcmp(&ipv6->saddr, &ipv6->daddr,
|
||||
sizeof(struct in6_addr)))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a received data packet.
|
||||
* Convert 802.2/LLC/SNAP to Ethernet II when appropriate, trim the
|
||||
* rxpd and extra headers, optionally drop gratuitous ARP/NA, cache
|
||||
* RX rate for unicast, then deliver to the stack.
|
||||
*/
|
||||
int nxpwifi_process_rx_packet(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
int ret;
|
||||
struct rx_packet_hdr *rx_pkt_hdr;
|
||||
struct rxpd *local_rx_pd;
|
||||
int hdr_chop;
|
||||
struct ethhdr *eth;
|
||||
u16 rx_pkt_off;
|
||||
u8 adj_rx_rate = 0;
|
||||
|
||||
local_rx_pd = (struct rxpd *)(skb->data);
|
||||
|
||||
rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset);
|
||||
rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off;
|
||||
|
||||
if (sizeof(rx_pkt_hdr->eth803_hdr) + sizeof(rfc1042_header) +
|
||||
rx_pkt_off > skb->len) {
|
||||
priv->stats.rx_dropped++;
|
||||
dev_kfree_skb_any(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sizeof(*rx_pkt_hdr) + rx_pkt_off <= skb->len &&
|
||||
((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
|
||||
sizeof(bridge_tunnel_header))) ||
|
||||
(!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
|
||||
sizeof(rfc1042_header)) &&
|
||||
rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_AARP) &&
|
||||
rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_IPX)))) {
|
||||
/*
|
||||
* Replace the 803 header and rfc1042 header (llc/snap) with an
|
||||
* EthernetII header, keep the src/dst and snap_type
|
||||
* (ethertype).
|
||||
* The firmware only passes up SNAP frames converting
|
||||
* all RX Data from 802.11 to 802.2/LLC/SNAP frames.
|
||||
* To create the Ethernet II, just move the src, dst address
|
||||
* right before the snap_type.
|
||||
*/
|
||||
eth = (struct ethhdr *)
|
||||
((u8 *)&rx_pkt_hdr->eth803_hdr
|
||||
+ sizeof(rx_pkt_hdr->eth803_hdr) +
|
||||
sizeof(rx_pkt_hdr->rfc1042_hdr)
|
||||
- sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
|
||||
- sizeof(rx_pkt_hdr->eth803_hdr.h_source)
|
||||
- sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
|
||||
|
||||
memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source,
|
||||
sizeof(eth->h_source));
|
||||
memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
|
||||
sizeof(eth->h_dest));
|
||||
|
||||
/*
|
||||
* Chop off the rxpd + the excess memory from the 802.2/llc/snap
|
||||
* header that was removed.
|
||||
*/
|
||||
hdr_chop = (u8 *)eth - (u8 *)local_rx_pd;
|
||||
} else {
|
||||
/* Chop off the rxpd */
|
||||
hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)local_rx_pd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chop off the leading header bytes so the it points to the start of
|
||||
* either the reconstructed EthII frame or the 802.2/llc/snap frame
|
||||
*/
|
||||
skb_pull(skb, hdr_chop);
|
||||
|
||||
if (priv->hs2_enabled &&
|
||||
nxpwifi_discard_gratuitous_arp(priv, skb)) {
|
||||
nxpwifi_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only stash RX bitrate for unicast packets. */
|
||||
if (likely(!is_multicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest))) {
|
||||
priv->rxpd_rate = local_rx_pd->rx_rate;
|
||||
priv->rxpd_htinfo = local_rx_pd->rate_info;
|
||||
}
|
||||
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA ||
|
||||
GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
|
||||
adj_rx_rate = nxpwifi_adjust_data_rate(priv,
|
||||
local_rx_pd->rx_rate,
|
||||
local_rx_pd->rate_info);
|
||||
nxpwifi_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr,
|
||||
local_rx_pd->nf);
|
||||
}
|
||||
|
||||
ret = nxpwifi_recv_packet(priv, skb);
|
||||
if (ret)
|
||||
nxpwifi_dbg(priv->adapter, ERROR,
|
||||
"recv packet failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a received buffer on the STA path.
|
||||
* Validate RxPD and lengths, handle monitor/mgmt frames, fast-path
|
||||
* non-unicast frames, and feed unicast data into 11n reorder/BA logic.
|
||||
*/
|
||||
int nxpwifi_process_sta_rx_packet(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
int ret = 0;
|
||||
struct rxpd *local_rx_pd;
|
||||
struct rx_packet_hdr *rx_pkt_hdr;
|
||||
u8 ta[ETH_ALEN];
|
||||
u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num;
|
||||
|
||||
local_rx_pd = (struct rxpd *)(skb->data);
|
||||
rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type);
|
||||
rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset);
|
||||
rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length);
|
||||
seq_num = le16_to_cpu(local_rx_pd->seq_num);
|
||||
|
||||
rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset;
|
||||
|
||||
if ((rx_pkt_offset + rx_pkt_length) > skb->len ||
|
||||
sizeof(rx_pkt_hdr->eth803_hdr) + rx_pkt_offset > skb->len) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n",
|
||||
skb->len, rx_pkt_offset, rx_pkt_length);
|
||||
priv->stats.rx_dropped++;
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (priv->adapter->enable_net_mon && rx_pkt_type == PKT_TYPE_802DOT11) {
|
||||
ret = nxpwifi_recv_packet_to_monif(priv, skb);
|
||||
if (ret)
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rx_pkt_type == PKT_TYPE_MGMT) {
|
||||
ret = nxpwifi_process_mgmt_packet(priv, skb);
|
||||
if (ret && (ret != -EINPROGRESS))
|
||||
nxpwifi_dbg(adapter, DATA, "Rx of mgmt packet failed");
|
||||
if (ret != -EINPROGRESS)
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the packet is not an unicast packet then send the packet
|
||||
* directly to os. Don't pass thru rx reordering
|
||||
*/
|
||||
if (!IS_11N_ENABLED(priv) ||
|
||||
!ether_addr_equal_unaligned(priv->curr_addr,
|
||||
rx_pkt_hdr->eth803_hdr.h_dest)) {
|
||||
nxpwifi_process_rx_packet(priv, skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (nxpwifi_queuing_ra_based(priv)) {
|
||||
memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
|
||||
} else {
|
||||
if (rx_pkt_type != PKT_TYPE_BAR &&
|
||||
local_rx_pd->priority < MAX_NUM_TID)
|
||||
priv->rx_seq[local_rx_pd->priority] = seq_num;
|
||||
memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address,
|
||||
ETH_ALEN);
|
||||
}
|
||||
|
||||
/* Reorder and send to OS */
|
||||
ret = nxpwifi_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority,
|
||||
ta, (u8)rx_pkt_type, skb);
|
||||
|
||||
if (ret || rx_pkt_type == PKT_TYPE_BAR)
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
if (ret)
|
||||
priv->stats.rx_dropped++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
190
drivers/net/wireless/nxp/nxpwifi/sta_tx.c
Normal file
190
drivers/net/wireless/nxp/nxpwifi/sta_tx.c
Normal file
@@ -0,0 +1,190 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* NXP Wireless LAN device driver: station TX data handling
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
#include "wmm.h"
|
||||
|
||||
/*
|
||||
* Fill TxPD for TX packets by inserting it before payload and setting required fields.
|
||||
*/
|
||||
void nxpwifi_process_sta_txpd(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct txpd *local_tx_pd;
|
||||
struct nxpwifi_txinfo *tx_info = NXPWIFI_SKB_TXCB(skb);
|
||||
unsigned int pad;
|
||||
u16 pkt_type, pkt_length, pkt_offset;
|
||||
int hroom = adapter->intf_hdr_len;
|
||||
u32 tx_control;
|
||||
|
||||
pkt_type = nxpwifi_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
|
||||
|
||||
pad = ((uintptr_t)skb->data - (sizeof(*local_tx_pd) + hroom)) &
|
||||
(NXPWIFI_DMA_ALIGN_SZ - 1);
|
||||
skb_push(skb, sizeof(*local_tx_pd) + pad);
|
||||
|
||||
local_tx_pd = (struct txpd *)skb->data;
|
||||
memset(local_tx_pd, 0, sizeof(struct txpd));
|
||||
local_tx_pd->bss_num = priv->bss_num;
|
||||
local_tx_pd->bss_type = priv->bss_type;
|
||||
|
||||
pkt_length = (u16)(skb->len - (sizeof(struct txpd) + pad));
|
||||
if (pkt_type == PKT_TYPE_MGMT)
|
||||
pkt_length -= NXPWIFI_MGMT_FRAME_HEADER_SIZE;
|
||||
local_tx_pd->tx_pkt_length = cpu_to_le16(pkt_length);
|
||||
|
||||
local_tx_pd->priority = (u8)skb->priority;
|
||||
local_tx_pd->pkt_delay_2ms =
|
||||
nxpwifi_wmm_compute_drv_pkt_delay(priv, skb);
|
||||
|
||||
if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS ||
|
||||
tx_info->flags & NXPWIFI_BUF_FLAG_ACTION_TX_STATUS) {
|
||||
local_tx_pd->tx_token_id = tx_info->ack_frame_id;
|
||||
local_tx_pd->flags |= NXPWIFI_TXPD_FLAGS_REQ_TX_STATUS;
|
||||
}
|
||||
|
||||
if (local_tx_pd->priority <
|
||||
ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) {
|
||||
/*
|
||||
* Set the priority specific tx_control field, setting of 0 will
|
||||
* cause the default value to be used later in this function
|
||||
*/
|
||||
tx_control =
|
||||
priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd->priority];
|
||||
local_tx_pd->tx_control = cpu_to_le32(tx_control);
|
||||
}
|
||||
|
||||
if (adapter->pps_uapsd_mode) {
|
||||
if (nxpwifi_check_last_packet_indication(priv)) {
|
||||
adapter->tx_lock_flag = true;
|
||||
local_tx_pd->flags =
|
||||
NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
/* Offset of actual data */
|
||||
pkt_offset = sizeof(struct txpd) + pad;
|
||||
if (pkt_type == PKT_TYPE_MGMT) {
|
||||
/* Set the packet type and add header for management frame */
|
||||
local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type);
|
||||
pkt_offset += NXPWIFI_MGMT_FRAME_HEADER_SIZE;
|
||||
}
|
||||
|
||||
local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset);
|
||||
|
||||
/* make space for adapter->intf_hdr_len */
|
||||
skb_push(skb, hroom);
|
||||
|
||||
if (!local_tx_pd->tx_control)
|
||||
/* TxCtrl set by user or default */
|
||||
local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
|
||||
}
|
||||
|
||||
/* Send a NULL-data frame with TxPD at highest priority. */
|
||||
int nxpwifi_send_null_packet(struct nxpwifi_private *priv, u8 flags)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct txpd *local_tx_pd;
|
||||
struct nxpwifi_tx_param tx_param;
|
||||
/* sizeof(struct txpd) + Interface specific header */
|
||||
#define NULL_PACKET_HDR 64
|
||||
u32 data_len = NULL_PACKET_HDR;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
struct nxpwifi_txinfo *tx_info = NULL;
|
||||
|
||||
if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags))
|
||||
return -EPERM;
|
||||
|
||||
if (!priv->media_connected)
|
||||
return -EPERM;
|
||||
|
||||
if (adapter->data_sent)
|
||||
return -EBUSY;
|
||||
|
||||
skb = dev_alloc_skb(data_len);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
tx_info = NXPWIFI_SKB_TXCB(skb);
|
||||
memset(tx_info, 0, sizeof(*tx_info));
|
||||
tx_info->bss_num = priv->bss_num;
|
||||
tx_info->bss_type = priv->bss_type;
|
||||
tx_info->pkt_len = data_len -
|
||||
(sizeof(struct txpd) + adapter->intf_hdr_len);
|
||||
skb_reserve(skb, sizeof(struct txpd) + adapter->intf_hdr_len);
|
||||
skb_push(skb, sizeof(struct txpd));
|
||||
|
||||
local_tx_pd = (struct txpd *)skb->data;
|
||||
local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
|
||||
local_tx_pd->flags = flags;
|
||||
local_tx_pd->priority = WMM_HIGHEST_PRIORITY;
|
||||
local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
|
||||
local_tx_pd->bss_num = priv->bss_num;
|
||||
local_tx_pd->bss_type = priv->bss_type;
|
||||
|
||||
skb_push(skb, adapter->intf_hdr_len);
|
||||
tx_param.next_pkt_len = 0;
|
||||
ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA,
|
||||
skb, &tx_param);
|
||||
|
||||
switch (ret) {
|
||||
case -EBUSY:
|
||||
dev_kfree_skb_any(skb);
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"%s: host_to_card failed: ret=%d\n",
|
||||
__func__, ret);
|
||||
adapter->dbg.num_tx_host_to_card_failure++;
|
||||
break;
|
||||
case 0:
|
||||
dev_kfree_skb_any(skb);
|
||||
nxpwifi_dbg(adapter, DATA,
|
||||
"data: %s: host_to_card succeeded\n",
|
||||
__func__);
|
||||
adapter->tx_lock_flag = true;
|
||||
break;
|
||||
case -EINPROGRESS:
|
||||
adapter->tx_lock_flag = true;
|
||||
break;
|
||||
default:
|
||||
dev_kfree_skb_any(skb);
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"%s: host_to_card failed: ret=%d\n",
|
||||
__func__, ret);
|
||||
adapter->dbg.num_tx_host_to_card_failure++;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check whether a last‑packet indication needs to be sent. */
|
||||
u8 nxpwifi_check_last_packet_indication(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u8 ret = false;
|
||||
|
||||
if (!adapter->sleep_period.period)
|
||||
return ret;
|
||||
if (nxpwifi_wmm_lists_empty(adapter))
|
||||
ret = true;
|
||||
|
||||
if (ret && !adapter->cmd_sent && !adapter->curr_cmd &&
|
||||
!nxpwifi_is_command_pending(adapter)) {
|
||||
adapter->delay_null_pkt = false;
|
||||
ret = true;
|
||||
} else {
|
||||
ret = false;
|
||||
adapter->delay_null_pkt = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
352
drivers/net/wireless/nxp/nxpwifi/txrx.c
Normal file
352
drivers/net/wireless/nxp/nxpwifi/txrx.c
Normal file
@@ -0,0 +1,352 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* NXP Wireless LAN device driver: generic TX/RX data handling
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
#include "fw.h"
|
||||
#include "main.h"
|
||||
#include "wmm.h"
|
||||
|
||||
/*
|
||||
* Parse the RxPD, select the target interface, and dispatch the packet for
|
||||
* handling.
|
||||
*/
|
||||
int nxpwifi_handle_rx_packet(struct nxpwifi_adapter *adapter,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_private *priv =
|
||||
nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
|
||||
struct rxpd *local_rx_pd;
|
||||
struct nxpwifi_rxinfo *rx_info = NXPWIFI_SKB_RXCB(skb);
|
||||
int ret;
|
||||
|
||||
local_rx_pd = (struct rxpd *)(skb->data);
|
||||
/* Get the BSS number from rxpd, get corresponding priv */
|
||||
priv = nxpwifi_get_priv_by_id(adapter, local_rx_pd->bss_num &
|
||||
BSS_NUM_MASK, local_rx_pd->bss_type);
|
||||
if (!priv)
|
||||
priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
|
||||
|
||||
if (!priv) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"data: priv not found. Drop RX packet\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nxpwifi_dbg_dump(adapter, DAT_D, "rx pkt:", skb->data,
|
||||
min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN));
|
||||
|
||||
memset(rx_info, 0, sizeof(*rx_info));
|
||||
rx_info->bss_num = priv->bss_num;
|
||||
rx_info->bss_type = priv->bss_type;
|
||||
|
||||
if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
|
||||
ret = nxpwifi_process_uap_rx_packet(priv, skb);
|
||||
else
|
||||
ret = nxpwifi_process_sta_rx_packet(priv, skb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nxpwifi_handle_rx_packet);
|
||||
|
||||
/*
|
||||
* Add TxPD, validate, send the packet to firmware, then run completion
|
||||
* callback.
|
||||
*/
|
||||
int nxpwifi_process_tx(struct nxpwifi_private *priv, struct sk_buff *skb,
|
||||
struct nxpwifi_tx_param *tx_param)
|
||||
{
|
||||
int hroom, ret;
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct txpd *local_tx_pd = NULL;
|
||||
struct nxpwifi_sta_node *dest_node;
|
||||
struct ethhdr *hdr = (void *)skb->data;
|
||||
|
||||
if (unlikely(!skb->len ||
|
||||
skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hroom = adapter->intf_hdr_len;
|
||||
|
||||
if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) {
|
||||
rcu_read_lock();
|
||||
dest_node = nxpwifi_get_sta_entry(priv, hdr->h_dest);
|
||||
if (dest_node) {
|
||||
dest_node->stats.tx_bytes += skb->len;
|
||||
dest_node->stats.tx_packets++;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
nxpwifi_process_uap_txpd(priv, skb);
|
||||
} else {
|
||||
nxpwifi_process_sta_txpd(priv, skb);
|
||||
}
|
||||
|
||||
if ((adapter->data_sent || adapter->tx_lock_flag)) {
|
||||
skb_queue_tail(&adapter->tx_data_q, skb);
|
||||
atomic_inc(&adapter->tx_queued);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
|
||||
local_tx_pd = (struct txpd *)(skb->data + hroom);
|
||||
ret = adapter->if_ops.host_to_card(adapter,
|
||||
NXPWIFI_TYPE_DATA,
|
||||
skb, tx_param);
|
||||
nxpwifi_dbg_dump(adapter, DAT_D, "tx pkt:", skb->data,
|
||||
min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN));
|
||||
|
||||
out:
|
||||
switch (ret) {
|
||||
case -ENOSR:
|
||||
nxpwifi_dbg(adapter, DATA, "data: -ENOSR is returned\n");
|
||||
break;
|
||||
case -EBUSY:
|
||||
if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) &&
|
||||
adapter->pps_uapsd_mode && adapter->tx_lock_flag) {
|
||||
priv->adapter->tx_lock_flag = false;
|
||||
if (local_tx_pd)
|
||||
local_tx_pd->flags = 0;
|
||||
}
|
||||
nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
|
||||
break;
|
||||
case -EINPROGRESS:
|
||||
break;
|
||||
case -EINVAL:
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"malformed skb (length: %u, headroom: %u)\n",
|
||||
skb->len, skb_headroom(skb));
|
||||
fallthrough;
|
||||
case 0:
|
||||
nxpwifi_write_data_complete(adapter, skb, 0, ret);
|
||||
break;
|
||||
default:
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"nxpwifi_write_data_async failed: 0x%X\n",
|
||||
ret);
|
||||
adapter->dbg.num_tx_host_to_card_failure++;
|
||||
nxpwifi_write_data_complete(adapter, skb, 0, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nxpwifi_host_to_card(struct nxpwifi_adapter *adapter,
|
||||
struct sk_buff *skb,
|
||||
struct nxpwifi_tx_param *tx_param)
|
||||
{
|
||||
struct txpd *local_tx_pd = NULL;
|
||||
u8 *head_ptr = skb->data;
|
||||
int ret = 0;
|
||||
struct nxpwifi_private *priv;
|
||||
struct nxpwifi_txinfo *tx_info;
|
||||
|
||||
tx_info = NXPWIFI_SKB_TXCB(skb);
|
||||
priv = nxpwifi_get_priv_by_id(adapter, tx_info->bss_num,
|
||||
tx_info->bss_type);
|
||||
if (!priv) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"data: priv not found. Drop TX packet\n");
|
||||
adapter->dbg.num_tx_host_to_card_failure++;
|
||||
nxpwifi_write_data_complete(adapter, skb, 0, 0);
|
||||
return ret;
|
||||
}
|
||||
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
|
||||
local_tx_pd = (struct txpd *)(head_ptr + adapter->intf_hdr_len);
|
||||
|
||||
ret = adapter->if_ops.host_to_card(adapter,
|
||||
NXPWIFI_TYPE_DATA,
|
||||
skb, tx_param);
|
||||
|
||||
switch (ret) {
|
||||
case -ENOSR:
|
||||
nxpwifi_dbg(adapter, ERROR, "data: -ENOSR is returned\n");
|
||||
break;
|
||||
case -EBUSY:
|
||||
if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) &&
|
||||
adapter->pps_uapsd_mode &&
|
||||
adapter->tx_lock_flag) {
|
||||
priv->adapter->tx_lock_flag = false;
|
||||
if (local_tx_pd)
|
||||
local_tx_pd->flags = 0;
|
||||
}
|
||||
skb_queue_head(&adapter->tx_data_q, skb);
|
||||
if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT)
|
||||
atomic_add(tx_info->aggr_num, &adapter->tx_queued);
|
||||
else
|
||||
atomic_inc(&adapter->tx_queued);
|
||||
nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
|
||||
break;
|
||||
case -EINPROGRESS:
|
||||
break;
|
||||
case 0:
|
||||
nxpwifi_write_data_complete(adapter, skb, 0, ret);
|
||||
break;
|
||||
default:
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"nxpwifi_write_data_async failed: 0x%X\n", ret);
|
||||
adapter->dbg.num_tx_host_to_card_failure++;
|
||||
nxpwifi_write_data_complete(adapter, skb, 0, ret);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_dequeue_tx_queue(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
struct sk_buff *skb, *skb_next;
|
||||
struct nxpwifi_txinfo *tx_info;
|
||||
struct nxpwifi_tx_param tx_param;
|
||||
|
||||
skb = skb_dequeue(&adapter->tx_data_q);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
tx_info = NXPWIFI_SKB_TXCB(skb);
|
||||
if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT)
|
||||
atomic_sub(tx_info->aggr_num, &adapter->tx_queued);
|
||||
else
|
||||
atomic_dec(&adapter->tx_queued);
|
||||
|
||||
if (!skb_queue_empty(&adapter->tx_data_q))
|
||||
skb_next = skb_peek(&adapter->tx_data_q);
|
||||
else
|
||||
skb_next = NULL;
|
||||
tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0);
|
||||
if (!tx_param.next_pkt_len) {
|
||||
if (!nxpwifi_wmm_lists_empty(adapter))
|
||||
tx_param.next_pkt_len = 1;
|
||||
}
|
||||
return nxpwifi_host_to_card(adapter, skb, &tx_param);
|
||||
}
|
||||
|
||||
void
|
||||
nxpwifi_process_tx_queue(struct nxpwifi_adapter *adapter)
|
||||
{
|
||||
do {
|
||||
if (adapter->data_sent || adapter->tx_lock_flag)
|
||||
break;
|
||||
if (nxpwifi_dequeue_tx_queue(adapter))
|
||||
break;
|
||||
} while (!skb_queue_empty(&adapter->tx_data_q));
|
||||
}
|
||||
|
||||
/*
|
||||
* Packet send completion callback handler.
|
||||
*
|
||||
* It either frees the buffer directly or forwards it to another
|
||||
* completion callback which checks conditions, updates statistics,
|
||||
* wakes up stalled traffic queue if required, and then frees the buffer.
|
||||
*/
|
||||
int nxpwifi_write_data_complete(struct nxpwifi_adapter *adapter,
|
||||
struct sk_buff *skb, int aggr, int status)
|
||||
{
|
||||
struct nxpwifi_private *priv;
|
||||
struct nxpwifi_txinfo *tx_info;
|
||||
struct netdev_queue *txq;
|
||||
int index;
|
||||
|
||||
if (!skb)
|
||||
return 0;
|
||||
|
||||
tx_info = NXPWIFI_SKB_TXCB(skb);
|
||||
priv = nxpwifi_get_priv_by_id(adapter, tx_info->bss_num,
|
||||
tx_info->bss_type);
|
||||
if (!priv)
|
||||
goto done;
|
||||
|
||||
nxpwifi_set_trans_start(priv->netdev);
|
||||
|
||||
if (tx_info->flags & NXPWIFI_BUF_FLAG_BRIDGED_PKT)
|
||||
atomic_dec_return(&adapter->pending_bridged_pkts);
|
||||
|
||||
if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT)
|
||||
goto done;
|
||||
|
||||
if (!status) {
|
||||
priv->stats.tx_packets++;
|
||||
priv->stats.tx_bytes += tx_info->pkt_len;
|
||||
if (priv->tx_timeout_cnt)
|
||||
priv->tx_timeout_cnt = 0;
|
||||
} else {
|
||||
priv->stats.tx_errors++;
|
||||
}
|
||||
|
||||
if (aggr)
|
||||
/* For skb_aggr, do not wake up tx queue */
|
||||
goto done;
|
||||
|
||||
atomic_dec(&adapter->tx_pending);
|
||||
|
||||
index = nxpwifi_1d_to_wmm_queue[skb->priority];
|
||||
if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) {
|
||||
txq = netdev_get_tx_queue(priv->netdev, index);
|
||||
if (netif_tx_queue_stopped(txq)) {
|
||||
netif_tx_wake_queue(txq);
|
||||
nxpwifi_dbg(adapter, DATA, "wake queue: %d\n", index);
|
||||
}
|
||||
}
|
||||
done:
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nxpwifi_write_data_complete);
|
||||
|
||||
void nxpwifi_parse_tx_status_event(struct nxpwifi_private *priv,
|
||||
void *event_body)
|
||||
{
|
||||
struct tx_status_event *tx_status = (void *)priv->adapter->event_body;
|
||||
struct sk_buff *ack_skb;
|
||||
struct nxpwifi_txinfo *tx_info;
|
||||
|
||||
if (!tx_status->tx_token_id)
|
||||
return;
|
||||
|
||||
spin_lock_bh(&priv->ack_status_lock);
|
||||
ack_skb = xa_erase(&priv->ack_status_frames, tx_status->tx_token_id);
|
||||
spin_unlock_bh(&priv->ack_status_lock);
|
||||
|
||||
if (ack_skb) {
|
||||
tx_info = NXPWIFI_SKB_TXCB(ack_skb);
|
||||
|
||||
if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS) {
|
||||
/* consumes ack_skb */
|
||||
skb_complete_wifi_ack(ack_skb, !tx_status->status);
|
||||
} else {
|
||||
/* Remove broadcast address which was added by driver */
|
||||
memmove(ack_skb->data +
|
||||
sizeof(struct ieee80211_hdr_3addr) +
|
||||
NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16),
|
||||
ack_skb->data +
|
||||
sizeof(struct ieee80211_hdr_3addr) +
|
||||
NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16) +
|
||||
ETH_ALEN, ack_skb->len -
|
||||
(sizeof(struct ieee80211_hdr_3addr) +
|
||||
NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16) +
|
||||
ETH_ALEN));
|
||||
ack_skb->len = ack_skb->len - ETH_ALEN;
|
||||
/*
|
||||
* Remove driver's proprietary header including 2 bytes
|
||||
* of packet length and pass actual management frame buffer
|
||||
* to cfg80211.
|
||||
*/
|
||||
cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie,
|
||||
ack_skb->data +
|
||||
NXPWIFI_MGMT_FRAME_HEADER_SIZE +
|
||||
sizeof(u16), ack_skb->len -
|
||||
(NXPWIFI_MGMT_FRAME_HEADER_SIZE
|
||||
+ sizeof(u16)),
|
||||
!tx_status->status, GFP_ATOMIC);
|
||||
dev_kfree_skb_any(ack_skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
1256
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c
Normal file
1256
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c
Normal file
File diff suppressed because it is too large
Load Diff
488
drivers/net/wireless/nxp/nxpwifi/uap_event.c
Normal file
488
drivers/net/wireless/nxp/nxpwifi/uap_event.c
Normal file
@@ -0,0 +1,488 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* NXP Wireless LAN device driver: AP event handling
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "main.h"
|
||||
#include "cmdevt.h"
|
||||
#include "11n.h"
|
||||
|
||||
#define NXPWIFI_BSS_START_EVT_FIX_SIZE 12
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_ps_awake(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (!adapter->pps_uapsd_mode &&
|
||||
priv->media_connected && adapter->sleep_period.period) {
|
||||
adapter->pps_uapsd_mode = true;
|
||||
nxpwifi_dbg(adapter, EVENT,
|
||||
"event: PPS/UAPSD mode activated\n");
|
||||
}
|
||||
adapter->tx_lock_flag = false;
|
||||
if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) {
|
||||
if (nxpwifi_check_last_packet_indication(priv)) {
|
||||
if (adapter->data_sent) {
|
||||
adapter->ps_state = PS_STATE_AWAKE;
|
||||
adapter->pm_wakeup_card_req = false;
|
||||
adapter->pm_wakeup_fw_try = false;
|
||||
} else {
|
||||
if (!nxpwifi_send_null_packet
|
||||
(priv,
|
||||
NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET |
|
||||
NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET))
|
||||
adapter->ps_state = PS_STATE_SLEEP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
adapter->ps_state = PS_STATE_AWAKE;
|
||||
adapter->pm_wakeup_card_req = false;
|
||||
adapter->pm_wakeup_fw_try = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_ps_sleep(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
adapter->ps_state = PS_STATE_PRE_SLEEP;
|
||||
nxpwifi_check_ps_cond(adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_sta_deauth(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u8 *deauth_mac;
|
||||
|
||||
deauth_mac = adapter->event_body +
|
||||
NXPWIFI_UAP_EVENT_EXTRA_HEADER;
|
||||
cfg80211_del_sta(priv->netdev->ieee80211_ptr, deauth_mac, GFP_KERNEL);
|
||||
|
||||
if (priv->ap_11n_enabled) {
|
||||
nxpwifi_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac);
|
||||
nxpwifi_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac);
|
||||
}
|
||||
nxpwifi_wmm_del_peer_ra_list(priv, deauth_mac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_sta_assoc(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct station_info *sinfo;
|
||||
struct nxpwifi_assoc_event *event;
|
||||
struct nxpwifi_sta_node *node;
|
||||
int len, i;
|
||||
|
||||
sinfo = kzalloc_obj(*sinfo, GFP_KERNEL);
|
||||
if (!sinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
event = (struct nxpwifi_assoc_event *)
|
||||
(adapter->event_body + NXPWIFI_UAP_EVENT_EXTRA_HEADER);
|
||||
if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) {
|
||||
len = -1;
|
||||
|
||||
if (ieee80211_is_assoc_req(event->frame_control))
|
||||
len = 0;
|
||||
else if (ieee80211_is_reassoc_req(event->frame_control))
|
||||
/*
|
||||
* There will be ETH_ALEN bytes of
|
||||
* current_ap_addr before the re-assoc ies.
|
||||
*/
|
||||
len = ETH_ALEN;
|
||||
|
||||
if (len != -1) {
|
||||
sinfo->assoc_req_ies = &event->data[len];
|
||||
len = (u8 *)sinfo->assoc_req_ies -
|
||||
(u8 *)&event->frame_control;
|
||||
sinfo->assoc_req_ies_len =
|
||||
le16_to_cpu(event->len) - (u16)len;
|
||||
}
|
||||
}
|
||||
cfg80211_new_sta(priv->netdev->ieee80211_ptr, event->sta_addr, sinfo,
|
||||
GFP_KERNEL);
|
||||
|
||||
node = nxpwifi_add_sta_entry(priv, event->sta_addr);
|
||||
if (!node) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"could not create station entry!\n");
|
||||
kfree(sinfo);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (!priv->ap_11n_enabled) {
|
||||
kfree(sinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nxpwifi_set_sta_ht_cap(priv, sinfo->assoc_req_ies,
|
||||
sinfo->assoc_req_ies_len, node);
|
||||
|
||||
for (i = 0; i < MAX_NUM_TID; i++) {
|
||||
if (node->is_11n_enabled || node->is_11ax_enabled)
|
||||
node->ampdu_sta[i] =
|
||||
priv->aggr_prio_tbl[i].ampdu_user;
|
||||
else
|
||||
node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
|
||||
}
|
||||
memset(node->rx_seq, 0xff, sizeof(node->rx_seq));
|
||||
kfree(sinfo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_check_uap_capabilities(struct nxpwifi_private *priv,
|
||||
struct sk_buff *event)
|
||||
{
|
||||
int evt_len;
|
||||
u8 *curr;
|
||||
u16 tlv_len;
|
||||
struct nxpwifi_ie_types_data *tlv_hdr;
|
||||
struct ieee80211_wmm_param_ie *wmm_param_ie = NULL;
|
||||
int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK;
|
||||
|
||||
priv->wmm_enabled = false;
|
||||
skb_pull(event, NXPWIFI_BSS_START_EVT_FIX_SIZE);
|
||||
evt_len = event->len;
|
||||
curr = event->data;
|
||||
|
||||
nxpwifi_dbg_dump(priv->adapter, EVT_D, "uap capabilities:",
|
||||
event->data, event->len);
|
||||
|
||||
skb_push(event, NXPWIFI_BSS_START_EVT_FIX_SIZE);
|
||||
|
||||
while ((evt_len >= sizeof(tlv_hdr->header))) {
|
||||
tlv_hdr = (struct nxpwifi_ie_types_data *)curr;
|
||||
tlv_len = le16_to_cpu(tlv_hdr->header.len);
|
||||
|
||||
if (evt_len < tlv_len + sizeof(tlv_hdr->header))
|
||||
break;
|
||||
|
||||
switch (le16_to_cpu(tlv_hdr->header.type)) {
|
||||
case WLAN_EID_HT_CAPABILITY:
|
||||
priv->ap_11n_enabled = true;
|
||||
break;
|
||||
|
||||
case WLAN_EID_VHT_CAPABILITY:
|
||||
priv->ap_11ac_enabled = true;
|
||||
break;
|
||||
|
||||
case WLAN_EID_VENDOR_SPECIFIC:
|
||||
/*
|
||||
* Point the regular IEEE element 2 bytes into the NXP element
|
||||
* and setup the IEEE element type and length byte fields
|
||||
*/
|
||||
wmm_param_ie = (void *)(curr + 2);
|
||||
wmm_param_ie->len = (u8)tlv_len;
|
||||
wmm_param_ie->element_id =
|
||||
WLAN_EID_VENDOR_SPECIFIC;
|
||||
nxpwifi_dbg(priv->adapter, EVENT,
|
||||
"info: check uap capabilities:\t"
|
||||
"wmm parameter set count: %d\n",
|
||||
wmm_param_ie->qos_info & mask);
|
||||
|
||||
nxpwifi_wmm_setup_ac_downgrade(priv);
|
||||
priv->wmm_enabled = true;
|
||||
nxpwifi_wmm_setup_queue_priorities(priv, wmm_param_ie);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
curr += (tlv_len + sizeof(tlv_hdr->header));
|
||||
evt_len -= (tlv_len + sizeof(tlv_hdr->header));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_bss_start(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
priv->port_open = false;
|
||||
eth_hw_addr_set(priv->netdev, adapter->event_body + 2);
|
||||
if (priv->hist_data)
|
||||
nxpwifi_hist_data_reset(priv);
|
||||
return nxpwifi_check_uap_capabilities(priv, adapter->event_skb);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_addba(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (priv->media_connected)
|
||||
nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_RSP,
|
||||
HOST_ACT_GEN_SET, 0,
|
||||
adapter->event_body, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_delba(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
if (priv->media_connected)
|
||||
nxpwifi_11n_delete_ba_stream(priv, adapter->event_body);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_ba_stream_timeout(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct host_cmd_ds_11n_batimeout *ba_timeout;
|
||||
|
||||
if (priv->media_connected) {
|
||||
ba_timeout = (void *)adapter->event_body;
|
||||
nxpwifi_11n_ba_stream_timeout(priv, ba_timeout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_amsdu_aggr_ctrl(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u16 ctrl;
|
||||
|
||||
ctrl = get_unaligned_le16(adapter->event_body);
|
||||
nxpwifi_dbg(adapter, EVENT,
|
||||
"event: AMSDU_AGGR_CTRL %d\n", ctrl);
|
||||
|
||||
if (priv->media_connected) {
|
||||
adapter->tx_buf_size =
|
||||
min_t(u16, adapter->curr_tx_buf_size, ctrl);
|
||||
nxpwifi_dbg(adapter, EVENT,
|
||||
"event: tx_buf_size %d\n",
|
||||
adapter->tx_buf_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_bss_idle(struct nxpwifi_private *priv)
|
||||
{
|
||||
priv->media_connected = false;
|
||||
priv->port_open = false;
|
||||
nxpwifi_clean_txrx(priv);
|
||||
nxpwifi_del_all_sta_list(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_bss_active(struct nxpwifi_private *priv)
|
||||
{
|
||||
priv->media_connected = true;
|
||||
priv->port_open = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_mic_countermeasures(struct nxpwifi_private *priv)
|
||||
{
|
||||
/* For future development */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_radar_detected(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
return nxpwifi_11h_handle_radar_detected(priv, adapter->event_skb);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_channel_report_rdy(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
return nxpwifi_11h_handle_chanrpt_ready(priv, adapter->event_skb);
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_tx_data_pause(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_process_tx_pause_event(priv, adapter->event_skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_ext_scan_report(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
void *buf = adapter->event_skb->data;
|
||||
int ret = 0;
|
||||
|
||||
if (adapter->ext_scan)
|
||||
ret = nxpwifi_handle_event_ext_scan_report(priv, buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_rxba_sync(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_11n_rxba_sync_event(priv, adapter->event_body,
|
||||
adapter->event_skb->len -
|
||||
sizeof(adapter->event_cause));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_remain_on_chan_expired(struct nxpwifi_private *priv)
|
||||
{
|
||||
cfg80211_remain_on_channel_expired(&priv->wdev,
|
||||
priv->roc_cfg.cookie,
|
||||
&priv->roc_cfg.chan,
|
||||
GFP_ATOMIC);
|
||||
memset(&priv->roc_cfg, 0x00, sizeof(struct nxpwifi_roc_cfg));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_multi_chan_info(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_process_multi_chan_event(priv, adapter->event_skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_tx_status_report(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_parse_tx_status_event(priv, adapter->event_body);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_bt_coex_wlan_para_change(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
nxpwifi_bt_coex_wlan_param_update_event(priv, adapter->event_skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nxpwifi_uap_event_vdll_ind(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
return nxpwifi_process_vdll_event(priv, adapter->event_skb);
|
||||
}
|
||||
|
||||
static const struct nxpwifi_evt_entry evt_table_uap[] = {
|
||||
{.event_cause = EVENT_PS_AWAKE,
|
||||
.event_handler = nxpwifi_uap_event_ps_awake},
|
||||
{.event_cause = EVENT_PS_SLEEP,
|
||||
.event_handler = nxpwifi_uap_event_ps_sleep},
|
||||
{.event_cause = EVENT_UAP_STA_DEAUTH,
|
||||
.event_handler = nxpwifi_uap_event_sta_deauth},
|
||||
{.event_cause = EVENT_UAP_STA_ASSOC,
|
||||
.event_handler = nxpwifi_uap_event_sta_assoc},
|
||||
{.event_cause = EVENT_UAP_BSS_START,
|
||||
.event_handler = nxpwifi_uap_event_bss_start},
|
||||
{.event_cause = EVENT_ADDBA,
|
||||
.event_handler = nxpwifi_uap_event_addba},
|
||||
{.event_cause = EVENT_DELBA,
|
||||
.event_handler = nxpwifi_uap_event_delba},
|
||||
{.event_cause = EVENT_BA_STREAM_TIEMOUT,
|
||||
.event_handler = nxpwifi_uap_event_ba_stream_timeout},
|
||||
{.event_cause = EVENT_AMSDU_AGGR_CTRL,
|
||||
.event_handler = nxpwifi_uap_event_amsdu_aggr_ctrl},
|
||||
{.event_cause = EVENT_UAP_BSS_IDLE,
|
||||
.event_handler = nxpwifi_uap_event_bss_idle},
|
||||
{.event_cause = EVENT_UAP_BSS_ACTIVE,
|
||||
.event_handler = nxpwifi_uap_event_bss_active},
|
||||
{.event_cause = EVENT_UAP_MIC_COUNTERMEASURES,
|
||||
.event_handler = nxpwifi_uap_event_mic_countermeasures},
|
||||
{.event_cause = EVENT_RADAR_DETECTED,
|
||||
.event_handler = nxpwifi_uap_event_radar_detected},
|
||||
{.event_cause = EVENT_CHANNEL_REPORT_RDY,
|
||||
.event_handler = nxpwifi_uap_event_channel_report_rdy},
|
||||
{.event_cause = EVENT_TX_DATA_PAUSE,
|
||||
.event_handler = nxpwifi_uap_event_tx_data_pause},
|
||||
{.event_cause = EVENT_EXT_SCAN_REPORT,
|
||||
.event_handler = nxpwifi_uap_event_ext_scan_report},
|
||||
{.event_cause = EVENT_RXBA_SYNC,
|
||||
.event_handler = nxpwifi_uap_event_rxba_sync},
|
||||
{.event_cause = EVENT_REMAIN_ON_CHAN_EXPIRED,
|
||||
.event_handler = nxpwifi_uap_event_remain_on_chan_expired},
|
||||
{.event_cause = EVENT_MULTI_CHAN_INFO,
|
||||
.event_handler = nxpwifi_uap_event_multi_chan_info},
|
||||
{.event_cause = EVENT_TX_STATUS_REPORT,
|
||||
.event_handler = nxpwifi_uap_event_tx_status_report},
|
||||
{.event_cause = EVENT_BT_COEX_WLAN_PARA_CHANGE,
|
||||
.event_handler = nxpwifi_uap_event_bt_coex_wlan_para_change},
|
||||
{.event_cause = EVENT_VDLL_IND,
|
||||
.event_handler = nxpwifi_uap_event_vdll_ind},
|
||||
};
|
||||
|
||||
/* Handle AP‑interface events by dispatching them to event‑specific routines. */
|
||||
int nxpwifi_process_uap_event(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
u32 eventcause = adapter->event_cause;
|
||||
int evt, ret = 0;
|
||||
|
||||
for (evt = 0; evt < ARRAY_SIZE(evt_table_uap); evt++) {
|
||||
if (eventcause == evt_table_uap[evt].event_cause) {
|
||||
if (evt_table_uap[evt].event_handler)
|
||||
ret = evt_table_uap[evt].event_handler(priv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (evt == ARRAY_SIZE(evt_table_uap))
|
||||
nxpwifi_dbg(adapter, EVENT,
|
||||
"%s: unknown event id: %#x\n",
|
||||
__func__, eventcause);
|
||||
else
|
||||
nxpwifi_dbg(adapter, EVENT,
|
||||
"%s: event id: %#x\n",
|
||||
__func__, eventcause);
|
||||
|
||||
return ret;
|
||||
}
|
||||
478
drivers/net/wireless/nxp/nxpwifi/uap_txrx.c
Normal file
478
drivers/net/wireless/nxp/nxpwifi/uap_txrx.c
Normal file
@@ -0,0 +1,478 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* NXP Wireless LAN device driver: AP TX and RX data handling
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#include "cfg.h"
|
||||
#include "main.h"
|
||||
#include "wmm.h"
|
||||
#include "11n_aggr.h"
|
||||
#include "11n_rxreorder.h"
|
||||
|
||||
/*
|
||||
* Drop bridged pkts from RA list until pending <= low threshold; return true if
|
||||
* any.
|
||||
*/
|
||||
static bool
|
||||
nxpwifi_uap_del_tx_pkts_in_ralist(struct nxpwifi_private *priv,
|
||||
struct list_head *ra_list_head,
|
||||
int tid)
|
||||
{
|
||||
struct nxpwifi_ra_list_tbl *ra_list;
|
||||
struct sk_buff *skb, *tmp;
|
||||
bool pkt_deleted = false;
|
||||
struct nxpwifi_txinfo *tx_info;
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
|
||||
list_for_each_entry(ra_list, ra_list_head, list) {
|
||||
if (skb_queue_empty(&ra_list->skb_head))
|
||||
continue;
|
||||
|
||||
skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
|
||||
tx_info = NXPWIFI_SKB_TXCB(skb);
|
||||
if (tx_info->flags & NXPWIFI_BUF_FLAG_BRIDGED_PKT) {
|
||||
__skb_unlink(skb, &ra_list->skb_head);
|
||||
nxpwifi_write_data_complete(adapter, skb, 0,
|
||||
-1);
|
||||
if (ra_list->tx_paused)
|
||||
priv->wmm.pkts_paused[tid]--;
|
||||
else
|
||||
atomic_dec(&priv->wmm.tx_pkts_queued);
|
||||
pkt_deleted = true;
|
||||
}
|
||||
if ((atomic_read(&adapter->pending_bridged_pkts) <=
|
||||
NXPWIFI_BRIDGED_PKTS_THR_LOW))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pkt_deleted;
|
||||
}
|
||||
|
||||
/* Delete bridged pkts from one RA list; rotate index to keep fairness. */
|
||||
static void nxpwifi_uap_cleanup_tx_queues(struct nxpwifi_private *priv)
|
||||
{
|
||||
struct list_head *ra_list;
|
||||
int i;
|
||||
|
||||
spin_lock_bh(&priv->wmm.ra_list_spinlock);
|
||||
|
||||
for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) {
|
||||
if (priv->del_list_idx == MAX_NUM_TID)
|
||||
priv->del_list_idx = 0;
|
||||
ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
|
||||
if (nxpwifi_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) {
|
||||
priv->del_list_idx++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_bh(&priv->wmm.ra_list_spinlock);
|
||||
}
|
||||
|
||||
static void
|
||||
nxpwifi_uap_queue_bridged_pkt(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct uap_rxpd *uap_rx_pd;
|
||||
struct rx_packet_hdr *rx_pkt_hdr;
|
||||
struct sk_buff *new_skb;
|
||||
struct nxpwifi_txinfo *tx_info;
|
||||
int hdr_chop;
|
||||
struct ethhdr *p_ethhdr;
|
||||
struct nxpwifi_sta_node *src_node;
|
||||
int index;
|
||||
|
||||
uap_rx_pd = (struct uap_rxpd *)(skb->data);
|
||||
rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
|
||||
|
||||
if ((atomic_read(&adapter->pending_bridged_pkts) >=
|
||||
NXPWIFI_BRIDGED_PKTS_THR_HIGH)) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"Tx: Bridge packet limit reached. Drop packet!\n");
|
||||
kfree_skb(skb);
|
||||
nxpwifi_uap_cleanup_tx_queues(priv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sizeof(*rx_pkt_hdr) +
|
||||
le16_to_cpu(uap_rx_pd->rx_pkt_offset) > skb->len) {
|
||||
priv->stats.rx_dropped++;
|
||||
dev_kfree_skb_any(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
|
||||
sizeof(bridge_tunnel_header))) ||
|
||||
(!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
|
||||
sizeof(rfc1042_header)) &&
|
||||
rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_AARP) &&
|
||||
rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_IPX))) {
|
||||
/*
|
||||
* Replace the 803 header and rfc1042 header (llc/snap) with
|
||||
* an Ethernet II header, keep the src/dst and snap_type
|
||||
* (ethertype).
|
||||
*
|
||||
* The firmware only passes up SNAP frames converting all RX
|
||||
* data from 802.11 to 802.2/LLC/SNAP frames.
|
||||
*
|
||||
* To create the Ethernet II, just move the src, dst address
|
||||
* right before the snap_type.
|
||||
*/
|
||||
p_ethhdr = (struct ethhdr *)
|
||||
((u8 *)(&rx_pkt_hdr->eth803_hdr)
|
||||
+ sizeof(rx_pkt_hdr->eth803_hdr)
|
||||
+ sizeof(rx_pkt_hdr->rfc1042_hdr)
|
||||
- sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
|
||||
- sizeof(rx_pkt_hdr->eth803_hdr.h_source)
|
||||
- sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
|
||||
memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source,
|
||||
sizeof(p_ethhdr->h_source));
|
||||
memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
|
||||
sizeof(p_ethhdr->h_dest));
|
||||
/*
|
||||
* Chop off the rxpd + the excess memory from
|
||||
* 802.2/llc/snap header that was removed.
|
||||
*/
|
||||
hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd;
|
||||
} else {
|
||||
/* Chop off the rxpd */
|
||||
hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chop off the leading header bytes so that it points
|
||||
* to the start of either the reconstructed EthII frame
|
||||
* or the 802.2/llc/snap frame.
|
||||
*/
|
||||
skb_pull(skb, hdr_chop);
|
||||
|
||||
if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"data: Tx: insufficient skb headroom %d\n",
|
||||
skb_headroom(skb));
|
||||
/* Insufficient skb headroom - allocate a new skb */
|
||||
new_skb =
|
||||
skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN);
|
||||
if (unlikely(!new_skb)) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"Tx: cannot allocate new_skb\n");
|
||||
kfree_skb(skb);
|
||||
priv->stats.tx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
skb = new_skb;
|
||||
nxpwifi_dbg(adapter, INFO,
|
||||
"info: new skb headroom %d\n",
|
||||
skb_headroom(skb));
|
||||
}
|
||||
|
||||
tx_info = NXPWIFI_SKB_TXCB(skb);
|
||||
memset(tx_info, 0, sizeof(*tx_info));
|
||||
tx_info->bss_num = priv->bss_num;
|
||||
tx_info->bss_type = priv->bss_type;
|
||||
tx_info->flags |= NXPWIFI_BUF_FLAG_BRIDGED_PKT;
|
||||
|
||||
rcu_read_lock();
|
||||
src_node = nxpwifi_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source);
|
||||
if (src_node) {
|
||||
src_node->stats.last_rx = jiffies;
|
||||
src_node->stats.rx_bytes += skb->len;
|
||||
src_node->stats.rx_packets++;
|
||||
src_node->stats.last_tx_rate = uap_rx_pd->rx_rate;
|
||||
src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) {
|
||||
/*
|
||||
* Update bridge packet statistics as the
|
||||
* packet is not going to kernel/upper layer.
|
||||
*/
|
||||
priv->stats.rx_bytes += skb->len;
|
||||
priv->stats.rx_packets++;
|
||||
|
||||
/*
|
||||
* Sending bridge packet to TX queue, so save the packet
|
||||
* length in TXCB to update statistics in TX complete.
|
||||
*/
|
||||
tx_info->pkt_len = skb->len;
|
||||
}
|
||||
|
||||
__net_timestamp(skb);
|
||||
|
||||
index = nxpwifi_1d_to_wmm_queue[skb->priority];
|
||||
atomic_inc(&priv->wmm_tx_pending[index]);
|
||||
nxpwifi_wmm_add_buf_txqueue(priv, skb);
|
||||
atomic_inc(&adapter->tx_pending);
|
||||
atomic_inc(&adapter->pending_bridged_pkts);
|
||||
|
||||
nxpwifi_queue_work(adapter, &adapter->main_work);
|
||||
}
|
||||
|
||||
/* AP fwd: mcast/bcast -> up + bridge; unicast -> bridge if RA assoc, else up. */
|
||||
int nxpwifi_handle_uap_rx_forward(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct uap_rxpd *uap_rx_pd;
|
||||
struct rx_packet_hdr *rx_pkt_hdr;
|
||||
u8 ra[ETH_ALEN];
|
||||
struct sk_buff *skb_uap;
|
||||
struct nxpwifi_sta_node *node;
|
||||
|
||||
uap_rx_pd = (struct uap_rxpd *)(skb->data);
|
||||
rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
|
||||
|
||||
/* don't do packet forwarding in disconnected state */
|
||||
if (!priv->media_connected) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"drop packet in disconnected state.\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN);
|
||||
|
||||
if (is_multicast_ether_addr(ra)) {
|
||||
skb_uap = skb_copy(skb, GFP_ATOMIC);
|
||||
if (likely(skb_uap)) {
|
||||
nxpwifi_uap_queue_bridged_pkt(priv, skb_uap);
|
||||
} else {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"failed to copy skb for uAP\n");
|
||||
priv->stats.rx_dropped++;
|
||||
dev_kfree_skb_any(skb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
node = nxpwifi_get_sta_entry_rcu(priv, ra);
|
||||
if (node) {
|
||||
/* Requeue Intra-BSS packet */
|
||||
nxpwifi_uap_queue_bridged_pkt(priv, skb);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Forward unicat/Inter-BSS packets to kernel. */
|
||||
return nxpwifi_process_rx_packet(priv, skb);
|
||||
}
|
||||
|
||||
int nxpwifi_uap_recv_packet(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct nxpwifi_sta_node *src_node, *dst_node;
|
||||
struct ethhdr *p_ethhdr;
|
||||
struct sk_buff *skb_uap;
|
||||
struct nxpwifi_txinfo *tx_info;
|
||||
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
p_ethhdr = (void *)skb->data;
|
||||
rcu_read_lock();
|
||||
src_node = nxpwifi_get_sta_entry(priv, p_ethhdr->h_source);
|
||||
if (src_node) {
|
||||
src_node->stats.last_rx = jiffies;
|
||||
src_node->stats.rx_bytes += skb->len;
|
||||
src_node->stats.rx_packets++;
|
||||
}
|
||||
dst_node = nxpwifi_get_sta_entry(priv, p_ethhdr->h_dest);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (is_multicast_ether_addr(p_ethhdr->h_dest) || dst_node) {
|
||||
if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN)
|
||||
skb_uap =
|
||||
skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN);
|
||||
else
|
||||
skb_uap = skb_copy(skb, GFP_ATOMIC);
|
||||
|
||||
if (likely(skb_uap)) {
|
||||
tx_info = NXPWIFI_SKB_TXCB(skb_uap);
|
||||
memset(tx_info, 0, sizeof(*tx_info));
|
||||
tx_info->bss_num = priv->bss_num;
|
||||
tx_info->bss_type = priv->bss_type;
|
||||
tx_info->flags |= NXPWIFI_BUF_FLAG_BRIDGED_PKT;
|
||||
__net_timestamp(skb_uap);
|
||||
nxpwifi_wmm_add_buf_txqueue(priv, skb_uap);
|
||||
atomic_inc(&adapter->tx_pending);
|
||||
atomic_inc(&adapter->pending_bridged_pkts);
|
||||
if ((atomic_read(&adapter->pending_bridged_pkts) >=
|
||||
NXPWIFI_BRIDGED_PKTS_THR_HIGH)) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"Tx: Bridge packet limit reached. Drop packet!\n");
|
||||
nxpwifi_uap_cleanup_tx_queues(priv);
|
||||
}
|
||||
|
||||
} else {
|
||||
nxpwifi_dbg(adapter, ERROR, "failed to allocate skb_uap");
|
||||
}
|
||||
|
||||
nxpwifi_queue_work(adapter, &adapter->main_work);
|
||||
/* Don't forward Intra-BSS unicast packet to upper layer*/
|
||||
|
||||
if (dst_node)
|
||||
return 0;
|
||||
}
|
||||
|
||||
skb->dev = priv->netdev;
|
||||
skb->protocol = eth_type_trans(skb, priv->netdev);
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
/* Forward multicast/broadcast packet to upper layer*/
|
||||
netif_rx(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Process AP RX: check RxPD/len, handle mgmt or 11n reorder/AMSDU, then forward. */
|
||||
int nxpwifi_process_uap_rx_packet(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
int ret;
|
||||
struct uap_rxpd *uap_rx_pd;
|
||||
struct rx_packet_hdr *rx_pkt_hdr;
|
||||
u16 rx_pkt_type;
|
||||
u8 ta[ETH_ALEN], pkt_type;
|
||||
struct nxpwifi_sta_node *node;
|
||||
|
||||
uap_rx_pd = (struct uap_rxpd *)(skb->data);
|
||||
rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type);
|
||||
rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
|
||||
|
||||
if (le16_to_cpu(uap_rx_pd->rx_pkt_offset) +
|
||||
sizeof(rx_pkt_hdr->eth803_hdr) > skb->len) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"wrong rx packet for struct ethhdr: len=%d, offset=%d\n",
|
||||
skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset));
|
||||
priv->stats.rx_dropped++;
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source);
|
||||
|
||||
if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) +
|
||||
le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16)skb->len) {
|
||||
nxpwifi_dbg(adapter, ERROR,
|
||||
"wrong rx packet: len=%d, offset=%d, length=%d\n",
|
||||
skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset),
|
||||
le16_to_cpu(uap_rx_pd->rx_pkt_length));
|
||||
priv->stats.rx_dropped++;
|
||||
rcu_read_lock();
|
||||
node = nxpwifi_get_sta_entry(priv, ta);
|
||||
if (node)
|
||||
node->stats.tx_failed++;
|
||||
rcu_read_unlock();
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rx_pkt_type == PKT_TYPE_MGMT) {
|
||||
ret = nxpwifi_process_mgmt_packet(priv, skb);
|
||||
if (ret && (ret != -EINPROGRESS))
|
||||
nxpwifi_dbg(adapter, DATA, "Rx of mgmt packet failed");
|
||||
if (ret != -EINPROGRESS)
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) {
|
||||
rcu_read_lock();
|
||||
node = nxpwifi_get_sta_entry(priv, ta);
|
||||
if (node)
|
||||
node->rx_seq[uap_rx_pd->priority] =
|
||||
le16_to_cpu(uap_rx_pd->seq_num);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (!priv->ap_11n_enabled ||
|
||||
(!nxpwifi_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) &&
|
||||
(le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) {
|
||||
ret = nxpwifi_handle_uap_rx_forward(priv, skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reorder and send to kernel */
|
||||
pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type);
|
||||
ret = nxpwifi_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num),
|
||||
uap_rx_pd->priority, ta, pkt_type, skb);
|
||||
|
||||
if (ret || rx_pkt_type == PKT_TYPE_BAR)
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
if (ret)
|
||||
priv->stats.rx_dropped++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build TxPD for AP TX: push aligned TxPD; set bss, len/off, prio, delay, txctl,
|
||||
* flags.
|
||||
*/
|
||||
void nxpwifi_process_uap_txpd(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_adapter *adapter = priv->adapter;
|
||||
struct uap_txpd *txpd;
|
||||
struct nxpwifi_txinfo *tx_info = NXPWIFI_SKB_TXCB(skb);
|
||||
int pad;
|
||||
u16 pkt_type, pkt_offset;
|
||||
int hroom = adapter->intf_hdr_len;
|
||||
|
||||
pkt_type = nxpwifi_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
|
||||
|
||||
pad = ((uintptr_t)skb->data - (sizeof(*txpd) + hroom)) &
|
||||
(NXPWIFI_DMA_ALIGN_SZ - 1);
|
||||
|
||||
skb_push(skb, sizeof(*txpd) + pad);
|
||||
|
||||
txpd = (struct uap_txpd *)skb->data;
|
||||
memset(txpd, 0, sizeof(*txpd));
|
||||
txpd->bss_num = priv->bss_num;
|
||||
txpd->bss_type = priv->bss_type;
|
||||
txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) +
|
||||
pad)));
|
||||
txpd->priority = (u8)skb->priority;
|
||||
|
||||
txpd->pkt_delay_2ms = nxpwifi_wmm_compute_drv_pkt_delay(priv, skb);
|
||||
|
||||
if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS ||
|
||||
tx_info->flags & NXPWIFI_BUF_FLAG_ACTION_TX_STATUS) {
|
||||
txpd->tx_token_id = tx_info->ack_frame_id;
|
||||
txpd->flags |= NXPWIFI_TXPD_FLAGS_REQ_TX_STATUS;
|
||||
}
|
||||
|
||||
if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
|
||||
/*
|
||||
* Set the priority specific tx_control field, setting of 0 will
|
||||
* cause the default value to be used later in this function.
|
||||
*/
|
||||
txpd->tx_control =
|
||||
cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]);
|
||||
|
||||
/* Offset of actual data */
|
||||
pkt_offset = sizeof(*txpd) + pad;
|
||||
if (pkt_type == PKT_TYPE_MGMT) {
|
||||
/* Set the packet type and add header for management frame */
|
||||
txpd->tx_pkt_type = cpu_to_le16(pkt_type);
|
||||
pkt_offset += NXPWIFI_MGMT_FRAME_HEADER_SIZE;
|
||||
}
|
||||
|
||||
txpd->tx_pkt_offset = cpu_to_le16(pkt_offset);
|
||||
|
||||
/* make space for adapter->intf_hdr_len */
|
||||
skb_push(skb, hroom);
|
||||
|
||||
if (!txpd->tx_control)
|
||||
/* TxCtrl set by user or default */
|
||||
txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
|
||||
}
|
||||
1381
drivers/net/wireless/nxp/nxpwifi/util.c
Normal file
1381
drivers/net/wireless/nxp/nxpwifi/util.c
Normal file
File diff suppressed because it is too large
Load Diff
155
drivers/net/wireless/nxp/nxpwifi/util.h
Normal file
155
drivers/net/wireless/nxp/nxpwifi/util.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* NXP Wireless LAN device driver: utility functions
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_UTIL_H_
|
||||
#define _NXPWIFI_UTIL_H_
|
||||
#include "fw.h"
|
||||
|
||||
struct nxpwifi_adapter;
|
||||
|
||||
struct nxpwifi_private;
|
||||
|
||||
struct nxpwifi_dma_mapping {
|
||||
dma_addr_t addr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct nxpwifi_cb {
|
||||
struct nxpwifi_dma_mapping dma_mapping;
|
||||
union {
|
||||
struct nxpwifi_rxinfo rx_info;
|
||||
struct nxpwifi_txinfo tx_info;
|
||||
};
|
||||
};
|
||||
|
||||
/* size/addr for nxpwifi_debug_info */
|
||||
#define item_size(n) (sizeof_field(struct nxpwifi_debug_info, n))
|
||||
#define item_addr(n) (offsetof(struct nxpwifi_debug_info, n))
|
||||
|
||||
/* size/addr for struct nxpwifi_adapter */
|
||||
#define adapter_item_size(n) (sizeof_field(struct nxpwifi_adapter, n))
|
||||
#define adapter_item_addr(n) (offsetof(struct nxpwifi_adapter, n))
|
||||
|
||||
struct nxpwifi_debug_data {
|
||||
char name[32]; /* variable/array name */
|
||||
u32 size; /* size of the variable/array */
|
||||
size_t addr; /* address of the variable/array */
|
||||
int num; /* number of variables in an array */
|
||||
};
|
||||
|
||||
static inline struct nxpwifi_rxinfo *NXPWIFI_SKB_RXCB(struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct nxpwifi_cb) > sizeof(skb->cb));
|
||||
return &cb->rx_info;
|
||||
}
|
||||
|
||||
static inline struct nxpwifi_txinfo *NXPWIFI_SKB_TXCB(struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb;
|
||||
|
||||
return &cb->tx_info;
|
||||
}
|
||||
|
||||
static inline void nxpwifi_store_mapping(struct sk_buff *skb,
|
||||
struct nxpwifi_dma_mapping *mapping)
|
||||
{
|
||||
struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb;
|
||||
|
||||
memcpy(&cb->dma_mapping, mapping, sizeof(*mapping));
|
||||
}
|
||||
|
||||
static inline void nxpwifi_get_mapping(struct sk_buff *skb,
|
||||
struct nxpwifi_dma_mapping *mapping)
|
||||
{
|
||||
struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb;
|
||||
|
||||
memcpy(mapping, &cb->dma_mapping, sizeof(*mapping));
|
||||
}
|
||||
|
||||
static inline dma_addr_t NXPWIFI_SKB_DMA_ADDR(struct sk_buff *skb)
|
||||
{
|
||||
struct nxpwifi_dma_mapping mapping;
|
||||
|
||||
nxpwifi_get_mapping(skb, &mapping);
|
||||
|
||||
return mapping.addr;
|
||||
}
|
||||
|
||||
int nxpwifi_debug_info_to_buffer(struct nxpwifi_private *priv, char *buf,
|
||||
struct nxpwifi_debug_info *info);
|
||||
|
||||
static inline void le16_unaligned_add_cpu(__le16 *var, u16 val)
|
||||
{
|
||||
put_unaligned_le16(get_unaligned_le16(var) + val, var);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over TLVs safely.
|
||||
* Ensures no out-of-bound access even if firmware sends malformed data.
|
||||
*/
|
||||
#define nxpwifi_for_each_tlv(tlv, buf, buf_len) \
|
||||
for (tlv = (const struct nxpwifi_tlv *)(buf); \
|
||||
(u8 *)(tlv) + sizeof(*tlv) <= (u8 *)(buf) + (buf_len) && \
|
||||
(u8 *)(tlv) + sizeof(*tlv) + le16_to_cpu(tlv->len) <= \
|
||||
(u8 *)(buf) + (buf_len); \
|
||||
tlv = (const struct nxpwifi_tlv *)((u8 *)(tlv) + sizeof(*tlv) + \
|
||||
le16_to_cpu(tlv->len)))
|
||||
|
||||
/* Return first TLV matching @type in given buffer. */
|
||||
static inline const struct nxpwifi_tlv *
|
||||
nxpwifi_find_tlv(u16 type, const u8 *buf, u32 buf_len)
|
||||
{
|
||||
const struct nxpwifi_tlv *tlv;
|
||||
|
||||
nxpwifi_for_each_tlv(tlv, buf, buf_len) {
|
||||
if (le16_to_cpu(tlv->type) == type)
|
||||
return tlv;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nxpwifi_append_data_tlv(u16 id, u8 *data, int len, u8 *pos, u8 *cmd_end);
|
||||
|
||||
int nxpwifi_download_vdll_block(struct nxpwifi_adapter *adapter,
|
||||
u8 *block, u16 block_len);
|
||||
|
||||
int nxpwifi_process_vdll_event(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb);
|
||||
|
||||
u64 nxpwifi_roc_cookie(struct nxpwifi_adapter *adapter);
|
||||
|
||||
void nxpwifi_queue_work(struct nxpwifi_adapter *adapter,
|
||||
struct work_struct *work);
|
||||
|
||||
void nxpwifi_queue_delayed_work(struct nxpwifi_adapter *adapter,
|
||||
struct delayed_work *dwork,
|
||||
unsigned long delay);
|
||||
|
||||
void nxpwifi_queue_wiphy_work(struct nxpwifi_adapter *adapter,
|
||||
struct wiphy_work *work);
|
||||
|
||||
void nxpwifi_queue_delayed_wiphy_work(struct nxpwifi_adapter *adapter,
|
||||
struct wiphy_delayed_work *dwork,
|
||||
unsigned long delay);
|
||||
|
||||
/*
|
||||
* Firmware cannot run AP and STA on different channels simultaneously,
|
||||
* and doing so may trigger a crash. Check whether check_chan can be set
|
||||
* safely; return true if allowed, false if another channel is already
|
||||
* active in firmware.
|
||||
*/
|
||||
bool nxpwifi_is_channel_setting_allowable(struct nxpwifi_private *priv,
|
||||
struct ieee80211_channel *check_chan);
|
||||
|
||||
void nxpwifi_convert_chan_to_band_cfg(struct nxpwifi_private *priv,
|
||||
u8 *band_cfg,
|
||||
struct cfg80211_chan_def *chan_def);
|
||||
|
||||
#endif /* !_NXPWIFI_UTIL_H_ */
|
||||
1318
drivers/net/wireless/nxp/nxpwifi/wmm.c
Normal file
1318
drivers/net/wireless/nxp/nxpwifi/wmm.c
Normal file
File diff suppressed because it is too large
Load Diff
77
drivers/net/wireless/nxp/nxpwifi/wmm.h
Normal file
77
drivers/net/wireless/nxp/nxpwifi/wmm.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* NXP Wireless LAN device driver: WMM
|
||||
*
|
||||
* Copyright 2011-2024 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NXPWIFI_WMM_H_
|
||||
#define _NXPWIFI_WMM_H_
|
||||
|
||||
enum ieee_types_wmm_aciaifsn_bitmasks {
|
||||
NXPWIFI_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)),
|
||||
NXPWIFI_ACM = BIT(4),
|
||||
NXPWIFI_ACI = (BIT(5) | BIT(6)),
|
||||
};
|
||||
|
||||
enum ieee_types_wmm_ecw_bitmasks {
|
||||
NXPWIFI_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)),
|
||||
NXPWIFI_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)),
|
||||
};
|
||||
|
||||
extern const u16 nxpwifi_1d_to_wmm_queue[];
|
||||
|
||||
/* Retrieve the TID of the given RA list. */
|
||||
static inline int
|
||||
nxpwifi_get_tid(struct nxpwifi_ra_list_tbl *ptr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (skb_queue_empty(&ptr->skb_head))
|
||||
return 0;
|
||||
|
||||
skb = skb_peek(&ptr->skb_head);
|
||||
|
||||
return skb->priority;
|
||||
}
|
||||
|
||||
void nxpwifi_wmm_add_buf_txqueue(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb);
|
||||
void nxpwifi_wmm_add_buf_bypass_txqueue(struct nxpwifi_private *priv,
|
||||
struct sk_buff *skb);
|
||||
void nxpwifi_ralist_add(struct nxpwifi_private *priv, const u8 *ra);
|
||||
void nxpwifi_rotate_priolists(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ra_list_tbl *ra, int tid);
|
||||
|
||||
bool nxpwifi_wmm_lists_empty(struct nxpwifi_adapter *adapter);
|
||||
bool nxpwifi_bypass_txlist_empty(struct nxpwifi_adapter *adapter);
|
||||
void nxpwifi_wmm_process_tx(struct nxpwifi_adapter *adapter);
|
||||
void nxpwifi_process_bypass_tx(struct nxpwifi_adapter *adapter);
|
||||
bool nxpwifi_is_ralist_valid(struct nxpwifi_private *priv,
|
||||
struct nxpwifi_ra_list_tbl *ra_list, int tid);
|
||||
|
||||
u8 nxpwifi_wmm_compute_drv_pkt_delay(struct nxpwifi_private *priv,
|
||||
const struct sk_buff *skb);
|
||||
void nxpwifi_wmm_init(struct nxpwifi_adapter *adapter);
|
||||
|
||||
u32 nxpwifi_wmm_process_association_req(struct nxpwifi_private *priv,
|
||||
u8 **assoc_buf,
|
||||
struct ieee80211_wmm_param_ie *wmmie,
|
||||
struct ieee80211_ht_cap *htcap);
|
||||
|
||||
void nxpwifi_wmm_setup_queue_priorities(struct nxpwifi_private *priv,
|
||||
struct ieee80211_wmm_param_ie *wmm_ie);
|
||||
void nxpwifi_wmm_setup_ac_downgrade(struct nxpwifi_private *priv);
|
||||
int nxpwifi_ret_wmm_get_status(struct nxpwifi_private *priv,
|
||||
const struct host_cmd_ds_command *resp);
|
||||
struct nxpwifi_ra_list_tbl *
|
||||
nxpwifi_wmm_get_queue_raptr(struct nxpwifi_private *priv, u8 tid,
|
||||
const u8 *ra_addr);
|
||||
u8 nxpwifi_wmm_downgrade_tid(struct nxpwifi_private *priv, u32 tid);
|
||||
void nxpwifi_update_ralist_tx_pause(struct nxpwifi_private *priv, u8 *mac,
|
||||
u8 tx_pause);
|
||||
|
||||
struct nxpwifi_ra_list_tbl *nxpwifi_wmm_get_ralist_node(struct nxpwifi_private
|
||||
*priv, u8 tid, const u8 *ra_addr);
|
||||
void nxpwifi_wmm_init_tos_to_tid_inv(struct nxpwifi_private *priv);
|
||||
#endif /* !_NXPWIFI_WMM_H_ */
|
||||
Reference in New Issue
Block a user