1. 程式人生 > >【linux】驅動-7-平臺裝置驅動

【linux】驅動-7-平臺裝置驅動

[toc] --- ## 前言 區分**裝置驅動模型**和**平臺裝置驅動模型**。 **裝置驅動模型** 可以理解為 **匯流排、裝置、驅動**。 **平臺裝置驅動模型** 就是那些 Linux 核心管理沒有物理匯流排(*即是不需要特殊時序控制的裝置*)(*也是Linux核心沒有自動建立相應驅動匯流排的裝置型別*)的裝置的一套 Linux **平臺匯流排、平臺模型、平臺驅動**的模型。 ## 7. 平臺裝置驅動 為解決驅動程式碼和裝置資訊耦合問題,linux提出了裝置驅動模型。 注意,前面的匯流排、裝置、驅動是一個軟體層面的抽象,與 **SOC** 中物理匯流排概念不一樣。 **物理匯流排**:晶片與各個功能外設之間傳送資訊的公共通訊幹線,包括資料匯流排、地址匯流排和控制匯流排,以此來傳輸各種**通訊時序**。 **驅動匯流排**:負責管理驅動和裝置。制定裝置和驅動的匹配規則。一旦總線上註冊了新裝置/驅動,匯流排便執行匹配程式。 對於常見的 **I2C、SPI、USB**等 **物理匯流排**,**Linux** 核心都會自動建立與之對應的 **驅動匯流排**。所以 I2C裝置、SPI裝置、USB裝置都會掛在在相應的總線上。 相對的,實際專案開發中還有很多結構簡單的裝置是不需要特殊的**時序控制**的。也就沒有相應的物理匯流排,Linux 也不會為它們建立相應的驅動匯流排。如 LED、RTC時鐘、按鍵等等。 但是為了這些簡單的裝置也能遵循裝置驅動模型,Linux 核心引入了一種虛擬匯流排--**平臺匯流排**。 **平臺匯流排** 用於管理、掛在那些沒有物理匯流排的裝置,且,這些裝置被稱為**平臺裝置**,對應的裝置驅動被稱為**平臺驅動**。 平臺裝置使用 **platform_device** 結構體進行表示,繼承了裝置驅動模型中的 **device** 結構體。 平臺驅動使用 **platform_driver** 結構體進行表示,繼承了裝置驅動模型中的 **device_driver** 結構體。 ### 7.1 平臺匯流排 Linux核心只有一條**平臺匯流排**,用於圖一管理簡單裝置--**platform_bus_type**。 #### 7.1.1 平臺匯流排註冊和匹配方式 **最先比較**: * 最先比較 **platform_device.driver_override** 和 **platform_driver.driver.name**。 * 可以設定 **platform_device** 的 **driver_override**,強制選擇某個 **platform_driver**。 **其次比較**: * 其次比較 **platform_device.name** 和 **platform_driver.id_table[i].name**。 * **platform_driver.id_table** 是 **platform_device_id** 指標,表示該 **drv** 支援若干個 **device**,它裡面列出了各個 **device** 的 **{.name, .driver_data}**,其中的 **name** 表示該 **drv** 支援的裝置的名字,**driver_data**是些提供給該 **device** 的私有資料。 **最後比較**: * 最後比較 **platform_device.name** 和 **platform_driver.driver.name**。 * 由於 **platform_driver.id_table** 可能為空,所以,接下來就可以使用 **platform_driver.driver.name** 來匹配。 #### 7.1.2 原始碼分析 **平臺匯流排**: * 核心使用 **bus_type** 來抽象系統中的匯流排。相應地,核心使用 **platform_bus_type** 來描述平臺匯流排。 * 原始碼: ```c struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, }; EXPORT_SYMBOL_GPL(platform_bus_type); ``` * 位於 **核心原始碼/driver/base/platform.c**。 * **該匯流排在Linux核心啟動的時候自動進行註冊**。 * **平臺匯流排**初始化函式原始碼: ```c int __init platform_bus_init(void){ int error; ... error = bus_register(&platform_bus_type); ... return error;} ``` * 位於 **核心原始碼/driver/base/platform.c**。 * **`error = bus_register(&platform_bus_type);`** 就是向Linux核心註冊 **plateform_bus_type** 平臺匯流排。 **建議**:以上匹配規則及原始碼,可以追蹤函式 **platform_match** 原始碼來分析。 ### 7.2 平臺裝置 #### 7.2.1 platform_device **平臺裝置**: * 使用 **platform_device** 描述平臺裝置。 * 原始碼: ```c struct platform_device { const char *name; int id; struct device dev; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; /* 省略部分成員 */ }; ``` * 位於 **核心原始碼/include/linux/platform_device.h** * **name**:裝置名稱,匯流排進行匹配時,會比較裝置和驅動的名稱是否一致; * **id**:指定裝置的編號,Linux支援同名的裝置,而同名裝置之間則是通過該編號進行區分; * **dev**:Linux裝置模型中的device結構體,platform_device 通過繼承該結構體可複用它的相關程式碼,方便核心管理平臺裝置; * **num_resources**:記錄資源的個數,當結構體成員 resource 存放的是陣列時,需要記錄 resource 陣列的個數,核心提供了巨集定義 ARRAY_SIZE 用於計算陣列的個數; * **resource**:平臺裝置提供給驅動的資源,如irq,dma,記憶體等等; * **id_entry**:平臺匯流排提供的另一種匹配方式,原理依然是通過比較字串,這裡的 id_entry 用於儲存匹配的結果; #### 7.2.2 裝置資訊 平臺裝置的工作是為 驅動程式 提供 裝置資訊。包括 硬體資訊 和 軟體資訊。 * **硬體資訊**:驅動程式需要使用到的暫存器、中斷號、記憶體資源、IO口等等; * **軟體資訊**:乙太網裝置中的 MAC 地址、I2C 裝置中的裝置地址等等。 **硬體資訊**: * 硬體資訊使用 **struct resource** 來儲存裝置所提供的資源。 * 原始碼: ```c /** * Resources are tree-like, allowing nesting etc.. */ struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; /* 省略部分成員 */ }; ``` * 位於 **核心原始碼/include/linux/ioport.h**; * **name**:資源名字,可為 NULL; * **start、end**:指定資源的起始地址及結束地址; * 對於 IORESOURCE_IO 和 IORESOURCE_MEM 是有起始和結束地址的,若只有一個引腳或者一個通道,那麼 **start == end**。 * **flags**:用於指定資源的型別,在 Linux 中,資源包括**I/O、Memory、Register、IRQ、DMA、Bus等多種型別**,如: * **IORESOURCE_IO**:IO 地址空間,對應於 IO 埠對映方式; * **IORESOURCE_MEM**:外設可直接定址的地址空間; * **IORESOURCE_IRQ**:指定該裝置使用哪個中斷; * **IORESOURCE_DMA**:指定 DMA 通道。 裝置驅動程式主要目的還是操作裝置的暫存器。 不同架構的計算機提供不同的操作介面,主要有**IO埠對映**和**IO記憶體對映**兩種方式。 **IO埠對映**方式: * 只能通過專門的介面函式才能訪問。 **IO記憶體對映**方式: * 可以像記憶體一樣訪問,去讀寫暫存器。 **軟體資訊**: * 軟體資訊需要以私有資料儲存; * **platform_device** 結構體中繼承有 **device** 結構體,成員為 **dev**,該結構體裡面的 **platform_data** 可以用於儲存裝置的私有資料。 * **platform__data** 是 **void \*** 型別的萬能指標,所以,只需要把私有資料的地址付給 **platform_data** 即可。 #### 7.2.3 註冊/登出平臺裝置 **註冊:platform_device_register**: * 註冊平臺裝置,掛在到平臺總線上。 * 函式原型:**`int platform_device_register(struct platform_device *pdev)`**;位於 **核心原始碼/drivers/base/platform.c**。 * **pded**:**platform_device** 型別結構體指標; * 返回: * 成功:0; * 失敗:負數。 **登出:platform_device_unregister**: * 登出平臺裝置。 * 函式原型:**`void platform_device_unregister(struct platform_device *pdev)`**;位於 ** 核心原始碼/drivers/base/platform.c**。 * **pded**:**platform_device** 型別結構體指標; ### 7.3 平臺驅動 #### 7.3.1 platform_driver **平臺驅動**: * 使用 **platform_driver** 描述平臺驅動。 * 原始碼: ```c struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; }; ``` * 位於 **核心原始碼/include/platform_device.h** * **probe**:匹配成功後執行的回撥函式。 * **remove**:移除某個平臺裝置是的回撥函式。 * **driver**:Linux 裝置模型中用於抽象驅動的 device_driver 結構體,platform_driver 繼承該結構體,也就獲取了裝置模型驅動物件的特性。 * **id_table**:表示該驅動能夠相容的裝置型別。 * **platform_device_id** * **name**:指定驅動名稱。(*用於和 platform_device 中的 name 比較,匹配。*) * **driver_data**:用於儲存裝置的配置。 * 原始碼: ```c struct platform_device_id { char name[PLATFORM_NAME_SIZE]; kernel_ulong_t driver_data; }; ``` #### 7.3.2 註冊/登出平臺驅動 **註冊:platform_driver_register**: * 註冊平臺驅動,掛在到平臺總線上。 * 函式原型:**`int platform_driver_register(struct platform_driver *drv)`**。 * **drv**:**platform_driver** 型別結構體指標; * 返回: * 成功:0; * 失敗:負數。 **登出:platform_driver_unregister**: * 登出平臺啟動驅動。 * 函式原型:**`void platform_driver_unregister(struct platform_driver *drv)`**;位於 ** 核心原始碼/drivers/base/platform.c**。 * **drv**:**platform_driver** 型別結構體指標; #### 7.3.3 平臺驅動獲取裝置資訊 首先要知道的是,平臺裝置的**硬體資訊**儲存在 **resource** 結構體中。而**軟體資訊**則儲存在 **platform_data** 中。 **獲取硬體資訊:platform_get_resource**: * 獲取平臺裝置提供的資源結構體。一般用在 **probe** 函式中。、 * 函式原型:**`struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);`** * **dev**:指定獲取哪個平臺裝置的資源; * **type**:指定獲取資源的型別,如IORESOURCE_MEM、IORESOURCE_IO等; * **num**:指定要獲取的資源編號。每個裝置所需要的資源的個數是不一樣的。且不同資源的編號是不一樣的。 * 返回: * 成功:struct resouce 結構體型別指標; * 失敗:NULL。 * 若要獲取的資源型別為 **IORESOURCE_IRQ**,平臺裝置驅動還提供以下函式介面,來獲取中斷引腳。 * 函式原型:**`int platform_get_irq(struct platform_device *pdev, unsigned int num)`** * **pedv**:指定需要獲取哪個平臺裝置的資源; * **num**:指定要獲取的資源編號。 * 返回: * 成功:可用中斷號; * 失敗:負數。 **獲取軟體資訊:dev_get_platdata**: * **dev**:struct device 結構體型別指標。 ```c static inline void *dev_get_platdata(const struct device *dev) { return dev->platform_data; } ``` ## 參考 * [李柱明部落格](https://www.cnblogs.com/lizhuming/p/14605803.html)