本指南将介绍创建最小 DFv2 驱动程序所涉及的步骤。
本指南中的说明基于最小 骨架驱动程序,该驱动程序提供了在 Fuchsia 系统中构建、加载和注册新 DFv2 驱动程序所需的最低实现 。
具体步骤如下:
如需了解更多与 DFv2 相关的功能,请参阅其他任务。
创建驱动程序头文件
如需为 DFv2 驱动程序创建头文件,请执行以下操作:
为驱动程序创建一个新的头文件 (
.h)(例如skeleton_driver.h)。将以下接口添加到头文件中:
#include <lib/driver/component/cpp/driver_base2.h>为
DriverBase2类添加一个接口, 例如:#include <lib/driver/component/cpp/driver_base2.h> namespace skeleton { class SkeletonDriver : public fdf::DriverBase2 { public: SkeletonDriver(); // Called by the driver framework to initialize the driver instance. zx::result<> Start(fdf::DriverContext context) override; }; } // namespace skeleton(来源:
skeleton_driver.h)
创建驱动程序源文件
如需实现 DriverBase2 类的基本方法,请执行以下操作:
为驱动程序创建一个新的源文件 (
.cc)(例如skeleton_driver.cc)。添加为驱动程序创建的头文件,例如:
#include "skeleton_driver.h"实现该类的基本方法,例如:
#include "skeleton_driver.h" namespace skeleton { SkeletonDriver::SkeletonDriver() : DriverBase2("skeleton_driver") {} zx::result<> SkeletonDriver::Start(fdf::DriverContext context) { return zx::ok(); } } // namespace skeleton(来源:
skeleton_driver.cc)此驱动程序构造函数需要将驱动程序名称(例如
"skeleton_driver")传递给DriverBase2类。
添加驱动程序导出宏
如需添加驱动程序导出宏,请执行以下操作:
在驱动程序源文件中,添加以下头文件:
#include <lib/driver/component/cpp/driver_export2.h>在驱动程序源文件的底部添加以下宏(用于导出驱动程序类):
FUCHSIA_DRIVER_EXPORT2(skeleton::SkeletonDriver);例如:
#include <lib/driver/component/cpp/driver_base2.h> #include <lib/driver/component/cpp/driver_export2.h> #include "skeleton_driver.h" namespace skeleton { SkeletonDriver::SkeletonDriver() : DriverBase2("skeleton_driver") {} zx::result<> SkeletonDriver::Start(fdf::DriverContext context) { return zx::ok(); } } // namespace skeleton FUCHSIA_DRIVER_EXPORT2(skeleton::SkeletonDriver);(来源:
skeleton_driver.cc)
创建 build 文件
如需为驱动程序创建 build 文件,请执行以下操作:
- 创建一个新的
BUILD.gn文件。 添加以下行以导入驱动程序 build 规则:
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文件中,添加以下行以导入绑定 build 规则: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文件中,添加以下行以导入组件 build 规则: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 驱动程序可以使用以下 Node 协议在
fuchsia.driver.framework FIDL 库中添加子节点:
open protocol Node {
flexible AddChild(resource struct {
args NodeAddArgs;
controller server_end:NodeController;
node server_end:<Node, optional>;
}) -> () error NodeError;
};
为了方便起见,在启动期间,驱动程序框架通过 DriverBase2 向 DFv2 驱动程序提供绑定节点的 Node 协议的客户端。
驱动程序可以随时访问其节点客户端,以便在其上创建子节点。
但是,直接使用此 FIDL 库需要进行设置,包括创建 FIDL 通道对和构建 NodeAddArgs 表。
因此,DriverBase2 类提供了一组辅助函数,以便更轻松地添加子节点。(如需查看这些辅助函数,请查看
driver_base2.h 文件。)
DFv2 驱动程序可以添加两种类型的节点:无所有者 和有所有者 。 无所有者节点和有所有者节点之间的主要区别在于它们是否 参与驱动程序匹配过程。
驱动程序框架会尝试查找与无所有者节点的属性匹配的驱动程序,以便将驱动程序绑定到该节点。驱动程序与节点匹配并绑定后,绑定的驱动程序将成为该节点的所有者。 另一方面,有所有者节点不参与匹配,因为创建该节点的驱动程序已经是所有者。
DriverBase2 辅助函数
驱动程序当前绑定的节点的客户端存储在 DriverBase2 对象中。这样,驱动程序就可以使用 DriverBase2 类的 AddChild() 和 AddOwnedChild() 函数向此节点添加子节点。
但是,如需使用这些 DriverBase2 辅助函数,节点不得已从驱动程序中移出。如果节点已移出,或者目标节点不是
驱动程序当前绑定的节点(即孙节点),
则需要改用
add_child.h文件中提供的命名空间方法。这些方法与 DriverBase2 辅助函数相同,不同之处在于,通过提供正确的父节点客户端作为目标,它们可用于向 DriverBase2 对象范围之外的节点添加子节点。
最后,这些辅助函数会处理错误记录(如果发生错误),因此驱动程序无需进行任何日志记录。
创建无所有者节点
如需创建无所有者节点,驱动程序可以使用 DriverBase2::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)
创建有所有者节点
如需创建有所有者节点,驱动程序可以使用 DriverBase2::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 驱动程序需要在停止之前执行拆解(例如停止线程),则需要替换并实现 DriverBase2 的 Stop() 方法(用于异步清理),或者在析构函数中执行同步清理。
Stop() 函数在驱动程序的 fdf 调度器关闭且驱动程序被取消分配之前调用。因此,如果驱动程序需要在驱动程序的调度器关闭之前执行某些操作,则需要实现 Stop(),例如:
void SimpleDriver::Stop(fdf::StopCompleter completer) {
// Teardown threads
fdf::info("Stopping SimpleDriver");
completer(zx::ok());
}
添加兼容设备服务器
如果您的 DFv2 驱动程序有尚未迁移到 DFv2 的后代 DFv1 驱动程序,则需要使用兼容性 shim,以使您的 DFv2 驱动程序能够与系统中的其他 DFv1 驱动程序通信。如需了解详情,请参阅 在 DFv2 驱动程序中设置兼容设备服务器 指南。