平臺裝置和驅動程式¶
有關平臺匯流排的驅動程式模型介面,請參見 <linux/platform_device.h>: platform_device 和 platform_driver。這個偽匯流排用於連線基礎設施最少的總線上的裝置,例如用於將外圍裝置整合到許多片上系統處理器上的那些匯流排,或者一些“遺留”PC互連;而不是像 PCI 或 USB 這樣的大型正式指定的匯流排。
平臺裝置¶
平臺裝置通常表現為系統中的獨立實體。這包括傳統的基於埠的裝置和外圍匯流排的主橋,以及整合到片上系統平臺中的大多數控制器。它們通常的共同點是 CPU 匯流排的直接定址。極少數情況下,platform_device 會透過某種匯流排段連線;但它的暫存器仍然可以直接定址。
平臺裝置被賦予一個名稱,用於驅動程式繫結,以及一個資源列表,例如地址和 IRQ
struct platform_device {
const char *name;
u32 id;
struct device dev;
u32 num_resources;
struct resource *resource;
};
平臺驅動程式¶
平臺驅動程式遵循標準的驅動程式模型約定,其中發現/列舉在驅動程式之外處理,並且驅動程式提供 probe() 和 remove() 方法。它們使用標準約定支援電源管理和關閉通知
struct platform_driver {
int (*probe)(struct platform_device *);
void (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
bool driver_managed_dma;
};
請注意,probe() 通常應驗證指定的裝置硬體是否實際存在;有時平臺設定程式碼無法確定。探測可以使用裝置資源,包括時鐘和裝置平臺數據。
平臺驅動程式以正常方式註冊自己
int platform_driver_register(struct platform_driver *drv);
或者,在裝置已知不可熱插拔的常見情況下,probe() 例程可以駐留在 init 部分中,以減少驅動程式的執行時記憶體佔用
int platform_driver_probe(struct platform_driver *drv,
int (*probe)(struct platform_device *))
核心模組可以由多個平臺驅動程式組成。平臺核心提供幫助程式來註冊和登出驅動程式陣列
int __platform_register_drivers(struct platform_driver * const *drivers,
unsigned int count, struct module *owner);
void platform_unregister_drivers(struct platform_driver * const *drivers,
unsigned int count);
如果其中一個驅動程式註冊失敗,則到目前為止註冊的所有驅動程式將以相反的順序登出。請注意,有一個方便的宏將 THIS_MODULE 作為所有者引數傳遞
#define platform_register_drivers(drivers, count)
裝置列舉¶
通常,特定於平臺的(通常是特定於板的)設定程式碼將註冊平臺裝置
int platform_device_register(struct platform_device *pdev);
int platform_add_devices(struct platform_device **pdevs, int ndev);
一般規則是僅註冊實際存在的裝置,但在某些情況下,可能會註冊額外的裝置。例如,核心可能配置為與並非在所有板上都填充的外部網路介面卡一起使用,或者與某些板可能未連線到任何外圍裝置的整合控制器一起使用。
在某些情況下,引導韌體將匯出描述給定板上填充的裝置的表。如果沒有這樣的表,系統設定程式碼設定正確裝置的唯一方法通常是為特定目標板構建核心。這種特定於板的核心在嵌入式和定製系統開發中很常見。
在許多情況下,與平臺裝置關聯的記憶體和 IRQ 資源不足以讓裝置的驅動程式工作。板設定程式碼通常會使用裝置的 platform_data 欄位來儲存其他資訊,從而提供額外的資訊。
嵌入式系統通常需要一個或多個用於平臺裝置的時鐘,這些時鐘通常保持關閉狀態,直到它們被主動需要(以節省電量)。系統設定還將這些時鐘與裝置關聯,以便根據需要呼叫 clk_get(&pdev->dev, clock_name) 返回它們。
舊驅動程式:裝置探測¶
某些驅動程式未完全轉換為驅動程式模型,因為它們承擔了非驅動程式的角色:驅動程式註冊其平臺裝置,而不是將其留給系統基礎設施。此類驅動程式無法熱插拔或冷插拔,因為這些機制要求裝置建立位於與驅動程式不同的系統元件中。
這樣做的唯一“好”理由是處理較舊的系統設計,例如原始 IBM PC,它們依賴於容易出錯的“探測硬體”模型進行硬體配置。較新的系統已在很大程度上放棄了該模型,轉而支援對動態配置(PCI、USB)的匯流排級支援,或引導韌體提供的裝置表(例如,x86 上的 PNPACPI)。關於什麼可能在哪裡存在太多衝突的選項,即使是作業系統做出的有根據的猜測也常常會出錯,從而造成麻煩。
不鼓勵使用這種驅動程式風格。如果您正在更新這樣的驅動程式,請嘗試將裝置列舉移到更合適的位置,即驅動程式之外。這通常是清理,因為此類驅動程式往往已經具有“正常”模式,例如使用由 PNP 或平臺裝置設定建立的裝置節點。
儘管如此,仍然有一些 API 支援此類舊驅動程式。除非使用此類缺少熱插拔功能的驅動程式,否則請避免使用這些呼叫
struct platform_device *platform_device_alloc(
const char *name, int id);
您可以使用 platform_device_alloc() 動態分配裝置,然後您將使用資源對其進行初始化並使用 platform_device_register() 進行註冊。更好的解決方案通常是
struct platform_device *platform_device_register_simple(
const char *name, int id,
struct resource *res, unsigned int nres);
您可以使用 platform_device_register_simple() 作為一步呼叫來分配和註冊裝置。
裝置命名和驅動程式繫結¶
platform_device.dev.bus_id 是裝置的規範名稱。它由兩個元件構建
platform_device.name ... 也用於驅動程式匹配。
platform_device.id ... 裝置例項編號,否則為 “-1” 表示只有一個。
這些被連線在一起,因此名稱/id “serial”/0 表示 bus_id “serial.0”,而 “serial/3” 表示 bus_id “serial.3”;兩者都將使用名為 “serial” 的 platform_driver。而 “my_rtc”/-1 將是 bus_id “my_rtc”(沒有例項 id)並使用名為 “my_rtc” 的 platform_driver。
驅動程式繫結由驅動程式核心自動執行,在找到裝置和驅動程式之間的匹配項後呼叫驅動程式 probe()。如果 probe() 成功,則驅動程式和裝置將像往常一樣繫結。有三種不同的方法可以找到這樣的匹配
每當註冊裝置時,都會檢查該匯流排的驅動程式是否存在匹配項。平臺裝置應在系統啟動期間儘早註冊。
當使用 platform_driver_register() 註冊驅動程式時,將檢查該總線上的所有未繫結裝置是否存在匹配項。驅動程式通常在啟動過程中稍後註冊,或者透過模組載入註冊。
使用 platform_driver_probe() 註冊驅動程式的工作方式與使用 platform_driver_register() 相同,只是如果另一個設備註冊,則不會稍後探測驅動程式。(這沒問題,因為此介面僅用於不可熱插拔裝置。)
早期平臺裝置和驅動程式¶
早期平臺介面在系統啟動期間儘早向平臺裝置驅動程式提供平臺數據。該程式碼構建在 early_param() 命令列解析之上,並且可以儘早執行。
示例: 6 步中的 “earlyprintk” 類早期序列控制檯
1. 註冊早期平臺裝置資料¶
體系結構程式碼使用函式 early_platform_add_devices() 註冊平臺裝置資料。對於早期序列控制檯,這應該是序列埠的硬體配置。此時註冊的裝置稍後將與早期平臺驅動程式匹配。
2. 解析核心命令列¶
體系結構程式碼呼叫 parse_early_param() 來解析核心命令列。這將執行所有匹配的 early_param() 回撥。使用者指定的早期平臺裝置將在此時註冊。對於早期序列控制檯,使用者可以在核心命令列上將埠指定為 “earlyprintk=serial.0”,其中 “earlyprintk” 是類字串,“serial” 是平臺驅動程式的名稱,0 是平臺裝置 id。如果 id 為 -1,則可以省略點和 id。
3. 安裝屬於特定類的早期平臺驅動程式¶
體系結構程式碼可以選擇使用函式 early_platform_driver_register_all() 強制註冊屬於特定類的所有早期平臺驅動程式。來自步驟 2 的使用者指定裝置優先於這些裝置。序列驅動程式示例省略了此步驟,因為除非使用者已在核心命令列上指定埠,否則應停用早期序列驅動程式程式碼。
4. 早期平臺驅動程式註冊¶
使用 early_platform_init() 的編譯到核心中的平臺驅動程式在步驟 2 或 3 中自動註冊。序列驅動程式示例應使用 early_platform_init(“earlyprintk”, &platform_driver)。
5. 探測屬於特定類的早期平臺驅動程式¶
體系結構程式碼呼叫 early_platform_driver_probe() 以將與特定類關聯的已註冊的早期平臺裝置與已註冊的早期平臺驅動程式匹配。匹配的裝置將被 probe()。此步驟可以在早期啟動期間的任何時間點執行。儘快可能對序列埠的情況有利。
6. 在早期平臺驅動程式 probe() 中¶
驅動程式程式碼需要在早期啟動期間特別小心,尤其是在記憶體分配和中斷註冊方面。probe() 函式中的程式碼可以使用 is_early_platform_device() 來檢查它是在早期平臺裝置時間還是在常規平臺裝置時間被呼叫的。早期序列驅動程式此時執行 register_console()。
有關更多資訊,請參見 <linux/platform_device.h>。