|
|
|
Contents: |
|
|
|
Related content: |
|
|
|
Subscriptions: |
|
|
| An introduction to USB, jUSB, and JSR-80
Qingye
Jiang (mailto:qjiang@ieee.org?cc=&subject=Access
USB devices from Java applications) Research Scientist, HappyFox
Engineering Solutions 2 September 2003
The Java platform has traditionally
prided itself on its platform independence. While that independence has
many benefits, it makes the process of writing Java applications that
interact with hardware quite tricky. In this article, research scientist
Qingye Jiang examines two projects that are making the process easier by
providing APIs through which Java applications can make use of USB
devices. While both projects are still in embryo form, both show promise
and are already serving as the foundations of some real-world
applications.
The first version of the Universal Serial Bus (USB) specification was
released in January 1996. Because of its low cost, high data-transfer
rate, ease of use, and flexibility, USB has gained wide acceptance in the
computer industry. Today, many peripherals and devices connect to
computers through USB interfaces. Currently, most general-purpose
operating systems provide support for USB devices, and it is relatively
easy to develop applications in C or C++ that access such peripherals.
However, the Java programming language by design provides very little
support for hardware access, so writing Java applications that interact
with USB devices has proved quite difficult.
Efforts to provide access to USB devices in the Java language were
initiated in 1999 by Dan Streetman at IBM. In 2001, his project was
accepted as a candidate extended standard of the Java language through the
Java Specification Request (JSR) process. The project is now called JSR-80
and has been officially assigned the Java package javax.usb .
Meanwhile, in June 2000, Mojo Jojo and David Brownell started the jUSB
project at SourceForge. Both of these projects have since produced usable
packages for Linux developers, although neither is close to perfect. Both
projects also have begun attempts to provide access to USB devices for
Java applications on other operating systems, though usable packages have
not yet emerged from either. (See Resources
for references to these and other projects discussed in this article.)
In this article, you'll get a brief introduction to the jUSB and JSR-80
projects; first, however, we'll take a look at the nuts and bolts of the
USB protocol, so that you can understand how both of those projects
interact with USB devices. We'll also offer code snippets to show how
you'd use both projects' APIs to access USB devices.
An introduction to USB In
1994, an alliance of four industrial partners (Compaq, Intel, Microsoft,
and NEC) started specifying the USB protocol. The original goal of the
protocol was to connect the PC to the telephone and to provide I/O
interfaces that were easy to expand and reconfigure. In January 1996, the
first version of the USB specification was released, and a subsequent
revision (version 1.1) was released in September 1998. The specification
allowed 127 devices to be connected together at the same time, with the
total communication bandwidth limited to 12 Mbps. Later on, three more
members (Hewlett-Packard, Lucent, and Philips) joined the alliance. In
April 2000, version 2.0 of the USB specification, which supports transfer
rates up to 480 Mbps, was released. Today, USB plays a key role in
high-speed (video, imaging, storage) and full-speed (audio, broadband,
microphone) data-transfer applications. It also connects a variety of
low-speed devices (keyboards, mice, game peripherals, virtual reality
peripherals) to the PC.
The USB protocol is strictly hierarchical. In any USB system there is
only a single host, and the USB interface to the host computer is referred
to as the host controller. There are two standards for host
controllers -- the Open Host Controller Interface (OHCI, by Compaq) and
the Universal Host Controller Interface (UHCI, by Intel). Both standards
provide the same capabilities and work with all USB devices; the hardware
implementation of a UHCI is simpler, but requires a more complex device
driver (and thus puts more load onto the CPU).
The USB physical interconnect is a tiered star topology, with up to
seven tiers. A hub is at the center of each star, and the USB host is
considered the root hub. Each wired segment is a point-to-point connection
between a hub and USB device; the latter can be either another hub that
provides additional attachment points to the system, or a device of some
sort that provides functional capabilities. The host uses a
master/subordinate protocol to communicate with the USB devices. This
approach solves the problem of packet collision but also prevents the
attached devices from establishing direct communication with each
other.
All the data transfers are initiated by the host controller. Data
directed from the host to a device is called downstream or
out transfer; data directed from a device to the host is called
upstream or in transfer. Data transfer occurs between the
host and a particular endpoint on the USB device, and the data link
between the host and the endpoint is called a pipe. A given USB
device may have many endpoints, and the number of data pipes between the
host and the device is the same as the number of endpoints on the device.
A pipe may be uni-directional or bi-directional, and the data flow in one
pipe is independent of the data flow in any other pipes.
Communication on the USB network can use any one of four different data
transfer types:
- Control transfers: These are short data packets for device
control and configuration, particularly at attach time.
- Bulk transfers: These are data packets in relatively large
quantities. Devices like scanners or SCSI adapters use this transfer
type.
- Interrupt transfers: These are data packets that are polled
periodically. The host controller will automatically post an interrupt
at a specified interval.
- Isochronous transfers: These are data streams in real time
with higher requirements for bandwidth than for reliability. Audio and
video devices generally use this transfer type.
Like a serial port, each USB port on a computer is assigned a unique
identification number (port ID) by the USB controller. When a USB device
is attached to a USB port, this unique port ID is assigned to the device
and the device descriptor is read by the USB controller The device
descriptor includes information that applies globally to the device, as
well as information on the configuration of the device. A
configuration defines the functionality and I/O behavior of a USB device.
A USB device may have one or more configurations, which are described by
their corresponding configuration descriptors. Each configuration has one
or more interfaces, which can be considered as a physical
communication channel; each interface has zero or more endpoints, which
can be either data providers or data consumers, or both. Interfaces are
described by interface descriptors, and endpoints are described by
end-point descriptors. Furthermore, a USB device might also have string
descriptors to provide additional information such as vendor name, device
name, or serial numbers.
As you can see, a protocol like USB offers challenges to developers who
use the Java language, which strives for platform- and
hardware-independence. Let's now take a look at two projects that have
tried to bridge the gap.
The jUSB API The jUSB
project was created by Mojo Jojo and David Brownell in June 2000. Its
objective was to provide a set of free software Java APIs to access USB
devices on Linux platforms. The API is distributed under the Lesser GPL
(LGPL), which means that you can use it in proprietary as well as free
software projects. The API provides multithreaded access to multiple
physical USB devices, and supports both native and remote devices. Devices
with multiple interfaces can be accessed by multiple applications (or
device drivers) simultaneously, with each application (or device driver)
claiming a different interface. The API supports control transfers, bulk
transfers, and interrupt transfers; isochronous transfers are not
supported because these are used for media data (such as audio and video)
that are already well supported by the JMF API (see Resources)
over other standardized device drivers. Currently, the API works on
GNU/Linux distributions with either the Linux 2.4 kernel or a back port
into 2.2.18 kernel. Thus, most recent distributions are supported; for
example, the API works on Red Hat 7.2 and 9.0 without any patches or other
upgrades.
The jUSB API includes the following packages:
usb.core : This package is the core part of the jUSB
API. It allows Java applications to access USB devices from USB hosts.
usb.linux : This package contains a Linux implementation
of a usb.core.Host object, bootstrapping support, and other
classes leveraging Linux USB support. This implementation accesses the
USB devices through the virtual USB device file system
(usbdevfs ).
usb.windows : This package has a Windows implementation
of a usb.core.Host object, bootstrapping support, and other
classes leveraging Windows USB support. This implementation is still in
its very early stage.
usb.remote : This package is a remote version of the
usb.core API. It includes an RMI proxy and a daemon
application, which allow Java applications to access USB devices on a
remote computer.
usb.util : This package provides some useful utilities
to download firmware to USB devices, dump the content of the USB system
into XML, and convert a USB device with only bulk I/O into a socket.
usb.devices : This optional package collects Java code
to access a variety of USB devices with the jUSB API, including Kodak
digital cameras and Rio 500 MP3 Players. These APIs were specially
written to simplify the process of accessing the designated USB devices
and cannot be used to access other devices. The APIs were built upon the
usb.core APIs, and they will work on any operating system
where jUSB is supported.
usb.view : This optional package provides a simple USB
tree browser based on Swing. It is a very good example program
illustrating the use of the jUSB API.
Although the implementation of the usb.core.Host object
varies from operating system to operating system, a Java programmer needs
to understand only the usb.core package to start developing
applications with the jUSB APIs. Table 1 outlines the interfaces and
classes from usb.core with which a Java programmer should be
familiar:
Table 1. Interfaces and classes in jUSB
Interface |
Description |
Bus |
Connects a set of USB devices to a Host |
Host |
Represents a USB controller with one or more
Bus es |
Class |
Description |
Configuration |
Provides access to a USB configuration supported by a device and
to the interfaces associated with that configuration |
Descriptor |
Base class for entities with USB typed descriptors |
Device |
Provides access to a USB device |
DeviceDescriptor |
Provides access to a USB device descriptor |
EndPoint |
Provides access to a USB end-point descriptor, structuring
device data input or output in a given device configuration |
HostFactory |
Contains bootstrapping methods |
Hub |
Provides access to a USB hub descriptor and some hub
operations |
Interface |
Describes sets of endpoints, and is associated with a particular
device configuration |
PortIdentifier |
Provides stable string identifiers for USB devices, appropriate
for use in operations and troubleshooting |
The normal procedure to access a USB device with the jUSB API is as
follows:
- Bootstrap by getting the USB
Host from the
HostFactory .
- Access the USB
Bus from the Host , then
access the USB root hub (which is a USB Device ) from the
Bus .
- Obtain the number of USB ports available on the hub, and traverse
through all the ports to find the appropriate
Device .
- Access the USB
Device that is attached to a particular
port. A Device can be accessed directly from the
Host with its PortIdentifier , or can be found
by traversing the USB Bus starting from the root hub.
- Interact with the
Device directly with
ControlMessage , or claim an Interface from the
current Configuration of the Device and
perform I/O with the Endpoint available on the
Interface .
Listing 1 illustrates how to obtain the content of a USB system with
the jUSB API. The program as written simply looks at the root hub for
available USB devices, but it would be easy to improve it to traverse the
whole USB tree. The logic here corresponds to steps 1 through 4 above.
Listing 1. Obtaining the content of a USB system
with the jUSB API
import usb.core.*;
public class ListUSB
{
public static void main(String[] args)
{
try
{
// Bootstrap by getting the USB Host from the HostFactory.
Host host = HostFactory.getHost();
// Obtain a list of the USB buses available on the Host.
Bus[] bus = host.getBusses();
int total_bus = bus.length;
// Traverse through all the USB buses.
for (int i=0; i<total_bus; i++)
{
// Access the root hub on the USB bus and obtain the
// number of USB ports available on the root hub.
Device root = bus[i].getRootHub();
int total_port = root.getNumPorts();
// Traverse through all the USB ports available on the
// root hub. It should be mentioned that the numbering
// starts from 1, not 0.
for (int j=1; j<=total_port; j++)
{
// Obtain the Device connected to the port.
Device device = root.getChild(j);
if (device != null)
{
// USB device available, do something here.
}
}
}
} catch (Exception e)
{
System.out.println(e.getMessage());
}
}
|
Listing 2 illustrates how to perform bulk I/O with
Interface and EndPoint , assuming that the
application has successfully located the Device . This code
snippet can also be modified to perform control or interrupt I/O. It
corresponds to step 5 above. Listing 2. Performing
bulk I/O with the jUSB API
if (device != null)
{
// Obtain the current Configuration of the device and the number of
// Interfaces available under the current Configuration.
Configuration config = device.getConfiguration();
int total_interface = config.getNumInterfaces();
// Traverse through the Interfaces
for (int k=0; k<total_interface; k++)
{
// Access the currently Interface and obtain the number of
// endpoints available on the Interface.
Interface itf = config.getInterface(k, 0);
int total_ep = itf.getNumEndpoints();
// Traverse through all the endpoints.
for (int l=0; l<total_ep; l++)
{
// Access the endpoint, and obtain its I/O type.
Endpoint ep = itf.getEndpoint(l);
String io_type = ep.getType();
boolean input = ep.isInput();
// If the endpoint is an input endpoint, obtain its
// InputStream and read in data.
if (input)
{
InputStream in;
in = ep.getInputStream();
// Read in data here
in.close();
}
// If the Endpoint is and output Endpoint, obtain its
// OutputStream and write out data.
else
{
OutputStream out;
out = ep.getOutputStream();
// Write out data here.
out.close();
}
}
}
}
|
The jUSB project was very active from June 2000 to February 2001. The
most recent release of the API, version 0.4.4, was made available on
February 14, 2001. Only some minor progress has been reported since that
time, probably due to the success of the IBM group in becoming a candidate
extended standard of the Java language. However, several third-party
applications have been developed based on jUSB, including the JPhoto
project (an application using jUSB to connect to digital cameras) and the
jSyncManager project (an application using jUSB to synchronize with a Palm
OS-based PDA).
The JSR-80 API
(javax.usb) As noted earlier, the JSR-80 project was created
by Dan Streetman at IBM in 1999. In 2001, the project was accepted as a
candidate extended standard of the Java language through the Java
Specification Request (JSR) process. The project is now called JSR-80 and
has been officially assigned the Java package javax.usb . The
project is licensed under the Common Public License and is developed using
the Java Community Process. The objective of this project is to develop a
USB interface for the Java platform that will allow full access to the USB
system for any Java application or middleware component. The JSR-80 API
provides full support for all four transfer types defined by the USB
specification. Currently, the Linux implementation of the API works on
most recent GNU/Linux distributions with 2.4 kernel support, such as Red
Hat 7.2 and 9.0.
The JSR-80 project includes three packages: javax-usb (the
javax.usb API), javax-usb-ri (the common part of
the OS-independent reference implementation), and
javax-usb-ri-linux (the reference implementation for the
Linux platform, which connects the common reference implementation to the
Linux USB stack). All three parts are required to form a complete
functioning java.usb API on the Linux platform. Independent
efforts aimed at porting the API to other operating systems (primarily
Microsoft Windows) have been reported on the project e-mail list, but no
functioning packages have been released yet.
Although the OS-dependent implementation of the JSR-80 APIs varies from
operating system to operating system, a Java programmer needs to
understand only the javax.usb package to start developing
applications. Table 2 lists the interfaces and classes in
javax.usb with which a Java programmer should be
familiar:
Table 2. Interfaces and classes in the JSR-80 APIs
Interface |
Description |
UsbConfiguration |
Represents a configuration of a USB device |
UsbConfigurationDescriptor |
Interface for a USB configuration descriptor |
UsbDevice |
Interface for a USB device |
UsbDeviceDescriptor |
Interface for a USB device descriptor |
UsbEndpoint |
Interface for a USB endpoint |
UsbEndpointDescriptor |
Interface for a USB endpoint descriptor |
UsbHub |
Interface for a USB hub |
UsbInterface |
Interface for a USB interface |
UsbInterfaceDescriptor |
Interface for a USB interface descriptor |
UsbPipe |
Interface for a USB pipe |
UsbPort |
Interface for a USB port |
UsbServices |
Interface for a javax.usb implementation |
Class |
Description |
UsbHostManager |
Entry point for javax.usb |
The normal procedure for accessing a USB device with the JSR-80 API is
as follows:
- Bootstrap by getting the appropriate
UsbServices from
the UsbHostManager .
- Access the root hub through the
UsbServices . The root
hub is considered as a UsbHub in the application.
- Obtain a list of the
UsbDevice s that are connected to
the root hub. Traverse through all the lower-level hubs to find the
appropriate UsbDevice .
- Interact with the
UsbDevice directly with a control
message (UsbControlIrp ), or claim a
UsbInterface from the appropriate
UsbConfiguration of the UsbDevice and perform
I/O with the UsbEndpoint available on the
UsbInterface .
- If a
UsbEndpoint is used to perform I/O, open the
UsbPipe associated with it. Both upstream data (from the
USB device to the host computer) and downstream data (from the host
computer to the USB device) can be submitted either synchronously or
asynchronously through the UsbPipe .
- Close the
UsbPipe and release the appropriate
UsbInterface when the application no longer needs access to
the UsbDevice .
In Listing 3, we obtain the content of the USB system with the JSR-80
API. The program recursively traverses through all the USB hubs on the USB
system and locates all the USB devices connected to the host computer. The
code corresponds to steps 1 through 3 above. Listing 3. Obtaining the content of the USB system with
the JSR-80 API
import javax.usb.*;
import java.util.List;
public class TraverseUSB
{
public static void main(String argv[])
{
try
{
// Access the system USB services, and access to the root
// hub. Then traverse through the root hub.
UsbServices services = UsbHostManager.getUsbServices();
UsbHub rootHub = services.getRootUsbHub();
traverse(rootHub);
} catch (Exception e) {}
}
public static void traverse(UsbDevice device)
{
if (device.isUsbHub())
{
// This is a USB Hub, traverse through the hub.
List attachedDevices =
((UsbHub) device).getAttachedUsbDevices();
for (int i=0; i<attachedDevices.size(); i++)
{
traverse((UsbDevice) attachedDevices.get(i));
}
}
else
{
// This is a USB function, not a hub.
// Do something.
}
}
}
|
Listing 4 illustrates how to perform I/O with Interface
and EndPoint , assuming that the application has successfully
located a Device . This code snippet can also be modified to
perform I/O of all four data transfer types. It corresponds to steps 4
through 6 above. Listing 4. Performing I/O with
the JSR-80 API
public static void testIO(UsbDevice device)
{
try
{
// Access to the active configuration of the USB device, obtain
// all the interfaces available in that configuration.
UsbConfiguration config = device.getActiveUsbConfiguration();
List totalInterfaces = config.getUsbInterfaces();
// Traverse through all the interfaces, and access the endpoints
// available to that interface for I/O.
for (int i=0; i<totalInterfaces.size(); i++)
{
UsbInterface interf = (UsbInterface) totalInterfaces.get(i);
interf.claim();
List totalEndpoints = interf.getUsbEndpoints();
for (int j=0; j<totalEndpoints.size(); j++)
{
// Access the particular endpoint, determine the direction
// of its data flow, and type of data transfer, and open the
// data pipe for I/O.
UsbEndpoint ep = (UsbEndpoint) totalEndpoints.get(i);
int direction = ep.getDirection();
int type = ep.getType();
UsbPipe pipe = ep.getUsbPipe();
pipe.open();
// Perform I/O through the USB pipe here.
pipe.close();
}
interf.release();
}
} catch (Exception e) {}
}
|
The JSR-80 project has been very active from its very beginning.
Version 0.10.0 of the javax.usb API, RI, and RI for Linux
were released in February 2003. It is likely that this version will be
submitted to the JSR-80 committee for final approval. It is expected that
implementations for other operating systems will soon be available after
JSR-80 formally becomes an extended standard of the Java language. The
Linux developer community seems to show more interest in the JSR-80
project than the jUSB project, and there have been an increasing number of
projects launched using the javax.usb API on the Linux
platform.
Conclusion Both the jUSB
API and the JSR-80 API provide Java applications with the capability to
access USB devices from a machine running the Linux operating system. The
JSR-80 API provides more functionality than the jUSB API, and has the
potential of becoming an extended standard of the Java language.
Currently, only Linux developers can take advantage of the jUSB and JSR-80
APIs. However, active efforts to port both APIs to other operating systems
have been reported. Java developers should be able to access USB devices
on other operating systems in the near future. By familiarizing yourself
with these APIs now, you can be ready to add USB functionality to your
applications when these projects are ready for prime time on multiple
platforms.
Resources
About the
author Qingye Jiang is a research scientist at HappyFox
Engineering Solutions. He received his B.Eng. from Tsinghua
University in 1999 and his M.S. from the University of Illinois at
Urbana-Champaign in 2000. His research interests include J2ME,
distributed computing, mobile computing, embedded systems, and
wireless communication. Contact Qingye at qjiang@ieee.org.
|
|
|