本指南將逐步說明如何建立最簡單的 DFv2 驅動程式庫。
本指南中的操作說明是以最少的骨架驅動程式為基礎,提供在 Fuchsia 系統中建構、載入及註冊新 DFv2 驅動程式庫所需的最低實作項目。
步驟如下:
如要瞭解更多 DFv2 相關功能,請參閱「其他工作」。
建立驅動程式庫標頭檔案
如要為 DFv2 驅動程式庫建立標頭檔案,請按照下列步驟操作:
為驅動程式庫建立新的標頭檔案 (
.h),例如skeleton_driver.h。在標頭檔案中加入下列介面:
#include <lib/driver/component/cpp/driver_base.h>為
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 類別的基本方法,請執行下列操作:
為驅動程式庫建立新的來源檔案 (
.cc),例如skeleton_driver.cc。納入為驅動程式庫建立的標頭檔案,例如:
#include "skeleton_driver.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(來源:
skeleton_driver.cc)這個驅動程式庫建構函式需要將驅動程式庫名稱 (例如
"skeleton_driver")、start_args和driver_dispatcher傳遞至DriverBase類別。
新增驅動程式庫匯出巨集
如要新增驅動程式庫匯出巨集,請按照下列步驟操作:
在驅動程式庫來源檔案中,加入下列標頭檔案:
#include <lib/driver/component/cpp/driver_export.h>在驅動程式庫程式來源檔案底部新增下列巨集 (匯出驅動程式庫類別):
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)
建立建構檔案
如要為驅動程式庫建立建構檔案,請執行下列步驟:
- 建立新的
BUILD.gn檔案。 加入下列程式碼行,匯入驅動程式庫建構規則:
import("//build/drivers.gni")為驅動程式庫新增目標,例如:
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欄位不得重複。
撰寫繫結規則
如要為驅動程式編寫繫結規則,請按照下列步驟操作:
在
meta目錄中,為驅動程式庫建立新的繫結規則檔案 (.bind),例如skeleton_driver.bind。新增基本繫結規則,例如:
using gizmo.example; gizmo.example.TEST_NODE_ID == "skeleton_driver";(來源:
skeleton_driver.bind)在
BUILD.gn檔案中加入下列程式碼行,匯入繫結建構規則:import("//build/bind/bind.gni")在
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 元件,請按照下列步驟操作:
在
meta目錄中建立新的元件資訊清單檔案 (.cml),例如skeleton_driver.cml。包含下列元件分片:
{ include: [ "inspect/client.shard.cml", "syslog/client.shard.cml", ], }使用下列格式新增驅動程式庫
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)在
meta目錄中建立新的 JSON 檔案,提供元件資訊 (例如component-info.json)。以 JSON 格式新增驅動程式庫元件的資訊,例如:
{ "short_description": "Driver Framework example for a skeleton DFv2 driver", "manufacturer": "", "families": [], "models": [], "areas": [ "DriverFramework" ] }(來源:
component-info.json)在
BUILD.gn檔案中加入以下程式碼行,匯入元件建構規則:import("//build/components.gni")在
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_driver和driver_bind_rules目標。
- 將
您現在可以在 Fuchsia 系統中建構、載入及註冊這個 DFv2 驅動程式庫
其他工作
本節提供可新增至精簡 DFv2 驅動程式庫的其他功能:
新增記錄
如要列印 DFv2 驅動程式庫的記錄,請使用記錄巨集 (fdf::info、fdf::error、fdf::warn、fdf::debug、fdf::trace)。這些巨集會使用 std::format 樣式的字串格式。
如要使用 DFv2 驅動程式庫的記錄,請按照下列步驟操作:
請加入下列標頭:
#include <lib/driver/logging/cpp/logger.h>使用
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 驅動程式中設定相容裝置伺服器」指南。