diff --git a/src/main/c/.cproject b/src/main/c/.cproject new file mode 100644 index 0000000..ed1d1d9 --- /dev/null +++ b/src/main/c/.cproject @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/c/src/Context.c b/src/main/c/src/Context.c index cf43868..275b3b5 100644 --- a/src/main/c/src/Context.c +++ b/src/main/c/src/Context.c @@ -10,6 +10,11 @@ void setContext(JNIEnv* env, const libusb_context* context, jobject object) SET_POINTER(env, context, object, "contextPointer"); } +jobject wrapContext(JNIEnv* env, const libusb_context* context) +{ + WRAP_POINTER(env, context, "Context", "contextPointer"); +} + libusb_context* unwrapContext(JNIEnv* env, jobject context) { UNWRAP_POINTER(env, context, libusb_context*, "contextPointer"); diff --git a/src/main/c/src/Context.h b/src/main/c/src/Context.h index 24c45c3..dae4fce 100644 --- a/src/main/c/src/Context.h +++ b/src/main/c/src/Context.h @@ -9,6 +9,7 @@ #include "usb4java.h" void setContext(JNIEnv*, const libusb_context*, jobject); +jobject wrapContext(JNIEnv*, const libusb_context*); libusb_context* unwrapContext(JNIEnv*, jobject); void resetContext(JNIEnv*, jobject); diff --git a/src/main/c/src/HotplugCallbackHandle.c b/src/main/c/src/HotplugCallbackHandle.c new file mode 100644 index 0000000..b641b1f --- /dev/null +++ b/src/main/c/src/HotplugCallbackHandle.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013 Luca Longinotti (l@longi.li) + * See COPYING file for copying conditions + */ + +#include "HotplugCallbackHandle.h" + +void setHotplugCallbackHandle(JNIEnv* env, const libusb_hotplug_callback_handle hotplugHandle, + jobject object) +{ + SET_POINTER(env, hotplugHandle, object, "hotplugCallbackHandleValue"); +} + +libusb_hotplug_callback_handle unwrapHotplugCallbackHandle(JNIEnv* env, jobject hotplugHandle) +{ + // Hotplug callback handles are integers, starting at 1. As such, we can use the same logic + // as for pointers, and consider 0 to be uninitialized/invalid. + if (!hotplugHandle) return 0; + jclass cls = (*env)->GetObjectClass(env, hotplugHandle); + jfieldID field = (*env)->GetFieldID(env, cls, "hotplugCallbackHandleValue", "J"); + jlong val = (*env)->GetLongField(env, hotplugHandle, field); + if (!val) illegalState(env, "hotplugCallbackHandleValue is not initialized"); + return (libusb_hotplug_callback_handle) val; +} + +void resetHotplugCallbackHandle(JNIEnv* env, jobject hotplugHandle) +{ + RESET_POINTER(env, hotplugHandle, "hotplugCallbackHandleValue"); +} diff --git a/src/main/c/src/HotplugCallbackHandle.h b/src/main/c/src/HotplugCallbackHandle.h new file mode 100644 index 0000000..736efd0 --- /dev/null +++ b/src/main/c/src/HotplugCallbackHandle.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2013 Luca Longinotti (l@longi.li) + * See COPYING file for copying conditions + */ + +#ifndef USB4JAVA_HOTPLUG_CALLBACK_HANDLE_H +#define USB4JAVA_HOTPLUG_CALLBACK_HANDLE_H + +#include "usb4java.h" + +void setHotplugCallbackHandle(JNIEnv*, const libusb_hotplug_callback_handle, jobject); +libusb_hotplug_callback_handle unwrapHotplugCallbackHandle(JNIEnv*, jobject); +void resetHotplugCallbackHandle(JNIEnv*, jobject); + +#endif diff --git a/src/main/c/src/LibUsb.c b/src/main/c/src/LibUsb.c index e61771b..bd69f8b 100644 --- a/src/main/c/src/LibUsb.c +++ b/src/main/c/src/LibUsb.c @@ -31,6 +31,7 @@ #include "SsUsbDeviceCapabilityDescriptor.h" #include "ContainerIdDescriptor.h" #include "Transfer.h" +#include "HotplugCallbackHandle.h" static int defaultContextRefcnt = 0; @@ -1407,3 +1408,86 @@ JNIEXPORT jint JNICALL METHOD_NAME(LibUsb, cancelTransfer) return libusb_cancel_transfer(transfer); } + +struct hotplug_data +{ + jobject callbackObject; + jmethodID callbackObjectMethod; + jobject callbackUserDataObject; +}; + +static int LIBUSB_CALL hotplugCallback(libusb_context *ctx, + libusb_device *device, libusb_hotplug_event event, void *user_data) +{ + THREAD_BEGIN(env) + + // TODO: check for return value and free memory appropriately. + + THREAD_END +} + +/** + * int hotplugRegisterCallback(Context, int, int, short, short, byte, + * HotplugCallback, Object, HotplugCallbackHandle) + */ +JNIEXPORT jint JNICALL METHOD_NAME(LibUsb, hotplugRegisterCallback) +( + JNIEnv *env, jclass class, jobject context, jint events, jint flags, + jshort vendorId, jshort productId, jbyte deviceClass, + jobject callback, jobject userData, jobject callbackHandle +) +{ + libusb_context *ctx = unwrapContext(env, context); + if (!ctx && context) return 0; + NOT_NULL(env, callback, return 0); + if (callbackHandle) { + // If callbackHandle is set, the Java object must be fresh/empty. + NOT_SET(env, callbackHandle, "hotplugCallbackHandleValue", return 0); + } + + // Allocate memory to hold the references to the callback on the C side. + struct hotplug_data *hotplugData = calloc(1, sizeof(*hotplugData)); + if (!hotplugData) + { + return LIBUSB_ERROR_NO_MEM; + } + + // Setup the needed global references to the callback objects. + hotplugData->callbackObject = NULL; + hotplugData->callbackObjectMethod = NULL; + hotplugData->callbackUserDataObject = userData; + + // Register the callback. + libusb_hotplug_callback_handle handle = 0; + int result = libusb_hotplug_register_callback(ctx, events, flags, + vendorId, productId, deviceClass, &hotplugCallback, hotplugData, &handle); + + // If callbackHandle is set and registering was successful, we set the handle + // to the value we've gotten from libusb. + if (callbackHandle && (result == LIBUSB_SUCCESS)) { + setHotplugCallbackHandle(env, handle, callbackHandle); + } + + return result; +} + +/* + * void hotplugDeregisterCallback(Context, HotplugCallbackHandle) + */ +JNIEXPORT void JNICALL METHOD_NAME(LibUsb, hotplugDeregisterCallback) +( + JNIEnv *env, jclass class, jobject context, jobject callbackHandle +) +{ + libusb_context *ctx = unwrapContext(env, context); + if (!ctx && context) return; + NOT_NULL(env, callbackHandle, return); + + libusb_hotplug_callback_handle handle = + unwrapHotplugCallbackHandle(env, callbackHandle); + if (!handle) return; + + + + resetHotplugCallbackHandle(env, callbackHandle); +} diff --git a/src/main/c/src/Makefile.am b/src/main/c/src/Makefile.am index abae2e5..2530c26 100644 --- a/src/main/c/src/Makefile.am +++ b/src/main/c/src/Makefile.am @@ -24,4 +24,5 @@ libusb4java_la_SOURCES = \ BosDevCapabilityDescriptor.c \ Usb20ExtensionDescriptor.c \ SsUsbDeviceCapabilityDescriptor.c \ - ContainerIdDescriptor.c + ContainerIdDescriptor.c \ + HotplugCallbackHandle.c diff --git a/src/main/java/de/ailis/usb4java/libusb/HotplugCallback.java b/src/main/java/de/ailis/usb4java/libusb/HotplugCallback.java new file mode 100644 index 0000000..f87ff6a --- /dev/null +++ b/src/main/java/de/ailis/usb4java/libusb/HotplugCallback.java @@ -0,0 +1,11 @@ +/* + * Copyright 2013 Luca Longinotti + * See LICENSE.md for licensing information. + */ + +package de.ailis.usb4java.libusb; + +public interface HotplugCallback +{ + int processEvent(Context context, Device device, int event, Object userData); +} diff --git a/src/main/java/de/ailis/usb4java/libusb/HotplugCallbackHandle.java b/src/main/java/de/ailis/usb4java/libusb/HotplugCallbackHandle.java new file mode 100644 index 0000000..1882ae3 --- /dev/null +++ b/src/main/java/de/ailis/usb4java/libusb/HotplugCallbackHandle.java @@ -0,0 +1,98 @@ +/* + * Copyright 2013 Luca Longinotti + * 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; + +/** + * Hotplug Callback Handle. + * + * Callback handles are generated by {@link LibUsb#hotplugRegisterCallback(Context, + * int, int, short, short, byte, HotplugCallback, Object, HotplugCallbackHandle)} + * and can be used to deregister callbacks. Callback handles are unique + * per {@link Context} and it is safe to call + * {@link LibUsb#hotplugDeregisterCallback(Context, HotplugCallbackHandle)} + * on an already deregistered callback. + * + * @author Luca Longinotti (l@longi.li) + */ +public final class HotplugCallbackHandle +{ + /** The hotplug callback handle, it's an integer (int) in C. */ + private long hotplugCallbackHandleValue; + + /** + * Constructs a new hotplug callback handle. Must be passed to + * {@link LibUsb#hotplugRegisterCallback(Context, int, int, short, short, + * byte, HotplugCallback, Object, HotplugCallbackHandle)} before passing it + * to any other method. + */ + public HotplugCallbackHandle() + { + // Empty + } + + /** + * Returns the hotplug callback handle value. + * + * @return The hotplug callback handle value. + */ + public long getValue() + { + return this.hotplugCallbackHandleValue; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = (prime * result) + + (int) (this.hotplugCallbackHandleValue ^ (this.hotplugCallbackHandleValue >>> 32)); + return result; + } + + @Override + public boolean equals(final Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (this.getClass() != obj.getClass()) + { + return false; + } + final HotplugCallbackHandle other = (HotplugCallbackHandle) obj; + if (this.hotplugCallbackHandleValue != other.hotplugCallbackHandleValue) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return String.format("libusb hotplug callback handle 0x%x", + this.hotplugCallbackHandleValue); + } +} diff --git a/src/main/java/de/ailis/usb4java/libusb/LibUsb.java b/src/main/java/de/ailis/usb4java/libusb/LibUsb.java index 0bce108..de0669a 100644 --- a/src/main/java/de/ailis/usb4java/libusb/LibUsb.java +++ b/src/main/java/de/ailis/usb4java/libusb/LibUsb.java @@ -34,6 +34,7 @@ import de.ailis.usb4java.utils.BufferUtils; * Static class providing the constants and functions of libusb. * * @author Klaus Reimer (k@ailis.de) + * @author Luca Longinotti (l@longi.li) */ public final class LibUsb { @@ -589,6 +590,32 @@ public final class LibUsb /** Device sent more data than requested. */ public static final int TRANSFER_OVERFLOW = 6; + // Flags for hotplug events + + /** + * Arm the callback and fire it for all matching currently attached devices. + */ + public static final int HOTPLUG_ENUMERATE = 1; + + // Hotplug events + + /** A device has been plugged in and is ready to use. */ + public static final int HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01; + + /** + * A device has left and is no longer available. + * It is the user's responsibility to call {@link #close(DeviceHandle)} on + * any handle associated with a disconnected device. + * It is safe to call {@link #getDeviceDescriptor(Device, DeviceDescriptor)} + * on a device that has left. + */ + public static final int HOTPLUG_EVENT_DEVICE_LEFT = 0x02; + + // Wildcard matching for hotplug events + + /** Match any vendorId or productId or deviceClass. */ + public static final byte HOTPLUG_MATCH_ANY = -1; + /** * pollfd listeners (to support different listeners for different contexts). */ @@ -2008,8 +2035,8 @@ public final class LibUsb * * The only way to implement this in Java is by passing a direct buffer, and * then accessing memory directly. IntBuffers can be direct, if they are - * created as a view of a direct ByteBuffer, as in the following code: - * {@link BufferUtils.allocateIntBuffer()} + * created as a view of a direct ByteBuffer, by using BufferUtils: + * {@link BufferUtils#allocateIntBuffer()} * * @param context * the context to operate on, or NULL for the default context @@ -2517,4 +2544,56 @@ public final class LibUsb return BufferUtils.slice(transfer.buffer(), offset, isoDescriptors[packet].length()); } + + /** + * Register a hotplug callback function. + * + * Register a callback with the {@link Context}. The callback will fire + * when a matching event occurs on a matching device. The callback is + * armed until either it is deregistered with + * {@link #hotplugDeregisterCallback(Context, HotplugCallbackHandle)} + * or the supplied callback returns 1 to indicate it is finished processing + * events. + * + * @param context + * context to register this callback with + * @param events + * bitwise or of events that will trigger this callback + * @param flags + * hotplug callback flags + * @param vendorId + * the vendor id to match or {@link #HOTPLUG_MATCH_ANY} + * @param productId + * the product id to match or {@link #HOTPLUG_MATCH_ANY} + * @param deviceClass + * the device class to match or {@link #HOTPLUG_MATCH_ANY} + * @param callback + * the function to be invoked on a matching event/device + * @param userData + * user data to pass to the callback function + * @param handle + * hotplug callback handle of the allocated callback. Only needed + * if you later want to deregister this callback, can be NULL. + * + * @return LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + */ + public static native int hotplugRegisterCallback(final Context context, + final int events, final int flags, final short vendorId, + final short productId, final byte deviceClass, + final HotplugCallback callback, final Object userData, + final HotplugCallbackHandle callbackHandle); + + /** + * Deregisters a hotplug callback. + * + * Deregister a callback from a {@link Context}. This function is safe to + * call from within a hotplug callback. + * + * @param context + * context this callback is registered with + * @param handle + * the handle of the callback to deregister + */ + public static native void hotplugDeregisterCallback(final Context context, + final HotplugCallbackHandle callbackHandle); }