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里面添加内容。
- Linux USB - uevent
- linux/android uevent
- linux/android uevent
- linux uevent分析
- linux input&&uevent使用
- uevent
- uevent
- Linux Uevent和Netlink socket
- Linux设备驱动模型-Uevent
- USB线插拔检测使用UEventObserver检测uevent事件的分析
- 检查内核反馈uevent消息,并提取出USB插入事件
- Linux 内核/sys 文件系统之uevent
- Linux 内核/sys 文件系统之uevent
- Linux 设备模型浅析之 uevent 篇
- linux电源管理的uevent上报
- Linux设备模型浅析之uevent篇
- Linux 设备模型浅析之 uevent 篇
- Linux设备模型浅析之uevent篇
- c# 实现查找mysql安装路径
- 一个UILabel上显示不同的字体、颜色
- SecureCRT连接Linux--Ubuntu 12.04
- putty客户端目录颜色太暗解决方法
- Listener学习
- Linux USB - uevent
- 对Haddop的认识
- linux panic 问题定位
- 字符编码ASCII, Unicode和UTF-8概念扫盲
- 创建者
- 第3周项目1三角形
- 第三周项【项目1 - 三角形类1】
- iOS:UISplitViewController的创建
- 网站开发中最常用的代码