传统写法:上下分层
典型的如字符设备驱动。
- 预先分配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总线设备驱动框架只是一个简单的认识,后续还需要深入学习,如有错误,敬请谅解。