Skip to content

Commit ced0f20

Browse files
committed
Added hotplug support
Added several API to support hotplug notifations. That is, when a device is connected/disconnected, a callback can be invoked to notify the application. Implemented using libusb's hotplug support.
1 parent 2afb07a commit ced0f20

File tree

2 files changed

+198
-2
lines changed

2 files changed

+198
-2
lines changed

liblabjackusb/labjackusb.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <sys/utsname.h>
2222
#include <fcntl.h>
2323
#include <errno.h>
24+
#include <assert.h>
2425

2526
#include <libusb-1.0/libusb.h>
2627

@@ -48,6 +49,10 @@
4849

4950
static bool gIsLibUSBInitialized = false;
5051
static struct libusb_context *gLJContext = NULL;
52+
static libusb_hotplug_callback_handle gHotPlugCallbackHandle = 0;
53+
static LJUSB_HotPlugConnectedCallback gHotPlugConnectedCallback = NULL;
54+
static LJUSB_HotPlugDisconnectedCallback gHotPlugDisconnectedCallback = NULL;
55+
static void *gHotPlugUserContext = NULL;
5156

5257
enum LJUSB_TRANSFER_OPERATION { LJUSB_WRITE, LJUSB_READ, LJUSB_STREAM };
5358

@@ -717,6 +722,149 @@ int LJUSB_OpenAllDevicesOfProductId(UINT productId, HANDLE **devHandles)
717722
return successCount;
718723
}
719724

725+
static int LJUSB_HotPlugCallback(libusb_context *ctx,
726+
libusb_device *dev,
727+
libusb_hotplug_event event,
728+
void *user_data)
729+
{
730+
assert(ctx == gLJContext);
731+
(void)ctx;
732+
assert(dev);
733+
(void)user_data;
734+
735+
// NB: this callback may, or may not, be called from a private libusb thread!
736+
737+
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
738+
fprintf(stderr, "LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED occurred\n");
739+
740+
HANDLE handle = NULL;
741+
742+
struct libusb_device_descriptor desc;
743+
int r = libusb_get_device_descriptor(dev, &desc);
744+
if (r < 0) {
745+
fprintf(stderr, "LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED failed to get device descriptor due to:\n");
746+
LJUSB_libusbError(r);
747+
}
748+
else {
749+
handle = LJUSB_OpenSpecificDevice(dev, &desc);
750+
if (!handle) {
751+
fprintf(stderr, "LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED failed to open device\n");
752+
}
753+
}
754+
gHotPlugConnectedCallback(handle, gHotPlugUserContext);
755+
}
756+
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
757+
fprintf(stderr, "LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT occurred\n");
758+
759+
gHotPlugDisconnectedCallback(dev, gHotPlugUserContext);
760+
}
761+
else {
762+
fprintf(stderr, "unknown hotplug event type received\n");
763+
}
764+
765+
// Always return 0 to indicate we want additional events.
766+
return 0;
767+
}
768+
769+
bool LJUSB_RegisterHotPlug(unsigned long productID,
770+
LJUSB_HotPlugConnectedCallback connectedCallback,
771+
LJUSB_HotPlugDisconnectedCallback disconnectedCallback,
772+
void *context)
773+
{
774+
assert(connectedCallback);
775+
assert(disconnectedCallback);
776+
777+
int r = 1;
778+
779+
if (!LJUSB_libusb_initialize()) {
780+
return false;
781+
}
782+
783+
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
784+
fprintf(stderr, "libusb hotplug not supported on this platform\n");
785+
LJUSB_libusbError(LIBUSB_ERROR_NOT_SUPPORTED);
786+
return false;
787+
}
788+
789+
int productID = (productID == 0) ? LIBUSB_HOTPLUG_MATCH_ANY : (int)productID;
790+
791+
// Store the callbacks now, since LIBUSB_HOTPLUG_ENUMERATE below could result in them being call before libusb_hotplug_register_callback() even returns.
792+
gHotPlugConnectedCallback = connectedCallback;
793+
gHotPlugDisconnectedCallback = disconnectedCallback;
794+
gHotPlugUserContext = context;
795+
796+
r = libusb_hotplug_register_callback(gLJContext,
797+
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
798+
LIBUSB_HOTPLUG_ENUMERATE,
799+
LJ_VENDOR_ID,
800+
productID,
801+
LIBUSB_HOTPLUG_MATCH_ANY,
802+
&LJUSB_HotPlugCallback,
803+
NULL,
804+
&gHotPlugCallbackHandle);
805+
806+
if (r < 0) {
807+
gHotPlugConnectedCallback = NULL;
808+
gHotPlugDisconnectedCallback = NULL;
809+
gHotPlugUserContext = NULL;
810+
gHotPlugCallbackHandle = 0;
811+
812+
fprintf(stderr, "failed to register hot plug callback due to:\n");
813+
LJUSB_libusbError(r);
814+
return false;
815+
}
816+
817+
return true;
818+
}
819+
820+
821+
void LJUSB_DeregisterHotPlug(void)
822+
{
823+
assert(gIsLibUSBInitialized);
824+
825+
if (gLJContext && gHotPlugCallbackHandle) {
826+
libusb_hotplug_deregister_callback(gLJContext, gHotPlugCallbackHandle);
827+
828+
gHotPlugConnectedCallback = NULL;
829+
gHotPlugDisconnectedCallback = NULL;
830+
gHotPlugUserContext = NULL;
831+
gHotPlugCallbackHandle = 0;
832+
}
833+
else {
834+
fprintf(stderr, "LJUSB_DeregisterHotPlug: nothing to do\n");
835+
}
836+
}
837+
838+
839+
int LJUSB_HandleEventsTimeoutCompleted(struct timeval *tv, int *completed)
840+
{
841+
if (!LJUSB_libusb_initialize()) {
842+
return false;
843+
}
844+
845+
return libusb_handle_events_timeout_completed(gLJContext, tv, completed);
846+
}
847+
848+
DEVICE LJUSB_DeviceFromHandle(HANDLE hDevice)
849+
{
850+
if (!hDevice) {
851+
return NULL;
852+
}
853+
854+
libusb_device *device = libusb_get_device(hDevice);
855+
return device;
856+
}
857+
858+
void LJUSB_RefCountIncrement(DEVICE device)
859+
{
860+
libusb_ref_device(device);
861+
}
862+
863+
void LJUSB_RefCountDecrement(DEVICE device)
864+
{
865+
libusb_unref_device(device);
866+
}
867+
720868
bool LJUSB_ResetConnection(HANDLE hDevice)
721869
{
722870
int r;

liblabjackusb/labjackusb.h

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@
7676
#define LJUSB_LIBRARY_VERSION 2.0600f
7777

7878
#include <stdbool.h>
79+
#include <sys/time.h>
7980

80-
typedef void * HANDLE;
81+
typedef void * HANDLE; // Really libusb_device_handle*.
82+
typedef void * DEVICE; // Really libusb_device*.
8183
typedef unsigned int UINT;
8284
typedef unsigned char BYTE;
8385

@@ -210,6 +212,52 @@ HANDLE LJUSB_OpenDevice(UINT DevNum, unsigned int dwReserved, unsigned long Prod
210212
// dwReserved = Not used, set to 0.
211213
// ProductID = The product ID of the LabJack USB device.
212214

215+
216+
typedef void (*LJUSB_HotPlugConnectedCallback)(HANDLE hDevice, void *context);
217+
// The function signature for a hot plug connection callback.
218+
// hDevice may be NULL if the internal call to LJUSB_OpenDevice() failed.
219+
// Note the asymetry between connection (giving a HANDLE) and disconnection (giving a DEVICE).
220+
221+
typedef void (*LJUSB_HotPlugDisconnectedCallback)(DEVICE hDevice, void *context);
222+
// The function signature for a hot plug disconnection callback.
223+
// hDevice will never be NULL.
224+
// Note the asymetry between connection (giving a HANDLE) and disconnection (giving a DEVICE).
225+
226+
bool LJUSB_RegisterHotPlug(unsigned long productID,
227+
LJUSB_HotPlugConnectedCallback connectedCallback,
228+
LJUSB_HotPlugDisconnectedCallback disconnectedCallback,
229+
void *context);
230+
// Registers for notification of Labjack devices being connected or disconnected.
231+
// Returns true if successfully setup, or false if some error occurs.
232+
// If true is returned, must eventually be balanced with a call to LJUSB_DeregisterHotPlug().
233+
// Do not invoke this more than once without first calling LJUSB_DeregisterHotPlug().
234+
// If you want notifications for all Labjack models, pass the special value 0 as productID.
235+
// The callbacks will be called when a device is connected or disconnected.
236+
// Both callback functions must be provided, neither may be NULL.
237+
// Note: device enumeration is performed immediately, so be prepared for the callback to occur even before this function returns.
238+
// Also, the callback may be invoked from a private libsub thread (or not).
239+
// For connected devices, LJUSB_OpenDevice() is called for you.
240+
// For disconnected devices, you need to call LJUSB_CloseDevice() yourself.
241+
// The context pointer can be anything, including NULL; it is not used, it is merely passed back to your callbacks.
242+
243+
void LJUSB_DeregisterHotPlug(void);
244+
// Deregisters for notification of Labjack devices being connected or disconnected.
245+
// Must eventually be called for every successful call to LJUSB_RegisterHotPlug().
246+
247+
int LJUSB_HandleEventsTimeoutCompleted(struct timeval *tv, int *completed);
248+
// In order for the hot plug callbacks to ever be called, you must call this function, for example, in your application's event loop.
249+
250+
DEVICE LJUSB_DeviceFromHandle(HANDLE hDevice);
251+
// Returns the DEVICE that owns the given HANDLE. Returns NULL if NULL is given.
252+
// This can be useful within your hot plug connection callback, where a HANDLE is provided. Your bookkeeping might want to keep track of the corresponding DEVICE for when the disconnection callback arrives (which provides a DEVICE, not a HANDLE).
253+
254+
void LJUSB_RefCountIncrement(DEVICE device);
255+
// Increments the reference count of the given device. Do not pass NULL.
256+
// 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.
257+
258+
void LJUSB_RefCountDecrement(DEVICE device);
259+
// Decrements the reference count of the given device. Do not pass NULL.
260+
213261
bool LJUSB_ResetConnection(HANDLE hDevice);
214262
// Performs a USB port reset to reinitialize a device.
215263
// Returns true on success, or false on error and errno is set.
@@ -284,7 +332,7 @@ void LJUSB_CloseDevice(HANDLE hDevice);
284332
// Closes the handle of a LabJack USB device.
285333

286334
bool LJUSB_IsHandleValid(HANDLE hDevice);
287-
// Returns true if the handle is valid; this is, it is still connected to a
335+
// Returns true if the handle is valid; that is, it is still connected to a
288336
// device on the system.
289337

290338
unsigned short LJUSB_GetDeviceDescriptorReleaseNumber(HANDLE hDevice);

0 commit comments

Comments
 (0)