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

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

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

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

摘要

這項 RFC 提案說明如何在 Driver Framework v2 (DFv2) 中,於執行階段定義複合節點。

提振精神

背景

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

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

問題

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

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

此外,由於繫結規則需要在建構時瞭解節點屬性,因此靜態繫結規則也難以編寫與主機板無關的複合驅動程式。舉例來說,觸控複合驅動程式庫可能需要特定功能的 GPIO 針腳節點。由於 GPIO 針腳會因主機板而異,因此使用該節點編寫與主機板無關的繫結規則會比較困難。

利害關係人

講師:cpu@google.com

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

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

社交:

意見請求草案已在 tq-drivers 之間共用

設計

需求條件

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

  • 複合節點中每個父項的繫結規則
  • 用於將複合節點與複合驅動程式庫相符的繫結屬性
  • 複合節點中的父項。如果複合節點應包含特定父項,則可能在執行階段決定

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

總覽

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

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

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

device-group-繫結-diagram

節點表示方式

群組中的每個節點表示法都會定義下列項目:

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

節點群組繫結規則

繫結規則包含接受和拒絕的繫結屬性值清單。 如要符合繫結規則,繫結屬性必須包含所有接受的繫結屬性值,且不得包含任何遭拒的值。舉例來說,如果節點群組節點包含繫結規則:

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

如果節點的 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 觸控驅動程式

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 中,您需要為每個複合式驅動程式庫建立相符的節點群組。

由於兩者都支援目前的複合驅動程式庫實作,因此驅動程式庫索引中可能會出現與相符節點的衝突。舉例來說,如果節點與複合式驅動程式庫或節點群組中的節點相符,則驅動程式庫索引可能只會傳回一個相符項目。如要避免這種情況,請優先將節點群組與複合驅動程式相符。

為避免發生回歸情形,系統會透過手動和測試方式驗證每個遷移的驅動程式庫。DFv1 的遷移並非機械式,因此需要花費更多時間驗證驅動程式。

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

效能

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

人體工學

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

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

回溯相容性

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

安全性考量

其中一個疑慮是,由於節點群組是動態定義,我們無法靜態稽核主機板設定。您可以在不繫結至任何節點的情況下,操控節點拓撲。

為解決這個問題,只有主機板驅動程式庫可以新增節點群組。這項操作可透過功能限制。此外,我們日後會將重要資料遷移至宣告式格式,例如裝置樹狀結構和 ACPI,方便稽核。

隱私權注意事項

測試

我們會為此編寫整合測試和單元測試。

說明文件

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

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

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

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

既有技術和參考資料

驅動程式架構

複合節點

驅動程式繫結