Chinaunix首页 | 论坛 | 博客
  • 博客访问: 464006
  • 博文数量: 65
  • 博客积分: 573
  • 博客等级: 中士
  • 技术积分: 693
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-09 17:16
文章分类

全部博文(65)

文章存档

2015年(12)

2014年(9)

2013年(22)

2012年(7)

2011年(15)

分类: LINUX

2014-09-17 09:25:25

示例程序运行kernel版本为3.0.36,示例程序是国嵌的,修改使之在3.0.36内核版本可运行,并消除编译时的警告信息

linux设备模型的介绍,在ldd3中已经介绍的很详细了,没有了解过的,可以去看看第十四章设备模型这一章,,这里只简单介绍一下设备总线,设备和驱动,并提供一个可运行的示例程序,用做备忘!

Linux设备模型是由总线(bus_type),设备(device),驱动(device_driver)这三个数据结构来描述的
总线:
在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟“platform”总线, 在Linux 设备模型中, 总线由 bus_type 结构表示:
struct bus_type {
 const char *name;   //总线名称
 struct bus_attribute *bus_attrs;  //总线属性
 struct device_attribute *dev_attrs;  //设备属性
 struct driver_attribute *drv_attrs;   //驱动属性

 int (*match)(struct device *dev, struct device_driver *drv);//当一个新设备或驱动添加到此总线时,该函数被调用,用于判断驱动程序是否能处理指定的设备,若可以,则返回非0值
 int (*uevent)(struct device *dev, struct kobj_uevent_env *env); //在为用户空间产生热插拔事件之前,该函数允许为总线添加环境变量
 int (*probe)(struct device *dev);
 int (*remove)(struct device *dev);
 void (*shutdown)(struct device *dev);

 int (*suspend)(struct device *dev, pm_message_t state);
 int (*resume)(struct device *dev);

 const struct dev_pm_ops *pm;

 struct subsys_private *p;
};


//总线属性
struct bus_attribute {
 struct attribute attr;
 ssize_t (*show)(struct bus_type *bus, char *buf);
 ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};

下面贴上创建一个名为my_bus总线的示例程序:

bus.c

  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. MODULE_LICENSE("Dual BSD/GPL");

  7. static char *Version = "$Revision: 1.9 $";

  8. //匹配函数,用来匹配设备和驱动是否兼容
  9. static int my_match(struct device *dev, struct device_driver *driver)
  10. {
  11.     printk("dev->kobj.name=%s,driver->name=%s\n",dev->kobj.name,driver->name);
  12.     //这里对比设备名字和驱动名字是否相等
  13.     return !strncmp(dev->kobj.name, driver->name, strlen(driver->name));
  14. }

  15. static void my_bus_release(struct device *dev)
  16. {
  17.     printk(KERN_DEBUG "my bus release\n");
  18. }

  19. //定义一个总线设备    
  20. struct device my_bus = {
  21.     .init_name = "my_bus0",//总线设备名
  22.     .release = my_bus_release//设备引用为0时的释放函数
  23. };

  24. //定义一个总线
  25. struct bus_type my_bus_type = {
  26.     .name = "my_bus",//总线名字
  27.     .match = my_match,//匹配函数,用来匹配设备和驱动是否兼容
  28. };

  29. //导出总线设备,供挂载在总线上的设备使用
  30. EXPORT_SYMBOL(my_bus);
  31. //导出总线设备,供挂载在总线上的设备和驱动使用
  32. EXPORT_SYMBOL(my_bus_type);


  33. /*
  34.  * Export a simple attribute.
  35.  */
  36. //读取bus属性version时调用该函数
  37. static ssize_t show_bus_version(struct bus_type *bus, char *buf)
  38. {
  39.     return snprintf(buf, PAGE_SIZE, "%s\n", Version);
  40. }
  41. //该宏在编译时创建和初始化bus_attr_version,创建的名字由bus_attr_加version组成
  42. static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

  43. //模块初始化函数
  44. static int __init my_bus_init(void)
  45. {
  46.     int ret;
  47.         
  48.     //注册总线
  49.     ret = bus_register(&my_bus_type);
  50.     if (ret)
  51.         return ret;
  52.         
  53.     //创建属性文件    
  54.     if (bus_create_file(&my_bus_type, &bus_attr_version))
  55.         printk(KERN_NOTICE "Fail to create version attribute!\n");
  56.     
  57.     //注册总线设备,总线也是一个设备,所以必需对它进行注册
  58.     ret = device_register(&my_bus);
  59.     if (ret)
  60.         printk(KERN_NOTICE "Fail to register device:my_bus!\n");
  61.     printk("bus register success!!!\n");    
  62.     return ret;
  63. }
  64. //模块退出函数
  65. static void my_bus_exit(void)
  66. {
  67.     device_unregister(&my_bus);
  68.     bus_unregister(&my_bus_type);
  69.     printk("bus remove success!!!\n");
  70. }

  71. module_init(my_bus_init);
  72. module_exit(my_bus_exit);
编译成bus.ko,加载到系统中 insmod bus.ko

该模块加载到系统之后,sys文件系统中多了两个目录,一个是/sys/bus/my_bus,一个是sys/devices/my_bus0, 分别对应于模块里面注册的总线和设备,/sys/bus/my_bus还有一个有我们自己闯属性文件version,读取该文件将调用show_bus_version函数


设备:
系统中的每个设备由一个struct device结构描述:
struct device {
 struct device *parent;  //父设备

 struct device_private *p;  

 struct kobject kobj;  //设备内嵌的kobject
 const char *init_name; /* initial name of the device */
 const struct device_type *type;

 struct mutex mutex; /* mutex to synchronize calls to
      * its driver.
      */

 struct bus_type *bus; /* type of bus device is on */
 struct device_driver *driver; /* which driver has allocated this
        device */
 void *platform_data; /* Platform specific data, device
        core doesn't touch it */
    /*
    这里省略部分信息
*/

 void (*release)(struct device *dev);
};

设备属性定义如下:
struct device_attribute {
 struct attribute attr;
 ssize_t (*show)(struct device *dev, struct device_attribute *attr,
   char *buf);
 ssize_t (*store)(struct device *dev, struct device_attribute *attr,
    const char *buf, size_t count);
};
下面贴上创建一个名为my_dev设备,并挂载在上面创建的my_bus总线上的示例程序:

device.c

  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. MODULE_LICENSE("Dual BSD/GPL");

  7. //声明外部变量my_bus和my_bus_type,由bus模块里导出
  8. extern struct device my_bus;
  9. extern struct bus_type my_bus_type;


  10. static void my_dev_release(struct device *dev)
  11. {     
  12. }
  13. //定义一个设备
  14. struct device my_dev = {
  15.     .init_name = "my_dev",//用于初始化设备名字,即设备内嵌kobject的名字,初始化操作完成之后,该指针赋值为NULL
  16.     .bus = &my_bus_type, //设备挂载对应的总线
  17.     .parent = &my_bus, //设备的父设备,此处即总线设备
  18.     .release = my_dev_release, //所有设备引用消失时,调用此函数
  19. };

  20. /*
  21.  * Export a simple attribute.
  22.  */
  23.  //读取设备属性dev时调用该函数
  24. static ssize_t mydev_show(struct device *dev, struct device_attribute *attr,char *buf)
  25. {
  26.     return sprintf(buf, "%s\n", "This is my device!");
  27. }
  28. //该宏在编译时创建和初始化dev_attr_dev,创建的名字由dev_attr_加dev组成
  29. static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);

  30. //模块初始化函数
  31. static int __init my_device_init(void)
  32. {
  33.     int ret = 0;
  34.     //注册设备
  35.     ret=device_register(&my_dev);
  36.     if(ret){
  37.         printk("device register error!!!\n");
  38.         return -1;
  39.     }

  40.     //创建设备属性文件
  41.     ret=device_create_file(&my_dev, &dev_attr_dev);
  42.     if(ret){
  43.         printk("create device file error!!!\n");
  44.     }
  45.     
  46.     printk("register device(%s) success\n",my_dev.kobj.name);
  47.     return ret;    

  48. }
  49. //模块退出函数
  50. static void my_device_exit(void)
  51. {
  52.     device_unregister(&my_dev);
  53.     printk("remove device success\n");
  54. }

  55. module_init(my_device_init);
  56. module_exit(my_device_exit);
编译成device.ko,加载到系统中 insmod device.ko(要先加载bus.ko,加载device.ko才能成功,因为device.ko引用了bus.ko的两个内核导出符)

device模块加载成功之后,/sys/devices/my_bus0/设备下面多了一个my_dev目录,/sys/bus/my_bus/devices/目录下也多了一个连接,是链接到
/sys/devices/my_bus0/my_dev目录的,在该目录下还生成了一个由我们创建的名为dev的可读属性文件,读取该文件会调用mydev_show该函数。

驱动:
系统中的每个驱动程序由一个struct device_driver结构描述:
struct device_driver {
 const char *name; //驱动名字
 struct bus_type *bus; //驱动程序所在的总线

 struct module *owner;
 const char *mod_name; /* used for built-in modules */

 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */

 const struct of_device_id *of_match_table;

 int (*probe) (struct device *dev);
 int (*remove) (struct device *dev);
 void (*shutdown) (struct device *dev);
 int (*suspend) (struct device *dev, pm_message_t state);
 int (*resume) (struct device *dev);
 const struct attribute_group **groups;

 const struct dev_pm_ops *pm;

 struct driver_private *p;
};

驱动属性:
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);
};

下面贴上创建一个名为my_dev的驱动程序,并挂载在上面创建的my_bus总线上的示例程序:

driver.c

  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. MODULE_LICENSE("Dual BSD/GPL");
  7. //声明外部总线my_bus_type,由bus模块里导出
  8. extern struct bus_type my_bus_type;

  9. //探测函数,当总线的match函数匹配成功后,该函数开始执行
  10. static int my_probe(struct device *dev)
  11. {
  12.     printk("Driver found device which my driver can handle!\n");
  13.     return 0;
  14. }

  15. static int my_remove(struct device *dev)
  16. {
  17.     printk("Driver found device unpluged!\n");
  18.     return 0;
  19. }
  20. //定义一个名为my_dev的驱动程序
  21. struct device_driver my_driver = {
  22.     .name = "my_dev", //驱动名
  23.     .bus = &my_bus_type,//驱动挂载对应的总线
  24.     .probe = my_probe, //探测函数,当总线的match函数匹配成功后,该函数开始执行
  25.     .remove    = my_remove, //当驱动移除时,该函数开始执行
  26. };

  27. /*
  28.  * Export a simple attribute.
  29.  */
  30.   //读取驱动属性drv时调用该函数
  31. static ssize_t mydriver_show(struct device_driver *driver, char *buf)
  32. {
  33.     return sprintf(buf, "%s\n", "This is my driver!");
  34. }
  35. //该宏在编译时创建和初始化driver_attr_drv,创建的名字由driver_attr_加drv组成
  36. static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);
  37. //模块初始化函数
  38. static int __init my_driver_init(void)
  39. {
  40.     int ret = 0;
  41.         
  42.     //注册驱动
  43.     ret=driver_register(&my_driver);
  44.     if(ret){
  45.         printk("driver register error!!!\n");
  46.         return -1;
  47.     }    
  48.     //创建驱动属性文件
  49.     ret=driver_create_file(&my_driver, &driver_attr_drv);
  50.     if(ret){
  51.         printk("create driver file error!!!\n");
  52.     }
  53.     
  54.     printk("register driver(%s) success\n",my_driver.name);
  55.     return ret;    

  56. }
  57. //模块退出函数
  58. static void my_driver_exit(void)
  59. {
  60.     driver_unregister(&my_driver);
  61.     printk("remove driver success\n");
  62. }

  63. module_init(my_driver_init);
  64. module_exit(my_driver_exit);
编译成driver.ko,加载到系统中 insmod driver.ko(要先加载bus.ko,driver.ko模块才能加载成功)

驱动加载过程中,系统会调用my_bus总线的match函数来对设备和驱动进行匹配。匹配成功之后,才调用驱动的probe函数。
驱动加载成功之后,会在/sys/bus/my_bus/drivers目录下面创建一个名为my_dev的驱动目录,该驱动目录下面包含一个由我们自己创建的名为drv的属性文件和一个名为my_dev的设备连接。



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