IBM Skip to main content
Search for:   within 
      Search help  
     IBM home  |  Products & services  |  Support & downloads   |  My account

developerWorks > Java technology | Linux
developerWorks
Access USB devices from Java applications
73 KBe-mail it!
Contents:
An introduction to USB
The jUSB API
The JSR-80 API (javax.usb)
Conclusion
Resources
About the author
Rate this article
Related content:
Java Media Framework basics
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
An introduction to USB, jUSB, and JSR-80

Level: Introductory

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 Buses
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:

  1. Bootstrap by getting the USB Host from the HostFactory.

  2. Access the USB Bus from the Host, then access the USB root hub (which is a USB Device) from the Bus.

  3. Obtain the number of USB ports available on the hub, and traverse through all the ports to find the appropriate Device.

  4. 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.

  5. 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:

  1. Bootstrap by getting the appropriate UsbServices from the UsbHostManager.

  2. Access the root hub through the UsbServices. The root hub is considered as a UsbHub in the application.

  3. Obtain a list of the UsbDevices that are connected to the root hub. Traverse through all the lower-level hubs to find the appropriate UsbDevice.

  4. 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.

  5. 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.

  6. 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.


73 KBe-mail it!

What do you think of this document?
Killer! (5) Good stuff (4) So-so; not bad (3) Needs work (2) Lame! (1)

Comments?



developerWorks > Java technology | Linux
developerWorks
  About IBM  |  Privacy  |  Terms of use  |  Contact