关于从 DFv1 迁移到 DFv2 的常见问题解答

在开始进行 DFv1 到 DFv2 迁移之前,以下常见问题解答可帮助您确定可能适用于您的驱动程序的特殊条件或极端情况。

什么是兼容性 shim?何时需要使用兼容性 shim?

DFv1 和 DFv2 驱动程序位于节点拓扑树的单个分支下(也就是说,在所有驱动程序都迁移到 DFv2 之前),并且它们需要能够相互通信。为帮助完成迁移过程,Fuchsia 的驱动程序框架团队创建了一个兼容性 shim,以便 DFv1 驱动程序能够在 DFv2 中运行。

如果您的目标驱动程序与仍使用 Banjo 的其他 DFv1 驱动程序通信,并且这些驱动程序不会一次性迁移到 DFv2,则您需要使用此兼容性 shim(通过手动创建 compat::DeviceServer),以便不同框架版本中的驱动程序能够相互通信。

如需详细了解如何在 DFv2 驱动程序中使用兼容性 shim,请参阅在 DFv2 驱动程序中设置兼容性设备服务器指南。

DFv2 驱动程序能否使用兼容性 shim 与 Banjo 协议通信?

虽然强烈建议您将 DFv1 驱动程序从 Banjo 迁移到 FIDL,但如果 DFv2 驱动程序需要与某些现有 Banjo 协议通信,兼容性 shim 可提供以下功能:

  • compat::BanjoServer 可简化 Banjo 的服务(请参阅 banjo_server.h)。
  • compat::ConnectBanjo 可让您更轻松地连接到 Banjo(请参阅 banjo_client.h)。

如需详细了解这些功能,请参阅在 DFv2 驱动程序中提供 Banjo 协议指南。

DFv2 驱动程序可以使用复合节点的兼容性 shim 吗?

复合驱动程序的迁移过程与普通驱动程序几乎完全相同,但复合驱动程序通过父节点连接到 Banjo 或 FIDL 协议的方式略有不同。

由于复合节点有多个父级,因此复合驱动程序在连接到父级时需要识别父级的名称。例如,以下是与其父级建立 Banjo 连接的常规驱动程序:

zx::result client_result =
  compat::ConnectBanjo<ddk::HidDeviceProtocolClient>(incoming());

复合驱动程序的方法几乎相同,只需添加父名称即可:

zx::result client_result =
  compat::ConnectBanjo<ddk::HidDeviceProtocolClient>(incoming(), "gpio-int")

新版 DFv2 驱动程序接口有哪些变化?

DFv2 的一个重大变化是,驱动程序会控制由驱动程序创建的子nodes(或设备)的生命周期。这与 DFv1 不同,在 DFv1 中,驱动程序框架通过设备树管理设备的生命周期,例如拆解设备

在 DFv1 中,设备由 zx_protocol_device 控制,而驱动程序由 zx_driver_ops 控制。如果使用 ddktl,则需要使用混入模板类中的 Ddk*() 函数封装 zx_protocol_device 中的接口。在 DFv2 中,这些接口发生了重大变化。

DFv2 中的服务发现如何运作?

在 DFv2 中,必须使用 FIDL 服务才能建立协议连接。父级驱动程序会将 FIDL 服务添加到 fdf::OutgoingDirectory 对象,并将其提供给子节点,从而使父级驱动程序能够向子节点提供该服务。

DFv1 和 DFv2 驱动程序在以下方面执行此操作的方式有所不同:

  • 在 DFv1 中,驱动程序会从 DeviceAddArgs::set_runtime_service_offers() 调用中设置和传递优惠。然后,驱动程序会创建一个 fdf::OutgoingDirectory 对象,并通过 DeviceAddArgs::set_outgoing_dir() 调用传递客户端端句柄。

  • 在 DFv2 中,驱动程序会从 NodeAddArgs::offers 对象设置和传递优惠。驱动程序会将服务添加到由 DriverBase 类封装的传出目录(最初由 Start() 函数提供)。当子驱动程序绑定到子节点时,驱动程序主机会将包含服务的传入命名空间传递给子驱动程序的 Start() 函数。

在子驱动程序端,DFv1 和 DFv2 驱动程序也通过不同的方式连接到提供服务的协议:

  • DFv1 驱动程序调用 DdkConnectRuntimeProtocol<ServiceInstanceName>() 方法。
  • 如果使用 DriverBase 类,DFv2 驱动程序会调用 incoming()->Connect<ServiceInstanceName>()

    如需了解详情,请参阅使用 DFv2 服务发现

如何在 DFv2 中将驱动程序的节点(或设备)公开到系统中?

驱动程序可以公开可路由到客户端的服务。此外,与所有组件一样,驱动程序也可以使用 ffx component list 列出。

DFv2 中未实现哪些在 DFv1 中可用的功能?

如果您的 DFv1 驱动程序调用 DDK 库中的 load_firmware() 函数,则您需要实现自己的函数,因为 DFv2 中不提供等效函数。不过,这应该易于实现

DFv2 中的绑定规则有哪些变化?

DFv2 节点包含通过其 FIDL 服务产品生成的其他节点属性

不过,在将现有 DFv1 驱动程序迁移到 DFv2 时,您不太可能需要修改绑定规则。

DFv2 中的日志记录有何变化?

DFv2 驱动程序无法使用 zxlogf() 函数或封装或使用此函数的任何调试库。zxlogf()//src/lib/ddk/include/lib/ddk/debug.h 中定义,并已从 DFv2 中的依赖项中移除。迁移到 DFv2 的驱动程序需要停止使用此库以及依赖于它的其他库。

不过,现在添加了一个仅在 Fuchsia 源代码树 (fuchsia.git) 环境中可用的兼容性库,以允许 DFv2 驱动程序使用 DFv1 风格的日志记录。

DFv2 中的“检查”功能有哪些变化?

DFv1 驱动程序使用驱动程序专用检查函数来创建和更新由驱动程序维护的指标。例如,在 DFv1 中,系统会调用 DeviceAddArgs::set_inspect_vmo() 函数来指明驱动程序用于检查的 VMO。不过,在 DFv2 中,我们只需创建 inspect::ComponentInspector 对象即可。

调度程序在 DFv2 中执行什么操作?

FIDL 文件会为客户端和服务器对生成模板和数据类型。客户端和服务器端之间有一个通道,每个端的调度程序都会从该通道提取数据。如需详细了解调度程序,请参阅驱动程序调度程序和线程

将 DFv1 驱动程序迁移到 DFv2 时,新线程模型会出现哪些问题?

DFv2 中的 FIDL 调用不是基于单个线程的,并且设计上是异步的(不过,您可以通过向 FIDL 调用添加 .sync() 或使用 fdf::WireSyncClient 将其设为同步)。通常不建议驱动程序进行同步调用,因为它们可能会阻止其他任务运行。(不过,如有必要,驱动程序可以使用 FDF_DISPATCHER_OPTION_ALLOW_SYNC_CALLS 选项创建调度程序,但仅同步调度程序支持此选项。)

鉴于 Banjo (DFv1) 和 FIDL (DFv2) 之间的线程模型存在差异,您需要确定在迁移过程中要使用哪种类型的 FIDL 调用(即同步或异步)。如果您的原始代码是围绕 Banjo 的同步特性设计的,并且很难展开即可使其完全异步,那么您不妨考虑先使用同步版本的 FIDL(不过,这目前可能会导致性能下降)。之后,您可以重新访问这些调用,并将其优化为使用同步调用。

DFv2 中测试驱动程序时有哪些变化?

mock_ddk 库用于驱动程序单元测试,仅适用于 DFv1。现在,DFv2 驱动程序提供新的测试库

在迁移过程中,我是否应将驱动程序分叉为 DFv2 版本?

是否要分叉现有驱动程序以进行迁移,取决于驱动程序的复杂性。一般来说,建议避免分叉驱动程序,因为这可能会导致更多工作。不过,对于较大的驱动程序,最好将驱动程序分叉为 DFv2 版本,以便您逐步在较小的补丁中完成迁移更改。

您可以通过在 GN 参数中添加新的驱动程序组件来分叉驱动程序,并使用标志来决定使用 DFv1 还是 DFv2 版本。此示例 CL 演示了如何添加 msd-arm-mali 驱动程序的 DFv2 分支。

fuchsia.dev 上的 DFv2 概念文档,以及之前的 DFv1 Intel Wi-Fi 驱动程序迁移中的此 Gerrit 更改pcie-iwlwifi-driver.cc 文件包含大多数新 API)。