一、引言
最近工作中,因为工作需要,特地学习了一下USB设备的检测流程,做个笔记。
二、大致流程
从中断响应到probe对应的usb设备驱动:
hub_irq -> kick_hub_wq -> queue_work -> hub_event -> port_event -> hub_port_connect_change -> hub_port_connect -> usb_alloc_dev -> choose_devnum -> hub_port_init -> hub_set_address -> usb_get_device_descriptor -> usb_new_device -> usb_enumerate_autosuspend -> announce_device -> device_add ->bus_probe_device。
三、源码概览
1、hub_irq
源码路径:kernel/drviers/usb/core/hub.c。
/* completion function, fires on port status changes and various faults */
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 hub_wq 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 hub_wq figure it out */
kick_hub_wq(hub);
resubmit:
if (hub->quiescing)
return;
status = usb_submit_urb(hub->urb, GFP_ATOMIC);
if (status != 0 && status != -ENODEV && status != -EPERM)
dev_err(hub->intfdev, "resubmit --> %d\n", status);
}
2、kick_hub_wq
源码路径:kernel/drviers/usb/core/hub.c。
static void kick_hub_wq(struct usb_hub *hub)
{
struct usb_interface *intf;
if (hub->disconnected || work_pending(&hub->events))
return;
/*
* Suppress autosuspend until the event is proceed.
*
* Be careful and make sure that the symmetric operation is
* always called. We are here only when there is no pending
* work for this hub. Therefore put the interface either when
* the new work is called or when it is canceled.
*/
intf = to_usb_interface(hub->intfdev);
usb_autopm_get_interface_no_resume(intf);
kref_get(&hub->kref);
if (queue_work(hub_wq, &hub->events))
return;
/* the work has already been scheduled */
usb_autopm_put_interface_async(intf);
kref_put(&hub->kref, hub_release);
}
3、hub_event
源码路径:kernel/drviers/usb/core/hub.c。
static void hub_event(struct work_struct *work)
{
……
/* deal with port status changes */
for (i = 1; i <= hdev->maxchild; i++) {
struct usb_port *port_dev = hub->ports[i - 1];
if (test_bit(i, hub->event_bits)
|| test_bit(i, hub->change_bits)
|| test_bit(i, hub->wakeup_bits)) {
/*
* The get_noresume and barrier ensure that if
* the port was in the process of resuming, we
* flush that work and keep the port active for
* the duration of the port_event(). However,
* if the port is runtime pm suspended
* (powered-off), we leave it in that state, run
* an abbreviated port_event(), and move on.
*/
pm_runtime_get_noresume(&port_dev->dev);
pm_runtime_barrier(&port_dev->dev);
usb_lock_port(port_dev);
port_event(hub, i);
usb_unlock_port(port_dev);
pm_runtime_put_sync(&port_dev->dev);
}
}
……
}
4、port_event
源码路径:kernel/drviers/usb/core/hub.c。
static void port_event(struct usb_hub *hub, int port1)__must_hold(&port_dev->status_lock)
{
……
#ifdef CONFIG_ATC_AOSP_ENHANCEMENT
dev_info(&port_dev->dev, "[USB] connect_change = %d\n", connect_change);
#endif
if (connect_change)
hub_port_connect_change(hub, port1, portstatus, portchange);
}
5、hub_port_connect_change
源码路径:kernel/drviers/usb/core/hub.c。
static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)__must_hold(&port_dev->status_lock)
{
……
usb_unlock_port(port_dev);
hub_port_connect(hub, port1, portstatus, portchange);
usb_lock_port(port_dev);
}
6、hub_port_connect
源码路径:kernel/drviers/usb/core/hub.c。
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)
{
……
for (i = 0; i < SET_CONFIG_TRIES; i++) {
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
*/
udev = usb_alloc_dev(hdev, hdev->bus, port1);
if (!udev) {
dev_err(&port_dev->dev,
"couldn't allocate usb_device\n");
goto done;
}
……
/* reset (non-USB 3.0 devices) and get descriptor */
usb_lock_port(port_dev);
status = hub_port_init(hub, udev, port1, i);
usb_unlock_port(port_dev);
……
/* Run it through the hoops (find a driver, etc) */
if (!status) {
status = usb_new_device(udev);
}
}
……
}
}
7、usb_alloc_dev
源码路径:kernel/drviers/usb/core/usb.c。
8、hub_port_init
源码路径:kernel/drviers/usb/core/hub.c。
static int hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter)
{
……
if (udev->speed < USB_SPEED_SUPER)
dev_info(&udev->dev,
"%s %s USB device number %d using %s\n",
(udev->config) ? "reset" : "new", speed,
devnum, udev->bus->controller->driver->name);
……
if (udev->wusb == 0) {
for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
retval = hub_set_address(udev, devnum);
if (retval >= 0)
break;
msleep(200);
}
……
}
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
}
9、usb_new_device
源码路径:kernel/drviers/usb/core/hub.c。
int usb_new_device(struct usb_device *udev)
{
……
err = usb_enumerate_device(udev); /* Read descriptors */
……
/* Tell the world! */
announce_device(udev);
……
/* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device
* notifier chain (used by usbfs and possibly others).
*/
err = device_add(&udev->dev);
……
}
10、announce_device
源码路径:kernel/drviers/usb/core/hub.c。
static void announce_device(struct usb_device *udev)
{
dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
dev_info(&udev->dev,
"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
udev->descriptor.iManufacturer,
udev->descriptor.iProduct,
udev->descriptor.iSerialNumber);
show_string(udev, "Product", udev->product);
show_string(udev, "Manufacturer", udev->manufacturer);
show_string(udev, "SerialNumber", udev->serial);
}