Linux内核大讲堂之设备驱动的基石驱动模型(2)

上节我们分析到int kset_register(struct kset *k)函数中的kobject_add_internal(&k->kobj),我们接着分析,先唤起来一下大家的记忆,给出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_uevent(&k->kobj, KOBJ_ADD),这个东西是用来告诉用户空间有新朋友来啦,他叫什么什么名字。我们来看看它具体是怎么干活的。int kobject_uevent(struct kobject *kobj, enum kobject_action action){return kobject_uevent_env(kobj, action, NULL);}里面就一个函数: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;

/* 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;}/* 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;}

/* originating subsystem */if (uevent_ops && uevent_ops->name)subsystem = uevent_ops->name(kset, kobj);elsesubsystem = 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;

/* complete object path */devpath = kobject_get_path(kobj, GFP_KERNEL);if (!devpath) {retval = -ENOENT;goto exit;}

/* 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;}}

/* 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, __func__, retval);goto exit;}}

/* * 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;

/* 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;} elseretval = -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;

retval = call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);}

exit:kfree(devpath);kfree(env);return retval;}我们经过前面那些初始化等之后,最后调用了call_usermodehelper函数。static inline intcall_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait){struct subprocess_info *info;gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;

info = call_usermodehelper_setup(path, argv, envp, gfp_mask);if (info == NULL)return -ENOMEM;return call_usermodehelper_exec(info, wait);}凭着在linux上的编程经验我们都知道,这个东西按照传入的参数执行了一个程序。没错,这个就是用于与用户空间交互的重要角色。后们将会在后续合适的章节中详细讲述。

终究还是会从指缝中一滴一滴流淌干净。

Linux内核大讲堂之设备驱动的基石驱动模型(2)

相关文章:

你感兴趣的文章:

标签云: