排解 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

如果清單中沒有顯示驅動程式庫,表示載入失敗。

可能原因如下:

  • 建構套件不含驅動程式。
  • 驅動程式庫的元件資訊清單 (.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 為基礎的節點屬性,您必須在子項中新增供應項目,才能納入以 FIDL 為基礎的屬性。

舉例來說,假設繫結規則指定下列條件:

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

接著,目標節點的父項驅動程式庫必須將下列供應項目新增至節點:

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),且不會回覆完成者。

如果驅動程式庫的 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. 檢查能力轉送問題。

此外,如要查看 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. 檢查能力轉送問題

請參閱下方的「功能路徑錯誤」一節。

能力轉送錯誤

如要在 Fuchsia 中使用服務,必須在執行階段將服務的功能正確轉送至驅動程式庫。如要將能力轉送至子驅動程式庫,父項驅動程式庫必須公開能力,並提供給子項。

下列指令有助於診斷能力轉送問題:

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

如要解決能力轉送相關問題,請嘗試下列步驟:

  1. 公開功能。
  2. 提供功能。
  3. 使用這項功能。

此外,如要瞭解功能在 DFv2 驅動程式中的路徑,請參閱這些傳輸範例。

1. 公開能力

如果父項驅動程式庫未正確公開能力,記錄中可能會顯示類似下列的訊息:

[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

如要修正這個問題,請檢查父項驅動程式庫程式的元件資訊清單 (.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. 提供能力

如要檢查子節點中是否有服務,請執行下列指令 (您也可以使用 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

此外,如果在子節點中找不到優惠,記錄中可能會顯示類似下方的訊息:

[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

如要修正這個問題,請檢查父項驅動程式庫程式包含提供給子項節點的供應項目設定,例如:

// 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. 使用能力

如要使用能力,驅動程式庫必須宣告要使用該能力。

如果未正確宣告,記錄中可能會顯示類似下方的訊息:

[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.

如要修正這個問題,請檢查驅動程式庫的元件資訊清單 (.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 not found

發生這項錯誤時,請檢查驅動程式庫的元件資訊清單 (.cml) 檔案是否正確。請參閱「駕駛人無法開啟」。

目標元件無法使用必要服務 <...>

如果驅動程式庫未正確公開服務能力,且未從父項驅動程式庫轉送至該功能,就會發生這項錯誤。

如要修正這個問題,請嘗試能力轉送錯誤工作流程。

不過,如果所需服務是 fuchsia.driver.compat,請按照下列操作說明進行:

如果驅動程式庫沒有從父項驅動程式庫轉送的 fuchsia.driver.compat 服務,就會發生錯誤。如要轉送這項服務,父項驅動程式庫必須為每個子項設定相容裝置伺服器。

如要修正這個問題,請從受影響的驅動程式庫開始,逐一檢查每個父項驅動程式庫。舉例來說,如果節點拓撲顯示 A->B->C->D,且 D 是受影響的驅動程式庫,請依序查看 C、B 和 A。請確認鏈結中每個父項驅動程式庫的相容裝置伺服器都已正確設定。

以下是設定相容裝置伺服器時常見的問題:

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__ 符號

開發驅動程式庫時,記錄中可能會出現下列錯誤訊息:

[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: 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 是建構檔案中 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。

無法啟動驅動程式庫,缺少「binary」引數:ZX_ERR_NOT_FOUND

編寫驅動程式庫時,記錄中可能會出現下列錯誤訊息:

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

binary 欄位值必須採用 driver/<driver_output>.so 格式,其中 driver_output 是建構檔案中 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;

其他資源