// 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) return false; if (!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; }