Android—— 4.2 Vold挂载管理_Kernel_USB_Uevent (七)

在前文Android—— 4.2 Vold挂载管理_NetlinkManager (四)中有解析到Vold 是从kernel中获取uevent事件,来获取device信息,其中是通过一个Netlink的套接字,目前整个Vold机制也分析完了,

上篇 Android—— 4.2 Vold挂载管理_MountService (六) 分析了机制中最上层的,这里分析一下最下层的kernel uevent事件的发送,以USB设备为例!

撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/39006999

一.USB_HUB_EVENT:

usb driver在/drivers/usb/目录下,这个目录下有多个文件夹,具体编译使用的是哪一个就要看kernel的.config了,可查看目录下的Makefile中的CONFIG*宏是否有定义.

我这边的kernel version 3.1.10,根据config我的kernel用的驱动在/drivers/usb/core目录下,usb.c 为入口.

static int __init usb_init(void)
{
    int retval;
    if (nousb) {
        pr_info("%s: USB support disabled\n", usbcore_name);
        return 0;
    }

    retval = usb_debugfs_init();//debug 文件的初始化
    if (retval)
        goto out;

    retval = bus_register(&usb_bus_type);
    if (retval)
        goto bus_register_failed;
    retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
    if (retval)
        goto bus_notifier_failed;
    retval = usb_major_init(); //
    if (retval)
        goto major_init_failed;
    retval = usb_register(&usbfs_driver);
    if (retval)
        goto driver_register_failed;
    retval = usb_devio_init();
    if (retval)
        goto usb_devio_init_failed;
    retval = usbfs_init();
    if (retval)
        goto fs_init_failed;
    retval = usb_hub_init(); //循环监测的初始化

  ...

}

subsys_initcall(usb_init);

可以看到初始入口即为usb_init,在里面初始化了usb相关各种参数配置,这里着重分析与插拔usb设备的usb_hub_init().

调用到/drivers/usb/core/hub.c中的usb_hub_init

int usb_hub_init(void)
{
    if (usb_register(&hub_driver) < 0) {
        printk(KERN_ERR "%s: can't register hub driver\n",
            usbcore_name);
        return -1;
    }

    khubd_task = kthread_run(hub_thread, NULL, "khubd");//创建一个线程,执行hub_thread函数,线程名为khubd
    if (!IS_ERR(khubd_task))
        return 0;

    /* Fall through if kernel_thread failed */
    usb_deregister(&hub_driver);
    printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);

    return -1;
}

看线程函数hub_thread

static int hub_thread(void *__unused)
{
    /* khubd needs to be freezable to avoid intefering with USB-PERSIST
     * port handover.  Otherwise it might see that a full-speed device
     * was gone before the EHCI controller had handed its port over to
     * the companion full-speed controller.
     */
    set_freezable();

    do {
        hub_events(); //进入有条件的循环,持续监测
        wait_event_freezable(khubd_wait,
                !list_empty(&hub_event_list) ||
                kthread_should_stop());
    } while (!kthread_should_stop() || !list_empty(&hub_event_list));

    pr_debug("%s: khubd exiting\n", usbcore_name);
    return 0;
}

再看hub_events:

static void hub_events(void)
{
    struct list_head *tmp;
    struct usb_device *hdev;
    struct usb_interface *intf;
    struct usb_hub *hub;
    struct device *hub_dev;

  ...

    while (1) {

        /* Grab the first entry at the beginning of the list */
        spin_lock_irq(&hub_event_lock);
        if (list_empty(&hub_event_list)) {
            spin_unlock_irq(&hub_event_lock);
            break;
        }

   ...

           /* deal with port status changes */
        for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {  // 遍历usb的端口,检测端口状态
            if (test_bit(i, hub->busy_bits))
                continue;
            connect_change = test_bit(i, hub->change_bits);
            if (!test_and_clear_bit(i, hub->event_bits) &&
                    !connect_change)
                continue;

            ret = hub_port_status(hub, i,
                    &portstatus, &portchange); // 依次判断端口状态是否被改变

            if (ret < 0)
                continue;

            if (portchange & USB_PORT_STAT_C_CONNECTION) { //判断端口状态改变情况
                clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_CONNECTION);
                connect_change = 1;
                #if defined(ENABLE_BATTERY_CHARGE) && (MP_USB_MSTAR==1)
                if (hdev->parent == NULL) {
                    usb_bc_enable(hcd, true);
                }
                #endif

                /* patch for DM always keep high issue */
                #if (_USB_HS_CUR_DRIVE_DM_ALLWAYS_HIGH_PATCH) && (MP_USB_MSTAR==1)
                /* turn on overwrite mode */
                if (hdev->parent == NULL)
                {
                    writeb(readb((void*)(hcd->utmi_base+0x0*2)) | BIT1, (void*) (hcd->utmi_base+0x0*2)); //tern_ov = 1
                }
                #endif
            }

           ...

                if (connect_change)
                hub_port_connect_change(hub, i,
                        portstatus, portchange); // 处理变化的端口

           ...

     }

     ...

  }

}

通过对usb端口状态的循环监测,来处理usb设备的插拔!

端口处理就从检测到变化开始:

static void hub_port_connect_change(struct usb_hub *hub, int port1,
                    u16 portstatus, u16 portchange)
{
    struct usb_device *hdev = hub->hdev;
    struct device *hub_dev = hub->intfdev;
    struct usb_hcd *hcd = bus_to_hcd(hdev->bus);

    unsigned wHubCharacteristics =
            le16_to_cpu(hub->descriptor->wHubCharacteristics);

    struct usb_device *udev;

   ...

    for (i = 0; i < SET_CONFIG_TRIES; i++) { 

        /* reallocate for each attempt, since references
         * to the previous one can escape in various ways
         */
        udev = usb_alloc_dev(hdev, hdev->bus, port1);
   ...

//一些初始赋值电流 速度什么的

        /* reset (non-USB 3.0 devices) and get descriptor */

        status = hub_port_init(hub, udev, port1, i); //对端口的初始操作
        if (status < 0)
            goto loop;
   ...
        status = usb_new_device(udev); //定义一个新的usb设备

 }
 ...
}

usb driver的检测部分大体就是这样,主要是通过对port的监测!

二.kobject_uevent_env:

在上面的hub_event中分析了kernel对插入设备的检测与识别,到usb_new_device

int usb_new_device(struct usb_device *udev)
{
    int err;
 ...

    /* Tell the world! */
    announce_device(udev); //打印出识别到的usb设备相关信息,包括 name idVendor idProduct 等

...

    err = device_add(&udev->dev); //添加识别到的这个设备

...

}

再看device_add:

int device_add(struct device *dev)
{
    struct device *parent = NULL;

...

    /* first, register with generic layer. */
    /* we require the name to be set before, and pass NULL */
    error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

...

    kobject_uevent(&dev->kobj, KOBJ_ADD); //发送这个设备事件,事件类型为添加

...

}

最后调用到真正发送uevent事件的是/lib/kobject_uevent.c下的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;

...

    /* originating subsystem */
    if (uevent_ops && uevent_ops->name)
        subsystem = uevent_ops->name(kset, kobj);
    else
        subsystem = kobject_name(&kset->kobj);

...

    /* environment buffer */
    env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

    retval = add_uevent_var(env, "ACTION=%s", action_string);//按照格式添加到env中
    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;

...

    /* send netlink message */ //从这里开始才是真正的发送操作,使用的是netlink的套接字

    mutex_lock(&uevent_sock_mutex);
    list_for_each_entry(ue_sk, &uevent_sock_list, list) {
        struct sock *uevent_sock = ue_sk->sk;
        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, "%[email protected]%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;
        } else
            retval = -ENOMEM;
    }
    mutex_unlock(&uevent_sock_mutex);

...

}

最后通过netlink_broadcast_filtered发送设备uevent的信息,对于kernel中的usb driver 我接触的不多,这里只是简单解析了一下往Vold 发送uevent的流程,

其中很多复杂的流程我都没写,以后有机会学习usb driver时再研究!

时间: 2024-12-20 23:58:23

Android—— 4.2 Vold挂载管理_Kernel_USB_Uevent (七)的相关文章

Android—— 4.2 Vold挂载管理_mmcblk内置-双sdcard (八)

Vold的学习过了一段时间了,才想着把mmcblock内置成sdcard的方法记录一下. 当初就是因为要做这个功能才去学习Vold机制,贴出之前的博客: Android-- 4.2 Vold挂载管理_主体构建main (一) Android-- 4.2 Vold挂载管理_CommandListener (二) Android-- 4.2 Vold挂载管理_VolumeManager (三) Android-- 4.2 Vold挂载管理_NetlinkManager (四) Android-- 4

Android—— 4.2 Vold挂载管理_NetlinkManager (四)

在前文Android-- 4.2 Vold挂载管理_主体构建main (一)中有结构图表示,Vold是kernel与用户层的一个交互管理模块, Android-- 4.2 Vold挂载管理_VolumeManager (三)简单介绍了核心VolumeManager的构建,这篇分析从kernel进程沟通到VolumeManager进程的关键:NetlinkManager 撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/3858602

Android—— 4.2 Vold挂载管理_MountService (六)

整个Vold机制应该算system层,与framwork层的交互在Android-- 4.2 Vold挂载管理_CommandListener (二)中有提到过,是通过一个"vold"的socket进行通信的,这里分析一下framework中负责与Vold通信的:MountService 撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/38978387 一.MountService启动: 在/frameworks/bas

Android—— 4.2 Vold挂载管理_CommandListener (二)

在前一篇博客中介绍了个大体结构 Android-- 4.2 Vold挂载管理_主体构建 (一),按照代码的顺序结构来依次分析,这里来看看CommandListener这个类做了什么. 撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/38434263 一:CommandListener构造 在/system/vold/main.cpp的main函数中构建实例: cl = new CommandListener(); vm->setB

Android—— 4.2 Vold挂载管理_VolumeManager (三)

在Android-- 4.2 Vold挂载管理_主体构建main (一)中分析了大体的框架,这里分析一下核心VolumeManager,再次补上结构框架图如下: 可以看到VolumeManager就是整个Android 磁盘挂载Vold机制的核心调度,上下连接的中转站! 我从Vold main代码的顺序结构来一次分析,上一篇Android-- 4.2 Vold挂载管理_CommandListener (二)  中分析了与framework层交互的CommandListener的功能作用. 这里分

Android—— 4.2 Vold挂载管理_DirectVolume/Volume (五)

在前文Android-- 4.2 Vold挂载管理_VolumeManager (三) 中解析了VolumeManager是怎么样抽取Volume实例以及DirectVolume与Volume之间的关系,在上篇Android-- 4.2 Vold挂载管理_NetlinkManager (四)中从kernel开始调用到handleBlockEvent,这里解析一下Vold挂载的真正操作,也就是Volume的操作! 撰写不易,转载请注明出处:http://blog.csdn.net/jscese/a

Android 6.0 - 动态权限管理的解决方案(转)

转自:http://www.cnblogs.com/dubo-/p/6018262.html Android 6.0 - 动态权限管理的解决方案 转载请标注 Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用户体验, 同时也为程序员带来新的负担. 动态权限管理就是这样, 一方面让用户更加容易的控制自己的隐私, 一方面需要重新适配应用权限. 时代总是不断发展, 程序总是以人为本, 让我们为应用添加动态权限管理吧! 这里提供了一个非常不错的解决方案, 提供源码, 项目可以直

yum安装LAMP环境与管理(七)

[教程主题]:yum安装LAMP环境与管理 [1] 什么是LAMP 目前世界最流行的企业建站方式是LAMP(Linux+Apache+MySQL+PHP),即使用Linux作为操作系统,Apache作为Web服务器,MySQL作为数据库,PHP作为服务器端脚本解释器.这四个软件都是遵循GPL的开放源码软件,它们安全.稳定.快速.功能强大,使用它们可以建立一个快速.稳定.免费的网站系统. [2]准备环境 yum添加163源 地址: http://mirrors.163.com/.help/cent

Android 仿今日头条频道管理(下)(GridView之间Item的移动和拖拽)

前言 上篇博客我们说到了今日头条频道管理的操作交互体验,我也介绍了2个GridView之间Item的相互移动.详情请參考:Android 仿今日头条频道管理(上)(GridView之间Item的移动和拖拽) 今天把相对照较复杂的gridView的拖拽也记录下.在開始之前我们事先要了解下Android的事件分发机制.网上这方面的资料也比較多.由于自己定义控件大部分要用到事件分发机制的知识. 实现思路 要实现Item的拖拽.事实上并非真正要去拖拽GridView的Item.而是使用WindowMan