Rust 驅動程式庫評分量表

撰寫基本驅動程式庫

程式碼結構應遵循 fx create 驅動程式庫 goldens 範本。

中繼目錄

每個驅動程式庫目錄都必須有 meta 子目錄,內含下列檔案:

驅動程式原始碼

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_bytecode
  • fuchsia_rust_driver
  • fuchsia_driver_component

與司機通訊

驅動程式會透過 FIDL 服務與上層驅動程式通訊。

提供服務

請參閱 examples/drivers/transport/driver/rust_next/parentexamples/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/childexamples/drivers/transport/zircon/rust_next/child/,瞭解如何使用 rust_next 繫結。Fuchsia 建議將這項繫結用於一般 FIDL 傳輸服務,並要求將其用於驅動程式庫程式傳輸服務 (因為舊版繫結不支援)。

如要使用服務,駕駛人應將其納入 bind 規則,並在 CML 的 uses 區段中指定服務。連線至服務時,請使用服務能力,而非通訊協定。

新增孩子

新增子節點的主要原因是,驅動程式庫需要為其他驅動程式庫提供服務或資源。請避免無故新增子項。 除非必要,否則子節點應在 start() 函式中新增為初始化邏輯的一部分。

應使用 Rust 包裝函式 Node::add_child 新增子節點。除非用於支援 devfs (Rust 驅動程式不支援),否則所有子節點都應為未擁有。因此不應有自有子項。

記錄

所有記錄都使用標準 Rust 記錄 API。請按照 Fuchsia 記錄指南,在記錄 FIDL 錯誤等失敗情形時,使用 warningerror 記錄層級。

Zircon 資源

針對 VmoInterrupt 等資源使用 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 傳遞至驅動程式庫。