LINUX下USB1.1设备学习小记(5)_uhci与设备(1)

2020-06-03 00:00:00 设备 状态 端口 检测 集线器

现在开始uhci与设备的通信分析
先看分析枚举过程,再分析数据通信
USB总线上设备的枚举:
1. 当设备插入时,设备的上拉电阻使信号线的电位升高,这时候根集线器检测到设备的插入
2. 主机发送Get_status到根集线器来获得当前端口的状态
3. 主机发送Set_Feature,让根集线器复位端口,使得端口上的设备处于复位状态
4. 主机发送Get_status检测端口的复位是否完成,如果完成,设备现在处于默认状态,并准备好使用端点0进行控制传输,这时候设备的地址为0
5. 主机检测设备的速度类型
6. 主机向设备发送Get_Device_Descriptor获得设备端点0包的大值
7. 主机向设备发送Set_Address来给设备设定一个新的地址
8. 主机向设备发送Get_Device_Descriptor获得完整的设备描述符
9. 主机向设备发送Get_Device_Configuration来获取所有的配置信息
10. 主机按照配置信息匹配驱动
其中6 7步在微软和LINUX中的处理并不一样,微软为先6再7,而LINUX为先7再6,而且取得包大值的方法也不一样,这会在后面详细说明

好,现在就从设备插入根集线器开始看
回到呢永不停息的uhci_hub_status_data

uhci_hub_status_data在/drivers/usb/core/hcd.c中

void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
    struct urb    *urb;
    int        length;
    unsigned long    flags;
    char        buffer[4];    /* Any root hubs with > 31 ports? */
    //检测主机控制器驱动是否已经注册
    if (unlikely(!hcd->rh_registered))
        return;
    if (!hcd->uses_new_polling && !hcd->status_urb)
        return;
    //进行集线器设备状态检测
    length = hcd->driver->hub_status_data(hcd, buffer);
    //端口有设备
    if (length > 0)
    {
        /* try to complete the status urb */
        spin_lock_irqsave(&hcd_root_hub_lock, flags);
        urb = hcd->status_urb;
        //检测urb是否存在
        if (urb) 
        {
            hcd->poll_pending = 0;
            //清除hcd的状态urb
            hcd->status_urb = NULL;
            //置实际传输长度为1
            urb->actual_length = length;
            //拷贝端口状态描述组到urb中
            memcpy(urb->transfer_buffer, buffer, length);
            //卸载urb与节点的连接
            usb_hcd_unlink_urb_from_ep(hcd, urb);        
            spin_unlock(&hcd_root_hub_lock);
            //返回urb给驱动程序
            usb_hcd_giveback_urb(hcd, urb, 0);
            spin_lock(&hcd_root_hub_lock);
        } 
        else 
        {
            length = 0;
            hcd->poll_pending = 1;
        }
        spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
    }
    /* The USB 2.0 spec says 256 ms. This is close enough and won't
     * exceed that limit if HZ is 100. The math is more clunky than
     * maybe expected, this is to make sure that all timers for USB devices
     * fire at the same time to give the CPU a break inbetween */

    if (hcd->uses_new_polling ? hcd->poll_rh :(length == 0 && hcd->status_urb != NULL))
        mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
} 

hcd->driver->hub_status_data负责检测端口和td队列的状态
hcd->driver->hub_status_data在UHCI中为uhci_hub_status_data
uhci_hub_status_data在/drivers/usb/host/uhci-hub.c中

static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
    //取得uhci_hcd结构
    struct uhci_hcd *uhci = hcd_to_uhci(hcd);
    unsigned long flags;
    int status = 0;
    spin_lock_irqsave(&uhci->lock, flags);
    //调度uhci中的帧队列
    uhci_scan_schedule(uhci);
    if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
        goto done;
    //检测端口
    uhci_check_ports(uhci);
    //获得端口状态
    status = get_hub_status_data(uhci, buf);
    //检测根集线器的状态
    switch (uhci->rh_state) 
    {
     case UHCI_RH_SUSPENDING:
     case UHCI_RH_SUSPENDED:
        /* if port change, ask to be resumed */
        if (status)
            //唤醒根集线器
            usb_hcd_resume_root_hub(hcd);
        break;
     case UHCI_RH_AUTO_STOPPED:
        /* if port change, auto start */
        if (status)
            //唤醒主机控制器
            wakeup_rh(uhci);
        break;
     //uhci的状态为运行
     case UHCI_RH_RUNNING:
        /* are any devices attached? */
        //检测是否有连接的设备
        if (!any_ports_active(uhci)) 
        {
            //改变uhci的状态为运行但无设备
            uhci->rh_state = UHCI_RH_RUNNING_NODEVS;
            //改变uhci的自动停止时间
            uhci->auto_stop_time = jiffies + HZ;
        }
        break;
     //uhci的状态为运行但无设备
     case UHCI_RH_RUNNING_NODEVS:
        /* auto-stop if nothing connected for 1 second */
        //检测是否有连接的设备
        if (any_ports_active(uhci))
            //改变uhci的状态为运行
            uhci->rh_state = UHCI_RH_RUNNING;
        //检测jiffies是否大于uhci->auto_stop_time
        else if (time_after_eq(jiffies, uhci->auto_stop_time))
            //悬挂主机控制器
            suspend_rh(uhci, UHCI_RH_AUTO_STOPPED);
        break;
     default:
        break;
    }
done:
    spin_unlock_irqrestore(&uhci->lock, flags);
    return status;
}

我们的目标就是get_hub_status_data,他负责端口连接状态的检测
get_hub_status_data在/drivers/usb/host/uhci-hub.c中

static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
{
    int port;
    //检测RWC的三个状态改变位
    int mask = RWC_BITS;
    /* Some boards (both VIA and Intel apparently) report bogus
     * overcurrent indications, causing massive log spam unless
     * we completely ignore them. This doesn't seem to be a problem
     * with the chipset so much as with the way it is connected on
     * the motherboard; if the overcurrent input is left to float
     * then it may constantly register false positives. */

    if (ignore_oc)
        mask &= ~USBPORTSC_OCC;
    *buf = 0;
    //历遍根集线器上的端口
    for (port = 0; port < uhci->rh_numports; ++port) 
    {
        //检测端口的状态改变位,或者端口悬挂位
        if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & mask) ||test_bit(port, &uhci->port_c_suspend))
            //记录端口号
            *buf |= (<< (port + 1));
    }
    return !!*buf;
}

当有设备连接到端口时, get_hub_status_data负责记录端口号到buf中,并返回1
uhci_hub_status_data会将这个1返回给usb_hcd_poll_rh_status的length,length为1,终于可以进入if中了
先取得主机控制器的状态检测urb,设置一些属性之后就进入到usb_hcd_giveback_urb中
在usb_hcd_giveback_urb中会运行urb的complete函数,还记得UHCI主机控制器中urb的complete函数是什么嘛?hub_irq,忘了的话回顾一下hub接口驱动中的注册内容 = 3= 
hub_irq在/drivers/usb/core/hub.c中

static void hub_irq(struct urb *urb)
{
    //取得集线器描述结构
    struct usb_hub *hub = urb->context;
    //取得urb的状态
    int status = urb->status;
    int i;
    unsigned long bits;
    //检测urb的状态
    switch (status) 
    {
    case -ENOENT:        /* synchronous unlink */
    case -ECONNRESET:    /* async unlink */
    case -ESHUTDOWN:    /* hardware going away */
        return;
    //推测为错误
    default:        /* presumably an error */
        /* Cause a hub reset after 10 consecutive errors */
        dev_dbg (hub->intfdev, "transfer --> %d\n", status);
        if ((++hub->nerrors < 10) || hub->error)
            goto resubmit;
        hub->error = status;
        /* FALL THROUGH */
    /* let khubd handle things */
    //激活khubd线程
    case 0:            /* we got data: port status changed */
        bits = 0;
        for (= 0; i < urb->actual_length; ++i)
            bits |= ((unsigned long) ((*hub->buffer)[i])) << (i*8);
        hub->event_bits[] = bits;
        break;
    }
    hub->nerrors = 0;
    /* Something happened, let khubd figure it out */
    kick_khubd(hub);
resubmit:
    if (hub->quiescing)
        return;
    //再次发送hcd的中断类型urb
    if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
            && status != -ENODEV && status != -EPERM)
        dev_err (hub->intfdev, "resubmit --> %d\n", status);
}

主要任务有两个,一是敲醒khubd线程,一是再次发送主机控制器的urb,让以后仍有状态urb可用
发送中断类型的urb就不多说了,来看kick_khubd(hub);
kick_khubd在/drivers/usb/core/hub.c中

static void kick_khubd(struct usb_hub *hub)
{
    unsigned long    flags;
    /* Suppress autosuspend until khubd runs */
    //将自动悬挂标志置1
    to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
    spin_lock_irqsave(&hub_event_lock, flags);
    //检测集线器的连接以及事件队列是否为空
    if (!hub->disconnected && list_empty(&hub->event_list))
    {
        //将hub挂进hub_event_list队列里
        list_add_tail(&hub->event_list, &hub_event_list);
        //唤醒khubd_wait
        wake_up(&khubd_wait);
    }
    spin_unlock_irqrestore(&hub_event_lock, flags);
}

主要任务也是两个, 将hub挂进hub_event_list队列里, 唤醒khubd_wait
唤醒khubd_wait是啥呢?给点提示,和khubd线程有关
khubd线程的主函数为hub_thread
hub_thread在/drivers/usb/core/hub.c中

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_event_list队列不会空,并且唤醒khubd后,我们终于可以来到神秘的hub_events中了
hub_events在/drivers/usb/core/hub.c中

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;
    u16 hubstatus;
    u16 hubchange;
    u16 portstatus;
    u16 portchange;
    int i, ret;
    int connect_change;
    /*
     * We restart the list every time to avoid a deadlock with
     * deleting hubs downstream from this one. This should be
     * safe since we delete the hub from the event list.
     * Not the most efficient, but avoids deadlocks.
     */

    while (1) 
    {
        /* Grab the first entry at the beginning of the list */
        spin_lock_irq(&hub_event_lock);
        //测试hub_event_list队列是否为空
        if (list_empty(&hub_event_list)) 
        {
            spin_unlock_irq(&hub_event_lock);
            break;
        }
        //获取队列的个事件
        tmp = hub_event_list.next;
        //卸载事件与hub_event_list的关联
        list_del_init(tmp);
        //从tmp算出包含它的集线器结构
        hub = list_entry(tmp, struct usb_hub, event_list);
        kref_get(&hub->kref);
        spin_unlock_irq(&hub_event_lock);
        //获取usb设备结构
        hdev = hub->hdev;
        //获取集线器的接口设备
        hub_dev = hub->intfdev;
        //获取接口结构
        intf = to_usb_interface(hub_dev);
        dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
                hdev->state, hub->descriptor
                    ? hub->descriptor->bNbrPorts
                    : 0,
                /* NOTE: expects max 15 ports... */
                (u16) hub->change_bits[],
                (u16) hub->event_bits[]);
        /* Lock the device, then check to see if we were
         * disconnected while waiting for the lock to succeed. */

        usb_lock_device(hdev);
        //检测集线器的连接状态
        if (unlikely(hub->disconnected))
            goto loop;
        /* If the hub has died, clean up after it */
        //检测usb设备的状态是否为0
        if (hdev->state == USB_STATE_NOTATTACHED) 
        {
            hub->error = -ENODEV;
            //卸载集线器上的所有设备
            hub_stop(hub);
            goto loop;
        }
        /* Autoresume */
        //自动恢复
        ret = usb_autopm_get_interface(intf);
        if (ret)
        {
            dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
            goto loop;
        }
        /* If this is an inactive hub, do nothing */
        //检测集线器是否为有效设备
        if (hub->quiescing)
            goto loop_autopm;
        //检测集线器的错误标志
        if (hub->error) 
        {
            dev_dbg (hub_dev, "resetting for error %d\n",hub->error);
            ret = usb_reset_composite_device(hdev, intf);
            if (ret) 
            {
                dev_dbg (hub_dev,"error resetting hub: %d\n",ret);
                goto loop_autopm;
            }
            hub->nerrors = 0;
            hub->error = 0;
        }
        /* deal with port status changes */
        //历遍下行端口
        for (= 1; i <= hub->descriptor->bNbrPorts; i++) 
        {
            //测试端口重置恢复位
            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 && !hub->activating)
                continue;
            //取得集线器端口状态
            ret = hub_port_status(hub, i,&portstatus, &portchange);
            if (ret < 0)
                continue;
            //检测集线器的活动位
            //检测端口对应的usb设备的子设备位是否为空
            //检测端口状态是否为连接
            if (hub->activating 
                    && !hdev->children[i-1] 
                    && (portstatus &USB_PORT_STAT_CONNECTION))
                connect_change = 1;
            //检测端口连接状态改变位中的连接状态是否有改变
            if (portchange & USB_PORT_STAT_C_CONNECTION) 
            {
                clear_port_feature(hdev,i,USB_PORT_FEAT_C_CONNECTION);

                connect_change = 1;
            }
            //检测端口连接状态改变位中的端口使能位
            if (portchange & USB_PORT_STAT_C_ENABLE) 
            {
                if (!connect_change)
                    dev_dbg (hub_dev,"port %d enable change, ""status %08x\n",i, portstatus);
                //清除端口有效位
                clear_port_feature(hdev,i,USB_PORT_FEAT_C_ENABLE);
                /*
                 * EM interference sometimes causes badly
                 * shielded USB devices to be shutdown by
                 * the hub, this hack enables them again.
                 * Works at least with mouse driver. 
                 */

                if (!(portstatus & USB_PORT_STAT_ENABLE) &&!connect_change && hdev->children[i-1]) 
                {
                    dev_err (hub_dev, "port %i " "disabled by hub (EMI?), " "re-enabling...\n", i);
                    connect_change = 1;
                }
            }
            //检测端口连接状态改变位中的端口悬挂是否改变
            if (portchange & USB_PORT_STAT_C_SUSPEND) 
            {
                //清除悬挂位
                clear_port_feature(hdev,i,USB_PORT_FEAT_C_SUSPEND);
                //检测端口对应的设备是否存在
                if (hdev->children[i-1]) 
                {
                    ret = remote_wakeup(hdev->children[i-1]);
                    
                    if (ret < 0)
                        connect_change = 1;
                } 
                else 
                {
                    ret = -ENODEV;
                    hub_port_disable(hub, i, 1);
                }   
                dev_dbg (hub_dev,"resume on port %d, status %d\n",i, ret);
            }
            //检测端口状态改变位中的过载电源位
            if (portchange & USB_PORT_STAT_C_OVERCURRENT) 
            {
                dev_err (hub_dev,"over-current change on port %d\n",i);
                //清除过载电源位
                clear_port_feature(hdev,i,USB_PORT_FEAT_C_OVER_CURRENT);
                hub_power_on(hub);
            }
            //检测端口重置位
            if (portchange & USB_PORT_STAT_C_RESET) 
            {
                dev_dbg (hub_dev,"reset change on port %d\n",i);
                //清除端口重置位
                clear_port_feature(hdev, i,USB_PORT_FEAT_C_RESET);
            }
            //检测连接状态改变位
            if (connect_change)
                //处理物理或者逻辑上的连接改变事件
                hub_port_connect_change(hub, i,portstatus,portchange);
        } /* end for i */
        /* deal with hub status changes */
        if (test_and_clear_bit(, hub->event_bits) == 0)
            ;    /* do nothing */
        else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
            dev_err (hub_dev, "get_hub_status failed\n");
        else 
        {
            if (hubchange & HUB_CHANGE_LOCAL_POWER) 
            {
                dev_dbg (hub_dev, "power change\n");
                clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
                if (hubstatus & HUB_STATUS_LOCAL_POWER)
                    /* FIXME: Is this always true? */
                    hub->limited_power = 1;
                else
                    hub->limited_power = 0;
            }
            if (hubchange & HUB_CHANGE_OVERCURRENT) 
            {
                dev_dbg (hub_dev, "overcurrent change\n");
                msleep(500);    /* Cool down */
                clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
                            hub_power_on(hub);
            }
        }
        hub->activating = 0;
        /* If this is a root hub, tell the HCD it's okay to
         * re-enable port-change interrupts now. */

        if (!hdev->parent && !hub->busy_bits[])
            usb_enable_root_hub_irq(hdev->bus);
loop_autopm:
        /* Allow autosuspend if we're not going to run again */
        if (list_empty(&hub->event_list))
            usb_autopm_enable(intf);
loop:
        usb_unlock_device(hdev);
        kref_put(&hub->kref, hub_release);
        } /* end while (1) */
}

hub = list_entry(tmp, struct usb_hub, event_list);
这句代码取得产生事件的根集线器
ret = hub_port_status(hub, i,&portstatus, &portchange); 取得集线器端口状态,有没有什么灵感呢?其实到这里我们就进入到枚举的第2步了
2. 主机发送Get_status到根集线器来获得当前端口的状态
兴奋吧,第3步也离我们不远了

进行一轮检测之后进入到hub_port_connect_change
hub_port_connect_change在/drivers/usb/core/hub.c中

static void hub_port_connect_change(struct usb_hub *hub, int port1,
                    u16 portstatus, u16 portchange)
{
    //取得usb设备
    struct usb_device *hdev = hub->hdev;
    //取得接口的设备结构
    struct device *hub_dev = hub->intfdev;
    //取得主机控制器驱动
    struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
    //取得集线器描述符中的特征字段    
    u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
    int status, i;
    dev_dbg (hub_dev,"port %d, status %04x, change %04x, %s\n",
        port1, portstatus, portchange, portspeed (portstatus));
    //检测集线器是否有指示器(LED灯)
    if (hub->has_indicators) 
    {
        set_port_led(hub, port1, HUB_LED_AUTO);
        hub->indicator[port1-1] = INDICATOR_AUTO;
    }
    /* Disconnect any existing devices under this port */
    //检测端口号对应的设备是否存在
    if (hdev->children[port1-1])
        //卸载该设备
        usb_disconnect(&hdev->children[port1-1]);
    //清除端口对应的状态改变位
    clear_bit(port1, hub->change_bits);
#ifdef    CONFIG_USB_OTG
    /* during HNP, don't repeat the debounce */
    if (hdev->bus->is_b_host)
        portchange &= ~USB_PORT_STAT_C_CONNECTION;
#endif
    //检测端口近状态改变位
    if (portchange & USB_PORT_STAT_C_CONNECTION) 
    {
        //端口防反跳
        status = hub_port_debounce(hub, port1);
        if (status < 0) 
        {
            if (printk_ratelimit())
                dev_err (hub_dev, "connect-debounce failed, ""port %d disabled\n", port1);
            goto done;
        }
        portstatus = status;
    }
    /* Return now if nothing is connected */
    //检测端口的连接位
    if (!(portstatus & USB_PORT_STAT_CONNECTION))
    {
        /* maybe switch power back on (e.g. root hub was reset) */
        if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 && !(portstatus & (<< USB_PORT_FEAT_POWER)))
            set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
         //检测端口有效位
        if (portstatus & USB_PORT_STAT_ENABLE)
              goto done;
        return;
    }
    //重复一定次数
    for (= 0; i < SET_CONFIG_TRIES; i++) 
    {
        //声明一个usb设备结构
        struct usb_device *udev;
        /* reallocate for each attempt, since references
         * to the previous one can escape in various ways
         */

         //分配该结构的空间
        udev = usb_alloc_dev(hdev, hdev->bus, port1);
        //分配失败则出错返回
        if (!udev) 
        {
            dev_err (hub_dev,"couldn't allocate port %d usb_device\n",port1);
            goto done;
        }
        //设置该usb设备的状态
        usb_set_device_state(udev, USB_STATE_POWERED);
        //设置该usb设备的速度模式
        udev->speed = USB_SPEED_UNKNOWN;
        //设置电流
         udev->bus_mA = hub->mA_per_port;
        //设置设备的层数
        udev->level = hdev->level + 1;
        //检测上层集线器是否为wusb
        udev->wusb = hub_is_wusb(hub);
        /* set the address */
        //寻找一个空的设备号
        choose_address(udev);
        //检测设备号是否分配成功
        if (udev->devnum <= 0) 
        {
            status = -ENOTCONN;    /* Don't retry */
            goto loop;
        }
        /* reset and get descriptor */
        //重置设备,取得描述符
        status = hub_port_init(hub, udev, port1, i);
        
        if (status < 0)
            goto loop;
        /* consecutive bus-powered hubs aren't reliable; they can
         * violate the voltage drop budget. if the new child has
         * a "powered" LED, users should notice we didn't enable it
         * (without reading syslog), even without per-port LEDs
         * on the parent.
         */

         //检测设备是否为集线器设备,并且需要的电流小于100
        if (udev->descriptor.bDeviceClass == USB_CLASS_HUB && udev->bus_mA <= 100) 
        {
            u16    devstat;
            status = usb_get_status(udev, USB_RECIP_DEVICE,,&devstat);
            if (status < 2) 
            {
                dev_dbg(&udev->dev, "get status %d ?\n", status);
                goto loop_disable;
            }
            le16_to_cpus(&devstat);
            if ((devstat & (<< USB_DEVICE_SELF_POWERED)) == 0) 
            {
                dev_err(&udev->dev,"can't connect bus-powered hub " "to this port\n");
                if (hub->has_indicators) 
                {
                    hub->indicator[port1-1] =INDICATOR_AMBER_BLINK;
                    schedule_delayed_work (&hub->leds, 0);
                }
                status = -ENOTCONN;    /* Don't retry */
                goto loop_disable;
            }
        }
        /* check for devices running slower than they could */
        if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
                && udev->speed == USB_SPEED_FULL
                && highspeed_hubs != 0)
            check_highspeed (hub, udev, port1);
        /* Store the parent's children[] pointer. At this point
         * udev becomes globally accessible, although presumably
         * no one will look at it until hdev is unlocked.
         */

        status = 0;
        /* We mustn't add new devices if the parent hub has
         * been disconnected; we would race with the
         * recursively_mark_NOTATTACHED() routine.
         */

        spin_lock_irq(&device_state_lock);
        if (hdev->state == USB_STATE_NOTATTACHED)
            status = -ENOTCONN;
        else
            hdev->children[port1-1] = udev;
        spin_unlock_irq(&device_state_lock);
        /* Run it through the hoops (find a driver, etc) */
        //检测是否有异常
        if (!status) 
        {
            //建立usb设备
            status = usb_new_device(udev);
            if (status) 
            {
                spin_lock_irq(&device_state_lock);
                hdev->children[port1-1] = NULL;
                spin_unlock_irq(&device_state_lock);
            }
        }
        if (status)
            goto loop_disable;
        status = hub_power_remaining(hub);
        if (status)
            dev_dbg(hub_dev, "%dmA power budget left\n", status);
        return;
loop_disable:
        hub_port_disable(hub, port1, 1);
loop:
        usb_ep0_reinit(udev);
        release_address(udev);
        usb_put_dev(udev);
        if ((status == -ENOTCONN) || (status == -ENOTSUPP))
            break;
    }
    if (hub->hdev->parent ||
            !hcd->driver->port_handed_over ||
            !(hcd->driver->port_handed_over)(hcd, port1))
        dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
                port1);
done:
    hub_port_disable(hub, port1, 1);
    if (hcd->driver->relinquish_port && !hub->hdev->parent)
        hcd->driver->relinquish_port(hcd, port1);
}

usb_alloc_dev为插入的usb设备分配一个usb-device数据结构
choose_address为插入的usb设备在所连接的主机控制器的usb总线上分配一个设备号
虽然分配了,但是此时还是用地址0进行通信,只是预先分配好而已
choose_address在/drivers/usb/core/hub.c中

相关文章