Describing Devices

Each USB device provides a hierarchy of descriptive information about the device and its capabilities. Each device has one or more configurations (though in practice most devices have just one configuration). Each configuration has one or more interfaces. Each interface has one or more endpoints, and each endpoint has exactly one pipe. Each level in this expanding hierarchy is represented by a different interface in the javax.usb package, which provides different information about the device. Figure 23-1 summarizes.

Figure 23-1. The device hierarchy

To get to the pipe that actually allows you to perform I/O, you need to start at the top and work your way down. That is, first you find the device. You ask the device to give you the active configuration, then you ask the configuration to give you the interfaces. Next, you choose an interface and ask it for its endpoints. Finally, you ask the endpoint for its pipe, and you then submit IRPs to this pipe to read or write data.

Thus, before writing software to talk to any particular device, youll need to know its configurations, interfaces, and endpoints. In an ideal world, this information would be provided in the devices technical documentation. However, the vast majority of devices don have any decent technical documentation, so its fortunate that the USB specification requires devices to tell you how they e organized when asked politely.

Some devices don correctly follow the USB specification. In particular, many devices that only suck power from the USB port, such as reading lamps and cell phone chargers, do not respond to any USB commands. Aside from their power drain, they e effectively invisible to the USB bus.

.4.1. UsbDevice

The UsbDevice interface sits at the top of the hierarchy. This interface declares three getter methods that query the attached devices for information about their serial numbers, manufacturers, and names:

public String getSerialNumberString( ) throws UsbException, UnsupportedEncodingException, UsbDisconnectedException public String getManufacturerString( ) throws UsbException, UnsupportedEncodingException, UsbDisconnectedException public String getProductString( ) throws UsbException, UnsupportedEncodingException, UsbDisconnectedException

All three methods may return null if the device does not provide the requested information. To get this information, the object needs to talk to the deviceit does not cache the information the first time it talks to the deviceso various exceptions can occur. A UsbDisconnectedException is thrown if the device you e querying has been removed from the bus. A UsbException is also thrown if the bus is having trouble. These methods are also declared to throw an UnsupportedEncodingException, which is a checked exception, so you have to handle it. However, this really shouldn happen. USB strings are little-endian UTF-16, and this encoding is supposed to be supported by all Java VMs.

Example 23-2 expands on Example 23-1 by using these three methods to print more information about each attached device.

Example 23-2. Enumerating attached USB devices

import java.io.UnsupportedEncodingException; import java.util.*; import javax.usb.*; public class PrettyUSBDeviceLister { public static void main(String[] args) throws UsbException, UnsupportedEncodingException { UsbServices services = UsbHostManager.getUsbServices( ); UsbHub root = services.getRootUsbHub( ); listDevices(root); } public static void listDevices(UsbHub hub) throws UnsupportedEncodingException, UsbException { List devices = hub.getAttachedUsbDevices( ); Iterator iterator = devices.iterator( ); while (iterator.hasNext( )) { UsbDevice device = (UsbDevice) iterator.next( ); System.out.println(device.getProductString( )); System.out.println(device.getSerialNumberString( )); System.out.println(device.getManufacturerString( )); System.out.println( ); if (device.isUsbHub( )) { listDevices((UsbHub) device); } } } }

Heres the output I got by running this program as root on my Linux workstation:

# java -cp jsr80-1.0.0.jar:.:jsr80_ri-1.0.0.jar PrettyUSBDeviceLister Silicon Integrated Systems [SiS] USB 1.0 Controller 0000:00:02.2 Linux 2.6.10-5-386 ohci_hcd USB-PS/2 Optical Mouse null Logitech Silicon Integrated Systems [SiS] USB 1.0 Controller (#2) 0000:00:02.3 Linux 2.6.10-5-386 ohci_hcd DMC-FZ5 null Panasonic

Now you can see that this computer has two USB hubs from Silicon Integrated Systems. A Logitech optical mouse with no serial number is attached to the first hub. A Panasonic DMC-FZ5 camera, also with no serial number, is plugged into the second hub. Interestingly, the manufacturer string for the controllers appears to be usurped by the native operating system (Linux, in this case) rather than coming from the devices themselves.

Devices may contain custom strings beyond these three standard strings. All the strings in a device are indexed by numbers from 0 to 255. (Few, if any, devices have all 256 entries in their tables, though.) You can enumerate the strings with the getString( ) method:

public String getString(byte index) throws UsbStallException, UsbException, UnsupportedEncodingException, UsbDisconnectedException

If you ask for a string index thats not in the table, getString( ) returns null. If you exceed the bounds of the table, getString( ) throws a UsbStallException, a subclass of UsbException that usually indicates that a device cannot perform the requested operation.

Example 23-3 is a program to dump the string tables of all currently attached devices.

Example 23-3. Listing all device strings

import java.io.UnsupportedEncodingException; import java.util.*; import javax.usb.*; public class USBStringLister { public static void main(String[] args) throws UsbException, UnsupportedEncodingException { UsbServices services = UsbHostManager.getUsbServices( ); UsbHub root = services.getRootUsbHub( ); listDevices(root); } public static void listDevices(UsbHub hub) throws UnsupportedEncodingException, UsbException { List devices = hub.getAttachedUsbDevices( ); Iterator iterator = devices.iterator( ); while (iterator.hasNext( )) { UsbDevice device = (UsbDevice) iterator.next( ); listStrings(device); if (device.isUsbHub( )) { listDevices((UsbHub) device); } } } public static void listStrings(UsbDevice device) throws UnsupportedEncodingException, UsbException { for (int i = 0; i <= 255; i++) { try { String s = device.getString((byte) i); System.out.println(" " + i + ": " + s); } catch (UsbStallException ex) { // Weve reached the end of the table for this device. break; } } System.out.println( ); } }

When run on my usual test system, this program found only the same strings previously exposed as the manufacturer, product, and serial number:

# java -cp jsr80_ri-1.0.0.jar:jsr80-1.0.0.jar:.:jsr80_linux-1.0.0.jar USBStringLister 0: null 1: 0000:00:02.2 2: Silicon Integrated Systems [SiS] USB 1.0 Controller 3: Linux 2.6.10-5-386 ohci_hcd 4: null 5: null ... 127: null 0: null 1: Logitech 2: USB-PS/2 Optical Mouse 0: null 1: 0000:00:02.3 2: Silicon Integrated Systems [SiS] USB 1.0 Controller (#2) 3: Linux 2.6.10-5-386 ohci_hcd 4: null 5: null ... 127: null

What else can we tell about the hubs and devices? First of all, there are several versions of USB: 1.0, 1.1, and 2.0. USB 1.x operates at a maximum speed of 12 Mbps. USB 2.0 can operate at up to 480 Mbps. However, not all USB 2.0 devices actually need to transfer that much data. For instance, 12 Mbps is more than enough for a mouse or a keyboard (though not nearly enough for a DVD burner).

USB devices operate at either 1.5 Mbps (low speed), 12 Mbps (full speed), or 480 Mbps (high speed, USB 2.0 only). Higher-speed devices can generally throttle back to lower speeds as necessary, but with a corresponding loss of performance. The getSpeed( ) method tells you the speed at which a given device operates:

public java.lang.Object getSpeed( )

The object it returns is one of these three constants:

The Java USB API only officially supports USB 1.1, so theres no UsbConst.DEVICE_SPEED_HIGH constant for 480-Mbps devices. Such USB 2.0 devices do work with the Java USB API, but theyll return UsbConst.DEVICE_SPEED_FULL as their speed.

The getParentUsbPort( ) method returns the port to which the device is connected on the hub:

public UsbPort getParentUsbPort( ) throws UsbDisconnectedException

The isUsbPort( ) method returns TRue if the device is a hub or false if it isn :

public boolean isUsbHub( )

.4.2. UsbDeviceDescriptor

For more detailed and technical information about a device, you can request its device descriptor:

public UsbDeviceDescriptor getUsbDeviceDescriptor( )

The UsbDeviceDescriptor interface is very closely tied to the USB hardware and tends to return information whose interpretation requires reading the USB specification (available at http://www.usb.org/developers/docs/). First, three methods get the indexes of the manufacturer, product, and serial number strings in the string table:

public byte iManufacturer( ) public byte iProduct( ) public byte iSerialNumber( )

The getSerialNumberString( ), getManufacturerString( ), and getProductString( ) methods in UsbDevice are convenience methods that basically do something like this:

public String getProductString( ) throws UsbException, UnsupportedEncodingexception { byte index = getUsbDeviceDescriptor().iProduct( ); return getString(index); }

These strings are meant for display to end users. Computers recognize devices by vendor ID and product ID:

public short idVendor( ) public short idProduct( )

The USB Implementers Forum assigns vendor IDs, after the payment of the appropriate four-figure fees. The vendors then choose the product IDs. For example, Hewlett-Packards assigned vendor ID is 0x03f0. The product ID for HPs DeskJet 895c printer is 0x0004. The vendor ID for the DeskJet 880c is the same, but its product ID is 0x0104. Some vendors don bother to change product IDs if two devices are similar enough to be supported by the same software and drivers. For example, the HP DeskJet 970c also has the product ID 0x104, same as the DeskJet 880c.

USB devices are organized into device classes. These classes are quite broad. For instance, the Human Interface Device class covers mice, keyboards, UPSs, and quite a bit more. What unifies the devices in a class is that they have similar data transfer requirements and can use the same basic drivers. For example, audio devices use isochronous transfers, and HID devices use control and interrupt transfers.

Classes are further subdivided into subclasses. Devices in a class or subclass speak a certain protocol. Each of these threeclass, subclass, and protocolis represented by an unsigned byte code. The next three methods return the devices USB class, subclass, and protocol codes:

public byte bDeviceClass( ) public byte bDeviceSubClass( ) public byte bDeviceProtocol( )

The USB Implementers Forum maintains a list of device class codes (currently available at http://www.usb.org/developers/defined_class/). Table 23-1 lists some common device class codes.

Table 23-1. USB device class codes

Class code

Meaning

0

Class information at the interface level

2

Communications devices: fax machines, phones, modems, etc.

9

USB hubs

220

Diagnostic devices

224

Wireless adapters

239

Miscellaneous

255

Vendor specific

Subclass and protocol codes depend on the class. For example, in class 224, a subclass of 1 indicates a radiofrequency controller and a protocol of 1 indicates that the controller uses Bluetooth. In class 224, a subclass of 1 indicates a reprogrammable diagnostic device, and in this subclass protocol 1 indicates USB2 diagnostics.

Not all devices have class codes at this level, though. For instance, HID and mass storage devices don . That is, the class code of an HID device such as a mouse or a mass storage device such as a hard drive is 0.

The bcdDevice( ) method returns the devices release number:

public short bcdDevice( )

The short returned is not really a short. Rather, its the two bytes of a binary-coded decimal (BCD) number. The high-order byte is a number between 0 and 255 that represents the major version. The low-order byte is divided into two nibbles. The top nibble represents the minor version and the low-order nibble represents the revision. For instance, Version 2.5.7 would be encoded as the byte 00000010 (2) followed by the nibble 0101 (5) followed by the nibble 0111 (7).

The bcdUSB( ) method returns the version of the USB specification the device adheres to, again encoded as a BCD:

public short bcdUSB( )

For example, Version 1.1 of the USB spec is a 1 byte, followed by a 1 nibble, followed by a 0 nibble; that is, 00000001 0001 0000 in binary or 272 in decimal. In other words, 1.1 is the same as 1.1.0. Version 2.0 is a 2 byte followed by a 0 byte; that is, 00000010 00000000 in binary or 512 in decimal.

The bMaxPacketSize0( ) method returns the maximum packet size for endpoint 0, the control channel endpoint:

public byte bMaxPacketSize0( )

This value should always be 8, 16, 32, or 64 because these are the only packet sizes the USB spec allows on the control channel.

The bNumConfigurations( ) method returns the number of different configurations the device has:

public byte bNumConfigurations( )

Its a very rare device that has more than one configuration.

Example 23-4 is a program that uses this interface to describe the devices attached to the USB controller. It also demonstrates how to decode binary-coded decimal strings.

Example 23-4. Listing all device strings

import java.io.UnsupportedEncodingException; import java.util.*; import javax.usb.*; public class USBDeviceDescriber { public static void main(String[] args) throws UsbException, UnsupportedEncodingException { UsbServices services = UsbHostManager.getUsbServices( ); UsbHub root = services.getRootUsbHub( ); listDevices(root); } public static void listDevices(UsbHub hub) throws UnsupportedEncodingException, UsbException { List devices = hub.getAttachedUsbDevices( ); Iterator iterator = devices.iterator( ); while (iterator.hasNext( )) { UsbDevice device = (UsbDevice) iterator.next( ); describe(device); if (device.isUsbHub( )) { listDevices((UsbHub) device); } } } public static void describe(UsbDevice device) throws UnsupportedEncodingException, UsbException { UsbDeviceDescriptor descriptor = device.getUsbDeviceDescriptor( ); byte manufacturerCode = descriptor.iManufacturer( ); System.out.println("Manufacturer index: " + manufacturerCode); System.out.println("Manufacturer string: " + device.getString(manufacturerCode)); byte productCode = descriptor.iProduct( ); System.out.println("Product index: " + productCode); System.out.println("Product string: " + device.getString(productCode)); byte serialCode = descriptor.iSerialNumber( ); System.out.println("Serial number index: " + serialCode); System.out.println("Serial Number string: " + device.getString(serialCode)); System.out.println("Vendor ID: 0x" + Integer.toHexString(descriptor.idVendor( ))); System.out.println("Product ID: 0x" + Integer.toHexString(descriptor.idProduct( ))); System.out.println("Class: " + descriptor.bDeviceClass( )); System.out.println("Subclass: " + descriptor.bDeviceSubClass( )); System.out.println("Protocol: " + descriptor.bDeviceProtocol( )); System.out.println("Device version: " + decodeBCD(descriptor.bcdDevice( ))); System.out.println("USB version: " + decodeBCD(descriptor.bcdUSB( ))); System.out.println("Maximum control packet size: " + descriptor.bMaxPacketSize0( )); System.out.println("Number of configurations: " + descriptor.bNumConfigurations( )); System.out.println( ); } public static String decodeBCD(short bcd) { int upper = (0xFF00 & bcd) >> 8; int middle = (0xF0 & bcd) >> 4; int lower = 0x0F & bcd; return upper + "." + middle + "." + lower; } }

For example, heres the description this program prints for my Lumix FZ-5 camera:

Manufacturer index: 1 Manufacturer string: Panasonic Product index: 2 Product string: DMC-FZ5 Serial number index: 0 Serial Number string: null Vendor ID: 0x4da Product ID: 0x2372 Class: 0 Subclass: 0 Protocol: 0 Device version: 0.1.0 USB version: 1.1.0 Maximum control packet size: 8 Number of configurations: 1

This device has one configuration, it supports USB 1.1, and the camera itself is only Version 0.1. Its class is 0, which means well have to look at the interface to figure out what class it really is.

.4.3. USB Configurations

Each USB device has at least one and possibly several configurations. The configuration specifies how much power the device uses, how many interfaces the device has, and whether the device draws power from the USB bus or has its own power supply. Since some devices have more than one possible configurationfor instance, both self powered and bus poweredthe UsbDevice interface has several methods to find out which configurations are available and determine which is currently active. In practice, though, most devices have only one configuration.

The getUsbConfigurations( ) method returns a list of all possible configurations for a given device:

public List getUsbConfigurations( )

The objects in this list are instances of the UsbConfiguration interface. There will be at least one.

Each configuration is identified by a byte value between 1 and 255. The getUsbConfiguration( ) method returns a specific configuration:

public UsbConfiguration getUsbConfiguration(byte number)

This method returns null if the device does not have the requested configuration. 0 stands for the unconfigured state.

The containsUsbConfiguration( ) method returns true if the device has the requested configuration or false if it doesn :

public boolean containsUsbConfiguration(byte number)

The getActiveUsbConfigurationNumber( ) returns the index of the devices current configuration:

public byte getActiveUsbConfigurationNumber( )

It returns 0 if the device is unconfigured.

The getActiveUsbConfiguration( ) method returns the current configuration:

public UsbConfiguration getActiveUsbConfiguration( )

It returns null if the device is unconfigured.

The isConfigured( ) method returns true if a device is configured or false if it isn :

public boolean isConfigured( )

In practice, Ive never seen an unconfigured device when working with Java. I suspect either the device autoconfigures itself, or the operating system does this before Java ever sees it.

Example 23-5 is an example method for enumerating all the configurations of a device, as well as identifying the active configuration and its number.

Example 23-5. Listing device configurations

public static void listConfigs(UsbDevice device) throws UsbDisconnectedException, UsbException { try { if (device.isConfigured( )) { System.out.println(device.getProductString( ) + " is configured."); System.out.println("The active configuration is " + device.getActiveUsbConfiguration( )); System.out.println("The active configuration is number " + device.getActiveUsbConfigurationNumber( )); } else { System.out.println(device.getProductString( ) + " is not configured."); } } catch (UnsupportedEncodingException ex) { throw new RuntimeException("This really shouldn happen"); } System.out.println("Available configurations include: "); List configs = device.getUsbConfigurations( ); Iterator iterator = configs.iterator( ); while (iterator.hasNext( )) { UsbConfiguration config = (UsbConfiguration) iterator.next( ); System.out.println(" " + config); } }

The typical output from this method looks like this:

USB-PS/2 Optical Mouse is configured. The active configuration is com.ibm.jusb.UsbConfigurationImp@1027b4d The active configuration is number 1 Available configurations include: com.ibm.jusb.UsbConfigurationImp@1027b4d

.4.4. UsbConfigurationDescriptor

You can find out more details about the configuration, such as whether it supports remote wakeup and whether or not the device is bus powered, using the UsbConfiguration and UsbConfigurationDescriptor interfaces. The getUsbConfigurationDescriptor( ) method in UsbConfiguration returns a UsbConfigurationDescriptor object:

public UsbConfigurationDescriptor getUsbConfigurationDescriptor( )

This interface has five getter methods that describe the device as it operates in that particular configuration. The simplest of these is bMaxPower( ):

public byte bMaxPower( )

This method returns an unsigned byte between 0 and 255. (b stands for byte.) That is, -1 is 255, -2 is 254, and so forth.

The javax.usb.util.UsbUtil class has a static unsignedInt( ) method that converts a signed byte in the range -128 to 127 to an unsigned int in the range 0 to 255.

This number is half the number of milliamps the device draws at maximum usage. In other words, convert the return value to an unsigned int and double it to get the number of milliamps the device draws. For example, given a UsbConfiguration object config, this code fragment prints the devices maximum power draw:

UsbConfigurationDescriptor descriptor = config.getUsbConfigurationDescriptor( ); byte power = descriptor.bMaxPower( ); int milliamps = 2 * UsbUtil.unsignedInt(power); System.out.println("Max power draw: " + milliamps + "mA");

It turns out my wired optical mouse can draw up to 98 milliamps (mA), and my global positioning system (GPS) receiver can draw a hefty 200 mA. On the other hand, USB devices are supposed to go to sleep after three milliseconds of inactivity, in which state they e not supposed to draw more than 500 microamps (half a milliamp) from the bus. Thus, bMaxPower( ) returns the maximum draw, not what a device pulls all the time. A bus can normally supply about half an amp, and a single device is allowed to draw up to the full 500 mA from the bus, if its available. However, buses have limits, especially on already low-powered devices such as PDAs and laptops running on battery power. In these situations, a much more conservative 100-mA maximum is a good idea. (My 200-mA GPS receiver is quite greedy.) Even at that level, a device may need its own battery or power cord if many devices are on the bus. In practice, quite a few devices exceed the power maximums (especially devices such as USB-powered lights and cell phone chargers that just suck power without actually doing anything else).

Anyway, enough hardware. What about the other methods? The bNumInterfaces( ) method returns the number of interfaces this configuration has. Most devices have one interface.

public byte bNumInterfaces( )

As with bMaxPower( ), this value should be interpreted as an unsigned byte between 0 and 255. (Honestly, this API is exposing far more of the hardware implementation details than it should. Just because the USB device returns an unsigned byte does not mean the API can or shouldn return an int.)

The bConfigurationValue( ) method returns the index of this configuration:

public byte bConfigurationValue( )

This is the value youd use to request this configuration by sending the appropriate control IRP to the device.

The iConfiguration( ) method returns the index of the string descriptor describing this configuration in the devices string table:

public byte iConfiguration( )

The bmAttributes( ) method returns a single byte containing two bit flags:

public byte bmAttributes( )

The fifth bit (counting from 0) is on if the device supports remote wakeup, and off if it doesn . (Remote wakeup means the device can wake up a suspended host if it has something to tell it.) The sixth bit is on if the device has its own power source (though it might still draw some power from the bus), and off if it doesn . The other six bits are not used in USB 2.0 and earlier.

.4.5. UsbInterface

Besides printing out information about configurations, the main thing youll want to do with a configuration is get the interfaces to the USB device. The getUsbInterfaces( ) method returns a list of all the interfaces for that configuration:

public List getUsbInterfaces( )

The objects in this list are instances of the UsbInterface interface. There will be at least one, and a multifunction device may have more than one. For example, a combination printer/scanner/copier could use one interface for each of those three tasks, all of which could be active simultaneously. However, most simple devices have exactly one interface per configuration.

Each USB interface is identified by a byte value between 0 and 255. The first interface will be 0, the second 1, the third 2, and so on. (All the devices I have handy start and stop with 0.) The getUsbInterface( ) method returns a specific interface:

public UsbConfiguration getUsbInterface(byte number)

This method returns null if the configuration does not have the requested interface.

The containsUsbInterface( ) method returns true if the configuration has the requested interface and false if it doesn :

public boolean containsUsbConfiguration(byte number)

The UsbInterface interface itself has methods to control the interface. Specifically, it has methods to:

.4.5.1. Settings

Each interface may have alternate settings, also represented as UsbInterface objects. Only one such setting for an interface is active at a time (though several different interfaces may be active simultaneously). The isActive( ) method returns TRue if this UsbInterface object is the active setting and false if it isn :

public boolean isActive( )

The getNumSettings( ) method tells you how many different settings the interface has. This will normally be one or two:

public int getNumSettings( )

Settings are numbered sequentially, starting at zero. Zero is the default setting. You can look up settings by their numbers using getSetting( ):

public UsbInterface getSetting(byte number)

This method returns null if there is no setting with the specified number. The containsSetting( ) method checks whether a numbered setting exists on this interface:

public boolean containsSetting(byte number)

The getSettings( ) method returns a list of all the settings for this interface:

public List getSettings( )

The objects in this list are UsbInterface objects.

Finally, you can request the active setting or its number specifically using these two methods:

public UsbInterface getActiveSetting( ) throws UsbNotActiveException public byte getActiveSettingNumber( ) throws UsbNotActiveException

This is important because only the active setting can be claimed and used for I/O operations. This method throws a UsbNotActiveException if the configuration to which this setting belongs is not active. That is, you can get an active setting only from an active configuration.

To change the active setting, you use the setInterface( ) method in the javax.usb.util.StandardRequest class.

.4.5.2. Claiming

Before writing to or reading from the active interface, it is necessary to claim it so that your program has exclusive access to it. This is similar to locking a file to prevent reading or writing while your program is working with it.

public void claim( ) throws UsbClaimException, UsbException, UsbNotActiveException, UsbDisconnectedException

If another program has already claimed this device, this method throws a UsbClaimException. In that case, the other program will need to release it before you can go any further. For example, you may be able to see the systems mouse or keyboard, but you probably won be able to control it unless you can convince the operating system to give it up.

In practice, this almost always happens. That is, the operating system grabs the device before Java has a chance to claim it and your program fails with a UsbClaimException. In this case, youll need to force the claim. I don know why this couldn be done with a simple Boolean, but it can . Instead, you need to supply an instance of the UsbInterfacePolicy interface that returns true from forceClaim( ). Its easiest to do this with an anonymous inner class:

theInterface.claim(new UsbInterfacePolicy( ) { public boolean forceClaim(UsbInterface usbInterface) { return true; } });

This step is not guaranteed to succeed, but it worked for me. If it fails theres not much else you can do, although you might try uninstalling or quitting any other running programs that try to grab the USB device before you see it.

The isClaimed( ) method checks whether a device is already claimed:

public boolean isClaimed( )

When you e done with a device, you should give up your claim with the release( ) method:

public void release( ) throws UsbClaimException, UsbException, UsbNotActiveException, UsbDisconnectedException

Youll need to close any pipes youve opened to the device before releasing it.

.4.6. UsbInterfaceDescriptor

The UsbInterfaceDescriptor interface offers more details about the interface. The getUsbInterfaceDescriptor( ) method in UsbInterface returns a UsbInterfaceDescriptor object:

public UsbInterfaceDescriptor getUsbInterfaceDescriptor( )

This interface has seven methods that describe the interface in that particular setting. Three of these return the numeric identifiers for the interfaces class, subclass, and protocol:

public byte bInterfaceClass( ) public byte bInterfaceSubClass( ) public byte bInterfaceProtocol

When the bDeviceClass is zero in the device descriptor (i.e., the value returned by bDeviceClass( ) in UsbDeviceDescriptor), you look at these three values to determine the devices class, subclass, and protocol.

The other information in this descriptor is rarely needed. bInterfaceNumber( ) returns this settings number amongst its alternate settings:

public byte bInterfaceNumber( )

bAlternateSetting( ) returns the number of this interfaces current alternate setting:

public byte bAlternateSetting( )

iInterface( ) returns the index of a string in the string table that provides a human-readable description of this interface:

public byte iInterface ( )

This method returns 0 if there is no such string.

bNumEndpoints( ) returns the number of endpoints this interface has, not counting endpoint 0:

public byte bNumEndpoints( )

.4.7. UsbEndpoints

Tired yet? Don worry. We e almost done. Interfaces have endpoints. Full- and high-speed devices can have up to 16 endpoints (8 in each direction). Low-speed devices have only two, one in each direction. Each endpoint is a memory buffer where incoming or outgoing data is put. For data flowing from the host to the device (input to the device, output from the host), the host places the data in the endpoint, and the devices microprocessor is interrupted to work on the data. When data is moving from the device to the host, the devices microprocessor puts the data into the endpoint and waits for the host to collect it.

The getUsbEndpoints( ) method in UsbInterface returns a list of all the non-control endpoints of that interface:

public List getUsbEndpoints( )

The objects in this list are instances of the UsbEndpoint interface. Some devices only have control endpoints; for these devices, this method returns an empty list.

Each non-control endpoint has an address between 1 and 255. These addresses do not necessarily start at 1, though. Some of my devices with only one or two non-control endpoints have endpoint addresses such as 129 and 130. The getUsbEndpoint( ) method returns a specific endpoint:

public UsbConfiguration getUsbEndPoint(byte number)

This method returns null if the configuration does not have the requested endpoint.

Endpoint 0 is reserved for the control channel. This is accessed through the UsbDevice and UsbControlIrp interfaces rather than UsbEndpoint and UsbPipe.

The containsUsbEndpoint( ) method returns true if the interface has the requested endpoint and false if it doesn :

public boolean containsUsbEndpoint(byte number)

The UsbEndpoint interface itself has methods to query the endpoint for its type (control, bulk, interrupt, or isochronous); direction (in or out); parent interface; and descriptor. The getUsbInterface( ) method returns the parent interface of this endpoint:

public UsbInterface getUsbInterface( )

The getdirection( ) method tells you whether data is going into the device through this endpoint or coming out of the device:

public byte getDirection( )

The return value should be either UsbConst.ENDPOINT_DIRECTION_IN or UsbConst.ENDPOINT_DIRECTION_OUT. This is from the perspective of the host. That is, in is into the host and out from the device, and out is out from the host and into the device.

The getType( ) method tells you whether the endpoint is for control, bulk, interrupt, or isochronous transfers:

public byte getType( )

The return value should be one of these four constants:

Finally, the getUsbEndpointDescriptor( ) method returns a UsbEndpointDescriptor for the endpoint:

public UsbEndpointDescriptor getUsbEndpointDescriptor( )

This interface has four getter methods to find out the maximum packet size for this endpoint, the actual endpoint address, the bmAttributes, and the bInterval:

package javax.usb; public interface UsbEndpointDescriptor { public byte bEndpointAddress( ); public byte bmAttributes( ); public short wMaxPacketSize( ); public byte bInterval( ); }

The bmAttributes determine whether this is a control, bulk, interrupt, or isochronous endpoint (though its easier to use getType( ) in UsbEndpoint). The bInterval is the amount of time that elapses between successive polls of the endpoint. It applies only to isochronous endpoints (for which it should always be 1) and interrupt endpoints. For high-speed bulk and control out endpoints, bInterval specifies the maximum NAK rate. NAK is the signal a device sends when it can accept a packet it previously told the host it would be able to accept. It has no particular meaning for low- and full-speed control and bulk endpoints or high-speed bulk and control in endpoints. The units vary depending on the device speed. For low-speed devices, this measures milliseconds. For full-speed devices, it measures eighths of a millisecond (125 microseconds). For high-speed devices, the measure is exponential rather than linear.

The code in Example 23-6 collects all the endpoints from a UsbInterface and prints various details about them.

Example 23-6. Enumerating device endpoints

public static void listEndpoints(UsbInterface theInterface) { List endpoints = theInterface.getUsbEndpoints( ); System.out.println(endpoints.size( ) + " endpoints"); Iterator iterator = endpoints.iterator( ); while (iterator.hasNext( )) { UsbEndpoint endpoint = (UsbEndpoint) iterator.next( ); listEndpointInfo(endpoint); } } public static void listEndpointInfo(UsbEndpoint endpoint) { int direction = endpoint.getDirection( ); int type = endpoint.getType( ); if (direction == UsbConst.ENDPOINT_DIRECTION_OUT) { System.out.println("Out endpoint"); } else { System.out.println("In endpoint"); } switch(type) { case UsbConst.ENDPOINT_TYPE_CONTROL: System.out.println("Control endpoint"); break; case UsbConst.ENDPOINT_TYPE_BULK: System.out.println("Bulk endpoint"); break; case UsbConst.ENDPOINT_TYPE_INTERRUPT: System.out.println("Interrupt endpoint"); break; case UsbConst.ENDPOINT_TYPE_ISOCHRONOUS: System.out.println("Isochronous endpoint"); break; default: System.out.println("Unrecognized type"); } UsbEndpointDescriptor descriptor = endpoint.getUsbEndpointDescriptor( ); System.out.println("Endpoint address: " + UsbUtil.unsignedInt(descriptor.bEndpointAddress( ))); System.out.println("Maximum packet size: " + UsbUtil.unsignedInt(descriptor.wMaxPacketSize( )) + " bytes"); // The meaning of bInterval depends on the speed of the device // and the type of the endpoint double interval = UsbUtil.unsignedInt(descriptor.bInterval( )); UsbDevice device = endpoint.getUsbInterface().getUsbConfiguration().getUsbDevice( ); boolean highSpeed = false; if (device.getSpeed( ) == UsbConst.DEVICE_SPEED_FULL) { interval = interval * 0.125; } else if (device.getSpeed( ) == UsbConst.DEVICE_SPEED_LOW) { interval = interval * 1; } else { // might be a high-speed device highSpeed = true; interval = Math.pow(2, interval-1) * 0.125; } if (type == UsbConst.ENDPOINT_TYPE_INTERRUPT || type == UsbConst.ENDPOINT_TYPE_ISOCHRONOUS) { System.out.println("Maximum latency: " + interval + "ms"); } else if (highSpeed && endpoint.getDirection( ) == UsbConst.ENDPOINT_DIRECTION_OUT) { System.out.println("Maximum NAK rate: " + interval + "ms"); } // bInterval means nothing for control endpoints }

Here are the results of using this on a Go!Temp probe, a typical interrupt-based device:

1 endpoints In endpoint Interrupt endpoint Endpoint address: 129 Maximum packet size: 8 bytes Maximum latency: 10.0ms

This device has a single interrupt endpoint for input. It also has two control endpoints, but these aren listed here.

And here are the results for my Panasonic Lumix camera, a typical mass storage device:

2 endpoints Out endpoint Bulk endpoint Endpoint address: 1 Maximum packet size: 64 bytes In endpoint Bulk endpoint Endpoint address: 130 Maximum packet size: 64 bytes

You can see that it has two endpoints, both bulk, one for input and one for output. It also has two control endpoints, but these aren listed here. They e managed at a lower level.

Категории

© amp.flylib.com,