To provide services for devices in a Fuchsia system, drivers must bind to device nodes. The driver manager maintains the topology of nodes, where each node represents access to a hardware or virtual device in the system. Once bound to a device node, the driver can start providing services for the device that the node represents.
The framework matches drivers to device nodes by correlating the node properties of each node with the set of bind rules provided by the driver. Bind rules are a set of logic rules that describe which node properties the driver supports.
In this section, you'll create a skeleton driver that binds to the edu
device
and implements the bare driver framework hooks.
Create a new driver component
To begin, create a new project directory in your Bazel workspace for a driver
component called qemu_edu
:
mkdir -p fuchsia-codelab/qemu_edu/drivers
After you complete this section, the project should have the following directory structure:
//fuchsia-codelab/qemu_edu/drivers
|- BUILD.bazel
|- meta
| |- qemu_edu.cml
|- qemu_edu.bind
|- qemu_edu.cc
|- qemu_edu.h
Create the qemu_edu/drivers/BUILD.bazel
file and add the following statement to
include the necessary build rules from the Fuchsia SDK:
qemu_edu/drivers/BUILD.bazel
:
load(
"@fuchsia_sdk//fuchsia:defs.bzl",
"fuchsia_cc_driver",
"fuchsia_component_manifest",
"fuchsia_driver_bind_bytecode",
"fuchsia_driver_component",
"fuchsia_package",
)
Add the component manifest
The component manifest file defines the attributes of the component's executable,
including binding rules and the component's capabilities. Drivers are loaded as
shared libraries (.so
) using the driver
runner.
Create the qemu_edu/drivers/meta/qemu_edu.cml
file and add the following:
qemu_edu/drivers/meta/qemu_edu.cml
:
{
include: [
"syslog/client.shard.cml",
],
program: {
runner: 'driver',
binary: 'driver/libqemu_edu.so',
bind: 'meta/bind/qemu_edu.bindbc',
// Identifies the device categories, for compatibility tests. This
// example driver uses the 'misc' category; real drivers should
// select a more specific category.
device_categories: [
{ category: 'misc', subcategory: '' },
],
},
}
Add the following build rules to the bottom of your qemu_edu/drivers/BUILD.bazel
file to compile the component manifest:
fuchsia_component_manifest()
: Describes the source file and dependencies to compile the driver's component manifest.
qemu_edu/drivers/BUILD.bazel
:
fuchsia_component_manifest(
name = "manifest",
src = "meta/qemu_edu.cml",
includes = [
"@fuchsia_sdk//pkg/syslog:client",
],
)
Configure bind rules
The bind rules describe which device nodes this driver can support. These are listed as a series of condition statements that reference the key/value pairs in the device node's properties. For a driver to be considered a match, all rules must evaluate to true for the given device node.
Create qemu_edu/drivers/qemu_edu.bind
and add the following bind rules to
declare the driver supports PCI devices with a VID and DID matching the edu
device:
qemu_edu/drivers/qemu_edu.bind
:
using fuchsia.pci;
fuchsia.BIND_FIDL_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
// PCI VID/DID pair defined in the QEMU edu device specification:
fuchsia.BIND_PCI_VID == 0x1234;
fuchsia.BIND_PCI_DID == 0x11e8;
Add the following build rules to the bottom of your qemu_edu/drivers/BUILD.bazel
file to compile the bind rules:
fuchsia_driver_bytecode_bind_rules()
: Describes the specifics of the bind rules for a driver. Therules
attribute specifies the file that contains the bind rules of this driver. Thedeps
attribute specifies the bind libraries to be used with the bind rules specified in rules.
qemu_edu/drivers/BUILD.bazel
:
fuchsia_driver_bind_bytecode(
name = "bind_bytecode",
output = "qemu_edu.bindbc",
rules = "qemu_edu.bind",
deps = [
"@fuchsia_sdk//bind/fuchsia.pci",
],
)
Implement driver hooks
Once a driver is bound, the framework loads the component binary and constructs
an instance of the driver class registered using the
FUCHSIA_DRIVER_EXPORT()
macro. The driver overrides the Start()
method to perform any initialization tasks.
Create qemu_edu/drivers/qemu_edu.h
and qemu_edu/drivers/qemu_edu.cc
and add
the following boilerplate code to create the driver class and configure the
initial Start()
hook:
qemu_edu/drivers/qemu_edu.h
:
#ifndef FUCHSIA_CODELAB_CC_QEMU_EDU_H_
#define FUCHSIA_CODELAB_CC_QEMU_EDU_H_
#include <fidl/examples.qemuedu/cpp/wire.h>
#include <lib/driver/component/cpp/driver_base.h>
#include <lib/driver/devfs/cpp/connector.h>
namespace qemu_edu {
class QemuEduDriver : public fdf::DriverBase {
public:
QemuEduDriver(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: fdf::DriverBase("qemu-edu", std::move(start_args), std::move(driver_dispatcher)),
devfs_connector_(fit::bind_member<&QemuEduDriver::Serve>(this)) {}
virtual ~QemuEduDriver() = default;
// Start hook called by the driver factory.
zx::result<> Start() override;
private:
zx::result<> ExportToDevfs();
void Serve(fidl::ServerEnd<examples_qemuedu::Device> request);
fidl::WireSyncClient<fuchsia_driver_framework::Node> node_;
fidl::WireSyncClient<fuchsia_driver_framework::NodeController> controller_;
driver_devfs::Connector<examples_qemuedu::Device> devfs_connector_;
};
} // namespace qemu_edu
#endif // FUCHSIA_CODELAB_CC_QEMU_EDU_H_
qemu_edu/drivers/qemu_edu.cc
:
#include "qemu_edu.h"
#include <lib/driver/component/cpp/driver_export.h>
namespace qemu_edu {
// Initialize this driver instance
zx::result<> QemuEduDriver::Start() {
FDF_SLOG(INFO, "edu driver loaded successfully");
return zx::ok();
}
} // namespace qemu_edu
// Register driver hooks with the framework
FUCHSIA_DRIVER_EXPORT(qemu_edu::QemuEduDriver);
Add the following build rule to the bottom of your qemu_edu/drivers/BUILD.bazel
file to compile the driver code into a shared library binary:
fuchsia_cc_driver()
: Specifies the source and header files (for instance,qemu_edu.cc
andqemu_edu.h
) for building a C++ driver for Fuchsia.
qemu_edu/drivers/BUILD.bazel
:
fuchsia_cc_driver(
name = "qemu_edu",
srcs = [
"qemu_edu.cc",
"qemu_edu.h",
],
deps = [
"@fuchsia_sdk//pkg/driver_component_cpp",
"@fuchsia_sdk//pkg/driver_devfs_cpp",
"@fuchsia_sdk//pkg/hwreg",
"@fuchsia_sdk//pkg/mmio",
],
)
Load the driver
With the initial scaffolding in place, you can publish the driver to a local
package repository and verify that it binds successfully to the edu
device.
Add the following rules to the bottom of your qemu_edu/drivers/BUILD.bazel
file
to build the driver component into a Fuchsia package:
fuchsia_driver_component()
: Describes the binaries and artifacts for theqemu_edu
driver component.fuchsia_package()
: Builds the driver component into a Fuchsia package.
qemu_edu/drivers/BUILD.bazel
:
fuchsia_driver_component(
name = "component",
bind_bytecode = ":bind_bytecode",
driver_lib = ":qemu_edu",
manifest = ":manifest",
)
fuchsia_package(
name = "pkg",
package_name = "qemu_edu",
components = [
":component",
],
visibility = ["//visibility:public"],
)
Use the bazel run
command to build and execute the component target:
bazel run //fuchsia-codelab/qemu_edu/drivers:pkg.component
The bazel run
command performs the following steps:
- Build the component and package.
- Publish the package to a local package repository.
- Register the package repository with the target device.
- Use
ffx driver register
to load the driver component.
You should see the driver framework automatically detect a match a bind the
driver to the edu
device node:
INFO: Build completed successfully, 1 total action
added repository bazel.pkg.component
Registering fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm
Successfully bound:
Node 'root.sys.platform.pt.PCI0.bus.00_06_0_.pci-00_06.0-fidl', Driver 'fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm'.
Inspect the system log and verify that you can see FDF_SLOG()
message from the
driver code after the driver successfully binds:
ffx log --filter qemu_edu
[driver_index][driver_index,driver][I] Registered driver successfully: fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm.
[driver_manager][driver_manager.cm][I]: [driver_runner.cc:959] Binding fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm to 00_06_0_
[full-drivers:root.sys.platform.pt.PCI0.bus.00_06_0_][qemu-edu,driver][I]: [fuchsia-codelab/qemu_edu/qemu_edu.cc:28] edu driver loaded successfully
[driver-hosts:driver-host-3][][I]: [../../src/devices/bin/driver_host/driver_host.cc:349] Started driver url=fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm
Explore the updated device node
Now that the driver has successfully bound to a device node, ffx driver list
reports that your driver is loaded:
ffx driver list --loaded
fuchsia-boot:///#meta/block.core.cm
fuchsia-boot:///#meta/bus-pci.cm
fuchsia-boot:///#meta/cpu-trace.cm
fuchsia-boot:///#meta/fvm.cm
fuchsia-boot:///#meta/hid.cm
fuchsia-boot:///#meta/intel-rtc.cm
fuchsia-boot:///#meta/netdevice-migration.cm
fuchsia-boot:///#meta/network-device.cm
fuchsia-boot:///#meta/pc-ps2.cm
fuchsia-boot:///#meta/platform-bus-x86.cm
fuchsia-boot:///#meta/platform-bus.cm
fuchsia-boot:///#meta/ramdisk.cm
fuchsia-boot:///#meta/sysmem.cm
fuchsia-boot:///#meta/virtio_block.cm
fuchsia-boot:///#meta/virtio_ethernet.cm
fuchsia-pkg://fuchsia.com/virtual_audio#meta/virtual_audio_driver.cm
fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm
Inspect the device node once more using ffx driver list-devices
, and verify
that your driver is now listed as attached to the edu
device node:
ffx driver list-devices root.sys.platform.pt.PCI0.bus.00_06_0_.pci-00_06.0-fidl --verbose
Name : 0-fidl
Moniker : root.sys.platform.pt.PCI0.bus.00_06_0_.pci-00_06.0-fidl
Driver : fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm
11 Properties
[ 1/ 11] : Key fuchsia.BIND_FIDL_PROTOCOL Value 0x000004
[ 2/ 11] : Key fuchsia.BIND_PCI_VID Value 0x001234
[ 3/ 11] : Key fuchsia.BIND_PCI_DID Value 0x0011e8
[ 4/ 11] : Key fuchsia.BIND_PCI_CLASS Value 0x000000
[ 5/ 11] : Key fuchsia.BIND_PCI_SUBCLASS Value 0x0000ff
[ 6/ 11] : Key fuchsia.BIND_PCI_INTERFACE Value 0x000000
[ 7/ 11] : Key fuchsia.BIND_PCI_REVISION Value 0x000010
[ 8/ 11] : Key fuchsia.BIND_PCI_TOPO Value 0x000030
[ 9/ 11] : Key "fuchsia.hardware.pci.Device" Value true
[10/ 11] : Key fuchsia.BIND_PROTOCOL Value 0x000000
[11/ 11] : Key "fuchsia.platform.DRIVER_FRAMEWORK_VERSION" Value 0x000002
Congratulations! You have successfully bound a driver component to a device node on Fuchsia.