blob: ca70a01c892a90131611ee8a4fcc91c3964f9d89 [file] [log] [blame] [view]
# Write bind rules for a driver
This guide walks through the steps for writing bind rules using the
[`i2c_temperature`][i2c-temperature-sample-driver] sample driver.
For a driver to bind to a [node][drivers-and-nodes] (which represents a hardware
or virtual device), the drivers bind rules must match the node properties of the
node. In this guide, well write bind rules for the `i2c_temperature` sample
driver so that they match the node properties of the `i2c-child` node.
The `i2c_controller` driver creates a child node named `i2c-child` for testing
the `i2c_temperature` sample driver. We can use this `i2c_controller` driver to
identify the node properties of the `i2c-child` node and write the matching
bind rules for `i2c_temperature`.
Before you begin, writing bind rules requires familiarity with the concepts
in [Driver binding][driver-binding].
The steps are:
1. [Identify the node properties](#identify-bind-properties).
1. [Write the bind rules](#write-bind-rules).
1. [Add a Bazel build target for the bind rules](#add-a-bazel-build-target).
## 1. Identify the node properties {:#identify-bind-properties}
You can identify the node properties of your target node in one of the following ways:
* [Use the ffx driver list-devices command](#use-ffx-driver-list-devices)
* [Look up the node properties in the driver source code](#look-up-the-driver-source-code)
### Use the ffx driver list-devices command {:#use-ffx-driver-list-devices}
To print the properties of every node in the Fuchsia system, run the following command:
```posix-terminal
ffx driver list-devices -v
```
This command prints the properties of a node in the following format:
```none {:.devsite-disable-click-to-copy}
Name : i2c-child
Moniker : root.sys.platform.pt.acpi.FWCF.i2c-child
Driver : None
2 Properties
[ 1/ 2] : Key "fuchsia.hardware.i2c.Service" Value Enum(fuchsia.hardware.i2c.Service.ZirconTransport)
[ 2/ 2] : Key fuchsia.BIND_I2C_ADDRESS Value 0x0000ff
```
The output above shows that the `i2c-child` node has the following node properties:
* Property key `fuchsia.hardware.i2c.Service` with an enum value of
`fuchsia.hardware.i2c.Service.ZirconTransport`.
* Property key `fuchsia.BIND_I2C_ADDRESS` with an integer value of `0xFF`.
### Look up the node properties in the driver source code {#look-up-the-driver-source-code}
When adding a child node, drivers can provide node properties to the node.
Reviewing the source code of the driver that creates your target node as a child
node helps you identify the node properties to include in your bind rules.
The `i2c_controller` driver creates a child node named `i2c-child` to which the
`i2c_temperature` sample driver binds. Examine the source code of the
`i2c_controller` driver to identify which node properties are passed to this
child node:
```cpp {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/i2c_temperature/controller/i2c_controller.cc" region_tag="add_child_properties" adjust_indentation="auto" %}
```
This code shows that the `i2c-child` node is created with the following bind
properties:
* Property key `fuchsia.hardware.i2c.Service` with an enum value of
`fuchsia.hardware.i2c.Service.ZirconTransport`.
* Property key `fuchsia.BIND_I2C_ADDRESS` with an integer value of `0xFF`.
Note: For more information on the `NodeAddArgs` struct used to pass the bind
properties to a child node, see
[NodeProperty and NodeAddArgs structs](#nodeproperty-and-nodeaddargs-structs).
## 2. Write the bind rules {:#write-bind-rules}
Once you know the node properties you want to match, you can use the bind
language to write the bind rules for your driver.
In the previous section, weve identified that the `i2c-child` node has the
following node properties:
* Property key `fuchsia.hardware.i2c` with an enum value of
`fuchsia.hardware.i2c.Service.ZirconTransport`.
* Property key `fuchsia.BIND_I2C_ADDRESS` with an integer value of `0xFF`.
To match these properties, the `i2c_temperature` driver declares the following
bind rules:
```none {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/i2c_temperature/driver/i2c_temperature.bind" region_tag="bind_rules" adjust_indentation="auto" %}
```
Integer-based node property keys that start with `BIND_` (defined in
[`binding_priv.h`][binding-prev-h] in the Fuchsia source tree) are old property
keys currently hardcoded in the bind compiler. See the following definition for
`BIND_I2C_ADDRESS` from `binding_priv.h`:
```cpp {:.devsite-disable-click-to-copy}
#define BIND_I2C_ADDRESS 0x0A02
```
When these keys are used in bind rules, they are prefixed with `fuchsia.`.
## 3. Add a Bazel build target for the bind rules {:#add-a-bazel-build-target}
Once you have written the bind rules for your driver, you need to update the
`BUILD.bazel` file to add a build target for the bind rules bytecode using the
`fuchsia_driver_bytecode_bind_rules()` template:
```bazel {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/i2c_temperature/driver/BUILD.bazel" region_tag="bind_rules" adjust_indentation="auto" %}
```
For each library used in your bind rules, add the library as a dependency to the
build target. For example, the `i2c_temperature` sample driver's bind rules use
the `fuchsia.hardware.i2c` library, so the build target includes the bind library
as a build dependency.
To determine which bind libraries are used in the bind rules, you can examine
the driver source code. In the node properties of the `i2c-child` node, the
first property key `fuchsia.hardware.i2c.Service` is from a generated bind
library from the FIDL protocol:
```cpp {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/i2c_temperature/controller/i2c_controller.cc" region_tag="add_child_properties" adjust_indentation="auto" highlight="3,4" %}
```
The prefix `fuchsia_hardware_i2c` implies that this node property’s key and
value are defined in the following header:
```cpp {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/i2c_temperature/controller/i2c_controller.cc" region_tag="bind_imports" adjust_indentation="auto" %}
```
These bind libraries will have corresponding dependencies in the driver's build
rules. See the following `fuchsia.hardware.i2c` dependency in the `i2c_controller`
binary target:
```bazel {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/sdk-samples/drivers" gerrit_path="src/i2c_temperature/controller/BUILD.bazel" region_tag="cc_binary" adjust_indentation="auto" highlight="12" %}
```
Note: For more information on the generated bind library, see
[Create a new bind library for a driver][bind-library-tutorial].
## Appendices
### NodeProperty and NodeAddArgs structs {:#nodeproperty-and-nodeaddargs-structs}
Node properties are represented by the `NodeProperty` struct in the
`fuchsia.driver.framework` FIDL library:
```fidl {:.devsite-disable-click-to-copy}
/// Definition of a property for a node. A property is commonly used to match a
/// node to a driver for driver binding.
type NodeProperty = table {
/// Key for the property.
1: key NodePropertyKey;
/// Value for the property.
2: value NodePropertyValue;
};
```
Then the node properties are passed to a child node using the `NodeAddArgs`
struct:
```fidl {:.devsite-disable-click-to-copy}
/// Arguments for adding a node.
type NodeAddArgs = table {
/// Name of the node.
1: name string:MAX_NODE_NAME_LENGTH;
/// Capabilities to offer to the driver that is bound to this node.
2: offers vector<fuchsia.component.decl.Offer>:MAX_OFFER_COUNT;
/// Functions to provide to the driver that is bound to this node.
3: symbols vector<NodeSymbol>:MAX_SYMBOL_COUNT;
/// Properties of the node.
4: properties vector<NodeProperty>:MAX_PROPERTY_COUNT;
};
```
<!-- Reference links -->
[i2c-temperature-sample-driver]: https://fuchsia.googlesource.com/sdk-samples/drivers/+/refs/heads/main/src/i2c_temperature/
[drivers-and-nodes]: /docs/concepts/drivers/drivers_and_nodes.md
[driver-binding]: /docs/concepts/drivers/driver_binding.md
[binding-prev-h]: /src/lib/ddk/include/lib/ddk/binding_priv.h
[protodefs-h]: /src/lib/ddk/include/lib/ddk/protodefs.h
[fuchsia-i2c-bind-library]: /src/devices/bind/fuchsia.i2c/fuchsia.i2c.bind
[bind-library-tutorial]: /docs/development/sdk/create-new-bind-library-for-driver.md