將其他服務更新至 DFv2

本頁面提供操作說明、最佳做法和範例,說明如何將 DFv1 驅動程式中的各種服務 (DDK 介面除外) 更新至 DFv2。

設定相容裝置伺服器

如果 DFv1 驅動程式庫會與尚未遷移至 DFv2 的其他 DFv1 驅動程式通訊,您必須使用相容性填充碼,讓現在的 DFv2 驅動程式庫與系統中的其他 DFv1 驅動程式通訊。如要進一步瞭解如何在 DFv2 驅動程式庫中設定及使用這個相容性 shim,請參閱「在 DFv2 驅動程式中設定相容裝置伺服器」指南。

使用 DFv2 服務探索

進行驅動程式庫遷移時,您可能會遇到下列一或多種情況,其中兩個驅動程式會建立 FIDL 連線 (格式為 child driver -> parent driver):

  • 情境 1:DFv2 驅動程式庫 -> DFv2 驅動程式庫
  • 情境 2:DFv1 驅動程式庫 -> DFv2 驅動程式庫
  • 情境 3:DFv2 驅動程式庫 -> DFv1 驅動程式庫

情境 1 是 DFv2 驅動程式的標準案例 (這個範例顯示新的 DFv2 語法)。如要在這種情況下更新驅動程式,請參閱下方的「DFv2 驅動程式庫至 DFv2 驅動程式」一節。

情境 2 和 3 較為複雜,因為 DFv1 驅動程式會包裝在 DFv2 世界的相容性墊片中。不過,兩者有以下差異:

  • 在情境 2 中,這項 Gerrit 變更顯示了從 DFv2 父項向 DFv1 子項公開服務的方法。

  • 在情境 3 中,驅動程式庫會連線至父項驅動程式庫相容性填充碼提供的 fuchsia_driver_compat::Service::Device 通訊協定,並透過此通訊協定呼叫 ConnectFidl() 方法,連線至實際通訊協定 (如需範例,請參閱這項 Gerrit 變更)。

如要更新情境 2 或 3 的驅動程式,請參閱下方的「DFv1 驅動程式庫至 DFv2 驅動程式庫 (含相容性墊片)」一節。

DFv2 驅動程式庫到 DFv2 驅動程式庫

如要讓其他 DFv2 驅動程式能找到您的驅動程式服務,請按照下列步驟操作:

  1. 更新驅動程式的 .fidl 檔案。

    在 DFv2 中探索通訊協定時,需要為驅動程式庫的通訊協定新增 service 欄位,例如:

    library fuchsia.example;
    
    @discoverable
    @transport("Driver")
    protocol MyProtocol {
        MyMethod() -> (struct {
            ...
        });
    };
    
    service Service {
        my_protocol client_end:MyProtocol;
    };
    
  2. 更新子帳戶驅動程式庫。

    DFv2 驅動程式可以連線至通訊協定,方式與 FIDL 服務相同,例如:

    incoming()->Connect<fuchsia_example::Service::MyProtocol>();
    

    您也需要更新元件資訊清單 (.cml) 檔案,才能使用驅動程式庫執行階段服務,例如:

    use: [
        { service: "fuchsia.example.Service" },
    ]
    
  3. 更新父項驅動程式庫。

    父項驅動程式庫必須使用 fdf::DriverBase 的 outgoing() 函式,才能取得 fdf::OutgoingDirectory 物件。請注意,您必須使用服務,而非通訊協定。如果您的驅動程式庫未使用 fdf::DriverBase,您必須自行建立及放送 fdf::OutgoingDirectory。

    接著,您必須將執行階段服務新增至傳出目錄。以下範例是繼承自 fdf::DriverBase 類別的驅動程式庫:

    zx::status<> Start() override {
      auto protocol = [this](
          fdf::ServerEnd<fuchsia_example::MyProtocol> server_end) mutable {
        // bindings_ is a class field with type fdf::ServerBindingGroup<fuchsia_example::MyProtocol>
        bindings_.AddBinding(
          dispatcher()->get(), std::move(server_end), this, fidl::kIgnoreBindingClosure);
      };
    
      fuchsia_example::Service::InstanceHandler handler(
           {.my_protocol = std::move(protocol)});
    
      auto status =
            outgoing()->AddService<fuchsia_wlan_phyimpl::Service>(std::move(handler));
      if (status.is_error()) {
        return status.take_error();
      }
    
      return zx::ok();
    }
    

    更新子節點的 NodeAddArgs,加入執行階段服務的方案,例如:

    auto offers =
        std::vector{fdf::MakeOffer2<fuchsia_example::Service>(arena, name)};
    
    fidl::WireSyncClient<fuchsia_driver_framework::Node> node(std::move(node()));
      auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
                        .name(arena, "example_node")
                        .offers2(offers)
                        .Build();
    
      zx::result controller_endpoints =
           fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
      ZX_ASSERT(controller_endpoints.is_ok());
    
      auto result = node_->AddChild(
          args, std::move(controller_endpoints->server), {});
    

    同樣地,請更新父項驅動程式庫程式的元件資訊清單 (.cml) 檔案,提供執行階段服務,例如:

    capabilities: [
        { service: "fuchsia.example.Service" },
    ],
    
    expose: [
        {
            service: "fuchsia.example.Service",
            from: "self",
        },
    ],
    

DFv1 驅動程式庫到 DFv2 驅動程式庫 (含相容性墊片)

如要讓其他 DFv1 驅動程式探索 DFv2 驅動程式庫的服務,請按照下列步驟操作:

  1. 更新 DFv1 驅動程式。

    您需要更新 DFv1 驅動程式的元件資訊清單 (.cml) 檔案,方法與上述「DFv1 驅動程式庫遷移至 DFv2 驅動程式」一節所述相同,例如:

    • 兒童驅動程式庫:

      {
          include: [
              "//sdk/lib/driver_compat/compat.shard.cml",
              "inspect/client.shard.cml",
              "syslog/client.shard.cml",
          ],
          program: {
              runner: "driver",
              compat: "driver/child-driver-name.so",
              bind: "meta/bind/child-driver-name.bindbc",
              colocate: "true",
          },
          use: [
              { service: "fuchsia.example.Service" },
          ],
      }
      
    • 主要驅動程式庫:

      {
          include: [
              "//sdk/lib/driver_compat/compat.shard.cml",
              "inspect/client.shard.cml",
              "syslog/client.shard.cml",
          ],
          program: {
              runner: "driver",
              compat: "driver/parent-driver-name.so",
              bind: "meta/bind/parent-driver-name.bindbc",
          },
          capabilities: [
              { service: "fuchsia.example.Service" },
          ],
          expose: [
              {
                  service: "fuchsia.example.Service",
                  from: "self",
              },
          ],
      }
      
  2. 更新 DFv2 驅動程式庫。

    以下範例說明如何透過方法,將 DFv2 父項的服務提供給 DFv1 子項:

      fit::result<fdf::NodeError> AddChild() {
        fidl::Arena arena;
    
        auto offer = fdf::MakeOffer2<ft::Service>(kChildName);
    
        // Set the properties of the node that a driver will bind to.
        auto property =
            fdf::MakeProperty(1 /*BIND_PROTOCOL */, bind_fuchsia_test::BIND_PROTOCOL_COMPAT_CHILD);
    
        auto args = fdf::NodeAddArgs{
          {
            .name = std::string(kChildName),
            .properties = std::vector{std::move(property)},
            .offers2 = std::vector{std::move(offer)},
          }
        };
    
        // Create endpoints of the `NodeController` for the node.
        auto endpoints = fidl::CreateEndpoints<fdf::NodeController>();
        if (endpoints.is_error()) {
          return fit::error(fdf::NodeError::kInternal);
        }
    
        auto add_result = node_.sync()->AddChild(fidl::ToWire(arena, std::move(args)),
                                                 std::move(endpoints->server), {});
    

    (來源:root-driver.cc)

更新其他驅動程式的元件資訊清單

如要完成從 DFv1 驅動程式庫遷移至 DFv2 的作業,您不僅需要更新目標驅動程式庫的元件資訊清單 (.cml) 檔案,可能還需要更新其他一些驅動程式的元件資訊清單檔案,這些驅動程式會與您現在的 DFv2 驅動程式庫互動。

請完成下列步驟:

  1. 請更新葉節點驅動程式 (即沒有子項驅動程式) 的元件資訊清單,進行下列變更:

    • 從 include 欄位中移除 //sdk/lib/driver/compat/compat.shard.cml。
    • 將 program.compat 欄位替換為 program.binary。
  2. 更新執行下列工作的其他驅動程式元件資訊清單:

    • 存取核心 args。
    • 建立複合裝置。
    • 偵測重新啟動、關機或重新繫結呼叫。
    • 使用 Banjo 通訊協定與其他驅動程式通訊。
    • 存取上層驅動程式庫的中繼資料,或轉送中繼資料。
    • 與繫結至驅動程式新增節點的 DFv1 驅動程式庫通訊。

    請按照下列變更更新這些驅動程式的元件資訊清單:

    • 將 compat.shard.cml 的部分 use 功能複製到元件資訊清單,例如:

      use: [
          {
              protocol: [
                  "fuchsia.boot.Items",
                  "fuchsia.driver.framework.CompositeNodeManager",
                  "fuchsia.system.state.SystemStateTransition",
              ],
          },
          { service: "fuchsia.driver.compat.Service" },
      ],
      
    • 將 program.runner 欄位設為 driver,例如:

      program: {
          runner: "driver",
          binary: "driver/compat.so",
      },
      

使用調度工具

調度工具會從 FIDL 用戶端和伺服器配對之間的管道擷取資料。根據預設,這個管道中的 FIDL 呼叫是非同步。

如要在 DFv2 中為驅動程式導入非同步功能,請參閱下列建議:

  • fdf::Dispatcher::GetCurrent() 方法會提供驅動程式庫執行的預設調度器 (請參閱這個 aml-ethernet 驅動程式庫範例)。建議您盡可能單獨使用這個預設調度工具。

  • 基於下列原因 (但不限於),請考慮使用多個調度器:

    • 驅動程式需要平行處理,才能發揮效能。

    • 驅動程式想執行封鎖作業 (因為這是舊版驅動程式庫,或是移植到 Fuchsia 的非 Fuchsia 驅動程式),且封鎖時需要處理更多工作。

  • 如果需要多個調度工具,fdf::Dispatcher::Create() 方法可以為驅動程式建立新的調度工具。不過,您必須在預設的調度器上呼叫這個方法 (例如在 Start() 勾點內呼叫),這樣驅動程式代管程序才能知道屬於驅動程式的其他調度器。

  • 在 DFv2 中,您不必手動關閉調度工具。系統會在 PrepareStop() 和 Stop() 呼叫之間關閉這些執行緒。

如要進一步瞭解如何遷移驅動程式庫,以便使用多個調度工具,請參閱「更新 DFv1 驅動程式庫,以便使用非預設調度工具」一節 (位於「從 Banjo 遷移至 FIDL」一文)。

使用 DFv2 檢查

如要在 DFv2 中設定由驅動程式庫維護的 inspect 指標,可以使用 fdf::DriverBase::inspector() 提供的 ComponentInspector:

inspect::Node& root = inspector().root();

如要使用自訂檢查器,請先呼叫 fdf::DriverBase::InitInspectorExactlyOnce(inspector),再存取 inspector() 方法。

DFv2 檢查不需要將 inspect::Inspector 的 VMO 傳遞至驅動程式庫架構。

DFv2 驅動程式的 Inspect 會顯示為歸因於驅動程式庫 (因為它是「一般」元件)。不過,DFv2 驅動程式的代號並不穩定,因此針對驅動程式庫編寫隱私權選取器時,應使用萬用字元和名稱篩選器參照特定驅動程式庫。例如:

bootstrap/*-drivers*:[name=sysmem]root

如要在偵錯期間存取驅動程式庫檢查工具,可以使用所有一般工具,例如:

ffx inspect show --name sysmem "bootstrap/*-drivers*:root"

或

ffx inspect show sysmem.cm

(選用) 實作自己的 load_firmware 方法

如果 DFv1 驅動程式庫呼叫 DDK 程式庫中的 load_firmware() 函式,您需要自行實作這個函式,因為 DFv2 沒有對應的函式。

這個函式應該很容易實作。您必須手動從路徑取得支援 VMO。如需範例,請參閱這項 Gerrit 變更。

(選用) 使用 FIDL 服務供應項目產生的節點屬性

DFv2 節點包含從父項 FIDL 服務供應項目產生的節點屬性。

舉例來說,在「父項驅動程式 (伺服器)」範例中,父項驅動程式庫會新增名為 "parent" 的節點,並提供 fidl.examples.echo.EchoService 服務。在 DFv2 中,繫結至這個節點的驅動程式庫可以擁有該 FIDL 服務節點屬性的繫結規則,例如:

fuchsia.Service == "fidl.examples.echo.EchoService";

將單元測試更新至 DFv2

mock_ddk 程式庫 (用於測試驅動程式庫和裝置生命週期的單元測試) 僅適用於 DFv1。新的 DFv2 測試架構 (請參閱這項 Gerrit 變更) 可透過 TestEnvironment 類別,為 DFv2 驅動程式提供模擬 FIDL 伺服器。

下列程式庫可用於對 DFv2 驅動程式進行單元測試:

  • //sdk/lib/driver/testing/cpp

    • TestNode:這個類別會實作 fuchsia_driver_framework::Node 通訊協定,可提供給驅動程式庫建立子節點。測試也會使用這個類別,存取驅動程式庫建立的子節點。

    • TestEnvironment:OutgoingDirectory 物件的包裝函式,可做為受測驅動程式庫傳入命名空間的備份 VFS (虛擬檔案系統)。

    • DriverUnderTest:這個類別是受測驅動程式庫的 RAII (資源取得即初始化) 包裝函式。

    • DriverRuntime - 這個類別是受管理驅動程式庫執行階段執行緒集區的 RAII 包裝函式。

  • //sdk/lib/driver/testing/cpp/driver_runtime.h

    • TestSynchronizedDispatcher - 這個類別是驅動程式庫調度器的 RAII 包裝函式。

下列程式庫可能有助於編寫驅動程式庫單元測試:

最後,下列範例單元測試涵蓋不同的設定和測試案例:

其他資源

DFv2 驅動程式範例:

本節提及的所有 Gerrit 變更:

本節提及的所有原始碼檔案:

本節提及的所有說明文件頁面: