接下来开始讲设备的枚举过程。
前面分析了root hub感应设备插拔的过程。root hub还有一个功能是对新插入的设备进行枚举。其实root hub完成了枚举的前几个步骤:get descriptor 、 set address 、 get configuration。 枚举的后面几个步骤是由上层的class driver来完成的,这个后面在分析。
在前面的hub_port_connect_change中调用了hub_port_init中,我们可以看到这个枚举的过程。
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;
char *speed, *type;
int devnum = udev->devnum;
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
*/
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. */
retval = hub_port_reset(hub, port1, udev, delay);
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].
*/
switch (udev->speed) {
case USB_SPEED_SUPER:
case USB_SPEED_VARIABLE: /* 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;
}
type = "";
switch (udev->speed) {
case USB_SPEED_LOW: speed = "low"; break;
case USB_SPEED_FULL: speed = "full"; break;
case USB_SPEED_HIGH: speed = "high"; break;
case USB_SPEED_SUPER:
speed = "super";
break;
case USB_SPEED_VARIABLE:
speed = "variable";
type = "Wireless ";
break;
default: speed = "?"; break;
}
if (udev->speed != USB_SPEED_SUPER)
dev_info(&udev->dev,
"%s %s speed %sUSB device using %s and address %d\n",
(udev->config) ? "reset" : "new", speed, type,
udev->bus->controller->driver->name, devnum);
/* Set up TT records, if needed */
if (hdev->tt) {
udev->tt = hdev->tt;
udev->ttport = hdev->ttport;
} else if (udev->speed != USB_SPEED_HIGH
&& hdev->speed == USB_SPEED_HIGH) {
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))) {
/*
* An xHCI controller cannot send any packets to a device until
* a set address command successfully completes.
*/
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
struct usb_device_descriptor *buf;
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;
kfree(buf);
retval = hub_port_reset(hub, port1, udev, delay);
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) {
dev_err(&udev->dev,
"device descriptor read/64, error %d\n",
r);
retval = -EMSGSIZE;
continue;
}
#undef GET_DESCRIPTOR_BUFSIZE
}
retval = usb_get_device_descriptor(udev, 8);
break;
}
}
if (retval)
goto fail;
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
udev->speed == USB_SPEED_SUPER)
i = 512;
else
i = udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (udev->speed != USB_SPEED_FULL ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
dev_err(&udev->dev, "ep0 maxpacket = %d\n", i);
retval = -EMSGSIZE;
goto fail;
}
dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
retval);
if (retval >= 0)
retval = -ENOMSG;
goto fail;
}
retval = 0;
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
update_address(udev, devnum); /* for disconnect processing */
}
mutex_unlock(&usb_address0_mutex);
return retval;
}
上面这个函数中,主要调用了get descriptor 和 set address。并且实现了一个retry的机制,即会多次调用 get descriptor和setaddress,直到成功为止。
这里的set address是给device的udc设置地址,udc专门有一个address寄存器,用于存储它的地址。
此外,每个endpoint也有各自的地址,不知道那个地址是用来做什么的??
另外,对于新插入的设备,还会调用usb_new_device。在usb_add_device中会调用get configuration。
int usb_new_device(struct usb_device *udev)
{
int err;
/* Increment the parent's count of unsuspended children */
if (udev->parent)
usb_autoresume_device(udev->parent);
usb_detect_quirks(udev); /* Determine quirks */
err = usb_configure_device(udev); /* detect & probe dev/intfs */
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);
/* 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;
}
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
return err;
fail:
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
usb_stop_pm(udev);
return err;
}
在上面的usb_configure_device中会调用usb_get_configuration. 另外,可以看到usb_create_ep_devs. 也就是说 会给device的ep0也创建一个对应的设备。
到这里,usb root hub 对 设备的枚举过程就结束了。