Passing metadata between drivers tutorial

This guide explains how to pass metadata from one driver to another using the metadata library. Metadata is any arbitrary data created by a driver that should be accessible to drivers bound to its child nodes. Normally, drivers can create their own FIDL protocol in order to pass metadata, however, the metadata library offers functionality to pass metadata between drivers with less code.

You can find an example of drivers sending, retrieving and forwarding metadata here.

Metadata definition

First, the type of metadata must be defined as a FIDL type and annotated with @serializable:

library fuchsia.examples.metadata;

// Type of the metadata to be passed.
// Make sure to annotate it with `@serializable`.
@serializable
type Metadata = table {
    1: test_property string:MAX;
};

This metadata will be served in the driver's outgoing namespace using the fuchsia.driver.metadata/Service FIDL service. The name of the FIDL service within the driver's outgoing namespace will not be fuchsia.driver.metadata.Service. Instead, it will be the serializable name of the FIDL type. The serializable name is only created if the FIDL type is annotated with @serializable.

The FIDL library's build target will be defined as the following:

fidl("fuchsia.examples.metadata") {
  sources = [ "fuchsia.examples.metadata.fidl" ]
}

Sending metadata

Initial setup

Let's say we have a driver that wants to send metadata to its children:

#include <lib/driver/component/cpp/driver_export.h>

class Sender : public fdf::DriverBase {
 public:
  Sender(fdf::DriverStartArgs start_args, fdf::UnownedSynchronizedDispatcher driver_dispatcher)
      : DriverBase("sender", std::move(start_args), std::move(driver_dispatcher)) {}
};

FUCHSIA_DRIVER_EXPORT(Sender);

It's component manifest is the following:

{
    include: [
        "inspect/client.shard.cml",
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/sender.so",
        bind: "meta/bind/sender.bindbc",
    },
}

It's build targets are defined as follows:

fuchsia_cc_driver("driver") {
  testonly = true
  output_name = "sender"
  sources = [
    "sender.cc",
  ]
  deps = [
    "//src/devices/lib/driver:driver_runtime",
  ]
}

fuchsia_driver_component("component") {
  testonly = true
  component_name = "sender"
  manifest = "meta/sender.cml"
  deps = [
    ":driver",
    ":bind", # Bind rules not specified in this tutorial.
  ]
  info = "sender.json" # Info not specified in this tutorial.
}

Send process

In order for this driver to send metadata to its child drivers, it will need to create an instance of the fdf_metadata::MetadataServer class, set the metadata by calling its fdf_metadata::MetadataServer::SetMetadata() method, and then serve the metadata to the driver's outgoing directory by calling its fdf_metadata::MetadataServer::Serve() method:


// Defines the fuchsia.examples.metadata types in C++.
#include <fidl/fuchsia.examples.metadata/cpp/fidl.h>

// Defines fdf::MetadataServer.
#include <lib/driver/metadata/cpp/metadata_server.h>

class Sender : public fdf::DriverBase {
 public:
  zx::result<> Start() override {
    // Set the metadata to be served.
    fuchsia_examples_metadata::Metadata metadata{{.test_property = "test value"}};
    ZX_ASSERT(metadata_server_.SetMetadata(std::move(metadata)).is_ok());

    // Serve the metadata to the driver's outgoing directory.
    ZX_ASSERT(metadata_server_.Serve(*outgoing(), dispatcher()).is_ok());

    return zx::ok();
  }

 private:
  // Responsible for serving metadata.
  fdf_metadata::MetadataServer<fuchsia_examples_metadata::Metadata> metadata_server_;
};

fdf_metadata::MetadataServer::SetMetadata() can be called multiple times, before or after fdf_metadata::MetadataServer::Serve(), and the metadata server will serve the latest metadata instance. A child driver will fail to retrieve the metadata if fdf_metadata::MetadataServer::SetMetadata() has not been called before it attempts to retrieve the metadata.

The driver build target will need to be updated to include the metadata library and the FIDL library that defines the metadata type: ``` fuchsia_cc_driver("driver") { testonly = true output_name = "sender" sources = [ "sender.cc", ] deps = [ "//src/devices/lib/driver:driver_runtime",

# This should be the fuchsia.examples.metadata FIDL library's C++ target.
":fuchsia.examples.metadata_cpp",

# This library contains `fdf::MetadataServer`.
"//sdk/lib/driver/metadata/cpp",

] } ```

Exposing the metadata service

Finally, the driver needs to declare and expose the fuchsia.examples.metadata.Metadata FIDL service in its component manifest:

{
    include: [
        "inspect/client.shard.cml",
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/sender.so",
        bind: "meta/bind/sender.bindbc",
    },
    capabilities: [
        { service: "fuchsia.examples.metadata.Metadata" },
    ],
    expose: [
        {
            service: "fuchsia.examples.metadata.Metadata",
            from: "self",
        },
    ],
}

Technically, there is no fuchsia.examples.metadata.Metadata FIDL service. Under the hood, fdf_metadata::MetadataServer serves the fuchsia.driver.metadata/Service FIDL service in order to send metadata. However, fdf_metadata::MetadataServer does not serve this service under the name fuchsia.driver.metadata.Service like a normal FIDL service would. Instead, the service is served under the metadata type's serializable name. In this case that is fuchsia.examples.metadata.Metadata. This name can be found in the C++ field fuchsia_examples_metadata::Metadata::kSerializableName. This allows the receiving driver to identify which type of metadata is being passed. This means that the driver needs to declare and expose the fuchsia.examples.metadata.Metadata FIDL service even though that service doesn't exist.

Retrieving metadata

Initial setup

Let's say we have a driver that wants to retrieve metadata from its parent driver:

#include <lib/driver/component/cpp/driver_export.h>

class Retriever : public fdf::DriverBase {
 public:
  Retriever(fdf::DriverStartArgs start_args, fdf::UnownedSynchronizedDispatcher driver_dispatcher)
      : DriverBase("retriever", std::move(start_args), std::move(driver_dispatcher)) {}
};

FUCHSIA_DRIVER_EXPORT(Retriever);

It's component manifest is the following:

{
    include: [
        "inspect/client.shard.cml",
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/retriever.so",
        bind: "meta/bind/retriever.bindbc",
    },
}

It's build targets are defined as follows:

fuchsia_cc_driver("driver") {
  testonly = true
  output_name = "retriever"
  sources = [
    "retriever.cc",
  ]
  deps = [
    "//src/devices/lib/driver:driver_runtime",
  ]
}

fuchsia_driver_component("component") {
  testonly = true
  component_name = "retriever"
  manifest = "meta/retriever.cml"
  deps = [
    ":driver",
    ":bind", # Bind rules not specified in this tutorial.
  ]
  info = "retriever.json" # Info not specified in this tutorial.
}

Retrieval process

In order for the driver to retrieve metadata from its parent driver, the driver needs to call fdf_metadata::GetMetadata():

// Defines the fuchsia.examples.metadata types in C++.
#include <fidl/fuchsia.examples.metadata/cpp/fidl.h>

// Defines fdf::GetMetadata().
#include <lib/driver/metadata/cpp/metadata.h>

class Retriever : public fdf::DriverBase {
 public:
  zx::result<> Start() override {
    zx::result<fuchsia_examples_metadata::Metadata> metadata =
      fdf_metadata::GetMetadata<fuchsia_examples_metadata::Metadata>(incoming());
    ZX_ASSERT(!metadata.is_error());

    return zx::ok();
  }
};

The driver build target will need to be updated to depend on the metadata library and the FIDL library that defines the metadata type:

fuchsia_cc_driver("driver") {
  testonly = true
  output_name = "retriever"
  sources = [
    "retriever.cc",
  ]
  deps = [
    "//src/devices/lib/driver:driver_runtime",

    # This should be the fuchsia.examples.metadata FIDL library's C++ target.
    ":fuchsia.examples.metadata_cpp",

    # This library contains `fdf::GetMetadata()`.
    "//sdk/lib/driver/metadata/cpp",
  ]
}

Using the metadata service

Finally, the driver will need to declare its usage of the fuchsia.examples.metadata.Metadata FIDL service in its component manifest:

{
    include: [
        "inspect/client.shard.cml",
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/retriever.so",
        bind: "meta/bind/retriever.bindbc",
    },
    use: [
        { service: "fuchsia.examples.metadata.Metadata" },
    ],
}

Forwarding metadata

Initial setup

Let's say we have a driver that wants to retrieve metadata from its parent driver and forward that metadata to its child drivers:

#include <lib/driver/component/cpp/driver_export.h>

class Forwarder : public fdf::DriverBase {
 public:
  Forwarder(fdf::DriverStartArgs start_args, fdf::UnownedSynchronizedDispatcher driver_dispatcher)
      : DriverBase("forwarder", std::move(start_args), std::move(driver_dispatcher)) {}
};

FUCHSIA_DRIVER_EXPORT(Forwarder);

It's component manifest is the following:

{
    include: [
        "inspect/client.shard.cml",
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/forwarder.so",
        bind: "meta/bind/forwarder.bindbc",
    },
}

It's build targets are defined as follows:

fuchsia_cc_driver("driver") {
  testonly = true
  output_name = "forwarder"
  sources = [
    "forwarder.cc",
  ]
  deps = [
    "//src/devices/lib/driver:driver_runtime",
  ]
}

fuchsia_driver_component("component") {
  testonly = true
  component_name = "forward"
  manifest = "meta/forward.cml"
  deps = [
    ":driver",
    ":bind", # Bind rules not specified in this tutorial.
  ]
  info = "forward.json" # Info not specified in this tutorial.
}

Forward process

In order for the driver to forward metadata from its parent driver to its child drivers, it will need to create an instance of the fdf_metadata::MetadataServer class, set the metadata by calling its fdf_metadata::MetadataServer::ForwardMetadata() method, and then serve the metadata to the driver's outgoing directory by calling its fdf_metadata::MetadataServer::Serve() method:

// Defines the fuchsia.examples.metadata types in C++.
#include <fidl/fuchsia.examples.metadata/cpp/fidl.h>

// Defines fdf::MetadataServer.
#include <lib/driver/metadata/cpp/metadata_server.h>

class Forwarder : public fdf::DriverBase {
 public:
  zx::result<> Start() override {
    // Set metadata using the driver's parent driver metadata.
    ZX_ASSERT(metadata_server_.ForwardMetadata(incoming()),is_ok());

    // Serve the metadata to the driver's outgoing directory.
    ZX_ASSERT(metadata_server_.Serve(*outgoing(), dispatcher()).is_ok());

    return zx::ok();
  }

 private:
  // Responsible for serving metadata.
  fdf_metadata::MetadataServer<fuchsia_examples_metadata::Metadata> metadata_server_;
};

It should be noted that fdf_metadata::MetadataServer::ForwardMetadata() does not check if the parent driver has changed what metadata it provides after fdf_metadata::MetadataServer::ForwardMetadata() has been called. The driver will have to call fdf_metadata::MetadataServer::ForwardMetadata() again in order to incorporate the change.

The driver build target will need to be updated to depend on the metadata library and FIDL library that defines the metadata type:

fuchsia_cc_driver("driver") {
  testonly = true
  output_name = "forwarder"
  sources = [
    "forwarder.cc",
  ]
  deps = [
    "//src/devices/lib/driver:driver_runtime",

    # This should be the fuchsia.examples.metadata FIDL library's C++ target.
    ":fuchsia.examples.metadata_cpp",

    # This library contains `fdf::MetadataServer`.
    "//sdk/lib/driver/metadata/cpp",
  ]
}

Exposing and using the metadata service

Finally, the driver will need to declare, use, and expose the fuchsia.examples.metadata.Metadata FIDL service in its component manifest:

{
    include: [
        "inspect/client.shard.cml",
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/forwarder.so",
        bind: "meta/bind/forwarder.bindbc",
    },
    capabilities: [
        { service: "fuchsia.examples.metadata.Metadata" },
    ],
    expose: [
        {
            service: "fuchsia.examples.metadata.Metadata",
            from: "self",
        },
    ],
    use: [
        { service: "fuchsia.examples.metadata.Metadata" },
    ],
}