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