| /** @file mlan_11ax.c |
| * |
| * @brief This file contains the functions for 11ax related features. |
| * |
| * |
| * Copyright 2018-2021 NXP |
| * |
| * This software file (the File) is distributed by NXP |
| * under the terms of the GNU General Public License Version 2, June 1991 |
| * (the License). You may use, redistribute and/or modify the File in |
| * accordance with the terms and conditions of the License, a copy of which |
| * is available by writing to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the |
| * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. |
| * |
| * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE |
| * ARE EXPRESSLY DISCLAIMED. The License provides additional details about |
| * this warranty disclaimer. |
| * |
| */ |
| |
| #include "mlan.h" |
| #include "mlan_join.h" |
| #include "mlan_util.h" |
| #include "mlan_ioctl.h" |
| #include "mlan_fw.h" |
| #include "mlan_main.h" |
| #include "mlan_wmm.h" |
| #include "mlan_11n.h" |
| #include "mlan_11ax.h" |
| #include "mlan_11ac.h" |
| |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| |
| #if 0 |
| /** |
| * @brief This function prints the 802.11ax HE mac capability |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param cap Capability value |
| * |
| * @return N/A |
| */ |
| static void |
| wlan_show_dot11axmaccap(pmlan_adapter pmadapter, t_u32 cap) |
| { |
| ENTER(); |
| |
| LEAVE(); |
| return; |
| } |
| #endif |
| |
| /** |
| * @brief This function check if AP support TWT Response. |
| * |
| * @param pbss_desc A pointer to BSSDescriptor_t structure |
| * |
| * @return MTRUE/MFALSE |
| */ |
| static t_u8 |
| wlan_check_ap_11ax_twt_supported(BSSDescriptor_t *pbss_desc) |
| { |
| if (!pbss_desc->phe_cap) |
| return MFALSE; |
| if (!(pbss_desc->phe_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) |
| return MFALSE; |
| if (!pbss_desc->pext_cap) |
| return MFALSE; |
| if (!ISSUPP_EXTCAP_EXT_TWT_RESP(pbss_desc->pext_cap->ext_cap)) |
| return MFALSE; |
| return MTRUE; |
| } |
| |
| /** |
| * @brief This function check if we should enable TWT support |
| * |
| * @param pbss_desc A pointer to BSSDescriptor_t structure |
| * |
| * @return MTRUE/MFALSE |
| */ |
| t_u8 |
| wlan_check_11ax_twt_supported(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc) |
| { |
| MrvlIEtypes_He_cap_t *phecap = |
| (MrvlIEtypes_He_cap_t *) & pmpriv->user_he_cap; |
| MrvlIEtypes_He_cap_t *hw_he_cap = (MrvlIEtypes_He_cap_t *) |
| & pmpriv->adapter->hw_he_cap; |
| if (pbss_desc && !wlan_check_ap_11ax_twt_supported(pbss_desc)) { |
| PRINTM(MINFO, "AP don't support twt feature\n"); |
| return MFALSE; |
| } |
| if (pbss_desc) { |
| if (pbss_desc->bss_band & BAND_A) { |
| hw_he_cap = (MrvlIEtypes_He_cap_t *) |
| & pmpriv->adapter->hw_he_cap; |
| phecap = (MrvlIEtypes_He_cap_t *) & pmpriv->user_he_cap; |
| } else { |
| hw_he_cap = (MrvlIEtypes_He_cap_t *) |
| & pmpriv->adapter->hw_2g_he_cap; |
| phecap = (MrvlIEtypes_He_cap_t *) |
| & pmpriv->user_2g_he_cap; |
| } |
| } |
| if (!(hw_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) { |
| PRINTM(MINFO, "FW don't support TWT\n"); |
| return MFALSE; |
| } |
| if (phecap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT) |
| return MTRUE; |
| PRINTM(MINFO, "USER HE_MAC_CAP don't support TWT\n"); |
| return MFALSE; |
| } |
| |
| #if 0 |
| /** |
| * @brief This function prints the 802.11ax HE PHY cap |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param support Support value |
| * |
| * @return N/A |
| */ |
| static void |
| wlan_show_dot11axphycap(pmlan_adapter pmadapter, t_u32 support) |
| { |
| ENTER(); |
| |
| LEAVE(); |
| return; |
| } |
| #endif |
| /** |
| * @brief This function fills the HE cap tlv out put format is LE, not CPU |
| * |
| * @param priv A pointer to mlan_private structure |
| * @param band 5G or 2.4 G |
| * @param phe_cap A pointer to MrvlIEtypes_Data_t structure |
| * @param flag TREU--pvht_cap has the setting for resp |
| * MFALSE -- pvht_cap is clean |
| * |
| * @return bytes added to the phe_cap |
| */ |
| t_u16 |
| wlan_fill_he_cap_tlv(mlan_private *pmpriv, t_u8 band, |
| MrvlIEtypes_Extension_t * phe_cap, t_u8 flag) |
| { |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| t_u16 len = 0; |
| #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || defined(PCIE9097) || defined(SD9097) || defined(USB9097) |
| t_u16 rx_nss = 0, tx_nss = 0; |
| #endif |
| MrvlIEtypes_He_cap_t *phecap = MNULL; |
| t_u8 nss = 0; |
| t_u16 cfg_value = 0; |
| t_u16 hw_value = 0; |
| MrvlIEtypes_He_cap_t *phw_hecap = MNULL; |
| |
| if (!phe_cap) { |
| LEAVE(); |
| return 0; |
| } |
| if (band & BAND_A) { |
| memcpy_ext(pmadapter, (t_u8 *)phe_cap, pmpriv->user_he_cap, |
| pmpriv->user_hecap_len, |
| sizeof(MrvlIEtypes_He_cap_t)); |
| len = pmpriv->user_hecap_len; |
| phw_hecap = (MrvlIEtypes_He_cap_t *) pmadapter->hw_he_cap; |
| } else { |
| memcpy_ext(pmadapter, (t_u8 *)phe_cap, pmpriv->user_2g_he_cap, |
| pmpriv->user_2g_hecap_len, |
| sizeof(MrvlIEtypes_He_cap_t)); |
| len = pmpriv->user_2g_hecap_len; |
| phw_hecap = (MrvlIEtypes_He_cap_t *) pmadapter->hw_2g_he_cap; |
| } |
| phe_cap->type = wlan_cpu_to_le16(phe_cap->type); |
| phe_cap->len = wlan_cpu_to_le16(phe_cap->len); |
| #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || defined(PCIE9097) || defined(SD9097) || defined(USB9097) |
| if (IS_CARD9098(pmpriv->adapter->card_type) || |
| IS_CARD9097(pmpriv->adapter->card_type)) { |
| if (band & BAND_A) { |
| rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream >> |
| 8); |
| tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream >> |
| 8) & 0x0f; |
| } else { |
| rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream); |
| tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream) & |
| 0x0f; |
| } |
| } |
| #endif |
| phecap = (MrvlIEtypes_He_cap_t *) phe_cap; |
| for (nss = 1; nss <= 8; nss++) { |
| cfg_value = GET_HE_NSSMCS(phecap->rx_mcs_80, nss); |
| hw_value = GET_HE_NSSMCS(phw_hecap->rx_mcs_80, nss); |
| #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || defined(PCIE9097) || defined(SD9097) || defined(USB9097) |
| if ((rx_nss != 0) && (nss > rx_nss)) |
| cfg_value = NO_NSS_SUPPORT; |
| #endif |
| if ((hw_value == NO_NSS_SUPPORT) || |
| (cfg_value == NO_NSS_SUPPORT)) |
| SET_HE_NSSMCS(phecap->rx_mcs_80, nss, NO_NSS_SUPPORT); |
| else |
| SET_HE_NSSMCS(phecap->rx_mcs_80, |
| nss, MIN(cfg_value, hw_value)); |
| } |
| for (nss = 1; nss <= 8; nss++) { |
| cfg_value = GET_HE_NSSMCS(phecap->tx_mcs_80, nss); |
| hw_value = GET_HE_NSSMCS(phw_hecap->tx_mcs_80, nss); |
| #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || defined(PCIE9097) || defined(SD9097) || defined(USB9097) |
| if ((tx_nss != 0) && (nss > tx_nss)) |
| cfg_value = NO_NSS_SUPPORT; |
| #endif |
| if ((hw_value == NO_NSS_SUPPORT) || |
| (cfg_value == NO_NSS_SUPPORT)) |
| SET_HE_NSSMCS(phecap->tx_mcs_80, nss, NO_NSS_SUPPORT); |
| else |
| SET_HE_NSSMCS(phecap->tx_mcs_80, |
| nss, MIN(cfg_value, hw_value)); |
| } |
| PRINTM(MCMND, "Set: HE rx mcs set 0x%08x tx mcs set 0x%08x\n", |
| phecap->rx_mcs_80, phecap->tx_mcs_80); |
| |
| DBG_HEXDUMP(MCMD_D, "fill_11ax_tlv", (t_u8 *)phecap, len); |
| LEAVE(); |
| return len; |
| } |
| |
| /** |
| * @brief This function append the 802_11ax HE capability tlv |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pbss_desc A pointer to BSSDescriptor_t structure |
| * @param ppbuffer A Pointer to command buffer pointer |
| * |
| * @return bytes added to the buffer |
| */ |
| int |
| wlan_cmd_append_11ax_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, |
| t_u8 **ppbuffer) |
| { |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| MrvlIEtypes_He_cap_t *phecap = MNULL; |
| int len = 0; |
| t_u8 bw_80p80 = MFALSE; |
| #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || defined(PCIE9097) || defined(SD9097) || defined(USB9097) |
| t_u16 rx_nss = 0, tx_nss = 0; |
| #endif |
| t_u8 nss = 0; |
| t_u16 cfg_value = 0; |
| t_u16 hw_value = 0; |
| MrvlIEtypes_He_cap_t *phw_hecap = MNULL; |
| |
| ENTER(); |
| |
| /* Null Checks */ |
| if (ppbuffer == MNULL) { |
| LEAVE(); |
| return 0; |
| } |
| if (*ppbuffer == MNULL) { |
| LEAVE(); |
| return 0; |
| } |
| /** check if AP support HE, if not return right away */ |
| if (!pbss_desc->phe_cap) { |
| LEAVE(); |
| return 0; |
| } |
| bw_80p80 = wlan_is_80_80_support(pmpriv, pbss_desc); |
| phecap = (MrvlIEtypes_He_cap_t *) * ppbuffer; |
| if (pbss_desc->bss_band & BAND_A) { |
| memcpy_ext(pmadapter, *ppbuffer, pmpriv->user_he_cap, |
| pmpriv->user_hecap_len, pmpriv->user_hecap_len); |
| *ppbuffer += pmpriv->user_hecap_len; |
| len = pmpriv->user_hecap_len; |
| phw_hecap = (MrvlIEtypes_He_cap_t *) pmadapter->hw_he_cap; |
| } else { |
| memcpy_ext(pmadapter, *ppbuffer, pmpriv->user_2g_he_cap, |
| pmpriv->user_2g_hecap_len, |
| pmpriv->user_2g_hecap_len); |
| *ppbuffer += pmpriv->user_2g_hecap_len; |
| len = pmpriv->user_2g_hecap_len; |
| phw_hecap = (MrvlIEtypes_He_cap_t *) pmadapter->hw_2g_he_cap; |
| } |
| phecap->type = wlan_cpu_to_le16(phecap->type); |
| phecap->len = wlan_cpu_to_le16(phecap->len); |
| #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || defined(PCIE9097) || defined(SD9097) || defined(USB9097) |
| if (IS_CARD9098(pmpriv->adapter->card_type) || |
| IS_CARD9097(pmpriv->adapter->card_type)) { |
| if (pbss_desc->bss_band & BAND_A) { |
| rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream >> |
| 8); |
| tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream >> |
| 8) & 0x0f; |
| } else { |
| rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream); |
| tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream) & |
| 0x0f; |
| } |
| /** force 1x1 when enable 80P80 */ |
| if (bw_80p80) |
| rx_nss = tx_nss = 1; |
| } |
| #endif |
| for (nss = 1; nss <= 8; nss++) { |
| cfg_value = GET_HE_NSSMCS(phecap->rx_mcs_80, nss); |
| hw_value = GET_HE_NSSMCS(phw_hecap->rx_mcs_80, nss); |
| #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || defined(PCIE9097) || defined(SD9097) || defined(USB9097) |
| if ((rx_nss != 0) && (nss > rx_nss)) |
| cfg_value = NO_NSS_SUPPORT; |
| #endif |
| if ((hw_value == NO_NSS_SUPPORT) || |
| (cfg_value == NO_NSS_SUPPORT)) |
| SET_HE_NSSMCS(phecap->rx_mcs_80, nss, NO_NSS_SUPPORT); |
| else |
| SET_HE_NSSMCS(phecap->rx_mcs_80, |
| nss, MIN(cfg_value, hw_value)); |
| } |
| for (nss = 1; nss <= 8; nss++) { |
| cfg_value = GET_HE_NSSMCS(phecap->tx_mcs_80, nss); |
| hw_value = GET_HE_NSSMCS(phw_hecap->tx_mcs_80, nss); |
| #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || defined(PCIE9097) || defined(SD9097) || defined(USB9097) |
| if ((tx_nss != 0) && (nss > tx_nss)) |
| cfg_value = NO_NSS_SUPPORT; |
| #endif |
| if ((hw_value == NO_NSS_SUPPORT) || |
| (cfg_value == NO_NSS_SUPPORT)) |
| SET_HE_NSSMCS(phecap->tx_mcs_80, nss, NO_NSS_SUPPORT); |
| else |
| SET_HE_NSSMCS(phecap->tx_mcs_80, |
| nss, MIN(cfg_value, hw_value)); |
| } |
| PRINTM(MCMND, "Set: HE rx mcs set 0x%08x tx mcs set 0x%08x\n", |
| phecap->rx_mcs_80, phecap->tx_mcs_80); |
| if (!bw_80p80) { |
| /** reset BIT3 and BIT4 channel width ,not support 80 + 80*/ |
| /** not support 160Mhz now, if support,not reset bit3 */ |
| phecap->he_phy_cap[0] &= ~(MBIT(3) | MBIT(4)); |
| } |
| DBG_HEXDUMP(MCMD_D, "append_11ax_tlv", (t_u8 *)phecap, len); |
| |
| LEAVE(); |
| return len; |
| } |
| |
| /** |
| * @brief This function save the 11ax cap from FW. |
| * |
| * @param pmadapater A pointer to mlan_adapter |
| * @param hw_he_cap A pointer to MrvlIEtypes_Extension_t |
| * |
| * @return N/A |
| */ |
| void |
| wlan_update_11ax_cap(mlan_adapter *pmadapter, |
| MrvlIEtypes_Extension_t * hw_he_cap) |
| { |
| MrvlIEtypes_He_cap_t *phe_cap = MNULL; |
| t_u8 i = 0; |
| t_u8 he_cap_2g = 0; |
| |
| ENTER(); |
| if ((hw_he_cap->len + sizeof(MrvlIEtypesHeader_t)) > |
| sizeof(pmadapter->hw_he_cap)) { |
| PRINTM(MERROR, "hw_he_cap too big, len=%d\n", hw_he_cap->len); |
| LEAVE(); |
| return; |
| } |
| phe_cap = (MrvlIEtypes_He_cap_t *) hw_he_cap; |
| if (phe_cap->he_phy_cap[0] & |
| (AX_2G_40MHZ_SUPPORT | AX_2G_20MHZ_SUPPORT)) { |
| pmadapter->hw_2g_hecap_len = |
| hw_he_cap->len + sizeof(MrvlIEtypesHeader_t); |
| memcpy_ext(pmadapter, pmadapter->hw_2g_he_cap, |
| (t_u8 *)hw_he_cap, |
| hw_he_cap->len + sizeof(MrvlIEtypesHeader_t), |
| sizeof(pmadapter->hw_2g_he_cap)); |
| pmadapter->fw_bands |= BAND_GAX; |
| pmadapter->config_bands |= BAND_GAX; |
| he_cap_2g = MTRUE; |
| DBG_HEXDUMP(MCMD_D, "2.4G HE capability IE ", |
| (t_u8 *)pmadapter->hw_2g_he_cap, |
| pmadapter->hw_2g_hecap_len); |
| } else { |
| pmadapter->fw_bands |= BAND_AAX; |
| pmadapter->config_bands |= BAND_AAX; |
| pmadapter->hw_hecap_len = |
| hw_he_cap->len + sizeof(MrvlIEtypesHeader_t); |
| memcpy_ext(pmadapter, pmadapter->hw_he_cap, (t_u8 *)hw_he_cap, |
| hw_he_cap->len + sizeof(MrvlIEtypesHeader_t), |
| sizeof(pmadapter->hw_he_cap)); |
| DBG_HEXDUMP(MCMD_D, "5G HE capability IE ", |
| (t_u8 *)pmadapter->hw_he_cap, |
| pmadapter->hw_hecap_len); |
| } |
| for (i = 0; i < pmadapter->priv_num; i++) { |
| if (pmadapter->priv[i]) { |
| pmadapter->priv[i]->config_bands = |
| pmadapter->config_bands; |
| if (he_cap_2g) { |
| pmadapter->priv[i]->user_2g_hecap_len = |
| pmadapter->hw_2g_hecap_len; |
| memcpy_ext(pmadapter, |
| pmadapter->priv[i]->user_2g_he_cap, |
| pmadapter->hw_2g_he_cap, |
| pmadapter->hw_2g_hecap_len, |
| sizeof(pmadapter->priv[i] |
| ->user_2g_he_cap)); |
| } else { |
| pmadapter->priv[i]->user_hecap_len = |
| pmadapter->hw_hecap_len; |
| memcpy_ext(pmadapter, |
| pmadapter->priv[i]->user_he_cap, |
| pmadapter->hw_he_cap, |
| pmadapter->hw_hecap_len, |
| sizeof(pmadapter->priv[i]-> |
| user_he_cap)); |
| } |
| } |
| } |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function check if 11AX is allowed in bandcfg |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param bss_band bss band |
| * |
| * @return 0--not allowed, other value allowed |
| */ |
| t_u16 |
| wlan_11ax_bandconfig_allowed(mlan_private *pmpriv, t_u16 bss_band) |
| { |
| if (!IS_FW_SUPPORT_11AX(pmpriv->adapter)) |
| return MFALSE; |
| if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { |
| if (bss_band & BAND_G) |
| return (pmpriv->adapter->adhoc_start_band & BAND_GAX); |
| else if (bss_band & BAND_A) |
| return (pmpriv->adapter->adhoc_start_band & BAND_AAX); |
| } else { |
| if (bss_band & BAND_G) |
| return (pmpriv->config_bands & BAND_GAX); |
| else if (bss_band & BAND_A) |
| return (pmpriv->config_bands & BAND_AAX); |
| } |
| return MFALSE; |
| } |
| |
| /** |
| * @brief Set 11ax configuration |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pioctl_req A pointer to ioctl request buffer |
| * |
| * @return MLAN_STATUS_PENDING --success, otherwise fail |
| */ |
| static mlan_status |
| wlan_11ax_ioctl_hecfg(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; |
| mlan_ds_11ax_cfg *cfg = MNULL; |
| t_u16 cmd_action = 0; |
| |
| ENTER(); |
| |
| if (pioctl_req->buf_len < sizeof(mlan_ds_11ax_cfg)) { |
| PRINTM(MINFO, "MLAN bss IOCTL length is too short.\n"); |
| pioctl_req->data_read_written = 0; |
| pioctl_req->buf_len_needed = sizeof(mlan_ds_11ax_cfg); |
| pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; |
| LEAVE(); |
| return MLAN_STATUS_RESOURCE; |
| } |
| |
| cfg = (mlan_ds_11ax_cfg *) pioctl_req->pbuf; |
| |
| if ((cfg->param.he_cfg.band & MBIT(0)) && |
| !(pmadapter->fw_bands & BAND_GAX)) { |
| PRINTM(MERROR, "FW don't support 2.4G AX\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| if ((cfg->param.he_cfg.band & MBIT(1)) && |
| !(pmadapter->fw_bands & BAND_AAX)) { |
| PRINTM(MERROR, "FW don't support 5G AX\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| if (pioctl_req->action == MLAN_ACT_SET) |
| cmd_action = HostCmd_ACT_GEN_SET; |
| else |
| cmd_action = HostCmd_ACT_GEN_GET; |
| |
| /* Send request to firmware */ |
| ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_11AX_CFG, cmd_action, 0, |
| (t_void *)pioctl_req, |
| (t_void *)&cfg->param.he_cfg); |
| if (ret == MLAN_STATUS_SUCCESS) |
| ret = MLAN_STATUS_PENDING; |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief 11ax configuration handler |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pioctl_req A pointer to ioctl request buffer |
| * |
| * @return MLAN_STATUS_SUCCESS --success, otherwise fail |
| */ |
| mlan_status |
| wlan_11ax_cfg_ioctl(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) |
| { |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| mlan_ds_11ax_cfg *cfg = MNULL; |
| |
| ENTER(); |
| |
| cfg = (mlan_ds_11ax_cfg *) pioctl_req->pbuf; |
| switch (cfg->sub_command) { |
| case MLAN_OID_11AX_HE_CFG: |
| status = wlan_11ax_ioctl_hecfg(pmadapter, pioctl_req); |
| break; |
| case MLAN_OID_11AX_CMD_CFG: |
| status = wlan_11ax_ioctl_cmd(pmadapter, pioctl_req); |
| break; |
| case MLAN_OID_11AX_TWT_CFG: |
| status = wlan_11ax_ioctl_twtcfg(pmadapter, pioctl_req); |
| break; |
| default: |
| pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; |
| status = MLAN_STATUS_FAILURE; |
| break; |
| } |
| LEAVE(); |
| return status; |
| } |
| |
| /** |
| * @brief This function prepares 11ax cfg command |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param cmd A pointer to HostCmd_DS_COMMAND structure |
| * @param cmd_action the action: GET or SET |
| * @param pdata_buf A pointer to data buffer |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_cmd_11ax_cfg(pmlan_private pmpriv, |
| HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, t_void *pdata_buf) |
| { |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| HostCmd_DS_11AX_CFG *axcfg = &cmd->params.axcfg; |
| mlan_ds_11ax_he_cfg *hecfg = (mlan_ds_11ax_he_cfg *) pdata_buf; |
| MrvlIEtypes_Extension_t *tlv = MNULL; |
| t_u8 *pos = MNULL; |
| |
| ENTER(); |
| cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11AX_CFG); |
| cmd->size = sizeof(HostCmd_DS_11AX_CFG) + S_DS_GEN; |
| |
| axcfg->action = wlan_cpu_to_le16(cmd_action); |
| axcfg->band_config = hecfg->band & 0xFF; |
| |
| pos = (t_u8 *)axcfg->val; |
| /**HE Capability */ |
| if (hecfg->he_cap.len && (hecfg->he_cap.ext_id == HE_CAPABILITY)) { |
| tlv = (MrvlIEtypes_Extension_t *) pos; |
| tlv->type = wlan_cpu_to_le16(hecfg->he_cap.id); |
| tlv->len = wlan_cpu_to_le16(hecfg->he_cap.len); |
| memcpy_ext(pmadapter, &tlv->ext_id, &hecfg->he_cap.ext_id, |
| hecfg->he_cap.len, |
| MRVDRV_SIZE_OF_CMD_BUFFER - cmd->size); |
| cmd->size += hecfg->he_cap.len + sizeof(MrvlIEtypesHeader_t); |
| pos += hecfg->he_cap.len + sizeof(MrvlIEtypesHeader_t); |
| } |
| |
| cmd->size = wlan_cpu_to_le16(cmd->size); |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function handles the command response of 11axcfg |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param resp A pointer to HostCmd_DS_COMMAND |
| * @param pioctl_buf A pointer to mlan_ioctl_req structure |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_ret_11ax_cfg(pmlan_private pmpriv, |
| HostCmd_DS_COMMAND *resp, mlan_ioctl_req *pioctl_buf) |
| { |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| mlan_ds_11ax_cfg *cfg = MNULL; |
| mlan_ds_11ax_he_capa *hecap = MNULL; |
| HostCmd_DS_11AX_CFG *axcfg = &resp->params.axcfg; |
| MrvlIEtypes_Extension_t *tlv = MNULL; |
| t_u16 left_len = 0, tlv_type = 0, tlv_len = 0; |
| |
| ENTER(); |
| |
| if (pioctl_buf == MNULL) |
| goto done; |
| |
| cfg = (mlan_ds_11ax_cfg *) pioctl_buf->pbuf; |
| cfg->param.he_cfg.band = axcfg->band_config; |
| hecap = (mlan_ds_11ax_he_capa *) & cfg->param.he_cfg.he_cap; |
| |
| /* TLV parse */ |
| left_len = resp->size - sizeof(HostCmd_DS_11AX_CFG) - S_DS_GEN; |
| tlv = (MrvlIEtypes_Extension_t *) axcfg->val; |
| |
| while (left_len > sizeof(MrvlIEtypesHeader_t)) { |
| tlv_type = wlan_le16_to_cpu(tlv->type); |
| tlv_len = wlan_le16_to_cpu(tlv->len); |
| if (tlv_type == EXTENSION) { |
| switch (tlv->ext_id) { |
| case HE_CAPABILITY: |
| hecap->id = tlv_type; |
| hecap->len = tlv_len; |
| memcpy_ext(pmadapter, (t_u8 *)&hecap->ext_id, |
| (t_u8 *)&tlv->ext_id, tlv_len, |
| sizeof(mlan_ds_11ax_he_capa) - |
| sizeof(MrvlIEtypesHeader_t)); |
| if (cfg->param.he_cfg.band & MBIT(1)) { |
| memcpy_ext(pmadapter, |
| (t_u8 *)&pmpriv->user_he_cap, |
| (t_u8 *)tlv, |
| tlv_len + |
| sizeof(MrvlIEtypesHeader_t), |
| sizeof(pmpriv->user_he_cap)); |
| pmpriv->user_hecap_len = MIN(tlv_len + |
| sizeof |
| (MrvlIEtypesHeader_t), |
| sizeof |
| (pmpriv-> |
| user_he_cap)); |
| PRINTM(MCMND, "user_hecap_len=%d\n", |
| pmpriv->user_hecap_len); |
| } else { |
| memcpy_ext(pmadapter, |
| (t_u8 *)&pmpriv-> |
| user_2g_he_cap, (t_u8 *)tlv, |
| tlv_len + |
| sizeof(MrvlIEtypesHeader_t), |
| sizeof(pmpriv-> |
| user_2g_he_cap)); |
| pmpriv->user_2g_hecap_len = |
| MIN(tlv_len + |
| sizeof(MrvlIEtypesHeader_t), |
| sizeof(pmpriv-> |
| user_2g_he_cap)); |
| PRINTM(MCMND, "user_2g_hecap_len=%d\n", |
| pmpriv->user_2g_hecap_len); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); |
| tlv = (MrvlIEtypes_Extension_t *) ((t_u8 *)tlv + tlv_len + |
| sizeof(MrvlIEtypesHeader_t)); |
| } |
| |
| done: |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief 11ax command handler |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pioctl_req A pointer to ioctl request buffer |
| * |
| * @return MLAN_STATUS_SUCCESS --success, otherwise fail |
| */ |
| mlan_status |
| wlan_11ax_ioctl_cmd(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) |
| { |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| mlan_ds_11ax_cmd_cfg *cfg = MNULL; |
| mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; |
| t_u16 cmd_action = 0; |
| |
| ENTER(); |
| |
| if (pioctl_req->buf_len < sizeof(mlan_ds_11ax_cmd_cfg)) { |
| PRINTM(MINFO, "MLAN bss IOCTL length is too short.\n"); |
| pioctl_req->data_read_written = 0; |
| pioctl_req->buf_len_needed = sizeof(mlan_ds_11ax_cmd_cfg); |
| pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; |
| LEAVE(); |
| return MLAN_STATUS_RESOURCE; |
| } |
| cfg = (mlan_ds_11ax_cmd_cfg *) pioctl_req->pbuf; |
| |
| if (pioctl_req->action == MLAN_ACT_SET) |
| cmd_action = HostCmd_ACT_GEN_SET; |
| else |
| cmd_action = HostCmd_ACT_GEN_GET; |
| |
| /* Send request to firmware */ |
| status = wlan_prepare_cmd(pmpriv, HostCmd_CMD_11AX_CMD, cmd_action, 0, |
| (t_void *)pioctl_req, (t_void *)cfg); |
| if (status == MLAN_STATUS_SUCCESS) |
| status = MLAN_STATUS_PENDING; |
| |
| LEAVE(); |
| return status; |
| } |
| |
| /** |
| * @brief This function prepares 11ax command |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param cmd A pointer to HostCmd_DS_COMMAND structure |
| * @param cmd_action the action: GET or SET |
| * @param pdata_buf A pointer to data buffer |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_cmd_11ax_cmd(pmlan_private pmpriv, |
| HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, t_void *pdata_buf) |
| { |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| HostCmd_DS_11AX_CMD_CFG *axcmd = &cmd->params.axcmd; |
| mlan_ds_11ax_cmd_cfg *ds_11ax_cmd = (mlan_ds_11ax_cmd_cfg *) pdata_buf; |
| mlan_ds_11ax_sr_cmd *sr_cmd = |
| (mlan_ds_11ax_sr_cmd *) & ds_11ax_cmd->param; |
| mlan_ds_11ax_beam_cmd *beam_cmd = |
| (mlan_ds_11ax_beam_cmd *) & ds_11ax_cmd->param; |
| mlan_ds_11ax_htc_cmd *htc_cmd = |
| (mlan_ds_11ax_htc_cmd *) & ds_11ax_cmd->param; |
| mlan_ds_11ax_txop_cmd *txop_cmd = |
| (mlan_ds_11ax_txop_cmd *) & ds_11ax_cmd->param; |
| mlan_ds_11ax_txomi_cmd *txomi_cmd = |
| (mlan_ds_11ax_txomi_cmd *) & ds_11ax_cmd->param; |
| mlan_ds_11ax_toltime_cmd *toltime_cmd = |
| (mlan_ds_11ax_toltime_cmd *) & ds_11ax_cmd->param; |
| MrvlIEtypes_Data_t *tlv = MNULL; |
| |
| ENTER(); |
| cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11AX_CMD); |
| cmd->size = sizeof(HostCmd_DS_11AX_CMD_CFG) + S_DS_GEN; |
| |
| axcmd->action = wlan_cpu_to_le16(cmd_action); |
| axcmd->sub_id = wlan_cpu_to_le16(ds_11ax_cmd->sub_id); |
| switch (ds_11ax_cmd->sub_id) { |
| case MLAN_11AXCMD_SR_SUBID: |
| tlv = (MrvlIEtypes_Data_t *)axcmd->val; |
| tlv->header.type = wlan_cpu_to_le16(sr_cmd->type); |
| tlv->header.len = wlan_cpu_to_le16(sr_cmd->len); |
| memcpy_ext(pmadapter, tlv->data, |
| &sr_cmd->param.obss_pd_offset.offset, sr_cmd->len, |
| sr_cmd->len); |
| cmd->size += sizeof(MrvlIEtypesHeader_t) + sr_cmd->len; |
| break; |
| case MLAN_11AXCMD_BEAM_SUBID: |
| axcmd->val[0] = beam_cmd->value; |
| cmd->size += sizeof(t_u8); |
| break; |
| case MLAN_11AXCMD_HTC_SUBID: |
| axcmd->val[0] = htc_cmd->value; |
| cmd->size += sizeof(t_u8); |
| break; |
| case MLAN_11AXCMD_TXOPRTS_SUBID: |
| memcpy_ext(pmadapter, axcmd->val, &txop_cmd->rts_thres, |
| sizeof(t_u16), sizeof(t_u16)); |
| cmd->size += sizeof(t_u16); |
| break; |
| case MLAN_11AXCMD_TXOMI_SUBID: |
| memcpy_ext(pmadapter, axcmd->val, &txomi_cmd->omi, |
| sizeof(t_u16), sizeof(t_u16)); |
| cmd->size += sizeof(t_u16); |
| break; |
| case MLAN_11AXCMD_OBSS_TOLTIME_SUBID: |
| memcpy_ext(pmadapter, axcmd->val, &toltime_cmd->tol_time, |
| sizeof(t_u32), sizeof(t_u32)); |
| cmd->size += sizeof(t_u32); |
| break; |
| default: |
| PRINTM(MERROR, "Unknown subcmd %x\n", ds_11ax_cmd->sub_id); |
| break; |
| } |
| |
| cmd->size = wlan_cpu_to_le16(cmd->size); |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function handles the command response of 11axcmd |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param resp A pointer to HostCmd_DS_COMMAND |
| * @param pioctl_buf A pointer to mlan_ioctl_req structure |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_ret_11ax_cmd(pmlan_private pmpriv, |
| HostCmd_DS_COMMAND *resp, mlan_ioctl_req *pioctl_buf) |
| { |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| mlan_ds_11ax_cmd_cfg *cfg = MNULL; |
| HostCmd_DS_11AX_CMD_CFG *axcmd = &resp->params.axcmd; |
| MrvlIEtypes_Data_t *tlv = MNULL; |
| t_s16 left_len = 0; |
| t_u16 tlv_len = 0; |
| |
| ENTER(); |
| |
| if (pioctl_buf == MNULL) |
| goto done; |
| |
| cfg = (mlan_ds_11ax_cmd_cfg *) pioctl_buf->pbuf; |
| cfg->sub_id = wlan_le16_to_cpu(axcmd->sub_id); |
| |
| switch (axcmd->sub_id) { |
| case MLAN_11AXCMD_SR_SUBID: |
| /* TLV parse */ |
| left_len = |
| resp->size - sizeof(HostCmd_DS_11AX_CMD_CFG) - S_DS_GEN; |
| // tlv = (MrvlIEtypes_Extension_t *)axcfg->val; |
| tlv = (MrvlIEtypes_Data_t *)axcmd->val; |
| while (left_len > (t_s16)sizeof(MrvlIEtypesHeader_t)) { |
| tlv_len = wlan_le16_to_cpu(tlv->header.len); |
| memcpy_ext(pmadapter, |
| cfg->param.sr_cfg.param.obss_pd_offset. |
| offset, tlv->data, tlv_len, tlv_len); |
| left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); |
| tlv = (MrvlIEtypes_Data_t |
| *)((t_u8 *)tlv + tlv_len + |
| sizeof(MrvlIEtypesHeader_t)); |
| } |
| break; |
| case MLAN_11AXCMD_BEAM_SUBID: |
| cfg->param.beam_cfg.value = *axcmd->val; |
| break; |
| case MLAN_11AXCMD_HTC_SUBID: |
| cfg->param.htc_cfg.value = *axcmd->val; |
| break; |
| case MLAN_11AXCMD_TXOPRTS_SUBID: |
| memcpy_ext(pmadapter, &cfg->param.txop_cfg.rts_thres, |
| axcmd->val, sizeof(t_u16), sizeof(t_u16)); |
| break; |
| case MLAN_11AXCMD_TXOMI_SUBID: |
| memcpy_ext(pmadapter, &cfg->param.txomi_cfg.omi, axcmd->val, |
| sizeof(t_u16), sizeof(t_u16)); |
| break; |
| case MLAN_11AXCMD_OBSS_TOLTIME_SUBID: |
| memcpy_ext(pmadapter, &cfg->param.toltime_cfg.tol_time, |
| axcmd->val, sizeof(t_u32), sizeof(t_u32)); |
| break; |
| default: |
| PRINTM(MERROR, "Unknown subcmd %x\n", axcmd->sub_id); |
| break; |
| } |
| |
| done: |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function prepares TWT cfg command to configure |
| * setup/teardown |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param cmd A pointer to HostCmd_DS_COMMAND structure |
| * @param cmd_action The action: GET or SET |
| * @param pdata_buf A pointer to data buffer |
| * @return Status returned |
| */ |
| mlan_status |
| wlan_cmd_twt_cfg(pmlan_private pmpriv, |
| HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, t_void *pdata_buf) |
| { |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| HostCmd_DS_TWT_CFG *hostcmd_twtcfg = |
| (HostCmd_DS_TWT_CFG *) & cmd->params.twtcfg; |
| mlan_ds_twtcfg *ds_twtcfg = (mlan_ds_twtcfg *) pdata_buf; |
| hostcmd_twt_setup *twt_setup_params = MNULL; |
| hostcmd_twt_teardown *twt_teardown_params = MNULL; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TWT_CFG); |
| |
| hostcmd_twtcfg->action = wlan_cpu_to_le16(cmd_action); |
| hostcmd_twtcfg->sub_id = wlan_cpu_to_le16(ds_twtcfg->sub_id); |
| |
| cmd->size = S_DS_GEN + sizeof(hostcmd_twtcfg->action) + |
| sizeof(hostcmd_twtcfg->sub_id); |
| |
| switch (hostcmd_twtcfg->sub_id) { |
| case MLAN_11AX_TWT_SETUP_SUBID: |
| twt_setup_params = &hostcmd_twtcfg->param.twt_setup; |
| memset(pmadapter, twt_setup_params, 0x00, |
| sizeof(hostcmd_twtcfg->param.twt_setup)); |
| twt_setup_params->implicit = |
| ds_twtcfg->param.twt_setup.implicit; |
| twt_setup_params->announced = |
| ds_twtcfg->param.twt_setup.announced; |
| twt_setup_params->trigger_enabled = |
| ds_twtcfg->param.twt_setup.trigger_enabled; |
| twt_setup_params->twt_info_disabled = |
| ds_twtcfg->param.twt_setup.twt_info_disabled; |
| twt_setup_params->negotiation_type = |
| ds_twtcfg->param.twt_setup.negotiation_type; |
| twt_setup_params->twt_wakeup_duration = |
| ds_twtcfg->param.twt_setup.twt_wakeup_duration; |
| twt_setup_params->flow_identifier = |
| ds_twtcfg->param.twt_setup.flow_identifier; |
| twt_setup_params->hard_constraint = |
| ds_twtcfg->param.twt_setup.hard_constraint; |
| twt_setup_params->twt_exponent = |
| ds_twtcfg->param.twt_setup.twt_exponent; |
| twt_setup_params->twt_mantissa = |
| wlan_cpu_to_le16(ds_twtcfg->param.twt_setup. |
| twt_mantissa); |
| twt_setup_params->twt_request = |
| ds_twtcfg->param.twt_setup.twt_request; |
| cmd->size += sizeof(hostcmd_twtcfg->param.twt_setup); |
| break; |
| case MLAN_11AX_TWT_TEARDOWN_SUBID: |
| twt_teardown_params = &hostcmd_twtcfg->param.twt_teardown; |
| memset(pmadapter, twt_teardown_params, 0x00, |
| sizeof(hostcmd_twtcfg->param.twt_teardown)); |
| twt_teardown_params->flow_identifier = |
| ds_twtcfg->param.twt_teardown.flow_identifier; |
| twt_teardown_params->negotiation_type = |
| ds_twtcfg->param.twt_teardown.negotiation_type; |
| twt_teardown_params->teardown_all_twt = |
| ds_twtcfg->param.twt_teardown.teardown_all_twt; |
| cmd->size += sizeof(hostcmd_twtcfg->param.twt_teardown); |
| break; |
| default: |
| PRINTM(MERROR, "Unknown subcmd %x\n", ds_twtcfg->sub_id); |
| ret = MLAN_STATUS_FAILURE; |
| break; |
| } |
| |
| cmd->size = wlan_cpu_to_le16(cmd->size); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief TWT config command handler |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pioctl_req A pointer to ioctl request buffer |
| * |
| * @return MLAN_STATUS_SUCCESS --success, otherwise fail |
| */ |
| mlan_status |
| wlan_11ax_ioctl_twtcfg(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; |
| mlan_ds_twtcfg *cfg = MNULL; |
| t_u16 cmd_action = 0; |
| |
| ENTER(); |
| |
| if (pioctl_req->buf_len < sizeof(mlan_ds_twtcfg)) { |
| PRINTM(MERROR, "MLAN bss IOCTL length is too short.\n"); |
| pioctl_req->data_read_written = 0; |
| pioctl_req->buf_len_needed = sizeof(mlan_ds_twtcfg); |
| pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; |
| LEAVE(); |
| return MLAN_STATUS_RESOURCE; |
| } |
| |
| cfg = (mlan_ds_twtcfg *) pioctl_req->pbuf; |
| |
| if (pioctl_req->action == MLAN_ACT_SET) |
| cmd_action = HostCmd_ACT_GEN_SET; |
| else |
| cmd_action = HostCmd_ACT_GEN_GET; |
| |
| /* Send request to firmware */ |
| ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TWT_CFG, cmd_action, 0, |
| (t_void *)pioctl_req, (t_void *)cfg); |
| if (ret == MLAN_STATUS_SUCCESS) |
| ret = MLAN_STATUS_PENDING; |
| |
| LEAVE(); |
| return ret; |
| } |