本指南介绍了如何为驱动程序编写绑定规则,以使其绑定到所需的设备。还说明了如何查找节点属性,然后使用绑定语言为其编写绑定规则。
本指南假定您熟悉驱动程序绑定。
节点属性的当前状态
目前,节点属性在绑定库和 C++ 头文件中定义。过去,节点属性是基于整数的键值对,描述为 C++ 结构体。所有属性均在 C++ 头文件中定义,并且绑定规则是驱动程序源代码的一部分。
不过,绑定系统最近经过了改版,现在使用绑定语言在单独的文件中定义绑定规则,并且节点属性可以支持具有布尔值、字符串、整数或枚举值、基于字符串的键。
正在迁移,以便将所有驱动程序从旧的绑定系统迁移到新的绑定系统。将在绑定库中重新定义 C++ 头文件中的节点属性。例如,所有设备协议 ID 绑定值都硬编码到 protodefs.h 中。每个设备协议现在都在各自的绑定库中进行定义,该库中包含协议 ID 的定义以及与协议相关联的其他节点属性。绑定库全部位于 src/devices/bind 中。
在迁移完成之前,需要同时支持新旧绑定系统。
节点属性的未来状态
绑定迁移完成后,我们可以停止支持旧的基于整数的节点属性,并移除 C++ 定义,例如 protodefs.h 和 binding_priv.h。所有属性都将在绑定库中定义,并且键将完全基于字符串。
您可以更新现有的属性,以便利用新系统的功能。例如,BIND_COMPOSITE
属性是仅为复合设备设置的标志。不过,由于旧系统仅支持整数值,因此使用整数而非布尔值表示属性值。移除旧的绑定系统后,属性值可以更改为布尔值。
可以做出的其他更改是对 VID 的表示方式。我们可以使用 VID 名称,而不是向 VID 分配唯一整数。例如,Intel VID 目前由整数值 0x8
表示:
library fuchsia.intel.platform;
extend uint fuchsia.BIND_PLATFORM_DEV_VID {
INTEL = 0x8,
};
使用新的绑定系统时,VID 可能由字符串值甚至枚举表示。
查找节点属性
使用 ffx 驱动程序 list-devices
ffx driver list-devices -v
命令以如下格式输出树中每个设备的属性:
Name : _TZ_
Moniker : root.sys.platform.pt.acpi._TZ_
Driver : None
5 Properties
[ 1/ 5] : Key fuchsia.BIND_PROTOCOL Value 0x1F
[ 2/ 5] : Key fuchsia.BIND_PCI_VID Value 0x1AF4
[ 3/ 5] : Key fuchsia.BIND_PCI_DID Value 0x1052
[ 4/ 5] : Key fuchsia.COMPOSITE_BIND Value 1
[ 5/ 5] : Key "fuchsia.acpi.HID" Value "GFSH0005"
驱动程序源代码中的驱动程序属性
添加子设备时,驱动程序可以提供绑定规则所匹配的属性。因此,您可以通过驱动程序源代码找到要绑定到的属性。
在 DFv1 中,源代码中的节点属性由“Properties”(属性)和“String Properties”(字符串属性)表示。属性包含基于整数的键和值。不过,字符串属性包含基于字符串的键和值,可以是整数、布尔值、字符串或枚举。
在以下代码段中,驱动程序可添加具有 BIND_PROTOCOL
属性和“ENABLE_TEST”字符串属性的设备。
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = "parent";
args.ops = &dev_ops;
zx_device_prop_t props[] = {
{BIND_PROTOCOL, 0, ZX_PROTOCOL_PCI},
}
args.props = props;
args.prop_count = std::size(props);
zx_device_str_prop_t str_props[] = {
zx_device_str_prop_t{.key = "ENABLE_TEST",
.property_value = str_prop_bool_val(true)}
};
args.str_props = str_props;
args.str_prop_count = std::size(str_props);
device_add(parent, &args, &dev);
在 DFv2 中,节点属性由 fuchsia.driver.framework FIDL 库中的 NodeProperty
结构体表示:
auto properties = fidl::VectorView<fdf::wire::NodeProperty>(arena, 2);
properties[0] = fdf::MakeProperty(arena, BIND_PROTOCOL, ZX_PROTOCOL_PCI);
properties[1] = fdf::MakeProperty(arena, "ENABLED_TEST", true);
auto args = fdf::wire::NodeAddArgs::Builder(arena)
.name(arena, "sample-child")
.properties(properties)
.Build();
在绑定库中定义属性
在从旧版绑定系统迁移到新版驱动程序时,我们会在新系统上的绑定库中重新定义旧属性。这些绑定库位于 src/devices/bind 目录中。
所有新属性都应在绑定库中定义。
旧绑定系统中的属性
大多数旧节点属性键和值在 binding_priv.h 和 protodefs.h 中定义。
binding_priv.h
包含硬编码的属性键。每个属性键都分配有一个唯一的整数值。在新的绑定系统中,这些键使用 fuchsia.BIND_
前缀重新定义。例如,BIND_PROTOCOL
将变为 fuchsia.BIND_PROTOCOL
,BIND_COMPOSITE
将变为 fuchsia.BIND_COMPOSITE
。
protodefs.h
包含设备协议的硬编码 ID 值。
设备协议绑定库
protodefs.h
中的每个设备协议都应有自己的绑定库,其中包含其协议 ID 和相关属性。例如 fuchsia.i2c 绑定库,它不仅定义了协议 ID 值,还定义了其他与 i2c 相关的属性:
library fuchsia.i2c;
extend uint fuchsia.BIND_PROTOCOL {
DEVICE = 24,
IMPL = 25,
};
extend uint fuchsia.BIND_I2C_CLASS {
HID = 0x01,
};
extend uint fuchsia.BIND_I2C_ADDRESS {
BACKLIGHT = 0x2C,
ETH = 0x18,
FOCALTECH_TOUCH = 0x38,
AMBIENTLIGHT = 0x39,
};
协议 ID 值与 protodefs.h
中定义的值一致:
DDK_PROTOCOL_DEF(I2C, 24, "i2c", 0)
DDK_PROTOCOL_DEF(I2C_IMPL , 25, "i2c-impl", 0)
IMPL
值表示实现和提供 i2c 协议的驱动程序。DEVICE
表示使用 IMPL
协议并提供 DEVICE
协议的核心驱动程序。
编写绑定规则
本部分介绍了如何为驱动程序绑定的当前状态编写绑定规则。对于仅绑定到上述一组属性的绑定规则,您需要编写以下代码:
using fuchsia.acpi;
using fuchsia.pci;
fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.VIRTIO;
fuchsia.BIND_PCI_DID == fuchsia.pci.BIND_PCI_DID.VIRTIO_DEV_TYPE_INPUT;
fuchsia.BIND_COMPOSITE == 1;
fuchsia.acpi.HID == "GFSH0005";
属性键
整数键的名称在 binding_priv.h
中定义,并以“fuchsia”为前缀。例如,dm dump
中的键 0x03 确定为 fuchsia.BIND_COMPOSITE
,因为 binding_priv.h
包含以下内容:
#define BIND_FLAGS 0x0000 // value of the flags register
#define BIND_PROTOCOL 0x0001 // primary protocol of the device
#define BIND_AUTOBIND 0x0002 // if this is an automated bind/load
#define BIND_COMPOSITE 0x003 // Whether this is a composite device
属性值
由于这些属性与 PCI 相关联,因此匹配的值可在 fuchsia.pci.bind 库中找到。通过将 0x001af4
与 fuchsia.BIND_PCI_VID
中的值匹配,可以找到 fuchsia.pci.BIND_PCI_VID.VIRTIO
。
extend uint fuchsia.BIND_PCI_VID {
TEST = 0x0eff,
AMD = 0x1002,
REALTEK = 0x10ec,
NVIDIA = 0x10de,
GOOGLE = 0x1ae0,
VIRTIO = 0x1af4,
BROADCOM = 0x14e4,
ATHEROS = 0x168c,
INTEL = 0x8086,
};
如果绑定库中缺少某个值,您需要在新的或现有的绑定库中添加缺少的定义。或者,您也可以直接为该值编写绑定规则:
fuchsia.BIND_PCI_VID == 0x1af4;
不过,最好在绑定库中定义一个值。
复合绑定规则
系统会对复合绑定规则使用相同的过程。对于要为其编写绑定规则的每个节点,您可以输出节点属性并为其编写绑定规则。
假设您要编写复合绑定规则,其中包含一个绑定到以上示例的节点,以及另一个绑定到以下代码的节点:
Name : I2C2
Moniker : root.sys.platform.pt.acpi.I2C2
Driver : None
6 Properties
[ 1/ 6] : Key fuchsia.BIND_ACPI_ID Value 0x000034
[ 2/ 6] : Key fuchsia.BIND_PCI_TOPO Value 0x0000aa
[ 3/ 6] : Key fuchsia.BIND_ACPI_BUS_TYPE Value 0x000001
[ 4/ 6] : Key "fuchsia.hardware.acpi.Device" Value true
[ 5/ 6] : Key fuchsia.BIND_PROTOCOL Value 0x00001e
[ 6/ 6] : Key "fuchsia.platform.DRIVER_FRAMEWORK_VERSION" Value 0x000002
然后,您可以编写以下内容:
using fuchsia.acpi;
using fuchsia.pci;
primary node "pci_sample" {
fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.VIRTIO;
fuchsia.BIND_PCI_DID == fuchsia.pci.BIND_PCI_DID.VIRTIO_DEV_TYPE_INPUT;
fuchsia.BIND_COMPOSITE == 1;
fuchsia.acpi.HID == "GFSH0005";
}
node "acpi" {
fuchsia.driver.framework.dfv2 == true;
fuchsia.BIND_ACPI_ID == 0x000034;
fuchsia.BIND_PCI_TOPO == 0x0000aa;
fuchsia.BIND_ACPI_BUS_TYPE == 0x000001;
}
可选节点
可选节点是指不必存在于设备组中才能与复合驱动程序匹配的节点。仅在使用设备组时支持此功能,在普通旧组合中(拓扑中的节点与复合驱动程序匹配),可选节点不会匹配。
在绑定规则中,在 node
关键字前面放置 optional
会将节点标记为可选。按照惯例,所有可选节点都应在常规额外节点之后写入。
可选节点用于编写更通用的复合驱动程序规则。例如,HID 按钮驱动程序支持 9 种不同的按钮类型(音量调高、摄像头静音等),但并非所有硬件设备都有所有这些按钮。借助可选节点,可以将绑定规则编写为与平台无关,以便板驱动程序可以使用 HID 按钮驱动程序支持的任意一组按钮创建组合。
绑定规则示例
分支绑定规则
绑定语言支持通过 if 语句进行分支,但有一些限制。
if 语句必须有 else 块,并且是终止语句。此限制通过明确表达执行分支来提高可读性。由于 if 语句后不得有语句,因此可以轻松跟踪绑定规则的路径。
if fuchsia.hardware.tee.Service == fuchsia.hardware.tee.Service.ZirconTransport {
fuchsia.BIND_PLATFORM_DEV_VID == fuchsia.platform.BIND_PLATFORM_DEV_VID.GENERIC;
} else {
fuchsia.BIND_PLATFORM_DEV_VID == fuchsia.platform.BIND_PLATFORM_DEV_VID.QEMU;
}
支持嵌套 if 语句,前提是它们遵循限制条件。例如:
if fuchsia.driver.framework.dfv2 == true {
if fuchsia.acpi.hid == "PNP0303" {
true;
} else {
fuchsia.acpi.first_cid == "PNP0303";
}
} else {
fuchsia.BIND_PROTOCOL == fuchsia.acpi.BIND_PROTOCOL.DEVICE;
}
排除多个属性值
要拒绝一个键的多个属性值,您可以将不等式条件语句链接在一起。例如,如果您需要拒绝多个 fuchsia.BIND_PCI_DID 值,可以执行以下操作:
fuchsia.BIND_PCI_DID != 0x191b;
fuchsia.BIND_PCI_DID != 0x1912;
fuchsia.BIND_PCI_DID != 0x191d;
fuchsia.BIND_PCI_DID != 0x1902;
fuchsia.BIND_PCI_DID != 0x1916;
fuchsia.BIND_PCI_DID != 0x191e;