Set up devfs in a DFv2 driver

This guide provides instructions on how to set up devfs in a DFv2 driver, which enables the driver's services to be discovered by other Fuchsia components in the system. This guide uses an example DFv2 driver to walk through the devfs setup process.

The steps are:

  1. Identify the target FIDL protocol
  2. Add a ServerBindingGroup object
  3. Create a callback function
  4. Initialize a devfs connector
  5. Bind the devfs connector to a dispatcher
  6. Add the devfs connector to a child node

1. Identify the target FIDL protocol

In the DFv2 driver's source code, identify the target FIDL protocol (which can be inherited from fidl::Server<> or fidl::WireServer<>) for this devfs setup, for example:

class RetrieverDriver final : public fdf::DriverBase,
                              public fidl::Server<fuchsia_examples_metadata::Retriever> {

  ...

  // fuchsia.hardware.test/Child implementation.
  void GetMetadata(GetMetadataCompleter::Sync& completer) override {
    ...
  }
};

(Source: retriever-driver.cc)

The examples in this guide use the Retriever protocol defined in fuchsia.examples.metadata.fidl.

2. Add a ServerBindingGroup object

Add a ServerBindingGroup object to the driver class for binding the FIDL protocol, for example:

class RetrieverDriver final : public fdf::DriverBase,
                              public fidl::Server<fuchsia_examples_metadata::Retriever> {

...

fidl::ServerBindingGroup<fuchsia_examples_metadata::Retriever> bindings_;

(Source: retriever-driver.cc)

3. Create a callback function

Create a callback function, which will be invoked when a client tries to connect to the driver. In this callback function, add the binding from the ServerBindingGroup<> object to a ServerEnd object, for example:

class RetrieverDriver final : public fdf::DriverBase,
                              public fidl::Server<fuchsia_examples_metadata::Retriever> {
  ...

  void Serve(fidl::ServerEnd<fuchsia_examples_metadata::Retriever> request) {
    bindings_.AddBinding(dispatcher(), std::move(request), this, fidl::kIgnoreBindingClosure);
  }

(Source: retriever-driver.cc)

4. Initialize a devfs connector

Initialize a devfs connector object by passing the target FIDL protocol in step 1 and the callback function created in step 3, for example:

  driver_devfs::Connector<fuchsia_examples_metadata::Retriever> devfs_connector_{
      fit::bind_member<&RetrieverDriver::Serve>(this)};

(Source: retriever-driver.cc)

5. Bind the devfs connector to a dispatcher

Bind the devfs connector to the driver's dispatcher, for example:

zx::result connector = devfs_connector_.Bind(dispatcher());
    if (connector.is_error()) {
      FDF_SLOG(ERROR, "Failed to bind devfs connector.", KV("status", connector.status_string()));
      return connector.status_value();
    }

(Source: retriever-driver.cc)

6. Add the devfs connector to a child node

When adding a child node, pass the bound devfs connector from step 5 to the node's NodeAddArgs object. To do so, create a DevfsAddArgs object, which is defined in fuchsia.driver.framework/topology.fidl, and set the connector field to the devfs connector, for example:

fuchsia_driver_framework::DevfsAddArgs devfs_args{
  {.connector = std::move(connector.value())}
};

zx::result owned_child = AddOwnedChild("retriever", devfs_args);
if (owned_child.is_error()) {
  FDF_SLOG(ERROR, "Failed to add owned child.", KV("status", owned_child.status_string()));
  return owned_child.error_value();
}

(Source: retriever-driver.cc)