本指南將逐步說明如何建立最小的 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_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
欄位不得重複。
編寫繫結規則
如要編寫驅動程式的繫結規則,請按照下列步驟操作:
為驅動程式庫建立新的繫結規則檔案 (
.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 = "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
)建立新的 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_LOG
巨集,例如:
FDF_LOG(INFO, "Starting SimpleDriver")
除了使用 FDF_LOG
巨集之外,您也可以使用 Fauchsia 的結構化記錄器程式庫 (structured_logger.h
),該程式庫使用 FDF_SLOG
巨集來列印記錄。
如要使用 DFv2 驅動程式的結構化記錄檔,請按照下列步驟操作:
加入以下標頭:
#include <lib/driver/logging/cpp/structured_logger.h>
使用
FDF_SLOG
巨集列印記錄,例如:FDF_SLOG(ERROR, "Failed to add child", KV("status", result.status_string()));
新增子節點
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;
};
驅動程式可使用 DriverStartArgs
物件中的 node()
值或 DriverBase
類別的 node()
函式,連線至 Node
通訊協定。
除了使用 Node
通訊協定以外,您還需要建立 NodeController
端點。AddChild()
方法需要 fuchsia.driver.framework NodeController
通訊協定的伺服器端。另一方面,驅動程式庫會負責儲存與保留用戶端的通訊協定端。如果此用戶端端已取消分配,驅動程式庫架構會移除該子節點。
如要使用 Node
通訊協定新增子節點,請執行下列步驟:
在 DFv2 驅動程式庫中建立
NodeAddArgs
物件,該物件會採用下列引數:type NodeAddArgs = resource table { /// Name of the node. 1: name NodeName; 2: offers vector<fuchsia.component.decl.Offer>:MAX_OFFER_COUNT; 3: symbols vector<NodeSymbol>:MAX_SYMBOL_COUNT; 4: properties NodePropertyVector; 5: devfs_args DevfsAddArgs; };
(資料來源:
topology.fidl
)name
是子節點的名稱。offers
是父項為子節點提供的功能。您可以使用node_adds_args
程式庫中的MakeOffer()
函式建立這些變數。symbols
是要提供給驅動程式庫的函式。不過,DFv2 驅動程式會忽略此變數。properties
是子項節點屬性,用於決定哪些驅動程式庫會繫結至子項節點 (詳情請參閱繫結規則教學課程)。您可以使用node_adds_args
程式庫中的MakeProperty()
函式建立節點屬性。- 如果子節點需要存取
devfs
,就必須提供devfs_args
。
以下範例會建立
NodeAddArgs
物件:fidl::Arena arena; auto properties = std::vector{fdf::MakeProperty(arena, bind_fuchsia_test::TEST_CHILD, "simple")}; auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) .name(arena, "simple_child") .properties(arena, std::move(properties)) .Build();
更新驅動程式庫
DriverBase
類別,新增 FIDL 用戶端物件。以下範例使用
fidl::WireSyncClient
物件:class SimpleDriver : public fdf::DriverBase { <...> private: <...> fidl::WireSyncClient<fuchsia_driver_framework::NodeController> child_controller_; };
這項設定可讓您將用戶端端儲存於驅動程式庫。
使用
fidl::CreateEndpoints
函式建立伺服器和用戶端端點,例如:zx::result controller_endpoints = fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>(); ZX_ASSERT_MSG(controller_endpoints.is_ok(), "Failed to create endpoints: %s", controller_endpoints.status_string());
將用戶端繫結至 FIDL 用戶端物件,例如:
child_controller_.Bind(std::move(endpoints->client));
您現在可以使用
Node
物件連線至Node
伺服器。連線至
Node
伺服器並呼叫AddChild()
方法,例如:fidl::WireResult result = fidl::WireCall(node())->AddChild(args, std::move(controller_endpoints->server), {});
範例一起完成所有步驟,如下所示:
void SimpleDriver::Start(fdf::StartCompleter completer) { fidl::Arena arena; auto properties = std::vector{fdf::MakeProperty(arena, bind_fuchsia_test::TEST_CHILD, "skeleton")}; auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) .name(arena, "skeleton_child") .properties(arena, std::move(properties)) .Build(); auto controller_endpoints = fidl::Endpoints<fuchsia_driver_framework::NodeController>::Create(); controller_.Bind(std::move(controller_endpoints.client)); fidl::WireResult result = fidl::WireCall(node())->AddChild(args, std::move(controller_endpoints.server), {}); if (!result.ok()) { FDF_LOG(ERROR, "Failed to add child %s", result.status_string()); return completer(result.status()); } completer(zx::ok()); }
清理驅動程式庫
如果 DFv2 驅動程式需要在停止前執行刪除作業 (例如停止執行緒),您就必須覆寫並實作其他 DriverBase
方法:PrepareStop()
和 Stop()
系統會在驅動程式庫 fdf
調度器關閉且驅動程式庫取消配置前,先呼叫 PrepareStop()
函式。因此,驅動程式庫需要在驅動程式庫程式的調度工具關閉前,實作 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 驅動程式庫具有尚未遷移至 DFv2 的子系 DFv1 驅動程式,您必須使用相容性填充碼,讓 DFv2 驅動程式庫能夠與其他 DFv1 驅動程式通訊。詳情請參閱「在 DFv2 驅動程式設定 compat 裝置伺服器」指南。