Complete hotplug support, with full support for deregistration.
This commit is contained in:
parent
a66013a393
commit
ef88482436
1
src/main/c/.gitignore
vendored
1
src/main/c/.gitignore
vendored
@ -26,3 +26,4 @@ m4
|
||||
missing
|
||||
ar-lib
|
||||
compile
|
||||
.cproject
|
||||
|
||||
@ -1272,14 +1272,14 @@ static void LIBUSB_CALL triggerPollfdAdded(int fd, short events, void *user_data
|
||||
{
|
||||
THREAD_BEGIN(env)
|
||||
|
||||
jclass fdcls = (*env)->FindClass(env, "java/io/FileDescriptor");
|
||||
jmethodID constructor = (*env)->GetMethodID(env, fdcls, "<init>", "(I)V");
|
||||
jobject object = (*env)->NewObject(env, fdcls, constructor, fd);
|
||||
jclass fdCls = (*env)->FindClass(env, "java/io/FileDescriptor");
|
||||
jmethodID fdConstructor = (*env)->GetMethodID(env, fdCls, "<init>", "(I)V");
|
||||
jobject fdObject = (*env)->NewObject(env, fdCls, fdConstructor, fd);
|
||||
|
||||
jclass cls = (*env)->FindClass(env, PACKAGE_DIR"/LibUsb");
|
||||
jmethodID method = (*env)->GetStaticMethodID(env, cls,
|
||||
"triggerPollfdAdded", "(Ljava/io/FileDescriptor;IJ)V");
|
||||
(*env)->CallStaticVoidMethod(env, cls, method, object, (jint) events,
|
||||
(*env)->CallStaticVoidMethod(env, cls, method, fdObject, (jint) events,
|
||||
(jlong) (intptr_t) user_data);
|
||||
|
||||
THREAD_END
|
||||
@ -1289,38 +1289,37 @@ static void LIBUSB_CALL triggerPollfdRemoved(int fd, void *user_data)
|
||||
{
|
||||
THREAD_BEGIN(env)
|
||||
|
||||
jclass fdcls = (*env)->FindClass(env, "java/io/FileDescriptor");
|
||||
jmethodID constructor = (*env)->GetMethodID(env, fdcls, "<init>", "(I)V");
|
||||
jobject object = (*env)->NewObject(env, fdcls, constructor, fd);
|
||||
jclass fdCls = (*env)->FindClass(env, "java/io/FileDescriptor");
|
||||
jmethodID fdConstructor = (*env)->GetMethodID(env, fdCls, "<init>", "(I)V");
|
||||
jobject fdObject = (*env)->NewObject(env, fdCls, fdConstructor, fd);
|
||||
|
||||
jclass cls = (*env)->FindClass(env, PACKAGE_DIR"/LibUsb");
|
||||
jmethodID method = (*env)->GetStaticMethodID(env, cls,
|
||||
"triggerPollfdRemoved", "(Ljava/io/FileDescriptor;J)V");
|
||||
(*env)->CallStaticVoidMethod(env, cls, method, object,
|
||||
(*env)->CallStaticVoidMethod(env, cls, method, fdObject,
|
||||
(jlong) (intptr_t) user_data);
|
||||
|
||||
THREAD_END
|
||||
}
|
||||
|
||||
/**
|
||||
* void setPollfdNotifiers(Context, long)
|
||||
* void setPollfdNotifiersNative(Context, long)
|
||||
*/
|
||||
JNIEXPORT void JNICALL METHOD_NAME(LibUsb, setPollfdNotifiers)
|
||||
JNIEXPORT void JNICALL METHOD_NAME(LibUsb, setPollfdNotifiersNative)
|
||||
(
|
||||
JNIEnv *env, jclass class, jobject context, jlong context_id
|
||||
)
|
||||
{
|
||||
libusb_context *ctx = unwrapContext(env, context);
|
||||
if (!ctx && context) return;
|
||||
|
||||
libusb_set_pollfd_notifiers(ctx, &triggerPollfdAdded, &triggerPollfdRemoved,
|
||||
(void *) (intptr_t) context_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* void unsetPollfdNotifiers(Context)
|
||||
* void unsetPollfdNotifiersNative(Context)
|
||||
*/
|
||||
JNIEXPORT void JNICALL METHOD_NAME(LibUsb, unsetPollfdNotifiers)
|
||||
JNIEXPORT void JNICALL METHOD_NAME(LibUsb, unsetPollfdNotifiersNative)
|
||||
(
|
||||
JNIEnv *env, jclass class, jobject context
|
||||
)
|
||||
@ -1409,62 +1408,55 @@ 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,
|
||||
static int LIBUSB_CALL hotplugCallback(libusb_context *context,
|
||||
libusb_device *device, libusb_hotplug_event event, void *user_data)
|
||||
{
|
||||
THREAD_BEGIN(env)
|
||||
|
||||
// TODO: check for return value and free memory appropriately.
|
||||
jobject ctx = wrapContext(env, context);
|
||||
jobject dev = wrapDevice(env, device);
|
||||
|
||||
jclass cls = (*env)->FindClass(env, PACKAGE_DIR"/LibUsb");
|
||||
jmethodID method = (*env)->GetStaticMethodID(env, cls,
|
||||
"hotplugCallback", "(L"PACKAGE_DIR"/Context;L"PACKAGE_DIR"/Device;IJ)I");
|
||||
int result = (*env)->CallStaticIntMethod(env, cls, method, ctx, dev,
|
||||
(jint) event, (jlong) (intptr_t) user_data);
|
||||
|
||||
THREAD_END
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* int hotplugRegisterCallback(Context, int, int, short, short, byte,
|
||||
* HotplugCallback, Object, HotplugCallbackHandle)
|
||||
* int hotplugRegisterCallbackNative(Context, int, int, short, short, byte,
|
||||
* HotplugCallbackHandle, long)
|
||||
*/
|
||||
JNIEXPORT jint JNICALL METHOD_NAME(LibUsb, hotplugRegisterCallback)
|
||||
JNIEXPORT jint JNICALL METHOD_NAME(LibUsb, hotplugRegisterCallbackNative)
|
||||
(
|
||||
JNIEnv *env, jclass class, jobject context, jint events, jint flags,
|
||||
jshort vendorId, jshort productId, jbyte deviceClass,
|
||||
jobject callback, jobject userData, jobject callbackHandle
|
||||
jobject callbackHandle, jlong hotplugId
|
||||
)
|
||||
{
|
||||
libusb_context *ctx = unwrapContext(env, context);
|
||||
if (!ctx && context) return 0;
|
||||
NOT_NULL(env, callback, return 0);
|
||||
if (callbackHandle) {
|
||||
|
||||
if (callbackHandle != NULL)
|
||||
{
|
||||
// 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;
|
||||
libusb_hotplug_callback_handle handle;
|
||||
int result = libusb_hotplug_register_callback(ctx, events, flags,
|
||||
vendorId, productId, deviceClass, &hotplugCallback, hotplugData, &handle);
|
||||
vendorId, productId, deviceClass, &hotplugCallback,
|
||||
(void *) (intptr_t) hotplugId, &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)) {
|
||||
if ((callbackHandle != NULL) && (result == LIBUSB_SUCCESS))
|
||||
{
|
||||
setHotplugCallbackHandle(env, handle, callbackHandle);
|
||||
}
|
||||
|
||||
@ -1472,22 +1464,25 @@ JNIEXPORT jint JNICALL METHOD_NAME(LibUsb, hotplugRegisterCallback)
|
||||
}
|
||||
|
||||
/*
|
||||
* void hotplugDeregisterCallback(Context, HotplugCallbackHandle)
|
||||
* long hotplugDeregisterCallbackNative(Context, HotplugCallbackHandle)
|
||||
*/
|
||||
JNIEXPORT void JNICALL METHOD_NAME(LibUsb, hotplugDeregisterCallback)
|
||||
JNIEXPORT jlong JNICALL METHOD_NAME(LibUsb, hotplugDeregisterCallbackNative)
|
||||
(
|
||||
JNIEnv *env, jclass class, jobject context, jobject callbackHandle
|
||||
)
|
||||
{
|
||||
libusb_context *ctx = unwrapContext(env, context);
|
||||
if (!ctx && context) return;
|
||||
NOT_NULL(env, callbackHandle, return);
|
||||
if (!ctx && context) return 0;
|
||||
NOT_NULL(env, callbackHandle, return 0);
|
||||
|
||||
libusb_hotplug_callback_handle handle =
|
||||
unwrapHotplugCallbackHandle(env, callbackHandle);
|
||||
if (!handle) return;
|
||||
|
||||
if (!handle) return 0;
|
||||
|
||||
// Deregister the callback.
|
||||
libusb_hotplug_deregister_callback(ctx, handle);
|
||||
|
||||
resetHotplugCallbackHandle(env, callbackHandle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -617,7 +617,15 @@ public final class LibUsb
|
||||
public static final byte HOTPLUG_MATCH_ANY = -1;
|
||||
|
||||
/**
|
||||
* pollfd listeners (to support different listeners for different contexts).
|
||||
* Hotplug callbacks (to correctly manage calls and additional data).
|
||||
*/
|
||||
private static long globalHotplugId = 1;
|
||||
|
||||
private static final ConcurrentMap<Long, ImmutablePair<HotplugCallback, Object>> hotplugCallbacks =
|
||||
new ConcurrentHashMap<Long, ImmutablePair<HotplugCallback, Object>>();
|
||||
|
||||
/**
|
||||
* Pollfd listeners (to support different listeners for different contexts).
|
||||
*/
|
||||
private static final ConcurrentMap<Long, ImmutablePair<PollfdListener, Object>> pollfdListeners =
|
||||
new ConcurrentHashMap<Long, ImmutablePair<PollfdListener, Object>>();
|
||||
@ -2228,7 +2236,7 @@ public final class LibUsb
|
||||
* User data to be passed back to callbacks (useful for passing
|
||||
* context information).
|
||||
*/
|
||||
public static void setPollfdNotifiers(final Context context,
|
||||
public static synchronized void setPollfdNotifiers(final Context context,
|
||||
final PollfdListener listener, final Object userData)
|
||||
{
|
||||
long contextId;
|
||||
@ -2244,13 +2252,13 @@ public final class LibUsb
|
||||
|
||||
if (listener == null)
|
||||
{
|
||||
unsetPollfdNotifiers(context);
|
||||
unsetPollfdNotifiersNative(context);
|
||||
|
||||
pollfdListeners.remove(contextId);
|
||||
}
|
||||
else
|
||||
{
|
||||
setPollfdNotifiers(context, contextId);
|
||||
setPollfdNotifiersNative(context, contextId);
|
||||
|
||||
pollfdListeners.put(contextId,
|
||||
new ImmutablePair<PollfdListener, Object>(listener, userData));
|
||||
@ -2309,7 +2317,7 @@ public final class LibUsb
|
||||
* @param contextId
|
||||
* A unique identifier for the given context.
|
||||
*/
|
||||
static native void setPollfdNotifiers(final Context context,
|
||||
static native void setPollfdNotifiersNative(final Context context,
|
||||
final long contextId);
|
||||
|
||||
/**
|
||||
@ -2319,7 +2327,7 @@ public final class LibUsb
|
||||
* @param context
|
||||
* The context to operate on, or NULL for the default context
|
||||
*/
|
||||
static native void unsetPollfdNotifiers(final Context context);
|
||||
static native void unsetPollfdNotifiersNative(final Context context);
|
||||
|
||||
/**
|
||||
* Allocate a libusb transfer without support for isochronous transfers.
|
||||
@ -2545,14 +2553,39 @@ public final class LibUsb
|
||||
isoDescriptors[packet].length());
|
||||
}
|
||||
|
||||
static int hotplugCallback(final Context context, final Device device,
|
||||
final int event, final long hotplugId)
|
||||
{
|
||||
final ImmutablePair<HotplugCallback, Object> callback = hotplugCallbacks
|
||||
.get(hotplugId);
|
||||
|
||||
int result = 0;
|
||||
|
||||
if (callback != null)
|
||||
{
|
||||
result = callback.left.processEvent(context, device, event,
|
||||
callback.right);
|
||||
}
|
||||
|
||||
// If callback indicates it is finished, it will get deregistered
|
||||
// automatically. As such, we have to remove it from the Java
|
||||
// map, like when deregistering manually.
|
||||
if (result == 1)
|
||||
{
|
||||
hotplugCallbacks.remove(hotplugId);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link #hotplugDeregisterCallback(Context, HotplugCallbackHandle)} or the
|
||||
* supplied callback returns 1 to indicate it is finished processing
|
||||
* events.
|
||||
*
|
||||
* @param context
|
||||
@ -2577,11 +2610,37 @@ public final class LibUsb
|
||||
*
|
||||
* @return LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
|
||||
*/
|
||||
public static native int hotplugRegisterCallback(final Context context,
|
||||
public static synchronized 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)
|
||||
{
|
||||
if (callback == null)
|
||||
{
|
||||
throw new IllegalArgumentException("callback must not be null");
|
||||
}
|
||||
|
||||
final int result = hotplugRegisterCallbackNative(context, events,
|
||||
flags, vendorId, productId, deviceClass, callbackHandle,
|
||||
globalHotplugId);
|
||||
|
||||
if (result == LibUsb.SUCCESS)
|
||||
{
|
||||
hotplugCallbacks.put(globalHotplugId,
|
||||
new ImmutablePair<HotplugCallback, Object>(callback, userData));
|
||||
|
||||
// Increment globalHotplugId by one, like the libusb handle.
|
||||
globalHotplugId++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static native int hotplugRegisterCallbackNative(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);
|
||||
final HotplugCallbackHandle callbackHandle, final long hotplugId);
|
||||
|
||||
/**
|
||||
* Deregisters a hotplug callback.
|
||||
@ -2594,6 +2653,24 @@ public final class LibUsb
|
||||
* @param handle
|
||||
* the handle of the callback to deregister
|
||||
*/
|
||||
public static native void hotplugDeregisterCallback(final Context context,
|
||||
public static void hotplugDeregisterCallback(final Context context,
|
||||
final HotplugCallbackHandle callbackHandle)
|
||||
{
|
||||
final long handle = hotplugDeregisterCallbackNative(context,
|
||||
callbackHandle);
|
||||
|
||||
// When a handle is assigned by a register call, its value is the same
|
||||
// as the one of globalHotplugId at that moment, which is what's used
|
||||
// to identify data in the hotplugCallbacks map.
|
||||
// This is because globalHotplugId pretty much mirrors the behavior of
|
||||
// the handle: integer starting at 1, incremented each time by one.
|
||||
// Problems could arise from concurrency, but are completely avoided by
|
||||
// fully serializing register calls.
|
||||
// As such, we can use the handle value from callbackHandle to
|
||||
// correctly remove the data from the hotplugCallbacks map.
|
||||
hotplugCallbacks.remove(handle);
|
||||
}
|
||||
|
||||
static native long hotplugDeregisterCallbackNative(final Context context,
|
||||
final HotplugCallbackHandle callbackHandle);
|
||||
}
|
||||
|
||||
@ -1160,27 +1160,27 @@ public class LibUSBTest
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link LibUsb#setPollfdNotifiers(Context, long)} with uninitialized
|
||||
* USB context.
|
||||
* Tests {@link LibUsb#setPollfdNotifiersNative(Context, long)}
|
||||
* with uninitialized USB context.
|
||||
*/
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testSetPollfdNotifiersWithUninitializedContext()
|
||||
{
|
||||
assumeUsbTestsEnabled();
|
||||
final Context context = new Context();
|
||||
LibUsb.setPollfdNotifiers(context, context.getPointer());
|
||||
LibUsb.setPollfdNotifiersNative(context, context.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link LibUsb#unsetPollfdNotifiers(Context)} with uninitialized USB
|
||||
* context.
|
||||
* Tests {@link LibUsb#unsetPollfdNotifiersNative(Context)} with
|
||||
* uninitialized USB context.
|
||||
*/
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testUnsetPollfdNotifiersWithUninitializedContext()
|
||||
{
|
||||
assumeUsbTestsEnabled();
|
||||
final Context context = new Context();
|
||||
LibUsb.unsetPollfdNotifiers(context);
|
||||
LibUsb.unsetPollfdNotifiersNative(context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user