为驱动程序编写绑定规则

本指南逐步介绍了使用 i2c_temperature 示例驱动程序编写绑定规则的步骤。

如需让驱动程序绑定到节点(表示硬件或虚拟设备),驱动程序的绑定规则必须与节点的节点属性匹配。在本指南中,我们将为 i2c_temperature 示例驱动程序编写绑定规则,使其与 i2c-child 节点的节点属性相匹配。

i2c_controller 驱动程序会创建一个名为 i2c-child 的子节点,用于测试 i2c_temperature 示例驱动程序。我们可以使用此 i2c_controller 驱动程序来识别 i2c-child 节点的节点属性,并为 i2c_temperature 编写匹配的绑定规则。

在开始之前,编写绑定规则需要熟悉驱动程序绑定中的概念。

相关步骤如下:

  1. 确定节点属性
  2. 编写绑定规则
  3. 为绑定规则添加 Bazel 构建目标

1. 确定节点属性

您可以通过以下方式之一标识目标节点的节点属性:

使用 ffx 驱动程序 list-devices 命令

如需输出 Fuchsia 系统中每个节点的属性,请运行以下命令:

ffx driver list-devices -v

此命令以如下格式输出节点的属性:

Name     : i2c-child
Moniker  : root.sys.platform.pt.acpi.FWCF.i2c-child
Driver   : None
3 Properties
[ 1/  3] : Key "fuchsia.hardware.i2c.Service"  Value Enum(fuchsia.hardware.i2c.Service.ZirconTransport)
[ 2/  3] : Key fuchsia.BIND_I2C_ADDRESS        Value 0x0000ff
[ 3/  3] : Key "fuchsia.platform.DRIVER_FRAMEWORK_VERSION" Value 0x000002

上面的输出显示 i2c-child 节点具有以下节点属性:

  • 枚举值为 fuchsia.hardware.i2c.Service.ZirconTransport 的属性键 fuchsia.hardware.i2c.Service
  • 整数值为 0xFF 的属性键 fuchsia.BIND_I2C_ADDRESS

在驱动程序源代码中查找节点属性

添加子节点时,驱动程序可以为节点提供节点属性。查看将目标节点创建为子节点的驱动程序的源代码有助于您确定要包含在绑定规则中的节点属性。

i2c_controller 驱动程序会创建一个名为 i2c-child 的子节点,i2c_temperature 示例驱动程序会绑定到该子节点。检查 i2c_controller 驱动程序的源代码,以确定传递给此子节点的节点属性:

// Set the properties of the node for drivers to target.
auto properties = fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty>(arena, 2);
properties[0] = fdf::MakeProperty(arena, bind_fuchsia_hardware_i2c::SERVICE,
                                  bind_fuchsia_hardware_i2c::SERVICE_ZIRCONTRANSPORT);
properties[1] = fdf::MakeProperty(arena, 0x0A02 /* BIND_I2C_ADDRESS */, 0xff);

此代码显示使用以下绑定属性创建 i2c-child 节点:

  • 枚举值为 fuchsia.hardware.i2c.Service.ZirconTransport 的属性键 fuchsia.hardware.i2c.Service
  • 整数值为 0xFF 的属性键 fuchsia.BIND_I2C_ADDRESS

2. 编写绑定规则

知道要匹配的节点属性后,您可以使用绑定语言为驱动程序编写绑定规则。

在上一部分中,我们已经确定 i2c-child 节点具有以下节点属性:

  • 枚举值为 fuchsia.hardware.i2c.Service.ZirconTransport 的属性键 fuchsia.hardware.i2c
  • 整数值为 0xFF 的属性键 fuchsia.BIND_I2C_ADDRESS

为了匹配这些属性,i2c_temperature 驱动程序声明了以下绑定规则:

using fuchsia.hardware.i2c;

fuchsia.hardware.i2c.Service == fuchsia.hardware.i2c.Service.ZirconTransport;
fuchsia.BIND_I2C_ADDRESS == 0xFF;

BIND_ 开头的基于整数的节点属性键(在 Fuchsia 源代码树的 binding_priv.h 中定义)是目前在绑定编译器中硬编码的旧属性键。请参阅 binding_priv.h 中的 BIND_I2C_ADDRESS 的以下定义:

#define BIND_I2C_ADDRESS 0x0A02

当这些键在绑定规则中使用时,它们带有 fuchsia. 前缀。

3. 为绑定规则添加 Bazel 构建目标

为驱动程序编写绑定规则后,您需要更新 BUILD.bazel 文件,以使用 fuchsia_driver_bytecode_bind_rules() 模板为绑定规则字节码添加构建目标:

fuchsia_driver_bind_bytecode(
    name = "bind_bytecode",
    output = "i2c_temperature.bindbc",
    rules = "i2c_temperature.bind",
    deps = [
        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_bindlib",
    ],
)

对于绑定规则中使用的每个库,将该库作为依赖项添加到构建目标中。例如,i2c_temperature 示例驱动程序的绑定规则使用 fuchsia.hardware.i2c 库,因此构建目标包含绑定库作为 build 依赖项。

如需确定绑定规则中使用的绑定库,您可以检查驱动程序源代码。在 i2c-child 节点的节点属性中,第一个属性键 fuchsia.hardware.i2c.Service 来自通过 FIDL 协议生成的绑定库:

// Set the properties of the node for drivers to target.
auto properties = fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty>(arena, 2);
properties[0] = fdf::MakeProperty(arena, bind_fuchsia_hardware_i2c::SERVICE,
                                  bind_fuchsia_hardware_i2c::SERVICE_ZIRCONTRANSPORT);
properties[1] = fdf::MakeProperty(arena, 0x0A02 /* BIND_I2C_ADDRESS */, 0xff);

前缀 fuchsia_hardware_i2c 表示此节点属性的键和值在以下标头中定义:

#include <bind/fuchsia/hardware/i2c/cpp/bind.h>

这些绑定库在驱动程序的构建规则中具有相应的依赖项。请参阅 i2c_controller 二进制文件目标中的以下 fuchsia.hardware.i2c 依赖项:

fuchsia_cc_driver(
    name = "i2c_controller",
    srcs = [
        "i2c_controller.cc",
        "i2c_controller.h",
        "i2c_server.cc",
        "i2c_server.h",
    ],
    deps = [
        "//src/i2c_temperature/lib",
        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_bindlib_cc",
        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_llcpp_cc",
        "@fuchsia_sdk//pkg/driver_component_cpp",
    ],
)

附录

NodeProperty 和 NodeAddArgs 结构体

节点属性由 fuchsia.driver.framework FIDL 库中的 NodeProperty 结构体表示:

/// 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;
};

然后,使用 NodeAddArgs 结构体将节点属性传递给子节点:

/// 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;
};