本頁面提供相關指示、最佳做法和範例,說明如何將 DFv1 驅動程式中的各種服務 (除了 DDK 介面) 更新為 DFv2。
設定相容性裝置伺服器
如果 DFv1 驅動程式庫與其他尚未遷移至 DFv2 的 DFv1 驅動程式通訊,您就需要使用相容性墊片,讓現已是 DFv2 的驅動程式庫能夠與系統中的其他 DFv1 驅動程式通訊。如要進一步瞭解如何在 DFv2 驅動程式庫中設定及使用這個相容性墊片,請參閱「在 DFv2 驅動程式中設定相容性裝置伺服器」指南。
使用 DFv2 服務探索功能
進行驅動程式庫遷移作業時,您可能會遇到下列三種情況中的一或多種情況,其中兩個驅動程式會建立 FIDL 連線 (以 child driver -> parent driver
格式):
- 情境 1:DFv2 驅動程式庫 -> DFv2 驅動程式庫
- 情境 2:DFv1 驅動程式庫 -> DFv2 驅動程式庫
- 情境 3:DFv2 驅動程式庫 -> DFv1 驅動程式庫
情境 1 是 DFv2 驅動程式的標準情況 (這個範例顯示新的 DFv2 語法)。如要在這種情況下更新驅動程式,請參閱下方的「DFv2 驅動程式庫轉換為 DFv2 驅動程式」一節。
情境 2 和 3 較為複雜,因為 DFv1 驅動程式會在 DFv2 環境中包裝在相容性墊片中。不過,兩者之間的差異如下:
在情境 2中,這個 Gerrit 變更顯示了一種方法,可將服務從 DFv2 父項公開至 DFv1 子項。
在情境 3中,驅動程式庫會連線至父項驅動程式庫的相容性墊片提供的
fuchsia_driver_compat::Service::Device
通訊協定,並透過此通訊協定呼叫ConnectFidl()
方法,連線至實際的通訊協定 (如需瞭解範例,請參閱此 Gerrit 變更)。
如要在情境 2 或 3下更新驅動程式,請參閱下方的「從 DFv1 驅動程式庫轉換至 DFv2 驅動程式庫 (含相容性墊片)」一節。
DFv2 驅動程式庫到 DFv2 驅動程式庫
如要讓其他 DFv2 駕駛人能夠發現你的驅動程式庫服務,請執行下列操作:
更新驅動程式的
.fidl
檔案。DFv2 中的通訊協定探索功能需要為驅動程式通訊協定新增
service
欄位,例如:library fuchsia.example; @discoverable @transport("Driver") protocol MyProtocol { MyMethod() -> (struct { ... }); }; service Service { my_protocol client_end:MyProtocol; };
更新子驅動程式庫。
DFv2 驅動程式可以透過與 FIDL 服務相同的方式連線至通訊協定,例如:
incoming()->Connect<fuchsia_example::Service::MyProtocol>();
您也需要更新元件資訊清單 (
.cml
) 檔案,才能使用驅動程式庫執行階段服務,例如:use: [ { service: "fuchsia.example.Service" }, ]
更新父項驅動程式庫。
父項驅動程式庫必須使用
fdf::DriverBase
的outgoing()
函式,才能取得fdf::OutgoingDirectory
物件。請注意,您必須使用服務,而非通訊協定。如果驅動程式未使用fdf::DriverBase
,您必須自行建立並提供fdf::OutgoingDirectory
。接著,您需要將執行階段服務新增至傳出目錄。以下範例是繼承自
fdf::DriverBase
類別的驅動程式庫:zx::status<> Start() override { auto protocol = [this]( fdf::ServerEnd<fuchsia_example::MyProtocol> server_end) mutable { // bindings_ is a class field with type fdf::ServerBindingGroup<fuchsia_example::MyProtocol> bindings_.AddBinding( dispatcher()->get(), std::move(server_end), this, fidl::kIgnoreBindingClosure); }; fuchsia_example::Service::InstanceHandler handler( {.my_protocol = std::move(protocol)}); auto status = outgoing()->AddService<fuchsia_wlan_phyimpl::Service>(std::move(handler)); if (status.is_error()) { return status.take_error(); } return zx::ok(); }
更新子節點的
NodeAddArgs
,加入執行階段服務的商品,例如:auto offers = std::vector{fdf::MakeOffer2<fuchsia_example::Service>(arena, name)}; fidl::WireSyncClient<fuchsia_driver_framework::Node> node(std::move(node())); auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) .name(arena, “example_node”) .offers2(offers) .Build(); zx::result controller_endpoints = fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>(); ZX_ASSERT(controller_endpoints.is_ok()); auto result = node_->AddChild( args, std::move(controller_endpoints->server), {});
同樣地,請更新父項驅動程式庫程式的元件資訊清單 (
.cml
) 檔案,以提供執行階段服務,例如:capabilities: [ { service: "fuchsia.example.Service" }, ], expose: [ { service: "fuchsia.example.Service", from: "self", }, ],
DFv1 驅動程式庫轉換為 DFv2 驅動程式庫 (搭配相容性墊片)
如要讓其他 DFv1 駕駛人能夠探索您的 DFv2 驅動程式庫服務,請執行下列操作:
更新 DFv1 驅動程式。
您必須按照上述「從 DFv2 驅動程式庫程式轉換為 DFv2 驅動程式」一節所述的方式,更新 DFv1 驅動程式的元件資訊清單 (
.cml
) 檔案,例如:未成年驅動程式庫:
{ include: [ "//sdk/lib/driver_compat/compat.shard.cml", "inspect/client.shard.cml", "syslog/client.shard.cml", ], program: { runner: "driver", compat: "driver/child-driver-name.so", bind: "meta/bind/child-driver-name.bindbc", colocate: "true", }, use: [ { service: "fuchsia.example.Service" }, ], }
父項驅動程式庫:
{ include: [ "//sdk/lib/driver_compat/compat.shard.cml", "inspect/client.shard.cml", "syslog/client.shard.cml", ], program: { runner: "driver", compat: "driver/parent-driver-name.so", bind: "meta/bind/parent-driver-name.bindbc", }, capabilities: [ { service: "fuchsia.example.Service" }, ], expose: [ { service: "fuchsia.example.Service", from: "self", }, ], }
更新 DFv2 驅動程式庫。
以下範例顯示從 DFv2 父項公開至 DFv1 子項的服務方法:
fit::result<fdf::NodeError> AddChild() { fidl::Arena arena; auto offer = fdf::MakeOffer2<ft::Service>(kChildName); // Set the properties of the node that a driver will bind to. auto property = fdf::MakeProperty(1 /*BIND_PROTOCOL */, bind_fuchsia_test::BIND_PROTOCOL_COMPAT_CHILD); auto args = fdf::NodeAddArgs{ { .name = std::string(kChildName), .properties = std::vector{std::move(property)}, .offers2 = std::vector{std::move(offer)}, } }; // Create endpoints of the `NodeController` for the node. auto endpoints = fidl::CreateEndpoints<fdf::NodeController>(); if (endpoints.is_error()) { return fit::error(fdf::NodeError::kInternal); } auto add_result = node_.sync()->AddChild(fidl::ToWire(arena, std::move(args)), std::move(endpoints->server), {});
(來源:
root-driver.cc
)
更新其他驅動程式的元件資訊清單
如要完成將 DFv1 驅動程式庫遷移至 DFv2,您不僅需要更新目標驅動程式庫的元件資訊清單 (.cml
) 檔案,也可能需要更新與現有 DFv2 驅動程式庫互動的其他驅動程式的元件資訊清單檔案。
請完成下列步驟:
請更新葉狀驅動程式 (也就是沒有子項驅動程式) 的元件資訊清單,並加入下列變更:
- 從
include
欄位中移除//sdk/lib/driver/compat/compat.shard.cml
。 - 將
program.compat
欄位替換為program.binary
。
- 從
更新執行下列工作內容的其他驅動程式的元件資訊清單:
- 存取核心
args
。 - 建立複合裝置。
- 偵測重新啟動、關機或重新繫結呼叫。
- 使用 Banjo 通訊協定與其他駕駛人通訊。
- 存取或轉寄來自父項驅動程式庫的中繼資料。
- 與 DFv1 驅動程式庫通訊,該驅動程式會繫結至由驅動程式新增的節點。
針對這些驅動程式,請更新其元件資訊清單,並加入下列變更:
將部分
use
功能從compat.shard.cml
複製到元件資訊清單,例如:use: [ { protocol: [ "fuchsia.boot.Arguments", "fuchsia.boot.Items", "fuchsia.driver.framework.CompositeNodeManager", "fuchsia.system.state.SystemStateTransition", ], }, { service: "fuchsia.driver.compat.Service" }, ],
將
program.runner
欄位設為driver
,例如:program: { runner: "driver", binary: "driver/compat.so", },
- 存取核心
從 DFv2 驅動程式庫公開 devfs 節點
如要從 DFv2 驅動程式庫公開 devfs
節點,您必須將 device_args
成員新增至 NodeAddArgs
。特別是,您必須指定類別名稱並實作連接器,這可以利用 Connector
程式庫簡化,例如:
zx::result connector = devfs_connector_.Bind(dispatcher());
if (connector.is_error()) {
return connector.take_error();
}
auto devfs =
fuchsia_driver_framework::wire::DevfsAddArgs::Builder(arena).connector(
std::move(connector.value()));
auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
.name(arena, name)
.devfs_args(devfs.Build())
.Build();
(來源:parent-driver.cc
)
如需更多資訊,請參閱 DFv2 驅動程式庫程式碼研究室中的「驅動程式庫」。另外,請參閱程式碼研究室中提到的 ExportToDevfs
方法實作項目。
如要進一步瞭解 devfs
設定程序,請參閱「在 DFv2 驅動程式中設定 devfs」指南。
使用調度器
調度器會從 FIDL 用戶端和伺服器之間的管道擷取資料。根據預設,這個管道的 FIDL 呼叫為非同步。
如要在 DFv2 中為驅動程式引入非同步功能,請參閱以下建議:
fdf::Dispatcher::GetCurrent()
方法會提供驅動程式庫執行的預設調度器 (請參閱這個aml-ethernet
驅動程式庫範例)。建議您盡可能單獨使用這個預設調度器。請考慮使用多個調度器,原因包括但不限於:
驅動程式需要平行處理才能提升效能。
驅動程式想要執行封鎖作業 (因為它是舊版驅動程式庫或非 Fuchsia 驅動程式庫,且已移植至 Fuchsia),因此需要在封鎖期間處理更多工作。
如果需要多個調度器,
fdf::Dispatcher::Create()
方法可以為驅動程式建立新的調度器。不過,您必須在預設調度器上呼叫這個方法 (例如在Start()
鉤子中呼叫),讓驅動程式代管程序瞭解屬於驅動程式的其他調度器。在 DFv2 中,您不需要手動關閉調度器。這些會在
PrepareStop()
和Stop()
呼叫之間關閉。
如要進一步瞭解如何遷移驅動程式庫,以便使用多個調度器,請參閱「更新 DFv1 驅動程式庫,以便使用非預設調度器」一節 (位於「從 Banjo 遷移至 FIDL」短語中)。
使用 DFv2 檢查
如要在 DFv2 中設定由驅動程式庫程式維護的檢查指標,您可以使用 fdf::DriverBase::inspector()
提供的 ComponentInspector
:
inspect::Node& root = inspector().root();
如果要使用自訂檢查器,請先呼叫 fdf::DriverBase::InitInspectorExactlyOnce(inspector)
,再存取 inspector()
方法。
DFv2 檢查不需要將 inspect::Inspector
的 VMO 傳遞至驅動程式庫架構。
DFv2 駕駛員的檢查畫面會顯示歸因於驅動程式庫 (因為這是「一般」元件)。不過,DFv2 驅動程式的別名不穩定,因此在針對驅動程式庫編寫隱私權選取器時,請使用萬用字元和名稱篩選器來參照特定驅動程式庫。例如:
bootstrap/*-drivers*:[name=sysmem]root
如要在偵錯期間存取驅動程式庫檢查功能,您可以使用所有一般工具,例如
ffx inspect show --name sysmem "bootstrap/*-drivers*:root"
或
ffx inspect show --component sysmem.cm
(選用) 實作您自己的 load_firmware 方法
如果您的 DFv1 驅動程式庫會呼叫 DDK 程式庫中的 load_firmware()
函式,您就需要實作此函式的專屬版本,因為 DFv2 中沒有對等的函式。
這個函式應可輕鬆實作。您必須手動從路徑取得備援 VMO。如需範例,請參閱這個 Gerrit 異動。
(選用) 使用由 FIDL 服務產品產生的節點屬性
DFv2 節點包含從父項提供的 FIDL 服務產生的節點屬性。
舉例來說,在「父項驅動程式 (伺服器)」範例中,父項驅動程式庫會新增名為 "parent"
的節點,並提供 fidl.examples.EchoService
的服務。在 DFv2 中,繫結至此節點的驅動程式庫可以為該 FIDL 服務節點屬性設定繫結規則,例如:
using fidl.examples.echo;
fidl.examples.echo.Echo == fidl.examples.echo.Echo.ZirconTransport;
詳情請參閱 FIDL 教學課程網頁中的「產生的繫結程式庫」一節。
將單元測試更新為 DFv2
mock_ddk
程式庫 (用於單元測試,用於測試驅動程式庫和裝置生命週期) 是 DFv1 專屬。新的 DFv2 測試架構 (請參閱這項 Gerrit 變更) 會透過 TestEnvironment
類別,為 DFv2 驅動程式提供模擬的 FIDL 伺服器。
您可以使用下列程式庫進行 DFv2 驅動程式的單元測試:
-
TestNode
:這個類別會實作fuchsia_driver_framework::Node
通訊協定,可提供給驅動程式庫來建立子節點。測試也會使用這個類別,存取驅動程式庫建立的子項。TestEnvironment
:覆蓋OutgoingDirectory
物件的包裝函式,可做為測試中驅動程式庫傳入命名空間的備用 VFS (虛擬檔案系統)。DriverUnderTest
:這個類別是測試中驅動程式庫的 RAII (Resource Acquisition Is Initialization) 包裝函式。DriverRuntime
:這個類別是 RAII 包裝函式,可用於受管理的驅動程式庫執行階段執行緒集區。
//sdk/lib/driver/testing/cpp/driver_runtime.h
TestSynchronizedDispatcher
:此類別是驅動程式庫調度器的 RAII 包裝函式。
下列程式庫可能有助於編寫驅動程式庫單元測試:
//src/devices/bus/testing/fake-pdev/fake-pdev.h
– 這個輔助程式庫會實作pdev
FIDL 通訊協定的假版本。
最後,以下範例單元測試涵蓋不同的設定和測試案例:
//sdk/lib/driver/component/cpp/tests/driver_base_test.cc
:這個檔案包含驅動程式庫測試可用的不同執行緒模型範例。//sdk/lib/driver/component/cpp/tests/driver_fidl_test.cc
- 這個檔案示範如何使用驅動程式庫傳輸和 Zircon 傳輸的傳入和傳出 FIDL 服務,以及devfs
。
其他資源
以下是一些 DFv2 驅動程式範例:
本節提及的所有 Gerrit 變更:
- [iwlwifi] 為 iwlwifi 驅動程式進行 Dfv2 遷移
- [compat-runtime-test] Migrate off usage of DeviceServer
- [msd-arm-mali] 新增 DFv2 版本
- [sdk][驅動程式庫][testing] 新增測試程式庫
本節提及的所有原始碼檔案:
//examples/drivers/transport/zircon/v2/parent-driver.cc
//sdk/fidl/fuchsia.driver.framework/topology.fidl
//sdk/lib/driver/component/cpp/driver_base.h
//sdk/lib/driver/component/cpp/tests/driver_base_test.cc
//sdk/lib/driver/component/cpp/tests/driver_fidl_test.cc
//sdk/lib/driver/compat/cpp/banjo_server.h
//sdk/lib/driver/compat/cpp/banjo_client.h
//sdk/lib/driver/compat/cpp/device_server.h
//sdk/lib/driver/testing/cpp/driver_runtime.h
//src/connectivity/wlan/testing/wlantap-driver/wlantap-driver.cc
//src/devices/bus/testing/fake-pdev/fake-pdev.h
//src/devices/tests/v2/compat-runtime/root-driver.cc
//src/lib/ddk/include/lib/ddk/device.h
//src/lib/ddk/include/lib/ddk/driver.h
本節提及的所有說明文件頁面:
- Banjo
- 驅動程式和節點
- 駕駛人通訊
- 驅動程式和節點
- 驅動程式調度器和執行緒
- Drivers
- 建立複合式節點
- 公開驅動程式庫
- Fuchsia 元件檢查總覽
- 模擬 DDK 遷移
- 拆解序列的範例 (取自「驅動程式庫」)
- 父項驅動程式 (伺服器) (取自 FIDL 教學課程)
- 產生的繫結程式庫 (取自 FIDL 教學課程)