blob: e660a402088b3f7bc50c0f742b636140f47b6421 [file] [log] [blame]
// Copyright 2025 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 "server.h"
#include <lib/driver/logging/cpp/logger.h>
#include "src/devices/block/drivers/ufs/uic/uic_commands.h"
#include "src/devices/block/drivers/ufs/upiu/attributes.h"
#include "src/devices/block/drivers/ufs/upiu/descriptors.h"
#include "src/devices/block/drivers/ufs/upiu/flags.h"
namespace ufs {
template <typename ResponseUpiu>
fit::result<QueryErrorCode, ResponseUpiu> UfsServer::HandleQueryRequestUpiu(
QueryRequestUpiu& request) {
auto response = controller_->GetTransferRequestProcessor().SendQueryRequestUpiu(request);
if (response.is_error()) {
return fit::error(QueryErrorCode::kGeneralFailure);
}
if (response->GetHeader().response != UpiuHeaderResponseCode::kTargetSuccess) {
return fit::error(QueryErrorCode(response->GetHeader().response));
}
return fit::ok(std::move(response->GetResponse<ResponseUpiu>()));
}
template fit::result<QueryErrorCode, DescriptorResponseUpiu>
UfsServer::HandleQueryRequestUpiu<DescriptorResponseUpiu>(QueryRequestUpiu& request);
template fit::result<QueryErrorCode, FlagResponseUpiu>
UfsServer::HandleQueryRequestUpiu<FlagResponseUpiu>(QueryRequestUpiu& request);
template fit::result<QueryErrorCode, AttributeResponseUpiu>
UfsServer::HandleQueryRequestUpiu<AttributeResponseUpiu>(QueryRequestUpiu& request);
namespace {
// Extracts the 'type' from the FIDL request object, converts it to an internal type,
// and then extracts the index from the 'identifier' to return it.
// Return value:
// - On success: Returns a std::pair containing the internal type and the index.
// - On failure: Returns an appropriate QueryErrorCode error.
// Parameters:
// - request: The FIDL request object.
// - max_count: The valid range for the internal type.
template <typename RequestView, typename InternalType>
fit::result<QueryErrorCode, std::pair<InternalType, uint8_t>> GetInternalTypeAndIndex(
const RequestView& request, InternalType max_count) {
if (!request.has_type()) {
fdf::error("Invalid FIDL request: missing request type");
return fit::error(QueryErrorCode::kInvalidIdn);
}
// Since the DescriptorType, Flags, and Attributes enums are defined to have the same numeric
// values as those defined in the UFS Spec for each entry in the FIDL enums DescriptorType,
// FlagType, and AttributeType, we can convert between them by casting the underlying numeric
// value.
uint8_t value = fidl::ToUnderlying(request.type());
if (value >= static_cast<uint8_t>(max_count)) {
fdf::error("Cannot convert fidl type to internal Type");
return fit::error(QueryErrorCode::kGeneralFailure);
}
uint8_t index = 0;
if (request.has_identifier() && request.identifier().has_index()) {
index = request.identifier().index();
}
return fit::ok(std::make_pair(static_cast<InternalType>(value), index));
}
} // namespace
void UfsServer::ReadDescriptor(ReadDescriptorRequestView request,
ReadDescriptorCompleter::Sync& completer) {
auto result = GetInternalTypeAndIndex<fuchsia_hardware_ufs::wire::Descriptor, DescriptorType>(
request->descriptor, DescriptorType::kDescriptorCount);
if (result.is_error()) {
completer.Reply(result.take_error());
return;
}
auto [type, index] = result.value();
ReadDescriptorUpiu read_desc_upiu(type, index);
auto response = HandleQueryRequestUpiu<DescriptorResponseUpiu>(read_desc_upiu);
if (response.is_error()) {
completer.Reply(response.take_error());
return;
}
auto data = response.value().GetData<QueryResponseUpiuData>()->command_data;
fidl::Arena<> arena;
fidl::VectorView<uint8_t> desc_data(arena, data.begin(), data.end());
completer.ReplySuccess(desc_data);
}
void UfsServer::WriteDescriptor(WriteDescriptorRequestView request,
WriteDescriptorCompleter::Sync& completer) {
auto result = GetInternalTypeAndIndex<fuchsia_hardware_ufs::wire::Descriptor, DescriptorType>(
request->descriptor, DescriptorType::kDescriptorCount);
if (result.is_error()) {
completer.Reply(result.take_error());
return;
}
auto [type, index] = result.value();
WriteDescriptorUpiu write_desc_upiu(type, &request->data, index);
auto response = HandleQueryRequestUpiu<DescriptorResponseUpiu>(write_desc_upiu);
if (response.is_error()) {
completer.Reply(response.take_error());
return;
}
completer.ReplySuccess();
}
void UfsServer::ReadFlag(ReadFlagRequestView request, ReadFlagCompleter::Sync& completer) {
auto result = GetInternalTypeAndIndex<fuchsia_hardware_ufs::wire::Flag, Flags>(request->flag,
Flags::kFlagCount);
if (result.is_error()) {
completer.Reply(result.take_error());
return;
}
auto [type, _] = result.value();
ReadFlagUpiu read_flag_upiu(type);
auto response = HandleQueryRequestUpiu<FlagResponseUpiu>(read_flag_upiu);
if (response.is_error()) {
completer.Reply(response.take_error());
return;
}
completer.ReplySuccess(response.value().GetFlag());
}
void UfsServer::SetFlag(SetFlagRequestView request, SetFlagCompleter::Sync& completer) {
auto result = GetInternalTypeAndIndex<fuchsia_hardware_ufs::wire::Flag, Flags>(request->flag,
Flags::kFlagCount);
if (result.is_error()) {
completer.Reply(result.take_error());
return;
}
auto [type, _] = result.value();
SetFlagUpiu set_flag_upiu(type);
auto response = HandleQueryRequestUpiu<FlagResponseUpiu>(set_flag_upiu);
if (response.is_error()) {
completer.Reply(response.take_error());
return;
}
completer.ReplySuccess(response.value().GetFlag());
}
void UfsServer::ClearFlag(ClearFlagRequestView request, ClearFlagCompleter::Sync& completer) {
auto result = GetInternalTypeAndIndex<fuchsia_hardware_ufs::wire::Flag, Flags>(request->flag,
Flags::kFlagCount);
if (result.is_error()) {
completer.Reply(result.take_error());
return;
}
auto [type, _] = result.value();
ClearFlagUpiu clear_flag_upiu(type);
auto response = HandleQueryRequestUpiu<FlagResponseUpiu>(clear_flag_upiu);
if (response.is_error()) {
completer.Reply(response.take_error());
return;
}
completer.ReplySuccess(response.value().GetFlag());
}
void UfsServer::ToggleFlag(ToggleFlagRequestView request, ToggleFlagCompleter::Sync& completer) {
auto result = GetInternalTypeAndIndex<fuchsia_hardware_ufs::wire::Flag, Flags>(request->flag,
Flags::kFlagCount);
if (result.is_error()) {
completer.Reply(result.take_error());
return;
}
auto [type, _] = result.value();
ToggleFlagUpiu toggle_flag_upiu(type);
auto response = HandleQueryRequestUpiu<FlagResponseUpiu>(toggle_flag_upiu);
if (response.is_error()) {
completer.Reply(response.take_error());
return;
}
completer.ReplySuccess(response.value().GetFlag());
}
void UfsServer::ReadAttribute(ReadAttributeRequestView request,
ReadAttributeCompleter::Sync& completer) {
auto result = GetInternalTypeAndIndex<fuchsia_hardware_ufs::wire::Attribute, Attributes>(
request->attr, Attributes::kAttributeCount);
if (result.is_error()) {
completer.Reply(result.take_error());
return;
}
auto [type, index] = result.value();
ReadAttributeUpiu read_attr_upiu(type, index);
auto response = HandleQueryRequestUpiu<AttributeResponseUpiu>(read_attr_upiu);
if (response.is_error()) {
completer.Reply(response.take_error());
return;
}
completer.ReplySuccess(response.value().GetAttribute());
}
void UfsServer::WriteAttribute(WriteAttributeRequestView request,
WriteAttributeCompleter::Sync& completer) {
auto result = GetInternalTypeAndIndex<fuchsia_hardware_ufs::wire::Attribute, Attributes>(
request->attr, Attributes::kAttributeCount);
if (result.is_error()) {
completer.Reply(result.take_error());
return;
}
auto [type, index] = result.value();
WriteAttributeUpiu write_attr_upiu(type, request->value, index);
auto response = HandleQueryRequestUpiu<AttributeResponseUpiu>(write_attr_upiu);
if (response.is_error()) {
completer.Reply(response.take_error());
return;
}
completer.ReplySuccess();
}
void UfsServer::SendUicCommand(SendUicCommandRequestView request,
SendUicCommandCompleter::Sync& completer) {
UicCommandOpcode opcode = static_cast<UicCommandOpcode>(fidl::ToUnderlying(request->opcode));
std::unique_ptr<UicCommand> command = CreateUicCommand(opcode, request);
if (!command) {
fdf::error("Unsupported UIC command opcode: 0x{:x}", static_cast<uint32_t>(opcode));
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
return;
}
auto result = command->SendCommand();
if (result.is_error()) {
fdf::error("Failed to send UicCommand Opcode: 0x{:x}", static_cast<uint32_t>(opcode));
completer.ReplyError(result.error_value());
return;
}
uint32_t response = result.value().value_or(0);
completer.ReplySuccess(response);
}
std::unique_ptr<UicCommand> UfsServer::CreateUicCommand(UicCommandOpcode opcode,
SendUicCommandRequestView request) {
uint16_t mib_attribute = (request->argument[0] >> 16) & 0xFFFF;
uint16_t gen_selector_index = request->argument[0] & 0xFFFF;
uint8_t attr_set_type = (request->argument[1] >> 16) & 0xFF;
switch (opcode) {
case UicCommandOpcode::kDmeGet:
return std::make_unique<DmeGetUicCommand>(*controller_, mib_attribute, gen_selector_index);
case UicCommandOpcode::kDmeSet:
return std::make_unique<DmeSetUicCommand>(*controller_, mib_attribute, gen_selector_index,
attr_set_type, request->argument[2]);
case UicCommandOpcode::kDmePeerGet:
return std::make_unique<DmePeerGetUicCommand>(*controller_, mib_attribute,
gen_selector_index);
case UicCommandOpcode::kDmePeerSet:
return std::make_unique<DmePeerSetUicCommand>(*controller_, mib_attribute, gen_selector_index,
attr_set_type, request->argument[2]);
default:
return nullptr;
}
}
void UfsServer::Request(RequestRequestView request, RequestCompleter::Sync& completer) {
uint8_t transaction_type = request->request[0];
if (transaction_type == UpiuTransactionCodes::kQueryRequest) {
ProcessQueryRequestUpiu(request, completer);
} else {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
}
void UfsServer::ProcessQueryRequestUpiu(const RequestRequestView& request,
RequestCompleter::Sync& completer) {
if (request->request.size() != sizeof(QueryRequestUpiuData)) {
fdf::error("Data size mismatch: expected {}, got {}", sizeof(QueryRequestUpiuData),
request->request.size());
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
QueryRequestUpiu query_request_upiu(
*reinterpret_cast<const QueryRequestUpiuData*>(request->request.data()));
auto response = controller_->GetTransferRequestProcessor()
.SendRequestUpiu<QueryRequestUpiu, QueryResponseUpiu>(query_request_upiu);
if (response.is_error()) {
completer.ReplyError(response.error_value());
return;
}
auto response_upiu_data =
reinterpret_cast<uint8_t*>(response.value()->GetData<QueryResponseUpiuData>());
fidl::Arena<> arena;
fidl::VectorView<uint8_t> request_data(arena, response_upiu_data,
response_upiu_data + sizeof(QueryResponseUpiuData));
completer.ReplySuccess(request_data);
}
void UfsServer::ReadBuffer(ReadBufferRequestView request, ReadBufferCompleter::Sync& completer) {
size_t size;
if (request->data.get_prop_content_size(&size); size < request->length) {
completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
return;
}
std::vector<uint8_t> buf(request->length);
if (zx_status_t status = controller_->ReadBuffer(
kPlaceholderTarget, request->lun, static_cast<uint8_t>(request->mode), request->buffer_id,
request->buffer_offset, {.iov_base = buf.data(), .iov_len = request->length});
status != ZX_OK) {
completer.ReplyError(status);
return;
}
if (zx_status_t status = request->data.write(buf.data(), 0, request->length); status != ZX_OK) {
completer.ReplyError(status);
return;
}
completer.ReplySuccess();
}
void UfsServer::WriteBuffer(WriteBufferRequestView request, WriteBufferCompleter::Sync& completer) {
std::vector<uint8_t> buf(request->length);
if (zx_status_t status = request->data.read(buf.data(), 0, request->length); status != ZX_OK) {
completer.ReplyError(status);
return;
}
if (zx_status_t status = controller_->WriteBuffer(
kPlaceholderTarget, request->lun, static_cast<uint8_t>(request->mode), request->buffer_id,
request->buffer_offset, {.iov_base = buf.data(), .iov_len = request->length});
status != ZX_OK) {
completer.ReplyError(status);
return;
}
completer.ReplySuccess();
}
} // namespace ufs