| // Copyright 2017 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/bus/drivers/platform/platform-device.h" |
| |
| #include <assert.h> |
| #include <fidl/fuchsia.driver.framework/cpp/fidl.h> |
| #include <fidl/fuchsia.hardware.platform.bus/cpp/fidl.h> |
| #include <fidl/fuchsia.hardware.power/cpp/fidl.h> |
| #include <lib/ddk/binding.h> |
| #include <lib/ddk/debug.h> |
| #include <lib/ddk/device.h> |
| #include <lib/ddk/driver.h> |
| #include <lib/ddk/metadata.h> |
| #include <lib/ddk/platform-defs.h> |
| #include <lib/fidl/cpp/wire/vector_view.h> |
| #include <lib/fidl/cpp/wire_natural_conversions.h> |
| #include <lib/fit/function.h> |
| #include <lib/zircon-internal/align.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <zircon/errors.h> |
| #include <zircon/syscalls/resource.h> |
| |
| #include <fbl/string_printf.h> |
| |
| #include "src/devices/bus/drivers/platform/node-util.h" |
| #include "src/devices/bus/drivers/platform/platform-bus.h" |
| #include "src/devices/bus/drivers/platform/platform-interrupt.h" |
| |
| namespace { |
| |
| zx::result<zx_device_prop_t> ConvertToDeviceProperty( |
| const fuchsia_driver_framework::NodeProperty& property) { |
| if (property.key().Which() != fuchsia_driver_framework::NodePropertyKey::Tag::kIntValue) { |
| return zx::error(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| return zx::ok(zx_device_prop_t{static_cast<uint16_t>(property.key().int_value().value()), 0, |
| property.value().int_value().value()}); |
| } |
| |
| zx::result<zx_device_str_prop_t> ConvertToDeviceStringProperty( |
| const fuchsia_driver_framework::NodeProperty& property) { |
| if (property.key().Which() != fuchsia_driver_framework::NodePropertyKey::Tag::kStringValue) { |
| return zx::error(ZX_ERR_NOT_SUPPORTED); |
| } |
| const char* key = property.key().string_value()->data(); |
| switch (property.value().Which()) { |
| using ValueTag = fuchsia_driver_framework::NodePropertyValue::Tag; |
| case ValueTag::kBoolValue: { |
| return zx::ok(zx_device_str_prop_t{ |
| .key = key, |
| .property_value = str_prop_bool_val(property.value().bool_value().value()), |
| }); |
| } |
| case ValueTag::kIntValue: { |
| return zx::ok(zx_device_str_prop_t{ |
| .key = key, |
| .property_value = str_prop_int_val(property.value().int_value().value()), |
| }); |
| } |
| case ValueTag::kEnumValue: { |
| return zx::ok(zx_device_str_prop_t{ |
| .key = key, |
| .property_value = str_prop_enum_val(property.value().enum_value()->data()), |
| }); |
| } |
| case ValueTag::kStringValue: { |
| return zx::ok(zx_device_str_prop_t{ |
| .key = key, |
| .property_value = str_prop_str_val(property.value().string_value()->data()), |
| }); |
| } |
| default: |
| return zx::error(ZX_ERR_INVALID_ARGS); |
| } |
| } |
| |
| } // namespace |
| |
| namespace platform_bus { |
| |
| namespace fpbus = fuchsia_hardware_platform_bus; |
| |
| // fuchsia.hardware.platform.bus.PlatformBus implementation. |
| void RestrictPlatformBus::NodeAdd(NodeAddRequestView request, fdf::Arena& arena, |
| NodeAddCompleter::Sync& completer) { |
| completer.buffer(arena).ReplyError(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| void RestrictPlatformBus::GetBoardInfo(fdf::Arena& arena, GetBoardInfoCompleter::Sync& completer) { |
| upstream_->GetBoardInfo(arena, completer); |
| } |
| void RestrictPlatformBus::SetBoardInfo(SetBoardInfoRequestView request, fdf::Arena& arena, |
| SetBoardInfoCompleter::Sync& completer) { |
| upstream_->SetBoardInfo(request, arena, completer); |
| } |
| void RestrictPlatformBus::SetBootloaderInfo(SetBootloaderInfoRequestView request, fdf::Arena& arena, |
| SetBootloaderInfoCompleter::Sync& completer) { |
| upstream_->SetBootloaderInfo(request, arena, completer); |
| } |
| |
| void RestrictPlatformBus::RegisterSysSuspendCallback( |
| RegisterSysSuspendCallbackRequestView request, fdf::Arena& arena, |
| RegisterSysSuspendCallbackCompleter::Sync& completer) { |
| completer.buffer(arena).ReplyError(ZX_ERR_NOT_SUPPORTED); |
| } |
| void RestrictPlatformBus::AddCompositeNodeSpec(AddCompositeNodeSpecRequestView request, |
| fdf::Arena& arena, |
| AddCompositeNodeSpecCompleter::Sync& completer) { |
| completer.buffer(arena).ReplyError(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| zx_status_t PlatformDevice::Create(fpbus::Node node, zx_device_t* parent, PlatformBus* bus, |
| Type type, std::unique_ptr<platform_bus::PlatformDevice>* out) { |
| fbl::AllocChecker ac; |
| std::unique_ptr<platform_bus::PlatformDevice> dev( |
| new (&ac) platform_bus::PlatformDevice(parent, bus, type, std::move(node))); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| auto status = dev->Init(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| out->swap(dev); |
| return ZX_OK; |
| } |
| |
| PlatformDevice::PlatformDevice(zx_device_t* parent, PlatformBus* bus, Type type, fpbus::Node node) |
| : PlatformDeviceType(parent), |
| bus_(bus), |
| type_(type), |
| vid_(node.vid().value_or(0)), |
| pid_(node.pid().value_or(0)), |
| did_(node.did().value_or(0)), |
| instance_id_(node.instance_id().value_or(0)), |
| node_(std::move(node)), |
| outgoing_(fdf::OutgoingDirectory::Create(fdf::Dispatcher::GetCurrent()->get())) { |
| strlcpy(name_, node_.name().value_or("no name?").data(), sizeof(name_)); |
| } |
| |
| zx_status_t PlatformDevice::Init() { |
| if (type_ == Protocol) { |
| // Protocol devices implement a subset of the platform bus protocol. |
| restricted_ = std::make_unique<RestrictPlatformBus>(bus_); |
| } |
| |
| if (node_.irq().has_value()) { |
| for (uint32_t i = 0; i < node_.irq()->size(); i++) { |
| auto fragment = std::make_unique<PlatformInterruptFragment>( |
| parent(), this, i, fdf::Dispatcher::GetCurrent()->async_dispatcher()); |
| zx_status_t status = fragment->Add(fbl::StringPrintf("%s-irq%03u", name_, i).data(), this, |
| node_.irq().value()[i]); |
| if (status != ZX_OK) { |
| zxlogf(WARNING, "Failed to create interrupt fragment %u", i); |
| continue; |
| } |
| |
| // The DDK takes ownership of the device. |
| [[maybe_unused]] auto unused = fragment.release(); |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformDevice::PDevGetMmio(uint32_t index, pdev_mmio_t* out_mmio) { |
| if (node_.mmio() == std::nullopt || index >= node_.mmio()->size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| const auto& mmio = node_.mmio().value()[index]; |
| if (unlikely(!IsValid(mmio))) { |
| return ZX_ERR_INTERNAL; |
| } |
| if (mmio.base() == std::nullopt) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| const zx_paddr_t vmo_base = ZX_ROUNDDOWN(mmio.base().value(), ZX_PAGE_SIZE); |
| const size_t vmo_size = |
| ZX_ROUNDUP(mmio.base().value() + mmio.length().value() - vmo_base, ZX_PAGE_SIZE); |
| zx::vmo vmo; |
| |
| zx_status_t status = zx::vmo::create_physical(*bus_->GetMmioResource(), vmo_base, vmo_size, &vmo); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: creating vmo failed %d", __FUNCTION__, status); |
| return status; |
| } |
| |
| char name[32]; |
| snprintf(name, sizeof(name), "mmio %u", index); |
| status = vmo.set_property(ZX_PROP_NAME, name, sizeof(name)); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: setting vmo name failed %d", __FUNCTION__, status); |
| return status; |
| } |
| |
| out_mmio->offset = mmio.base().value() - vmo_base; |
| out_mmio->vmo = vmo.release(); |
| out_mmio->size = mmio.length().value(); |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformDevice::PDevGetInterrupt(uint32_t index, uint32_t flags, |
| zx::interrupt* out_irq) { |
| if (node_.irq() == std::nullopt || index >= node_.irq()->size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| if (out_irq == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| const auto& irq = node_.irq().value()[index]; |
| if (unlikely(!IsValid(irq))) { |
| return ZX_ERR_INTERNAL; |
| } |
| if (flags == 0) { |
| flags = irq.mode().value(); |
| } |
| zx_status_t status = |
| zx::interrupt::create(*bus_->GetIrqResource(), irq.irq().value(), flags, out_irq); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "platform_dev_map_interrupt: zx_interrupt_create failed %d", status); |
| return status; |
| } |
| return status; |
| } |
| |
| zx_status_t PlatformDevice::PDevGetBti(uint32_t index, zx::bti* out_bti) { |
| if (node_.bti() == std::nullopt || index >= node_.bti()->size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| if (out_bti == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| const auto& bti = node_.bti().value()[index]; |
| if (unlikely(!IsValid(bti))) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| return bus_->IommuGetBti(bti.iommu_index().value(), bti.bti_id().value(), out_bti); |
| } |
| |
| zx_status_t PlatformDevice::PDevGetSmc(uint32_t index, zx::resource* out_resource) { |
| if (node_.smc() == std::nullopt || index >= node_.smc()->size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| if (out_resource == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| const auto& smc = node_.smc().value()[index]; |
| if (unlikely(!IsValid(smc))) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| uint32_t options = ZX_RSRC_KIND_SMC; |
| if (smc.exclusive().value()) |
| options |= ZX_RSRC_FLAG_EXCLUSIVE; |
| char rsrc_name[ZX_MAX_NAME_LEN]; |
| snprintf(rsrc_name, ZX_MAX_NAME_LEN - 1, "%s.pbus[%u]", name_, index); |
| return zx::resource::create(*bus_->GetSmcResource(), options, smc.service_call_num_base().value(), |
| smc.count().value(), rsrc_name, sizeof(rsrc_name), out_resource); |
| } |
| |
| zx_status_t PlatformDevice::PDevGetDeviceInfo(pdev_device_info_t* out_info) { |
| pdev_device_info_t info = { |
| .vid = vid_, |
| .pid = pid_, |
| .did = did_, |
| .mmio_count = static_cast<uint32_t>(node_.mmio().has_value() ? node_.mmio()->size() : 0), |
| .irq_count = static_cast<uint32_t>(node_.irq().has_value() ? node_.irq()->size() : 0), |
| .bti_count = static_cast<uint32_t>(node_.bti().has_value() ? node_.bti()->size() : 0), |
| .smc_count = static_cast<uint32_t>(node_.smc().has_value() ? node_.smc()->size() : 0), |
| .metadata_count = |
| static_cast<uint32_t>(node_.metadata().has_value() ? node_.metadata()->size() : 0), |
| .reserved = {}, |
| .name = {}, |
| }; |
| static_assert(sizeof(info.name) == sizeof(name_), ""); |
| memcpy(info.name, name_, sizeof(out_info->name)); |
| memcpy(out_info, &info, sizeof(info)); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformDevice::PDevGetBoardInfo(pdev_board_info_t* out_info) { |
| auto info = bus_->board_info(); |
| out_info->pid = info.pid(); |
| out_info->vid = info.vid(); |
| out_info->board_revision = info.board_revision(); |
| strlcpy(out_info->board_name, info.board_name().data(), sizeof(out_info->board_name)); |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformDevice::PDevDeviceAdd(uint32_t index, const device_add_args_t* args, |
| zx_device_t** device) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx_status_t PlatformDevice::DdkGetProtocol(uint32_t proto_id, void* out) { |
| if (proto_id == ZX_PROTOCOL_PDEV) { |
| auto proto = static_cast<pdev_protocol_t*>(out); |
| proto->ops = &pdev_protocol_ops_; |
| proto->ctx = this; |
| return ZX_OK; |
| } |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| void PlatformDevice::DdkRelease() { delete this; } |
| |
| zx_status_t PlatformDevice::Start() { |
| char name[ZX_DEVICE_NAME_MAX]; |
| if (vid_ == PDEV_VID_GENERIC && pid_ == PDEV_PID_GENERIC && did_ == PDEV_DID_KPCI) { |
| strlcpy(name, "pci", sizeof(name)); |
| } else if (did_ == PDEV_DID_DEVICETREE_NODE) { |
| strlcpy(name, name_, sizeof(name)); |
| } else { |
| if (instance_id_ == 0) { |
| // For backwards compatability, we elide instance id when it is 0. |
| snprintf(name, sizeof(name), "%02x:%02x:%01x", vid_, pid_, did_); |
| } else { |
| snprintf(name, sizeof(name), "%02x:%02x:%01x:%01x", vid_, pid_, did_, instance_id_); |
| } |
| } |
| |
| std::vector<zx_device_prop_t> dev_props{ |
| {BIND_PLATFORM_DEV_VID, 0, vid_}, |
| {BIND_PLATFORM_DEV_PID, 0, pid_}, |
| {BIND_PLATFORM_DEV_DID, 0, did_}, |
| {BIND_PLATFORM_DEV_INSTANCE_ID, 0, instance_id_}, |
| }; |
| |
| std::vector<zx_device_str_prop_t> dev_str_props; |
| if (node_.properties().has_value()) { |
| for (auto& prop : node_.properties().value()) { |
| if (auto dev_prop = ConvertToDeviceProperty(prop); dev_prop.is_ok()) { |
| dev_props.emplace_back(dev_prop.value()); |
| } else if (auto dev_str_prop = ConvertToDeviceStringProperty(prop); dev_str_prop.is_ok()) { |
| dev_str_props.emplace_back(dev_str_prop.value()); |
| } else { |
| zxlogf(WARNING, "Node '%s' has unsupported property key type %lu.", name, |
| static_cast<unsigned long>(prop.key().Which())); |
| } |
| } |
| } |
| |
| ddk::DeviceAddArgs args(name); |
| args.set_props(dev_props).set_str_props(dev_str_props).set_proto_id(ZX_PROTOCOL_PDEV); |
| |
| std::array fidl_service_offers = { |
| fuchsia_hardware_platform_device::Service::Name, |
| }; |
| std::array runtime_service_offers = { |
| fuchsia_hardware_platform_bus::Service::Name, |
| }; |
| |
| // Set our FIDL offers. |
| { |
| zx::result result = outgoing_.AddService<fuchsia_hardware_platform_device::Service>( |
| fuchsia_hardware_platform_device::Service::InstanceHandler({ |
| .device = device_bindings_.CreateHandler( |
| this, fdf::Dispatcher::GetCurrent()->async_dispatcher(), |
| fidl::kIgnoreBindingClosure), |
| })); |
| if (result.is_error()) { |
| zxlogf(ERROR, "could not add service to outgoing directory: %s", result.status_string()); |
| return result.error_value(); |
| } |
| |
| args.set_fidl_service_offers(fidl_service_offers); |
| } |
| |
| switch (type_) { |
| case Protocol: { |
| fuchsia_hardware_platform_bus::Service::InstanceHandler handler({ |
| .platform_bus = bus_bindings_.CreateHandler( |
| restricted_.get(), fdf::Dispatcher::GetCurrent()->get(), fidl::kIgnoreBindingClosure), |
| }); |
| |
| zx::result result = |
| outgoing_.AddService<fuchsia_hardware_platform_bus::Service>(std::move(handler)); |
| if (result.is_error()) { |
| return result.error_value(); |
| } |
| |
| args.set_runtime_service_offers(runtime_service_offers); |
| break; |
| } |
| |
| case Isolated: { |
| // Isolated devices run in separate devhosts. |
| // Protocol devices must be in same devhost as platform bus. |
| // Composite device fragments are also in the same devhost as platform bus, |
| // but the actual composite device will be in a new devhost or devhost belonging to |
| // one of the other fragments. |
| args.set_flags(DEVICE_ADD_MUST_ISOLATE); |
| break; |
| } |
| |
| case Fragment: { |
| break; |
| } |
| } |
| |
| // Setup the outgoing directory. |
| zx::result endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| if (endpoints.is_error()) { |
| return endpoints.status_value(); |
| } |
| if (zx::result result = outgoing_.Serve(std::move(endpoints->server)); result.is_error()) { |
| return result.error_value(); |
| } |
| args.set_outgoing_dir(endpoints->client.TakeChannel()); |
| return DdkAdd(std::move(args)); |
| } |
| |
| void PlatformDevice::DdkInit(ddk::InitTxn txn) { |
| const size_t metadata_count = node_.metadata() == std::nullopt ? 0 : node_.metadata()->size(); |
| const size_t boot_metadata_count = |
| node_.boot_metadata() == std::nullopt ? 0 : node_.boot_metadata()->size(); |
| if (metadata_count > 0 || boot_metadata_count > 0) { |
| for (size_t i = 0; i < metadata_count; i++) { |
| const auto& metadata = node_.metadata().value()[i]; |
| if (!IsValid(metadata)) { |
| txn.Reply(ZX_ERR_INTERNAL); |
| return; |
| } |
| zx_status_t status = |
| DdkAddMetadata(metadata.type().value(), metadata.data()->data(), metadata.data()->size()); |
| if (status != ZX_OK) { |
| return txn.Reply(status); |
| } |
| } |
| |
| for (size_t i = 0; i < boot_metadata_count; i++) { |
| const auto& metadata = node_.boot_metadata().value()[i]; |
| if (!IsValid(metadata)) { |
| txn.Reply(ZX_ERR_INTERNAL); |
| return; |
| } |
| zx::result data = |
| bus_->GetBootItemArray(metadata.zbi_type().value(), metadata.zbi_extra().value()); |
| zx_status_t status = data.status_value(); |
| if (data.is_ok()) { |
| status = DdkAddMetadata(metadata.zbi_type().value(), data->data(), data->size()); |
| } |
| if (status != ZX_OK) { |
| zxlogf(WARNING, "%s failed to add metadata for new device", __func__); |
| } |
| } |
| } |
| return txn.Reply(ZX_OK); |
| } |
| |
| void PlatformDevice::GetMmioById(GetMmioByIdRequestView request, |
| GetMmioByIdCompleter::Sync& completer) { |
| pdev_mmio_t banjo_mmio; |
| zx_status_t status = PDevGetMmio(request->index, &banjo_mmio); |
| if (status != ZX_OK) { |
| completer.ReplyError(status); |
| return; |
| } |
| |
| fidl::Arena arena; |
| fuchsia_hardware_platform_device::wire::Mmio mmio = |
| fuchsia_hardware_platform_device::wire::Mmio::Builder(arena) |
| .offset(banjo_mmio.offset) |
| .size(banjo_mmio.size) |
| .vmo(zx::vmo(banjo_mmio.vmo)) |
| .Build(); |
| completer.ReplySuccess(std::move(mmio)); |
| } |
| |
| void PlatformDevice::GetMmioByName(GetMmioByNameRequestView request, |
| GetMmioByNameCompleter::Sync& completer) { |
| if (request->name.empty()) { |
| return completer.ReplyError(ZX_ERR_INVALID_ARGS); |
| } |
| std::optional<uint32_t> index = GetMmioIndex(node_, request->name.get()); |
| if (!index.has_value()) { |
| return completer.ReplyError(ZX_ERR_OUT_OF_RANGE); |
| } |
| |
| pdev_mmio_t banjo_mmio; |
| zx_status_t status = PDevGetMmio(index.value(), &banjo_mmio); |
| if (status != ZX_OK) { |
| completer.ReplyError(status); |
| return; |
| } |
| |
| fidl::Arena arena; |
| fuchsia_hardware_platform_device::wire::Mmio mmio = |
| fuchsia_hardware_platform_device::wire::Mmio::Builder(arena) |
| .offset(banjo_mmio.offset) |
| .size(banjo_mmio.size) |
| .vmo(zx::vmo(banjo_mmio.vmo)) |
| .Build(); |
| completer.ReplySuccess(std::move(mmio)); |
| } |
| |
| void PlatformDevice::GetInterruptById(GetInterruptByIdRequestView request, |
| GetInterruptByIdCompleter::Sync& completer) { |
| zx::interrupt interrupt; |
| zx_status_t status = PDevGetInterrupt(request->index, request->flags, &interrupt); |
| if (status == ZX_OK) { |
| completer.ReplySuccess(std::move(interrupt)); |
| } else { |
| completer.ReplyError(status); |
| } |
| } |
| |
| void PlatformDevice::GetInterruptByName(GetInterruptByNameRequestView request, |
| GetInterruptByNameCompleter::Sync& completer) { |
| if (request->name.empty()) { |
| return completer.ReplyError(ZX_ERR_INVALID_ARGS); |
| } |
| std::optional<uint32_t> index = GetIrqIndex(node_, request->name.get()); |
| if (!index.has_value()) { |
| return completer.ReplyError(ZX_ERR_OUT_OF_RANGE); |
| } |
| zx::interrupt interrupt; |
| zx_status_t status = PDevGetInterrupt(index.value(), request->flags, &interrupt); |
| if (status == ZX_OK) { |
| completer.ReplySuccess(std::move(interrupt)); |
| } else { |
| completer.ReplyError(status); |
| } |
| } |
| |
| void PlatformDevice::GetBtiById(GetBtiByIdRequestView request, |
| GetBtiByIdCompleter::Sync& completer) { |
| zx::bti bti; |
| zx_status_t status = PDevGetBti(request->index, &bti); |
| if (status == ZX_OK) { |
| completer.ReplySuccess(std::move(bti)); |
| } else { |
| completer.ReplyError(status); |
| } |
| } |
| |
| void PlatformDevice::GetBtiByName(GetBtiByNameRequestView request, |
| GetBtiByNameCompleter::Sync& completer) { |
| if (request->name.empty()) { |
| return completer.ReplyError(ZX_ERR_INVALID_ARGS); |
| } |
| std::optional<uint32_t> index = GetBtiIndex(node_, request->name.get()); |
| if (!index.has_value()) { |
| return completer.ReplyError(ZX_ERR_OUT_OF_RANGE); |
| } |
| zx::bti bti; |
| zx_status_t status = PDevGetBti(index.value(), &bti); |
| if (status == ZX_OK) { |
| completer.ReplySuccess(std::move(bti)); |
| } else { |
| completer.ReplyError(status); |
| } |
| } |
| |
| void PlatformDevice::GetSmcById(GetSmcByIdRequestView request, |
| GetSmcByIdCompleter::Sync& completer) { |
| zx::resource resource; |
| zx_status_t status = PDevGetSmc(request->index, &resource); |
| if (status == ZX_OK) { |
| completer.ReplySuccess(std::move(resource)); |
| } else { |
| completer.ReplyError(status); |
| } |
| } |
| |
| void PlatformDevice::GetSmcByName(GetSmcByNameRequestView request, |
| GetSmcByNameCompleter::Sync& completer) { |
| if (request->name.empty()) { |
| return completer.ReplyError(ZX_ERR_INVALID_ARGS); |
| } |
| std::optional<uint32_t> index = GetSmcIndex(node_, request->name.get()); |
| if (!index.has_value()) { |
| return completer.ReplyError(ZX_ERR_OUT_OF_RANGE); |
| } |
| zx::resource resource; |
| zx_status_t status = PDevGetSmc(index.value(), &resource); |
| if (status == ZX_OK) { |
| completer.ReplySuccess(std::move(resource)); |
| } else { |
| completer.ReplyError(status); |
| } |
| } |
| |
| void PlatformDevice::GetPowerConfiguration(GetPowerConfigurationCompleter::Sync& completer) { |
| std::optional<std::vector<fuchsia_hardware_power::PowerElementConfiguration>> config = |
| node_.power_config(); |
| if (config.has_value()) { |
| auto element_configs = config.value(); |
| fidl::Arena arena; |
| fidl::VectorView<fuchsia_hardware_power::wire::PowerElementConfiguration> elements; |
| elements.Allocate(arena, element_configs.size()); |
| |
| size_t offset = 0; |
| for (auto& config : element_configs) { |
| fuchsia_hardware_power::wire::PowerElementConfiguration wire_config = |
| fidl::ToWire(arena, config); |
| elements.at(offset) = wire_config; |
| |
| offset++; |
| } |
| completer.ReplySuccess(elements); |
| |
| } else { |
| completer.ReplyError(ZX_ERR_NOT_FOUND); |
| } |
| } |
| |
| void PlatformDevice::GetNodeDeviceInfo(GetNodeDeviceInfoCompleter::Sync& completer) { |
| pdev_device_info_t banjo_info; |
| zx_status_t status = PDevGetDeviceInfo(&banjo_info); |
| if (status == ZX_OK) { |
| fidl::Arena arena; |
| completer.ReplySuccess(fuchsia_hardware_platform_device::wire::NodeDeviceInfo::Builder(arena) |
| .vid(banjo_info.vid) |
| .pid(banjo_info.pid) |
| .did(banjo_info.did) |
| .mmio_count(banjo_info.mmio_count) |
| .irq_count(banjo_info.irq_count) |
| .bti_count(banjo_info.bti_count) |
| .smc_count(banjo_info.smc_count) |
| .metadata_count(banjo_info.metadata_count) |
| .name(banjo_info.name) |
| .Build()); |
| } else { |
| completer.ReplyError(status); |
| } |
| } |
| |
| void PlatformDevice::GetBoardInfo(GetBoardInfoCompleter::Sync& completer) { |
| pdev_board_info_t banjo_info; |
| zx_status_t status = PDevGetBoardInfo(&banjo_info); |
| if (status == ZX_OK) { |
| fidl::Arena arena; |
| completer.ReplySuccess(fuchsia_hardware_platform_device::wire::BoardInfo::Builder(arena) |
| .vid(banjo_info.vid) |
| .pid(banjo_info.pid) |
| .board_name(banjo_info.board_name) |
| .board_revision(banjo_info.board_revision) |
| .Build()); |
| } else { |
| completer.ReplyError(status); |
| } |
| } |
| |
| void PlatformDevice::handle_unknown_method( |
| fidl::UnknownMethodMetadata<fuchsia_hardware_platform_device::Device> metadata, |
| fidl::UnknownMethodCompleter::Sync& completer) { |
| zxlogf(WARNING, "PlatformDevice received unknown method with ordinal: %lu", |
| metadata.method_ordinal); |
| } |
| |
| } // namespace platform_bus |