Android—— 4.2 Vold掛載管理_Kernel_USB_Uevent (七)

來源:互聯網
上載者:User

標籤:kernel   hub   uevent   usb   port   

  在前文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時再研究!




Android—— 4.2 Vold掛載管理_Kernel_USB_Uevent (七)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.