I/O体系结构和设备驱动程序(三)
2011-08-06 19:31
295 查看
2.3、设备驱动程序模型的组件
设备驱动程序模型建立在以下几个基本数据结构之上:2.3.1、设备
设备驱动程序模型中每个设备对应一个device对象。struct device { struct klist klist_children; /*链表表头,指向该设备的所有子设备构成的链表*/ struct klist_node knode_parent; /* node in sibling list 用于子设备对象加入parent设备的 klist_children链表*/ struct klist_node knode_driver; /*用于将所有被同一驱动程序管理的设备连接到一个链表中*/ struct klist_node knode_bus; /*用于将所有安装在同一类型总线上的设备连接到一个链表中*/ struct device *parent; /*指向父节点的device实例*/ struct kobject kobj; /*控制通用对象属性*/ char bus_id[BUS_ID_SIZE]; /*总线上该设备的位置*//* position on parent bus */ struct device_type *type; unsigned is_registered:1; unsigned uevent_suppress:1; struct semaphore sem; /* semaphore 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 *driver_data;/* data private to the driver *//*驱动程序的私有成员,不能由通用代码修改*/ void *platform_data; /*私有成员,用于将特定于体系结构的数据关联到设备 通用驱动程序模型不会访问这些数据*//* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; /*电源管理信息*/ #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) *//*指向设备DMA屏蔽字的指针*/ u64 coherent_dma_mask;/*设备的一致性DMA的屏蔽字*//* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct list_head dma_pools; /*聚集的DMA缓冲池链表的首部*//* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /*指向设备所使用的一致性DMA存储器描述符的指针*//* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata; spinlock_t devres_lock; struct list_head devres_head; /* class_device migration path */ struct list_head node; /*指向兄弟设备链表的指针*/ struct class *class; dev_t devt; /* dev_t, creates the sysfs "dev" */ struct attribute_group **groups; /* optional groups */ void (*release)(struct device * dev);/*设备(或device实例) 不再使用时,将分配的资源释放回内核*/ };
device对象全部收集在devices_subsys子系统中,该子系统对应的目录是/sys/devices.
<base/sys.c>
extern struct kset devices_subsys;
设备是按照层次关系组织的:一个设备是某个“孩子”的父亲,其条件为子设备离开父设备无法正常工作。例如在基于PCI总线的计算机上,位于PCI总线和USB总线之间的桥就是连接在USB总线上所有设备的父设备。
klist_children字段是子设备链表的首部,而knode_parent字段则用于加入父设备的子设备链表所用,device对象中内嵌的kobject间的亲子关系也反映了设备的层次关系,因此/sys/devices下的目录结构与硬件设备的物理组织是匹配的。
引用计数器记录device对象的使用情况,它包含在kobject类型的kobj结构中,通过get_device()和put_device()函数分别增加和减少该计数器的值。
device_register()功能是往设备驱动程序模型中插入一个新的device对象,并自动地在/sys/devices目录下为其新建一个新的目录,相反的操作是device_unregister()。
通常,device对象被静态的嵌入到一个更大的描述符中,例如PCI设备是由数据结构pci_dev描述;该数据结构的dev字段就是一个device对象,其他字段是PCI总线所特有的。在PCI内核层上,当注册或注销设备时就会分别执行device_register()和device_unregister()。
2.3.2、设备驱动程序
struct device_driver { const char * name; /*设备驱动程序的名称*/ struct bus_type * bus; /*指向总线描述符的指针,总线连接所支持的设备*/ struct kobject kobj; struct klist klist_devices;/*该驱动程序控制的所有设备的device实例链表首部,链表中的各个设 备通过device->knode_driver彼此连接*/ struct klist_node knode_bus; /*用于连接一条公共总线上的所有设备*/ struct module * owner; /*标识实现设备驱动程序的模块,如果有的话*/ const char * mod_name; /* used for built-in modules */ struct module_kobject * mkobj; 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); /*设备恢复正常状态时所用的方法*/ };
device_driver对象包括四个方法,它们用于处理热插拔、即插即用和电源管理。当总线设备驱动程序发现一个可能由它处理的设备时就会调用probe方法;相应的函数将会探测该硬件,从而对该设备进行更进一步的检查。当移走一个可热插拔的设备时,驱动程序会调用remove方法;而驱动程序本身被卸载时,它所处理的每个设备也都会调用remove()方法。当内核必须改变设备的供电状态时,设备会调用shutdown、suspend和resume三个方法。
内嵌在描述符中的kobject类型的kobj所包含的引用计数器用于记录device_driver对象的使用情况,相应函数get_driver()和put_driver()分别增加和减少该计数器的值。
dirver_register()函数的功能是往设备驱动程序模型中插入一个新的device_driver对象,并自动地在sysfs文件系统下为其创建一个新的目录。相反的,driver_unregister()用于从设备驱动程序模型中移走一个设备驱动对象。
通常,device_driver对象静态地被嵌入到一个更大的描述符中,例如,PCI设备驱动程序是由数据结构pci_driver描述的,该结构的driver字段是一个device_driver对象,而其他字段是PCI总线所特有的。
2.3.3、总线
struct bus_type { const char * name; /*总线类型的名称,用于在sysfs中标识该总线*/ struct module * owner; struct kset subsys; /*设备和驱动都是个kobject,总线是个kset */ struct kset drivers; /*与总线关联的所有驱动程序的kobject集合*/ struct kset devices; /*与总线关联的所有设备的kobject集合*/ struct klist klist_devices;/*与总线关联的所有设备的链表*/ struct klist klist_drivers;/*与总线关联的所有驱动程序的链表*/ struct blocking_notifier_head bus_notifier; struct bus_attribute * bus_attrs; /*指向对象的指针,该对象包含总线属性和用于导出此属性到sysfs 文件系统的方法*/ struct device_attribute * dev_attrs; /*指向对象的指针,该对象包含设备属性和用于导出此属性到sysfs 文件系统的方法*/ struct driver_attribute * drv_attrs; /*指向对象的指针,该对象包含驱动程序属性和用于导出此属性到 sysfs文件系统的方法*/ int (*match)(struct device * dev, struct device_driver * drv);/*检验给定的设备驱动是否支持特定 设备的方法*/ int (*uevent)(struct device *dev, struct kobj_uevent_env *env);/**/ int (*probe)(struct device * dev);/*在有必要将驱动程序关联到设备时,用probe检测设备在系 统中是否真的存在*/ int (*remove)(struct device * dev);/*删除驱动程序和设备之间的关联*/ void (*shutdown)(struct device * dev); int (*suspend)(struct device * dev, pm_message_t state);/*保存硬件设备的上下文状态并改变设备供电 状态的方法*/ int (*suspend_late)(struct device * dev, pm_message_t state); int (*resume_early)(struct device * dev); int (*resume)(struct device * dev);/*改变供电状态和恢复硬件设备上下文的方法*/ unsigned int drivers_autoprobe:1; };
每个bus_type对象都包含一个内嵌的子系统:
l 存放在bus_subsys成员中的子系统把嵌入在bus_type对象中的所有子系统都集合在一起。bus_subsys子系统与目录/sys/bus是对应的,例如,有一个/sys/bus/pci目录与pci总线类型相对应。
l 每种总线的子系统分为2类kset:drivers和devices,分别对应于bus_type对象中的drivers和devices字段。
n 名为drivers的kset包含描述符device_driver,描述与该总线类型相关的所有设备驱动
n 名为devices的kset包含描述符device,描述与给定总线类型上连接的所与设备。因为设备的kobject目录已经出现咋/sys/devices下的sysfs中,所以每种总线子系统的devices目录存放了指向/sys/devices下目录的符号链接。
函数bus_for_each_drv()和bus_for_each_dev()分别用于循环扫描klist_drivers和klist_devices链表中所有元素。
当内核检查一个给定设备是否可以由给定的驱动处理时,执行match方法。对于连接设备的总线而言,即使其上每个设备的标识符都拥有一个特定的格式,实现match方法的函数通常也很简单,因为它只需要在所支持标识符的驱动程序表中搜索设备的描述符。在设备驱动程序模型中注册某个设备时会执行hotplug方法;实现函数应该通过环境变量把总线的具体信息传递给用户态程序,以通告一个新的可用设备。
2.3.4、类
每个类是由一个class对象描述的。所有的类对象都属于与/sys/class目录相对应的class_subsys的子系统。此外,每个类对象还包括一个内嵌的子系统,因此对于/sys/class/input目录,它就与设备驱动程序模型的input类相对应。/* * device classes */ struct class { const char * name; struct module * owner; struct kset subsys;//通过subsys.kobj.kset = &class_subsys把该类注册到class_sybsys中 struct list_head children; struct list_head devices; /*属于该类对象的class_dev描述符链表,每个描述符描述了一个属于该 类的单独逻辑设备*/ struct list_head interfaces; /*一个硬件设备可能包括几个不同的子设备,每个子设备都需要一个不同 的用户态接口*/ struct kset class_dirs; struct semaphore sem; /* locks both the children and interfaces lists */ struct class_attribute * class_attrs; struct class_device_attribute * class_dev_attrs; struct device_attribute * dev_attrs; int (*uevent)(struct class_device *dev, struct kobj_uevent_env *env); int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); void (*release)(struct class_device *dev); void (*class_release)(struct class *class); void (*dev_release)(struct device *dev); int (*suspend)(struct device *, pm_message_t state); int (*resume)(struct device *); };
每个类对象包括一个属于该类对象的class_dev描述符链表,每个描述符描述了一个属于该
类的单独逻辑设备。在class_device结构中包含一个dev字段,它指向一个设备描述符,因此一个逻辑设备总是对应于设备驱动模型中的一个给定设备,然而,可以存在多个class_device描述符对应同一个设备。事实上,一个硬件设备可能包括几个不同的子设备,每个子设备都需要一个不同的用户态接口。例如,声卡是一个硬件设备,通常包括一个DSP、一个混音器、一个游戏端口接口等等;每个子设备需要一个属于自己的用户态接口,因此sysfs文件系统中都有与它们相对应的目录。
注册类的时候,注意使用cls
int class_register(struct class * cls) { int error; pr_debug("device class '%s': registering\n", cls->name); INIT_LIST_HEAD(&cls->children); INIT_LIST_HEAD(&cls->devices); INIT_LIST_HEAD(&cls->interfaces); kset_init(&cls->class_dirs); init_MUTEX(&cls->sem); error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name); if (error) return error; cls->subsys.kobj.kset = &class_subsys; error = subsystem_register(&cls->subsys); if (!error) { error = add_class_attrs(class_get(cls)); class_put(cls); } return error; }
同一类中的设备驱动程序可以对用户态应用程序提供相同的功能;设备驱动程序模型中的类本质上是要提供一个标准的方法,从而为向用户态应用程序导出逻辑设备的接口。每个class_device中内嵌一个kobject,这是一个名为dev的属性(特殊文件)。该属性存放设备文件的主设备号和次设备号,通过它们可以访问相应的逻辑设备。
/** * struct class_device - class devices * @class: pointer to the parent class for this class device. This is required. * @devt: for internal use by the driver core only. * @node: for internal use by the driver core only. * @kobj: for internal use by the driver core only. * @groups: optional additional groups to be created * @dev: if set, a symlink to the struct device is created in the sysfs * directory for this struct class device. * @class_data: pointer to whatever you want to store here for this struct * class_device. Use class_get_devdata() and class_set_devdata() to get and * set this pointer. * @parent: pointer to a struct class_device that is the parent of this struct * class_device. If NULL, this class_device will show up at the root of the * struct class in sysfs (which is probably what you want to have happen.) * @release: pointer to a release function for this struct class_device. If * set, this will be called instead of the class specific release function. * Only use this if you want to override the default release function, like * when you are nesting class_device structures. * @uevent: pointer to a uevent function for this struct class_device. If * set, this will be called instead of the class specific uevent function. * Only use this if you want to override the default uevent function, like * when you are nesting class_device structures. */ struct class_device { struct list_head node; struct kobject kobj; struct class * class; /* required */ dev_t devt; /* dev_t, creates the sysfs "dev" */ struct device * dev; /* not necessary, but nice to have */ void * class_data; /* class-specific data */ struct class_device *parent; /* parent of this child device, if there is one */ struct attribute_group ** groups; /* optional groups */ void (*release)(struct class_device *dev); int (*uevent)(struct class_device *dev, struct kobj_uevent_env *env); char class_id[BUS_ID_SIZE]; /* unique to this class */ };
2.3.5、链表关系
父设备与子设备:![](http://hi.csdn.net/attachment/201108/6/0_1312630536pP5L.gif)
设备驱动程序与其驱动的设备
![](http://hi.csdn.net/attachment/201108/6/0_1312630652SQNq.gif)
总线与设备
![](http://hi.csdn.net/attachment/201108/6/0_1312630722Ra1A.gif)
总线与设备驱动程序
![](http://hi.csdn.net/attachment/201108/6/0_131263079879ei.gif)
l 总线与设备及设备驱动的kset关系
除了上面图示的设备驱动程序模型中总线、驱动和设备之间关系外,为在sysfs文件系统中显示信息,总线、驱动、设备中各自的kobject、kset也建立了关联。
相关文章推荐
- Linux PCI设备驱动程序开发 --- PCI 体系结构(一)
- 第十三章--I/O体系结构和设备驱动程序
- I/O体系结构和设备驱动程序
- Linux PCI设备驱动程序开发 --- PCI 体系结构(一)
- wdm - Win32设备驱动程序体系结构
- linux I-O体系结构和设备驱动程序
- I/O体系结构和设备驱动程序(一)
- I/O体系结构和设备驱动程序(二)
- I/O体系结构和设备驱动程序(五)
- I/O体系结构和设备驱动程序(四)
- linux下I/O体系结构和设备驱动程序
- I/O体系结构和设备驱动程序(五)
- I/O体系结构和设备驱动程序(六)
- 深入理解Linux内核-I/O体系结构和设备驱动程序
- 64位操作系统,mysql ODBC 驱动程序和应用程序之间的体系结构不匹配
- ODBC连接数据库提示“ [Microsoft][ODBC 驱动程序管理器] 在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配”的解决方法
- [Microsoft][ODBC 驱动程序管理器] 在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
- "在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配" 问题总结
- Linux内核代码笔记5----I/O体系结构和设备驱动模型
- xen块设备体系结构(6)