This page provides instructions, best practices, and examples related to
updating various services (other than the DDK interfaces) in DFv1 drivers
to DFv2.

## Set up the compat device server

If your DFv1 driver talks to other DFv1 drivers that haven't yet migrated
to DFv2, you need to use the compatibility shim to enable your now-DFv2 driver to
talk to other DFv1 drivers in the system. For more information on setting up and
using this compatibility shim in a DFv2 driver, see the
[Set up the compat device server in a DFv2 driver](https://fuchsia.dev/docs/development/drivers/migration/set-up-compat-device-server)
guide.

## Use the DFv2 service discovery

When working on driver migration, you will likely encounter one or more
of the following three scenarios in which two drivers establish a FIDL
connection (in `child driver -> parent driver` format):

- **Scenario 1**: DFv2 driver -\> DFv2 driver
- **Scenario 2**: DFv1 driver -\> DFv2 driver
- **Scenario 3**: DFv2 driver -\> DFv1 driver

**Scenario 1** is the standard case for DFv2 drivers (this
[example](http://cs/fuchsia/src/devices/tests/v2/runtime-protocol/) shows the new DFv2 syntax). To update
your driver under this scenario, see the
[DFv2 driver to DFv2 driver](https://fuchsia.dev/fuchsia-src/development/drivers/migration/migrate-from-dfv1-to-dfv2/update-other-services-to-dfv2.md.txt#dfv2-driver-to-dfv2-driver) section below.

**Scenario 2 and 3** are more complicated because the DFv1 driver is
wrapped in the compatibility shim in the DFv2 world. However,
the differences are:

- In **scenario 2** , this [Gerrit change](https://fuchsia-review.git.corp.google.com/c/fuchsia/+/920734) shows
  a method that exposes a service from the DFv2 parent to the DFv1 child.

- In **scenario 3** , the driver is connected to the
  `fuchsia_driver_compat::Service::Device` protocol provided by the
  compatibility shim of the parent driver, and the driver calls the
  `ConnectFidl()` method through this protocol to connect to the real
  protocol (for an example, see this
  [Gerrit change](https://fuchsia-review.git.corp.google.com/c/fuchsia/+/692243/47/src/connectivity/wlan/drivers/third_party/intel/iwlwifi/platform/pcie-iwlwifi-driver.cc#323)).

To update your driver under **scenario 2 or 3** , see the
[DFv1 driver to DFv2 driver (with compatibility shim)](https://fuchsia.dev/fuchsia-src/development/drivers/migration/migrate-from-dfv1-to-dfv2/update-other-services-to-dfv2.md.txt#dfv1-driver-to-dfv2-driver)
section below.

### DFv2 driver to DFv2 driver

To enable other DFv2 drivers to discover your driver's service,
do the following:

1. Update your driver's `.fidl` file.

   The protocol discovery in DFv2 requires adding `service` fields for
   the driver's protocols, for example:

       library fuchsia.example;

       @discoverable
       @transport("Driver")
       protocol MyProtocol {
           MyMethod() -> (struct {
               ...
           });
       };

       service Service {
       my_protocol client_end:MyProtocol;
       };

2. Update the child driver.

   DFv2 drivers can connect to protocols in the same way as FIDL services,
   for example:

   ```c++
   incoming()->Connect<fuchsia_example::Service::MyProtocol>();
   ```

   You also need to update the component manifest (`.cml`) file to use
   your driver runtime service, for example:

       use: [
           { service: "fuchsia.example.Service" },
       ]

3. Update the parent driver.

   Your parent driver needs to use the `fdf::DriverBase`'s `outgoing()` function to get the
   `fdf::OutgoingDirectory` object. Note that you must use services rather than protocols.
   If your driver isn't using `fdf::DriverBase` you must create and serve an
   `fdf::OutgoingDirectory` on your own.

   Then you need to add the runtime service to your outgoing directory.
   The example below is a driver that inherits from the `fdf::DriverBase`
   class:

   ```c++
   zx::status<> Start() override {
     auto protocol = [this](
         fdf::ServerEnd<fuchsia_example::MyProtocol> server_end) mutable {
       // bindings_ is a class field with type fdf::ServerBindingGroup<fuchsia_example::MyProtocol>
       bindings_.AddBinding(
         dispatcher()->get(), std::move(server_end), this, fidl::kIgnoreBindingClosure);
     };

     fuchsia_example::Service::InstanceHandler handler(
          {.my_protocol = std::move(protocol)});

     auto status =
           outgoing()->AddService<fuchsia_wlan_phyimpl::Service>(std::move(handler));
     if (status.is_error()) {
       return status.take_error();
     }

     return zx::ok();
   }
   ```

   Update the child node's [`NodeAddArgs`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.driver.framework/topology.fidl;l=81) to include an offer
   for your runtime service, for example:

   ```c++
   auto offers =
   std::vector{fdf::MakeOffer2\<fuchsia_example::Service\>(arena, name)};

   fidl::WireSyncClient<fuchsia_driver_framework::Node> node(std::move(node()));
     auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
                       .name(arena, "example_node")
                       .offers2(offers)
                       .Build();

     zx::result controller_endpoints =
          fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
     ZX_ASSERT(controller_endpoints.is_ok());

     auto result = node_->AddChild(
         args, std::move(controller_endpoints->server), {});
   ```

   Similarly, update the parent driver's component manifest (`.cml`) file to
   offer your runtime service, for example:

   ```c++
   capabilities: [
       { service: "fuchsia.example.Service" },
   ],

   expose: [
       {
           service: "fuchsia.example.Service",
           from: "self",
       },
   ],
   ```

### DFv1 driver to DFv2 driver (with compatibility shim)

To enable other DFv1 drivers to discover your DFv2 driver's service,
do the following:

1. Update the DFv1 drivers.

   You need to update the component manifest (`.cml`) files of the DFv1
   drivers in the same way as mentioned in the
   [DFv2 driver to DFv2 driver](https://fuchsia.dev/fuchsia-src/development/drivers/migration/migrate-from-dfv1-to-dfv2/update-other-services-to-dfv2.md.txt#dfv2-driver-to-dfv2-driver) section above,
   for example:
   - Child driver:

         {
             include: [
                 "//sdk/lib/driver_compat/compat.shard.cml",
                 "inspect/client.shard.cml",
                 "syslog/client.shard.cml",
             ],
             program: {
                 runner: "driver",
                 compat: "driver/child-driver-name.so",
                 bind: "meta/bind/child-driver-name.bindbc",
                 colocate: "true",
             },
             use: \[
         { service: "fuchsia.example.Service" },
         \],
         }

   - Parent driver:

         {
             include: [
                 "//sdk/lib/driver_compat/compat.shard.cml",
                 "inspect/client.shard.cml",
                 "syslog/client.shard.cml",
             ],
             program: {
                 runner: "driver",
                 compat: "driver/parent-driver-name.so",
                 bind: "meta/bind/parent-driver-name.bindbc",
             },
             capabilities: \[
         { service: "fuchsia.example.Service" },
         \],
         expose: \[
         {
         service: "fuchsia.example.Service",
         from: "self",
         },
         \],
         }

2. Update the DFv2 driver.

   The example below shows a method that exposes a service from the DFv2
   parent to the DFv1 child:

   ```c++
     fit::result<fdf::NodeError> AddChild() {
       fidl::Arena arena;

       auto offer = fdf::MakeOffer2<ft::Service>(kChildName);

       // Set the properties of the node that a driver will bind to.
       auto property =
           fdf::MakeProperty(1 /*BIND_PROTOCOL */, bind_fuchsia_test::BIND_PROTOCOL_COMPAT_CHILD);

       auto args = fdf::NodeAddArgs{
         {
           .name = std::string(kChildName),
           .properties = std::vector{std::move(property)},
           .offers2 = std::vector{std::move(offer)},
         }
       };

       // Create endpoints of the `NodeController` for the node.
       auto endpoints = fidl::CreateEndpoints<fdf::NodeController>();
       if (endpoints.is_error()) {
         return fit::error(fdf::NodeError::kInternal);
       }

       auto add_result = node_.sync()->AddChild(fidl::ToWire(arena, std::move(args)),
                                                std::move(endpoints->server), {});
   ```

   (Source: [`root-driver.cc`](https://source.corp.google.com/fuchsia/src/devices/tests/v2/compat-runtime/root-driver.cc))

## Update component manifests of other drivers

To complete the migration of a DFv1 driver to DFv2, you not only need
to update the component manifest (`.cml`) file of your target driver,
but you may also need to update the component manifest files of some
other drivers that interact with your now-DFv2 driver.

Do the following:

1. Update the component manifests of leaf drivers (that is, without
   child drivers) with the changes below:

   - Remove `//sdk/lib/driver/compat/compat.shard.cml` from the `include` field.
   - Replace the `program.compat` field with `program.binary`.
2. Update the component manifests of other drivers that perform the
   following tasks:

   - Access kernel `args`.
   - Create composite devices.
   - Detect reboot, shutdown, or rebind calls.
   - Talk to other drivers using the Banjo protocol.
   - Access metadata from a parent driver or forward it.
   - Talk to a DFv1 driver that binds to a node added by your driver.

   For these drivers, update their component manifest with the changes
   below:
   - Copy some of the `use` capabilities from
     [`compat.shard.cml`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/compat/compat.shard.cml) to the component manifest,
     for example:

         use: [
             {
                 protocol: [
                     "fuchsia.boot.Items",
                     "fuchsia.driver.framework.CompositeNodeManager",
                     "fuchsia.system.state.SystemStateTransition",
                 ],
             },
             { service: "fuchsia.driver.compat.Service" },
         ],

   - Set the `program.runner` field to `driver`, for example:

         program: {
             runner: "driver",
             binary: "driver/compat.so",
         },

## Use dispatchers

[Dispatchers](https://fuchsia.dev/docs/concepts/drivers/driver-dispatcher-and-threads) fetch data from a channel between
a FIDL client-and-server pair. By default, FIDL calls in this channel
are asynchronous.

For introducing asynchronization to drivers in DFv2, see the following
suggestions:

- The `fdf::Dispatcher::GetCurrent()` method gives you the default
  dispatcher that the driver is running on (see this
  [`aml-ethernet`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/connectivity/ethernet/drivers/aml-ethernet/aml-ethernet.cc;l=181) driver example). If possible, it is
  recommended to use this default dispatcher alone.

- Consider using multiple dispatchers for the following reasons
  (but not limited to):

  - The driver requires parallelism for performance.

  - The driver wants to perform blocking operations (because it is
    either a legacy driver or a non-Fuchsia driver being ported to
    Fuchsia) and it needs to handle more work while blocked.

- If multiple dispatchers are needed, the `fdf::Dispatcher::Create()`
  method can create new dispatchers for your driver. However, you must
  call this method on the default dispatcher (for example, call it
  inside the `Start()` hook) so that the driver host is aware of the
  additional dispatchers that belong to your driver.

- In DFv2, you don't need to shut down the dispatchers manually. They
  will be shut down between the `PrepareStop()` and `Stop()` calls.

For more details on migrating a driver to use multiple dispatchers,
see the
[Update the DFv1 driver to use non-default dispatchers](https://fuchsia.dev/docs/development/drivers/migration/migrate-from-banjo-to-fidl/convert-banjo-protocols-to-fidl-protocols#update-the-dfv1-driver-to-use-non-default-dispatchers)
section (in the [Migrate from Banjo to FIDL](https://fuchsia.dev/docs/development/drivers/migration/migrate-from-banjo-to-fidl/overview)
phrase).

## Use the DFv2 inspect

To set up driver-maintained [inspect](https://fuchsia.dev/docs/development/drivers/diagnostics/inspect) metrics in DFv2,
you may use the `ComponentInspector` provided by `fdf::DriverBase::inspector()`:

```c++
inspect::Node& root = inspector().root();
```

If a custom Inspector should be used, call `fdf::DriverBase::InitInspectorExactlyOnce(inspector)`
before accessing the `inspector()` method.

DFv2 inspect does not require passing the VMO of `inspect::Inspector` to the driver framework.

DFv2 drivers' Inspect will show up attributed to the driver (since it is a "normal" component).
The monikers of DFv2 drivers are not stable, however, so when writing privacy selectors against
a driver you should use wildcards and name filters to refer to a specific driver. For example,

    bootstrap/*-drivers*:[name=sysmem]root

To access a driver's Inspect during debugging, you can use all the normal tools, such as

    ffx inspect show --name sysmem "bootstrap/*-drivers*:root"

or

    ffx inspect show sysmem.cm

## (Optional) Implement your own load_firmware method

If your DFv1 driver calls the [`load_firmware()`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/lib/ddk/include/lib/ddk/driver.h;l=408)
function in the DDK library, you need to implement your own version
of this function because an equivalent function is not available in DFv2.

This function is expected to be simple to implement. You need to get
the backing VMO from the path manually. For an example, see this
[Gerrit change](https://fuchsia-review.git.corp.google.com/c/fuchsia/+/692243/47/src/connectivity/wlan/drivers/third_party/intel/iwlwifi/platform/pcie-iwlwifi-driver.cc#60).

## (Optional) Use the node properties generated from FIDL service offers

DFv2 nodes contain the node properties generated from the FIDL service
offers from their parents.

For instance, in the [Parent Driver (The Server)](https://fuchsia.dev/docs/development/drivers/tutorials/fidl-tutorial#parent_driver_the_server)
example, the parent driver adds a node called `"parent"` with a service
offer for `fidl.examples.echo.EchoService`. In DFv2, a driver that binds to this
node can have a bind rule for that FIDL service node property, for example:

    fuchsia.Service == "fidl.examples.echo.EchoService";

## Update unit tests to DFv2

The `mock_ddk` library (which is used in unit tests for testing
driver and device life cycle) is specific to DFv1. The new DFv2 test
framework (see this [Gerrit change](https://fuchsia-review.git.corp.google.com/c/fuchsia/+/770412)) makes
mocked FIDL servers available to DFv2 drivers through the `TestEnvironment`
class.

The following libraries are available for unit testing DFv2 drivers:

- [`//sdk/lib/driver/testing/cpp`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/testing/cpp/)

  - `TestNode` - This class implements the `fuchsia_driver_framework::Node`
    protocol, which can be provided to a driver to create child nodes. This
    class is also used by tests to access the child nodes that the driver
    has created.

  - `TestEnvironment` - A wrapper over an `OutgoingDirectory` object that
    serves as the backing VFS (virtual file system) for the incoming
    namespace of the driver under test.

  - `DriverUnderTest` - This class is a RAII
    ([Resource Acquisition Is Initialization](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)) wrapper
    for the driver under test.

  - `DriverRuntime` - This class is a RAII wrapper over the managed driver
    runtime thread pool.

- [`//sdk/lib/driver/testing/cpp/driver_runtime.h`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/testing/cpp/driver_runtime.h)

  - `TestSynchronizedDispatcher` - This class is a RAII wrapper over the driver dispatcher.

The following library may be helpful for writing driver unit tests:

- [`//src/devices/bus/testing/fake-pdev/fake-pdev.h`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/devices/bus/testing/fake-pdev/fake-pdev.h) - This helper library implements a fake version of the `pdev` FIDL protocol.

Lastly, the following example unit tests cover different configurations and
test cases:

- [`//sdk/lib/driver/component/cpp/tests/driver_base_test.cc`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/component/cpp/tests/driver_base_test.cc) -
  This file contains examples of the different threading models that driver
  tests can have.

- [`//sdk/lib/driver/component/cpp/tests/driver_fidl_test.cc`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/component/cpp/tests/driver_fidl_test.cc) -
  This file demonstrates how to work with incoming and outgoing FIDL
  services for both driver transport and Zircon transport.

## Additional resources

Some DFv2 drivers examples:

- [`MagmaDriverBase`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/graphics/magma/lib/magma_service/sys_driver/magma_driver_base.h)
- [`WlanTapDriver`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/connectivity/wlan/testing/wlantap-driver/wlantap-driver.cc)
- [`AdcButtonsDriver`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/ui/input/drivers/adc-buttons/adc-buttons.h)

All the **Gerrit changes** mentioned in this section:

- [\[iwlwifi\] Dfv2 migration for iwlwifi driver](https://fuchsia-review.git.corp.google.com/c/fuchsia/+/692243)
- [\[compat-runtime-test\] Migrate off usage of DeviceServer](https://fuchsia-review.git.corp.google.com/c/fuchsia/+/920734)
- [\[msd-arm-mali\] Add DFv2 version](https://fuchsia-review.git.corp.google.com/c/fuchsia/+/853637)
- [\[sdk\]\[driver\]\[testing\] Add testing library](https://fuchsia-review.git.corp.google.com/c/fuchsia/+/770412)

All the **source code files** mentioned in this section:

- [`//examples/drivers/transport/zircon/v2/parent-driver.cc`](https://cs.opensource.google/fuchsia/fuchsia/+/main:examples/drivers/transport/zircon/v2/parent-driver.cc;l=93)
- [`//sdk/fidl/fuchsia.driver.framework/topology.fidl`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.driver.framework/topology.fidl;l=80)
- [`//sdk/lib/driver/component/cpp/driver_base.h`](https://source.corp.google.com/fuchsia/sdk/lib/driver/component/cpp/driver_base.h;l=70?q=driverbase&sq=package:fuchsia)
- [`//sdk/lib/driver/component/cpp/tests/driver_base_test.cc`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/component/cpp/tests/driver_base_test.cc)
- [`//sdk/lib/driver/component/cpp/tests/driver_fidl_test.cc`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/component/cpp/tests/driver_fidl_test.cc)
- [`//sdk/lib/driver/compat/cpp/banjo_server.h`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/compat/cpp/banjo_server.h)
- [`//sdk/lib/driver/compat/cpp/banjo_client.h`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/compat/cpp/banjo_client.h)
- [`//sdk/lib/driver/compat/cpp/device_server.h`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/compat/cpp/device_server.h;l=23?q=deviceserver&sq&ss=fuchsia/fuchsia)
- [`//sdk/lib/driver/testing/cpp/driver_runtime.h`](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/driver/testing/cpp/driver_runtime.h)
- [`//src/connectivity/wlan/testing/wlantap-driver/wlantap-driver.cc`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/connectivity/wlan/testing/wlantap-driver/wlantap-driver.cc)
- [`//src/devices/bus/testing/fake-pdev/fake-pdev.h`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/devices/bus/testing/fake-pdev/fake-pdev.h)
- [`//src/devices/tests/v2/compat-runtime/root-driver.cc`](https://source.corp.google.com/fuchsia/src/devices/tests/v2/compat-runtime/root-driver.cc)
- [`//src/lib/ddk/include/lib/ddk/device.h`](https://source.corp.google.com/fuchsia/src/lib/ddk/include/lib/ddk/device.h;l=77)
- [`//src/lib/ddk/include/lib/ddk/driver.h`](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/lib/ddk/include/lib/ddk/driver.h;l=408)

All the **documentation pages** mentioned in this section:

- [Drivers and nodes](https://fuchsia.dev/docs/concepts/drivers/drivers_and_nodes)
- [Driver communication](https://fuchsia.dev/docs/concepts/drivers/driver_communication)
- [Drivers and nodes](https://fuchsia.dev/docs/concepts/drivers/drivers_and_nodes)
- [Driver dispatcher and threads](https://fuchsia.dev/docs/concepts/drivers/driver-dispatcher-and-threads)
- [Drivers](https://fuchsia.dev/docs/concepts/drivers/README)
- [Create a composite node](https://fuchsia.dev/docs/development/drivers/developer_guide/create-a-composite-node)
- [Expose the driver capabilities](https://fuchsia.dev/docs/development/drivers/tutorials/sdk_build_driver/driver-service)
- [Fuchsia component inspection overview](https://fuchsia.dev/docs/development/drivers/diagnostics/inspect)
- [Parent Driver (The Server)](https://fuchsia.dev/docs/development/drivers/tutorials/fidl-tutorial#parent_driver_the_server) (from *FIDL tutorial*)

[](https://issues.fuchsia.dev/issues/new?component=1439230&template=1881640&description=Issue+on:+https://fuchsia.dev/docs/development/drivers/migration/migrate-from-dfv1-to-dfv2/update-other-services-to-dfv2.md "Report a bug or suggest improvements on this page") [](https://cs.opensource.google/fuchsia/fuchsia/+/main:docs/development/drivers/migration/migrate-from-dfv1-to-dfv2/update-other-services-to-dfv2.md "View the source code of this page") [](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/development/drivers/migration/migrate-from-dfv1-to-dfv2/update-other-services-to-dfv2.md&cloneRepo=fuchsia/fuchsia:refs/heads/main&fullPath=fuchsia/docs/development/drivers/migration/migrate-from-dfv1-to-dfv2/update-other-services-to-dfv2.md "Edit the source code of this page")