教程:在驱动程序之间传递元数据

本指南介绍了如何使用元数据库将元数据从一个驱动程序传递到另一个驱动程序。元数据是驱动程序创建的任何任意数据,应可供绑定到其子节点的驱动程序访问。通常,驱动程序可以创建自己的 FIDL 协议来传递元数据,不过,元数据库提供了一些功能,可让您使用更少的代码在驱动程序之间传递元数据。

如需查看驱动程序发送、检索和转发元数据的示例,请点击此处

元数据定义

首先,必须将元数据类型定义为 FIDL 类型并添加 @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;
};

系统将使用 fuchsia.driver.metadata/Service FIDL 服务在驱动程序的传出命名空间中提供此元数据。驱动程序的传出命名空间中的 FIDL 服务的名称不会是 fuchsia.driver.metadata.Service。而是 FIDL 类型的可序列化名称。只有在为 FIDL 类型添加了 @serializable 注解时,系统才会创建可序列化名称。

FIDL 库的 build 目标将定义为:

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

发送元数据

初始设置

假设我们有一个驱动程序想要向其子级发送元数据:

#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);

其组件清单如下所示:

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

其 build 目标定义如下:

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.
}

发送流程

为了让此驱动程序向其子驱动程序发送元数据,它需要创建 fdf_metadata::MetadataServer 类的实例,通过调用其 fdf_metadata::MetadataServer::SetMetadata() 方法设置元数据,通过调用其 fdf_metadata::MetadataServer::Serve() 方法将元数据提供给驱动程序的传出目录,并将由 fdf_metadata::MetadataServer::MakeOffer() 创建的元数据服务器的优惠传递给子节点:


// 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());

    // The metadata server's offer must be provided to the child node.
    std::vector<fuchsia_driver_framework::Offer> offers{
      metadata_server_.MakeOffer()};

    zx::result child = AddChild("child", {}, offers);
    if (child.is_error()) {
      return child.take_error();
    }

    return zx::ok();
  }

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

您可以在 fdf_metadata::MetadataServer::Serve() 之前或之后多次调用 fdf_metadata::MetadataServer::SetMetadata(),元数据服务器将提供最新的元数据实例。如果子驱动程序在尝试检索元数据之前未调用 fdf_metadata::MetadataServer::SetMetadata(),则会检索元数据失败。

需要更新 driver build 目标,以添加元数据库和用于定义元数据类型的 FIDL 库:

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

公开元数据服务

最后,驱动程序需要在其组件清单中声明并公开 fuchsia.examples.metadata.Metadata FIDL 服务:

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

从技术层面讲,没有 fuchsia.examples.metadata.Metadata FIDL 服务。在底层,fdf_metadata::MetadataServer 会提供 fuchsia.driver.metadata/Service FIDL 服务,以便发送元数据。不过,fdf_metadata::MetadataServer 不会像普通 FIDL 服务那样以 fuchsia.driver.metadata.Service 的名称提供此服务。而是以元数据类型的可序列化名称提供服务。在本例中,该值为 fuchsia.examples.metadata.Metadata。您可以在 C++ 字段 fuchsia_examples_metadata::Metadata::kSerializableName 中找到此名称。这样,接收驱动程序便可确定传递的是哪种类型的元数据。这意味着,即使该服务不存在,驱动程序也需要声明和公开 fuchsia.examples.metadata.Metadata FIDL 服务。

检索元数据

初始设置

假设我们有一个驱动程序想要从其父级驱动程序检索元数据:

#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);

其组件清单如下所示:

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

其 build 目标定义如下:

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.
}

检索流程

为了让驱动程序从其父级驱动程序检索元数据,驱动程序需要调用 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();
  }
};

需要更新 driver build 目标,使其依赖于元数据库和定义元数据类型的 FIDL 库:

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

使用元数据服务

最后,驱动程序需要在其组件清单中声明对 fuchsia.examples.metadata.Metadata FIDL 服务的使用:

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

转发元数据

初始设置

假设我们有一个驱动程序想要从其父级驱动程序检索元数据,并将这些元数据转发给其子级驱动程序:

#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);

其组件清单如下所示:

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

其 build 目标定义如下:

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.
}

正向处理

为了让驱动程序将元数据从其父级驱动程序转发到其子级驱动程序,它需要创建 fdf_metadata::MetadataServer 类的实例,通过调用其 fdf_metadata::MetadataServer::ForwardMetadata() 方法设置元数据,通过调用其 fdf_metadata::MetadataServer::Serve() 方法将元数据提供给驱动程序的传出目录,并将由 fdf_metadata::MetadataServer::MakeOffer() 创建的元数据服务器的优惠传递给子节点:

// 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());

    // The metadata server's offer must be provided to the child node.
    std::vector<fuchsia_driver_framework::Offer> offers{
      metadata_server_.MakeOffer()};

    zx::result child = AddChild("child", {}, offers);
    if (child.is_error()) {
      return child.take_error();
    }

    return zx::ok();
  }

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

请注意,fdf_metadata::MetadataServer::ForwardMetadata() 不会检查父级驱动程序在调用 fdf_metadata::MetadataServer::ForwardMetadata() 后是否更改了其提供的元数据。驱动程序必须再次调用 fdf_metadata::MetadataServer::ForwardMetadata() 才能纳入更改。

需要更新 driver build 目标,使其依赖于定义元数据类型的元数据库和 FIDL 库:

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

公开和使用元数据服务

最后,驱动程序需要在其组件清单中声明、使用和公开 fuchsia.examples.metadata.Metadata FIDL 服务:

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