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

时间:2021-03-30 17:53:43

转载请注明出处:http://blog.csdn.net/z2007b,请勿未经本人许可用于商业用途。版权归无为和尚所有。

上节我们分析到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);
 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;

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

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

exit:
 kfree(devpath);
 kfree(env);
 return retval;
}
我们经过前面那些初始化等之后,最后调用了call_usermodehelper函数。
static inline int
call_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上的编程经验我们都知道,这个东西按照传入的参数执行了一个程序。
没错,这个就是用于与用户空间交互的重要角色。后们将会在后续合适的章节中详细讲述。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/z2007b/archive/2011/05/04/6395333.aspx