hn-classics/_stories/2007/7912751.md

275 lines
19 KiB
Markdown

---
created_at: '2014-06-18T20:48:24.000Z'
title: What actually happens when you plug in a USB device? (2007)
url: https://www.technovelty.org/linux/what-actually-happens-when-you-plug-in-a-usb-device.html
author: mafuyu
points: 80
story_text: ''
comment_text:
num_comments: 20
story_id:
story_title:
story_url:
parent_id:
created_at_i: 1403124504
_tags:
- story
- author_mafuyu
- story_7912751
objectID: '7912751'
year: 2007
---
[Source](https://www.technovelty.org/linux/what-actually-happens-when-you-plug-in-a-usb-device.html "Permalink to Technovelty - What actually happens when you plug in a USB device? ")
# Technovelty - What actually happens when you plug in a USB device?
[Technovelty][1]
# What actually happens when you plug in a USB device?
_Fri 20 July 2007_
Today I started wondering what _actually_ happens when you plug in a USB device. The little tour below goes from starting the USB subsystem to plugging something in, and I hope it is reasonably accurate. This entry is probably best read with a copy of the Linux kernel handy.
## Linux USB Core, lower-layers
We can start our tour right at the very bottom in the heart of the USB core.
Things really start at the USB initialisation function in `drivers/usb/core/usb.c:usb_init()`. The first interesting call is to `drivers/base/bus.c:bus_register()`. We see that it passes as `struct bus_type` which looks like:
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.suspend = usb_suspend,
.resume = usb_resume,
};
This is registering a new type of bus with the Linux driver core framework. The bus doesn't have much yet, just a name and some helper functions, but registering a bus sets up the `kobject` hierarchy that gets exported through `/sys/bus/` (`/sys/bus/usb` in this case) and will allow the further hierarchical building of devices underneath by attaching them as the system runs. This is like the root directory of the USB system.
Your desktop/laptop/palmtop etc has a host controller which directly interfaces to USB devices; common types are UHCI, OHCI and EHCI. The drivers for these various types of controllers live in `drivers/usb/host`. These controllers are similar but different, so to minimise code duplication Linux has a Host Controller Driver framework (`drivers/usb/core/hcd.c`) which abstracts most of the common operations from the host controller driver.
The HCD layer does this by keeping a `struct usb_hcd` (`drivers/usb/core/hcd.h`) with all common information in it for a host controller. Each of host controller drivers fills out a `struct hc_driver` for its hardware dependent operations, as per below (taken from the UHCI driver)
static const struct hc_driver uhci_driver = {
.description = hcd_name,
.product_desc = "UHCI Host Controller",
.hcd_priv_size = sizeof(struct uhci_hcd),
/* Generic hardware linkage */
.irq = uhci_irq,
.flags = HCD_USB11,
/* Basic lifecycle operations */
.reset = uhci_init,
.start = uhci_start,
#ifdef CONFIG_PM
.suspend = uhci_suspend,
.resume = uhci_resume,
.bus_suspend = uhci_rh_suspend,
.bus_resume = uhci_rh_resume,
#endif
.stop = uhci_stop,
.urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue,
.endpoint_disable = uhci_hcd_endpoint_disable,
.get_frame_number = uhci_hcd_get_frame_number,
.hub_status_data = uhci_hub_status_data,
.hub_control = uhci_hub_control,
};
## USB overview
It might be helpful to clarify a few USB concepts now. A USB device defines a group of end-points, where are grouped together into an interface. An end-point can be either "IN" or "OUT", and sends data in one direction only. End-points can have a number of different types:
* **Control** end-points are for configuring the device, etc.
* **Interrupt** end-points are for transferring small amounts of data. They have higher priority than ...
* **Bulk** end-points, who can transfer more data but do not get guaranteed time constraints.
* **Isochronous** transfers are high-priority real-time transfers, but if they are missed they are not re-tried. This is for streaming data like video or audio where there is no point sending data again.
There can be many interfaces (made of multiple end-points) and interfaces are grouped into "configurations". Most devices only have a single configuration.
![Intel UHCI][2]
You can see how this works at the host controller level with the above diagram clagged from the Intel UHCI documents. The controller has a "frame" register which is incremented every millisecond. Each frame pointer points to a queue of "transfer descriptors". The driver needs to schedule this work so that 90% of the time is given to isochronous data, and 10% left for control data. Should any time remain, the bulk data is transferred. You can see that any transfer descriptor for isochronous data will not be retried, but other data sits in a queue so it is never lost.
The USB layer communicates through USB request blocks, or **URB**s. A URB contains information about what end-point this request relates to, data, any related information or attributes and a call-back function to be called when the URB is complete. Drivers submit URBs to the USB core, which manages them in co-ordination with the USB host (see the `urb_enqueue` functions provided by the host driver). Your data gets sent off to the USB device by the USB core, and when its done your call-back is triggered.
## Root Hub
There is one more element quite fundamental to the USB core, which is the hub -- all USB devices plug into a hub. The USB controller implements a _root hub_; you can have multiple root hubs in a machine, and other hubs can then connect to root hubs. The hub driver lives in `drivers/usb/core/hub.c`. The USB initialisation function starts up the `khubd` thread (`drivers/usb/core/hub.c:usb_hub_init()`) which waits for and handles USB events, but we will return to that later.
The hub driver is the first USB driver to be setup, so by examining how that works we can get a feel for how other drivers work.
The hub driver setup starts in `drivers/usb/core/usb.c:usb_init()` where the `drivers/usb/core/hub.c:usb_hub_init()` function is called. This calls `drivers/usb/core/driver.c:usb_register_driver()` which adds itself to the USB bus we mentioned previously. This sets up `usb_probe_device()` to handle any probe events from the Linux driver core. At this point the hub driver is ready to claim anything that looks like a USB hub.
The root hub setup phase comes out of the HCD setup phase, which proceeds something like this. The Linux driver core goes through all devices on the PCI bus (including the USB host controller of course) and calls the `probe()` function the device's driver has registered (the host controller registered itself in its initialisation function `drivers/usb/core/uhci-hcd.c:uhci_hcd_init()`). The USB host controller driver wires up the HCD layer function `drivers/usb/core/hcd-pci.c:usb_hcd_pci_probe()` to handle this probe (see `struct pci_driver uhci_pci_driver` in `uhci-hcd.c`; `usb_hcd_pci_probe()` does some generic setup, but then calls back into the host driver `start()` function to do any device specific setup).
`usb_hcd_pci_probe()` ends up calling `drivers/usb/core/hcd.c:usb_add_hcd()` which does some generic HCD setup and ends up calling `register_root_hub()`.
`register_root_hub()` creates a new USB device and registers it with `drivers/usb/core/hub.c:usb_new_device()`. `usb_new_device()` first calls `drivers/usb/core/config.c:usb_get_configuration` which sets up the interface (all hubs only have one interface; the interrupt interface to notify of events on the hub) for the device before registering with the Linux driver core via `drivers/base/core/device_add()`. `device_add()` then causes the USB bus to be rescanned.
## Binding root hub to a driver
Now we can examine how a USB device gets associated with a driver. To summarise, what needs to happen is the hub driver needs to bind to the host controllers root hub. This illustrates the general concept of a new device binding to a driver.
There is, believe it or not, more layers that come into play now. There is a "generic" USB driver that handles the setup of interfaces in the USB core. As we mentioned earlier a device has a series of end-points grouped together into an interface, and then may have multiple interfaces for different things. Drivers really only care about communicating with the device at the interface level, so the USB core takes care of getting things to this stage for you.
In `drivers/usb/core/usb.c:usb_init()` the final call is to `drivers/usb/core/driver.c:usb_register_device_driver()`. This does some simple wrapping of the driver, most importantly setting up `usb_probe_device()` to handle any probes. It then registers this with Linux driver core with a call to `driver_register`.
Remembering that `drivers/usb/hub.c:usb_new_device()` has called `device_add()`, the driver core will now see this new device and start probing for a driver to handle it. The USB generic driver is going to be called, which has registered `drivers/usb/core/driver.c:usb_probe_device()` as its probe function. This converts the Linux driver core device back to a USB device (i.e. the USB device that was registered by `register_root_hub()`) and calls calls the drivers probe function `drivers/usb/core/generic.c:generic_probe()`.
The role of the generic driver is to get the interfaces on the device up and running. Firstly it calls `drivers/usb/generic.c:choose_configuration()` which simply reads through the device data and chooses a sane configuration. But wait, how does it know what is a sane configuration for the root hub? All the information has been "faked" for the root hub in `drivers/usb/core/hcd.c:usb2_rh_dev_descriptor` and the like. The root hub details are defined by the USB specification, so these can be kept statically.
Assuming everything is OK, `drivers/usb/core/message.c:usb_set_configuration()` is called to set up the chosen configuration. It uses the helper function `usb_control_message()` to send a message to the device about what configuration mode to go into. It then goes through the available interfaces setting up the kernel structures, and adds them with `device_add()`.
Inch by inch, we are getting closer to having the USB system up and running. When a new interface is added, the driver core will now try and find a driver to handle it. When an interface driver is registered with `drivers/usb/core/driver.c:usb_register_driver()` it sets the probe function to `drivers/usb/core/driver.c:usb_probe_interface()`. So the driver core calls this function, and the first thing it does is checks the ID against the IDs the driver is happy to handle from the drivers `id_table`. It uses `usb_match_one_id()` for this, which can match the class, subclass and protocol to make sure the it will work with this driver.
The root hub is part of the hub class, so can be handled by the hub driver. Therefore `drivers/usb/core/hub.c:hub_probe()` will be called to get the hub driver to bind with the new hub. This does some sanity checking and calls into `hub_configure()`. This then does some more general setup, but things get interesting when the interrupt end-point is setup. The interrupt end-point on a hub will send an event whenever something happens on the hub, such as a device being plugged in or unplugged. This is done by creating a URB and binding it to the end-point, asking that `hub_irq` be called whenever this URB is complete (e.g. when an event is received).
## New events on the hub
At this point, the system is waiting for something to happen. The root hub is setup and listening for new events - we are ready to plug in our device.
When this happens the host controller will raise an interrupt signalling that one of its ports has changed state. This will be handled by `drivers/usb/host/uhci-hcd.c:uhci_irq()`. This checks that the interrupt wasn't due to an error and then calls `drivers/usb/host/uhci-q.c:uhci_scan_schedule()`. This function implements the interface between the UHCI "transfer data" messages and the Linux URB scheme. It goes through the queues as illustrated in the figure above and finds any complete URBs and calls their completion function.
You may remember that the interrupt end-point of the hub was associated with a URB that would call `drivers/usb/core/hub.c:hub_irq()`. The UHCI code will go through it's queues, find that this URB is complete and therfore call the completion function.
We previously mentioned that the kernel starts the `khubd` daemon to handle hub events. We can see that `hub_irq` does some simple error checking, but its real job is to notify `khubd` that there is a new event on this hub.
`hub_events()` is the `khubd` daemon thread doing all the work. Once notified the hub has new event, it goes and reads the hub status to see what to do. A new device appearing will trigger a port change event, which is handled by `hub_port_connect_change()`. This does some initial setup of the device, but most importantly now calls `usb_new_device()`.
At this point, if the USB driver for this device is loaded, the device is essentially ready to go. As with the root hub, the device will be probed and its interfaces found with the generic driver, and then those interfaces will be registered and any driver asked if they wish to bind to them.
## Loading drivers
The question arises, however, about how modules are dynamically loaded. Keeping every single module for every possible USB device that may plug into the system is clearly sub-optimal, so we wish to load a device driver module only when required.
Most everyone is aware of `udev` which handles `/dev` device nodes these days. `udev` sits around listening for `uevent`s which get sent to it via the kernel. These `uevents` get sent via `netlink`, which is like Unix sockets but different. To get `uevent` messages all you need to do is open a `PF_NETLINK` socket to the `NETLINK_KOBJECT_UEVENT` "port", e.g. as the code below extract from `udev` does.
memset(&snl, 0x00, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = getpid();
snl.nl_groups = 1;
uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (uevent_netlink_sock == -1) {
err("error getting socket: %s", strerror(errno));
return -1;
}
So we have `udev` sitting around up in userspace waiting for messages that come from the kernel (as a side note, if `/sys/kernel/uevent_helper` is filled in with a path that will be run and receive the events with environment variables set; this is useful in early boot before `udev` has started.
Thus we want to get a message out to `udev` that there is a new USB interface around, and it should try and load a module to bind to it.
We previously identified `drivers/usb/core/message.c:usb_set_configuration()` as reading the interfaces for a device and registering them with the driver core. When this registers the interface, it also registers a `uevent` helper `drivers/usb/core/message.c:usb_if_uevent()`. This function gets called by the Linux driver core when the driver is added into the driver hierarchy. It adds some information to the `uevent`:
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"INTERFACE=%d/%d/%d",
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice),
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
`udev` now has all the information required ask `modprobe` to load a module, if it can. The rule on my system looks something like:
# load the drivers
ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe --use-blacklist $env{MODALIAS}"
Now, all going well, `modprobe` loads an appropriate module based on the vendor id, etc. Loading the driver causes a re-scan of the USB bus and the driver will be probed to see if wants to handle any of the unbinded USB devices (using the same procedure as was done setting up the root hub). It should take over control of the device, and that's it your USB device is up and running!
Simple!
Handy resources if you want to know more: [Linux Device Drivers][3], [USB2 Specification][4], [UHCI Design Guide][5].
Collected writings by [ Ian Wienand <ian@wienand.org>][6] mostly about various systems topics and anything else I find interesting.
## [Categories »][7]
* [apps][8]
* [arch][9]
* [badcode][10]
* [c][11]
* [code][12]
* [debian][13]
* [general][14]
* [gnome][15]
* [hacks][16]
* [hosting][17]
* [humor][18]
* [junkcode][19]
* [linux][20]
* [music][21]
* [openstack][22]
* [personal][23]
* [python][24]
* [review][25]
* [talks][26]
* [tips][27]
* [toys][28]
* [web][29]
* [weblog][30]
© 2016
[1]: https://www.technovelty.org/
[2]: https://www.technovelty.org/static/images/intel-uhci.png
[3]: https://www.technovelty.org/tp://lwn.net/Kernel/LDD3/
[4]: https://www.technovelty.org/tp://www.usb.org/developers/usb_20.zip
[5]: https://www.technovelty.org/tp://developer.intel.com/technology/usb/UHCI11D.pdf
[6]: https://www.technovelty.org/ilto:ian@wienand.org
[7]: https://www.technovelty.org/ategories
[8]: https://www.technovelty.org//category/apps.html
[9]: https://www.technovelty.org//category/arch.html
[10]: https://www.technovelty.org//category/badcode.html
[11]: https://www.technovelty.org//category/c.html
[12]: https://www.technovelty.org//category/code.html
[13]: https://www.technovelty.org//category/debian.html
[14]: https://www.technovelty.org//category/general.html
[15]: https://www.technovelty.org//category/gnome.html
[16]: https://www.technovelty.org//category/hacks.html
[17]: https://www.technovelty.org//category/hosting.html
[18]: https://www.technovelty.org//category/humor.html
[19]: https://www.technovelty.org//category/junkcode.html
[20]: https://www.technovelty.org//category/linux.html
[21]: https://www.technovelty.org//category/music.html
[22]: https://www.technovelty.org//category/openstack.html
[23]: https://www.technovelty.org//category/personal.html
[24]: https://www.technovelty.org//category/python.html
[25]: https://www.technovelty.org//category/review.html
[26]: https://www.technovelty.org//category/talks.html
[27]: https://www.technovelty.org//category/tips.html
[28]: https://www.technovelty.org//category/toys.html
[29]: https://www.technovelty.org//category/web.html
[30]: https://www.technovelty.org//category/weblog.html