http://blog.csdn.net/zqixiao_09/article/details/50888795
前面我們已經學習了platform裝置的理論知識Linux 裝置驅動開發 —— platform 裝置驅動 ,下面將通過一個執行個體來深入我們的學習。
一、platform 驅動的工作過程
platform模型驅動編程,需要實現platform_device(裝置)與platform_driver(驅動)在platform(虛擬匯流排)上的註冊、匹配,相互綁定,然後再做為一個普通的字元裝置進行相應的應用,總之如果編寫的是基於字元裝置的platform驅動,在遵循並實現platform匯流排上驅動與裝置的特定介面的情況下,最核心的還是字元裝置的核心結構:cdev、 file_operations(他包含的操作函數介面)、dev_t(裝置號)、裝置檔案(/dev)等,因為用platform機制編寫的字元驅動,它的本質是字元驅動。
我們要記住,platform 驅動只是在字元裝置驅動外套一層platform_driver 的外殼。
在一般情況下,2.6核心中已經初始化並掛載了一條platform匯流排在sysfs檔案系統中。那麼我們編寫platform模型驅動時,需要完成兩個工作:
a -- 實現platform驅動
b -- 實現platform裝置
然而在實現這兩個工作的過程中還需要實現其他的很多小工作,在後面介紹。platform模型驅動的實現過程核心架構就很簡單,如下所示:
platform驅動模型三個對象:platform匯流排、platform裝置、platform驅動。
platform匯流排對應的核心結構:struct bus_type-->它包含的最關鍵的函數:match() (要注意的是,這塊由核心完成,我們不參與)
platform裝置對應的核心結構:struct platform_device-->註冊:platform_device_register(unregister)
platform驅動對應的核心結構:struct platform_driver-->註冊:platform_driver_register(unregister)
那具體platform驅動的工作過程是什麼呢:
裝置(或驅動)註冊的時候,都會引發匯流排調用自己的match函數來尋找目前platform匯流排是否掛載有與該裝置(或驅動)名字匹配的驅動(或裝置),如果存在則將雙方綁定;
如果先註冊裝置,驅動還沒有註冊,那麼裝置在被註冊到匯流排上時,將不會匹配到與自己同名的驅動,然後在驅動註冊到匯流排上時,因為裝置登入,那麼匯流排會立即匹配與綁定這時的同名的裝置與驅動,再調用驅動中的probe函數等;
如果是驅動先註冊,同裝置驅動一樣先會匹配失敗,匹配失敗將導致它的probe函數暫不調用,而是要等到裝置註冊成功並與自己匹配綁定後才會調用。
二、實現platform 驅動與裝置的詳細過程
1、思考問題。
在分析platform 之前,可以先思考一下下面的問題:
a -- 為什麼要用 platform 驅動。不用platform驅動可以嗎。
b -- 裝置驅動中引入platform 概念有什麼好處。
現在先不回答,看完下面的分析就明白了,後面會附上總結。
2、platform_device 結構體 VS platform_driver 結構體
這兩個結構體分別描述了裝置和驅動,二者有什麼關係呢。先看一下具體結構體對比
| 裝置(硬體部分):中斷號,寄存器,DMA等 platform_device 結構體 |
驅動(軟體部分) platform_driver 結構體 |
| struct platform_device { const char *name; 名字 int id; bool id_auto; struct device dev; 硬體模組必須包含該結構體 u32 num_resources; 資源個數 struct resource *resource; 資源 人脈 const struct platform_device_id * id_entry; /* arch specific additions */ struct pdev_archdata archdata; }; |
struct platform_driver { int (* probe )(struct platform_device *); 硬體和軟體匹配成功之後調用該函數 int (* 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; 八字 }; |
| 裝置執行個體: static struct platform_device hello_device= { .name = "bigbang", .id = -1, .dev.release = hello_release, }; |
驅動執行個體: static struct platform_driver hello_driver= { .driver.name = "bigbang", .probe = hello_probe, .remove = hello_remove, }; |
前面提到,實現platform模型的過程就是匯流排對裝置和驅動的匹配過程 。打個比方,就好比相親,匯流排是紅娘,裝置是男方,驅動是女方:
a -- 紅娘(匯流排)負責男方(裝置)和女方(驅動)的撮合;
b -- 男方(女方)找到紅娘,說我來登記一下,看有沒有合適的姑娘(漢子)—— 裝置或驅動的註冊;
c -- 紅娘這時候就需要看看有沒有八字(二者的name 欄位)匹配的姑娘(漢子)——match 函數進行匹配,看name是否相同;
d -- 如果八字不合,就告訴男方(女方)沒有合適的對象,先等著,別急著亂做事 —— 裝置和驅動會等待,直到匹配成功;
e -- 終於遇到八字匹配的了,那就結婚唄。接完婚,男方就向女方交代,我有多少存款,我的房子在哪,錢放在哪等等( struct resource *resource),女方說好啊,於是去房子裡拿錢,去給男方買菜啦,給自己買衣服、化妝品、首飾啊等等(int (*probe)(struct platform_device *) 匹配成功後驅動執行的第一個函數),當然如果男的跟小三跑了(裝置卸載),女方也不會繼續待下去的( int (*remove)(struct platform_device *))。
3、裝置資源結構體
在struct platform_device 結構體中有一重要成員 struct resource *resource [cpp] view plain copy struct resource { resource_size_t start; 資源起始地址 resource_size_t end; 資源結束位址 const char *name; unsigned long flags; 區分是資源什麼類型的 struct resource *parent, *sibling, *child; }; #define IORESOURCE_MEM 0x00000200 #define IORESOURCE_IRQ 0x00000400
flags 指資源類型,我們常用的是 IORESOURCE_MEM、IORESOURCE_IRQ 這兩種。start 和 end 的含義會隨著 flags而變更,如
a -- flags為IORESOURCE_MEM 時,start 、end 分別表示該platform_device佔據的記憶體的開始地址和結束值;
b -- flags為 IORESOURCE_IRQ 時,start 、end 分別表示該platform_device使用的中斷號的開始地址和結束值;
下面看一個執行個體: [cpp] view plain copy static struct resource beep_resource[] = { [0] = { &nbs