1. Study of USB device drivers under Linux
Gamesh Kamath,
MS EWT Student
ganeshkamath89@gmail.com
MCIS
Manipal University
Manipal, India
Abstract — The Universal Serial Bus (USB) Implementation
Forum was created by joint venture of 7 companies namely Intel,
Compaq, NEC, DEC, Nortel, IBM and Microsoft in 1994. The
main idea behind the device was universality in a bus system
which also supported hot-pluggability/hot-swappability. The goal
was reached and the resulting device is continuing to breach
newer and newer applications everyday and has become a sort of
icon in the design of custom hardware peripheral .In this paper
we have attempted to summarize our findings on the chain of
events that take place between the host and the peripheral. The
study began by first learning the fundamental of device drivers,
followed by executing the USB skeleton which contained all the
essential parts of USB device drivers in it. Then the study was
extended to working USB modules which showed us how exactly
each module behaved and what the role of each module and
data-structures in the USB driver was. And the mechanism
behind how data exchange happens in a USB device was verified.
This paper presents the study, the relevance of various modules
mapped to the hardware and the function of several code
snippets available in the skeleton module to understand USB
better as a character driver.
Keywords— USB, Device Drivers, Universal Serial Bus,
Descriptors,
I. INTRODUCTION
USB was aimed at being low-power to operate at 5V power
supply and to be of low cost. Even if the system does not have
the necessary drivers to address the device requirements,
when an USB device is connected, the OS still detects the
device.
Essentially the idea behind universality was to have a single
device bus system which supported all 3 kinds of device
speeds:
• Low speed (1.5 Mbps) - necessary for user interface
devices like mouse and keyboard.
• Full speed (12 Mbps) - bulk transfer devices like
Printers and scanner.
• High speed (480 Mbps) - devices like broadband and
mass storage devices.
As soon as a device is plugged in into the USB port, the
system collects all associated descriptors and then after the
device is given an End Point ID, transaction takes place. The
USB devices are also capable of operating in 4 modes of
transfer namely:
• Control Transfer:
This is a bi-directional transfer which uses
both an IN and an OUT endpoint. Each control transfer
is made up of from 2 to several transactions.
• Bulk Transfer:
These are designed to transfer large amounts
of data with error-free delivery, but with no
guarantee of bandwidth. The host will schedule bulk
transfers after the other transfer types have been
allocated.
• Interrupt Transfer:
Interrupt transfers have nothing to do with
interrupts. The name is chosen because they are used
for the sort of purpose where an interrupt would have
been used in earlier connection types.
• Isochronous Transfer:
Isochronous transfers have a guaranteed
bandwidth, but error-free delivery is not guaranteed.
II. DATA EXCHANGE IN THE USB
USB devices operate based on descriptor exchange between
the host (computer) and the USB device. The host initially
gives a USB device an End Point number 0. And after the host
has decided on a free end point number to the device, the host
uses the end point number to select specific descriptors related
to the device. It uses the descriptors to identify the speed type
of the device. Also, it uses fields like the transfer data size and
USB version.
III. DESCRIPTORS
Descriptor – It is a defined structure and format in which the
data is transferred. Descriptors are necessary to find out the
type of device driver to be loaded. When a device is first
detected it starts with an address 0. Through a quiz it finds
out as many details as it feels necessary. Descriptors have a
hierarchical structure with four levels
Device level
Configuration level
Interface level
Endpoint level
2. Fig. 1 Overview of the USB implementation at the kernel
IV.STRUCTURE OF THE USB DEVICE DRIVER
Though a Host computer can be looked at as 3 level system
(figure 2) with a user-interface, kernel and a hardware, the
kernel provides several abstractions which enable good user
experience. However for a thorough study, the function of the
USB Core and USB Host Controller Interface will need to be
understood before any real attempt to study the descriptors are
made; because, it is at this level that the descriptors are
fetched using special buffers called URBs (USB receive
Buffers).
From figure 2 it can also be seen that the USB core lies
between the USB Host-Controller which interfaces the
hardware, while the device driver layer which contains the
USB device driver lies above the core. After the Host
Controller identifies the type of USB peripheral, the Core
fetches the appropriate Device driver associated with it.
Fig. 2 Overview of the USB implementation at the kernel
V. ASSOCIATED DATA STRUCTURES
While trying to study the device in Linux, the first thing
one needs to know is the structure of USB skeleton, made of
328 lines. It essentially consists of the following fields and
data structures (table, fops, class, usb_driver), which enables a
proper study of the device and categorizing will help us study
modules better.
Fig. 3 Example of an unacceptable low-resolution image
From figure 3 we can learn that:
• table consists of the VendorID and ProductID fields
which are necessary for initially registering the device.
static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID,
USB_SKEL_PRODUCT_ID) },{ }
};
• fops (file operations) tells the systems the different file
operation functions of the USB that the device drivers
over-rides over the existing system functions related to
the corresponding devices.
static struct file_operations skel_fops = {
.owner = THIS_MODULE,
.read = skel_read,
.write = skel_write,
.open = skel_open,
.release = skel_release,
};
• class gives the details of the data-structures and fields like
name which may be required for specific applications.
static struct usb_class_driver skel_class = {
.name = "usb/skel%d",
.fops = &skel_fops,
.mode = S_IFCHR | S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IROTH,
.minor_base = USB_SKEL_MINOR_BASE,
};
• usb_driver also gives details of the data-structures but
additionally it tells about probe which tells the host that is
needs to use the USB modules version of host to probe
and report when and what kind of USB device activities
are going on.
static struct usb_driver skel_driver = {
.owner = THIS_MODULE,
.name = "skeleton",
.id_table = skel_table,
.probe = skel_probe,
.disconnect = skel_disconnect,
};
3. VI.CLASSIFICATION BASED ON RETURN TYPE
While studying the USB driver, the return type of the
functions tells a lot about the functionality of the device.
Based on this classification figure 4 can be arrived:
Fig. 4 Various functions defined in the skeleton.
Where:
• int return - functions which on failure need to notify the
system so that necessary actions can be taken or the
function call needs to be made again, depending on the
functionally. A positive value indicates partial success. A
0 indicates success and a negative value indicates a
failure in the function operation.
• void return - functions don’t return anything to the
calling function because they are either the last functions
called, or the actions to be done by such functions is done
by default at boot-up.
• ssize return - functions namely read and write, tell the
number of bytes successfully sent between the host and
the device.
VII. USB UNDER LINUX
Linux being open source OS, enables us to study the
operations of the device driver. More over, several of the
modules were readily available as system calls and the user
has the freedom of inserting and removing modules even
while the OS continues to execute other activities. Linux
devices can be studied with the help of the following
commands:
• lsusb shows a basic listing of the detected USB devices.
• Like any other Kernel module it follows the sequence
o make - to create the object and the insertable
module
o insmod - to insert the module
o lsmod - to check if the device driver has been
inserted
o rmmod - to remove the module from the kernel
process tree
VIII. SEQUENCE OF FUNCTION CALLS
Fig. 5 Life-cycle of the USB driver module
When the life-cycle (figure 5) begins, it calls the init
module that initiates all the necessary data structures fills the
table with VendorID, ProductID, identifies the various
permissions, and then probes the Host Controller for the
device actions and registers the devices connected to the
system. Calls register module after which the operating
system takes over:
static int __init pen_init(void)
{
return usb_register(&pen_driver);
}
The C code contained in the skeleton probes all end points
and calls necessary function from the skeleton whenever an
action needs to take place, such as open, close, read, write and
release. Additionally the module describes a callback function
which checks how many bytes had been successfully read
from or written to the device.
The read function also calls the copy_to_user() function,
which essentially checks if there has been any fault while
reading and returns fault if there is an error:
if(copy_to_user(buf, bulk_buf, MIN(cnt, read_cnt)))
return -EFAULT;
The write function similarly checks for error using the
function copy_from_user to check for error:
if(copy_from_user(bulk_buf, buf, MIN(cnt,
MAX_PKT_SIZE)))
return -EFAULT;
The probe function contains the following line in it:
for(i=0;i<iface_desc->desc.bNumEndpoints; i++)
endpoint = &iface_desc->endpoint[i].desc;
In the code above, the for loop is used to assign the end
point with each of the peripheral connected to the system. The
loop starts with i = 0 and goes till desc.bNumEndpoints, a
variable which holds the values about the number of endpoints
4. assigned to the USB devices. This field directly points to
descriptors obtained from the USB device and is accessed
using the iface_desc variable, which is initialized as
iface_desc = interface->cur_altsetting;
The variable is described as of type usb_host_interface in
the program because it is the host controller that gets the
corresponding descriptors.
struct usb_host_interface *iface_desc;
All activities like deregistering and cleanup of the variables
is done in the exit (clean-up) function. But before this is done,
an instance is destroyed using the delete function. And the
device disconnect module needs to be called so that the driver
can be successfully disconnected so disconnect is always
called before the exit function. Even if the USB device is
disconnected from the system, the driver calls disconnect and
only then calls exit.
static void __exit pen_exit(void)
{
usb_deregister(&pen_driver);
}
IX.CONCLUSIONS
The study of USB device drivers helped us to better
understand character drivers. USB has become a very popular
bus because of its simple yet elegant characters mentioned in
the introduction. Each function in the device driver was
studied and its functionality was compared with working
modules. The similarity of the skeleton with the working
module was studied and documented
REFERENCES
[1] USB Complete, Third Edition, Jan Axelson, Lakeview Research LLC
[2] USB Specification 2.0, Joh Leuker et. al. (25 authors)
[3] Jungo WinDriver User’s Manual, Version 9.2X
[4] http://usbmadesimple.co.uk/
[5] LinuxForU October, November, December 2011
[6] Linux Device Drivers, 3rd
Edition, Jonathan Corbet, Alessandro Rubini
& Greg Kroah-Hartman, O’Reilly Publishers 2011