RFC-0197:複合節點的節點群組

RFC-0197:複合組合的節點群組
狀態已接受
區域
  • Driver SDK
說明

支援在執行階段定義複合體。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2022-11-01
審查日期 (年-月-日)2022-09-26

摘要

這份 RFC 提出了設計,說明如何在 Driver Framework 2 (DFv2) 的執行階段定義組合節點。

提振精神

背景

在 Driver Framework 1 版 (DFv1) 中,驅動程式可以在執行階段透過 AddComposite() 函式建立複合節點。驅動程式會為複合式項目的父項和繫結屬性定義繫結規則,而驅動程式管理器會將複合式節點建立為驅動程式庫的子項。個別驅動程式庫會繫結至複合節點。

在 DFv2 中,複合驅動程式會透過繫結規則定義複合節點,這些規則是使用驅動程式庫繫結語言編寫的靜態檔案。在執行階段載入驅動程式時,Driver Runner 會收集符合組合繫結規則的上層節點,然後建立組合節點。複合驅動程式庫會繫結至節點。

問題

由於複合驅動程式只能以靜態方式定義複合節點,因此無法完全取代 DFv1 的 AddComposite() 功能。這會導致部分驅動程式無法遷移至 DFv2。

部分複合節點只有在執行階段才會出現,因此無法在複合驅動程式中定義。舉例來說,ACPI 匯流排驅動程式會在執行階段建立 ACPI 複合節點。當匯流排驅動程式啟動時,會讀取 ACPI 表格,並使用該資訊建立節點。

靜態繫結規則也會導致難以編寫不受板卡限制的複合驅動程式,因為繫結規則需要在建構期間瞭解節點屬性。舉例來說,觸控複合式驅動程式庫可能需要 GPIO 針腳的節點,才能提供特定功能。由於 GPIO 針腳會因板子而異,因此要使用該節點編寫不受板子限制的繫結規則會更加困難。

相關人員

講師:cpu@google.com

審查者:suramialhotra@google.com (FDF)、dgilhooley@google.com (FDF)

諮詢對象:駕駛員團隊成員

社會化:

tq-drivers 之間共用 RFC 草稿

設計

需求條件

由於設計目標是建立機制,讓驅動程式在執行階段定義複合節點,因此下列項目必須為動態:

  • 複合節點中每個父項的繫結規則
  • 用於將複合節點與複合式驅動程式庫比對的繫結屬性
  • 複合節點中的父項。系統可能會在執行階段判斷組合節點是否應納入特定父項

此外,DFv1 和 DFv2 也必須支援該機制。為了轉換至 DFv2,所有 DFv1 驅動程式都必須從 AddComposite() 遷移至複合驅動程式。

總覽

這項提案會在驅動程式架構中新增 API,讓驅動程式用於定義一組裝置節點。

當驅動程式庫定義節點群組時,程序如下:

  1. 驅動程式管理器要求驅動程式庫索引尋找與節點群組相符的複合式驅動程式庫
  2. 找到相符的複合驅動程式庫後,驅動程式管理器會為每個節點表示法找出相符的裝置節點
  3. 每個節點表示法都找到相符項目後,驅動程式管理器會建立複合節點,並將節點設為父項,然後將其繫結至複合驅動程式庫。主要節點和節點名稱由複合式驅動程式庫提供

device-group-繫結-diagram

節點表示法

群組中的每個節點表示法皆會使用下列定義:

  • 繫結規則:將節點表示法比對至裝置節點的規則
  • 繫結屬性:節點表示法中的繫結屬性,用於比對複合式驅動程式庫的靜態繫結規則

節點群組繫結規則

繫結規則包含一組已接受和拒絕的繫結資源值。為了符合繫結規則,繫結屬性必須包含所有接受的繫結屬性值,而非任何遭拒的值。舉例來說,如果節點群組節點包含繫結規則:

  • 接受 fuchsia.BIND_PROTOCOL 值 15 和 17
  • 拒絕 fuchsia.BIND_PLATFORM_DEV_VID 值「Intel」

接著,如果裝置的 fuchsia.BIND_PROTOCOL 屬性值為 15 或 17,且不含 fuchsia.BIND_PLATFORM_DEV_VID 屬性的「Intel」值,則裝置會繫結至節點。

複合繫結規則中的選用節點

由於部分父項的供應情形只有在執行階段才會知曉,因此組合驅動程式必須能夠支援選用節點。為此,您必須更新繫結語言,讓複合驅動程式將節點標示為選用:

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

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

比對複合式驅動程式

比對程序是透過將複合驅動程式庫繫結規則套用至節點表示法的繫結屬性來完成。只要符合下列條件,系統就會成功比對:

  • 所有節點表示法都必須與複合繫結規則中的節點相符
  • 所有非選用組合繫結規則節點都必須與節點表示法相符
  • 比對結果不得含糊不清的內容:
    • 每個節點表示法都必須只對應至一個複合繫結規則節點
    • 節點表示法無法與複合繫結規則中的相同節點相符
  • 節點不必依序配對

如果發生不確定的情況,系統會顯示警告訊息。

composite_bind_diagram

節點群組 API

驅動程式需要使用 FIDL,透過 fuchsia.driver.framework FIDL 程式庫中的 NodeGroupManager 通訊協定新增節點群組:

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 Touch Driver

DFv1 定義

在 DFv1 中,focaltech 觸控驅動程式包含下列繫結規則:using 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 中,您需要為每個複合驅動程式庫建立相符的節點群組。

由於兩者都支援目前的複合驅動程式庫實作,因此驅動程式庫索引可能會與相符的節點發生衝突。舉例來說,如果節點與複合驅動程式庫或節點群組中的某個節點相符,驅動程式庫索引可能只會傳回一個相符項目。避免這種情況發生的方法之一,就是將節點群組比複合驅動程式優先,以便進行比對。

為避免回歸,我們會手動驗證每個已遷移的驅動程式庫,並透過測試驗證。對於 DFv1,遷移作業並非機械式,因此需要花費更多時間驗證驅動程式。

所有複合驅動程式都遷移至節點群組後,我們就可以移除目前的實作。

成效

這不會對效能造成太大影響,因為這與在 DFv1 中建立複合節點的方式相似。

人體工學

直接透過 FIDL 繫結建立節點群組並不符合人體工學。為了簡化操作並提高可讀性,我們會在 driver2 中建立輔助程式庫,用於定義繫結規則和屬性。

未來,大多數節點群組都會透過 DSL 形式定義,例如 ACPI 和裝置樹狀結構。因此,透過板卡驅動程式中的程式碼寫入節點群組並不符合人體工學,因此並非優先事項

回溯相容性

這個值必須與 DFv1 和 DFv2 相容。為解決這個問題,我們也可以為 DFv1 實作節點群組。DFv1 驅動程式可以透過 DDK 新增節點群組。所有 AddComposite() 呼叫都會遷移至節點群組。相容性 shim 將負責在 DFv1 和 DFv2 之間建立橋接。

安全性考量

其中一個疑慮是,由於節點群組是動態定義,因此我們無法以靜態方式稽核主機板設定。您可以不綁定任何節點,直接操作節點拓樸圖。

為解決這個問題,板卡驅動程式庫將可新增節點群組。這項功能可透過能力進行限制。此外,我們日後會將重要資料遷移至裝置樹狀結構和 ACPI 等宣告式格式,以利稽核。

隱私權注意事項

測試

並為此編寫整合測試和單元測試。

說明文件

我們會更新複合裝置概念文件。此外,您也可以在撰寫板卡驅動程式庫的教學課程中展示如何建立節點群組。

缺點、替代方案和未知事項

雖然 AddNodeGroup() 提供 AddComposite() 中的所有功能,但仍可能存在無法解決的差異或用途。此外,當我們開始將 AddComposite() 案例遷移至節點群組時,可能會發現更多邊緣案例。

有些情況較為複雜,例如 ACPI 匯流排會透過 ACPI 表格動態列舉並新增複合項目。由於許多驅動程式都會繫結至 ACPI 複合體,因此我們可能必須一次遷移多個驅動程式。

既有技術與參考資料

驅動程式架構

複合節點

驅動程式繫結