如需了解如何编写绑定规则,请参阅绑定规则教程。
在 Fuchsia 中,驱动程序框架维护着系统中驱动程序和设备的树。在本课中, 设备代表对操作系统可用的某些硬件的访问。驱动程序同时发布和 绑定到设备例如,USB 驱动程序可能会绑定到 PCI 设备(其父设备)并发布 以太网设备(其子设备)。为了确定驱动程序可以绑定到哪些设备,每个驱动程序 具有绑定规则,每个设备都有一组属性。绑定规则定义了一个条件 与要绑定的设备属性相匹配的设备。
绑定规则及其引用的条件由特定网域的语言定义。绑定 编译器会使用这种语言并为绑定规则生成字节码。该语言有两个 各种类型的源文件:规则和库。库用于共享属性定义 和绑定规则之间的连接。编译器还会从绑定库生成 FIDL 文件, 驱动程序可能会在代码中引用设备属性。
对于迁移的这一阶段,有一点需要注意,那就是不支持
属性键(见下文)。旧驱动程序绑定系统中的键
(lib/ddk/binding.h) 可用于扩展。
这些键已硬编码到绑定编译器中,可在 fuchsia
命名空间下使用。
例如,PCI 供应商 ID 密钥为 fuchsia.BIND_PCI_VID
。最终硬编码的键将
将从此命名空间中移除,并且所有节点属性键都将在绑定库中定义。
编译器
编译器会获取一系列库源和一个规则源。例如:
fx bindc compile \
--include src/devices/bind/fuchsia.usb/fuchsia.usb.bind \
--output tools/bindc/examples/gizmo.h \
tools/bindc/examples/gizmo.bind
目前,它生成的是驱动程序可能包含的 C 头文件。头文件定义 宏:
ZIRCON_DRIVER(Driver, Ops, VendorName, Version);
Driver
是驱动程序的名称。Ops
是一个zx_driver_ops
,即驱动程序操作钩子VendorName
是一个字符串,表示驱动程序供应商的名称。Version
是一个表示驱动程序版本的字符串。
有关详情,请参阅 驱动程序开发文档。
绑定规则
绑定规则定义了调用驱动程序的 bind()
钩子的条件。绑定中的每个语句
规则是针对设备属性的条件,必须符合
要绑定的驱动程序。如果绑定规则执行完毕且所有条件均为 true,则设备
协调员会调用驱动程序的 bind()
钩子。
绑定规则应视为 驱动程序应该绑定。因此,条件表达式的执行顺序与其 最终评估。不妨将绑定规则视为布尔公式。
共有四种语句:
- 条件语句是采用以下形式的等式(或不等式)表达式
<key> == <value>
(或<key> != <value>
)。 - Accept 语句是给定键的允许值列表。
- If 语句提供简单的分支。
- True 和 false 语句可用于显式评估绑定规则。
示例
您可以在 //tools/bindc/examples/gizmo.bind 找到此示例绑定规则。
using fuchsia.usb;
// The device must be a USB device.
fuchsia.BIND_PROTOCOL == fuchsia.usb.BIND_PROTOCOL.INTERFACE;
if fuchsia.BIND_USB_VID == fuchsia.usb.BIND_USB_VID.INTEL {
// If the device's vendor is Intel, the device class must be audio.
fuchsia.BIND_USB_CLASS == fuchsia.usb.BIND_USB_CLASS.AUDIO;
} else if fuchsia.BIND_USB_VID == fuchsia.usb.BIND_USB_VID.REALTEK {
// If the device's vendor is Realtek, the device class must be one of the following values:
accept fuchsia.BIND_USB_CLASS {
fuchsia.usb.BIND_USB_CLASS.COMM,
fuchsia.usb.BIND_USB_CLASS.VIDEO,
}
} else {
// If the vendor is neither Intel or Realtek, do not bind.
false;
}
语言限制
为了提高可读性并确保 绑定规则是驱动程序应绑定的条件的简单表示法。
不允许使用空块。 空块是否应表示驱动程序将绑定还是中止并不明确。通过 应使用显式
true
或false
语句。If 语句必须具有 else 代码块并且为终端。 此限制通过明确执行分支来提高可读性。由于没有 语句可以跟在
if
语句之后,所以可以通过绑定规则轻松跟踪路径。判断正误语句必须是其范围内的唯一语句。 绑定规则不是命令式程序,评估的顺序不重要。混合 包含其他条件的布尔值语句(尤其是
true
)可能会导致此 不清楚。
语法
rule = using-list , ( statement )+ ;
using-list = ( using , ";" )* ;
using = "using" , compound-identifier , ( "as" , IDENTIFIER ) ;
statement = condition , ";" | accept | if-statement | true | false ;
condition = compound-identifier , condition-op , value ;
condition-op = "==" | "!=" ;
accept = "accept" , compound-identifier , "{" ( value , "," )+ "}" ;
if-statement = "if" , condition , "{" , ( statement )+ , "}" ,
( "else if" , "{" , ( statement )+ , "}" )* ,
"else" , "{" , ( statement )+ , "}" ;
true = "true" , ";" ;
false = "false" , ";" ;
compound-identifier = IDENTIFIER ( "." , IDENTIFIER )* ;
value = compound-identifier | STRING-LITERAL | NUMERIC-LITERAL | "true" | "false" ;
标识符与正则表达式 [a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])?
匹配,且不得与任何标识符匹配
关键字。关键字列表如下:
accept
as
else
false
if
true
using
字符串字面量与正则表达式 ”[^”]*”
匹配,数字字面量与正则表达式 [0-9]+
或
0x[0-9A-F]+
。
绑定编译器会忽略(视为空格)所有以 //
为前缀的行以及任何多个
几行,以 /*
和 */
分隔。
复合材料绑定
除了将驱动程序绑定到设备之外,Fuchsia 中的驱动程序还可以使用绑定规则 通过节点创建复合设备。绑定规则遵循相同的语言 规范为非复合绑定,但又分成了包含 以及一组语句。
绑定规则中必须只有一个主节点。复合驱动程序将 在主节点所在的驱动程序主机中启动。
您可以在以下位置找到复合绑定规则文件示例: //tools/bindc/examples/composite-gizmo.bind.
composite gizmo_pci;
using fuchsia.pci;
using fuchsia.platform;
using fuchsia.tee;
primary node "pci" {
fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
}
node "tee" {
if fuchsia.BIND_PROTOCOL == fuchsia.tee.BIND_PROTOCOL.DEVICE {
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;
}
}
复合绑定的语法为:
composite-bind-rules = [composite-device], using-list , ( node )+ ;
composite-device = “composite” , IDENTIFIER;
node = [ "primary" ], "node" , STRING-LITERAL , "{" , ( statement )+ , "}"
编译目标
如需在 Fuchsia 构建系统中声明绑定规则,请使用以下构建目标:
driver_bind_rules("bind") {
rules = <bind rules filename>
bind_output = <generated bind binary filename>
deps = [ <list of bind library targets> ]
}
如需了解详情,请参阅 //build/bind/bind.gni。
测试
绑定编译器支持适用于绑定规则的数据驱动型单元测试框架, 在独立于驱动程序的情况下测试绑定规则。绑定规则的测试用例 设备规范和预期结果,即绑定或中止。将测试用例传递给绑定 JSON 规范文件形式的编译器, 运行调试程序。
JSON 规范必须是测试用例对象列表,其中每个对象都包含:
name
:表示测试用例名称的字符串。expected
预期结果。必须为“match”
或“abort”
。device
描述设备属性的字符串键值对列表。这是 类似于调试程序的设备规格(请参阅 此示例)。
如果测试针对的是复合设备,那么设备中的每个节点都可以 测试用例对象列表。单元测试的 JSON 规范将是 节点对象的列表。每个节点对象都包含:
node
:表示节点名称的字符串。它必须与绑定规则中的某个节点匹配 测试。tests
测试用例对象的列表。
示例
这是一个示例测试用例,全部测试位于 //tools/bindc/examples/test.json
。本次
case 检查绑定规则是否与具有所列属性的设备(即 Intel USB 音频)匹配
设备。
[
{
"name": "Intel",
"expected": "match",
"device": {
"fuchsia.BIND_PROTOCOL": "fuchsia.usb.BIND_PROTOCOL.INTERFACE",
"fuchsia.BIND_USB_VID": "fuchsia.usb.BIND_USB_VID.INTEL",
"fuchsia.BIND_USB_CLASS": "fuchsia.usb.BIND_USB_CLASS.AUDIO"
}
}
]
以下是包含测试用例的复合绑定节点的示例。完整的测试集位于
“//tools/bindc/examples/composite-tests.json
”。每个测试用例都会检查节点的
绑定规则与具有所列属性的设备匹配。
[
{
"node": "pci",
"tests": [
{
"name": "Match",
"expected": "match",
"device": {
"fuchsia.BIND_PROTOCOL": "fuchsia.pci.BIND_PROTOCOL.DEVICE"
}
},
{
"name": "Abort pci",
"expected": "abort",
"device": {
"fuchsia.BIND_PROTOCOL": "fuchsia.tee.BIND_PROTOCOL.DEVICE"
}
}
]
}
]
构建
按如下方式定义测试 build 目标:
bind_test("example_bind_test") {
rules = <bind rules filename>
tests = <test specification filename>
deps = [ <list of bind library targets> ]
}
或者,您也可以直接将 tests
参数添加到现有的 bind_rules
中,以生成
测试目标。该名称将是原始目标的名称加上 _test
。例如,以下
会生成 example_bind_test
。
driver_bind_rules("example_bind") {
rules = "meta/gizmo.bind"
bind_output = “gizmo.bindbc”
tests = "meta/tests.json"
deps = [ "//src/devices/bind/fuchsia.usb" ]
}
运行
如果您已为测试定义了构建目标,则可以像往常一样使用 fx 测试运行测试。
fx test example_bind_test
否则,您可以直接运行绑定工具。例如:
fx bindc test \
tools/bindc/examples/gizmo.bind \
--test-spec tools/bindc/examples/tests.json \
--include src/devices/bind/fuchsia.usb/fuchsia.usb.bind
绑定库
绑定库定义了一组可供驱动程序分配给其子项的属性。此外, 绑定规则可能涉及绑定库
命名空间
绑定库首先要定义其命名空间:
library <vendor>.<library>;
每个命名空间都必须以供应商开头,并且每个供应商都应确保不冲突
在其自己的命名空间内运行不过,这种语言允许一家供应商扩展
另一个。对于公开库,Google 将使用 fuchsia
。
库引入的任何值都带有命名空间。例如,以下库定义了
新的 PCI 设备 ID GIZMO_VER_1
。
library gizmotronics.gizmo;
using fuchsia.pci as pci;
extend uint pci.device_id {
GIZMO_VER_1 = 0x4242,
};
如需引用此值,驱动程序作者应使用完全限定名称,如下所示。
using fuchsia.pci as pci;
using gizmotronics.gizmo;
pci.device_id == gizmotronics.gizmo.device_id.GIZMO_VER_1
键和值
设备属性定义类似于其他语言的变量声明。
<type> <name>;
Or:
<type> <name> {
<value>,
<value>,
…
};
绑定库还可以扩展其他库的属性。
extend <type> <name> {
<value>,
…
};
每个键都有一个类型,与该键对应的所有值都必须是该类型。语言
支持基元类型:uint
、string
或 bool
其中之一;和枚举 (enum
)。时间
定义键时,您应首选枚举,除非值由外部
例如硬件
定义基元值时,请使用 <identifier> = <literal>
形式,对于枚举
仅需要标识符定义多个具有相同
文字。
语法
library = library-header , using-list , declaration-list ;
library-header = "library" , compound-identifier , ";" ;
using-list = ( using , ";" )* ;
using = "using" , compound-identifier , ( "as" , IDENTIFIER ) ;
compound-identifier = IDENTIFIER ( "." , IDENTIFIER )* ;
declaration-list = ( declaration , ";" )* ;
declaration = primitive-declaration | enum-declaration ;
primitive-declaration = ( "extend" ) , type , compound-identifier ,
( "{" primitive-value-list "}" ) ;
type = "uint" | "string" | "bool";
primitive-value-list = ( IDENTIFIER , "=" , literal , "," )* ;
enum-declaration = ( "extend" ) , "enum" , compound-identifier ,
( "{" , enum-value-list , "}" ) ;
enum-value-list = ( IDENTIFIER , "," )* ;
literal = STRING-LITERAL | NUMERIC-LITERAL | "true" | "false" ;
标识符与正则表达式 [a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])?
匹配,且不得与任何标识符匹配
关键字。关键字列表如下:
as
bool
enum
extend
library
string
uint
using
字符串字面量与正则表达式 ”[^”]*”
匹配,数字字面量与正则表达式 [0-9]+
或
0x[0-9A-F]+
。
绑定编译器会忽略(视为空格)所有以 //
为前缀的行以及任何多个
几行,以 /*
和 */
分隔。
编译目标
如需在 Fuchsia 构建系统中声明绑定库,请使用以下构建目标:
bind_library(<library name>) {
source = <bind library filename>
public_deps = [ <list of bind library targets> ]
}
如需了解详情,请参阅 //build/bind/bind.gni。