Linux设备模型(中)之上层容器 (转)

 

努力成为linux kernel hacker的人李万鹏原创作品,转载请标明出处

Linux设备模型是由总线(bus_type),设备(device),驱动(device_driver)这三个数据结构来描述的。在设备模型中,所有的设备都通过总线来连接。即使有些设备没有连接到一根物理上的总线,Linux为其设置了一个内部的,虚拟的platform总线,来维持总线,驱动,设备的关系。总线是处理器与一个或者多个设备之间的通道。比如一个USB控制器通常是一个PCI设备,设备模型展示了总线和他们所控制的设备之间的连接。
一般来说可以这么理解,整个的设备模型是一个复杂的数据体系结构,总线,设备,驱动都是其中鲜活存在的对象,kobject是他们的基类,所实现的只是一些公共的接口,kset是同种类型的kobject对象的集合,也可以说是对象的容器。只是因为C语言里不可能会有C++语言里类的class继承等概念,只有通过kobject嵌入到对象结构中来实现。这样,内核使用kobject将各个对象连接起来组成一个分层的体系结构。kobject结构中包含了parent成员,指向了另一个kobject结构,也就是这个分层结构的上一层结点。而kset是通过链表来实现的。

kobject是Linux在2.6中新引进的统一的设备管理模型,目的是对Linux的2.6系统所有的设备进行统一的管理。kobject是组成设备模型的基本结构。kobject是驱动程序模型中的一个核心数据结构,与sysfs文件系统自然的绑定在一起:——每个kobject对应sysfs文件系统中的一个目录。kobject往往被嵌入到设备驱动程序模型中的组件中,如总线,设备和驱动程序的描述符。kobject的作用是,为所属“容器”提供
.引用计数器
.维持容器的层次列表或组
.为容器的属性提供一种用户态查看的视图
kset是同类型kobject结构的一个集合体,通过kset数据结构可将kobjects组成一棵层次树。

C-sharp代码 复制代码
  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 (*suspend_late)(struct device *dev, pm_message_t state);   
  13.     int (*resume_early)(struct device *dev);   
  14.     int (*resume)(struct device *dev);   
  15.     struct dev_pm_ops *pm;   
  16.     struct bus_type_private *p;   
  17. };   
  18. struct bus_type_private {   
  19.     struct kset subsys;                  //该总线的subsystem  
  20.     struct kset *drivers_kset;           //所有与该总线相关的驱动集合  
  21.     struct kset *devices_kset;           //所有挂接在该总线上的设备集合  
  22.     struct klist klist_devices;   
  23.     struct klist klist_drivers;   
  24.     struct blocking_notifier_head bus_notifier;   
  25.     unsigned int drivers_autoprobe:1;   
  26.     struct bus_type *bus;   
  27. };   
  28. struct bus_attribute {   
  29.     struct attribute    attr;   
  30.     ssize_t (*show)(struct bus_type *bus, char *buf);   
  31.     ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);   
  32. };  

subsys描述该总线的子系统,subsys是一个kset结构,他连接到一个全局变量kset bus_subsys中。这样,每一根总线系统都会通过bus_subsys结构连接起来。kset *devices_kset是指向该总线所有设备的集合的指针,kset *drivers_kset是指向该总线所有驱动的集合的指针。该总线上的设备和驱动分别用一个链表连接在一起,分别是klist_devices,klist_drivers。每次都用kset *drivers_kset,kset *devices_kset遍历所有设备/驱动很麻烦,用klist比较直接方便。

C-sharp代码 复制代码
  1. struct device {   
  2.     struct klist        klist_children;   
  3.     struct klist_node   knode_parent;   /* node in sibling list */  
  4.     struct klist_node   knode_driver;   
  5.     struct klist_node   knode_bus;   
  6.     struct device       *parent;   
  7.     struct kobject kobj;   
  8.     char    bus_id[BUS_ID_SIZE];    /* position on parent bus */  
  9.     unsigned        uevent_suppress:1;   
  10.     const char      *init_name; /* initial name of the device */  
  11.     struct device_type  *type;   
  12.     struct semaphore    sem;    /* semaphore to synchronize calls to 
  13.                      * its driver.  
  14.                      */  
  15.     struct bus_type *bus;       /* type of bus device is on */  
  16.     struct device_driver *driver;   /* which driver has allocated this 
  17.                        device */  
  18.     void        *driver_data;   /* data private to the driver */  
  19.     void        *platform_data; /* Platform specific data, device 
  20.                        core doesn't touch it */  
  21.     struct dev_pm_info  power; 
  22. #ifdef CONFIG_NUMA   
  23.     int     numa_node;  /* NUMA node this device is close to */ 
  24. #endif   
  25.     u64     *dma_mask;  /* dma mask (if dma'able device) */  
  26.     u64     coherent_dma_mask;/* Like dma_mask, but for 
  27.                          alloc_coherent mappings as 
  28.                          not all hardware supports 
  29.                          64 bit addresses for consistent 
  30.                          allocations such descriptors. */  
  31.     struct device_dma_parameters *dma_parms;   
  32.     struct list_head    dma_pools;  /* dma pools (if dma'ble) */  
  33.     struct dma_coherent_mem *dma_mem; /* internal for coherent mem 
  34.                          override */  
  35.     /* arch specific additions */  
  36.     struct dev_archdata archdata;   
  37.     dev_t           devt;   /* dev_t, creates the sysfs "dev" */  
  38.     spinlock_t      devres_lock;   
  39.     struct list_head    devres_head;   
  40.     struct klist_node   knode_class;   
  41.     struct class        *class;   
  42.     struct attribute_group  **groups;   /* optional groups */  
  43.     void    (*release)(struct device *dev);   
  44. };   
  45. struct device_private {   
  46.     struct klist klist_children;   
  47.     struct klist_node knode_parent;   
  48.     struct klist_node knode_driver;   
  49.     struct klist_node knode_bus;   
  50.     struct device *device;   
  51. };   
  52. struct device_attribute {   
  53.     struct attribute    attr;   
  54.     ssize_t (*show)(struct device *dev, struct device_attribute *attr,   
  55.             char *buf);   
  56.     ssize_t (*store)(struct device *dev, struct device_attribute *attr,   
  57.              const char *buf, size_t count);   
  58. };  

需要注意的是,总线也是设备,也必须按设备注册。这里的parent是指该设备所属的父设备,struct kobject kobj;表示该设备并把它连接到结构体系中的kobject。请注意,作为一个通用准则,device->kobj->parent与&device->parent->kobj是相同的。bus_id是在总线上唯一标识该设备的字符串。struct bus_type *bus;标识了该设备连接在何种类型的总线上。struct device_driver *driver;管理该设备的驱动。void (*release)(struct device *dev);当指向设备的最后一个引用被删除时,内核调用该方法。它将从内嵌的kobject的release方法中调用。device_private中的knode_parent,knode_driver,knode_bus分别是挂入parent,驱动,总线链表中的指针。

C-sharp代码 复制代码
  1. struct device_driver {   
  2.     const char      *name;               //设备驱动程序的名称  
  3.     struct bus_type     *bus;                //该驱动所管理的设备挂接的总线类型  
  4.     struct module       *owner;   
  5.     const char      *mod_name;  /* used for built-in modules */  
  6.     int (*probe) (struct device *dev);   
  7.     int (*remove) (struct device *dev);   
  8.     void (*shutdown) (struct device *dev);   
  9.     int (*suspend) (struct device *dev, pm_message_t state);   
  10.     int (*resume) (struct device *dev);   
  11.     struct attribute_group **groups;   
  12.     struct dev_pm_ops *pm;   
  13.     struct driver_private *p;   
  14. };   
  15. struct driver_private {   
  16.     struct kobject kobj;   
  17.     struct klist klist_devices;               //该驱动所管理的设备链表头  
  18.     struct klist_node knode_bus;              //挂入总线链表中的指针  
  19.     struct module_kobject *mkobj;   
  20.     struct device_driver *driver;   
  21. };   
  22. struct driver_attribute {   
  23.     struct attribute attr;   
  24.     ssize_t (*show)(struct device_driver *driver, char *buf);   
  25.     ssize_t (*store)(struct device_driver *driver, const char *buf,   
  26.              size_t count);   
  27. };  

name指向驱动的名字,上边的device中也有一个名为bus_id的字符数组。查看一下,struct bus_type中有一个match,函数,这个是干什么用的呢。设备有了驱动才可以工作,只有驱动没有设备也是不行,驱动和设备需要关联上,这就需要这个match函数。驱动和设备是通过name来管理的,所以在match函数中要比较device的bus_id和driver中的name是否相等。如果相等,就说明驱动和设备互相找到了,这时device_driver中的probe函数被调用。我下边的例子中是这样实现的:

C-sharp代码 复制代码
  1. static ssize_t show_driver_author(struct device_driver *driver, char *buf){   
  2.     return snprintf(buf, PAGE_SIZE, "%s/n", author);   
  3. }  

下面是一个测试程序:

BUS:

C-sharp代码 复制代码
  1. #include <linux/module.h>  
  2. #include <linux/init.h>  
  3. #include <linux/string.h>  
  4. #include <linux/device.h>  
  5. #include <linux/kernel.h>   
  6.   
  7. static char *author = "LiWanPeng";   
  8.   
  9. static ssize_t show_bus_author(struct bus_type *bus, char *buf){   
  10.     return snprintf(buf, PAGE_SIZE, "%s/n", author);   
  11. }   
  12.   
  13. void my_bus_release(struct device *dev){   
  14.     printk(KERN_DEBUG "my bus release/n");     
  15. }      
  16.   
  17. static int virtual_bus_match(struct device *dev, struct device_driver *drv){   
  18.     return !strncmp(dev->bus_id, drv->name, strlen(drv->name));   
  19. }   
  20.   
  21. struct bus_type virtual_bus = {   
  22.     .name = "my_bus",   
  23.     .match = virtual_bus_match,   
  24. };   
  25.   
  26. struct device my_bus = {   
  27.     .init_name = "my_bus0",   
  28.     .release = my_bus_release,   
  29. };   
  30.   
  31. EXPORT_SYMBOL(my_bus);   
  32. EXPORT_SYMBOL(virtual_bus);   
  33.   
  34. static BUS_ATTR(author, S_IRUGO, show_bus_author, NULL);    
  35.   
  36. static int __init bus_init(void){   
  37.     int ret;   
  38.     ret = bus_register(&virtual_bus);   
  39.     if(ret)   
  40.         return ret;   
  41.     if(bus_create_file(&virtual_bus, &bus_attr_author))   
  42.         printk(KERN_NOTICE "Unable to create author attribute/n");   
  43.     ret = device_register(&my_bus);   
  44.     if(ret)   
  45.         printk(KERN_NOTICE "Fail to register device/n");   
  46.     printk("bus regiter success/n");   
  47.     return ret;   
  48. }   
  49.   
  50. static void __exit bus_exit(void){   
  51.     bus_unregister(&virtual_bus);   
  52.     device_unregister(&my_bus);   
  53. }   
  54.   
  55. module_init(bus_init);   
  56. module_exit(bus_exit);   
  57. MODULE_LICENSE("GPL");     

DEVICE:

C-sharp代码 复制代码
  1. #include <linux/module.h>  
  2. #include <linux/init.h>  
  3. #include <linux/string.h>  
  4. #include <linux/device.h>   
  5.   
  6. char *author = "LiWanPeng";   
  7. extern struct bus_type virtual_bus;   
  8. extern struct device my_bus;   
  9.   
  10. static ssize_t show_device_author(struct device *dev, struct device_attribute *attr, char *buf){   
  11.     return snprintf(buf, PAGE_SIZE, "%s/n", author);   
  12. }   
  13.   
  14. void virtual_device_release(struct device *dev){   
  15.     printk("virtual_device is released/n");   
  16. }   
  17.   
  18. struct device virtual_device ={   
  19.     .bus_id = "my_dev",   
  20.     .bus = &virtual_bus,   
  21.     .parent = &my_bus,   
  22.     .release = virtual_device_release,   
  23. };   
  24.   
  25. static DEVICE_ATTR(author, S_IRUGO, show_device_author, NULL);    
  26.   
  27. static int __init device_init(void){   
  28.     int ret;   
  29.     ret = device_register(&virtual_device);   
  30.     if(ret)   
  31.         return ret;   
  32.     if(device_create_file(&virtual_device, &dev_attr_author))   
  33.         printk(KERN_NOTICE "Unable to create author attribute/n");   
  34.     printk("device register success/n");   
  35.     return ret;   
  36. }   
  37.   
  38. static void __exit device_exit(void){   
  39.     device_unregister(&virtual_device);   
  40. }   
  41.   
  42. module_init(device_init);   
  43. module_exit(device_exit);   
  44. MODULE_AUTHOR("liwanpeng");   
  45. MODULE_LICENSE("GPL");  

DRIVER:

C-sharp代码 复制代码
  1. #include <linux/module.h>  
  2. #include <linux/init.h>  
  3. #include <linux/string.h>  
  4. #include <linux/device.h>  
  5. #include <linux/kernel.h>   
  6.   
  7. extern struct bus_type virtual_bus;   
  8. char *author = "LiWanPeng";   
  9.   
  10. static ssize_t show_driver_author(struct device_driver *driver, char *buf){   
  11.     return snprintf(buf, PAGE_SIZE, "%s/n", author);   
  12. }   
  13.   
  14. int my_driver_remove(struct device *dev){   
  15.     printk("driver is removed/n");   
  16.     return 0;   
  17. }      
  18.   
  19. int my_driver_probe(struct device *dev){   
  20.     printk("driver can handle the device/n");      
  21.     return 0;   
  22. }   
  23.   
  24. struct device_driver virtual_driver = {   
  25.     .name = "my_dev",   
  26.     .bus = &virtual_bus,   
  27.     .probe = my_driver_probe,   
  28.     .remove = my_driver_remove,   
  29. };     
  30.   
  31. static DRIVER_ATTR(author, S_IRUGO, show_driver_author, NULL);    
  32.   
  33. static int __init my_driver_init(void){   
  34.     int ret;   
  35.     ret = driver_register(&virtual_driver);   
  36.     if(ret)   
  37.         return ret;   
  38.     if(driver_create_file(&virtual_driver, &driver_attr_author))   
  39.         printk(KERN_NOTICE "Unable to create author attribute/n");   
  40.     printk("driver register success/n");   
  41.     return ret;   
  42. }   
  43.   
  44. static void __exit my_driver_exit(void){   
  45.     driver_unregister(&virtual_driver);   
  46. }   
  47.   
  48. module_init(my_driver_init);   
  49. module_exit(my_driver_exit);   
  50. MODULE_LICENSE("GPL");   
  51. MODULE_AUTHOR("liwanpeng");  

Makefile:

C-sharp代码 复制代码
  1. ifneq ($(KERNELRELEASE),)   
  2.     obj-m:= driver.o bus.o device.o   
  3. else  
  4.     KERNELDIR ?= /lib/modules/$(shell uname -r)/build   
  5.     PWD := $(shell pwd)   
  6. modules:   
  7.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules   
  8. clear:   
  9.     rm -rf *.o   
  10. endif  

测试:

C-sharp代码 复制代码
  1. root@hacker:/home/hacker/program# dmesg   
  2. [  500.120888] bus regiter success   
  3. [  503.635832] device register success   
  4. [  515.237701] driver can handle the device   
  5. [  515.237772] driver register success   
  6.   
  7.   
  8. root@hacker:/home/hacker/program# dmesg   
  9. [  627.552494] bus regiter success   
  10. [  631.652273] driver register success   
  11. [  641.867854] driver can handle the device   
  12. [  641.867861] device register success   
  13.   
  14. root@hacker:/sys/bus/my_bus/drivers/my_dev# ls -l   
  15. total 0   
  16. -r--r--r-- 1 root root 4096 2011-05-06 22:46 author   
  17. --w------- 1 root root 4096 2011-05-06 22:46 bind   
  18. lrwxrwxrwx 1 root root    0 2011-05-06 22:46 my_dev -> ../../../../devices/my_bus0/my_dev   
  19. --w------- 1 root root 4096 2011-05-06 22:46 uevent   
  20. --w------- 1 root root 4096 2011-05-06 22:46 unbind   
  21. root@hacker:/sys/bus/my_bus/drivers/my_dev# cat author    
  22. LiWanPeng  

参考:
《Linux设备驱动程序(第三版)》
《Linux那些事儿之我是USB》

Linux常见驱动源码分析(kernel hacker修炼之道)--李万鹏 李万鹏 IBM Linux Technology Center kernel team 驱动资料清单内容如下: Linux设备模型)之上层容器.pdf Linux设备模型(上)之底层模型.pdf Linux驱动修炼之道-驱动一些常见的宏.pdf Linux驱动修炼之道-内存映射.pdf Linux驱动修炼之道-看门狗框架源码分析.pdf Linux驱动修炼之道-触摸屏驱动之s3c2410_ts源码分析.pdf Linux驱动修炼之道-SPI驱动框架源码分析().pdf Linux驱动修炼之道-SPI驱动框架源码分析(下).pdf Linux驱动修炼之道-SPI驱动框架源码分析(上).pdf Linux驱动修炼之道-RTC子系统框架与源码分析.pdf Linux驱动修炼之道-platform.pdf Linux驱动修炼之道-LCD背光与gpio控制.pdf Linux驱动修炼之道-INPUT子系统(下).pdf Linux驱动修炼之道-INPUT子系统(上).pdf Linux驱动修炼之道-framebuffer().pdf Linux驱动修炼之道-framebuffer(下).pdf Linux驱动修炼之道-framebuffer(上).pdf Linux驱动修炼之道-DMA框架源码分析(下).pdf Linux驱动修炼之道-DMA框架源码分析(上).pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析(下).pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析(上).pdf Linux驱动修炼之道-clock框架.pdf Linux驱动修炼之道-ADC驱动.pdf Linux内核访问外设I O资源的方式.pdf LINUX内核USB子系统学习笔记之初识USB.pdf kernel hacker修炼之道之驱动-流水灯.pdf kernel hacker修炼之道之驱动-混杂设备.pdf kernel hacker修炼之道之驱动-按键.pdf kernel hacker修炼之道之PCI subsystem(五).pdf kernel hacker修炼之道之PCI subsystem(四).pdf kernel hacker修炼之道之PCI subsystem(三).pdf kernel hacker修炼之道之PCI subsystem(六).pdf kernel hacker修炼之道之PCI subsystem(二).pdf
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值