Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added hotplug support #37

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions liblabjackusb/labjackusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <sys/utsname.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>

#include <libusb-1.0/libusb.h>

Expand Down Expand Up @@ -48,6 +49,10 @@

static bool gIsLibUSBInitialized = false;
static struct libusb_context *gLJContext = NULL;
static libusb_hotplug_callback_handle gHotPlugCallbackHandle = 0;
static LJUSB_HotPlugConnectedCallback gHotPlugConnectedCallback = NULL;
static LJUSB_HotPlugDisconnectedCallback gHotPlugDisconnectedCallback = NULL;
static void *gHotPlugUserContext = NULL;

enum LJUSB_TRANSFER_OPERATION { LJUSB_WRITE, LJUSB_READ, LJUSB_STREAM };

Expand Down Expand Up @@ -717,6 +722,151 @@ int LJUSB_OpenAllDevicesOfProductId(UINT productId, HANDLE **devHandles)
return successCount;
}

static int LJUSB_HotPlugCallback(libusb_context *ctx,
libusb_device *dev,
libusb_hotplug_event event,
void *user_data)
{
assert(ctx == gLJContext);
(void)ctx;
assert(dev);
(void)user_data;

// NB: this callback may, or may not, be called from a private libusb thread!

if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
fprintf(stderr, "LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED occurred\n");

HANDLE handle = NULL;

struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
fprintf(stderr, "LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED failed to get device descriptor due to:\n");
LJUSB_libusbError(r);
}
else {
handle = LJUSB_OpenSpecificDevice(dev, &desc);
if (!handle) {
fprintf(stderr, "LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED failed to open device, errno: %i\n", errno);
}
}
gHotPlugConnectedCallback(dev, handle, gHotPlugUserContext);
}
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
fprintf(stderr, "LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT occurred\n");

gHotPlugDisconnectedCallback(dev, gHotPlugUserContext);
}
else {
fprintf(stderr, "unknown hotplug event type received\n");
}

// Always return 0 to indicate we want additional events.
return 0;
}

bool LJUSB_RegisterHotPlug(unsigned long productID,
LJUSB_HotPlugConnectedCallback connectedCallback,
LJUSB_HotPlugDisconnectedCallback disconnectedCallback,
void *context)
{
assert(connectedCallback);
assert(disconnectedCallback);

int r = 1;

if (!LJUSB_libusb_initialize()) {
return false;
}

if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
fprintf(stderr, "libusb hotplug not supported on this platform\n");
LJUSB_libusbError(LIBUSB_ERROR_NOT_SUPPORTED);
return false;
}

int actualProductID = (productID == 0) ? LIBUSB_HOTPLUG_MATCH_ANY : (int)productID;

// Store the callbacks now, since LIBUSB_HOTPLUG_ENUMERATE below could result in them being call before libusb_hotplug_register_callback() even returns.
gHotPlugConnectedCallback = connectedCallback;
gHotPlugDisconnectedCallback = disconnectedCallback;
gHotPlugUserContext = context;

r = libusb_hotplug_register_callback(gLJContext,
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
LIBUSB_HOTPLUG_ENUMERATE,
LJ_VENDOR_ID,
actualProductID,
LIBUSB_HOTPLUG_MATCH_ANY,
&LJUSB_HotPlugCallback,
NULL,
&gHotPlugCallbackHandle);

if (r < 0) {
gHotPlugConnectedCallback = NULL;
gHotPlugDisconnectedCallback = NULL;
gHotPlugUserContext = NULL;
gHotPlugCallbackHandle = 0;

fprintf(stderr, "failed to register hot plug callback due to:\n");
LJUSB_libusbError(r);
return false;
}

return true;
}


void LJUSB_DeregisterHotPlug(void)
{
assert(gIsLibUSBInitialized);

if (gLJContext && gHotPlugCallbackHandle) {
libusb_hotplug_deregister_callback(gLJContext, gHotPlugCallbackHandle);

gHotPlugConnectedCallback = NULL;
gHotPlugDisconnectedCallback = NULL;
gHotPlugUserContext = NULL;
gHotPlugCallbackHandle = 0;
}
else {
fprintf(stderr, "LJUSB_DeregisterHotPlug: nothing to do\n");
}
}


int LJUSB_HandleEventsTimeoutCompleted(struct timeval *tv, int *completed)
{
if (!LJUSB_libusb_initialize()) {
return false;
}

return libusb_handle_events_timeout_completed(gLJContext, tv, completed);
}

DEVICE LJUSB_DeviceFromHandle(HANDLE hDevice)
{
if (!hDevice) {
return NULL;
}

libusb_device *device = libusb_get_device(hDevice);
return device;
}

void LJUSB_RefCountIncrement(DEVICE device)
{
assert(device);
libusb_ref_device(device);
}

void LJUSB_RefCountDecrement(DEVICE device)
{
assert(device);
libusb_unref_device(device);
}

bool LJUSB_ResetConnection(HANDLE hDevice)
{
int r;
Expand Down
51 changes: 49 additions & 2 deletions liblabjackusb/labjackusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@
#define LJUSB_LIBRARY_VERSION 2.0700f

#include <stdbool.h>
#include <sys/time.h>

typedef void * HANDLE;
typedef void * HANDLE; // Really libusb_device_handle*.
typedef void * DEVICE; // Really libusb_device*.
typedef unsigned int UINT;
typedef unsigned char BYTE;

Expand Down Expand Up @@ -212,6 +214,51 @@ HANDLE LJUSB_OpenDevice(UINT DevNum, unsigned int dwReserved, unsigned long Prod
// dwReserved = Not used, set to 0.
// ProductID = The product ID of the LabJack USB device.


typedef void (*LJUSB_HotPlugConnectedCallback)(DEVICE hDevice, HANDLE hHandle, void *context);
// The function signature for a hot plug connection callback.
// The provided hDevice will never be NULL.
// hHandle may be NULL if the internal call to LJUSB_OpenDevice() failed.

typedef void (*LJUSB_HotPlugDisconnectedCallback)(DEVICE hDevice, void *context);
// The function signature for a hot plug disconnection callback.
// The provided hDevice will never be NULL.

bool LJUSB_RegisterHotPlug(unsigned long productID,
LJUSB_HotPlugConnectedCallback connectedCallback,
LJUSB_HotPlugDisconnectedCallback disconnectedCallback,
void *context);
// Registers for notification of Labjack devices being connected or disconnected.
// Returns true if successfully setup, or false if some error occurs.
// If true is returned, must eventually be balanced with a call to LJUSB_DeregisterHotPlug().
// Do not invoke this more than once without first calling LJUSB_DeregisterHotPlug().
// If you want notifications for all Labjack models, pass the special value 0 as productID.
// The callbacks will be called when a device is connected or disconnected.
// Both callback functions must be provided, neither may be NULL.
// Note: device enumeration is performed immediately, so be prepared for the callback to occur even before this function returns.
// Also, the callback may be invoked from a private libsub thread (or not).
// For connected devices, LJUSB_OpenDevice() is called for you.
// For disconnected devices, you need to call LJUSB_CloseDevice() yourself.
// The context pointer can be anything, including NULL; it is not used, it is merely passed back to your callbacks.

void LJUSB_DeregisterHotPlug(void);
// Deregisters for notification of Labjack devices being connected or disconnected.
// Must eventually be called for every successful call to LJUSB_RegisterHotPlug().

int LJUSB_HandleEventsTimeoutCompleted(struct timeval *tv, int *completed);
// In order for the hot plug callbacks to ever be called, you must call this function, for example, in your application's event loop.

DEVICE LJUSB_DeviceFromHandle(HANDLE hDevice);
// Returns the DEVICE that owns the given HANDLE. Returns NULL if NULL is given.
// This can be useful within your hot plug connection callbacks.

void LJUSB_RefCountIncrement(DEVICE device);
// Increments the reference count of the given device. Do not pass NULL.
// If your hot plug bookkeeping holds onto DEVICES, you should increment the reference count to make sure the object does not get deallocated while you are still holding on to it.

void LJUSB_RefCountDecrement(DEVICE device);
// Decrements the reference count of the given device. Do not pass NULL.

bool LJUSB_ResetConnection(HANDLE hDevice);
// Performs a USB port reset to reinitialize a device.
// Returns true on success, or false on error and errno is set.
Expand Down Expand Up @@ -286,7 +333,7 @@ void LJUSB_CloseDevice(HANDLE hDevice);
// Closes the handle of a LabJack USB device.

bool LJUSB_IsHandleValid(HANDLE hDevice);
// Returns true if the handle is valid; this is, it is still connected to a
// Returns true if the handle is valid; that is, it is still connected to a
// device on the system.

unsigned short LJUSB_GetDeviceDescriptorReleaseNumber(HANDLE hDevice);
Expand Down