驅動程式資訊清單語言 (DML) 參考資料

本文將全面介紹 Fuchsia Driver Framework v2 (DFv2) 中的驅動程式資訊清單語言 (DML)。


1. 總覽

在 Driver Framework v2 (DFv2) 中,驅動程式是由驅動程式庫執行元件執行的元件。過去,編寫 DFv2 驅動程式庫時,需要維護多個不相連的設定檔: * 元件資訊清單 (.cml):宣告能力、使用的服務、執行元件設定和二進位檔位置。* 繫結規則檔案 (.bind):宣告驅動程式庫比對規則、硬體 ID 和複合式父項規格。* 主機板設定 (.fidl):主機板驅動程式需要主機板拓撲資訊清單、能力路徑對應和靜態中繼資料酬載。* 中繼資料還原序列化樣板:手動還原序列化結構化 FIDL 字典或原始中繼資料位元組。

分別維護這些檔案會導致重複:服務名稱、父項節點名稱和硬體通訊協定需求會在 .cml 和 .bind 檔案中重複,造成漂移和設定錯誤的風險。

DML (驅動程式資訊清單語言) 提供單一的統一宣告式 JSON5 資訊清單 (.dml),解決了這個問題。DML 編譯器 (dmlc) 會處理 .dml 檔案,並自動產生:1. 元件資訊清單 (.cml):已清除並驗證的元件資訊清單,可與 cmc 進行編譯。 2. 繫結規則 (.bind):將使用 bindc 編譯的程式原始碼繫結至驅動程式庫位元組碼 (.bindbc)。 3. FIDL 板級設定:平台匯流排驅動程式的板級拓撲二進位檔 (BoardConfig)。4. C++ 和 Rust 中繼資料剖析器:從內嵌 JSON 結構定義自動產生的型別安全剖析器程式庫。

DML 資訊清單的正式結構定義已在 //src/devices/tools/dmlc/dml.schema.json 中簽入。


2. 驅動程式資訊清單與主機板資訊清單

DML 支援兩大類資訊清單:

功能 司機資訊清單 (compile-driver) 主機板資訊清單 (compile-board)
目標 獨立或複合式葉片/中介裝置驅動程式 系統板驅動程式 (例如 Vim3、QEMU、Astro)
輸出內容 .cml、.bind、選用的 C++/Rust 中繼資料剖析器 .cml、.bind、.fidl 主面板設定
頂層區段 name,program,use,capabilities,expose,include,config name、program、children、offer、metadata_mappings、use、include、capabilities、expose
繫結樣式 非複合式 (program.bind) 或複合式 (use[].bind) 平台匯流排裝置繫結 (program.bind)
硬體節點 取用父項節點 宣告子節點和能力路徑 (offer)

3. 語法參考資料

3.1 頂層屬性

{
  // Name of the driver or board component (required).
  name: "sample_driver",

  // Optional composite name override. Defaults to 'name' with '-' replaced by '_'.
  composite_name: "custom_composite_name",

  // Shards and manifests to include.
  include: [
    "//sdk/lib/driver/compat/compat.shard.cml",
    "subsystem.shard.dml"
  ],

  // Execution parameters and standalone bind rules.
  program: { ... },

  // Capabilities consumed, and parent node definitions for composite drivers.
  use: [ ... ],

  // Capabilities provided by this driver, including metadata schemas.
  capabilities: [ ... ],

  // Capabilities exposed to the framework or parent component.
  expose: [ ... ],

  // Optional structured component configuration schema.
  config: { ... },

  // Board manifest only: child device node definitions.
  children: [ ... ],

  // Board manifest only: capability offers and constraint routing.
  offer: [ ... ],

  // Board manifest only: rules for aggregating metadata across children.
  metadata_mappings: [ ... ]
}

3.2「program」專區

program 區段會定義獨立 (非複合) 驅動程式的驅動程式庫執行參數和繫結規則。

屬性 類型 說明
runner string 元件執行元件名稱。預設值為 "driver"。
binary string 驅動程式庫共用程式庫的路徑 (例如 "driver/sample.so")。預設為 "driver/<name>.so"。
compat string DFv1 相容性驅動程式庫共用程式庫的路徑 (例如 "driver/sample_compat.so")。
colocate 字串或布林值 是否將驅動程式庫共置於父項驅動程式代管程序 ("true" 或 true)。
default_dispatcher_opts 字串陣列 驅動程式庫預設調度員的選項 (例如 ["allow_sync_calls"])。
bind/requirements 物件 獨立驅動程式的結構化繫結區塊 (請參閱「硬體匯流排區塊」)。注意:DML 不允許使用字串繫結路徑。
driver_name string 覆寫驅動程式名稱 (主要用於主機板資訊清單)。

範例:獨立驅動程式 program 封鎖

program: {
  binary: "driver/usb_mass_storage.so",
  colocate: "true",
  default_dispatcher_opts: [ "allow_sync_calls" ],
  bind: {
    protocol: "fuchsia.usb.BIND_PROTOCOL.INTERFACE",
    usb: {
      class: "fuchsia.usb.BIND_USB_CLASS.MASS_STORAGE",
      subclass: "fuchsia.usb.massstorage.BIND_USB_SUBCLASS.SCSI",
      protocol: "fuchsia.usb.massstorage.BIND_USB_PROTOCOL.BULK_ONLY"
    }
  }
}

3.3「use」部分

「use」部分有雙重用途: 1. 元件功能路由:傳遞標準 CML 功能 (service、protocol、directory)。 2. 複合式父項節點規格:定義複合式驅動程式庫繫結的父項節點。

屬性 類型 說明
service string FIDL 服務名稱 (例如 "fuchsia.hardware.gpio.Service")。會在父項繫結規則中發出 fuchsia.Service == "<service>";。
protocol string FIDL 通訊協定名稱。
banjo string Banjo 通訊協定 ID (例如 "fuchsia.gpio.BIND_PROTOCOL.DEVICE")。會在繫結規則中發出 fuchsia.BIND_PROTOCOL == <banjo>;,並從 CML 中排除。
name/instance_name string 複合裝置規格中父項節點的名稱。
primary 布林值 在一個 use 項目中設為 true,將其指定為主要父項節點。
availability string "required" (預設) 或 "optional"。在繫結規則中產生 optional parent "<name>"。
transport string 服務傳輸方式:"Zircon"、"Driver" 或 "Banjo"。
generate_bind_rule 布林值 預設為 true。如果設為 false,這項能力會透過 CML 使用,不會產生複合父項繫結節點。
bind/requirements 物件 這個父項節點的內嵌繫結規則 (請參閱「硬體匯流排區塊」)。

初始化步驟的特殊處理方式

系統會自動偵測代表平台初始化步驟的服務 (例如 fuchsia.gpio.Init 和 fuchsia.pwm.Init): * 這些服務會產生適當的繫結規則 (例如 fuchsia.BIND_INIT_STEP == fuchsia.gpio.BIND_INIT_STEP.GPIO;)。 * 這些服務會自動從執行階段 CML use 項目中篩除,因為初始化步驟是暫時的繫結里程碑,而非可連線的執行階段 FIDL 服務。


3.4 capabilities 和 expose 區段

功能

宣告驅動程式庫提供的功能。除了標準 CML 能力類型,DML 也支援架構導向的中繼資料定義:

capabilities: [
  {
    service: "fuchsia.hardware.buttons.Service"
  },
  {
    metadata: {
      id: "fuchsia.hardware.buttons.Metadata",
      schema: {
        title: "ButtonsMetadata",
        type: "object",
        definitions: {
          ButtonItem: {
            type: "object",
            properties: {
              type: { type: "integer", fuchsia_type: "uint8" },
              gpio: { type: "integer", fuchsia_type: "uint32" }
            },
            required: [ "type", "gpio" ]
          }
        },
        properties: {
          buttons: {
            type: "array",
            items: {
              "$ref": "#/definitions/ButtonItem"
            }
          }
        },
        required: [ "buttons" ]
      }
    }
  }
]

使用 --h-output、--cc-output 或 --rs-output 執行 dmlc compile-driver 時,會產生完整的 C++ 和 Rust 剖析器程式碼,可根據結構定義剖析及驗證傳入的中繼資料。

公開

標準 DFv2 能力曝光:

expose: [
  {
    service: "fuchsia.hardware.buttons.Service",
    from: "self"
  }
]

3.5 板子資訊清單頂層區段

主機板資訊清單 (使用 dmlc compile-board 編譯) 可設定平台匯流排和拓撲:

children

宣告發布至平台匯流排的裝置:

children: [
  {
    name: "adc-buttons",
    url: "fuchsia-pkg://fuchsia.com/adc-buttons#meta/adc-buttons.cm",
    compatible: "fuchsia,adc-buttons",
    metadata: [
      {
        id: "fuchsia.hardware.adc.Metadata",
        data: [ 1, 0, 0, 0 ]
      }
    ]
  }
]

offer

在父項控制器和子項驅動程式之間,根據硬體限制條件傳送路徑功能:

offer: [
  {
    service: "fuchsia.hardware.gpio.Service",
    name: "power",
    from: "#gpio-controller-ff634400",
    to: "#gpio-buttons",
    constraints: {
      pin: 92,
      name: "power"
    }
  }
]
使用者空間中斷控制器

當平台裝置耗用由使用者空間中斷控制器驅動程式庫管理的中斷 (而非直接由核心中斷控制器管理) 時,需要兩個 offer 項目: 1. 將路徑 fuchsia.hardware.interrupt.ControllerRegistryService 從 "parent" 路由至中斷控制器節點 (name: "pdev")。dmlc 會自動為該控制器節點分配專屬的平台匯流排 interrupt_controller_id。 2. 透過 controller: "#<controller-node>" 參照消費者 fuchsia.hardware.platform.device.Service interrupts 限制中的控制器節點。

offer: [
  {
    name: "pdev",
    service: "fuchsia.hardware.interrupt.ControllerRegistryService",
    from: "parent",
    to: "#gpio-controller-ff634400",
  },
  {
    name: "pdev",
    service: "fuchsia.hardware.platform.device.Service",
    from: "parent",
    to: "#touchscreen",
    constraints: {
      interrupts: [
        {
          name: "touch-irq",
          number: 14,
          mode: "EdgeLow",
          controller: "#gpio-controller-ff634400",
        },
      ],
    },
  },
]

metadata_mappings

將子項裝置限制彙整至統一的 FIDL 中繼資料:

metadata_mappings: [
  {
    metadata_id: "fuchsia.hardware.pinimpl.Metadata",
    aggregations: [
      {
        service: "fuchsia.hardware.gpio.Service",
        field: "pins"
      },
      {
        service: "fuchsia.hardware.pin.PinStatesService",
        field: "device_pin_states",
        use_node_name: true
      }
    ]
  }
]

4. 硬體匯流排區塊

DML 提供主要硬體互連和探索通訊協定的結構化區塊。

4.1 平台匯流排和裝置樹狀結構

平台裝置會比對供應商 ID (VID)、產品 ID (PID)、裝置 ID (DID) 或裝置樹狀結構 compatible 字串:

bind: {
  vid: "fuchsia.khadas.platform.BIND_PLATFORM_DEV_VID.KHADAS",
  pid: "fuchsia.khadas.platform.BIND_PLATFORM_DEV_PID.VIM3",
  did: "fuchsia.platform.BIND_PLATFORM_DEV_DID.GPIO",
  compat: "fuchsia,gpio-buttons"
}

產生的繫結規則: bind fuchsia.BIND_PLATFORM_DEV_VID == fuchsia.khadas.platform.BIND_PLATFORM_DEV_VID.KHADAS; fuchsia.BIND_PLATFORM_DEV_PID == fuchsia.khadas.platform.BIND_PLATFORM_DEV_PID.VIM3; fuchsia.BIND_PLATFORM_DEV_DID == fuchsia.platform.BIND_PLATFORM_DEV_DID.GPIO; fuchsia.COMPATIBLE == "fuchsia,gpio-buttons";

4.2 PCI 匯流排

PCI 裝置會根據 PCI 供應商、裝置、類別、子類別、介面、修訂版本或拓撲進行比對:

bind: {
  service: "fuchsia.hardware.pci.Service",
  pci: {
    vid: "fuchsia.pci.BIND_PCI_VID.INTEL",
    did: "0x1234",
    class: "fuchsia.pci.BIND_PCI_CLASS.GENERIC_SYSTEM_PERIPHERAL",
    subclass: "0x05",
    interface: "0x01",
    revision: "0x04",
    topo: "0x05"
  }
}

產生的繫結規則: bind fuchsia.Service == "fuchsia.hardware.pci.Service"; fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.INTEL; fuchsia.BIND_PCI_DID == 0x1234; fuchsia.BIND_PCI_CLASS == fuchsia.pci.BIND_PCI_CLASS.GENERIC_SYSTEM_PERIPHERAL; fuchsia.BIND_PCI_SUBCLASS == 0x05; fuchsia.BIND_PCI_INTERFACE == 0x01; fuchsia.BIND_PCI_REVISION == 0x04; fuchsia.BIND_PCI_TOPO == 0x05;

4.3 USB 匯流排

USB 介面和裝置的 USB 供應商、產品、類別、子類別、通訊協定和介面編號相符:

bind: {
  usb: {
    vid: "fuchsia.usb.BIND_USB_VID.GOOGLE",
    pid: "0x1234",
    class: "fuchsia.usb.BIND_USB_CLASS.MASS_STORAGE",
    subclass: "0x02",
    protocol: 0,
    interface_number: 1,
    bind_protocol: "fuchsia.usb.BIND_PROTOCOL.INTERFACE"
  }
}

產生的繫結規則: bind fuchsia.BIND_PROTOCOL == fuchsia.usb.BIND_PROTOCOL.INTERFACE; fuchsia.BIND_USB_VID == fuchsia.usb.BIND_USB_VID.GOOGLE; fuchsia.BIND_USB_PID == 0x1234; fuchsia.BIND_USB_CLASS == fuchsia.usb.BIND_USB_CLASS.MASS_STORAGE; fuchsia.BIND_USB_SUBCLASS == 0x02; fuchsia.BIND_USB_PROTOCOL == 0; fuchsia.BIND_USB_INTERFACE_NUMBER == 1;

4.4 ACPI 匯流排

ACPI 裝置會根據硬體 ID (hid)、相容 ID (first_cid) 或 ACPI 匯流排類型進行比對:

bind: {
  acpi: {
    hid: "PNP0C0A",
    first_cid: "PNP0C0B",
    bus_type: "fuchsia.acpi.BIND_ACPI_BUS_TYPE.PCI"
  }
}

產生的繫結規則: bind fuchsia.acpi.HID == "PNP0C0A"; fuchsia.acpi.FIRST_CID == "PNP0C0B"; fuchsia.BIND_ACPI_BUS_TYPE == fuchsia.acpi.BIND_ACPI_BUS_TYPE.PCI;


5. 複合繫結規則和父項比對

複合式驅動程式必須繫結至多個父項裝置 (例如平台裝置、GPIO 針腳和 I2C 匯流排)。

5.1 主要父項節點

在 use 項目中,使用 primary: true 指定主要父項:

use: [
  {
    service: "fuchsia.hardware.platform.device.Service",
    name: "pdev",
    primary: true,
    bind: {
      compat: "sample,buttons"
    }
  },
  {
    service: "fuchsia.hardware.gpio.Service",
    name: "mic-mute",
    availability: "optional"
  }
]

產生的繫結規則: ```bind composite sample_composite;

使用紫紅色;

primary parent "pdev" { fuchsia.COMPATIBLE == "sample,buttons"; }

optional parent "mic-mute" { fuchsia.Service == "fuchsia.hardware.gpio.Service"; } ```

5.2 父節點比對 (match_name: true)

如果驅動程式庫連線至多個相同服務類型的父項 (例如多個 GPIO 接腳或 ADC),請在父項的 bind 區塊內指定 match_name: true。dmlc 會自動發出符合父項拓撲節點名稱 (fuchsia.NAME) 的規則:

use: [
  {
    service: "fuchsia.hardware.gpio.Service",
    name: "volume-up",
    bind: {
      match_name: true
    }
  }
]

產生的繫結規則: bind parent "volume-up" { fuchsia.Service == "fuchsia.hardware.gpio.Service"; fuchsia.NAME == "volume-up"; }

5.3 父項分組

如果多個 use 項目參照同一個父項 name (例如 Banjo 介面和 FIDL 服務或 init 步驟各有一個項目),dmlc 會自動將這些項目分組為單一父項規格:

use: [
  {
    service: "fuchsia.gpio.Init",
    name: "gpio-init"
  },
  {
    service: "fuchsia.hardware.gpio.Service",
    name: "gpio-init",
    availability: "optional"
  }
]

產生的繫結規則: bind parent "gpio-init" { fuchsia.BIND_INIT_STEP == fuchsia.gpio.BIND_INIT_STEP.GPIO; fuchsia.Service == "fuchsia.hardware.gpio.Service"; }


6. 接受條件式分支和陣列

6.1 陣列接受 (accept)

如要比對多個可接受的值,請傳遞陣列,而非單一純量。dmlc 會在繫結規則中產生 accept <property> { ... } 區塊。

bind: {
  pci: {
    vid: "fuchsia.pci.BIND_PCI_VID.INTEL",
    did: [ "0x1234", "0x5678" ]
  }
}

產生的繫結規則: bind fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.INTEL; accept fuchsia.BIND_PCI_DID { 0x1234, 0x5678, }

適用於 compat、vid、pid、did、PCI 屬性、USB 屬性、ACPI hid 和自訂規則。

6.2 條件式分支 (one_of)

如果比對邏輯需要在不同匯流排或裝置世代之間分支,請使用 one_of。dmlc 會檢查每個分支的觸發條件,並將其編譯為 if ... else if ... else ... 陳述式。

bind: {
  one_of: [
    {
      pci: {
        vid: "fuchsia.pci.BIND_PCI_VID.INTEL",
        did: "0x1234"
      }
    },
    {
      pci: {
        class: "0x02",
        subclass: "0x00"
      }
    }
  ]
}

產生的繫結規則: bind if fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.INTEL { fuchsia.BIND_PCI_DID == 0x1234; } else if fuchsia.BIND_PCI_CLASS == 0x02 { fuchsia.BIND_PCI_SUBCLASS == 0x00; } else { false; }

one_of 支援的觸發條件屬性包括: * node_name / fuchsia.NAME * acpi.hid 和 acpi.bus_type * compat (fuchsia.COMPATIBLE) * protocol / banjo (fuchsia.BIND_PROTOCOL) * service (fuchsia.Service) * vid、pid、did * pci.vid、pci.did、pci.class * usb.vid、usb.pid、usb.class * 自訂 rules 鍵 (例如 fuchsia.BIND_AUTOBIND)

6.3 不等式限制

如要要求屬性「不」等於某個值,請在 rules 中指定 { "neq": <value> }:

bind: {
  rules: {
    "fuchsia.BIND_COMPOSITE": { neq: 1 }
  }
}

產生的繫結規則: bind fuchsia.BIND_COMPOSITE != 1;


7. 實際前後對比範例

範例 1:獨立的 USB 大量儲存裝置驅動程式

之前:個別檔案

meta/usb_mass_storage.cml: json5 { include: [ "inspect/client.shard.cml", "syslog/client.shard.cml", ], program: { runner: "driver", binary: "driver/usb_mass_storage.so", bind: "meta/bind/usb_mass_storage.bindbc", colocate: "true", default_dispatcher_opts: [ "allow_sync_calls" ], }, capabilities: [ { service: "fuchsia.hardware.block.volume.Service" } ], expose: [ { service: "fuchsia.hardware.block.volume.Service", from: "self" } ] }

meta/usb_mass_storage.bind: ```繫結 using fuchsia.usb; using fuchsia.usb.massstorage;

fuchsia.BIND_PROTOCOL == fuchsia.usb.BIND_PROTOCOL.INTERFACE; fuchsia.BIND_USB_CLASS == fuchsia.usb.BIND_USB_CLASS.MASS_STORAGE; fuchsia.BIND_USB_SUBCLASS == fuchsia.usb.massstorage.BIND_USB_SUBCLASS.SCSI; fuchsia.BIND_USB_PROTOCOL == fuchsia.usb.massstorage.BIND_USB_PROTOCOL.BULK_ONLY; ```

變更後:統一 meta/usb_mass_storage.dml

{
  name: "usb-mass-storage",
  program: {
    colocate: "true",
    default_dispatcher_opts: [ "allow_sync_calls" ],
    bind: {
      protocol: "fuchsia.usb.BIND_PROTOCOL.INTERFACE",
      usb: {
        class: "fuchsia.usb.BIND_USB_CLASS.MASS_STORAGE",
        subclass: "fuchsia.usb.massstorage.BIND_USB_SUBCLASS.SCSI",
        protocol: "fuchsia.usb.massstorage.BIND_USB_PROTOCOL.BULK_ONLY"
      }
    }
  },
  capabilities: [
    { service: "fuchsia.hardware.block.volume.Service" }
  ],
  expose: [
    {
      service: "fuchsia.hardware.block.volume.Service",
      from: "self"
    }
  ]
}

範例 2:複合按鈕驅動程式

之前:個別檔案

meta/buttons.cml: json5 { include: [ "inspect/client.shard.cml", "syslog/client.shard.cml", ], program: { runner: "driver", binary: "driver/buttons.so", bind: "meta/bind/buttons.bindbc", }, use: [ { service: "fuchsia.hardware.platform.device.Service" }, { service: "fuchsia.hardware.gpio.Service" }, ], capabilities: [ { service: "fuchsia.input.report.Service" } ], expose: [ { service: "fuchsia.input.report.Service", from: "self" } ] }

meta/buttons.bind: ```繫結 composite buttons;

using fuchsia; using fuchsia.gpio;

primary parent "pdev" { fuchsia.COMPATIBLE == "fuchsia,gpio-buttons"; }

optional parent "gpio-init" { fuchsia.BIND_INIT_STEP == fuchsia.gpio.BIND_INIT_STEP.GPIO; }

選用父項「volume-up」{ fuchsia.Service == "fuchsia.hardware.gpio.Service"; fuchsia.NAME == "volume-up"; } ```

變更後:統一 meta/buttons.dml

{
  name: "buttons",
  use: [
    {
      service: "fuchsia.hardware.platform.device.Service",
      name: "pdev",
      primary: true,
      bind: {
        compat: "fuchsia,gpio-buttons"
      }
    },
    {
      service: "fuchsia.gpio.Init",
      name: "gpio-init",
      availability: "optional"
    },
    {
      service: "fuchsia.hardware.gpio.Service",
      name: "volume-up",
      availability: "optional",
      bind: {
        match_name: true
      }
    }
  ],
  capabilities: [
    { service: "fuchsia.input.report.Service" }
  ],
  expose: [
    {
      service: "fuchsia.input.report.Service",
      from: "self"
    }
  ]
}

請注意: * pdev 是明確的主要父項。 * fuchsia.gpio.Init 會轉換為 init 步驟繫結規則,並自動從產生的 CML 中篩除。 * volume-up 會自動與 fuchsia.NAME == "volume-up" 相符。match_name: true