blob: 34a2b22fd0a5a931d167b5846230bfda02bf7469 [file] [log] [blame]
// 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]