You are on page 1of 71

Edinburgh University

Division of Informatics:

A USB Monitor
Computer Science 4th Year Project Report:

Author: David Harding

Supervisor: Archie Howitt

30th May 2001


Abstract: A Monitoring system for analysing the traffic being sent to
peripherals on a Universal Serial Bus. The system is composed of a
patch to the Linux kernel and a user space monitoring application.

Acknowlegments: This project would have been much more difficult without the support
and advice of my supervisor, the helpful and relaxed atmosphere of the hardware bunker,
and all the musings of the Linux USB developers on the linux-usb-devel mailing list.

1 Introduction:..............................................................................................................4

1.1 Universal Serial Bus and the need for monitoring..................................................4

1.2 Possible Solutions...................................................................................................5

1.3 Linux and USB.......................................................................................................5

1.4 The Filesystem Interface.........................................................................................6

1.5 The Monitoring Application...................................................................................6

1.6 Summary of Results................................................................................................7

2 Background:..............................................................................................................8
2.1 USB Structure.........................................................................................................8

2.2 Topology and Configuration...................................................................................8

2.3 Transfer Types:.....................................................................................................10

2.4 Host Controllers....................................................................................................11

2.5 The Future of USB................................................................................................11

2.6 The Linux Kernel..................................................................................................11

2.7 The Linux Virtual Filesystem...............................................................................12

2.8 Linux and USB.....................................................................................................13

2.9 Linux USB Host Controller Drivers.....................................................................14

2.10 Linux USB Data Transfers......................................................................................15

2.11 The Linux USB filesystem......................................................................................15

2.12 Linux USB development in the future....................................................................15

2.13 USB on other Operating Systems...........................................................................16

2.14 Existing USB Monitoring Solutions.......................................................................16

3 System Definition...................................................................................................18

3.1 The problem with hardware interception..............................................................18

3.2 The Kernel Alternative ................................................................................19

4 System Design........................................................................................................21

4.1 Functional Requirements of the kernel patch.......................................................21

4.2 Design of the Kernel Patch...................................................................................23

4.2.1 Intercepting URBs..............................................................................................23

4.2.2 Interpreting the Contents of a URB....................................................................24

4.2.3 Storing the Monitored Information.....................................................................24


4.2.4 Passing the Monitored Information to User Space.............................................25

4.2.5 Formatting the Contents of the Monitoring Files...............................................25

4.2.6 Setting the Monitoring Level of Devices............................................................27

4.3 Kernel Patch Implementation Issues.....................................................................28

4.3.1 Adapting the USB filesystem..............................................................................29

4.3.2 Implementing Timing in the kernel....................................................................29

4.3.3 Problems with Interrupt Handlers.......................................................................30

4.3.4 Changes to different Host Controller Drivers.....................................................31

4.3.5 Where to put the code.........................................................................................31

4.3.6 Compatibility on different kernel versions.........................................................31

5 System Design – The Monitoring Application.......................................................32

5.1 Functional Requirements of a Monitoring Application........................................32

5.2 Attributes desirable in the Monitoring Application..............................................33

5.3 Design of the Monitoring Application..................................................................33

5.3.1 Structure of the USB Model...............................................................................33

5.3.2 Obtaining the Topology and Configuration Information....................................34

5.3.3 Polling for the Monitored Data ..........................................................................34

5.3.4 Displaying Configuration and Monitored Data..................................................35

5.4 Monitoring Application Implementation Issues...................................................36

5.4.1 Java and Swing vs. C and Gtk............................................................................36

5.4.2 The USB Object Model......................................................................................36

5.4.3 The Threaded Monitoring File Reader ..............................................................36

5.4.4 Displaying the Device Tree................................................................................37


5.4.5 Splitting Up the Information...............................................................................37

5.4.6 Displaying the URB Tables................................................................................38

6 System Evaluation..................................................................................................39

6.1 Suitability of Core Design....................................................................................39

6.2 Effectiveness of Implementation..........................................................................40

6.3 Impact on the Linux USB Subsystem of the kernel patch....................................41

6.3.1 Impact of Functionality.......................................................................................41

6.3.2 The impact of Monitoring on processing time....................................................42

6.4 Performance of the Monitoring Application.........................................................46

6.5 User Adoption.......................................................................................................46

6.6 Future Development..............................................................................................46

Bibliography:.....................................................................................................................47

1 Introduction:
1.1 Universal Serial Bus and the need for monitoring
The Universal Serial Bus is a system for connecting peripherals to a host
computer. It was created to give a simpler, higher-bandwidth, and more flexible
replacement to RS-232 serial and parallel interfaces. The first Specification for the
Universal Serial Bus was released in 1996 by a consortium of Intel, Compaq, Microsoft,
and NEC. Hardware was soon available and the system has grown to becoming a
standard feature on 100% of new personal computers [30]. By the end of 1998 over 10
million USB devices had been shipped[31].

One of the key features of USB is that it allows the chaining together of devices in
a tree so that many devices can be connected together via hubs to one port on a host
system. This capability can bring with it a difficulty in analysing what is going on with
the bus in general or with a particular peripheral. USB’s greater complexities also make it
difficult for developers of both devices and drivers to accurately and effectively debug
them. This has lead to a number of expensive hardware systems that can physically
monitor the signals over a USB cable and interpret the signals in a way that can be
analysed by a separate monitoring computer. This is an expensive but wholly unobtrusive
method of USB monitoring.

The aims of this project were to create a system that allowed a Host computer
with USB attached to operate normally while selectively monitoring the traffic to and
from devices on that USB. The resulting system was intended to produce minimal
interference in terms of performance and functionality of the USB hardware, OS drivers,
and user mode applications.

1.2 Possible Solutions


There are different possible solutions to this requirement for monitoring. The most
obvious is to intercept the signals in the wires connecting USB devices to a host system.
For many reasons this is both impractical and only provides a limited set of monitored
information (See Section 3.1 on page 25 for details).

The only realistic alternative to hardware interception is to monitor information


inside the operation system. This makes any system produced operating system
dependent which is unfortunate but unavoidable given the difficulties of hardware
interception. There is no option of an operating system independent Java implementation
as a standardised java-USB interface is still at an early stage of definition.[8]

1.3 Linux and USB


Linux is an open source operating system under a very high degree of active
development. Originally created by one man (Linus Torvalds) Linux is now composed of
code written by thousands of developers around the world. This means that all the source
and documentation is available in the public domain, even down to the discussions and
debates of the principal developers. Linux has come late to USB support compared to
more widely used proprietary operating systems but Linux use is growing, especially in
some niche areas. International Data Corporation estimates that Linux commercial
shipments will increase at a growth rate of 25 percent from 1999 to 2003, compared with
a 10 percent growth rate for all other client operating environments combined. Many
devices drivers are being ported to Linux[25] and new devices are emerging with their
first drivers available in Linux. This combination makes Linux a desirable platform to
develop this project on and a platform with a demonstrable need for a system like this.

The Linux USB sub-system almost entirely resides in the kernel of the operating
system (The structure of the Linux USB subsystem is shown in Figure 2 on page 20).
Although user-mode device drivers are possible most device drivers are written to exist
as kernel modules. These modules are written to interface with the core of the Linux USB
sub-system; this then interfaces via a driver to the host controller hardware.
The principal intention of the monitoring system is to provide information as to
what data is being transferred when and to or from which part of which device. In order
to implement this the kernel has to be changed. This is because the monitoring system
must have access to all the data transfers that occur on the USB. These transfers are split
up in the kernel, as many of the device drivers exist in the kernel.

It is not desirable to have the whole monitoring system in the kernel. The kernel is
a dangerous place to program due to the damage that can be done. It also has much less
library support for user-mode style applications. So the desired system must consist of
two parts: a modification to the Linux kernel and a user space monitoring application.

1.4 The Filesystem Interface


The two parts of the system need to communicate. The user-space monitoring
application must tell the kernel which transfers to monitor and the kernel based
monitoring system must pass the monitored information back to user-space so that it can
be displayed to the user. Moving data from the kernel space to the user space and vice
versa is a complex issue. After a lot of deliberation the best method was determined to be
using a filesystem interface so the user-space application could use standard file access
functions to query the kernel space. An added advantage was that this could be
implemented as an extension to an existing feature of the Linux USB sub system.

Using this system allows maximum flexibility in the implementation of


monitoring applications, as they require no new interface capabilities other simple file
access. Simply opening a particular file and writing a set of characters will set the
monitoring level for a particular connection on a particular USB device. Reading from
the same file will dump the monitored data down to the user space monitoring
application. This turns the system from a symbiotic pair of programs into a prototype
monitoring architecture with an example monitoring application that supports the
additional functionality provided by the kernel modification. This is a theme that
exemplifies the difference between kernel programming and application programming.
The kernel provides specific services, which can be used in different ways by different
applications.

1.5 The Monitoring Application


The sample monitoring application can be implemented in any programming
language. This is because of the standard file access nature of the interface between the
modified kernel and the monitoring application. The monitoring application uses normal
file access to set the level of monitoring and read the monitored data buffers from the
kernel.

The existing Linux USB subsystem provides a ‘file’ from which contains the
topology and configuration of the buses. The content of this ‘devices’ file enables the
monitoring application to build up a model of the bus. With this model the Monitoring
Application has enough information to set monitoring levels in the modified kernel for
particular types of transfer on particular devices. The monitoring application can then
interpret the monitoring output of the modified kernel so as to display this information
usefully to the user.

1.6 Summary of Results


The system that has been produced consists of a 22kb patch to the 2.4 Linux
kernel tree and a Java monitoring application. This combination is capable of selectively
setting the monitoring level for different types of transfers on different devices,
intercepting those transfers, and retrieving details as well as (optionally) the full data
buffer. This information can then be retrieved from the kernel to user space and displayed
in a graphical interface. This interface allows the comparison of different transfers
visually and the analysis of the data contents using different encoding methods.

This system has been released to the public domain under the title USBMon via
the World Wide Web. All information relating to USBMon has been released at
http://www.dcs.ed.ac.uk/~dxh/public/USB/. It is the only system available for Linux that
can obtain real-time USB monitoring information from USB devices in Linux with no
additional hardware. It also provides a combination of features unavailable on other
proprietary operating systems.

Figure 1: USBMon showing details of monitored URBs on a USB floppy drive


2 Background:
2.1 USB Structure
The universal serial bus was created as a replacement for the venerable RS-232
serial and parallel communication standards that have featured on almost every personal
computer ever made. USB was intended to remove the need for costly proprietary
interfaces to ISA or PCI busses or the use of costly SCSI interfaces for peripherals such
as scanners and removable hard drives. The primary deficiencies in the serial and parallel
interfaces were seen as being:

• Limited Bandwidth – UART technology and types of cable specified by RS-232


limits the potential bandwidth of a serial connection to little over 100 kbits/s

• Lack of Chaining – No matter how many serial ports a machine had it might
never have enough and so the ability to connect more than one device to a single
port was seen as necessary.

• Lack of power distribution – With the exception of mice in most cases a serial or
parallel connection cannot power the peripheral. A greater degree of power
distribution was desired.

USB as defined by the 1.1 Specification made the following characteristics widely
available:

• Up to 127 devices connected together on one bus utilizing up to five layers of


hubs.

• Total bandwidth capability of 12MBps.

• Devices may each draw a maximum of 2.5W of power from the bus.

2.2 Topology and Configuration


USB defines various entities that have specific meaning in a USB context: Bus,
Device, Configuration, Interface, and Endpoint. With the exception of a Bus each of
these entities has a descriptor that holds various characteristics. The descriptors are
available to the host system as soon as a device attaches to the bus even if there is no
device driver for the device. They are part of the topological and configuration
information.
A USB Device has the following characteristics:

• A Device has a Class, Subclass, and Protocol. These correspond to USB defined
types of device[4]. For example Classes are defined for mass-storage, audio,
modems, scanners, etc. These classes have different sub-classes and can conform
to various USB defined protocols. It is possible for a device to be vendor-specific
in which case it’s subclass and protocol values are not relevant. It is also possible
for Devices to maintain different interfaces of different classes in this case the
Device is not of a specific class but some of its interfaces have their own class.

• A Device also carries values for it’s manufacturer serial number and version
numbers as part of the device descriptor.

• A Device has one or more possible configurations, one of which is active at any
one time.

Each Configuration has the following characteristics:

• A Configuration is either bus-powered or self-powered, and either capable of


remote-wake up or not.

• A Configuration has a maximum power usage specified in its descriptor

• A Configuration has one or more Interfaces.

An Interface has the following characteristics:

• An Interface has Class, Subclass, and Protocol. These are similar to the
characteristics of Devices but relate only to a particular interface.

• An Interface has one or more endpoints.


An Endpoint has the following characteristics:

• An Endpoint is either an Input or an Output

• An Endpoint is of a particular type relating to the type of data transfer that is


possible:

o Control

o Bulk

o Isochronous

o Interrupt

• An Endpoint has a Maximum Packet Length that it can send or receive.

• For Interrupt Endpoints only there is an Interval value that signals the time
between interrupts.

2.3 Transfer Types:


The four different types of transfer are:

• Control – This is a data transfer that attempts to change the configuration of the
device. This can include a bulk-style data transfer.

• Bulk – This is the lossless one-time bulk movement of data to or from the device.

• Isochronous – This relates to a steady stream of real-time data where the data
carries implicit timing in the rate it arrives. Such transfers require a set allocation
of bandwidth. This type of transfer is normally reserved for real-world streamed
data such as an audio channel or video stream from a camera.

• Interrupt – This transfer type can transfer a set size of data at regular intervals,
timed in milliseconds.
2.4 Host Controllers
The USB specification is general enough to allow different implementations of
the Host Controller hardware. There are two common implementations in wide usage,
these are known as UHCI[6] and OHCI[5]. UHCI was created by Intel and is considered
a more simplistic hardware implementation that requires slightly more software
management. OHCI was created by Compaq and is commonly found in all Apple USB
implementations. The two different host controllers both completely support the USB
standard and will work with all USB devices; the only thing that must change is the Host
Controller Driver on the Host Computer.

As part of the USB 2.0 improvements (as discussed below) a new host controller
has emerged (the only one to support 2.0). This is called EHCI. Having an EHCI host
controller will imply the ability to cope with USB 2.0 devices.

2.5 The Future of USB


At about the same time as USB appeared another standard was announced called
IEE 1394, sometimes branded as FireWire an i.link. IEE 1394 does not have all the
chaining capabilities of USB but it does have vastly greater bandwidth (400MBPS) than
USB. IEE 1394 also has the ability to carry out peer-to-peer connections instead of one
acting as a host and the other as a much simpler device.

The emerging consumer digital multimedia market has adopted IEE 1394 widely.
It is the most common interface on new digital camcorders and is now present on high-
end games platforms. This has demonstrated the demand for higher-than-existing-USB
bandwidth when connecting some peripherals, especially in the consumer electronics
sector. This has spurred the extension of the existing 1.1 USB specification. In December
2000 the USB 2.0 Specification[3] was released. The principal advantage of USB 2.0 is a
new higher bandwidth capability of 480MBPS.

Currently the adoption of USB 2.0 and IEE 1394 is of particular note. There is
uncertainty about the adoption of the two standards as many hardware producers wish to
standardise on one system. It is currently uncertain as to whether USB 2.0 will be able to
usurp IEE 1394’s dominant position in the consumer electronics market but it is unlikely
that IEE 1394 will replace USB as a standardised method for connection of peripherals to
Personal Computers. An IEE 1394 mouse is a little far fetched. It is unclear whether IEE
1394’s better peer-to-peer networking capability will make it more popular for the
interconnection of broadband digital consumer devices.[29]

2.6 The Linux Kernel


Due to its open source distribution the Linux Kernel is extremely configurable
and it is very common for experienced users to recompile their own kernel with slightly
altered configuration options. The principal area of configuration is what parts are
included in the kernel as statically linked parts of a macrokernel and what as dynamically
loadable modules, and what is not included at all. The Linux USB sub-system can be
implemented in whole or in part as dynamically loadable modules. It is common to keep
the Linux-USB core statically linked while dynamically loading the different device
drivers. Other features that can be configured in this way is which Host Controller Driver
is used by the system and the level of debug information sent to the kernel system log.

The latest stable version of the Linux kernel (at the time of writing) is 2.4.5.
Version 2.4.0 was released in January 2001; this was a major update from the previous
stable kernel 2.2.17. Minor updates (changes to the third number) happen quite often and
do not usually signify major change. Kernel versions with odd middle numbers (e.g.
2.3.x) are unstable kernels that are used for development and experimentation.

2.7 The Linux Virtual Filesystem


The Virtual File System (VFS) is a core part of the Linux Operating System and
key to much of its functionality. Linux can mount new filesystems within one unified
file-structure. Not all files that appear in the unified filesystem are sitting on the same
disk; in fact not all files are actually files on any disk. The Virtual File System allows
storage devices that use very different formats to be used together in one filesystem. The
VFS has a set interface and filesystems are written to support it. This results in the details
of a particular filesystem being completely transparent from the user. The user cannot tell
(from how it is accessed) if a file is held on a DOS or EXT2 partition of an IDE or SCSI
hard drive or even a Zip drive on a parallel port interface, it is completely transparent.

This flexibility and transparency combined with a very strictly defined interface
that has not changed much over time has lead to the use of the Virtual File System for
different purposes. Files in the VFS can represent devices and services. Direct interfaces
to devices are often found in the /dev file-space. Services such as CPU usage reporting
are reported through files in the /proc filesystem.

In Linux a filesystem has an entity called a superblock. The superblock is used to


register and mount the filesystem. The superblock contains all details of how a filesystem
is accessed and points to functions that define inode structures for particular files. An
inode is the kernel reference to a file in a filesystem. The principal function of the Virtual
Filesystem is to cache inodes and manage their access. User-programs never actually get
to find out the inode of a file. The user program is given a file-descriptor, which acts as a
pointer to a reference of the actual inode of the file. This indirection is a feature of the
Virtual File System cache.

An inode structure contains details about the file such as modification time,
ownership (user and group), as well as a pointer to a file_operations structure. The
file_operations structure contains pointers to functions that implement the possible
actions on a file such as open, read, write, release, ioctl, etc.

2.8 Linux and USB

Linux adopted USB later than its proprietary rivals. USB support has only really
become widespread since the release of the 2.4 kernel, which was first released in
January 2001. Major Distributions have adopted this kernel in subsequent months and
now USB support is a standard feature of most new Linux installations.

Support for USB devices in Linux is now growing dramatically, Major device
classes are well supported such as the Human Interface Device (HID) driver (this
supports mice, keyboards etc.) and the mass-storage driver (this is used by floppy drives,
Zip drives, CDRs, even some MP3 players and Digital Cameras). Proprietary interfaces
have also begun to be supported; high profile examples include Alcatel’s ADSL USB
modem and Phillips popular digital cameras[25].

The Linux USB Subsystem has been developed by a group of developers spread
over the world communicating via the public mailing lists. The basic design is shown in
Figure 2. The Linux USB subsystem is a part of the kernel. It is composed of three layers:
The Device Drivers, the USB Core, and the Host Controller Driver. Due to the nature of
the distributed and disjointed development of open source systems the interfaces between
the layers have become tightly defined. This means that there is a tightly defined
interface between drivers and the USB core, and the USB core and the Host Controller
driver.

Linux USB device drivers are normally kernel based drivers that interface to user
applications via a number of methods such as the /dev device filesystem and the SCSI
filesystem where USB storage devices are represented as if they are SCSI storage
devices. It is possible however, to have user-space based device drivers. These user space
drivers interface to the USB core via the Linux USB filesystem.

Figure 2: The existing Linux USB Subsystem


2.9 Linux USB Host Controller Drivers
Linux has a stable driver for the OHCI host controller, it is commonly known as
usb-ohci, however Linux has had several problems with its implementation of the most
common UHCI host controller. Two different implementations now exist, known by the
filenames of their object code: uhci and usb-uhci. Both drivers are under active
development by different people but use significantly different internal methods. The
history is complex but in brief the uhci driver is older and for a long time had problems
with isochronous transfers; while the newer usb-uhci driver was developed to use similar
internal structure and methods as the OHCI driver. There is active discussion about how
best to move to a simpler situation for UHCI users. This, along with early development of
the EHCI driver has lead to a strong discussion of the possibility and desirability of
merging common functions in the different host controller drivers in what is referred to as
a Host Controller layer. The exact future of this is currently uncertain.

In theory it should not matter to users or device driver writers which host
controller driver a system is using, as the interface to the USB core is common between
all the Host Controller drivers.
2.10 Linux USB Data Transfers
The Linux USB core handles all four types of data transfer (control, bulk,
interrupt, and isochronous) via one structure. This is called a URB (Universal Request
Block). A URB contains all important information about a data transfer including a
pointer to the actual data buffer. URBs are queued asynchronously and can trigger
completion routines via a callback function specified in the URB (this is also extremely
important to how monitoring can be achieved). The Host Controller determines the exact
scheduling of the transfers. The URB structure contains parameters that are required to be
set before a URB is submitted and results that will be filled in by the core and returned
when the URB has completed.

2.11 The Linux USB filesystem


The Linux USB filesystem has its own filesystem. This can be mounted in the
/proc filesystem at /proc/bus/usb. The /proc filesystem is a memory-only filesystem that
allows the kernel mode to send and receive data as if it were the contents of a file being
read from or written to respectively. When a file in the /proc filesystem is read or written
the filesystem securely calls kernel mode functions that reply to the request with
dynamically generated contents of the requested ‘file’.

The Linux USB filesystem is intended as both a method for relaying from the
kernel to user mode applications the configuration details of the USB peripherals and as a
method for user-mode device drivers – allowing the sending and receiving of URBs from
user-mode processes.

The configuration information is found in two files in the top level of the usb
filesystem (/proc/bus/usb). These files are called ‘devices’ and ‘drivers’. The ‘drivers’
file consists of a list of the currently loaded drivers. The ‘devices’ file contains the details
of the bus topology and the contents of all the device descriptors of attached devices. This
includes all characteristics of devices, configurations, interfaces, and endpoints. A
detailed explanation of the rather complicated formatting of this file is in [15].

The device interface part of the Linux USB filesystem is provided in bus
directories. There is one directory per bus, the name being the three-digit bus number
allocated by the USB core. For single bus systems this will almost always be 001. Inside
these directories there is a file for every device, the name being the three-digit device
number allocated on connection. So the device interface for device 1 on bus 1 is at
/proc/bus/usb/001/001. These device interface files can be used to submit data transfers
and carry out other actions on the devices.

2.12 Linux USB development in the future


There has been much discussion among the Linux USB developers about the
future direction of development effort. One of the prime efforts is USB 2.0 support. As
well as this there is active discussion of standardisation within Linux of the way
peripheral interfaces such as PCI, USB, IEE 1394, IRDA, and possibly Bluetooth
represent devices and their configuration. This has already led to a standardisation of the
arrangements for Hot-plugging devices. The Hotplug system is more general than just the
USB subsystem and is used by other parts of the operating system.[22]

The important aspects of this are really what is not about to change rather than
what is. There is little prospect of the URB system and the Host Controller interface
changing even with the introduction of USB 2.0. The standardisation between peripheral
interfaces in Linux should not affect the internals of the USB core or device driver design
to any great extent (to break existing drivers on a large scale would be very bad and is
extremely unlikely).

2.13 USB on other Operating Systems


USB has been extremely widely adopted. In 1998 there were 138 million USB
enabled PCs in the world and that is projected to increase to over 500 million in 2001
with 100 per cent of new PCs supporting USB[30]. This level of support from the
hardware providers is matched by almost all widely used operating systems. Different
versions of Windows[27] and Mac-OS[28] both have extensive USB support. Because
these different operating systems are closed source and tend to have more single-user
orientated security models the position of the USB drivers is different. There are often
things called ‘device layers’ which make all devices look similar to drivers whatever
interface they have. This means that monitoring the goings on of a USB is very different
in other operating systems.

2.14 Existing USB Monitoring Solutions


There is an application called USB Snoopy[19] available for Microsoft Windows
98 that allows the analysis of data sent between drivers and devices. This is often very
useful to software engineers who are attempting to write ‘port’ a driver to a new
operating system such as Linux. USB Snoopy is still at a delicate stage of development
and can cause problems with other Windows applications. A Linux USB device driver
called usb-robot[20] has been developed that can use the output of USB Snoopy to replay
USB transactions on devices where there is no existing Linux device driver, this is used
to reverse engineer drivers.
Figure 3: The main window of USBview showing a well populated bus.

On Linux there is no equivalent to the USB Snoopy Windows application in


Linux; that is partially the aim of this project. There is an application available for Linux
called USBview[21] that displays the topology and configuration of the USB attached to
the host. Figure 3 shows a screenshot from USBview. USBview only shows information
available in either the devices or drivers files in the Linux USB filesystem. This data is
presented in a split frame with a tree format showing each device as a node on the left
and the details of a selected device in the right-hand.

The functions that USBview provides are often inadequate for device developers
and driver writers. These users need not only to be able to tell what configuration and
topology devices and busses have, they need to know exactly what is being
communicated and when between the device and the driver. This has resulted in a
significant market for expensive hardware monitoring systems that physically monitor the
wires in a USB bus. Providers of these include Intel, Cypress, and CATC. These devices
are normally extremely expensive and require an additional monitoring computer to
analyse the data that they produce.

3 System Definition
3.1 The problem with hardware interception
Figure 4: The traditional hardware monitoring method
The most logical solution to USB monitoring on any platform is to unobtrusively
monitor the electrical signals on the cable just outside the host system, analyse these
signals into data transfers, and pass these data transfers to a monitoring application.

The principal advantage of this method is that there is no impact on the operation
of the system. No component of the USB system does anything it would not do in a
normal USB set-up. This means that problems that appear when not monitoring are not
likely to be obscured by the presence of monitoring and new problems are unlikely to be
triggered by the presence of monitoring. This makes this solution very desirable to device
developers and device driver writers attempting to debug exactly what is happening on a
system. This type of system is also very desirable for reverse-engineers attempting to
work out how closed source device/driver combinations are talking to each other.

The main problem with this method is the technology required to perform it. The
Monitoring hardware must be fast enough to analyse the electrical signals on the bus, and
interface with a communication pipe fast enough to communicate all the data appearing
on the USB. This means both making something almost as complicated as a host-
controller and building a communication pipe with a bandwidth greater than that of USB
(as it must take the maximum USB traffic plus an overhead of Monitoring configuration
information). Since one of the reasons that USB was created was to increase the available
bandwidth of peripheral connections this is difficult. IEE 1394, and a direct PCI interface
are possibilities but both carry heavy implementation costs in both price of components
and complexity.

Given that this style of solution is offered by several large companies at high cost
and the feasibility of completing this style of monitoring within the scope of this project
it was decided not to attempt this option.

3.2 The Kernel Alternative


The alternative is to intercept the data transfers when already inside the host
system. As is shown in Figure 2 on page 20 the Linux USB Subsystem is composed of
three layers, Host Controller Driver, USB core, and Device Drivers. As different device
drivers interfaced with the USB core only access information relating to the specific
device they are interested in this top level is too far into the host system for effective
monitoring of the USB as a whole. Individual device drivers are capable of providing
their own debugging features if they want. Added to this device drivers are often
distributed in binary only closed source form (device drivers are an exception in the
open-source nature of the GNU public licence under which Linux is released). Binary
only device drivers could not easily be modified to include added debugging features. It
is therefore desirable to implement monitoring after the signals are decomposed by the
hardware Host Controller and before the communication is split up and passed to
different device drivers.
This leaves two possible locations for effective USB monitoring: the USB core
and the Host Controller Driver. Monitoring inside the host controller driver would give
access to much more information. The host controller is responsible for the scheduling of
actual transactions and this can be directed and determined by the driver and as such the
timing over individual parts of data transfers could be monitored. On this evidence the
host controller driver seems a very sensible place to monitor a USB; however there are
several problems.

Figure 5: The Kernel Monitoring Method

Practically, the number of different drivers and host controllers complicates


making major additions to host controller drivers. The two most common host controllers
(UHCI and OHCI) are significantly different in the amount of scheduling work that is
carried out in the hardware. As such the variety of events and values that can be
measured would be different on different host controllers and in the case of UHCI host
controllers the measurable values would differ depending upon which driver was loaded.
These differences would lead to radically different monitoring systems for the different
combinations. Creating a unified interface for this style of monitoring would pose several
challenges.

As well as these practical issues there is a strong argument that the additional
information that can be garnered from within the host controller driver is only really
relevant to host controller driver design and implementation which is a very narrow
market compared with device developers and device driver writers. Why do device driver
writers care about scheduling they have no control over? If Host Controller Driver writers
want more information about what Host Controller Drivers are doing they can insert
debugging calls themselves.

So by a process of elimination the USB core is the only place to implement a


kernel based monitoring system. This is the most sensible location because it is the only
level that operates on a granularity of data that is the same as the device drivers while
having easy access to all data transactions occurring on the bus. The information is the
same across all host controllers and as such is relevant to the device driver developer.

Having decided to monitor in the USB core the unit of monitoring becomes clear.
This is the URB (Universal Resource Block). This is the structure used by the device
drivers to submit transactions and understand their results. Events indexed by URBs are a
logical frame of reference for device driver writers and a simple one for device
developers to understand. The URB structure is defined in Appendix A.

4 System Design
The system design is clearly split into two parts: the kernel patch and the
monitoring application. The kernel patch is a list of additions (and deletions) to the kernel
source. This can be applied to a kernel source, the kernel then configured as to the
system’s requirements in the usual way and compiled.

The monitoring should be flexible. Monitoring all the data that is passed to or
from an endpoint may be excessive. So three levels of monitoring are defined – No
Monitoring, URB Headers only, and Full Data Monitoring.

The Monitoring Application is a user-space application that can set the


monitoring level of the kernel element and read its output and display this information in
a useful way.

4.1 Functional Requirements of the kernel patch


The patch to the kernel needs to enable three things to happen:
1. URBs passing to and from the host computer must be selectively
intercepted. This will involve:

a. Trapping all URBs after they have completed but before the driver
is notified of completion.

b. Analysing the trapped URB and determining what (if any) details
to monitor of it, by referring to a maintained list of monitored
endpoints.

c. Additionally the monitored URB should be time-stamped at the


time of monitoring.

2. The relevant information obtained from a URB must be stored in a buffer.


Execution must then return to the normal kernel operations.

3. The information in the buffer must be transferred to user space when


stimulated by the monitoring application.

4. The kernel patch should be able to interpret a correctly formatted


command sent from the monitoring application and set the monitoring
level appropriately
Attributes that should be present in the kernel patch

1. The patch should have minimal performance impact on USB transactions


that are not being monitored. This should mean less than a few hundred
extra processor cycles for a non-monitored URB to complete.

2. The patch should not in any way affect the operation of any other kernel
service or user space application. All existing kernel services should be
completely usable with the patch in place.

3. Changes to existing functions in the kernel source should be kept to a


minimum so as to reduce the likelihood of developmental changes to the
kernel tree breaking the patch.

4.2 Design of the Kernel Patch


4.2.1 Intercepting URBs

The URB structure is defined in usb.h (in the kernel include files) and is included
by all the core source files (see Appendix A for URB definition). The URB contains a
pointer to a completion function that is key to the monitoring. The completion function is
a device driver function that is referenced when the device driver submits the URB to the
USB core. This function is then used as a callback as soon as the URB has completed.
This callback is in fact called from within the hardware interrupt handler in the Host
Controller Driver that is caused by the completion signal from the Host Controller.

Initially the idea was to replace this completion call with a new function that
performed the monitoring required and then called the original device driver completion
function. This was simplified slightly to adding in a new function that is called just before
the completion call, with the Host Controller driver’s call to the completion function
remaining in the code. So when modified the Host Controller driver will call a ‘pre-
completion function’ that will perform the monitoring then the Host Controller driver will
call the original device driver defined completion call.

The advantage of this method is that it means less change to the URB structure
and causes less intermediate processing to all URBs, thus slightly reducing the
performance impact of the kernel patch. The principal disadvantage is that this requires a
modification to several places in each of the host controllers. This is an annoyance from a
design point of view as it extends slightly the interface between the USB core and the
Host Controller Driver. This decision is finely balanced between marginal performance
impact versus source code ugliness and reduced future flexibility (in that any new or
radically changed host controller driver will also require minor modifications). This is a
deviation from the original plan where it was aimed to only modify the USB core while
leaving the Host Controller Drivers unaffected by the kernel patch.
4.2.2 Interpreting the Contents of a URB

It is all very well calling the pre-completion function for every URB but not all
URBs will need to be monitored. For example it may be that a system includes a USB
keyboard, which will cause a URB to occur every time a key is pressed.

The URB structure stores the information about the device, endpoint, direction,
and type of the URB in a structure called a pipe. This is in fact an 18-bit structure
encoded in an unsigned integer (see Appendix B for details). Assuming that the pre-
completion function has access to a list of monitored device-endpoint combinations (see
section 4.2.6 on page 27) it can use values that represent masks of the pipe structure to
check if the monitored URB is on the list of monitored pipes. So the pre-completion
function will take a URB as a parameter and compare its pipe with each of the pipe
masks it has in a list of monitored pipes.

Once the pre-completion function has determined that the URB is one that should
be monitored it can copy all the appropriate details into a new URB structure (including
the data buffer depending on monitoring level). The pre-completion function must also
timestamp this new URB. There is no timing information available within the Linux USB
subsystem so this is an addition to the URB structure in the kernel patch.

4.2.3 Storing the Monitored Information

Once a URB has been copied the copy must be stored somewhere. It cannot
immediately be sent to user space as it is being copied in the middle on an interrupt
handler so time is of the essence. The monitored URBs must be stored in a buffer. All the
monitored URBs could be held together in one big list. The disadvantage of this is that
user-space applications would have to obtain the whole list when they may only want to
see if one device has any URBs in its buffer. Separating the monitored URBs at the
device level allows multiple different monitoring applications to operate concurrently and
monitor different devices. The reason for this is explained below in 4.2.4 Passing the
Monitored Information to User Space.

The list of monitored URBs could be separated at the endpoint level, however this
would add a small amount of extra processing on every monitored URB (calculating the
endpoint, and then finding the correct endpoint structure) as well as increasing the
complexity of transporting URBs to user-space. There are no obvious advantages in
separating the monitored URBs at this level, as it is very unlikely that two concurrent
monitoring applications would want to monitor different endpoints on the same device.
The most logical answer then is to separate the URBs at the device level maintaining a
separate buffer for URBs on each device.

As well as the pipe information the URB carries a pointer to a usb_device


structure. One usb_device structure is maintained for each USB device that is currently
on the Bus. This structure contains details about the device and references to structures
describing the configurations. This structure provides the perfect location to store the
monitored URBs. The functions that pass the URBs to user-space can easily access the
correct usb_device structure; this is explained in section 4.3.1.

4.2.4 Passing the Monitored Information to User Space

Moving data from the kernel mode to the user mode and vice versa in a relatively
secure operating system such as Linux is surprisingly difficult. The most classical method
that was looked at for some time is the idea of new system calls that could be called from
user mode to both set the monitoring level and request the dumping of a buffer of
monitored URBs down to user mode. This method is largely workable but has various
side effects that make it undesirable from an operating system design point of view.
Firstly this method has some significant security issues in allowing the dumping of kernel
space memory on request; there is ample room for the introduction of security holes. Also
the new system calls would have to be statically compiled into the Linux kernel. This
would change the nature of the USB sub-system, as at the moment it is possible to
compile it all as dynamically loadable modules. Another problem would be the
enumeration of new system calls. It is entirely probable that new system calls will be
introduced to Linux in the future and these would then introduce incompatibilities with
this kernel patch.

A simpler method to move data from the kernel space to the user space is to use
the /proc filesystem. Since each ‘file read’ on a file in the /proc filesystem is serviced by
a definable kernel function this function can be set to dynamically generate the correctly
formatted URBs. This system can therefore dump a monitored URB buffer from the
kernel mode to a user application.

The creation of an individual /proc file in kernel code is quite easy, however it is
much simpler in terms of creating the correct files at the same time to use the Linux USB
sub-system’s own filesystem that is mounted within the /proc structure. This filesystem
contains a directory for every maintained bus with each bus directory containing a file for
every device on that bus, with the three-digit device number (allocated by the USB core
on connection) as the filename.

So as not to break any currently working system all these files and their behaviour
should not be changed. Under the modified kernel a new file could be added for every
device. As well as a “***” file (where *** is the device number) a second “***M” file
would also be created. These monitoring files would be set up in the same way as the
existing device files but with different ‘read’ and ‘write’ calls. When a URB for a
monitored endpoint completes it’s relevant details are copied to a monitoring buffer.
When this file is next read then the buffer is outputted to the file. This action empties the
buffer, thereby limiting this system to one monitoring application in operation at any one
time per USB device. This is a limitation of this design and there is no simple method
around it.

4.2.5 Formatting the Contents of the Monitoring Files


The content of the monitoring file is the information that has been monitored by
the kernel and is being passed to the user-space application. This information can contain
two main parts: the header information and the actual data buffer. Only on endpoints set
to ‘Full Data Monitoring’ is the data buffer included. The URB header contains values
from the URB structure that could be useful to monitor as well as a time stamp generated
at the time that particular URB was monitored. The values from the URB structure that
are passed over are: device number, endpoint number, pipe, status, size, transfer flags,
actual length, and error count. The meaning of these values is shown in Figure 6.

Device number The number (as given by the USB


core at connection) of the device
this URB was sent to or from
Endpoint number The number of the endpoint this
URB was sent to or from
Pipe A structure containing device and
endpoint numbers as well as
transfer type and direction.
Status The status of the URB when
monitored. This indicates whether
it completed or failed in some
way.
Size The intended size of the data
transfer
Transfer Flags Flags that can affect transfer
scheduling
Actual Length The actual size of the associated
data buffer.
Error Count The number of physical
transmission errors that occurred
during the transfer of this URB

Figure 6: Table showing Items in a URB header

The information must be structured in such a way that the monitoring application
can parse it easily and unambiguously. It is also useful for debugging and advanced use
of the system that the content is easily readable by a user.

A decision was taken to use plain ASCII text to convey information about a URB.
This fits with the two attributes above and also continues the style of most /proc files.
Linus Torvalds (the original author of Linux) has stated that binary data should only be
present in the /proc filesystem when absolutely necessary [13].
For the URB header information plain ASCII text is fine, but the data buffer of
the URB must be sent in full binary format. The header information contains details of
the lengths of the data buffer. The structure of a monitored URB being reported in a
monitoring file is shown in Figure 7.

[URB completed on Device=%03d, Endpoint=%02d, Pipe=


%08X, status= % 02d, size= %08d, transfer_flags =
%08X, actual_length = %08d, error count = %05d
completion_time = %02d:%02d:%02d:%06d dp=%01d
data=]\n

Figure 7: URB encoding in a Monitoring File (Number values are shown in C


format where %2d is a 2 digit decimal, and %08X is an 8 character long
hexadecimal number)

In order for the Monitoring Application to easily parse the content of the
monitoring file it can be important that the length of the ASCII header is fixed. This can
be achieved by placing leading zeros at the beginning of possibly multi-digit numbers. In
C notation this is represented by a 0 before the number of digits in a number modifier, i.e.
%02d instead of %2d for a 2 digit number. Note that the status number in Figure 7 uses
the C notation %[SPACE]02d and opposed to %02d. This means that if the status is
negative (which is possible) a minus sign will be inserted and if positive a space inserted
so that the length of the header will not change. Keeping the header length constant is
important for some parsing systems as some systems deal very differently with ASCII
and binary information.

4.2.6 Setting the Monitoring Level of Devices

Given that the ***M monitoring files are being created anyway to be read from, a
logical extension of this idea is to use the writing case to set the monitoring level. A
simple command structure can easily be defined where a few bytes represent a
monitoring level setting command. Because there are monitoring files for every device
the command need only state the particular endpoint and the desired monitoring level.
The formatting of the command need not be very complex at all. One option would be
simply to have a two-byte command where the first byte is the endpoint number and the
second byte is the monitoring level (in fact given the maximum number of endpoints this
could all be encoded in one byte), however this is too inflexible for future development.
It was decided that a four-byte command structure where the first and third byte is fixed
would provide greater redundancy for future features. The first and third byte can be
verified by the kernel as a check against accidental input from another source.

Byte No. 1 2 3 4
Value ‘E’ Endpoint ‘L’ Monitoring
Number Level
Figure 8: Table containing the byte coding of commands to set the monitoring level. All
bytes are plain-text encoded ASCII.

Once the endpoint value and monitoring level (and the device and bus numbers
derived implicitly from the path of the file) are known by the ‘write’ function in the
filesystem (remember a ‘write’ means information going in to the kernel) it must store
them somewhere. The two considerations on deciding where to store this small amount of
data (the endpoint to be monitored and the level at which to monitor) are that the location
must a) be easily known by the filesystem call that must write to it and b) be easily
known by the pre-completion function during the monitoring of URBs. Of these two
factors the second is much more important as the pre-completion function is inherently
the most performance-sensitive part of the system.

An option would be to have a globally visible list of monitored device-endpoint


combinations, however this is seen as bad in terms of kernel design due to it’s lack of
scalability and reliance on one unified global structure. In fact, as it turns out, both
functions can have very easy access to the usb_device structure of the relevant device.
This is possible for the filesystem function because of a field in the file structure, which is
used in the kernel to represent files. There is a space called ‘private_data’ where the files
system can hide a pointer to a structure of its choice for later reference. For the data
outputting functions a pointer to the appropriate usb_device structure was already being
placed there so that the reading function (a user ‘read’ means a kernel output) had access
to the monitored URBs. This can be re-used in the writing functions to allow the setting
of a monitoring level variable in the usb_device structure.

At first it seems odd for the pre-completion function to have easy access to the
relevant usb_device structure as it is dealing with URBs appearing on all devices not just
one at a time, but in the URB structure that it gets passed as a parameter there is a pointer
to the relevant usb_device structure so the pre-completion function will always be able to
reference straight to the correct usb_device structure.

Maintaining a list of monitored endpoints on a per device basis has significant


implications on the performance of all URB transfers during any monitoring compared
with keeping a unified list of all monitored device-endpoint combinations. The speed
with which the pre-completion function can determine whether the URB it is analysing is
to be monitored or not is proportional to the number of comparisons it must make with
device-endpoint combinations. Using this method of storing the monitored endpoints of a
device in the usb_device structure reduces the number of comparisons to the number of
monitored endpoints in that device rather than on the whole system. This means that
performance is reduced only for a device on which there is monitoring being performed,
others are largely unaffected.

4.3 Kernel Patch Implementation Issues


4.3.1 Adapting the USB filesystem

Within the Linux USB subsystem source code the implementation of the Linux
usb filesystem to which additions are made is split between two main source files known
as inode.c and devio.c. The devio.c file contains all the filesystem functions that provide
the functionality for the device interface files in the bus directories. The inode.c file
contains the functions to define these files and build them up in a filesystem and register
that filesystem. So for example when a new device is attached a function in inode.c is
called that defines a new inode structure, adds references to the functions in devio.c, and
adds this inode to the superblock. Then when a directory in the filesystem is read a
function in inode.c will return the list of inodes in that directory. If the user space wants
the filesystem to open a file it will use the function pointers in the relevant inode structure
to call the device file opening functions defined in devio.c. The same method will cause
the reading functions in devio.c to be called if the file is read.

So in order to add the monitoring files to this system a number of things must be
done. Changes must be made to inode.c to create inodes for the additional monitoring
files. These inodes must be added to the superblock in the same way as the device
interface files but must have pointers to different file operation functions replacing the
existing functions in devio.c.

These additional file operations functions must handle the opening, closing,
reading and writing of the files. In order to have access to the appropriate data there is a
field in the inode structure that allows a pointer to the relevant usb_device structure. This
is what allows access to the URB data that has been monitored. It is important to
remember here that file reads translates to the kernel ‘writing’ information to user space
and vice versa in reading.

The added opening function for the monitoring files moves the monitored URB
buffer to a new buffer, so that monitoring can continue to the old buffer while the user
reads this temporary one. This temporary buffer is destroyed by the closing function. This
means that when a monitoring file is opened and closed the monitored URBs up until the
open are lost from kernel space. This is the attribute of the system that prevents multiple
user programs monitoring the same device at one time.

4.3.2 Implementing Timing in the kernel

Implementing timing routines proved more difficult than expected. This was
primarily due to incompatibilities between the different versions of timing structures in
Linux. Kernel functions do not have access to the standard C library at all but have most
of the functions normally provided by the C library provided by very similar Linux calls.
However there are sometimes conflicts between these definitions.

There were several options of what structure the timing data could be stored in.
The classical C time_t type could be used and set by the time() function. The
disadvantage of this is that the granularity is only in seconds, which is not small enough
for many of the possible applications that this timing information might be wanted for.

Another option is the timeval structure that includes a time_t-like value and a
value for the number of microseconds within that second. This is perfectly adequate for
the USB monitoring purposes. There is another option that is a result of the POSIX 4
specification and that is called the timespec structure, which is doe to supersede the
timeval structure. This is similar to timeval but stores nanoseconds instead of
microseconds. Unfortunately timespec support is incomplete in Linux and although some
of the support functions are available it is not considered ready to be used.

So the logical decision to use timeval structures was made and implementation
attempted. Obtaining and storing the timeval structure proved no problem. However, it
proved impossible to use the normal functions available in C to decompose the time_t
part of the timeval structure due to the fact that in the Linux kernel include files there are
two possible time.h files that can be included. One file that provides the timeval
structures and the time-obtaining functions and one that provide the original time_t
structures and the functions to decompose this time_t functions. These two files cannot be
included together due to subtle incompatibilities. This is only the case for kernel
functions.

After attempting many possible solutions the only workable possibility was to
implicitly code the decomposition of the time_t type rather than rely on the normally
available functions. This is not too complex as date information is not really relevant so
can be disregarded. The time_t type is merely a value giving the number of seconds since
January 1st 1970.

4.3.3 Problems with Interrupt Handlers

For a period of time during the development and testing of the kernel patch the
development system exhibited occasional but very fatal kernel crashes (the whole system
suddenly froze). This was very intermittent but usually during Usb activity and almost
certainly connected to the kernel patch. Searching for a bug that can cause this kind of
error is not easy and after some attempts there appeared no easy way of determining the
cause. At a later stage of development when full data monitoring was finally
implemented in full the system exhibited exactly the same crash symptoms but this time
in an entirely repeatable way. The system crashed when attempting to allocate some
memory.

After some research the cause was discovered. When in the kernel, memory is
allocated using kmalloc() instead of the normal malloc() function. kmalloc() takes an
additional parameter which is a flag specifying the priority. GFP_KERNEL had always
been used as the second value, which specified the correct priority for normal kernel
service. However, the URB intercepting pre_completion function is run from within an
interrupt handler and as such is not allowed to sleep under any circumstances. In order to
stop kmalloc from sleeping under any circumstances it must be called with
GFP_ATOMIC. This means that kmalloc will occasionally fail to allocate memory but
will not sleep and therefore will not cause a systemic crash. Changing all the kmallocs in
the interrupt handler fixed the intermittent crashes. Only very occasionally does a
kmalloc fail in which case it is reported to the system log.

4.3.4 Changes to different Host Controller Drivers

The only alterations to the host controller drivers that is required is the addition of
calls to the pre-completion function just prior to all calls to URB completion functions.
The different Host Controller Drivers have different internal structure so the number and
location of these calls varies widely. The ohci host controller driver centralises its
completion call in one function but the usb-uhci driver makes USB completion calls from
11 different locations in the source code. This means that there is literally a one line
addition required to the latest ohci host controller driver, whereas it about 15 for the usb-
uhci driver. The smaller the number of changes the more likely the patch is to work on
future versions of these drivers.

4.3.5 Where to put the code

For a lot of the changes to the kernel code there is no option on where to put the
code as it is made up of small modifications to existing functions, however when
introducing new functions into the kernel there is a choice between adding to an existing
file or adding in a new source file to package up all the functions to do with the patch.
This second method would make it easier to make the patch a fully-fledged kernel
configuration option, however this would mean changing various Makefiles as well as
creating the new files.

Primarily due to the required changes to Makefiles and configuration files it was
chosen not to create a new file for the new monitoring files. Instead the new functions
were placed at the bottom of the devio.c file as almost all are related to the extensions to
the Linux USB filesystem functionality, which is implemented in the devio.c file.

4.3.6 Compatibility on different kernel versions

The kernel modifications were developed using a stock Linux kernel version
2.4.1[12]. The vast majority of changes that are introduced in successive versions of the
kernel are fixes to old device drivers, new drivers, or fixes to the Host Controller
Drivers. This means that as most of the modifications for this application are to the USB
core and in particular the usbdevfs filesystem there should be little conflict with later
versions of the Linux kernel. The modifications made to the kernel for this system are
mostly additions and specifically adding in new functions, this should reduce further the
likelihood of conflict.
When releasing a working example of this system the patch was applied to a 2.4.4
kernel and found to fail in two areas. These two areas were very quickly fixed and were a
result of design changes within one of the Host Controller Drivers (usb-ohci). This
highlights the disadvantages of the method chosen for intercepting URBs (as discussed in
section 4.2.1 on page 30). Whenever the code in a host controller is changed around the
calling of the URB completion callback the kernel patch may no longer apply. The Linux
Patch utility can cope with some degree of small change but if the actual design changes
then this requires manual intervention.

5 System Design – The Monitoring


Application
Monitoring applications can have a multitude of functionality that helps the user
deal with the data that is monitored. This is ancillary to the core purpose of this
application. The main aim of this monitoring application is to fully demonstrate the
functionality of the kernel patch and provide a useful and usable interface to it.

A simple command-line interface is too simplistic and inadequate at displaying


effectively the monitored data. A graphical user interface provides a much greater ability
to show the complexity and flexible nature of the monitoring abilities of the kernel patch.
The production of advanced applications using complex GUIs is much simpler now than
it has been in the past and there are many libraries that make the implementation of
‘standard’ looking GUIs a minimal extra effort on top of the main application
functionality.

5.1 Functional Requirements of a Monitoring


Application
1. The Monitoring Application should be able to detect from the existing
Linux USB subsystem the topology and configuration of all the attached
busses.

2. The Monitoring Application should be capable of setting the monitoring


level in the modified kernel of different endpoints independently.

3. The Monitoring Application should be able to read the new monitoring


files in the /proc filesystem and parse them in a way such that it can store
data about monitored URBs.

4. The Monitoring Application should be able to display the details of URBs


on different endpoints on different devices.
5.2 Attributes desirable in the Monitoring Application
1. Running the Application should not heavily impact General System
Performance.

2. The Application should be relatively simple to use and data should be


easily obtained by anyone experienced in the concepts of USBs.

5.3 Design of the Monitoring Application


5.3.1 Structure of the USB Model

The Monitoring Application must maintain a model of the USB connected to the
Host system that the Application is running on. This model must be flexible enough to
hold any possible state that a USB can be in. It must also be able to hold all available
configuration information about all devices attached to the bus including details about the
configurations, interfaces, and endpoints.
Figure 9: Simple Object Model representing a USB configuration

Using Object Orientated Methodology this model contains various classes of


object such as Bus, Device, Configuration, Endpoint, and Interface. These classes are
interconnected by a form of ownership. For Example a Configuration has one or more
Interfaces and each interface has one or more endpoints associated with it. This leads
easily to a simple Object Model of the whole bus. In Figure 9 a simple object model is
shown for a bus, obviously more than one of these can be held for a system. The details
of the implemented USB model objects are contained in Appendix D.

This Object Model provides the perfect location to store the monitored data that is
received from the kernel via the monitoring files in the /proc filesystem. This is best kept
in the Endpoint objects as this is the level at which monitoring can be switched on or off.
Note that this is different from the kernel patch where URBs are buffered in device-level
buffers as opposed to endpoint-level buffers. This is not a problem but simply highlights
different priorities in different parts of the system. The monitored data can be kept in a
new object called URB. This will contain all the attributes that can be monitored about a
URB as well as a time stamp that is attached by the kernel patch at the time of
monitoring. Optionally the URB object can also contain the data that the URB was
transferring.

5.3.2 Obtaining the Topology and Configuration Information

All the topological and configuration information is obtained from the ‘devices’
file in the Linux usb filesystem, as referred to in section 2.11. Its structure is exhaustively
defined in [15]. The file gives seven different types of line each describing different sets
of characteristics that devices, endpoints, configurations, and interfaces can have. This
contains all the characteristics from the device descriptors available to the Linux USB
subsystem. This file can be parsed to give the structure and contents of the object model.
As the object is parsed the object model can be built up and the fields filled in.

5.3.3 Polling for the Monitored Data

In order to keep receiving monitored URBs as they are monitored in the kernel the
monitoring application must poll the /proc monitoring files at regular intervals. This is
best achieved in a multi-threaded architecture where a separate thread is started for each
monitored device. These threads can simply read the contents of the monitoring file (thus
emptying the kernel buffer), construct a URB object for every URB in the monitoring file
and place that URB in the list in the relevant endpoint structure.

This design does call for a multithreaded architecture accessing a unified set of
data structures. This would imply that there are thread safety contention issues, however
because the ‘reader’ threads are only updating a particular set of structures that no other
reader thread can be updating and the display threads will only ever read values from
these structures for display there is no danger of thread contention problems.

5.3.4 Displaying Configuration and Monitored Data

In deciding how to best present the data it is important to remember the different
information that people may be interested in finding out. One of the primary concerns is
making it simple to navigate around a bus and select devices of interest. It can be
assumed that target users would want to focus on the details of one device at a time
although retaining the ability to monitor multiple devices simultaneously.

It is clear that USBview (see page 16) has an efficient way of navigating around a
USB bus. A tree is a logical mechanism to navigate around a bus and select a device of
interest and USBview shows this. In USBview another pane contains all information
about a selected device, however here USBview’s solution of having one pane for all
information is inadequate for this system. The potential amount of information that will
be stored in the USB model about a device could be huge and far too big to display all in
one pane. The solution is to break this information down into different areas and make
the second pane selectable as to which information is shown.

The different types of data that a user of the monitoring application will be able to
access includes:

• Basic configuration information from the device descriptors (as is


given in USBview).

• The Monitoring Level settings of the different endpoints in each


device.

• The monitored URBs for each of the endpoints showing the


characteristics of each of the URBs and the ability to show their data
(if present).

This division lends itself to a second pane having three possible states each
serving the three possible uses described above. The three states should be easily
switchable between but need not be displayed simultaneously.

Given the way USB devices work, using different endpoints for different parts of
one task, it is important to be able to see easily the characteristics of monitored URBs on
different endpoints. To this end the URB characteristics can be placed in tables with
scrollbars that allow the viewing of a small number of URBs for one endpoint at the same
time as a small number of URBs on other endpoints.
On monitored URBs with data attached the data need not be shown all the time.
Often there will be a lot of it and most of the time may not be essential to the detail of
monitoring. USB monitored data can represent many different things. And as such it
would be useful to be able to display the data using different

5.4 Monitoring Application Implementation Issues


5.4.1 Java and Swing vs. C and Gtk

The implementation language is significant primarily because of the library


available. The Gtk library is a C based library that is widely used in Linux GUIs. The
Swing Library is an integral part of the Java language that allows extremely flexible and
intelligent presentation in a standardised cross-platform style.

The decision as to which language to implement the monitoring application in is


largely irrelevant. Both language/library combinations are more than adequate for the
proposed functionality. In the end the decision was made to use Java and Swing primarily
to use the stronger object-orientated features of Java. This proved very useful in
implementing the USB object model in the Swing GUI.

5.4.2 The USB Object Model

As described on page 42 the model of the USB can easily be based on a collection
of five different classes of object: Bus, Device, Configuration, Interface, and Endpoint.
The exact details of these objects are shown in Appendix D.

Many of the objects need to keep variable sized lists of objects. For example a
device needs to keep a list of child devices attached to it and a list of possible
configuration objects. In these cases variable sized lists have been implemented as
Vectors.

5.4.3 The Threaded Monitoring File Reader

Interfacing with the kernel via the monitoring files in the /proc filesystem the
Monitoring application must update its usb model as and when it receives new
monitoring information. In order to achieve this the Monitoring Application must
regularly poll all the relevant monitoring files in the /proc filesystem. Java provides an
easy method to set up a thread that will repeat an action repeatedly after a specified time
interval. This is via the Timer and TimerTask Classes. The Timer class sets up a timer that
will run a method in a TimerTask Object at a set frequency. In setting the frequency of
polling it is desirable to have it often enough so that the monitoring appears almost
instantaneous while not so fast as to impose too high a workload on the kernel functions
that dump the buffers down to the monitoring application.

The actions inside this TimerTask method are simply to:

1. Open the relevant monitoring file.

2. Read all the contents into a byte array.

3. Parse the contents of the byte array and construct URB objects to represent the
contents.

4. Place those URB objects in the relevant Endpoint object.

5. Close the file.

The parsing element of this process is slightly complicated by the way java treats
text. Since its inception Java has treated all characters as being 16-bit Unicode entities. It
has different objects for dealing with character streams (which are converted into
Unicode) and byte streams (which are left as unsigned bytes). A problem arises with the
use in this system of one monitoring file to deliver a stream containing plain ASCII text
descriptions of the URB headers and binary data representing the data content of the
URB in the same file. Here the fixed length nature of the URB header in the monitoring
file becomes important. Different methods can be used for the header part and the data
part. Once the header part has been read, the presence and size of the data part can be
determined. Then the data part can be read using an alternative data method that reads the
data as bytes instead of Unicode characters.

5.4.4 Displaying the Device Tree

One of the most useful features of the Java/Swing combination is the ability to
create standardised user features like tables and trees from different object types as long
as they implement an interface by including a few specific methods. This means that the
USB model objects can be used as the models for the display structures.

In order to achieve a tree of devices that can be navigated to select devices of


interest the Device class can be made to implement the TreeModel interface. This
requires the addition of eight methods that allow the implementation of the tree such as
get_child(), get_child_count(), is_root(), etc…

Using this method the collection of Device objects in the USB model implements
the tree model. A navigable tree can easily then be created using the JTree class. The
navigable tree is visible in Figure 10.

5.4.5 Splitting Up the Information


The information displayed about a device selected on the tree has to be selected
from three options. The best solution for this is to use a feature of Swing called Tabbed
Panes. This allows the display to be quickly changed between ‘screens’.

5.4.6 Displaying the URB Tables

The same method that was used to implement the trees can be used to implement
tables. A class can be made to be an extension of the AbstractTableModel class. This
involves adding six new methods such as getRowCount(), getColumnName(),
getValue(), and setValue(). This turns the class into a table model and a simple table can
easily be displayed using the JTable class.

There are two locations where tables are useful. In the Configurations pane
different interfaces need to show all the details of their endpoints and the set monitoring
level.

The first case can be achieved by making changes to the Interface class. Making a table
from the interface class gives a table of endpoints with their characteristics. The
monitoring level of the endpoint needs to be editable. There are only three possible
values. To avoid erroneous input an editor called a ComboBox can be attached to this
column that allows the selection from a drop-down menu. Figure 10 shows the
configuration pane showing the endpoints of a USB storage device (in fact a floppy
drive).

Figure 10: The Configuration pane showing the endpoints of a USB storage device

The second case is where the details of URBs monitored on a URB need to be
displayed. In this case each table model can be represented by an Endpoint class. Similar
extensions to those made to the Interface class can be made to the Endpoint class to allow
the display of the URBs’ details.
Figure 11: The Endpoints pane showing URBs monitored on a USB storage device

The data element of the URB need


not be shown in the table. It is much better if
this is shown in an independent window as
Figure 12: URB data window showing then it can be compared side to side with
the buffer in hex view other URB data buffers. To achieve this
another ComboBox can be used to provide a
drop-down menu on URB rows that have an
associated data buffer. The data can then be
displayed in a new window. The data that is
passed in URBs can have very different
meanings and so different types of encoding
can be used to view the data. A tabbed pane
is used to allow access to Hexadecimal,
Binary, and Textual views of the data.

6 System Evaluation
6.1 Suitability of Core Design
On purely functional and performance grounds a system that unobtrusively
intercepts the USB traffic on the cable outside the host subsystem and passed the
monitored information to a separate monitoring computer provides the most unobtrusive
and accurate monitoring system. This is why large companies succeed in selling such
systems at very high cost. However this system provides a solution to many problems as
effectively as an expensive hardware solution.

Types of problem that can be detected and analysed using this system that it was
not possible to do before without hardware monitoring:

1. Analyse the data sent to and from a closed source driver.

2. Debug the data sent to and from an open source driver without changing
the code.

3. Clearly show the relative activity of different devices on a bus.

4. Determine the time taken between a driver receiving a URB and sending a
reply URB.

5. Determine whether errors in the data transmissions are occurring (as


drivers may not report them).

Areas that this system cannot observe and is not able to aid:

1. Analysing bugs inside the Host Controller hardware.

2. Analysing bugs inside the Host Controller Driver software.

3. Determining the accuracy of power distribution across the bus.

4. Obtaining the timings of sub-parts of data-transfers such as the time


difference between a control request and the acknowledgment.

5. Debug erroneous transfers from devices that are not of the correct format.

Prior to this project Linux users were unable to carry out any real monitoring of
USBs without specialist hardware. USBview (as described on page 22) only provides
configuration information and only really displays the information that is already
available in the devices file in the Linux USB filesystem. This project does provide a new
capability to USB on Linux but it does not replace the need for hardware monitoring
systems completely. Experts who are designing complex devices and fine-tuning first
time device drivers will doubtless want to be certain about the exact signals that are being
transmitted. However there is a group of part-time developers who are writing device
drivers for niche devices that have been implemented on other operating systems. For this
class of device driver developer this system is a very cost-effective solution to debugging
the operation of devices and device drivers.

6.2 Effectiveness of Implementation


Writing code for the kernel is often portrayed as a kind of black art where the
traditional rules of software do not wholly apply, a bit like the quantum mechanics of
programming. This is less true of Linux due to the number of people involved in its
development and the amount of documentation available, however there are still some
major complexities that must be taken into account.

The kernel patch has proved largely stable. Although not thoroughly or rigorously
tested for memory leaks and other possible low-visibility defects, the patch has proved
stable being run as the primary kernel on the development system throughout the
progression of the project. This has shown both that the (patched) kernel is stable for
prolonged periods of time (over a week between re-boots) in that it shows no outward
sign of deterioration in either normal use or USB performance.

After the kmalloc() priority changes (see section 4.3.3) there were no more
unexplained kernel failures.

There are some areas where the monitoring system is less stable than might be
desired. It only has a partial solution to the issue of hotplugging, and can give unwanted
error messages when devices are disconnected.

The way the monitoring application handles alternate interfaces is not ideal. The
concept of alternate interfaces was largely forgotten about during the design as none of
the devices that were experimented on had them. The problem is that each alternate
device’s endpoints are given their own endpoint object and assumed to be active (which
is not always true). This does not prevent monitoring but is certainly not ideal as the
display becomes very cluttered by endpoints that are not relevant to the user.

6.3 Impact on the Linux USB Subsystem of the kernel


patch
One of the key attributes desired in the system was that is did not impact on
normal USB operation. Verification of this was done in two parts:

1. Impact on Functionality - The Impact on performing normal USB operations


and looking for any loss of functionality in USB using operations.

2. The Timing Impact of Monitoring - Obtaining timing information for the added
processing done to non-monitored URBs.

6.3.1 Impact of Functionality

Testing functionality was achieved by carrying out a variety of normal tasks that
used a range of transfer types and amounts of bandwidth. Items that were tested included:
• A USB mouse (high frequency of interrupt transfers). This device was as a
standard mouse input to X Windows environment.

• A USB web cam streaming video image (primarily using isochronous


transfer). A video stream was set up and displayed on screen using the
Video4Linux API.

• A USB floppy drive using the usb-storage driver (uses bulk, control, and
occasional interrupt transfers). File transfers were carried out on a variety of
file sizes.

The results were that no difference in behaviour was observed under any of the
specified operations.

6.3.2 The impact of Monitoring on processing time

The principal addition in the kernel patch that affects all URB transfers regardless
of what the monitoring level is the pre-completion function that is run on every URB
prior to the driver being informed of completion. The amount of processing that is carried
out in this function is a measure of the cost of monitoring.

Analysis of the code in this function can give factors that affect the amount of
processing that will occur. The source code to the pre-completion functions is in
Appendix C. The factors that can be deduced from the structure of the code are:

• For devices on which none of the endpoints is monitored in any way there is very
little processing that will occur. This is because no comparisons are necessary to
check whether the URB needs to be monitored.

• The amount of processing for all transfers on a device with monitored endpoints
will increase with the number of endpoints that are monitored. This is because
even for transfers on endpoints that are not monitored the function will have to
check the endpoint value against more ‘monitored endpoint’ values to determine
whether or not this endpoint is on the list to be monitored.

• Transfers on monitored endpoints will require more processing than transfers on


endpoints that are not monitored. This is because allocating the memory and
copying the details of the URB takes processing time.

• Transfers on endpoints with ‘full data monitoring’ will require more processing
than transfers on endpoints set to ‘URB headers only’. This is because of the extra
memory that must be allocated and the extra copying.
To obtain the timing information, a modification can be made to the pre-
compilation function to get the length of time that the system takes to carry out the
processing in that function. In a normal user-space C program this could easily be done
using the clock() call and clock_t variables, however the problem that was discovered
when implementing timing in the monitoring system (see section 4.3.2 on page 37)
resurfaces. The clock() function gives the number of clock cycles that have occurred.
This value looks back to zero every few minutes on a fast 32-bit machine but is useful for
very accurate timing. Unfortunately the timing functions that are available within the
Linux kernel do not include the clock() function. It seems odd that user space can access
a service from the kernel that code in the kernel cannot easily access itself, but it was not
possible to find a way to do this. There is no evidence in the kernel itself or in any kernel
documentation of a similar function.

An alternative to the clock() is to use the timing functions used to give a


monitoring time accurate to 1 microsecond. On a fast PC several hundred machine
instructions will occur every microsecond but this is just enough to determine the delay
caused by the processing in the pre-completion function. The absolute time (in
microseconds) can be got at the start of the function and again at the end. The difference
can then be sent to the system log. This does not include the additional cost of calling the
pre-completion function and returning, this is assumed to be irrelevantly small. The
timing results give the approximate amount of time taken to perform the contents of the
pre-completion function. It is not completely accurate because the start and finish times
are not necessarily on microsecond boundaries so there could be an error of up to 2
microseconds. Taking that into account and after experimenting with this timing
mechanism it was decided to average the results over several transfers.

The testing that was timed consisted of file transfers to and from a USB storage
device. USB storage devices use the Primary Control pie as well as two bulk pipes (one
in and one out), and an incoming Interrupt pipe. File transfers will cause activity on all
these pipes. The device was re-mounted between every test in order to clear any file
caches. The first run was also repeated to counter the possible effect of memory caches in
the system.

Two sets of tests were performed on a relatively high specification machine (650
MHz Pentium III with 128Mb RAM):

Set 1: URB Only Monitoring – All endpoints were initially set to ‘No Monitoring’
with the number of endpoints set to ‘URB only monitoring’ incremented after each timed
run. After each timed run the pre-compilation time for 10 transfers on monitored
endpoints and 10 on unmonitored endpoints were averaged to a figure.

Set 2: Full Data Monitoring – All endpoints were initially set to ‘No Monitoring’
with the number of endpoints set to ‘Full Data Monitoring’ incremented after each timed
run. After each timed run the pre-compilation time for 10 transfers on monitored
endpoints and 10 on unmonitored endpoints were averaged to a figure. Here the timing of
monitored data points refers only to 512 byte bulk transfers. The usb-storage driver will
only shift data in blocks of 512 bytes so this is a good standardisation to eliminate
variance depending on the size of the data that is copied.

Set 1: URB Headers only:

Average time taken


Average time taken on
Number of Monitored on unmonitored
monitored endpoints /
Endpoints endpoints /
microseconds
microseconds
0 0.5
1 0.6 1.9
2 0.4 1.8
3 0.9 1.4
4 1.5

Figure 13: Table and Graph showing the results of timing analysis of the pre-completion
function.

The results of this first set of tests are largely as expected with one slight
anomaly. The fact that time taken by monitored transfers is greater than unmonitored
transfers is as expected, as is the fact that the time taken by unmonitored endpoints
appears to marginally increase with the number of endpoints. The anomaly is that the
results indicate that as the number of monitored endpoints is increased the length of time
taken inside monitored endpoints appears to decrease. There is no obvious explanation
for this. The important conclusions that can be made from these results are that the
number of monitored endpoints (up to at least four of a possible sixteen) does not have
significant impact on the USB performance of a device and that even monitored transfers
are only impacted by about two microseconds per transfer.
Set 2: Full Data Monitoring:

Average time taken on


Average time taken on
Number of Monitored unmonitored
monitored endpoints /
Endpoints endpoints /
microseconds
microseconds
0 0.5
1 0.8 5.1
2 0.7 5.3
3 0.8 4.9
4 5.2

The results of the second set of tests shows that Full Data monitoring is much
more expensive in processing time that simple URB header only monitoring. Full Data
monitoring on a bulk transfer of 512 bytes adds about 5 microseconds to the amount of
time taken to process a URB transfer.

The significance of this timing data on potential users can only really be guessed
at. USB allows interrupt transfers to happen every millisecond and it is conceivable that
when monitoring such an endpoint the difference between a five microsecond penalty as
opposed to a two microsecond penalty might be important. Unfortunately it is very
difficult (and beyond the scope of this project) to put these figures in context by
benchmarking the other elements of the Linux-USB subsystem that are essential to its
operation.
6.4 Performance of the Monitoring Application
The monitoring Application’s performance is much less important than the
performance of key parts of the kernel patch as the monitoring application runs at a much
lower priority in user space and can be interrupted at any time. It is still however relevant
as the monitoring application must be run I order for the monitoring that the kernel
performs is retrieved and displayed. The performance of the Monitoring Application is
heavily dependent on the java virtual machine that is being run on the host system. On a
sample system (650MHz Pentium III with 128Mb SDRAM running IBM JVM for Linux
1.3) the Monitoring Application never used more than 10 per cent of available CPU
utilisation and mostly much less. This is perfectly acceptable.

6.5 User Adoption


At two points during this project code was released for public consumption and a
notice was made on the linux-usb-developers and linux-usb-users mailing lists. The code
was released as under the name USBMon. These releases were named versions 0.1 and
0.2 respectively. Version 0.1 only contained a kernel patch that did not support full data
monitoring. Simple command line tools were supplied with version 0.1 to control and
view the monitoring.

The USBMon system (as well as this document) can be downloaded from
http://www.dcs.ed.ac.uk/~dxh/public/USB/. Over thirty-five downloads have been
recorded since version 0.2 was released.

6.6 Future Development


Possible future development of this system could take many possible routes
depending on the usage of the system and implementation decisions made in the main
Linux USB tree. The most obvious next step is to develop the kernel patch into a
configuration option in the Kernel. This could even prepare the patch for possible
submission into the Linux kernel. Before any submission to the Linux kernel the absolute
stability under a much wider range of systems would be necessary. A lot more reviewing
of kernel code by experienced kernel developers would also be desirable.

The amount of monitored data values monitored by the kernel could also be
increased. The interface to user-space could be made more flexibly defined so as to allow
greater selectivity of monitored events by the monitoring application, for example only
monitoring URBs that returned with error codes.

A possible addition to the kernel patch is the provision of generalised statistics for
usage and activity over the whole USB. This could take the form of a file in the /proc
filesystem that gave a few global statistics much in the same way as CPU usage is
reported. This could then be used by simple desktop applets to signal activity on the USB
bus.
On the monitoring application side there are many possible data analysis
mechanisms that could be implemented such as graphs and charts as well as the
calculation of generalised statistics on a device basis.

Bibliography:
[1] USB Implementers Forum. USB Home Page. http://www.usb.org

[2] Microsoft, Intel, NEC, Compaq. USB Specification 1.1, 23rd September 1998.
http://www.usb.org/developers/data/usbspec.zip

[3] Microsoft, Intel, NEC, Compaq. USB Specification 2.0. 27th April 2000.
http://www.usb.org/developers/data/usb_20.zip

[4] USB Device Class Working Group. Approved Device Class Specifications.
http://www.usb.org/developers/devclass_docs.html

[5] Compaq. OHCI host controller Specification.


http://www.compaq.com/productinfo/development/openhci.html

[6] Intel. UHCI Design Guide. http://developer.intel.com/design/USB/UHCI11D.htm

[7] Microsoft, Intel, NEC, Compaq. Guide to USB 2.0.


http://www.usb.org/developers/data/usb_20g.pdf

[8] Java USB expert group, Java Specification Request: USB API, 14th May 2001.
http://java.sun.com/aboutJava/communityprocess/jsr/jsr_080_usb.html

[9] John Garney, Intel Architecture Labs. An analysis of throughput. Characteristics


of Universal Serial Busses.
http://www.usb.org/developers/data/whitepapers/bwpaper2.pdf

[10] USB Workshop. http://www.usbworkshop.com

[11] Linux Documentation Project. http://www.linuxdoc.org

[12] Linux Kernel Download page. http://www.kernel.org

[13] Linus Torvalds summarised by Zack Brown, Linus on devfs, 24th April 2000.

http://kt.zork.net/kernel-traffic/kt20000424_64_print.html
[14] Linux USB Home Page. http://www.linux-usb.org

[15] Linux USB Guide. http://www.linux-usb.org/USB-guide/book1.html

[16] Linux USB SourceForge Page. http://sourceforge.net/projects/linux-usb

[17] Linux USB Developers mailing list archive.


http://www.geocrawler.com/lists/3/SourceForge/2571/0/

[18] Linux USB Users mailing list archive.


http://www.geocrawler.com/lists/3/SourceForge/4563/0/

[19] Roland and Tom. USBSnoopy Web Page. http://www.jps.net/~koma/

[20] USB robot Homepage. http://usb-robot.sourceforge.net/

[21] Greg Kroah-Hartman. USBView Web Site. http://www.kroah.com/linux-usb/

[22] Linux Hotplugging Homepage. http://linux-hotplug.sourceforge.net/

[23] David Brownell. jUSB Web Site. http://jusb.sourceforge.net/

[24] Video4Linux Web Site. http://roadrunner.swansea.linux.org.uk/v4l.shtml

[25] Linux USB supported devices page. http://www.linux-usb.org/devices.html

[26] Sun. Sun Java Homepage. http://www.java.sun.com

[27] Microsoft. USB Support on Windows 98 vs. Windows 95, 3rd January 2001.
http://www.microsoft.com/hwdev/busbios/usbwin98.htm

[28] Apple Corp, Apple Usb systems. http://www.apple.com/usb/

[29] S Hughes and DJ Thorne, British Telecom. Broadband in home Networks


http://www.bt.com/bttj/vol16no4/06.pdf

[30] Mark A Kelner, Government Computer News. USB: This Bus is going places, July
19 1999 http://www.gcn.com/shopper/vol18_no22/peripherals/278-1.html
[31] Alan Zisman, Canada Computer Paper Inc. USB is (finally) a contender. June
1999. http://www.ccwmag.com/articles/99-
06/9906/TECHTALK/TECHTALK/TECHTALK.html

[32] Steven Brody, CNN. Linux projected to outpace all contenders through 2003, 22nd
April 1999. http://www.cnn.com/TECH/computing/9904/02/linuxgrow.ent.idg/

[33] Kelvin Taylor, PC Magazine. Intel announces royalty-free USB 2.0, March 2001.
http://www.zdnet.co.uk/pcmag/trends/2001/03/07.html

[34] CATC – Computer Access Technology Corporation USB products.


http://www.catc.com/products/d_usb.htm

Appendix A - The URB Structure

This is the modified URB structure as found in include/linux/usb.h in the Linux source
code. The only modification is the addition of the final timestamp.
typedef struct urb

spinlock_t lock; // lock for the URB

void *hcpriv; // private data for host controller

struct list_head urb_list; // list pointer to all active urbs

struct urb *next; // pointer to next URB

struct usb_device *dev; // pointer to associated USB device

unsigned int pipe; // pipe information

int status; // returned status

unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | etc.

void *transfer_buffer; // associated data buffer

int transfer_buffer_length; // data buffer length

int actual_length; // actual data buffer length

int bandwidth; // bandwidth for this transfer request (INT or ISO)

unsigned char *setup_packet; // setup packet (control only)

//
int start_frame; // start frame (iso/irq only)

int number_of_packets; // number of packets in this request (iso)

int interval; // polling interval (irq only)

int error_count; // number of errors in this transfer (iso only)

int timeout; // timeout (in jiffies)

//

void *context; // context for completion routine

usb_complete_t complete; // pointer to completion routine

//

iso_packet_descriptor_t iso_frame_desc[0];

//

struct timeval completion_time; // for USBMon timestamping

} urb_t, *purb_t;

Appendix B - The pipe structure

This is a comment taken from include/linux/usb.h in the Linux source code version
2.4.4. It describes the pipe structure.
/*

* Calling this entity a "pipe" is glorifying it. A USB pipe

* is something embarrassingly simple: it basically consists

* of the following information:

* - device number (7 bits)

* - endpoint number (4 bits)

* - current Data0/1 state (1 bit)

* - direction (1 bit)

* - speed (1 bit)

* - max packet size (2 bits: 8, 16, 32 or 64) [Historical; now gone.]

* - pipe type (2 bits: control, interrupt, bulk, isochronous)


*

* That's 18 bits. Really. Nothing more. And the USB people have

* documented these eighteen bits as some kind of glorious

* virtual data structure.

* Let's not fall in that trap. We'll just encode it as a simple

* unsigned int. The encoding is:

* - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64) [Historical;


now gone.]

* - direction: bit 7 (0 = Host-to-Device [Out], 1 = Device-to-Host [In])

* - device: bits 8-14

* - endpoint: bits 15-18

* - Data0/1: bit 19

* - speed: bit 26 (0 = Full, 1 = Low Speed)

* - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, 10 = control, 11 =


bulk)

* Why? Because it's arbitrary, and whatever encoding we select is really

* up to us. This one happens to share a lot of bit positions with the UHCI

* specification, so that much of the uhci driver can just mask the bits

* appropriately.

*/

Appendix C - The Pre-Completion Functions

Below is the source code of the pre-completion function that is part of the kernel patch.
On the next page is the function that copies the URB and is called from the
pre_completion function.
void USBMon_urb_pre_completion(urb_t *purb)

{
urb_t *pcpy_urb;

struct usbmon_endpoint_ll *ep;

ep = (struct usbmon_endpoint_ll *)

purb->dev->USBMon_data->USBMon_monitored_endpoints;

while(ep != NULL){

/* test this ep against list */

if((purb->pipe & ((0x4f << 8) | (0xf << 15))) == ep->value){

/* We are now monitoring this ep*/

pcpy_urb = USBMon_cpy_urb(purb, ep->level);

if(pcpy_urb != NULL){

/* copying did not fail -> add to linked list */

pcpy_urb->next=NULL;

if(purb->dev->USBMon_data->USBMon_urb_ll_head ==

NULL){

purb->dev->USBMon_data->

USBMon_urb_ll_head =

pcpy_urb;

purb->dev->USBMon_data->

USBMon_urb_ll_last =

pcpy_urb;
}else{

purb->dev->USBMon_data->

USBMon_urb_ll_last->next =

pcpy_urb;

wmb();

purb->dev->USBMon_data->

USBMon_urb_ll_last=

pcpy_urb;

}else{

printk("[USBMon] failed to copy URB for monitoring\n");

ep = NULL; /* So as to break out of while loop faster*/

}else{ /* endif check on endpoint */

ep = ep->next;

purb_t USBMon_cpy_urb(urb_t *orig, int level)

urb_t *cpy;

cpy = kmalloc(sizeof(*cpy), GFP_ATOMIC);

if(!cpy){
printk("[USBMon] Unable to kmalloc URB copy\n");

return NULL;

cpy->dev = orig->dev;

cpy->pipe = orig->pipe;

cpy->status = orig->status;

cpy->transfer_flags = orig->transfer_flags;

cpy->transfer_buffer_length = orig->transfer_buffer_length;

cpy->actual_length = orig->actual_length;

cpy->bandwidth = orig->bandwidth;

cpy->setup_packet = orig->setup_packet;

cpy->start_frame = orig->start_frame;

cpy->number_of_packets = orig->number_of_packets;

cpy->interval = orig->interval;

cpy->error_count = orig->error_count;

get_fast_time(&(cpy->completion_time));

if(level==3){

cpy->transfer_buffer = (void *)

kmalloc(orig->transfer_buffer_length,

GFP_ATOMIC);

if(!(cpy->transfer_buffer)){

printk("[USBMon] Unable to kmalloc URB buffer\n");

cpy->transfer_buffer = NULL;

}else{

memcpy( cpy->transfer_buffer,
orig->transfer_buffer,

orig->transfer_buffer_length);

}else{

cpy->transfer_buffer = NULL;

return(cpy);

Appendix D - The USB Model Java Classes

Class Bus

java.lang.Object
|
+--Bus

public class Bus

extends java.lang.Object

Class used to store information about a Bus.

Field Summary
int alloc
The amount of bandwidth allocated to this Bus
int max_bandwidth
The total amount of bandwidth possible on this Bus
int num_intrpts
The number of interrupt requests
int num_iso
The number of isochronous requests
int number
The number of the Bus.
Device root_hub
The root hub device.
Constructor Summary
Bus(int bus_no)

Method Summary
Device search(int num)
Search for a particular device and return it.
java.lang.String toString(int indent)
This returns a textual representation of the Device and its
children.

Methods inherited from class java.lang.Object


clone, equals, finalize, getClass, hashCode, notify, notifyAll,
toString, wait, wait, wait
Class Device

java.lang.Object
|
+--Device

All Implemented Interfaces:

javax.swing.tree.TreeModel

public class Device

extends java.lang.Object

implements javax.swing.tree.TreeModel

Class used to store information about device.

Field Summary
int bus_num
The number bus that this device is on.
java.util.Vector cfg_list
The list of configurations.
java.util.Vector children
The list of devices connected to this one.
int cls
The Class number.
java.lang.String cls_string
The Class as a string.
javax.swing.JComponent configs_pane
This holds the Configurations Pane that is plays the
tables of interfaces and Endpoints.
Endpoint Control_Pipe
The Primary Control Pipe that all USB devices must
have.
javax.swing.JComponent details_pane
This holds the Details Pane that holds the lists of
URBs by endpoints.
int driver
The Driver that is associated with this device (or
'none').
int level
The level within the bus.
int max_monitoring_level

Mfilereader mfilereader
This is the mfilereader for this device.
int mxps
The size of packets from endpoint 0.
int num
The Device Number.
int num_configs
The number of different configurations.
int num_endpoints
Number of endpointd (** INTERNAL **)
int num_ports
The number of ports (0 for hub).
int parent
The device number of the parent
int parent_port
The port number of the parent that this device is
connected to.
int product
The Manufaturer's device number.
java.lang.String product_string
The Manufaturer's device name.
int protocol
The Protocol.
int revision_maj
The revision number of the hardware.
int revision_min
The revision number of the hardware.
Device root_hub
The root_hub device
java.lang.String serial_number
The Manufaturer's serial number.
java.lang.String speed
The Speed of the device in Mbps.
int subclass
The Sub-class number.
javax.swing.JTabbedPane tabbed_pane
This holds the tabbed pane for this device.
java.util.Vector URB_list
The list of URBs monitored on this device
int vendor
The Manufacturer number.
java.lang.String vendor_string
The Manufacturer as a string.
int version_maj
The version of USB that this device claims to support
(before point).
int version_min
The version of USB that this device claims to support
(after point).

Constructor Summary
Device()

Method Summary
void addTreeModelListener(javax.swing.event.TreeModelListener l)
addTreeModelListener not fully implemented.
java.lang.String details()
This returns a full textual representation of the Device
java.lang.Object getChild(java.lang.Object parent, int index)
This returns the child of the particular.
int getChildCount(java.lang.Object parent)
This method gets the number of children for a parent.
int getIndexOfChild(java.lang.Object parent,
java.lang.Object child)
This method returns the index of the child in parent's children.
java.lang.Object getRoot()
This returns the Bus object on which this device sits.
boolean isLeaf(java.lang.Object dev)
This method returns whether the object is a leaf or not.
void make_configs_pane()
This makes the configs pane.
void make_details_pane()
This makes the details pane.
void make_tabbed_pane()
This makes the details pane.
void removeTreeModelListener(javax.swing.event.TreeModelListener l)
removeTreeModelListener not fully implemented.
Device search(int num)
The search method searches for devices with a particular device number.
java.lang.String toString()
This returns a brief textual representation of the Device
java.lang.String toString(int indent)
This returns a textual representation of the Device and its children.
void valueForPathChanged(javax.swing.tree.TreePath path,
java.lang.Object NewValue)
valueForPathChanged not fully implemented.

Methods inherited from class java.lang.Object


clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait,
wait, wait
Class Configuration

java.lang.Object
|
+--Configuration

public class Configuration

extends java.lang.Object

Class used to store information about a configuration of a device

See Also:

Device

Field Summary
boolean active
true if this is the active configuration of the device.
int attr
The attributes of this configuration.
java.util.Vector if_list

int num
The number of this configuration.
int num_ifs
The number of different interfaces that this configuration has.

Constructor Summary
Configuration()

Method Summary
boolean bus_powered()
bool that is derived from the attr integer.
boolean self_powered()
bool that is derived from the attr integer.
java.lang.String toString()

boolean wakeup_capable()
bool that is derived from the attr integer.

Methods inherited from class java.lang.Object


clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait,
wait, wait
Class Interface

java.lang.Object
|
+--javax.swing.table.AbstractTableModel
|
+--Interface

All Implemented Interfaces:

java.io.Serializable, javax.swing.table.TableModel

public class Interface

extends javax.swing.table.AbstractTableModel

Class used to store information about an interface (part of a cfg of a device.

See Also:

Device, Configuration, Serialized Form

Field Summary
int alt
The alternate that is being described.
int alt_cls
The alternate's class as an int.
java.lang.String alt_cls_string
The alternate's class as a String.
java.lang.String alt_driver
The alternate's driver as an int.
int alt_protocol
The alternate's protocol as an int.
int alt_subclass
The alternate's subclass as an int.
java.util.Vector endpoint_list
List of the endpoints for this interface.
int num_eps
The number of endpoints in this interface.
int number
The interface number.
Fields inherited from class javax.swing.table.AbstractTableModel
listenerList

Constructor Summary
Interface()
Constructor...

Method Summary
int getColumnCount()
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Configurations Page for a device.
java.lang.String getColumnName(int col)
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Configurations Page for a device.
int getRowCount()
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Configurations Page for a device.
java.lang.Object getValueAt(int row, int col)
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Configurations Page for a device.
boolean isCellEditable(int row, int col)
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Configurations Page for a device.
void setValueAt(java.lang.Object value, int row, int col)
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Configurations Page for a device.

Methods inherited from class javax.swing.table.AbstractTableModel


addTableModelListener, findColumn, fireTableCellUpdated,
fireTableChanged, fireTableDataChanged, fireTableRowsDeleted,
fireTableRowsInserted, fireTableRowsUpdated, fireTableStructureChanged,
getColumnClass, getListeners, removeTableModelListener
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll,
toString, wait, wait, wait
Class Endpoint

java.lang.Object
|
+--javax.swing.table.AbstractTableModel
|
+--Endpoint

All Implemented Interfaces:

java.io.Serializable, javax.swing.table.TableModel

public class Endpoint

extends javax.swing.table.AbstractTableModel

Class used to store information about device.

See Also:

Serialized Form

Field Summary
int address
The address of the endpoint.
int attr
The type of transfer as an int.
java.lang.String attr_string
The type of transfer as an String.
boolean input
The endpoint is an input.
int interval
The interval in ms between the polling of interrupt endpoints.
int max_packet_size
The maximum packet size of this endpoint.
int monitoring_level
The level at which this endpoint is currently monitored.
int number
The endpoint number
boolean output
The endpoint is an input.
java.util.Vector URB_list
The list of URBs monitored on this device

Fields inherited from class javax.swing.table.AbstractTableModel


listenerList

Constructor Summary
Endpoint(Device d)

Method Summary
int getColumnCount()
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Details Page for a device.
java.lang.String getColumnName(int col)
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Deatils Page for a device.
int getRowCount()
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Details Page for a device.
java.lang.Object getValueAt(int row, int col)
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Details Page for a device.
boolean isCellEditable(int row, int col)
In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Details Page for a device.
void set_monitoring_level(int val)

void setValueAt(java.lang.Object value, int row, int col)


In Order to Extend AbstractTableModel so that the table of
endpoints can be shown on the Details Page for a device.
Methods inherited from class javax.swing.table.AbstractTableModel
addTableModelListener, findColumn, fireTableCellUpdated,
fireTableChanged, fireTableDataChanged, fireTableRowsDeleted,
fireTableRowsInserted, fireTableRowsUpdated, fireTableStructureChanged,
getColumnClass, getListeners, removeTableModelListener

Methods inherited from class java.lang.Object


clone, equals, finalize, getClass, hashCode, notify, notifyAll,
toString, wait, wait, wait
Class URB

java.lang.Object
|
+--URB

public class URB

extends java.lang.Object

Class used to store details of a URB that occurred in a Linux USB subsytem. The exact
configuration of this URB object relates to the attributes that this monitoring application
needs to maintain. This is not a definition of what a URB contains.

Field Summary
int actual_length
This is the actual length of the transmitted URB
Configuration cfg
Device Configuration - at time of load.
byte[] data
The Vector containing the data
boolean data_present
True if data is present in this URB
Device dev
The Device that this URB was sent/recieved on
int devnum
The actual device number as passed in ***M
Endpoint ep
The Endpoint that this URB was sent/recieved on
int epnum
The actual Endpoint number as passed in ***M
int error_count
Error Count - the number of transmission errors.
int hour
the hour that this URB was completed
long millisecond
the millisecond that this URB was completed
int minute
the minute that this URB was completed
int pipe
The Pipe information: This contains the direction and type
of pipe information.
int second
the second that this URB was completed
boolean showing_data
True if data is present in this URB
int size
The size of the transmitted buffer.
int status
Status:
int transfer_flags
transfer flags
javax.swing.JFrame window
A JFrame placeholder to store the Frame showing the
contents of the URB.

Constructor Summary
URB(java.lang.String str, byte[] rec_data, Bus bus)
The constructor that creates the URB structures from the line in the ***M file in
the USB Monitoring part of the /proc file system.

Method Summary
java.lang.String toString()

Methods inherited from class java.lang.Object


clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait,
wait, wait

Manufacturers of these systems include Intel, Compaq, CATC, and Catalyst Enterprises
A minor update to the 1.0 specification released primarily to clarify issues relating to
hardware device design.

This is derived from a maximum of 500mA current from a supply that can vary 4.75V –
5.25V as specified in [2] p.134-139

This situation was not necessarily intended. It is primarily a result of Intel refusing to
licence the UHCI design very widely prompting the development of OHCI by
competitors. Intel has now agreed to licence the USB 2.0 EHCI design freely [33].

Apple Computers Inc usually refers this to.

Sony Corp uses this.

According to Apple Corp. over 125 peripherals are available at this time, in the year 2000
over 12 million IEE 1394 devices were shipped.

DOS is the Disk Operating System format common on floppy disks. EXT2 is the most
common native Linux hard drive filesystem.

USB support was started in the previous 2.2 kernel (starting at 2.2.7). However
development soon moved to the 2.3 experimental kernel tree. USB usage on 2.2 kernels
was possible via the use of a kernel back-port patch that some distributions used to allow
basic USB mouse and Keyboard use. USB usage beyond this was possible but not easy.

This is the validation for the accuracy of the timestamp taken at time of monitoring.

Valid values for return status are detailed in [15].