Before you start working DFv1-to-DFv2 migration, the frequently asked questions below can help you identify special conditions or edge cases that may apply to your driver.
What is the compatibility shim and when is it necessary?
DFv1 and DFv2 drivers exist under a single branch of the node topology tree (that is, until all the drivers are migrated to DFv2) and they need to be able to talk to each other. To help with the migration process, Fuchsia's driver framework team created a compatibility shim to enable DFv1 drivers to live in DFv2.
If your target driver talks to other DFv1 drivers that still use
Banjo and those drivers won't be migrated to DFv2 all at once,
you need to use this compatibility shim (by manually creating
compat::DeviceServer
) for enabling the drivers in different framework
versions to talk to each other.
For more details on using the compatibility shim in a DFv2 driver, see the Set up the compat device server in a DFv2 driver guide.
Can DFv2 drivers talk to Banjo protocols using the compatibility shim?
While it's strongly recommended that your DFv1 driver is migrated from Banjo to FIDL, if it is necessary for a DFv2 driver to talk to some existing Banjo protocols, the compatibility shim provides the following features:
compat::BanjoServer
makes it easier to serve Banjo (seebanjo_server.h
).compat::ConnectBanjo
makes it easier to connect to Banjo (seebanjo_client.h
).
For more details on these features, see the Serve Banjo protocols in a DFv2 driver guide.
Can DFv2 drivers use the compatibility shim for composite nodes?
The migration process for composite drivers are nearly identical to normal drivers, but composite drivers have slightly different ways for connecting to Banjo or FIDL protocols from parent nodes.
Because composite nodes have multiple parents, composite drivers need to identify the parent’s name when connecting to it. For example, below is a normal driver establishing a Banjo connection with its parent:
zx::result client_result =
compat::ConnectBanjo<ddk::HidDeviceProtocolClient>(incoming());
The composite driver’s method is almost identical, except the parent name needs to be added:
zx::result client_result =
compat::ConnectBanjo<ddk::HidDeviceProtocolClient>(incoming(), "gpio-int")
What has changed in the new DFv2 driver interfaces?
One major change in DFv2 is that drivers take control of the life cycle of the child nodes (or devices) created by the drivers. This is different from DFv1 where the driver framework manages the life cycles of devices, such as tearing down devices, through the device tree.
In DFv1, devices are controlled by zx_protocol_device
while drivers are controlled by zx_driver_ops
.
If ddktl
is used, the interfaces in zx_protocol_device
need to be
wrapped by Ddk*()
functions in the mixin template class. In DFv2,
those interfaces have changed
significantly.
How does service discovery work in DFv2?
In DFv2, using a FIDL service is required to establish a protocol
connection. The parent driver adds a FIDL service to the
fdf::OutgoingDirectory
object and serves it to the child node,
which then enables the parent driver to offer the service to the
child node.
DFv1 and DFv2 drivers do this differently in the following ways:
In DFv1, the driver sets and passes the offer from the
DeviceAddArgs::set_runtime_service_offers()
call. Then the driver creates anfdf::OutgoingDirectory
object and passes the client end handle through theDeviceAddArgs::set_outgoing_dir()
call.In DFv2, the driver sets and passes the offer from the
NodeAddArgs::offers
object. The driver adds the service to the outgoing directory wrapped by theDriverBase
class (originally provided by theStart()
function). When the child driver binds to the child node, the driver host passes the incoming namespace containing the service to the child driver'sStart()
function.
On the child driver side, DFv1 and DFv2 drivers also connect to the protocol providing the service in different ways:
- A DFv1 driver calls the
DdkConnectRuntimeProtocol<ServiceInstanceName>()
method. A DFv2 driver calls
incoming()->Connect<ServiceInstanceName>()
if theDriverBase
class is used.For more information, see Use the DFv2 service discovery.
How does my driver's node (or device) get exposed in the system in DFv2?
Fuchsia has a global tree of devices exposed as a filesystem known as
devfs
, which is routed to most components as /dev
. When
a driver adds a device node, it has the option of adding
a "file" into devfs
. Then this file in devfs
allows other components
in the system to talk to the driver. For instance, an audio driver may add
a speaker device node and the audio driver wants to make sure that other
components can use this node to output audio to the speaker. To accomplish
this, the audio driver adds (or exposes) a devfs
node
for the speaker so that it appears as /dev/class/audio/<random_number>
in the system.
For more details, see the Set up devfs in a DFv2 driver guide.
What is not implemented in DFv2 that was available in DFv1?
If your DFv1 driver calls the load_firmware()
function
in the DDK library, you need to implement your own since an equivalent
function is not available in DFv2. However, this is expected to be
simple to implement.
What has changed in the bind rules in DFv2?
DFv2 nodes contain additional node properties generated from their FIDL service offers.
However, it is unlikely that you will need to modify bind rules when migrating an existing DFv1 driver to DFv2.
What has changed in logging in DFv2?
DFv2 drivers cannot use the zxlogf()
function or any debug library
that wraps or uses this function. zxlogf()
is defined in
//src/lib/ddk/include/lib/ddk/debug.h
and is removed from the
dependencies in DFv2. Drivers migrating to DFv2 need to
stop using this library and other libraries
that depend on it.
However, a new compatibility library, which is only
available in the Fuchsia source tree (fuchsia.git
) environment, is
now added to allow DFv2 drivers to use DFv1-style logging.
What has changed in inspect in DFv2?
DFv1 drivers use driver-specific inspect functions to create and update
driver-maintained metrics. For instance, in DFv1 the
DeviceAddArgs::set_inspect_vmo()
function is called to indicate the
VMO that the driver uses for inspect. In DFv2, however, we can just
create an inspect::ComponentInspector
object.
What do dispatchers do in DFv2?
A FIDL file generates templates and data types for a client-and-server pair. Between these client and server ends is a channel, and the dispatchers at each end fetch data from the channel. For more information on dispatchers, see Driver dispatcher and threads.
What are some issues with the new threading model when migrating a DFv1 driver to DFv2?
FIDL calls in DFv2 are not on a single thread basis and are asynchronous
by design (although you can make them synchronous by adding .sync()
to FIDL calls or using fdf::WireSyncClient
). Drivers are generally
discouraged from making synchronous calls because they can block other
tasks from running. (However, if necessary, a driver can create a
dispatcher with the FDF_DISPATCHER_OPTION_ALLOW_SYNC_CALLS
option,
which is only supported for
synchronized dispatchers.)
Given the differences in the threading models between Banjo (DFv1) and FIDL (DFv2), you'll need to decide which kind of FIDL call (that is, synchronous or asynchronous) you want to use while migrating. If your original code is designed around the synchronous nature of Banjo and is hard to unwind to make it all asynchronous, then you may want to consider using the synchronous version of FIDL at first (which, however, may result in performance degradation for the time being). Later, you can revisit these calls and optimize them into using synchronous calls.
What has changed in testing drivers in DFv2?
The mock_ddk
library, which is used in driver unit tests, is
specific to DFv1. New testing libraries are now
available for DFv2 drivers.
Should I fork my driver into a DFv2 version while working on migration?
Forking an existing driver for migration depends on the complexity of the driver. In general, it is recommended to avoid forking a driver because it could end up creating more work. However, for larger drivers, it may make sense to fork the driver into a DFv2 version so that you can gradually land migration changes in smaller patches.
You can fork a driver by adding a new driver component in the GN args
and use a flag to decide between the DFv1 or DFv2 version. This
example CL demonstrates how a DFv2 fork
of the msd-arm-mali
driver was added.
What are some recommended readings?
The DFv2 concept docs on fuchsia.dev and this
Gerrit change from the previous DFv1
Intel WiFi driver migration (the
pcie-iwlwifi-driver.cc
file
contains most of the new APIs).