本文主要分析hub驱动的probe过程,如何判断hub端口的变化。
直接看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);
- /* Hubs have proper suspend/resume support. USB 3.0 device suspend is
- * different from USB 2.0/1.1 device suspend, and unfortunately we
- * don't support it yet. So leave autosuspend disabled for USB 3.0
- * external hubs for now. Enable autosuspend for USB 3.0 roothubs,
- * since that isn't a "real" hub.
- */
- if (!hub_is_superspeed(hdev) || !hdev->parent)
- usb_disable_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); //分配一个usb_hub
- if (!hub) {
- dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");
- return -ENOMEM;
- }
- 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;
- if (hdev->speed == USB_SPEED_HIGH)
- highspeed_hubs++;
- if (hub_configure(hub, endpoint) >= 0) //对hub进行配置
- return 0;
- hub_disconnect (intf);
- return -ENODEV;
- }
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);
/* Hubs have proper suspend/resume support. USB 3.0 device suspend is
* different from USB 2.0/1.1 device suspend, and unfortunately we
* don't support it yet. So leave autosuspend disabled for USB 3.0
* external hubs for now. Enable autosuspend for USB 3.0 roothubs,
* since that isn't a "real" hub.
*/
if (!hub_is_superspeed(hdev) || !hdev->parent)
usb_disable_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); //分配一个usb_hub
if (!hub) {
dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");
return -ENOMEM;
}
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;
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs++;
if (hub_configure(hub, endpoint) >= 0) //对hub进行配置
return 0;
hub_disconnect (intf);
return -ENODEV;
}
hub _configure这个函数很长,主要干了两件事:
(1)usb_alloc_urb分配一个urb,调用usb_fill_int_urb 填充该urb结构,并且回调函数为hub_irq,hub_irq函数比较重要
(2)hub_activate(hub, HUB_INIT)激活hub
hub_activate函数也很长,主要干了两件事:
(1)usb_submit_urb(hub->urb, GFP_NOIO),向主机控制器层提交urb。
(2)kick_khubd(hub),唤醒处于睡眠中的khubd内核线程。
- static void hub_irq(struct urb *urb)
- {
- struct usb_hub *hub = urb->context;
- int status = urb->status;
- unsigned i;
- unsigned long bits;
- switch (status) {
- case -ENOENT: /* synchronous unlink */
- case -ECONNRESET: /* async unlink */
- case -ESHUTDOWN: /* hardware going away */
- return;
- default: /* presumably an error */
- /* Cause a hub reset after 10 consecutive errors */
- dev_dbg (hub->intfdev, "transfer --> %d\n", status);
- if ((++hub->nerrors < 10) || hub->error)
- goto resubmit;
- hub->error = status;
- /* FALL THROUGH */
- /* let khubd handle things */
- case 0: /* we got data: port status changed */
- bits = 0;
- for (i = 0; i < urb->actual_length; ++i)
- bits |= ((unsigned long) ((*hub->buffer)[i]))
- << (i*8);
- hub->event_bits[0] = bits;
- break;
- }
- hub->nerrors = 0;
- /* Something happened, let khubd figure it out */
- kick_khubd(hub); //hub_irq也调用了kick_khubd函数
- resubmit:
- if (hub->quiescing)
- return;
- if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
- && status != -ENODEV && status != -EPERM)
- dev_err (hub->intfdev, "resubmit --> %d\n", status);
- }
- 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_event_list
- /* Suppress autosuspend until khubd runs */
- usb_autopm_get_interface_no_resume(
- to_usb_interface(hub->intfdev));
- wake_up(&khubd_wait); //唤醒khubd
- }
- spin_unlock_irqrestore(&hub_event_lock, flags);
- }
static void hub_irq(struct urb *urb)
{
struct usb_hub *hub = urb->context;
int status = urb->status;
unsigned i;
unsigned long bits;
switch (status) {
case -ENOENT: /* synchronous unlink */
case -ECONNRESET: /* async unlink */
case -ESHUTDOWN: /* hardware going away */
return;
default: /* presumably an error */
/* Cause a hub reset after 10 consecutive errors */
dev_dbg (hub->intfdev, "transfer --> %d\n", status);
if ((++hub->nerrors < 10) || hub->error)
goto resubmit;
hub->error = status;
/* FALL THROUGH */
/* let khubd handle things */
case 0: /* we got data: port status changed */
bits = 0;
for (i = 0; i < urb->actual_length; ++i)
bits |= ((unsigned long) ((*hub->buffer)[i]))
<< (i*8);
hub->event_bits[0] = bits;
break;
}
hub->nerrors = 0;
/* Something happened, let khubd figure it out */
kick_khubd(hub); //hub_irq也调用了kick_khubd函数
resubmit:
if (hub->quiescing)
return;
if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
&& status != -ENODEV && status != -EPERM)
dev_err (hub->intfdev, "resubmit --> %d\n", status);
}
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_event_list
/* Suppress autosuspend until khubd runs */
usb_autopm_get_interface_no_resume(
to_usb_interface(hub->intfdev));
wake_up(&khubd_wait); //唤醒khubd
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
看khubd线程干了什么事情,应该是等某个事情处于睡眠状态,等待被唤醒。
- 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;
- }
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_events函数。
- static void hub_events(void)
- {
- struct list_head *tmp;
- struct usb_device *hdev;
- struct usb_interface *intf;
- struct usb_hub *hub;
- struct device *hub_dev;
- u16 hubstatus;
- u16 hubchange;
- u16 portstatus;
- u16 portchange;
- int i, ret;
- int connect_change;
- /*
- * We restart the list every time to avoid a deadlock with
- * deleting hubs downstream from this one. This should be
- * safe since we delete the hub from the event list.
- * Not the most efficient, but avoids deadlocks.
- */
- while (1) {
- /* Grab the first entry at the beginning of the list */
- spin_lock_irq(&hub_event_lock);
- if (list_empty(&hub_event_list)) { //如果hub_event_list为空跳出循环
- spin_unlock_irq(&hub_event_lock);
- break;
- }
- tmp = hub_event_list.next; //取出一项
- list_del_init(tmp);
- hub = list_entry(tmp, struct usb_hub, event_list); //得到usb_hub结构
- kref_get(&hub->kref);
- spin_unlock_irq(&hub_event_lock);
- hdev = hub->hdev;
- hub_dev = hub->intfdev;
- intf = to_usb_interface(hub_dev);
- dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
- hdev->state, hub->descriptor
- ? hub->descriptor->bNbrPorts
- : 0,
- /* NOTE: expects max 15 ports... */
- (u16) hub->change_bits[0],
- (u16) hub->event_bits[0]);
- /* Lock the device, then check to see if we were
- * disconnected while waiting for the lock to succeed. */
- usb_lock_device(hdev);
- if (unlikely(hub->disconnected)) //是否在链接
- goto loop_disconnected;
- /* If the hub has died, clean up after it */ //如果处于NOTATTACHED状态
- if (hdev->state == USB_STATE_NOTATTACHED) {
- hub->error = -ENODEV;
- hub_quiesce(hub, HUB_DISCONNECT);
- goto loop;
- }
- /* Autoresume */
- ret = usb_autopm_get_interface(intf);
- if (ret) {
- dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
- goto loop;
- }
- /* If this is an inactive hub, do nothing */
- if (hub->quiescing)
- goto loop_autopm;
- //如果有错误
- if (hub->error) {
- dev_dbg (hub_dev, "resetting for error %d\n",
- hub->error);
- ret = usb_reset_device(hdev); //如果有错误重启hub
- 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)) //如果port处于resume或reset状态,跳过
- continue;
- connect_change = test_bit(i, hub->change_bits);
- //如果event_bits为0,change_bits也为0,也跳过
- if (!test_and_clear_bit(i, hub->event_bits) &&
- !connect_change)
- continue;
- //获取portstatus,portchange
- ret = hub_port_status(hub, i,
- &portstatus, &portchange);
- if (ret < 0)
- continue;
- //连接状态发生变化
- if (portchange & USB_PORT_STAT_C_CONNECTION) {
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_CONNECTION);
- connect_change = 1;
- }
- //使能状态发生变化
- if (portchange & USB_PORT_STAT_C_ENABLE) {
- if (!connect_change)
- dev_dbg (hub_dev,
- "port %d enable change, "
- "status %08x\n",
- i, portstatus);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_ENABLE);
- /*
- * EM interference sometimes causes badly
- * shielded USB devices to be shutdown by
- * the hub, this hack enables them again.
- * Works at least with mouse driver.
- */
- //EMI引起的状态变化
- if (!(portstatus & USB_PORT_STAT_ENABLE)
- && !connect_change
- && hdev->children[i-1]) {
- dev_err (hub_dev,
- "port %i "
- "disabled by hub (EMI?), "
- "re-enabling...\n",
- i);
- connect_change = 1;
- }
- }
- //suspend状态发生变化
- if (portchange & USB_PORT_STAT_C_SUSPEND) {
- struct usb_device *udev;
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_SUSPEND);
- udev = hdev->children[i-1];
- if (udev) {
- /* TRSMRCY = 10 msec */
- msleep(10);
- usb_lock_device(udev);
- ret = usb_remote_wakeup(hdev->
- children[i-1]);
- usb_unlock_device(udev);
- if (ret < 0)
- connect_change = 1;
- } else {
- ret = -ENODEV;
- hub_port_disable(hub, i, 1);
- }
- dev_dbg (hub_dev,
- "resume on port %d, status %d\n",
- i, ret);
- }
- //port的过流发生变化
- if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
- u16 status = 0;
- u16 unused;
- dev_dbg(hub_dev, "over-current change on port "
- "%d\n", i);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_OVER_CURRENT);
- msleep(100); /* Cool down */
- hub_power_on(hub, true);
- hub_port_status(hub, i, &status, &unused);
- if (status & USB_PORT_STAT_OVERCURRENT)
- dev_err(hub_dev, "over-current "
- "condition on port %d\n", i);
- }
- //port reset完成
- if (portchange & USB_PORT_STAT_C_RESET) {
- dev_dbg (hub_dev,
- "reset change on port %d\n",
- i);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_RESET);
- }
- if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
- hub_is_superspeed(hub->hdev)) {
- dev_dbg(hub_dev,
- "warm reset change on port %d\n",
- i);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_BH_PORT_RESET);
- }
- if (portchange & USB_PORT_STAT_C_LINK_STATE) {
- clear_port_feature(hub->hdev, i,
- USB_PORT_FEAT_C_PORT_LINK_STATE);
- }
- if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
- dev_warn(hub_dev,
- "config error on port %d\n",
- i);
- clear_port_feature(hub->hdev, i,
- USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
- }
- /* Warm reset a USB3 protocol port if it's in
- * SS.Inactive state.
- */
- if (hub_is_superspeed(hub->hdev) &&
- (portstatus & USB_PORT_STAT_LINK_STATE)
- == USB_SS_PORT_LS_SS_INACTIVE) {
- dev_dbg(hub_dev, "warm reset port %d\n", i);
- hub_port_warm_reset(hub, i);
- }
- //如果发生变化,调用hub_port_connect_change函数
- if (connect_change)
- 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;
- else
- hub->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) */
- }
static void hub_events(void)
{
struct list_head *tmp;
struct usb_device *hdev;
struct usb_interface *intf;
struct usb_hub *hub;
struct device *hub_dev;
u16 hubstatus;
u16 hubchange;
u16 portstatus;
u16 portchange;
int i, ret;
int connect_change;
/*
* We restart the list every time to avoid a deadlock with
* deleting hubs downstream from this one. This should be
* safe since we delete the hub from the event list.
* Not the most efficient, but avoids deadlocks.
*/
while (1) {
/* Grab the first entry at the beginning of the list */
spin_lock_irq(&hub_event_lock);
if (list_empty(&hub_event_list)) { //如果hub_event_list为空跳出循环
spin_unlock_irq(&hub_event_lock);
break;
}
tmp = hub_event_list.next; //取出一项
list_del_init(tmp);
hub = list_entry(tmp, struct usb_hub, event_list); //得到usb_hub结构
kref_get(&hub->kref);
spin_unlock_irq(&hub_event_lock);
hdev = hub->hdev;
hub_dev = hub->intfdev;
intf = to_usb_interface(hub_dev);
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
hdev->state, hub->descriptor
? hub->descriptor->bNbrPorts
: 0,
/* NOTE: expects max 15 ports... */
(u16) hub->change_bits[0],
(u16) hub->event_bits[0]);
/* Lock the device, then check to see if we were
* disconnected while waiting for the lock to succeed. */
usb_lock_device(hdev);
if (unlikely(hub->disconnected)) //是否在链接
goto loop_disconnected;
/* If the hub has died, clean up after it */ //如果处于NOTATTACHED状态
if (hdev->state == USB_STATE_NOTATTACHED) {
hub->error = -ENODEV;
hub_quiesce(hub, HUB_DISCONNECT);
goto loop;
}
/* Autoresume */
ret = usb_autopm_get_interface(intf);
if (ret) {
dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
goto loop;
}
/* If this is an inactive hub, do nothing */
if (hub->quiescing)
goto loop_autopm;
//如果有错误
if (hub->error) {
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
ret = usb_reset_device(hdev); //如果有错误重启hub
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)) //如果port处于resume或reset状态,跳过
continue;
connect_change = test_bit(i, hub->change_bits);
//如果event_bits为0,change_bits也为0,也跳过
if (!test_and_clear_bit(i, hub->event_bits) &&
!connect_change)
continue;
//获取portstatus,portchange
ret = hub_port_status(hub, i,
&portstatus, &portchange);
if (ret < 0)
continue;
//连接状态发生变化
if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_CONNECTION);
connect_change = 1;
}
//使能状态发生变化
if (portchange & USB_PORT_STAT_C_ENABLE) {
if (!connect_change)
dev_dbg (hub_dev,
"port %d enable change, "
"status %08x\n",
i, portstatus);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_ENABLE);
/*
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
* Works at least with mouse driver.
*/
//EMI引起的状态变化
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
&& hdev->children[i-1]) {
dev_err (hub_dev,
"port %i "
"disabled by hub (EMI?), "
"re-enabling...\n",
i);
connect_change = 1;
}
}
//suspend状态发生变化
if (portchange & USB_PORT_STAT_C_SUSPEND) {
struct usb_device *udev;
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_SUSPEND);
udev = hdev->children[i-1];
if (udev) {
/* TRSMRCY = 10 msec */
msleep(10);
usb_lock_device(udev);
ret = usb_remote_wakeup(hdev->
children[i-1]);
usb_unlock_device(udev);
if (ret < 0)
connect_change = 1;
} else {
ret = -ENODEV;
hub_port_disable(hub, i, 1);
}
dev_dbg (hub_dev,
"resume on port %d, status %d\n",
i, ret);
}
//port的过流发生变化
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
u16 status = 0;
u16 unused;
dev_dbg(hub_dev, "over-current change on port "
"%d\n", i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_OVER_CURRENT);
msleep(100); /* Cool down */
hub_power_on(hub, true);
hub_port_status(hub, i, &status, &unused);
if (status & USB_PORT_STAT_OVERCURRENT)
dev_err(hub_dev, "over-current "
"condition on port %d\n", i);
}
//port reset完成
if (portchange & USB_PORT_STAT_C_RESET) {
dev_dbg (hub_dev,
"reset change on port %d\n",
i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_RESET);
}
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
hub_is_superspeed(hub->hdev)) {
dev_dbg(hub_dev,
"warm reset change on port %d\n",
i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_BH_PORT_RESET);
}
if (portchange & USB_PORT_STAT_C_LINK_STATE) {
clear_port_feature(hub->hdev, i,
USB_PORT_FEAT_C_PORT_LINK_STATE);
}
if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
dev_warn(hub_dev,
"config error on port %d\n",
i);
clear_port_feature(hub->hdev, i,
USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
}
/* Warm reset a USB3 protocol port if it's in
* SS.Inactive state.
*/
if (hub_is_superspeed(hub->hdev) &&
(portstatus & USB_PORT_STAT_LINK_STATE)
== USB_SS_PORT_LS_SS_INACTIVE) {
dev_dbg(hub_dev, "warm reset port %d\n", i);
hub_port_warm_reset(hub, i);
}
//如果发生变化,调用hub_port_connect_change函数
if (connect_change)
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;
else
hub->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) */
}
- static void hub_port_connect_change(struct usb_hub *hub, int port1,
- u16 portstatus, u16 portchange)
- {
- struct usb_device *hdev = hub->hdev;
- struct device *hub_dev = hub->intfdev;
- struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
- unsigned wHubCharacteristics =
- le16_to_cpu(hub->descriptor->wHubCharacteristics);
- struct usb_device *udev;
- int status, i;
- dev_dbg (hub_dev,
- "port %d, status %04x, change %04x, %s\n",
- port1, portstatus, portchange, portspeed(hub, portstatus));
- //hub支持indicator指示灯
- 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 */
- //试着唤醒一些处于suspend的设备
- udev = hdev->children[port1-1];
- if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
- udev->state != USB_STATE_NOTATTACHED) {
- usb_lock_device(udev);
- if (portstatus & USB_PORT_STAT_ENABLE) { //已经使能
- status = 0; /* Nothing to do */
- #ifdef CONFIG_USB_SUSPEND
- } else if (udev->state == USB_STATE_SUSPENDED &&
- udev->persist_enabled) { //处于suspend状态
- /* 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) //把一些处于NOTATTACHED的设备从hub断开
- usb_disconnect(&hdev->children[port1-1]);
- clear_bit(port1, hub->change_bits);
- /* We can forget about a "removed" device when there's a physical
- * disconnect or the connect status changes.
- */
- if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
- (portchange & USB_PORT_STAT_C_CONNECTION))
- clear_bit(port1, hub->removed_bits);
- if (portchange & (USB_PORT_STAT_C_CONNECTION |
- USB_PORT_STAT_C_ENABLE)) {
- status = hub_port_debounce(hub, port1); //防抖处理
- if (status < 0) {
- if (printk_ratelimit())
- dev_err(hub_dev, "connect-debounce failed, "
- "port %d disabled\n", port1);
- portstatus &= ~USB_PORT_STAT_CONNECTION;
- } else {
- portstatus = status;
- }
- }
- /* Return now if debouncing failed or nothing is connected or
- * the device was "removed".
- */
- if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
- test_bit(port1, hub->removed_bits)) {
- /* maybe switch power back on (e.g. root hub was reset) */
- if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
- && !port_is_power_on(hub, portstatus))
- set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
- if (portstatus & USB_PORT_STAT_ENABLE)
- goto done;
- return;
- }
- //对于一个新插入的设备,为这个usb设备枚举
- 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_device结构
- 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; //可以从port上获取的电量
- udev->level = hdev->level + 1; //等级
- udev->wusb = hub_is_wusb(hub);
- /* Only USB 3.0 devices are connected to SuperSpeed hubs. */
- if (hub_is_superspeed(hub->hdev))
- udev->speed = USB_SPEED_SUPER;
- else
- udev->speed = USB_SPEED_UNKNOWN;
- choose_devnum(udev); //获取一个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); //对hub设备进行reset
- 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.
- */
- //如果该usb_device是class_hub
- if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
- && udev->bus_mA <= 100) {
- u16 devstat;
- status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
- &devstat);
- if (status < 2) {
- dev_dbg(&udev->dev, "get status %d ?\n", status);
- goto loop_disable;
- }
- le16_to_cpus(&devstat);
- if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
- dev_err(&udev->dev,
- "can't connect bus-powered hub "
- "to this port\n");
- if (hub->has_indicators) {
- hub->indicator[port1-1] =
- INDICATOR_AMBER_BLINK;
- schedule_delayed_work (&hub->leds, 0);
- }
- status = -ENOTCONN; /* Don't retry */
- goto loop_disable;
- }
- }
- /* check for devices running slower than they could */
- if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
- && udev->speed == USB_SPEED_FULL
- && highspeed_hubs != 0)
- check_highspeed (hub, udev, port1);
- /* Store the parent's children[] pointer. At this point
- * udev becomes globally accessible, although presumably
- * no one will look at it until hdev is unlocked.
- */
- status = 0;
- /* We mustn't add new devices if the parent hub has
- * been disconnected; we would race with the
- * recursively_mark_NOTATTACHED() routine.
- */
- spin_lock_irq(&device_state_lock);
- if (hdev->state == USB_STATE_NOTATTACHED)
- status = -ENOTCONN;
- else
- hdev->children[port1-1] = udev; //设置子usb_device指针
- spin_unlock_irq(&device_state_lock);
- /* Run it through the hoops (find a driver, etc) */
- if (!status) {
- status = usb_new_device(udev); //添加到系统中
- if (status) {
- spin_lock_irq(&device_state_lock);
- hdev->children[port1-1] = NULL;
- spin_unlock_irq(&device_state_lock);
- }
- }
- if (status)
- goto loop_disable;
- status = hub_power_remaining(hub);
- if (status)
- dev_dbg(hub_dev, "%dmA power budget left\n", status);
- return;
- loop_disable:
- hub_port_disable(hub, port1, 1);
- loop:
- usb_ep0_reinit(udev);
- release_devnum(udev);
- hub_free_dev(udev);
- usb_put_dev(udev);
- if ((status == -ENOTCONN) || (status == -ENOTSUPP))
- break;
- }
- if (hub->hdev->parent ||
- !hcd->driver->port_handed_over ||
- !(hcd->driver->port_handed_over)(hcd, port1))
- dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
- port1);
- done:
- hub_port_disable(hub, port1, 1);
- if (hcd->driver->relinquish_port && !hub->hdev->parent)
- hcd->driver->relinquish_port(hcd, port1);
- }
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
{
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
unsigned wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
struct usb_device *udev;
int status, i;
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed(hub, portstatus));
//hub支持indicator指示灯
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 */
//试着唤醒一些处于suspend的设备
udev = hdev->children[port1-1];
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
udev->state != USB_STATE_NOTATTACHED) {
usb_lock_device(udev);
if (portstatus & USB_PORT_STAT_ENABLE) { //已经使能
status = 0; /* Nothing to do */
#ifdef CONFIG_USB_SUSPEND
} else if (udev->state == USB_STATE_SUSPENDED &&
udev->persist_enabled) { //处于suspend状态
/* 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) //把一些处于NOTATTACHED的设备从hub断开
usb_disconnect(&hdev->children[port1-1]);
clear_bit(port1, hub->change_bits);
/* We can forget about a "removed" device when there's a physical
* disconnect or the connect status changes.
*/
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
(portchange & USB_PORT_STAT_C_CONNECTION))
clear_bit(port1, hub->removed_bits);
if (portchange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce(hub, port1); //防抖处理
if (status < 0) {
if (printk_ratelimit())
dev_err(hub_dev, "connect-debounce failed, "
"port %d disabled\n", port1);
portstatus &= ~USB_PORT_STAT_CONNECTION;
} else {
portstatus = status;
}
}
/* Return now if debouncing failed or nothing is connected or
* the device was "removed".
*/
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
test_bit(port1, hub->removed_bits)) {
/* maybe switch power back on (e.g. root hub was reset) */
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
&& !port_is_power_on(hub, portstatus))
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)
goto done;
return;
}
//对于一个新插入的设备,为这个usb设备枚举
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_device结构
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; //可以从port上获取的电量
udev->level = hdev->level + 1; //等级
udev->wusb = hub_is_wusb(hub);
/* Only USB 3.0 devices are connected to SuperSpeed hubs. */
if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else
udev->speed = USB_SPEED_UNKNOWN;
choose_devnum(udev); //获取一个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); //对hub设备进行reset
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.
*/
//如果该usb_device是class_hub
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
&& udev->bus_mA <= 100) {
u16 devstat;
status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
&devstat);
if (status < 2) {
dev_dbg(&udev->dev, "get status %d ?\n", status);
goto loop_disable;
}
le16_to_cpus(&devstat);
if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
dev_err(&udev->dev,
"can't connect bus-powered hub "
"to this port\n");
if (hub->has_indicators) {
hub->indicator[port1-1] =
INDICATOR_AMBER_BLINK;
schedule_delayed_work (&hub->leds, 0);
}
status = -ENOTCONN; /* Don't retry */
goto loop_disable;
}
}
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
&& highspeed_hubs != 0)
check_highspeed (hub, udev, port1);
/* Store the parent's children[] pointer. At this point
* udev becomes globally accessible, although presumably
* no one will look at it until hdev is unlocked.
*/
status = 0;
/* We mustn't add new devices if the parent hub has
* been disconnected; we would race with the
* recursively_mark_NOTATTACHED() routine.
*/
spin_lock_irq(&device_state_lock);
if (hdev->state == USB_STATE_NOTATTACHED)
status = -ENOTCONN;
else
hdev->children[port1-1] = udev; //设置子usb_device指针
spin_unlock_irq(&device_state_lock);
/* Run it through the hoops (find a driver, etc) */
if (!status) {
status = usb_new_device(udev); //添加到系统中
if (status) {
spin_lock_irq(&device_state_lock);
hdev->children[port1-1] = NULL;
spin_unlock_irq(&device_state_lock);
}
}
if (status)
goto loop_disable;
status = hub_power_remaining(hub);
if (status)
dev_dbg(hub_dev, "%dmA power budget left\n", status);
return;
loop_disable:
hub_port_disable(hub, port1, 1);
loop:
usb_ep0_reinit(udev);
release_devnum(udev);
hub_free_dev(udev);
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
}
if (hub->hdev->parent ||
!hcd->driver->port_handed_over ||
!(hcd->driver->port_handed_over)(hcd, port1))
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
hcd->driver->relinquish_port(hcd, port1);
}
hub_port_connect_change主要调用了3个函数:
usb_alloc_dev分配usb_device结构,hub_port_init重置该usb_device,usb_new_device(框架分析2中已经分析过)添加到系统中。接下来分析hub_port_init函数:
- static int
- hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
- int retry_counter)
- {
- static DEFINE_MUTEX(usb_address0_mutex);
- struct usb_device *hdev = hub->hdev;
- struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
- int i, j, retval;
- unsigned delay = HUB_SHORT_RESET_TIME;
- enum usb_device_speed oldspeed = udev->speed;
- char *speed, *type;
- int devnum = udev->devnum;
- /* root hub ports have a slightly longer reset period
- * (from USB 2.0 spec, section 7.1.7.5)
- */
- //root hub的处理
- 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; //低速设备需要200ms
- 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); //reset设备
- 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].
- */
- //根据usb速度初始化endpoint0的最大发送数据长度
- switch (udev->speed) {
- case USB_SPEED_SUPER:
- case USB_SPEED_WIRELESS: /* fixed at 512 */
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
- break;
- case USB_SPEED_HIGH: /* fixed at 64 */
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
- break;
- case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
- /* to determine the ep0 maxpacket size, try to read
- * the device descriptor to get bMaxPacketSize0 and
- * then correct our initial guess.
- */
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
- break;
- case USB_SPEED_LOW: /* fixed at 8 */
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
- break;
- default:
- goto fail;
- }
- type = "";
- switch (udev->speed) {
- case USB_SPEED_LOW: speed = "low"; break;
- case USB_SPEED_FULL: speed = "full"; break;
- case USB_SPEED_HIGH: speed = "high"; break;
- case USB_SPEED_SUPER:
- speed = "super";
- break;
- case USB_SPEED_WIRELESS:
- speed = "variable";
- type = "Wireless ";
- break;
- default: speed = "?"; break;
- }
- if (udev->speed != USB_SPEED_SUPER)
- dev_info(&udev->dev,
- "%s %s speed %sUSB device number %d using %s\n",
- (udev->config) ? "reset" : "new", speed, type,
- devnum, udev->bus->controller->driver->name);
- /* Set up TT records, if needed */
- if (hdev->tt) {
- udev->tt = hdev->tt;
- udev->ttport = hdev->ttport;
- } else if (udev->speed != USB_SPEED_HIGH
- && hdev->speed == USB_SPEED_HIGH) {
- if (!hub->tt.hub) {
- dev_err(&udev->dev, "parent hub has no TT\n");
- retval = -EINVAL;
- goto fail;
- }
- udev->tt = &hub->tt;
- udev->ttport = port1;
- }
- /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
- * Because device hardware and firmware is sometimes buggy in
- * this area, and this is how Linux has done it for ages.
- * Change it cautiously.
- *
- * NOTE: If USE_NEW_SCHEME() is true we will start by issuing
- * a 64-byte GET_DESCRIPTOR request. This is what Windows does,
- * so it may help with some non-standards-compliant devices.
- * Otherwise we start with SET_ADDRESS and then try to read the
- * first 8 bytes of the device descriptor to get the ep0 maxpacket
- * value.
- */
- //通过获取usb设备描述符来得到usb设备的endpoint0的最大发送数据长度
- for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
- if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
- struct usb_device_descriptor *buf;
- int r = 0;
- #define GET_DESCRIPTOR_BUFSIZE 64
- buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
- if (!buf) {
- retval = -ENOMEM;
- continue;
- }
- /* Retry on all errors; some devices are flakey.
- * 255 is for WUSB devices, we actually need to use
- * 512 (WUSB1.0[4.8.1]).
- */
- for (j = 0; j < 3; ++j) {
- buf->bMaxPacketSize0 = 0;
- r = usb_control_msg(udev, usb_rcvaddr0pipe(),
- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
- USB_DT_DEVICE << 8, 0,
- buf, GET_DESCRIPTOR_BUFSIZE,
- initial_descriptor_timeout);
- switch (buf->bMaxPacketSize0) {
- case 8: case 16: case 32: case 64: case 255:
- if (buf->bDescriptorType ==
- USB_DT_DEVICE) {
- r = 0;
- break;
- }
- /* FALL THROUGH */
- default:
- if (r == 0)
- r = -EPROTO;
- break;
- }
- if (r == 0)
- break;
- }
- udev->descriptor.bMaxPacketSize0 =
- buf->bMaxPacketSize0;
- kfree(buf);
- retval = hub_port_reset(hub, port1, udev, delay);
- if (retval < 0) /* error or disconnect */
- goto fail;
- if (oldspeed != udev->speed) {
- dev_dbg(&udev->dev,
- "device reset changed speed!\n");
- retval = -ENODEV;
- goto fail;
- }
- if (r) {
- dev_err(&udev->dev,
- "device descriptor read/64, error %d\n",
- r);
- retval = -EMSGSIZE;
- continue;
- }
- #undef GET_DESCRIPTOR_BUFSIZE
- }
- /*
- * If device is WUSB, we already assigned an
- * unauthorized address in the Connect Ack sequence;
- * authorization will assign the final address.
- */
- if (udev->wusb == 0) {
- for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
- retval = hub_set_address(udev, devnum);
- if (retval >= 0)
- break;
- msleep(200);
- }
- if (retval < 0) {
- dev_err(&udev->dev,
- "device not accepting address %d, error %d\n",
- devnum, retval);
- goto fail;
- }
- if (udev->speed == USB_SPEED_SUPER) {
- devnum = udev->devnum;
- dev_info(&udev->dev,
- "%s SuperSpeed USB device number %d using %s\n",
- (udev->config) ? "reset" : "new",
- devnum, udev->bus->controller->driver->name);
- }
- /* cope with hardware quirkiness:
- * - let SET_ADDRESS settle, some device hardware wants it
- * - read ep0 maxpacket even for high and low speed,
- */
- msleep(10);
- if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
- break;
- }
- retval = usb_get_device_descriptor(udev, 8); //获取设备描述符,8个字节
- if (retval < 8) {
- dev_err(&udev->dev,
- "device descriptor read/8, error %d\n",
- retval);
- if (retval >= 0)
- retval = -EMSGSIZE;
- } else {
- retval = 0;
- break;
- }
- }
- if (retval)
- goto fail;
- //对于无线usb设备和超速usb设备
- if (udev->descriptor.bMaxPacketSize0 == 0xff ||
- udev->speed == USB_SPEED_SUPER)
- i = 512;
- else
- i = udev->descriptor.bMaxPacketSize0;
- //初始化时设置的maxpacketsize和设备描述符的不一样,修正
- if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
- if (udev->speed == USB_SPEED_LOW ||
- !(i == 8 || i == 16 || i == 32 || i == 64)) {
- dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
- retval = -EMSGSIZE;
- goto fail;
- }
- if (udev->speed == USB_SPEED_FULL)
- dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
- else
- dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
- usb_ep0_reinit(udev);
- }
- retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); //获取完整的设备描述符
- if (retval < (signed)sizeof(udev->descriptor)) {
- dev_err(&udev->dev, "device descriptor read/all, error %d\n",
- retval);
- if (retval >= 0)
- retval = -ENOMSG;
- goto fail;
- }
- retval = 0;
- /* notify HCD that we have a device connected and addressed */
- if (hcd->driver->update_device)
- hcd->driver->update_device(hcd, udev);
- fail:
- if (retval) {
- hub_port_disable(hub, port1, 0);
- update_devnum(udev, devnum); /* for disconnect processing */
- }
- mutex_unlock(&usb_address0_mutex);
- return retval;
- }
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
static DEFINE_MUTEX(usb_address0_mutex);
struct usb_device *hdev = hub->hdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
int i, j, retval;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
char *speed, *type;
int devnum = udev->devnum;
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
*/
//root hub的处理
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; //低速设备需要200ms
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); //reset设备
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].
*/
//根据usb速度初始化endpoint0的最大发送数据长度
switch (udev->speed) {
case USB_SPEED_SUPER:
case USB_SPEED_WIRELESS: /* fixed at 512 */
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
break;
case USB_SPEED_HIGH: /* fixed at 64 */
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
break;
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
/* to determine the ep0 maxpacket size, try to read
* the device descriptor to get bMaxPacketSize0 and
* then correct our initial guess.
*/
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
break;
case USB_SPEED_LOW: /* fixed at 8 */
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
break;
default:
goto fail;
}
type = "";
switch (udev->speed) {
case USB_SPEED_LOW: speed = "low"; break;
case USB_SPEED_FULL: speed = "full"; break;
case USB_SPEED_HIGH: speed = "high"; break;
case USB_SPEED_SUPER:
speed = "super";
break;
case USB_SPEED_WIRELESS:
speed = "variable";
type = "Wireless ";
break;
default: speed = "?"; break;
}
if (udev->speed != USB_SPEED_SUPER)
dev_info(&udev->dev,
"%s %s speed %sUSB device number %d using %s\n",
(udev->config) ? "reset" : "new", speed, type,
devnum, udev->bus->controller->driver->name);
/* Set up TT records, if needed */
if (hdev->tt) {
udev->tt = hdev->tt;
udev->ttport = hdev->ttport;
} else if (udev->speed != USB_SPEED_HIGH
&& hdev->speed == USB_SPEED_HIGH) {
if (!hub->tt.hub) {
dev_err(&udev->dev, "parent hub has no TT\n");
retval = -EINVAL;
goto fail;
}
udev->tt = &hub->tt;
udev->ttport = port1;
}
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
* Change it cautiously.
*
* NOTE: If USE_NEW_SCHEME() is true we will start by issuing
* a 64-byte GET_DESCRIPTOR request. This is what Windows does,
* so it may help with some non-standards-compliant devices.
* Otherwise we start with SET_ADDRESS and then try to read the
* first 8 bytes of the device descriptor to get the ep0 maxpacket
* value.
*/
//通过获取usb设备描述符来得到usb设备的endpoint0的最大发送数据长度
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
struct usb_device_descriptor *buf;
int r = 0;
#define GET_DESCRIPTOR_BUFSIZE 64
buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
if (!buf) {
retval = -ENOMEM;
continue;
}
/* Retry on all errors; some devices are flakey.
* 255 is for WUSB devices, we actually need to use
* 512 (WUSB1.0[4.8.1]).
*/
for (j = 0; j < 3; ++j) {
buf->bMaxPacketSize0 = 0;
r = usb_control_msg(udev, usb_rcvaddr0pipe(),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
USB_DT_DEVICE << 8, 0,
buf, GET_DESCRIPTOR_BUFSIZE,
initial_descriptor_timeout);
switch (buf->bMaxPacketSize0) {
case 8: case 16: case 32: case 64: case 255:
if (buf->bDescriptorType ==
USB_DT_DEVICE) {
r = 0;
break;
}
/* FALL THROUGH */
default:
if (r == 0)
r = -EPROTO;
break;
}
if (r == 0)
break;
}
udev->descriptor.bMaxPacketSize0 =
buf->bMaxPacketSize0;
kfree(buf);
retval = hub_port_reset(hub, port1, udev, delay);
if (retval < 0) /* error or disconnect */
goto fail;
if (oldspeed != udev->speed) {
dev_dbg(&udev->dev,
"device reset changed speed!\n");
retval = -ENODEV;
goto fail;
}
if (r) {
dev_err(&udev->dev,
"device descriptor read/64, error %d\n",
r);
retval = -EMSGSIZE;
continue;
}
#undef GET_DESCRIPTOR_BUFSIZE
}
/*
* If device is WUSB, we already assigned an
* unauthorized address in the Connect Ack sequence;
* authorization will assign the final address.
*/
if (udev->wusb == 0) {
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
retval = hub_set_address(udev, devnum);
if (retval >= 0)
break;
msleep(200);
}
if (retval < 0) {
dev_err(&udev->dev,
"device not accepting address %d, error %d\n",
devnum, retval);
goto fail;
}
if (udev->speed == USB_SPEED_SUPER) {
devnum = udev->devnum;
dev_info(&udev->dev,
"%s SuperSpeed USB device number %d using %s\n",
(udev->config) ? "reset" : "new",
devnum, udev->bus->controller->driver->name);
}
/* cope with hardware quirkiness:
* - let SET_ADDRESS settle, some device hardware wants it
* - read ep0 maxpacket even for high and low speed,
*/
msleep(10);
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
break;
}
retval = usb_get_device_descriptor(udev, 8); //获取设备描述符,8个字节
if (retval < 8) {
dev_err(&udev->dev,
"device descriptor read/8, error %d\n",
retval);
if (retval >= 0)
retval = -EMSGSIZE;
} else {
retval = 0;
break;
}
}
if (retval)
goto fail;
//对于无线usb设备和超速usb设备
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
udev->speed == USB_SPEED_SUPER)
i = 512;
else
i = udev->descriptor.bMaxPacketSize0;
//初始化时设置的maxpacketsize和设备描述符的不一样,修正
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (udev->speed == USB_SPEED_LOW ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
retval = -EMSGSIZE;
goto fail;
}
if (udev->speed == USB_SPEED_FULL)
dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
else
dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); //获取完整的设备描述符
if (retval < (signed)sizeof(udev->descriptor)) {
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
retval);
if (retval >= 0)
retval = -ENOMSG;
goto fail;
}
retval = 0;
/* notify HCD that we have a device connected and addressed */
if (hcd->driver->update_device)
hcd->driver->update_device(hcd, udev);
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
update_devnum(udev, devnum); /* for disconnect processing */
}
mutex_unlock(&usb_address0_mutex);
return retval;
}
这里把上节略过,这一节又遇到的函数
usb_enumerate_device的函数调用说下:
usb_enumerate_device
usb_get_configuration
usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,desc, USB_DT_CONFIG_SIZE);
usb_parse_configuration
usb_parse_interface
usb_parse_endpoint
就是对设备描述符一级一级的解析并且分配相应的数据结构。