Frequently asked questions for Banjo-to-FIDL migration

Before you start working on Banjo-to-FIDL migration, the frequently asked questions below can help you identify special conditions or edge cases that may apply to your driver.

What is the difference between Banjo and FIDL?

Banjo is a "transpiler" (similar to fidlc in FIDL). It converts an interface definition language (IDL) into target language specific files. FIDL is the inter-process communication (IPC) system for Fuchsia.

What is new in the driver runtime?

Drivers may talk to entities like the driver framework, other drivers and, non-driver components. Among the drivers in the same driver host, communication can occur using the FIDL bindings backed by the driver runtime transport primitives (that is, arena, channel and dispatcher). This new flavor of FIDL is called driver runtime FIDL. The driver runtime FIDL enables the drivers to realize the advantages that the new driver runtime provides, which includes stable ABI, thread safety, performance, driver author ergonomics, security and resilience. (For more information, see this RFC.)

Do I need to migrate my DFv1 driver to use the driver runtime?

When migrating a DFv1 driver from Banjo to FIDL, the driver runtime migration is needed only if your driver talks to other drivers co-located in the same driver host. (see How do I know if my driver talks to other drivers co-located in the same process? below).

One major advantage of migrating a driver to use the new driver runtime is that it changes the way that the driver communicates with co-located drivers, which is done by using the driver runtime FIDL. However, before you can start migrating a driver to use the driver runtime, if your driver is using Banjo or is already using FIDL but it's based on the original transport (Zircon primitives), you first need to make changes so that all communications in the driver take place using FIDL.

The good news is that the syntax of the driver runtime FIDL is similar to FIDL C++ wire bindings. The only difference is that there are some additional parameters in the function calls. And the namespace of some classes or primitives it uses is fdf instead of the original one (for example, fdf::WireServer), but FIDL wire binding types are still used in data transactions (for example, fidl::VectorView).

How do I know if my driver talks to other drivers co-located in the same process?

To find out whether your driver talks to other drivers co-located in the same process (in which case you need to migrate the driver to use the driver runtime), check the component manifest file (.cml) of the driver and look for the colocate field, for example:

program: {
  runner: "driver",
  compat: "driver/wlansoftmac.so",
  bind: "meta/bind/wlansoftmac.bindbc",
  colocate: "true",
  default_dispatcher_opts: [ "allow_sync_calls" ],
},
use: [
  { service: "fuchsia.wlan.softmac.Service" },
],

(Source: wlansoftmac.cml)

If the colocate field is true, this driver talks to other drivers co-located in the same process.

To check which drivers are co-located together, you can run the ffx driver list-hosts command, for example:

$ ffx driver list-hosts
...
Driver Host: 11040
  fuchsia-boot:///#meta/virtio_netdevice.cm
  fuchsia-boot:///network-device#meta/network-device.cm

Driver Host: 11177
  fuchsia-boot:///#meta/virtio_input.cm
  fuchsia-boot:///hid#meta/hid.cm
  fuchsia-boot:///hid-input-report#meta/hid-input-report.cm

Driver Host: 11352
  fuchsia-boot:///#meta/ahci.cm

...

Co-located drivers share the same driver host. In this example, the virtio_netdevice.cm and network-device.cm drivers are co-located.

When do I need to use dispatchers?

Creating threads in drivers is not encouraged. Instead, drivers need to use dispatchers. Dispatchers are virtual threads that get scheduled on the driver runtime thread pool. A FIDL file generates client and server templates and data types, and in the middle of these client and server ends is a channel where dispatchers at each end fetch data from the channel.

Dispatchers are specific to the driver runtime, which is independent of DFv1 and DFv2. Dispatchers are primarily used for FIDL communication, although they can have other uses such as waiting on interrupts from the kernel.

What are some issues with the new threading model when migrating a DFv1 driver from Banjo to FIDL?

FIDL calls 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.)

While migrating from Banjo to FIDL, the first problem you'll likely encounter is that, unlike FIDL, Banjo translates IDL (Interface Definition Language) into structures consisting of function pointers or data types. So in essence, bridging drivers with Banjo means bridging drivers with synchronous function calls.

Given the differences in the threading models between Banjo and FIDL, 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 asynchronous calls.

What changes do I need to make in my driver's unit tests after migrating from Banjo to FIDL?

If there are unit tests based on the Banjo APIs for your driver, you'll need to create a mock FIDL client (or server depending on whether you're testing server or client) in the test class. For more information, see Update the DFv1 driver's unit tests to use FIDL.