This document provides a comprehensive reference for the Driver Manifest Language (DML) in Fuchsia's Driver Framework v2 (DFv2).
1. Overview
In Driver Framework v2 (DFv2), drivers are components executed by the driver runner.
Historically, authoring a DFv2 driver required maintaining multiple disconnected configuration files:
* A Component Manifest (.cml): Declaring capabilities, used services, runner configuration, and binary locations.
* A Bind Rules File (.bind): Declaring driver matching rules, hardware identifiers, and composite parent specifications.
* Board Configurations (.fidl): Board drivers required board topology manifests, capability route mappings, and static metadata payloads.
* Metadata Deserialization Boilerplate: Manual deserialization of structured FIDL dictionaries or raw metadata bytes.
Maintaining these files separately introduced duplication: service names, parent node names, and
hardware protocol requirements were repeated across both .cml and .bind files, creating a risk
of drift and configuration bugs.
DML (Driver Manifest Language) solves this by providing a single, unified declarative JSON5 manifest
(.dml). The DML compiler (dmlc) processes .dml files and automatically produces:
1. Component Manifests (.cml): Cleaned and validated component manifests ready for compilation with cmc.
2. Bind Rules (.bind): Bind program source code compiled with bindc into driver bytecode (.bindbc).
3. FIDL Board Configurations: Board topology binaries (BoardConfig) for platform bus drivers.
4. C++ and Rust Metadata Parsers: Type-safe parser libraries automatically generated from embedded JSON Schemas.
The official schema for DML manifests is checked in at
//src/devices/tools/dmlc/dml.schema.json.
2. Driver Manifest vs Board Manifest
DML supports two categories of manifests:
| Feature | Driver Manifest (compile-driver) |
Board Manifest (compile-board) |
|---|---|---|
| Target | Standalone or composite leaf/intermediate device drivers | System board drivers (e.g. Vim3, QEMU, Astro) |
| Outputs | .cml, .bind, optional C++/Rust metadata parsers |
.cml, .bind, .fidl board configuration |
| Top-Level Sections | name, program, use, capabilities, expose, include, config |
name, program, children, offers, metadata_mappings, use, include, capabilities, expose |
| Binding Style | Non-composite (program.bind) or composite (use[].bind) |
Platform bus device binding (program.bind) |
| Hardware Nodes | Consumes parent nodes | Declares child nodes and capability routes (offers) |
3. Syntax Reference
3.1 Top-Level Properties
{
// 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.
offers: [ ... ],
// Board manifest only: rules for aggregating metadata across children.
metadata_mappings: [ ... ]
}
3.2 The program Section
The program section defines driver execution parameters and bind rules for standalone (non-composite) drivers.
| Property | Type | Description |
|---|---|---|
runner |
string | Component runner name. Defaults to "driver". |
binary |
string | Path to driver shared library (e.g. "driver/sample.so"). Defaults to "driver/<name>.so". |
compat |
string | Path to DFv1 compatibility driver shared library (e.g. "driver/sample_compat.so"). |
colocate |
string or boolean | Whether to colocate the driver in its parent driver host ("true" or true). |
default_dispatcher_opts |
array of string | Options for the driver's default dispatcher (e.g. ["allow_sync_calls"]). |
bind / requirements |
object | Structured bind block for standalone drivers (see Hardware Bus Blocks). Note: string bind paths are not allowed in DML. |
driver_name |
string | Driver name override (primarily used in board manifests). |
Example: Standalone Driver program Block
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 The use Section
The use section serves a dual purpose:
1. Component Capability Routing: Passing standard CML capabilities (service, protocol, directory).
2. Composite Parent Node Specification: Defining the parent nodes that a composite driver binds to.
| Property | Type | Description |
|---|---|---|
service |
string | FIDL service name (e.g. "fuchsia.hardware.gpio.Service"). Emits fuchsia.Service == "<service>"; in parent bind rules. |
protocol |
string | FIDL protocol name. |
banjo |
string | Banjo protocol identifier (e.g. "fuchsia.gpio.BIND_PROTOCOL.DEVICE"). Emits fuchsia.BIND_PROTOCOL == <banjo>; in bind rules and is excluded from CML. |
name / instance_name |
string | Name of the parent node in the composite device specification. |
primary |
boolean | Set to true on exactly one use entry to designate it as the primary parent node. |
availability |
string | "required" (default) or "optional". Generates optional parent "<name>" in bind rules. |
transport |
string | Service transport: "Zircon", "Driver", or "Banjo". |
generate_bind_rule |
boolean | Defaults to true. If set to false, the capability is consumed via CML without generating a composite parent bind node. |
bind / requirements |
object | Inline bind rules for this parent node (see Hardware Bus Blocks). |
Special Handling for Initialization Steps
Services representing platform init steps (such as fuchsia.gpio.Init and fuchsia.pwm.Init) are automatically detected:
* They generate the appropriate bind rule (e.g. fuchsia.BIND_INIT_STEP == fuchsia.gpio.BIND_INIT_STEP.GPIO;).
* They are automatically filtered out of runtime CML use entries because init steps are transient bind milestones rather than connectable runtime FIDL services.
3.4 The capabilities and expose Sections
Capabilities
Declares capabilities provided by the driver. In addition to standard CML capability types, DML supports schema-driven metadata definitions:
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" ]
}
}
}
]
When dmlc compile-driver is executed with --h-output, --cc-output, or --rs-output, it generates complete C++ and Rust parser code that parses and validates incoming metadata against the schema.
Expose
Standard DFv2 capability exposure:
expose: [
{
service: "fuchsia.hardware.buttons.Service",
from: "self"
}
]
3.5 Board Manifest Top-Level Sections
Board manifests (compiled with dmlc compile-board) configure the platform bus and topology:
children
Declares devices published to the platform bus:
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 ]
}
]
}
]
offers
Routes capabilities between parent controllers and child drivers with hardware constraints:
offers: [
{
service: "fuchsia.hardware.gpio.Service",
name: "power",
from: "#gpio-controller-ff634400",
to: "#gpio-buttons",
constraints: {
pin: 92,
name: "power"
}
}
]
metadata_mappings
Aggregates child device constraints into unified FIDL metadata:
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. Hardware Bus Blocks
DML provides structured blocks for major hardware interconnects and discovery protocols.
4.1 Platform Bus & Devicetree
Platform devices match on Vendor ID (VID), Product ID (PID), Device ID (DID), or Devicetree compatible strings:
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"
}
Generated bind rules:
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 Bus
PCI devices match on PCI vendor, device, class, subclass, interface, revision, or topology:
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"
}
}
Generated bind rules:
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 Bus
USB interfaces and devices match on USB vendor, product, class, subclass, protocol, and interface number:
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"
}
}
Generated bind rules:
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 Bus
ACPI devices match on Hardware ID (hid), Compatible ID (first_cid), or ACPI Bus Type:
bind: {
acpi: {
hid: "PNP0C0A",
first_cid: "PNP0C0B",
bus_type: "fuchsia.acpi.BIND_ACPI_BUS_TYPE.PCI"
}
}
Generated bind rules:
bind
fuchsia.acpi.HID == "PNP0C0A";
fuchsia.acpi.FIRST_CID == "PNP0C0B";
fuchsia.BIND_ACPI_BUS_TYPE == fuchsia.acpi.BIND_ACPI_BUS_TYPE.PCI;
5. Composite Binding Rules & Parent Matching
Composite drivers require binding to multiple parent devices (e.g. a platform device, plus GPIO pins, plus I2C buses).
5.1 Primary Parent Node
Designate the primary parent with primary: true in its use entry:
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"
}
]
Generated bind rules: ```bind composite sample_composite;
using fuchsia;
primary parent "pdev" { fuchsia.COMPATIBLE == "sample,buttons"; }
optional parent "mic-mute" { fuchsia.Service == "fuchsia.hardware.gpio.Service"; } ```
5.2 Parent Node Matching (match_name: true)
When a driver connects to multiple parents of the same service type (e.g. multiple GPIO pins or ADCs), specify match_name: true inside the parent's bind block. dmlc will automatically emit a rule matching the parent's topological node name (fuchsia.NAME):
use: [
{
service: "fuchsia.hardware.gpio.Service",
name: "volume-up",
bind: {
match_name: true
}
}
]
Generated bind rules:
bind
parent "volume-up" {
fuchsia.Service == "fuchsia.hardware.gpio.Service";
fuchsia.NAME == "volume-up";
}
5.3 Parent Grouping
If multiple use entries reference the same parent name (for example, one for a Banjo interface and another for a FIDL service or init step), dmlc automatically groups them into a single parent specification:
use: [
{
service: "fuchsia.gpio.Init",
name: "gpio-init"
},
{
service: "fuchsia.hardware.gpio.Service",
name: "gpio-init",
availability: "optional"
}
]
Generated bind rules:
bind
parent "gpio-init" {
fuchsia.BIND_INIT_STEP == fuchsia.gpio.BIND_INIT_STEP.GPIO;
fuchsia.Service == "fuchsia.hardware.gpio.Service";
}
6. Conditional Branching and Array Acceptance
6.1 Array Acceptance (accept)
To match against any one of multiple acceptable values, pass an array instead of a single scalar. dmlc will generate an accept <property> { ... } block in bind rules.
bind: {
pci: {
vid: "fuchsia.pci.BIND_PCI_VID.INTEL",
did: [ "0x1234", "0x5678" ]
}
}
Generated bind rules:
bind
fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.INTEL;
accept fuchsia.BIND_PCI_DID {
0x1234,
0x5678,
}
Works for compat, vid, pid, did, PCI properties, USB properties, ACPI hid, and custom rules.
6.2 Conditional Branching (one_of)
When matching logic requires branching across different buses or device generations, use one_of. dmlc inspects each branch's trigger conditions and compiles them into if ... else if ... else ... statements.
bind: {
one_of: [
{
pci: {
vid: "fuchsia.pci.BIND_PCI_VID.INTEL",
did: "0x1234"
}
},
{
pci: {
class: "0x02",
subclass: "0x00"
}
}
]
}
Generated bind rules:
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;
}
Supported trigger properties in one_of include:
* node_name / fuchsia.NAME
* acpi.hid and 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
* Custom rules keys (e.g. fuchsia.BIND_AUTOBIND)
6.3 Inequality Constraints
To require that a property does not equal a value, specify { "neq": <value> } in rules:
bind: {
rules: {
"fuchsia.BIND_COMPOSITE": { neq: 1 }
}
}
Generated bind rule:
bind
fuchsia.BIND_COMPOSITE != 1;
7. Real-World Before / After Examples
Example 1: Standalone USB Mass Storage Driver
Before: Separate Files
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:
```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; ```
After: Unified 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"
}
]
}
Example 2: Composite Buttons Driver
Before: Separate Files
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:
```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; }
optional parent "volume-up" { fuchsia.Service == "fuchsia.hardware.gpio.Service"; fuchsia.NAME == "volume-up"; } ```
After: Unified 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"
}
]
}
Notice that:
* pdev is explicitly the primary parent.
* fuchsia.gpio.Init is converted into an init step bind rule and automatically filtered out of the generated CML.
* volume-up with match_name: true automatically matches fuchsia.NAME == "volume-up".