驱动程序绑定

如需了解如何编写绑定规则,请参阅绑定规则教程

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

语言限制

为了提高可读性并确保 绑定规则是驱动程序应绑定的条件的简单表示法。

  • 不允许使用空块。 空块是否应表示驱动程序将绑定还是中止并不明确。通过 应使用显式 truefalse 语句。

  • 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>,
  …
};

每个键都有一个类型,与该键对应的所有值都必须是该类型。语言 支持基元类型:uintstringbool 其中之一;和枚举 (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