blob: e3fc2c021501fedc662979f686178d910b572200 [file] [log] [blame]
// Copyright 2024 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 "task_management_request_processor.h"
#include <lib/driver/logging/cpp/logger.h>
#include <lib/trace/event.h>
#include <memory>
#include "src/devices/block/drivers/ufs/task_management_request_descriptor.h"
#include "src/devices/block/drivers/ufs/ufs.h"
namespace ufs {
zx::result<> TaskManagementRequestProcessor::Init() {
zx_paddr_t paddr =
request_list_.GetRequestDescriptorPhysicalAddress<TaskManagementRequestDescriptor>(0);
UtmrListBaseAddressReg::Get().FromValue(paddr & 0xffffffff).WriteTo(&register_);
UtmrListBaseAddressUpperReg::Get().FromValue(paddr >> 32).WriteTo(&register_);
if (!HostControllerStatusReg::Get()
.ReadFrom(&register_)
.utp_task_management_request_list_ready()) {
fdf::error("UTP task management request list is not ready\n");
return zx::error(ZX_ERR_INTERNAL);
}
if (UtmrListDoorBellReg::Get().ReadFrom(&register_).door_bell() != 0) {
fdf::error("UTP task management request list door bell is not ready\n");
return zx::error(ZX_ERR_INTERNAL);
}
// Start UTP task management request list.
UtmrListRunStopReg::Get().FromValue(1).WriteTo(&register_);
return zx::ok();
}
uint32_t TaskManagementRequestProcessor::ProcessCompletionOfIoRequests() {
uint32_t completion_count = 0;
if (disable_completion_) {
return completion_count;
}
// Search for all pending slots and signed the ones already done.
request_list_.ForEachSlot([&](uint8_t slot_num, RequestSlot &request_slot) {
if (request_slot.state == SlotState::kScheduled) {
if (!(UtmrListDoorBellReg::Get().ReadFrom(&register_).door_bell() & (1 << slot_num))) {
zx::result<> result = zx::ok();
// Check task management response.
auto descriptor =
request_list_.GetRequestDescriptor<TaskManagementRequestDescriptor>(slot_num);
TaskManagementResponseUpiu response(descriptor->GetResponseData());
if (response.GetHeader().response != UpiuHeaderResponseCode::kTargetSuccess) {
fdf::error("UTP task management request command failure: response={}",
response.GetHeader().response);
result = zx::error(ZX_ERR_BAD_STATE);
}
request_slot.result = result.status_value();
sync_completion_signal(&request_slot.complete);
++completion_count;
}
}
});
return completion_count;
}
zx::result<TaskManagementResponseUpiu> TaskManagementRequestProcessor::SendTaskManagementRequest(
TaskManagementRequestUpiu &request) {
// 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());
zx::result<uint8_t> slot = ReserveSlot();
if (slot.is_error()) {
return zx::error(ZX_ERR_NO_RESOURCES);
}
RequestSlot &request_slot = request_list_.GetSlot(slot.value());
ZX_DEBUG_ASSERT_MSG(request_slot.state == SlotState::kReserved, "Invalid slot state");
if (zx::result<> result = FillDescriptorAndSendRequest(slot.value(), request);
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();
}
// Wait for completion.
TRACE_DURATION("ufs", "SendTaskManagementRequest::sync_completion_wait", "slot", slot.value());
zx_status_t status = sync_completion_wait(&request_slot.complete, GetTimeout().get());
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("SendTaskManagementRequest timed out: {}", zx_status_get_string(status));
return zx::error(status);
}
if (request_result != ZX_OK) {
return zx::error(request_result);
}
// Get the UTP task management response UPIU.
auto descriptor =
request_list_.GetRequestDescriptor<TaskManagementRequestDescriptor>(slot.value());
TaskManagementResponseUpiu response(descriptor->GetResponseData());
return zx::ok(response);
}
zx::result<TaskManagementServiceResponse>
TaskManagementRequestProcessor::GetTaskManagementServiceResponse(TaskManagementFunction function,
uint8_t lun, uint8_t task_tag) {
TaskManagementRequestUpiu query_task(function, lun, task_tag);
zx::result<TaskManagementResponseUpiu> response = SendTaskManagementRequest(query_task);
if (response.is_error()) {
fdf::error("Failed to task management(0x{:x}) command, {}", static_cast<uint8_t>(function),
response.status_string());
return response.take_error();
}
return zx::ok(static_cast<TaskManagementServiceResponse>(
response->GetData<TaskManagementResponseUpiuData>()->output_param1));
}
zx::result<> TaskManagementRequestProcessor::FillDescriptorAndSendRequest(
uint8_t slot, TaskManagementRequestUpiu &request) {
auto descriptor = request_list_.GetRequestDescriptor<TaskManagementRequestDescriptor>(slot);
// Fill up UTP task management request descriptor.
memset(descriptor, 0, sizeof(TaskManagementRequestDescriptor));
descriptor->set_interrupt(true);
// If the command was successful, overwrite |overall_command_status| field with |kSuccess|.
descriptor->set_overall_command_status(OverallCommandStatus::kInvalid);
// Copy the UTP task management request UPIU to the descriptor.
memcpy(descriptor->GetRequestData(), request.GetData(), sizeof(TaskManagementRequestUpiuData));
if (zx::result<> result = controller_.Notify(NotifyEvent::kSetupTaskManagementRequestList, 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();
}
return zx::ok();
}
} // namespace ufs