From 9b6eb1aa075f1e5bdbbf446937291a5e014df3f5 Mon Sep 17 00:00:00 2001 From: Klaus Reimer Date: Sun, 23 Jan 2011 00:03:08 +0100 Subject: [PATCH] Started using NIO buffers. --- .../src/main/java/de/ailis/usb4java/USB.java | 137 ++++++++++++++---- .../usb4java/USB_Descriptor_Header2.java | 60 ++++++++ .../ailis/usb4java/USB_String_Descriptor.java | 60 ++++++++ .../src/test/java/de/ailis/usb4java/Dump.java | 17 ++- usb4java-lib/src/main/c/Makefile.am | 3 +- usb4java-lib/src/main/c/USB.c | 57 ++++---- .../src/main/c/USB_String_Descriptor.c | 77 ++++++++++ 7 files changed, 351 insertions(+), 60 deletions(-) create mode 100644 usb4java-jni/src/main/java/de/ailis/usb4java/USB_Descriptor_Header2.java create mode 100644 usb4java-jni/src/main/java/de/ailis/usb4java/USB_String_Descriptor.java create mode 100644 usb4java-lib/src/main/c/USB_String_Descriptor.c diff --git a/usb4java-jni/src/main/java/de/ailis/usb4java/USB.java b/usb4java-jni/src/main/java/de/ailis/usb4java/USB.java index 3dc7dd6..4f64deb 100644 --- a/usb4java-jni/src/main/java/de/ailis/usb4java/USB.java +++ b/usb4java-jni/src/main/java/de/ailis/usb4java/USB.java @@ -5,7 +5,8 @@ package de.ailis.usb4java; -import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; /** @@ -24,6 +25,12 @@ import java.io.UnsupportedEncodingException; public class USB { + /** The maximum size of a descriptor. */ + private static final int MAX_DESCRIPTOR_SIZE = 256; + + /** The maximum size of a string. */ + private static final int MAX_STRING_SIZE = (MAX_DESCRIPTOR_SIZE - 2) / 2; + // === USB class constants =============================================== /** Per interface class. */ @@ -218,13 +225,11 @@ public class USB * The language id. * @param buffer * The buffer to write the string to. - * @param buflen - * The maximum number of bytes to read. * @return The number of bytes read or < 0 on error. */ public static native int usb_get_string(USB_Dev_Handle handle, - int index, int langid, byte[] buffer, int buflen); + int index, int langid, ByteBuffer buffer); /** @@ -238,23 +243,35 @@ public class USB * The language id. * @param size * The maximum number of bytes to read. - * @return The string or null if an error occurred. + * @return The string descriptor or null if an error occurred. */ - public static String usb_get_string(final USB_Dev_Handle handle, + public static USB_String_Descriptor usb_get_string(final USB_Dev_Handle handle, final int index, final int langid, final int size) { - final byte[] buffer = new byte[size]; - final int len = usb_get_string(handle, index, langid, buffer, size); + final ByteBuffer buffer = ByteBuffer.allocateDirect(size); + final int len = usb_get_string(handle, index, langid, buffer); if (len < 0) return null; - try - { - return new String(buffer, 0, len, "UTF-8"); - } - catch (final UnsupportedEncodingException e) - { - return new String(buffer, 0, len); - } + return new USB_String_Descriptor(buffer); + } + + + /** + * Returns a string descriptor from a device. + * + * @param handle + * The USB device handle. + * @param index + * The string description index. + * @param langid + * The language id. + * @return The string descriptor or null if an error occurred. + */ + + public static USB_String_Descriptor usb_get_string(final USB_Dev_Handle handle, + final int index, final int langid) + { + return usb_get_string(handle, index, langid, MAX_STRING_SIZE * 2); } @@ -272,40 +289,98 @@ public class USB * The string description index. * @param buffer * The buffer to write the string to. - * @param buflen - * The maximum number of bytes to read. * @return The number of bytes read or < 0 on error. */ public static native int usb_get_string_simple(USB_Dev_Handle handle, - int index, byte[] buffer, int buflen); + int index, ByteBuffer buffer); /** * Returns a string descriptor from a device using the first language. + * Unlike the native usb_get_string_simple() function this method returns + * the string in correct unicode encoding. * * @param handle * The USB device handle. * @param index * The string description index. * @param size - * The maximum number of bytes to read. + * The maximum number of characters to read. * @return The string or null if an error occurred. */ public static String usb_get_string_simple(final USB_Dev_Handle handle, final int index, final int size) { - final byte[] buffer = new byte[size]; - final int len = usb_get_string_simple(handle, index, buffer, size); - if (len < 0) return null; - try - { - return new String(buffer, 0, len, "ISO-8859-15"); - } - catch (final UnsupportedEncodingException e) - { - return new String(buffer, 0, len); - } + final short[] languages = usb_get_languages(handle); + if (languages == null) return null; + final short langid = languages.length == 0 ? 0 : languages[0]; + final USB_String_Descriptor descriptor = usb_get_string(handle, index, langid, size * 2); + if (descriptor == null) return null; + return descriptor.toString(); } + + + /** + * Returns a string descriptor from a device using the first language. + * Unlike the native usb_get_string_simple() function this method returns + * the string in correct unicode encoding. + * + * @param handle + * The USB device handle. + * @param index + * The string description index. + * @return The string or null if an error occurred. + */ + + public static String usb_get_string_simple(final USB_Dev_Handle handle, + final int index) + { + return usb_get_string_simple(handle, index, MAX_STRING_SIZE); + } + + + /** + * Returns the languages the specified device supports. + * + * @param handle + * The USB device handle. + * @return Array with supported language codes. + */ + + public static short[] usb_get_languages(final USB_Dev_Handle handle) + { + final ByteBuffer buffer = ByteBuffer + .allocateDirect(MAX_DESCRIPTOR_SIZE); + final int len = usb_get_descriptor(handle, USB_DT_STRING, 0, buffer); + if (len < 0) return null; + final short[] languages = new short[(len - 2) / 2]; + if (languages.length == 0) return languages; + buffer.position(2); + buffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(languages); + return languages; + } + + + /** + * Retrieves a descriptor from a device's default control pipe. + * + * usb_get_descriptor retrieves a descriptor from the device identified by + * the type and index of the descriptor from the default control pipe. + * Returns number of bytes read for the descriptor or < 0 on error. + * + * @param handle + * The device handle. + * @param type + * The descriptor type. + * @param index + * The descriptor index. + * @param buffer + * The buffer to put the read bytes in. + * @return Number of bytes read or < 0 on error. + */ + + public static native int usb_get_descriptor(USB_Dev_Handle handle, + int type, int index, ByteBuffer buffer); } diff --git a/usb4java-jni/src/main/java/de/ailis/usb4java/USB_Descriptor_Header2.java b/usb4java-jni/src/main/java/de/ailis/usb4java/USB_Descriptor_Header2.java new file mode 100644 index 0000000..0bfc57a --- /dev/null +++ b/usb4java-jni/src/main/java/de/ailis/usb4java/USB_Descriptor_Header2.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 Klaus Reimer + * See LICENSE.txt for licensing information. + */ + +package de.ailis.usb4java; + +import java.nio.ByteBuffer; + + +/** + * All standard descriptors have the two fields bLength and bDescriptorType + * in common. So this base class implements them. + * + * @author Klaus Reimer (k@ailis.de) + */ + +public abstract class USB_Descriptor_Header2 +{ + /** The descriptor data. */ + protected final ByteBuffer data; + + + /** + * Constructor. + * + * @param data + * The descriptor data. + */ + + public USB_Descriptor_Header2(final ByteBuffer data) + { + this.data = data; + this.data.limit(bLength()); + } + + + /** + * Returns the size of the descriptor in bytes. + * + * @return The size of the descriptor in bytes (unsigned byte). + */ + + public final int bLength() + { + return this.data.get(0) & 0xff; + } + + + /** + * Returns the interface descriptor type. + * + * @return The interface descriptor type (unsigned byte). + */ + + public final int bDescriptorType() + { + return this.data.get(1) & 0xff; + } +} diff --git a/usb4java-jni/src/main/java/de/ailis/usb4java/USB_String_Descriptor.java b/usb4java-jni/src/main/java/de/ailis/usb4java/USB_String_Descriptor.java new file mode 100644 index 0000000..08ae881 --- /dev/null +++ b/usb4java-jni/src/main/java/de/ailis/usb4java/USB_String_Descriptor.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 Klaus Reimer + * See LICENSE.txt for licensing information. + */ + +package de.ailis.usb4java; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.CharBuffer; + + +/** + * A string descriptor. + * + * @author Klaus Reimer (k@ailis.de) + */ + +public class USB_String_Descriptor extends USB_Descriptor_Header2 +{ + /** + * Constructor. + * + * @param data + * The descriptor data. + */ + + USB_String_Descriptor(final ByteBuffer data) + { + super(data); + } + + + /** + * Returns the string data in UTF-16 encoding. + * + * @return The string data. + */ + + public char[] wData() + { + this.data.position(2); + final CharBuffer chars = this.data.order(ByteOrder.LITTLE_ENDIAN) + .asCharBuffer(); + final char[] output = new char[chars.remaining()]; + chars.get(output); + return output; + } + + + /** + * @see java.lang.Object#toString() + */ + + @Override + public String toString() + { + return new String(wData()); + } +} diff --git a/usb4java-jni/src/test/java/de/ailis/usb4java/Dump.java b/usb4java-jni/src/test/java/de/ailis/usb4java/Dump.java index 48beab6..d365168 100644 --- a/usb4java-jni/src/test/java/de/ailis/usb4java/Dump.java +++ b/usb4java-jni/src/test/java/de/ailis/usb4java/Dump.java @@ -213,12 +213,23 @@ public class Dump dump_device_descriptor(device.descriptor()); level--; final USB_Dev_Handle handle = usb_open(device); - final String manufacturer = usb_get_string_simple(handle, device.descriptor().iManufacturer(), 255); + final String manufacturer = usb_get_string_simple(handle, device.descriptor().iManufacturer()); indent(); System.out.format("Manufacturer: %s\n", manufacturer != null ? manufacturer : "Unknown"); - final String product = usb_get_string_simple(handle, device.descriptor().iProduct(), 255); + final String product = usb_get_string_simple(handle, device.descriptor().iProduct()); indent(); System.out.format("Product: %s\n", product != null ? product : "Unknown"); - final String serialNumber = usb_get_string_simple(handle, device.descriptor().iSerialNumber(), 255); + final String serialNumber = usb_get_string_simple(handle, device.descriptor().iSerialNumber()); indent(); System.out.format("Serial: %s\n", serialNumber != null ? serialNumber : "Unknown"); + indent(); System.out.print("Languages:"); + final short[] languages = USB.usb_get_languages(handle); + if (languages != null) + { + for (final short language: languages) + System.out.format(" 0x%04x", language); + System.out.println(); + } + else + System.out.println("Unknown"); + usb_close(handle); indent(); System.out.format("config descriptors:\n"); level++; diff --git a/usb4java-lib/src/main/c/Makefile.am b/usb4java-lib/src/main/c/Makefile.am index e3c454f..8806972 100644 --- a/usb4java-lib/src/main/c/Makefile.am +++ b/usb4java-lib/src/main/c/Makefile.am @@ -12,5 +12,6 @@ libusb4java_la_SOURCES = \ USB_Dev_Handle.c \ USB_Interface.c \ USB_Interface_Descriptor.c \ - USB_Endpoint_Descriptor.c + USB_Endpoint_Descriptor.c \ + USB_String_Descriptor.c \ No newline at end of file diff --git a/usb4java-lib/src/main/c/USB.c b/usb4java-lib/src/main/c/USB.c index 5a11295..8a0445b 100644 --- a/usb4java-lib/src/main/c/USB.c +++ b/usb4java-lib/src/main/c/USB.c @@ -105,41 +105,48 @@ JNIEXPORT jint JNICALL METHOD_NAME(USB, usb_1close) JNIEXPORT jint JNICALL METHOD_NAME(USB, usb_1get_1string) ( JNIEnv *env, jclass class, jobject handle, jint index, jint langid, - jbyteArray buffer, jint buflen + jobject buffer ) { - char *buf = (char*) malloc(buflen); - int result = usb_get_string(unwrap_usb_dev_handle(env, handle), - index, langid, buf, buflen); - if (result >= 0) - { - (*env)->SetByteArrayRegion(env, buffer, 0, result, - (const jbyte *) buf); - } - free(buf); - return result; + void *buf = (*env)->GetDirectBufferAddress(env, buffer); + if (!buf) return -1; + jlong buflen = (*env)->GetDirectBufferCapacity(env, buffer); + return usb_get_string(unwrap_usb_dev_handle(env, handle), + index, langid, buf, buflen); } /** - * int usb_get_simple_string(USB_Handle handle, int index, byte[] buffer, - * int buflen) + * int usb_get_simple_string(USB_Handle handle, int index, ByteBuffer buffer) */ JNIEXPORT jint JNICALL METHOD_NAME(USB, usb_1get_1string_1simple) ( - JNIEnv *env, jclass class, jobject handle, jint index, jbyteArray buffer, - jint buflen + JNIEnv *env, jclass class, jobject handle, jint index, jobject buffer ) { - char *buf = (char*) malloc(buflen); - int result = usb_get_string_simple(unwrap_usb_dev_handle(env, handle), - index, buf, buflen); - if (result >= 0) - { - (*env)->SetByteArrayRegion(env, buffer, 0, result, - (const jbyte *) buf); - } - free(buf); - return result; + void *buf = (*env)->GetDirectBufferAddress(env, buffer); + if (!buf) return -1; + jlong buflen = (*env)->GetDirectBufferCapacity(env, buffer); + return usb_get_string_simple(unwrap_usb_dev_handle(env, handle), + index, buf, buflen); +} + + +/** + * int usb_get_descriptor(USB_Dev_Handle handle, int type, int index, + * ByteBuffer buffer); + */ + +JNIEXPORT jint JNICALL METHOD_NAME(USB, usb_1get_1descriptor) +( + JNIEnv *env, jclass class, jobject handle, int type, int index, + jobject buffer +) +{ + void *buf = (*env)->GetDirectBufferAddress(env, buffer); + if (!buf) return -1; + jlong buflen = (*env)->GetDirectBufferCapacity(env, buffer); + return usb_get_descriptor(unwrap_usb_dev_handle(env, handle), + type, index, buf, buflen); } diff --git a/usb4java-lib/src/main/c/USB_String_Descriptor.c b/usb4java-lib/src/main/c/USB_String_Descriptor.c new file mode 100644 index 0000000..a05416a --- /dev/null +++ b/usb4java-lib/src/main/c/USB_String_Descriptor.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2011 Klaus Reimer (k@ailis.de) + * See COPYING file for copying conditions + */ + +/** + * @name USB_String_Descriptor + * + * Native methods for the USB_String_Descriptor class. + * + * @author Klaus Reimer + */ + +#include +#include +#include "usb4java.h" + +/** + * Creates and returns a new USB string descriptor wrapper object. + * + * @param env + * The JNI environment. + * @param device + * The USB string descriptor. + * @return The USB string descriptor wrapper object. + */ + +jobject wrap_usb_string_descriptor(JNIEnv *env, + struct usb_string_descriptor *descriptor) +{ + if (!descriptor) return NULL; + jclass cls = (*env)->FindClass(env, + PACKAGE_DIR"/USB_String_Descriptor"); + if (cls == NULL) return NULL; + jmethodID constructor = (*env)->GetMethodID(env, cls, "", "(J)V"); + if (constructor == NULL) return NULL; + return (*env)->NewObject(env, cls, constructor, (long) descriptor); +} + + +/** + * Returns the wrapped USB string descriptor object from the specified + * wrapper object. + * + * @param env + * The JNI environment. + * @param obj + * The USB string descriptor wrapper object. + * @return The USB string descriptor object. + */ + +struct usb_string_descriptor *unwrap_usb_string_descriptor(JNIEnv *env, + jobject obj) +{ + jclass cls = (*env)->GetObjectClass(env, obj); + jfieldID field = (*env)->GetFieldID(env, cls, "pointer", "J"); + return (struct usb_string_descriptor *) ((*env)->GetLongField(env, + obj, field)); +} + + +/** + * char[] wData() + */ + +JNIEXPORT jcharArray JNICALL METHOD_NAME(USB_1String_1Descriptor, wData) +( + JNIEnv *env, jobject this +) +{ + struct usb_string_descriptor *descriptor = unwrap_usb_string_descriptor(env, this); + int size = (descriptor->bLength - 2) / 2; + jcharArray array = (*env)->NewByteArray(env, size); + (*env)->SetCharArrayRegion(env, array, 0, size, + (const jchar *) descriptor->wData); + return array; +}