linux总线设备驱动程序框架
注释:文章部分内容来源于韦东山B站视频:https://www.bilibili.com/video/av65976587?p=56
传统写法:上下分层
典型的如字符设备驱动。
- 预先分配GPIO
- 注册file_operations
- 使用ioremap映射寄存器,操作寄存器
这种写发的缺点:
- 硬件绑定很死
- 不适合扩展
总线模型:左右分离
把固定的硬件资源放到平台结构中
把固定的驱动程序放到paltform_driver结构中
设备与驱动程序通过bus联系起来(这里的总线是一个虚拟的概念)
struct platform_device led_device; <====> struct platform_driver led_drv;
struct platform_device btn_device; <====> struct platform_driver btn_drv;
struct platform_device lcd_device; <====> struct platform_driver lcd_drv;
这种写法的优缺点:
- 方便扩展
- 对于每一个单板都需要提供一个平台,重复的代码太多,从而有了后续的设备树驱动模型(此处暂不研究)
设备与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;
char *driver_override; //Driver name to force a match
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* 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; //{.name = "xxx"}
const struct platform_device_id *id_table; //能支持的设备,一个driver可能支持多个设备
};
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, // 判断(dev,drv)是否匹配,若匹配,则调用drv->probe
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
内核里面有一个虚拟总线(平台总线类型),其上挂着两个链表,左边是设备链表,右边是驱动链表。当我们去注册平台设备时,这个平台设备就会加入左边的链表;当我们去注册一个平台driver时这个平台driver就会加入右边的链表。
这些结构体加入链表后,就会马上和对方的链表的成员一一比较,如果有匹配上的就调用平台驱动的probe函数,来处理dev。
- 如果platform_device.driver_override存在,则比较platform_device.driver_override和platform_driver.driver.name
- 如果platform_driver id_table 不为空,比较platform_device.name和platform_driver.driver.name
- 最后比较platform_device.name 和 platform_driver.driver.name
函数调用关系
platform_device_register
platform_device_add
device_add
bus_add_device // 放入链表
bus_probe_device // probe枚举设备,即找到匹配的(dev, drv)
device_initial_probe
__device_attach
bus_for_each_drv(...,__device_attach_driver,...)
__device_attach_driver
driver_match_device(drv, dev) // 是否匹配
driver_probe_device // 调用drv的probe
platform_driver_register
__platform_driver_register
driver_register
bus_add_driver // 放入链表
driver_attach(drv)
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
__driver_attach
driver_match_device(drv, dev) // 是否匹配
driver_probe_device // 调用drv的probe
总结
对于Linux总线设备驱动框架只是一个简单的认识,后续还需要深入学习,如有错误,敬请谅解。