編寫最小的 DFv2 驅動程式庫

本指南將逐步說明建立最小 DFv2 驅動程式庫的相關步驟。

本指南中的指示是以 架構驅動程式,其中提供基本實作 ,在 Fuchsia 系統中建構、載入及註冊新的 DFv2 驅動程式庫。

步驟如下:

  1. 建立驅動程式庫標頭檔案
  2. 建立驅動程式庫來源檔案
  3. 新增驅動程式庫匯出巨集
  4. 建立建構檔案
  5. 寫入繫結規則
  6. 建立驅動程式庫元件

如需更多 DFv2 相關功能,請參閱其他工作

建立驅動程式庫標頭檔案

如要建立 DFv2 驅動程式庫的標頭檔案,請按照下列步驟操作:

  1. 為驅動程式庫建立新的標頭檔案 (.h),例如 skeleton_driver.h)。

  2. 在標頭檔案中加入下列介面:

    #include <lib/driver/component/cpp/driver_base.h>
    
  3. DriverBase 類別新增介面。 例如:

    #include <lib/driver/component/cpp/driver_base.h>
    
    namespace skeleton {
    
    class SkeletonDriver : public fdf::DriverBase {
     public:
      SkeletonDriver(fdf::DriverStartArgs start_args, fdf::UnownedSynchronizedDispatcher driver_dispatcher);
    
      // Called by the driver framework to initialize the driver instance.
      zx::result<> SkeletonDriver::Start() override;
    };
    
    }  // namespace skeleton
    

    (資料來源:skeleton_driver.h)

建立驅動程式庫來源檔案

如要實作 DriverBase 類別的基本方法, :

  1. 建立驅動程式庫的新來源檔案 (.cc),例如 skeleton_driver.cc)。

  2. 納入為驅動程式庫建立的標頭檔案,例如:

    #include "skeleton_driver.h"
    
  3. 實作類別的基本方法,例如:

    #include "skeleton_driver.h"
    
    namespace skeleton {
    
    SkeletonDriver::SkeletonDriver(fdf::DriverStartArgs start_args,
                               fdf::UnownedSynchronizedDispatcher driver_dispatcher)
        : DriverBase("skeleton_driver", std::move(start_args),
              std::move(driver_dispatcher)) {
    }
    
    zx::result<> SkeletonDriver::Start() {
      return zx::ok();
    }
    
    }  // namespace skeleton
    

    (資料來源:skeleton_driver.cc)

    此驅動程式庫建構函式必須傳遞驅動程式庫名稱 (例如 "skeleton_driver"start_argsdriver_dispatcher 都移至 DriverBase 類別。

新增驅動程式庫匯出巨集

如要新增驅動程式庫匯出巨集,請按照下列步驟操作:

  1. 在驅動程式庫來源檔案中,納入下列標頭檔案:

    #include <lib/driver/component/cpp/driver_export.h>
    
  2. 在 驅動程式庫來源檔案的底部:

    FUCHSIA_DRIVER_EXPORT(skeleton::SkeletonDriver);
    

    例如:

    #include <lib/driver/component/cpp/driver_base.h>
    #include <lib/driver/component/cpp/driver_export.h>
    
    #include "skeleton_driver.h"
    
    namespace skeleton {
    
    SkeletonDriver::SkeletonDriver(fdf::DriverStartArgs start_args,
                               fdf::UnownedSynchronizedDispatcher driver_dispatcher)
        : DriverBase("skeleton_driver", std::move(start_args),
              std::move(driver_dispatcher)) {
    }
    
    zx::result<> SkeletonDriver::Start() {
      return zx::ok();
    }
    
    }  // namespace skeleton
    
    FUCHSIA_DRIVER_EXPORT(skeleton::SkeletonDriver);
    

    (資料來源:skeleton_driver.cc)

建立建構檔案

如要為驅動程式庫建立建構檔案,請按照下列步驟操作:

  1. 建立新的 BUILD.gn 檔案。
  2. 加入下列程式碼以匯入驅動程式庫建構規則:

    import("//build/drivers.gni")
    
  3. 為驅動程式庫新增目標,例如:

    fuchsia_driver("driver") {
      output_name = "skeleton_driver"
      sources = [ "skeleton_driver.cc" ]
      deps = [
        "//sdk/lib/driver/component/cpp",
        "//src/devices/lib/driver:driver_runtime",
      ]
    }
    

    (資料來源:BUILD.gn)

    所有驅動程式的 output_name 欄位均不得重複。

寫入繫結規則

如要編寫驅動程式的繫結規則,請按照下列步驟操作:

  1. 為驅動程式庫建立新的繫結規則檔案 (.bind) (例如 skeleton_driver.bind)。meta

  2. 新增基本繫結規則,例如:

    using gizmo.example;
    gizmo.example.TEST_NODE_ID == "skeleton_driver";
    

    (資料來源:skeleton_driver.bind)

  3. BUILD.gn 檔案中,加入下列程式碼以匯入 繫結建構規則:

    import("//build/bind/bind.gni")
    
  4. BUILD.gn 檔案中,為驅動程式庫的繫結規則新增目標。 例如:

    driver_bind_rules("bind") {
      rules = "meta/skeleton.bind"
      bind_output = "skeleton_driver.bindbc"
      deps = [ "//examples/drivers/bind_library:gizmo.example" ]
    }
    

    (資料來源:BUILD.gn)

    所有驅動程式的 bind_output 欄位均不得重複。

建立驅動程式庫元件

如要為驅動程式庫建立 Fuchsia 元件,請按照下列步驟操作:

  1. meta 中建立新的元件資訊清單檔案 (.cml) 目錄內 (例如 skeleton_driver.cml)。

  2. 必須包含下列元件資料分割:

    {
        include: [
            "inspect/client.shard.cml",
            "syslog/client.shard.cml",
        ],
    }
    
  3. 請使用下列格式新增驅動程式庫 program 資訊:

    {
        program: {
            runner: "driver",
            binary: "driver/<OUTPUT_NAME>.so",
            bind: "meta/bind/<BIND_OUTPUT>",
        },
    }
    

    binary 欄位必須與output_name BUILD.gn 檔案的 fuchsia_driver 目標,以及 bind 欄位必須與 driver_bind_rules 目標中的 bind_output 相符, 例如:

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

    (資料來源:skeleton_driver.cml)

  4. 建立新的 JSON 檔案,提供元件資訊 (例如 component-info.json)。meta

  5. 以 JSON 格式新增驅動程式庫元件資訊,例如:

    {
        "short_description": "Driver Framework example for a skeleton DFv2 driver",
        "manufacturer": "",
        "families": [],
        "models": [],
        "areas": [
            "DriverFramework"
        ]
    }
    

    (資料來源:component-info.json)

  6. BUILD.gn 檔案中,加入下列程式碼以匯入元件 建構規則:

    import("//build/components.gni")
    
  7. BUILD.gn 檔案中,為驅動程式庫元件新增目標,例如:

    fuchsia_driver_component("component") {
      component_name = "skeleton"
      manifest = "meta/skeleton.cml"
      deps = [
         ":bind",
         ":driver"
      ]
      info = "component-info.json"
    }
    

    (資料來源:BUILD.gn)

    請參閱下列欄位規則:

    • manifest 欄位設為驅動程式庫 .cml 檔案的位置。
    • info 欄位設為驅動程式庫元件的位置 資訊 JSON 檔案
    • deps 陣列設為包含 fuchsia_driverBUILD.gn 檔案中的 driver_bind_rules 目標。

您現在可以在 Fuchsia 系統中建構、載入及註冊這個 DFv2 驅動程式庫

其他工作

本節提供您可在最小 DFv2 中加入的其他功能 驅動程式庫:

新增記錄檔

根據預設,如要列印 DFv2 驅動程式庫的記錄,請使用 FDF_LOG 巨集。 範例:

FDF_LOG(INFO, "Starting SimpleDriver")

除了使用 FDF_LOG 巨集之外,您也可以使用 Fuchsia 的結構化 Logger 程式庫 (structured_logger.h),後者會使用 FDF_SLOG 巨集。

如要使用 DFv2 驅動程式的結構化記錄,請按照下列步驟操作:

  1. 加入下列標頭:

    #include <lib/driver/logging/cpp/structured_logger.h>
    
  2. 使用 FDF_SLOG 巨集輸出記錄,例如:

    FDF_SLOG(ERROR, "Failed to add child", KV("status", result.status_string()));
    

新增子節點

DFv2 驅動程式可使用以下 Node 通訊協定新增子節點: fuchsia.driver.framework FIDL 程式庫:

open protocol Node {
    flexible AddChild(resource struct {
        args NodeAddArgs;
        controller server_end:NodeController;
        node server_end:<Node, optional>;
    }) -> () error NodeError;
};

為了方便起見,在啟動期間,驅動程式庫架構會提供 透過 DriverBase 繫結節點的 Node 通訊協定至 DFv2 驅動程式。 驅動程式隨時可以存取節點用戶端,在其上建立子節點。 不過,直接使用這個 FIDL 程式庫需要設定包含 建立 FIDL 管道組合,並建構 NodeAddArgs 資料表。 因此 DriverBase 類別會提供一組輔助函式,以建立 新增子節點(若要瞭解這些輔助工具,請參閱 driver_base.h file.)

DFv2 驅動程式庫可新增的節點類型有兩種:「非擁有」和「擁有」。 非擁有節點和自有節點之間的主要差異在於 決定是否要參與駕駛人比對程序

驅動程式庫程式架構會嘗試找出與 非自有節點,以便將驅動程式庫繫結至節點。一旦成功找到驅動程式庫成功 繫結至節點,繫結的驅動程式庫就會成為節點的擁有者。 另一方面,擁有的節點不會參與比對,因為驅動程式庫 建立節點的 Pod 已經是擁有者

DriverBase 輔助函式

驅動程式目前繫結節點的用戶端儲存在 DriverBase 物件。如此一來,驅動程式庫就能使用 DriverBase 類別的 使用 AddChild()AddOwnedChild() 函式將子節點新增至這個節點。

不過,如要使用這些 DriverBase 輔助函式,節點不得 。如果節點被移出,或您的目標節點不是 驅動程式庫目前繫結的節點 (亦即大子項節點); 您需要使用 add_child.h 檔案。這些方法與 DriverBase 輔助函式,但可用來將子項新增至節點 而不只是 DriverBase 物件所能觸及的地方,而是提供正確的父項 做為目標

最後,如果記錄錯誤發生,這些輔助函式會負責處理記錄錯誤。 因此驅動程式庫不需要記錄

建立非擁有的節點

如要建立非自有節點,驅動程式庫可以使用 DriverBase::AddChild() 輔助程式 函式。這些函式有兩種:一種可讓您提供 DevfsAddArgs 和其他不需要。使用這些函式 並用於尋找相符節點之上空屬性 驅動程式。兩者的傳回結果都是 NodeController 通訊協定的用戶端。 這些物件可由驅動程式庫保留,或安全捨棄。

下列程式碼範例會在驅動程式庫繫結節點下建立非從屬節點:

// Add a child node.
auto properties = std::vector{fdf::MakeProperty(bind_fuchsia_test::TEST_CHILD, "simple")};
zx::result child_result = AddChild(child_name, properties, compat_server_.CreateOffers2());
if (child_result.is_error()) {
  return child_result.take_error();
}

child_controller_.Bind(std::move(child_result.value()));

(資料來源:simple_driver.cc)

建立自有節點

如要建立自有節點,驅動程式庫可以使用 DriverBase::AddOwnedChild() 輔助程式 函式。又有兩種類型:一種允許 DevfsAddArgs,另一種類型允許 但它不會顯示這些函式不提供屬性引數,因為 節點不會參與驅動程式庫比對。兩者的傳回結果都是 OwnedChildNode 物件,內含為 NodeController 的用戶端 (該 安全捨棄) 以及用戶端的 Node 通訊協定 捨棄安全。駕駛必須在 Node 用戶端保持連線狀態, 希望擁有的節點留在系統中捨棄這個用戶端會導致驅動程式庫 移除節點

下列程式碼範例會使用 devfs 引數建立自有節點:

fuchsia_driver_framework::DevfsAddArgs devfs_args{{.connector = std::move(connector.value())}};
zx::result owned_child = AddOwnedChild("retriever", devfs_args);
if (owned_child.is_error()) {
  return owned_child.error_value();
}

child_node_.emplace(std::move(owned_child.value()));

(資料來源:retriever.cc)

清潔驅動程式庫

如果 DFv2 驅動程式庫需要在停止前執行拆解 (例如 停止執行緒),您就必須覆寫並實作 DriverBase 方法:PrepareStop()Stop()

系統會先呼叫 PrepareStop() 函式,再呼叫驅動程式庫的 fdf 調度工具 並撤離驅動程式庫位置因此,驅動程式 實作 PrepareStop()if 時,必須先執行特定作業 驅動程式庫的調度器關閉,例如:

void SimpleDriver::PrepareStop(fdf::PrepareStopCompleter completer) {
 // Teardown threads
  FDF_LOG(INFO, "Preparing to stop SimpleDriver");
  completer(zx::ok());
}

在這個驅動程式庫的所有調度器之後,會呼叫 Stop() 函式 例如:

void SimpleDriver::Stop() {
  FDF_LOG(INFO, "Stopping SimpleDriver");
}

新增相容裝置伺服器

如果 DFv2 驅動程式庫具有尚未遷移的 DFv1 驅動程式 DFv2 後,您必須使用相容性盾牌才能讓 DFv2 驅動程式庫進行通訊 與系統中的其他 DFv1 驅動程式整合詳情請參閱 在 DFv2 驅動程式中設定 Compat 裝置伺服器 指南。