本文將全面介紹 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