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(); + } +}