Fuchsia 驅動程式庫開發 (DFv1)

Fuchsia 驅動程式是可在驅動程式代管程序中動態載入的共用程式庫 建立使用者空間中的處理程序驅動程式庫的載入程序是由 。詳情請見 裝置型號 有關駕駛座、驅動程式管理器以及驅動程式庫和裝置的詳細資訊 生命週期

目錄結構

駕駛人或許可在來源樹狀結構的 driver 子目錄中找到 指定區域中的 原始碼版面配置文件。大多數 您可以在 //src/devices/ 底下找到 Fuchsia 驅動程式。這些圖片會歸類在一起 動態調整驅動程式通訊協定的定義 ddk/include/lib/ddk/protodefs.h.適用對象 例如 USB 乙太網路驅動程式庫 //src/connectivity/ethernet/drivers/ 而不是 //src/devices/usb/drivers/,因為 會實作乙太網路通訊協定。但實作 USB 堆疊的驅動程式 位於 //src/devices/usb/drivers/,原因是 實作 USB 通訊協定

在驅動程式庫的 BUILD.gn 中,應有 fuchsia_driver_component 目標。 為了讓驅動程式庫顯示在/boot/driver底下,請將該司機納入內 如下方相關主面板檔案的 board_bootfs_labels 清單中 //boards。為了讓產品顯示在 /system/driver 中,內容應為 已加入含有 driver_package 建構目標的系統套件中 則由 //boards 中的相關董事會檔案參照。驅動程式管理器 會在 /boot/driver 中優先顯示,然後為 /system/driver/ 載入可供載入的驅動程式。

建立新的驅動程式庫

可使用 建立工具。只要執行下列指令即可:

fx create driver --path <PATH> --lang cpp

這會建立 <PATH> 目錄,其中包含一個空白驅動程式庫,其中 <PATH> 的最後一個部分是驅動程式庫名稱和 GN 目標名稱。之後 指令執行,請執行下列步驟:

  1. 在以下位置加入 fuchsia_driver_componentdriver_package 建構目標: 將司機加入系統的正確位置
  2. 如果是封裝驅動程式,應將 driver_package 建構目標新增至 收到有關 //boards//vendor/<foo>/boards 中的相關主面板檔案, xx_package_labels GN 引數。
  3. 如果是啟動驅動程式,應新增 fuchsia_driver_component 建構目標 至 //boards//vendor/<foo>/boards 中的相關主面板檔案, board_bootfs_labels GN 引數。
  4. <PATH>:tests 建構目標中加入 tests 建構目標,即可取得 進行深入的整合
  5. meta/<NAME>.bind 中新增適當的繫結規則。
  6. meta/<NAME>-info.json 中新增驅動程式庫資訊。檔案必須包含 short_descriptionareas至少符合以下列出的其中一個區域 //build/drivers/areas.txt
  7. 編寫驅動程式庫的功能。

宣告驅動程式庫

驅動程式庫至少應包含驅動程式庫宣告並實作 bind()驅動程式庫任務。

驅動程式管理器成功載入驅動程式並繫結至裝置 會尋找與裝置相符的驅動程式庫。駕駛人宣告自身的裝置 與透過繫結規則相容,因此應置於 .bind 檔案中 一起驅動程式庫。繫結編譯器會編譯這些規則, 驅動程式庫宣告巨集,包含 C 標頭檔案中的這些規則。 下列繫結規則會將 AHCI 驅動程式

using fuchsia.pci;
using fuchsia.pci.massstorage;

fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
fuchsia.BIND_PCI_CLASS == fuchsia.pci.BIND_PCI_CLASS.MASS_STORAGE;
fuchsia.BIND_PCI_SUBCLASS == fuchsia.pci.massstorage.BIND_PCI_SUBCLASS_SATA;
fuchsia.BIND_PCI_INTERFACE == 0x01;
fuchsia.BIND_COMPOSITE == 1;

這些繫結規則狀態表示驅動程式庫繫結至具有 BIND_PROTOCOL 的裝置 與 pci 命名空間中的 DEVICE 和指定 PCI 相符的屬性 class/subclass/interface已從 fucnsia.pci 匯入 pci 命名空間 第一行程式碼庫詳情請參閱繫結 說明文件

如要產生包含這些繫結規則的驅動程式庫宣告巨集, 為對應的 bind_rules 建構目標。這應宣告依附元件 「使用」陳述式。

driver_bind_rules("bind") {
    rules = "meta/ahci.bind"
    bind_output = "ahci.bindbc"
    deps = [
        "//src/devices/bind/fuchsia.pci",
        "//src/devices/bind/fuchsia.pci.massstorage",
    ]
}

驅動程式現在可包含產生的標頭,並使用 下方的巨集。"zircon" 是供應商 ID,"0.1" 則是驅動程式庫版本。

#include <lib/ddk/binding_driver.h>
...
ZIRCON_DRIVER(ahci, ahci_driver_ops, "zircon", "0.1");

PCI 驅動程式會發布 並包含下列屬性:

zx_device_str_prop_t pci_device_props[] = {
    ddk::MakeStrProperty(bind_fuchsia::PCI_VID,
        static_cast<uint32_t>(device.info.vendor_id)),
    ddk::MakeStrProperty(bind_fuchsia::PCI_DID,
        static_cast<uint32_t>(device.info.device_id)),
    ddk::MakeStrProperty(bind_fuchsia::PCI_CLASS,
        static_cast<uint32_t>(device.info.base_class)),
    ddk::MakeStrProperty(bind_fuchsia::PCI_SUBCLASS,
        static_cast<uint32_t>(device.info.sub_class)),
    ddk::MakeStrProperty(bind_fuchsia::PCI_INTERFACE,
        static_cast<uint32_t>(device.info.program_interface)),
    ddk::MakeStrProperty(bind_fuchsia::PCI_REVISION,
        static_cast<uint32_t>(device.info.revision_id)),
    ddk::MakeStrProperty(bind_fuchsia::PCI_TOPO, pci_bind_topo),
};

目前,繫結變數和巨集已在 lib/ddk/binding.h.在不久的將來 所有節點屬性都會由 fuchsia.pci 等繫結程式庫定義。 程式庫匯入了如要推出新的裝置類別, 將新的節點屬性導入繫結標頭中 繫結程式庫

節點屬性為 32 位元值。如果您的變數值必須大於 32 位元值,分成多個 32 位元變數。例如 ACPI HID 值,長度為 8 個字元 (64 位元)。並分為 《BIND_ACPI_HID_0_3》和《BIND_ACPI_HID_4_7》。遷移作業進行繫結後 程式庫完成後,您就能使用其他資料類型,例如字串、 較大的數字和布林值

您可以在 bind_rules 建構規則中指定 disable_autobind = true, 停用自動繫結行為在這種情況下,可以將驅動程式庫繫結至 裝置使用 fuchsia.device.Controller/Bind FIDL 呼叫。

驅動程式繫結

當使用者與裝置配對時,系統會呼叫驅動程式的 bind() 函式。通用 驅動程式庫會初始化裝置所需的任何資料結構 初始化此函式中的硬體因此不應執行任何耗時 或封鎖這個函式,因為它是從驅動程式代管程序的 RPC 執行緒,且在此期間將無法服務其他要求。 而應產生新的執行緒來執行長時間的工作。

驅動程式不應假設 bind()。可能需要重設硬體,或確保裝置處於已知狀態 時間。因為系統會藉由轉動驅動程式庫程式,從驅動程式庫中復原 主機,叫用 bind() 時硬體可能處於不明狀態。

bind() 通常有四個結果:

  1. 驅動程式會判斷裝置確實受到支援,因此不需要執行任何操作 需花費較多心力,因此推出 device_add() 的新裝置 (C/ ddk::Device::DdkAdd()DDKTL C++ 包裝函式程式庫並傳回 `ZX_OK。

  2. 即使繫結規則相符,驅動程式仍會判斷 無法支援 (可能是因為檢查 hw 版本位元或不正確) 以及 傳回錯誤。

  3. 驅動程式需要先進一步初始化,裝置才會準備就緒,或是 但確定可以支援,因此會發布隱形裝置 實作 init() 掛鉤 啟動執行緒以繼續工作,同時將 ZX_OK 傳回 bind()。 該執行緒最後會呼叫 C 中的 device_init_reply(),或 ddk::InitTxn::Reply()DDKTL C++ 包裝函式程式庫。在 已收到回覆。狀態會指出 ZX_OK 是否能成功 裝置初始化成功,且應一律顯示在畫面上,或 錯誤訊息,指出應移除裝置。

  4. 駕駛人代表有 0..n 子項的公車或控制器 會動態顯示或消失。在此情況下,它應該發布裝置 以代表公車或控制器,接著以動態方式發布 代表硬體上鎖的子項 (繫結至該下游驅動程式) 搭乘大眾運輸工具例如:AHCI/SATA、USB 等

裝置新增並設定為可偵測後,就可供 用戶端程序,並供相容驅動程式建立繫結。

Banjo 協定

驅動程式為裝置提供一組裝置作業,以及選用的通訊協定作業。 裝置作業會實作裝置生命週期方法和外部介面, 其他使用者空間應用程式和服務所呼叫的裝置。 通訊協定作業會為裝置呼叫的 其他驅動程式載入同一個驅動程式代管程序。

您可以在 device_add_args_t 中為裝置傳遞一組通訊協定作業。如果 裝置支援多種通訊協定,請實作 get_protocol() 裝置作業。A 罩杯 裝置只能有一個通訊協定 ID。通訊協定 ID 會與類別對應 裝置是在 devfs 底下發布

驅動程式作業

司機運作時,通常會處理兒童駕駛人發出的用戶端要求 或其他程序用來執行要求時 與家長溝通 (例如透過 MMIO) (例如將 USB 交易排入佇列)。

由驅動程式代管程序外部程序所發出的外部用戶端要求,會由 兒童駕駛人通常以相同程序完成 裝置類別對應的通訊協定。駕駛對驅動程式庫要求應 使用 banjo 通訊協定取代裝置作業

裝置只要呼叫 device_get_protocol()

裝置幹擾

裝置中斷是由中斷物件實作,這類物件屬於 核心物件駕駛人要求裝置的控制代碼導致裝置中斷 裝置通訊協定方法。傳回的帳號代碼將會繫結至 由家長驅動程式庫程式定義,導致裝置的適當中斷情形。適用對象 舉例來說,PCI 通訊協定會為 PCI 子項實作 map_interrupt()。A 罩杯 驅動程式庫應產生執行緒,來等待中斷控點。

核心會自動處理遮蓋及解除遮蓋中斷情形,即 視情況而定 都屬於使用者層級對於層級觸發的硬體中斷 「zx_interrupt_wait()會套用遮罩 在再次呼叫之前,中斷狀態並取消遮蓋中斷情形 下次再接再厲針對邊緣觸發的中斷,中斷情形會保持解除遮罩。

中斷執行緒不應執行任何長時間執行的工作。對於有下列需求的駕駛 或執行長時間的工作,請使用背景工作執行緒。

你可以透過以下方式指出中斷控點: zx_interrupt_trigger() 已開啟 要從 zx_interrupt_wait() 返回的運算單元 ZX_INTERRUPT_SLOT_USER。這是 而在驅動程式庫清理期間關閉中斷執行緒。

FIDL 訊息

非驅動程式庫程序

要定義各裝置類別的訊息,請參閱 FIDL。每部裝置 實作零或多個 FIDL 通訊協定,每個管道都會透過多工處理 用戶端。駕駛人有機會理解 FIDL 訊息 message() 掛鉤。只有以下項目可存取非驅動程式庫元件: 開發 Kubernetes 叢集

其他程序中的驅動程式

如果驅動程式庫必須以獨立程序與驅動程式庫溝通, 而不是定義通訊協定作業,而必須代管傳出目錄,類似於 元件,其中應代管子項驅動程式庫程式會存取的所有 FIDL 通訊協定 在繫結。

通訊協定運算與 FIDL 訊息

通訊協定作業為裝置定義 In-Process API。FIDL 訊息會定義 用於獨立程序的 API。如果函式符合以下條件,請定義通訊協定運算 僅由其他駕駛人在相同程序中呼叫駕駛人應打電話給 以便使用這些功能。

隔離裝置

使用 DEVICE_ADD_MUST_ISOLATE 新增的裝置會產生新的驅動程式代管程序。 裝置必須有代管 FIDL 的傳出目錄 通訊協定繫結至裝置的驅動程式會載入至新裝置 驅動程式代管程序提供的 FIDL 通訊協定 並由父項驅動程式庫提供的傳出目錄

駕駛權

雖然駕駛人會在使用者空間處理程序中執行,但該程式對一組較嚴格的限制 額外權利。不允許驅動程式存取檔案系統 包括 devfs這表示驅動程式庫無法與任意裝置互動。如果 您的驅動程式庫需要執行此操作,請考慮改為編寫服務元件。適用對象 虛擬控制台是由 virtcon 元件。

特殊權限作業,例如 zx_vmo_create_contiguous()zx_interrupt_create 需要 根資源控點這個帳號代碼僅供 系統驅動程式庫 (在 x86 系統上使用 ACPI平台)。裝置 要求其父項為該函式執行此類作業與 父項驅動程式庫。

同樣地,驅動程式庫也不得要求任意 MMIO 範圍,導致中斷 或 GPIOPCI 和平台等匯流排驅動程式只會傳回資源 正確連結。

進階主題和訣竅

初始化花費較長時間

如果裝置需要很長的時間才能完成初始化,該怎麼辦?先前提到 上方顯示 null_bind() 函式,表示系統成功傳回 驅動程式管理器,此驅動程式庫已與裝置建立關聯。我們無法透過 在繫結函式中長時間執行基本上應該將 發布裝置、加以發布,輕鬆完成

不過,裝置可能需要執行較長的初始化作業,例如 為:

  • 列舉硬體點
  • 載入韌體。
  • 協商通訊協定

以此類推,這可能需要較長的時間

您可以將裝置發布為「隱藏」只要實作裝置「init()」 吊人胃口的情節片段。系統會在透過 device_add() 新增裝置後執行 init() 掛鉤。 也可以用於安全存取裝置狀態及產生背景工作執行緒。 裝置會繼續顯示,而且保證在 系統會呼叫 device_init_reply(),可以從任何執行緒完成。這符合 或是沒有人可使用您的裝置 (因為目前還沒有人知道這個地點)。現在你的裝置 可以透過背景執行緒執行長時間作業

當裝置準備好處理用戶端要求時,請呼叫 device_init_reply() 如此一來,路徑名稱空間就會顯示

省電

你的裝置依序提供兩則摘要 (suspend()resume()) 以便支援電力或其他資源節省功能。

兩者都接收到裝置結構定義指標和旗標引數,但是旗標引數 只有在暫停的情況下使用

檢舉 意義
DEVICE_SUSPEND_FLAG_REBOOT 驅動程式應自行關閉,準備重新啟動或關機
DEVICE_SUSPEND_FLAG_REBOOT_BOOTLOADER
DEVICE_SUSPEND_FLAG_REBOOT_RECOVERY
DEVICE_SUSPEND_FLAG_POWEROFF 駕駛人應自行關機準備關機
DEVICE_SUSPEND_FLAG_MEXEC 驅動程式應自行關閉以準備柔性重新啟動
DEVICE_SUSPEND_FLAG_SUSPEND_RAM 驅動程式應排列,以便從 RAM 重新啟動

參考資料:支援函式

本節列出提供給驅動程式使用的支援功能。

存取子函式

傳遞內容區塊,做為第一個引數傳遞至驅動程式通訊協定 函式是一種不透明的資料結構。這表示為了存取 資料元素,您需呼叫存取子函式:

函式 目的
device_get_name() 擷取裝置名稱

管理功能

以下函式可用於管理裝置:

函式 目的
device_add() 將裝置新增至家長
device_async_remove() 安排裝置及所有子項的移除作業