排查 DFv2 驱动程序开发中的常见问题

本问题排查指南提供了调试工作流,用于解决 Fuchsia 驱动程序开发中的一些常见错误。

  • 调试工作流 - 用于诊断和修复已知问题的一系列调试工作流 。
  • 出错提示 - 与驱动程序开发相关的一系列常见出错提示 以及如何修复这些错误。

调试工作流

针对常见问题的调试工作流:

驱动程序无法启动

如果驱动程序无法按预期加载、绑定或启动,则无法“启动”。

如需调试此问题,请尝试以下步骤:

  1. 验证驱动程序是否已成功加载。
  2. 验证驱动程序是否尝试绑定。
  3. 验证驱动程序是否已成功启动。

1. 验证驱动程序是否已成功加载

如果驱动程序已成功加载,您会在日志中看到类似以下内容的消息:

Found boot driver: fuchsia-boot:///#meta/ahci.cm

或者,您也可以使用以下命令转储所有驱动程序的列表:

ffx driver list

此命令会输出类似以下内容:

fuchsia-boot:///adc#meta/adc.cm
fuchsia-boot:///ahci#meta/ahci.cm
fuchsia-boot:///alc5514#meta/alc5514.cm
fuchsia-boot:///alc5663#meta/alc5663.cm

如果列表中缺少驱动程序,则表示该驱动程序加载失败。

以下是一些需要考虑的原因:

  • 驱动程序未包含在 build 软件包中。
  • 驱动程序的组件清单 (.cml) 文件存在问题。

与组件清单文件相关的常见出错提示:

2. 验证驱动程序是否尝试绑定

如果驱动程序尝试绑定到节点,您会在日志中看到类似以下内容的消息:

Binding driver fuchsia-boot:///#meta/focaltech.cm

如果您没有看到此日志消息,则可能是驱动程序的绑定规则与节点属性不匹配。如需调试不匹配问题,请检查驱动程序的绑定规则,并将其与节点属性进行比较。

如需查看所有节点属性,请运行以下命令:

ffx driver list-devices -v

此命令会输出类似以下内容:

Name     : pt
Moniker  : acpi._SB_.PCI0.ISA_.RTC_.pt
Driver   : unbound
5 Properties
[ 1/  5] : Key fuchsia.BIND_ACPI_ID           Value 0x000004
[ 2/  5] : Key "fuchsia.acpi.HID"             Value "PNP0B00"
[ 3/  5] : Key fuchsia.BIND_PROTOCOL          Value 0x00001e
[ 4/  5] : Key "fuchsia.Service"              Value "fuchsia.driver.compat.Service"
[ 5/  5] : Key "fuchsia.Service"              Value "fuchsia.hardware.acpi.Service"
2 Offers
Service: fuchsia.driver.compat.Service
  Source: board
  Instances: default
Service: fuchsia.hardware.acpi.Service
  Source: board
  Instances: default

但是,如果驱动程序是复合驱动程序,您需要验证 复合节点规范是否与 复合驱动程序的绑定规则以及父 节点的节点属性匹配。

如需查看复合节点规范,请运行以下命令:

ffx driver list-composite-node-specs -v

此命令会输出类似以下内容:

Name      : COM1-composite-spec
Driver    : fuchsia-boot:///uart16550#meta/uart16550.cm
Nodes     : 3
Node 0    : "acpi" (Primary)
  2 Bind Rules
  [ 1/ 2] : Accept "fuchsia.BIND_PROTOCOL" { 0x00001e }
  [ 2/ 2] : Accept fuchsia.BIND_ACPI_ID { 0x000007 }
  3 Properties
  [ 1/ 3] : Key "fuchsia.BIND_PROTOCOL"        Value 0x00001e
  [ 2/ 3] : Key fuchsia.BIND_ACPI_ID           Value 0x000007
  [ 3/ 3] : Key "fuchsia.acpi.HID"             Value "PNP0501"
Node 1    : "sysmem"
  1 Bind Rules
  [ 1/ 1] : Accept "fuchsia.Service" { "fuchsia.hardware.sysmem.Service" }
  1 Properties
  [ 1/ 1] : Key "fuchsia.Service"              Value "fuchsia.hardware.sysmem.Service"
Node 2    : "irq000"
  3 Bind Rules
  [ 1/ 3] : Accept "fuchsia.BIND_ACPI_ID" { 0x000007 }
  [ 2/ 3] : Accept "fuchsia.BIND_PLATFORM_DEV_INTERRUPT_ID" { 0x000001 }
  [ 3/ 3] : Accept "fuchsia.Service" { "fuchsia.hardware.interrupt.Service" }
  3 Properties
  [ 1/ 3] : Key "fuchsia.BIND_ACPI_ID"         Value 0x000007
  [ 2/ 3] : Key "fuchsia.BIND_PLATFORM_DEV_INTERRUPT_ID" Value 0x000001
  [ 3/ 3] : Key "fuchsia.Service"              Value "fuchsia.hardware.interrupt.Service"

如果复合驱动程序与规范不匹配,Driver 字段将显示 None。

如果绑定规则与节点属性之间存在不一致,您需要进行调整,直到它们匹配为止。

此外,如果绑定规则使用基于 FIDL 的节点属性,您需要向子项添加 offer,以便包含基于 FIDL 的属性。

例如,假设绑定规则指定了以下条件:

fuchsia.Service == "fuchsia.examples.gizmo.Service";

然后,目标节点的父级驱动程序需要向该节点添加以下 offer:

zx::result child_result =
    AddChild("zircon_transport_child", {}, {fdf::MakeOffer2<fuchsia_examples_gizmo::Service>()});

3. 验证驱动程序是否已成功启动

如果驱动程序已成功加载,您会在日志中看到类似以下内容的消息:

Started driver url=fuchsia-boot:///ramdisk#meta/ramdisk.cm

但如果缺少此日志消息,则表示驱动程序启动失败。

如需调试此问题,请先检查是否调用了驱动程序的 Start() 函数。 对于此任务,建议更新 Start() 函数以输出一些日志消息。

如果从未调用过 Start() 函数,则可能存在以下原因:

但是,如果调用了 Start() 函数,但驱动程序仍然无法启动,则可能会以以下某种方式失败:

  • 驱动程序的 Start() 函数回复或返回错误。
  • 驱动程序替换 void Start(StartCompleter completer),并且不回复 completer。

如果驱动程序的 Start() 函数回复或返回错误,您会在日志中看到类似以下内容的消息:

[driver_host,driver] ERROR: [src/devices/bin/driver_host/driver_host.cc(134)] Failed to start driver url=fuchsia-pkg://fuchsia.com/simple_driver#meta/simple.cm status_str=ZX_ERR_INTERNAL
[driver_manager.cm] ERROR: [src/devices/bin/driver_manager/driver_host.cc(152)] Failed to start driver 'driver/simple.so' in driver host: ZX_ERR_INTERNAL

此时,开始调试 Start() 函数以找出错误原因。可能存在以下原因:

驱动程序无法与 FIDL 服务通信(PEER_CLOSED 错误)

如果 FIDL 服务在驱动程序中未正确设置,则当驱动程序尝试向 FIDL 服务发送请求时,可能会收到 PEER_CLOSED 错误。

如需调试 PEER_CLOSED 错误,请尝试以下步骤:

  1. 验证传出目录。
  2. 检查实例名称。
  3. 检查是否存在 capability 路由问题。

此外,如需查看 DFv2 驱动程序中的正确 FIDL 服务设置,请查看 这些 传输示例。

1. 验证传出目录

检查父级驱动程序向其传出目录添加 FIDL 服务的设置,例如:

zx::result result = outgoing()->AddService<fuchsia_examples_gizmo::Service>(std::move(handler));
if (result.is_error()) {
  fdf::error("Failed to add service: {}", result);
  return result.take_error();
}

2. 检查实例名称

如果目标驱动程序是复合驱动程序,则需要将 父节点的名称传递给 FIDL 服务实例。

例如,假设复合驱动程序具有以下绑定规则:

composite mali;

using fuchsia.arm.platform;
using fuchsia.platform;

primary parent "mali" {
  fuchsia.Service == "fuchsia.hardware.gpu.mali.Service";
}

parent "pdev" {
  fuchsia.BIND_PROTOCOL == fuchsia.platform.BIND_PROTOCOL.DEVICE;
  fuchsia.BIND_PLATFORM_DEV_VID == fuchsia.arm.platform.BIND_PLATFORM_DEV_VID.ARM;
  fuchsia.BIND_PLATFORM_DEV_PID == fuchsia.platform.BIND_PLATFORM_DEV_PID.GENERIC;
  fuchsia.BIND_PLATFORM_DEV_DID == fuchsia.arm.platform.BIND_PLATFORM_DEV_DID.MAGMA_MALI;
}

如果我们想从名为 pdev 的父节点连接到 fuchsia.hardware.platform.device 服务,则驱动程序需要包含以下代码:

auto platform_device = incoming->Connect<fuchsia_hardware_platform_device::Service::Device>("pdev");

3. 检查是否存在 capability 路由问题

请参阅下文的 capability 路由错误 部分。

capability 路由错误

如需在 Fuchsia 中使用服务,必须在运行时将该服务的 capability 正确 路由到驱动程序。如需将 capability 路由到子级驱动程序,父级驱动程序需要公开该 capability 并将其提供给子级。

以下命令有助于诊断 capability 路由问题:

ffx component doctor <DRIVER_URL>

此命令会输出类似以下内容:

$ ffx component doctor fuchsia-pkg://fuchsia.com/driver_transport#meta/driver_transport_child.cm
Moniker: bootstrap/full-drivers:dev.driver_transport_parent.driver_transport_child
      Used Capability                 Result
 [✓]  fuchsia.logger.LogSink          Success
 [✗]  fuchsia.examples.gizmo.Service  `fuchsia.examples.gizmo.Service` was not offered to `bootstrap/full-pkg-
                                      drivers:dev.driver_transport_parent.driver_transport_child`
                                      by parent.

For further diagnosis, try:

  $ ffx component route bootstrap/full-drivers:dev.driver_transport_parent.driver_transport_child fuchsia.examples.gizmo.Service

如需解决与 capability 路由相关的问题,请尝试以下步骤:

  1. 公开 capability。
  2. 提供 capability。
  3. 使用 capability。

此外,如需了解如何在 DFv2 驱动程序中路由 capability, 请查看这些 传输示例。

1. 公开 capability

如果父级驱动程序未正确公开 capability,您可能会在日志中看到类似以下内容的消息:

[full-drivers:dev.driver_transport_parent.driver_transport_child] WARN: Required service `fuchsia.examples.gizmo.Service` was not available for target component `bootstrap/full-drivers:dev.driver_transport_parent.driver_transport_child`: could not find capability: `fuchsia.examples.gizmo.Service` was not exposed to `bootstrap` from child `#full-drivers:dev.driver_transport_parent`. For more, run `ffx component doctor bootstrap`.
To learn more, see https://fuchsia.dev/go/components/connect-errors

如需解决此问题,请检查 capability 在父级驱动程序的组件清单 (.cml) 文件中的公开方式,例如:

{
    include: [ 'syslog/client.shard.cml' ],
    program: {
        runner: 'driver',
        binary: 'driver/driver_transport_parent.so',
        bind: 'meta/bind/parent-driver.bindbc',
    },
    capabilities: [
        { service: 'fuchsia.examples.gizmo.Service' },
    ],
    expose: [
        {
            service: 'fuchsia.examples.gizmo.Service',
            from: 'self',
        },
    ],
}

2. 提供 capability

如需检查子节点中是否存在服务的 offer,请运行以下命令(您也可以使用 ffx component doctor 命令):

ffx driver node list <child_node_name>

此命令会输出每个节点中提供的所有服务的详细信息,例如:

          Name:  intel-hda-000
       Moniker:  PCI0.bus.00_01_0.00_01_0.intel-hda-000
         Owner:  parent
    Node State:  Bound
     Host Koid:
  Parent Count:  1
   Child Count:  1

  Bus Topology:  Bus Type  Stability  Address
                 Pci       Stable     0, 1, 0

  Node Properties:  Key                            Value
                    fuchsia.BIND_PROTOCOL          76
                    fuchsia.Service                fuchsia.driver.compat.Service

  Node Offers:  Service                        Source                    Instances
                fuchsia.driver.compat.Service  PCI0.bus.00_01_0.00_01_0  default

此外,如果在子节点中找不到 offer,您可能会在日志中看到类似以下内容的消息:

[00008.035682][full-drivers:dev.driver_transport_parent.driver_transport_child] WARN: Required service `fuchsia.examples.gizmo.Service` was not available for target component `bootstrap/full-drivers:dev.driver_transport_parent.driver_transport_child`: could not find capability: `fuchsia.examples.gizmo.Service` was not offered to `bootstrap/full-drivers:dev.driver_transport_parent.driver_transport_child` by parent.
To learn more, see https://fuchsia.dev/go/components/connect-errors

如需解决此问题,请检查父级驱动程序向子节点添加 offer 的设置,例如:

// Add a child with a `fuchsia.examples.gizmo.Service` offer.
zx::result child_result =
    AddChild("driver_transport_child", {}, {fdf::MakeOffer2<fuchsia_examples_gizmo::Service>()});
if (child_result.is_error()) {
  return child_result.take_error();
}

3. 使用 capability

如需使用 capability,驱动程序需要声明它想要使用该 capability。

如果未正确声明,您可能会在日志中看到类似以下内容的消息:

[00008.631682][full-drivers:dev.driver_transport_parent.driver_transport_child] WARN: No capability available at path /svc/fuchsia.examples.gizmo.Service/default/device for component bootstrap/full-drivers:dev.driver_transport_parent.driver_transport_child, verify the component has the proper `use` declaration.

如需解决此问题,请检查 capability 的使用在驱动程序的组件清单 (.cml) 文件中的声明方式,例如:

{
    include: [ 'syslog/client.shard.cml' ],
    program: {
        runner: 'driver',
        binary: 'driver/driver_transport_child.so',
        bind: 'meta/bind/child-driver.bindbc',
        colocate: 'true',
    },
    use: [
        { service: 'fuchsia.examples.gizmo.Service' },
    ],
}

出错提示

Fuchsia 驱动程序开发中的常见出错提示:

Failed to load driver <...> driver note not found

开发驱动程序时,您可能会在日志中遇到以下出错提示:

Failed to load driver <...> driver not found

发生此错误时,请检查驱动程序的组件清单 (.cml) 文件是否正确。请参阅驱动程序无法启动。

Required service <...> was not available for target component

如果驱动程序没有正确公开的服务 capability,并且父级驱动程序没有将该 capability 路由到该驱动程序,则会发生此错误。

如需解决此问题,请尝试 capability 路由错误 工作流。

但是,如果所需服务是 fuchsia.driver.compat,请按照以下说明操作:

如果驱动程序没有从父级驱动程序路由到它的 fuchsia.driver.compat 服务,则会发生此错误。如需路由此服务,父级驱动程序需要 设置兼容设备服务器为每个子级。

如需解决此问题,请从受影响的驱动程序开始,检查每个父级驱动程序。 例如,如果节点拓扑显示 A->B->C->D,并且 D 是受影响的驱动程序, 则检查 C、B 和 A。对于链中的每个父级驱动程序,请验证其兼容设备服务器是否已正确设置。

以下是与设置兼容设备服务器相关的一些常见问题:

  • 必须使用正确的子节点名称初始化兼容设备服务器。
  • 必须使用来自 兼容设备服务器的 offer 添加子级。

Driver-Loader: libdriver.so: Not in allowlist

如果 libdriver 位于驱动程序的某个依赖项中(可以是传递依赖项),则会发生此错误。

如需解决此问题,请搜索并移除依赖项中的 //src/devices/lib/driver。

如需查找依赖项,请使用以下命令:

fx gn path <out_directory> //path/to/driver //path/to/libdriver

__fuchsia_driver_registration__ symbol not available

开发驱动程序时,您可能会在日志中遇到以下出错提示:

[driver_host,driver] ERROR: [src/devices/bin/driver_host/driver.cc(120)] __fuchsia_driver_registration__ symbol not available_.
[driver_host,driver] ERROR: [src/devices/bin/driver_host/driver.cc(316)] Failed to start driver 'fuchsia-pkg://fuchsia.com/fake-battery#meta/fake_battery.cm', could not Load driver: ZX_ERR_NOT_FOUND

当驱动程序不包含 FUCHSIA_DRIVER_EXPORT2 宏时,会发生此错误。

如需解决此问题,请参阅添加驱动程序导出宏。

此外,请确保该宏是在 .cc 文件中定义的,而不是在头文件 (.h) 中定义的。

Could not load driver: <...>: Failed to open bind file (or Missing bind path)

开发驱动程序时,您可能会在日志中遇到以下出错提示:

Could not load driver: fuchsia-pkg://fuchsia.com/fake-battery#meta/fake_battery.cm: Failed to open bind file 'meta/bind/fake-battery-driver_2.bindbc'

或

Could not load driver: fuchsia-pkg://fuchsia.com/fake-battery#meta/fake_battery.cm: Missing bind path

当驱动程序的组件清单 (.cml) 文件中缺少 bind 字段或该字段不正确时,会发生此错误,例如:

{
    include: [
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/fake_battery.so",
        bind: <incorrect bind file path>,
    },
}

bind 字段值需要遵循 meta/bind/<bind_output> 格式,其中 bind_output 是 build 文件中 driver_bind_rules 目标 中具有相同名称的字段,例如:

driver_bind_rules("fake_battery_bind") {
  rules = "meta/fake-battery-driver.bind"
  bind_output = "fake-battery-driver.bindbc"
  deps = [ "//src/devices/bind/fuchsia.test" ]
  deps += [ "//src/devices/bind/fuchsia.platform" ]
}

对于此绑定规则目标示例,组件清单文件中的正确 bind 字段如下所示:

{
    include: [
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/fake_battery.so",
        bind: "meta/bind/fake-battery-driver.bindbc",
    },
}

但是,如果未在绑定规则目标中明确定义 bind_output, 则默认值是目标名称,并在其后附加 .bindbc,在上面的示例中为 fake_battery_bind.bindbc。

Failed to start driver, missing 'binary' argument: ZX_ERR_NOT_FOUND

编写驱动程序时,您可能会在日志中遇到以下出错提示:

Failed to start driver, missing 'binary' argument: ZX_ERR_NOT_FOUND

binary 字段值需要遵循 driver/<driver_output>.so 格式,其中 driver_output 是 build 文件中 fuchsia_driver 目标中具有相同名称的字段,例如:

fuchsia_cc_driver("driver") {
  output_name = "simple"
  sources = [ "simple_driver.cc" ]
  deps = [
    "//sdk/fidl/fuchsia.driver.compat:fuchsia.driver.compat_cpp",
    "//sdk/lib/driver/compat/cpp",
    "//sdk/lib/driver/component/cpp",
    "//src/devices/bind/fuchsia.test:fuchsia.test_cpp",
    "//src/devices/lib/driver:driver_runtime",
  ]
}

对于此绑定规则目标示例,组件清单文件中的正确 binary 字段如下所示:

{
    include: [
        "driver_component/driver.shard.cml",
        "inspect/client.shard.cml",
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "driver",
        binary: "driver/simple.so",
        bind: "meta/bind/simple_driver.bindbc",
    },
}

no member named 'destroy' in 'fdf_internal::DriverServer<...>'

如果驱动程序实现开放 FIDL 协议(即 open protocol <PROTOCOL_NAME>),则驱动程序需要替换并实现 handle_unknown_method() 函数。

例如,假设驱动程序实现以下协议:

open protocol VirtualController {
    ...
};

然后,此驱动程序需要包含以下 handle_unknown_method() 函数:

void handle_unknown_method(
    fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::VirtualController> metadata,
    fidl::UnknownMethodCompleter::Sync& completer) override;

其他资源