RFC-0197:复合组的节点组

RFC-0197:复合的节点组
状态已接受
领域
  • 驱动程序 SDK
说明

支持在运行时定义复合对象。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-11-01
审核日期(年-月-日)2022-09-26

摘要

此 RFC 就如何在运行时定义复合节点 驱动程序框架 v2 (DFv2)。

设计初衷

背景

在驱动程序框架 v1 (DFv1) 中,驱动程序可以创建 复合节点 通过 AddComposite() 函数在运行时调用。驱动程序定义绑定 为复合的父级以及绑定属性和驱动程序管理器设置的规则 用于创建复合节点作为驱动程序的子节点。一个单独的驱动程序会绑定到 复合节点

在 DFv2 中,复合驱动程序通过 bind rules, 也就是以驱动程序绑定语言编写的静态文件司机在什么情况下 则 Driver Runner 会收集与 复合绑定规则,然后创建复合节点。复合驱动程序 绑定至该节点

问题

由于复合驱动程序只能以静态方式定义复合节点,因此无法 完全取代了 DFv1 的 AddComposite() 功能。这样可以防止某些驱动程序 从迁移到 DFv2

某些复合节点仅在运行时才已知,因此 定义。例如,ACPI 总线驱动程序 复合节点当总线驱动程序启动时,它会读取 ACPI 并使用该信息创建节点。

静态绑定规则还会导致难以编写与板无关的复合材料 驱动程序,因为绑定规则要求在构建时知道节点属性 。例如,触摸复合驱动程序可能需要节点作为 GPIO 引脚 特定功能。由于 GPIO 引脚因开发板而异,因此 使用该节点编写与板无关的绑定规则。

利益相关方

教员:cpu@google.com

审核者:surajmalhotra@google.com (FDF)、dgilhooley@google.com (FDF)

已咨询:驾驶员团队成员

社交化

RFC 的草稿已在 tq-driver 之间共享

设计

要求

本设计的目标是创建一种机制,让驱动程序能够 复合节点,则以下内容必须是动态的:

  • 复合节点中每个父级的绑定规则
  • 用于将复合节点与复合驱动程序进行匹配的绑定属性
  • 复合节点中的父级。如果存在以下情况,则可以在运行时确定 复合节点应包含特定的父级

此外,该机制需要受 DFv1 和 DFv2 支持。作为 过渡到 DFv2 时,所有 DFv1 驱动程序都需要从 AddComposite() 迁移 到复合驱动因素。

概览

此提案在驱动程序框架中新增了一个 API,驾驶员可以使用该 API 定义一组设备节点。

当驱动程序定义节点组时,流程如下:

  1. 驱动程序管理器要求驱动程序索引查找一个复合驱动程序, 匹配节点组
  2. 找到匹配的复合驱动程序后,驱动程序管理器会查找 每个节点表示的匹配设备节点
  3. 一旦每个节点表示法都匹配成功,驱动程序管理器就会创建 以这些节点作为父项的复合节点,并将其绑定到 主节点和节点名称由复合驱动程序提供

device-group-bind-diagram

节点表示法

组中的每个节点表示法使用以下内容进行定义:

  • 绑定规则 - 用于将节点表示法与设备匹配的规则 节点
  • 绑定属性 - 以节点形式表示的以 将用于与复合驱动程序的静态绑定规则匹配

节点组绑定规则

绑定规则由已接受和已拒绝的绑定属性值列表组成。 为了匹配绑定规则,绑定属性必须包含接受的所有 绑定属性值,而不是任何遭拒的属性值。例如,如果某个节点 群组节点包含以下绑定规则:

  • 接受 fuchsia.BIND_PROTOCOL 值 15 和 17
  • 拒绝 fuchsia.BIND_PLATFORM_DEV_VID 个值“Intel”

然后,如果设备的 fuchsia.BIND_PROTOCOL 属性,并且不包含“Intel”值 fuchsia.BIND_PLATFORM_DEV_VID 属性。

复合绑定规则中的可选节点

由于某些父级的可用性仅在运行时才知道,因此复合 驱动程序需要能够支持可选节点为此,绑定语言 需要更新,以便复合驱动程序可以将节点标记为可选:

primary node "sysmem" {
  fuchsia.BIND_PROTOCOL == fuchsia.sysmem.BIND_PROTOCOL.DEVICE;
}

optional node "acpi" {
  fuchsia.BIND_PROTOCOL == fuchsia.acpi.BIND_PROTOCOL.DEVICE;
}

匹配复合驱动程序

匹配过程通过将复合驱动程序的绑定规则应用于 节点表示法的绑定属性。如果满足以下条件,则匹配成功 已履单:

  • 所有节点表示法必须与复合绑定规则中的某个节点匹配
  • 所有非可选复合绑定规则节点必须与一个节点匹配 表示法
  • 匹配不得含糊不清: <ph type="x-smartling-placeholder">
      </ph>
    • 每个节点表示法只能与一个复合绑定相对应 规则节点
    • 节点表示法不能与组合中的同一节点匹配 绑定规则
  • 节点无需按顺序进行匹配

如果出现不确定情况,系统会输出一则警告消息。

composite_bind_diagram

节点组 API

驱动程序需要使用 FIDL 通过 NodeGroupManager 添加节点组 fuchsia.driver.framework FIDL 库中的 API 协议:

device_group.fidl

@discoverable
protocol NodeGroupManager {
    AddNodeGroup(fuchsia.driver.framework.NodeGroup) -> (struct {}) error zx.status;
};

节点组以 FIDL 表示:

/// Represents the conditions for evaluating the device
/// group properties.
type Condition = strict enum {
    ACCEPT = 0;
    REJECT = 1;
};

/// Represents a bind rule for a node group node.
type BindRule = struct {
    /// Property key.
    key NodePropertyKey;

    /// Condition for evaluating the property values in
    /// the matching process. The values are accepted or
    /// rejected based on the condition.
    condition Condition;

    /// A list of property values. Must not be empty. The property
    /// values must be the same type.
    values vector<NodePropertyValue>:MAX_PROPERTY_COUNT;
};

/// Struct that represents a node in a node group.
type NodeRepresentation = struct {
    /// Bind rules for the node group node. Keys must be unique.
    bind_rules: vector<BindRule>:MAX_PROPERTY_COUNT;

    /// Properties used for matching composite bind rules. Keys must be unique.
    bind_properties vector<NodeProperty>:MAX_PROPERTY_COUNT;
};

/// Struct that represents a node group.
type NodeGroup = table {
    /// The node group's name.
    1: name string:MAX;

    /// The nodes in the node group.
    2: nodes vector<NodeRepresentation>:MAX;
};

*NodeProperty 在 topology.fidl 中定义

示例:Focaltech 触摸驱动程序

DFv1 定义

在 DFv1 中,focaltech 触摸驱动程序包含以下绑定规则:使用 fuchsia.platform;

fuchsia.BIND_COMPOSITE == 1;
fuchsia.BIND_PLATFORM_DEV_VID == fuchsia.platform.BIND_PLATFORM_DEV_VID.GENERIC;
fuchsia.BIND_PLATFORM_DEV_DID == fuchsia.platform.BIND_PLATFORM_DEV_DID.FOCALTOUCH;

复合设备在 astro-touch 中定义:

// Composite binding rules for focaltech touch driver.
const zx_bind_inst_t ft_i2c_match[] = {
    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_I2C),
    BI_ABORT_IF(NE, BIND_I2C_BUS_ID, ASTRO_I2C_2),
    BI_MATCH_IF(EQ, BIND_I2C_ADDRESS, I2C_FOCALTECH_TOUCH_ADDR),
};
const zx_bind_inst_t goodix_i2c_match[] = {
    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_I2C),
    BI_ABORT_IF(NE, BIND_I2C_BUS_ID, ASTRO_I2C_2),
    BI_MATCH_IF(EQ, BIND_I2C_ADDRESS, I2C_GOODIX_TOUCH_ADDR),
};
static const zx_bind_inst_t gpio_int_match[] = {
    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
    BI_MATCH_IF(EQ, BIND_GPIO_PIN, GPIO_TOUCH_INTERRUPT),
};
static const zx_bind_inst_t gpio_reset_match[] = {
    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
    BI_MATCH_IF(EQ, BIND_GPIO_PIN, GPIO_TOUCH_RESET),
};

static const device_fragment_part_t ft_i2c_fragment[] = {
    {countof(ft_i2c_match), ft_i2c_match},
};
static const device_fragment_part_t goodix_i2c_fragment[] = {
    {countof(goodix_i2c_match), goodix_i2c_match},
};
static const device_fragment_part_t gpio_int_fragment[] = {
    {countof(gpio_int_match), gpio_int_match},
};
static const device_fragment_part_t gpio_reset_fragment[] = {
    {countof(gpio_reset_match), gpio_reset_match},
};

static const device_fragment_t ft_fragments[] = {
    {"i2c", countof(ft_i2c_fragment), ft_i2c_fragment},
    {"gpio-int", countof(gpio_int_fragment), gpio_int_fragment},
    {"gpio-reset", countof(gpio_reset_fragment), gpio_reset_fragment},
};
static const device_fragment_t goodix_fragments[] = {
    {"i2c", countof(goodix_i2c_fragment), goodix_i2c_fragment},
    {"gpio-int", countof(gpio_int_fragment), gpio_int_fragment},
    {"gpio-reset", countof(gpio_reset_fragment), gpio_reset_fragment},
};

const zx_device_prop_t props[] = {
    {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_GENERIC},
    {BIND_PLATFORM_DEV_PID, 0, PDEV_PID_ASTRO},
    {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_FOCALTOUCH},
};

然后使用 DdkAddComposite() API 进行添加:

const composite_device_desc_t comp_desc = {
    .props = props,
    .props_count = std::size(props),
    .fragments = ft3x27_touch_fragments,
    .fragments_count = std::size(ft3x27_touch_fragments),
    .primary_fragment = "i2c",
};

zx_status_t status = DdkAddComposite("ft3x27-touch", &comp_desc);
if (status != ZX_OK) {
   zxlogf(ERROR, "%s(ft3x27): CompositeDeviceAdd failed: %d", __func__, status);
   return status;
}
包含节点组的复合驱动程序

对于节点组,节点表示绑定规则可以与 GPIO 引脚匹配 ID 并为 GPIO 引脚类型提供绑定属性。这样, 复合驱动程序,用于包含与其所在的开发板无关的绑定规则 。

假设 fuchsia.gpio 绑定库:

library fuchsia.gpio;

extend uint fuchsia.BIND_PROTOCOL {
  DEVICE = 20,
  IMPL = 21,
};

enum FUNCTION {
   TOUCH_INTERRUPT,
   TOUCH_RESET,
};

节点组可定义如下:

node {
   bind_rules {
     fuchsia.BIND_FIDL_PROTOCOL == fuchsia.i2c.BIND_FIDL_PROTOCOL.DEVICE;
     fuchsia.BIND_I2C_BUS_ID == fuchsia.i2c.BIND_I2C_BUS_ID.ASTRO_2;
     fuchsia.BIND_I2C_ADDRESS == fuchsia.i2c.BIND_I2C_ADDRESS.FOCALTECH_TOUCH;
   },
   bind_properties {
     fuchsia.BIND_FIDL_PROTOCOL: fuchsia.i2c.BIND_FIDL_PROTOCOL.DEVICE,
     fuchsia.gpio.FUNCTION: fuchsia.platform.BIND_PLATFORM_DEV_DID.FOCALTOUCH,
   }
}

node {
   bind_rules {
     fuchsia.BIND_PROTOCOL == fuchsia.gpio.BIND_PROTOCOL.DEVICE;
     fuchsia.BIND_GPIO_PIN == fuchsia.amlogic.platform.s905d3.GPIOZ_PIN_ID.PIN_6;
   },
   bind_properties {
     fuchsia.BIND_PROTOCOL: fuchsia.gpio.BIND_PROTOCOL.DEVICE,
     fuchsia.gpio.FUNCTION: fuchsia.gpio.FUNCTION.TOUCH_INTERRUPT,
     fuchsia.gpio.BIND_PLATFORM_DEV_DID:
        fuchsia.platform.BIND_PLATFORM_DEV_DID.FOCALTOUCH,
   }
}

node {
   bind_rules {
     fuchsia.BIND_PROTOCOL == fuchsia.gpio.BIND_PROTOCOL.DEVICE;
     fuchsia.BIND_GPIO_PIN == fuchsia.amlogic.platform.s905d3.GPIOZ_PIN_ID.PIN_9;
   },
   bind_properties {
     fuchsia.BIND_PROTOCOL: fuchsia.gpio.BIND_PROTOCOL.DEVICE,
     fuchsia.gpio.FUNCTION: fuchsia.gpio.FUNCTION.TOUCH_RESET,
     fuchsia.BIND_PLATFORM_DEV_DID:
         fuchsia.platform.BIND_PLATFORM_DEV_DID.FOCALTOUCH,
   }
}

借助 driver2 库(DFv1 的 DDK),驱动程序代码随后可以添加节点 群组:

const fdf::BindRule i2c_bind_rules[] = {
  fdf::BindRule::Accept(
      BIND_FIDL_PROTOCOL, bind_fuchsia_i2c::BIND_FIDL_PROTOCOL_DEVICE),
  fdf::BindRule::Accept(
      BIND_I2C_BUS_ID, bind_fuchsia_i2c::BIND_I2C_BUS_ID_ASTRO_2),
  fdf::BindRule::Accept(
      BIND_I2C_ADDRESS, bind_fuchsia_i2c::BIND_I2C_ADDRESS_FOCALTECH_TOUCH),
};

const fdf::NodeProperty i2c_bind_properties[] = {
  fdf::MakeProperty(BIND_FIDL_PROTOCOL,
     bind_fuchsia_i2c::BIND_FIDL_PROTOCOL_DEVICE),
  fdf::MakeProperty(BIND_PLATFORM_DEV_DID,
     bind_fuchsia_platform::BIND_PLATFORM_DEV_DID_FOCALTOUCH),

};

const fdf::BindRule gpio_interrupt_bind_rules[] = {
  fdf::BindRule::Accept(
      BIND_PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
  fdf::BindRule::Accept(
      BIND_GPIO_PIN, bind_fuchsia_amlogic_platform_s905d2::GPIOZ_PIN_ID_PIN_4),
}

const fdf::NodeProperty gpio_interrupt_bind_properties[] = {
  fdf::MakeProperty(BIND_PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
  fdf::MakeProperty(bind_fuchsia_gpio::FUNCTION,
     bind_fuchsia_gpio::FUNCTION_TOUCH_INTERRUPT),
  fdf::MakeProperty(BIND_PLATFORM_DEV_DID,
     bind_fuchsia_platform::BIND_PLATFORM_DEV_DID_FOCALTOUCH),
};

const fdf::BindRule gpio_reset_bind_rules[] = {
  fdf::BindRule::Accept(
      BIND_PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
  fdf::BindRule::Accept(
      BIND_GPIO_PIN, bind_fuchsia_amlogic_platform_s905d2::GPIOZ_PIN_ID_PIN_9),
};

const fdf::NodeProperty gpio_reset_bind_properties[] = {
  fdf::MakeProperty(BIND_PROTOCOL, bind_fuchsia_gpio::BIND_PROTOCOL_DEVICE),
  fdf::MakeProperty(bind_fuchsia_gpio::FUNCTION,
       bind_fuchsia_gpio::FUNCTION_TOUCH_RESET),
  fdf::MakeProperty(BIND_PLATFORM_DEV_DID,
     bind_fuchsia_platform::BIND_PLATFORM_DEV_DID_FOCALTOUCH),
};

auto focaltech_touch_device =
  fdf::NodeGroup(i2c_bind_rules, i2c_bind_properties)
      .AddNodeRepresentation(gpio_interrupt_bind_rules,
          gpio_interrupt_bind_properties)
      .AddNodeRepresentation(gpio_reset_bind_rules, gpio_reset_bind_properties);
fdf::AddNodeGroup(node, focaltech_touch_device);

然后,需要更新 Focaltech 驱动程序复合绑定规则:

composite ft3x27_touch;

using fuchsia.amlogic.platform.s905d2;
using fuchsia.gpio;
using fuchsia.i2c;
using fuchsia.platform;

primary node "i2c" {
  fuchsia.BIND_FIDL_PROTOCOL == fuchsia.i2c.BIND_FIDL_PROTOCOL.DEVICE;
  fuchsia.BIND_PLATFORM_DEV_DID ==
      fuchsia.platform.BIND_PLATFORM_DEV_DID.FOCALTOUCH;
}

node "gpio-int" {
  fuchsia.BIND_PROTOCOL == fuchsia.gpio.BIND_PROTOCOL.DEVICE;
  fuchsia.gpio.GPIO_FUNCTION == fuchsia.gpio.GPIO_FUNCTION.TOUCH_INTERRUPT;
  fuchsia.BIND_PLATFORM_DEV_DID ==
      fuchsia.platform.BIND_PLATFORM_DEV_DID.FOCALTOUCH;
}

node "gpio-reset" {
  fuchsia.BIND_PROTOCOL == fuchsia.gpio.BIND_PROTOCOL.DEVICE;
  fuchsia.gpio.GPIO_FUNCTION == fuchsia.gpio.GPIO_FUNCTION.TOUCH_RESET;
  fuchsia.BIND_PLATFORM_DEV_DID ==
      fuchsia.platform.BIND_PLATFORM_DEV_DID.FOCALTOUCH;
}

DFv2 复合驱动程序方面的变更

节点组将取代 DFv2 复合驱动程序的当前机制。在 将来只能通过节点组创建复合节点将节点 组在 DFv2 中完全实现,我们会将所有复合驱动程序迁移到 并移除当前机制。

实现

此次变更将涉及为 fuchsia.driver.framework 提供支持节点组 FIDL API。需要更新驱动程序管理器和索引, 处理和跟踪所有节点组

为了支持可选节点,需要更新绑定编译器,使其支持 绑定语言中的 optional 关键字,并将信息编码为 字节码。

迁移到节点组

由于所有复合资源都将通过节点组创建,因此所有现有 DFv1 和 DFv2 中的复合对象需要在迁移完成后 。

在 DFv1 中,AddComposite() 的所有用法都将替换为 DDK 中的 AddNodeGroup() 函数调用。这涉及将驱动程序迁移到 使用复合绑定规则的复合驱动程序,然后使用 AddNodeGroup()

在 DFv2 中,需要为每个复合驱动程序创建一个匹配的节点组。

由于这两者都支持当前的复合驱动程序实现,因此可以 驱动程序索引中会与匹配节点产生冲突。对于 例如,如果某个节点与复合驱动程序或节点组中的一个节点匹配,则 Driver 索引可能仅返回一个匹配的结果。防止发生这种情况的一种方法是 优先匹配节点组而不是复合驱动程序。

为防止回归,将手动验证每个已迁移的驱动程序, 测试。对于 DFv1,迁移不是机械的,因此需要更多时间 花在验证司机上。

将所有复合驱动程序迁移到节点组后,我们就可以移除 当前实现。

性能

这不会影响性能,因为这类似于 创建 DFv1 节点

工效学设计

直接通过 FIDL 绑定创建节点组不符合人体工程学。接收者 简化代码并提高可读性,我们将在 driver2,用于定义绑定规则和属性。

大多数节点组都将通过 DSL 形式进行定义,例如 ACPI 和设备 未来的架构。因此,确保按照人体工程学设计 通过板级驱动程序中的代码写入节点组

向后兼容性

此属性需要与 DFv1 和 DFv2 兼容。为解决这一问题,我们可以 同时为 DFv1 实现节点组DFv1 驱动程序可通过 DDK。所有 AddComposite() 个调用都将迁移到节点组。兼容型填充码 将负责在 DFv1 和 DFv2 之间进行桥接。

安全注意事项

但有一个顾虑是 由于节点组是动态定义的 对主板配置进行静态审核可以通过 而无需绑定到任何节点

为了解决此问题,板驱动程序将是可以添加节点组的驱动程序。这个 可能会受到功能的限制此外,今后我们将 将重要数据(如设备树和 ACPI)迁移到声明性格式 让审核变得更加轻松

隐私注意事项

测试

我们将为此编写集成测试和单元测试。

文档

我们会更新复合设备概念文档。此外, 都可以在编写板级驱动程序的教程中展示。

缺点、替代方案和未知问题

虽然 AddNodeGroup() 提供了 AddComposite() 中的所有功能, 但仍然可能存在无法解决的漏洞或用例。在 此外,当我们开始将 AddComposite() 个支持请求迁移到节点组时, 可能会发现更多极端情况。

还有一些更复杂的情况,例如 ACPI 总线, 通过 ACPI 表进行枚举,并添加复合对象。由于存在许多 驱动程序,我们可能需要将多个 驱动程序。

先验技术和参考资料

驱动程序框架

复合节点

驱动程序绑定