Linux USB - uevent

来源:互联网 发布:淘宝认证信息可以改吗 编辑:程序博客网 时间:2024/06/08 05:34

Kernel version: 3.10.5

本来是想看USB CORE的。当看到注册bus的时候:

struct bus_type usb_bus_type = {
    .name =        "usb",
    .match =    usb_device_match,
    .uevent =    usb_uevent,
};
match 已经是很脸熟了。突然觉得那个uevent 怎么看都不顺眼。看了一下uevent 的实现。就调用add_uevent_var,把设备的厂商,类型等写到struct kobj_uevent_env里面去。

那么我就有两个问题了。

1. 这个uevent是什么。

2. 这个函数什么时候会被调用。

 

 1. uevent是什么。

我首先把压箱底的LDD3拿出来,翻到设备模型那一张。竟然没有uevent。但是竟然没有讲到这个。只能一边baidu一边看code了。

2. uevent ==  hotplug?

然后我在code里面找到了这个结构体

struct kset_uevent_ops {int (* const filter)(struct kset *kset, struct kobject *kobj);const char *(* const name)(struct kset *kset, struct kobject *kobj);int (* const uevent)(struct kset *kset, struct kobject *kobj,      struct kobj_uevent_env *env);};
和LDD3里面的

struct kset_hotplug_ops {int (*filter)(struct kset *kset, struct kobject *kobj);char *(*name)(struct kset *kset, struct kobject *kobj);int (*hotplug)(struct kset *kset, struct kobject *kobj,char **envp, int num_envp, char *buffer,int buffer_size);};

长得真的很像。看到这里,基本能猜到uevent实现的功能应该是和hotplug是一样的。

3. uevent的概念

uevent是sysfs向用户空间发出的消息。;无论何时一个 kobject 被创建或销毁就产生它们. 这样事件被产生, 例如, 当一个数字摄像头使用一个 USB 线缆插入, 当一个用户切换控制台模式, 或者当一个磁盘被重新分区.

比如在bus_register 中,会调用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是发出这个消息的kobject,KOBJ_ADD 是消息的类型。目前

比如,device_add函数中,会调用kobject_uevent(&dev->kobj, KOBJ_ADD); 这里kobj是发消息的kobj,KOBJ_ADD是发出的事件。目前有6种类型的event。

enum kobject_action {

       KOBJ_ADD,

       KOBJ_REMOVE,

       KOBJ_CHANGE,

       KOBJ_MOVE,

       KOBJ_ONLINE,

       KOBJ_OFFLINE,

       KOBJ_MAX

};

4. 发出事件后内核如何反应?

 kobject_uevent 只是一个wrapper。

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;const struct kset_uevent_ops *uevent_ops;int i = 0;int retval = 0;#ifdef CONFIG_NETstruct uevent_sock *ue_sk;#endifpr_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;mutex_lock(&uevent_sock_mutex);/* we will send an event, so request a new sequence number */retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);if (retval) {mutex_unlock(&uevent_sock_mutex);goto exit;}#if defined(CONFIG_NET)/* send netlink message */list_for_each_entry(ue_sk, &uevent_sock_list, list) {struct sock *uevent_sock = ue_sk->sk;struct sk_buff *skb;size_t len;if (!netlink_has_listeners(uevent_sock, 1))continue;/* 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_filtered(uevent_sock, skb,    0, 1, GFP_KERNEL,    kobj_bcast_filter,    kobj);/* ENOBUFS should be handled in userspace */if (retval == -ENOBUFS || retval == -ESRCH)retval = 0;} elseretval = -ENOMEM;}#endifmutex_unlock(&uevent_sock_mutex);/* call uevent_helper, usually only enabled during early boot */if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {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;}
http://write.blog.csdn.net/postedit/44457573
1. 检查这个kobject 的 kset是不是有效,如果为空,那么就顺着parent往上找,一直找到一个kset为止。

2. 调用kset 中uevent_ops  的filter。如filter返回0,那么这个event就不会再往上传了。否则就继续。

struct kset {struct list_head list;spinlock_t list_lock;struct kobject kobj;const struct kset_uevent_ops *uevent_ops;};

struct kset_uevent_ops {int (* const filter)(struct kset *kset, struct kobject *kobj);const char *(* const name)(struct kset *kset, struct kobject *kobj);int (* const uevent)(struct kset *kset, struct kobject *kobj,      struct kobj_uevent_env *env);};

3. 然后通过uevent_ops->name 取得subsystem的名字,如果这个成员为空。那么就直接取这个kobject的名字。

4. 给struct kobj_uevent_env 分配内存。然后开始调用add_uevent_var 给 它添加内容。

5. 调用uevent_ops->uevent

6. 后面用一个 COFIG_NET 包起来的code。从注释上来看是netlink相关。暂时就不把摊子铺太大了。

7. 最后调用uevent_helper,EVENT 被传到用户空间:这一点的细节也以后再补上。

5. kset ->uevent_ops

对于USB 设备来说,这个kset 就是bus_kset,它的uevent_ops 就是

static const struct kset_uevent_ops bus_uevent_ops = {.filter = bus_uevent_filter,};
只有一个filter. 这个filter里面只是判断产生event的kobject是不是属于usb  ktype.

6. usb _uevent  是在哪里调用的。

前面以bus_register  为例,分析KOBJ_ADD产生后内核会做什么动作。那么还剩下一个问题。usb_uevent 是在哪里调用的呢。用关键字在code中搜索发现dev_uevent 中会有这么一行。

if (dev->bus && dev->bus->uevent) {retval = dev->bus->uevent(dev, env);if (retval)pr_debug("device: '%s': %s: bus uevent() returned %d\n", dev_name(dev), __func__, retval);}

这里在调用一个bus的uevent。不就是usb_uevent 吗。

顺着这条线一直追下去。

static const struct kset_uevent_ops device_uevent_ops = {.filter =dev_uevent_filter,.name =dev_uevent_name,.uevent =dev_uevent,};

devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);

void device_initialize(struct device *dev){dev->kobj.kset = devices_kset;kobject_init(&dev->kobj, &device_ktype);INIT_LIST_HEAD(&dev->dma_pools);mutex_init(&dev->mutex);lockdep_set_novalidate_class(&dev->mutex);spin_lock_init(&dev->devres_lock);INIT_LIST_HEAD(&dev->devres_head);device_pm_init(dev);set_dev_node(dev, -1);}


这个device_initialize 从名字上看就知道他管得很宽。那我们来看看对于USB 来说它是个什么角色吧。

struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1){// 此处省略一大串device_initialize(&dev->dev);

}

也就是说每个usb设备都会调用这个函数,然后按照之前的分析,kernel就会调到devices_kset 的uevent_ops,最终就会调到 usb_uevent 。

总结一下,每个USB 设备插入的时候就调用usb_uevent.

7. usb_uevent 做了什么。

static int usb_uevent(struct device *dev, struct kobj_uevent_env *env){struct usb_device *usb_dev;if (is_usb_device(dev)) {usb_dev = to_usb_device(dev);} else if (is_usb_interface(dev)) {struct usb_interface *intf = to_usb_interface(dev);usb_dev = interface_to_usbdev(intf);} else {return 0;}if (usb_dev->devnum < 0) {/* driver is often null here; dev_dbg() would oops */pr_debug("usb %s: already deleted?\n", dev_name(dev));return -ENODEV;}if (!usb_dev->bus) {pr_debug("usb %s: bus removed?\n", dev_name(dev));return -ENODEV;}/* per-device configurations are common */if (add_uevent_var(env, "PRODUCT=%x/%x/%x",   le16_to_cpu(usb_dev->descriptor.idVendor),   le16_to_cpu(usb_dev->descriptor.idProduct),   le16_to_cpu(usb_dev->descriptor.bcdDevice)))return -ENOMEM;/* class-based driver binding models */if (add_uevent_var(env, "TYPE=%d/%d/%d",   usb_dev->descriptor.bDeviceClass,   usb_dev->descriptor.bDeviceSubClass,   usb_dev->descriptor.bDeviceProtocol))return -ENOMEM;return 0;}

也是在往
struct kobj_uevent_env里面添加内容。


0 0
原创粉丝点击