linux usb枚举过程分析

32人阅读 评论(0) 收藏 举报
分类:

插入一个 USB设备的处理机制总体计: 
1. 中断定时查询: 
这里写图片描述 
2. 总体架构设计: 
这里写图片描述 
3. 解析各个部分:

中断定时查询: 
这里写图片描述

Hub层处理 
这里写图片描述
usb枚举 
这里写图片描述

当守护程序第一次运行或usb port上状态发生变化,守护进程被唤醒都会运行hub_events函数,这个函数在usb系统中处理核心位置,usb的枚举过程就是由它完成。

usb具体的枚举流程: 
这里写图片描述

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;
    u16 hubstatus;
    u16 hubchange;
    u16 portstatus;
    u16 portchange;
    int i, ret;
    int connect_change, wakeup_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);
        if (list_empty(&hub_event_list)) {
            spin_unlock_irq(&hub_event_lock);
            break;
        }

        tmp = hub_event_list.next;
        list_del_init(tmp);

        hub = list_entry(tmp, struct usb_hub, event_list);
        kref_get(&hub->kref);

        /* make sure hdev is not freed before accessing it */
        if (hub->disconnected) {  //判断hub是否连接
            spin_unlock_irq(&hub_event_lock);
            goto hub_disconnected;
        } else {
            usb_get_dev(hub->hdev);
        }
        spin_unlock_irq(&hub_event_lock);

        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[0],
                (u16) hub->event_bits[0]);

        /* 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_disconnected;

        /* If the hub has died, clean up after it */
        if (hdev->state == USB_STATE_NOTATTACHED) {
            hub->error = -ENODEV;
            hub_quiesce(hub, HUB_DISCONNECT);
            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_device(hdev);
            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 */
        //遍历hub中的所有port,对各个port的状态进行查看,判断port是否发生了状态变化
        for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
            if (test_bit(i, hub->busy_bits))
                continue;
            connect_change = test_bit(i, hub->change_bits);
            wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
            if (!test_and_clear_bit(i, hub->event_bits) &&
                    !connect_change && !wakeup_change)
                continue;

            ret = hub_port_status(hub, i,
                    &portstatus, &portchange);
            if (ret < 0)
                continue;

            if (portchange & USB_PORT_STAT_C_CONNECTION) {
                usb_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);
                usb_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
                    && hub->ports[i - 1]->child) {
                    dev_err (hub_dev,
                        "port %i "
                        "disabled by hub (EMI?), "
                        "re-enabling...\n",
                        i);
                    connect_change = 1;
                }
            }

            if (hub_handle_remote_wakeup(hub, i,
                        portstatus, portchange))
                connect_change = 1;

            if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
                u16 status = 0;
                u16 unused;

                dev_dbg(hub_dev, "over-current change on port "
                    "%d\n", i);
                usb_clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_OVER_CURRENT);
                msleep(100);    /* Cool down */
                hub_power_on(hub, true);
                hub_port_status(hub, i, &status, &unused);
                if (status & USB_PORT_STAT_OVERCURRENT)
                    dev_err(hub_dev, "over-current "
                        "condition on port %d\n", i);
            }

            if (portchange & USB_PORT_STAT_C_RESET) {
                dev_dbg (hub_dev,
                    "reset change on port %d\n",
                    i);
                usb_clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_RESET);
            }
            if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
                    hub_is_superspeed(hub->hdev)) {
                dev_dbg(hub_dev,
                    "warm reset change on port %d\n",
                    i);
                usb_clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_BH_PORT_RESET);
            }
            if (portchange & USB_PORT_STAT_C_LINK_STATE) {
                usb_clear_port_feature(hub->hdev, i,
                        USB_PORT_FEAT_C_PORT_LINK_STATE);
            }
            if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
                dev_warn(hub_dev,
                    "config error on port %d\n",
                    i);
                usb_clear_port_feature(hub->hdev, i,
                        USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
            }

            /* Warm reset a USB3 protocol port if it's in
             * SS.Inactive state.
             */
            if (hub_port_warm_reset_required(hub, portstatus)) {
                int status;
                struct usb_device *udev =
                    hub->ports[i - 1]->child;

                dev_dbg(hub_dev, "warm reset port %d\n", i);
                if (!udev ||
                    !(portstatus & USB_PORT_STAT_CONNECTION) ||
                    udev->state == USB_STATE_NOTATTACHED) {
                    status = hub_port_reset(hub, i,
                            NULL, HUB_BH_RESET_TIME,
                            true);
                    if (status < 0)
                        hub_port_disable(hub, i, 1);
                } else {
                    usb_lock_device(udev);
                    status = usb_reset_device(udev);
                    usb_unlock_device(udev);
                    connect_change = 0;
                }
            }

            if (connect_change)//如果port状态发生变化,则调用hub_port_connect_change
                hub_port_connect_change(hub, i,
                        portstatus, portchange);
        } /* end for i */

        /* deal with hub status changes */
        if (test_and_clear_bit(0, 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) {
                u16 status = 0;
                u16 unused;

                dev_dbg(hub_dev, "over-current change\n");
                clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
                msleep(500);    /* Cool down */
                            hub_power_on(hub, true);
                hub_hub_status(hub, &status, &unused);
                if (status & HUB_STATUS_OVERCURRENT)
                    dev_err(hub_dev, "over-current "
                        "condition\n");
            }
        }

 loop_autopm:
        /* Balance the usb_autopm_get_interface() above */
        usb_autopm_put_interface_no_suspend(intf);
 loop:
        /* Balance the usb_autopm_get_interface_no_resume() in
         * kick_khubd() and allow autosuspend.
         */
        usb_autopm_put_interface(intf);
 loop_disconnected:
        usb_unlock_device(hdev);
        usb_put_dev(hdev);
 hub_disconnected:
        kref_put(&hub->kref, hub_release);

        } /* end while (1) */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269

hub_events本身是一个死循环,只要条件满足它便会一直执行。 
首先判断hub_event_list是否为空,如果为空,则跳出循环,hub_events运行结束,会进入休眠状态;如果不为空,则从hub_event_list列表中取出某一项,并把它从hub_event_list中删除,通过list_entry来获取event_list所对应的hub,并增加hub使用计数;然后做了一些逻辑判断,判断hub是否连接,hub上是否有错误,如果有错误就重启hub,如果没有错误,接下来就对hub 上的每个port进行扫描,判断各个port是否发生状态变化;

接着遍历hub中的所有port,对各个port的状态进行查看,判断port是否发生了状态变化,如果产生了变化则调用hub_port_connect_change进行处理;

在hub结构中存在多个标志位,用来表示port一些状态:

  1. 如果usb的第i个port处于resume或reset状态,则hub中busy_bits中第i个位置1;如果busy_bits中第i个位置1,则退过当前port;
  2. event_bits中第0位用来表示hub本身是否产生local power status change和是否产生过流,其它的各位用来表示hub下各个port状态是否发生变化,这些状态包括: ConnectStatusChange 连接状态发生变化,PortEnableStatusChange端口使能状态发生变化,PortSuspendStatusChange端口从挂起到恢复完成,PortOverCurrentIndicatorChange端口过流状态发生变化,PortResetStatusChange端口reset10ms置位;

  3. remove_bits用来表示hub下各个port是否有设备连接,如果置位表示没有设备连接或设备已经断开;

  4. change_bits用来表示hub下各个端口逻辑状态是否发生变化,它是在hub初始化的时候赋值的,这些状态主要有:1.原先port上没有设备,现在检测到有设备连接;2.原先port上有设备,由于控制器不正常关闭或禁止usb port等原图使得该设备处于NOATTACHED态,则需要断开设备;

在遍历port时,首先对这些标志进行判断,如果没有产生变化则跳过当前port,否则读取当前port的status,判断出产生状态变化的原因; 
如果发生变化port的连接状态发生变化,清除相应的端口状态,设置connect_change变量为1; 
如果port的使能状态发生变化,清除相应的状态标志,如果是由于EMI电磁干扰引起的状态变化,则设置connect_change变量为1; 
……

经过一系列列port判断,如果发现port的连接状态发生变化或由于EMI导致连接使能发生变化,即connect_change=1,则调用hub_port_connect_change.


/* Handle physical or logical connection change events.
 * This routine is called when:
 *  a port connection-change occurs;
 *  a port enable-change occurs (often caused by EMI);
 *  usb_reset_and_verify_device() encounters changed descriptors (as from
 *      a firmware download)
 * caller already locked the hub
 */
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;
    int status, i;
    unsigned unit_load;

    dev_dbg (hub_dev,
        "port %d, status %04x, change %04x, %s\n",
        port1, portstatus, portchange, portspeed(hub, portstatus));

    if (hub->has_indicators) {//如果当前的hub支持indicator指示灯,则设备指示灯为HUB_LED_AUTO
        set_port_led(hub, port1, HUB_LED_AUTO);
        hub->indicator[port1-1] = INDICATOR_AUTO;
    }

#ifdef  CONFIG_USB_OTG
    /* during HNP, don't repeat the debounce */
    if (hdev->bus->is_b_host)
        portchange &= ~(USB_PORT_STAT_C_CONNECTION |
                USB_PORT_STAT_C_ENABLE);
#endif

    /* Try to resuscitate an existing device */
    udev = hub->ports[port1 - 1]->child;
    if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
            udev->state != USB_STATE_NOTATTACHED) {
        usb_lock_device(udev);
        if (portstatus & USB_PORT_STAT_ENABLE) {
            status = 0;     /* Nothing to do */

#ifdef CONFIG_PM_RUNTIME
        } else if (udev->state == USB_STATE_SUSPENDED &&
                udev->persist_enabled) {
            /* For a suspended device, treat this as a
             * remote wakeup event.
             */
            status = usb_remote_wakeup(udev);
#endif

        } else {
            status = -ENODEV;   /* Don't resuscitate */
        }
        usb_unlock_device(udev);

        if (status == 0) {  //清除当前port的change_bits标志
            clear_bit(port1, hub->change_bits);
            return;
        }
    }

    /* Disconnect any existing devices under this port */
    if (udev) {
        if (hcd->phy && !hdev->parent &&
                !(portstatus & USB_PORT_STAT_CONNECTION))
            usb_phy_notify_disconnect(hcd->phy, udev->speed);
        usb_disconnect(&hub->ports[port1 - 1]->child);
    }
    clear_bit(port1, hub->change_bits);

    /* We can forget about a "removed" device when there's a physical
     * disconnect or the connect status changes.
     */
    if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
            (portchange & USB_PORT_STAT_C_CONNECTION))
        clear_bit(port1, hub->removed_bits);

    if (portchange & (USB_PORT_STAT_C_CONNECTION |
                USB_PORT_STAT_C_ENABLE)) {
        status = hub_port_debounce_be_stable(hub, port1);
        if (status < 0) {
            if (status != -ENODEV && printk_ratelimit())
                dev_err(hub_dev, "connect-debounce failed, "
                        "port %d disabled\n", port1);
            portstatus &= ~USB_PORT_STAT_CONNECTION;
        } else {
            portstatus = status;
        }
    }

    /* Return now if debouncing failed or nothing is connected or
     * the device was "removed".
     */
    if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
            test_bit(port1, hub->removed_bits)) {

        /* maybe switch power back on (e.g. root hub was reset) */
        if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
                && !port_is_power_on(hub, portstatus))
            set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);

        if (portstatus & USB_PORT_STAT_ENABLE)
            goto done;
        return;
    }
    if (hub_is_superspeed(hub->hdev))
        unit_load = 150;
    else
        unit_load = 100;

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

        /* reallocate for each attempt, since references
         * to the previous one can escape in various ways
         * 通过usb_alloc_dev为新的USB设备申请资源,并进行一些初始化,
         * 将usb设置为USB_STATE_ATTACHED,表示设备已经连接
         */
        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_set_device_state(udev, USB_STATE_POWERED);//设置usb设备为USB_STATE_POWERED态
        udev->bus_mA = hub->mA_per_port;//设置usb设备可以从port上获取的电流量
        udev->level = hdev->level + 1;,//设置usb设备的拓扑层级
        udev->wusb = hub_is_wusb(hub);

        /* Only USB 3.0 devices are connected to SuperSpeed hubs. */
        //如果hub支持超速,则设置usb设备的速度为超速,否则设置usb设备速度 为unknow
        if (hub_is_superspeed(hub->hdev))
            udev->speed = USB_SPEED_SUPER;
        else
            udev->speed = USB_SPEED_UNKNOWN;

        choose_devnum(udev);  //从usb 总线中找到一个usb地址
        if (udev->devnum <= 0) {
            status = -ENOTCONN; /* Don't retry */
            goto loop;
        }

        /* reset (non-USB 3.0 devices) and get descriptor */
        status = hub_port_init(hub, udev, port1, i);
        if (status < 0)
            goto loop;

        usb_detect_quirks(udev);
        if (udev->quirks & USB_QUIRK_DELAY_INIT)
            msleep(1000);

        /* 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.
         */ 
         //如果当前的usb设备是一个hub,它由hub供电
        if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
                && udev->bus_mA <= unit_load) {
            u16 devstat;

            status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
                    &devstat);
            if (status < 2) {
                dev_dbg(&udev->dev, "get status %d ?\n", status);
                goto loop_disable;
            }
            le16_to_cpus(&devstat);
            if ((devstat & (1 << 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 */ 
        //如果usb设备支持高速运行,而现在却工作在全速,它就会通过点亮绿色指示灯来指示这种错误
        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
            hub->ports[port1 - 1]->child = udev;
        spin_unlock_irq(&device_state_lock);

        /* Run it through the hoops (find a driver, etc) */
        if (!status) {
            status = usb_new_device(udev);//通过usb_new_device去获取usb设备的配置信息,将它注册到系统中
            if (status) {
                spin_lock_irq(&device_state_lock);
                hub->ports[port1 - 1]->child = 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_devnum(udev);
        hub_free_dev(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)) {
        if (status != -ENOTCONN && status != -ENODEV)
            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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256

hub_port_connect_change分为两部分: 
第一部分主要是:确认是否有新设备插入; 
第二部分主要是:确认port口上有新设备插入后,分配设备资源,并进行枚举操作;

对于一些已经连接的设备,进行一些类似防抖动处理,处理机制:每隔25ms去读取port的status和change状态,查看port连接状态是否稳定,如果port处于稳定状态大于100ms,则认为当前port上的设备已经稳定,这种处理机制最长时间为1.5s;如果port处于稳定状态的时间小于100ms,则认为连接是不稳定的,则跳出当前port,去执行下一个port;

对于一个新插入的设备,并已经确定它的存在,则接下来会为这个usb设备进行枚举

一条usb总线下总共可以有128个设备,usb总线通过位图的形式来管理usb设备的地址,choose_devnum是从usb 总线中找到一个usb地址,usb地址在1和128之间; 
通过hub_port_init对usb设备进行reset,并设置usb设备的地址,获取usb设备的设备描述符,这个函数相对比较重要,所以对它进行了进一步分析:


/* Reset device, (re)assign address, get device descriptor.
 * Device connection must be stable, no more debouncing needed.
 * Returns device in USB_STATE_ADDRESS, except on error.
 *
 * If this is called for an already-existing device (as part of
 * usb_reset_and_verify_device), the caller must own the device lock.  For a
 * newly detected device that is not accessible through any global
 * pointers, it's not necessary to lock the device.
 */
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        int retry_counter)
{
    static DEFINE_MUTEX(usb_address0_mutex);

    struct usb_device   *hdev = hub->hdev;
    struct usb_hcd      *hcd = bus_to_hcd(hdev->bus);
    int         i, j, retval;
    unsigned        delay = HUB_SHORT_RESET_TIME;
    enum usb_device_speed   oldspeed = udev->speed;
    const char      *speed;
    int         devnum = udev->devnum;

    /* root hub ports have a slightly longer reset period
     * (from USB 2.0 spec, section 7.1.7.5)
     * 设置用于读取port状态的时间间隔,root hub需要50ms,普通的hub需要10ms,
     * 对于低速的usb设备需要200ms
     */
    if (!hdev->parent) {
        delay = HUB_ROOT_RESET_TIME;
        if (port1 == hdev->bus->otg_port)
            hdev->bus->b_hnp_enable = 0;
    }

    /* Some low speed devices have problems with the quick delay, so */
    /*  be a bit pessimistic with those devices. RHbug #23670 */
    if (oldspeed == USB_SPEED_LOW)
        delay = HUB_LONG_RESET_TIME;

    mutex_lock(&usb_address0_mutex);

    /* Reset the device; full speed may morph to high speed */
    /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ 
    //通过hub_port_reset来reset设备
    retval = hub_port_reset(hub, port1, udev, delay, false);
    if (retval < 0)     /* error or disconnect */
        goto fail;
    /* success, speed is known */

    retval = -ENODEV;

    if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
        dev_dbg(&udev->dev, "device reset changed speed!\n");
        goto fail;
    }
    oldspeed = udev->speed;

    /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
     * it's fixed size except for full speed devices.
     * For Wireless USB devices, ep0 max packet is always 512 (tho
     * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
     * 根据usb设备的速度来初始化endpont0的最大发送数据长度,
     * 对于无线usb其maxpacketsize为512字节,对于高速和全速为64字节,而低速为8个字节
     */
    switch (udev->speed) {
    case USB_SPEED_SUPER:
    case USB_SPEED_WIRELESS:    /* fixed at 512 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
        break;
    case USB_SPEED_HIGH:        /* fixed at 64 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
        break;
    case USB_SPEED_FULL:        /* 8, 16, 32, or 64 */
        /* to determine the ep0 maxpacket size, try to read
         * the device descriptor to get bMaxPacketSize0 and
         * then correct our initial guess.
         */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
        break;
    case USB_SPEED_LOW:     /* fixed at 8 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
        break;
    default:
        goto fail;
    }

    if (udev->speed == USB_SPEED_WIRELESS)
        speed = "variable speed Wireless";
    else
        speed = usb_speed_string(udev->speed);

    if (udev->speed != USB_SPEED_SUPER)
        dev_info(&udev->dev,
                "%s %s USB device number %d using %s\n",
                (udev->config) ? "reset" : "new", speed,
                devnum, udev->bus->controller->driver->name);

    /* Set up TT records, if needed  */ 
    //如果hub为高速,而usb设备为低速或全速,则在它们之间需要有一个速度转换设备tt
    if (hdev->tt) {
        udev->tt = hdev->tt;
        udev->ttport = hdev->ttport;
    } else if (udev->speed != USB_SPEED_HIGH
            && hdev->speed == USB_SPEED_HIGH) {
        if (!hub->tt.hub) {
            dev_err(&udev->dev, "parent hub has no TT\n");
            retval = -EINVAL;
            goto fail;
        }
        udev->tt = &hub->tt;
        udev->ttport = port1;
    }

    /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
     * Because device hardware and firmware is sometimes buggy in
     * this area, and this is how Linux has done it for ages.
     * Change it cautiously.
     *
     * NOTE:  If USE_NEW_SCHEME() is true we will start by issuing
     * a 64-byte GET_DESCRIPTOR request.  This is what Windows does,
     * so it may help with some non-standards-compliant devices.
     * Otherwise we start with SET_ADDRESS and then try to read the
     * first 8 bytes of the device descriptor to get the ep0 maxpacket
     * value.
     */
    for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
        if (USE_NEW_SCHEME(retry_counter) &&
            !(hcd->driver->flags & HCD_USB3) &&
            !((hcd->driver->flags & HCD_RT_OLD_ENUM) &&
                !hdev->parent)) {
            struct usb_device_descriptor *buf;
            ushort idvendor;
            int r = 0;

#define GET_DESCRIPTOR_BUFSIZE  64
            buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
            if (!buf) {
                retval = -ENOMEM;
                continue;
            }

            /* Retry on all errors; some devices are flakey.
             * 255 is for WUSB devices, we actually need to use
             * 512 (WUSB1.0[4.8.1]).
             */
            for (j = 0; j < 3; ++j) {
                buf->bMaxPacketSize0 = 0;
                r = usb_control_msg(udev, usb_rcvaddr0pipe(),
                    USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                    USB_DT_DEVICE << 8, 0,
                    buf, GET_DESCRIPTOR_BUFSIZE,
                    initial_descriptor_timeout);
                switch (buf->bMaxPacketSize0) {
                case 8: case 16: case 32: case 64: case 255:
                    if (buf->bDescriptorType ==
                            USB_DT_DEVICE) {
                        r = 0;
                        break;
                    }
                    /* FALL THROUGH */
                default:
                    if (r == 0)
                        r = -EPROTO;
                    break;
                }
                if (r == 0)
                    break;
            }
            udev->descriptor.bMaxPacketSize0 =
                    buf->bMaxPacketSize0;
            idvendor = le16_to_cpu(buf->idVendor);
            kfree(buf);

            /*
             * If it is a HSET Test device, we don't issue a
             * second reset which results in failure due to
             * speed change.
             */
            if (idvendor != 0x1a0a) {
                retval = hub_port_reset(hub, port1, udev,
                             delay, false);
                if (retval < 0) /* error or disconnect */
                    goto fail;
                if (oldspeed != udev->speed) {
                    dev_dbg(&udev->dev,
                           "device reset changed speed!\n");
                    retval = -ENODEV;
                    goto fail;
                }
            }
            if (r) {
                if (r != -ENODEV)
                    dev_err(&udev->dev, "device descriptor read/64, error %d\n",
                            r);
                retval = -EMSGSIZE;
                continue;
            }
#undef GET_DESCRIPTOR_BUFSIZE
        }

        /*
         * If device is WUSB, we already assigned an
         * unauthorized address in the Connect Ack sequence;
         * authorization will assign the final address.
         */
        if (udev->wusb == 0) {
            for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
                retval = hub_set_address(udev, devnum);
                if (retval >= 0)
                    break;
                msleep(200);
            }
            if (retval < 0) {
                if (retval != -ENODEV)
                    dev_err(&udev->dev, "device not accepting address %d, error %d\n",
                            devnum, retval);
                goto fail;
            }
            if (udev->speed == USB_SPEED_SUPER) {
                devnum = udev->devnum;
                dev_info(&udev->dev,
                        "%s SuperSpeed USB device number %d using %s\n",
                        (udev->config) ? "reset" : "new",
                        devnum, udev->bus->controller->driver->name);
            }

            /* cope with hardware quirkiness:
             *  - let SET_ADDRESS settle, some device hardware wants it
             *  - read ep0 maxpacket even for high and low speed,
             */
            msleep(10);
            if (USE_NEW_SCHEME(retry_counter) &&
                !(hcd->driver->flags & HCD_USB3) &&
                !((hcd->driver->flags & HCD_RT_OLD_ENUM) &&
                    !hdev->parent))
                break;
        }

        retval = usb_get_device_descriptor(udev, 8);
        if (retval < 8) {
            if (retval != -ENODEV)
                dev_err(&udev->dev,
                    "device descriptor read/8, error %d\n",
                    retval);
            if (retval >= 0)
                retval = -EMSGSIZE;
        } else {
            retval = 0;
            break;
        }
    }
    if (retval)
        goto fail;

    if (hcd->phy && !hdev->parent)
        usb_phy_notify_connect(hcd->phy, udev->speed);

    /*
     * Some superspeed devices have finished the link training process
     * and attached to a superspeed hub port, but the device descriptor
     * got from those devices show they aren't superspeed devices. Warm
     * reset the port attached by the devices can fix them.
     */
    if ((udev->speed == USB_SPEED_SUPER) &&
            (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
        dev_err(&udev->dev, "got a wrong device descriptor, "
                "warm reset device\n");
        hub_port_reset(hub, port1, udev,
                HUB_BH_RESET_TIME, true);
        retval = -EINVAL;
        goto fail;
    }

    if (udev->descriptor.bMaxPacketSize0 == 0xff ||
            udev->speed == USB_SPEED_SUPER)
        i = 512;
    else
        i = udev->descriptor.bMaxPacketSize0;
    if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
        if (udev->speed == USB_SPEED_LOW ||
                !(i == 8 || i == 16 || i == 32 || i == 64)) {
            dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
            retval = -EMSGSIZE;
            goto fail;
        }
        if (udev->speed == USB_SPEED_FULL)
            dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
        else
            dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
        usb_ep0_reinit(udev);
    }

      //根据maxpacketsize去获取完整的设备描述符
    retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
    if (retval < (signed)sizeof(udev->descriptor)) {
        if (retval != -ENODEV)
            dev_err(&udev->dev, "device descriptor read/all, error %d\n",
                    retval);
        if (retval >= 0)
            retval = -ENOMSG;
        goto fail;
    }

    if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
        retval = usb_get_bos_descriptor(udev);
        if (!retval) {
            udev->lpm_capable = usb_device_supports_lpm(udev);
            usb_set_lpm_parameters(udev);
        }
    }

    retval = 0;
    /* notify HCD that we have a device connected and addressed */
    if (hcd->driver->update_device)
        hcd->driver->update_device(hcd, udev);
fail:
    if (retval) {
        hub_port_disable(hub, port1, 0);
        update_devnum(udev, devnum);    /* for disconnect processing */
    }
    mutex_unlock(&usb_address0_mutex);
    return retval;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326

hub_port_init首先对port进行reset,复位成功后设备usb设备的速度和端口0最大发送数据长度,设置usb设备的地址,并获取usb设备的设备描述符; 
通过hub_port_reset来reset设备,reset机制为:通过set_port_feature来传输USB_PORT_FEAT_RESET指令,设置成功后循环延时由之前确定的时间间隔后去读取port的status和change状态,要确保usb设备在reset后还能正常存在,如reset还能正常,则通过port的status状态位来确定usb设备的速度,循环延时总时间为500ms,而usb设备reset次数为5次, 
根据usb设备的速度来初始化endpont0的最大发送数据长度,对于无线usb其maxpacketsize为512字节,对于高速和全速为64字节,而低速为8个字节; 
如果hub为高速,而usb设备为低速或全速,则在它们之间需要有一个速度转换设备tt;

通过获取usb设备的设备描述符来得到usb设备的endpoint0的最大发送数据长度,设备描述符实际 上有18个字节,而endpoint0的maxpacketsize在设备描述符里的第8个字节位,所以只要获取设备描述符的前8个字节数据就可以了,但有些USB设备它并不支持只获取8个字节这样不完整的usb请求,为解决这种问题usb驱动里采用了两种策略去获取maxpacketsize,一种是windows作法,它直接向usb设备请求64字节数据,对于一些maxpacketsize为32或64的,它可以直接一次性把18个字节的设备描述符传输回来,而对于像low speed的设备它的maxpacketsize只有8个字节,就需要进行多次发送; 而Linux作法是先设置usb设备的地址,然后再发送8个字节数据请求,从返回的8个字节里获取endpoint0的maxpacketsize;


/**
 * usb_new_device - perform initial device setup (usbcore-internal)
 * @udev: newly addressed device (in ADDRESS state)
 *
 * This is called with devices which have been detected but not fully
 * enumerated.  The device descriptor is available, but not descriptors
 * for any device configuration.  The caller must have locked either
 * the parent hub (if udev is a normal device) or else the
 * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
 * udev has already been installed, but udev is not yet visible through
 * sysfs or other filesystem code.
 *
 * It will return if the device is configured properly or not.  Zero if
 * the interface was registered with the driver core; else a negative
 * errno value.
 *
 * This call is synchronous, and may not be used in an interrupt context.
 *
 * Only the hub driver or root-hub registrar should ever call this.
 */
int usb_new_device(struct usb_device *udev)
{
    int err;

    if (udev->parent) {
        /* Initialize non-root-hub device wakeup to disabled;
         * device (un)configuration controls wakeup capable
         * sysfs power/wakeup controls wakeup enabled/disabled
         */
        device_init_wakeup(&udev->dev, 0);
    }

    /* Tell the runtime-PM framework the device is active */
    pm_runtime_set_active(&udev->dev);
    pm_runtime_get_noresume(&udev->dev);
    pm_runtime_use_autosuspend(&udev->dev);
    pm_runtime_enable(&udev->dev);

    /* By default, forbid autosuspend for all devices.  It will be
     * allowed for hubs during binding.
     */
    usb_disable_autosuspend(udev);

    err = usb_enumerate_device(udev);   /* Read descriptors */
    if (err < 0)
        goto fail;
    dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
            udev->devnum, udev->bus->busnum,
            (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
    /* export the usbdev device-node for libusb */
    udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
            (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));

    /* Tell the world! */
    announce_device(udev);

    if (udev->serial)
        add_device_randomness(udev->serial, strlen(udev->serial));
    if (udev->product)
        add_device_randomness(udev->product, strlen(udev->product));
    if (udev->manufacturer)
        add_device_randomness(udev->manufacturer,
                      strlen(udev->manufacturer));

    device_enable_async_suspend(&udev->dev);

    /*
     * check whether the hub marks this port as non-removable. Do it
     * now so that platform-specific data can override it in
     * device_add()
     */
    if (udev->parent)
        set_usb_port_removable(udev);

    /* Register the device.  The device driver is responsible
     * for configuring the device and invoking the add-device
     * notifier chain (used by usbfs and possibly others).
     */
    err = device_add(&udev->dev);
    if (err) {
        dev_err(&udev->dev, "can't device_add, error %d\n", err);
        goto fail;
    }

    /* Create link files between child device and usb port device. */
    if (udev->parent) {
        struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
        struct usb_port *port_dev;

        if (!hub)
            goto fail;

        port_dev = hub->ports[udev->portnum - 1];
        if (!port_dev)
            goto fail;

        err = sysfs_create_link(&udev->dev.kobj,
                &port_dev->dev.kobj, "port");
        if (err)
            goto fail;

        err = sysfs_create_link(&port_dev->dev.kobj,
                &udev->dev.kobj, "device");
        if (err) {
            sysfs_remove_link(&udev->dev.kobj, "port");
            goto fail;
        }

        pm_runtime_get_sync(&port_dev->dev);
    }

    (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
    usb_mark_last_busy(udev);
    pm_runtime_put_sync_autosuspend(&udev->dev);
    return err;

fail:
    usb_set_device_state(udev, USB_STATE_NOTATTACHED);
    pm_runtime_disable(&udev->dev);
    pm_runtime_set_suspended(&udev->dev);
    return err;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123

usb_new_device主是要获取usb设备的配置信息,然后将usb设备添加到系统中,这里usb_enumerate_device相对比较重要,就是它完成了配置信息获取及分配;



/**
 * usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal)
 * @udev: newly addressed device (in ADDRESS state)
 *
 * This is only called by usb_new_device() and usb_authorize_device()
 * and FIXME -- all comments that apply to them apply here wrt to
 * environment.
 *
 * If the device is WUSB and not authorized, we don't attempt to read
 * the string descriptors, as they will be errored out by the device
 * until it has been authorized.
 */
static int usb_enumerate_device(struct usb_device *udev)
{
    int err;

    if (udev->config == NULL) {
        err = usb_get_configuration(udev);
        if (err < 0) {
            if (err != -ENODEV)
                dev_err(&udev->dev, "can't read configurations, error %d\n",
                        err);
            return err;
        }
    }

    /* read the standard strings and cache them if present */
    udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
    udev->manufacturer = usb_cache_string(udev,
                          udev->descriptor.iManufacturer);
    udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);

    err = usb_enumerate_device_otg(udev);
    if (err < 0)
        return err;

    usb_detect_interface_quirks(udev);

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

通过usb_get_configuration获取和分配usb设备的配置,接口,端口信息



/*
 * Get the USB config descriptors, cache and parse'em
 *
 * hub-only!! ... and only in reset path, or usb_new_device()
 * (used by real hubs and virtual root hubs)
 */
int usb_get_configuration(struct usb_device *dev)
{
    struct device *ddev = &dev->dev;
    int ncfg = dev->descriptor.bNumConfigurations;
    int result = 0;
    unsigned int cfgno, length;
    unsigned char *bigbuffer;
    struct usb_config_descriptor *desc;

    cfgno = 0;
    result = -ENOMEM; 
    //如果usb设备的配置个数大于最大允许配置数8,则发出警告,并把设备的配置个数改成最大允许配置数8
    if (ncfg > USB_MAXCONFIG) {
        dev_warn(ddev, "too many configurations: %d, "
            "using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
        dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
    }

    //如果usb设备没有配置,则不允许,直接返回错误信息
    if (ncfg < 1) {
        dev_err(ddev, "no configurations\n");
        return -EINVAL;
    }

    //根据配置数,申请usb配置结构usb_host_config,并为用于存放配置结构的指针数据申请空间
    length = ncfg * sizeof(struct usb_host_config);
    dev->config = kzalloc(length, GFP_KERNEL);
    if (!dev->config)
        goto err2;

    length = ncfg * sizeof(char *);
    dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
    if (!dev->rawdescriptors)
        goto err2;

    //申请用于暂时存放配置描述符的结构空间
    desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
    if (!desc)
        goto err2;

    result = 0;
    //依次获取usb设备的配置信息
    for (; cfgno < ncfg; cfgno++) {
        /* We grab just the first descriptor so we know how long
         * the whole configuration is */
        result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
            desc, USB_DT_CONFIG_SIZE);
        if (result < 0) {
            dev_err(ddev, "unable to read config index %d "
                "descriptor/%s: %d\n", cfgno, "start", result);
            if (result != -EPIPE)
                goto err;
            dev_err(ddev, "chopping to %d config(s)\n", cfgno);
            dev->descriptor.bNumConfigurations = cfgno;
            break;
        } else if (result < 4) {
            dev_err(ddev, "config index %d descriptor too short "
                "(expected %i, got %i)\n", cfgno,
                USB_DT_CONFIG_SIZE, result);
            result = -EINVAL;
            goto err;
        }
        length = max((int) le16_to_cpu(desc->wTotalLength),
            USB_DT_CONFIG_SIZE);//得到wTotallLength

        /* Now that we know the length, get the whole thing */ 
        //根据得到的wTotallLength,申请用于存放这些信息的内存空间
        bigbuffer = kmalloc(length, GFP_KERNEL);
        if (!bigbuffer) {
            result = -ENOMEM;
            goto err;
        }

        if (dev->quirks & USB_QUIRK_DELAY_INIT)
            msleep(100);

        //根据得到的wTotallLength,去获取完整的配置,接口,端口等描述符信息,把这些信息存放到bigbuffer里
        result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
            bigbuffer, length);
        if (result < 0) {
            dev_err(ddev, "unable to read config index %d "
                "descriptor/%s\n", cfgno, "all");
            kfree(bigbuffer);
            goto err;
        }
        if (result < length) {
            dev_warn(ddev, "config index %d descriptor too short "
                "(expected %i, got %i)\n", cfgno, length, result);
            length = result;
        }

    //把用于存放配置等信息的地址保存在之前申请的rawdescriptors里
        dev->rawdescriptors[cfgno] = bigbuffer;

    //通过usb_parse_configuration函数,把获得的配置等信息按照类型进行分类 
        result = usb_parse_configuration(dev, cfgno,
            &dev->config[cfgno], bigbuffer, length);
        if (result < 0) {
            ++cfgno;
            goto err;
        }
    }
    result = 0;

err:
    kfree(desc);
    dev->descriptor.bNumConfigurations = cfgno;
err2:
    if (result == -ENOMEM)
        dev_err(ddev, "out of memory\n");
    return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121

这个函数主要分成二部分,第一部分是向usb device侧获取设备的配置,接口,端口信息。首先,它为这些信息申请存放空间,然后像之前获取设备描述符一样,先发送一个9 个字节的请求,用来获取配置,接口,端口等描述符的总长度,最后根据得到的总长度去得到完成 的设备配置,接口,端口信息;第二部分是把获取到的这个信息按照类别分别进行分类,并存到相应的结构中;

通过usb_parse_configuration函数,把获得的配置等信息按照类型进行分类


static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
    struct usb_host_config *config, unsigned char *buffer, int size)
{
    struct device *ddev = &dev->dev;
    unsigned char *buffer0 = buffer;
    int cfgno;
    int nintf, nintf_orig;
    int i, j, n;
    struct usb_interface_cache *intfc;
    unsigned char *buffer2;
    int size2;
    struct usb_descriptor_header *header;
    int len, retval;
    u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
    unsigned iad_num = 0;

    memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
    if (config->desc.bDescriptorType != USB_DT_CONFIG ||
        config->desc.bLength < USB_DT_CONFIG_SIZE ||
        config->desc.bLength > size) {
        dev_err(ddev, "invalid descriptor for config index %d: "
            "type = 0x%X, length = %d\n", cfgidx,
            config->desc.bDescriptorType, config->desc.bLength);
        return -EINVAL;
    }
    cfgno = config->desc.bConfigurationValue;

    buffer += config->desc.bLength;
    size -= config->desc.bLength;

    nintf = nintf_orig = config->desc.bNumInterfaces;
    if (nintf > USB_MAXINTERFACES) {
        dev_warn(ddev, "config %d has too many interfaces: %d, "
            "using maximum allowed: %d\n",
            cfgno, nintf, USB_MAXINTERFACES);
        nintf = USB_MAXINTERFACES;
    }

    /* Go through the descriptors, checking their length and counting the
     * number of altsettings for each interface */
    n = 0;
    for ((buffer2 = buffer, size2 = size);
          size2 > 0;
         (buffer2 += header->bLength, size2 -= header->bLength)) {

        if (size2 < sizeof(struct usb_descriptor_header)) {
            dev_warn(ddev, "config %d descriptor has %d excess "
                "byte%s, ignoring\n",
                cfgno, size2, plural(size2));
            break;
        }

        header = (struct usb_descriptor_header *) buffer2;
        if ((header->bLength > size2) || (header->bLength < 2)) {
            dev_warn(ddev, "config %d has an invalid descriptor "
                "of length %d, skipping remainder of the config\n",
                cfgno, header->bLength);
            break;
        }

        if (header->bDescriptorType == USB_DT_INTERFACE) {
            struct usb_interface_descriptor *d;
            int inum;

            d = (struct usb_interface_descriptor *) header;
            if (d->bLength < USB_DT_INTERFACE_SIZE) {
                dev_warn(ddev, "config %d has an invalid "
                    "interface descriptor of length %d, "
                    "skipping\n", cfgno, d->bLength);
                continue;
            }

            inum = d->bInterfaceNumber;

            if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) &&
                n >= nintf_orig) {
                dev_warn(ddev, "config %d has more interface "
                    "descriptors, than it declares in "
                    "bNumInterfaces, ignoring interface "
                    "number: %d\n", cfgno, inum);
                continue;
            }

            if (inum >= nintf_orig)
                dev_warn(ddev, "config %d has an invalid "
                    "interface number: %d but max is %d\n",
                    cfgno, inum, nintf_orig - 1);

            /* Have we already encountered this interface?
             * Count its altsettings */
            for (i = 0; i < n; ++i) {
                if (inums[i] == inum)
                    break;
            }
            if (i < n) {
                if (nalts[i] < 255)
                    ++nalts[i];
            } else if (n < USB_MAXINTERFACES) {
                inums[n] = inum;
                nalts[n] = 1;
                ++n;
            }

        } else if (header->bDescriptorType ==
                USB_DT_INTERFACE_ASSOCIATION) {
            if (iad_num == USB_MAXIADS) {
                dev_warn(ddev, "found more Interface "
                           "Association Descriptors "
                           "than allocated for in "
                           "configuration %d\n", cfgno);
            } else {
                config->intf_assoc[iad_num] =
                    (struct usb_interface_assoc_descriptor
                    *)header;
                iad_num++;
            }

        } else if (header->bDescriptorType == USB_DT_DEVICE ||
                header->bDescriptorType == USB_DT_CONFIG)
            dev_warn(ddev, "config %d contains an unexpected "
                "descriptor of type 0x%X, skipping\n",
                cfgno, header->bDescriptorType);

    }   /* for ((buffer2 = buffer, size2 = size); ...) */
    size = buffer2 - buffer;
    config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);

    if (n != nintf)
        dev_warn(ddev, "config %d has %d interface%s, different from "
            "the descriptor's value: %d\n",
            cfgno, n, plural(n), nintf_orig);
    else if (n == 0)
        dev_warn(ddev, "config %d has no interfaces?\n", cfgno);
    config->desc.bNumInterfaces = nintf = n;

    /* Check for missing interface numbers */
    for (i = 0; i < nintf; ++i) {
        for (j = 0; j < nintf; ++j) {
            if (inums[j] == i)
                break;
        }
        if (j >= nintf)
            dev_warn(ddev, "config %d has no interface number "
                "%d\n", cfgno, i);
    }

    /* Allocate the usb_interface_caches and altsetting arrays */
    for (i = 0; i < nintf; ++i) {
        j = nalts[i];
        if (j > USB_MAXALTSETTING) {
            dev_warn(ddev, "too many alternate settings for "
                "config %d interface %d: %d, "
                "using maximum allowed: %d\n",
                cfgno, inums[i], j, USB_MAXALTSETTING);
            nalts[i] = j = USB_MAXALTSETTING;
        }

        len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
        config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL);
        if (!intfc)
            return -ENOMEM;
        kref_init(&intfc->ref);
    }

    /* FIXME: parse the BOS descriptor */

    /* Skip over any Class Specific or Vendor Specific descriptors;
     * find the first interface descriptor */
    config->extra = buffer;
    i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
        USB_DT_INTERFACE, &n);
    config->extralen = i;
    if (n > 0)
        dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
            n, plural(n), "configuration");
    buffer += i;
    size -= i;

    /* Parse all the interface/altsetting descriptors */
    while (size > 0) {
        retval = usb_parse_interface(ddev, cfgno, config,
            buffer, size, inums, nalts);
        if (retval < 0)
            return retval;

        buffer += retval;
        size -= retval;
    }

    /* Check for missing altsettings */
    for (i = 0; i < nintf; ++i) {
        intfc = config->intf_cache[i];
        for (j = 0; j < intfc->num_altsetting; ++j) {
            for (n = 0; n < intfc->num_altsetting; ++n) {
                if (intfc->altsetting[n].desc.
                    bAlternateSetting == j)
                    break;
            }
            if (n >= intfc->num_altsetting)
                dev_warn(ddev, "config %d interface %d has no "
                    "altsetting %d\n", cfgno, inums[i], j);
        }
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208

这里写图片描述


查看评论

USB枚举过程详细分析

  • 2017年02月10日 17:14
  • 214KB
  • 下载

linux usb枚举过程分析【host】

当守护程序第一次运行或usb port上状态发生变化,守护进程被唤醒都会运行hub_events函数,这个函数在usb系统中处理核心位置,usb的枚举过程就是由它完成,usb枚举过程流程图如图1所示;...
  • u011279649
  • u011279649
  • 2014-12-01 22:25:58
  • 1643

USB枚举过程分析

1. 枚举是什么?        枚举就是从设备读取一些信息,知道设备是什么样的设备,如何进行通信,这样主机就可以根据这些信息来加载合适的驱动程序。调试USB设备,很重要的一点就是USB的枚举过...
  • skyflying2012
  • skyflying2012
  • 2014-12-13 22:31:42
  • 4106

USB枚举的详细流程

附一个很好的枚举过程的详细流程: ◆ 用户将一个USB设备插入USB端口,主机为端口供电,设备此时处于上电状态。 ◆ 主机检测设备。 ◆ 集线器使用中断通道将事件报告给主机。 ◆ 主机发送Ge...
  • amd123linux
  • amd123linux
  • 2013-09-18 09:54:17
  • 960

USB 枚举过程详解

USB 枚举过程详解
  • zoosenpin
  • zoosenpin
  • 2014-06-10 08:51:04
  • 2439

基于STM32的USB枚举过程学习笔记(二)

接下来介绍USB设备的枚举,枚举就是从设备读取各种描述符信息,这样主机就可以根据这些信息来加载合适的驱动,从而知道是什么样的设备,如何进行通信。 枚举过程使用的是控制传输。控制传输可以保证数据的正确性...
  • qq236106303
  • qq236106303
  • 2012-11-13 11:58:50
  • 7376

使用“USB协议分析仪”分析USB协议

  • 2010年03月17日 18:50
  • 335KB
  • 下载

实例讲解USB的枚举(配置)过程

所需要工具 USB Monitor2.26优盘一个   网上大量介绍用bus hound作监控软件,我们为什么不选bus hound而选USB Monitor呢,因为bus hound在Win...
  • michaelcao1980
  • michaelcao1980
  • 2016-09-30 10:31:05
  • 1589

两个常用的USB协议分析软件工具

  • 2013年09月15日 22:00
  • 4.66MB
  • 下载

网友USB枚举过程分析

USB设备检测的一般过程 USB设备检测也是通过/proc目录下的USB文件系统进行的。为了使一个USB设备能够正常工作,必须要现在系统中插入USB桥接器模块。在检测开始时,一般要先检测是否存在/p...
  • darennet
  • darennet
  • 2015-07-03 18:07:36
  • 1424
    个人资料
    等级:
    访问量: 1741
    积分: 76
    排名: 152万+
    文章存档