Linux 设备总线驱动模型

2019-07-13 07:31发布

尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要。
    Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述。换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。         在内核里,有各种各样的总线,如 usb_bus_type、spi_bus_type、pci_bus_type、platform_bus_type、i2c_bus_type 等,内核通过总线将设备与驱动分离。此文,基于 Linux2.6.32.2 简单分析设备驱动模型,以后看具体的总线设备模型时会更加清晰。     设备模型是层次的结构,层次的每一个节点都是通过kobject实现的。在文件上则体现在sysfs文件系统。     关于kobkect,前面的文章已经分析过了,如果不清楚请移步 http://blog.csdn.NET/lizuobin2/article/details/51523693
    kobject 结构可能的层次结构如图:
    关于 uevet mdev 前面也说过了,请参考 http://blog.csdn.Net/lizuobin2/article/details/51534385     对于整个 设备总线驱动模型 的样子,大概如下图吧,也并不复杂。简单来说,bus 负责维护 注册进来的devcie 与 driver ,每注册进来一个device 或者 driver 都会调用 Bus->match 函数 将device 与 driver 进行配对,并将它们加入链表,如果配对成功,调用Bus->probe或者driver->probe函数, 调用 kobject_uevent 函数设置环境变量,mdev进行创建设备节点等操作。后面,我们从 Bus driver 到 device三个部分进行详细的分析。
一、总线
    内核通过 bus_register 进行 Bus  注册,那么它注册到了哪里,“根”在哪? 答案是 bus_kest  [cpp] view plain copy  print?在CODE上查看代码片派生到我的代码片
  1. int __init buses_init(void)  
  2. {  
  3.     // /sys/bus 目录 这里创建的  
  4.     bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);  
  5.     if (!bus_kset)  
  6.         return -ENOMEM;  
  7.     return 0;  
  8. }  
    bus 的类型为 bus_type [cpp] view plain copy  print?在CODE上查看代码片派生到我的代码片
  1. struct bus_type {  
  2.     const char      *name;  
  3.     struct bus_attribute    *bus_attrs;  
  4.     struct device_attribute *dev_attrs;  
  5.     struct driver_attribute *drv_attrs;  
  6.   
  7.     int (*match)(struct device *dev, struct device_driver *drv);  
  8.     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
  9.     int (*probe)(struct device *dev);  
  10.     int (*remove)(struct device *dev);  
  11.     void (*shutdown)(struct device *dev);  
  12.   
  13.     int (*suspend)(struct device *dev, pm_message_t state);  
  14.     int (*resume)(struct device *dev);  
  15.   
  16.     const struct dev_pm_ops *pm;  
  17.   
  18.     struct bus_type_private *p;  
  19. };  
[cpp] view plain copy  print?在CODE上查看代码片派生到我的代码片
  1. struct bus_type_private {  
  2.     struct kset subsys;  
  3.     struct kset *drivers_kset;  
  4.     struct kset *devices_kset;  
  5.     struct klist klist_devices;  
  6.     struct klist klist_drivers;  
  7.     struct blocking_notifier_head bus_notifier;  
  8.     unsigned int drivers_autoprobe:1;  
  9.     struct bus_type *bus;  
  10. };  
[cpp] view plain copy  print?在CODE上查看代码片派生到我的代码片
  1. int bus_register(struct bus_type *bus)  
  2. {  
  3.     int retval;  
  4.     struct bus_type_private *priv;  
  5.   
  6.     priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);  
  7. /* 1. bus 与 prv 相互建立联系 */  
  8.     // 私有数据 .bus ->  bus 本身  
  9.     priv->bus = bus;  
  10.     // bus->p 指向 priv  
  11.     bus->p = priv;  
  12.     // 内核通知链  
  13.     BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);  
  14.   
  15. /* 设置 bus->prv->subsys->kobj */  
  16.     // 设置 priv->subsys.kobj.name = bus->name  对应于/sys/ 目录下的目录名  
  17.     retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  
  18.     // 所有的 priv->subsys.kobj.kset 指向 bus_kse 对应于图中④与六的关系  
  19.     priv->subsys.kobj.kset = bus_kset;  
  20.     // 所有的priv->subsys.kobj.ktype 等于 bus_ktype  
  21.     priv->subsys.kobj.ktype = &bus_ktype;  
  22.       
  23.     priv->drivers_autoprobe = 1;  
  24.       
  25. /* 注册 kset (bus->prv->subsys priv->devices_kset priv->drivers_kset) */    
  26.     // 注册 priv->subsys ,由于 priv->subsys.kobj.kset = bus_kset,所以会在 /sys/bus/目录下创建 目录 如/sys/bus/plateform  
  27.     retval = kset_register(&priv->subsys);  
  28.     // sysfs_create_file(&bus->p->subsys.kobj, &bus_attr_uevent->attr);  
  29.     retval = bus_create_file(bus, &bus_attr_uevent);  
  30.   
  31.     // 由于 priv->subsys.kobj.kset = bus_kset ,因此会创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/devices  
  32.     priv->devices_kset = kset_create_and_add("devices", NULL,  
  33.                          &priv->subsys.kobj);  
  34.     // 同理 创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/drivers  
  35.     priv->drivers_kset = kset_create_and_add("drivers", NULL,  
  36.                          &priv->subsys.kobj);  
  37.     // 初始化 klist_devices 并设置get put 函数  初始化 klist_drivers 不知为何没有get put ?  
  38.     klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  
  39.     klist_init(&priv->klist_drivers, NULL, NULL);  
  40.   
  41.     retval = add_probe_files(bus);  // static inline int add_probe_files(struct bus_type *bus) { return 0; }  
  42.     // 添加 bus->attrs 属性文件  
  43.     retval = bus_add_attrs(bus);  
  44.   
  45.     return 0;  
  46.   
  47. }  

目前,能通过 bus_register 函数处理的工作有:     1、将 Bus 与 priv 相互建立联系
    2、BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
    3、设置 bus->priv->subsys(kset).kobj 的名字为 bus->name
    4、设置 bus->priv->subsys(kset).kobj.kset 指向 bus_kset
    5、设置 bus->priv->subsys(kset).kobj.ktype 为 bus_ktype ,提供 show store 函数
    6、设置 bus->priv->drivers_autoprobe = 1;
    7、注册 bus->priv->subsys(kset)   对应于图中④与⑥的关系
    由于4,且没有指定bus->priv->subsys(kset).kobj.Parent,会将 bus_kest.kobj 设置为 bus->priv->subsys(kset).kobj.Parent 因此,会将bus->priv->subsys(kset).kobj.entry 加入 bus_kest 链表,且会在/sys/bus目录下创建相应的总线目录/sys/bus/$(bus->name),例如 /sys/bus/platform 
    8、创建 bus_attr_uevent->attr 属性文件
    9、创建并注册 devices_kset ,devices_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 device ,因此会创建 /sys/bus/$(bus->name)/devices
    10、创建并注册 drivers_kset ,drivers_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 drivers ,因此会创建 /sys/bus/$(bus->name)/drivers
    11、初始化 bus->priv->klist_devices 链表
    12、初始化 bus->priv->klist_drivers 链表
    13、创建 bus->bus_attrs 属性文件

下面来看个例子 ,修改自LDD3 。基于Linux 2.6.32.2 内核
[cpp] view plain copy  print?在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * Definitions for the virtual LDD bus. 
  3.  * 
  4.  * lddbus.h 
  5.  */  
  6.   
  7. extern struct device ldd_bus;  
  8. extern struct bus_type ldd_bus_type;