本指南将介绍创建最小 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 文件
如需为驱动程序创建 build 文件,请执行以下操作:
- 创建一个新的
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
文件中,添加以下行以导入绑定 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
字段必须与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
数组以包含fuchsia_driver
和BUILD.gn
文件中的driver_bind_rules
目标。
- 将
您现在可以在 Fuchsia 系统中构建、加载和注册此 DFv2 驱动程序
其他任务
本部分提供了可添加到最小 DFv2 的其他功能 驱动程序:
添加日志
默认情况下,如需从 DFv2 驱动程序输出日志,请使用 FDF_LOG
宏,例如:
FDF_LOG(INFO, "Starting SimpleDriver")
除了使用 FDF_LOG
宏之外,您还可以使用 Fuchsia 的结构化日志记录器库 (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 驱动程序可以使用以下 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;
};
为此,在启动期间,驱动程序框架会通过 DriverBase
向 DFv2 驱动程序提供绑定节点的 Node
协议的客户端。驱动程序可以随时访问其节点客户端,以便在其上创建子节点。不过,直接使用此 FIDL 库需要进行的设置包括
创建 FIDL 通道对并构建 NodeAddArgs
表。
因此,DriverBase
类提供了一组辅助函数,以便更轻松地添加子节点。(要查看这些帮助程序,请查看
driver_base.h
file.)
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 驱动程序包含尚未迁移到的后代 DFv1 驱动程序 DFv2,您需要使用兼容性填充码,以便 DFv2 驱动程序能够进行通信 至系统中的其他 DFv1 驱动程序。有关详情,请参阅 在 DFv2 驱动程序中设置兼容型设备服务器 指南。