This guide walks through the steps for writing bind rules for the i2c_temperature
sample driver.
For a driver to bind to a node (which represents a hardware or virtual device), the driver’s bind rules must match the bind properties of the node. In this guide, we’ll write bind rules for the i2c_temperature
sample driver so that they match the bind properties of the i2c-child
node.
The test_i2c_controller
driver creates a child node named i2c-child
for testing the i2c_temperature
sample driver. We can use this test_i2c_controller
driver to identify the bind properties of the i2c-child
node for writing i2c_temperature
’s bind rules.
Before you begin, writing bind rules requires familiarity with the concepts in Driver binding.
The steps are:
You can identify the bind properties of your target node in one of the following ways:
To print the properties of every node in the Fuchsia system, run the following command:
ffx driver list-devices -v
This command prints the properties of a node in the following format:
Name : i2c-child Moniker : root.sys.platform.platform-passthrough.acpi.acpi-FWCF.i2c-child Driver : None 3 Properties [ 1/ 3] : Key fuchsia.BIND_PROTOCOL Value 0x000018 [ 2/ 3] : Key fuchsia.BIND_I2C_ADDRESS Value 0x0000ff [ 3/ 3] : Key "fuchsia.driver.framework.dfv2" Value true
The output above shows that the i2c-child
node has the following bind properties:
fuchsia.BIND_PROTOCOL
with an integer value of 24
(0x000018
)fuchsia.BIND_I2C_ADDRESS
with an integer value of 0xFF
.When adding a child node, drivers can provide bind properties to the node. So if you view the source code of the driver that creates your target node as a child node, you can identify the bind properties for which you want to write the bind rules.
For instance, the test_i2c_controller
driver creates a child node named i2c-child
which we want the i2c_temperature
sample driver to bind to. So we’ll examine the source code of the test_i2c_controller
driver to identify which bind properties are passed to this child node.
In test_i2c_controller.cc
, the code below adds a child node named i2c-child
:
// Set the properties of the node that a driver will bind to. auto properties = fidl::VectorView<fdf::wire::NodeProperty>(arena, 2); properties[0] = fdf::wire::NodeProperty::Builder(arena) .key(fdf::wire::NodePropertyKey::WithStringValue( arena, bind_fuchsia_hardware_i2c::DEVICE)) .value(fdf::wire::NodePropertyValue::WithEnumValue( arena, bind_fuchsia_hardware_i2c::DEVICE_ZIRCONTRANSPORT)) .Build(); properties[1] = fdf::wire::NodeProperty::Builder(arena) .key(fdf::wire::NodePropertyKey::WithIntValue(0x0A02 /* BIND_I2C_ADDRESS */)) .value(fdf::wire::NodePropertyValue::WithIntValue(0xff)) .Build(); auto args = fdf::wire::NodeAddArgs::Builder(arena) .name(arena, "i2c-child") .offers(fidl::VectorView<fcd::wire::Offer>::FromExternal(&offer, 1)) .properties(properties) .Build();
This code shows that the i2c-child
node is created with the following two bind properties:
fuchsia.BIND_PROTOCOL
with an integer value of 24
.fuchsia.BIND_I2C_ADDRESS
with an integer value of 0xFF
.(For more information on the NodeAddArgs
struct used to pass the bind properties to a child node, see the NodeProperty and NodeAddArgs structs section in Appendices.)
Once you know the bind properties you want to match, you can use the bind language to write the bind rules for your driver (for instance, the i2c_temperature
sample driver in this case).
In the previous section, we’ve identified that the i2c-child
node has the following bind properties:
fuchsia.BIND_PROTOCOL
with an integer value of 24
.fuchsia.BIND_I2C_ADDRESS
with an integer value of 0xFF
.The i2c_temperature.bind
file shows that the following bind rules are written for these bind properties:
using fuchsia.i2c; fuchsia.BIND_PROTOCOL == fuchsia.i2c.BIND_PROTOCOL.DEVICE; fuchsia.BIND_I2C_ADDRESS == 0xFF;
Integer-based bind property keys that start with BIND_
(defined in binding_priv.h
in the Fuchsia source tree) are old property keys currently hardcoded in the bind compiler. When these keys are used in bind rules, they are prefixed with fuchsia.
. Hence, BIND_PROTOCOL
becomes fuchsia.BIND_PROTOCOL
and BIND_I2C_ADDRESS
becomes fuchsia.BIND_I2C_ADDRESS
.
The property values of fuchsia.BIND_PROTOCOL
are defined in protodefs.h
. From the file, we can discover that the protocol that maps to an integer value of 24
is i2c
:
DDK_PROTOCOL_DEF(I2C, 24, "i2c", 0)
We can then navigate to the fuchsia.i2c
bind library and find the list of the BIND_PROTOCOL
property values:
extend uint fuchsia.BIND_PROTOCOL { DEVICE = 24, IMPL = 25, };
This gives us the property value of fuchsia.i2c.BIND_PROTOCOL.DEVICE
for the property key of fuchsia.BIND_PROTOCOL
in the bind rules above.
Once you have a file that contains the bind rules for your driver (i2c_temperature
), you need to update the BUILD.bazel
file to add a build target for the bind rules.
To add a Bazel build target for the bind rules, use the following template:
fuchsia_driver_bytecode_bind_rules( name = <target name>, output = "<bind rules driver>.bindbc", rules = <bind rules filename>, deps = [ <bind library dependencies> ], )
Using the i2c_temperature.bind
file, the Bazel build target for the i2c_temperature
sample driver's bind rules may look like below:
fuchsia_driver_bytecode_bind_rules( name = "bind_bytecode", output = "i2c_temperature.bindbc", rules = "i2c_temperature.bind", deps = [ "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_bindlib", ], )
For each library used in your bind rules, you need to add the library as a dependency to the build target. The i2c_temperature
sample driver's bind rules use the fuchsia.hardware.i2c
bind library, so it needs to be included as a dependency in deps
.
To determine which bind libraries are used in the bind rules, you can examine the driver source code. In the bind properties of the i2c-child
node, the first property key fuchsia.hardware.i2c.Device
is from a generated bind library from the FIDL protocol. This information can be derived from examining the following driver source code:
properties[0] = fdf::wire::NodeProperty::Builder(arena) .key(fdf::wire::NodePropertyKey::WithStringValue( arena, bind_fuchsia_hardware_i2c::DEVICE)) .value(fdf::wire::NodePropertyValue::WithEnumValue( arena, bind_fuchsia_hardware_i2c::DEVICE_ZIRCONTRANSPORT)) .Build();
The prefix fuchsia.hardware.i2c
implies that this bind property’s key and value are defined in the following header:
#include <bind/fuchsia/hardware/i2c/cpp/bind.h>
Also, in the BUILD.bazel
file, notice that the target test_i2c_controller
contains the dependency @fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_bindlib_cc
, which can be seen in the cc_binary
build target below:
cc_binary( name = "test_controller", srcs = [ "constants.h", "test_i2c_controller.cc", "test_i2c_controller.h", ], linkshared = True, deps = [ "@fuchsia_sdk//fidl/fuchsia.device.fs:fuchsia.device.fs_llcpp_cc", "@fuchsia_sdk//fidl/fuchsia.driver.compat:fuchsia.driver.compat_llcpp_cc", "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_bindlib_cc", ...
The prefix @fuchsia_sdk//fidl/
in the dependency means that the values are pulled from a bind library that is generated from the fuchsia.hardware.i2c
FIDL interface. (For more information on the generated bind library, see the FIDL tutorial for drivers.)
Bind properties are represented by the NodeProperty
struct in the fuchsia.driver.framework
FIDL library:
/// 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 bind properties are passed to a child node using the NodeAddArgs
struct:
/// 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; };