Although the Universal Serial Bus (USB) is an integral part of many computers, Java does not officially support USB. Getting your Java programs to interact with arbitrary USB devices thus requires either a third-party Java/USB API or your own Java/USB API. This article introduces two third-party APIs and my own API, which provides a partial USB interaction.
Before it introduces the APIs, this article explores some USB fundamentals. The article next introduces JSR-80 (an API that attempts to bring official USB support to Java) and jUSB (a rival API). Moving on, the article presents my own bus-enumeration API, which only works in a Windows context. The article closes by pondering whether or not Java should officially support the USB.
I developed and tested this article's code with Sun's J2SE 5.0 SDK and Borland C++ 5.5.1. Windows 98 SE and Windows ME were the underlying OSes.
USB in a Nutshell
USB is a serial bus standard for connecting devices to computers, video game consoles, televisions, and so on. This standard has advantages over the legacy personal computer (PC) architecture (expansion cards, I/O ports, interrupts, device drivers) previously used to connect devices to PCs. These advantages include:
The serial bus, in partnership with plug and play, eliminates I/O port and interrupt conflicts that often arise when adding new devices to a legacy PC.
There is no need to open a USB-enabled PC and insert expansion cards into its system bus. Devices plug into the serial bus through external connectors.
Devices can be connected to or removed from the serial bus without having to first power down or reboot the USB-enabled PC. This is known as hot swapping.
Compaq, Intel, Microsoft, and NEC started to work on the USB standard in 1994. In January 1996, they released version 1.0. Following revisions, these companies released version 1.1 in September 1998. And in April 2000, after Hewlett-Packard, Lucent, and Philips joined the group, version 2.0 was released.
USB 1.1 was the first widely implemented version of the USB standard. It featured two data-transfer speeds: full speed (12 megabits per second) and low speed (1.5 megabits per second). It is now fading into history as version 2.0 takes over. This version adds a third data-transfer speed: high speed (480 megabits per second).
To fully understand the USB standard, you'll need to review the USB 1.1 specification and the USB 2.0 specification. For the purpose of understanding this article, however, you only need to grasp a few fundamentals, starting with an overview of host controllers, hubs, and functions.
Host Controllers, Hubs, and Functions
USB is implemented in terms of software drivers (which I don't discuss here) and hardware. The hardware divides into host controllers, hubs, and functions:
The host controller connects a tree of USB devices to a host (computer, console, and so on). Multiple host controllers can be present. Each host controller anchors one device tree.
A hub is a device with multiple ports, into which are plugged other hubs and functions. The root hub integrates with the host controller. All other hubs are external hubs.
A function is a device that provides a capability to the host. It plugs into a hub (external or root). Mice, keyboards, cameras, scanners, and printers are examples.
This hardware arranges into a tiered-star topology where each star's center is a hub: both the root hub and external hubs sit at the center of their connected external hubs/functions. Figure 1 reveals this topology.
Figure 1. Three external hubs (Hub 1/Hub 2/Hub 3 or Hub 1/Hub 2/Hub 4) are chained together from the root hub
Configurations, Interfaces, and Endpoints
A function can have one or more configurations that control how the function behaves. Configurations can differ in how much power they consume, whether and how they remotely wake up a suspended computer, and more.
A trackball device that can be configured as a mouse or as a joystick is an example of a function with multiple configurations. Another example is an Integrated Services Digital Network (ISDN) communications device that offers one 128Kb channel or two 56Kb channels.
Each configuration contains one or more interfaces that specify how software accesses the hardware. Interfaces frequently have alternate settings corresponding to different bandwidth requirements.
An interface exposes endpoints that each serve as a data transfer source or a data transfer destination. Each endpoint supports data transfer in a single direction and has a unique numeric identifier. Figure 2 relates endpoints to interfaces and configurations.
Figure 2. The relationship between a function's configurations, interfaces, and endpoints
Note: Microsoft drivers tend to work with only the first configuration. As a result, multi-configuration functions are rare and are discouraged by Microsoft.
Data Flow Model
USB supports the flow of data between applications running on a host and physical devices attached to the USB. This data flow can be understood in terms of a data flow model--such as Figure 3's USB data flow model.
Figure 3. The USB data flow model divides into three layers
Actual data transfers take place in a physical device's lowest bus layer. This layer consists of the hubs and cables (and associated connectors) that connect the host controller to the physical device's bus interface.
Data physically flows vertically up and down both sides of the model. Data virtually flows horizontally across the corresponding sides of the upper two layers by way of pipes, logical channels that associate host software memory buffers with endpoints.
Pipes can be categorized as message pipes and stream pipes. Message pipes transfer data with some USB structure. In contrast, stream pipes transfer data with no USB structure.
The device layer has a logical view of and controls the physical device. It uses a special message pipe, the Default Control Pipe, to read configuration data from input endpoint 0 and output control data to output endpoint 0. These endpoints are always present.
The function layer has a function view of the physical device. This layer uses data pipes (stream pipes) to transfer data to and from the function.
Transfer Types
A pipe transfers data according to an endpoint's transfer type, which governs how much data transfers in a transaction (part of a data transfer), whether or not the transfer is lossless, and other factors:
Bulk transfers are used to transfer files and perform other kinds of transfers that involve large blocks of unstructured data. These transfers guarantee that all data will be delivered.
Control transfers are used to control the physical device. These transfers send and receive structured data in the form of commands and statuses.
Isochronous transfers are used to transfer realtime audio, video, and other kinds of unstructured data at a guaranteed speed. However, there is the possibility of data loss.
Interrupt transfers are used by pointing devices, keyboards, and any other physical devices that need a guaranteed quick response. Interrupt transfers are similar to bulk transfers.
Note: Although I don't focus on data transfer in this article, you need to be aware of these transfer types when working with the JSR 80 and jUSB APIs.
Descriptors
USB devices present onboard data structures known as descriptors. These data structures let a device's function (or functions) identify itself (or themselves) to software running on the host:
Device descriptors describe an entire device.
Device qualifier descriptors are used with USB 2.0 devices.
Configuration descriptors describe a device's configurations.
Other speed configuration descriptors are used with USB 2.0 devices.
Interface descriptors describe a configuration's interfaces.
Endpoint descriptors describe an interface's endpoints.
String descriptors contain human-readable Unicode strings that each describe a device, a configuration, an interface, or an endpoint.
Later in this article, I present a bus-enumeration example, whose C++ code accesses device and string descriptors during the enumeration process.
Device Classes
USB devices belong to device classes, which define an expected behavior in terms of device and interface descriptors. A device class's device driver can be used for any device belonging to the device class.
OSes usually provide generic drivers for all device classes, to support all USB devices. When a device attaches to the USB, its device class identifier is read from a specific descriptor and used to load the proper driver.
The USB-IF standardizes device class identifiers. Examples include 1 (audio class: a sound card), 3 (human interface device class, such as keyboards and mice), 8 (mass storage device class: flash drives and portable hard drives), and 9 (hub class). Identifiers 0 and 255 are reserved.
If the class belongs to the entire device, the identifier is assigned to the device descriptor's 8-bit bDeviceClass field. If the class is for a single interface, the identifier is assigned to the interface descriptor's 8-bit bInterfaceClass field (0 is assigned to bDeviceClass).