The USB subsystem is based on message passing transactions. The messages are called URBs, which stands for USB request block. URBs are sent by calling the usb_submit_urb method, struct urb *urb, int mem_flags). This is an asynchronous call, and it returns immediately. The URB is put in a queue, and later, it reaches a completion handler. The completion handler is a member of the URB structure called complete, a pointer to a function in the URB struct. In the completion handler, you can check urb -> status to see if any errors have been detected. To cancel pending requests, use usb_unlink_urb(). URBs are allocated by calling usb_alloc_urb(), and they are freed with a call to usb_free_urb(). Three helper methods are available to help fill URBs: usb_fill_control_urb(), usb_fill_bulk_urb and usb_fill_int_urb. The URB struct resides in include/usb/usb.h, which is one of the two most important headers that define the USB interface. The second is include/linux/usb_ch9.h. Another little file also is called usb.h and can be found in usb/core, but it is not important in this context. The header file called usb_ch9.h hold constants and structures for USB. The name comes from chapter 9 of the USB 2.0 specification. Currently, there are two specifications for USB, 2.0 and 1.1. USB 2.0 operates at high speed, defined as 60MB/s (480Mb/s), which is 40 times faster than USB full speed. USB 1.1 operates either at full speed, which is 1.5MB/s (12Mb/s), or at low speed, which is 1.5Mb/s. When you connect high speed devices to USB 1.1 systems, they operate at USB 1.1 speeds. The USB_SPEED_LOW, USB_SPEED_FULL and USB_SPEED_HIGH constants are defined in usb_ch9.h. The constants are encountered here and there in the USB sources in the kernel. Four types of transfers exist: control transfers, bulk transfers, interrupt transfers, and high speed isochronous (ISO) transfers. USB Webcams usually use ISO transfer, and you rarely find use of bulk transfers there. In the kernel source tree, under /drivers/usb/host, you find five types of USB host controllers defined:
So what do we have in the USB initialization? Let's take a look at /drivers/usb/core/usb.c for that information. We call six initialization routines, but only three of them are part of the sub-subsystem.
The nine mandatory ingredients of the USBcore are: usb.c, hub.c, hcd.c, urb.c, message.c, config.c, file.c, buffer.c and sysfs.c. If, when configuring the kernel, you set CONFIG_USB_DEVICEFS to yes, add three more items to the USBcore list: devio.c, inode.c and devices.c. inode.c implements the USBFS, and it makes a call to register_filesystem. khubd is a daemon thread. After we create it, we call daemonize(), which blocks all signals. See daemonize() implementation in /kernel/exit.c for more details. We want to be able to send a kill signal (SIGKILL) when cleaning up, so we enable this by calling allow_signal(SIGKILL) immediately after creating khubd. Usually, when you run ps -aux and look at the khubd process, you see that it is sleeping, denoted by the letter S. When we plug a USB device into the USB port, the hardware layer initiates an interrupt; we reach the hub_irq() method. The hub data, represented by struct usb_hub, is passed to the hub_irq() method as part of the URB, the context member of struct urb. A global list of hub events, called hub_event_list, is available. If this list is empty, we add an event to this list, so that the khubd thread can handle it. We also call wake_up(&khubd_wait), as khubd is in waiting status. Waking up causes us to call hub_events(). We also reach the host controller IRQ. For example, when I am working with OHCI, I reach ohci_irq()). The hub driver writers admit that restarting the list every time to avoid creating a deadlock by deleting hubs downstream from this current hub is not the most efficient method, but it is safe. When we unplug a USB device out from it USB port, the process described above is repeated until we reach hub_events(). There we call the hub_port_connect_change() method, which is implemented in hub.c. The second parameter of this method is the port number, so this method disconnects all existing devices on this port by calling usb_disconnect(). usb_disconnect(struct usb_device **pdev) also is a USBcore method implemented in usb.c. The usb_disconnect() calls usb_disable_device() which disables all the endpoints for a USB device. It also removes that USB device endpoints from /proc/bus/usb/devices by calling usbfs_remove_device(dev). Notice, however, that hub_disconnect() is not called during this process. hub_disconnect() is called when we perform rmmod ohci_hcd, for example, when working with OHCI. It also is called in the probing process at bootime; more specifically, hub_probe() calls hub_disconnect() as it's last step before returning. The entries under /sys/usb/usb/drivers/hub are not removed after unplugging a device. They are removed, for example, after rmmod ohci_hcd is executed, assuming that you're working with ohci_hcd. When working with the USB subsystem, I found some tools and libraries to be especially helpful. USBView is a GTK+ 1.2-based tool written by Greg Kroah-Hartman that enables you to see the characteristics of the USB devices. This tool displays the topography of the devices plugged into the USB bus on a Linux machine. When plugging and unplugging the USB device, the display is updated and refreshed in real time. The source code also is available. Other tools and libraries that might help include UsbSnoop, which is some kind of USB sniffer that runs only on Windows. A new patch called USBMon, written by Pete Zaitcev, is available that enables you to monitor USB traffic. Finally, the libusb project is trying to create a library that can be used by user-level applications to access USB devices regardless of OS. I end this article with a brief discussion of the USB Mass Storage Driver. This driver is intended for mass storage USB devices, including USB disks and USB DiskOnKeys. What follows below is relaxant to USB DiskOnKeys, but I assume USB disks also behave more or less the same. To mount a DiskOnKey, you first should create a mounting point, let's say /mnt/dok, and then run: mount /dev/sda /mnt/dok The code for the USB Mass Storage Driver resides, naturally, under drivers/usb/storage. The module name is usb-storage.ko; it uses SCSI emulation. The us_data structure, from storage/usb.h, probably is the most important struct in storage mass storage. The protocol we use is transparent SCSI. The transport layer is bulk, which corresponds to US_PR_BULK in usb/transport.h. The use of SCSI emulation appears also in ATAPI devices, such as CD-RW devices. It is a good question to ask why we use this emulation at all. Why not use the IDE interface? I personally wonder what the reason is. To me, it does not seem to be related to the fact that IDE disks are not hot plugged while USB disk can be hot plugged. The usb_stor_control_thread() is a daemon thread that controls the USB device. It adds the PF_NOFREEZE flag to the current process. Aside from this module, use of PF_NOFREEZE appears in only two other places under the drivers subtree of the Linux kernel source: in block/loop.c in the loop_thread(void *data) method and in scsi/scsi_error.c in scsi_error_handler(void *data). The PF_NOFREEZE flag is used for Swsusp, which stands for software suspend in Linux and is the equivalent of Windows' hibernate mode. The code cited in this article is from the USB layer in the 2.67 kernel; it has changed little in newer versions. Useful Information about Linux Device Drivers The linux-usb-users Mailing List Archive The linux-usb-developer Mailing List Archive A Linux Kernel Mailing list (lkml) Thread About the New USBMon Patch The GNU/Linux usbnet Driver, by David Brownell "Modifying a Dynamic Library Without Changing the Source Code". An Linux Journal by Greg Kroah-Hartman in which he explains how to use the libusb. USB Mass Storage Driver for Linux. Take into account, however, that this site was updated last in July 2002. |
|