1. 前提:

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

2. hub_probe()流程图

hub_probe()_ide

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; //绑定总线类型为子系统usb
new_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_HUB
if (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); //分配一个hub
if (!hub) {
dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");
return -ENOMEM;
}

//初始化一个hub
kref_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);
} else
dev_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_work
switch (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;
} else
dev_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;
else
hdev->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_func2
schedule_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_PM
udev->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_func3
schedule_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点。