| // 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 "third_party/iwlwifi/test/sim-nvm.h" |
| |
| #include <string.h> |
| #include <zircon/assert.h> |
| |
| #include <algorithm> |
| #include <vector> |
| |
| extern "C" { |
| #include "third_party/iwlwifi/fw/api/nvm-reg.h" |
| #include "third_party/iwlwifi/mvm/mvm.h" |
| } |
| |
| #include "third_party/iwlwifi/test/sim-nvm-data.inc" |
| |
| namespace wlan::testing { |
| |
| std::vector<uint8_t> SimNvm::HandleChunkRead(uint8_t target, uint16_t type, uint16_t offset, |
| uint16_t length) { |
| auto sections = GetDefaultNvmSections(); |
| for (auto iter : sections) { |
| if (iter.target != target || iter.type != type) { |
| continue; |
| } |
| |
| // Offsetting a null pointer is undefined behavior, even if the offset is zero. Passing a null |
| // pointer to memcpy (or any similar function) is also undefined behavior. |
| if (iter.data.data() == nullptr) { |
| // There is no other <target, type> pair existing so that we can have early return. See the |
| // comment of GetDefaultNvmSections(). |
| return {}; |
| } |
| |
| // Handle the boundary cases. |
| const size_t read_offset = std::min<size_t>(offset, iter.data.size()); |
| const size_t read_length = std::min<size_t>(length, iter.data.size() - read_offset); |
| std::vector<uint8_t> ret; |
| ret.reserve(read_length); |
| std::copy_n(iter.data.begin() + read_offset, read_length, std::back_inserter(ret)); |
| return ret; |
| } |
| |
| return {}; // No segment found. |
| } |
| |
| zx_status_t SimNvm::HandleCommand(struct iwl_host_cmd* cmd, SimMvmResponse* resp) { |
| // Currently we only support the first data segment. |
| ZX_ASSERT(!cmd->data[1]); |
| |
| const struct iwl_nvm_access_cmd* nvm_access_cmd = |
| reinterpret_cast<const struct iwl_nvm_access_cmd*>(cmd->data[0]); |
| uint8_t target = nvm_access_cmd->target; |
| uint16_t type = le16_to_cpu(nvm_access_cmd->type); |
| uint16_t offset = le16_to_cpu(nvm_access_cmd->offset); |
| uint16_t length = le16_to_cpu(nvm_access_cmd->length); |
| |
| switch (nvm_access_cmd->op_code) { |
| case NVM_READ_OPCODE: { |
| std::vector<uint8_t> payload = HandleChunkRead(target, type, offset, length); |
| resp->resize(sizeof(struct iwl_nvm_access_resp) + payload.size()); |
| struct iwl_nvm_access_resp* nvm_resp = |
| reinterpret_cast<struct iwl_nvm_access_resp*>(resp->data()); |
| nvm_resp->offset = cpu_to_le16(offset); |
| nvm_resp->type = cpu_to_le16(type); |
| nvm_resp->status = cpu_to_le16(READ_NVM_CHUNK_SUCCEED); |
| nvm_resp->length = cpu_to_le16(payload.size()); |
| memcpy(nvm_resp->data, payload.data(), payload.size()); |
| return ZX_OK; |
| } |
| |
| default: |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| } |
| |
| } // namespace wlan::testing |