usb枚举过程分析之hub_events
当守护程序第一次运行或usb port上状态发生变化,守护进程被唤醒都会运行hub_events函数,这个函数在usb系统中处理核心位置,usb的枚举过程就是由它完成,usb枚举过程流程图如图1所示;
图1 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; /* * 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); 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; }
hub_events本身也是一个死循环,只要条件满足它便会一直执行。
25-28行,判断hub_event_list是否为空,如果为空,则跳出循环,hub_events运行结束,会进入休眠状态;
31-35行,从hub_event_list列表中取出某一项,并把它从hub_event_list中删除,通过list_entry来获取event_list所对应的hub,并增加hub使用计数;
51-85行,做了一些逻辑判断,判断hub是否连接,hub上是否有错误,如果有错误就重启hub,如果没有错误,接下来就对hub 上的每个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); if (!test_and_clear_bit(i, hub->event_bits) && !connect_change) continue; ret = hub_port_status(hub, i, &portstatus, &portchange); if (ret < 0) continue; 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) { struct usb_device *udev; clear_port_feature(hdev, i, USB_PORT_FEAT_C_SUSPEND); udev = hdev->children[i-1]; if (udev) { /* TRSMRCY = 10 msec */ msleep(10); usb_lock_device(udev); ret = usb_remote_wakeup(hdev-> children[i-1]); usb_unlock_device(udev); 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) { u16 status = 0; u16 unused; dev_dbg(hub_dev, "over-current change on port " "%d\n", i); 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); 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); clear_port_feature(hdev, i, USB_PORT_FEAT_C_BH_PORT_RESET); } if (portchange & USB_PORT_STAT_C_LINK_STATE) { 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); 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_is_superspeed(hub->hdev) && (portstatus & USB_PORT_STAT_LINK_STATE) == USB_SS_PORT_LS_SS_INACTIVE) { dev_dbg(hub_dev, "warm reset port %d\n", i); hub_port_warm_reset(hub, i); } if (connect_change) hub_port_connect_change(hub, i, portstatus, portchange); } /* end for i */
本段程序主要是遍历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态,则需要断开设备;
2-12行,对这些标志进行判断,如果没有产生变化则跳过当前port,否则读取当前port的status,判断出产生状态变化的原因;
15-19行,表示port的连接状态发生变化,清除相应的端口状态,设置connect_change变量为1;
21-46行,表示port的使能状态发生变化,清除相应的状态标志,如果是由于EMI电磁干扰引起的状态变化,则设置connect_change为1;
47-70行,表示port的suspend状态发生变化,它由suspend转向resume态;
71-86行,表示port的过流状态发生变化;
88-93行,表示port reset完成;
95-123行,它们都是usb 3.0协议里才出现的,用来表示port link的状态;
如果发现port的连接状态发生变化或由于EMI导致连接使能发生变化,即connect_change=1,则调用hub_port_connect_change.
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; dev_dbg (hub_dev, "port %d, status %04x, change %04x, %s\n", port1, portstatus, portchange, portspeed(hub, portstatus)); if (hub->has_indicators) { 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 = hdev->children[port1-1]; 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_USB_SUSPEND } 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) { clear_bit(port1, hub->change_bits); return; } } /* Disconnect any existing devices under this port */ if (udev) usb_disconnect(&hdev->children[port1-1]); 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(hub, port1); if (status < 0) { if (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; } for (i = 0; i < SET_CONFIG_TRIES; i++) { /* 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_set_device_state(udev, USB_STATE_POWERED); udev->bus_mA = hub->mA_per_port; udev->level = hdev->level + 1; udev->wusb = hub_is_wusb(hub); /* Only USB 3.0 devices are connected to SuperSpeed hubs. */ if (hub_is_superspeed(hub->hdev)) udev->speed = USB_SPEED_SUPER; else udev->speed = USB_SPEED_UNKNOWN; choose_devnum(udev); 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. */ if (udev->descriptor.bDeviceClass == USB_CLASS_HUB && udev->bus_mA <= 100) { 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 */ 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) { 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_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)) 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); }
hub_port_connect_change分为两部分:第一部分主要是:确认是否有新设备插入;第二部分主要是:确认port口上有新设备插入后,分配设备资源,并进行枚举操作;
16-19行, 如果当前的hub支持indicator指示灯,则设备指示灯为HUB_LED_AUTO;
29-54行,试着去唤醒hub上一些处于suspend的设备,对于两类型设备它不会去执行枚举操作,一是usb设备本身已经使能,二是usb 设备处于suspend状态,并可以唤醒,对于这两类设备,它不需要去申请usb设备结构,所以在清除相应port的change_bits后,返回;
57-58行,hub port连接状态变化可以是从有到无,也可以是从无到有,对于后者这里不可能有udev,它以属于前者,把一些已经处于NOATTACHED的设备从hub中断开;
59行,清除当前port的change_bits标志;
68-95行,对于一些已经连接的设备,进行一些类似防抖动处理,处理机制:每隔25ms去读取port的status和change状态,查看port连接状态是否稳定,如果port处于稳定状态大于100ms,则认为当前port上的设备已经稳定,这种处理机制最长时间为1.5s;如果port处于稳定状态的时间小于100ms,则认为连接是不稳定的,则跳出当前port,去执行下一个port;
对于一个新插入的设备,并已经确定它的存在,则接下来会为这个usb设备进行枚举;
102-108行,通过usb_alloc_dev为新的USB设备申请资源,并进行一些初始化,将usb设置为USB_STATE_ATTACHED,表示设备已经连接;
110行,设置usb设备为USB_STATE_POWERED态;
111-113行,设置usb设备可以从port上获取的电流量,及usb设备的拓扑层级;
116-119行,如果hub支持超速,则设置usb设备的速度为超速,否则设置usb设备速度 为unknow;
121-125行,一条usb总线下总共可以有128个设备,usb总线通过位图的形式来管理usb设备的地址,choose_devnum是从usb 总线中找到一个usb地址,usb地址在1和128之间;
128行,通过hub_port_init对usb设备进行reset,并设置usb设备的地址,获取usb设备的设备描述符,这个函数相对比较重要,所以对它进行了进一步分析:
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_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; } 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_WIRELESS: speed = "variable"; type = "Wireless "; break; default: speed = "?"; break; } if (udev->speed != USB_SPEED_SUPER) dev_info(&udev->dev, "%s %s speed %sUSB device number %d using %s\n", (udev->config) ? "reset" : "new", speed, type, devnum, udev->bus->controller->driver->name); /* 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) { 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)) { 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 } /* * 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) { 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)) break; } retval = usb_get_device_descriptor(udev, 8); if (retval < 8) { 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 (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_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); } 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; /* 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; }
hub_port_init首先对port进行reset,复位成功后设备usb设备的速度和端口0最大发送数据长度,设置usb设备的地址,并获取usb设备的设备描述符;
18-27行,设置用于读取port状态的时间间隔,root hub需要50ms,普通的hub需要10ms,对于低速的usb设备需要200ms;
33行,通过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次,
51-72行,根据usb设备的速度来初始化endpont0的最大发送数据长度,对于无线usb其maxpacketsize为512字节,对于高速和全速为64字节,而低速为8个字节;
94-106行,如果hub为高速,而usb设备为低速或全速,则在它们之间需要有一个速度转换设备tt,这几行代码就是 用来设置TT的;
120-228行,通过获取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;
232-236行,根据usb设备描述符确定endpoint0的maxpacketsize,对于无线usb设备和超速usb设备,maxpacketsize为512,而其它类型就为从设备侧获取的maxpacketsize;
237-250行,如果初始化时设置的maxpacketsize和设备描述符里的maxpacketsize不一样,则将endpint0 maxpacketsize设置成和设备描述符里的一样;
252-259行,根据maxpacketsize去获取完整的设备描述符;
在取得usb的设备描述符后,返回到hub_port_connect_change函数,接下来根据设备描述符对usb设备合法性进行进一步判断,如果合法则去获取这个设备的配置,接口,端口描述符,并将usb设备注册到系统中,为它找到合适的设备驱动;
142-165行,如果当前的usb设备是一个hub,它由hub供电,而它从上一级hub上分配到的电流少于100mA,这种情况 是不允许的,它会通过点亮AMBER指示灯来显示出现这种错误,指示灯由延时任务队列完成;
168-171行,如果usb设备支持高速运行,而现在却工作在全速,它就会通过点亮绿色指示灯来指示这种错误;
187行,如果通过这些判断后,usb设备还是正常的, 则将它放到用于表示hub每个port的指针数据hub->children里,并通过usb_new_device去获取usb设备的配置信息,最后将它注册到系统中;
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); device_enable_async_suspend(&udev->dev); /* 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); 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; }
usb_new_device主是要获取usb设备的配置信息,然后将usb设备添加到系统中,这里usb_enumerate_device相对比较得要,就是它完成了配置信息获取及分配;
static int usb_enumerate_device(struct usb_device *udev) { int err; if (udev->config == NULL) { err = usb_get_configuration(udev); if (err < 0) { dev_err(&udev->dev, "can't read configurations, error %d\n", err); goto fail; } } if (udev->wusb == 1 && udev->authorized == 0) { udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL); udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL); udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL); } else { /* 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); fail: return err; }
5-12行,通过usb_get_configuration获取和分配usb设备的配置,接口,端口信息;
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; if (dev->authorized == 0) /* Not really an error */ goto out_not_authorized; result = -ENOMEM; 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; } if (ncfg < 1) { dev_err(ddev, "no configurations\n"); return -EINVAL; } 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; 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); 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); /* Now that we know the length, get the whole thing */ bigbuffer = kmalloc(length, GFP_KERNEL); if (!bigbuffer) { result = -ENOMEM; goto err; } 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; } dev->rawdescriptors[cfgno] = bigbuffer; result = usb_parse_configuration(dev, cfgno, &dev->config[cfgno], bigbuffer, length); if (result < 0) { ++cfgno; goto err; } } result = 0; err: kfree(desc); out_not_authorized: dev->descriptor.bNumConfigurations = cfgno; err2: if (result == -ENOMEM) dev_err(ddev, "out of memory\n"); return result; }
这个函数主要分成二部分,第一部分是向usb device侧获取设备的配置,接口,端口信息,这部分由第1-79行完成,首先,它为这些信息申请存放空间,然后像之前获取设备描述符一样,先发送一个9 个字节的请求,用来获取配置,接口,端口等描述符的总长度,最后根据得到的总长度去得到完成 的设备配置,接口,端口信息;第二部分是把获取到的这个信息按照类别分别进行分类,并存到相应的结构中;
11-12行,如果usb设备没有得到授权,则直接退出;
14-18行,如果usb设备的配置个数大于最大允许配置数8,则发出警告,并把设备的配置个数改成最大允许配置数8
20-23行,如果usb设备没有配置,则不允许,直接返回错误信息;
25-33行,根据配置数,申请usb配置结构usb_host_config,并为用于存放配置结构的指针数据申请空间;
35-37行,申请用于暂时存放配置描述符的结构空间;
40-89行,依次获取usb设备的配置信息;
43-57行,由于接口,端口,产家专有等描述符信息是直接跟在配置描述符后面的,配置描述符进里有一项wTotalLength,专门用来表示配置,接口,端口,产品和产家专有等描述符的总长度,所以它先获取配置描述符信息,从而得到总长度;wTotallLength在配置描述符里的第3-4个字节位置,所以在获取配置描述时,只要得到的数据大于等于4个字节就可以,但如果少于4字节,就表示出错;
58-59行,得到wTotallLength;
62-66行,根据得到的wTotallLength,申请用于存放这些信息的内存空间;
67-74行,根据得到的wTotallLength,去获取完整的配置,接口,端口等描述符信息,把这些信息存放到bigbuffer里;
81行,把用于存放配置等信息的地址保存在之前申请的rawdescriptors里;
83-88行,通过usb_parse_configuration函数,把获得的配置等信息按照类型进行分类 ;
这里的usb_get_descriptor,只是向设备发送一个获取配置信息的请求,相对比较简单,所以就不深入讲解,而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) { 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; }
获取得到的信息里它包含了多种信息:配置,相关联接口,接口,端口等;这个函数的作用就是将这些信息把它补全到之前申请的usb_host_config结构里,把各类信息放到相应的结构中;
获取得到的信息最前面就是配置信息,所以17行,根据配置信息长度把配置描述符信息保存到usb_host_config中的desc中;
18-24行,如果获得的信息最前面不是配置描述符信息或描述符信息长度少于标准数据长度,则出错,直接返回;
25行,从配置描述符里得到当前配置值;
27-28行,更新buffer地址和size大小,从而可以获得接下来的信息;
30-36行,取得当前配置里接口个数,如果接口个数大于允许的最大接口数据,发出警告,并重新设置设备接口数;
40-123行,遍历当前配置信息,统计得到当前配置下每个接口对应的接口设置数;
其中n用来表示接口数,inums和nalts数组用来分别存放接口号及该接口号对应的接口设置数;
45-58行,根据usb的描述符头里的长度对buffer里的数据进行判断,判断是否满足长度需求,如果不满足,则跳出当前统计循环;
USB3.0协议引进了一个interrface association descriptor概念,它用来描述一个复合型接口,复合型接口是指由2个或以上接口组成一个功能的接口。如果存在这类描述符,则它是紧跟着配置描述符的;
60-86行,如果配置描述符后紧跟着的是接口描述符,则根据接口描述符对长度和接口号进行判断,判断这个接口描述符是否合法;
90-93行,遍历inums数组,判断接口描述符中接口号是否已经存在inums数组里;
94-101行,如果在inums里找到和当前接口号相同项,并且这个接口对应的接口设置数量没有超过255,则增加接口设置数。如果不能在inums里找到与当前接口与相同项,表示这是一个新的接口,如果接口数n没有超过最大接口数32,则将当前接口号保存到inums数组里,并设置对应的接口设置数为1;
103-115行,如果当前描述符是interface association descriptor,则把它保存到配置结构intf_assoc里;
117-123行,在当前配置描述下,如果还有其它配置或设备描述符,则发出警告;
124-125行,重新获取配置等描述符信息长度;
127-133行,对统计出来的接口数进行判断,如果n与配置描述中的接口数不相等或n等于0,则发生警告;
136-144行,对inums里的接口号进行检查,查看对就的接口号是否存在;
147-162行,为当前接口和接口下的接口设置申请内存空间,并把它存放在配置里的intf_cache结构里,intf_cache本身 是一个指针数组,它用来存放接口和接口设置这个内存空间的地址;
169行,通过find_next_descriptor函数从buffer里查找类型为接口的描述符,返回值i表示buffer与接口描述符描述符之间数据长度,n用来表示buffer与接口描述符之间需要跳过的其它描述符个数。buffer最开始的地方有可能不是接口描述符,所以要去寻找第一次出现接口描述符类型的位置,这个位置可以由返回值来确定,即接口描述符开始地址=buffer+返回值i;
175-176行,如果在接口描述符前面还存在其它数据,则更新buffer和size,以确保接下来buffer就为接口描述符首地址;
179-187行,接下来由usb_parse_interface函数对buffer里的接口和端口等信息进行分解;
static int usb_parse_interface(struct device *ddev, int cfgno, struct usb_host_config *config, unsigned char *buffer, int size, u8 inums[], u8 nalts[]) { unsigned char *buffer0 = buffer; struct usb_interface_descriptor *d; int inum, asnum; struct usb_interface_cache *intfc; struct usb_host_interface *alt; int i, n; int len, retval; int num_ep, num_ep_orig; d = (struct usb_interface_descriptor *) buffer; buffer += d->bLength; size -= d->bLength; if (d->bLength < USB_DT_INTERFACE_SIZE) goto skip_to_next_interface_descriptor; /* Which interface entry is this? */ intfc = NULL; inum = d->bInterfaceNumber; for (i = 0; i < config->desc.bNumInterfaces; ++i) { if (inums[i] == inum) { intfc = config->intf_cache[i]; break; } } if (!intfc || intfc->num_altsetting >= nalts[i]) goto skip_to_next_interface_descriptor; /* Check for duplicate altsetting entries */ asnum = d->bAlternateSetting; for ((i = 0, alt = &intfc->altsetting[0]); i < intfc->num_altsetting; (++i, ++alt)) { if (alt->desc.bAlternateSetting == asnum) { dev_warn(ddev, "Duplicate descriptor for config %d " "interface %d altsetting %d, skipping\n", cfgno, inum, asnum); goto skip_to_next_interface_descriptor; } } ++intfc->num_altsetting; memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE); /* Skip over any Class Specific or Vendor Specific descriptors; * find the first endpoint or interface descriptor */ alt->extra = buffer; i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, &n); alt->extralen = i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", n, plural(n), "interface"); buffer += i; size -= i; /* Allocate space for the right(?) number of endpoints */ num_ep = num_ep_orig = alt->desc.bNumEndpoints; alt->desc.bNumEndpoints = 0; /* Use as a counter */ if (num_ep > USB_MAXENDPOINTS) { dev_warn(ddev, "too many endpoints for config %d interface %d " "altsetting %d: %d, using maximum allowed: %d\n", cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS); num_ep = USB_MAXENDPOINTS; } if (num_ep > 0) { /* Can't allocate 0 bytes */ len = sizeof(struct usb_host_endpoint) * num_ep; alt->endpoint = kzalloc(len, GFP_KERNEL); if (!alt->endpoint) return -ENOMEM; } /* Parse all the endpoint descriptors */ n = 0; while (size > 0) { if (((struct usb_descriptor_header *) buffer)->bDescriptorType == USB_DT_INTERFACE) break; retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt, num_ep, buffer, size); if (retval < 0) return retval; ++n; buffer += retval; size -= retval; } if (n != num_ep_orig) dev_warn(ddev, "config %d interface %d altsetting %d has %d " "endpoint descriptor%s, different from the interface " "descriptor's value: %d\n", cfgno, inum, asnum, n, plural(n), num_ep_orig); return buffer - buffer0; skip_to_next_interface_descriptor: i = find_next_descriptor(buffer, size, USB_DT_INTERFACE, USB_DT_INTERFACE, NULL); return buffer - buffer0 + i; }
14-19行,更新buffer和size,并判断接口描述符的长度是否正常,如果不正常则查找下一个接口描述符;
23-29行,根据当前接口描述符中的接口号来得到用于存放接口信息的intf_cache;
34-44行,把接口描述符中的接口设置值与存放在intf_cache中的接口设置值进行比较,如果找到相同的设置值,则表示这个接口设置已经保存在intf_cache里了,则跳过当前接口设置,直接去查找下一个接口;如果找不到相同的,则表示这是一个新的接口设置,则通过for 循环中++alt来确定接口设置存放位置;
46-47行,更新接口设置值,并将接口描述符保存到相应接口下;
52-59行,在剩下的buffer里找到endpoint或interface描述符首次出现位置,并根据返回值来更新buffer和size;
62-77行,得到当前接口设置下有多少个endpoint端口,并为这些端口申请空间;
81-93行,如果在接口描述符后面紧跟着端口描述符,则通过usb_parse_endpoint对端口描述符进行分解,并将它保存在之前申请的endpoint里;
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_interface *ifp, int num_ep, unsigned char *buffer, int size) { unsigned char *buffer0 = buffer; struct usb_endpoint_descriptor *d; struct usb_host_endpoint *endpoint; int n, i, j, retval; d = (struct usb_endpoint_descriptor *) buffer; buffer += d->bLength; size -= d->bLength; if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE) n = USB_DT_ENDPOINT_AUDIO_SIZE; else if (d->bLength >= USB_DT_ENDPOINT_SIZE) n = USB_DT_ENDPOINT_SIZE; else { dev_warn(ddev, "config %d interface %d altsetting %d has an " "invalid endpoint descriptor of length %d, skipping\n", cfgno, inum, asnum, d->bLength); goto skip_to_next_endpoint_or_interface_descriptor; } i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK; if (i >= 16 || i == 0) { dev_warn(ddev, "config %d interface %d altsetting %d has an " "invalid endpoint with address 0x%X, skipping\n", cfgno, inum, asnum, d->bEndpointAddress); goto skip_to_next_endpoint_or_interface_descriptor; } /* Only store as many endpoints as we have room for */ if (ifp->desc.bNumEndpoints >= num_ep) goto skip_to_next_endpoint_or_interface_descriptor; endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; ++ifp->desc.bNumEndpoints; memcpy(&endpoint->desc, d, n); INIT_LIST_HEAD(&endpoint->urb_list); /* Fix up bInterval values outside the legal range. Use 32 ms if no * proper value can be guessed. */ i = 0; /* i = min, j = max, n = default */ j = 255; if (usb_endpoint_xfer_int(d)) { i = 1; switch (to_usb_device(ddev)->speed) { case USB_SPEED_SUPER: case USB_SPEED_HIGH: /* Many device manufacturers are using full-speed * bInterval values in high-speed interrupt endpoint * descriptors. Try to fix those and fall back to a * 32 ms default value otherwise. */ n = fls(d->bInterval*8); if (n == 0) n = 9; /* 32 ms = 2^(9-1) uframes */ j = 16; break; default: /* USB_SPEED_FULL or _LOW */ /* For low-speed, 10 ms is the official minimum. * But some "overclocked" devices might want faster * polling so we'll allow it. */ n = 32; break; } } else if (usb_endpoint_xfer_isoc(d)) { i = 1; j = 16; switch (to_usb_device(ddev)->speed) { case USB_SPEED_HIGH: n = 9; /* 32 ms = 2^(9-1) uframes */ break; default: /* USB_SPEED_FULL */ n = 6; /* 32 ms = 2^(6-1) frames */ break; } } if (d->bInterval < i || d->bInterval > j) { dev_warn(ddev, "config %d interface %d altsetting %d " "endpoint 0x%X has an invalid bInterval %d, " "changing to %d\n", cfgno, inum, asnum, d->bEndpointAddress, d->bInterval, n); endpoint->desc.bInterval = n; } /* Some buggy low-speed devices have Bulk endpoints, which is * explicitly forbidden by the USB spec. In an attempt to make * them usable, we will try treating them as Interrupt endpoints. */ if (to_usb_device(ddev)->speed == USB_SPEED_LOW && usb_endpoint_xfer_bulk(d)) { dev_warn(ddev, "config %d interface %d altsetting %d " "endpoint 0x%X is Bulk; changing to Interrupt\n", cfgno, inum, asnum, d->bEndpointAddress); endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT; endpoint->desc.bInterval = 1; if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8) endpoint->desc.wMaxPacketSize = cpu_to_le16(8); } /* * Some buggy high speed devices have bulk endpoints using * maxpacket sizes other than 512. High speed HCDs may not * be able to handle that particular bug, so let's warn... */ if (to_usb_device(ddev)->speed == USB_SPEED_HIGH && usb_endpoint_xfer_bulk(d)) { unsigned maxp; maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize) & 0x07ff; if (maxp != 512) dev_warn(ddev, "config %d interface %d altsetting %d " "bulk endpoint 0x%X has invalid maxpacket %d\n", cfgno, inum, asnum, d->bEndpointAddress, maxp); } /* Parse a possible SuperSpeed endpoint companion descriptor */ if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) usb_parse_ss_endpoint_companion(ddev, cfgno, inum, asnum, endpoint, buffer, size); /* Skip over any Class Specific or Vendor Specific descriptors; * find the next endpoint or interface descriptor */ endpoint->extra = buffer; i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, &n); endpoint->extralen = i; retval = buffer - buffer0 + i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", n, plural(n), "endpoint"); return retval; skip_to_next_endpoint_or_interface_descriptor: i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, NULL); return buffer - buffer0 + i; }
10-23行,根据端口描述符长度去更新buffer和size,并确认端口描述符的长度;
25-35行,确认端口的地址及端口数量,标准的端口地址是从1到15;
37-41行,将接口描述符中的端口数当成接口下端口数组endpoint的索引,获取用于存放当前endpoint信息的地址,并把当前的端口描述符保存到endpoint下;
45-87行,对于中断和等时传输类型的端口,根据usb设备速度设置时间时隔interval;
93-102行,在usb2.0协议中有定义说:在低速时是没有bulk类型的端口的,但实际上有可能存在这种情况,这里的话,我们把这个bulk端口当做interrupt类型端口使用;
109-119行,对于运行在高速下的bulk端口,usb2.0定义这类端口最大传输长度为512,如果不为512则发出警告;
122-124行,如果当前设备是超速usb设备,则还要usb_parse_ss_endpoint_companion取得用于描述高速设备的描述符;
129-132行,继续分解端口和接口描述符;
由这个配置描述符分解过程可以知道,配置描述符里包含下面内容:配置描述符,关联接口描述符或接口描述符,端口描述符对于超速设备还有用于描述超速设备的描述符,这个分解过程验证了:
usb设备由一个或多个usb 配置组成,usb配置由一个或多个usb接口组成,usb接口下包含多个usb接口设置,usb接口设置由多个usb端口组成;
讲到这里usb_enumerate_device中的得要部分已经讲完,接下来回到usb_new_device继续讲解;
31-32行,根据usb设备的端口获取usb设备的设备号;
35行,通过announce_device输出一些usb设备信息;
42行,将usb设备通过device_add添加到系统中。
到这里为止,usb设备枚举第一阶段算是已经完成,这部分主要工作是:遍历hub下的各个port,查看这些port是否有新设备加入,如果有新设备加入,则为这个设备申请资源,并去获取设备描述符,配置描述符去完善这个usb 设备,最后将这个port上的usb 设备注册到系统中;
当usb设备通过device_add注册到usb 总线上后,系统会去遍历usb总线上的所有驱动,寻找与usb设备驱动,通过usb_match_device进行匹配。对于usb设备,它的驱动应该是usb_generic_driver,当驱动与设备匹配成功后,就会调用usb_generic_driver中的generic_probe函数,它个函数会从usb设备中找出一个合适 的usb配置,并对usb设备进行配置;
static int generic_probe(struct usb_device *udev) { int err, c; /* Choose and set the configuration. This registers the interfaces * with the driver core and lets interface drivers bind to them. */ if (usb_device_is_owned(udev)) ; /* Don't configure if the device is owned */ else if (udev->authorized == 0) dev_err(&udev->dev, "Device is not authorized for usage\n"); else { c = usb_choose_configuration(udev); if (c >= 0) { err = usb_set_configuration(udev, c); if (err) { dev_err(&udev->dev, "can't set config #%d, error %d\n", c, err); /* This need not be fatal. The user can try to * set other configurations. */ } } } /* USB device state == configured ... usable */ usb_notify_add_device(udev); return 0; }
generic_probe函数由三部分程序构成:第一是由usb_choose_configuration选择一个适合的usb配置;第二是根据选择得到的配置值配置usb设备;第三是如果系统支持usb class类设备文件系统,则在/sys/usb_device/下创建usb设备的设备属性文件;
选择配置函数usb_choose_configuration中有许多注释信息,在这里我已经把它们都去掉了,具体程序如下所示:
int usb_choose_configuration(struct usb_device *udev) { int i; int num_configs; int insufficient_power = 0; struct usb_host_config *c, *best; best = NULL; c = udev->config; num_configs = udev->descriptor.bNumConfigurations; for (i = 0; i < num_configs; (i++, c++)) { struct usb_interface_descriptor *desc = NULL; /* It's possible that a config has no interfaces! */ if (c->desc.bNumInterfaces > 0) desc = &c->intf_cache[0]->altsetting->desc; /* Rule out configs that draw too much bus current */ if (c->desc.bMaxPower * 2 > udev->bus_mA) { insufficient_power++; continue; } if (i == 0 && num_configs > 1 && desc && (is_rndis(desc) || is_activesync(desc))) { #if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) continue; #else best = c; #endif } else if (udev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC && (desc && desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC)) { best = c; break; } /* If all the remaining configs are vendor-specific, * choose the first one. */ else if (!best) best = c; } if (insufficient_power > 0) dev_info(&udev->dev, "rejected %d configuration%s " "due to insufficient available bus power\n", insufficient_power, plural(insufficient_power)); if (best) { i = best->desc.bConfigurationValue; dev_dbg(&udev->dev, "configuration #%d chosen from %d choice%s\n", i, num_configs, plural(num_configs)); } else { i = -1; dev_warn(&udev->dev, "no configuration chosen from %d choice%s\n", num_configs, plural(num_configs)); } return i; }
15-16行,如果当前配置下有接口,则选择得到接口0中接口设置0的接口描述符;
20-23行,判断当前usb设备从hub上可以获得的电流是滞能够满足当前配置,如果不能满足则选择下一个配置;
25-38行,对于一些micsoft公司喜欢的非标准设备配置,除非指定说需要,否则不考虑这些配置;
32-38行,选择非生产产家规定类的配置比由生产产家规定类的配置更合适;
42-43行,如果只有生产 产家规定类的配置,那么选择这睦配置中的第一个配置;
46-49行,如果这些配置中所需要的电流比较从hub上获得的允许值大,就会发出警告;
51-62行,选择好合适 的配置值后,函数usb_choose_configuration返回;
如果得到了合适的配置值,则通过usb_set_configuration将这个值对usb设备进行配置;
int usb_set_configuration(struct usb_device *dev, int configuration) { int i, ret; struct usb_host_config *cp = NULL; struct usb_interface **new_interfaces = NULL; struct usb_hcd *hcd = bus_to_hcd(dev->bus); int n, nintf; if (dev->authorized == 0 || configuration == -1) configuration = 0; else { for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { cp = &dev->config[i]; break; } } } if ((!cp && configuration != 0)) return -EINVAL; if (cp && configuration == 0) dev_warn(&dev->dev, "config 0 descriptor??\n"); n = nintf = 0; if (cp) { nintf = cp->desc.bNumInterfaces; new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), GFP_NOIO); if (!new_interfaces) { dev_err(&dev->dev, "Out of memory\n"); return -ENOMEM; } for (; n < nintf; ++n) { new_interfaces[n] = kzalloc( sizeof(struct usb_interface), GFP_NOIO); if (!new_interfaces[n]) { dev_err(&dev->dev, "Out of memory\n"); ret = -ENOMEM; free_interfaces: while (--n >= 0) kfree(new_interfaces[n]); kfree(new_interfaces); return ret; } } i = dev->bus_mA - cp->desc.bMaxPower * 2; if (i < 0) dev_warn(&dev->dev, "new config #%d exceeds power " "limit by %dmA\n", configuration, -i); } /* Wake up the device so we can send it the Set-Config request */ ret = usb_autoresume_device(dev); if (ret) goto free_interfaces; /* if it's already configured, clear out old state first. * getting rid of old interfaces means unbinding their drivers. */ mutex_lock(hcd->bandwidth_mutex); if (dev->state != USB_STATE_ADDRESS) usb_disable_device(dev, 1); /* Skip ep0 */ /* Get rid of pending async Set-Config requests for this device */ cancel_async_set_config(dev); ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); if (ret < 0) { mutex_unlock(hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; } ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (ret < 0) { /* All the old state is gone, so what else can we do? * The device is probably useless now anyway. */ cp = NULL; } dev->actconfig = cp; if (!cp) { usb_set_device_state(dev, USB_STATE_ADDRESS); usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); mutex_unlock(hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; } mutex_unlock(hcd->bandwidth_mutex); usb_set_device_state(dev, USB_STATE_CONFIGURED); /* Initialize the new interface structures and the * hc/hcd/usbcore interface/endpoint state. */ for (i = 0; i < nintf; ++i) { struct usb_interface_cache *intfc; struct usb_interface *intf; struct usb_host_interface *alt; cp->interface[i] = intf = new_interfaces[i]; intfc = cp->intf_cache[i]; intf->altsetting = intfc->altsetting; intf->num_altsetting = intfc->num_altsetting; intf->intf_assoc = find_iad(dev, cp, i); kref_get(&intfc->ref); alt = usb_altnum_to_altsetting(intf, 0); /* No altsetting 0? We'll assume the first altsetting. * We could use a GetInterface call, but if a device is * so non-compliant that it doesn't have altsetting 0 * then I wouldn't trust its reply anyway. */ if (!alt) alt = &intf->altsetting[0]; intf->cur_altsetting = alt; usb_enable_interface(dev, intf, true); intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; intf->dev.groups = usb_interface_groups; intf->dev.dma_mask = dev->dev.dma_mask; INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); intf->minor = -1; device_initialize(&intf->dev); pm_runtime_no_callbacks(&intf->dev); dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, alt->desc.bInterfaceNumber); } kfree(new_interfaces); if (cp->string == NULL && !(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) cp->string = usb_cache_string(dev, cp->desc.iConfiguration); for (i = 0; i < nintf; ++i) { struct usb_interface *intf = cp->interface[i]; dev_dbg(&dev->dev, "adding %s (config #%d, interface %d)\n", dev_name(&intf->dev), configuration, intf->cur_altsetting->desc.bInterfaceNumber); device_enable_async_suspend(&intf->dev); ret = device_add(&intf->dev); if (ret != 0) { dev_err(&dev->dev, "device_add(%s) --> %d\n", dev_name(&intf->dev), ret); continue; } create_intf_ep_devs(intf); } usb_autosuspend_device(dev); return 0; }
9-24行,根据配置值得到usb配置;
27-49行,根据接口数申请用于暂时存放当前配置下接口的内存空间,这里包含两部分,首先,new_interfaces是一个指针数组,所以要根据接口数为这个指针数据申请空间,其次还要为接口这个结构本身申请空间;
51-57行,如果当前配置不能满足电源供给需求,则发出警告;
80-88行,向usb device 侧发送设备配置请求;
99行,如果配置成功,则将usb 设备设置成USB_STATE_CONFIGURED态;
104-141行,对当前配置下的各接口进行初始化, 完善配置结构下interface结构,并初始化各接口设备对就的设备模型;
148-163行,将当前配置下的各接口设备进行注册,并创建端口设备文件;
到这里为止,usb的设备枚举基本上已经完成 ,接下来系统会根据各接口的类型为它分配接口驱动,这里所说的接口驱动就是驱动工程师所需要去完成 的关于usb设备功能使用程序。当然,如果当前port上接的是一个hub,则它会调用hub_probe对hub进行配置。