本页提供了与以下内容相关的说明、最佳做法和示例: 更新 DFv1 驱动程序中的各种服务(DDK 接口除外) 到 DFv2
设置兼容型设备服务器
如果您的 DFv1 驱动程序与尚未迁移的其他 DFv1 驱动程序通信 到 DFv2,您需要使用兼容性填充码,让现在的 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
协议提供的 父驱动程序的兼容性 shim,并且驱动程序会调用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 驱动程序。
您需要更新 DFv1 的组件清单 (
.cml
) 文件 添加方法,如 从 DFv2 驱动程序到 DFv2 驱动程序部分, 例如:子女司机:
{ 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 公开 Service 的方法 设置为 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 驱动程序交互的其他驱动程序。
请执行以下操作:
更新叶驱动程序的组件清单(即不使用 子驱动程序)进行如下更改:
- 从以下位置移除
//sdk/lib/driver/compat/compat.shard.cml
:include
字段。 - 将
program.compat
字段替换为program.binary
。
- 从以下位置移除
更新执行 以下任务:
- 访问内核
args
。 - 创建复合设备。
- 检测重新启动、关机或重新绑定调用。
- 使用 Banjo 协议与其他驱动程序交谈。
- 访问或转发来自家长驱动程序的元数据。
- 与绑定到您的驱动程序添加的节点的 DFv1 驱动程序对话。
对于这些驱动程序,请使用相应更改更新其组件清单 如下:
从该 API 中复制部分
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
)
如需了解详情,请参阅
在 API 中公开驱动程序功能
DFv2 驱动程序 Codelab。另请参阅此实现
此 Codelab 中提到的 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 中设置驱动程序维护的inspect指标,请执行以下操作:
您可以使用 fdf::DriverBase::inspector()
提供的 ComponentInspector
:
inspect::Node& root = inspector().root();
如果应使用自定义检查器,请调用 fdf::DriverBase::InitInspectorExactlyOnce(inspector)
然后才能使用 inspector()
方法。
DFv2 检查不需要将 inspect::Inspector
的 VMO 传递给驱动程序框架。
DFv2 驱动程序检查将归因于驱动程序(因为它属于“常规”组件)。 不过,DFv2 驱动程序的称呼并不稳定,因此在针对 DFv2 驱动程序编写隐私选择器时, 驱动程序,那么您应使用通配符和名称过滤器来引用特定驱动程序。例如,
bootstrap/*-drivers*:[name=sysmem]root
如需在调试期间访问驱动程序的“Inspect”(检查),您可以使用所有常规工具,例如
ffx inspect show "bootstrap/*-drivers*:[name=sysmem]root"
或
ffx inspect show --manifest sysmem.cm
(可选)实现您自己的 load_firmware 方法
如果您的 DFv1 驱动程序调用 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
- 此类是对 驱动程序调度程序。
以下库可能有助于编写驱动程序单元测试:
//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
- 此文件演示了如何使用传入和传出 FIDL 提供司机运输和 Zircon 运输服务,以及devfs
。
其他资源
以下是一些 DFv2 驱动程序示例:
本部分中提到的所有 Gerrit 更改如下:
- [iwlwifi] 针对 iwlwifi 驱动程序的 Dfv2 迁移
- [compat-runtime-test] 停止使用 DeviceServer
- [msd-arm-mali] 添加了 DFv2 版本
- [sdk][driver][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
本部分中提到的所有文档页面:
- 班卓琴
- 驱动程序和节点
- 驾驶员通信
- 驱动程序和节点
- 驱动程序调度程序和线程
- 驱动程序
- 复合节点
- 公开驱动程序功能
- Fuchsia 组件检查概览
- 模拟 DDK 迁移
- 拆解序列示例 (来自设备驱动程序生命周期)
- 家长驱动程序(服务器) (出自 FIDL 教程)
- 生成的绑定库 (出自 FIDL 教程)