![JAR search and dependency download from the Maven repository](/logo.png)
org.usb4java.javax.AbstractDevice Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of usb4java-javax Show documentation
Show all versions of usb4java-javax Show documentation
Extension for usb4java which implements javax.usb (JSR-80).
/*
* Copyright (C) 2013 Klaus Reimer
* See LICENSE.md for licensing information.
*/
package org.usb4java.javax;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.usb.UsbClaimException;
import javax.usb.UsbConst;
import javax.usb.UsbControlIrp;
import javax.usb.UsbDevice;
import javax.usb.UsbDeviceDescriptor;
import javax.usb.UsbDisconnectedException;
import javax.usb.UsbException;
import javax.usb.UsbPlatformException;
import javax.usb.UsbPort;
import javax.usb.UsbStringDescriptor;
import javax.usb.event.UsbDeviceEvent;
import javax.usb.event.UsbDeviceListener;
import javax.usb.util.DefaultUsbControlIrp;
import org.usb4java.ConfigDescriptor;
import org.usb4java.Device;
import org.usb4java.DeviceHandle;
import org.usb4java.LibUsb;
import org.usb4java.javax.descriptors.SimpleUsbStringDescriptor;
/**
* A Usb device.
*
* @author Klaus Reimer ([email protected])
*/
abstract class AbstractDevice implements UsbDevice
{
/** The USB device manager. */
private final DeviceManager manager;
/** The device id. */
private final DeviceId id;
/** The parent id. Null if no parent exists. */
private final DeviceId parentId;
/** The device speed. */
private final int speed;
/** The device configurations. */
private List configurations;
/** Mapping from configuration value to configuration. */
private Map configMapping =
new HashMap();
/** The USB device listener list. */
private final DeviceListenerList listeners = new DeviceListenerList();
/** The device handle. Null if not open. */
private DeviceHandle handle;
/** The number of the currently active configuration. */
private byte activeConfigurationNumber = 0;
/** The numbers of the currently claimed interface. */
private Set claimedInterfaceNumbers = new HashSet();
/** The port this device is connected to. */
private UsbPort port;
/** The IRP queue. */
private final ControlIrpQueue queue = new ControlIrpQueue(this,
this.listeners);
/** If kernel driver was detached when interface was claimed. */
private boolean detachedKernelDriver;
/**
* Constructs a new device.
*
* @param manager
* The USB device manager which is responsible for this device.
* @param id
* The device id. Must not be null.
* @param parentId
* The parent device id. May be null if this device has no parent
* (Because it is a root device).
* @param speed
* The device speed.
* @param device
* The libusb device. This reference is only valid during the
* constructor execution, so don't store it in a property or
* something like that.
* @throws UsbPlatformException
* When device configuration could not be read.
*/
AbstractDevice(final DeviceManager manager, final DeviceId id,
final DeviceId parentId, final int speed, final Device device)
throws UsbPlatformException
{
if (manager == null)
throw new IllegalArgumentException("manager must be set");
if (id == null) throw new IllegalArgumentException("id must be set");
this.manager = manager;
this.id = id;
this.parentId = parentId;
this.speed = speed;
// Read device configurations
final int numConfigurations =
id.getDeviceDescriptor().bNumConfigurations() & 0xff;
final List configurations =
new ArrayList(numConfigurations);
for (int i = 0; i < numConfigurations; i += 1)
{
final ConfigDescriptor configDescriptor = new ConfigDescriptor();
final int result = LibUsb.getConfigDescriptor(device, (byte) i,
configDescriptor);
if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Unable to get configuation " + i + " for device " + id,
result);
}
try
{
final Configuration config = new Configuration(
this, configDescriptor);
configurations.add(config);
this.configMapping.put(configDescriptor.bConfigurationValue(),
config);
}
finally
{
LibUsb.freeConfigDescriptor(configDescriptor);
}
}
this.configurations = Collections.unmodifiableList(configurations);
// Determine the active configuration number
final ConfigDescriptor configDescriptor = new ConfigDescriptor();
final int result =
LibUsb.getActiveConfigDescriptor(device, configDescriptor);
// ERROR_NOT_FOUND is returned when device is in unconfigured state.
// On OSX it may return INVALID_PARAM in this case because of a bug
// in libusb
if (result == LibUsb.ERROR_NOT_FOUND ||
result == LibUsb.ERROR_INVALID_PARAM)
{
this.activeConfigurationNumber = 0;
}
else if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Unable to read active config descriptor from device " + id,
result);
}
else
{
this.activeConfigurationNumber =
configDescriptor.bConfigurationValue();
LibUsb.freeConfigDescriptor(configDescriptor);
}
}
/**
* Returns the device id.
*
* @return The device id.
*/
public final DeviceId getId()
{
return this.id;
}
/**
* Returns the parent device id.
*
* @return The parent device id or null of there is no parent.
*/
public final DeviceId getParentId()
{
return this.parentId;
}
/**
* Ensures the device is connected.
*
* @throws UsbDisconnectedException
* When device is disconnected.
*/
final void checkConnected()
{
if (this.port == null) throw new UsbDisconnectedException();
}
/**
* Opens the USB device and returns the USB device handle. If device was
* already open then the old handle is returned.
*
* @return The USB device handle.
* @throws UsbException
* When USB device could not be opened.
*/
public final DeviceHandle open() throws UsbException
{
if (this.handle == null)
{
final Device device = this.manager.getLibUsbDevice(this.id);
try
{
final DeviceHandle handle = new DeviceHandle();
final int result = LibUsb.open(device, handle);
if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Can't open device " + this.id, result);
}
this.handle = handle;
}
finally
{
this.manager.releaseDevice(device);
}
}
return this.handle;
}
/**
* Closes the device. If device is not open then nothing is done.
*/
public final void close()
{
if (this.handle != null)
{
LibUsb.close(this.handle);
this.handle = null;
}
}
@Override
public final UsbPort getParentUsbPort()
{
checkConnected();
return this.port;
}
/**
* Sets the parent USB port. If port is unset then a usbDeviceDetached event
* is send.
*
* @param port
* The port to set. Null to unset.
*/
final void setParentUsbPort(final UsbPort port)
{
if (this.port == null && port == null)
throw new IllegalStateException("Device already detached");
if (this.port != null && port != null)
throw new IllegalStateException("Device already attached");
// Disconnect client devices
if (port == null && isUsbHub())
{
final Hub hub = (Hub) this;
for (final AbstractDevice device: hub.getAttachedUsbDevices())
{
hub.disconnectUsbDevice(device);
}
}
this.port = port;
final Services services = Services.getInstance();
if (port == null)
{
this.listeners.usbDeviceDetached(new UsbDeviceEvent(this));
services.usbDeviceDetached(this);
}
else
{
services.usbDeviceAttached(this);
}
}
@Override
public final String getManufacturerString() throws UsbException,
UnsupportedEncodingException
{
checkConnected();
final byte index = getUsbDeviceDescriptor().iManufacturer();
if (index == 0) return null;
return getString(index);
}
@Override
public final String getSerialNumberString() throws UsbException,
UnsupportedEncodingException
{
checkConnected();
final byte index = getUsbDeviceDescriptor().iSerialNumber();
if (index == 0) return null;
return getString(index);
}
@Override
public final String getProductString() throws UsbException,
UnsupportedEncodingException
{
checkConnected();
final byte index = getUsbDeviceDescriptor().iProduct();
if (index == 0) return null;
return getString(index);
}
@Override
public final Object getSpeed()
{
switch (this.speed)
{
case LibUsb.SPEED_FULL:
return UsbConst.DEVICE_SPEED_FULL;
case LibUsb.SPEED_LOW:
return UsbConst.DEVICE_SPEED_LOW;
default:
return UsbConst.DEVICE_SPEED_UNKNOWN;
}
}
@Override
public final List getUsbConfigurations()
{
return this.configurations;
}
@Override
public final Configuration getUsbConfiguration(final byte number)
{
return this.configMapping.get(number);
}
@Override
public final boolean containsUsbConfiguration(final byte number)
{
return this.configMapping.containsKey(number);
}
@Override
public final byte getActiveUsbConfigurationNumber()
{
return this.activeConfigurationNumber;
}
/**
* Sets the active USB configuration.
*
* @param number
* The number of the USB configuration to activate.
* @throws UsbException
* When configuration could not be activated.
*/
final void setActiveUsbConfigurationNumber(final byte number)
throws UsbException
{
if (number != this.activeConfigurationNumber)
{
if (!this.claimedInterfaceNumbers.isEmpty())
throw new UsbException("Can't change configuration while an "
+ "interface is still claimed");
final int result = LibUsb.setConfiguration(open(), number & 0xff);
if (result < 0)
throw ExceptionUtils.createPlatformException(
"Unable to set configuration", result);
this.activeConfigurationNumber = number;
}
}
/**
* Claims the specified interface.
*
* @param number
* The number of the interface to claim.
* @param force
* If claim should be forces if possible.
* @throws UsbException
* When interface could not be claimed.
*/
final void claimInterface(final byte number, final boolean force)
throws UsbException
{
if (this.claimedInterfaceNumbers.contains(number))
throw new UsbClaimException("An interface is already claimed");
final DeviceHandle handle = open();
// Detach existing driver from the device if requested and
// libusb supports it.
if (force)
{
int result = LibUsb.kernelDriverActive(handle, number);
if (result == LibUsb.ERROR_NO_DEVICE)
throw new UsbDisconnectedException();
if (result == 1)
{
result = LibUsb.detachKernelDriver(handle, number);
if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Unable to detach kernel driver", result);
}
this.detachedKernelDriver = true;
}
}
final int result = LibUsb.claimInterface(handle, number & 0xff);
if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Unable to claim interface", result);
}
this.claimedInterfaceNumbers.add(number);
}
/**
* Releases a claimed interface.
*
* @param number
* The number of the interface to release.
* @throws UsbException
* When interface could not be claimed.
*/
final void releaseInterface(final byte number) throws UsbException
{
if (this.claimedInterfaceNumbers.isEmpty())
throw new UsbClaimException("No interface is claimed");
if (!this.claimedInterfaceNumbers.contains(number))
throw new UsbClaimException("Interface not claimed");
final DeviceHandle handle = open();
int result = LibUsb.releaseInterface(handle, number & 0xff);
if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Unable to release interface", result);
}
if (this.detachedKernelDriver)
{
result = LibUsb.attachKernelDriver(handle, number & 0xff);
if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Uanble to re-attach kernel driver", result);
}
}
this.claimedInterfaceNumbers.remove(number);
}
/**
* Checks if the specified interface is claimed.
*
* @param number
* The number of the interface to check.
* @return True if interface is claimed, false if not.
*/
final boolean isInterfaceClaimed(final byte number)
{
return this.claimedInterfaceNumbers.contains(number);
}
@Override
public final Configuration getActiveUsbConfiguration()
{
return getUsbConfiguration(getActiveUsbConfigurationNumber());
}
@Override
public final boolean isConfigured()
{
return getActiveUsbConfigurationNumber() != 0;
}
@Override
public final UsbDeviceDescriptor getUsbDeviceDescriptor()
{
return this.id.getDeviceDescriptor();
}
@Override
public final UsbStringDescriptor getUsbStringDescriptor(final byte index)
throws UsbException
{
checkConnected();
final short[] languages = getLanguages();
final DeviceHandle handle = open();
final short langId = languages.length == 0 ? 0 : languages[0];
final ByteBuffer data = ByteBuffer.allocateDirect(256);
final int result =
LibUsb.getStringDescriptor(handle, index, langId, data);
if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Unable to get string descriptor " + index + " from device "
+ this, result);
}
return new SimpleUsbStringDescriptor(data);
}
@Override
public final String getString(final byte index) throws UsbException,
UnsupportedEncodingException
{
return getUsbStringDescriptor(index).getString();
}
/**
* Returns the languages the specified device supports.
*
* @return Array with supported language codes. Never null. May be empty.
* @throws UsbException
* When string descriptor languages could not be read.
*/
private short[] getLanguages() throws UsbException
{
final DeviceHandle handle = open();
final ByteBuffer buffer = ByteBuffer.allocateDirect(256);
final int result = LibUsb.getDescriptor(handle, LibUsb.DT_STRING,
(byte) 0, buffer);
if (result < 0)
{
throw ExceptionUtils.createPlatformException(
"Unable to get string descriptor languages", result);
}
if (result < 2)
throw new UsbException("Received illegal descriptor length: "
+ result);
final short[] languages = new short[(result - 2) / 2];
if (languages.length == 0) return languages;
buffer.position(2);
buffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(languages);
return languages;
}
@Override
public final void syncSubmit(final UsbControlIrp irp) throws UsbException
{
if (irp == null)
throw new IllegalArgumentException("irp must not be null");
checkConnected();
this.queue.add(irp);
irp.waitUntilComplete();
if (irp.isUsbException()) throw irp.getUsbException();
}
@Override
public final void asyncSubmit(final UsbControlIrp irp)
{
if (irp == null)
throw new IllegalArgumentException("irp must not be null");
checkConnected();
this.queue.add(irp);
}
@Override
public final void syncSubmit(final List list) throws UsbException
{
if (list == null)
throw new IllegalArgumentException("list must not be null");
checkConnected();
for (final Object item: list)
{
if (!(item instanceof UsbControlIrp))
throw new IllegalArgumentException(
"List contains non-UsbControlIrp objects");
syncSubmit((UsbControlIrp) item);
}
}
@Override
public final void asyncSubmit(final List list)
{
if (list == null)
throw new IllegalArgumentException("list must not be null");
checkConnected();
for (final Object item: list)
{
if (!(item instanceof UsbControlIrp))
throw new IllegalArgumentException(
"List contains non-UsbControlIrp objects");
asyncSubmit((UsbControlIrp) item);
}
}
@Override
public final UsbControlIrp createUsbControlIrp(final byte bmRequestType,
final byte bRequest, final short wValue, final short wIndex)
{
return new DefaultUsbControlIrp(bmRequestType, bRequest, wValue,
wIndex);
}
@Override
public final void addUsbDeviceListener(final UsbDeviceListener listener)
{
this.listeners.add(listener);
}
@Override
public final void removeUsbDeviceListener(final UsbDeviceListener listener)
{
this.listeners.remove(listener);
}
@Override
public final String toString()
{
return this.id.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy