分类:
2011-12-26 00:24:39
原文地址:Linux设备驱动模型 作者:tq08g2z
Linux2.6提供了一些数据结构和辅助函数,他们为系统中所有的总线、设备以及设备驱动程序提供了一个统一的视图;这个框架被称为设备驱动模型。
Sysfs文件系统是一种特殊的文件系统,同样被设计成允许用户态应用程序访问内核数据结构的一种文件系统。它还提供关于内核数据结构的附加信息。Sysfs文件系统的目标是要展现设备驱动程序模型组件的层次关系。来看一下这个sysfs:
该文件系统的相应高层目录是:
block:
块设备,他们独立于所有连接的总线。
devices:所有被内核识别的硬件设备,依照连接他们的总线对其进行组织。
bus:系统中用于连接设备的总线。
drivers:在内核中注册的设备驱动程序。
class:系统中设备的类型(声卡、网卡、显卡等等),同一类可能包含有不同总线连接的设备,于是由不同的驱动程序驱动。
power:处理一些硬件设备电源状态的文件。
firmware:处理一些硬件设备的固件的文件。
Sysfs文件系统中所表示的设备驱动程序模型组件之间的关系就像目录和文件、符号链接之间的关系一样。Sysfs文件系统中普通文件的主要作用是表示驱动程序和设备的属性。
kobject
设备驱动程序模型的核心数据结构是一个普通的数据结构,叫做kobject,他与sysfs文件系统自然的绑定在一起:每个kobject对应于sysfs文件系统中的一个目录。
通常情况下,kobject不会被单独的使用,而是被嵌入一个叫做“容器”的更大的对象中,这就像是Linux内核里在许多数据结构中都能见到的list_head结构一样,而容器描述设备驱动程序模型中的组件。容器(通常为一个内核对象)的典型例子有总线、设备以及驱动程序的描述符。
将一个kobject嵌入容器中允许内核:
1、为容器保持一个引用计数。
2、维持容器的层次列表或组。
3、为容器的属性提供一中用户态查看的视图。
kobject、kset和kobj_type
每个kobject有kobject数据结构描述,其定义如下:
---------------------------------------------------------------------
include/linux/kobject.h
59 struct kobject {
60
const char *name;
61
struct list_head entry;
62
struct kobject *parent;
63
struct kset *kset;
64
struct kobj_type *ktype;
65
struct sysfs_dirent *sd;
66
struct kref kref;
67
unsigned int state_initialized:1;
68
unsigned int state_in_sysfs:1;
69
unsigned int state_add_uevent_sent:1;
70
unsigned int state_remove_uevent_sent:1;
71
unsigned int uevent_suppress:1;
72 };
---------------------------------------------------------------------
name:指向包含容器名称的字符串。
entry:用于kobject所插入的链表的指针,比如同一个kset的kobject会被连接起来。
parent:指向父kobject对象。
kset:指向包含的kset。
ktype:指向kobject的类型描述符。
sd:指向与kobject对应的sysfs目录项对象
kref:容器的引用计数。
ktype字段指向kobj_type对象,该对象描述了kobject的“类型”——本质上它描述的是包括kobject的容器的类型。其定义如下:
---------------------------------------------------------------------
include/linux/kobject.h
107
struct kobj_type {
108 void (*release)(struct kobject *kobj);
109 const struct sysfs_ops *sysfs_ops;
110 struct attribute **default_attrs;
111
};
---------------------------------------------------------------------
kobject对象的生存周期通常是难以预测的,创建者并不知道kobject的引用计数什么时候会变为0。为了异步的通知创建者,于是便产生了release方法,它在kobject对象的引用计数变为0时调用,通常release方法都是做一些清理工作。每一个kobject都需要一个kobj_type和它关联起来。
另外两个字段sysfs_ops和default_attrs控制如何在sysfs文件系统中来呈现这一类型的对象。
default_attrs指向一个任何注册为该类型的kobject都会自动拥有的默认属性的表。属性在sysfs中用一个文件表示。
kref字段是一个kref类型的结构,它仅包括一个refcount字段。这个字段就是kobject的引用计数器,但它也可以作为kobject的容器的引用计数器。为了安全,不能手动的释放kobject对象,系统专门提供了两个函数用于管理管理引用计数器:
struct
kobject *kobject_get(struct kobject *kobj);
void
kobject_put(struct kobject *kobj);
通过kset数据结构,可以将kobjects组织成一棵层次树。kset仅仅是一个相关的kobjects的集合。同一个kset中的kobject没有限制要求他们必须具有相同的ktype,但若他们不是则必须要小心。kset定义为:
---------------------------------------------------------------------
include/linux/kobject.h
137
/**
138 * struct kset - a set of kobjects of a
specific type, belonging to
139 * a specific subsystem.
140 * A kset defines a group of kobjects. They can be individually
141 * different "types" but overall
these kobjects all want to be grouped
142 * together and operated on in the same
manner. ksets are used to
143 * define the attribute callbacks and other
common events that happen
144 * to a kobject.
145 *
146 * @list: the list of all kobjects for this
kset
147 * @list_lock: a lock for iterating over the
kobjects
148 * @kobj: the embedded kobject for this kset
(recursion, isn't it
fun...)
149 * @uevent_ops: the set of uevent operations
for this kset. These
150 * are called whenever a kobject has something
happen to it so that
151 * the kset can add new environment variables,
or filter out the
152 * uevents if so desired.
153 */
154
struct kset {
155 struct list_head list;
156 spinlock_t list_lock;
157 struct kobject kobj;
158 const struct kset_uevent_ops
*uevent_ops;
159
};
---------------------------------------------------------------------
kset是sysfs中的一个子目录,可以展示与kset相关的kobjects。kobj字段是嵌入在kset数据结构中的kobject。它可以被设置为属于该kset的kobject的父kobject,即由kobject的parent指向它。如果属于一个kset的kobject在注册时没有指定父kobject,那么它将被添加进kset的目录。并不是一个kset的所有的成员都必须位于kset目录。如果在注册的时候明确的指定了父kobject的换,则注册到kset的kobject将会被添加在父kobject下。
注册kobject和kset
一般来说,如果想让kobject和kset出现在sysfs子树中,就必须首先注册它们。与kobject对应的目录总是出现在其父kobject的目录中。因此,sysfs子树的结构就描述了各种已注册的kobject之间以及各种容器对象之间的层次关系。
创建kobject的代码必须初始化它。通过强制调用kobject_init()函数
kobject的一些内部字段会被适当的设置:
void kobject_init(struct kobject *kobj,
struct kobj_type *ktype);
调用了kobject_init()之后则可以向sysfs注册kobject,这必须调用kobject_add()函数:
int __must_check kobject_add(struct
kobject *kobj,
struct kobject *parent,
const char *fmt, ...);
也可以通过调用kobject_init_and_add()在同一时刻完成kobject的初始化和添加进系统:
int __must_check
kobject_init_and_add(struct kobject *kobj,
struct kobj_type *ktype,
struct kobject *parent,
const char *fmt, ...);
kobject_del()用于将kobject的目录从sysfs文件系统移走:
void kobject_del(struct kobject *kobj);
为了更易于内核开发者进行开发,Linux也提供了另外一些函数:
int kset_register(struct kset *kset);
void kset_unregister(struct kset
*kset);
struct kset * kset_create_and_add(const
char *name,
const
struct kset_uevent_ops *u,
struct
kobject *parent_kobj);
但他们本质上是围绕kobject的封装。
许多kobject目录都包括称作属性(attribute)的普通文件。sysfs_create_file()函数接受kobject的地址和属性描述符作为它的参数,并在合适的目录中创建特殊文件:
int sysfs_create_file(struct kobject
*kobj,
const struct attribute *attr);
sysfs文件系统中所描述的对象间的其他关系可以通过符号链接的方式来建立,sysfs_create_link()函数为目录中其他kobject相关联的特定kobject创建一个符号链接:
int sysfs_create_link(struct kobject
*kobj, struct kobject *target,
const char *name);
设备驱动程序模型的组件
设备驱动程序模型建立在几个基本数据结构之上,这些结构描述了总线、设备、设备驱动等等。让我们来看一下他们。
设备
设备驱动模型中的每个设备是由一个device对象来描述的。其定义如下:
---------------------------------------------------------------------
include/linux/device.h
398
struct device {
399 struct device *parent;/* 指向父设备的指针 */
400
401 struct device_private *p;
402
403 struct kobject kobj; /* 内嵌的kobject结构 */
404 const char *init_name; /* initial name of the device */
405 struct device_type *type;
406
407
struct semaphore sem; /*
semaphore to synchronize calls to
408 * its
driver.
409 */
410
411 struct bus_type *bus; /* type of bus device is on */
412 struct device_driver *driver;/* which
driver has allocated this
413
device */
414 void
*platform_data; /* Platform specific
data, device
415
core doesn't touch it */
416 struct dev_pm_info power;
417
418
#ifdef CONFIG_NUMA
419 int
numa_node; /* NUMA node this
device is close to */
420
#endif
421 u64 *dma_mask; /* dma mask (if dma'able device) */
422 u64 coherent_dma_mask;/* Like
dma_mask, but for
423 alloc_coherent mappings as
424 not all hardware supports
425
64 bit addresses for
consistent
426 allocations such
descriptors. */
427
428 struct device_dma_parameters *dma_parms;
429
430 struct list_head dma_pools;
/* dma pools (if dma'ble) */
431
432 struct
dma_coherent_mem *dma_mem; /* internal for coherent mem
433
override */
434 /* arch specific additions */
435 struct dev_archdata archdata;
436
437 dev_t devt;
/* dev_t, creates the sysfs "dev" */
438
439 spinlock_t devres_lock;
440 struct list_head devres_head;
441
442 struct klist_node knode_class;
443 struct class *class;
444 const struct attribute_group
**groups; /* optional groups */
445
446 void
(*release)(struct device *dev);
447
};
---------------------------------------------------------------------
驱动程序
设备驱动程序模型中的每个驱动程序都可由device_driver对象描述。其定义为:
---------------------------------------------------------------------
include/linux/device.h
122
struct device_driver {
123 const char *name;
124 struct bus_type *bus;
125
126 struct
module *owner;
127 const char *mod_name;
/* used for built-in modules */
128
129 bool suppress_bind_attrs; /* disables
bind/unbind via sysfs */
130
131 int (*probe) (struct device *dev);
132 int
(*remove) (struct device *dev);
133 void (*shutdown) (struct device *dev);
134 int (*suspend) (struct device *dev,
pm_message_t state);
135 int (*resume) (struct device *dev);
136 const struct attribute_group **groups;
137
138 const struct dev_pm_ops *pm;
139
140 struct driver_private *p;
141
};
---------------------------------------------------------------------
总线
内核所支持的每一种总线类型都由一个bus_type对象描述。其定义为:
---------------------------------------------------------------------
include/linux/device.h
51 struct bus_type {
52 const
char *name;
53 struct
bus_attribute *bus_attrs;
54 struct
device_attribute *dev_attrs;
55 struct
driver_attribute *drv_attrs;
56
57 int
(*match)(struct device *dev, struct device_driver *drv);
58 int
(*uevent)(struct device *dev, struct kobj_uevent_env *env);
59 int
(*probe)(struct device *dev);
60 int
(*remove)(struct device *dev);
61 void
(*shutdown)(struct device *dev);
62
63 int
(*suspend)(struct device *dev, pm_message_t state);
64 int
(*resume)(struct device *dev);
65
66 const
struct dev_pm_ops *pm;
67
68 struct
bus_type_private *p;
69 };
---------------------------------------------------------------------
类
每个类是由一个class对象描述的。其定义为:
---------------------------------------------------------------------
include/linux/device.h
189
struct class {
190 const char *name;
191 struct module *owner;
192
193 struct class_attribute *class_attrs;
194 struct device_attribute *dev_attrs;
195 struct kobject *dev_kobj;
196
197 int (*dev_uevent)(struct device *dev, struct
kobj_uevent_env *env);
198 char *(*devnode)(struct device *dev, mode_t
*mode);
199
200 void (*class_release)(struct class *class);
201 void (*dev_release)(struct device *dev);
202
203 int (*suspend)(struct device *dev,
pm_message_t state);
204 int (*resume)(struct device *dev);
205
206 const struct dev_pm_ops *pm;
207
208 struct class_private *p;
209
};
---------------------------------------------------------------------
动态创建设备文件
Linux的设备模型子系统实在是一个非常复杂的系统。除了内核中各种复杂的对象之外,为了实现目标,比如热插拔,动态插入,设备文件的动态建立等特性的,在用户空间也需要有程序来配合,这就是udev工具集,用户态应用程序。当系统启动时,/dev目录是清空的,这是udev程序将扫描/sys/class子目录来寻找dev文件。对每一个这样的文件(主设备号和次设备号的组合表示一个内核所支持的逻辑设备文件),udev程序都会在/dev目录下为它创建一个相应的设备文件。udev也会根据配置文件分配一个文件名并创建一个符号链接。最后,/dev目录里只有存放了系统中内核所支持的所有设备的设备文件,而没有任何其他的文件。