1. 流程图

2. usb_hub_init()

int usb_hub_init(void)
{//1. 注册usb hub驱动if (usb_register(&hub_driver) < 0) { //注册hub驱动到usb子系统总线上printk(KERN_ERR "%s: can't register hub driver\n",usbcore_name);return -1;}//2. hub_thread内部是完成设备的枚举,即创建usb设备,最终调用1.中的驱动match完成相应的驱动注册khubd_task = kthread_run(hub_thread, NULL, "khubd"); //创建hub内核线程if (!IS_ERR(khubd_task))return 0;/* Fall through if kernel_thread failed */usb_deregister(&hub_driver);printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);return -1;
}

前面已经分析过了hub驱动的注册流程,详见:点击打开链接,这里主要分析hub_thread线程内部的工作原理。

3. hub_thread()

khubd_task = kthread_run(hub_thread, NULL, "khubd"); //创建hub内核线程
static int hub_thread(void *__unused)
{/* khubd needs to be freezable to avoid intefering with USB-PERSIST* port handover.  Otherwise it might see that a full-speed device* was gone before the EHCI controller had handed its port over to* the companion full-speed controller.*/set_freezable();do {hub_events();wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());} while (!kthread_should_stop() || !list_empty(&hub_event_list));pr_debug("%s: khubd exiting\n", usbcore_name);return 0;
}

hub_thread()函数内部的主要工作是等待khubd_wait事件的发生,前提是该事件的条件hub_event_list不为NULL,在ehci主机控制器这一节(详见:点击打开链接, “第5.4 usb_hcd_request_irqs()”)我们分析过是如何唤醒该khubd_wait,等待事件的,这里不再赘述!所以当主机控制器ehci上有设备接入时(如U盘),ehci将唤醒khubd_wait,这里wait_event_freezable()等待事件成立将调用hub_event()事件函数。

4. hub_event()

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; //获取hub_event_list.next的指针,即当前hublist_del_init(tmp); //将当前hub从hub_event_list链表中删除hub = list_entry(tmp, struct usb_hub, event_list); //这里需要看下是如何将hub设备添加到链表中的???kref_get(&hub->kref);spin_unlock_irq(&hub_event_lock);hdev = hub->hdev; //从usb-hub中获取usb设备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是否没有被附上(断开)*/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) //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 */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;}}//确定是否远程唤醒,如usb模块进入了休眠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) //从上面遍历的端口中获取到usb口有改变,将调用如下接口!!!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;elsehub->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);kref_put(&hub->kref, hub_release);} /* end while (1) */
}

从hub_event_list链表中获取当前的hub,然后遍历该hub的端口,判断该端口对应的状态(状态包括:是否过流、端口是否使能、端口复位...)是否改变正确(正确connect_change=1,反之=0),正确将调用hub_port_connect_change()。

5. 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; //这个值在usb_hcd_poll_rh_status()函数内部有操作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) { 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) {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;elseunit_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*/udev = usb_alloc_dev(hdev, hdev->bus, port1); //分配一个usb设备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;elseudev->speed = USB_SPEED_UNKNOWN;choose_devnum(udev); //选择一个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.*/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 */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;elsehub->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设备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);
}

该函数通过对端口状态的改变进行端口连接状态的判定(包括是否重新复位端口、对端口防抖处理...),最后通过分配一个usb设备usb_alloc_dev()、选择设备编号choose_devnum(udev), hub_port_init()端口初始化、usb_new_device()新建一个usb设备。

5.1 usb_alloc_dev()

struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1)
{struct usb_device *dev;struct usb_hcd *usb_hcd = bus_to_hcd(bus);unsigned root_hub = 0;dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return NULL;if (!usb_get_hcd(usb_hcd)) {kfree(dev);return NULL;}/* Root hubs aren't true devices, so don't allocate HCD resources */if (usb_hcd->driver->alloc_dev && parent &&!usb_hcd->driver->alloc_dev(usb_hcd, dev)) {usb_put_hcd(bus_to_hcd(bus));kfree(dev);return NULL;}device_initialize(&dev->dev);dev->dev.bus = &usb_bus_type; //绑定设备的总线为“usb”dev->dev.type = &usb_device_type; //设备的类型dev->dev.groups = usb_device_groups;dev->dev.dma_mask = bus->controller->dma_mask;set_dev_node(&dev->dev, dev_to_node(bus->controller));dev->state = USB_STATE_ATTACHED; //设置usb状态为绑定dev->lpm_disable_count = 1;atomic_set(&dev->urbnum, 0);INIT_LIST_HEAD(&dev->ep0.urb_list);dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; //设置断点长度dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; //设置端点描述符/* ep0 maxpacket comes later, from device descriptor */usb_enable_endpoint(dev, &dev->ep0, false);dev->can_submit = 1;/* Save readable and stable topology id, distinguishing devices* by location for diagnostics, tools, driver model, etc.  The* string is a path along hub ports, from the root.  Each device's* dev->devpath will be stable until USB is re-cabled, and hubs* are often labeled with these port numbers.  The name isn't* as stable:  bus->busnum changes easily from modprobe order,* cardbus or pci hotplugging, and so on.*/if (unlikely(!parent)) {dev->devpath[0] = '0';dev->route = 0;dev->dev.parent = bus->controller;dev_set_name(&dev->dev, "usb%d", bus->busnum);root_hub = 1;} else {/* match any labeling on the hubs; it's one-based */if (parent->devpath[0] == '0') {snprintf(dev->devpath, sizeof dev->devpath,"%d", port1);/* Root ports are not counted in route string */dev->route = 0;} else {snprintf(dev->devpath, sizeof dev->devpath,"%s.%d", parent->devpath, port1);/* Route string assumes hubs have less than 16 ports */if (port1 < 15)dev->route = parent->route +(port1 << ((parent->level - 1)*4));elsedev->route = parent->route +(15 << ((parent->level - 1)*4));}dev->dev.parent = &parent->dev;dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);/* hub driver sets up TT records */}dev->portnum = port1;dev->bus = bus;dev->parent = parent;INIT_LIST_HEAD(&dev->filelist);#ifdef CONFIG_PMpm_runtime_set_autosuspend_delay(&dev->dev,usb_autosuspend_delay * 1000);dev->connect_time = jiffies;dev->active_duration = -jiffies;
#endifif (root_hub) /* Root hub always ok [and always wired] */dev->authorized = 1;else {dev->authorized = usb_hcd->authorized_default;dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;}return dev;
}

分配一个usb设备,包括绑定总线类型为usb,设备的类型为设备(非接口),设置端点0的描述符长度、类型,最后设置该设备的名称:dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath)。

5.2 choose_devnum()

static void choose_devnum(struct usb_device *udev)
{int        devnum;struct usb_bus   *bus = udev->bus;/* If khubd ever becomes multithreaded, this will need a lock */if (udev->wusb) {devnum = udev->portnum + 1;BUG_ON(test_bit(devnum, bus->devmap.devicemap));} else {/* Try to allocate the next devnum beginning at* bus->devnum_next. */devnum = find_next_zero_bit(bus->devmap.devicemap, 128,bus->devnum_next);if (devnum >= 128)devnum = find_next_zero_bit(bus->devmap.devicemap,128, 1);bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);}if (devnum < 128) {set_bit(devnum, bus->devmap.devicemap);udev->devnum = devnum;}
}

从bus->devmap.devicemap中分配一个未被占用的bit位作为当前分配的设备编号。

5.3 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;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)*/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, 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].*///先根据不同的速率,初始化传输包的字节数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";elsespeed = 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  *///usb2.0与usb1.0、usb1.1转换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  64buf = 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(), //获取64字节的设备描述符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, 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); //设置设备的地址为devnumif (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))break;}retval = usb_get_device_descriptor(udev, 8); //获取长度为8个字节的设备描述,这是因为第8个字节的偏移为bMaxPacketSize0,而下面需要!!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;elsei = 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);elsedev_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); //再次获取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;
}

该函数主要的功能是:复位设备、分配地址、获取分配地址对应设备的描述符。

5. 4 usb_new_device() 设备枚举

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 = hub->ports[udev->portnum - 1];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;
}

该函数主要是通过usb_enumerate_device(udev)完成“设备枚举”(设备枚举的具体细节已经分析过,详见:点击打开链接, 5.6 小节),最后通过device_add(&udev->dev)将设备通添加到usb总线上,同时匹配对应的驱动,接下来将详细分析!!!

5.4.1 device_add()

int device_add(struct device *dev)
{struct device *parent = NULL;struct kobject *kobj;struct class_interface *class_intf;int error = -EINVAL;dev = get_device(dev);if (!dev)goto done;if (!dev->p) {error = device_private_init(dev);if (error)goto done;}/** for statically allocated devices, which should all be converted* some day, we need to initialize the name. We prevent reading back* the name, and force the use of dev_name()*/if (dev->init_name) {dev_set_name(dev, "%s", dev->init_name); //dev->init_name = "platform"dev->init_name = NULL;}/* subsystems can specify simple device enumeration */if (!dev_name(dev) && dev->bus && dev->bus->dev_name)dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);if (!dev_name(dev)) {error = -EINVAL;goto name_error;}pr_debug("device: '%s': %s\n", dev_name(dev), __func__);parent = get_device(dev->parent); //dev->parent=platform_bus结构体kobj = get_device_parent(dev, parent);if (kobj)dev->kobj.parent = kobj;/* use parent numa_node */if (parent)set_dev_node(dev, dev_to_node(parent));/* first, register with generic layer. *//* we require the name to be set before, and pass NULL */error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //对象增加,其实就是在文件系统/sys/device/platform下新建文件pinctrlif (error)goto Error;/* notify platform of device entry */if (platform_notify)platform_notify(dev);error = device_create_file(dev, &uevent_attr);if (error)goto attrError;if (MAJOR(dev->devt)) {error = device_create_file(dev, &devt_attr);if (error)goto ueventattrError;error = device_create_sys_dev_entry(dev);if (error)goto devtattrError;devtmpfs_create_node(dev);}error = device_add_class_symlinks(dev);if (error)goto SymlinkError;error = device_add_attrs(dev); if (error)goto AttrsError;error = bus_add_device(dev); //增加设备到总线上,是理解设备和驱动匹配的关键因素之一!if (error)goto BusError;error = dpm_sysfs_add(dev);if (error)goto DPMError;device_pm_add(dev);/* Notify clients of device addition.  This call must come* after dpm_sysfs_add() and before kobject_uevent().*/if (dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);kobject_uevent(&dev->kobj, KOBJ_ADD); //kobject内核用户层事件增加bus_probe_device(dev); //if (parent)klist_add_tail(&dev->p->knode_parent,&parent->p->klist_children);if (dev->class) {mutex_lock(&dev->class->p->mutex);/* tie the class to the device */klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices);/* notify any interfaces that the device is here */list_for_each_entry(class_intf,&dev->class->p->interfaces, node)if (class_intf->add_dev)class_intf->add_dev(dev, class_intf);mutex_unlock(&dev->class->p->mutex);}
done:put_device(dev);return error;DPMError:bus_remove_device(dev);BusError:device_remove_attrs(dev);AttrsError:device_remove_class_symlinks(dev);SymlinkError:if (MAJOR(dev->devt))devtmpfs_delete_node(dev);if (MAJOR(dev->devt))device_remove_sys_dev_entry(dev);devtattrError:if (MAJOR(dev->devt))device_remove_file(dev, &devt_attr);ueventattrError:device_remove_file(dev, &uevent_attr);attrError:kobject_uevent(&dev->kobj, KOBJ_REMOVE);kobject_del(&dev->kobj);Error:cleanup_device_parent(dev);if (parent)put_device(parent);
name_error:kfree(dev->p);dev->p = NULL;goto done;
}

该函数内部知识点较多,包括内核对象添加kobject_add()、sys文件系统属性绑定device_create_file()、将当前设备添加到usb总线的设备链表中bus_add_device()、最后通过bus_probe_device()匹配对应的驱动,这里将重点分析!

void bus_probe_device(struct device *dev)
{struct bus_type *bus = dev->bus;struct subsys_interface *sif;int ret;if (!bus)return;if (bus->p->drivers_autoprobe) { //在USB总线注册时,drivers_autoprobe已初始化为1ret = device_attach(dev); //设备绑定,见下面分析WARN_ON(ret < 0);}mutex_lock(&bus->p->mutex);list_for_each_entry(sif, &bus->p->interfaces, node)if (sif->add_dev)sif->add_dev(dev, sif);mutex_unlock(&bus->p->mutex);
}
int device_attach(struct device *dev)
{int ret = 0;device_lock(dev);//由于设备和驱动有对应的关系,一个设备只能对应一个驱动,而一个驱动可以对应//多个设备,所以dev->driver为真就表示当前设备已经绑定了对应的驱动,否则就要//遍历驱动链表寻找匹配的if (dev->driver) {if (klist_node_attached(&dev->p->knode_driver)) {ret = 1;goto out_unlock;}ret = device_bind_driver(dev); //确定是否绑定对应的驱动if (ret == 0)ret = 1;else {dev->driver = NULL;ret = 0;}} else {ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);pm_request_idle(dev);}
out_unlock:device_unlock(dev);return ret;
}
EXPORT_SYMBOL_GPL(device_attach);

由于设备和驱动有对应的关系,一个设备只能对应一个驱动,而一个驱动可以对应多个设备,所以dev->driver为真就表示当前设备已经绑定了对应的驱动,否则就要遍历驱动链表寻找匹配的,我们这里时第一次执行,所以条件不成立,要匹配新的驱动。

ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,void *data, int (*fn)(struct device_driver *, void *))
{struct klist_iter i;struct device_driver *drv;int error = 0;if (!bus)return -EINVAL;klist_iter_init_node(&bus->p->klist_drivers, &i,start ? &start->p->knode_bus : NULL); //遍历usb总线上的驱动链表进行匹配while ((drv = next_driver(&i)) && !error)error = fn(drv, data);klist_iter_exit(&i);return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_drv);

该函数内部将遍历usb总线上的驱动链表,将当前设备与驱动进行匹配,这里调用回调函数__device_attach(),详见如下:

static int __device_attach(struct device_driver *drv, void *data)
{struct device *dev = data;if (!driver_match_device(drv, dev)) //驱动和设备匹配判定return 0;return driver_probe_device(drv, dev); //调用usb总线上的探测函数
}
static inline int driver_match_device(struct device_driver *drv, struct device *dev)
{return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

这里的match函数将调用usb总线上的usb_device_match匹配函数,drv->bus->match=usb_bus_type.usb_device_match:

struct bus_type usb_bus_type = {.name =        "usb",.match =   usb_device_match, //将被调用.uevent =  usb_uevent,
};
static int usb_device_match(struct device *dev, struct device_driver *drv)
{/* devices and interfaces are handled separately */if (is_usb_device(dev)) { //是否是usb设备/* interface drivers never match devices */if (!is_usb_device_driver(drv))return 0;/* TODO: Add real matching code */return 1;} else if (is_usb_interface(dev)) { //是否是usb接口struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id;/* device drivers never match interfaces */if (is_usb_device_driver(drv))return 0;intf = to_usb_interface(dev);usb_drv = to_usb_driver(drv);id = usb_match_id(intf, usb_drv->id_table);if (id)return 1;id = usb_match_dynamic_id(intf, usb_drv);if (id)return 1;}return 0;
}

在该函数内部我们这里匹配的时设备,所以执行if语句,在else语句内部是接口设备,我们知道,在usb驱动里真正与外设设备对应的时驱动而不是设备...现在在回到__device_attach函数内部的driver_probe_device()

int driver_probe_device(struct device_driver *drv, struct device *dev)
{int ret = 0;if (!device_is_registered(dev)) //判定当前设备是否已经注册过return -ENODEV;pr_debug("bus: '%s': %s: matched device %s with driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);pm_runtime_barrier(dev);ret = really_probe(dev, drv); //调用真实的probe探测函数pm_request_idle(dev);return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{int ret = 0;atomic_inc(&probe_count);pr_debug("bus: '%s': %s: probing driver %s with device %s\n",drv->bus->name, __func__, drv->name, dev_name(dev));WARN_ON(!list_empty(&dev->devres_head));dev->driver = drv;/* If using pinctrl, bind pins now before probing */ret = pinctrl_bind_pins(dev); //待分析if (ret)goto probe_failed;if (driver_sysfs_add(dev)) {printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}if (dev->bus->probe) { //通过usb_driver_register(...)函数知道该条件不成立ret = dev->bus->probe(dev); if (ret)goto probe_failed;} else if (drv->probe) { //终于找到你了,哈哈哈!!!ret = drv->probe(dev); //这里的probe函数就是usb驱动通用probe函数generic_probeif (ret)goto probe_failed;}driver_bound(dev);ret = 1;pr_debug("bus: '%s': %s: bound device %s to driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);goto done;probe_failed:devres_release_all(dev);driver_sysfs_remove(dev);dev->driver = NULL;dev_set_drvdata(dev, NULL);if (ret == -EPROBE_DEFER) {/* Driver requested deferred probing */dev_info(dev, "Driver %s requests probe deferral\n", drv->name);driver_deferred_probe_add(dev);} else if (ret != -ENODEV && ret != -ENXIO) {/* driver matched but the probe failed */printk(KERN_WARNING"%s: probe of %s failed with error %d\n",drv->name, dev_name(dev), ret);} else {pr_debug("%s: probe of %s rejects match %d\n",drv->name, dev_name(dev), ret);}/** Ignore errors returned by ->probe so that the next driver can try* its luck.*/ret = 0;
done:atomic_dec(&probe_count);wake_up(&probe_waitqueue);return ret;
}

在该函数内部将usb通用驱动程序函数generic_probe,关于该函数的注册详见:点击打开链接,这里直接进入该函数内部分析。

6. generic_probe()

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 (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 && err != -ENODEV) {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;
}

6.1 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;if (usb_device_is_owned(udev))return 0;best = NULL;c = udev->config; //获取配置的指针num_configs = udev->descriptor.bNumConfigurations; //通过打印的报文,确定配置个数为1个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;/** HP's USB bus-powered keyboard has only one configuration* and it claims to be self-powered; other devices may have* similar errors in their descriptors.  If the next test* were allowed to execute, such configurations would always* be rejected and the devices would not work as expected.* In the meantime, we run the risk of selecting a config* that requires external power at a time when that power* isn't available.  It seems to be the lesser of two evils.** Bugzilla #6448 reports a device that appears to crash* when it receives a GET_DEVICE_STATUS request!  We don't* have any other way to tell whether a device is self-powered,* but since we don't use that information anywhere but here,* the call has been removed.** Maybe the GET_DEVICE_STATUS call and the test below can* be reinstated when device firmwares become more reliable.* Don't hold your breath.*/
#if 0/* Rule out self-powered configs for a bus-powered device */if (bus_powered && (c->desc.bmAttributes &USB_CONFIG_ATT_SELFPOWER))continue;
#endif/** The next test may not be as effective as it should be.* Some hubs have errors in their descriptor, claiming* to be self-powered when they are really bus-powered.* We will overestimate the amount of current such hubs* make available for each port.** This is a fairly benign sort of failure.  It won't* cause us to reject configurations that we should have* accepted.*//* Rule out configs that draw too much bus current */if (usb_get_max_power(udev, c) > udev->bus_mA) {insufficient_power++;continue;}/* When the first config's first interface is one of Microsoft's* pet nonstandard Ethernet-over-USB protocols, ignore it unless* this kernel has enabled the necessary host side driver.* But: Don't ignore it if it's the only config.*/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;
#elsebest = c;
#endif}/* From the remaining configs, choose the first one whose* first interface is for a non-vendor-specific class.* Reason: Linux is more likely to have a class driver* than a vendor-specific driver. */else if (udev->descriptor.bDeviceClass != //USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型  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;
}

该函数的功能是从usb的设备中获取一个有效的配置,如果成功将返回配置序号,i = best->desc.bConfigurationValue。

6.2 usb_set_configuration()

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) { //在usb_choose_configuration()函数中返回了配置序号configuration,这里是通过配置序号找到对应的配置cp = &dev->config[i];break;}}}if ((!cp && configuration != 0))return -EINVAL;/* The USB spec says configuration 0 means unconfigured.* But if a device includes a configuration numbered 0,* we will accept it as a correctly configured state.* Use -1 if you really want to unconfigure the device.*/if (cp && configuration == 0)dev_warn(&dev->dev, "config 0 descriptor??\n");/* Allocate memory for new interfaces before doing anything else,* so that if we run out then nothing will have changed. */n = nintf = 0;if (cp) {nintf = cp->desc.bNumInterfaces; //获取配置里的接口个数new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),  //分配nintf个接口指针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 - usb_get_max_power(dev, cp); //i = 枚举时获取到的电流-最大的标准电流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.*/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);/* Make sure we have bandwidth (and available HCD resources) for this* configuration.  Remove endpoints from the schedule if we're dropping* this configuration to set configuration 0.  After this point, the* host controller will not allow submissions to dropped endpoints.  If* this call fails, the device state is unchanged.*/mutex_lock(hcd->bandwidth_mutex);/* Disable LPM, and re-enable it once the new configuration is* installed, so that the xHCI driver can recalculate the U1/U2* timeouts.*/if (dev->actconfig && usb_disable_lpm(dev)) {dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);mutex_unlock(hcd->bandwidth_mutex);ret = -ENOMEM;goto free_interfaces;}ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);if (ret < 0) {if (dev->actconfig)usb_enable_lpm(dev);mutex_unlock(hcd->bandwidth_mutex);usb_autosuspend_device(dev);goto free_interfaces;}/** 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;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->intf_assoc =find_iad(dev, cp, alt->desc.bInterfaceNumber);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; //将当前设备绑定总线为USBintf->dev.type = &usb_if_device_type; //设备类型为接口intf->dev.groups = usb_interface_groups; //usb接口类型为组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);//设置主机控制器上的配置索引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 && cp) {/** All the old state is gone, so what else can we do?* The device is probably useless now anyway.*/usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);for (i = 0; i < nintf; ++i) {usb_disable_interface(dev, cp->interface[i], true);put_device(&cp->interface[i]->dev);cp->interface[i] = NULL;}cp = NULL;}dev->actconfig = cp; //绑定实际的配置mutex_unlock(hcd->bandwidth_mutex);if (!cp) {usb_set_device_state(dev, USB_STATE_ADDRESS);/* Leave LPM disabled while the device is unconfigured. */usb_autosuspend_device(dev);return ret;}usb_set_device_state(dev, USB_STATE_CONFIGURED);if (cp->string == NULL &&!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))cp->string = usb_cache_string(dev, cp->desc.iConfiguration);/* Now that the interfaces are installed, re-enable LPM. */usb_unlocked_enable_lpm(dev);/* Enable LTM if it was turned off by usb_disable_device. */usb_enable_ltm(dev);/* Now that all the interfaces are set up, register them* to trigger binding of drivers to interfaces.  probe()* routines may install different altsettings and may* claim() any interfaces not yet bound.  Many class drivers* need that: CDC, audio, video, etc.*///遍历接口,注册接口到usb总线上,同时匹配对应的驱动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;
}

该函数的主要功能时为配置下的每个接口分配内存空间,然后遍历每一个接口,为每一个接口初始化设备模型,包括usb总线的绑定,设备类型绑定为接口,最后为每个接口进行usb总线上的注册

intf->dev.bus = &usb_bus_type; //将当前设备绑定总线为USB
intf->dev.type = &usb_if_device_type; //设备类型为接口
intf->dev.groups = usb_interface_groups; //usb接口类型为组
......
ret = device_add(&intf->dev);

这里我们又见到了非常熟悉的device_add函数,关于该函数我们在上面的“5.4.1小节”有详细的分析,这里最终在match函数内部将调用的是接口,再次贴出源码方便分析!

static int usb_device_match(struct device *dev, struct device_driver *drv)
{  /* devices and interfaces are handled separately */  if (is_usb_device(dev)) { //是否是usb设备  /* interface drivers never match devices */  if (!is_usb_device_driver(drv))  return 0;  /* TODO: Add real matching code */  return 1;  } else if (is_usb_interface(dev)) { //是否是usb接口  struct usb_interface *intf;  struct usb_driver *usb_drv;  const struct usb_device_id *id;  /* device drivers never match interfaces */  if (is_usb_device_driver(drv))   //条件过滤return 0;  intf = to_usb_interface(dev);  usb_drv = to_usb_driver(drv);  id = usb_match_id(intf, usb_drv->id_table);  //将当前接口与对应的驱动id_table表进行匹配if (id)  return 1;  id = usb_match_dynamic_id(intf, usb_drv);  if (id)  return 1;  }  return 0;
}  

该函数内部我们最关心的函数是

id = usb_match_id(intf, usb_drv->id_table);  //将当前接口与对应的驱动id_table表进行匹配

该函数两个参数,一个为当前的接口设备,另外一个为usb的驱动id_table表,当接口设备和id_table表中的id匹配后将调用usb_drv的probe探测函数,我们这里以usb转串口驱动分析,详见usb转串口驱动分析路径: 点击打开链接

7. usb_match_id()接口匹配

const struct usb_device_id *usb_match_id(struct usb_interface *interface,const struct usb_device_id *id)
{/* proc_connectinfo in devio.c may call us with id == NULL. */if (id == NULL)return NULL;/* It is important to check that id->driver_info is nonzero,since an entry that is all zeroes except for a nonzeroid->driver_info is the way to create an entry thatindicates that the driver want to examine everydevice and interface. */for (; id->idVendor || id->idProduct || id->bDeviceClass ||id->bInterfaceClass || id->driver_info; id++) {if (usb_match_one_id(interface, id)) //见下分析return id;}return NULL;
}
EXPORT_SYMBOL_GPL(usb_match_id);
int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
{if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&id->idVendor != le16_to_cpu(dev->descriptor.idVendor))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&id->idProduct != le16_to_cpu(dev->descriptor.idProduct))return 0;/* No need to test id->bcdDevice_lo != 0, since 0 is nevergreater than any unsigned number. */if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&(id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&(id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&(id->bDeviceClass != dev->descriptor.bDeviceClass))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&(id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))return 0;return 1;
}

该函数主要时完成id字段匹配,寻找对应的驱动,如果这里匹配成功,将调用usb_if_device_type类型。

8. usb_probe_interface()接口探测函数

static int usb_probe_interface(struct device *dev)
{struct usb_driver *driver = to_usb_driver(dev->driver);struct usb_interface *intf = to_usb_interface(dev);struct usb_device *udev = interface_to_usbdev(intf);const struct usb_device_id *id;int error = -ENODEV;int lpm_disable_error;dev_dbg(dev, "%s\n", __func__);intf->needs_binding = 0;if (usb_device_is_owned(udev))return error;if (udev->authorized == 0) {dev_err(&intf->dev, "Device is not authorized for usage\n");return error;}id = usb_match_id(intf, driver->id_table); //driver->id_table是哪里赋值的???if (!id)id = usb_match_dynamic_id(intf, driver);if (!id)return error;dev_dbg(dev, "%s - got id\n", __func__);error = usb_autoresume_device(udev);if (error)return error;intf->condition = USB_INTERFACE_BINDING;/* Probed interfaces are initially active.  They are* runtime-PM-enabled only if the driver has autosuspend support.* They are sensitive to their children's power states.*/pm_runtime_set_active(dev);pm_suspend_ignore_children(dev, false);if (driver->supports_autosuspend)pm_runtime_enable(dev);/* If the new driver doesn't allow hub-initiated LPM, and we can't* disable hub-initiated LPM, then fail the probe.** Otherwise, leaving LPM enabled should be harmless, because the* endpoint intervals should remain the same, and the U1/U2 timeouts* should remain the same.** If we need to install alt setting 0 before probe, or another alt* setting during probe, that should also be fine.  usb_set_interface()* will attempt to disable LPM, and fail if it can't disable it.*/lpm_disable_error = usb_unlocked_disable_lpm(udev);if (lpm_disable_error && driver->disable_hub_initiated_lpm) {dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",__func__, driver->name);error = lpm_disable_error;goto err;}/* Carry out a deferred switch to altsetting 0 */if (intf->needs_altsetting0) {error = usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);if (error < 0)goto err;intf->needs_altsetting0 = 0;}error = driver->probe(intf, id); //这里将调用usb_serial_probe()if (error)goto err;intf->condition = USB_INTERFACE_BOUND;/* If the LPM disable succeeded, balance the ref counts. */if (!lpm_disable_error)usb_unlocked_enable_lpm(udev);usb_autosuspend_device(udev);return error;err:usb_set_intfdata(intf, NULL);intf->needs_remote_wakeup = 0;intf->condition = USB_INTERFACE_UNBOUND;usb_cancel_queued_reset(intf);/* If the LPM disable succeeded, balance the ref counts. */if (!lpm_disable_error)usb_unlocked_enable_lpm(udev);/* Unbound interfaces are always runtime-PM-disabled and -suspended */if (driver->supports_autosuspend)pm_runtime_disable(dev);pm_runtime_set_suspended(dev);usb_autosuspend_device(udev);return error;
}

该接口探测函数的主要功能是通过usb_match_id(intf, driver->id_table),从id_table表中匹配与接口对应的id,如果成功将调用如下探测函数指针:

error = driver->probe(intf, id); //这里将调用usb_serial_probe()

为什么这里会调用usb_serial_probe函数,需要分析options.c文件是如何注册到usb总线上的???我猜想,应该时usb_serial_probe对应的结构体注册到了usb总线上,而usb_serial_probe自身又在“usb-serial”总线上,所以在匹配时是先匹配对应的usb总线上的驱动,然后才匹配对应的usb-serial总线上的驱动

9. usb_serial_probe() usb串口探测函数

static int usb_serial_probe(struct usb_interface *interface,const struct usb_device_id *id)
{struct device *ddev = &interface->dev;struct usb_device *dev = interface_to_usbdev(interface);struct usb_serial *serial = NULL;struct usb_serial_port *port;struct usb_host_interface *iface_desc;struct usb_endpoint_descriptor *endpoint;struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];struct usb_serial_driver *type = NULL;int retval;unsigned int minor;int buffer_size;int i;int j;int num_interrupt_in = 0;int num_interrupt_out = 0;int num_bulk_in = 0;int num_bulk_out = 0;int num_ports = 0;int max_endpoints;mutex_lock(&table_lock);type = search_serial_device(interface); //根据接口,从usb_serial_driver_list链表中搜索匹配的id对应的驱动if (!type) {mutex_unlock(&table_lock);dev_dbg(ddev, "none matched\n");return -ENODEV;}if (!try_module_get(type->driver.owner)) {mutex_unlock(&table_lock);dev_err(ddev, "module get failed, exiting\n");return -EIO;}mutex_unlock(&table_lock);serial = create_serial(dev, interface, type);if (!serial) {module_put(type->driver.owner);return -ENOMEM;}/* if this device type has a probe function, call it */if (type->probe) {const struct usb_device_id *id;id = get_iface_id(type, interface);retval = type->probe(serial, id); //调用上面匹配到的驱动探测函数if (retval) {dev_dbg(ddev, "sub driver rejected device\n");usb_serial_put(serial);module_put(type->driver.owner);return retval;}}/* descriptor matches, let's find the endpoints needed *//* check out the endpoints */iface_desc = interface->cur_altsetting;for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {endpoint = &iface_desc->endpoint[i].desc;if (usb_endpoint_is_bulk_in(endpoint)) {/* we found a bulk in endpoint */dev_dbg(ddev, "found bulk in on endpoint %d\n", i);bulk_in_endpoint[num_bulk_in] = endpoint;++num_bulk_in;}if (usb_endpoint_is_bulk_out(endpoint)) {/* we found a bulk out endpoint */dev_dbg(ddev, "found bulk out on endpoint %d\n", i);bulk_out_endpoint[num_bulk_out] = endpoint;++num_bulk_out;}if (usb_endpoint_is_int_in(endpoint)) {/* we found a interrupt in endpoint */dev_dbg(ddev, "found interrupt in on endpoint %d\n", i);interrupt_in_endpoint[num_interrupt_in] = endpoint;++num_interrupt_in;}if (usb_endpoint_is_int_out(endpoint)) {/* we found an interrupt out endpoint */dev_dbg(ddev, "found interrupt out on endpoint %d\n", i);interrupt_out_endpoint[num_interrupt_out] = endpoint;++num_interrupt_out;}}#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)/* BEGIN HORRIBLE HACK FOR PL2303 *//* this is needed due to the looney way its endpoints are set up */if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&(le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&(le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&(le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID)) ||((le16_to_cpu(dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&(le16_to_cpu(dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_EF81))) {if (interface != dev->actconfig->interface[0]) {/* check out the endpoints of the other interface*/iface_desc = dev->actconfig->interface[0]->cur_altsetting;for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {endpoint = &iface_desc->endpoint[i].desc;if (usb_endpoint_is_int_in(endpoint)) {/* we found a interrupt in endpoint */dev_dbg(ddev, "found interrupt in for Prolific device on separate interface\n");interrupt_in_endpoint[num_interrupt_in] = endpoint;++num_interrupt_in;}}}/* Now make sure the PL-2303 is configured correctly.* If not, give up now and hope this hack will work* properly during a later invocation of usb_serial_probe*/if (num_bulk_in == 0 || num_bulk_out == 0) {dev_info(ddev, "PL-2303 hack: descriptors matched but endpoints did not\n");usb_serial_put(serial);module_put(type->driver.owner);return -ENODEV;}}/* END HORRIBLE HACK FOR PL2303 */
#endif#ifdef CONFIG_USB_SERIAL_GENERICif (type == &usb_serial_generic_device) {num_ports = num_bulk_out;if (num_ports == 0) {dev_err(ddev, "Generic device with no bulk out, not allowed.\n");usb_serial_put(serial);module_put(type->driver.owner);return -EIO;}dev_info(ddev, "The \"generic\" usb-serial driver is only for testing and one-off prototypes.\n");dev_info(ddev, "Tell linux-usb@vger.kernel.org to add your device to a proper driver.\n");}
#endifif (!num_ports) {/* if this device type has a calc_num_ports function, call it */if (type->calc_num_ports)num_ports = type->calc_num_ports(serial);if (!num_ports)num_ports = type->num_ports;}serial->num_ports = num_ports;serial->num_bulk_in = num_bulk_in;serial->num_bulk_out = num_bulk_out;serial->num_interrupt_in = num_interrupt_in;serial->num_interrupt_out = num_interrupt_out;/* found all that we need */dev_info(ddev, "%s converter detected\n", type->description);/* create our ports, we need as many as the max endpoints *//* we don't use num_ports here because some devices have moreendpoint pairs than ports */max_endpoints = max(num_bulk_in, num_bulk_out);max_endpoints = max(max_endpoints, num_interrupt_in);max_endpoints = max(max_endpoints, num_interrupt_out);max_endpoints = max(max_endpoints, (int)serial->num_ports);serial->num_port_pointers = max_endpoints;dev_dbg(ddev, "setting up %d port structures for this device", max_endpoints);for (i = 0; i < max_endpoints; ++i) {port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);if (!port)goto probe_error;tty_port_init(&port->port);port->port.ops = &serial_port_ops;port->serial = serial;spin_lock_init(&port->lock);/* Keep this for private driver use for the moment butshould probably go away */INIT_WORK(&port->work, usb_serial_port_work);serial->port[i] = port;port->dev.parent = &interface->dev;port->dev.driver = NULL;port->dev.bus = &usb_serial_bus_type;port->dev.release = &usb_serial_port_release;device_initialize(&port->dev);}/* set up the endpoint information */for (i = 0; i < num_bulk_in; ++i) {endpoint = bulk_in_endpoint[i];port = serial->port[i];buffer_size = max_t(int, serial->type->bulk_in_size,usb_endpoint_maxp(endpoint));port->bulk_in_size = buffer_size;port->bulk_in_endpointAddress = endpoint->bEndpointAddress;for (j = 0; j < ARRAY_SIZE(port->read_urbs); ++j) {set_bit(j, &port->read_urbs_free);port->read_urbs[j] = usb_alloc_urb(0, GFP_KERNEL);if (!port->read_urbs[j])goto probe_error;port->bulk_in_buffers[j] = kmalloc(buffer_size,GFP_KERNEL);if (!port->bulk_in_buffers[j])goto probe_error;usb_fill_bulk_urb(port->read_urbs[j], dev,usb_rcvbulkpipe(dev,endpoint->bEndpointAddress),port->bulk_in_buffers[j], buffer_size,serial->type->read_bulk_callback,port);}port->read_urb = port->read_urbs[0];port->bulk_in_buffer = port->bulk_in_buffers[0];}for (i = 0; i < num_bulk_out; ++i) {endpoint = bulk_out_endpoint[i];port = serial->port[i];if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))goto probe_error;buffer_size = serial->type->bulk_out_size;if (!buffer_size)buffer_size = usb_endpoint_maxp(endpoint);port->bulk_out_size = buffer_size;port->bulk_out_endpointAddress = endpoint->bEndpointAddress;for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j) {set_bit(j, &port->write_urbs_free);port->write_urbs[j] = usb_alloc_urb(0, GFP_KERNEL);if (!port->write_urbs[j])goto probe_error;port->bulk_out_buffers[j] = kmalloc(buffer_size,GFP_KERNEL);if (!port->bulk_out_buffers[j])goto probe_error;usb_fill_bulk_urb(port->write_urbs[j], dev,usb_sndbulkpipe(dev,endpoint->bEndpointAddress),port->bulk_out_buffers[j], buffer_size,serial->type->write_bulk_callback,port);}port->write_urb = port->write_urbs[0];port->bulk_out_buffer = port->bulk_out_buffers[0];}if (serial->type->read_int_callback) {for (i = 0; i < num_interrupt_in; ++i) {endpoint = interrupt_in_endpoint[i];port = serial->port[i];port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);if (!port->interrupt_in_urb)goto probe_error;buffer_size = usb_endpoint_maxp(endpoint);port->interrupt_in_endpointAddress =endpoint->bEndpointAddress;port->interrupt_in_buffer = kmalloc(buffer_size,GFP_KERNEL);if (!port->interrupt_in_buffer)goto probe_error;usb_fill_int_urb(port->interrupt_in_urb, dev,usb_rcvintpipe(dev,endpoint->bEndpointAddress),port->interrupt_in_buffer, buffer_size,serial->type->read_int_callback, port,endpoint->bInterval);}} else if (num_interrupt_in) {dev_dbg(ddev, "The device claims to support interrupt in transfers, but read_int_callback is not defined\n");}if (serial->type->write_int_callback) {for (i = 0; i < num_interrupt_out; ++i) {endpoint = interrupt_out_endpoint[i];port = serial->port[i];port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);if (!port->interrupt_out_urb)goto probe_error;buffer_size = usb_endpoint_maxp(endpoint);port->interrupt_out_size = buffer_size;port->interrupt_out_endpointAddress =endpoint->bEndpointAddress;port->interrupt_out_buffer = kmalloc(buffer_size,GFP_KERNEL);if (!port->interrupt_out_buffer)goto probe_error;usb_fill_int_urb(port->interrupt_out_urb, dev,usb_sndintpipe(dev,endpoint->bEndpointAddress),port->interrupt_out_buffer, buffer_size,serial->type->write_int_callback, port,endpoint->bInterval);}} else if (num_interrupt_out) {dev_dbg(ddev, "The device claims to support interrupt out transfers, but write_int_callback is not defined\n");}usb_set_intfdata(interface, serial);/* if this device type has an attach function, call it */if (type->attach) {retval = type->attach(serial);if (retval < 0)goto probe_error;serial->attached = 1;if (retval > 0) {/* quietly accept this device, but don't bind to aserial port as it's about to disappear */serial->num_ports = 0;goto exit;}} else {serial->attached = 1;}/* Avoid race with tty_open and serial_install by setting the* disconnected flag and not clearing it until all ports have been* registered.*/serial->disconnected = 1;if (get_free_serial(serial, num_ports, &minor) == NULL) {dev_err(ddev, "No more free serial devices\n");goto probe_error;}serial->minor = minor;/* register all of the individual ports with the driver core */for (i = 0; i < num_ports; ++i) {port = serial->port[i];dev_set_name(&port->dev, "ttyUSB%d", port->number);dev_dbg(ddev, "registering %s", dev_name(&port->dev));device_enable_async_suspend(&port->dev);retval = device_add(&port->dev);if (retval)dev_err(ddev, "Error registering port device, continuing\n");}serial->disconnected = 0;usb_serial_console_init(minor);
exit:module_put(type->driver.owner);return 0;probe_error:usb_serial_put(serial);module_put(type->driver.owner);return -EIO;
}

该函数usb_serial_probe()主要通过search_serial_device()函数遍历链表usb_serial_driver_list链表,根据ID寻找匹配的驱动,如下源码:

static struct usb_serial_driver *search_serial_device(struct usb_interface *iface)
{const struct usb_device_id *id = NULL;struct usb_serial_driver *drv;struct usb_driver *driver = to_usb_driver(iface->dev.driver);/* Check if the usb id matches a known device */list_for_each_entry(drv, &usb_serial_driver_list, driver_list) {if (drv->usb_driver == driver)id = get_iface_id(drv, iface); //见下if (id)return drv;}return NULL;
}
static const struct usb_device_id *get_iface_id(struct usb_serial_driver *drv,struct usb_interface *intf)
{const struct usb_device_id *id;id = usb_match_id(intf, drv->id_table);if (id) {dev_dbg(&intf->dev, "static descriptor matches\n");goto exit;}id = match_dynamic_id(intf, drv);if (id)dev_dbg(&intf->dev, "dynamic descriptor matches\n");
exit:return id;
}

如果接口和驱动匹配成功,将调用驱动的探测函数,如下源码:

   if (type->probe) {const struct usb_device_id *id;id = get_iface_id(type, interface);retval = type->probe(serial, id); //调用上面匹配到的驱动探测函数if (retval) {dev_dbg(ddev, "sub driver rejected device\n");usb_serial_put(serial);module_put(type->driver.owner);return retval;}}

最后是统计端点是否为批量或中断端点的个数,最后通过该端点创建相应的信息......

我们再回到上面的函数:
retval = type->probe(serial, id); //调用上面匹配到的驱动探测函数

这里将调用options.c的探测函数,这个函数也是我们最终的根源,不容易!!!

10. option_probe()

static int option_probe(struct usb_serial *serial,const struct usb_device_id *id)
{struct usb_interface_descriptor *iface_desc =&serial->interface->cur_altsetting->desc;struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;/* Never bind to the CD-Rom emulation interface   */if (iface_desc->bInterfaceClass == 0x08)return -ENODEV;/** Don't bind reserved interfaces (like network ones) which often have* the same class/subclass/protocol as the serial interfaces.  Look at* the Windows driver .INF files for reserved interface numbers.*/if (is_blacklisted(iface_desc->bInterfaceNumber,OPTION_BLACKLIST_RESERVED_IF,(const struct option_blacklist_info *) id->driver_info))return -ENODEV;/** Don't bind network interface on Samsung GT-B3730, it is handled by* a separate module.*/if (dev_desc->idVendor == cpu_to_le16(SAMSUNG_VENDOR_ID) &&dev_desc->idProduct == cpu_to_le16(SAMSUNG_PRODUCT_GT_B3730) &&iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)return -ENODEV;/* Store device id so we can use it during attach. */usb_set_serial_data(serial, (void *)id);return 0;
}

至此,就完成了usb主机控制器上结束设备时的分析过程,本篇博客涉及的内容较多,但流程比较明确,先是线程检测到链表有变化-->设备枚举-->设备和驱动的匹配-->后面是接口和驱动的匹配-->再后来是调用usb_serial_probe-->再后来是调用option_probe。从上面第9节开始,没有详细的分析源码,只是简单的描述了usb物理口上接入设备的的创建过程,由于usb串口驱动篇幅较多,这里另起一篇博客,详见: 点击打开链接

hub_thread相关推荐

  1. 驱动07.USB驱动程序

    1 了解USB识别的过程 eg:在Windows系统下的一个现象:把手机的USB设备接到PC 1. 右下角弹出"发现android phone" 2. 跳出一个对话框,提示你安装驱 ...

  2. 分析USB平台设备模型框架(1)

    start_kernelrest_init();kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);do_basic_setup(); ...

  3. 设备和驱动在第一次加载,会遍历总线

    一个新设备加入到总线的过程 hub_thread是一个重要的hub守护进程会调用 hub_events hub_events会检测端口状态是否发生改变,改变后会调用 hub_port_connect_ ...

  4. 《Linux总线、设备与驱动》USB设备发现机制

    http://blog.csdn.net/tankai19880619/article/details/11639185 说明:本分析基于mstar801平台Linux2.6.35.11内核,其他内核 ...

  5. linux usb初始化

    linux usb 初始化 谨以此文纪念过往的岁月 一.前言 对于usb的普通驱动,我们了解了不少,但是对于usb的真正核心还是不是太理解.该文中对于usb的初始化进行一定的学习,如有不对之处,请各位 ...

  6. android6鼠标驱动,USB鼠标按键驱动(示例代码)

    现象:把USB设备接到PC 1. 右下角弹出"发现android phone" 2. 跳出一个对话框,提示你安装驱动程序 问1. 既然还没有"驱动程序",为何能 ...

  7. Usb设备驱动3:root hub守护进程2

    Hub正常工作后,主控制器就会定时询问hub是否有中断产生,当hub端口上有一个设备插入或拔除,hub就向主控制器发送urb请求,即把hub端口的变化状况告诉主控制器,这是通过urb请求来完成的,主机 ...

  8. USB设备被识别流程【转】

    转自:http://blog.csdn.net/myarrow/article/details/8286876 USB模块包括usb core,host,hub,device驱动,其中hub会启动一个 ...

  9. Linux复位usb hub,Linux USB subsystem --- USB Hub initialize

    目的:对USB作深入学习,在此留下笔记.欢迎讨论. [Linux 3.2] [driver/usb/core/hub.c] 函数:usb_hub_init intusb_hub_init(void) ...

最新文章

  1. 祝51CTO 生日快乐
  2. mysql 加密方式 caching_sha2_password 和 mysql_native_password 说明
  3. CloudStack管理员文档 - 服务方案
  4. 现代c++之列表初始化/统一初始化
  5. linux虚拟工具平台,【工具】打造适用于 Linux 的 Windows 子系统——替代虚拟机的好帮手...
  6. 最短哈密尔顿圈matlab解法_复杂制造过程最优哈密尔顿圈算法的MATLAB仿真与分析.doc...
  7. JAVA中如何取map的值_如何在java中取map中的键值 的两种方法
  8. Error creating bean with name 'dataSource' defined in class path resource [spring/spring-dao.xml]:
  9. 面向深度学习研究人员的自然语言处理实例教程
  10. 如果你是加勒比海盗首领,会选择哪种算法来使价值最大化?
  11. 1、matplotlib绘制一个简单的图形
  12. 趣说cpu指令集之别
  13. java 自定义 转换器_自定义类型转换器
  14. Linux系统备份及迁移到新硬盘
  15. LTSPICE使用教程:入门指导
  16. Java中dynamic是什么_java中dynamic web project与web project 的区别
  17. blender FLIP-Fluids 流体插件中文帮助文档系列01
  18. 骆昊python100天 github_GitHub - zj775230/Python-100-Days: Python - 100天从新手到大师
  19. 马云退隐前,在年会上说了最重要的三件事
  20. windows串口调试linux工具,推荐一款好用的串口调试软件PuTTY

热门文章

  1. http://cl.s6e.xyz/index.php,index.html · Pear Admin/Pear Admin Site - Gitee.com
  2. 【项目经验】Java web 页面跳转中文乱码
  3. 中国5G产业发展的五大优势
  4. 增值电信服务费是什么意思_中国移动的增值业务费是什么意思
  5. 面对困惑,职场之路该怎么走(附案例)---职场达人的忠告(转)
  6. 传智健康day05 预约管理-预约设置
  7. SGU 187 Twist and whirl - want to cheat(splay)
  8. win7安装onenote2016时碰到30094-1011(0)的 问题
  9. 临床医学计算机思维的应用情况,计算机模拟病例系统在医学生临床思维培养中应用价值分析.doc...
  10. 点云孔洞定位_孔洞修补研究总结