struct bus_type 结构体

来源:互联网 发布:太阳帆轨迹优化 编辑:程序博客网 时间:2024/06/09 19:45

这个结构体的定义在include/Linux/device.h中。本节先简单介绍结构体中的成员,再对每个成员作详细描述。

[cpp] view plain copy
  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.     int (*match)(struct device *dev, struct device_driver *drv);  
  7.     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
  8.     int (*probe)(struct device *dev);  
  9.     int (*remove)(struct device *dev);  
  10.     void (*shutdown)(struct device *dev);  
  11.     int (*suspend)(struct device *dev, pm_message_t state);  
  12.     int (*resume)(struct device *dev);  
  13.     const struct dev_pm_ops *pm;  
  14.     struct bus_type_private *p;  
  15. };  

 

1. 成员简单介绍

[cpp] view plain copy
  1. const char *name;  
  2.   总线名称。  
  3. struct bus_attribute *bus_attrs;  
  4.   总线属性。  
  5. struct device_attribute    *dev_attrs;  
  6.   该总线上所有设备的默认属性。  
  7. struct driver_attribute    *drv_attrs;  
  8.   该总线上所有驱动的默认属性。  
  9. int (*match)(struct device *dev, struct device_driver *drv);  
  10.   驱动匹配。  
  11. int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
  12.   添加环境变量。  
  13. int (*probe)(struct device *dev);  
  14.   驱动匹配。  
  15. int (*remove)(struct device *dev);  
  16.   设备移除时调用。  
  17. void (*shutdown)(struct device *dev);  
  18.   关机时调用。  
  19. int (*suspend)(struct device *dev, pm_message_t state);  
  20.   挂起(投入休眠)时调用。  
  21. int (*resume)(struct device *dev);  
  22.   恢复时调用。  
  23. const struct dev_pm_ops *pm;  
  24.   设备电源管理。  
  25. struct bus_type_private *p;  
  26.   私有数据。完全由驱动核心初始化并使用。  

 

2. 成员详细描述

 

2.0 预备知识

在开始之前,我们先简单介绍一个结构体struct attribute。这个结构体作为属性的基本结构,嵌入在struct bus_attribute等结构体中。如下:

[cpp] view plain copy
  1. /* FIXME 
  2.  * The *owner field is no longer used. 
  3.  * x86 tree has been cleaned up. The owner 
  4.  * attribute is still left for other arches. 
  5.  */  
  6. struct attribute {  
  7.     const char      *name;  
  8.     struct module       *owner;  
  9.     mode_t          mode;  
  10. #ifdef CONFIG_DEBUG_LOCK_ALLOC  
  11.     struct lock_class_key   *key;  
  12.     struct lock_class_key   skey;  
  13. #endif  
  14. };  
  15. // 注:在2.6.37中,owner已经被移除了。  

我们只关心name和mode成员。name作为属性文件名称,会出现在sysfs伪系统系统的特定目录下(具体目录和struct attribute所在的驱动相关),而mode是name文件的权限。
作为文件名称,name不能含有”/”,同时最好不含有空格,因为 shell分隔符IFS一般包含空格,空格对shell应用处理等会带来不便和错误。name值必须具有持久属性,例如静态字符数组或者字符串字面值(不能是栈内字符数组)。
mode和用户空间中文件的属性一致,可以针对所有者、同组、其他用户分别设置读、写、可执行权限等,需要注意的是,即使设置可执行权限,属性文件也是不允许执行的,最起码在当前的内核版本下是不允许执行的。

 

2.1 const char *name;
总线名称,体现在sysfs文件系统的/sys/bus下,不能和别的总线名称有冲突。作为文件名称,它不能含有”/”;同时最好不含有空格,值必须有持久性。例如:
struct bus_type ycbus_type = { .name = “ycbus” };

 

2.2 struct bus_attribute *bus_attrs;
总线属性。表现为/sys/bus/<name>/文件夹下的文件。这个结构体的具体定义如下:

[cpp] view plain copy
  1. struct bus_attribute {  
  2.     struct attribute    attr;  
  3.     ssize_t (*show)(struct bus_type *bus, char *buf);  
  4.     ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);  
  5. };  


其中struct attribute attr的如2.0所述。show和store方法的第一个参数都是struct bus_type指针,用来指示属性所在总线。
show方法实现数据读取。当用户空间读取属性值时,核心调用该方法实现编码,结果存放在形参buf中,注意大小不能超过过PAGE_SIZE。
store方法实现数据保存。当用户控件设置属性值时,核心调用该方法实现解码,使用buf传递的数据解码,count指示buf传递的数据长度。注意buf信息来自用户空间,因此在解码前应当检测数据合法性,如果数据格式或者数值和期望的不符,应该返回一个负的错误码,而不是采取不可预期或者无法恢复的动作。
另外一个需要注意的是,对于store方法,不能返回0,否则会产生死循环。因为如果store返回小于形参count,驱动核心会认为解码未完成,并以本次解码剩余的缓冲区继续调用store。我们假设一个最多一次只能解码4个字符的store函数,见如下代码调用
const char *p=”1234567890”;
attrs->store(bus,p,10);

第一次返回5,驱动核心会接着调用:
attrs->store(bus,p+4,10-4);
attrs->store(bus,p+8,10-8);


持续直到count为0。因此一个返回0的store会导致永久循环。
下文的设备属性和驱动属性的store有同样的限制。

2.3 struct device_attribute *dev_attrs;
默认设备属性。对于每个将要注册到该总线上的设备,在设备注册时,默认添加dev_attrs数组指定的属性。这个结构体定义如下:

[cpp] view plain copy
  1. struct device_attribute {  
  2.     struct attribute    attr;  
  3.     ssize_t (*show)(struct device *dev, struct device_attribute *attr,  
  4.             char *buf);  
  5.     ssize_t (*store)(struct device *dev, struct device_attribute *attr,  
  6.              const char *buf, size_t count);  
  7. };  


这个结构体和struct bus_attribute的成员相类似。成员attr保存名称和权限,show和store方法分别在用户空间获取/设置属性值时调用。值得注意的是,此处show和store的函数接口,与struct bus_attribute中的完全不同。

 

2.4 struct driver_attribute *drv_attrs;
默认驱动属性。对于每个将要注册到该总线上的驱动,在驱动注册时,默认添加drv_attrs数组指定的属性。这个结构体定义如下:
struct driver_attribute {
    struct attribute attr;
    ssize_t (*show)(struct device_driver *driver, char *buf);
    ssize_t (*store)(struct device_driver *driver, const char *buf,
             size_t count);
};

现在观看这个结构体,会觉得十分熟悉。如同struct bus_attribute或struct device_attribute,attr设定属性的名称和权限,show和store方法指定用户空间数据获取/设置的编解码算法。需要注意show和store有不同的形参列表。

 

2.5 int (*match)(struct device *dev, struct device_driver *drv);
匹配函数。
判定设备和驱动是否匹配,是总线体系相关的。驱动核心通过match和probe两个函数来完成匹配。其中match就是此处讨论的函数,而probe在下文提及。
每当有设备添加到总线时,驱动核心遍历总线上的驱动链表(执行流程device_register->device_add->bus_probe_device->device_attach->bus_for_each_drv->match/probe)查找设备驱动;每当有驱动添加到总线时,驱动核心遍历总线上的设备链表(执行流程driver_register->bus_add_driver->driver_attach->bus_for_each_dev->match/probe)查找驱动可操控的设备。当前,match一般只执行总线特定的匹配处理,而在probe中,通过回调设备驱动probe,完成设备特定的匹配、设备初始化等。举例来说,对于PCI总线,match判断驱动支持的ID列表是否包含设备ID,如果包含则匹配,否则失配;probe会再次执行ID匹配判断,并回调驱动提供的probe函数。
match匹配成功则返回1,失配返回0。match函数指针为NULL,驱动核心返回1。
对于一次遍历匹配而言,如果match和probe均成功,则结束匹配过程;如果match成功而probe失配,继续遍历查找匹配;如果遍历结束而没有找到成功的匹配,对于驱动而言表示没有可操控设备,对于设备而言表示没有适当的驱动。

 

2.6 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
在发送热插拔事件消息到用户空间之前添加环境变量。
在设备注册、移除,或者状态更改时,内核负责发送通知事件到用户空间。通过man udev和man udevd可获取相关帮助。uevent在事件发送到用户空间之前调用,用来给事件添加总线特定的环境变量。
在Linux-2.6.13以后,采用udev机制动态创建设备文件,本文暂时不讨论这个细节。

2.7 int (*probe)(struct device *dev);
探测函数。
如在match中所述,probe执行设备相关的匹配探测、设备初始化、资源分配等。
需要注意,在probe调用时,dev->driver已经被设置为match成功匹配的驱动指针了,因此不再需要struct device_driver指针。

2.8 int (*remove)(struct device *dev);
移除设备。
设备移除时,调用该方法,完成部分清理工作。如删除设备驱动中,设备链表下的该设备。

2.9 void (*shutdown)(struct device *dev);
系统关机。
系统关机时,调用该方法关闭设备。

2.10 int (*suspend)(struct device *dev, pm_message_t state);
设备休眠(挂起)。
设备休眠(挂起)时调用该方法。一般在该方法中设置设备为低耗电状态。

2.11 int (*resume)(struct device *dev);
设备恢复。
设备从休眠中恢复时调用该方法。

2.12 const struct dev_pm_ops *pm;
电源管理。
一些设备有电源状态转换。结构体内部提供很多方法实现这个过程。暂时忽略这个结构体。

2.13 struct bus_type_private *p;
总线私有数据。
驱动核心设置并使用,总线驱动不必关心这个成员,并且一般不要去修改它。

3. 示例代码

我们实现一个示例驱动,以ycbus为基础,添加一个设备ycbus-dev0和一个驱动ycbus-drv0。设置总线属性、默认设备属性和默认驱动属性,各自包含一个只读version属性和一个读写rw-test属性,因为共享驱动数据缓冲,其中一个设置rw-test另一个也更改。如下代码保存为ycbus.c

[cpp] view plain copy
  1. /* 
  2.  * ycbus: a software bus driver (virtual bus driver) 
  3.  * 
  4.  * a trivial ycbus driver 
  5.  */  
  6. #include <linux/device.h>  
  7. #include <linux/module.h>  
  8. /* 
  9.  * attributes helper 
  10.  */  
  11. static const char *attrs_rw_test(bool bset, const char *value, size_t l)  
  12. {  
  13.   static char buf[64] = "rw-test-default";  
  14.   if (bset)  
  15.   {  
  16.     if (value)  
  17.     {  
  18.       /* Despite of c-library snprintf, kernel snprintf will appended '/0' automatically */  
  19.       if (l > 63) l = 63;  
  20.       memcpy(buf, value, l);  
  21.       buf[l] = '/0';  
  22.     }  
  23.     else  
  24.     {  
  25.       buf[0] = '/0';  
  26.     }  
  27.   }  
  28.   return buf;  
  29. }  
  30. static inline ssize_t attrs_version_show(const char *prefix, char *buf)  
  31. {  
  32.   return snprintf(buf, PAGE_SIZE, "%s: version 1.0.0/n", prefix);  
  33. }  
  34. static inline ssize_t attrs_rw_test_show(const char *prefix, char *buf)  
  35. {  
  36.   return snprintf(buf, PAGE_SIZE, "%s: %s/n", prefix,  attrs_rw_test(false, NULL, 0));  
  37. }  
  38. static inline ssize_t attrs_rw_test_store(const char *prefix, const char *buf, size_t count)  
  39. {  
  40.   /* it will not be failed */  
  41.   attrs_rw_test(true, buf, count);  
  42.   return count;  
  43. }  
  44. /* 
  45.  * bus attributes methods 
  46.  */  
  47. ssize_t bus_attrs_version_show(struct bus_type *bus, char *buf)  
  48. {  
  49.   return attrs_version_show("ycbus", buf);  
  50. }  
  51. ssize_t bus_attrs_rw_test_show(struct bus_type *bus, char *buf)  
  52. {  
  53.   return attrs_rw_test_show("ycbus", buf);  
  54. }  
  55. ssize_t bus_attrs_rw_test_store(struct bus_type *bus, const char *buf, size_t count)  
  56. {  
  57.   return attrs_rw_test_store("ycbus", buf, count);  
  58. }  
  59. /* 
  60.  * device attribute methods 
  61.  */  
  62. ssize_t dev_attrs_version_show(struct device *dev, struct device_attribute *attr, char *buf)  
  63. {  
  64.   return attrs_version_show("ycbus-dev0", buf);  
  65. }  
  66. ssize_t dev_attrs_rw_test_show(struct device *dev, struct device_attribute *attr, char *buf)  
  67. {  
  68.   return attrs_rw_test_show("ycbus-dev0", buf);  
  69. }  
  70. ssize_t dev_attrs_rw_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)  
  71. {  
  72.   return attrs_rw_test_store("ycbus-dev0", buf, count);  
  73. }  
  74. /* 
  75.  * driver attribute 
  76.  */  
  77. ssize_t drv_attrs_version_show(struct device_driver *driver, char *buf)  
  78. {  
  79.   return attrs_version_show("ycbus-drv0", buf);  
  80. }  
  81. ssize_t drv_attrs_rw_test_show(struct device_driver *drv, char *buf)  
  82. {  
  83.   return attrs_rw_test_show("ycbus-drv0", buf);  
  84. }  
  85. ssize_t drv_attrs_rw_test_store(struct device_driver *drv, const char *buf, size_t count)  
  86. {  
  87.   return attrs_rw_test_store("ycbus-drv0", buf, count);  
  88. }  
  89. void dev_release(struct device *dev)  
  90. {  
  91. }  
  92. static struct bus_attribute bus_attrs[] = {  
  93.   __ATTR(version, S_IRUGO, bus_attrs_version_show, NULL),  
  94.   __ATTR(rw-test, (S_IRUGO|S_IWUGO), bus_attrs_rw_test_show, bus_attrs_rw_test_store),  
  95.   __ATTR_NULL,  
  96. };  
  97. static struct device_attribute dev_attrs[] = {  
  98.   __ATTR(version, S_IRUGO, dev_attrs_version_show, NULL),  
  99.   __ATTR(rw-test, (S_IRUGO|S_IWUGO), dev_attrs_rw_test_show, dev_attrs_rw_test_store),  
  100.   __ATTR_NULL,  
  101. };  
  102. struct driver_attribute drv_attrs[] = {  
  103.   __ATTR(version, S_IRUGO, drv_attrs_version_show, NULL),  
  104.   __ATTR(rw-test, (S_IRUGO|S_IWUGO), drv_attrs_rw_test_show, drv_attrs_rw_test_store),  
  105.   __ATTR_NULL,  
  106. };  
  107. static struct bus_type ycbus_type = {  
  108.   .name      = "ycbus",  
  109.   .bus_attrs = bus_attrs,  
  110.   .dev_attrs = dev_attrs,  
  111.   .drv_attrs = drv_attrs,  
  112. };  
  113. static struct device ycbus_dev = {  
  114.   .init_name = "ycbus-dev0",  
  115.   .bus       = &ycbus_type,  
  116. };   
  117. static struct device_driver ycbus_drv = {  
  118.   .name = "ycbus-drv0",  
  119.   .bus = &ycbus_type,  
  120. };  
  121. static int __init ycbus_driver_init(void)  
  122. {  
  123.   int ret;  
  124.   printk(KERN_DEBUG "ycbus_driver_init/n");  
  125.     
  126.   ret = bus_register(&ycbus_type);  
  127.   if (ret) goto bus_fail;  
  128.   ret = device_register(&ycbus_dev);  
  129.   if (ret) goto dev_fail;  
  130.   ret = driver_register(&ycbus_drv);  
  131.   if (ret) goto drv_fail;  
  132.   return ret;  
  133. drv_fail:  
  134.   device_unregister(&ycbus_dev);  
  135. dev_fail:  
  136.   bus_unregister(&ycbus_type);  
  137. bus_fail:  
  138.   return ret;  
  139. }  
  140. static void __exit ycbus_driver_exit(void)  
  141. {  
  142.   printk(KERN_DEBUG "ycbus_driver_exit/n");  
  143.   driver_unregister(&ycbus_drv);  
  144.   device_unregister(&ycbus_dev);  
  145.   bus_unregister(&ycbus_type);  
  146. }  
  147. MODULE_AUTHOR("yc <cppgp@qq.com>");  
  148. MODULE_DESCRIPTION("yc pseudo-bus driver");  
  149. MODULE_LICENSE("GPL");  
  150. module_init(ycbus_driver_init);  
  151. module_exit(ycbus_driver_exit);  

提供Makefile如下。保存为Makefile,注意大写首字母M。

[c-sharp] view plain copy
  1. # A trivial bus driver Makefile. Saved as “Makefile” exactly  
  2. ifneq ($(KERNELRELEASE),)  
  3.   obj-m := ycbus.o  
  4. else  
  5.   KERNDIR ?= /lib/modules/$(shell uname -r)/build  
  6.   PWD := $(shell pwd)  
  7.   default:  
  8.     $(MAKE) -C ${KERNDIR} M=${PWD} modules  
  9. endif  
  10. clean:  
  11.     rm -rf modules.order Module.symvers .tmp_versions ycbus.ko .ycbus.ko.cmd ycbus.mod.c ycbus.mod.o .ycbus.mod.o.cmd ycbus.o .ycbus.o.cmd  

注意 $(MAKE)和rm两行前是TAB而不是空格。

 

编译、加载、测试:

[c-sharp] view plain copy
  1. ~$ make  
  2. ~$ sudo insmod ycbus.ko  
  3. ~$ tree /sys/bus/ycbus/  
  4. /sys/bus/ycbus/  
  5. ├── devices  
  6. │   └── ycbus-dev0 -> ../../../devices/ycbus-dev0  
  7. ├── drivers  
  8. │   └── ycbus-drv0  
  9. │       ├── bind  
  10. │       ├── rw-test  
  11. │       ├── uevent  
  12. │       ├── unbind  
  13. │       ├── version  
  14. │       └── ycbus-dev0 -> ../../../../devices/ycbus-dev0  
  15. ├── drivers_autoprobe  
  16. ├── drivers_probe  
  17. ├── rw-test  
  18. ├── uevent  
  19. └── version  
  20. 5 directories, 10 files  
  21. ~$ tree /sys/devices/ycbus-dev0/  
  22. /sys/devices/ycbus-dev0/  
  23. ├── driver -> ../../bus/ycbus/drivers/ycbus-drv0  
  24. ├── power  
  25. │   ├── control  
  26. │   ├── runtime_active_time  
  27. │   ├── runtime_status  
  28. │   ├── runtime_suspended_time  
  29. │   └── wakeup  
  30. ├── rw-test  
  31. ├── subsystem -> ../../bus/ycbus  
  32. ├── uevent  
  33. └── version  
  34. 3 directories, 8 files  
  35. $ tree /sys/bus/ycbus/drivers/ycbus-drv0/  
  36. /sys/bus/ycbus/drivers/ycbus-drv0/  
  37. ├── bind  
  38. ├── rw-test  
  39. ├── uevent  
  40. ├── unbind  
  41. ├── version  
  42. └── ycbus-dev0 -> ../../../../devices/ycbus-dev0  
  43. 1 directory, 5 files  
  44. ~$ ls /sys/bus/ycbus/ -l  
  45. total 0  
  46. drwxr-xr-x 2 root root    0 2011-04-19 11:39 devices  
  47. drwxr-xr-x 3 root root    0 2011-04-19 11:38 drivers  
  48. -rw-r--r-- 1 root root 4096 2011-04-19 11:39 drivers_autoprobe  
  49. --w------- 1 root root 4096 2011-04-19 11:39 drivers_probe  
  50. -rw-rw-rw- 1 root root 4096 2011-04-19 11:39 rw-test  
  51. --w------- 1 root root 4096 2011-04-19 11:39 uevent  
  52. -r--r--r-- 1 root root 4096 2011-04-19 11:39 version  
  53. ~$ cat /sys/bus/ycbus/rw-test   
  54. ycbus: rw-test-default  
  55. ~$ cat /sys/bus/ycbus/devices/ycbus-dev0/rw-test   
  56. ycbus-dev0: rw-test-default  
  57. ~$ cat /sys/bus/ycbus/drivers/ycbus-drv0/rw-test   
  58. ycbus-drv0: rw-test-default  
  59. ~$ echo -n "set ycbus new value" > /sys/bus/ycbus/rw-test   
  60. ~$ cat /sys/bus/ycbus/rw-test   
  61. ycbus: set ycbus new value  
  62. ~$ cat /sys/bus/ycbus/devices/ycbus-dev0/rw-test   
  63. ycbus-dev0: set ycbus new value  
  64. ~$ cat /sys/bus/ycbus/drivers/ycbus-drv0/rw-test   
  65. ycbus-drv0: set ycbus new value  
  66. ~$ echo -n "set ycbus-dev0 new value" > /sys/bus/ycbus/devices/ycbus-dev0/rw-test   
  67. ~$ cat /sys/bus/ycbus/drivers/ycbus-drv0/rw-test   
  68. ycbus-drv0: set ycbus-dev0 new value  
  69. :~$ cat /sys/bus/ycbus/version   
  70. ycbus: version 1.0.0  
  71. ~$ cat /sys/bus/ycbus/drivers/ycbus-drv0/version   
  72. ycbus-drv0: version 1.0.0  
  73. ~$ cat /proc/kallsyms | grep ycbus  
  74. e0aae280 t ycbus_driver_exit    [ycbus]  
  75. e0aae3c0 d ycbus_drv    [ycbus]  
  76. e0aae400 d ycbus_dev    [ycbus]  
  77. e0aae540 d ycbus_type   [ycbus]  
  78. e0aae580 d buf.15065    [ycbus]  
  79. e0aae5c0 d bus_attrs    [ycbus]  
  80. e0aae600 d dev_attrs    [ycbus]  
  81. e0aae640 d __this_module        [ycbus]  
  82. e0aae1b0 t bus_attrs_rw_test_show       [ycbus]  
  83. e0aae280 t cleanup_module       [ycbus]  
  84. e0aae010 t drv_attrs_rw_test_store      [ycbus]  
  85. e0aae0d0 t bus_attrs_rw_test_store      [ycbus]  
  86. e0aae000 t dev_release  [ycbus]  
  87. e0aae220 t dev_attrs_version_show       [ycbus]  
  88. e0aae070 t dev_attrs_rw_test_store      [ycbus]  
  89. e0aae380 d drv_attrs    [ycbus]  
  90. e0aae1f0 t drv_attrs_version_show       [ycbus]  
  91. e0aae250 t bus_attrs_version_show       [ycbus]  
  92. e0aae170 t dev_attrs_rw_test_show       [ycbus]  
  93. e0aae130 t drv_attrs_rw_test_show       [ycbus]  

 

代码很好理解,只是增加了一个虚拟设备和一个驱动到总线上,因为我们并未提供match和probe,而核心的默认处理是认为设备和驱动是匹配的(对于match和probe为空,核心直接返回1),因此它们是匹配的。可以在ycbus-dev0设备下看到驱动,也可以在ycbus-drv0下看到设备。另外,一个需要注意的问题是,注册一个设备必须提供release方法,否则核心会发出抱怨,并打印Call Trace信息。

0 0
原创粉丝点击