驅動程式繫結

如需編寫繫結規則,請參閱繫結規則教學課程

在 Fuchsia 中,驅動程式庫架構會維護系統中驅動程式和裝置的樹狀結構。在本樹狀圖中,裝置代表 OS 可用的部分硬體。驅動程式會發布及繫結至裝置。舉例來說,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 是驅動程式庫的名稱。
  • Opszx_driver_ops,這是驅動程式庫作業掛鉤
  • VendorName 字串代表驅動程式庫供應商名稱。
  • Version 是代表驅動程式庫版本的字串。

詳情請參閱驅動程式庫開發說明文件

繫結規則

繫結規則定義呼叫驅動程式庫 bind() 掛鉤的條件。繫結規則中的每個陳述式都是裝置屬性的條件,前提是裝置必須含有 true,才能繫結至驅動程式庫。如果繫結規則已完成執行,且所有條件都符合,裝置協調工具就會呼叫驅動程式庫 bind() 掛鉤。

繫結規則應視為驅動程式庫應繫結的條件的宣告式運算式。因此,條件運算式的執行順序與最終評估作業無關。建議您將繫結規則視為布林值公式。

對帳單有四種類型:

  • 條件陳述式<key> == <value> (或 <key> != <value>) 格式的等式 (不等式) 運算式。
  • 接受陳述式是特定鍵允許的值清單。
  • If 陳述式提供簡單的分支。
  • 是非題 可用來明確評估繫結規則。

範例

您可以在 //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 陳述式,因此可透過繫結規則輕鬆追蹤路徑。

  • 對正確與否的敘述必須是其範圍中的唯一陳述式。繫結規則並非強制性計畫,評估順序也並不重要。將布林值陳述式 (尤其是 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" ;

ID 與規則運算式 [a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])? 相符,且不得與任何關鍵字相符。關鍵字清單如下:

accept
as
else
false
if
true
using

字串常值與規則運算式 ”[^”]*” 相符,數字常值與規則運算式 [0-9]+0x[0-9A-F]+ 相符。

繫結編譯器會忽略 // 開頭的任何行,以及以 /**/ 分隔的多行行。

複合繫結

除了將驅動程式繫結至裝置外,Fussia 中的驅動程式還可以使用繫結規則,從節點建立複合裝置。繫結規則的語言規格與非複合式繫結相同,但會分隔成包含名稱和一組陳述式的節點。

繫結規則中只能有一個主要節點。複合驅動程式庫會在與主要節點相同的驅動程式代管程序中啟動。

//tools/bindc/examples/composite-gizmo.bind 提供複合式繫結規則檔案範例。

composite gizmo_sysmem;

using fuchsia.platform;
using fuchsia.sysmem;
using fuchsia.tee;

primary node "sysmem" {
  fuchsia.BIND_PROTOCOL == fuchsia.sysmem.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。此情況會檢查繫結規則是否與列出的屬性相符,即 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": "sysmem",
        "tests": [
            {
                "name": "Match",
                "expected": "match",
                "device": {
                    "fuchsia.BIND_PROTOCOL": "fuchsia.sysmem.BIND_PROTOCOL.DEVICE"
                }
            },
            {
                "name": "Abort sysmem",
                "expected": "abort",
                "device": {
                    "fuchsia.BIND_PROTOCOL": "fuchsia.tee.BIND_PROTOCOL.DEVICE"
                }
            }
        ]
    }
]

建構

定義測試建構目標,如下所示

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 = "gizmo.bind"
  bind_output = “gizmo.bindbc”
  tests = "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> 形式,而針對列舉項目則只需要 ID。定義多個具有相同常值的原始值是有效的。

文法

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" ;

ID 與規則運算式 [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