| // Copyright 2023 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 "transfer_request_processor.h" |
| |
| #include <lib/driver/logging/cpp/logger.h> |
| #include <lib/trace/event.h> |
| |
| #include <optional> |
| |
| #include <safemath/checked_math.h> |
| #include <safemath/safe_conversions.h> |
| |
| #include "src/devices/block/drivers/ufs/ufs.h" |
| #include "src/devices/block/drivers/ufs/upiu/upiu_transactions.h" |
| |
| namespace ufs { |
| |
| namespace { |
| void FillPrdt(PhysicalRegionDescriptionTableEntry *prdt, |
| const std::vector<zx_paddr_t> &buffer_physical_addresses, uint32_t prdt_count, |
| uint32_t data_length) { |
| for (uint32_t i = 0; i < prdt_count; ++i) { |
| // It only supports 4KB data buffers for each entry in the scatter-gather. |
| ZX_ASSERT(buffer_physical_addresses[i] != 0); |
| uint32_t byte_count = data_length < kPrdtEntryDataLength ? data_length : kPrdtEntryDataLength; |
| prdt->set_data_base_address(static_cast<uint32_t>(buffer_physical_addresses[i] & 0xffffffff)); |
| prdt->set_data_base_address_upper(static_cast<uint32_t>(buffer_physical_addresses[i] >> 32)); |
| prdt->set_data_byte_count(byte_count - 1); |
| |
| ++prdt; |
| data_length -= byte_count; |
| } |
| ZX_DEBUG_ASSERT(data_length == 0); |
| } |
| } // namespace |
| |
| template <> |
| std::tuple<uint16_t, uint32_t> TransferRequestProcessor::PreparePrdt<ScsiCommandUpiu>( |
| ScsiCommandUpiu &request, const uint8_t lun, const uint8_t slot, |
| const std::vector<zx_paddr_t> &buffer_phys, const uint16_t response_offset, |
| const uint16_t response_length) { |
| const uint32_t data_transfer_length = std::min(request.GetTransferBytes(), kMaxPrdtDataLength); |
| |
| request.GetHeader().lun = lun; |
| request.SetExpectedDataTransferLength(data_transfer_length); |
| |
| // Prepare PRDT(physical region description table). |
| const uint32_t prdt_entry_count = |
| fbl::round_up(data_transfer_length, kPrdtEntryDataLength) / kPrdtEntryDataLength; |
| ZX_DEBUG_ASSERT(prdt_entry_count <= kMaxPrdtEntryCount); |
| |
| uint16_t prdt_offset = response_offset + response_length; |
| uint32_t prdt_length_in_bytes = prdt_entry_count * sizeof(PhysicalRegionDescriptionTableEntry); |
| const size_t total_length = static_cast<size_t>(prdt_offset) + prdt_length_in_bytes; |
| |
| ZX_DEBUG_ASSERT_MSG(total_length <= request_list_.GetDescriptorBufferSize(slot), |
| "Invalid UPIU size for prdt"); |
| auto prdt = |
| request_list_.GetDescriptorBuffer<PhysicalRegionDescriptionTableEntry>(slot, prdt_offset); |
| memset(prdt, 0, prdt_length_in_bytes); |
| |
| FillPrdt(prdt, buffer_phys, prdt_entry_count, data_transfer_length); |
| |
| // TODO(https://fxbug.dev/42075643): Enable unmmap and write buffer command. Umap and writebuffer |
| // must set the xfer->count value differently. |
| |
| return {prdt_offset, prdt_entry_count}; |
| } |
| |
| zx::result<> TransferRequestProcessor::Init() { |
| zx_paddr_t paddr = |
| request_list_.GetRequestDescriptorPhysicalAddress<TransferRequestDescriptor>(0); |
| UtrListBaseAddressReg::Get().FromValue(paddr & 0xffffffff).WriteTo(®ister_); |
| UtrListBaseAddressUpperReg::Get().FromValue(paddr >> 32).WriteTo(®ister_); |
| |
| if (!HostControllerStatusReg::Get().ReadFrom(®ister_).utp_transfer_request_list_ready()) { |
| fdf::error("UTP transfer request list is not ready\n"); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| if (UtrListDoorBellReg::Get().ReadFrom(®ister_).door_bell() != 0) { |
| fdf::error("UTP transfer request list door bell is not ready\n"); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| if (UtrListCompletionNotificationReg::Get().ReadFrom(®ister_).notification() != 0) { |
| fdf::error("UTP transfer request list notification is not ready\n"); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| // Start Utp Transfer Request list. |
| UtrListRunStopReg::Get().FromValue(0).set_value(true).WriteTo(®ister_); |
| |
| return zx::ok(); |
| } |
| |
| zx::result<uint8_t> TransferRequestProcessor::ReserveAdminSlot() { |
| RequestSlot &slot = request_list_.GetSlot(kAdminCommandSlotNumber); |
| if (slot.state == SlotState::kFree) { |
| slot.state = SlotState::kReserved; |
| return zx::ok(kAdminCommandSlotNumber); |
| } |
| fdf::debug("Failed to reserve a admin request slot"); |
| return zx::error(ZX_ERR_NO_RESOURCES); |
| } |
| |
| zx::result<std::unique_ptr<ResponseUpiu>> TransferRequestProcessor::SendScsiUpiuUsingSlot( |
| ScsiCommandUpiu &request, uint8_t lun, uint8_t slot, std::optional<zx::unowned_vmo> data_vmo, |
| IoCommand *io_cmd, bool is_sync) { |
| uint32_t offset = 0; |
| uint32_t length = 0; |
| if (io_cmd != nullptr) { |
| offset = |
| safemath::checked_cast<uint32_t>(io_cmd->device_op.op.command.opcode == BLOCK_OPCODE_TRIM |
| ? io_cmd->device_op.op.trim.offset_dev |
| : io_cmd->device_op.op.rw.offset_dev); |
| length = io_cmd->device_op.op.command.opcode == BLOCK_OPCODE_TRIM |
| ? io_cmd->device_op.op.trim.length |
| : io_cmd->device_op.op.rw.length; |
| } |
| TRACE_DURATION("ufs", "SendScsiUpiu", "slot", slot, "offset", offset, "length", length); |
| |
| zx::result<void *> response; |
| // Admin commands should be performed synchronously and non-admin (data) commands should be |
| // performed asynchronously. |
| if (response = SendRequestUsingSlot<ScsiCommandUpiu>(request, lun, slot, std::move(data_vmo), |
| io_cmd, is_sync); |
| response.is_error()) { |
| return response.take_error(); |
| } |
| auto response_upiu = std::make_unique<ResponseUpiu>(response.value()); |
| return zx::ok(std::move(response_upiu)); |
| } |
| |
| zx::result<std::unique_ptr<ResponseUpiu>> TransferRequestProcessor::SendScsiUpiu( |
| ScsiCommandUpiu &request, uint8_t lun, std::optional<zx::unowned_vmo> data_vmo, |
| IoCommand *io_cmd) { |
| const bool is_admin = io_cmd == nullptr; |
| zx::result<uint8_t> slot; |
| zx::result<std::unique_ptr<ResponseUpiu>> response_upiu; |
| if (is_admin) { |
| std::lock_guard<std::mutex> lock(admin_slot_lock_); |
| slot = ReserveAdminSlot(); |
| if (slot.is_error()) { |
| return zx::error(ZX_ERR_NO_RESOURCES); |
| } |
| // Execute `SendScsiUpiuUsingSlot()` with a admin_slot_lock_ to avoid a race condition for the |
| // admin slot. |
| response_upiu = SendScsiUpiuUsingSlot(request, lun, slot.value(), data_vmo, io_cmd, is_admin); |
| } else { |
| slot = ReserveSlot(); |
| if (slot.is_error()) { |
| return zx::error(ZX_ERR_NO_RESOURCES); |
| } |
| response_upiu = SendScsiUpiuUsingSlot(request, lun, slot.value(), data_vmo, io_cmd, is_admin); |
| } |
| |
| if (response_upiu.is_error()) { |
| return response_upiu.take_error(); |
| } |
| |
| return zx::ok(std::move(response_upiu.value())); |
| } |
| |
| zx::result<std::unique_ptr<QueryResponseUpiu>> TransferRequestProcessor::SendQueryRequestUpiu( |
| QueryRequestUpiu &request) { |
| auto response = SendRequestUpiu<QueryRequestUpiu, QueryResponseUpiu>(request); |
| if (response.is_error()) { |
| QueryOpcode query_opcode = |
| static_cast<QueryOpcode>(request.GetData<QueryRequestUpiuData>()->opcode); |
| uint8_t type = request.GetData<QueryRequestUpiuData>()->idn; |
| fdf::error("Failed {}(type:0x{:x}) query request UPIU: {}", QueryOpcodeToString(query_opcode), |
| type, response.status_string()); |
| } |
| return response; |
| } |
| |
| template <class RequestType> |
| zx::result<void *> TransferRequestProcessor::SendRequestUsingSlot( |
| RequestType &request, uint8_t lun, uint8_t slot, std::optional<zx::unowned_vmo> data_vmo, |
| IoCommand *io_cmd, bool is_sync) { |
| if (is_sync) { |
| // TODO(https://fxbug.dev/42075643): Needs to be changed to be compatible with DFv2's dispatcher |
| // Since the completion is handled by the I/O thread, submitting a synchronous command from the |
| // I/O thread will cause a deadlock. |
| // ZX_DEBUG_ASSERT(controller_.GetIoThread() != thrd_current()); |
| } |
| |
| RequestSlot &request_slot = request_list_.GetSlot(slot); |
| ZX_DEBUG_ASSERT_MSG(request_slot.state == SlotState::kReserved, "Invalid slot state"); |
| |
| const uint16_t response_offset = request.GetResponseOffset(); |
| const uint16_t response_length = request.GetResponseLength(); |
| |
| request_slot.io_cmd = io_cmd; |
| request_slot.is_scsi_command = std::is_base_of<ScsiCommandUpiu, RequestType>::value; |
| request_slot.is_sync = is_sync; |
| request_slot.response_upiu_offset = response_offset; |
| |
| uint16_t prdt_offset = 0; |
| uint32_t prdt_entry_count = 0; |
| std::vector<zx_paddr_t> data_paddrs; |
| |
| if (data_vmo.has_value()) { |
| // Assign physical addresses(pin) to data vmo. The return value is the physical address of |
| // the pinned memory. |
| const uint32_t kPageSize = zx_system_get_page_size(); |
| uint64_t offset, length; |
| uint32_t option; |
| |
| if (io_cmd) { |
| // Non-admin (data) command. |
| if (io_cmd->device_op.op.command.opcode == BLOCK_OPCODE_TRIM) { |
| offset = 0; |
| length = kPageSize; |
| option = ZX_BTI_PERM_READ; |
| } else { |
| offset = io_cmd->device_op.op.rw.offset_vmo * io_cmd->block_size_bytes; |
| length = static_cast<uint64_t>(io_cmd->device_op.op.rw.length) * io_cmd->block_size_bytes; |
| option = (io_cmd->device_op.op.command.opcode == BLOCK_OPCODE_READ) ? ZX_BTI_PERM_WRITE |
| : ZX_BTI_PERM_READ; |
| } |
| } else { |
| // Admin command. |
| offset = 0; |
| data_vmo.value()->get_size(&length); |
| option = ZX_BTI_PERM_WRITE; |
| } |
| ZX_DEBUG_ASSERT(length > 0 && length % kPageSize == 0); |
| |
| data_paddrs.resize(length / kPageSize, 0); |
| if (zx_status_t status = |
| GetBti()->pin(option, *data_vmo.value(), offset, length, data_paddrs.data(), |
| length / kPageSize, &request_slot.pmt); |
| status != ZX_OK) { |
| fdf::error("Failed to pin IO buffer: {}", zx_status_get_string(status)); |
| |
| if (zx::result<> result = ClearSlot(request_slot); result.is_error()) { |
| return result.take_error(); |
| } |
| return zx::error(status); |
| } |
| } |
| |
| std::tie(prdt_offset, prdt_entry_count) = |
| PreparePrdt<RequestType>(request, lun, slot, data_paddrs, response_offset, response_length); |
| |
| // Record the slot number to |task_tag| for debugging. |
| request.GetHeader().task_tag = slot; |
| |
| // Copy request and prepare response. |
| const size_t length = static_cast<size_t>(response_offset) + response_length; |
| ZX_DEBUG_ASSERT_MSG(length <= request_list_.GetDescriptorBufferSize(slot), "Invalid UPIU size"); |
| |
| memcpy(request_list_.GetDescriptorBuffer(slot), request.GetData(), response_offset); |
| memset(request_list_.GetDescriptorBuffer<uint8_t>(slot) + response_offset, 0, response_length); |
| auto response = request_list_.GetDescriptorBuffer(slot, response_offset); |
| |
| if (zx::result<> result = |
| FillDescriptorAndSendRequest(slot, request.GetDataDirection(), response_offset, |
| response_length, prdt_offset, prdt_entry_count); |
| result.is_error()) { |
| fdf::error("Failed to send upiu: {}", result); |
| |
| if (zx::result<> result = ClearSlot(request_slot); result.is_error()) { |
| return result.take_error(); |
| } |
| return result.take_error(); |
| } |
| |
| if (is_sync) { |
| // Wait for completion. |
| TRACE_DURATION("ufs", "SendRequestUsingSlot::sync_completion_wait", "slot", slot); |
| zx_status_t status = |
| sync_completion_wait_deadline(&request_slot.complete, request_slot.deadline); |
| zx_status_t request_result = request_slot.result; |
| if (zx::result<> result = ClearSlot(request_slot); result.is_error()) { |
| return result.take_error(); |
| } |
| if (status != ZX_OK) { |
| fdf::error("SendRequestUsingSlot request timed out: {}", zx_status_get_string(status)); |
| return zx::error(status); |
| } |
| if (request_result != ZX_OK) { |
| return zx::error(request_result); |
| } |
| |
| UtrListCompletionNotificationReg::Get().FromValue(0).set_notification(1 << slot).WriteTo( |
| ®ister_); |
| } |
| |
| return zx::ok(response); |
| } |
| |
| template zx::result<void *> TransferRequestProcessor::SendRequestUsingSlot<QueryRequestUpiu>( |
| QueryRequestUpiu &request, uint8_t lun, uint8_t slot, std::optional<zx::unowned_vmo> data_vmo, |
| IoCommand *io_cmd, bool is_sync); |
| template zx::result<void *> TransferRequestProcessor::SendRequestUsingSlot<ScsiCommandUpiu>( |
| ScsiCommandUpiu &request, uint8_t lun, uint8_t slot, std::optional<zx::unowned_vmo> data_vmo, |
| IoCommand *io_cmd, bool is_sync); |
| template zx::result<void *> TransferRequestProcessor::SendRequestUsingSlot<NopOutUpiu>( |
| NopOutUpiu &request, uint8_t lun, uint8_t slot, std::optional<zx::unowned_vmo> data_vmo, |
| IoCommand *io_cmd, bool is_sync); |
| |
| zx_status_t TransferRequestProcessor::UpiuCompletion(uint8_t slot_num, RequestSlot &request_slot, |
| bool is_timeout) { |
| TRACE_DURATION("ufs", "UpiuCompletion", "slot", slot_num); |
| |
| scsi::StatusMessage status_message; |
| std::optional<std::reference_wrapper<scsi::FixedFormatSenseDataHeader>> sense_data = std::nullopt; |
| |
| ResponseUpiu response( |
| request_list_.GetDescriptorBuffer<ResponseUpiu>(slot_num, request_slot.response_upiu_offset)); |
| |
| zx::result<> request_result = zx::ok(); |
| if (is_timeout) { |
| status_message.host_status_code = scsi::HostStatusCode::kTimeout; |
| status_message.scsi_status_code = scsi::StatusCode::GOOD; |
| } else { |
| request_result = CheckResponse(slot_num, response); |
| |
| if (request_slot.is_scsi_command) { |
| status_message = CheckScsiAndGetStatusMessage(slot_num, response); |
| sense_data = *reinterpret_cast<scsi::FixedFormatSenseDataHeader *>(response.GetSenseData()); |
| } |
| } |
| |
| if (request_slot.is_scsi_command && request_result.is_ok()) { |
| // Until native UFS IO commands are defined by the UFS specification, we assume that only SCSI |
| // commands can be IO commands. |
| request_result = controller_.ScsiComplete(status_message, sense_data); |
| |
| IoCommand *io_cmd = request_slot.io_cmd; |
| if (io_cmd) { |
| io_cmd->data_vmo.reset(); |
| io_cmd->device_op.Complete(request_result.status_value()); |
| } |
| } |
| |
| if (response.GetHeader().event_alert()) { |
| if (zx::result result = controller_.GetDeviceManager().PostExceptionEventsTask(); |
| result.is_error()) { |
| fdf::error("Failed to handle Exception Event slot[{}]: {}", slot_num, result.status_string()); |
| } |
| } |
| |
| return request_result.status_value(); |
| } |
| |
| void TransferRequestProcessor::RequestCompletion(uint8_t slot_num, RequestSlot &request_slot, |
| bool is_timeout) { |
| // Check request response. |
| zx_status_t status = UpiuCompletion(slot_num, request_slot, is_timeout); |
| if (status != ZX_OK) { |
| fdf::error("Failed to complete request, slot[{}]: {}", slot_num, zx_status_get_string(status)); |
| } |
| request_slot.result = status; |
| |
| if (is_timeout) { |
| request_slot.state = SlotState::kTimeout; |
| } else if (request_slot.is_sync) { |
| sync_completion_signal(&request_slot.complete); |
| } else { |
| UtrListCompletionNotificationReg::Get() |
| .FromValue(0) |
| .set_notification(1 << slot_num) |
| .WriteTo(®ister_); |
| |
| if (zx::result result = ClearSlot(request_slot); result.is_error()) { |
| fdf::error("Failed to clear slot[{}]: {}", slot_num, result); |
| } |
| } |
| |
| --inflight_io_count_; |
| } |
| |
| bool TransferRequestProcessor::ProcessSlotCompletion(uint8_t slot_num) { |
| bool is_completed = false; |
| |
| RequestSlot &request_slot = request_list_.GetSlot(slot_num); |
| if (request_slot.state == SlotState::kScheduled) { |
| if (!(UtrListDoorBellReg::Get().ReadFrom(®ister_).door_bell() & (1 << slot_num))) { |
| RequestCompletion(slot_num, request_slot, /*timeout*/ false); |
| is_completed = true; |
| } else if (request_slot.deadline < zx_clock_get_monotonic()) { |
| RequestCompletion(slot_num, request_slot, /*timeout*/ true); |
| is_completed = true; |
| } |
| } |
| return is_completed; |
| } |
| |
| uint32_t TransferRequestProcessor::ProcessCompletionOfAdminRequests() { |
| uint32_t completion_count = 0; |
| |
| if (disable_completion_) { |
| return completion_count; |
| } |
| |
| completion_count = ProcessSlotCompletion(kAdminCommandSlotNumber); |
| return completion_count; |
| } |
| |
| uint32_t TransferRequestProcessor::ProcessCompletionOfIoRequests() { |
| uint32_t completion_count = 0; |
| |
| if (disable_completion_) { |
| return completion_count; |
| } |
| |
| // Search for all pending slots and signal the ones already done. |
| request_list_.ForEachSlot([&](uint8_t slot_num, RequestSlot &request_slot) { |
| if (slot_num != kAdminCommandSlotNumber) { |
| completion_count += ProcessSlotCompletion(slot_num); |
| } |
| }); |
| return completion_count; |
| } |
| |
| zx_time_t TransferRequestProcessor::GetEarliestTimeoutDeadline() { |
| zx_time_t deadline = ZX_TIME_INFINITE; |
| request_list_.ForEachSlot([&](uint8_t slot_num, RequestSlot &request_slot) { |
| if (request_slot.state == SlotState::kScheduled) { |
| deadline = std::min(deadline, request_slot.deadline); |
| } |
| }); |
| return deadline; |
| } |
| |
| zx::result<> TransferRequestProcessor::FillDescriptorAndSendRequest( |
| uint8_t slot, const DataDirection data_dir, const uint16_t response_offset, |
| const uint16_t response_length, const uint16_t prdt_offset, const uint32_t prdt_entry_count) { |
| auto descriptor = request_list_.GetRequestDescriptor<TransferRequestDescriptor>(slot); |
| zx_paddr_t paddr = request_list_.GetSlot(slot).command_descriptor_io->phys(); |
| |
| // Fill up UTP Transfer Request Descriptor. |
| memset(descriptor, 0, sizeof(TransferRequestDescriptor)); |
| descriptor->set_interrupt(true); |
| descriptor->set_data_direction(data_dir); |
| descriptor->set_command_type(kCommandTypeUfsStorage); |
| // If the command was successful, overwrite |overall_command_status| field with |kSuccess|. |
| descriptor->set_overall_command_status(OverallCommandStatus::kInvalid); |
| descriptor->set_utp_command_descriptor_base_address(static_cast<uint32_t>(paddr & 0xffffffff)); |
| descriptor->set_utp_command_descriptor_base_address_upper(static_cast<uint32_t>(paddr >> 32)); |
| |
| constexpr uint16_t kDwordSize = 4; |
| descriptor->set_response_upiu_offset(response_offset / kDwordSize); |
| descriptor->set_response_upiu_length(response_length / kDwordSize); |
| descriptor->set_prdt_offset(prdt_offset / kDwordSize); |
| descriptor->set_prdt_length(prdt_entry_count); |
| |
| TRACE_DURATION("ufs", "RingRequestDoorbell", "slot", slot); |
| if (zx::result<> result = controller_.Notify(NotifyEvent::kSetupTransferRequestList, slot); |
| result.is_error()) { |
| return result.take_error(); |
| } |
| if (zx::result<> result = RingRequestDoorbell(slot); result.is_error()) { |
| fdf::error("Failed to send cmd {}", result); |
| return result.take_error(); |
| } |
| ++inflight_io_count_; |
| |
| return zx::ok(); |
| } |
| |
| scsi::HostStatusCode TransferRequestProcessor::ScsiStatusToHostStatus( |
| scsi::StatusCode scsi_status) { |
| scsi::HostStatusCode host_status; |
| switch (scsi_status) { |
| case scsi::StatusCode::GOOD: |
| case scsi::StatusCode::CHECK_CONDITION: |
| host_status = scsi::HostStatusCode::kOk; |
| break; |
| case scsi::StatusCode::BUSY: |
| case scsi::StatusCode::TASK_SET_FULL: |
| host_status = scsi::HostStatusCode::kRequeue; |
| break; |
| case scsi::StatusCode::RESERVATION_CONFILCT: // optional |
| host_status = scsi::HostStatusCode::kOk; |
| break; |
| default: |
| host_status = scsi::HostStatusCode::kError; |
| break; |
| } |
| return host_status; |
| } |
| |
| scsi::HostStatusCode TransferRequestProcessor::GetScsiCommandHostStatus( |
| OverallCommandStatus ocs, UpiuHeaderResponseCode header_response, |
| scsi::StatusCode scsi_status) { |
| scsi::HostStatusCode host_status; |
| switch (ocs) { |
| case kSuccess: |
| if (header_response == UpiuHeaderResponseCode::kTargetSuccess) { |
| host_status = ScsiStatusToHostStatus(static_cast<scsi::StatusCode>(scsi_status)); |
| } else { |
| host_status = scsi::HostStatusCode::kError; |
| } |
| break; |
| case kAborted: |
| host_status = scsi::HostStatusCode::kAbort; |
| break; |
| case kInvalid: |
| host_status = scsi::HostStatusCode::kRequeue; |
| break; |
| default: |
| host_status = scsi::HostStatusCode::kError; |
| break; |
| } |
| return host_status; |
| } |
| |
| scsi::StatusMessage TransferRequestProcessor::CheckScsiAndGetStatusMessage( |
| uint8_t slot_num, AbstractResponseUpiu &response) { |
| auto descriptor = request_list_.GetRequestDescriptor<TransferRequestDescriptor>(slot_num); |
| OverallCommandStatus ocs = descriptor->overall_command_status(); |
| auto header_response = static_cast<UpiuHeaderResponseCode>(response.GetHeader().response); |
| auto scsi_status = static_cast<scsi::StatusCode>(response.GetHeader().status); |
| |
| scsi::StatusMessage message; |
| message.host_status_code = GetScsiCommandHostStatus(ocs, header_response, scsi_status); |
| message.scsi_status_code = scsi_status; |
| return message; |
| } |
| |
| zx::result<> TransferRequestProcessor::CheckResponse(uint8_t slot_num, |
| AbstractResponseUpiu &response) { |
| auto transaction_type = static_cast<UpiuTransactionCodes>(response.GetHeader().trans_type); |
| auto descriptor = request_list_.GetRequestDescriptor<TransferRequestDescriptor>(slot_num); |
| OverallCommandStatus ocs = descriptor->overall_command_status(); |
| auto header_response = static_cast<UpiuHeaderResponseCode>(response.GetHeader().response); |
| |
| switch (transaction_type) { |
| case UpiuTransactionCodes::kResponse: |
| if (response.GetHeader().command_set_type() != UpiuCommandSetType::kScsi) { |
| fdf::error( |
| "Unknown command(set type = 0x{:x}) response: ocs=0x{:x}, header_response=0x{:x}", |
| static_cast<uint32_t>(response.GetHeader().command_set_type()), |
| static_cast<uint32_t>(ocs), static_cast<uint32_t>(header_response)); |
| return zx::error(ZX_ERR_BAD_STATE); |
| } |
| // For SCSI commands, check ocs and header_response in CheckScsiAndGetStatusMessage(). |
| break; |
| case UpiuTransactionCodes::kQueryResponse: |
| if (ocs != OverallCommandStatus::kSuccess || |
| header_response != static_cast<uint8_t>(QueryResponseCode::kSuccess)) { |
| fdf::error("Query request failure: ocs=0x{:x}, header_response=0x{:x}", |
| static_cast<uint32_t>(ocs), static_cast<uint32_t>(header_response)); |
| return zx::error(ZX_ERR_BAD_STATE); |
| } |
| break; |
| default: |
| if (ocs != OverallCommandStatus::kSuccess || |
| header_response != UpiuHeaderResponseCode::kTargetSuccess) { |
| fdf::error("Generic request(transaction type = 0x{:x}) failure: ocs=0x{:x}", |
| static_cast<uint32_t>(transaction_type), static_cast<uint32_t>(ocs)); |
| return zx::error(ZX_ERR_BAD_STATE); |
| } |
| break; |
| } |
| return zx::ok(); |
| } |
| |
| } // namespace ufs |