| # Driver Manifest Language (DML) Reference | |
| This document provides a comprehensive reference for the **Driver Manifest Language (DML)** in | |
| Fuchsia's [Driver Framework v2 (DFv2)][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`](https://cs.opensource.google/fuchsia/fuchsia/+/main: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`, `offer`, `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 (`offer`) | | |
| --- | |
| ## 3. Syntax Reference | |
| ### 3.1 Top-Level Properties | |
| ```json5 | |
| { | |
| // 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 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](#4-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 | |
| ```json5 | |
| 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](#4-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**: | |
| ```json5 | |
| 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: | |
| ```json5 | |
| 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: | |
| ```json5 | |
| 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` | |
| Routes capabilities between parent controllers and child drivers with hardware constraints: | |
| ```json5 | |
| offer: [ | |
| { | |
| service: "fuchsia.hardware.gpio.Service", | |
| name: "power", | |
| from: "#gpio-controller-ff634400", | |
| to: "#gpio-buttons", | |
| constraints: { | |
| pin: 92, | |
| name: "power" | |
| } | |
| } | |
| ] | |
| ``` | |
| ##### Userspace Interrupt Controllers | |
| When a platform device consumes an interrupt managed by a userspace interrupt controller driver (rather than the kernel interrupt controller directly), two `offer` entries are required: | |
| 1. Route `fuchsia.hardware.interrupt.ControllerRegistryService` from `"parent"` to the interrupt controller node (`name: "pdev"`). `dmlc` automatically allocates a unique platform bus `interrupt_controller_id` for that controller node. | |
| 2. Reference the controller node in the consumer's `fuchsia.hardware.platform.device.Service` `interrupts` constraint via `controller: "#<controller-node>"`. | |
| ```json5 | |
| 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` | |
| Aggregates child device constraints into unified FIDL metadata: | |
| ```json5 | |
| 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: | |
| ```json5 | |
| 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: | |
| Note: Legacy flat PCI fields (`pci_class`, `pci_subclass`, `pci_interface`) are rejected by `dmlc`. Always use the structured `pci: { ... }` block. | |
| ```json5 | |
| 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: | |
| ```json5 | |
| 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: | |
| ```json5 | |
| 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: | |
| ```json5 | |
| 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"; | |
| } | |
| ``` | |
| Note: If a driver is a composite driver, specifying bind rules under `program.bind` is forbidden. Place all bind rules in the corresponding `use` entries instead. | |
| ### 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`): | |
| ```json5 | |
| 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: | |
| ```json5 | |
| 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. | |
| ```json5 | |
| 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. | |
| ```json5 | |
| 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`: | |
| ```json5 | |
| 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` | |
| ```json5 | |
| { | |
| 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` | |
| ```json5 | |
| { | |
| 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"`. | |
| --- | |
| [dfv2]: /docs/development/drivers/dfv2-overview.md |