首先,我們來理解一下在platform架構中的知識點。
在Linux 2.6的設備驅動模型中,關心總線、設備和驅動這3個實體,總線將設備和驅動綁定。在系統每注冊一個設備的時候,會尋找與之匹配的驅動;相反的,在系統每注冊一個驅動的時候,會尋找與之匹配的設備,而匹配由總線完成。
一個現實的Linux設備和驅動通常都需要掛接在一種總線上,對於本身依附於PCI、USB、I2 C、SPI等的設備而言,這自然不是問題,但是在嵌入式系統里面,SoC系統中集成的獨立的外設控制器、掛接在SoC內存空間的外設等確不依附於此類總線。基於這一背景,Linux發明了一種虛擬的總線,稱為platform總線,相應的設備稱為platform_device,而驅動成為 platform_driver。
也就是說platform的設備是外設或者其控制器,就像EC外設一樣,被申請為platform結構。
整個platform架構主要涉及到三個結構體
struct platform_driver /*驅動*/
struct platform device /*設備*/
struct resource /*資源*/
下面來看一看各個結構體的組成
一.platform_driver
1 struct platform_driver { 2 3 int (*probe)(struct platform_device *); 4 5 int (*remove)(struct platform_device *); 6 7 void (*shutdown)(struct platform_device *); 8 9 int (*suspend)(struct platform_device *, pm_message_t state); 10 11 int (*resume)(struct platform_device *); 12 13 struct device_driver driver; 14 15 const struct platform_device_id *id_table; 16 17 };
我們可以看見其中包含了probe(),remove(),shutdown(),suspend(),resume()等的函數,這些都需要用驅動來實現。
其中最主要的就是下面這幾項:
- int (*probe)(struct platform_device *);
這是一個匹配函數,通過比較platform_driver和platform_device的name來進行匹配。還有就是會在這個函數中初始化一些和該驅動有關的初始化。
例如,在3A代碼中,wpce775l_probe()函數中實現了與EC相關的backlight,power等的初始化。
因此該函數是一個比較雜的函數,需要在具體的情況下具體分析吧。
- struct device_driver driver;
在這個結構體中,主要實現const char *name;和struct module *owner;這兩個項。
其中name就是我們用來匹配設備和驅動的。必須與device的name一致。
owner則表明該模塊的擁有者,一般都是THIS_MODULE。
- const struct platform_device_id *id_table;
這個雖然不是必實現的,但是這個的實現是另外一種匹配驅動和設備的方法。
這種方式中,匹配通過id_table來實現。這種實現的最終還是通過名字對應來匹配,但是匹配的名字被列在一個表中,platform_device的name和這個表中的每一個值進行比較,知道找到相同的那一個。
主要是為了實現一個platform_driver對應多個platform_device的情況。設備之間的區別是通過platform_device_id的.driver_data成員區分的,從而在連接設備與驅動時可以針對設備區別對加載的驅動程序進行調整。
二.platform_device
1 struct platform_device { 2 3 const char * name; 4 5 int id; 6 7 struct device dev; 8 9 u32 num_resources; 10 11 struct resource * resource; 12 13 const struct platform_device_id *id_entry; 14 15 struct pdev_archdata archdata; 16 17 };
我們看見其中主要的是一些device的屬性。
我們需要注意的是以下幾項:
- const char *name;
這個應該不需要過多的去說明了,就是我們匹配用的name。匹配中的關鍵,不管在driver中用什么哪種方式去匹配,歸根結底還是用了name的方法。
- int id
設備id,用於給插入給該總線並且具有相同name的設備編號,如果只有一個設備的話填-1。
一般我們會見到XXX.0就是因為在這里的id=0的緣故。當id=-1是,顯示出來的就是XXX。
- struct resource * resource;
資源數
- struct resource * resource;
用於存放資源的數組
三.resource
1 struct resource { 2 3 resource_size_t start; 4 5 resource_size_t end; 6 7 const char *name; 8 9 unsigned long flags; 10 11 struct resource *parent, *sibling, *child; 12 13 };
在該結構體中主要的就是資源的分配。具體就是資源的起始地址,結束地址,name以及flag標志。同樣,這里的name要和platform_device和platform_driver中的name要一致。
這里復雜一點就是flag。flags可以為IORESOURCE_IO, IORESOURCE_MEM, IORESOURCE_IRQ, IORESOURCE_DMA等。start、end的含義會隨着flags而變更。
如當flags為IORESOURCE_MEM時,start、end分別表示該platform_device占據的內存的開始地址和結束地址;當flags為IORESOURCE_IRQ時,start、end分別表示該platform_device使用的中斷號的開始值和結束值,如果只使用了1個中斷號,開始和結束值相同。 對於同種類型的資源而言,可以有多份,譬如說某設備占據了2個內存區域,則可以定義2個IORESOURCE_MEM資源。
這樣,我們就將platform框架中有關的結構體簡單的介紹了一下.