裝置驅動¶
請參閱 struct device_driver 的 kerneldoc。
分配¶
裝置驅動程式是靜態分配的結構。儘管一個系統中可能有多個裝置由一個驅動程式支援,但 struct device_driver 代表的是整個驅動程式(而不是特定的裝置例項)。
初始化¶
驅動程式必須至少初始化 name 和 bus 欄位。 它還應該初始化 devclass 欄位(當它到達時),以便它可以在內部獲得正確的連結。 它還應該儘可能多地初始化回撥,儘管每個回撥都是可選的。
宣告¶
如上所述,struct device_driver 物件是靜態分配的。 下面是 eepro100 驅動程式的示例宣告。 此宣告僅是假設性的。 它依賴於將驅動程式完全轉換為新模型
static struct device_driver eepro100_driver = {
.name = "eepro100",
.bus = &pci_bus_type,
.probe = eepro100_probe,
.remove = eepro100_remove,
.suspend = eepro100_suspend,
.resume = eepro100_resume,
};
大多數驅動程式將無法完全轉換為新模型,因為它們所屬的匯流排具有帶有特定於匯流排的欄位的特定於匯流排的結構,這些欄位無法概括。
最常見的例子是裝置 ID 結構。 驅動程式通常定義它支援的裝置 ID 陣列。 這些結構的格式以及比較裝置 ID 的語義完全是特定於匯流排的。 將它們定義為特定於匯流排的實體會犧牲型別安全性,因此我們保留特定於匯流排的結構。
特定於匯流排的驅動程式應在特定於匯流排的驅動程式的定義中包含通用的 struct device_driver。 像這樣
struct pci_driver {
const struct pci_device_id *id_table;
struct device_driver driver;
};
包含特定於匯流排的欄位的定義如下所示(再次使用 eepro100 驅動程式)
static struct pci_driver eepro100_driver = {
.id_table = eepro100_pci_tbl,
.driver = {
.name = "eepro100",
.bus = &pci_bus_type,
.probe = eepro100_probe,
.remove = eepro100_remove,
.suspend = eepro100_suspend,
.resume = eepro100_resume,
},
};
有些人可能會發現嵌入式結構初始化的語法很笨拙,甚至有點難看。 到目前為止,這是我們找到的實現我們想要的最好的方法...
註冊¶
int driver_register(struct device_driver *drv);
驅動程式在啟動時註冊該結構。 對於沒有特定於匯流排的欄位的驅動程式(即沒有特定於匯流排的驅動程式結構),它們將使用 driver_register 並傳遞指向其 struct device_driver 物件的指標。
但是,大多數驅動程式將具有特定於匯流排的結構,並且需要使用類似 pci_driver_register 的方式向匯流排註冊。
驅動程式應儘早註冊其驅動程式結構,這一點很重要。 向核心註冊會初始化 struct device_driver 物件中的多個欄位,包括引用計數和鎖。 假定這些欄位始終有效,並且裝置模型核心或匯流排驅動程式可以使用它們。
過渡匯流排驅動程式¶
透過定義包裝函式,可以更輕鬆地過渡到新模型。 驅動程式可以完全忽略通用結構,並讓匯流排包裝器填寫欄位。 對於回撥,匯流排可以定義通用回撥,這些回撥將呼叫轉發到驅動程式的特定於匯流排的回撥。
此解決方案僅旨在作為臨時解決方案。 為了在驅動程式中獲取類資訊,無論如何都必須修改驅動程式。 由於將驅動程式轉換為新模型應該減少一些基礎架構複雜性和程式碼大小,因此建議在新增類資訊時轉換驅動程式。
訪問¶
註冊物件後,它可以訪問物件的公共欄位,如鎖和裝置列表
int driver_for_each_dev(struct device_driver *drv, void *data,
int (*callback)(struct device *dev, void *data));
devices 欄位是已繫結到驅動程式的所有裝置的列表。 LDM 核心提供了一個輔助函式來對驅動程式控制的所有裝置進行操作。 此輔助函式鎖定每個節點訪問上的驅動程式,並在每次訪問裝置時進行正確的引用計數。
sysfs¶
註冊驅動程式後,將在其匯流排目錄中建立一個 sysfs 目錄。 在此目錄中,驅動程式可以匯出使用者空間介面以全域性控制驅動程式的操作。 例如,切換驅動程式中的除錯輸出。
此目錄的未來功能將是“devices”目錄。 此目錄將包含指向其支援的裝置目錄的符號連結。
回撥¶
int (*probe) (struct device *dev);
probe() 條目在任務上下文中呼叫,匯流排的 rwsem 被鎖定,並且驅動程式部分繫結到裝置。 驅動程式通常使用 container_of() 將 “dev” 轉換為特定於匯流排的型別,無論是在 probe() 中還是在其他例程中。 該型別通常提供裝置資源資料,例如 pci_dev.resource[] 或 platform_device.resources,這些資料除了 dev->platform_data 之外,還用於初始化驅動程式。
此回撥儲存特定於驅動程式的邏輯,以將驅動程式繫結到給定裝置。 這包括驗證裝置是否存在,是否是驅動程式可以處理的版本,是否可以分配和初始化驅動程式資料結構,以及是否可以初始化任何硬體。 驅動程式通常使用 dev_set_drvdata() 儲存指向其狀態的指標。 當驅動程式已成功將其自身繫結到該裝置時,probe() 將返回零,並且驅動程式模型程式碼將完成將驅動程式繫結到該裝置的部分。
驅動程式的 probe() 可能會返回一個負的 errno 值,以指示該驅動程式未繫結到此裝置,在這種情況下,它應該釋放它分配的所有資源。
(可選)如果驅動程式依賴於尚未提供的資源(例如,由尚未初始化的驅動程式提供),則 probe() 可能會返回 -EPROBE_DEFER。 驅動程式核心會將該裝置放入延遲探測列表,並稍後嘗試再次呼叫它。 如果驅動程式必須延遲,它應該儘早返回 -EPROBE_DEFER,以減少花費在需要解開並在以後重新執行的設定工作上的時間。
警告
如果 probe() 已經建立了子裝置,即使這些子裝置在清理路徑中再次刪除,也不能返回 -EPROBE_DEFER。 如果在註冊子裝置後返回 -EPROBE_DEFER,則可能導致對同一驅動程式的 .probe() 呼叫的無限迴圈。
void (*sync_state) (struct device *dev);
sync_state 僅針對裝置呼叫一次。 當裝置的所有消費者裝置都已成功探測時,將呼叫它。 裝置的消費者列表是透過檢視將該裝置連線到其消費者裝置的裝置連結來獲得的。
首次嘗試呼叫 sync_state() 是在 late_initcall_sync() 期間進行的,以便為韌體和驅動程式提供時間將裝置相互連結。 在首次嘗試呼叫 sync_state() 期間,如果此時裝置的所有消費者都已成功探測,則會立即呼叫 sync_state()。 如果在首次嘗試期間沒有裝置的消費者,那麼這也將被視為“裝置的所有消費者都已探測”,並且會立即呼叫 sync_state()。
如果在首次嘗試呼叫裝置的 sync_state() 期間,仍有尚未成功探測的消費者,則 sync_state() 呼叫將被推遲,並且僅在裝置的一個或多個消費者成功探測時才會在將來重新嘗試。 如果在重新嘗試期間,驅動程式核心發現有一個或多個尚未探測的裝置的消費者,則 sync_state() 呼叫將再次被推遲。
sync_state() 的一個典型用例是讓核心乾淨地接管啟動載入程式對裝置的管理。 例如,如果啟動載入程式將裝置保持開啟狀態並處於特定的硬體配置,則裝置的驅動程式可能需要將裝置保持在啟動配置中,直到裝置的所有消費者都已探測。 一旦裝置的所有消費者都已探測,裝置的驅動程式就可以同步裝置的硬體狀態,以匹配所有消費者請求的聚合軟體狀態。 因此命名為 sync_state()。
雖然可以從 sync_state() 中受益的資源的明顯例子包括調節器等資源,但 sync_state() 對於像 IOMMU 這樣的複雜資源也可能有用。 例如,具有多個消費者(其地址被 IOMMU 重新對映的裝置)的 IOMMU 可能需要將其對映固定在(或新增到)啟動配置,直到其所有消費者都已探測。
雖然 sync_state() 的典型用例是讓核心乾淨地接管啟動載入程式對裝置的管理,但 sync_state() 的使用並不限於此。 只要在裝置的所有消費者都探測後採取操作有意義,就可以使用它
int (*remove) (struct device *dev);
呼叫 remove 以從裝置取消繫結驅動程式。 如果裝置從系統中物理刪除,如果驅動程式模組正在解除安裝,在重新啟動序列期間或其他情況下,可能會呼叫此方法。
由驅動程式決定裝置是否存在。 它應該釋放專門為裝置分配的任何資源。 即,裝置 driver_data 欄位中的任何內容。
如果裝置仍然存在,它應該使裝置靜止並將其置於受支援的低功耗狀態。
int (*suspend) (struct device *dev, pm_message_t state);
呼叫 suspend 以使裝置處於低功耗狀態。
int (*resume) (struct device *dev);
Resume 用於將裝置從低功耗狀態恢復。
屬性¶
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *driver, char *buf);
ssize_t (*store)(struct device_driver *, const char *buf, size_t count);
};
裝置驅動程式可以透過其 sysfs 目錄匯出屬性。 驅動程式可以使用 DRIVER_ATTR_RW 和 DRIVER_ATTR_RO 宏來宣告屬性,這些宏的工作方式與 DEVICE_ATTR_RW 和 DEVICE_ATTR_RO 宏相同。
示例
DRIVER_ATTR_RW(debug);
這等效於宣告
struct driver_attribute driver_attr_debug;
然後可以使用以下命令從驅動程式的目錄新增和刪除該屬性
int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);