如需編寫繫結規則,請參閱繫結規則教學課程。
在 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
是驅動程式庫的名稱。Ops
是zx_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;
}
語言限制
語言設有一些限制,以提高可讀性,並確保繫結規則是簡單表示驅動程式庫應繫結的條件。
不得使用空白區塊。 空白區塊是否意味著驅動程式庫會繫結或中止。作者應使用明確的
true
或false
陳述式。如果陳述式必須具有其他區塊且為終端機。這項限制會明確顯示執行作業分支版本,藉此提升可讀性。由於沒有任何陳述式位於
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>,
…
};
每個鍵都有類型,且所有對應到該鍵的值都必須屬於該型別。該語言支援基本類型:uint
、string
或 bool
;以及列舉類型 (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。