C++ 驅動程式庫評分標準

撰寫基本驅動程式庫

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

中繼目錄

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

驅動程式原始碼

使用 Driver Component 程式庫 (sdk/lib/driver/component/) 編寫驅動程式。驅動程式應從標頭 <lib/driver/component/cpp/driver_base2.h> 中定義的 fdf::DriverBase2 類別繼承。

驅動程式提供 FUCHSIA_DRIVER_EXPORT2 巨集,用於匯出標頭 <lib/driver/component/cpp/driver_export2.h> 中定義的驅動程式庫符號。這個巨集應位於 .cc 檔案中。

初始化驅動程式庫

請勿在驅動程式庫的建構函式中放置任何邏輯。您必須覆寫 fdf::DriverBase2 提供的其中一個 Start() 方法,實作驅動程式的初始化邏輯,即使初始化邏輯為空白也一樣,而非使用建構函式。請務必只使用單一 Start() 變體。

初始化邏輯包含:

  • 擷取及設定所有驅動程式庫資源。
  • 建立服務連線。
  • 將驅動程式庫的自有服務新增至傳出目錄。
  • 新增子節點 (在資源設定完成並提供自有服務後執行)。
  • 使用 zx_bti_release_quarantine() 從隔離區釋出 BTI。

關閉驅動程式庫

驅動程式的解構函式應不含任何邏輯。在大多數情況下,不需要覆寫 DriverBase2::Stop() 並提供實作項目。除非驅動程式庫功能特別需要 DriverBase2::Stop(),例如執行正常硬體關機或取消固定 DMA,否則請勿實作這項功能。函式參數中的 fdf::StopCompleter 必須在函式結尾呼叫。

建構檔案

所有新驅動程式都必須使用 Bazel 進行建構程序,且必須包含下列目標:

  • fuchsia_driver_bind_bytecode
  • fuchsia_cc_driver
  • fuchsia_driver_component

與司機通訊

驅動程式會透過 FIDL 服務與父項驅動程式通訊。

提供服務

提供 FIDL 服務的驅動程式必須維護 fidl::ServerBindingGroup (使用 Zircon 傳輸時) 或 fdf::ServerBindingGroup (適用於驅動程式庫傳輸)。除非驅動程式庫必須限制為單一用戶端連線,否則請避免使用 fidl::ServerBindingfdf::ServerBinding

建立任何子節點之前,必須先將伺服器繫結新增至 Start() 函式內的驅動程式傳出目錄。此外,CML 檔案必須在能力中指定服務,並從 self 公開服務。

使用服務

如要使用服務,駕駛人應將服務納入 bind 規則,並在 CML 的 uses 區段中指定服務。

新增孩子

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

應使用 DriverBase2AddChild()add_child 輔助程式庫 (sdk/lib/driver/node/cpp/add_child.h) 新增子節點。只有在子節點需要使用其他記錄器時,才應使用輔助程式庫。

除非用於支援已淘汰的 devfs,否則所有子節點都應為未擁有狀態。如果擁有子節點,驅動程式庫必須儲存從輔助函式收到的 fuchsia_driver_framework::Node 用戶端。

新增子節點時,請明確定義子節點的名稱,不要使用 DriverBase2 中的 name()。使用 node_offers 輔助程式庫 (sdk/lib/driver/component/cpp/node_offers.h) 建立優惠。使用 node_properties 程式庫 (sdk/lib/driver/component/cpp/node_properties.h) 建立屬性。節點屬性鍵和值應使用產生的繫結程式庫程式碼繫結。

記錄

如要記錄,驅動程式應使用格式型記錄 API,例如fdf::info() 驅動程式記錄器程式庫 (sdk/lib/driver/logging/cpp/logger.h) 中的 API。如要記錄自訂型別,請實作 std::format

請按照 Fuchsia 記錄指南,使用 warningerror 記錄層級記錄 FIDL 錯誤等失敗情形。

Zircon 資源

中斷

初始化期間,應在驅動程式庫中擷取並儲存中斷。 中斷應由 async::Irq 物件 (sdk/lib/async/include/lib/async/cpp/irq.h) 包裝。

應使用 IrqMethod 物件處理中斷觸發條件。處理中斷觸發程序後,應使用 ack() 確認 Irq 物件,以便重新啟動中斷程序,並再次觸發。

DMA

如要執行記憶體作業,驅動程式應使用 MmioBuffer,位於 sdk/lib/driver/mmio/cpp/mmio-buffer.h。這個程式庫提供原始 mmio_block_t 物件的包裝函式,用於讀取和寫入。

為確保安全存取位元欄位,建議使用 hwreg/bitfields 程式庫。這個程式庫位於 zircon/system/ulib/hwreg/include/hwreg/bitfields.h

驅動程式應控管 BTI,並停止初始化期間可能正在進行的任何 DMA。完成後,應呼叫 zx_bti_release_quarantine(),告知 BTI 已重新取得硬體控制權。如要與硬體共用 DMA 的驅動程式,必須事先釘選 BTI。硬體存取記憶體完成後,驅動程式庫會負責取消釘選記憶體。在任何情況下,都不應在解構函式中取消釘選 BTI。

時鐘

時鐘是透過 fuchsia.hardware.clock FIDL 服務控制。 驅動程式必須在所有依賴的時鐘上呼叫 Enable(),並在不再需要時鐘訊號時呼叫 Disable()。駕駛人必須先啟用時鐘,才能呼叫 Disable()

測試

確認建構目標包含必要的驅動程式庫測試。

單元測試

單元測試應使用 gtests (third_party/googletest/src/googletest/include/gtest/gtest.h) 編寫。

測試整個驅動程式庫

如果測試整個驅動程式庫,驅動程式庫應由 ForegroundDriverTestBackgroundDriverTest驅動程式測試程式庫 (sdk/lib/driver/testing/cpp/driver_test.h) 包裝。單元測試應在測試初始化後立即呼叫 StartDriver(),並在 Teardown() 函式中呼叫 StopDriver()。驅動程式庫所需的所有服務和資源,都應在自訂驅動程式庫測試的 Environment 類別中初始化及提供。

如果驅動程式庫需要平台裝置服務,測試應例項化 FakePlatformDevice (sdk/lib/driver/fake-platform-device/) DriverTestEnvironment 並提供服務。同樣地,如果驅動程式庫需要 FIDL 服務,則應在 DriverTestEnvironment 中例項化並提供服務的測試實作。

測試驅動程式庫的部分

如果只隔離驅動程式庫程式邏輯的子集進行測試,測試可能需要特定的驅動程式庫環境元件。

評估使用驅動程式記錄器程式庫 (sdk/lib/driver/logging/cpp/logger.h) 的邏輯時,測試必須例項化並維護 fdf_testing::ScopedGlobalLogger 例項 (sdk/lib/driver/testing/cpp/scoped_global_logger.h)。

此外,如果邏輯需要驅動程式庫調度器,測試就必須設定並保留 fdf_testing::DriverRuntime 物件 (sdk/lib/driver/testing/cpp/driver_runtime.h)。

模擬/仿冒程式庫

需要虛擬 FIDL 服務或資源的測試,應盡可能使用 sdk/lib/driver/ 中提供的虛擬和模擬程式庫。

下列程式庫適用於常見的 FIDL 服務:

下列程式庫適用於常見的內部 Zircon 物件、記憶體區域或驅動程式庫執行階段資源:

整合測試

整合測試應使用 Driver Test Realm (sdk/lib/driver_test_realm/) 架構。首先,測試必須建立與 DriverTestRealm 服務的連線,在 RealmArgs 中執行必要設定,然後使用引數呼叫 Start()