前言
區分裝置驅動模型和平臺裝置驅動模型。
裝置驅動模型 可以理解為 匯流排、裝置、驅動。
平臺裝置驅動模型 就是那些 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;
}