`
cloudtech
  • 浏览: 4609787 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

LINUX设备驱动之设备模型二--kset

 
阅读更多

我们已经知道了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。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics