本指南會逐步說明如何建立 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
)。meta
新增基本繫結規則,例如:
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
目錄 (例如skeleton_driver.cml
) 中建立新的元件資訊清單檔案 (.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
欄位必須與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
)建立新的 JSON 檔案,在
meta
目錄中提供元件資訊 (例如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
巨集之外,您也可以使用
Fuchsia 的結構化 Logger 程式庫
(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;
};
為方便這項操作,驅動程式架構會在啟動期間,透過 DriverBase
將已繫結節點的 Node
通訊協定用戶端提供給 DFv2 驅動程式。驅動程式隨時可以存取其節點用戶端,在節點上建立子節點。不過,直接使用這個 FIDL 程式庫需要設定,包括建立 FIDL 管道組合和建構 NodeAddArgs
表格。因此,DriverBase
類別提供一組輔助函式,方便您新增子節點。(如要查看這些輔助程式,請查看 driver_base.h
檔案)。
DFv2 驅動程式庫可新增的節點類型有兩種:「非擁有」和「擁有」。 非擁有節點和自有節點之間的主要差異在於 決定是否要參與駕駛人比對程序。
驅動程式庫程式架構會嘗試找出與 非自有節點,以便將驅動程式庫繫結至節點。一旦成功找到驅動程式庫成功 繫結至節點,繫結的驅動程式庫就會成為節點的擁有者。 另一方面,已擁有的節點不會參與比對,因為建立節點的駕駛人已是擁有者。
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-driver.cc
)
清理驅動程式
如果 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 裝置伺服器 指南。