本指南介绍了创建最小 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
)。添加基本绑定规则,例如:
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 = "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
字段必须与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
)创建新的 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
文件中,添加下面这行代码以导入组件构建规则: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 的结构化日志记录器库 (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;
};
驱动程序可以使用 DriverStartArgs
对象中的 node()
值或 DriverBase
类的 node()
函数来连接到 Node
协议。
除了使用 Node
协议之外,您还需要创建 NodeController
端点。AddChild()
方法需要 fuchsia.driver.framework NodeController
协议的服务器端。另一方面,驱动程序负责存储并保存协议的客户端。如果此客户端已取消分配,则驱动程序框架将移除该子节点。
如需使用 Node
协议添加子节点,请执行以下操作:
在您的 DFv2 驱动程序中,创建一个采用以下参数的
NodeAddArgs
对象:type NodeAddArgs = resource table { /// Name of the node. 1: name NodeName; 2: offers vector<fuchsia.component.decl.Offer>:MAX_OFFER_COUNT; 3: symbols vector<NodeSymbol>:MAX_SYMBOL_COUNT; 4: properties NodePropertyVector; 5: devfs_args DevfsAddArgs; };
(来源:
topology.fidl
)name
是子节点的名称。offers
是父节点为子节点提供的功能。您可以使用node_adds_args
库中的MakeOffer()
函数创建这些模块。symbols
是要提供给驱动程序的函数。对于 DFv2 驱动程序,可以忽略此参数。properties
是子节点属性,用于确定将哪个驱动程序绑定到该子节点(如需了解详情,请参阅绑定规则教程)。您可以使用node_adds_args
库中的MakeProperty()
函数创建节点属性。- 如果子节点需要访问
devfs
,则devfs_args
是必需的。
以下示例创建了一个
NodeAddArgs
对象:fidl::Arena arena; auto properties = std::vector{fdf::MakeProperty(arena, bind_fuchsia_test::TEST_CHILD, "simple")}; auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) .name(arena, "simple_child") .properties(arena, std::move(properties)) .Build();
更新驱动程序的
DriverBase
类,以添加 FIDL 客户端对象。下面的示例使用
fidl::WireSyncClient
对象:class SimpleDriver : public fdf::DriverBase { <...> private: <...> fidl::WireSyncClient<fuchsia_driver_framework::NodeController> child_controller_; };
此设置允许您将客户端存储在驱动程序中。
使用
fidl::CreateEndpoints
函数创建服务器端点和客户端端点,例如:zx::result controller_endpoints = fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>(); ZX_ASSERT_MSG(controller_endpoints.is_ok(), "Failed to create endpoints: %s", controller_endpoints.status_string());
将客户端绑定到 FIDL 客户端对象,例如:
child_controller_.Bind(std::move(endpoints->client));
您现在可以使用
Node
对象连接到Node
服务器,连接到
Node
服务器并调用AddChild()
方法,例如:fidl::WireResult result = fidl::WireCall(node())->AddChild(args, std::move(controller_endpoints->server), {});
将所有步骤放在一起,如以下示例所示:
void SimpleDriver::Start(fdf::StartCompleter completer) { fidl::Arena arena; auto properties = std::vector{fdf::MakeProperty(arena, bind_fuchsia_test::TEST_CHILD, "skeleton")}; auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) .name(arena, "skeleton_child") .properties(arena, std::move(properties)) .Build(); auto controller_endpoints = fidl::Endpoints<fuchsia_driver_framework::NodeController>::Create(); controller_.Bind(std::move(controller_endpoints.client)); fidl::WireResult result = fidl::WireCall(node())->AddChild(args, std::move(controller_endpoints.server), {}); if (!result.ok()) { FDF_LOG(ERROR, "Failed to add child %s", result.status_string()); return completer(result.status()); } completer(zx::ok()); }
清理驱动程序
如果 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 驱动程序中设置兼容型设备服务器指南。