教學課程:在驅動程式之間傳遞中繼資料

本指南說明如何使用 metadata 程式庫,將中繼資料從一個驅動程式庫傳遞至另一個驅動程式。中繼資料是驅動程式庫建立的任意資料,應可供繫結至子節點的驅動程式存取。一般來說,驅動程式可以建立自己的 FIDL 通訊協定來傳遞中繼資料,但 metadata 程式庫提供在驅動程式之間傳遞中繼資料的功能,且程式碼較少。

如要查看驅動程式傳送、擷取及轉送中繼資料的範例,請參閱這篇文章

中繼資料定義

首先,中繼資料類型必須定義為 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 程式庫的建構目標會定義如下:

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

建構目標定義如下:

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::Serve() 來提供中繼資料,並將 fdf_metadata::MetadataServer::CreateOffer() 建立的中繼資料伺服器提案傳遞至子項節點:


// 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 {
    // Serve the metadata to the driver's outgoing directory.
    fuchsia_examples_metadata::Metadata metadata({.test_property = "test value"});
    ZX_ASSERT(metadata_server_.Serve(*outgoing(), dispatcher(), metadata).is_ok());

    std::vector<fuchsia_driver_framework::Offer> offers;

    std::optional<fuchsia_driver_framework::Offer> metadata_offer =
      metadata_server.CreateOffer();

    // The metadata server should create an offer because it is serving
    // metadata.
    ZX_ASSERT(metadata_offer.has_value());

    // The metadata server's offer must be provided to the child node.
    offers.push_back(metadata_offer);

    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() 可能會多次呼叫,中繼資料伺服器會提供最新的中繼資料執行個體。

您需要更新 driver 建構目標,加入中繼資料程式庫和定義中繼資料類型的 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",
    },
}

建構目標定義如下:

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 建構目標,才能依附於中繼資料程式庫和定義中繼資料類型的 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",
    },
}

建構目標定義如下:

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::ForwardAndServe() 從父項擷取中繼資料並提供給驅動程式的外送目錄,然後將 fdf_metadata::MetadataServer::CreateOffer() 建立的中繼資料伺服器提案傳遞至子項節點。

如果 fdf_metadata::MetadataServer::ForwardAndServe() 無法從父項擷取中繼資料,不會傳回錯誤。而是會傳回 zx::ok(false),且不會放送任何內容。```cpp // Defines the fuchsia.examples.metadata types in C++.

包含

// Defines fdf::MetadataServer.

包含

class Forwarder : public fdf::DriverBase { public: zx::result<> Start() override { // Retrieve the metadata from the 驅動程式庫's parent and serve it to the // 驅動程式庫's 傳出目錄. zx::result is_serving = metadataserver.ForwardAndServe( *outgoing(), dispatcher(), incoming()).is_ok(); ZX_ASSERT(is_serving.is_ok());

std::vector<fuchsia_driver_framework::Offer> offers;
std::optional<fuchsia_driver_framework::Offer> metadata_offer =
  metadata_server_.CreateOffer();

// The metadata server may not be serving metadata if the driver failed to
// retrieve it from the driver's parent. In that case, the metadata server
// will not have an offer.
if (metadata_offer.has_value()) {
  // The metadata server's offer must be provided to the child node.
  offers.push_back(metadata_offer.value());
}

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 metadataserver; }; ```

請注意,fdf_metadata::MetadataServer::ForwardAndServe() 不會檢查父項驅動程式庫是否在呼叫 fdf_metadata::MetadataServer::ForwardAndServe() 後變更提供的中繼資料。駕駛人必須再次呼叫 fdf_metadata::MetadataServer::ForwardAndServe(),才能納入變更。

您需要更新 driver 建構目標,才能依附於中繼資料程式庫和定義中繼資料類型的 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" },
    ],
}