From cf4dd66d31238eb91d7aa98d413ca04e56b96123 Mon Sep 17 00:00:00 2001 From: Klaus Reimer Date: Thu, 3 Feb 2011 00:01:44 +0100 Subject: [PATCH] Implemented interfaces and alternate settings. --- .../ailis/usb4java/jsr80/AbstractDevice.java | 156 +++++++++++++++++- .../usb4java/jsr80/UsbConfigurationImpl.java | 141 ++++++++++++++-- .../usb4java/jsr80/UsbInterfaceImpl.java | 126 ++++++++++---- 3 files changed, 366 insertions(+), 57 deletions(-) diff --git a/src/main/java/de/ailis/usb4java/jsr80/AbstractDevice.java b/src/main/java/de/ailis/usb4java/jsr80/AbstractDevice.java index 1fcd2c2..561a6cd 100644 --- a/src/main/java/de/ailis/usb4java/jsr80/AbstractDevice.java +++ b/src/main/java/de/ailis/usb4java/jsr80/AbstractDevice.java @@ -6,10 +6,14 @@ package de.ailis.usb4java.jsr80; import static de.ailis.usb4java.USB.USB_DT_STRING; +import static de.ailis.usb4java.USB.usb_claim_interface; import static de.ailis.usb4java.USB.usb_close; +import static de.ailis.usb4java.USB.usb_control_msg; import static de.ailis.usb4java.USB.usb_get_descriptor; import static de.ailis.usb4java.USB.usb_get_string; import static de.ailis.usb4java.USB.usb_open; +import static de.ailis.usb4java.USB.usb_release_interface; +import static de.ailis.usb4java.USB.usb_set_configuration; import static de.ailis.usb4java.USB.usb_strerror; import java.io.UnsupportedEncodingException; @@ -19,6 +23,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.usb.UsbClaimException; import javax.usb.UsbConfiguration; import javax.usb.UsbConst; import javax.usb.UsbControlIrp; @@ -64,7 +69,10 @@ abstract class AbstractDevice implements UsbDevice private USB_Dev_Handle handle; /** The number of the currently active configuration. */ - private final byte activeConfigurationNumber = 0; + private byte activeConfigurationNumber = 0; + + /** The number of the currently claimed interface. */ + private Byte claimedInterfaceNumber = null; /** The port this device is connected to. */ private UsbPort port; @@ -84,9 +92,21 @@ abstract class AbstractDevice implements UsbDevice final USB_Config_Descriptor[] configs = device.config(); final List configurations = - new ArrayList(configs.length); + new ArrayList(configs.length); for (final USB_Config_Descriptor config : configs) - configurations.add(new UsbConfigurationImpl(this, config)); + { + final UsbConfiguration configuration = new UsbConfigurationImpl( + this, config); + configurations.add(configuration); + + // TODO No idea how to find out the active configuration via + // libusb. So for now we use the first configuration. + if (this.activeConfigurationNumber == 0) + { + this.activeConfigurationNumber = (byte) config + .bConfigurationValue(); + } + } this.configurations = Collections.unmodifiableList(configurations); } @@ -317,6 +337,117 @@ abstract class AbstractDevice implements UsbDevice } + /** + * 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.claimedInterfaceNumber != null) + throw new UsbException("Can't change configuration while an " + + "interface is still claimed"); + + USBLock.acquire(); + try + { + final int result = usb_set_configuration(open(), number & 0xff); + if (result < 0) throw new UsbException(usb_strerror()); + this.activeConfigurationNumber = number; + } + finally + { + USBLock.release(); + } + } + } + + + /** + * Claims the specified interface. + * + * @param number + * The number of the interface to claim. + * @throws UsbException + * When interface could not be claimed. + * @throws UsbClaimException + * When an interface is already claimed. + */ + + final void claimInterface(final byte number) throws UsbClaimException, + UsbException + { + if (this.claimedInterfaceNumber != null) + throw new UsbClaimException("A interface is already claimed"); + + USBLock.acquire(); + try + { + final int result = usb_claim_interface(open(), number & 0xff); + if (result < 0) throw new UsbException(usb_strerror()); + this.claimedInterfaceNumber = number; + } + finally + { + USBLock.release(); + } + } + + + /** + * Releases a claimed interface. + * + * @param number + * The number of the interface to release. + * @throws UsbClaimException + * When the interface is not claimed. + * @throws UsbException + * When interface could not be claimed. + */ + + final void releaseInterface(final byte number) throws UsbClaimException, + UsbException + { + if (this.claimedInterfaceNumber == null) + throw new UsbClaimException("No interface is claimed"); + if (!Byte.valueOf(number).equals(this.claimedInterfaceNumber)) + throw new UsbClaimException("Interface not claimed"); + + USBLock.acquire(); + try + { + final int result = usb_release_interface(open(), number & 0xff); + if (result < 0) throw new UsbException(usb_strerror()); + this.claimedInterfaceNumber = null; + } + finally + { + USBLock.release(); + } + } + + + /** + * 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 Byte.valueOf(number).equals(this.claimedInterfaceNumber); + } + + /** * @see UsbDevice#getActiveUsbConfiguration() */ @@ -437,8 +568,23 @@ abstract class AbstractDevice implements UsbDevice @Override public final void syncSubmit(final UsbControlIrp irp) throws UsbException { - // TODO - throw new UnsupportedOperationException(); + USBLock.acquire(); + try + { + final ByteBuffer buffer = ByteBuffer + .allocateDirect(irp.getLength()); + final USB_Dev_Handle handle = open(); + final int len = usb_control_msg(handle, irp.bmRequestType(), + irp.bRequest(), + irp.wValue(), irp.wIndex(), buffer, 250); + if (len < 0) throw new UsbException(usb_strerror()); + buffer.rewind(); + buffer.get(irp.getData(), 0, len); + } + finally + { + USBLock.release(); + } } diff --git a/src/main/java/de/ailis/usb4java/jsr80/UsbConfigurationImpl.java b/src/main/java/de/ailis/usb4java/jsr80/UsbConfigurationImpl.java index 1804ffd..afdf857 100644 --- a/src/main/java/de/ailis/usb4java/jsr80/UsbConfigurationImpl.java +++ b/src/main/java/de/ailis/usb4java/jsr80/UsbConfigurationImpl.java @@ -5,7 +5,15 @@ package de.ailis.usb4java.jsr80; +import static de.ailis.usb4java.USB.usb_set_altinterface; +import static de.ailis.usb4java.USB.usb_strerror; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.usb.UsbConfiguration; import javax.usb.UsbConfigurationDescriptor; @@ -13,7 +21,10 @@ import javax.usb.UsbDevice; import javax.usb.UsbException; import javax.usb.UsbInterface; +import de.ailis.usb4java.USBLock; import de.ailis.usb4java.USB_Config_Descriptor; +import de.ailis.usb4java.USB_Interface; +import de.ailis.usb4java.USB_Interface_Descriptor; /** @@ -24,14 +35,21 @@ import de.ailis.usb4java.USB_Config_Descriptor; public final class UsbConfigurationImpl implements UsbConfiguration { - /** The low-level USB configuration descriptor. */ - private final USB_Config_Descriptor lowLevelDescriptor; - /** The JSR 80 USB configuration descriptor. */ private final UsbConfigurationDescriptor descriptor; /** The USB device. */ - private final UsbDevice device; + private final AbstractDevice device; + + /** + * The interfaces. This is a map from interface number to a map of + * altsettings which maps setting numbers to actual interfaces. + */ + private final Map> interfaces = + new HashMap>(); + + /** This map contains the active USB interfaces. */ + private final Map activeSettings = new HashMap(); /** @@ -43,13 +61,43 @@ public final class UsbConfigurationImpl implements UsbConfiguration * The low-level USB configuration descriptor. */ - public UsbConfigurationImpl(final UsbDevice device, + public UsbConfigurationImpl(final AbstractDevice device, final USB_Config_Descriptor lowLevelDescriptor) { this.device = device; - this.lowLevelDescriptor = lowLevelDescriptor; this.descriptor = new UsbConfigurationDescriptorImpl( lowLevelDescriptor); + + // Build interfaces + for (final USB_Interface iface : lowLevelDescriptor.iface()) + { + for (final USB_Interface_Descriptor desc : iface.altsetting()) + { + final int ifaceNumber = desc.bInterfaceNumber(); + final int settingNumber = desc.bAlternateSetting(); + + Map settings = this.interfaces + .get(ifaceNumber); + if (settings == null) + { + settings = new HashMap(); + this.interfaces.put(ifaceNumber, settings); + } + final UsbInterface usbInterface = new UsbInterfaceImpl(this, + desc); + + // If we have no active setting for current interface number + // yet or the alternate setting number is 0 (which marks the + // default alternate setting) then set current interface as + // the active setting. + if (!this.activeSettings.containsKey(ifaceNumber) || + desc.bAlternateSetting() == 0) + this.activeSettings.put(ifaceNumber, usbInterface); + + // Add the interface to the settings list + settings.put(settingNumber, usbInterface); + } + } } @@ -60,8 +108,8 @@ public final class UsbConfigurationImpl implements UsbConfiguration @Override public boolean isActive() { - // TODO - throw new UnsupportedOperationException(); + return this.device.getActiveUsbConfigurationNumber() == this.descriptor + .bConfigurationValue(); } @@ -72,8 +120,36 @@ public final class UsbConfigurationImpl implements UsbConfiguration @Override public List getUsbInterfaces() { - // TODO - throw new UnsupportedOperationException(); + return Collections.unmodifiableList(new ArrayList( + this.activeSettings.values())); + } + + + /** + * Returns the alternate settings for the specified interface. + * + * @param number + * The interface number. + * @return The alternate settings for the specified interface. + */ + + Map getSettings(final byte number) + { + return this.interfaces.get(number); + } + + + /** + * Returns the number of alternate settings of the specified interface. + * + * @param number + * The interface number. + * @return The number of alternate settings. + */ + + int getNumSettings(final byte number) + { + return this.interfaces.get(number).size(); } @@ -84,8 +160,39 @@ public final class UsbConfigurationImpl implements UsbConfiguration @Override public UsbInterface getUsbInterface(final byte number) { - // TODO - throw new UnsupportedOperationException(); + return this.activeSettings.get(number); + } + + + /** + * Sets the active USB interface setting. + * + * @param number + * THe interface number. + * @param iface + * The interface setting to activate. + * @throws UsbException + * When interface setting could not be set. + */ + + void setUsbInterface(final byte number, final UsbInterface iface) + throws UsbException + { + if (this.activeSettings.get(number & 0xff) != iface) + { + USBLock.acquire(); + try + { + final int result = usb_set_altinterface(this.device.open(), + iface.getUsbInterfaceDescriptor().bAlternateSetting()); + if (result < 0) throw new UsbException(usb_strerror()); + this.activeSettings.put(number & 0xff, iface); + } + finally + { + USBLock.release(); + } + } } @@ -96,7 +203,7 @@ public final class UsbConfigurationImpl implements UsbConfiguration @Override public boolean containsUsbInterface(final byte number) { - return number >= 0 && number < this.lowLevelDescriptor.bNumInterfaces(); + return this.activeSettings.containsKey(number); } @@ -127,9 +234,11 @@ public final class UsbConfigurationImpl implements UsbConfiguration */ @Override - public String getConfigurationString() throws UsbException + public String getConfigurationString() throws UsbException, + UnsupportedEncodingException { - // TODO - throw new UnsupportedOperationException(); + final byte iConfiguration = this.descriptor.iConfiguration(); + if (iConfiguration == 0) return null; + return this.device.getString(iConfiguration); } } diff --git a/src/main/java/de/ailis/usb4java/jsr80/UsbInterfaceImpl.java b/src/main/java/de/ailis/usb4java/jsr80/UsbInterfaceImpl.java index 9feaa4d..d4105fe 100644 --- a/src/main/java/de/ailis/usb4java/jsr80/UsbInterfaceImpl.java +++ b/src/main/java/de/ailis/usb4java/jsr80/UsbInterfaceImpl.java @@ -5,15 +5,22 @@ package de.ailis.usb4java.jsr80; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import javax.usb.UsbClaimException; import javax.usb.UsbConfiguration; import javax.usb.UsbEndpoint; +import javax.usb.UsbException; import javax.usb.UsbInterface; import javax.usb.UsbInterfaceDescriptor; import javax.usb.UsbInterfacePolicy; +import javax.usb.UsbNotActiveException; -import de.ailis.usb4java.USB_Interface; +import de.ailis.usb4java.USBLock; +import de.ailis.usb4java.USB_Interface_Descriptor; /** @@ -25,10 +32,10 @@ import de.ailis.usb4java.USB_Interface; public final class UsbInterfaceImpl implements UsbInterface { /** The USB configuration. */ - private final UsbConfiguration configuration; + private final UsbConfigurationImpl configuration; - /** The low-level USB interface. */ - private final USB_Interface iface; + /** The interface descriptor. */ + private final UsbInterfaceDescriptor descriptor; /** @@ -36,15 +43,39 @@ public final class UsbInterfaceImpl implements UsbInterface * * @param configuration * The USB configuration. - * @param iface - * The low-level USB-interface. + * @param lowLevelDescriptor + * The low-level USB interface descriptor. */ - public UsbInterfaceImpl(final UsbConfiguration configuration, - final USB_Interface iface) + public UsbInterfaceImpl(final UsbConfigurationImpl configuration, + final USB_Interface_Descriptor lowLevelDescriptor) { this.configuration = configuration; - this.iface = iface; + this.descriptor = new UsbInterfaceDescriptorImpl(lowLevelDescriptor); + } + + + /** + * Checks if the configuration is active. If not then an + * UsbNotActiveException is thrown. + */ + + private void checkConfigurationActive() + { + if (!this.configuration.isActive()) + throw new UsbNotActiveException("Configuration is not active"); + } + + + /** + * Checks if setting is active. Throws an UsbNotActiveException if not. + */ + + private void checkSettingActive() + { + checkConfigurationActive(); + if (!isActive()) + throw new UsbNotActiveException("Setting is not active"); } @@ -53,22 +84,36 @@ public final class UsbInterfaceImpl implements UsbInterface */ @Override - public void claim() + public void claim() throws UsbException { - // TODO - throw new UnsupportedOperationException(); + claim(null); } /** * @see UsbInterface#claim(UsbInterfacePolicy) + * + * TODO Policy is ignored */ @Override public void claim(final UsbInterfacePolicy policy) + throws UsbClaimException, UsbException { - // TODO - throw new UnsupportedOperationException(); + final AbstractDevice device = (AbstractDevice) this.configuration + .getUsbDevice(); + USBLock.acquire(); + try + { + device.setActiveUsbConfigurationNumber(this.configuration + .getUsbConfigurationDescriptor().bConfigurationValue()); + device.claimInterface(this.descriptor.bInterfaceNumber()); + this.configuration.setUsbInterface(this.descriptor.bInterfaceNumber(), this); + } + finally + { + USBLock.release(); + } } @@ -77,10 +122,10 @@ public final class UsbInterfaceImpl implements UsbInterface */ @Override - public void release() + public void release() throws UsbClaimException, UsbException { - // TODO - throw new UnsupportedOperationException(); + ((AbstractDevice) this.configuration.getUsbDevice()) + .releaseInterface(this.descriptor.bInterfaceNumber()); } @@ -91,8 +136,8 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public boolean isClaimed() { - // TODO - throw new UnsupportedOperationException(); + return ((AbstractDevice) this.configuration.getUsbDevice()) + .isInterfaceClaimed(this.descriptor.bInterfaceNumber()); } @@ -103,8 +148,8 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public boolean isActive() { - // TODO - throw new UnsupportedOperationException(); + return this.configuration.getUsbInterface(this.descriptor + .bInterfaceNumber()) == this; } @@ -115,7 +160,8 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public int getNumSettings() { - return this.iface.num_altsetting(); + return ((this.configuration) + .getNumSettings(this.descriptor.bInterfaceNumber())); } @@ -126,8 +172,11 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public byte getActiveSettingNumber() { - // TODO - throw new UnsupportedOperationException(); + checkConfigurationActive(); + checkSettingActive(); + return this.configuration + .getUsbInterface(this.descriptor.bInterfaceNumber()) + .getUsbInterfaceDescriptor().bAlternateSetting(); } @@ -138,8 +187,10 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public UsbInterface getActiveSetting() { - // TODO - throw new UnsupportedOperationException(); + checkConfigurationActive(); + checkSettingActive(); + return this.configuration.getUsbInterface(this.descriptor + .bInterfaceNumber()); } @@ -150,8 +201,8 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public UsbInterface getSetting(final byte number) { - // TODO - throw new UnsupportedOperationException(); + return (this.configuration).getSettings( + this.descriptor.bInterfaceNumber()).get(number); } @@ -162,7 +213,8 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public boolean containsSetting(final byte number) { - return number >= 0 && number < this.iface.num_altsetting(); + return (this.configuration).getSettings( + this.descriptor.bInterfaceNumber()).containsKey(number); } @@ -173,8 +225,9 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public List getSettings() { - // TODO - throw new UnsupportedOperationException(); + return Collections.unmodifiableList(new ArrayList( + (this.configuration).getSettings( + this.descriptor.bInterfaceNumber()).values())); } @@ -232,8 +285,7 @@ public final class UsbInterfaceImpl implements UsbInterface @Override public UsbInterfaceDescriptor getUsbInterfaceDescriptor() { - // TODO - throw new UnsupportedOperationException(); + return this.descriptor; } @@ -242,9 +294,11 @@ public final class UsbInterfaceImpl implements UsbInterface */ @Override - public String getInterfaceString() + public String getInterfaceString() throws UsbException, + UnsupportedEncodingException { - // TODO - throw new UnsupportedOperationException(); + final byte iInterface = this.descriptor.iInterface(); + if (iInterface == 0) return null; + return this.configuration.getUsbDevice().getString(iInterface); } }