用户名: 密码: 忘记密码? 注册

(转)Usb Hub代码分析

作者:  时间: 2010-12-23
Usb Hub代码分析
如需引用请注明出处:http://blog.csdn.net/zkami 作者:ZhengKui

在host controller初始化的时候一定会调用hub_probe进行初始化,至少对root hub初始化
hub_probe(struct usb_interface *intf, const struct usb_device_id *id) (hub.c)
此时struct usb_interface 和struct usb_device的数据已经在hc初始化的过程中得到
    -> hdev = interface_to_usbdev(intf); 
       由struct usb_interface 得到usb_device, 中间通过struct device转换,
    然后:分配struct usb_hub, 并初始化hub->event_list和hub->leds
    -> INIT_DELAYED_WORK(&hub->leds, led_work);初始化led_work工作队列
       以后调用schedule_delayed_work()来唤醒led_work, 暂时不用led
    -> usb_get_intf(intf);
    -> usb_set_intfdata (intf, hub);让struct usb_hub和struct usb_interface关联
    -> hub_configure(hub, endpoint)
        ->get_hub_descriptor(hdev, hub->descriptor,sizeof(*hub->descriptor));
          得到hub->descriptor
            -> usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
                USB_DT_HUB << 8, 0, data, size,
                USB_CTRL_GET_TIMEOUT);
        对tt初始化, struct usb_tt用来匹配低速设备和高速hub.tt与hub相关与OHCI/EHCI无关
        这样一个port既能支持high也能支持full/low
        -> INIT_WORK (&hub->tt.kevent, hub_tt_kevent);
            -> hub_tt_kevent()
        -> usb_set_interface(hdev, 0, 1); high speed hub
        -> usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
            -> usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
                USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status,
                sizeof(*status), USB_CTRL_GET_TIMEOUT);
        接着:定义每一个port的电流
        -> hub_hub_status(hub, &hubstatus, &hubchange);    
           这个请求是hub自己定义的(spec P425),将当前hub状态保存在hubstatus,已经改变的
            hub状态保存在hubchange里
            -> get_hub_status(hub->hdev, &hub->status->hub);
                ->usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                             USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
                         data, sizeof(*data), USB_STS_TIMEOUT);
        -> usb_alloc_urb(0, GFP_KERNEL); 分配urb
            -> usb_init_urb(urb);    初始化urb
        -> usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
            hub, endpoint->bInterval);  填充int urb
        -> hub_power_on(hub);
            -> set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
                  对每一个port
        -> hub_activate(hub);
            -> usb_submit_urb(hub->urb, GFP_NOIO);
               将之前usb_fill_int_urb()的urb提交
            -> schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
               延迟LED_CYCLE_PERIOD一段时间后,调度hub->leds, 让led一闪一闪亮晶晶
            -> kick_khubd(hub);向&hub_event_list链表挂载event,并唤醒hub_thread()
                -> to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
                   由usb_hub->struct device->usb_interface,增加pm的引用计数
                   hub要使用了,就别让电源管理把它挂起来了
                -> list_add_tail(&hub->event_list, &hub_event_list);
                   如果list_empty(&hub->event_list)即第一次调用,就把当前hub->event_list
                   加入到全局hub_event_list中去
                -> wake_up(&khubd_wait);
                   唤醒休眠的hub_thread(),从wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list));                         继续向下执行,khubd_wait是wait_queue_head_t类型。hub_events()将再次被执行
    -> hub_disconnect (intf);
        -> usb_get_intfdata (intf);
           由usb_interface->struct device->usb_hub 得到usb_hub
        -> list_del_init(&hub->event_list);删除当前hub 的event_list

usb_hub_init(void) hub.c
    ->usb_register(&hub_driver) 注册hub_driver  那末怎莫把hub_driver与hub_device挂接起来呢????
    -> kthread_run(hub_thread, NULL, "khubd"); 创建并唤醒线程hub_thread
    -> usb_deregister(&hub_driver) 注销hub_driver

hub执行线程
hub_thread(void *__unused) hub.c 
    -> hub_events();
        -> list_entry(tmp, struct usb_hub, event_list); 得到那个触发hub_events()的hub (struct usb_hub)
        -> to_usb_interface(hub_dev); 再得到 struct usb_interface
        -> usb_lock_device(hdev); 通过semaphone锁住这个usb_device
        -> hub_pre_reset(intf);      如果设备状态是USB_STATE_NOTATTACHED
        -> usb_autopm_get_interface(intf); 电源管理方面,让这个usb_interface引用计数加1,以防hub自动挂起
            -> usb_autopm_do_interface(intf, 1);
        -> usb_reset_composite_device(hdev, intf); 如果hub出错,把复合设备reset,复合设备就是有多个interface的设备
            -> usb_autoresume_device(udev); 防止在reset过程中自动挂起了
            接着:针对设备中的每个interface,找到其driver (usb_driver)并调用usb_driver->pre_reset(usb_interface)
            -> usb_reset_device(udev);
            接着:针对设备中的每个interface,找到其driver (usb_driver)并调用usb_driver->post_reset(usb_interface)
            -> usb_autosuspend_device(udev);

        接着:轮询该hub的每个端口,查看是否有变化
        -> hub_port_status(hub, i, &portstatus, &portchange);    
           通过发送Get Port Status标准hub请求,得到Status Bits和Status Change Bits
           根据port变化的原因,分别判断处理
            -> get_port_status(hub->hdev, port1, &hub->status->port);
                -> usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                    USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
                    data, sizeof(*data), USB_STS_TIMEOUT);
        -> clear_port_feature(hdev, i, USB_PORT_FEAT_C_CONNECTION);     //USB_PORT_STAT_C_CONNECTION
           hub标准命令,清除USB_PORT_FEAT_C_CONNECTION这个feature, 说明已经确认这个feature了
            -> usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),    
                USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
                NULL, 0, 1000);
        -> clear_port_feature(hdev, i, USB_PORT_FEAT_C_ENABLE);        //USB_PORT_STAT_C_ENABLE
        -> clear_port_feature(hdev, i, USB_PORT_FEAT_C_SUSPEND);    //USB_PORT_FEAT_C_SUSPEND
        -> remote_wakeup(hdev->    children[i-1]);
        -> hub_port_disable(hub, i, 1);    
        -> clear_port_feature(hdev, i, USB_PORT_FEAT_C_OVER_CURRENT);    //USB_PORT_STAT_C_OVERCURRENT
        -> hub_power_on(hub); 轮询hub的每一个port并设置port状态
            -> set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); 
                -> usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
                    USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
                    NULL, 0, 1000);
        -> clear_port_feature(hdev, i, USB_PORT_FEAT_C_RESET);        //USB_PORT_STAT_C_RESET
        -> hub_port_connect_change(hub, i,portstatus, portchange); 一个usb设备插入port,最终会发生的事情
        -> hub_hub_status(hub, &hubstatus, &hubchange)
        -> clear_hub_feature(hdev, C_HUB_LOCAL_POWER);        //HUB_CHANGE_LOCAL_POWER
        -> clear_hub_feature(hdev, C_HUB_OVER_CURRENT);        //HUB_CHANGE_OVERCURRENT
        -> hub_power_on(hub);
        -> usb_enable_root_hub_irq(hdev->bus); 如果是root hub
        -> usb_autopm_enable(intf);
        -> usb_unlock_device(hdev);
    -> wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list));
       只要hub_event_list不为空, 就唤醒。否则休眠,线程不会继续执行。ecos只要用“条件变量”把这个线程阻塞就行了
       当再向下执行时,hub_event_list已经不为空了