| // 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/driver2/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 = |
| driver::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. |
| driver::ServiceInstanceHandler handler; |
| examples_qemuedu::Service::Handler service(&handler); |
| |
| auto result = |
| service.add_device([this](fidl::ServerEnd<examples_qemuedu::Device> request) -> void { |
| QemuEduServer::BindDeviceClient(&logger(), dispatcher(), device_, std::move(request)); |
| }); |
| ZX_ASSERT(result.is_ok()); |
| |
| 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::status<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 |
| auto devfs_path = compat_context_->TopologicalPath(name()); |
| auto service_path = std::string(examples_qemuedu::Service::Name) + "/" + |
| component::kDefaultInstance + "/" + |
| examples_qemuedu::Service::Device::Name; |
| |
| // Export an entry to devfs for examples.qemuedu as a generic device |
| auto status = compat_context_->devfs_exporter().ExportSync( |
| service_path, devfs_path, fuchsia_device_fs::ExportOptions(), 0); |
| if (status != ZX_OK) { |
| FDF_SLOG(ERROR, "Failed to export to devfs: %s", |
| KV("status", zx_status_get_string(status))); |
| // Reset the node to signal unbind to the driver framework. |
| node().reset(); |
| return; |
| } |
| |
| FDF_SLOG(INFO, "Exported", KV("service_path", service_path.c_str()), |
| KV("devfs_path", devfs_path.c_str())); |
| }); |
| // [END devfs_export] |
| |
| // [START start_method_end] |
| return zx::ok(); |
| } |
| // [END start_method_end] |
| |
| // [START namespace_end] |
| } // namespace qemu_edu |
| // [END namespace_end] |
| |
| // [START driver_hook] |
| // Register driver hooks with the framework |
| FUCHSIA_DRIVER_RECORD_CPP_V2(driver::Record<qemu_edu::QemuEduDriver>); |
| // [END driver_hook] |