科创园

科创园地,分享技术知识,为科技助力发展,贡献一己之力。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

驱动设备模型之总线设备驱动

Posted on 2013-02-19 18:11  科创园  阅读(829)  评论(0编辑  收藏  举报

为了适用要求越来越高的硬件设备需求,linux2.6内核提供了一种全新的内核设备模型。

设备模型三元素:总线、设备、驱动;

第一节:总线

总线是处理器与设备之间的通道,所有的设备通过总线相连;总线由bus_type定义(位于<linux/device.h>

 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 device
18     struct subsys_private *p;
19 };

bus_type结构体中重要的方法:

match:当一个新设备或驱动被添加到这条总线时,该方法被调用。用来判断制定的驱动程序是否能够处理指定的设备。若可以,返回非零值。

uevent:为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量;

bus_attribute: 总线属性,这个结构体如下

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 };

总线属性的操作方法:

int bus_create_file(struct bus_type *bus,struct bus_attribute *attr)   //创建属性

void bus_remove_file(struct bus_type*bus, struct bus_attribute *attr)   //删除属性

总线的常用操作函数:

int bus_register(struct bus_type * bus)     //总线的注册

void bus_unregister(struct bus_type *bus)   //总线的删除

第二节:设备

linux每个设备又结构体devices来定义(位于linux/device.h),如下:

View Code
 1 struct device {
 2     struct device        *parent;
 3 
 4     struct device_private    *p;
 5 
 6     struct kobject kobj;
 7     const char        *init_name; /* initial name of the device */
 8     struct device_type    *type;
 9 
10     struct mutex        mutex;    /* mutex to synchronize calls to
11                      * its driver.
12                      */
13 
14     struct bus_type    *bus;        /* type of bus device is on */
15     struct device_driver *driver;    /* which driver has allocated this
16                        device */
17     void        *platform_data;    /* Platform specific data, device
18                        core doesn't touch it */
19     struct dev_pm_info    power;
20 
21 #ifdef CONFIG_NUMA
22     int        numa_node;    /* NUMA node this device is close to */
23 #endif
24     u64        *dma_mask;    /* dma mask (if dma'able device) */
25     u64        coherent_dma_mask;/* Like dma_mask, but for
26                          alloc_coherent mappings as
27                          not all hardware supports
28                          64 bit addresses for consistent
29                          allocations such descriptors. */
30 
31     struct device_dma_parameters *dma_parms;
32 
33     struct list_head    dma_pools;    /* dma pools (if dma'ble) */
34 
35     struct dma_coherent_mem    *dma_mem; /* internal for coherent mem
36                          override */
37     /* arch specific additions */
38     struct dev_archdata    archdata;
39 #ifdef CONFIG_OF
40     struct device_node    *of_node;
41 #endif
42 
43     dev_t            devt;    /* dev_t, creates the sysfs "dev" */
44 
45     spinlock_t        devres_lock;
46     struct list_head    devres_head;
47 
48     struct klist_node    knode_class;
49     struct class        *class;
50     const struct attribute_group **groups;    /* optional groups */
51 
52     void    (*release)(struct device *dev);
53 };

设备属性由struct device_attribute描述:

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 };

设备属性的常用操作方法:

int device_create_file(struct device *device, struct device_attribute * entry)   //创建属性

void device_remove_file(struct device * dev, struct device_attribute * attr)    //删除属性

设备的常用操作函数:

int device_register(struct device *dev)    //注册一个设备;

void device_unregister(struct device *dev)   //注销设备

注意:一条总线也是个设备,也必须按设备注册;

第三节:驱动

驱动程序由struct device_driver描述,代码如下:

View Code
 1 struct device_driver {
 2     const char        *name;
 3     struct bus_type        *bus;
 4 
 5     struct module        *owner;
 6     const char        *mod_name;    /* used for built-in modules */
 7 
 8     bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */
 9 
10 #if defined(CONFIG_OF)
11     const struct of_device_id    *of_match_table;
12 #endif
13 
14     int (*probe) (struct device *dev);
15     int (*remove) (struct device *dev);
16     void (*shutdown) (struct device *dev);
17     int (*suspend) (struct device *dev, pm_message_t state);
18     int (*resume) (struct device *dev);
19     const struct attribute_group **groups;
20 
21     const struct dev_pm_ops *pm;
22 
23     struct driver_private *p;
24 };

驱动属性使用struct driver_attribute来描述,如下:

1 struct driver_attribute {
2     struct attribute attr;
3     ssize_t (*show)(struct device_driver *driver, char *buf);
4     ssize_t (*store)(struct device_driver *driver, const char *buf,
5              size_t count);
6 };

驱动属性的常用操作方法:

int driver_create_file(struct device_driver * drv,struct driver_attribute * attr)   //创建属性

void driver_remove_file(struct device_driver * drv,struct driver_attribute * attr)  /删除属性

驱动常用的操作函数:

int driver_register(struct device_driver *drv)    //注册驱动

void driver_unregister(struct device_driver *drv)   //注销驱动

 

实例分析:

至此,已经介绍完设备模型的基本结构,下面以一个实例分析三者之间的关系:

bus.c    在sysfs/bus下创建一个总线目录my_bus

View Code
 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 
 7 MODULE_AUTHOR("David Xie");
 8 MODULE_LICENSE("Dual BSD/GPL");
 9 
10 static char *Version = "$Revision: 1.9 $";
11 
12 /*比较设备的bus_id与驱动的名字是否匹配,匹配一致则在insmod驱动 时候调用probe函数*/
13 
14 static int my_match(struct device *dev, struct device_driver *driver)
15 {
16     return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
17 }
18 
19 static void my_bus_release(struct device *dev)
20 {
21     printk(KERN_DEBUG "my bus release\n");
22 }
23 
24 struct bus_type my_bus_type = {
25     .name = "my_bus",
26     .match = my_match,
27 };
28     
29 struct device my_bus = {
30     .bus_id   = "my_bus0",
31     .release  = my_bus_release
32 };
33 
34 
35 /*符号导出
36  * Export a simple attribute.
37  */
38 EXPORT_SYMBOL(my_bus);
39 EXPORT_SYMBOL(my_bus_type);
40 
41 static ssize_t show_bus_version(struct bus_type *bus, char *buf)
42 {
43     return snprintf(buf, PAGE_SIZE, "%s\n", Version);
44 }
45 
46 static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
47 
48 
49 static int __init my_bus_init(void)
50 {
51     int ret;
52         
53         /*注册总线*/
54     ret = bus_register(&my_bus_type);
55     if (ret)
56         return ret;
57         
58     /*创建属性文件*/    
59     if (bus_create_file(&my_bus_type, &bus_attr_version))
60         printk(KERN_NOTICE "Fail to create version attribute!\n");
61     
62     /*注册总线设备,设备是挂在my_bus总线上的,怎么做的呢?看device.c*/
63     ret = device_register(&my_bus);
64     if (ret)
65         printk(KERN_NOTICE "Fail to register device:my_bus!\n");
66         
67     return ret;
68 }
69 
70 static void my_bus_exit(void)
71 {
72     device_unregister(&my_bus);
73     bus_unregister(&my_bus_type);
74 }
75 
76 module_init(my_bus_init);
77 module_exit(my_bus_exit);

device.c 在sysfs/bus/my_bus/devices目录下创建一个设备my_dev

这里my_dev是一个连接文件,它连接到sysfs/devices/my_bus0/my_dev

View Code
 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 
 7 MODULE_AUTHOR("David Xie");
 8 MODULE_LICENSE("Dual BSD/GPL");
 9 
10 extern struct device my_bus; 
11 extern struct bus_type my_bus_type;
12 
13 /* Why need this ?*/
14 static void my_dev_release(struct device *dev)
15 { 
16     
17 }
18 
19 struct device my_dev = {
20     .bus = &my_bus_type,
21     .parent = &my_bus,
22     .release = my_dev_release,
23 };
24 
25 /*
26  * Export a simple attribute.
27  */
28 static ssize_t mydev_show(struct device *dev,struct device_attribute *attr, char *buf)
29 {
30     return sprintf(buf, "%s\n", "This is my device!");
31 }
32 
33 static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);
34 
35 static int __init my_device_init(void)
36 {
37     int ret = 0;
38         
39         /* 初始化设备 */
40     strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE);
41         
42         /*注册设备*/
43     device_register(&my_dev);
44         
45     /*创建属性文件*/
46     device_create_file(&my_dev, &dev_attr_dev);
47     
48     return ret;    
49 
50 }
51 
52 static void my_device_exit(void)
53 {
54     device_unregister(&my_dev);
55 }
56 
57 module_init(my_device_init);
58 module_exit(my_device_exit);

driver.c   当加载驱动时候,会在总线上找到它能够处理的设备

View Code
 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 
 7 MODULE_AUTHOR("David Xie");
 8 MODULE_LICENSE("Dual BSD/GPL");
 9 
10 extern struct bus_type my_bus_type;
11 
12 static int my_probe(struct device *dev)
13 {
14     printk("Driver found device which my driver can handle!\n");
15     return 0;
16 }
17 
18 static int my_remove(struct device *dev)
19 {
20     printk("Driver found device unpluged!\n");
21     return 0;
22 }
23 
24 struct device_driver my_driver = {
25     .name = "my_dev",
26     .bus = &my_bus_type, /*指明这个驱动程序是属于my_bus_type这条总线上的设备*/
27     .probe = my_probe,  /*在my_bus_type总线上找到它能够处理的设备,就调用my_probe;删除设备调用my_remove*/
28   .remove    = my_remove,  
29   /*能够处理的设备? 
30      1、什么时候驱动程序从总线找能够处理的设备;答:驱动注册时候
31   2、凭什么说能够处理呢?(或者说标准是什么);答:驱动与设备都是属于总线上的,利用总线的结构bus_type中match函数
32   */
33 };
34 
35 /*
36  * Export a simple attribute.
37  */
38 static ssize_t mydriver_show(struct device_driver *driver, char *buf)
39 {
40     return sprintf(buf, "%s\n", "This is my driver!");
41 }
42 
43 static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);
44 
45 static int __init my_driver_init(void)
46 {
47     int ret = 0;
48         
49   /*注册驱动*/
50     driver_register(&my_driver);
51         
52     /*创建属性文件*/
53     driver_create_file(&my_driver, &driver_attr_drv);
54     
55     return ret;    
56 
57 }
58 
59 static void my_driver_exit(void)
60 {
61     driver_unregister(&my_driver);
62 }
63 
64 module_init(my_driver_init);
65 module_exit(my_driver_exit);

注意:无论是先加载驱动还是设备,都会调用probe这个函数。好比先插u盘,在启动系统 与启动系统后再插u盘,系统一样能够支持。