繫結規則教學課程

本指南說明如何為驅動程式庫編寫繫結規則,讓駕駛人繫結至所需的裝置。內容說明如何找出節點屬性,並使用繫結語言為節點寫入繫結規則。

本指南假定您熟悉驅動程式繫結

節點屬性的目前狀態

目前,在繫結程式庫和 C++ 標頭檔案中定義節點屬性。過去,節點屬性是整數型鍵/值組合,描述為 C++ 結構。所有屬性都已在 C++ 標頭檔案中定義,繫結規則也是驅動程式庫原始碼的一部分。

然而,繫結系統日前經過調整,因此繫結規則會使用繫結語言在個別檔案中定義,節點屬性則支援採用布林值、字串、整數或列舉值的字串型鍵。

遷移作業目前正在進行,將所有驅動程式從舊的繫結系統移至新版系統。系統會在繫結程式庫中重新定義 C++ 標頭中的節點屬性。舉例來說,所有裝置通訊協定 ID 繫結值都是在 protodefs.h 中以硬式編碼方式寫入。現在每個裝置通訊協定都是由各自的繫結程式庫定義,其中包含通訊協定 ID 以及與該通訊協定相關聯的其他節點屬性。繫結程式庫都位於 src/devices/bind 中。

在遷移作業完成前,新舊系統必須同時支援新舊系統繫結系統。

節點屬性的未來狀態

繫結遷移作業完成後,我們即可停止支援舊版整數型節點屬性,並移除 C++ 定義,例如 protodefs.hbinding_priv.h。所有屬性都會在繫結程式庫中定義,且鍵將完全以字串為基礎。

現有屬性可以更新,以便使用新系統的功能。例如,BIND_COMPOSITE 屬性是僅適用於複合裝置的標記。不過,由於舊系統只支援整數值,因此會以整數取代布林值表示屬性值。移除舊繫結系統後,屬性值可以變更為布林值。

其他可進行的變更是 VID 的表示方式。我們可以使用 VID 名稱,而不是為 VID 指派不重複的整數。舉例來說,Intel VID 目前是以整數值 0x8 表示:

library fuchsia.intel.platform;

extend uint fuchsia.BIND_PLATFORM_DEV_VID {
  INTEL = 0x8,
};

改用新的繫結系統後,VID 可能會以字串值或甚至是列舉的方式表示。

查詢節點屬性

使用 ffx 驅動程式庫清單裝置

ffx driver list-devices -v 指令會以樹狀結構輸出所有裝置的屬性:

Name     : _TZ_
Moniker  : root.sys.platform.pt.acpi._TZ_
Driver   : None
5 Properties
[ 1/  5] : Key fuchsia.BIND_PROTOCOL Value 0x1F
[ 2/  5] : Key fuchsia.BIND_PCI_VID Value 0x1AF4
[ 3/  5] : Key fuchsia.BIND_PCI_DID Value 0x1052
[ 4/  5] : Key fuchsia.COMPOSITE_BIND Value 1
[ 5/  5] : Key "fuchsia.acpi.HID" Value "GFSH0005"

驅動程式庫原始碼中的驅動程式屬性

新增子項裝置時,駕駛人可以提供繫結規則相符的屬性。因此,您可以找到要透過驅動程式庫原始碼繫結至的屬性。

在 DFv1 中,原始碼中的節點屬性會以「屬性」和「字串屬性」表示。屬性包含整數式的鍵和值。字串屬性則包含以字串為基礎的鍵和值,可以是整數、布林值、字串或列舉。

以下程式碼片段是驅動程式庫新增具有 BIND_PROTOCOL 屬性和「ENABLE_TEST」字串屬性的裝置。

device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = "parent";
args.ops = &dev_ops;

zx_device_str_prop_t str_props[] = {
      zx_device_str_prop_t{.key = "ENABLE_TEST",
                           .property_value = str_prop_bool_val(true)}
};
args.str_props = str_props;
args.str_prop_count = std::size(str_props);

device_add(parent, &args, &dev);

在 DFv2 中,節點屬性以 fuchsia.driver.framework FIDL 程式庫中的 NodeProperty 結構表示:

auto properties = fidl::VectorView<fdf::wire::NodeProperty>(arena, 2);
properties[0] = fdf::MakeProperty(arena, BIND_PROTOCOL, ZX_PROTOCOL_PCI);
properties[1] = fdf::MakeProperty(arena, "ENABLED_TEST", true);

auto args = fdf::wire::NodeAddArgs::Builder(arena)
                  .name(arena, "sample-child")
                  .properties(properties)
                  .Build();

定義繫結程式庫中的屬性

隨著驅動程式從舊繫結系統遷移至新系統,我們會在新系統的繫結程式庫中重新定義舊屬性。這些繫結程式庫位於 src/devices/bind 目錄中。

應在繫結程式庫中定義任何新屬性。

舊繫結系統中的屬性

大多數的舊節點屬性鍵與值都是在 binding_priv.hprotodefs.h 中定義。

binding_priv.h 包含硬式編碼的屬性鍵。每個屬性鍵都會獲得一個不重複的整數值。在新繫結系統中,這些鍵會以 fuchsia.BIND_ 前置字串重新定義。舉例來說,BIND_PROTOCOL 會變為 fuchsia.BIND_PROTOCOLBIND_COMPOSITE 會變為 fuchsia.BIND_COMPOSITE

protodefs.h 包含適用於裝置通訊協定的硬式編碼 ID 值。

裝置通訊協定繫結程式庫

protodefs.h 中的每個裝置通訊協定都應該有專屬的繫結程式庫,其中包含本身的通訊協定 ID 和相關屬性。其中一個範例就是 fuchsia.i2c 繫結程式庫,這個程式庫不僅定義通訊協定 ID 值,也會定義其他 i2c 相關屬性:

library fuchsia.i2c;

extend uint fuchsia.BIND_PROTOCOL {
  DEVICE = 24,
  IMPL = 25,
};

extend uint fuchsia.BIND_I2C_CLASS {
  HID = 0x01,
};

extend uint fuchsia.BIND_I2C_ADDRESS {
  BACKLIGHT = 0x2C,
  ETH = 0x18,
  FOCALTECH_TOUCH = 0x38,
  AMBIENTLIGHT = 0x39,
};

通訊協定 ID 值與 protodefs.h 中定義的值相符:

DDK_PROTOCOL_DEF(I2C,                     24,   "i2c", 0)
DDK_PROTOCOL_DEF(I2C_IMPL ,               25,   "i2c-impl", 0)

IMPL 值代表實作並提供 i2c 通訊協定的驅動程式庫。DEVICE 代表使用 IMPL 通訊協定並提供 DEVICE 通訊協定的核心驅動程式。

寫入繫結規則

本節說明如何針對驅動程式庫繫結目前的狀態編寫繫結規則。如果是只繫結至上述屬性集的繫結規則,必須寫入:

using fuchsia.acpi;
using fuchsia.pci;

fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.VIRTIO;
fuchsia.BIND_PCI_DID == fuchsia.pci.BIND_PCI_DID.VIRTIO_DEV_TYPE_INPUT;
fuchsia.BIND_COMPOSITE == 1;
fuchsia.acpi.HID == "GFSH0005";

屬性鍵

整數鍵名稱是在 binding_priv.h 中定義,且前置字串為「fuchsia」。舉例來說,dm dump 中的鍵 0x03 會判定為 fuchsia.BIND_COMPOSITE,因為 binding_priv.h 包含下列內容:

#define BIND_FLAGS 0x0000          // value of the flags register
#define BIND_PROTOCOL 0x0001       // primary protocol of the device
#define BIND_AUTOBIND 0x0002       // if this is an automated bind/load
#define BIND_COMPOSITE 0x003       // Whether this is a composite device

屬性值

由於屬性與 PCI,因此可在 fuchsia.pci.bind 程式庫中找到相符的值。將 0x001af4fuchsia.BIND_PCI_VID 中的值進行比對,即可找到 fuchsia.pci.BIND_PCI_VID.VIRTIO

extend uint fuchsia.BIND_PCI_VID {
  TEST = 0x0eff,
  AMD = 0x1002,
  REALTEK = 0x10ec,
  NVIDIA = 0x10de,
  GOOGLE = 0x1ae0,
  VIRTIO = 0x1af4,
  BROADCOM = 0x14e4,
  ATHEROS = 0x168c,
  INTEL = 0x8086,
};

如果繫結程式庫缺少某個值,您必須在新的或現有的繫結程式庫中新增缺少的定義。或者,您可以直接為該值編寫繫結規則:

fuchsia.BIND_PCI_VID == 0x1af4;

不過,建議您在繫結程式庫中定義一個值。

複合繫結規則

複合式繫結規則也會採用相同的程序。您可以針對要寫入繫結規則的各個節點顯示節點屬性,並為節點寫入繫結規則。

假設您要寫入複合繫結規則,其中包含一個繫結至上述範例的節點,以及另一個繫結至以下項目的節點:

Name     : I2C2
Moniker  : root.sys.platform.pt.acpi.I2C2
Driver   : None
6 Properties
[ 1/  6] : Key fuchsia.BIND_ACPI_ID           Value 0x000034
[ 2/  6] : Key fuchsia.BIND_PCI_TOPO          Value 0x0000aa
[ 3/  6] : Key fuchsia.BIND_ACPI_BUS_TYPE     Value 0x000001
[ 4/  6] : Key "fuchsia.hardware.acpi.Device" Value true
[ 5/  6] : Key fuchsia.BIND_PROTOCOL          Value 0x00001e
[ 6/  6] : Key "fuchsia.platform.DRIVER_FRAMEWORK_VERSION" Value 0x000002

接著,您可以編寫下列內容:

using fuchsia.acpi;
using fuchsia.pci;

primary node "pci_sample" {
  fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
  fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.VIRTIO;
  fuchsia.BIND_PCI_DID == fuchsia.pci.BIND_PCI_DID.VIRTIO_DEV_TYPE_INPUT;
  fuchsia.BIND_COMPOSITE == 1;
  fuchsia.acpi.HID == "GFSH0005";
}

node "acpi" {
  fuchsia.driver.framework.dfv2 == true;
  fuchsia.BIND_ACPI_ID == 0x000034;
  fuchsia.BIND_PCI_TOPO == 0x0000aa;
  fuchsia.BIND_ACPI_BUS_TYPE == 0x000001;
}

選用節點

選用節點是指無須存在於裝置群組內的節點,即可 以便與複合驅動程式庫進行比對 這項功能僅適用於使用裝置群組,系統不會比對選用節點 將拓撲中的節點與複合驅動程式庫配對的純舊複合材料。

在繫結規則中,在 node 關鍵字前方加上 optional 即可標記節點 這兩個標籤代表可選填按照慣例,所有選用節點都應寫在 一般額外節點

選用節點是用來寫入較通用的複合驅動程式庫規則。適用對象 舉例來說,HID 按鈕驅動程式庫可支援 9 種不同的按鈕類型 (調高音量、攝影機靜音等), 但並非所有硬體裝置都會有以上所有按鈕。含有選用節點 可編寫適用於各平台通用的繫結規則,讓董事會駕駛人 具備一組 HID 按鈕驅動程式庫支援的任意按鈕組合。

繫結規則範例

分支繫結規則

繫結語言支援透過 if 陳述式建立分支版本,但有一些限制。

如果陳述式必須有其他區塊,且屬於終端機,這項限制會明確地做出執行作業分支,藉此提高可讀性。由於沒有陳述式可能會遵循 if 陳述式,因此可以透過繫結規則輕鬆追蹤路徑。

if fuchsia.hardware.tee.Service == fuchsia.hardware.tee.Service.ZirconTransport {
  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;
}

如果陳述式遵循相關限制,就能夠建立巢狀結構。例如:

if fuchsia.driver.framework.dfv2 == true {
  if fuchsia.acpi.hid == "PNP0303" {
    true;
  } else {
    fuchsia.acpi.first_cid == "PNP0303";
  }
} else {
  fuchsia.BIND_PROTOCOL == fuchsia.acpi.BIND_PROTOCOL.DEVICE;
}

排除多個屬性值

如要拒絕單一鍵的多個屬性值,可以將不等式條件陳述式鏈結在一起。例如,如果您需要拒絕多個 fuchsia.BIND_PCI_DID 值,可以執行以下動作:

fuchsia.BIND_PCI_DID != 0x191b;
fuchsia.BIND_PCI_DID != 0x1912;
fuchsia.BIND_PCI_DID != 0x191d;
fuchsia.BIND_PCI_DID != 0x1902;
fuchsia.BIND_PCI_DID != 0x1916;
fuchsia.BIND_PCI_DID != 0x191e;