blob: caa3c94eac9f98395e260656c73f9743658ad0ad [file] [log] [blame] [view] [edit]
# Driver sample walkthrough: qemu_edu
The [`qemu_edu`][qemu-edu] driver (in the
[SDK driver samples][sdk-driver-samples] repository) is a good sample to
study if youre interested in creating a new driver or understanding how drivers
work in a Fuchsia system.
One of the services that the `qemu_edu` driver provides is that it enables
Fuchsia components to calculate a factorial using the computing power of a
device (in fact, a virtual device) in the system.
This development walkthrough guide contains the following sections:
* [How the driver works](#how_this_driver_works)
* [Build rules: BUILD.bazel](#build_rules_buildbazel)
* [Driver component: qemu_edu](#driver_component_qemu_edu)
* [Tools component: eductl](#tools_component_eductl)
## How this driver works
For the `qemu_edu` driver to work, we first need to launch an instance of the
[Fuchsia emulator][femu] (for instance, using a Fuchsias Workstation image).
[QEMU][qemu]{:.external} (which underpins the Fuchsia emulator)
then creates a virtual device named [`edu`][edu-device], which is an educational
device for writing drivers. It is this `edu` device that provides a service that
computes a factorial given an integer.
Soon after the emulator instance starts running, Fuchsias
[driver framework][driver-framework] discovers the `edu` device (which is
represented as a [node][nodes]) and looks for a driver that is a good match for
the `edu` device. However, at first the driver framework wont be able to find
a driver with bind rules that can satisfy the [binding properties][driver-binding]
of the `edu` device. At this point, the `edu` device is left unmatched,
therefore its node unbound.
Without a driver, the unmatched `edu` device cannot provide services to other
components in the system. However, here we have the `qemu_edu` driver, which we
know is suitable for serving the `edu` device. To supply this driver to the
`edu` device in the system, we need to do the following:
1. Build a Fuchsia package that contains the `qemu_edu` driver component.
2. Publish the package to a local Fuchsia package repository.
3. Register the `qemu_edu` driver to the Fuchsia emulator instance.
Note: The [`bazel run`][get-started-drivers] command is configured to run the
sequence of all three actions above.
When a new driver is registered, Fuchsias
[driver manager][driver-manager] informs the [driver index][driver-index]
about the arrival of the new driver. The driver index then tries to match this
new `qemu_edu` driver to unbound nodes in the node topology.
Were lucky this time because the `qemu_edu` drivers bind rules (see
[`qemu_edu.bind`](#qemu_edubind)) are precisely designed to match the binding
properties of the `edu` device to be exact, the binding properties of the node
that represents the `edu` device in the topology.
Once they are matched and bound, the driver manager examines the `qemu_edu`
drivers component manifest (see [`meta/qemu_edu.cml`](#metaqemu_educml))
to learn more about the `qemu_edu` driver. For instance, the drivers
component manifest describes which binary to run, which runtime to use, and
whether to place the driver in a new [driver host][driver-host]
or in the same driver host as its parent driver. In this case, by default the
driver manager places the `qemu_edu` driver in a new driver host.
The driver host (which runs as a Fuchsia component) calls the `Start()` function
in the drivers code (see [`qemu_edu.cc`](#qemu_educc)) to initialize the
driver. Once initialized, the `qemu_edu` driver can start providing the `edu`
devices services to other components in the system.
## Build rules: BUILD.bazel
This [`BUILD.bazel`][build-bazel] file contains the build rules of the
[`qemu_edu` driver component](#driver_component_qemu_edu)
and the [`eductl` "tools" component](#tools_component_eductl). Using this
`BUILD.bazel` file, these two components are built into the same package labeled
`qemu_edu` (see the `fuchsia_package` rule in `BUILD.bazel`).
While the build rules of a driver component mostly look similar to the build
rules of other Fuchsia components, there exist a few rules and attributes
specific to the driver development.
Below is the `load` field shown at the top of the `qemu_edu` drivers
`BUILD.bazel` file:
```none {:.devsite-disable-click-to-copy}
load(
"@rules_fuchsia//fuchsia:defs.bzl",
"fuchsia_cc_binary",
"fuchsia_component",
"fuchsia_component_manifest",
{{ '<strong>' }}"fuchsia_driver_bytecode_bind_rules",{{ '</strong>' }}
"fuchsia_fidl_library",
{{ '<strong>' }}"fuchsia_fidl_llcpp_library",{{ '</strong>' }}
"fuchsia_package",
)
```
Of these entries, the following build rules are specific to the driver
development:
* [`fuchsia_driver_bytecode_bind_rules`](#fuchsia_driver_bytecode_bind_rules)
* [`fuchsia_fidl_llcpp_library`](#fuchsia_fidl_llcpp_library)
And the following build rules contain additional entries that are specific to
the driver development:
* [`cc_binary`](#cc_binary)
* [`fuchsia_component`](#fuchsia_component)
### fuchsia_driver_bytecode_bind_rules
The `fuchsia_driver_bytecode_bind_rules` rule describes the specifics of the
[bind rules][driver-binding] of a driver. Fuchsias driver framework uses a
drivers bind rules to match the driver to a device in a Fuchsia system.
Below are the `fuchsia_driver_bytecode_bind_rules` attributes of
the `qemu_edu` driver:
```none {:.devsite-disable-click-to-copy}
fuchsia_driver_bytecode_bind_rules(
name = "bind_bytecode",
output = "qemu_edu.bindbc",
rules = "qemu_edu.bind",
deps = [
"@fuchsia_sdk//bind/fuchsia.pci",
],
)
```
The `rules` attribute above specifies that the [`qemu_edu.bind`](#qemu_edubind)
file contains the bind rules of this driver. The `deps` attribute
specifies the bind libraries to be used with the bind rules specified
in `rules`. To see all available bind libraries supported by Fuchsia,
look in the `//bind` directory in the Fuchsia SDK. (For more information,
see [Bind libraries][bind-libraries].)
### fuchsia_fidl_llcpp_library
The `fuchsia_fidl_llcpp_library` rule describes the specifics of the `llcpp`
(Low-Level C++) FIDL library.
This Low-Level C++ FIDL library provides a collection of low-level C++ FIDL
calls used by drivers in a Fuchsia system. The library is optimized to meet the
needs of low-level systems programming and multi-thread environments for
increased security. (For more information, see
[Comparing C, Low-Level C++, and High-Level C++ Language Bindings][c-family-comparison].)
Notice there are two FIDL library build rules in this `BUILD.bazel` file:
* `fuchsia_fidl_library()`
* `fuchsia_fidl_llcpp_library()`
Below are the `qemu_edu` drivers attributes of these two FIDL library
build rules:
```none {:.devsite-disable-click-to-copy}
fuchsia_fidl_library(
name = "fuchsia.hardware.qemuedu",
srcs = [
"qemu_edu.fidl",
],
library = "fuchsia.hardware.qemuedu",
visibility = ["//visibility:public"],
)
fuchsia_fidl_llcpp_library(
name = "fuchsia.hardware.qemuedu_cc",
library = ":fuchsia.hardware.qemuedu",
visibility = ["//visibility:public"],
deps = [
"@fuchsia_sdk//fidl/zx:zx_llcpp_cc",
"@fuchsia_sdk//pkg/fidl-llcpp",
],
)
```
The generic `fuchsia_fidl_library` rule defines a Fuchsia components FIDL
capabilities, which are specified in [`qemu_edu.fidl`](#qemu_edufidl) for the
`qemu_edu` driver. The `fuchsia_fidl_llcpp_library` rule describes additional
dependencies for generating the FIDL code that is based on the Low-Level C++
FIDL library.
### cc_binary
The `cc_binary` rule specifies the source and header files (for instance,
`qemu_edu.cc`, `qemu_edu.h`, and `registers.h`) for building a C++ binary for
the `qemu_edu` driver.
Notice some driver-specific dependencies in this `cc_binary` rule, mainly the
`pci_llcpp` FIDL library for PCI FIDL communication and `hwreg` and `mmio`
packages for working with a devices hardware registers and MMIO
(Memory-Mapped I/O):
```none {:.devsite-disable-click-to-copy}
cc_binary(
name = "qemu_edu",
srcs = [
"qemu_edu.cc",
"qemu_edu.h",
"registers.h",
],
{{ '<strong>' }}linkshared = True,{{ '</strong>' }}
deps = [
":fuchsia.hardware.qemuedu_cc",
"@fuchsia_sdk//fidl/fuchsia.device.fs:fuchsia.device.fs_llcpp_cc",
"@fuchsia_sdk//fidl/fuchsia.driver.compat:fuchsia.driver.compat_llcpp_cc",
{{ '<strong>' }}"@fuchsia_sdk//fidl/fuchsia.hardware.pci:fuchsia.hardware.pci_llcpp_cc",{{ '</strong>' }}
"@fuchsia_sdk//fidl/zx:zx_cc",
"@fuchsia_sdk//pkg/driver2-llcpp",
"@fuchsia_sdk//pkg/driver_runtime_cpp",
"@fuchsia_sdk//pkg/fidl-llcpp",
{{ '<strong>' }}"@fuchsia_sdk//pkg/hwreg",{{ '</strong>' }}
{{ '<strong>' }}"@fuchsia_sdk//pkg/mmio",{{ '</strong>' }}
"@fuchsia_sdk//pkg/sys_component_llcpp",
"@fuchsia_sdk//pkg/zx-status",
],
)
```
Whats also important for Fuchsia drivers is that the line `linkshared = True,`
must be included in this build rule. This line enables the binary of a Fuchsia
driver to be shared with other processes.
### fuchsia_component
For a driver component, the `fuchsia_component` rule mainly describes where to
place binaries and artifacts within the components storage space.
Below are the `fuchsia_component` attributes of the `qemu_edu` driver:
```none {:.devsite-disable-click-to-copy}
fuchsia_component(
name = "component",
content = {
":qemu_edu": "lib/",
":bind_bytecode": "meta/bind/",
},
manifest = ":manifest",
)
```
According to these attributes, the content of the drivers binary
(generated from the `qemu_edu` target) is to be placed in the `lib` directory,
and the content of the bytecode that contains the drivers bind rules (generated
from the `bind_bytecode` target) is to be placed in the `meta/bind` directory of
the component.
The file placement convention above is recommended for driver components.
The Fuchsia platform does not compress files under a components `meta`
directory. So it helps to place files that need to be loaded fast, such as
bytecode containing bind rules, in the `meta` directory.
## Driver component: qemu_edu
The `qemu_edu` component is the `qemu_edu` driver itself.
The `qemu_edu` driver component comprises the following files:
* Build rules: [`BUILD.bazel`](#build_rules_buildbazel)
* Component manifests: [`meta/qemu_edu.cml`](#metaqemu_educml)
* Bind rules: [`qemu_edu.bind`](#qemu_edubind)
* FIDL interface: [`qemu_edu.fidl`](#qemu_edufidl)
* Driver component header: [`qemu_edu.h`](#qemu_eduh)
* Registers header: [`registers.h`](#registersh)
* Component implementation: [`qemu_edu.cc`](#qemu_educc)
### meta/qemu_edu.cml
The [`meta/qemu_edu.cml`][qemu-edu-cml] file contains the
[component manifest][component-manifests] of the `qemu_edu` component.
Below are the attributes of the component manifest for the `qemu_edu` component:
```none {:.devsite-disable-click-to-copy}
{
program: {
runner: 'driver',
binary: 'lib/libqemu_edu.so',
bind: 'meta/bind/qemu_edu.bindbc'
},
use: [
{
protocol: [
'fuchsia.logger.LogSink',
'fuchsia.device.fs.Exporter'
],
},
{
directory: 'fuchsia.driver.compat.Service-default',
rights: ['rw*'],
path: '/fuchsia.driver.compat.Service/default',
}
],
}
```
The `program` field describes which program to start when this component starts
running on the Fuchsia platform. For driver components, the `runner` attribute
needs to be `driver` , which means to use the [driver runtime][driver-runtime].
The `binary` attribute indicates that the `lib/libqemu_edu.so` file contains the
binary of the driver. According to the [`fuchsia_component`](#fuchsia_component)
rule in the `BUILD.bazel` file, the drivers binary (`libqemu_edu.so`)
is located in the `lib` directory, and the bind rules (`qemu_edu.bindbc`)
is located in the `meta/bind` directory of the components storage space.
The `use` field specifies the capabilities that this component uses when it
starts running on the Fuchsia platform.
### qemu_edu.bind
The [`qemu_edu.bind`][qemu-edu-bind] file contains the bind rules for the
`qemu_edu` driver.
A drivers bind rules are a series of Boolean expressions that the driver must
provide to ensure that its matched to the right device running in a system.
These rules are compared against the binding properties of devices
in the system. When a devices binding properties can satisfy the drivers bind
rules (thus returning `true` for all conditions in the bind rules), then the
driver is said to be matched to the device. Once they are matched, the driver
can provide the devices services to other components in the system.
Below are the bind rules of the `qemu_edu` driver:
```none {:.devsite-disable-click-to-copy}
using fuchsia.pci;
fuchsia.BIND_FIDL_PROTOCOL == fuchsia.pci.BIND_FIDL_PROTOCOL.DEVICE;
fuchsia.BIND_PCI_VID == 0x1234;
fuchsia.BIND_PCI_DID == 0x11e8;
```
The line `using fuchsia.pci;` indicates the bind library necessary to generate
the bind rules into bytecode. This bind library is also specified in the
[`fuchsia_driver_bytecode_bind_rules`](#fuchsia_driver_bytecode_bind_rules) rule
in `BUILD.bazel`.
The bind rules for the `qemu_edu` driver say the following:
* The device is a PCI device.
* The vendor ID (`BIND_PCI_VID`) needs to be `0x1234`, which is a globally
unique ID that identifies the vendor.
* The device ID (`BIND_PCI_DID`) needs to be `0x11e8`, which is a globally
unique ID that the vendor has assigned to the device.
Note: For more information on the bind rules, see the
[Driver Binding][driver-binding-dfv1],
which was written for the previous version of the driver framework (DFv1),
but most of its content is still relevant to the new driver framework (DFv2).
### qemu_edu.fidl
The [`qemu_edu.fdl`][qemu-edu-fidl] file describes the FIDL interface of the
`qemu_edu` driver.
Below are the two FIDL services that the `qemu_edu` driver provides:
```none {:.devsite-disable-click-to-copy}
library fuchsia.hardware.qemuedu;
protocol Device {
// Computes the factorial of `input` using the edu device and returns the
// result.
ComputeFactorial(struct {
input uint32;
}) -> (struct {
output uint32;
});
// Performs a liveness check and return true if the device passes.
LivenessCheck() -> (struct {
result bool;
});
};
```
The line `library fuchsia.hardware.qemuedu` indicates the FIDL library used by
these FIDL services (see [FIDL reference docs][fidl-reference])
This FIDL library needs to match the `library` attribute specified in the
[`fuchsia_fidl_library`](#fuchsia_fidl_llcpp_library) rule in `BUILD.bazel`.
The implementation of the `ComputeFactorial()` and `LivenessCheck()` FIDL
services is in the [`qemu_edu.cc`](#qemu_educc) file.
### qemu_edu.h
The [`qemu_edu.h`][qemu-edu-h] file is the C++ header for the implementation of
the `qemu_edu` component ([`qemu_edu.cc`](#qemu_educc)).
Below are the public C++ function declarations for the `qemu_edu` driver:
```none {:.devsite-disable-click-to-copy}
class QemuEduDriver : public fidl::WireServer<fuchsia_hardware_qemuedu::Device> {
public:
QemuEduDriver(async_dispatcher_t* dispatcher,
fidl::WireSharedClient<fuchsia_driver_framework::Node> node, driver::Namespace ns,
driver::Logger logger)
: outgoing_(component::OutgoingDirectory::Create(dispatcher)),
node_(std::move(node)),
ns_(std::move(ns)),
logger_(std::move(logger)) {}
virtual ~QemuEduDriver() = default;
{{ '<strong>' }}static constexpr const char* Name() { return "qemu-edu"; }{{ '</strong>' }}
{{ '<strong>' }}static zx::status<std::unique_ptr<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);{{ '</strong>' }}
// fuchsia.hardware.qemuedu/Device implementation.
void ComputeFactorial(ComputeFactorialRequestView request,
ComputeFactorialCompleter::Sync& completer);
void LivenessCheck(LivenessCheckRequestView request, LivenessCheckCompleter::Sync& completer);
static uint32_t ComputeFactorial(uint32_t input);
...
```
All driver components are required to supply the following two `public` functions:
* `Name()` The name returned from this function is often used in
`qemu_edu.cc` to identify the driver (for instance, to identify the drivers
placement in the topology).
* `Start()` This is the main function that gets executed as the start hook
for the driver.
### registers.h
The [`registers.h`][registers-h] file is the header file that describes the
registers of the `edu` device.
Below are the offset values of the registers in the `edu` device
(for which the `qemu_edu` driver provides services):
```none {:.devsite-disable-click-to-copy}
constexpr uint32_t kIdentificationOffset = 0x00;
constexpr uint32_t kLivenessCheckOffset = 0x04;
constexpr uint32_t kFactorialCompoutationOffset = 0x08;
constexpr uint32_t kStatusRegisterOffset = 0x20;
constexpr uint32_t kInterruptStatusRegisterOffset = 0x24;
constexpr uint32_t kInterruptRaiseRegisterOffset = 0x60;
constexpr uint32_t kInterruptAcknowledgeRegisterOffset = 0x64;
constexpr uint32_t kDmaSourceAddressOffset = 0x80;
constexpr uint32_t kDmaDestinationAddressOffset = 0x80;
constexpr uint32_t kDmaTransferCountOffset = 0x90;
constexpr uint32_t kDmaCommandRegisterOffset = 0x98;
```
The `qemu_edu` driver uses these offset values to understand how to read and
write values from the registers in the `edu` device. In general, driver
developers would use their target devices specification sheet to provide this
offset information.
### qemu_edu.cc
The [`qemu_edu.cc`][qemu-edu-cc] file contains the source code of the `qemu_edu`
component, which is the `qemu_edu` driver itself.
#### Implementing the start hook
A driver is required to provide the implementation of a public `Start()`
function, which is the main function that gets executed as the start hook for
the driver. The code below is the `Start()` function of the `qemu_edu` driver:
```none {:.devsite-disable-click-to-copy}
zx::status<std::unique_ptr<QemuEduDriver>> QemuEduDriver::Start(
fdf::wire::DriverStartArgs& start_args, fdf::UnownedDispatcher dispatcher,
fidl::WireSharedClient<fdf::Node> node, driver::Namespace ns, driver::Logger logger) {
auto driver = std::make_unique<QemuEduDriver>(dispatcher->async_dispatcher(), std::move(node),
std::move(ns), std::move(logger));
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));
}
```
This `Start()` function initializes an instance of the `QemuEduDriver` class
and assigns it to the `driver` variable. The function then calls the private
`Run()` function, which performs most of the initialization tasks.
In the `Run()` function, the first task is to connect to Fuchsias device
filesystem (`devfs`), which enables the driver to expose the `edu` device and
its services to other components in the system:
```none {:.devsite-disable-click-to-copy}
auto exporter = ns_.Connect<fuchsia_device_fs::Exporter>();
if (exporter.is_error()) {
return exporter.take_error();
}
exporter_ = fidl::WireClient(std::move(*exporter), dispatcher);
```
To discover which driver services are available in the system, a non-driver
component would look up the device filesystem (usually mounted to /`dev` in a
components storage space) and scan for the directories and files under this
filesystem. Once the non-driver component locates a service, which shows up as
a file in `devfs`, the component can contact the driver by opening this file in
and writing to it (similar to a POSIX system).
After this initial contact, the driver sets up a FIDL connection
(more precisely, by exchanging FIDL protocols in /`svc`) to the non-driver
component. From this point, all communication between the component and the
driver takes place using FIDL calls. (For more information, see
[Driver communication][driver-communication].) The rest of the code in the
`Run()` function implements the setting up and mapping of this `devfs`-to-FIDL
connection.
#### Computing a factorial
Once initialized, the `qemu_edu` driver provides the following two services
to its clients (which include both non-driver components and other drivers):
* `ComputeFactorial()`: Write an integer to the `edu` device and read the
value, which is the factorial of the integer, returned from the device.
* `LivenessCheck()`: Write a challenge value to the `edu` devices register
and read the value returned from the device to confirm that the device is
working as expected.
When passing values to the `edu` device, the `qemu_edu` driver uses predefined
offsets in the [`registers.h`](#registersh) file to
determine exactly which memory locations to write the input values
in the devices registers.
In [Line 133][qemu-edu-cc-line-133] of `qemu_edu.cc`, the `Run()` function
includes the following function call:
```none {:.devsite-disable-click-to-copy}
auto pci_status = MapInterruptAndMmio(std::move(pci_endpoints->client));
```
The ​​`MapInterruptAndMmio()` function (whose implementation starts in
[Line 45][qemu-edu-cc-line-45]) sets up access to the register and extracts
Fuchsias [VMO][vmo] (Virtual Memory Object), which enables MMIO operations
(such as reading and writing to the register). And in the same function, the
`SetInterruptMode` function in [Line 82][qemu-edu-cc-line-82] initializes
interrupt calls for the driver.
After reading an integer value from its register, the `edu` device uses its own
factorial function (see [`edu.c`][edu-c-line-314]) and its own (virtual)
computing power to compute a factorial using the input integer retrieved from
the register.
The `ComputeFactorial()` function (starting at
[Line 177][qemu-edu-cc-line-177]) handles the factorial computation by writing
a value into the `edu` devices register (using an offset) and reading a value
from the register:
```none {:.devsite-disable-click-to-copy}
void QemuEduDriver::ComputeFactorial(ComputeFactorialRequestView request,
ComputeFactorialCompleter::Sync& completer) {
// Write a value into the factorial register.
uint32_t input = request->input;
mmio_->Write32(input, regs::kFactorialCompoutationOffset);
// Busy wait on the factorial status bit.
while (true) {
const auto status = regs::Status::Get().ReadFrom(&*mmio_);
if (!status.busy())
break;
}
// Return the result.
uint32_t factorial = mmio_->Read32(regs::kFactorialCompoutationOffset);
FDF_SLOG(INFO, "Replying with", KV("factorial", factorial));
completer.Reply(factorial);
}
```
When the [`eductl` tools component](#tools_component_eductl) calls
this `ComputeFactorial()` function on the driver (using a FIDL call)
and passes an integer as input, the function writes this input value into the
`edu` devices register (using the `mmio` object). The function then waits until
the register is ready to read. When the device finishes computing the factorial
and writes its result back into the register, the `while` loop breaks out and
the driver reads the value from the register to obtain the value of the
factorial computation performed by the `edu` device.
To emulate a virtual device that processes requests from the `qemu_edu` driver,
the [`edu`][edu-device] device (which is implemented as part of QEMU) provides
the following functions in [`edu.c`][edu-c]:
* The `edu_mmio_write` function (in [Line 251][edu-c-line-251])
writes the input value to the `edu` devices register and signals the
factorial worker thread :
```none {:.devsite-disable-click-to-copy}
case 0x08:
if (atomic_read(&edu->status) & EDU_STATUS_COMPUTING) {
break;
}
/* EDU_STATUS_COMPUTING cannot go 0->1 concurrently, because it is only
* set in this function and it is under the iothread mutex.
*/
qemu_mutex_lock(&edu->thr_mutex);
edu->fact = val;
atomic_or(&edu->status, EDU_STATUS_COMPUTING);
qemu_cond_signal(&edu->thr_cond);
qemu_mutex_unlock(&edu->thr_mutex);
break;
```
* The factorial thread (see the `edu_fact_thread()` function in
[Line 314][edu-c-line-314]) wakes up and computes the factorial and stores
the result in the devices register.
* The `edu_mmio_read()` function (in [Line 206][edu-c-line-206]) reads a value
from the devices register:
```none {:.devsite-disable-click-to-copy}
case 0x08:
qemu_mutex_lock(&edu->thr_mutex);
val = edu->fact;
qemu_mutex_unlock(&edu->thr_mutex);
break;
```
However, keep in mind that the `edu` device performs operations that are far
more complex than necessary in order to make itself behave like a real hardware
device, such as producing results asynchronously.
## Tools component: eductl
The `eductl` component is a helper component (known as a tools component) that
serves as a tool for testing the features of the `qemu_edu` driver while its
running in the system.
The `eductl` tools component comprises the following files:
* Build rules: [`BUILD.bazel`](#build_rules_buildbazel)
* Component manifests: [`meta/eductl.cml`](#metaeductlcml)
* Component implementation: [`eductl.cc`](#eductlcc)
### meta/eductl.cml
The [`meta/eductl.cml`][eductl-cml] file contains the
[component manifest][component-manifests] of the `eductl` component
Below are the attributes of the component manifest for the `eductl` component:
```none {:.devsite-disable-click-to-copy}
{
program: {
runner: 'elf',
binary: 'bin/eductl',
forward_stderr_to: "log",
forward_stdout_to: "log",
args: [
'factorial',
'12',
]
},
use: [
{
directory: "dev",
rights: [ "rw*" ],
path: "/dev",
},
{ protocol: "fuchsia.logger.LogSink" },
],
}
```
The `program` field describes which program to start when this component starts
running on the Fuchsia platform. The `runner` attribute specifies that this
component uses the `elf` runtime, which is commonly used to run C++ and Rust
binaries on the Fuchsia platform. The `binary` attribute indicates that the
`bin/eductl` file contains the binary of the program. Lastly, the `args` field
shows that the default input arguments provided to the program is
`factorial` `12`.
The `use` field sets up access to Fuchsias device filesystem (`devfs`) for
the component. This setup is required for non-driver components to interact
with drivers in a Fuchsia system. (For more information on the role of the
device filesystem, see [Driver communication][driver-communication].)
### eductl.cc
The [`eductl.cc`][eductl-cc] file contains the source code of the tools
component, which enables driver developers to interact with the `qemu_edu`
driver while the driver is running in a Fuchsia system. Its common for driver
developers to create these tools components for the purpose of testing and
debugging during development. However, in production, the ability to use these
driver-specific tools will be disabled.
Below are the hardcoded device paths that are mapped to the `qemu_edu` device
running in a Fuchsia system:
```none {:.devsite-disable-click-to-copy}
constexpr char kEduDevicePath[] =
"/dev/sys/platform/platform-passthrough/PCI0/bus/00:06.0_/qemu-edu";
constexpr char kEduDevicePath2[] = "/dev/sys/platform/platform-passthrough/acpi/acpi-KBLT/qemu-edu";
```
Using paths in Fuchsias device filesystem (`devfs`), non-driver components,
such as the `eductl` component, can discover services provided by the drivers
running in the system. (For more information, see
[Service discovery (using devfs)][service-discovery].)
Below is the function that uses the device paths to establish a FIDL connection
to the `qemu_edu` driver:
```none {:.devsite-disable-click-to-copy}
fidl::WireSyncClient<fuchsia_hardware_qemuedu::Device> OpenDevice() {
int device = open(kEduDevicePath, O_RDWR);
if (device < 0) {
device = open(kEduDevicePath2, O_RDWR);
}
if (device < 0) {
fprintf(stderr, "Failed to open qemu edu device: %s\n", strerror(errno));
return {};
}
fidl::ClientEnd<fuchsia_hardware_qemuedu::Device> client_end;
zx_status_t st = fdio_get_service_handle(device, client_end.channel().reset_and_get_address());
if (st != ZX_OK) {
fprintf(stderr, "Failed to get service handle: %s\n", zx_status_get_string(st));
return {};
}
return fidl::BindSyncClient(std::move(client_end));
}
```
In this `OpenDevice()` function, the `eductl` component uses the device paths
in `devfs` to contact the `qemu_edu` driver in the system. Once the initial
contact is made, a FIDL connection is established between the `eductl` component
and the `qemu_edu` driver. From this point, all communication between these
two takes place using this FIDL channel.
<!-- Reference links -->
[qemu-edu]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/
[sdk-driver-samples]: https://fuchsia.googlesource.com/sdk-samples/drivers/
[edu-device]: https://fuchsia.googlesource.com/third_party/qemu/+/refs/heads/main/docs/specs/edu.txt
[qemu]: https://www.qemu.org/
[driver-framework]: /docs/concepts/drivers/driver_framework.md
[nodes]: /docs/concepts/drivers/drivers_and_nodes.md
[driver-binding]: /docs/concepts/drivers/driver_binding.md
[get-started-drivers]: /docs/get-started/sdk/get-started-with-driver.md
[driver-manager]: /docs/concepts/drivers/driver_framework.md#driver_manager
[driver-index]: /docs/concepts/drivers/driver_framework.md#driver_index
[driver-host]: /docs/concepts/drivers/driver_framework.md#driver_host
[driver-runtime]: /docs/concepts/drivers/driver_framework.md#driver_runtime
[driver-communication]: /docs/concepts/drivers/driver_communication.md
[build-bazel]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/BUILD.bazel
[bind-libraries]: /docs/development/drivers/concepts/device_driver_model/driver-binding.md#bind-libraries
[c-family-comparison]: /docs/development/languages/fidl/guides/c-family-comparison.md
[qemu-edu-cml]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/meta/qemu_edu.cml
[component-manifests]: /docs/concepts/components/v2/component_manifests.md
[qemu-edu-bind]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/qemu_edu.bind
[driver-binding-dfv1]: /docs/development/drivers/concepts/device_driver_model/driver-binding.md
[qemu-edu-fidl]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/qemu_edu.fidl
[fidl-reference]: https://fuchsia.dev/reference/fidl
[qemu-edu-h]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/qemu_edu.h
[registers-h]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/registers.h
[qemu-edu-cc]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/qemu_edu.cc
[qemu-edu-cc-line-133]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/qemu_edu.cc#133
[qemu-edu-cc-line-45]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/qemu_edu.cc#45
[vmo]: /docs/concepts/kernel/concepts.md#shared_memory_virtual_memory_objects_vmos
[qemu-edu-cc-line-82]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/qemu_edu.cc#82
[edu-c-line-314]: https://fuchsia.googlesource.com/third_party/qemu/+/refs/heads/main/hw/misc/edu.c#314
[qemu-edu-cc-line-177]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/qemu_edu.cc#177
[edu-c]: https://fuchsia.googlesource.com/third_party/qemu/+/refs/heads/main/hw/misc/edu.c
[edu-c-line-251]: https://fuchsia.googlesource.com/third_party/qemu/+/refs/heads/main/hw/misc/edu.c#251
[edu-c-line-314]: https://fuchsia.googlesource.com/third_party/qemu/+/refs/heads/main/hw/misc/edu.c#314
[edu-c-line-206]: https://fuchsia.googlesource.com/third_party/qemu/+/refs/heads/main/hw/misc/edu.c#206
[eductl-cml]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/meta/eductl.cml
[eductl-cc]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/qemu_edu/eductl.cc
[service-discovery]: /docs/concepts/drivers/driver_communication.md#service_discovery_using_devfs
[femu]: /docs/development/sdk/ffx/start-the-fuchsia-emulator.md