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

在开始从 DFv1 到 DFv2 的迁移之前,请参阅以下常见问题解答,了解可能适用于驱动程序的特殊条件或极端情况。

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

DFv1 和 DFv2 驱动程序存在于节点拓扑树的单个分支下(即,直到所有驱动程序都迁移到 DFv2 为止),并且它们需要能够相互通信。为了帮助完成迁移过程,Fuchsia 的驱动程序框架团队创建了一个兼容性 shim,使 DFv1 驱动程序能够存在于 DFv2 中。

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

如需详细了解如何在 DFv2 驱动程序中使用兼容性填充码,请参阅在 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 驱动程序是否可以将兼容性填充码用于复合节点?

复合驱动程序的迁移过程与普通驱动程序的迁移过程几乎完全相同,但复合驱动程序从父节点连接到 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 中的一个主要变化是,驱动程序会控制驱动程序创建的子节点(或设备)的生命周期。这与 DFv1 不同,在 DFv1 中,驱动程序框架通过设备树管理设备的生命周期,例如拆解设备

在 DFv1 中,设备由 zx_protocol_device 控制,而驱动程序由 zx_driver_ops 控制。如果使用 ddktlzx_protocol_device 中的接口需要由 mixin 模板类中的 Ddk*() 函数封装。在 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 系统中公开?

Fuchsia 拥有一个设备全局树,该树公开为一个称为 devfs 的文件系统,该文件系统以 /dev 的形式路由到大多数组件。当驱动程序添加设备节点时,可以选择在 devfs 中添加“文件”。然后,devfs 中的此文件允许系统中的其他组件与驱动程序进行通信。例如,音频驱动程序可能会添加扬声器设备节点,而音频驱动程序想要确保其他组件可以使用此节点将音频输出到扬声器。为此,音频驱动程序会添加(或公开)扬声器的 devfs 节点,使其在系统中显示为 /dev/class/audio/<random_number>

哪些功能未在 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 驱动程序迁移(pcie-iwlwifi-driver.cc 文件包含大多数新 API)所做的此 Gerrit 更改