/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.driver.bus.usb;
import java.util.HashMap;
import org.jnode.driver.Device;
/**
* USB device.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
public class USBDevice extends Device implements USBConstants {
private static int usbIdCounter = 0;
/**
* My device id 0..127
*/
private int devId;
/**
* Speed of this device
*/
private int speed;
/**
* The maximum packet size per endpoint
*/
private final int[] maxPacketSize;
/**
* The device descriptor
*/
private DeviceDescriptor deviceDescriptor;
/**
* The configurations
*/
private USBConfiguration[] confs;
/**
* The active configuration
*/
private USBConfiguration activeConf;
/**
* The string cache
*/
private final HashMap<Integer, String> stringCache = new HashMap<Integer, String>();
/**
* The default language ID for getString, -1 means not initialized yet
*/
private int defaultLangID = -1;
/**
* The default control pipe
*/
private final USBControlPipe defaultControlPipe;
/**
* Initialize this device. The USB device id is not set yet. The speed is set to the given
* parameter.
*
* @param bus
* @param speed
*/
public USBDevice(USBBus bus, int speed) {
super(bus, "usb" + usbIdCounter++);
this.devId = 0;
if ((speed < USB_SPEED_LOW) || (speed > USB_SPEED_HIGH)) {
throw new IllegalArgumentException("Invalid speed value");
}
this.speed = speed;
this.maxPacketSize = new int[USB_ENDPOINT_MAX];
if (speed == USB_SPEED_HIGH) {
// Required for high speed devices
this.maxPacketSize[0] = 64;
} else {
// Default for both low & full speed devices
this.maxPacketSize[0] = 8;
}
this.defaultControlPipe = bus.getHcApi().createDefaultControlPipe(this);
}
/**
* Gets the USB bus i'm connected to.
*/
public final USBBus getUSBBus() {
return (USBBus) getBus();
}
/**
* @return Returns the USB device id.
*/
public final int getUSBDeviceId() {
return this.devId;
}
/**
* Terminate this object
*
* @see java.lang.Object#finalize()
*/
public void finalize() {
getUSBBus().freeDeviceID(devId);
}
/**
* Gets the speed of this device.
*
* @return Returns the speed.
* @see USBConstants#USB_SPEED_LOW
* @see USBConstants#USB_SPEED_FULL
* @see USBConstants#USB_SPEED_HIGH
*/
public final int getSpeed() {
return this.speed;
}
/**
* Is this a low speed device.
*/
public final boolean isLowSpeed() {
return (this.speed == USB_SPEED_LOW);
}
/**
* Is this a full speed device.
*/
public final boolean isFullSpeed() {
return (this.speed == USB_SPEED_FULL);
}
/**
* Is this a high speed device.
*/
public final boolean isHighSpeed() {
return (this.speed == USB_SPEED_HIGH);
}
/**
* Gets the maximum packet size for a given endpoint.
*/
public final int getMaxPacketSize(int endPoint) {
return this.maxPacketSize[endPoint];
}
/**
* @return Returns the deviceDescriptor.
*/
public final DeviceDescriptor getDescriptor() {
return this.deviceDescriptor;
}
/**
* Sets the device descriptor. This method can only be called once. It also set the maximum
* packet size for endpioint 0.
*
* @param deviceDescriptor The deviceDescriptor to set.
*/
final void setDescriptor(DeviceDescriptor deviceDescriptor) {
if (this.deviceDescriptor != null) {
throw new IllegalStateException("Cannot overwrite the device descriptor");
} else {
this.deviceDescriptor = deviceDescriptor;
this.maxPacketSize[0] = deviceDescriptor.getMaxPacketSize0();
}
}
/**
* Sets the device descriptor. This method can only be called once. It also set the maximum
* packet size for endpioint 0.
*
* @param deviceDescriptor The deviceDescriptor to set.
*/
final void setFullDescriptor(DeviceDescriptor deviceDescriptor) {
if (this.confs != null) {
throw new IllegalStateException("Cannot overwrite the full device descriptor");
} else {
this.deviceDescriptor = deviceDescriptor;
this.maxPacketSize[0] = deviceDescriptor.getMaxPacketSize0();
this.confs = new USBConfiguration[deviceDescriptor.getNumConfigurations()];
}
}
/**
* Gets a specific configuration.
*
* @param index
* @return The configuration at the given index.
*/
public final USBConfiguration getConfiguration(int index) {
return this.confs[index];
}
/**
* Sets a specific configuration.
*
* @param index
* @param conf
*/
final void setConfiguration(int index, USBConfiguration conf) {
if (this.confs[index] != null) {
throw new SecurityException("Cannot overwrite a specific configuration");
} else {
this.confs[index] = conf;
}
}
/**
* Gets a String from the device.
*
* @param strIndex
* @param langID If <= 0, the device first language is used
*/
public final String getString(int strIndex, int langID) throws USBException {
if (strIndex <= 0) {
throw new IllegalArgumentException("Invalid string index " + strIndex);
}
// Is the language ID given?
if (langID <= 0) {
// Not given, use the default
if (defaultLangID < 0) {
// Initialize the default langID first
final StringDescriptorZero descr = new StringDescriptorZero(256);
try {
// Get on the first langID (length=4)
readDescriptor(USB_RECIP_DEVICE, USB_DT_STRING, 0, 0, 4, descr);
defaultLangID = descr.getLangID(0);
} catch (USBException ex) {
// Just pick something then
defaultLangID = 0;
}
}
langID = defaultLangID;
}
final int cacheKey = (langID << 16) | strIndex;
final String cacheValue = stringCache.get(cacheKey);
if (cacheValue != null) {
return cacheValue;
}
final StringDescriptor descr = new StringDescriptor(256);
readDescriptor(USB_RECIP_DEVICE, USB_DT_STRING, strIndex, langID, -1, descr);
// Put it in the cache
stringCache.put(cacheKey, cacheValue);
// Return the string
return descr.getString();
}
/**
* Read a descriptor from the device.
*
* @param reqType
* @param descrType
* @param index
* @param langID
* @param length The length of the descriptor to read. When (length < 0) then first the length
* of the descriptor is read, after which the full length is read.
* @param descr
*/
public final void readDescriptor(int reqType, int descrType, int index, int langID, int length, USBPacket descr)
throws USBException {
USBException lastEx = null;
for (int attempt = 0; attempt < GET_DESCRIPTOR_ATTEMPTS; attempt++) {
try {
if (length < 0) {
// Determine length first
final USBRequest initReq = defaultControlPipe.createRequest(
SetupPacket.createGetDescriptorPacket(reqType, descrType, index, langID, 1), descr);
defaultControlPipe.syncSubmit(initReq, GET_TIMEOUT);
length = descr.getByte(0); // The length field
}
final USBRequest req = defaultControlPipe.createRequest(
SetupPacket.createGetDescriptorPacket(reqType, descrType, index, langID, length), descr);
defaultControlPipe.syncSubmit(req, GET_TIMEOUT);
} catch (USBException ex) {
lastEx = ex;
}
}
if (lastEx != null) {
throw lastEx;
}
}
/**
* Set the USB device address of this device. A SET_ADDRESS request is send to the device.
*
* @param usbAddress
* @throws USBException
*/
final void setAddress(int usbAddress) throws USBException {
if ((this.devId != 0) && (usbAddress != 0)) {
throw new SecurityException("Cannot overwrite the USB device id.");
}
final USBRequest req =
defaultControlPipe.createRequest(SetupPacket.createDeviceSetAddressPacket(usbAddress), null);
defaultControlPipe.syncSubmit(req, SET_TIMEOUT);
this.devId = usbAddress;
}
/**
* Gets the active configuration
*
* @return The active configuration, or null if the active configuration has not been set.
*/
public final USBConfiguration getConfiguration() {
return this.activeConf;
}
/**
* Sets the active configuration.
*
* @param activeConf The configuration to set.
*/
public final void setConfiguration(USBConfiguration activeConf) throws USBException {
final int confNum = activeConf.getDescriptor().getConfigurationValue();
final USBRequest req =
defaultControlPipe.createRequest(SetupPacket.createDeviceSetConfigurationPacket(confNum), null);
defaultControlPipe.syncSubmit(req, SET_TIMEOUT);
this.activeConf = activeConf;
}
/**
* Issue a GET_STATUS request to this device.
*
* @return The status returned by the device.
* @throws USBException
*/
public final int getStatus() throws USBException {
final USBPacket data = new USBPacket(2);
final USBRequest req =
defaultControlPipe.createRequest(SetupPacket.createGetStatusPacket(USB_RECIP_DEVICE, 0), data);
defaultControlPipe.syncSubmit(req, GET_TIMEOUT);
return data.getShort(0);
}
/**
* Issue a SET_FEATURE request.
*
* @param featureSelector
* @throws USBException
*/
public final void setFeature(int reqType, int index, int featureSelector) throws USBException {
final USBRequest req =
defaultControlPipe.createRequest(SetupPacket.createSetFeaturePacket(reqType, index, featureSelector), null);
defaultControlPipe.syncSubmit(req, SET_TIMEOUT);
}
/**
* Issue a CLEAR_FEATURE request.
*
* @param featureSelector
* @throws USBException
*/
public final void clearFeature(int reqType, int index, int featureSelector) throws USBException {
final USBRequest req =
defaultControlPipe
.createRequest(SetupPacket.createClearFeaturePacket(reqType, index, featureSelector), null);
defaultControlPipe.syncSubmit(req, SET_TIMEOUT);
}
/**
* Gets the default control pipe.
*
* @return Returns the defaultControlPipe.
*/
public final USBControlPipe getDefaultControlPipe() {
return this.defaultControlPipe;
}
}