blob: 2d98032035c0d4e3ea911ea83d344b07c8fe3d2e [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/iwlwifi/test/sim-mvm.h"
extern "C" {
#include "src/iwlwifi/fw/api/commands.h"
#include "src/iwlwifi/fw/api/datapath.h"
#include "src/iwlwifi/fw/api/sta.h"
} // extern "C"
#include "src/iwlwifi/test/inspect-host-cmd.h"
namespace wlan {
namespace testing {
static void build_response_with_status(SimMvmResponse* resp, uint32_t status) {
// Check 'struct iwl_rx_packet'. The 'len_n_flags' includes the 'iwl_cmd_header' size and
// its payload, which is 'iwl_cmd_response'.
resp->resize(sizeof(struct iwl_cmd_header) + sizeof(struct iwl_cmd_response));
struct iwl_cmd_response* cmd_resp = reinterpret_cast<struct iwl_cmd_response*>(resp->data());
cmd_resp->status = cpu_to_le32(status);
}
zx_status_t SimMvm::SendCmd(struct iwl_host_cmd* cmd, bool* notify_wait) {
INSPECT_HOST_CMD(cmd);
uint8_t opcode = iwl_cmd_opcode(cmd->id);
uint8_t group_id = iwl_cmd_groupid(cmd->id);
SimMvmResponse resp; // Used by command functions to return packet.
zx_status_t ret;
*notify_wait = false;
switch (group_id) {
case LONG_GROUP:
switch (opcode) { // enum iwl_legacy_cmds
// No state change for the following commands.
// On real hardware, this command will reply a pakcet to unblock the driver waiting.
// In the simulated code, we don't generate the packet. Instead, we unblock it directly.
case PHY_CONFIGURATION_CMD:
// passthru
// The driver code expects 2 notifications from the firmware in this command:
//
// 1. In iwl_mvm_time_event_send_add(), it waits for the notification of the completion of
// TIME_EVENT_CMD command.
// 2. In iwl_mvm_protect_session(), it waits for the TIME_EVENT_NOTIFICATION.
//
// However, the current sim-mvm code can only unblock the first wait. So added
// TODO(fxbug.dev/51671) to track this.
case TIME_EVENT_CMD:
// passthru
// The above commands require unblock the notification.
*notify_wait = true;
__FALLTHROUGH;
case SHARED_MEM_CFG:
case TX_ANT_CONFIGURATION_CMD:
case PHY_DB_CMD:
case PHY_CONTEXT_CMD:
case REPLY_THERMAL_MNG_BACKOFF:
case POWER_TABLE_CMD:
case BT_CONFIG:
case MAC_CONTEXT_CMD:
case TXPATH_FLUSH:
case SCAN_OFFLOAD_REQUEST_CMD:
case SCAN_CFG_CMD:
case SCAN_REQ_UMAC:
case MAC_PM_POWER_TABLE:
case SCD_QUEUE_CFG:
case FW_PAGING_BLOCK_CMD:
case TIME_QUOTA_CMD:
case MCAST_FILTER_CMD:
case REPLY_BEACON_FILTERING_CMD:
return ZX_OK;
// Command would return 'status' back to driver.
case BINDING_CONTEXT_CMD:
build_response_with_status(&resp, 0);
ret = ZX_OK;
break;
case ADD_STA: // fall-thru
case REMOVE_STA:
case ADD_STA_KEY:
build_response_with_status(&resp, ADD_STA_SUCCESS);
ret = ZX_OK;
break;
case NVM_ACCESS_CMD:
ret = nvm_.HandleCommand(cmd, &resp);
break;
default:
IWL_ERR(nullptr, "unsupported long command ID : %#x\n", cmd->id);
INSPECT_HOST_CMD(cmd);
return ZX_ERR_NOT_SUPPORTED;
}
break;
case DATA_PATH_GROUP:
switch (opcode) { // enum iwl_data_path_subcmd_ids
case DQA_ENABLE_CMD:
ret = ZX_OK;
break;
default:
IWL_ERR(nullptr, "unsupported data path command ID : %#x\n", cmd->id);
INSPECT_HOST_CMD(cmd);
return ZX_ERR_NOT_SUPPORTED;
}
break;
default:
IWL_ERR(nullptr, "unsupported command ID : %#x\n", cmd->id);
INSPECT_HOST_CMD(cmd);
return ZX_ERR_NOT_SUPPORTED;
}
// Prepare the response packet buffer if the command requires a response.
struct iwl_rx_packet* resp_pkt = reinterpret_cast<struct iwl_rx_packet*>(resp_buf_.data());
ZX_ASSERT(sizeof(*resp_pkt) + resp.size() <= resp_buf_.size()); // avoid overflow
if (cmd->flags & CMD_WANT_SKB) {
resp_pkt->len_n_flags = cpu_to_le32(resp.size());
resp_pkt->hdr.cmd = opcode;
resp_pkt->hdr.group_id = group_id;
resp_pkt->hdr.sequence = 0;
memcpy(resp_pkt->data, resp.data(), resp.size());
cmd->resp_pkt = resp_pkt;
} else {
cmd->resp_pkt = nullptr;
}
return ret;
}
} // namespace testing
} // namespace wlan