Chinaunix首页 | 论坛 | 博客
  • 博客访问: 165727
  • 博文数量: 205
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-21 21:11
文章分类

全部博文(205)

文章存档

2016年(2)

2015年(203)

我的朋友

分类: LINUX

2015-11-17 09:51:23

原文地址:usb热插拔实现机制 作者:jinxinxin163

原文地址:
一.等待
static struct usb_driver hub_driver = {
    .name =        "hub",
    .probe =    hub_probe,
    .disconnect =    hub_disconnect,
    .suspend =    hub_suspend,
    .resume =    hub_resume,
    .reset_resume =    hub_reset_resume,
    .pre_reset =    hub_pre_reset,
    .post_reset =    hub_post_reset,
    .ioctl =    hub_ioctl,
    .id_table =    hub_id_table,
    .supports_autosuspend =    1,
};
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");
    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;
}

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_thread执行时,会进入hub_events,但是这时候hub_event_list队列为空,于是hub_events退出并wait

二.唤醒
当主控制器初始化时,会初始化root hub,之后调用:
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)

hub_probe()所做的工作:
1.为此root hub申请struct usb_hub结构体并初始化它
2.填充并提交中断in端点(由hub_activate完成)
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
        hub, endpoint->bInterval);
usb_submit_urb(hub->urb, GFP_NOIO);
3.调用kick_khubd(hub)

static void kick_khubd(struct usb_hub *hub)
{
    unsigned long    flags;

    /* Suppress autosuspend until khubd runs */
    to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;

    spin_lock_irqsave(&hub_event_lock, flags);
    if (!hub->disconnected && list_empty(&hub->event_list)) {
        list_add_tail(&hub->event_list, &hub_event_list);
        wake_up(&khubd_wait);
    }
    spin_unlock_irqrestore(&hub_event_lock, flags);
}
由于这个时候root hub已经成功初始化了,所以kick_khubd会将root hub的event_list,添加到
hub_event_list,表示root hub已经被识别了,同时wake_up(&khubd_wait)会唤醒上面的等待,
于是hub_events()又一次执行了,但是这次,它是有备而来,因为hub_event_list不为空

三.hub_events

hub_events函数所做的工作:
对每个端口号(共计bNbrPorts个端口,bNbrPorts这个值从hub描述符里边得到,因为此值描述了hub所用用的端口的情况),假如满足下列条件则调用hub_port_connect_change()
1.连接有变化
2.端口本身重新使能,即所谓的enable,这种情况通常就是为了对付电磁干扰的,正如我们前面的判断中所说的那样
3.在复位一个设备的时候发现其描述符变了,这通常对应的是硬件本身有了升级.很显然,第一种情况是真正的物理变化,后两者就算是逻辑变化
代码模型如下:
    for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
        ...
        if (connect_change) //对root hub上活跃的port调用hub_port_connect_change
                hub_port_connect_change(hub, i,
                        portstatus, portchange);
        ...    
    }

hub_port_connect_change()所做的工作:
1.udev = usb_alloc_dev(hdev, hdev_bus, port1);
  原型:struct usb_device *usb_alloc_dev(struct usb_device *parent,
                 struct usb_bus *bus, unsigned port1)
  为探测到的usb设备(包括普通hub,u盘等)分配并初始化udev;
2.status = hub_port_init(hub, udev, port1, i);
  先进行两次新的策略(i=0和=1时),如果不行就再进行两次旧的策略(i=2和i=3时).所有这一切只有一个目的,就是为了获得设备的描述符,设置了udev->tt、udev->ttport和udev->ep0.desc.wMaxPacketSize,设置udev->status=     
  USB_STATE_ADDRESS
3.usb_new_device(udev);
(1)usb_configure_device(udev)->
    usb_get_configuration(udev);
        a.usb_get_descriptor()        //得到设备的描述符(包括设备描述符、配置描述符、接口描述符等)
        b.usb_parse_configuration()    //分析以上描述符信息,提取出配置、接口等,并赋值给udev结构里相应的字段
(2)device_add(&udev->dev);
   将usb设备注册到系统里,这个动作将触发驱动的匹配,由于这是个usb设备,所以万能usb驱动usb_generic_driver会匹配上,
   从而generic_probe会得到执行.关于 generic_probe所做的工作,请参考:
  http://blog.chinaunix.net/uid-20727076-id-3273535.html

从上面可以看出来,这一次hub_events()调用是由于主控制器初始化调用了hub_probe,从而引发hub_events调用。那root hub初始化完成以后hub_events会如何触发呢?
答案是通过中断!而这个中断的服务函数就是hub_irq,也即是说,凡是真正的有端口变化事件发生,hub_irq就会被调用,而hub_irq()最终会调用kick_khubd(),触发hub的event_list,于是再次调用hub_events().
那hub_irq是什么时候注册的呢?
前面我们讲到:
hub_probe()所做的第二项工作是:填充并提交中断in端点(由hub_activate完成)
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
        hub, endpoint->bInterval);
usb_submit_urb(hub->urb, GFP_NOIO);
hub_irq作为参数传给了usb_fill_int_urb,这样设定以后,只要root hub的端口有变化,hub_irq就会执行到
原文地址:


阅读(1160) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~