【linux】驅動-7-平台設備驅動
- 2021 年 4 月 1 日
- 筆記
- /label/driver, /label/driver/07, /label/linux, /label/linux/driver, /label/lzm, C語言, linux, 嵌入式, 教程集合, 程序框架
前言
區分設備驅動模型和平台設備驅動模型。
設備驅動模型 可以理解為 總線、設備、驅動。
平台設備驅動模型 就是那些 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 來描述平台總線。
- 源碼:
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內核啟動的時候自動進行註冊。
-
平台總線初始化函數源碼:
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 描述平台設備。
- 源碼:
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 來保存設備所提供的資源。
- 源碼:
/**
* 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 描述平台驅動。
- 源碼:
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:用於保存設備的配置。
- 源碼:
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 結構體類型指針。
static inline void *dev_get_platdata(const struct device *dev)
{
return dev->platform_data;
}