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" },
],
}