撰寫基本驅動程式庫
程式碼結構應遵循 fx create 驅動程式庫 goldens 範本。
中繼目錄
每個驅動程式庫目錄都必須有 meta 子目錄,內含下列檔案:
- 定義驅動程式庫
bind規則的bind檔案 - 元件資訊清單
驅動程式原始碼
Rust 驅動程式庫原始碼應位於 src 子目錄中,且名為 lib.rs。Rust 原始碼應使用 fdf_component 程式庫定義驅動程式庫。
驅動程式必須定義為實作 fdf_component::Driver 特徵的結構體。實作的 start 方法會接收 DriverContext 結構體,其中包含連線至驅動程式庫並提供通訊協定和記錄所需的結構體。此外,驅動程式庫程式碼必須使用 driver_register!() 巨集,向驅動程式架構註冊驅動程式庫。
元件資訊清單
元件資訊清單不應向 allow_sync_calls 宣告主要調度器,因為 Rust 驅動程式必須是非同步。
初始化驅動程式庫
所有初始化邏輯都應放在 start 方法中,且必須將 DriverContext 中的節點控制代碼儲存至 Driver 結構體。初始化邏輯包含:
- 取得 (使用 DriverContext::take_node) 並儲存驅動程式庫的 Node 物件,直到關機為止。無論是有意或無意,放下
Node物件都會導致驅動程式庫關閉。在大多數情況下,這不會用到,但仍應儲存在Driver物件中 (做為_node,以免系統發出未使用的警告)。這樣一來,當驅動程式庫關閉時,Node就會正確捨棄。 - 擷取及設定所有驅動程式庫資源。
- 建立服務連線。
- 將驅動程式庫的自有服務新增至傳出目錄。
- 新增子節點 (在資源設定完成並提供自有服務後執行)。
- 從隔離區釋出 BTI。
關閉驅動程式庫
在大多數情況下,Driver stop() 實作項目都是空白。除非驅動程式庫程式功能明確需要清除作業 (例如執行正常硬體關機或取消固定 DMA),否則請勿新增更多項目。
如果驅動程式庫控制關閉時間,則應將 Node 物件儲存為可捨棄的項目,例如在 Option<Node> 中,或視需要儲存在互斥鎖後方。放下 Node 物件後,驅動程式庫就會開始關閉。
建構檔案
所有 Rust 驅動程式都必須使用 GN 進行建構程序,且必須包含下列目標:
fuchsia_driver_bind_bytecodefuchsia_rust_driverfuchsia_driver_component
與司機通訊
驅動程式會透過 FIDL 服務與上層驅動程式通訊。
提供服務
請參閱 examples/drivers/transport/driver/rust_next/parent 和
examples/drivers/transport/zircon/rust_next/parent/,瞭解如何使用 rust_next 繫結。Fuchsia 建議將這項繫結用於一般 FIDL 傳輸服務,並要求將其用於驅動程式庫程式傳輸服務 (因為舊版繫結不支援)。
提供 FIDL 服務的驅動程式必須將 FIDL 服務實作為特徵。初始化期間,它需要在傳出目錄中新增 ServiceOffer,然後使用 serve_outgoing() 函式將目錄提供給 DriverContext。目錄提供服務後,請使用 fuchsia_async Scope 程式庫產生工作,在 ServiceFs 事件迴圈中執行。
CML 檔案必須在能力中指定服務,並從 self 公開服務。
使用服務
請參閱 examples/drivers/transport/driver/rust_next/child 和
examples/drivers/transport/zircon/rust_next/child/,瞭解如何使用 rust_next 繫結。Fuchsia 建議將這項繫結用於一般 FIDL 傳輸服務,並要求將其用於驅動程式庫程式傳輸服務 (因為舊版繫結不支援)。
如要使用服務,駕駛人應將其納入 bind 規則,並在 CML 的 uses 區段中指定服務。連線至服務時,請使用服務能力,而非通訊協定。
新增孩子
新增子節點的主要原因是,驅動程式庫需要為其他驅動程式庫提供服務或資源。請避免無故新增子項。
除非必要,否則子節點應在 start() 函式中新增為初始化邏輯的一部分。
應使用 Rust 包裝函式 Node::add_child 新增子節點。除非用於支援 devfs (Rust 驅動程式不支援),否則所有子節點都應為未擁有。因此不應有自有子項。
記錄
所有記錄都使用標準 Rust 記錄 API。請按照 Fuchsia 記錄指南,在記錄 FIDL 錯誤等失敗情形時,使用 warning 或 error 記錄層級。
Zircon 資源
針對 Vmo 和 Interrupt 等資源使用 Zircon 核心繫結。
中斷
請避免將中斷儲存為原始 zx::Handle 物件,因為這會導致等待作業期間必須使用 unsafe 程式碼。請改為將裝置 struct 定義為 K 的泛型:zx::InterruptKind (例如 Device<K>),使用 Zircon 中斷繫結 (sdk/rust/zx/src/interrupt.rs),並用來包裝及儲存 zx::Interrupt<K> 物件中的控制代碼。
使用 fasync::OnInterrupt 建立中斷串流,並在非同步工作處理這些中斷,而不是使用 std::thread::spawn 產生專屬執行緒,並在 irq.wait() 上封鎖。
DMA
MMIO 控制代碼必須使用 VmoMapping::map() (sdk/lib/驅動程式庫/mmio/rust/) 對應至 MmioRegion 物件。存取 MMIO 暫存器時,驅動程式絕不能對原始整數執行手動位元運算 (例如 val |= 1 << 5;)。請改用 mmio::register! 和 mmio::register_block! 巨集
時鐘
時鐘是透過 fuchsia.hardware.clock FIDL 服務控制。
驅動程式必須在所有依賴的時鐘上呼叫 Enable(),並在不再需要時鐘訊號時呼叫 Disable()。駕駛人必須先啟用時鐘,才能呼叫 Disable()。
非同步程式碼
請勿使用 .detach() 分離工作。請改為在驅動程式庫 start 方法中初始化 fuchsia_async::Scope,並使用該方法產生並行工作。駕駛人必須保留 Scope 的擁有權。
測試
確認建構目標包含必要的驅動程式庫測試。
單元測試
您應該盡可能在一般單元測試中測試驅動程式的程式碼。如要在包含驅動程式庫啟動和關閉作業時「單元測試」某個項目,可以使用 fdf_component::testing::TestHarness 啟動驅動程式並與其互動。
如果驅動程式庫聲明的 output_name 為 my_driver,則驅動程式的 GN 目標會是 my_driver_test。如需範例,請參閱 //examples/drivers 中任何 Rust 範例驅動程式的 GN 規則。
整合測試
使用 DriverTestRealm 程式庫編寫整合測試,就像使用 C++ 驅動程式庫一樣。
一般測試建議
測試使用 MmioRegion 的驅動程式時:
- 避免手動模擬:請勿模擬
read/write方法,而是使用實際的 VMO 來支援記憶體區域。 - 使用 VMO 插入:在測試中,建立
zx::Vmo,使用VmoMapping::map()對應,然後將產生的MmioRegion傳遞至驅動程式庫。