| // Copyright 2022 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. |
| |
| // [START imports] |
| #include "qemu_edu.h" |
| // [END imports] |
| |
| // [START compat_imports] |
| #include <lib/driver/component/cpp/service_client.h> |
| // [END compat_imports] |
| |
| // [START fidl_imports] |
| #include "edu_server.h" |
| // [END fidl_imports] |
| |
| // [START namespace_start] |
| namespace qemu_edu { |
| // [END namespace_start] |
| |
| // [START start_method_start] |
| // Initialize this driver instance |
| zx::result<> QemuEduDriver::Start() { |
| // [END start_method_start] |
| // [START connect_device] |
| // Connect to the parent device node. |
| auto compat_parent = fdf::Connect<fuchsia_driver_compat::Service::Device>(*context().incoming()); |
| if (compat_parent.is_error()) { |
| FDF_SLOG(ERROR, "Failed to connect to compat", KV("status", compat_parent.status_string())); |
| return compat_parent.take_error(); |
| } |
| |
| auto parent = fidl::WireSyncClient(std::move(compat_parent.value())); |
| |
| // Connect to fuchsia.hardware.pci FIDL protocol from the parent device |
| auto pci_endpoints = fidl::CreateEndpoints<fuchsia_hardware_pci::Device>(); |
| if (pci_endpoints.is_error()) { |
| return pci_endpoints.take_error(); |
| } |
| |
| auto connect_result = parent->ConnectFidl( |
| fidl::StringView::FromExternal(fidl::DiscoverableProtocolName<fuchsia_hardware_pci::Device>), |
| pci_endpoints->server.TakeChannel()); |
| |
| if (!connect_result.ok()) { |
| return zx::error(connect_result.status()); |
| } |
| // [END connect_device] |
| |
| // [START hw_resources] |
| // Map hardware resources from the PCI device |
| device_ = std::make_shared<edu_device::QemuEduDevice>(&logger(), dispatcher(), |
| std::move(pci_endpoints->client)); |
| auto pci_status = device_->MapInterruptAndMmio(); |
| if (pci_status.is_error()) { |
| return pci_status.take_error(); |
| } |
| // [END hw_resources] |
| |
| // [START device_registers] |
| // Report the version information from the edu device. |
| auto version_reg = device_->IdentificationRegister(); |
| FDF_SLOG(INFO, "edu device version", KV("major", version_reg.major_version()), |
| KV("minor", version_reg.minor_version())); |
| // [END device_registers] |
| |
| // [START serve_outgoing] |
| // Serve the examples.qemuedu/Service capability. |
| examples_qemuedu::Service::InstanceHandler handler({ |
| .device = fit::bind_member<&QemuEduDriver::Serve>(this), |
| }); |
| |
| auto result = context().outgoing()->AddService<examples_qemuedu::Service>(std::move(handler)); |
| if (result.is_error()) { |
| FDF_SLOG(ERROR, "Failed to add Device service", KV("status", result.status_string())); |
| return result.take_error(); |
| } |
| // [END serve_outgoing] |
| |
| // [START devfs_export] |
| // Create and export a devfs entry for the driver service. |
| compat::Context::ConnectAndCreate( |
| &context(), dispatcher(), |
| [this](zx::result<std::unique_ptr<compat::Context>> result) mutable { |
| if (result.is_error()) { |
| FDF_SLOG(ERROR, "Failed to get compat::Context", KV("status", result.status_string())); |
| // Reset the node to signal unbind to the driver framework. |
| node().reset(); |
| return; |
| } |
| compat_context_ = std::move(result.value()); |
| // Construct a devfs path that matches the device nodes topological path |
| std::string devfs_path = compat_context_->TopologicalPath(name()); |
| |
| // Export to devfs. |
| zx::result connection = this->context().incoming()->Connect<fuchsia_device_fs::Exporter>(); |
| if (connection.is_error()) { |
| FDF_SLOG(ERROR, "Failed to connect to fuchsia_device_fs::Exporter", |
| KV("status", connection.status_string())); |
| node().reset(); |
| return; |
| } |
| fidl::WireSyncClient devfs_exporter{std::move(connection.value())}; |
| |
| zx::result connector = devfs_connector_.Bind(dispatcher()); |
| if (connector.is_error()) { |
| FDF_SLOG(ERROR, "Failed to bind devfs_connector: %s", |
| KV("status", connector.status_string())); |
| node().reset(); |
| return; |
| } |
| fidl::WireResult export_result = devfs_exporter->ExportV2( |
| std::move(connector.value()), |
| fidl::StringView::FromExternal(devfs_path), |
| fidl::StringView(), fuchsia_device_fs::ExportOptions()); |
| if (!export_result.ok()) { |
| FDF_SLOG(ERROR, "Failed to export to devfs: %s", |
| KV("status", export_result.status_string())); |
| node().reset(); |
| return; |
| } |
| if (export_result.value().is_error()) { |
| FDF_SLOG(ERROR, "Failed to export to devfs: %s", |
| KV("status", zx_status_get_string(export_result.value().error_value()))); |
| node().reset(); |
| return; |
| } |
| |
| FDF_SLOG(INFO, "Exported", KV("devfs_path", devfs_path.c_str())); |
| }); |
| // [END devfs_export] |
| |
| // [START start_method_end] |
| return zx::ok(); |
| } |
| // [END start_method_end] |
| |
| void QemuEduDriver::Serve(fidl::ServerEnd<examples_qemuedu::Device> request) { |
| QemuEduServer::BindDeviceClient(&logger(), dispatcher(), device_, std::move(request)); |
| } |
| |
| // [START namespace_end] |
| } // namespace qemu_edu |
| // [END namespace_end] |
| |
| // [START driver_hook] |
| // Register driver hooks with the framework |
| FUCHSIA_DRIVER_RECORD_CPP_V2(fdf::Record<qemu_edu::QemuEduDriver>); |
| // [END driver_hook] |