您的位置:首页 > 其它

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、链表关系

父设备与子设备:



设备驱动程序与其驱动的设备



总线与设备



总线与设备驱动程序



l 总线与设备及设备驱动的kset关系

除了上面图示的设备驱动程序模型中总线、驱动和设备之间关系外,为在sysfs文件系统中显示信息,总线、驱动、设备中各自的kobject、kset也建立了关联。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: