Chinaunix首页 | 论坛 | 博客
  • 博客访问: 803948
  • 博文数量: 869
  • 博客积分: 201
  • 博客等级: 入伍新兵
  • 技术积分: 3376
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-21 19:42
文章分类

全部博文(869)

文章存档

2014年(4)

2013年(415)

2012年(453)

我的朋友

分类:

2012-08-02 13:01:01

原文地址:Linux内核驱动之总线 作者:luozhiyong131

总线是处理器和一个或多个设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟"platform"总线。总线可以相互插入。设备模型展示了总线和它们所控制的设备之间的实际连接。
在 Linux 设备模型中, 总线由 bus_type 结构表示, 定义在  :

点击(此处)折叠或打开

  1. *linux/device.h*/
  2. 51 struct bus_type {
  3. 52 const char *name;
  4. 53 struct bus_attribute *bus_attrs;
  5. 54 struct device_attribute *dev_attrs;
  6. 55 struct driver_attribute *drv_attrs;
  7. 56
  8. 57 int (*match)(struct device *dev, struct device_driver *drv);
  9. 58 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
  10. 59 int (*probe)(struct device *dev);
  11. 60 int (*remove)(struct device *dev);
  12. 61 void (*shutdown)(struct device *dev);
  13. 62
  14. 63 int (*suspend)(struct device *dev, pm_message_t state);
  15. 64 int (*suspend_late)(struct device *dev, pm_message_t state);
  16. 65 int (*resume_early)(struct device *dev);
  17. 66 int (*resume)(struct device *dev);
  18. 67
  19. 68 struct dev_pm_ops *pm;
  20. 69
  21. 70 struct bus_type_private *p;
  22. 71 };

其中name是总线的名字, bus_attrs是总线的属性

总线的注册和删除:

总线的注册有两个步骤:

1、定义一个bus_type结构体,并设置好需要设置的结构体成员。

2、调用函数bus_register注册总线。函数原型如下:

/*drivers/base/bus.c*/

865 int bus_register(struct bus_type *bus)

该调用有可能失败,所以必须检查它的返回值,如果注册成功,会在/sys/bus下看到指定名字的总线。


总线删除时调用:

/*drivers/base/bus.c*/

946 void bus_unregister(struct bus_type *bus) 

总线注册实例:

点击(此处)折叠或打开

  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/kernel.h>
  4. #include <linux/device.h>

  5. struct bus_type my_usb_bus =
  6. {
  7.     .name = "my_usb", //定义总线的名字为usb,注册成功后将在/sys/bus目录下看到
  8. };

  9. static int __init usb_bus_init(void)
  10. {
  11.     int ret;
  12.     
  13.     /*总线注册,必须检测返回值*/
  14.     ret = bus_register(&my_usb_bus);
  15.     if(ret){
  16.         printk("bus register failed!\n");
  17.         return ret;
  18.     }

  19.     printk("usb bus init\n");

  20.     return ret;
  21. }

  22. static void __exit usb_bus_exit(void)
  23. {
  24.     bus_unregister(&my_usb_bus);    
  25. }
  26.     
  27. module_init(usb_bus_init);
  28. module_exit(usb_bus_exit);

  29. MODULE_LICENSE("GPL");


总线方法

int (*match)(struct device * dev, struct device_driver * drv) 当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。

int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) 在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。

 

总线属性添加和删除:

    个人理解,设置总线的属性后,会在对应的总线目录下增加了一个新的文件,通过对该文件的读写访问,触发相应的函数操作,从而实现/sys/的文件接口与内核设备模型的数据交互

点击(此处)折叠或打开

  1. /*linux/sysfs.h*/
  2. 28 struct attribute {
  3. 29 const char *name; //设定该文件的名字
  4. 30 struct module *owner; //设定该文件的属主
  5. 31 mode_t mode; //设定该文件的文件操作权限
  6. 32 };
  7. /*linux/device.h*/
  8. 38 struct bus_attribute {
  9. 39 struct attribute attr;
  10. 40 ssize_t (*show)(struct bus_type *bus, char *buf);
  11. 41 ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
  12. 42 };

bus_attribute中有两个函数指针,showstore

当访问总线目录中的name文件时,就会触发show函数,一般会将指定的信息存放到数组buf,并传到用户空间显示。

当修改总线目录中的name文件是,就会触发stroe函数,一般会将从用户空间传来的buf指针存放的count个字节内容存放到内核中。

由此可以看到,通过这样的文件,就能实现sys目录下的文件与内核设备模型之间的数据交互。

设置总线属性有两个步骤:

1、创建并初始化bus_attribute结构,使用宏BUS_ATTR

BUS_ATTR(_name, _mode, _show, _store) 

该宏会定义一个名叫bus_attr__name(红色部分是固定的)的bus_attibute的结构,并且成员name设置为_name,文件权限mode设置为_mode,两个函数调用分别人showstore

2、将bus_attibute添加到指定的总线上,使用以下调用:

/*/drivers/base/bus.c*/

123 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) 

该函数失败时返回错误号。

一旦调用该函数,会就在指定bus总线的目录下新建一个名叫_name的文件,权限为_mode,当访问和修改该文件是会分别调用showstore函数调用。

如果不需要该属性时,使用以下函数删除:

/*/drivers/base/bus.c*/

135 void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) 


源码实例:

点击(此处)折叠或打开

  1. #include <linux/device.h>
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <linux/init.h>
  5. #include <linux/string.h>

  6. static char *Version = "$Revision: 1.0 $";

  7. /*当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。*/
  8. static int my_match(struct device *dev, struct device_driver *driver)
  9. {
  10.         return !strncmp(dev->kobj.name, driver->name, strlen(driver->name));
  11. }

  12. /*声明总线*/
  13. struct bus_type my_bus_type = {
  14.         .name = "my_bus", //总线名字
  15.         .match = my_match, //总线match函数指针
  16. };

  17. static ssize_t show_bus_version(struct bus_type *bus, char *buf)
  18. {
  19.         return snprintf(buf, PAGE_SIZE, "%s\n", Version);
  20. }

  21. /*内核代码中如此定义:#define BUS_ATTR(_name, _mode, _show, _store) \
  22. struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store),
  23. 它将bus_attr_作为给定的name的前缀来创建总线的真正名称。对应下面的是bus_attr_version*/
  24. static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

  25. /*模块加载函数*/
  26. static int __init my_bus_init(void)
  27. {
  28.         int ret;

  29.         /*注册总线*/
  30.         ret = bus_register(&my_bus_type);
  31.         if (ret)
  32.                 return ret;

  33.         /*创建属性文件*/
  34.         if (bus_create_file(&my_bus_type, &bus_attr_version))
  35.                 printk(KERN_NOTICE "Fail to create version attribute!\n");

  36.         return ret;
  37. }

  38. /*模块卸载函数*/
  39. static void my_bus_exit(void)
  40. {
  41.         bus_unregister(&my_bus_type);
  42. }

  43. module_init(my_bus_init);
  44. module_exit(my_bus_exit);

  45. MODULE_AUTHOR("Lzy");
  46. MODULE_LICENSE("GPL");


阅读(132) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~