編寫最小的 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_cc_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. meta 目錄中,為驅動程式庫建立新的繫結規則檔案 (.bind),例如 skeleton_driver.bind

  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 欄位必須與 BUILD.gn 檔案 fuchsia_driver 目標中的 output_name 欄位相符,且 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. meta 目錄中建立新的 JSON 檔案,提供元件資訊 (例如 component-info.json)。

  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 陣列設為包含 BUILD.gn 檔案中的 fuchsia_driverdriver_bind_rules 目標。

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

其他工作

本節提供可新增至精簡 DFv2 驅動程式庫的其他功能:

新增記錄

如要列印 DFv2 驅動程式庫的記錄,請使用記錄巨集 (fdf::infofdf::errorfdf::warnfdf::debugfdf::trace)。這些巨集會使用 std::format 樣式的字串格式。

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

  1. 請加入下列標頭:

    #include <lib/driver/logging/cpp/logger.h>
    
  2. 使用 fdf:: 巨集列印記錄,例如:

    fdf::info("Starting SimpleDriver");
    fdf::error("Failed to add child: {}", status);
    

BUILD.gn 中,請務必依附於記錄程式庫: gn deps = [ "//sdk/lib/driver/logging/cpp", ]

新增子節點

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

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 檔案)。

DFv2 驅動程式可新增兩種節點:未擁有已擁有。 無主節點和自有節點的主要差異在於是否參與司機媒合程序

驅動程式庫架構會嘗試尋找符合未擁有節點屬性的驅動程式庫,以便將驅動程式庫繫結至節點。驅動程式庫與節點配對並繫結後,繫結的驅動程式庫就會成為節點擁有者。另一方面,由於建立節點的驅動程式庫已是擁有者,因此擁有的節點不會參與比對。

DriverBase 輔助函式

與驅動程式目前繫結的節點用戶端會儲存在 DriverBase 物件中。這可讓驅動程式庫使用 DriverBase 類別的 AddChild()AddOwnedChild() 函式,將子節點新增至這個節點。

不過,如要使用這些 DriverBase 輔助函式,節點不得移出驅動程式庫。如果節點移出,或目標節點不是驅動程式庫目前繫結的節點 (即孫子節點),則必須改用 add_child.h 檔案中提供的命名空間方法。這些方法與 DriverBase 輔助函式相同,但可用於將子項新增至 DriverBase 物件範圍以外的節點,方法是提供正確的父項節點用戶端做為目標。

最後,這些輔助函式會在發生錯誤時負責記錄錯誤,因此驅動程式庫不需要記錄任何內容。

建立未擁有的節點

如要建立未擁有的節點,驅動程式庫可以使用 DriverBase::AddChild() 輔助函式。這些函式可讓您在未擁有的節點上設定屬性,驅動程式庫架構會使用這些屬性尋找相符的驅動程式庫。兩者傳回的結果都是 NodeController 通訊協定的用戶端結尾,可由驅動程式庫保留或安全捨棄。

以下程式碼範例會在驅動程式庫繫結節點下建立未擁有的節點:

// Add a child node.
auto properties = std::vector{fdf::MakeProperty2(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() 輔助函式。由於擁有的節點不會參與驅動程式庫比對,因此這些函式不會提供屬性引數。兩者的傳回結果都是 OwnedChildNode 物件,其中包含 NodeController 的用戶端結尾 (可安全捨棄),以及 Node 通訊協定的用戶端結尾 (不可捨棄)。只要想保留擁有的節點,驅動程式就必須持有 Node 用戶端。捨棄這個用戶端會導致驅動程式庫架構移除節點。

下列程式碼範例會建立自有節點:

// Add an owned child node.
zx::result owned_child_result = AddOwnedChild("owned_child");
if (owned_child_result.is_error()) {
  fdf::error("Failed to add owned child: {}", owned_child_result);
  return owned_child_result.take_error();
}
owned_child_ = std::move(owned_child_result.value());

(來源:simple_driver.cc)

清理驅動程式庫

如果 DFv2 驅動程式在停止前需要執行終止作業 (例如停止執行緒),則必須覆寫並實作額外的 DriverBase 方法:PrepareStop()Stop()

系統會在驅動程式庫的 fdf 分派器關閉前呼叫 PrepareStop() 函式,並解除分配驅動程式庫。因此,驅動程式庫需要實作 PrepareStop()if,才能在驅動程式庫的調度器關閉前執行特定作業,例如:

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

系統會在屬於這個驅動程式庫的所有調度器關閉後呼叫 Stop() 函式,例如:

void SimpleDriver::Stop() {
  fdf::info("Stopping SimpleDriver");
}

新增相容裝置伺服器

如果 DFv2 驅動程式庫有尚未遷移至 DFv2 的 DFv1 後代驅動程式,您需要使用相容性墊片,讓 DFv2 驅動程式庫與系統中的其他 DFv1 驅動程式通訊。詳情請參閱「在 DFv2 驅動程式中設定相容裝置伺服器」指南。