1.一個重要思想
分離思想:將設備和驅動進行分離,各自管理。直到一端加入到總線,進行匹配。
2、什麼是platform總線?
相對於USB、PCI、I2C、SPI等物理總線來說,platform總線是一種虛擬、抽象出來的總線,實際中並不存在這樣的總線。那爲什麼需要platform總線呢?其實是Linux設備驅動模型爲了保持設備驅動的統一性而虛擬出來的總線。因爲對於usb設備、i2c設備、pci設備、spi設備等等,他們與cpu的通信都是直接掛在相應的總線下面與我們的cpu進行數據交互的,但是在我們的嵌入式系統當中,並不是所有的設備都能夠歸屬於這些常見的總線,在嵌入式系統裏面,SoC系統中集成的獨立的外設控制器、掛接在SoC內存空間的外設卻不依附與此類總線。所以Linux驅動模型爲了保持完整性,將這些設備掛在一條虛擬的總線上(platform總線),而不至於使得有些設備掛在總線上,另一些設備沒有掛在總線上。
platform總線相關代碼聲明:linux/platform_device.h 文件
3、platform總線的兩個重點結構體platform_device和platform_driver
對於任何一種Linux設備驅動模型下的總線都由兩個部分組成:描述設備相關的結構體和描述驅動相關的結構體在platform總線下就是platform_device和platform_driver,下面是對兩個結構體的各個元素進行分析:
(1)platform_device結構體及其中的結構體分析
struct platform_device {
const char * name; //平臺設備的名字
int id; //ID區分設備名字,如果一個驅動對應一個設備,傳-1
struct device dev; //描述設備信息device結構體
u32 num_resources; //資源結構體數量,資源信息結構體數組的大小
struct resource * resource; //資源結構體,一般定義一個數組
};
struct device {
struct device_driver *driver; //設備驅動的結構體指針
struct device_node *of_node; //設備樹節點
u32 id;
void (*release)(struct device *dev); //設備端卸載的時候,須調用的函數
};
struct resource {
resource_size_t start; //起始地址
resource_size_t end; //結束地址
const char *name; //資源名字
unsigned long flags; //標誌 IORESOURCE_IO IORESOURCE_MEM
//IORESOURCE_IRQ
};
設備端調用的代碼
int platform_device_register(struct platform_device *pdev);
功能:platfrom平臺總線設備註冊
參數:
@pdev platform平臺總線設備端結構體指針
返回值:成功返回0,失敗返回負數錯誤碼
void platform_device_unregister(struct platform_device *);
功能:platfrom平臺總線設備卸載
參數:
@pdev platform平臺總線設備端結構體指針
返回值:成功返回0,失敗返回負數錯誤碼
(2)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; //id_table表
};
struct device_driver {
const char *name; //名字 匹配
struct bus_type *bus; //總線信息結構體
struct module *owner; //THIS_MODULE
const struct of_device_id *of_match_table; //設備樹的相關信息
};
struct platform_device_id {
char name[PLATFORM_NAME_SIZE]; //名字
kernel_ulong_t driver_dat;
};
驅動端調用的代碼
int platform_driver_register(struct platform_driver *pdrv);
功能:platform平臺總線驅動端註冊函數
參數:
@pdrv platform平臺總線驅動端結構體指針
返回值:成功返回0,失敗返回負數錯誤碼
void platform_driver_unregister(struct platform_driver *);
功能:platform平臺總線驅動端卸載函數
參數:
@pdrv platform平臺總線驅動端結構體指針
返回值:成功返回0,失敗返回負數錯誤碼
重點:
一.platform驅動註冊過程:
platform_driver_register
--->>>driver_register(&drv->driver);
--->>>bus_add_driver(drv);
--->>>driver_attach(drv); //驅動綁定設備
/*遍歷設備端的鏈表,完成匹配*/
--->>>bus_for_each_dev(drv->bus, NULL, drv,__driver_attach);
--->>>__driver_attach
--->>>driver_match_device(drv, dev)
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/*和設備樹進行匹配*/
if (of_driver_match_device(dev, drv))
return 1;
/* 和id_table表進行匹配 */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* 和名字進行匹配*/
return (strcmp(pdev->name, drv->name) == 0);
}
二.platform設備端註冊:
platform_device_register
--->>>platform_device_add(pdev);
--->>>device_add(&pdev->dev);
--->>>bus_probe_device(dev);
--->>>device_attach(dev);
--->>>bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
以上調用的最後:
**driver_probe_device**
總結:platform平臺總線,設備和驅動在註冊的過程中,都會遍歷對方的鏈表,來查找是否有匹配的,如果有匹配的,就會調用驅動中的probe探測函數,獲取設備信息。