| // 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 "driver_compat.h" |
| // [END compat_imports] |
| |
| // [START namespace_start] |
| namespace qemu_edu { |
| // [END namespace_start] |
| |
| // [START driver_start] |
| // Static start hook called by the driver framwork on bind |
| zx::status<std::unique_ptr<QemuEduDriver>> QemuEduDriver::Start( |
| fuchsia_driver_framework::wire::DriverStartArgs& start_args, fdf::UnownedDispatcher dispatcher, |
| fidl::WireSharedClient<fuchsia_driver_framework::Node> node, driver::Namespace ns, |
| driver::Logger logger) { |
| // Create a unique driver instance |
| auto driver = std::make_unique<QemuEduDriver>(dispatcher->async_dispatcher(), std::move(node), |
| std::move(ns), std::move(logger)); |
| // Initialize the driver instance |
| auto result = driver->Run(dispatcher->async_dispatcher(), std::move(start_args.outgoing_dir())); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| |
| return zx::ok(std::move(driver)); |
| } |
| // [END driver_start] |
| |
| // [START run_method_start] |
| // Initialize this driver instance |
| zx::status<> QemuEduDriver::Run(async_dispatcher* dispatcher, |
| fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir) { |
| // [END run_method_start] |
| // [START connect_device] |
| // Connect to the parent device node. |
| auto parent = edu_driver_compat::ConnectToParentDevice(&ns_, "default"); |
| if (parent.is_error()) { |
| FDF_SLOG(ERROR, "Failed to connect to parent", KV("status", parent.status_string())); |
| return parent.take_error(); |
| } |
| |
| // 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 = fidl::WireCall(*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()); |
| } |
| auto pci_client = |
| fidl::WireSyncClient<fuchsia_hardware_pci::Device>(std::move(pci_endpoints->client)); |
| // [END connect_device] |
| |
| // [START hw_resources] |
| // Map hardware resources from the PCI device |
| device_ = std::make_shared<edu_device::QemuEduDevice>(ns_, dispatcher, std::move(pci_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 devfs_export] |
| // Add the fuchsia.examples.qemuedu/Device protocol to be served as |
| // "/svc/fuchsia.examples.qemuedu/default/device" |
| component::ServiceHandler handler; |
| fuchsia_examples_qemuedu::Service::Handler service(&handler); |
| |
| auto device_handler = |
| [this, dispatcher](fidl::ServerEnd<fuchsia_examples_qemuedu::Device> request) -> void { |
| auto server_impl_ = std::make_shared<QemuEduServer>(ns_, dispatcher, device_); |
| fidl::BindServer(dispatcher, std::move(request), std::move(server_impl_), |
| std::mem_fn(&QemuEduServer::OnUnbound)); |
| }; |
| auto result = service.add_device(device_handler); |
| ZX_ASSERT(result.is_ok()); |
| |
| result = outgoing_.AddService<fuchsia_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(); |
| } |
| |
| const std::string kExportName = std::string("svc/") + fuchsia_examples_qemuedu::Service::Name + |
| "/" + component::kDefaultInstance + "/device"; |
| auto service_dir = fidl::StringView::FromExternal(kExportName); |
| |
| // Construct a devfs path that matches the device nodes topological path |
| auto path_result = fidl::WireCall(*parent)->GetTopologicalPath(); |
| if (!path_result.ok()) { |
| FDF_SLOG(ERROR, "Failed to get topological path", KV("status", path_result.status_string())); |
| return zx::error(path_result.status()); |
| } |
| std::string parent_path(path_result->path.get()); |
| auto devfs_path = fidl::StringView::FromExternal(parent_path.append("/").append(Name())); |
| |
| // Export an entry to devfs for fuchsia.examples.qemuedu as a generic device |
| auto devfs_dir = edu_driver_compat::ExportDevfsEntry(&ns_, service_dir, devfs_path, 0); |
| if (devfs_dir.is_error()) { |
| FDF_SLOG(ERROR, "Failed to export service", KV("status", devfs_dir.status_string())); |
| return devfs_dir.take_error(); |
| } |
| // [END devfs_export] |
| |
| // [START serve_outgoing] |
| // Serve the driver's FIDL protocol to clients |
| auto status = outgoing_.Serve(std::move(devfs_dir.value())); |
| if (status.is_error()) { |
| FDF_SLOG(ERROR, "Failed to serve devfs directory", KV("status", status.status_string())); |
| return status.take_error(); |
| } |
| status = outgoing_.Serve(std::move(outgoing_dir)); |
| if (status.is_error()) { |
| FDF_SLOG(ERROR, "Failed to serve outgoing directory", KV("status", status.status_string())); |
| return status.take_error(); |
| } |
| // [END serve_outgoing] |
| |
| // [START run_method_end] |
| return zx::ok(); |
| } |
| // [END run_method_end] |
| |
| // [START compute_factorial] |
| // Driver Service: Compute factorial on the edu device |
| void QemuEduServer::ComputeFactorial(ComputeFactorialRequestView request, |
| ComputeFactorialCompleter::Sync& completer) { |
| auto edu_device = device_.lock(); |
| if (!edu_device) { |
| FDF_LOG(ERROR, "Unable to access device resources."); |
| completer.ReplyError(ZX_ERR_BAD_STATE); |
| return; |
| } |
| |
| uint32_t input = request->input; |
| |
| edu_device->ComputeFactorial( |
| input, [completer = completer.ToAsync()](zx::status<uint32_t> result_status) mutable { |
| if (result_status.is_error()) { |
| completer.ReplyError(result_status.error_value()); |
| return; |
| } |
| uint32_t factorial = result_status.value(); |
| completer.ReplySuccess(factorial); |
| }); |
| } |
| // [END compute_factorial] |
| |
| // [START liveness_check] |
| // Driver Service: Complete a liveness check on the edu device |
| void QemuEduServer::LivenessCheck(LivenessCheckRequestView request, |
| LivenessCheckCompleter::Sync& completer) { |
| auto edu_device = device_.lock(); |
| if (!edu_device) { |
| FDF_LOG(ERROR, "Unable to access device resources."); |
| completer.ReplyError(ZX_ERR_BAD_STATE); |
| return; |
| } |
| |
| constexpr uint32_t kChallenge = 0xdeadbeef; |
| constexpr uint32_t kExpectedResponse = ~(kChallenge); |
| |
| auto status = edu_device->LivenessCheck(kChallenge); |
| if (status.is_error()) { |
| FDF_LOG(ERROR, "Unable to send liveness check request."); |
| completer.ReplyError(status.error_value()); |
| return; |
| } |
| const bool alive = (status.value() == kExpectedResponse); |
| |
| FDF_SLOG(INFO, "Replying with", KV("result", alive)); |
| completer.ReplySuccess(alive); |
| } |
| // [END liveness_check] |
| |
| // [START namespace_end] |
| } // namespace qemu_edu |
| // [END namespace_end] |
| |
| // [START driver_hook] |
| // Register driver hooks with the framework |
| FUCHSIA_DRIVER_RECORD_CPP_V1(qemu_edu::QemuEduDriver); |
| // [END driver_hook] |