1. 前提:

在前面这边博客详细介绍了ehci驱动是如何创建usb主机控制器,最终调用hub驱动的hub_probe()探测函数,关于ehci驱动注册感兴趣的可以参考点击打开链接,本文主要讲述接口驱动最终调用hub_probe函数的处理。

2. hub_probe()流程图

3. hub驱动注册

retval = 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;}......
}

注意!在usb_register()函数内部会绑定接口驱动:

int usb_register_driver(struct usb_driver *new_driver, struct module *owner,const char *mod_name)
{int retval = 0;if (usb_disabled())return -ENODEV;//对drvwrap USB驱动包初始化new_driver->drvwrap.for_devices = 0;new_driver->drvwrap.driver.name = (char *) new_driver->name; //初始化驱动名称为“hub”new_driver->drvwrap.driver.bus = &usb_bus_type; //绑定总线类型为子系统usbnew_driver->drvwrap.driver.probe = usb_probe_interface; //重要,是接口的探针函数!!!该函数内部会调用usb_serial_probe()new_driver->drvwrap.driver.remove = usb_unbind_interface;new_driver->drvwrap.driver.owner = owner;new_driver->drvwrap.driver.mod_name = mod_name;spin_lock_init(&new_driver->dynids.lock);INIT_LIST_HEAD(&new_driver->dynids.list);retval = driver_register(&new_driver->drvwrap.driver); //驱动注册if (retval)goto out;retval = usb_create_newid_files(new_driver);if (retval)goto out_newid;pr_info("%s: registered new interface driver %s\n",usbcore_name, new_driver->name);out:return retval;out_newid:driver_unregister(&new_driver->drvwrap.driver);printk(KERN_ERR "%s: error %d registering interface "" driver %s\n",usbcore_name, retval, new_driver->name);goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);

hub_driver结构体驱动:

static struct usb_driver hub_driver = {.name =     "hub",.probe =   hub_probe, //hub探测接口,重要,待分析!.disconnect = hub_disconnect,.suspend =  hub_suspend, //hub挂起,涉及到PM电源管理驱动控制.resume = hub_resume, //hub恢复,.reset_resume = hub_reset_resume, //通过对hub复位来引发复位.pre_reset =  hub_pre_reset, //与PM电源管理系统相关.post_reset =  hub_post_reset, //与PM电源管理系统相关.unlocked_ioctl = hub_ioctl,.id_table =  hub_id_table, //hub id表,重要,待分析!!.supports_autosuspend =    1,
};

在本文“1. 前提”里提到过ehci驱动最终会调用hub_probe探测函数,ok,现在我们开始分析...

4. hub_probe()

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{struct usb_host_interface *desc;struct usb_endpoint_descriptor *endpoint;struct usb_device *hdev;struct usb_hub *hub;desc = intf->cur_altsetting; //获取当前设置的主机控制器接口hdev = interface_to_usbdev(intf);/** Set default autosuspend delay as 0 to speedup bus suspend,* based on the below considerations:** - Unlike other drivers, the hub driver does not rely on the*   autosuspend delay to provide enough time to handle a wakeup*   event, and the submitted status URB is just to check future*   change on hub downstream ports, so it is safe to do it.** - The patch might cause one or more auto supend/resume for*   below very rare devices when they are plugged into hub*   first time:**      devices having trouble initializing, and disconnect*    themselves from the bus and then reconnect a second*    or so later**       devices just for downloading firmware, and disconnects*     themselves after completing it**   For these quite rare devices, their drivers may change the*   autosuspend delay of their parent hub in the probe() to one*   appropriate value to avoid the subtle problem if someone*   does care it.** - The patch may cause one or more auto suspend/resume on*   hub during running 'lsusb', but it is probably too*   infrequent to worry about.** - Change autosuspend delay of hub can avoid unnecessary auto*   suspend timer for hub, also may decrease power consumption*   of USB bus.*/pm_runtime_set_autosuspend_delay(&hdev->dev, 0);/* Hubs have proper suspend/resume support. */usb_enable_autosuspend(hdev);if (hdev->level == MAX_TOPO_LEVEL) {dev_err(&intf->dev,"Unsupported bus topology: hub nested too deep\n");return -E2BIG;}#ifdef CONFIG_USB_OTG_BLACKLIST_HUBif (hdev->parent) {dev_warn(&intf->dev, "ignoring external hub\n");return -ENODEV;}
#endif/* Some hubs have a subclass of 1, which AFAICT according to the *//*  specs is not defined, but it works */if ((desc->desc.bInterfaceSubClass != 0) &&(desc->desc.bInterfaceSubClass != 1)) {
descriptor_error:dev_err (&intf->dev, "bad descriptor, ignoring hub\n");return -EIO;}/* Multiple endpoints? What kind of mutant ninja-hub is this? */if (desc->desc.bNumEndpoints != 1)goto descriptor_error;endpoint = &desc->endpoint[0].desc;/* If it's not an interrupt in endpoint, we'd better punt! */if (!usb_endpoint_is_int_in(endpoint))goto descriptor_error;/* We found a hub */dev_info (&intf->dev, "USB hub found\n");hub = kzalloc(sizeof(*hub), GFP_KERNEL); //分配一个hubif (!hub) {dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");return -ENOMEM;}//初始化一个hubkref_init(&hub->kref);INIT_LIST_HEAD(&hub->event_list);hub->intfdev = &intf->dev;hub->hdev = hdev;INIT_DELAYED_WORK(&hub->leds, led_work);INIT_DELAYED_WORK(&hub->init_work, NULL);usb_get_intf(intf);usb_set_intfdata (intf, hub);intf->needs_remote_wakeup = 1;pm_suspend_ignore_children(&intf->dev, true);if (hdev->speed == USB_SPEED_HIGH)highspeed_hubs++;if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)hub->quirk_check_port_auto_suspend = 1;if (hub_configure(hub, endpoint) >= 0)return 0;hub_disconnect (intf);return -ENODEV;
}

初始化hub,最终调用hub_configure():

5. hub_configure()

static int hub_configure(struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint)
{struct usb_hcd *hcd;struct usb_device *hdev = hub->hdev;struct device *hub_dev = hub->intfdev;u16 hubstatus, hubchange;u16 wHubCharacteristics;unsigned int pipe;int maxp, ret, i;char *message = "out of memory";unsigned unit_load;unsigned full_load;hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);if (!hub->buffer) {ret = -ENOMEM;goto fail;}hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);if (!hub->status) {ret = -ENOMEM;goto fail;}mutex_init(&hub->status_mutex);hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);if (!hub->descriptor) {ret = -ENOMEM;goto fail;}/* Request the entire hub descriptor.* hub->descriptor can handle USB_MAXCHILDREN ports,* but the hub can/will return fewer bytes here.*/ret = get_hub_descriptor(hdev, hub->descriptor);if (ret < 0) {message = "can't read hub descriptor";goto fail;} else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {message = "hub has too many ports!";ret = -ENODEV;goto fail;} else if (hub->descriptor->bNbrPorts == 0) {message = "hub doesn't have any ports!";ret = -ENODEV;goto fail;}hdev->maxchild = hub->descriptor->bNbrPorts; //端口个数dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,(hdev->maxchild == 1) ? "" : "s");hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *), //给端口分配空间GFP_KERNEL);if (!hub->ports) {ret = -ENOMEM;goto fail;}wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);if (hub_is_superspeed(hdev)) { //是否是usb3.0?unit_load = 150;full_load = 900;} else {unit_load = 100;full_load = 500;}/* FIXME for USB 3.0, skip for now */if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&!(hub_is_superspeed(hdev))) {int    i;char  portstr [USB_MAXCHILDREN + 1];for (i = 0; i < hdev->maxchild; i++)portstr[i] = hub->descriptor->u.hs.DeviceRemovable[((i + 1) / 8)] & (1 << ((i + 1) % 8))? 'F' : 'R';portstr[hdev->maxchild] = 0;dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);} elsedev_dbg(hub_dev, "standalone hub\n");switch (wHubCharacteristics & HUB_CHAR_LPSM) {case HUB_CHAR_COMMON_LPSM:dev_dbg(hub_dev, "ganged power switching\n");break;case HUB_CHAR_INDV_PORT_LPSM:dev_dbg(hub_dev, "individual port power switching\n");break;case HUB_CHAR_NO_LPSM:case HUB_CHAR_LPSM:dev_dbg(hub_dev, "no power switching (usb 1.0)\n");break;}switch (wHubCharacteristics & HUB_CHAR_OCPM) {case HUB_CHAR_COMMON_OCPM:dev_dbg(hub_dev, "global over-current protection\n");break;case HUB_CHAR_INDV_PORT_OCPM:dev_dbg(hub_dev, "individual port over-current protection\n");break;case HUB_CHAR_NO_OCPM:case HUB_CHAR_OCPM:dev_dbg(hub_dev, "no over-current protection\n");break;}spin_lock_init (&hub->tt.lock);INIT_LIST_HEAD (&hub->tt.clear_list);INIT_WORK(&hub->tt.clear_work, hub_tt_work); //初始化一个工作队列,绑定回调函数hub_tt_workswitch (hdev->descriptor.bDeviceProtocol) { //设备协议转换case USB_HUB_PR_FS:break;case USB_HUB_PR_HS_SINGLE_TT:dev_dbg(hub_dev, "Single TT\n");hub->tt.hub = hdev;break;case USB_HUB_PR_HS_MULTI_TT:ret = usb_set_interface(hdev, 0, 1);if (ret == 0) {dev_dbg(hub_dev, "TT per port\n");hub->tt.multi = 1;} elsedev_err(hub_dev, "Using single TT (err %d)\n",ret);hub->tt.hub = hdev;break;case USB_HUB_PR_SS:/* USB 3.0 hubs don't have a TT */break;default:dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",hdev->descriptor.bDeviceProtocol);break;}/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */switch (wHubCharacteristics & HUB_CHAR_TTTT) {case HUB_TTTT_8_BITS:if (hdev->descriptor.bDeviceProtocol != 0) {hub->tt.think_time = 666;dev_dbg(hub_dev, "TT requires at most %d ""FS bit times (%d ns)\n",8, hub->tt.think_time);}break;case HUB_TTTT_16_BITS:hub->tt.think_time = 666 * 2;dev_dbg(hub_dev, "TT requires at most %d ""FS bit times (%d ns)\n",16, hub->tt.think_time);break;case HUB_TTTT_24_BITS:hub->tt.think_time = 666 * 3;dev_dbg(hub_dev, "TT requires at most %d ""FS bit times (%d ns)\n",24, hub->tt.think_time);break;case HUB_TTTT_32_BITS:hub->tt.think_time = 666 * 4;dev_dbg(hub_dev, "TT requires at most %d ""FS bit times (%d ns)\n",32, hub->tt.think_time);break;}/* probe() zeroes hub->indicator[] */if (wHubCharacteristics & HUB_CHAR_PORTIND) {hub->has_indicators = 1;dev_dbg(hub_dev, "Port indicators are supported\n");}dev_dbg(hub_dev, "power on to power good time: %dms\n",hub->descriptor->bPwrOn2PwrGood * 2);/* power budgeting mostly matters with bus-powered hubs,* and battery-powered root hubs (may provide just 8 mA).*/ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);if (ret < 2) {message = "can't get hub status";goto fail;}le16_to_cpus(&hubstatus);hcd = bus_to_hcd(hdev->bus);if (hdev == hdev->bus->root_hub) {if (hcd->power_budget > 0)hdev->bus_mA = hcd->power_budget;elsehdev->bus_mA = full_load * hdev->maxchild;if (hdev->bus_mA >= full_load)hub->mA_per_port = full_load;else {hub->mA_per_port = hdev->bus_mA;hub->limited_power = 1;}} else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {int remaining = hdev->bus_mA -hub->descriptor->bHubContrCurrent;dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",hub->descriptor->bHubContrCurrent);hub->limited_power = 1;if (remaining < hdev->maxchild * unit_load)dev_warn(hub_dev,"insufficient power available ""to use all downstream ports\n");hub->mA_per_port = unit_load; /* 7.2.1 */} else { /* Self-powered external hub *//* FIXME: What about battery-powered external hubs that* provide less current per port? */hub->mA_per_port = full_load;}if (hub->mA_per_port < full_load)dev_dbg(hub_dev, "%umA bus power budget for each child\n",hub->mA_per_port);/* Update the HCD's internal representation of this hub before khubd* starts getting port status changes for devices under the hub.*/if (hcd->driver->update_hub_device) {ret = hcd->driver->update_hub_device(hcd, hdev,&hub->tt, GFP_KERNEL);if (ret < 0) {message = "can't update HCD hub info";goto fail;}}ret = hub_hub_status(hub, &hubstatus, &hubchange);if (ret < 0) {message = "can't get hub status";goto fail;}/* local power status reports aren't always correct */if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)dev_dbg(hub_dev, "local power source is %s\n",(hubstatus & HUB_STATUS_LOCAL_POWER)? "lost (inactive)" : "good");if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)dev_dbg(hub_dev, "%sover-current condition exists\n",(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");/* set up the interrupt endpoint* We use the EP's maxpacket size instead of (PORTS+1+7)/8* bytes as USB2.0[11.12.3] says because some hubs are known* to send more data (and thus cause overflow). For root hubs,* maxpktsize is defined in hcd.c's fake endpoint descriptors* to be big enough for at least USB_MAXCHILDREN ports. */pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));if (maxp > sizeof(*hub->buffer))maxp = sizeof(*hub->buffer);hub->urb = usb_alloc_urb(0, GFP_KERNEL);if (!hub->urb) {ret = -ENOMEM;goto fail;}usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,hub, endpoint->bInterval);/* maybe cycle the hub leds */if (hub->has_indicators && blinkenlights)hub->indicator [0] = INDICATOR_CYCLE;for (i = 0; i < hdev->maxchild; i++) {ret = usb_hub_create_port_device(hub, i + 1);if (ret < 0) {dev_err(hub->intfdev,"couldn't create port%d device.\n", i + 1);hdev->maxchild = i;goto fail_keep_maxchild;}}usb_hub_adjust_deviceremovable(hdev, hub->descriptor);hub_activate(hub, HUB_INIT);return 0;fail:hdev->maxchild = 0;
fail_keep_maxchild:dev_err (hub_dev, "config failed, %s (err %d)\n",message, ret);/* hub_disconnect() frees urb and descriptor */return ret;
}

该函数内部所做的工作比较多:

a> 获取hub描述符

b> 获取hub状态

c> 填充一个hub中断 hub_irq()

最终调用hub_activate()激活hub。

6. hub_activate()

static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{struct usb_device *hdev = hub->hdev;struct usb_hcd *hcd;int ret;int port1;int status;bool need_debounce_delay = false;unsigned delay;/* Continue a partial initialization */if (type == HUB_INIT2)goto init2;if (type == HUB_INIT3)goto init3;/* The superspeed hub except for root hub has to use Hub Depth* value as an offset into the route string to locate the bits* it uses to determine the downstream port number. So hub driver* should send a set hub depth request to superspeed hub after* the superspeed hub is set configuration in initialization or* reset procedure.** After a resume, port power should still be on.* For any other type of activation, turn it on.*/if (type != HUB_RESUME) {if (hdev->parent && hub_is_superspeed(hdev)) {ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),HUB_SET_DEPTH, USB_RT_HUB,hdev->level - 1, 0, NULL, 0,USB_CTRL_SET_TIMEOUT);if (ret < 0)dev_err(hub->intfdev,"set hub depth failed\n");}/* Speed up system boot by using a delayed_work for the* hub's initial power-up delays.  This is pretty awkward* and the implementation looks like a home-brewed sort of* setjmp/longjmp, but it saves at least 100 ms for each* root hub (assuming usbcore is compiled into the kernel* rather than as a module).  It adds up.** This can't be done for HUB_RESUME or HUB_RESET_RESUME* because for those activation types the ports have to be* operational when we return.  In theory this could be done* for HUB_POST_RESET, but it's easier not to.*/if (type == HUB_INIT) { //初始化delay = hub_power_on(hub, false); //hub上电PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2); //创建工作队列,同时绑定对应的回调函数hub_init_func2schedule_delayed_work(&hub->init_work,msecs_to_jiffies(delay));/* Suppress autosuspend until init is done */usb_autopm_get_interface_no_resume(to_usb_interface(hub->intfdev));return;     /* Continues at init2: below */} else if (type == HUB_RESET_RESUME) {/* The internal host controller state for the hub device* may be gone after a host power loss on system resume.* Update the device's info so the HW knows it's a hub.*/hcd = bus_to_hcd(hdev->bus);if (hcd->driver->update_hub_device) {ret = hcd->driver->update_hub_device(hcd, hdev,&hub->tt, GFP_NOIO);if (ret < 0) {dev_err(hub->intfdev, "Host not ""accepting hub info ""update.\n");dev_err(hub->intfdev, "LS/FS devices ""and hubs may not work ""under this hub\n.");}}hub_power_on(hub, true);} else {hub_power_on(hub, true);}}init2:/* Check each port and set hub->change_bits to let khubd know* which ports need attention.*/for (port1 = 1; port1 <= hdev->maxchild; ++port1) {struct usb_device *udev = hub->ports[port1 - 1]->child;u16 portstatus, portchange;portstatus = portchange = 0;status = hub_port_status(hub, port1, &portstatus, &portchange);if (udev || (portstatus & USB_PORT_STAT_CONNECTION))dev_dbg(hub->intfdev,"port %d: status %04x change %04x\n",port1, portstatus, portchange);/* After anything other than HUB_RESUME (i.e., initialization* or any sort of reset), every port should be disabled.* Unconnected ports should likewise be disabled (paranoia),* and so should ports for which we have no usb_device.*/if ((portstatus & USB_PORT_STAT_ENABLE) && (type != HUB_RESUME ||!(portstatus & USB_PORT_STAT_CONNECTION) ||!udev ||udev->state == USB_STATE_NOTATTACHED)) {/** USB3 protocol ports will automatically transition* to Enabled state when detect an USB3.0 device attach.* Do not disable USB3 protocol ports.*/if (!hub_is_superspeed(hdev)) {usb_clear_port_feature(hdev, port1,USB_PORT_FEAT_ENABLE);portstatus &= ~USB_PORT_STAT_ENABLE;} else {/* Pretend that power was lost for USB3 devs */portstatus &= ~USB_PORT_STAT_ENABLE;}}/* Clear status-change flags; we'll debounce later */if (portchange & USB_PORT_STAT_C_CONNECTION) {need_debounce_delay = true;usb_clear_port_feature(hub->hdev, port1,USB_PORT_FEAT_C_CONNECTION);}if (portchange & USB_PORT_STAT_C_ENABLE) {need_debounce_delay = true;usb_clear_port_feature(hub->hdev, port1,USB_PORT_FEAT_C_ENABLE);}if (portchange & USB_PORT_STAT_C_RESET) {need_debounce_delay = true;usb_clear_port_feature(hub->hdev, port1,USB_PORT_FEAT_C_RESET);}if ((portchange & USB_PORT_STAT_C_BH_RESET) &&hub_is_superspeed(hub->hdev)) {need_debounce_delay = true;usb_clear_port_feature(hub->hdev, port1,USB_PORT_FEAT_C_BH_PORT_RESET);}/* 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 (!udev || udev->state == USB_STATE_NOTATTACHED) {/* Tell khubd to disconnect the device or* check for a new connection*/if (udev || (portstatus & USB_PORT_STAT_CONNECTION))set_bit(port1, hub->change_bits);} else if (portstatus & USB_PORT_STAT_ENABLE) {bool port_resumed = (portstatus &USB_PORT_STAT_LINK_STATE) ==USB_SS_PORT_LS_U0;/* The power session apparently survived the resume.* If there was an overcurrent or suspend change* (i.e., remote wakeup request), have khubd* take care of it.  Look at the port link state* for USB 3.0 hubs, since they don't have a suspend* change bit, and they don't set the port link change* bit on device-initiated resume.*/if (portchange || (hub_is_superspeed(hub->hdev) &&port_resumed))set_bit(port1, hub->change_bits);} else if (udev->persist_enabled) {struct usb_port *port_dev = hub->ports[port1 - 1];#ifdef CONFIG_PMudev->reset_resume = 1;
#endif/* Don't set the change_bits when the device* was powered off.*/if (port_dev->power_is_on)set_bit(port1, hub->change_bits);} else {/* The power session is gone; tell khubd */usb_set_device_state(udev, USB_STATE_NOTATTACHED);set_bit(port1, hub->change_bits);}}/* If no port-status-change flags were set, we don't need any* debouncing.  If flags were set we can try to debounce the* ports all at once right now, instead of letting khubd do them* one at a time later on.** If any port-status changes do occur during this delay, khubd* will see them later and handle them normally.*/if (need_debounce_delay) {delay = HUB_DEBOUNCE_STABLE;/* Don't do a long sleep inside a workqueue routine */if (type == HUB_INIT2) {PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3); //初始化工作队列,同时绑定回调函数hub_init_func3schedule_delayed_work(&hub->init_work,msecs_to_jiffies(delay));return;      /* Continues at init3: below */} else {msleep(delay);}}init3:hub->quiescing = 0;status = usb_submit_urb(hub->urb, GFP_NOIO);if (status < 0)dev_err(hub->intfdev, "activate --> %d\n", status);if (hub->has_indicators && blinkenlights)schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);/* Scan all ports that need attention */kick_khubd(hub);/* Allow autosuspend if it was suppressed */if (type <= HUB_INIT3)usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
}

该函数内部使用了几个延时工作队列,

PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2); //创建工作队列,同时绑定对应的回调函数hub_init_func2
static void hub_init_func2(struct work_struct *ws)
{struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);hub_activate(hub, HUB_INIT2); //激活HUB_INIT2
}
PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3); //初始化工作队列,同时绑定回调函数hub_init_func3
static void hub_init_func3(struct work_struct *ws)
{struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);hub_activate(hub, HUB_INIT3);
}

最终调用kick_khubd()

7. kick_khubd()

kick_khubd(hub);
static void kick_khubd(struct usb_hub *hub)
{unsigned long  flags;spin_lock_irqsave(&hub_event_lock, flags);if (!hub->disconnected && list_empty(&hub->event_list)) {list_add_tail(&hub->event_list, &hub_event_list); //将hub添加到hub_event_list链表中,/* Suppress autosuspend until khubd runs */usb_autopm_get_interface_no_resume(to_usb_interface(hub->intfdev));wake_up(&khubd_wait); //唤醒等待队列}spin_unlock_irqrestore(&hub_event_lock, flags);
}

到这里我们见到了熟悉的工作机制,就是将当前hub加入到链表hub_event_list,同时唤醒等待队列wake_up(&khubd_wait),而该链表和等待队列就是在hub_thread()线程内部完成的,详见 点击打开链接第3点。

hub_probe()相关推荐

  1. Linux那些事儿 之 戏说USB(25)设备的生命线(八)

    回到struct usb_hcd,继续努力的往下看. 7行,又见kref,usb主机控制器的引用计数.struct usb_hcd也有自己专用的引用计数函数,看drivers/usb/core/hcd ...

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

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

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

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

  4. MSM8953 core 3.0 usb otg USB 笔记

    https://blog.csdn.net/gjy_it/article/details/82223151 8953走的是dwc3-msm.c,8909走的是phy-msm-usb.c // devi ...

  5. USB OTG(Host) 、 USB ADB(Device)、DWC3 Charge

    USB ADB: Corresponding to USB Device Mode USB OTG: Corresponding to USB Host Mode USB EAP: Correspon ...

  6. Linux boot 时 USB 的初始化过程分析2

    现状 OK6410A linux-5.11 1个 roothub 上面接了2个 port , 无接其他设备 具体过程:=========================== 初始化第1.1阶段 ohc ...

  7. 高通SDX55平台:adb功能异常

    展锐UDX710:LAN7800 PHY驱动调试 1. 问题描述 2. 问题分析 2.1 测试环境 2.2 初步分析 2.3 USB驱动初始化 2.3.1 USB驱动加载流程 2.3.1.1 USB_ ...

  8. USB鼠标驱动开发流程

    USB驱动开发,针对某一个USB设备的某个功能(接口)构建的驱动程序.USB驱动并不直接和USB设备进行数据交互,而是通过USB总线驱动程序(USB Core和USB HCD)来操作USB设备的.一般 ...

  9. Linux USB 驱动开发—— 热插拔

    转自:https://blog.csdn.net/zqixiao_09/article/details/51056903 学习USB热插拔之前,先学习一些USB的硬件知识. 一.USB基础概念 1.硬 ...

最新文章

  1. Java项目:仿天猫网上商城项目(java+jsp+servlet+mysql+ajax)
  2. JDBC Driver常用连接方法列表
  3. [网络流24题]最小路径覆盖问题
  4. mysql handlers,2 Handlers
  5. bzoj1699[Usaco2007 Jan]Balanced Lineup排队
  6. 操作表格_Excel表格基础操作-新手入门级
  7. 信息学奥赛一本通 1133:输出亲朋字符串 | OpenJudge NOI 1.7 05
  8. mysql binary模式_MySQL数据库之MySQL的binary类型操作
  9. svn拉取文件合并_svn - SVN - 大象笔记
  10. 任意Android设备上运行测试
  11. 匠心耕耘,新华三大学筑梦远航
  12. 苹果往事:乔布斯和 iPod 的诞生
  13. 二级计算机题世界动物日,计算机二级考试真题-PPT-张宇-世界动物日介绍
  14. Ubuntu下安装为知笔记
  15. 报错:Apache提示 the requested operation has failed无法启动问题(最全!)
  16. 【转载】HBase基本概念和hbase shell常用命令用法
  17. 简单js模仿浏览器crtl+f功能
  18. linux deploy安装kali和磁盘扩容
  19. 教育平台、刷题、技术社区
  20. python语言程序设计基础考试题库_中国大学MOOC(慕课)_Python语言程序设计基础_章节考试选修课答案...

热门文章

  1. 你写的JSP就是JSP么?
  2. 微信小程序-wxml笔记(更新中)
  3. Django-Docker容器化部署:Django-Docker-MySQL-Nginx-Gunicorn云端部署
  4. 来自Bitly的USA.gov数据,数据分析案例
  5. excel表格数据汇总计数如何完成?
  6. 利用background-position 制作图片爆炸的效果
  7. linux重启domino服务,在Unix平台中通过脚本来启动和停止Domino
  8. Linux之scp与sftp的使用
  9. win10桌面文件丢失的处理总结
  10. python打印数字对应的星号_python打印表格式数据-星号或注释