编写基本驱动程序
代码结构应遵循 fx create driver goldens 模板。
Meta 目录
每个驱动程序目录都必须有一个 meta 子目录,其中包含以下文件:
- 一个
bind文件,用于定义驱动程序的bind规则 - 组件清单
驱动程序源代码
使用驱动程序组件库 (sdk/lib/driver/component/) 编写驱动程序。驱动程序应继承自头文件 <lib/driver/component/cpp/driver_base2.h> 中定义的 fdf::DriverBase2 类。
该驱动程序提供用于导出驱动程序符号的 FUCHSIA_DRIVER_EXPORT2 宏,该符号在头文件 <lib/driver/component/cpp/driver_export2.h> 中定义。此宏应位于 .cc 文件中。
初始化驱动程序
避免在驱动程序的构造函数中放置任何逻辑。您必须通过替换 fdf::DriverBase2 提供的 Start() 方法(即使初始化逻辑为空),来实现驱动程序的初始化逻辑,而不是使用构造函数。确保仅使用单个 Start() 变体来实现此目的。
初始化逻辑包含:
- 提取并配置所有驱动程序资源。
- 建立服务连接。
- 将驱动程序自己的服务添加到传出目录。
- 添加子节点(在资源设置并提供自己的服务后执行)。
- 使用
zx_bti_release_quarantine()从隔离区中释放 BTI。
关闭驱动程序
驱动程序的析构函数应不包含任何逻辑。在大多数情况下,无需替换 DriverBase2::Stop() 并为其提供实现。除非驱动程序的功能(例如执行正常硬件关机或取消固定 DMA)明确需要,否则请勿实现 DriverBase2::Stop()。函数形参中的 fdf::StopCompleter 必须在函数结束时调用。
build 文件
所有新驱动程序都必须使用 Bazel 进行构建流程,并且必须包含以下目标:
fuchsia_driver_bind_bytecodefuchsia_cc_driverfuchsia_driver_component
司机沟通
驱动程序通过 FIDL 服务与其父驱动程序通信。
提供服务
提供 FIDL 服务的驱动程序必须维护 fidl::ServerBindingGroup(使用 Zircon 传输时)或 fdf::ServerBindingGroup(对于驱动程序传输)。除非必须将驱动程序限制为单个客户端连接,否则请避免使用 fidl::ServerBinding 或 fdf::ServerBinding。
必须在创建任何子节点之前,将服务器绑定添加到驱动程序的传出目录中的 Start() 函数内。此外,CML 文件必须在功能中指定服务,并从 self 中公开该服务。
使用服务
如需使用某项服务,驱动程序应将其纳入 bind 规则中,并在 CML 的 uses 部分中指定该服务。
添加孩子
添加子节点的主要原因是,驾驶员需要为另一位驾驶员提供服务或资源。避免无故添加子级。
除非驱动程序需要动态添加子节点,否则应在驱动程序的 Start() 函数中添加子节点作为初始化逻辑的一部分。
应使用 DriverBase2 的 AddChild() 或 add_child 辅助库 (sdk/lib/driver/node/cpp/add_child.h) 添加子节点。只有当子节点需要使用其他记录器时,才应使用辅助库。
所有子节点都应处于无所有权状态,除非用于支持已弃用的 devfs。如果子节点归驱动程序所有,则驱动程序必须存储从辅助函数接收的 fuchsia_driver_framework::Node 客户端端点。
添加子节点时,请明确定义子节点的名称,而不是使用 DriverBase2 中的 name()。使用 node_offers 辅助库 (sdk/lib/driver/component/cpp/node_offers.h) 创建优惠。使用 node_properties 库 (sdk/lib/driver/component/cpp/node_properties.h) 创建属性。节点属性键和值应使用生成的绑定库代码绑定。
日志记录
对于日志记录,驱动程序应使用基于格式的日志 API(例如驱动程序记录器库 [sdk/lib/driver/logging/cpp/logger.h] 中的 fdf::info())。如果您需要记录自定义类型,请实现 std::format。
遵循 Fuchsia 日志记录指南,在记录 FIDL 错误等失败情况时使用 warning 或 error 日志级别。
Zircon 资源
中断
在初始化期间,应检索中断并将其存储在驱动程序中。中断应封装在 async::Irq 对象 (sdk/lib/async/include/lib/async/cpp/irq.h) 中。
应使用 IrqMethod 对象来处理中断触发。处理完中断触发后,应使用 ack() 确认 Irq 对象,以便重新启用中断并再次触发。
DMA
对于内存操作,驱动程序应使用位于 sdk/lib/driver/mmio/cpp/mmio-buffer.h 的 MmioBuffer。此库提供了一个用于读取和写入的原始 mmio_block_t 对象的封装容器。
为确保对寄存器位字段的安全访问,建议使用 hwreg/bitfields 库。此库位于 zircon/system/ulib/hwreg/include/hwreg/bitfields.h。
驱动程序应控制其 BTI 并停止初始化期间可能正在进行的任何 DMA。完成后,它应通过调用 BTI 上的 zx_bti_release_quarantine() 来告知 BTI 它已重新获得硬件控制权。打算与硬件共享 DMA 的驱动程序必须提前固定 BTI。硬件完成对内存的访问后,驱动程序负责取消固定内存。在析构函数中,无论在任何情况下都不应取消固定 BTI。
时钟
时钟通过 fuchsia.hardware.clock FIDL 服务进行控制。驱动程序需要在其依赖的所有时钟上调用 Enable(),并在不再需要时钟信号时随后调用 Disable()。驱动程序不得在未先启用时钟的情况下调用 Disable()。
测试
验证 build 目标是否包含必要的驱动程序测试。
单元测试
单元测试应使用 gtests (third_party/googletest/src/googletest/include/gtest/gtest.h) 编写。
测试整个驱动程序
如果测试整个驱动程序,则应使用驱动程序测试库 (sdk/lib/driver/testing/cpp/driver_test.h) 中的 ForegroundDriverTest 或 BackgroundDriverTest 封装该驱动程序。单元测试应在初始化测试后立即调用 StartDriver(),并在 Teardown() 函数中调用 StopDriver()。驱动程序所需的所有服务和资源都应在自定义驱动程序测试的 Environment 类中进行初始化和提供。
如果驱动程序需要平台设备服务,测试应实例化 FakePlatformDevice (sdk/lib/driver/fake-platform-device/) DriverTestEnvironment 并提供该服务。同样,如果驱动程序需要 FIDL 服务,则应在 DriverTestEnvironment 中实例化并提供该服务的测试实现。
测试驱动程序的部分内容
如果仅隔离驱动程序逻辑的一部分进行测试,则测试可能需要特定的驱动程序环境组件。
在评估使用驱动程序记录器库 (sdk/lib/driver/logging/cpp/logger.h) 的逻辑时,测试需要实例化并维护 fdf_testing::ScopedGlobalLogger 实例 (sdk/lib/driver/testing/cpp/scoped_global_logger.h)。
此外,如果逻辑需要驱动程序调度程序,则测试必须配置并持有 fdf_testing::DriverRuntime 对象 (sdk/lib/driver/testing/cpp/driver_runtime.h)。
伪对象/模拟对象库
需要虚假 FIDL 服务或资源的测试应尽可能使用 sdk/lib/driver/ 中提供的虚假和模拟库。
以下库适用于常见的 FIDL 服务:
- //sdk/lib/driver/fake-clock -
fuchsia.hardware.clock服务。 - //sdk/lib/driver/fake-gpio -
fuchsia.hardware.gpio服务。 - //sdk/lib/driver/fake-interconnect -
fuchsia.hardware.interconnect服务。 - //sdk/lib/driver/fake-pin -
fuchsia.hardware.pin服务。 - //sdk/lib/driver/fake-platform-device -
fuchsia.hardware.platform.device和fuchsia.hardware.power服务。 - //sdk/lib/driver/fake-reset -
fuchsia.hardware.reset服务。 - //sdk/lib/driver/fake-vreg -
fuchsia.hardware.vreg服务。
以下库适用于常见的内部 Zircon 对象、内存区域或驱动程序运行时资源:
- //sdk/lib/driver/fake-bti:伪造 Zircon BTI (
zx::bti) 对象。 - //sdk/lib/driver/fake-object:伪造常规 Zircon 内核对象 (
zx::handle)。 - //sdk/lib/driver/fake-resource:伪造 Zircon 资源 (
zx::resource) 对象。 - //sdk/lib/driver/fake-mmio-reg:通过内存结构伪造 MMIO 寄存器。
- //sdk/lib/driver/mock-mmio:模拟内存映射 I/O 寄存器测试的 MMIO 区域。
集成测试
集成测试应使用 Driver Test Realm (sdk/lib/driver_test_realm/) 框架。首先,测试必须建立与 DriverTestRealm 服务的连接,在 RealmArgs 中执行必要的配置,然后使用实参调用 Start()。