blob: 73f18c43910179505352daafce05d0dc7ef9fcaa [file] [log] [blame]
// 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 "src/devices/serial/drivers/aml-uart/aml-uart-dfv2.h"
#include <fidl/fuchsia.hardware.power/cpp/fidl.h>
#include <lib/ddk/metadata.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/driver/logging/cpp/structured_logger.h>
#include <lib/driver/platform-device/cpp/pdev.h>
#include <lib/driver/power/cpp/element-description-builder.h>
#include <lib/driver/power/cpp/power-support.h>
#include <bind/fuchsia/cpp/bind.h>
#include <bind/fuchsia/serial/cpp/bind.h>
namespace serial {
namespace {
constexpr std::string_view kPdevName = "pdev";
constexpr std::string_view kChildName = "aml-uart";
constexpr std::string_view kDriverName = "aml-uart";
} // namespace
AmlUartV2::AmlUartV2(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: fdf::DriverBase(kDriverName, std::move(start_args), std::move(driver_dispatcher)),
driver_config_(take_config<aml_uart_config::Config>()) {}
void AmlUartV2::Start(fdf::StartCompleter completer) {
start_completer_.emplace(std::move(completer));
parent_node_client_.Bind(std::move(node()), dispatcher());
device_server_.Begin(incoming(), outgoing(), node_name(), kChildName,
fit::bind_member<&AmlUartV2::OnDeviceServerInitialized>(this),
compat::ForwardMetadata::Some({DEVICE_METADATA_MAC_ADDRESS}));
}
void AmlUartV2::PrepareStop(fdf::PrepareStopCompleter completer) {
if (aml_uart_.has_value()) {
aml_uart_->Enable(false);
}
completer(zx::ok());
}
AmlUart& AmlUartV2::aml_uart_for_testing() {
ZX_ASSERT(aml_uart_.has_value());
return aml_uart_.value();
}
void AmlUartV2::OnDeviceServerInitialized(zx::result<> device_server_init_result) {
if (device_server_init_result.is_error()) {
CompleteStart(device_server_init_result.take_error());
return;
}
fdf::PDev pdev;
{
auto result = incoming()->Connect<fuchsia_hardware_platform_device::Service::Device>(kPdevName);
if (result.is_error()) {
FDF_LOG(ERROR, "Failed to connect to platform device: %s", result.status_string());
CompleteStart(result.take_error());
return;
}
pdev = fdf::PDev{std::move(result.value())};
}
zx::result metadata = pdev.GetFidlMetadata<fuchsia_hardware_serial::SerialPortInfo>(
fuchsia_hardware_serial::SerialPortInfo::kSerializableName);
if (metadata.is_error()) {
if (metadata.status_value() == ZX_ERR_NOT_FOUND) {
FDF_LOG(DEBUG, "Serial port info metadata not found.");
} else {
FDF_LOG(ERROR, "Failed to get metadata: %s", metadata.status_string());
CompleteStart(metadata.take_error());
return;
}
} else {
serial_port_info_ = {
.serial_class = metadata->serial_class(),
.serial_vid = metadata->serial_vid(),
.serial_pid = metadata->serial_pid(),
};
}
zx::result mmio = pdev.MapMmio(0);
if (mmio.is_error()) {
FDF_SLOG(ERROR, "Failed to map mmio.", KV("status", mmio.status_string()));
CompleteStart(mmio.take_error());
return;
}
fidl::ClientEnd<fuchsia_power_system::ActivityGovernor> sag;
if (driver_config_.enable_suspend()) {
zx::result result = incoming()->Connect<fuchsia_power_system::ActivityGovernor>();
if (result.is_error() || !result->is_valid()) {
FDF_LOG(WARNING, "Failed to connect to activity governor: %s", result.status_string());
CompleteStart(result.take_error());
}
sag = std::move(result.value());
}
aml_uart_.emplace(std::move(pdev), serial_port_info_, std::move(mmio.value()),
driver_config_.enable_suspend(), std::move(sag));
// Default configuration for the case that serial_impl_config is not called.
constexpr uint32_t kDefaultBaudRate = 115200;
constexpr uint32_t kDefaultConfig = fuchsia_hardware_serialimpl::kSerialDataBits8 |
fuchsia_hardware_serialimpl::kSerialStopBits1 |
fuchsia_hardware_serialimpl::kSerialParityNone;
aml_uart_->Config(kDefaultBaudRate, kDefaultConfig);
zx::result node_controller_endpoints =
fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
if (node_controller_endpoints.is_error()) {
FDF_LOG(ERROR, "Failed to create NodeController endpoints %s",
node_controller_endpoints.status_string());
CompleteStart(node_controller_endpoints.take_error());
return;
}
fuchsia_hardware_serialimpl::Service::InstanceHandler handler({
.device =
[this](fdf::ServerEnd<fuchsia_hardware_serialimpl::Device> server_end) {
serial_impl_bindings_.AddBinding(driver_dispatcher()->get(), std::move(server_end),
&aml_uart_.value(), fidl::kIgnoreBindingClosure);
},
});
zx::result<> add_result =
outgoing()->AddService<fuchsia_hardware_serialimpl::Service>(std::move(handler), kChildName);
if (add_result.is_error()) {
FDF_LOG(ERROR, "Failed to add fuchsia_hardware_serialimpl::Service %s",
add_result.status_string());
CompleteStart(add_result.take_error());
return;
}
auto offers = device_server_.CreateOffers2();
offers.push_back(fdf::MakeOffer2<fuchsia_hardware_serialimpl::Service>(kChildName));
fuchsia_driver_framework::NodeAddArgs args{
{
.name = std::string(kChildName),
.properties = {{
fdf::MakeProperty(bind_fuchsia::SERIAL_CLASS,
static_cast<uint32_t>(aml_uart_->serial_port_info().serial_class)),
}},
.offers2 = std::move(offers),
},
};
fidl::Arena arena;
parent_node_client_
->AddChild(fidl::ToWire(arena, std::move(args)), std::move(node_controller_endpoints->server),
{})
.Then(fit::bind_member<&AmlUartV2::OnAddChildResult>(this));
}
void AmlUartV2::OnAddChildResult(
fidl::WireUnownedResult<fuchsia_driver_framework::Node::AddChild>& add_child_result) {
if (!add_child_result.ok()) {
FDF_LOG(ERROR, "Failed to add child %s", add_child_result.status_string());
CompleteStart(zx::error(add_child_result.status()));
return;
}
if (add_child_result.value().is_error()) {
FDF_LOG(ERROR, "Failed to add child. NodeError: %d",
static_cast<uint32_t>(add_child_result.value().error_value()));
CompleteStart(zx::error(ZX_ERR_INTERNAL));
return;
}
FDF_LOG(INFO, "Successfully started aml-uart-dfv2 driver.");
CompleteStart(zx::ok());
}
void AmlUartV2::CompleteStart(zx::result<> result) {
ZX_ASSERT(start_completer_.has_value());
start_completer_.value()(result);
start_completer_.reset();
}
} // namespace serial
FUCHSIA_DRIVER_EXPORT(serial::AmlUartV2);