// SPDX-License-Identifier: GPL-2.0-only /* * nxpwifi: station command 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" #include "11ac.h" #include "11ax.h" static bool disable_auto_ds; static int nxpwifi_cmd_sta_get_hw_spec(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; cmd->command = cpu_to_le16(HOST_CMD_GET_HW_SPEC); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN); memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); return 0; } static int nxpwifi_ret_sta_get_hw_spec(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; struct nxpwifi_adapter *adapter = priv->adapter; struct nxpwifi_ie_types_header *tlv; struct hw_spec_api_rev *api_rev; struct hw_spec_max_conn *max_conn; struct hw_spec_extension *hw_he_cap; struct hw_spec_fw_cap_info *fw_cap; struct hw_spec_secure_boot_uuid *sb_uuid; u16 resp_size, api_id; int i, left_len, parsed_len = 0; adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); if (IS_SUPPORT_MULTI_BANDS(adapter)) adapter->fw_bands = GET_FW_DEFAULT_BANDS(adapter); else adapter->fw_bands = BAND_B; if ((adapter->fw_bands & BAND_A) && (adapter->fw_bands & BAND_GN)) adapter->fw_bands |= BAND_AN; if (!(adapter->fw_bands & BAND_G) && (adapter->fw_bands & BAND_GN)) adapter->fw_bands &= ~BAND_GN; adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff; adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna) & 0xf; if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) { adapter->is_hw_11ac_capable = true; /* Copy 11AC cap */ adapter->hw_dot_11ac_dev_cap = le32_to_cpu(hw_spec->dot_11ac_dev_cap); adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap & ~NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK; adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap & ~NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK; /* Copy 11AC mcs */ adapter->hw_dot_11ac_mcs_support = le32_to_cpu(hw_spec->dot_11ac_mcs_support); adapter->usr_dot_11ac_mcs_support = adapter->hw_dot_11ac_mcs_support; } else { adapter->is_hw_11ac_capable = false; } resp_size = le16_to_cpu(resp->size) - S_DS_GEN; if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) { /* we have variable HW SPEC information */ left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec); while (left_len > sizeof(struct nxpwifi_ie_types_header)) { tlv = (void *)&hw_spec->tlv + parsed_len; switch (le16_to_cpu(tlv->type)) { case TLV_TYPE_API_REV: api_rev = (struct hw_spec_api_rev *)tlv; api_id = le16_to_cpu(api_rev->api_id); switch (api_id) { case KEY_API_VER_ID: adapter->key_api_major_ver = api_rev->major_ver; adapter->key_api_minor_ver = api_rev->minor_ver; nxpwifi_dbg(adapter, INFO, "key_api v%d.%d\n", adapter->key_api_major_ver, adapter->key_api_minor_ver); break; case FW_API_VER_ID: adapter->fw_api_ver = api_rev->major_ver; nxpwifi_dbg(adapter, MSG, "Firmware api version %d.%d\n", adapter->fw_api_ver, api_rev->minor_ver); break; case UAP_FW_API_VER_ID: nxpwifi_dbg(adapter, INFO, "uAP api version %d.%d\n", api_rev->major_ver, api_rev->minor_ver); break; case CHANRPT_API_VER_ID: nxpwifi_dbg(adapter, INFO, "channel report api version %d.%d\n", api_rev->major_ver, api_rev->minor_ver); break; case FW_HOTFIX_VER_ID: adapter->fw_hotfix_ver = api_rev->major_ver; nxpwifi_dbg(adapter, INFO, "Firmware hotfix version %d\n", api_rev->major_ver); break; default: nxpwifi_dbg(adapter, FATAL, "Unknown api_id: %d\n", api_id); break; } break; case TLV_TYPE_MAX_CONN: max_conn = (struct hw_spec_max_conn *)tlv; adapter->max_sta_conn = max_conn->max_sta_conn; nxpwifi_dbg(adapter, INFO, "max sta connections: %u\n", adapter->max_sta_conn); break; case TLV_TYPE_EXTENSION_ID: hw_he_cap = (struct hw_spec_extension *)tlv; if (hw_he_cap->ext_id == WLAN_EID_EXT_HE_CAPABILITY) nxpwifi_update_11ax_cap(adapter, hw_he_cap); break; case TLV_TYPE_FW_CAP_INFO: fw_cap = (struct hw_spec_fw_cap_info *)tlv; adapter->fw_cap_info = le32_to_cpu(fw_cap->fw_cap_info); adapter->fw_cap_ext = le32_to_cpu(fw_cap->fw_cap_ext); nxpwifi_dbg(adapter, INFO, "fw_cap_info:%#x fw_cap_ext:%#x\n", adapter->fw_cap_info, adapter->fw_cap_ext); break; case TLV_TYPE_SECURE_BOOT_UUID: sb_uuid = (struct hw_spec_secure_boot_uuid *)tlv; adapter->uuid_lo = le64_to_cpu(sb_uuid->uuid_lo); adapter->uuid_hi = le64_to_cpu(sb_uuid->uuid_hi); nxpwifi_dbg(adapter, INFO, "uuid: %#llx%#llx\n", adapter->uuid_lo, adapter->uuid_hi); break; default: nxpwifi_dbg(adapter, FATAL, "Unknown GET_HW_SPEC TLV type: %#x\n", le16_to_cpu(tlv->type)); break; } parsed_len += le16_to_cpu(tlv->len) + sizeof(struct nxpwifi_ie_types_header); left_len -= le16_to_cpu(tlv->len) + sizeof(struct nxpwifi_ie_types_header); } } if (adapter->key_api_major_ver < KEY_API_VER_MAJOR_V2) return -EOPNOTSUPP; nxpwifi_dbg(adapter, INFO, "info: GET_HW_SPEC: fw_release_number- %#x\n", adapter->fw_release_number); nxpwifi_dbg(adapter, INFO, "info: GET_HW_SPEC: permanent addr: %pM\n", hw_spec->permanent_addr); nxpwifi_dbg(adapter, INFO, "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", le16_to_cpu(hw_spec->hw_if_version), le16_to_cpu(hw_spec->version)); ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr); adapter->region_code = le16_to_cpu(hw_spec->region_code); /* If it's unidentified region code, use the default (USA/FCC) */ if (!nxpwifi_is_valid_region_code(adapter->region_code)) { nxpwifi_dbg(adapter, WARN, "cmd: unknown region code %#x, use default (USA/%#x)\n", adapter->region_code, NXPWIFI_DEFAULT_REGION_CODE); adapter->region_code = NXPWIFI_DEFAULT_REGION_CODE; } adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; adapter->hw_mpdu_density = GET_MPDU_DENSITY(le32_to_cpu(hw_spec->hw_dev_cap)); adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support; adapter->user_htstream = adapter->hw_dev_mcs_support; if (adapter->fw_bands & BAND_A) adapter->user_htstream |= (adapter->user_htstream << 8); if (adapter->if_ops.update_mp_end_port) { u16 mp_end_port; mp_end_port = le16_to_cpu(hw_spec->mp_end_port); adapter->if_ops.update_mp_end_port(adapter, mp_end_port); } if (adapter->fw_api_ver == NXPWIFI_FW_V15) adapter->scan_chan_gap_enabled = true; for (i = 0; i < adapter->priv_num; i++) adapter->priv[i]->config_bands = adapter->fw_bands; return 0; } static int nxpwifi_cmd_sta_802_11_scan(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_802_11_scan(cmd, data_buf); } static int nxpwifi_ret_sta_802_11_scan(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_adapter *adapter = priv->adapter; int ret; ret = nxpwifi_ret_802_11_scan(priv, resp); adapter->curr_cmd->wait_q_enabled = false; return ret; } static int nxpwifi_cmd_sta_802_11_get_log(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(HOST_CMD_802_11_GET_LOG); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + S_DS_GEN); return 0; } static int nxpwifi_ret_sta_802_11_get_log(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_802_11_get_log *get_log = &resp->params.get_log; struct nxpwifi_ds_get_stats *stats = (struct nxpwifi_ds_get_stats *)data_buf; if (stats) { stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); stats->failed = le32_to_cpu(get_log->failed); stats->retry = le32_to_cpu(get_log->retry); stats->multi_retry = le32_to_cpu(get_log->multi_retry); stats->frame_dup = le32_to_cpu(get_log->frame_dup); stats->rts_success = le32_to_cpu(get_log->rts_success); stats->rts_failure = le32_to_cpu(get_log->rts_failure); stats->ack_failure = le32_to_cpu(get_log->ack_failure); stats->rx_frag = le32_to_cpu(get_log->rx_frag); stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); stats->fcs_error = le32_to_cpu(get_log->fcs_error); stats->tx_frame = le32_to_cpu(get_log->tx_frame); stats->wep_icv_error[0] = le32_to_cpu(get_log->wep_icv_err_cnt[0]); stats->wep_icv_error[1] = le32_to_cpu(get_log->wep_icv_err_cnt[1]); stats->wep_icv_error[2] = le32_to_cpu(get_log->wep_icv_err_cnt[2]); stats->wep_icv_error[3] = le32_to_cpu(get_log->wep_icv_err_cnt[3]); stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt); stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt); } return 0; } static int nxpwifi_cmd_sta_mac_multicast_adr(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; struct nxpwifi_multicast_list *mcast_list = (struct nxpwifi_multicast_list *)data_buf; cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + S_DS_GEN); cmd->command = cpu_to_le16(HOST_CMD_MAC_MULTICAST_ADR); mcast_addr->action = cpu_to_le16(cmd_action); mcast_addr->num_of_adrs = cpu_to_le16((u16)mcast_list->num_multicast_addr); memcpy(mcast_addr->mac_list, mcast_list->mac_list, mcast_list->num_multicast_addr * ETH_ALEN); return 0; } static int nxpwifi_cmd_sta_802_11_associate(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_802_11_associate(priv, cmd, data_buf); } static int nxpwifi_ret_sta_802_11_associate(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_802_11_associate(priv, resp); } static int nxpwifi_cmd_sta_802_11_snmp_mib(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib; u16 *ul_temp = (u16 *)data_buf; nxpwifi_dbg(priv->adapter, CMD, "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_type); cmd->command = cpu_to_le16(HOST_CMD_802_11_SNMP_MIB); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) + S_DS_GEN); snmp_mib->oid = cpu_to_le16((u16)cmd_type); if (cmd_action == HOST_ACT_GEN_GET) { snmp_mib->query_type = cpu_to_le16(HOST_ACT_GEN_GET); snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); le16_unaligned_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); } else if (cmd_action == HOST_ACT_GEN_SET) { snmp_mib->query_type = cpu_to_le16(HOST_ACT_GEN_SET); snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); put_unaligned_le16(*ul_temp, snmp_mib->value); le16_unaligned_add_cpu(&cmd->size, sizeof(u16)); } nxpwifi_dbg(priv->adapter, CMD, "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t" "OIDSize=0x%x, Value=0x%x\n", cmd_action, cmd_type, le16_to_cpu(snmp_mib->buf_size), get_unaligned_le16(snmp_mib->value)); return 0; } static int nxpwifi_ret_sta_802_11_snmp_mib(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; u16 oid = le16_to_cpu(smib->oid); u16 query_type = le16_to_cpu(smib->query_type); u32 ul_temp; nxpwifi_dbg(priv->adapter, INFO, "info: SNMP_RESP: oid value = %#x,\t" "query_type = %#x, buf size = %#x\n", oid, query_type, le16_to_cpu(smib->buf_size)); if (query_type == HOST_ACT_GEN_GET) { ul_temp = get_unaligned_le16(smib->value); if (data_buf) *(u32 *)data_buf = ul_temp; switch (oid) { case FRAG_THRESH_I: nxpwifi_dbg(priv->adapter, INFO, "info: SNMP_RESP: FragThsd =%u\n", ul_temp); break; case RTS_THRESH_I: nxpwifi_dbg(priv->adapter, INFO, "info: SNMP_RESP: RTSThsd =%u\n", ul_temp); break; case SHORT_RETRY_LIM_I: nxpwifi_dbg(priv->adapter, INFO, "info: SNMP_RESP: TxRetryCount=%u\n", ul_temp); break; case DTIM_PERIOD_I: nxpwifi_dbg(priv->adapter, INFO, "info: SNMP_RESP: DTIM period=%u\n", ul_temp); break; default: break; } } return 0; } static int nxpwifi_cmd_sta_reg_access(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct nxpwifi_ds_reg_rw *reg_rw = data_buf; cmd->command = cpu_to_le16(cmd_no); switch (cmd_no) { case HOST_CMD_MAC_REG_ACCESS: { struct host_cmd_ds_mac_reg_access *mac_reg; cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); mac_reg = &cmd->params.mac_reg; mac_reg->action = cpu_to_le16(cmd_action); mac_reg->offset = cpu_to_le16((u16)reg_rw->offset); mac_reg->value = cpu_to_le32(reg_rw->value); break; } case HOST_CMD_BBP_REG_ACCESS: { struct host_cmd_ds_bbp_reg_access *bbp_reg; cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); bbp_reg = &cmd->params.bbp_reg; bbp_reg->action = cpu_to_le16(cmd_action); bbp_reg->offset = cpu_to_le16((u16)reg_rw->offset); bbp_reg->value = (u8)reg_rw->value; break; } case HOST_CMD_RF_REG_ACCESS: { struct host_cmd_ds_rf_reg_access *rf_reg; cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); rf_reg = &cmd->params.rf_reg; rf_reg->action = cpu_to_le16(cmd_action); rf_reg->offset = cpu_to_le16((u16)reg_rw->offset); rf_reg->value = (u8)reg_rw->value; break; } case HOST_CMD_PMIC_REG_ACCESS: { struct host_cmd_ds_pmic_reg_access *pmic_reg; cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); pmic_reg = &cmd->params.pmic_reg; pmic_reg->action = cpu_to_le16(cmd_action); pmic_reg->offset = cpu_to_le16((u16)reg_rw->offset); pmic_reg->value = (u8)reg_rw->value; break; } case HOST_CMD_CAU_REG_ACCESS: { struct host_cmd_ds_rf_reg_access *cau_reg; cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); cau_reg = &cmd->params.rf_reg; cau_reg->action = cpu_to_le16(cmd_action); cau_reg->offset = cpu_to_le16((u16)reg_rw->offset); cau_reg->value = (u8)reg_rw->value; break; } case HOST_CMD_802_11_EEPROM_ACCESS: { struct nxpwifi_ds_read_eeprom *rd_eeprom = data_buf; struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = &cmd->params.eeprom; cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); cmd_eeprom->action = cpu_to_le16(cmd_action); cmd_eeprom->offset = cpu_to_le16(rd_eeprom->offset); cmd_eeprom->byte_count = cpu_to_le16(rd_eeprom->byte_count); cmd_eeprom->value = 0; break; } default: return -EINVAL; } return 0; } static int nxpwifi_ret_sta_reg_access(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_ds_reg_rw *reg_rw; struct nxpwifi_ds_read_eeprom *eeprom; union reg { struct host_cmd_ds_mac_reg_access *mac; struct host_cmd_ds_bbp_reg_access *bbp; struct host_cmd_ds_rf_reg_access *rf; struct host_cmd_ds_pmic_reg_access *pmic; struct host_cmd_ds_802_11_eeprom_access *eeprom; } r; if (!data_buf) return 0; reg_rw = data_buf; eeprom = data_buf; switch (cmdresp_no) { case HOST_CMD_MAC_REG_ACCESS: r.mac = &resp->params.mac_reg; reg_rw->offset = (u32)le16_to_cpu(r.mac->offset); reg_rw->value = le32_to_cpu(r.mac->value); break; case HOST_CMD_BBP_REG_ACCESS: r.bbp = &resp->params.bbp_reg; reg_rw->offset = (u32)le16_to_cpu(r.bbp->offset); reg_rw->value = (u32)r.bbp->value; break; case HOST_CMD_RF_REG_ACCESS: r.rf = &resp->params.rf_reg; reg_rw->offset = (u32)le16_to_cpu(r.rf->offset); reg_rw->value = (u32)r.bbp->value; break; case HOST_CMD_PMIC_REG_ACCESS: r.pmic = &resp->params.pmic_reg; reg_rw->offset = (u32)le16_to_cpu(r.pmic->offset); reg_rw->value = (u32)r.pmic->value; break; case HOST_CMD_CAU_REG_ACCESS: r.rf = &resp->params.rf_reg; reg_rw->offset = (u32)le16_to_cpu(r.rf->offset); reg_rw->value = (u32)r.rf->value; break; case HOST_CMD_802_11_EEPROM_ACCESS: r.eeprom = &resp->params.eeprom; pr_debug("info: EEPROM read len=%x\n", le16_to_cpu(r.eeprom->byte_count)); if (eeprom->byte_count < le16_to_cpu(r.eeprom->byte_count)) { eeprom->byte_count = 0; pr_debug("info: EEPROM read length is too big\n"); return -ENOMEM; } eeprom->offset = le16_to_cpu(r.eeprom->offset); eeprom->byte_count = le16_to_cpu(r.eeprom->byte_count); if (eeprom->byte_count > 0) memcpy(&eeprom->value, &r.eeprom->value, min((u16)MAX_EEPROM_DATA, eeprom->byte_count)); break; default: return -EINVAL; } return 0; } static int nxpwifi_cmd_sta_rf_tx_pwr(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp; cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) + S_DS_GEN); cmd->command = cpu_to_le16(HOST_CMD_RF_TX_PWR); txp->action = cpu_to_le16(cmd_action); return 0; } static int nxpwifi_ret_sta_rf_tx_pwr(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp; u16 action = le16_to_cpu(txp->action); priv->tx_power_level = le16_to_cpu(txp->cur_level); if (action == HOST_ACT_GEN_GET) { priv->max_tx_power_level = txp->max_power; priv->min_tx_power_level = txp->min_power; } nxpwifi_dbg(priv->adapter, INFO, "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n", priv->tx_power_level, priv->max_tx_power_level, priv->min_tx_power_level); return 0; } static int nxpwifi_cmd_sta_rf_antenna(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo; struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso; struct nxpwifi_ds_ant_cfg *ant_cfg = (struct nxpwifi_ds_ant_cfg *)data_buf; cmd->command = cpu_to_le16(HOST_CMD_RF_ANTENNA); switch (cmd_action) { case HOST_ACT_GEN_SET: if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_mimo) + S_DS_GEN); ant_mimo->action_tx = cpu_to_le16(HOST_ACT_SET_TX); ant_mimo->tx_ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); ant_mimo->action_rx = cpu_to_le16(HOST_ACT_SET_RX); ant_mimo->rx_ant_mode = cpu_to_le16((u16)ant_cfg->rx_ant); } else { cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_siso) + S_DS_GEN); ant_siso->action = cpu_to_le16(HOST_ACT_SET_BOTH); ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); } break; case HOST_ACT_GEN_GET: if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_mimo) + S_DS_GEN); ant_mimo->action_tx = cpu_to_le16(HOST_ACT_GET_TX); ant_mimo->action_rx = cpu_to_le16(HOST_ACT_GET_RX); } else { cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_siso) + S_DS_GEN); ant_siso->action = cpu_to_le16(HOST_ACT_GET_BOTH); } break; } return 0; } static int nxpwifi_ret_sta_rf_antenna(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo; struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso; struct nxpwifi_adapter *adapter = priv->adapter; if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) { priv->tx_ant = le16_to_cpu(ant_mimo->tx_ant_mode); priv->rx_ant = le16_to_cpu(ant_mimo->rx_ant_mode); nxpwifi_dbg(adapter, INFO, "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x\t" "Rx action = 0x%x, Rx Mode = 0x%04x\n", le16_to_cpu(ant_mimo->action_tx), le16_to_cpu(ant_mimo->tx_ant_mode), le16_to_cpu(ant_mimo->action_rx), le16_to_cpu(ant_mimo->rx_ant_mode)); } else { priv->tx_ant = le16_to_cpu(ant_siso->ant_mode); priv->rx_ant = le16_to_cpu(ant_siso->ant_mode); nxpwifi_dbg(adapter, INFO, "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n", le16_to_cpu(ant_siso->action), le16_to_cpu(ant_siso->ant_mode)); } return 0; } static int nxpwifi_cmd_sta_802_11_deauthenticate(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; u8 *mac = (u8 *)data_buf; cmd->command = cpu_to_le16(HOST_CMD_802_11_DEAUTHENTICATE); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) + S_DS_GEN); /* Set AP MAC address */ memcpy(deauth->mac_addr, mac, ETH_ALEN); nxpwifi_dbg(priv->adapter, CMD, "cmd: Deauth: %pM\n", deauth->mac_addr); deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); return 0; } static int nxpwifi_ret_sta_802_11_deauthenticate(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_adapter *adapter = priv->adapter; adapter->dbg.num_cmd_deauth++; if (!memcmp(resp->params.deauth.mac_addr, &priv->curr_bss_params.bss_descriptor.mac_address, sizeof(resp->params.deauth.mac_addr))) nxpwifi_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING, false); return 0; } static int nxpwifi_cmd_sta_mac_control(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl; u32 *action = (u32 *)data_buf; if (cmd_action != HOST_ACT_GEN_SET) { nxpwifi_dbg(priv->adapter, ERROR, "mac_control: only support set cmd\n"); return -EINVAL; } cmd->command = cpu_to_le16(HOST_CMD_MAC_CONTROL); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); mac_ctrl->action = cpu_to_le32(*action); return 0; } static int nxpwifi_cmd_sta_802_11_mac_address(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(HOST_CMD_802_11_MAC_ADDRESS); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + S_DS_GEN); cmd->result = 0; cmd->params.mac_addr.action = cpu_to_le16(cmd_action); if (cmd_action == HOST_ACT_GEN_SET) memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, ETH_ALEN); return 0; } static int nxpwifi_ret_sta_802_11_mac_address(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_802_11_mac_address *cmd_mac_addr; cmd_mac_addr = &resp->params.mac_addr; memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); nxpwifi_dbg(priv->adapter, INFO, "info: set mac address: %pM\n", priv->curr_addr); return 0; } static int nxpwifi_cmd_sta_802_11d_domain_info(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct nxpwifi_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11d_domain_info *domain_info = &cmd->params.domain_info; struct nxpwifi_ietypes_domain_param_set *domain = &domain_info->domain; struct nxpwifi_ietypes_domain_code *domain_code; u8 no_of_triplet = adapter->domain_reg.no_of_triplet; int triplet_size; nxpwifi_dbg(adapter, INFO, "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); cmd->command = cpu_to_le16(HOST_CMD_802_11D_DOMAIN_INFO); cmd->size = cpu_to_le16(S_DS_GEN); domain_info->action = cpu_to_le16(cmd_action); le16_unaligned_add_cpu(&cmd->size, sizeof(domain_info->action)); if (cmd_action == HOST_ACT_GEN_GET) return 0; triplet_size = no_of_triplet * sizeof(struct ieee80211_country_ie_triplet); domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); domain->header.len = cpu_to_le16(sizeof(domain->country_code) + triplet_size); memcpy(domain->country_code, adapter->domain_reg.country_code, sizeof(domain->country_code)); if (no_of_triplet) memcpy(domain->triplet, adapter->domain_reg.triplet, triplet_size); le16_unaligned_add_cpu(&cmd->size, sizeof(*domain) + triplet_size); domain_code = (struct nxpwifi_ietypes_domain_code *)((u8 *)cmd + le16_to_cpu(cmd->size)); domain_code->header.type = cpu_to_le16(TLV_TYPE_REGION_DOMAIN_CODE); domain_code->header.len = cpu_to_le16(sizeof(*domain_code) - sizeof(struct nxpwifi_ie_types_header)); le16_unaligned_add_cpu(&cmd->size, sizeof(*domain_code)); return 0; } static int nxpwifi_ret_sta_802_11d_domain_info(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = &resp->params.domain_info_resp; struct nxpwifi_ietypes_domain_param_set *domain = &domain_info->domain; u16 action = le16_to_cpu(domain_info->action); u8 no_of_triplet; no_of_triplet = (u8)((le16_to_cpu(domain->header.len) - IEEE80211_COUNTRY_STRING_LEN) / sizeof(struct ieee80211_country_ie_triplet)); nxpwifi_dbg(priv->adapter, INFO, "info: 11D Domain Info Resp: no_of_triplet=%d\n", no_of_triplet); if (no_of_triplet > NXPWIFI_MAX_TRIPLET_802_11D) { nxpwifi_dbg(priv->adapter, FATAL, "11D: invalid number of triplets %d returned\n", no_of_triplet); return -EINVAL; } switch (action) { case HOST_ACT_GEN_SET: /* Proc Set Action */ break; case HOST_ACT_GEN_GET: break; default: nxpwifi_dbg(priv->adapter, ERROR, "11D: invalid action:%d\n", domain_info->action); return -EINVAL; } return 0; } static int nxpwifi_set_aes_key(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, struct nxpwifi_ds_encrypt_key *enc_key, struct host_cmd_ds_802_11_key_material *km) { struct nxpwifi_adapter *adapter = priv->adapter; u16 size, len = KEY_PARAMS_FIXED_LEN; u8 key_type, key_type_igtk; if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { key_type = KEY_TYPE_ID_AES; key_type_igtk = KEY_TYPE_ID_AES_CMAC; } else { key_type = KEY_TYPE_ID_GCMP_256; key_type_igtk = KEY_TYPE_ID_BIP_GMAC_256; } if (enc_key->is_igtk_key) { km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST); km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); km->key_param_set.key_type = key_type_igtk; if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { nxpwifi_dbg(adapter, INFO, "%s: Set CMAC AES Key\n", __func__); if (enc_key->is_rx_seq_valid) memcpy(km->key_param_set.key_params.cmac_aes.ipn, enc_key->pn, enc_key->pn_len); km->key_param_set.key_params.cmac_aes.key_len = cpu_to_le16(enc_key->key_len); memcpy(km->key_param_set.key_params.cmac_aes.key, enc_key->key_material, enc_key->key_len); len += sizeof(struct nxpwifi_cmac_aes_param); } else { nxpwifi_dbg(adapter, INFO, "%s: Set GMAC AES Key\n", __func__); if (enc_key->is_rx_seq_valid) memcpy(km->key_param_set.key_params.gmac_aes.ipn, enc_key->pn, enc_key->pn_len); km->key_param_set.key_params.gmac_aes.key_len = cpu_to_le16(enc_key->key_len); memcpy(km->key_param_set.key_params.gmac_aes.key, enc_key->key_material, enc_key->key_len); len += sizeof(struct nxpwifi_gmac_aes_param); } } else if (enc_key->is_igtk_def_key) { nxpwifi_dbg(adapter, INFO, "%s: Set CMAC default Key index\n", __func__); km->key_param_set.key_type = key_type_igtk; km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; } else { nxpwifi_dbg(adapter, INFO, "%s: Set AES Key\n", __func__); if (enc_key->is_rx_seq_valid) memcpy(km->key_param_set.key_params.aes.pn, enc_key->pn, enc_key->pn_len); km->key_param_set.key_type = key_type; km->key_param_set.key_params.aes.key_len = cpu_to_le16(enc_key->key_len); memcpy(km->key_param_set.key_params.aes.key, enc_key->key_material, enc_key->key_len); len += sizeof(struct nxpwifi_aes_param); } km->key_param_set.len = cpu_to_le16(len); size = len + sizeof(struct nxpwifi_ie_types_header) + sizeof(km->action) + S_DS_GEN; cmd->size = cpu_to_le16(size); return 0; } static int nxpwifi_cmd_sta_802_11_key_material(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct nxpwifi_adapter *adapter = priv->adapter; struct nxpwifi_ds_encrypt_key *enc_key = (struct nxpwifi_ds_encrypt_key *)data_buf; u8 *mac = enc_key->mac_addr; u16 key_info, len = KEY_PARAMS_FIXED_LEN; struct host_cmd_ds_802_11_key_material *km = &cmd->params.key_material; cmd->command = cpu_to_le16(HOST_CMD_802_11_KEY_MATERIAL); km->action = cpu_to_le16(cmd_action); if (cmd_action == HOST_ACT_GEN_GET) { nxpwifi_dbg(adapter, INFO, "%s: Get key\n", __func__); km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); ether_addr_copy(km->key_param_set.mac_addr, mac); if (enc_key->key_index & NXPWIFI_KEY_INDEX_UNICAST) key_info = KEY_UNICAST; else key_info = KEY_MCAST; if (enc_key->is_igtk_key) key_info |= KEY_IGTK; km->key_param_set.key_info = cpu_to_le16(key_info); cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + S_DS_GEN + KEY_PARAMS_FIXED_LEN + sizeof(km->action)); return 0; } memset(&km->key_param_set, 0, sizeof(struct nxpwifi_ie_type_key_param_set)); if (enc_key->key_disable) { nxpwifi_dbg(adapter, INFO, "%s: Remove key\n", __func__); km->action = cpu_to_le16(HOST_ACT_GEN_REMOVE); km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; key_info = KEY_MCAST | KEY_UNICAST; km->key_param_set.key_info = cpu_to_le16(key_info); ether_addr_copy(km->key_param_set.mac_addr, mac); cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + S_DS_GEN + KEY_PARAMS_FIXED_LEN + sizeof(km->action)); return 0; } km->action = cpu_to_le16(HOST_ACT_GEN_SET); km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); key_info = KEY_ENABLED; ether_addr_copy(km->key_param_set.mac_addr, mac); if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) { nxpwifi_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__); len += sizeof(struct nxpwifi_wep_param); km->key_param_set.len = cpu_to_le16(len); km->key_param_set.key_type = KEY_TYPE_ID_WEP; if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { key_info |= KEY_MCAST | KEY_UNICAST; } else { if (enc_key->is_current_wep_key) { key_info |= KEY_MCAST | KEY_UNICAST; if (km->key_param_set.key_idx == (priv->wep_key_curr_index & KEY_INDEX_MASK)) key_info |= KEY_DEFAULT; } else { if (is_broadcast_ether_addr(mac)) key_info |= KEY_MCAST; else key_info |= KEY_UNICAST | KEY_DEFAULT; } } km->key_param_set.key_info = cpu_to_le16(key_info); km->key_param_set.key_params.wep.key_len = cpu_to_le16(enc_key->key_len); memcpy(km->key_param_set.key_params.wep.key, enc_key->key_material, enc_key->key_len); cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + len + sizeof(km->action) + S_DS_GEN); return 0; } if (is_broadcast_ether_addr(mac)) key_info |= KEY_MCAST | KEY_RX_KEY; else key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; /* Enable default key for WPA/WPA2 */ if (!priv->wpa_is_gtk_set) key_info |= KEY_DEFAULT; km->key_param_set.key_info = cpu_to_le16(key_info); if (enc_key->key_cipher != WLAN_CIPHER_SUITE_TKIP && enc_key->key_len >= WLAN_KEY_LEN_CCMP) return nxpwifi_set_aes_key(priv, cmd, enc_key, km); if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { nxpwifi_dbg(adapter, INFO, "%s: Set TKIP Key\n", __func__); if (enc_key->is_rx_seq_valid) memcpy(km->key_param_set.key_params.tkip.pn, enc_key->pn, enc_key->pn_len); km->key_param_set.key_type = KEY_TYPE_ID_TKIP; km->key_param_set.key_params.tkip.key_len = cpu_to_le16(enc_key->key_len); memcpy(km->key_param_set.key_params.tkip.key, enc_key->key_material, enc_key->key_len); len += sizeof(struct nxpwifi_tkip_param); km->key_param_set.len = cpu_to_le16(len); cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + len + sizeof(km->action) + S_DS_GEN); } return 0; } static int nxpwifi_ret_sta_802_11_key_material(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_802_11_key_material *key; int len; key = &resp->params.key_material; len = le16_to_cpu(key->key_param_set.key_params.aes.key_len); if (len > sizeof(key->key_param_set.key_params.aes.key)) return -EINVAL; if (le16_to_cpu(key->action) == HOST_ACT_GEN_SET) { if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) { nxpwifi_dbg(priv->adapter, INFO, "info: key: GTK is set\n"); priv->wpa_is_gtk_set = true; priv->scan_block = false; priv->port_open = true; } } if (key->key_param_set.key_type != KEY_TYPE_ID_AES && key->key_param_set.key_type != KEY_TYPE_ID_GCMP_256) return 0; memset(priv->aes_key.key_param_set.key_params.aes.key, 0, sizeof(key->key_param_set.key_params.aes.key)); priv->aes_key.key_param_set.key_params.aes.key_len = cpu_to_le16(len); memcpy(priv->aes_key.key_param_set.key_params.aes.key, key->key_param_set.key_params.aes.key, len); return 0; } static int nxpwifi_cmd_sta_802_11_bg_scan_config(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_802_11_bg_scan_config(priv, cmd, data_buf); } static int nxpwifi_cmd_sta_802_11_bg_scan_query(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_802_11_bg_scan_query(cmd); } static int nxpwifi_ret_sta_802_11_bg_scan_query(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_adapter *adapter = priv->adapter; int ret; ret = nxpwifi_ret_802_11_scan(priv, resp); cfg80211_sched_scan_results(priv->wdev.wiphy, 0); nxpwifi_dbg(adapter, CMD, "info: CMD_RESP: BG_SCAN result is ready!\n"); return ret; } static int nxpwifi_cmd_sta_wmm_get_status(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(HOST_CMD_WMM_GET_STATUS); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + S_DS_GEN); return 0; } static int nxpwifi_ret_sta_wmm_get_status(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_wmm_get_status(priv, resp); } static int nxpwifi_cmd_sta_802_11_subsc_evt(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt; struct nxpwifi_ds_misc_subsc_evt *subsc_evt_cfg = (struct nxpwifi_ds_misc_subsc_evt *)data_buf; struct nxpwifi_ie_types_rssi_threshold *rssi_tlv; u16 event_bitmap; u8 *pos; cmd->command = cpu_to_le16(HOST_CMD_802_11_SUBSCRIBE_EVENT); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) + S_DS_GEN); subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action); nxpwifi_dbg(priv->adapter, CMD, "cmd: action: %d\n", subsc_evt_cfg->action); /* For query requests, no configuration TLV structures are to be added. */ if (subsc_evt_cfg->action == HOST_ACT_GEN_GET) return 0; subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events); event_bitmap = subsc_evt_cfg->events; nxpwifi_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n", event_bitmap); if ((subsc_evt_cfg->action == HOST_ACT_BITWISE_CLR || subsc_evt_cfg->action == HOST_ACT_BITWISE_SET) && event_bitmap == 0) { nxpwifi_dbg(priv->adapter, ERROR, "Error: No event specified\t" "for bitwise action type\n"); return -EINVAL; } /* * Append TLV structures for each of the specified events for * subscribing or re-configuring. This is not required for * bitwise unsubscribing request. */ if (subsc_evt_cfg->action == HOST_ACT_BITWISE_CLR) return 0; pos = ((u8 *)subsc_evt) + sizeof(struct host_cmd_ds_802_11_subsc_evt); if (event_bitmap & BITMASK_BCN_RSSI_LOW) { rssi_tlv = (struct nxpwifi_ie_types_rssi_threshold *)pos; rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW); rssi_tlv->header.len = cpu_to_le16(sizeof(struct nxpwifi_ie_types_rssi_threshold) - sizeof(struct nxpwifi_ie_types_header)); rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value; rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq; nxpwifi_dbg(priv->adapter, EVENT, "Cfg Beacon Low Rssi event,\t" "RSSI:-%d dBm, Freq:%d\n", subsc_evt_cfg->bcn_l_rssi_cfg.abs_value, subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq); pos += sizeof(struct nxpwifi_ie_types_rssi_threshold); le16_unaligned_add_cpu (&cmd->size, sizeof(struct nxpwifi_ie_types_rssi_threshold)); } if (event_bitmap & BITMASK_BCN_RSSI_HIGH) { rssi_tlv = (struct nxpwifi_ie_types_rssi_threshold *)pos; rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); rssi_tlv->header.len = cpu_to_le16(sizeof(struct nxpwifi_ie_types_rssi_threshold) - sizeof(struct nxpwifi_ie_types_header)); rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value; rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq; nxpwifi_dbg(priv->adapter, EVENT, "Cfg Beacon High Rssi event,\t" "RSSI:-%d dBm, Freq:%d\n", subsc_evt_cfg->bcn_h_rssi_cfg.abs_value, subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq); pos += sizeof(struct nxpwifi_ie_types_rssi_threshold); le16_unaligned_add_cpu (&cmd->size, sizeof(struct nxpwifi_ie_types_rssi_threshold)); } return 0; } static int nxpwifi_ret_sta_subsc_evt(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = &resp->params.subsc_evt; /* * For every subscribe event command (Get/Set/Clear), FW reports the current * set of subscribed events */ nxpwifi_dbg(priv->adapter, EVENT, "Bitmap of currently subscribed events: %16x\n", le16_to_cpu(cmd_sub_event->events)); return 0; } static int nxpwifi_cmd_sta_802_11_tx_rate_query(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(HOST_CMD_802_11_TX_RATE_QUERY); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + S_DS_GEN); priv->tx_rate = 0; return 0; } static int nxpwifi_ret_sta_802_11_tx_rate_query(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { priv->tx_rate = resp->params.tx_rate.tx_rate; priv->tx_htinfo = resp->params.tx_rate.ht_info; if (!priv->is_data_rate_auto) priv->data_rate = nxpwifi_index_to_data_rate(priv, priv->tx_rate, priv->tx_htinfo); return 0; } static int nxpwifi_cmd_sta_mem_access(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct nxpwifi_ds_mem_rw *mem_rw = (struct nxpwifi_ds_mem_rw *)data_buf; struct host_cmd_ds_mem_access *mem_access = (void *)&cmd->params.mem; cmd->command = cpu_to_le16(HOST_CMD_MEM_ACCESS); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) + S_DS_GEN); mem_access->action = cpu_to_le16(cmd_action); mem_access->addr = cpu_to_le32(mem_rw->addr); mem_access->value = cpu_to_le32(mem_rw->value); return 0; } static int nxpwifi_ret_sta_mem_access(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_mem_access *mem = (void *)&resp->params.mem; priv->mem_rw.addr = le32_to_cpu(mem->addr); priv->mem_rw.value = le32_to_cpu(mem->value); return 0; } static u32 nxpwifi_parse_cal_cfg(u8 *src, size_t len, u8 *dst) { u8 *s = src, *d = dst; while (s - src < len) { if (*s && (isspace(*s) || *s == '\t')) { s++; continue; } if (isxdigit(*s)) { if (kstrtou8(s, 16, d)) return 0; d++; s += 2; } else { s++; } } return d - dst; } static int nxpwifi_cmd_sta_cfg_data(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct nxpwifi_adapter *adapter = priv->adapter; u32 len; u8 *data = (u8 *)cmd + S_DS_GEN; if (adapter->cal_data->data && adapter->cal_data->size > 0) { len = nxpwifi_parse_cal_cfg((u8 *)adapter->cal_data->data, adapter->cal_data->size, data); nxpwifi_dbg(adapter, INFO, "download cfg_data from config file\n"); } else { return -EINVAL; } cmd->command = cpu_to_le16(HOST_CMD_CFG_DATA); cmd->size = cpu_to_le16(S_DS_GEN + len); return 0; } static int nxpwifi_ret_sta_cfg_data(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { if (resp->result != HOST_RESULT_OK) { nxpwifi_dbg(priv->adapter, ERROR, "Cal data cmd resp failed\n"); return -EINVAL; } return 0; } static int nxpwifi_cmd_sta_ver_ext(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->params.verext.version_str_sel = (u8)(get_unaligned((u32 *)data_buf)); memcpy(&cmd->params, data_buf, sizeof(struct host_cmd_ds_version_ext)); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + S_DS_GEN); return 0; } static int nxpwifi_ret_sta_ver_ext(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; struct host_cmd_ds_version_ext *version_ext = (struct host_cmd_ds_version_ext *)data_buf; if (test_and_clear_bit(NXPWIFI_IS_REQUESTING_FW_VEREXT, &priv->adapter->work_flags)) { if (strncmp(ver_ext->version_str, "ChipRev:20, BB:9b(10.00), RF:40(21)", NXPWIFI_VERSION_STR_LENGTH) == 0) { struct nxpwifi_ds_auto_ds auto_ds = { .auto_ds = DEEP_SLEEP_OFF, }; nxpwifi_dbg(priv->adapter, MSG, "Bad HW revision detected, disabling deep sleep\n"); if (nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, false)) { nxpwifi_dbg(priv->adapter, MSG, "Disabling deep sleep failed.\n"); } } return 0; } if (version_ext) { version_ext->version_str_sel = ver_ext->version_str_sel; memcpy(version_ext->version_str, ver_ext->version_str, NXPWIFI_VERSION_STR_LENGTH); memcpy(priv->version_str, ver_ext->version_str, NXPWIFI_VERSION_STR_LENGTH); /* Ensure the version string from the firmware is 0-terminated */ priv->version_str[NXPWIFI_VERSION_STR_LENGTH - 1] = '\0'; } return 0; } static int nxpwifi_cmd_append_rpn_expression(struct nxpwifi_private *priv, struct nxpwifi_mef_entry *mef_entry, u8 **buffer) { struct nxpwifi_mef_filter *filter = mef_entry->filter; int i, byte_len; u8 *stack_ptr = *buffer; for (i = 0; i < NXPWIFI_MEF_MAX_FILTERS; i++) { filter = &mef_entry->filter[i]; if (!filter->filt_type) break; put_unaligned_le32((u32)filter->repeat, stack_ptr); stack_ptr += 4; *stack_ptr = TYPE_DNUM; stack_ptr += 1; byte_len = filter->byte_seq[NXPWIFI_MEF_MAX_BYTESEQ]; memcpy(stack_ptr, filter->byte_seq, byte_len); stack_ptr += byte_len; *stack_ptr = byte_len; stack_ptr += 1; *stack_ptr = TYPE_BYTESEQ; stack_ptr += 1; put_unaligned_le32((u32)filter->offset, stack_ptr); stack_ptr += 4; *stack_ptr = TYPE_DNUM; stack_ptr += 1; *stack_ptr = filter->filt_type; stack_ptr += 1; if (filter->filt_action) { *stack_ptr = filter->filt_action; stack_ptr += 1; } if (stack_ptr - *buffer > STACK_NBYTES) return -ENOMEM; } *buffer = stack_ptr; return 0; } static int nxpwifi_cmd_sta_mef_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg; struct nxpwifi_ds_mef_cfg *mef = (struct nxpwifi_ds_mef_cfg *)data_buf; struct nxpwifi_fw_mef_entry *mef_entry = NULL; u8 *pos = (u8 *)mef_cfg; u16 i; int ret = 0; cmd->command = cpu_to_le16(HOST_CMD_MEF_CFG); mef_cfg->criteria = cpu_to_le32(mef->criteria); mef_cfg->num_entries = cpu_to_le16(mef->num_entries); pos += sizeof(*mef_cfg); for (i = 0; i < mef->num_entries; i++) { mef_entry = (struct nxpwifi_fw_mef_entry *)pos; mef_entry->mode = mef->mef_entry[i].mode; mef_entry->action = mef->mef_entry[i].action; pos += sizeof(*mef_entry); ret = nxpwifi_cmd_append_rpn_expression(priv, &mef->mef_entry[i], &pos); if (ret) return ret; mef_entry->exprsize = cpu_to_le16(pos - mef_entry->expr); } cmd->size = cpu_to_le16((u16)(pos - (u8 *)mef_cfg) + S_DS_GEN); return ret; } static int nxpwifi_cmd_sta_802_11_rssi_info(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(HOST_CMD_RSSI_INFO); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + S_DS_GEN); cmd->params.rssi_info.action = cpu_to_le16(cmd_action); cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor); cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor); /* Reset SNR/NF/RSSI values in private structure */ 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; return 0; } static int nxpwifi_ret_sta_802_11_rssi_info(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = &resp->params.rssi_info_rsp; struct nxpwifi_ds_misc_subsc_evt *subsc_evt = &priv->async_subsc_evt_storage; priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); if (priv->subsc_evt_rssi_state == EVENT_HANDLED) return 0; memset(subsc_evt, 0x00, sizeof(struct nxpwifi_ds_misc_subsc_evt)); /* Resubscribe low and high rssi events with new thresholds */ subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; subsc_evt->action = HOST_ACT_BITWISE_SET; if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) { subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - priv->cqm_rssi_hyst); subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) { subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + priv->cqm_rssi_hyst); } subsc_evt->bcn_l_rssi_cfg.evt_freq = 1; subsc_evt->bcn_h_rssi_cfg.evt_freq = 1; priv->subsc_evt_rssi_state = EVENT_HANDLED; nxpwifi_send_cmd(priv, HOST_CMD_802_11_SUBSCRIBE_EVENT, 0, 0, subsc_evt, false); return 0; } static int nxpwifi_cmd_sta_func_init(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { if (priv->adapter->hw_status == NXPWIFI_HW_STATUS_RESET) priv->adapter->hw_status = NXPWIFI_HW_STATUS_READY; cmd->command = cpu_to_le16(cmd_no); cmd->size = cpu_to_le16(S_DS_GEN); return 0; } static int nxpwifi_cmd_sta_func_shutdown(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { priv->adapter->hw_status = NXPWIFI_HW_STATUS_RESET; cmd->command = cpu_to_le16(cmd_no); cmd->size = cpu_to_le16(S_DS_GEN); return 0; } static int nxpwifi_cmd_sta_11n_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_11n_cfg(priv, cmd, cmd_action, data_buf); } static int nxpwifi_cmd_sta_11n_addba_req(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_11n_addba_req(cmd, data_buf); } static int nxpwifi_ret_sta_11n_addba_req(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_11n_addba_req(priv, resp); } static int nxpwifi_cmd_sta_11n_addba_rsp(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_11n_addba_rsp_gen(priv, cmd, data_buf); } static int nxpwifi_ret_sta_11n_addba_rsp(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_11n_addba_resp(priv, resp); } static int nxpwifi_cmd_sta_11n_delba(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_11n_delba(cmd, data_buf); } static int nxpwifi_ret_sta_11n_delba(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_11n_delba(priv, resp); } static int nxpwifi_cmd_sta_tx_power_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct nxpwifi_types_power_group *pg_tlv; struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; struct host_cmd_ds_txpwr_cfg *txp = (struct host_cmd_ds_txpwr_cfg *)data_buf; cmd->command = cpu_to_le16(HOST_CMD_TXPWR_CFG); cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); switch (cmd_action) { case HOST_ACT_GEN_SET: if (txp->mode) { pg_tlv = (struct nxpwifi_types_power_group *)((unsigned long)txp + sizeof(struct host_cmd_ds_txpwr_cfg)); memmove(cmd_txp_cfg, txp, sizeof(struct host_cmd_ds_txpwr_cfg) + sizeof(struct nxpwifi_types_power_group) + le16_to_cpu(pg_tlv->length)); pg_tlv = (struct nxpwifi_types_power_group *)((u8 *) cmd_txp_cfg + sizeof(struct host_cmd_ds_txpwr_cfg)); cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + sizeof(struct nxpwifi_types_power_group) + le16_to_cpu(pg_tlv->length)); } else { memmove(cmd_txp_cfg, txp, sizeof(*txp)); } cmd_txp_cfg->action = cpu_to_le16(cmd_action); break; case HOST_ACT_GEN_GET: cmd_txp_cfg->action = cpu_to_le16(cmd_action); break; } return 0; } static int nxpwifi_get_power_level(struct nxpwifi_private *priv, void *data_buf) { int length, max_power = -1, min_power = -1; struct nxpwifi_types_power_group *pg_tlv_hdr; struct nxpwifi_power_group *pg; if (!data_buf) return -ENOMEM; pg_tlv_hdr = (struct nxpwifi_types_power_group *)((u8 *)data_buf); pg = (struct nxpwifi_power_group *) ((u8 *)pg_tlv_hdr + sizeof(struct nxpwifi_types_power_group)); length = le16_to_cpu(pg_tlv_hdr->length); /* At least one structure required to update power */ if (length < sizeof(struct nxpwifi_power_group)) return 0; max_power = pg->power_max; min_power = pg->power_min; length -= sizeof(struct nxpwifi_power_group); while (length >= sizeof(struct nxpwifi_power_group)) { pg++; if (max_power < pg->power_max) max_power = pg->power_max; if (min_power > pg->power_min) min_power = pg->power_min; length -= sizeof(struct nxpwifi_power_group); } priv->min_tx_power_level = (u8)min_power; priv->max_tx_power_level = (u8)max_power; return 0; } static int nxpwifi_ret_sta_tx_power_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_adapter *adapter = priv->adapter; struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; struct nxpwifi_types_power_group *pg_tlv_hdr; struct nxpwifi_power_group *pg; u16 action = le16_to_cpu(txp_cfg->action); u16 tlv_buf_left; pg_tlv_hdr = (struct nxpwifi_types_power_group *) ((u8 *)txp_cfg + sizeof(struct host_cmd_ds_txpwr_cfg)); pg = (struct nxpwifi_power_group *) ((u8 *)pg_tlv_hdr + sizeof(struct nxpwifi_types_power_group)); tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg); if (tlv_buf_left < le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr)) return 0; switch (action) { case HOST_ACT_GEN_GET: if (adapter->hw_status == NXPWIFI_HW_STATUS_INITIALIZING) nxpwifi_get_power_level(priv, pg_tlv_hdr); priv->tx_power_level = (u16)pg->power_min; break; case HOST_ACT_GEN_SET: if (!le32_to_cpu(txp_cfg->mode)) break; if (pg->power_max == pg->power_min) priv->tx_power_level = (u16)pg->power_min; break; default: nxpwifi_dbg(adapter, ERROR, "CMD_RESP: unknown cmd action %d\n", action); return 0; } nxpwifi_dbg(adapter, INFO, "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", priv->tx_power_level, priv->max_tx_power_level, priv->min_tx_power_level); return 0; } static int nxpwifi_cmd_sta_tx_rate_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; u16 *pbitmap_rates = (u16 *)data_buf; struct nxpwifi_rate_scope *rate_scope; struct nxpwifi_rate_drop_pattern *rate_drop; u32 i; cmd->command = cpu_to_le16(HOST_CMD_TX_RATE_CFG); rate_cfg->action = cpu_to_le16(cmd_action); rate_cfg->cfg_index = 0; rate_scope = (struct nxpwifi_rate_scope *)((u8 *)rate_cfg + sizeof(struct host_cmd_ds_tx_rate_cfg)); rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); rate_scope->length = cpu_to_le16 (sizeof(*rate_scope) - sizeof(struct nxpwifi_ie_types_header)); if (pbitmap_rates) { rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(pbitmap_rates[2 + i]); if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) { for (i = 0; i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); i++) rate_scope->vht_mcs_rate_bitmap[i] = cpu_to_le16(pbitmap_rates[10 + i]); } } else { rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(priv->bitmap_rates[0]); rate_scope->ofdm_rate_bitmap = cpu_to_le16(priv->bitmap_rates[1]); for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(priv->bitmap_rates[2 + i]); if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) { for (i = 0; i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); i++) rate_scope->vht_mcs_rate_bitmap[i] = cpu_to_le16(priv->bitmap_rates[10 + i]); } } rate_drop = (struct nxpwifi_rate_drop_pattern *)((u8 *)rate_scope + sizeof(struct nxpwifi_rate_scope)); rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); rate_drop->rate_drop_mode = 0; cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + sizeof(struct nxpwifi_rate_scope) + sizeof(struct nxpwifi_rate_drop_pattern)); return 0; } static void nxpwifi_ret_rate_scope(struct nxpwifi_private *priv, u8 *tlv_buf) { struct nxpwifi_rate_scope *rate_scope; int i; rate_scope = (struct nxpwifi_rate_scope *)tlv_buf; priv->bitmap_rates[0] = le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); priv->bitmap_rates[1] = le16_to_cpu(rate_scope->ofdm_rate_bitmap); for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) priv->bitmap_rates[2 + i] = le16_to_cpu(rate_scope->ht_mcs_rate_bitmap[i]); if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) { for (i = 0; i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); i++) priv->bitmap_rates[10 + i] = le16_to_cpu(rate_scope->vht_mcs_rate_bitmap[i]); } } static int nxpwifi_ret_sta_tx_rate_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; struct nxpwifi_ie_types_header *head; u16 tlv, tlv_buf_len, tlv_buf_left; u8 *tlv_buf; tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg); while (tlv_buf_left >= sizeof(*head)) { head = (struct nxpwifi_ie_types_header *)tlv_buf; tlv = le16_to_cpu(head->type); tlv_buf_len = le16_to_cpu(head->len); if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) break; switch (tlv) { case TLV_TYPE_RATE_SCOPE: nxpwifi_ret_rate_scope(priv, tlv_buf); break; /* Add RATE_DROP tlv here */ } tlv_buf += (sizeof(*head) + tlv_buf_len); tlv_buf_left -= (sizeof(*head) + tlv_buf_len); } priv->is_data_rate_auto = nxpwifi_is_rate_auto(priv); if (priv->is_data_rate_auto) priv->data_rate = 0; else return nxpwifi_send_cmd(priv, HOST_CMD_802_11_TX_RATE_QUERY, HOST_ACT_GEN_GET, 0, NULL, false); return 0; } static int nxpwifi_cmd_sta_reconfigure_rx_buff(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_recfg_tx_buf(priv, cmd, cmd_action, data_buf); } static int nxpwifi_ret_sta_reconfigure_rx_buff(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_adapter *adapter = priv->adapter; if (0xffff != (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { adapter->tx_buf_size = (u16)le16_to_cpu(resp->params.tx_buf.buff_size); adapter->tx_buf_size = (adapter->tx_buf_size / NXPWIFI_SDIO_BLOCK_SIZE) * NXPWIFI_SDIO_BLOCK_SIZE; adapter->curr_tx_buf_size = adapter->tx_buf_size; nxpwifi_dbg(adapter, CMD, "cmd: curr_tx_buf_size=%d\n", adapter->curr_tx_buf_size); if (adapter->if_ops.update_mp_end_port) { u16 mp_end_port; mp_end_port = le16_to_cpu(resp->params.tx_buf.mp_end_port); adapter->if_ops.update_mp_end_port(adapter, mp_end_port); } } return 0; } static int nxpwifi_cmd_sta_chan_report_request(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_issue_chan_report_request(priv, cmd, data_buf); } static int nxpwifi_cmd_sta_amsdu_aggr_ctrl(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_amsdu_aggr_ctrl(cmd, cmd_action, data_buf); } static int nxpwifi_cmd_sta_robust_coex(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_robust_coex *coex = &cmd->params.coex; bool *is_timeshare = (bool *)data_buf; struct nxpwifi_ie_types_robust_coex *coex_tlv; cmd->command = cpu_to_le16(HOST_CMD_ROBUST_COEX); cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN); coex->action = cpu_to_le16(cmd_action); coex_tlv = (struct nxpwifi_ie_types_robust_coex *) ((u8 *)coex + sizeof(*coex)); coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX); coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode)); if (coex->action == HOST_ACT_GEN_GET) return 0; if (*is_timeshare) coex_tlv->mode = cpu_to_le32(NXPWIFI_COEX_MODE_TIMESHARE); else coex_tlv->mode = cpu_to_le32(NXPWIFI_COEX_MODE_SPATIAL); return 0; } static int nxpwifi_ret_sta_robust_coex(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_robust_coex *coex = &resp->params.coex; bool *is_timeshare = (bool *)data_buf; struct nxpwifi_ie_types_robust_coex *coex_tlv; u16 action = le16_to_cpu(coex->action); u32 mode; if (!is_timeshare) return 0; coex_tlv = (struct nxpwifi_ie_types_robust_coex *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex)); if (action == HOST_ACT_GEN_GET) { mode = le32_to_cpu(coex_tlv->mode); if (mode == NXPWIFI_COEX_MODE_TIMESHARE) *is_timeshare = true; else *is_timeshare = false; } return 0; } static int nxpwifi_cmd_sta_enh_power_mode(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = &cmd->params.psmode_enh; u16 ps_bitmap = (u16)cmd_type; struct nxpwifi_ds_auto_ds *auto_ds = (struct nxpwifi_ds_auto_ds *)data_buf; u8 *tlv; u16 cmd_size = 0; cmd->command = cpu_to_le16(HOST_CMD_802_11_PS_MODE_ENH); if (cmd_action == DIS_AUTO_PS) { psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + sizeof(psmode_enh->params.ps_bitmap)); } else if (cmd_action == GET_PS) { psmode_enh->action = cpu_to_le16(GET_PS); psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + sizeof(psmode_enh->params.ps_bitmap)); } else if (cmd_action == EN_AUTO_PS) { psmode_enh->action = cpu_to_le16(EN_AUTO_PS); psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); cmd_size = S_DS_GEN + sizeof(psmode_enh->action) + sizeof(psmode_enh->params.ps_bitmap); tlv = (u8 *)cmd + cmd_size; if (ps_bitmap & BITMAP_STA_PS) { struct nxpwifi_adapter *adapter = priv->adapter; struct nxpwifi_ie_types_ps_param *ps_tlv = (struct nxpwifi_ie_types_ps_param *)tlv; struct nxpwifi_ps_param *ps_mode = &ps_tlv->param; ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - sizeof(struct nxpwifi_ie_types_header)); cmd_size += sizeof(*ps_tlv); tlv += sizeof(*ps_tlv); nxpwifi_dbg(priv->adapter, CMD, "cmd: PS Command: Enter PS\n"); ps_mode->null_pkt_interval = cpu_to_le16(adapter->null_pkt_interval); ps_mode->multiple_dtims = cpu_to_le16(adapter->multiple_dtim); ps_mode->bcn_miss_timeout = cpu_to_le16(adapter->bcn_miss_time_out); ps_mode->local_listen_interval = cpu_to_le16(adapter->local_listen_interval); ps_mode->delay_to_ps = cpu_to_le16(adapter->delay_to_ps); ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode); } if (ps_bitmap & BITMAP_AUTO_DS) { struct nxpwifi_ie_types_auto_ds_param *auto_ds_tlv = (struct nxpwifi_ie_types_auto_ds_param *)tlv; u16 idletime = 0; auto_ds_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); auto_ds_tlv->header.len = cpu_to_le16(sizeof(*auto_ds_tlv) - sizeof(struct nxpwifi_ie_types_header)); cmd_size += sizeof(*auto_ds_tlv); tlv += sizeof(*auto_ds_tlv); if (auto_ds) idletime = auto_ds->idle_time; nxpwifi_dbg(priv->adapter, CMD, "cmd: PS Command: Enter Auto Deep Sleep\n"); auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime); } cmd->size = cpu_to_le16(cmd_size); } return 0; } static int nxpwifi_ret_sta_enh_power_mode(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = &resp->params.psmode_enh; struct nxpwifi_ds_pm_cfg *pm_cfg = (struct nxpwifi_ds_pm_cfg *)data_buf; u16 action = le16_to_cpu(ps_mode->action); u16 ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); u16 auto_ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); nxpwifi_dbg(adapter, INFO, "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", __func__, resp->result, action); if (action == EN_AUTO_PS) { if (auto_ps_bitmap & BITMAP_AUTO_DS) { nxpwifi_dbg(adapter, CMD, "cmd: Enabled auto deep sleep\n"); priv->adapter->is_deep_sleep = true; } if (auto_ps_bitmap & BITMAP_STA_PS) { nxpwifi_dbg(adapter, CMD, "cmd: Enabled STA power save\n"); if (adapter->sleep_period.period) nxpwifi_dbg(adapter, CMD, "cmd: set to uapsd/pps mode\n"); } } else if (action == DIS_AUTO_PS) { if (ps_bitmap & BITMAP_AUTO_DS) { priv->adapter->is_deep_sleep = false; nxpwifi_dbg(adapter, CMD, "cmd: Disabled auto deep sleep\n"); } if (ps_bitmap & BITMAP_STA_PS) { nxpwifi_dbg(adapter, CMD, "cmd: Disabled STA power save\n"); if (adapter->sleep_period.period) { adapter->delay_null_pkt = false; adapter->tx_lock_flag = false; adapter->pps_uapsd_mode = false; } } } else if (action == GET_PS) { if (ps_bitmap & BITMAP_STA_PS) adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP; else adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM; nxpwifi_dbg(adapter, CMD, "cmd: ps_bitmap=%#x\n", ps_bitmap); if (pm_cfg) { /* This section is for get power save mode */ if (ps_bitmap & BITMAP_STA_PS) pm_cfg->param.ps_mode = 1; else pm_cfg->param.ps_mode = 0; } } return 0; } static int nxpwifi_cmd_sta_802_11_hs_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct nxpwifi_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; struct nxpwifi_hs_config_param *hscfg_param = (struct nxpwifi_hs_config_param *)data_buf; u8 *tlv = (u8 *)hs_cfg + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh); struct nxpwifi_ps_param_in_hs *psparam_tlv = NULL; bool hs_activate = false; u16 size; if (!hscfg_param) /* New Activate command */ hs_activate = true; cmd->command = cpu_to_le16(HOST_CMD_802_11_HS_CFG_ENH); if (!hs_activate && hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL) && (adapter->arp_filter_size > 0 && adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE)) { nxpwifi_dbg(adapter, CMD, "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", adapter->arp_filter_size); memcpy(((u8 *)hs_cfg) + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), adapter->arp_filter, adapter->arp_filter_size); size = adapter->arp_filter_size + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + S_DS_GEN; tlv = (u8 *)hs_cfg + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + adapter->arp_filter_size; } else { size = S_DS_GEN + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh); } if (hs_activate) { hs_cfg->action = cpu_to_le16(HS_ACTIVATE); hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED); adapter->hs_activated_manually = true; nxpwifi_dbg(priv->adapter, CMD, "cmd: Activating host sleep manually\n"); } else { hs_cfg->action = cpu_to_le16(HS_CONFIGURE); hs_cfg->params.hs_config.conditions = hscfg_param->conditions; hs_cfg->params.hs_config.gpio = hscfg_param->gpio; hs_cfg->params.hs_config.gap = hscfg_param->gap; size += sizeof(struct nxpwifi_ps_param_in_hs); psparam_tlv = (struct nxpwifi_ps_param_in_hs *)tlv; psparam_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAMS_IN_HS); psparam_tlv->header.len = cpu_to_le16(sizeof(struct nxpwifi_ps_param_in_hs) - sizeof(struct nxpwifi_ie_types_header)); psparam_tlv->hs_wake_int = cpu_to_le32(HS_DEF_WAKE_INTERVAL); psparam_tlv->hs_inact_timeout = cpu_to_le32(HS_DEF_INACTIVITY_TIMEOUT); nxpwifi_dbg(adapter, CMD, "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", hs_cfg->params.hs_config.conditions, hs_cfg->params.hs_config.gpio, hs_cfg->params.hs_config.gap); } cmd->size = cpu_to_le16(size); return 0; } static int nxpwifi_ret_sta_802_11_hs_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_802_11_hs_cfg(priv, resp); } static int nxpwifi_cmd_sta_set_bss_mode(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); if (priv->bss_mode == NL80211_IFTYPE_STATION) cmd->params.bss_mode.con_type = CONNECTION_TYPE_INFRA; else if (priv->bss_mode == NL80211_IFTYPE_AP) cmd->params.bss_mode.con_type = CONNECTION_TYPE_AP; cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_set_bss_mode) + S_DS_GEN); return 0; } static int nxpwifi_cmd_sta_802_11_net_monitor(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct nxpwifi_802_11_net_monitor *net_mon; struct host_cmd_ds_802_11_net_monitor *cmd_net_mon = &cmd->params.net_mon; struct chan_band_param *chan_band = NULL; u8 sec_chan_offset = 0; u32 bw_offset = 0; net_mon = (struct nxpwifi_802_11_net_monitor *)data_buf; cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_802_11_net_monitor) + sizeof(struct chan_band_param)); cmd->command = cpu_to_le16(cmd_no); cmd_net_mon->action = cpu_to_le16(cmd_action); if (cmd_action == HOST_ACT_GEN_SET) { if (net_mon->enable_net_mon) { cmd_net_mon->enable_net_mon = cpu_to_le16(0x1); cmd_net_mon->filter_flag = cpu_to_le16((u16) net_mon->filter_flag); } if (net_mon->enable_net_mon && net_mon->channel) { chan_band = &cmd_net_mon->monitor_chan.chan_band_param[0]; cmd_net_mon->monitor_chan.header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); cmd_net_mon->monitor_chan.header.len = cpu_to_le16(sizeof(struct chan_band_param)); chan_band->chan_number = (u8)net_mon->channel; chan_band->band_cfg.chan_band = nxpwifi_band_to_radio_type((u16)net_mon->band); if (net_mon->band & BAND_GN || net_mon->band & BAND_AN || net_mon->band & BAND_GAC || net_mon->band & BAND_AAC) { bw_offset = net_mon->chan_bandwidth; if (bw_offset == CHANNEL_BW_40MHZ_ABOVE) { chan_band->band_cfg.chan_2O_ffset = NXPWIFI_SEC_CHAN_ABOVE; chan_band->band_cfg.chan_width = CHAN_BW_40MHZ; } else if (bw_offset == CHANNEL_BW_40MHZ_BELOW) { chan_band->band_cfg.chan_2O_ffset = NXPWIFI_SEC_CHAN_BELOW; chan_band->band_cfg.chan_width = CHAN_BW_40MHZ; } else if (bw_offset == CHANNEL_BW_80MHZ) { sec_chan_offset = nxpwifi_get_sec_chan_offset(net_mon->channel); if (sec_chan_offset == NXPWIFI_SEC_CHAN_ABOVE) chan_band->band_cfg.chan_2O_ffset = NXPWIFI_SEC_CHAN_ABOVE; else if (sec_chan_offset == NXPWIFI_SEC_CHAN_BELOW) chan_band->band_cfg.chan_2O_ffset = NXPWIFI_SEC_CHAN_BELOW; chan_band->band_cfg.chan_width = CHAN_BW_80MHZ; } } } } return 0; } static int nxpwifi_ret_sta_802_11_net_monitor(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_802_11_net_monitor *cmd_net_mon = &resp->params.net_mon; nxpwifi_dbg(priv->adapter, CMD, "cmd: NET_MONITOR_CMD: action: %d, enable: %d, flag: %d ch: %d band: %d bw: %d offset: %d\n", le16_to_cpu(cmd_net_mon->action), le16_to_cpu(cmd_net_mon->enable_net_mon), le16_to_cpu(cmd_net_mon->filter_flag), cmd_net_mon->monitor_chan.chan_band_param[0].chan_number, cmd_net_mon->monitor_chan.chan_band_param[0].band_cfg.chan_band, cmd_net_mon->monitor_chan.chan_band_param[0].band_cfg.chan_width, cmd_net_mon->monitor_chan.chan_band_param[0].band_cfg.chan_2O_ffset); priv->adapter->enable_net_mon = le16_to_cpu(cmd_net_mon->enable_net_mon); return 0; } static int nxpwifi_cmd_sta_802_11_scan_ext(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_802_11_scan_ext(priv, cmd, data_buf); } static int nxpwifi_ret_sta_802_11_scan_ext(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_adapter *adapter = priv->adapter; int ret; ret = nxpwifi_ret_802_11_scan_ext(priv, resp); adapter->curr_cmd->wait_q_enabled = false; return ret; } static int nxpwifi_cmd_sta_coalesce_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_coalesce_cfg *coalesce_cfg = &cmd->params.coalesce_cfg; struct nxpwifi_ds_coalesce_cfg *cfg = (struct nxpwifi_ds_coalesce_cfg *)data_buf; struct coalesce_filt_field_param *param; u16 cnt, idx, length; struct coalesce_receive_filt_rule *rule; cmd->command = cpu_to_le16(HOST_CMD_COALESCE_CFG); cmd->size = cpu_to_le16(S_DS_GEN); coalesce_cfg->action = cpu_to_le16(cmd_action); coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules); rule = (void *)coalesce_cfg->rule_data; for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE); rule->max_coalescing_delay = cpu_to_le16(cfg->rule[cnt].max_coalescing_delay); rule->pkt_type = cfg->rule[cnt].pkt_type; rule->num_of_fields = cfg->rule[cnt].num_of_fields; length = 0; param = rule->params; for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) { param->operation = cfg->rule[cnt].params[idx].operation; param->operand_len = cfg->rule[cnt].params[idx].operand_len; param->offset = cpu_to_le16(cfg->rule[cnt].params[idx].offset); memcpy(param->operand_byte_stream, cfg->rule[cnt].params[idx].operand_byte_stream, param->operand_len); length += sizeof(struct coalesce_filt_field_param); param++; } /* * Total rule length is sizeof max_coalescing_delay(u16), * num_of_fields(u8), pkt_type(u8) and total length of the all * params */ rule->header.len = cpu_to_le16(length + sizeof(u16) + sizeof(u8) + sizeof(u8)); /* Add the rule length to the command size */ le16_unaligned_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) + sizeof(struct nxpwifi_ie_types_header)); rule = (void *)((u8 *)rule->params + length); } /* Add sizeof action, num_of_rules to total command length */ le16_unaligned_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); return 0; } static int nxpwifi_cmd_sta_mgmt_frame_reg(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->params.reg_mask.action = cpu_to_le16(cmd_action); cmd->params.reg_mask.mask = cpu_to_le32(get_unaligned((u32 *)data_buf)); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) + S_DS_GEN); return 0; } static int nxpwifi_cmd_sta_remain_on_chan(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); memcpy(&cmd->params, data_buf, sizeof(struct host_cmd_ds_remain_on_chan)); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) + S_DS_GEN); return 0; } static int nxpwifi_ret_sta_remain_on_chan(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_remain_on_chan *resp_cfg = &resp->params.roc_cfg; struct host_cmd_ds_remain_on_chan *roc_cfg = (struct host_cmd_ds_remain_on_chan *)data_buf; if (roc_cfg) memcpy(roc_cfg, resp_cfg, sizeof(*roc_cfg)); return 0; } static int nxpwifi_cmd_sta_gtk_rekey_offload(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_gtk_rekey_params *rekey = &cmd->params.rekey; struct cfg80211_gtk_rekey_data *data = (struct cfg80211_gtk_rekey_data *)data_buf; u64 rekey_ctr; cmd->command = cpu_to_le16(HOST_CMD_GTK_REKEY_OFFLOAD_CFG); cmd->size = cpu_to_le16(sizeof(*rekey) + S_DS_GEN); rekey->action = cpu_to_le16(cmd_action); if (cmd_action == HOST_ACT_GEN_SET) { memcpy(rekey->kek, data->kek, NL80211_KEK_LEN); memcpy(rekey->kck, data->kck, NL80211_KCK_LEN); rekey_ctr = be64_to_cpup((__be64 *)data->replay_ctr); rekey->replay_ctr_low = cpu_to_le32((u32)rekey_ctr); rekey->replay_ctr_high = cpu_to_le32((u32)((u64)rekey_ctr >> 32)); } return 0; } static int nxpwifi_cmd_sta_11ac_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_11ac_cfg(priv, cmd, cmd_action, data_buf); } static int nxpwifi_cmd_sta_hs_wakeup_reason(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(HOST_CMD_HS_WAKEUP_REASON); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_wakeup_reason) + S_DS_GEN); return 0; } static int nxpwifi_ret_sta_hs_wakeup_reason(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_wakeup_reason *wakeup_reason = (struct host_cmd_ds_wakeup_reason *)data_buf; if (!wakeup_reason) return 0; wakeup_reason->wakeup_reason = resp->params.hs_wakeup_reason.wakeup_reason; return 0; } static int nxpwifi_cmd_sta_mc_policy(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy; const u16 *drcs_info = data_buf; mc_pol->action = cpu_to_le16(cmd_action); mc_pol->policy = cpu_to_le16(*drcs_info); cmd->command = cpu_to_le16(HOST_CMD_MC_POLICY); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) + S_DS_GEN); return 0; } static int nxpwifi_cmd_sta_sdio_rx_aggr_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = &cmd->params.sdio_rx_aggr_cfg; cmd->command = cpu_to_le16(HOST_CMD_SDIO_SP_RX_AGGR_CFG); cmd->size = cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) + S_DS_GEN); cfg->action = cmd_action; if (cmd_action == HOST_ACT_GEN_SET) cfg->enable = *(u8 *)data_buf; return 0; } static int nxpwifi_ret_sta_sdio_rx_aggr_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct nxpwifi_adapter *adapter = priv->adapter; struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = &resp->params.sdio_rx_aggr_cfg; adapter->sdio_rx_aggr_enable = cfg->enable; adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size); return 0; } static int nxpwifi_cmd_sta_get_chan_info(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_sta_configure *sta_cfg_cmd = &cmd->params.sta_cfg; struct host_cmd_tlv_channel_band *tlv_band_channel = (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer; cmd->command = cpu_to_le16(HOST_CMD_STA_CONFIGURE); cmd->size = cpu_to_le16(sizeof(*sta_cfg_cmd) + sizeof(*tlv_band_channel) + S_DS_GEN); sta_cfg_cmd->action = cpu_to_le16(cmd_action); memset(tlv_band_channel, 0, sizeof(*tlv_band_channel)); tlv_band_channel->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); tlv_band_channel->header.len = cpu_to_le16(sizeof(*tlv_band_channel) - sizeof(struct nxpwifi_ie_types_header)); return 0; } static int nxpwifi_ret_sta_get_chan_info(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_sta_configure *sta_cfg_cmd = &resp->params.sta_cfg; struct nxpwifi_channel_band *channel_band = (struct nxpwifi_channel_band *)data_buf; struct host_cmd_tlv_channel_band *tlv_band_channel; if (!channel_band) return 0; tlv_band_channel = (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer; memcpy(&channel_band->band_config, &tlv_band_channel->band_config, sizeof(struct nxpwifi_band_config)); channel_band->channel = tlv_band_channel->channel; return 0; } static int nxpwifi_cmd_sta_chan_region_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { struct host_cmd_ds_chan_region_cfg *reg = &cmd->params.reg_cfg; cmd->command = cpu_to_le16(HOST_CMD_CHAN_REGION_CFG); cmd->size = cpu_to_le16(sizeof(*reg) + S_DS_GEN); if (cmd_action == HOST_ACT_GEN_GET) reg->action = cpu_to_le16(cmd_action); return 0; } static struct ieee80211_regdomain * nxpwifi_create_custom_regdomain(struct nxpwifi_private *priv, u8 *buf, u16 buf_len) { u16 num_chan = buf_len / 2; struct ieee80211_regdomain *regd; struct ieee80211_reg_rule *rule; bool new_rule; int idx, freq, prev_freq = 0; u32 bw, prev_bw = 0; u8 chflags, prev_chflags = 0, valid_rules = 0; if (WARN_ON_ONCE(num_chan > NL80211_MAX_SUPP_REG_RULES)) return ERR_PTR(-EINVAL); regd = kzalloc_flex(*regd, reg_rules, num_chan, GFP_KERNEL); if (!regd) return ERR_PTR(-ENOMEM); for (idx = 0; idx < num_chan; idx++) { u8 chan; enum nl80211_band band; chan = *buf++; if (!chan) { kfree(regd); return NULL; } chflags = *buf++; band = (chan <= 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; freq = ieee80211_channel_to_frequency(chan, band); new_rule = false; if (chflags & NXPWIFI_CHANNEL_DISABLED) continue; if (band == NL80211_BAND_5GHZ) { if (!(chflags & NXPWIFI_CHANNEL_NOHT80)) bw = MHZ_TO_KHZ(80); else if (!(chflags & NXPWIFI_CHANNEL_NOHT40)) bw = MHZ_TO_KHZ(40); else bw = MHZ_TO_KHZ(20); } else { if (!(chflags & NXPWIFI_CHANNEL_NOHT40)) bw = MHZ_TO_KHZ(40); else bw = MHZ_TO_KHZ(20); } if (idx == 0 || prev_chflags != chflags || prev_bw != bw || freq - prev_freq > 20) { valid_rules++; new_rule = true; } rule = ®d->reg_rules[valid_rules - 1]; rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10); prev_chflags = chflags; prev_freq = freq; prev_bw = bw; if (!new_rule) continue; rule->freq_range.start_freq_khz = MHZ_TO_KHZ(freq - 10); rule->power_rule.max_eirp = DBM_TO_MBM(19); if (chflags & NXPWIFI_CHANNEL_PASSIVE) rule->flags = NL80211_RRF_NO_IR; if (chflags & NXPWIFI_CHANNEL_DFS) rule->flags = NL80211_RRF_DFS; rule->freq_range.max_bandwidth_khz = bw; } regd->n_reg_rules = valid_rules; regd->alpha2[0] = '9'; regd->alpha2[1] = '9'; return regd; } static int nxpwifi_ret_sta_chan_region_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_chan_region_cfg *reg = &resp->params.reg_cfg; u16 action = le16_to_cpu(reg->action); u16 tlv, tlv_buf_len, tlv_buf_left; struct nxpwifi_ie_types_header *head; struct ieee80211_regdomain *regd; u8 *tlv_buf; if (action != HOST_ACT_GEN_GET) return 0; tlv_buf = (u8 *)reg + sizeof(*reg); tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*reg); while (tlv_buf_left >= sizeof(*head)) { head = (struct nxpwifi_ie_types_header *)tlv_buf; tlv = le16_to_cpu(head->type); tlv_buf_len = le16_to_cpu(head->len); if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) break; switch (tlv) { case TLV_TYPE_CHAN_ATTR_CFG: nxpwifi_dbg_dump(priv->adapter, CMD_D, "CHAN:", (u8 *)head + sizeof(*head), tlv_buf_len); regd = nxpwifi_create_custom_regdomain(priv, (u8 *)head + sizeof(*head), tlv_buf_len); if (!IS_ERR(regd)) priv->adapter->regd = regd; break; } tlv_buf += (sizeof(*head) + tlv_buf_len); tlv_buf_left -= (sizeof(*head) + tlv_buf_len); } return 0; } static int nxpwifi_cmd_sta_pkt_aggr_ctrl(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->params.pkt_aggr_ctrl.action = cpu_to_le16(cmd_action); cmd->params.pkt_aggr_ctrl.enable = cpu_to_le16(*(u16 *)data_buf); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_pkt_aggr_ctrl) + S_DS_GEN); return 0; } static int nxpwifi_ret_sta_pkt_aggr_ctrl(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { struct host_cmd_ds_pkt_aggr_ctrl *pkt_aggr_ctrl = &resp->params.pkt_aggr_ctrl; struct nxpwifi_adapter *adapter = priv->adapter; adapter->bus_aggr.enable = le16_to_cpu(pkt_aggr_ctrl->enable); if (adapter->bus_aggr.enable) adapter->intf_hdr_len = INTF_HEADER_LEN; adapter->bus_aggr.mode = NXPWIFI_BUS_AGGR_MODE_LEN_V2; adapter->bus_aggr.tx_aggr_max_size = le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_size); adapter->bus_aggr.tx_aggr_max_num = le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_num); adapter->bus_aggr.tx_aggr_align = le16_to_cpu(pkt_aggr_ctrl->tx_aggr_align); return 0; } static int nxpwifi_cmd_sta_11ax_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_11ax_cfg(priv, cmd, cmd_action, data_buf); } static int nxpwifi_ret_sta_11ax_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_11ax_cfg(priv, resp, data_buf); } static int nxpwifi_cmd_sta_11ax_cmd(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_11ax_cmd(priv, cmd, cmd_action, data_buf); } static int nxpwifi_ret_sta_11ax_cmd(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_11ax_cmd(priv, resp, data_buf); } static int nxpwifi_cmd_sta_twt_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_no, void *data_buf, u16 cmd_action, u32 cmd_type) { return nxpwifi_cmd_twt_cfg(priv, cmd, cmd_action, data_buf); } static int nxpwifi_ret_sta_twt_cfg(struct nxpwifi_private *priv, struct host_cmd_ds_command *resp, u16 cmdresp_no, void *data_buf) { return nxpwifi_ret_twt_cfg(priv, resp, data_buf); } static const struct nxpwifi_cmd_entry cmd_table_sta[] = { {.cmd_no = HOST_CMD_GET_HW_SPEC, .prepare_cmd = nxpwifi_cmd_sta_get_hw_spec, .cmd_resp = nxpwifi_ret_sta_get_hw_spec}, {.cmd_no = HOST_CMD_802_11_SCAN, .prepare_cmd = nxpwifi_cmd_sta_802_11_scan, .cmd_resp = nxpwifi_ret_sta_802_11_scan}, {.cmd_no = HOST_CMD_802_11_GET_LOG, .prepare_cmd = nxpwifi_cmd_sta_802_11_get_log, .cmd_resp = nxpwifi_ret_sta_802_11_get_log}, {.cmd_no = HOST_CMD_MAC_MULTICAST_ADR, .prepare_cmd = nxpwifi_cmd_sta_mac_multicast_adr, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_802_11_ASSOCIATE, .prepare_cmd = nxpwifi_cmd_sta_802_11_associate, .cmd_resp = nxpwifi_ret_sta_802_11_associate}, {.cmd_no = HOST_CMD_802_11_SNMP_MIB, .prepare_cmd = nxpwifi_cmd_sta_802_11_snmp_mib, .cmd_resp = nxpwifi_ret_sta_802_11_snmp_mib}, {.cmd_no = HOST_CMD_MAC_REG_ACCESS, .prepare_cmd = nxpwifi_cmd_sta_reg_access, .cmd_resp = nxpwifi_ret_sta_reg_access}, {.cmd_no = HOST_CMD_BBP_REG_ACCESS, .prepare_cmd = nxpwifi_cmd_sta_reg_access, .cmd_resp = nxpwifi_ret_sta_reg_access}, {.cmd_no = HOST_CMD_RF_REG_ACCESS, .prepare_cmd = nxpwifi_cmd_sta_reg_access, .cmd_resp = nxpwifi_ret_sta_reg_access}, {.cmd_no = HOST_CMD_RF_TX_PWR, .prepare_cmd = nxpwifi_cmd_sta_rf_tx_pwr, .cmd_resp = nxpwifi_ret_sta_rf_tx_pwr}, {.cmd_no = HOST_CMD_RF_ANTENNA, .prepare_cmd = nxpwifi_cmd_sta_rf_antenna, .cmd_resp = nxpwifi_ret_sta_rf_antenna}, {.cmd_no = HOST_CMD_802_11_DEAUTHENTICATE, .prepare_cmd = nxpwifi_cmd_sta_802_11_deauthenticate, .cmd_resp = nxpwifi_ret_sta_802_11_deauthenticate}, {.cmd_no = HOST_CMD_MAC_CONTROL, .prepare_cmd = nxpwifi_cmd_sta_mac_control, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_802_11_MAC_ADDRESS, .prepare_cmd = nxpwifi_cmd_sta_802_11_mac_address, .cmd_resp = nxpwifi_ret_sta_802_11_mac_address}, {.cmd_no = HOST_CMD_802_11_EEPROM_ACCESS, .prepare_cmd = nxpwifi_cmd_sta_reg_access, .cmd_resp = nxpwifi_ret_sta_reg_access}, {.cmd_no = HOST_CMD_802_11D_DOMAIN_INFO, .prepare_cmd = nxpwifi_cmd_sta_802_11d_domain_info, .cmd_resp = nxpwifi_ret_sta_802_11d_domain_info}, {.cmd_no = HOST_CMD_802_11_KEY_MATERIAL, .prepare_cmd = nxpwifi_cmd_sta_802_11_key_material, .cmd_resp = nxpwifi_ret_sta_802_11_key_material}, {.cmd_no = HOST_CMD_802_11_BG_SCAN_CONFIG, .prepare_cmd = nxpwifi_cmd_sta_802_11_bg_scan_config, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_802_11_BG_SCAN_QUERY, .prepare_cmd = nxpwifi_cmd_sta_802_11_bg_scan_query, .cmd_resp = nxpwifi_ret_sta_802_11_bg_scan_query}, {.cmd_no = HOST_CMD_WMM_GET_STATUS, .prepare_cmd = nxpwifi_cmd_sta_wmm_get_status, .cmd_resp = nxpwifi_ret_sta_wmm_get_status}, {.cmd_no = HOST_CMD_802_11_SUBSCRIBE_EVENT, .prepare_cmd = nxpwifi_cmd_sta_802_11_subsc_evt, .cmd_resp = nxpwifi_ret_sta_subsc_evt}, {.cmd_no = HOST_CMD_802_11_TX_RATE_QUERY, .prepare_cmd = nxpwifi_cmd_sta_802_11_tx_rate_query, .cmd_resp = nxpwifi_ret_sta_802_11_tx_rate_query}, {.cmd_no = HOST_CMD_MEM_ACCESS, .prepare_cmd = nxpwifi_cmd_sta_mem_access, .cmd_resp = nxpwifi_ret_sta_mem_access}, {.cmd_no = HOST_CMD_CFG_DATA, .prepare_cmd = nxpwifi_cmd_sta_cfg_data, .cmd_resp = nxpwifi_ret_sta_cfg_data}, {.cmd_no = HOST_CMD_VERSION_EXT, .prepare_cmd = nxpwifi_cmd_sta_ver_ext, .cmd_resp = nxpwifi_ret_sta_ver_ext}, {.cmd_no = HOST_CMD_MEF_CFG, .prepare_cmd = nxpwifi_cmd_sta_mef_cfg, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_RSSI_INFO, .prepare_cmd = nxpwifi_cmd_sta_802_11_rssi_info, .cmd_resp = nxpwifi_ret_sta_802_11_rssi_info}, {.cmd_no = HOST_CMD_FUNC_INIT, .prepare_cmd = nxpwifi_cmd_sta_func_init, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_FUNC_SHUTDOWN, .prepare_cmd = nxpwifi_cmd_sta_func_shutdown, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_PMIC_REG_ACCESS, .prepare_cmd = nxpwifi_cmd_sta_reg_access, .cmd_resp = nxpwifi_ret_sta_reg_access}, {.cmd_no = HOST_CMD_11N_CFG, .prepare_cmd = nxpwifi_cmd_sta_11n_cfg, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_11N_ADDBA_REQ, .prepare_cmd = nxpwifi_cmd_sta_11n_addba_req, .cmd_resp = nxpwifi_ret_sta_11n_addba_req}, {.cmd_no = HOST_CMD_11N_ADDBA_RSP, .prepare_cmd = nxpwifi_cmd_sta_11n_addba_rsp, .cmd_resp = nxpwifi_ret_sta_11n_addba_rsp}, {.cmd_no = HOST_CMD_11N_DELBA, .prepare_cmd = nxpwifi_cmd_sta_11n_delba, .cmd_resp = nxpwifi_ret_sta_11n_delba}, {.cmd_no = HOST_CMD_TXPWR_CFG, .prepare_cmd = nxpwifi_cmd_sta_tx_power_cfg, .cmd_resp = nxpwifi_ret_sta_tx_power_cfg}, {.cmd_no = HOST_CMD_TX_RATE_CFG, .prepare_cmd = nxpwifi_cmd_sta_tx_rate_cfg, .cmd_resp = nxpwifi_ret_sta_tx_rate_cfg}, {.cmd_no = HOST_CMD_RECONFIGURE_TX_BUFF, .prepare_cmd = nxpwifi_cmd_sta_reconfigure_rx_buff, .cmd_resp = nxpwifi_ret_sta_reconfigure_rx_buff}, {.cmd_no = HOST_CMD_CHAN_REPORT_REQUEST, .prepare_cmd = nxpwifi_cmd_sta_chan_report_request, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_AMSDU_AGGR_CTRL, .prepare_cmd = nxpwifi_cmd_sta_amsdu_aggr_ctrl, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_ROBUST_COEX, .prepare_cmd = nxpwifi_cmd_sta_robust_coex, .cmd_resp = nxpwifi_ret_sta_robust_coex}, {.cmd_no = HOST_CMD_802_11_PS_MODE_ENH, .prepare_cmd = nxpwifi_cmd_sta_enh_power_mode, .cmd_resp = nxpwifi_ret_sta_enh_power_mode}, {.cmd_no = HOST_CMD_802_11_HS_CFG_ENH, .prepare_cmd = nxpwifi_cmd_sta_802_11_hs_cfg, .cmd_resp = nxpwifi_ret_sta_802_11_hs_cfg}, {.cmd_no = HOST_CMD_CAU_REG_ACCESS, .prepare_cmd = nxpwifi_cmd_sta_reg_access, .cmd_resp = nxpwifi_ret_sta_reg_access}, {.cmd_no = HOST_CMD_SET_BSS_MODE, .prepare_cmd = nxpwifi_cmd_sta_set_bss_mode, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_802_11_NET_MONITOR, .prepare_cmd = nxpwifi_cmd_sta_802_11_net_monitor, .cmd_resp = nxpwifi_ret_sta_802_11_net_monitor}, {.cmd_no = HOST_CMD_802_11_SCAN_EXT, .prepare_cmd = nxpwifi_cmd_sta_802_11_scan_ext, .cmd_resp = nxpwifi_ret_sta_802_11_scan_ext}, {.cmd_no = HOST_CMD_COALESCE_CFG, .prepare_cmd = nxpwifi_cmd_sta_coalesce_cfg, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_MGMT_FRAME_REG, .prepare_cmd = nxpwifi_cmd_sta_mgmt_frame_reg, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_REMAIN_ON_CHAN, .prepare_cmd = nxpwifi_cmd_sta_remain_on_chan, .cmd_resp = nxpwifi_ret_sta_remain_on_chan}, {.cmd_no = HOST_CMD_GTK_REKEY_OFFLOAD_CFG, .prepare_cmd = nxpwifi_cmd_sta_gtk_rekey_offload, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_11AC_CFG, .prepare_cmd = nxpwifi_cmd_sta_11ac_cfg, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_HS_WAKEUP_REASON, .prepare_cmd = nxpwifi_cmd_sta_hs_wakeup_reason, .cmd_resp = nxpwifi_ret_sta_hs_wakeup_reason}, {.cmd_no = HOST_CMD_MC_POLICY, .prepare_cmd = nxpwifi_cmd_sta_mc_policy, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_FW_DUMP_EVENT, .prepare_cmd = nxpwifi_cmd_fill_head_only, .cmd_resp = NULL}, {.cmd_no = HOST_CMD_SDIO_SP_RX_AGGR_CFG, .prepare_cmd = nxpwifi_cmd_sta_sdio_rx_aggr_cfg, .cmd_resp = nxpwifi_ret_sta_sdio_rx_aggr_cfg}, {.cmd_no = HOST_CMD_STA_CONFIGURE, .prepare_cmd = nxpwifi_cmd_sta_get_chan_info, .cmd_resp = nxpwifi_ret_sta_get_chan_info}, {.cmd_no = HOST_CMD_CHAN_REGION_CFG, .prepare_cmd = nxpwifi_cmd_sta_chan_region_cfg, .cmd_resp = nxpwifi_ret_sta_chan_region_cfg}, {.cmd_no = HOST_CMD_PACKET_AGGR_CTRL, .prepare_cmd = nxpwifi_cmd_sta_pkt_aggr_ctrl, .cmd_resp = nxpwifi_ret_sta_pkt_aggr_ctrl}, {.cmd_no = HOST_CMD_11AX_CFG, .prepare_cmd = nxpwifi_cmd_sta_11ax_cfg, .cmd_resp = nxpwifi_ret_sta_11ax_cfg}, {.cmd_no = HOST_CMD_11AX_CMD, .prepare_cmd = nxpwifi_cmd_sta_11ax_cmd, .cmd_resp = nxpwifi_ret_sta_11ax_cmd}, {.cmd_no = HOST_CMD_TWT_CFG, .prepare_cmd = nxpwifi_cmd_sta_twt_cfg, .cmd_resp = nxpwifi_ret_sta_twt_cfg}, }; /* * Prepare a command before sending it to firmware by invoking the * appropriate handler based on the command ID. */ int nxpwifi_sta_prepare_cmd(struct nxpwifi_private *priv, struct cmd_ctrl_node *cmd_node, u16 cmd_action, u32 cmd_oid) { struct nxpwifi_adapter *adapter = priv->adapter; u16 cmd_no = cmd_node->cmd_no; struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *)cmd_node->skb->data; void *data_buf = cmd_node->data_buf; int i, ret = -EINVAL; for (i = 0; i < ARRAY_SIZE(cmd_table_sta); i++) { if (cmd_no == cmd_table_sta[i].cmd_no) { if (cmd_table_sta[i].prepare_cmd) ret = cmd_table_sta[i].prepare_cmd(priv, cmd, cmd_no, data_buf, cmd_action, cmd_oid); cmd_node->cmd_resp = cmd_table_sta[i].cmd_resp; break; } } if (i == ARRAY_SIZE(cmd_table_sta)) nxpwifi_dbg(adapter, ERROR, "%s: unknown command: %#x\n", __func__, cmd_no); else nxpwifi_dbg(adapter, CMD, "%s: command: %#x\n", __func__, cmd_no); return ret; } /* * Initialize firmware after download or during virtual interface * reinitialization to bring the device to a working state. */ int nxpwifi_sta_init_cmd(struct nxpwifi_private *priv, u8 first_sta, bool init) { struct nxpwifi_adapter *adapter = priv->adapter; int ret; struct nxpwifi_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; struct nxpwifi_ds_auto_ds auto_ds; enum state_11d_t state_11d; struct nxpwifi_ds_11n_tx_cfg tx_cfg; u8 sdio_sp_rx_aggr_enable; if (first_sta) { ret = nxpwifi_send_cmd(priv, HOST_CMD_FUNC_INIT, HOST_ACT_GEN_SET, 0, NULL, true); if (ret) return ret; if (adapter->cal_data) nxpwifi_send_cmd(priv, HOST_CMD_CFG_DATA, HOST_ACT_GEN_SET, 0, NULL, true); /* Read MAC address from HW */ ret = nxpwifi_send_cmd(priv, HOST_CMD_GET_HW_SPEC, HOST_ACT_GEN_GET, 0, NULL, true); if (ret) return ret; /* * Set SDIO Single Port RX Aggr Info */ if (priv->adapter->iface_type == NXPWIFI_SDIO && ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && !priv->adapter->host_disable_sdio_rx_aggr) { sdio_sp_rx_aggr_enable = true; ret = nxpwifi_send_cmd(priv, HOST_CMD_SDIO_SP_RX_AGGR_CFG, HOST_ACT_GEN_SET, 0, &sdio_sp_rx_aggr_enable, true); if (ret) { nxpwifi_dbg(priv->adapter, ERROR, "error while enabling SP aggregation..disable it"); adapter->sdio_rx_aggr_enable = false; } } /* Reconfigure tx buf size */ ret = nxpwifi_send_cmd(priv, HOST_CMD_RECONFIGURE_TX_BUFF, HOST_ACT_GEN_SET, 0, &priv->adapter->tx_buf_size, true); if (ret) return ret; if (priv->bss_type != NXPWIFI_BSS_TYPE_UAP) { /* Enable IEEE PS by default */ priv->adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP; ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, EN_AUTO_PS, BITMAP_STA_PS, NULL, true); if (ret) return ret; } nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REGION_CFG, HOST_ACT_GEN_GET, 0, NULL, true); } /* get tx rate */ ret = nxpwifi_send_cmd(priv, HOST_CMD_TX_RATE_CFG, HOST_ACT_GEN_GET, 0, NULL, true); if (ret) return ret; priv->data_rate = 0; /* get tx power */ ret = nxpwifi_send_cmd(priv, HOST_CMD_RF_TX_PWR, HOST_ACT_GEN_GET, 0, NULL, true); if (ret) return ret; memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); amsdu_aggr_ctrl.enable = true; /* Send request to firmware */ ret = nxpwifi_send_cmd(priv, HOST_CMD_AMSDU_AGGR_CTRL, HOST_ACT_GEN_SET, 0, &amsdu_aggr_ctrl, true); if (ret) return ret; /* MAC Control must be the last command in init_fw */ /* set MAC Control */ ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL, HOST_ACT_GEN_SET, 0, &priv->curr_pkt_filter, true); if (ret) return ret; if (!disable_auto_ds && first_sta && priv->bss_type != NXPWIFI_BSS_TYPE_UAP) { /* Enable auto deep sleep */ auto_ds.auto_ds = DEEP_SLEEP_ON; auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, EN_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true); if (ret) return ret; } if (priv->bss_type != NXPWIFI_BSS_TYPE_UAP) { /* Send cmd to FW to enable/disable 11D function */ state_11d = ENABLE_11D; ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, HOST_ACT_GEN_SET, DOT11D_I, &state_11d, true); if (ret) nxpwifi_dbg(priv->adapter, ERROR, "11D: failed to enable 11D\n"); } /* * Send cmd to FW to configure 11n specific configuration * (Short GI, Channel BW, Green field support etc.) for transmit */ tx_cfg.tx_htcap = NXPWIFI_FW_DEF_HTTXCFG; ret = nxpwifi_send_cmd(priv, HOST_CMD_11N_CFG, HOST_ACT_GEN_SET, 0, &tx_cfg, true); return ret; }