diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d4af2fb..8b5cb35 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -28,7 +28,8 @@
Implement support for SuperSpeed Endpoint Companion Descriptor.
- Implement support for BOS Descriptor.
+ Implement support for BOS Descriptor, BOS Device Capability
+ Descriptor and USB 2.0 Extension Descriptor.
Using uninitialized descriptors now throw an IllegalStateException
diff --git a/src/main/c/src/BOSDevCapabilityDescriptor.h b/src/main/c/src/BOSDevCapabilityDescriptor.h
index 71d8c25..2d92cad 100644
--- a/src/main/c/src/BOSDevCapabilityDescriptor.h
+++ b/src/main/c/src/BOSDevCapabilityDescriptor.h
@@ -10,5 +10,7 @@
jobjectArray wrapBOSDevCapabilityDescriptors(JNIEnv*, int,
struct libusb_bos_dev_capability_descriptor**);
+struct libusb_bos_dev_capability_descriptor
+ *unwrapBOSDevCapabilityDescriptor(JNIEnv *, jobject);
#endif
diff --git a/src/main/c/src/LibUsb.c b/src/main/c/src/LibUsb.c
index 71c2502..c59b5d4 100644
--- a/src/main/c/src/LibUsb.c
+++ b/src/main/c/src/LibUsb.c
@@ -24,6 +24,8 @@
#include "EndpointDescriptor.h"
#include "SSEndpointCompanionDescriptor.h"
#include "BOSDescriptor.h"
+#include "BOSDevCapabilityDescriptor.h"
+#include "Usb20ExtensionDescriptor.h"
#include "Transfer.h"
static JavaVM *jvm;
@@ -781,6 +783,45 @@ JNIEXPORT void JNICALL METHOD_NAME(LibUsb, freeBOSDescriptor)
resetBOSDescriptor(env, bosDescriptor);
}
+/**
+ * int getUsb20ExtensionDescriptor(Context, BOSDevCapabilityDescriptor, Usb20ExtensionDescriptor)
+ */
+JNIEXPORT jint JNICALL METHOD_NAME(LibUsb, getUsb20ExtensionDescriptor)
+(
+ JNIEnv *env, jclass class, jobject context, jobject devCapDescriptor,
+ jobject extensionDescriptor
+)
+{
+ NOT_NULL(env, devCapDescriptor, return 0);
+ NOT_NULL(env, extensionDescriptor, return 0);
+ libusb_context *ctx = unwrapContext(env, context);
+ struct libusb_bos_dev_capability_descriptor *devcap_descriptor =
+ unwrapBOSDevCapabilityDescriptor(env, devCapDescriptor);
+ if (!devcap_descriptor) return 0;
+ struct libusb_usb_2_0_extension_descriptor *extension_descriptor;
+ int result = libusb_get_usb_2_0_extension_descriptor(ctx,
+ devcap_descriptor, &extension_descriptor);
+ if (!result) setUsb20ExtensionDescriptor(env, extension_descriptor,
+ extensionDescriptor);
+ return result;
+}
+
+/**
+ * void freeUsb20ExtensionDescriptor(Usb20ExtensionDescriptor)
+ */
+JNIEXPORT void JNICALL METHOD_NAME(LibUsb, freeUsb20ExtensionDescriptor)
+(
+ JNIEnv *env, jclass class, jobject extensionDescriptor
+)
+{
+ if (!extensionDescriptor) return;
+ struct libusb_usb_2_0_extension_descriptor *extension_descriptor =
+ unwrapUsb20ExtensionDescriptor(env, extensionDescriptor);
+ if (!extension_descriptor) return;
+ libusb_free_usb_2_0_extension_descriptor(extension_descriptor);
+ resetUsb20ExtensionDescriptor(env, extensionDescriptor);
+}
+
/**
* int getDescriptor(DeviceHandle, int, int, ByteBuffer)
*/
diff --git a/src/main/c/src/Makefile.am b/src/main/c/src/Makefile.am
index af1a09e..1f1af70 100644
--- a/src/main/c/src/Makefile.am
+++ b/src/main/c/src/Makefile.am
@@ -20,4 +20,5 @@ libusb4java_la_SOURCES = \
Transfer.c \
SSEndpointCompanionDescriptor.c \
BOSDescriptor.c \
- BOSDevCapabilityDescriptor.c
+ BOSDevCapabilityDescriptor.c \
+ Usb20ExtensionDescriptor.c
diff --git a/src/main/c/src/Usb20ExtensionDescriptor.c b/src/main/c/src/Usb20ExtensionDescriptor.c
new file mode 100644
index 0000000..e5412b1
--- /dev/null
+++ b/src/main/c/src/Usb20ExtensionDescriptor.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2013 Klaus Reimer (k@ailis.de)
+ * See COPYING file for copying conditions
+ */
+
+#include "Usb20ExtensionDescriptor.h"
+
+void setUsb20ExtensionDescriptor(JNIEnv* env,
+ struct libusb_usb_2_0_extension_descriptor* descriptor, jobject object)
+{
+ SET_POINTER(env, descriptor, object, "usb20ExtensionDescriptorPointer");
+}
+
+struct libusb_usb_2_0_extension_descriptor* unwrapUsb20ExtensionDescriptor(
+ JNIEnv* env, jobject descriptor)
+{
+ UNWRAP_POINTER(env, descriptor,
+ struct libusb_usb_2_0_extension_descriptor*,
+ "usb20ExtensionDescriptorPointer");
+}
+
+void resetUsb20ExtensionDescriptor(JNIEnv* env, jobject obj)
+{
+ RESET_POINTER(env, obj, "usb20ExtensionDescriptorPointer");
+}
+
+/**
+ * byte bLength()
+ */
+JNIEXPORT jbyte JNICALL METHOD_NAME(Usb20ExtensionDescriptor, bLength)
+(
+ JNIEnv *env, jobject this
+)
+{
+ struct libusb_usb_2_0_extension_descriptor *descriptor =
+ unwrapUsb20ExtensionDescriptor(env, this);
+ if (!descriptor) return 0;
+ return descriptor->bLength;
+}
+
+/**
+ * byte bDescriptorType()
+ */
+JNIEXPORT jbyte JNICALL METHOD_NAME(Usb20ExtensionDescriptor, bDescriptorType)
+(
+ JNIEnv *env, jobject this
+)
+{
+ struct libusb_usb_2_0_extension_descriptor *descriptor =
+ unwrapUsb20ExtensionDescriptor(env, this);
+ if (!descriptor) return 0;
+ return descriptor->bDescriptorType;
+}
+
+/**
+ * byte bDevCapabilityType()
+ */
+JNIEXPORT jbyte JNICALL METHOD_NAME(Usb20ExtensionDescriptor,
+ bDevCapabilityType)
+(
+ JNIEnv *env, jobject this
+)
+{
+ struct libusb_usb_2_0_extension_descriptor *descriptor =
+ unwrapUsb20ExtensionDescriptor(env, this);
+ if (!descriptor) return 0;
+ return descriptor->bDevCapabilityType;
+}
+
+/**
+ * int bDevCapabilityType()
+ */
+JNIEXPORT jint JNICALL METHOD_NAME(Usb20ExtensionDescriptor,
+ bmAttributes)
+(
+ JNIEnv *env, jobject this
+)
+{
+ struct libusb_usb_2_0_extension_descriptor *descriptor =
+ unwrapUsb20ExtensionDescriptor(env, this);
+ if (!descriptor) return 0;
+ return descriptor->bmAttributes;
+}
diff --git a/src/main/c/src/Usb20ExtensionDescriptor.h b/src/main/c/src/Usb20ExtensionDescriptor.h
new file mode 100644
index 0000000..d076ec4
--- /dev/null
+++ b/src/main/c/src/Usb20ExtensionDescriptor.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2013 Klaus Reimer (k@ailis.de)
+ * See COPYING file for copying conditions
+ */
+
+#ifndef USB4JAVA_USB_2_0_EXTENSION_DESCRIPTOR_H
+#define USB4JAVA_USB_2_0_EXTENSION_DESCRIPTOR_H
+
+#include "usb4java.h"
+
+void setUsb20ExtensionDescriptor(JNIEnv*,
+ struct libusb_usb_2_0_extension_descriptor*, jobject);
+struct libusb_usb_2_0_extension_descriptor*
+ unwrapUsb20ExtensionDescriptor(JNIEnv*, jobject);
+void resetUsb20ExtensionDescriptor(JNIEnv*, jobject);
+
+#endif
diff --git a/src/main/java/de/ailis/usb4java/libusb/LibUsb.java b/src/main/java/de/ailis/usb4java/libusb/LibUsb.java
index 38e4fad..5f8e29f 100644
--- a/src/main/java/de/ailis/usb4java/libusb/LibUsb.java
+++ b/src/main/java/de/ailis/usb4java/libusb/LibUsb.java
@@ -1494,6 +1494,40 @@ public final class LibUsb
public static native void freeBOSDescriptor(final BOSDescriptor
descriptor);
+ /**
+ * Get an USB 2.0 Extension descriptor.
+ *
+ * @param context
+ * The context to operate on, or NULL for the default context.
+ * @param devCapDescriptor
+ * Device Capability descriptor with a bDevCapabilityType of
+ * {@link #BT_USB_2_0_EXTENSION}.
+ * @param extensionDescriptor
+ * Output location for the USB 2.0 Extension descriptor. Only
+ * valid if 0 was returned. Must be freed with
+ * {@link #freeUsb20ExtensionDescriptor(
+ * Usb20ExtensionDescriptor)} after use.
+ * @return 0 on success a LIBUSB_ERROR code on error
+ */
+ public static native int getUsb20ExtensionDescriptor(
+ final Context context,
+ final BOSDevCapabilityDescriptor devCapDescriptor,
+ final Usb20ExtensionDescriptor extensionDescriptor);
+
+ /**
+ * Free a USB 2.0 Extension descriptor obtained from
+ * {@link #getUsb20ExtensionDescriptor(Context, BOSDevCapabilityDescriptor,
+ * Usb20ExtensionDescriptor)}.
+ *
+ * It is safe to call this function with a NULL parameter, in which case
+ * the function simply returns.
+ *
+ * @param extensionDescriptor
+ * The USB 2.0 Extension descriptor to free.
+ */
+ public static native void freeUsb20ExtensionDescriptor(
+ final Usb20ExtensionDescriptor extensionDescriptor);
+
/**
* Retrieve a descriptor from the default control pipe.
*
diff --git a/src/main/java/de/ailis/usb4java/libusb/Usb20ExtensionDescriptor.java b/src/main/java/de/ailis/usb4java/libusb/Usb20ExtensionDescriptor.java
new file mode 100644
index 0000000..25ee5e4
--- /dev/null
+++ b/src/main/java/de/ailis/usb4java/libusb/Usb20ExtensionDescriptor.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2013 Klaus Reimer
+ * See LICENSE.md for licensing information.
+ *
+ * Based on libusb :
+ *
+ * Copyright 2001 Johannes Erdfelt
+ * Copyright 2007-2009 Daniel Drake
+ * Copyright 2010-2012 Peter Stuge
+ * Copyright 2008-2011 Nathan Hjelm
+ * Copyright 2009-2012 Pete Batard
+ * Copyright 2009-2012 Ludovic Rousseau
+ * Copyright 2010-2012 Michael Plante
+ * Copyright 2011-2012 Hans de Goede
+ * Copyright 2012 Martin Pieuchot
+ * Copyright 2012-2013 Toby Gray
+ */
+
+package de.ailis.usb4java.libusb;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+/**
+ * A structure representing the USB 2.0 Extension descriptor. This descriptor is
+ * documented in section 9.6.2.1 of the USB 3.0 specification.
+ *
+ * All multiple-byte fields are represented in host-endian format.
+ *
+ * @author Klaus Reimer (k@ailis.de)
+ */
+public final class Usb20ExtensionDescriptor
+{
+ /** The native pointer to the descriptor structure. */
+ private long usb20ExtensionDescriptorPointer;
+
+ /**
+ * Constructs a new BOS descriptor which can be passed to the
+ * {@link LibUsb#getUsb20ExtensionDescriptor(Context,
+ * BOSDevCapabilityDescriptor, Usb20ExtensionDescriptor)} method.
+ */
+ public Usb20ExtensionDescriptor()
+ {
+ // Empty
+ }
+
+ /**
+ * Returns the native pointer.
+ *
+ * @return The native pointer.
+ */
+ public long getPointer()
+ {
+ return this.usb20ExtensionDescriptorPointer;
+ }
+
+ /**
+ * Returns the size of this descriptor (in bytes).
+ *
+ * @return The descriptor size in bytes;
+ */
+ public native byte bLength();
+
+ /**
+ * Returns the descriptor type.
+ *
+ * @return The descriptor type.
+ */
+ public native byte bDescriptorType();
+
+ /**
+ * Returns the device capability type.
+ *
+ * @return The device capability type.
+ */
+ public native byte bDevCapabilityType();
+
+ /**
+ * Returns the bitmap of supported device level features.
+ *
+ * @return The supported device level features.
+ */
+ public native int bmAttributes();
+
+ /**
+ * Returns a dump of this descriptor.
+ *
+ * @return The descriptor dump.
+ */
+ public String dump()
+ {
+ return String.format("USB 2.0 Extension Descriptor:%n"
+ + " bLength %18d%n"
+ + " bDescriptorType %10d%n"
+ + " bDevCapabilityType %7d%n"
+ + " bmAttributes %13s%n",
+ bLength() & 0xff,
+ bDescriptorType() & 0xff,
+ bDevCapabilityType() & 0xff,
+ String.format("0x%08x", bmAttributes() & 0xffffffff));
+ }
+
+ @Override
+ public boolean equals(final Object obj)
+ {
+ if (obj == null) return false;
+ if (obj == this) return true;
+ if (obj.getClass() != getClass()) return false;
+ final Usb20ExtensionDescriptor other =
+ (Usb20ExtensionDescriptor) obj;
+ return new EqualsBuilder()
+ .append(bDescriptorType(), other.bDescriptorType())
+ .append(bLength(), other.bLength())
+ .append(bDevCapabilityType(), other.bDevCapabilityType())
+ .append(bmAttributes(), other.bmAttributes()).isEquals();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return new HashCodeBuilder()
+ .append(bLength())
+ .append(bDescriptorType())
+ .append(bDevCapabilityType())
+ .append(bmAttributes())
+ .toHashCode();
+ }
+
+ @Override
+ public String toString()
+ {
+ return dump();
+ }
+}
diff --git a/src/test/java/de/ailis/usb4java/libusb/LibUSBTest.java b/src/test/java/de/ailis/usb4java/libusb/LibUSBTest.java
index 536ba1c..280b3c5 100644
--- a/src/test/java/de/ailis/usb4java/libusb/LibUSBTest.java
+++ b/src/test/java/de/ailis/usb4java/libusb/LibUSBTest.java
@@ -731,6 +731,58 @@ public class LibUSBTest
LibUsb.freeBOSDescriptor(null);
}
+ /**
+ * Tests the
+ * {@link LibUsb#getUsb20ExtensionDescriptor(Context,
+ * BOSDevCapabilityDescriptor, Usb20ExtensionDescriptor)}
+ * method with uninitialized endpoint.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testGetUsb20ExtensionDescriptorWithUninitializedEndpoint()
+ {
+ assumeUsbTestsEnabled();
+ LibUsb.getUsb20ExtensionDescriptor(null,
+ new BOSDevCapabilityDescriptor(),
+ new Usb20ExtensionDescriptor());
+ }
+
+ /**
+ * Tests the
+ * {@link LibUsb#getUsb20ExtensionDescriptor(Context,
+ * BOSDevCapabilityDescriptor, Usb20ExtensionDescriptor)}
+ * method without descriptors.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetUsb20ExtensionDescriptorWithoutDescriptors()
+ {
+ assumeUsbTestsEnabled();
+ LibUsb.getUsb20ExtensionDescriptor(null, null, null);
+ }
+
+ /**
+ * Tests the
+ * {@link LibUsb#freeUsb20ExtensionDescriptor(Usb20ExtensionDescriptor)}
+ * method with uninitialized descriptor.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testFreeUsb20ExtensionDescriptorWithUninitializedDescriptor()
+ {
+ assumeUsbTestsEnabled();
+ LibUsb.freeUsb20ExtensionDescriptor(new Usb20ExtensionDescriptor());
+ }
+
+ /**
+ * Tests the
+ * {@link LibUsb#freeUsb20ExtensionDescriptor(Usb20ExtensionDescriptor)}
+ * method with null parameter. Must do nothing.
+ */
+ @Test
+ public void testFreeUsb20ExtensionDescriptorWithNull()
+ {
+ assumeUsbTestsEnabled();
+ LibUsb.freeUsb20ExtensionDescriptor(null);
+ }
+
/**
* Tests the
* {@link LibUsb#getDescriptor(DeviceHandle, int, int, ByteBuffer)} method
diff --git a/src/test/java/de/ailis/usb4java/libusb/Usb20ExtensionDescriptorTest.java b/src/test/java/de/ailis/usb4java/libusb/Usb20ExtensionDescriptorTest.java
new file mode 100644
index 0000000..5366863
--- /dev/null
+++ b/src/test/java/de/ailis/usb4java/libusb/Usb20ExtensionDescriptorTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 Klaus Reimer
+ * See LICENSE.md for licensing information.
+ */
+
+package de.ailis.usb4java.libusb;
+
+import static de.ailis.usb4java.test.UsbAssume.assumeUsbTestsEnabled;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests the {@link Usb20ExtensionDescriptor} class.
+ *
+ * @author Klaus Reimer (k@ailis.de)
+ */
+public class Usb20ExtensionDescriptorTest
+{
+ /** The test subject. */
+ private Usb20ExtensionDescriptor descriptor;
+
+ /**
+ * Setup test.
+ */
+ @Before
+ public void setUp()
+ {
+ assumeUsbTestsEnabled();
+ LibUsb.init(null);
+ this.descriptor = new Usb20ExtensionDescriptor();
+ }
+
+ /**
+ * Tear down test.
+ */
+ @After
+ public void tearDown()
+ {
+ LibUsb.exit(null);
+ }
+
+ /**
+ * Tests uninitialized access to
+ * {@link Usb20ExtensionDescriptor#bLength()}
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testUninitializedLength()
+ {
+ assumeUsbTestsEnabled();
+ this.descriptor.bLength();
+ }
+
+ /**
+ * Tests uninitialized access to
+ * {@link Usb20ExtensionDescriptor#bDescriptorType()}
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testUninitializedDescriptorType()
+ {
+ assumeUsbTestsEnabled();
+ this.descriptor.bDescriptorType();
+ }
+
+ /**
+ * Tests uninitialized access to
+ * {@link Usb20ExtensionDescriptor#bDevCapabilityType()}
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testUninitializedDevCapabilityType()
+ {
+ assumeUsbTestsEnabled();
+ this.descriptor.bDevCapabilityType();
+ }
+
+ /**
+ * Tests uninitialized access to
+ * {@link Usb20ExtensionDescriptor#bmAttributes()}
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testUninitializedAttributes()
+ {
+ assumeUsbTestsEnabled();
+ this.descriptor.bmAttributes();
+ }
+}