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探测函数,获取设备信息。