LINUX 设备驱动驱动程序模型的核心数据结构是 kobject , kobject 数据结构在 /linux/kobject.h 中定义:
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;
struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
每个 kobject 都有它的父节点 parent 、 kset 、 kobj_type 指针,这三者是驱动模型的基本结构, kset 是 kobject 的集合,在 /linux/kobject.h 中定义:
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
可以看到每个 kset 内嵌了一个 kobject ( kobj 字段),用来表示其自身节点,其 list 字段指向了所包含的 kobject的链表头。我们在后面的分析中将看到 kobject 如果没有指定父节点, parent 将指向其 kset 内嵌的 kobject 。
每个 kobject 都有它的 kobj_type 字段指针,用来表示 kobject 在文件系统中的操作方法, kobj_type 结构也在/linux/kobject.h 中定义:
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute ** default_attrs;
};
release 方法是在 kobject 释放是调用, sysfs_ops 指向 kobject 对应的文件操作, default_attrskobject 的默认属性,sysfs_ops 的将使用 default_attrs 属性(在后面的分析中我们将会看到)。
从上面的分析我们可以想象到 kobject 、 kset 、 kobj_type 的层次结构:
我们可以把一个 kobject 添加到文件系统中去(实际上是添加到其父节点所代表的 kset 中去),内核提供kobject_create_and_add() 接口函数:
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
int retval;
kobj = kobject_create();
if (!kobj)
return NULL;
retval = kobject_add(kobj, parent, "%s", name);
if (retval) {
printk(KERN_WARNING "%s: kobject_add error: %d/n",
__func__, retval);
kobject_put(kobj);
kobj = NULL;
}
return kobj;
}
kobject _create() 为要创建的 kobject 分配内存空间并对其初始化。
struct kobject *kobject_create(void)
{
struct kobject *kobj;
kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return NULL;
kobject_init(kobj, &dynamic_kobj_ktype);
return kobj;
}
kobject_init() 对 kobject 基本字段进行初始化,用输入参数设置 kobj_type 属性。
这里粘出代码以供参考:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!/n";
goto error;
}
if (kobj->state_initialized) {
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong./n", kobj);
dump_stack();
}
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
error:
printk(KERN_ERR "kobject (%p): %s/n", kobj, err_str);
dump_stack();
}
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1;
}
接着看 kobject_add() 函数:
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval;
if (!kobj)
return -EINVAL;
if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
"uninitialized object, something is seriously wrong./n",
kobject_name(kobj), kobj);
dump_stack();
return -EINVAL;
}
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
在上面的初始化中已把位变量设位 1
va_start(args, fmt) 和 va_end(args) 使用可变参数(可见参数用法不在这里分析),在 kobject_add_varg 中将把 fmt指向的内容赋给 kobject 的 name 字段。下面我们详细看看 kobject_add_varg 函数:
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs);
if (retval) {
printk(KERN_ERR "kobject: can not set name properly!/n");
return retval;
}
kobj->parent = parent;
return kobject_add_internal(kobj);
}
kobject_set_name_vargs(kobj, fmt, vargs) ,如果 kobj 的 name 字段指向的内容为空,则为分配一个内存空间并用fmt 指向的内容初始化,把地址赋给 kobj 的 name 字段。
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
const char *old_name = kobj->name;
char *s;
if (kobj->name && !fmt)
return 0;
kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
if (!kobj->name)
return -ENOMEM;
/* ewww... some of these buggers have '/' in the name ... */
while ((s = strchr(kobj->name, '/')))
s[0] = '!';
kfree(old_name);
return 0;
}
char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
{
unsigned int len;
char *p;
va_list aq;
va_copy(aq, ap);
len = vsnprintf(NULL, 0, fmt, aq);
va_end(aq);
p = kmalloc(len+1, gfp);
if (!p)
return NULL;
vsnprintf(p, len+1, fmt, ap);
return p;
}
继续 kobject_add_varg ()返回 kobject_add_internal(kobj) ,就是在这个函数理为 kobj 创建文件系统结构:
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
if (!kobj)
return -ENOENT;
if (!kobj->name || !kobj->name[0]) {
WARN(1, "kobject: (%p): attempted to be registered with empty "
"name!/n", kobj);
return -EINVAL;
}
检查 kobj 和它的 name 字段,不存在则返回错误信息。
parent = kobject_get(kobj->parent);
获得其父节点,并增加父节点的计数器, kobject 结构中的 kref 字段用于容器的计数, kobject_get 和 kobject_put分别增加和减少计数器,如果计数器为 0 ,则释放该 kobject , kobject_get 返回该 kobject 。
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
在这里我们可以看到,如果调用 kobject_create_and_add ()时参数 parent 设为 NULL ,则会去检查 kobj 的 kset是否存在,如果存在就会把 kset 所嵌套的 kobj 作为其父节点,并把 kobj 添加到 kset 中去。
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "<NULL>",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
打印一些调试信息,接着为 kobj 创建目录:
error = create_dir(kobj);
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;
/* be noisy on error issues */
if (error == -EEXIST)
printk(KERN_ERR "%s failed for %s with "
"-EEXIST, don't try to register things with "
"the same name in the same directory./n",
__func__, kobject_name(kobj));
else
printk(KERN_ERR "%s failed for %s (%d)/n",
__func__, kobject_name(kobj), error);
dump_stack();
} else
kobj->state_in_sysfs = 1;
return error;
}
如果创建不成功,则回滚上面的操作,成功的话则设置 kobj 的 state_in_sysfs 标志。
在看看 create_dir ()函数中具体创建了那些内容:
static int create_dir(struct kobject *kobj)
{
int error = 0;
if (kobject_name(kobj)) {
error = sysfs_create_dir(kobj);
if (!error) {
error = populate_dir(kobj);
if (error)
sysfs_remove_dir(kobj);
}
}
return error;
}
sysfs_create_dir ()先为 kobj 创建了一个目录文件
int sysfs_create_dir(struct kobject * kobj)
{
struct sysfs_dirent *parent_sd, *sd;
int error = 0;
BUG_ON(!kobj);
if (kobj->parent)
parent_sd = kobj->parent->sd;
else
parent_sd = &sysfs_root;
error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
if (!error)
kobj->sd = sd;
return error;
}
如果 kobj->parent 为 NULL ,就把 &sysfs_root 作为父节点 sd ,即 在/sys 下面创建结点。
然后调用 populate_dir :
static int populate_dir(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj);
struct attribute *attr;
int error = 0;
int i;
if (t && t->default_attrs) {
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
error = sysfs_create_file(kobj, attr);
if (error)
break;
}
}
return error;
}
得到 kobj 的 kobj_type ,历遍 kobj_type 的 default_attrs 并创建属性文件,文件的操作会回溯到sysfs_ops的show 和store 会调用封装了attribute 的kobj_attribute 结构的store 和show 方法(在后面的代码中将会分析)。
由于上面kobject_init(kobj, &dynamic_kobj_ktype) 用默认dynamic_kobj_ktype 作为 kobj_type 参数,而dynamic_kobj_ktype 的 default_attrs 为 NULL ,所以这里没有创建属性文件。
至此,我们已经知道了 kobject_create_and_add() 函数创建 kobject ,挂到父 kobject ,并设置其 kobj_type ,在文件系统中为其创建目录和属性文件等。
另外,如果我们已静态定义了要创建的 kobject ,则可以调用 kobject_init_and_add() 来注册 kobject ,其函数如下:
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
{
va_list args;
int retval;
kobject_init(kobj, ktype);
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
通过上面的分析我们很轻松就能理解这个函数。
内核提供注销 kobject 的函数是 kobject_del()
void kobject_del(struct kobject *kobj)
{
if (!kobj)
return;
sysfs_remove_dir(kobj);
kobj->state_in_sysfs = 0;
kobj_kset_leave(kobj);
kobject_put(kobj->parent);
kobj->parent = NULL;
}
删除 kobj 目录及其目录下的属性文件,清 kobj 的 state_in_sysfs 标志,把 kobj 从 kset 中删除,减少 kobj->parent 的计数并设其指针为空。
我们已经知道了kset 内嵌了kobject 来表示自身的节点,创建kset 就要完成其内嵌kobject ,注册kset 时会产生一个事件,事件而最终会调用uevent_ops 字段指向结构中的函数,这个事件是通过用户空间的hotplug 程序处理。下面我们一步一步分析。
内核同样提供了创建和注册kset 的函数kset_create_and_add()
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create (name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
输入参数有一个kset_uevent_ops 类型的结构变量,其结构包含三个函数指针,我们在后面的分析到这三个函数在什么时候被调用,kset_uevent_ops 结构定义如下:
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
继续看上面的函数,先调用kset_create () 创建一个kset ,接着调用kset_register() 注册它。
static struct kset *kset_create(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int retval;
kset = kzalloc(sizeof(*kset), GFP_KERNEL);
if (!kset)
return NULL;
retval = kobject_set_name(&kset->kobj, name);
if (retval) {
kfree(kset);
return NULL;
}
kset->uevent_ops = uevent_ops;
kset->kobj.parent = parent_kobj;
/*
* The kobject of this kset will have a type of kset_ktype and belong to
* no kset itself. That way we can properly free it when it is
* finished being used.
*/
kset->kobj.ktype = &kset_ktype;
kset->kobj.kset = NULL;
return kset;
}
为kset 分配内存,如我们上面分析,初始化了kset 内嵌的kobject (这里还未将kobject 注册到文件系统),另外用输入参数初始化kset 的uevent_ops 字段。
接着看kset 的注册函数kset_register() :
int kset_register(struct kset *k)
{
int err;
if (!k)
return -EINVAL;
kset_init(k);
err = kobject_add_internal(&k->kobj);
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
在这里终于看到调用kobject_add_internal ()将kset 内嵌的kobject 注册到文件系统,这个函数我们在上面已经分析。
我们上面说到注册kset 会产生一个事件,就是在这里调用了kobject_uevent(&k->kobj, KOBJ_ADD)
kobject_uevent() 在/lib/ kobject_uevent.c 中:
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
转入kobject_uevent_env() :
这个函数比较长,我们分段分析
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action];
const char *devpath = NULL;
const char *subsystem;
struct kobject *top_kobj;
struct kset *kset;
struct kset_uevent_ops *uevent_ops;
u64 seq;
int i = 0;
int retval = 0;
pr_debug("kobject: '%s' (%p): %s/n",
kobject_name(kobj), kobj, __func__);
/* search the kset we belong to */
top_kobj = kobj;
while (!top_kobj->kset && top_kobj-> parent)
top_kobj = top_kobj->parent;
if (!top_kobj->kset) {
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
"without kset!/n", kobject_name(kobj), kobj,
__func__);
return -EINVAL;
}
kset = top_kobj->kset;
uevent_ops = kset-> uevent_ops;
如果如果kobj 的kset 和parent 字段都不存在,说明找不到所属kset ,也就没有uevent_ops ,不能产生事件,返回错误信息;相反则找到了存在kset 的kobj 或父kobject (依次往上找),并赋值给uevent_ops 。
/* skip the event, if uevent_suppress is set*/
if (kobj-> uevent_suppress) {
pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
"caused the event to drop!/n",
kobject_name(kobj), kobj, __func__);
return 0;
}
如果设置了uevent_suppress 字段,说明不希望产生事件,忽略事件正确返回。注意驱动程序将在适当的地方产生改事件。
/* skip the event, if the filter returns zero. */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject: '%s' (%p): %s: filter function "
"caused the event to drop!/n",
kobject_name(kobj), kobj, __func__);
return 0;
}
如果uevent_ops->filter 返回0 ,同样忽略事件正确返回。
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);
else
subsystem = kobject_name(&kset->kobj);
if (!subsystem) {
pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
"event to drop!/n", kobject_name(kobj), kobj,
__func__);
return 0;
}
获得子系统的名称,不存在则返回。
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
分配一个kobj_uevent_env 结构内存,用于存放环境变量的值。
/* complete object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
获得引发事件的kobject 在sysfs 中的路径。
/* default keys */
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, "%s", envp_ext[i]);
if (retval)
goto exit;
}
}
调用add_uevent_var()kobj_uevent_env 填充action_string,kobject 路径, 子系统名称以及其他指定环境变量。
/* let the kset specific function add its stuff */
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
"%d/n", kobject_name(kobj), kobj,
__FUNCTION__, retval);
goto exit;
}
}
调用uevent_ops 的uevent 函数,编程人员可在此函数中实现自定义的功能。
/*
* Mark "add" and "remove" events in the object to ensure proper
* events to userspace during automatic cleanup. If the object did
* send an "add" event, "remove" will automatically generated by
* the core, if not already done by the caller.
*/
if (action == KOBJ_ADD)
kobj->state_add_uevent_sent = 1;
else if (action == KOBJ_REMOVE)
kobj->state_remove_uevent_sent = 1;
设置KOBJ_ADD 和KOBJ_REMOVE 的标志。
/* we will send an event, so request a new sequence number */
spin_lock(&sequence_lock);
seq = ++uevent_seqnum;
spin_unlock(&sequence_lock);
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
if (retval)
goto exit;
#if defined(CONFIG_NET)
/* send netlink message */
if (uevent_sock) {
struct sk_buff *skb;
size_t len;
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
char *scratch;
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i < env->envp_idx; i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env->envp[i]);
}
NETLINK_CB(skb).dst_group = 1;
retval = netlink_broadcast(uevent_sock, skb, 0, 1,
GFP_KERNEL);
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS)
retval = 0;
} else
retval = -ENOMEM;
}
#endif
/* call uevent_helper, usually only enabled during early boot */
if (uevent_helper[0]) {
char *argv [3];
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
retval = add_uevent_var(env,
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
添加HOME 和PATH 环境变量。
retval = call_usermodehelper(argv[0], argv,
env->envp, UMH_WAIT_EXEC);
}
exit:
kfree(devpath);
kfree(env);
return retval;
}
调用hotplug 函数。
看一下kset_unregister ()
void kset_unregister (struct kset *k)
{
if (!k)
return;
kobject_put(&k-> kobj);
}
减少其内嵌的kobj 计数,为0 则释放其内存空间。
已经分析完kobject 和kset ,linux 的设备模型就是基于这两个数据结构的,在此基础上,后续将分析设备模型中的device 、driver 、和bus 。
在清楚了 kobject 之后,就可以继续分析 device 、 driver 、 bus 了,这三者是设备驱动程序的基本数据结构。
我们可以这样理解,内核用 device 来表示各种设备,然后用 driver 来表示它的驱动,而设备有很多种,也属于相同类型或不同类型,而其对应的驱动可能同时也是另外一个设备的驱动,为了管理这些设备和驱动,就引入了总线 bus_type ,总线上有两个集合(也可以理解为两条链,如上图中的 bus ),分别用来存放该总线类型的设备和驱动,当添加一个设备时就将设备添加到总线的设备集合(图中操作 2 ),同时可能会到驱动集合去匹配适合它的驱动(图中操作 3 ,在此之前 device 和 driver 没有挂钩),如何找到了就会将它们联系起来(图中操作 6 )。同样注册一个驱动,就会把它挂到相应总线类型的驱动集合里(图中操作 1 ),同时去设备集合中找出它锁驱动的设备(图中操作 4 ,在此之前 device 和 driver 没有挂钩),如果找到就把设备链接到它支持的设备链表上(图中操作 6 )。
下面我们进入到代码中去:
struct bus_type 结构定义如下:
struct bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
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);
const struct dev_pm_ops *pm;
struct bus_type_private *p;
};
较之先前一些内核版本, bus_type 把部分私有字段封装到 bus_type_private 类型结构里:
struct bus_type_private {
struct kset subsys;
struct kset *drivers_kset;
struct kset *devices_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
字段 klist_devices 和 klist_drivers 分别表示挂在 bus_type 上的驱动和设备链表, bus_type 的其他字段和函数指针将在分析过程中说明。
首先我们要为设备和驱动注册一个总线类型:
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
分配私有区域的内存空间,并将其关联
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
初始化回调函数
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
if (retval)
goto out;
这里我们看到了 subsys 用来表示它的文件系统,可以回想上一节 kset 的注册。
这个 bus_kset 是系统启动是创建的,系统 init 进程 kernel_init() 中调用 do_basic_setup() ,其中调用 driver_init(), 其中调用的 buses_init() ,如下
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}
从而知道创建的文件系统目录在 /sys/bus 下。
static struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{
struct kobj_type *ktype = get_ktype(kobj);
if (ktype == &bus_ktype)
return 1;
return 0;
}
继续 bus_register ()中的代码:
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
bus_create_file ()如下 :
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
int error;
if (bus_get(bus)) {
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
} else
error = -EINVAL;
return error;
}
用 bus_attr_uevent 创建了 bus->p->subsys.kobj 的属性文件,由上面的赋值知道其读写操作在 bus_ktype 的sysfs_ops ,其定义如下:
static struct kobj_type bus_ktype = {
.sysfs_ops = &bus_sysfs_ops,
};
static struct sysfs_ops bus_sysfs_ops = {
.show = bus_attr_show,
.store = bus_attr_store,
};
static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->show)
ret = bus_attr->show(bus_priv->bus, buf);
return ret;
}
static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->store)
ret = bus_attr->store(bus_priv->bus, buf, count);
return ret;
}
由上面的程序可以看出文件的读写操作最终会回到 struct bus_attribute &bus_attr_uevent 的 show 和 store 方法。
bus_attr_uevent 是通过宏定义的:
#define __ATTR(_name,_mode,_show,_store) { /
.attr = {.name = __stringify(_name), .mode = _mode }, /
.show = _show, /
.store = _store, /
}
#define BUS_ATTR(_name, _mode, _show, _store) /
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
其 show 方法为 NULL ,说明不可读。
static ssize_t bus_uevent_store(struct bus_type *bus,
const char *buf, size_t count)
{
enum kobject_action action;
if (kobject_action_type(buf, count, &action) == 0)
kobject_uevent(&bus->p->subsys.kobj, action);
return count;
}
在用户空间可以控制事件的发生 , 如 echo add > event 将产生一个 add 的事件。
接着继续 bus_register ()中的代码:
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
创建两个 kset ,其内嵌 object 的 parent 都指向 priv->subsys.kobj ,说明其文件系统在 bus 所在目录下。由上回分析中知道 kset_create_and_add() 时其内嵌 kobj 的 ktype 指向 kset_ktype ,而这里没有输入参数的 uevent_ops 为NULL ,则会以 priv->subsys.kobj->kset->uevent_ops 来产生事件,我们上面分析中知道这个 uevent_ops 为bus_uevent_ops ,其 filter 会比较 kobj 的 ktype 是不是 &bus_ktype ,而这里是 &kset_ktype ,所以这里是忽略了事件。
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
初始化总线上设备和驱动的链表。
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
add_probe_files() 函数如下:
static int add_probe_files(struct bus_type *bus)
{
int retval;
retval = bus_create_file(bus, &bus_attr_drivers_probe);
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
if (retval)
bus_remove_file(bus, &bus_attr_drivers_probe);
out:
return retval;
}
同上面创建 bus_attr_uevent 属性一样创建 bus_attr_drivers_probe 和 bus_attr_drivers_autoprobe 属性文件。粘出属性的代码:
static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
show_drivers_autoprobe, store_drivers_autoprobe);
bus_attr_drivers_autoprobe 的 show 指向 NULL ,说明其改文件不可写。
static ssize_t store_drivers_probe(struct bus_type *bus,
const char *buf, size_t count)
{
struct device *dev;
dev = bus_find_device_by_name(bus, NULL, buf);
if (!dev)
return -ENODEV;
if (bus_rescan_devices_helper(dev, NULL) != 0)
return -EINVAL;
return count;
}
将用户输 ( 在用户空间 ) 和的设备名称对应的设备与驱动匹配一次。
static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%d/n", bus->p->drivers_autoprobe);
}
在用户空间可以打印 drivers_autoprobe 的值 , 如 cat drivers_autoprobe
static ssize_t store_drivers_autoprobe(struct bus_type *bus,
const char *buf, size_t count)
{
if (buf[0] == '0')
bus->p->drivers_autoprobe = 0;
else
bus->p->drivers_autoprobe = 1;
return count;
}
在用户空间可以改变 drivers_autoprobe 的值 , 如 echo 1 > drivers_autoprobe
继续分析 bus_register ()中的代码:
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
bus_add_attrs() 如下:
static int bus_add_attrs(struct bus_type *bus)
{
int error = 0;
int i;
if (bus->bus_attrs) {
for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
error = bus_create_file(bus, &bus->bus_attrs[i]);
if (error)
goto err;
}
}
done:
return error;
err:
while (--i >= 0)
bus_remove_file(bus, &bus->bus_attrs[i]);
goto done;
}
如果 bus->bus_attrs 存在,则同样为其创建属性文件。
pr_debug("bus: '%s': registered/n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
kfree(bus->p);
out:
bus->p = NULL;
return retval;
}
bus_register() 分析完了,总结一下,它注册了一个总线类型,创建对应的文件系统(包括目录和属性),初始化总线上的驱动和设备,这样我们就可以通过内核提供的函数往总线上注册设备和驱动了。
接上一篇文章,在往总线注册注册设备前要先创建device,我们可以静态的定义device结构变量,然后调用device_register()将其注册,或者通过内核提供的device_create()接口函数创建和注册device。先看看device的数据结构定义:
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
struct device_type *type;
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 *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) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
dev_t devt; /* dev_t, creates the sysfs "dev" */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
和总线类型一样,device也把部分私有数据封装到device_private结构中:
struct device_private {
struct klist klist_children;
struct klist_node knode_parent;
struct klist_node knode_driver;
struct klist_node knode_bus;
void *driver_data;
struct device *device;
};
创建device的函数device_create()如下:
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
调用device_create_vargs():
struct device *device_create_vargs(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt,
va_list args)
{
struct device *dev = NULL;
int retval = -ENODEV;
if (class == NULL || IS_ERR(class))
goto error;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata);
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
if (retval)
goto error;
retval = device_register(dev);
if (retval)
goto error;
return dev;
error:
put_device(dev);
return ERR_PTR(retval);
}
该函数为创建的device分配内存空间,根据输入参数和一些默认值对其初始化,然后调用device_register将其注册到相应的总线上。
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
device_initialize()先对dev初始化:
void device_initialize(struct device *dev)
{
dev-> kobj.kset = devices_kset;
为其内嵌kobj的kset赋值,类似bus_kset,devices_kset也是系统启动是创建的:
int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
dev_kobj = kobject_create_and_add("dev", NULL);
if (!dev_kobj)
goto dev_kobj_err;
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
if (!sysfs_dev_block_kobj)
goto block_kobj_err;
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
if (!sysfs_dev_char_kobj)
goto char_kobj_err;
return 0;
char_kobj_err:
kobject_put(sysfs_dev_block_kobj);
block_kobj_err:
kobject_put(dev_kobj);
dev_kobj_err:
kset_unregister(devices_kset);
return -ENOMEM;
}
对应sysfs中的/sys/devices、/sys/devices/block、/sys/devices/char。
接着device_initialize()中的代码:
kobject_init(&dev-> kobj, &device_ktype);
初始化dev内嵌的kobj
INIT_LIST_HEAD(&dev->dma_pools);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
初始化一些其它的字段。
device_init_wakeup(dev, 0);
device_pm_init(dev);
电源管理的一些初始化。
set_dev_node(dev, -1);
设置numa。
}
初始化dev后调用device_add() 往相应总线上添加设备。分段分析这个函数:
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
增加dev的计数,初始化其私有结构,实际上面初始化调用的dev_set_drvdata(dev, drvdata)里边已经包含了device_private_init(),但如果是直接调用device_register()而不是device_create()时,必须在这里再初始化一次,显然第一个调用device_private_init()是不必要id。所以在这里才分析device_private_init(),函数如下:
int device_private_init(struct device *dev)
{
dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
if (!dev->p)
return -ENOMEM;
为其分配内存。
dev->p->device = dev;
关联到dev。
klist_init(&dev->p-> klist_children, klist_children_get,
klist_children_put);
初始化klist_children
return 0;
}
接着device_add()函数。
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
if (!dev_name(dev))
goto name_error;
pr_debug("device: '%s': %s/n", dev_name(dev), __func__);
用dev->init_name设置dev->kobj.name,而后dev->init_name指向NULL,如果dev->kobj.name则跳到name_error。
parent = get_device(dev->parent);
增加dev->parent-> kobj的计数。
setup_parent(dev, parent);
看下这个函数:
static void setup_parent(struct device *dev, struct device *parent)
{
struct kobject *kobj;
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
}
static struct kobject *get_device_parent(struct device *dev,
struct device *parent)
{
int retval;
if (dev->class) {
struct kobject *kobj = NULL;
struct kobject *parent_kobj;
struct kobject *k;
/*
* If we have no parent, we live in "virtual".
* Class-devices with a non class-device as parent, live
* in a "glue" directory to prevent namespace collisions.
*/
if (parent == NULL)
parent_kobj = virtual_device_parent(dev);
else if (parent->class)
return &parent->kobj;
else
parent_kobj = &parent->kobj;
/* find our class-directory at the parent and reference it */
spin_lock(&dev->class->p->class_dirs.list_lock);
list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
if (k->parent == parent_kobj) {
kobj = kobject_get(k);
break;
}
spin_unlock(&dev->class->p->class_dirs.list_lock);
if (kobj)
return kobj;
/* or create a new class-directory at the parent device */
k = kobject_create();
if (!k)
return NULL;
k->kset = &dev->class->p->class_dirs;
retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
if (retval < 0) {
kobject_put(k);
return NULL;
}
/* do not emit an uevent for this simple "glue" directory */
return k;
}
if (parent)
return &parent->kobj;
return NULL;
}
dev-> kobj.parent的设置如下:如果dev->parent,则将dev->parent->kobj赋给它,否则如果device_create() 的class参数不为为NULL时,则通过virtual_device_parent(dev)获得其parent,否则指向NULL,在调用kobject_add()时使其kset字段的kset内嵌的object。
继续device_add()函数
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
将dev->kobj添加到系统中。
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
如果定义了platform_notify()函数,则调用它,在drivers/base/core.c中有:
int (*platform_notify)(struct device *dev) = NULL;
说明默认将不会调用它。
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
建立dev的uevent_attr属性文件,这里的uevent_attr定义如下:
static struct device_attribute uevent_attr =
__ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
show_uevent()函数如下:
static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct kobject *top_kobj;
struct kset *kset;
struct kobj_uevent_env *env = NULL;
int i;
size_t count = 0;
int retval;
/* search the kset, the device belongs to */
top_kobj = &dev->kobj;
while (!top_kobj->kset && top_kobj->parent)
top_kobj = top_kobj->parent;
if (!top_kobj->kset)
goto out;
kset = top_kobj->kset;
if (!kset->uevent_ops || !kset->uevent_ops->uevent)
goto out;
/* respect filter */
if (kset->uevent_ops && kset->uevent_ops->filter)
if (!kset->uevent_ops->filter(kset, &dev->kobj))
goto out;
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/* let the kset specific function add its keys */
retval = kset->uevent_ops->uevent(kset, &dev->kobj, env);
if (retval)
goto out;
/* copy keys to file */
for (i = 0; i < env->envp_idx; i++)
count += sprintf(&buf[count], "%s/n", env->envp[i]);
out:
kfree(env);
return count;
}
改函数表明如果读dev的uevent则会显示dev-> kobj所属kset产生的环境变量。
store_uevent定义如下:
static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
enum kobject_action action;
if (kobject_action_type(buf, count, &action) == 0) {
kobject_uevent(&dev->kobj, action);
goto out;
}
dev_err(dev, "uevent: unsupported action-string; this will "
"be ignored in a future kernel version/n");
kobject_uevent(&dev->kobj, KOBJ_ADD);
out:
return count;
}
从程序可以看出对这个文件的作用写则会产生相应的事件。如果输入的字符串不合法,则就会产生一个add事件。
接着device_add()函数中的代码:
if (MAJOR(dev->devt)) {
error = device_create_file(dev, & devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
如果dev->devt的煮设备号不为空,则创建devt_attr属性文件和连接文件,devt_attr定义如下
static struct device_attribute devt_attr =
__ATTR(dev, S_IRUGO, show_dev, NULL);
该属性的store函数为NULL,说明为只读。读操作函数show_dev():
static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
char *buf)
{
return print_dev_t(buf, dev->devt);
}
#define print_dev_t(buffer, dev) /
sprintf((buffer), "%u:%u/n", MAJOR(dev), MINOR(dev))
读操作为打印dev的设备号。
static int device_create_sys_dev_entry(struct device *dev)
{
struct kobject *kobj = device_to_dev_kobj(dev);
int error = 0;
char devt_str[15];
if (kobj) {
format_dev_t(devt_str, dev->devt);
error = sysfs_create_link(kobj, &dev->kobj, devt_str);
}
return error;
}
static struct kobject *device_to_dev_kobj(struct device *dev)
{
struct kobject *kobj;
if (dev->class)
kobj = dev->class->dev_kobj;
else
kobj = sysfs_dev_char_kobj;
return kobj;
}
如果定义了dev->class,则在相应的目录下建立链接文件,否则默认在/sys/devices/char下建立链接文件。
接着device_add ()函数:
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
同样在class子系统下建立链接文件、class->dev_attrs属性文件、group等。
继续device_add()中的代码:
error = bus_add_device(dev);
if (error)
goto BusError;
在对应总线目录下的 device 目录下创建几个到 device 的链接文件。
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
添加 power 文件。
/* Notify clients of device addition. This call must come
* after dpm_sysf_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
调用 bus 的回调函数。
kobject_uevent(&dev->kobj, KOBJ_ADD);
产生一个 add 事件。
bus_probe_device(dev);
这个函数将匹配已经注册到总线的驱动程序,如下:
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret;
if (bus && bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
}
只有 bus->p->drivers_autoprobe 设置为 1 是才会去匹配, device_attach() 如下:
int device_attach(struct device *dev)
{
int ret = 0;
down(&dev->sem);
if (dev->driver) {
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
pm_runtime_get_noresume(dev);
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_runtime_put_sync(dev);
}
up(&dev->sem);
return ret;
}
如果已指定了 dev->driver ,则直接在相应的 driver 目录下建立链接文件,将驱动和设备绑定。
int device_bind_driver(struct device *dev)
{
int ret;
ret = driver_sysfs_add(dev);
if (!ret)
driver_bound(dev);
return ret;
}
driver_bound() 函数如下:
static void driver_bound(struct device *dev)
{
if (klist_node_attached(&dev->p->knode_driver)) {
printk(KERN_WARNING "%s: device %s already bound/n",
__func__, kobject_name(&dev->kobj));
return;
}
pr_debug("driver: '%s': %s: bound to device '%s'/n", dev_name(dev),
__func__, dev->driver->name);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
}
通知总线绑定驱动,将设备添加到驱动的设备链表。
否则调用 bus_for_each_drv() 匹配总线上的驱动:
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{
struct klist_iter i;
struct device_driver *drv;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_drivers, &i,
start ? &start->p->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}
历遍总线上的驱动,每次都调用回调函数 fn() (这里是 __device_attach ),如果 fn() 返回 1 则匹配成功, __device_attach() 如下:
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
if (!driver_match_device(drv, dev))
return 0;
return driver_probe_device(drv, dev);
}
driver_match_device() 如下:
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus ->match(dev, drv) : 1;
}
用 drv->bus 的 match 方法进行匹配,如果成功就会继续调用 driver_probe_device() :
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s/n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_runtime_put_sync(dev);
return ret;
}
进行一下验证和同步后调用 really_probe() 最终详细的匹配:
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s/n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s/n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d/n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
先假定 dev 的驱动为 drv ,然后如果总线闪的 probe 存在,则调用它,否则调用 drv 的 probe ()函数,返回 0 则匹配成功,调用 driver_bound() 进行设备和驱动的绑定。 driver_bound() 函数在上面已经分析。
接着 device_add() 函数中的代码:
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->class_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
Dev 所属 class 的一些操作。
done:
put_device(dev);
return error;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
最后是一下除错撤销处理。
}
至此,已经详细的分析了创建和注册设备的函数。
内核撤销设备注册的函数为 device_unregister() ,这里不再分析。
内核提供注册驱动的函数为 driver_register() :
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods/n", drv->name);
验证 driver 的包含了需要的方法,并打印信息。
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting.../n", drv->name);
return -EBUSY;
}
如果驱动已注册则返回 -EBUSY 。
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
bus_add_driver() 代码如下:
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s/n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed/n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed/n",
__func__, drv->name);
}
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
out_unregister:
kfree(drv->p);
drv->p = NULL;
kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
简单的分析一下: 为 drv 分配内存并初始化,创建文件系统中相应的目录、属性文件和链接,调用driver_attach() 匹配总线上的设备,将驱动注册到 bus 上,产生一个 kobject_uevent() ADD 事件。详细的过程请参考内核源码。
同样内核提供 driver_unregister() 函数撤销驱动注册,这里不再分析。
至此,已经清楚了设备驱动模型的 device 、 driver 和 bus ,在此基础上就可以轻松的去分析各个模块的驱动程序了 ^_^!