Start work on Hotplug support.

This commit is contained in:
Luca Longinotti 2013-07-16 17:12:47 +02:00
parent 8d6a1f0d50
commit 85111765e9
10 changed files with 390 additions and 3 deletions

64
src/main/c/.cproject Normal file
View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.394788388">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.394788388" moduleId="org.eclipse.cdt.core.settings" name="Build (GNU)">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.Cygwin_PE" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildProperties="" description="" id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.394788388" name="Build (GNU)" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.394788388.1088432350" name="/" resourcePath="">
<toolChain id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.1733067510" name="org.eclipse.linuxtools.cdt.autotools.core.toolChain" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolChain">
<targetPlatform id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform.451986690" isAbstract="false" name="GNU Autotools Target Platform" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform"/>
<builder enableAutoBuild="true" id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder.177220660" keepEnvironmentInBuildfile="false" managedBuildOn="false" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder"/>
<tool id="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure.1517814383" name="configure" superClass="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure">
<option id="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name.28633164" name="Name" superClass="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name" value="org.eclipse.linuxtools.cdt.autotools.core.toolChain.394788388" valueType="string"/>
</tool>
<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen.1780153072" name="autogen.sh" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen"/>
<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.1015446012" name="GCC C Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc">
<option id="gnu.c.compiler.option.include.paths.10958235" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
<listOptionValue builtIn="false" value="/usr/java/jdk1.7.0_25/include"/>
<listOptionValue builtIn="false" value="/usr/java/jdk1.7.0_25/include/linux"/>
<listOptionValue builtIn="false" value="/usr/local/include/libusb-1.0"/>
</option>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.2015853583" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.1569144164" name="GCC C++ Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp"/>
</toolChain>
</folderInfo>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="usb4java-c.null.656890687" name="usb4java-c"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope" versionNumber="2">
<configuration configurationName="Build (GNU)">
<resource resourceType="PROJECT" workspacePath="/usb4java-c"/>
</configuration>
</storageModule>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.so.debug.696476069;cdt.managedbuild.config.gnu.so.debug.696476069.;cdt.managedbuild.tool.gnu.c.compiler.so.debug.409762159;cdt.managedbuild.tool.gnu.c.compiler.input.1732681239">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.toolChain.394788388;org.eclipse.linuxtools.cdt.autotools.core.toolChain.394788388.1088432350;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.1015446012;cdt.managedbuild.tool.gnu.c.compiler.input.2015853583">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.so.release.1454871484;cdt.managedbuild.config.gnu.so.release.1454871484.;cdt.managedbuild.tool.gnu.c.compiler.so.release.2113262423;cdt.managedbuild.tool.gnu.c.compiler.input.82897281">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
</cproject>

View File

@ -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");

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -24,4 +24,5 @@ libusb4java_la_SOURCES = \
BosDevCapabilityDescriptor.c \
Usb20ExtensionDescriptor.c \
SsUsbDeviceCapabilityDescriptor.c \
ContainerIdDescriptor.c
ContainerIdDescriptor.c \
HotplugCallbackHandle.c

View File

@ -0,0 +1,11 @@
/*
* Copyright 2013 Luca Longinotti <l@longi.li>
* See LICENSE.md for licensing information.
*/
package de.ailis.usb4java.libusb;
public interface HotplugCallback
{
int processEvent(Context context, Device device, int event, Object userData);
}

View File

@ -0,0 +1,98 @@
/*
* Copyright 2013 Luca Longinotti <l@longi.li>
* See LICENSE.md for licensing information.
*
* Based on libusb <http://www.libusb.org/>:
*
* Copyright 2001 Johannes Erdfelt <johannes@erdfelt.com>
* Copyright 2007-2009 Daniel Drake <dsd@gentoo.org>
* Copyright 2010-2012 Peter Stuge <peter@stuge.se>
* Copyright 2008-2011 Nathan Hjelm <hjelmn@users.sourceforge.net>
* Copyright 2009-2012 Pete Batard <pete@akeo.ie>
* Copyright 2009-2012 Ludovic Rousseau <ludovic.rousseau@gmail.com>
* Copyright 2010-2012 Michael Plante <michael.plante@gmail.com>
* Copyright 2011-2012 Hans de Goede <hdegoede@redhat.com>
* Copyright 2012 Martin Pieuchot <mpi@openbsd.org>
* Copyright 2012-2013 Toby Gray <toby.gray@realvnc.com>
*/
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);
}
}

View File

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