Linux驱动之设备模型(4)-总线

来源:互联网 发布:防木马软件 编辑:程序博客网 时间:2024/06/05 09:01

1 总线

   总线,是处理器与一个或者多个设备之间的通道。在Linux设备模型中,所有的设备都通过总线相连,甚至是那些内部的虚拟"platform"总线。用bus_type结构来描述。

[cpp] view plaincopy
  1.    struct bus_type {  
  2.     const char      *name;         /* 总线名 */  
  3.     const char      *dev_name;      
  4.     struct device       *dev_root;  
  5.     struct bus_attribute    *bus_attrs;  /* 总线属性 */  
  6.     struct device_attribute *dev_attrs;   /* 设备属性 */  
  7.     struct driver_attribute *drv_attrs;    /* 驱动属性 */  
  8.   
  9.    /* 当向该总线注册设备或总线调用,用于设备与驱动的匹配 */  
  10.     int (*match)(struct device *dev, struct device_driver *drv);  
  11.    /* 当添加或移除设备时,调用此函数向用户空间产生环境变量 */  
  12.     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
  13.    /* 当设备与驱动匹配上时调用,初始化匹配设备 */  
  14.     int (*probe)(struct device *dev);  
  15.    /* 当设备移除时调用 */  
  16.     int (*remove)(struct device *dev);  
  17.     void (*shutdown)(struct device *dev);  
  18.   
  19.    /* 总线的电源管理操作 */  
  20.     int (*suspend)(struct device *dev, pm_message_t state);  
  21.     int (*resume)(struct device *dev);  
  22.   
  23.    /* 设备、驱动的电源管理操作集 */  
  24.     const struct dev_pm_ops *pm;  
  25.   
  26.   
  27.     struct iommu_ops *iommu_ops;  
  28.   
  29.    /* 私有数据指针,这个比较重要的一个成员 */  
  30.     struct subsys_private *p;  
  31. };   

2. 总线声明

     内核中的每一种总线类型(PCI,I2C,USB,etc)都用bus_type结构体来描述,需要初始化相应的name成员和match函数。

[cpp] view plaincopy
  1. struct bus_type pci_bus_type = {  
  2.  .name    = "pci",  
  3.  .match   = pci_bus_match,  
  4. };  

3. 注册和注销函数

     int bus_register(struct bus_type *bus);

     void bus_unregister(struct bus_type *bus);

    总线 注册函数分析

[cpp] view plaincopy
  1. int bus_register(struct bus_type *bus)  
  2. {  
  3.          retval =kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  
  4.    
  5.          priv->subsys.kobj.kset = bus_kset;  
  6.          priv->subsys.kobj.ktype =&bus_ktype;  
  7.          priv->drivers_autoprobe = 1;  
  8.    
  9.          /* 注册bus kset,在bus/下产生bus->name目录 */  
  10.          retval = kset_register(&priv->subsys);  
  11.    
  12.          retval = bus_create_file(bus,&bus_attr_uevent);  
  13.    
  14.          /* 产生device目录 */  
  15.          priv->devices_kset =kset_create_and_add("devices", NULL,  
  16.                                                          &priv->subsys.kobj);  
  17.    
  18.          /* 产生driver目录 */  
  19.          priv->drivers_kset =kset_create_and_add("drivers", NULL,  
  20.                                                          &priv->subsys.kobj);  
  21.    
  22.          /* 初始化设备链和驱动链 */  
  23.          klist_init(&priv->klist_devices,klist_devices_get, klist_devices_put);  
  24.          klist_init(&priv->klist_drivers,NULL, NULL);  
  25.    
  26.          retval = add_probe_files(bus);  
  27.    
  28.          retval = bus_add_attrs(bus);  
  29. }  

Klist_drivers和klist_devices这两条链非常重要,用来遍历drivers_kset和devices_kset,drivers_kset和devices_kset分别是总线上所有driver和device的集合。


4. match() 函数

    match函数为总线提供了检测驱动是否支持此设备的功能,通过device IDs来进行匹配。当驱动被注册到总线上,总线上的设备链表会被遍历,match()被调用来完成对每个设备的匹配。


5. subsys_private

    subsys_private是一个比较重要的成员。

[cpp] view plaincopy
  1.    struct subsys_private {  
  2. struct kset subsys;  
  3. struct kset *devices_kset;      /* 设备集,包含总线上所有的设备 */  
  4. struct list_head interfaces;  
  5. struct mutex mutex;  
  6.   
  7.   
  8. struct kset *drivers_kset;  /* 驱动集,包含总线所有的驱动 */  
  9. struct klist klist_devices;  /* 设备链表,连接总线上所有的设备 */  
  10. struct klist klist_drivers;    /* 驱动链表,连接总线上所有的驱动 */  
  11. struct blocking_notifier_head bus_notifier;  
  12. unsigned int drivers_autoprobe:1;  
  13. struct bus_type *bus;       /* 指回bus_type */  
  14.   
  15.   
  16. struct kset glue_dirs;  
  17. struct class *class;  
  18. ;  

设备链表和驱动链表连接了注册到总线上的所有设备和驱动,经常用来遍历和查询总线上的设备和驱动。设备模型核心层提供了两个API,用来遍历设备和驱动。

[cpp] view plaincopy
  1. int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data,  
  2.                      int (*fn)(struct device *, void *));  
  3.   
  4. int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,  
  5.                      void * data, int (*fn)(struct device_driver *, void *));  


6. 导出属性,总线的属性用bus_attribute来描述

[cpp] view plaincopy
  1. struct bus_attribute {  
  2.         struct attribute        attr;      /* 属性 */  
  3.         ssize_t (*show)(struct bus_type *, char * buf);    /* 读属性方法 */  
  4.         ssize_t (*store)(struct bus_type *, const char * buf, size_t count);  /* 写属性方法 */  
  5. };  

BUS_ATTR宏可以在编译时刻创建和初始化bus_attribute结构体

[cpp] view plaincopy
  1. BUS_ATTR(_name, _mode, _show, _store)  

在LDM里提供了两个接口用来在sysfs目录下产生和移除属性文件
[cpp] view plaincopy
  1. int bus_create_file(struct bus_type *, struct bus_attribute *);  
  2. void bus_remove_file(struct bus_type *, struct bus_attribute *);  

7. 实例分析

创建一条scbus总线,并添加版本属性。实际中我们并不需要自己创建总线,此试验仅用来学习

[cpp] view plaincopy
  1. /* 
  2. * for learn bus 
  3. */  
  4. #include <linux/init.h>  
  5. #include <linux/module.h>  
  6. #include <linux/kernel.h>  
  7. #include <linux/device.h>  
  8.    
  9. static char *Version = "revision 1.0,scbus";  
  10.    
  11. /* 匹配函数,通过设备名和驱动名来匹配 */  
  12. static int scbus_match(struct device *dev,struct device_driver *driver)  
  13. {  
  14.          printk("\n%s,%s\n", dev_name(dev), driver->name);  
  15.          return!strncmp(dev_name(dev), driver->name, strlen(driver->name));  
  16. }  
  17.    
  18. static void scbus_release(struct device*dev)  
  19. {  
  20.          printk("scbusrelease\n");  
  21. }  
  22.    
  23. struct bus_type scbus_type = {  
  24.          .name       = "scbus",  
  25.          .match   = scbus_match,  
  26. };  
  27. EXPORT_SYMBOL_GPL(scbus_type);  
  28.    
  29. struct device scbus = {  
  30.          .init_name        = "scbus0",  
  31.          .release   = scbus_release,  
  32. };  
  33. EXPORT_SYMBOL_GPL(scbus);  
  34.    
  35. /* 
  36. * export bus attribute 
  37. */  
  38. static ssize_t show_bus_version(structbus_type *bus, char *buf)  
  39. {  
  40.          returnsnprintf(buf, PAGE_SIZE, "%s\n", Version);  
  41. }  
  42. static BUS_ATTR(version, S_IRUGO,show_bus_version, NULL);  
  43.    
  44. static int __init scbus_init(void)  
  45. {  
  46.          intret;  
  47.    
  48.          ret= bus_register(&scbus_type);  
  49.          if(ret)  
  50.                    returnret;  
  51.    
  52.          ret= bus_create_file(&scbus_type, &bus_attr_version);  
  53.          if(ret)  
  54.                    gotocreate_error;  
  55.    
  56.          ret= device_register(&scbus);  
  57.          if(ret)  
  58.                    gotodevice_error;  
  59.    
  60.          printk("Createa scbus\n");  
  61.          return0;  
  62.    
  63. device_error:  
  64.          bus_remove_file(&scbus_type,&bus_attr_version);  
  65. create_error:  
  66.          bus_unregister(&scbus_type);  
  67.          returnret;  
  68. }  
  69.    
  70. static void __exit scbus_exit(void)  
  71. {  
  72.          device_unregister(&scbus);  
  73.          bus_remove_file(&scbus_type,&bus_attr_version);  
  74.          bus_unregister(&scbus_type);  
  75.          printk("Removea scbus\n");  
  76. }  
  77.    
  78. module_init(scbus_init);  
  79. module_exit(scbus_exit);  
  80.    
  81. MODULE_LICENSE("Dual BSD/GPL");  
  82. MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");  

试验结果:

0 0
原创粉丝点击