2024-06-28 21:24:46 -07:00
//---------------------------------------------------------------------------
//
// labjackusb.c
//
// Library for accessing U3, U6, UE9, SkyMote bridge, T4, T7, and Digit
// devices over USB.
//
// support@labjack.com
//
//---------------------------------------------------------------------------
//
# include "labjackusb.h"
# include <unistd.h>
# include <stdlib.h>
# include <stdbool.h>
# include <string.h>
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
# if defined(__linux__)
# include <sys/utsname.h>
# endif // defined(__linux__)
# include <fcntl.h>
# include <errno.h>
# include <libusb-1.0/libusb.h>
# define LJ_LIBUSB_TIMEOUT_DEFAULT 1000 // Milliseconds to wait on USB transfers
// With a recent Linux kernel, firmware and hardware checks aren't necessary
# define LJ_RECENT_KERNEL_MAJOR 2
# define LJ_RECENT_KERNEL_MINOR 6
# define LJ_RECENT_KERNEL_REV 28
# define MIN_UE9_FIRMWARE_MAJOR 1
# define MIN_UE9_FIRMWARE_MINOR 49
# define U3C_HARDWARE_MAJOR 1
# define U3C_HARDWARE_MINOR 30
# define MIN_U3C_FIRMWARE_MAJOR 1
# define MIN_U3C_FIRMWARE_MINOR 18
# define MIN_U6_FIRMWARE_MAJOR 0
# define MIN_U6_FIRMWARE_MINOR 81
static bool gIsLibUSBInitialized = false ;
static struct libusb_context * gLJContext = NULL ;
enum LJUSB_TRANSFER_OPERATION { LJUSB_WRITE , LJUSB_READ , LJUSB_STREAM } ;
struct LJUSB_FirmwareHardwareVersion
{
unsigned char firmwareMajor ;
unsigned char firmwareMinor ;
unsigned char hardwareMajor ;
unsigned char hardwareMinor ;
} ;
static void LJUSB_U3_FirmwareHardwareVersion ( HANDLE hDevice , struct LJUSB_FirmwareHardwareVersion * fhv )
{
unsigned long i = 0 , r = 0 ;
unsigned long epOut = U3_PIPE_EP1_OUT , epIn = U3_PIPE_EP2_IN ;
const unsigned long COMMAND_LENGTH = 26 ;
const unsigned long RESPONSE_LENGTH = 38 ;
BYTE command [ COMMAND_LENGTH ] ;
BYTE response [ RESPONSE_LENGTH ] ;
memset ( command , 0 , COMMAND_LENGTH ) ;
memset ( response , 0 , RESPONSE_LENGTH ) ;
//Checking firmware for U3. Using ConfigU3 Low-level command
command [ 0 ] = 11 ;
command [ 1 ] = ( BYTE ) ( 0xF8 ) ;
command [ 2 ] = ( BYTE ) ( 0x0A ) ;
command [ 3 ] = ( BYTE ) ( 0x08 ) ;
LJUSB_BulkWrite ( hDevice , epOut , command , COMMAND_LENGTH ) ;
if ( ( r = LJUSB_BulkRead ( hDevice , epIn , response , RESPONSE_LENGTH ) ) < RESPONSE_LENGTH ) {
fprintf ( stderr , " ConfigU3 response failed when getting firmware and hardware versions \n " ) ;
fprintf ( stderr , " Response was: \n " ) ;
for ( i = 0 ; i < r ; i + + ) {
fprintf ( stderr , " %d " , response [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
return ;
}
if ( response [ 1 ] ! = command [ 1 ] | | response [ 2 ] ! = ( BYTE ) ( 0x10 ) | | response [ 3 ] ! = command [ 3 ] ) {
fprintf ( stderr , " Invalid ConfigU3 command bytes when getting firmware and hardware versions \n " ) ;
fprintf ( stderr , " Response was: \n " ) ;
for ( i = 0 ; i < r ; i + + ) {
fprintf ( stderr , " %d " , response [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
return ;
}
fhv - > firmwareMajor = response [ 10 ] ;
fhv - > firmwareMinor = response [ 9 ] ;
fhv - > hardwareMajor = response [ 14 ] ;
fhv - > hardwareMinor = response [ 13 ] ;
return ;
}
static void LJUSB_U6_FirmwareHardwareVersion ( HANDLE hDevice , struct LJUSB_FirmwareHardwareVersion * fhv )
{
unsigned long i = 0 , r = 0 ;
unsigned long epOut = U6_PIPE_EP1_OUT , epIn = U6_PIPE_EP2_IN ;
const unsigned long COMMAND_LENGTH = 26 ;
const unsigned long RESPONSE_LENGTH = 38 ;
BYTE command [ COMMAND_LENGTH ] ;
BYTE response [ RESPONSE_LENGTH ] ;
memset ( command , 0 , COMMAND_LENGTH ) ;
memset ( response , 0 , RESPONSE_LENGTH ) ;
//Checking firmware for U6. Using ConfigU3 Low-level command
command [ 0 ] = 11 ;
command [ 1 ] = ( BYTE ) ( 0xF8 ) ;
command [ 2 ] = ( BYTE ) ( 0x0A ) ;
command [ 3 ] = ( BYTE ) ( 0x08 ) ;
LJUSB_BulkWrite ( hDevice , epOut , command , COMMAND_LENGTH ) ;
if ( ( r = LJUSB_BulkRead ( hDevice , epIn , response , RESPONSE_LENGTH ) ) < RESPONSE_LENGTH ) {
fprintf ( stderr , " ConfigU6 response failed when getting firmware and hardware versions \n " ) ;
fprintf ( stderr , " Response was: \n " ) ;
for ( i = 0 ; i < r ; i + + ) {
fprintf ( stderr , " %d " , response [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
return ;
}
if ( response [ 1 ] ! = command [ 1 ] | | response [ 2 ] ! = ( BYTE ) ( 0x10 ) | | response [ 3 ] ! = command [ 3 ] ) {
fprintf ( stderr , " Invalid ConfigU6 command bytes when getting firmware and hardware versions \n " ) ;
fprintf ( stderr , " Response was: \n " ) ;
for ( i = 0 ; i < r ; i + + ) {
fprintf ( stderr , " %d " , response [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
return ;
}
fhv - > firmwareMajor = response [ 10 ] ;
fhv - > firmwareMinor = response [ 9 ] ;
/* TODO: Add hardware major and minor */
return ;
}
static void LJUSB_UE9_FirmwareHardwareVersion ( HANDLE hDevice , struct LJUSB_FirmwareHardwareVersion * fhv )
{
unsigned long i = 0 , r = 0 ;
unsigned long epOut = UE9_PIPE_EP1_OUT , epIn = UE9_PIPE_EP1_IN ;
const unsigned long COMMAND_LENGTH = 38 ;
const unsigned long RESPONSE_LENGTH = 38 ;
BYTE command [ COMMAND_LENGTH ] ;
BYTE response [ RESPONSE_LENGTH ] ;
memset ( command , 0 , COMMAND_LENGTH ) ;
memset ( response , 0 , RESPONSE_LENGTH ) ;
//Checking firmware for UE9. Using CommConfig Low-level command
command [ 0 ] = 137 ;
command [ 1 ] = ( BYTE ) ( 0x78 ) ;
command [ 2 ] = ( BYTE ) ( 0x10 ) ;
command [ 3 ] = ( BYTE ) ( 0x01 ) ;
LJUSB_BulkWrite ( hDevice , epOut , command , COMMAND_LENGTH ) ;
if ( ( r = LJUSB_BulkRead ( hDevice , epIn , response , RESPONSE_LENGTH ) ) < RESPONSE_LENGTH ) {
fprintf ( stderr , " CommConfig response failed when getting firmware and hardware versions \n " ) ;
fprintf ( stderr , " Response was: \n " ) ;
for ( i = 0 ; i < r ; i + + ) {
fprintf ( stderr , " %d " , response [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
return ;
}
if ( response [ 1 ] ! = command [ 1 ] | | response [ 2 ] ! = command [ 2 ] | | response [ 3 ] ! = command [ 3 ] ) {
fprintf ( stderr , " Invalid CommConfig command bytes when getting firmware and hardware versions \n " ) ;
fprintf ( stderr , " Response was: \n " ) ;
for ( i = 0 ; i < r ; i + + ) {
fprintf ( stderr , " %d " , response [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
return ;
}
fhv - > firmwareMajor = response [ 37 ] ;
fhv - > firmwareMinor = response [ 36 ] ;
/* TODO: Add hardware major and minor */
return ;
}
static bool LJUSB_isNullHandle ( HANDLE hDevice )
{
if ( hDevice = = NULL ) {
// TODO: Consider different errno here
errno = EINVAL ;
return true ;
}
return false ;
}
static int LJUSB_libusbError ( int r )
{
switch ( r ) {
case LIBUSB_SUCCESS :
// No error
return 0 ;
case LIBUSB_ERROR_IO :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_IO \n " ) ;
# endif
errno = EIO ;
break ;
case LIBUSB_ERROR_INVALID_PARAM :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_INVALID_PARAM \n " ) ;
# endif
errno = EINVAL ;
break ;
case LIBUSB_ERROR_ACCESS :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_ACCESS \n " ) ;
# endif
errno = EACCES ;
break ;
case LIBUSB_ERROR_NO_DEVICE :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_NO_DEVICE \n " ) ;
# endif
errno = ENXIO ;
break ;
case LIBUSB_ERROR_NOT_FOUND :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_NOT_FOUND \n " ) ;
# endif
errno = ENOENT ;
break ;
case LIBUSB_ERROR_BUSY :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_BUSY \n " ) ;
# endif
errno = EBUSY ;
break ;
case LIBUSB_ERROR_TIMEOUT :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_TIMEOUT \n " ) ;
# endif
errno = ETIMEDOUT ;
break ;
case LIBUSB_ERROR_OVERFLOW :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_OVERFLOW \n " ) ;
# endif
errno = EOVERFLOW ;
break ;
case LIBUSB_ERROR_PIPE :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_PIPE \n " ) ;
# endif
errno = EPIPE ;
break ;
case LIBUSB_ERROR_INTERRUPTED :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_INTERRUPTED \n " ) ;
# endif
errno = EINTR ;
break ;
case LIBUSB_ERROR_NO_MEM :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_NO_MEM \n " ) ;
# endif
errno = ENOMEM ;
break ;
case LIBUSB_ERROR_NOT_SUPPORTED :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_NOT_SUPPORTED \n " ) ;
# endif
errno = ENOSYS ;
break ;
case LIBUSB_ERROR_OTHER :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: LIBUSB_ERROR_OTHER \n " ) ;
# endif
if ( errno = = 0 ) {
errno = ENOSYS ;
}
break ;
default :
# if LJ_DEBUG
fprintf ( stderr , " libusb error: Unexpected error code: %d. \n " , r ) ;
# endif
if ( errno = = 0 ) {
errno = ENOSYS ;
}
break ;
}
return - 1 ;
}
static bool LJUSB_U3_isMinFirmware ( const struct LJUSB_FirmwareHardwareVersion * fhv )
{
if ( fhv - > hardwareMajor = = U3C_HARDWARE_MAJOR & & fhv - > hardwareMinor = = U3C_HARDWARE_MINOR ) {
if ( fhv - > firmwareMajor > MIN_U3C_FIRMWARE_MAJOR | | ( fhv - > firmwareMajor = = MIN_U3C_FIRMWARE_MAJOR & & fhv - > firmwareMinor > = MIN_U3C_FIRMWARE_MINOR ) ) {
return true ;
}
else {
fprintf ( stderr , " Minimum U3 firmware is not met for this kernel. Please update from firmware %d.%02d to firmware %d.%d or upgrade to kernel %d.%d.%d. \n " , fhv - > firmwareMajor , fhv - > firmwareMinor , MIN_U3C_FIRMWARE_MAJOR , MIN_U3C_FIRMWARE_MINOR , LJ_RECENT_KERNEL_MAJOR , LJ_RECENT_KERNEL_MINOR , LJ_RECENT_KERNEL_REV ) ;
return false ;
}
}
else {
fprintf ( stderr , " Minimum U3 hardware version is not met for this kernel. This driver supports only hardware %d.%d and above. Your hardware version is %d.%d. \n " , U3C_HARDWARE_MAJOR , U3C_HARDWARE_MINOR , fhv - > hardwareMajor , fhv - > hardwareMinor ) ;
fprintf ( stderr , " This hardware version is supported under kernel %d.%d.%d. \n " , LJ_RECENT_KERNEL_MAJOR , LJ_RECENT_KERNEL_MINOR , LJ_RECENT_KERNEL_REV ) ;
return false ;
}
return false ;
}
static bool LJUSB_U6_isMinFirmware ( const struct LJUSB_FirmwareHardwareVersion * fhv )
{
if ( fhv - > firmwareMajor > MIN_U6_FIRMWARE_MAJOR | | ( fhv - > firmwareMajor = = MIN_U6_FIRMWARE_MAJOR & & fhv - > firmwareMinor > = MIN_U6_FIRMWARE_MINOR ) ) {
return true ;
}
else {
fprintf ( stderr , " Minimum U6 firmware is not met for this kernel. Please update from firmware %d.%d to firmware %d.%d or upgrade to kernel %d.%d.%d. \n " , fhv - > firmwareMajor , fhv - > firmwareMinor , MIN_U6_FIRMWARE_MAJOR , MIN_U6_FIRMWARE_MINOR , LJ_RECENT_KERNEL_MAJOR , LJ_RECENT_KERNEL_MINOR , LJ_RECENT_KERNEL_REV ) ;
return false ;
}
return false ;
}
static bool LJUSB_UE9_isMinFirmware ( const struct LJUSB_FirmwareHardwareVersion * fhv )
{
# if LJ_DEBUG
fprintf ( stderr , " In LJUSB_UE9_isMinFirmware \n " ) ;
# endif
if ( fhv - > firmwareMajor > MIN_UE9_FIRMWARE_MAJOR | | ( fhv - > firmwareMajor = = MIN_UE9_FIRMWARE_MAJOR & & fhv - > firmwareMinor > = MIN_UE9_FIRMWARE_MINOR ) ) {
# if LJ_DEBUG
fprintf ( stderr , " Minimum UE9 firmware met. Version is %d.%d. \n " , fhv - > firmwareMajor , fhv - > firmwareMinor ) ;
# endif
return true ;
}
else {
fprintf ( stderr , " Minimum UE9 firmware is not met for this kernel. Please update from firmware %d.%d to firmware %d.%d or upgrade to kernel %d.%d.%d. \n " , fhv - > firmwareMajor , fhv - > firmwareMinor , MIN_UE9_FIRMWARE_MAJOR , MIN_UE9_FIRMWARE_MINOR , LJ_RECENT_KERNEL_MAJOR , LJ_RECENT_KERNEL_MINOR , LJ_RECENT_KERNEL_REV ) ;
return false ;
}
return false ;
}
static bool LJUSB_isRecentKernel ( void )
{
# if defined(__linux__)
struct utsname u ;
char * tok = NULL ;
unsigned long kernelMajor = 0 , kernelMinor = 0 , kernelRev = 0 ;
if ( uname ( & u ) ! = 0 ) {
fprintf ( stderr , " Error calling uname(2). \n " ) ;
return false ;
}
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_recentKernel: sysname: %s. \n " , u . sysname ) ;
fprintf ( stderr , " LJUSB_recentKernel: Kernel release: %s. \n " , u . release ) ;
# endif
tok = strtok ( u . release , " .- " ) ;
kernelMajor = strtoul ( tok , NULL , 10 ) ;
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_recentKernel: tok: %s \n " , tok ) ;
fprintf ( stderr , " LJUSB_recentKernel: kernelMajor: %lu \n " , kernelMajor ) ;
# endif
tok = strtok ( NULL , " .- " ) ;
kernelMinor = strtoul ( tok , NULL , 10 ) ;
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_recentKernel: tok: %s \n " , tok ) ;
fprintf ( stderr , " LJUSB_recentKernel: kernelMinor: %lu \n " , kernelMinor ) ;
# endif
tok = strtok ( NULL , " .- " ) ;
kernelRev = strtoul ( tok , NULL , 10 ) ;
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_recentKernel: tok: %s \n " , tok ) ;
fprintf ( stderr , " LJUSB_recentKernel: kernelRev: %lu \n " , kernelRev ) ;
# endif
return ( kernelMajor = = LJ_RECENT_KERNEL_MAJOR & & kernelMinor = = LJ_RECENT_KERNEL_MINOR & & kernelRev > = LJ_RECENT_KERNEL_REV ) | |
( kernelMajor = = LJ_RECENT_KERNEL_MAJOR & & kernelMinor > LJ_RECENT_KERNEL_MINOR ) | |
( kernelMajor > LJ_RECENT_KERNEL_MAJOR ) ;
# else
// There are no known kernel-compatibility problems with other OSes.
return true ;
# endif
}
static bool LJUSB_isMinFirmware ( HANDLE hDevice , unsigned long ProductID )
{
struct LJUSB_FirmwareHardwareVersion fhv = { 0 , 0 , 0 , 0 } ;
// If we are running on a recent linux kernel (or other OS), no firmware check is necessary.
if ( LJUSB_isRecentKernel ( ) ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_isMinFirmware: LJUSB_isRecentKernel: true \n " ) ;
# endif
return true ;
}
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_isMinFirmware: LJUSB_isRecentKernel: false \n " ) ;
# endif
switch ( ProductID ) {
case U3_PRODUCT_ID :
LJUSB_U3_FirmwareHardwareVersion ( hDevice , & fhv ) ;
return LJUSB_U3_isMinFirmware ( & fhv ) ;
case U6_PRODUCT_ID :
LJUSB_U6_FirmwareHardwareVersion ( hDevice , & fhv ) ;
return LJUSB_U6_isMinFirmware ( & fhv ) ;
case UE9_PRODUCT_ID :
LJUSB_UE9_FirmwareHardwareVersion ( hDevice , & fhv ) ;
return LJUSB_UE9_isMinFirmware ( & fhv ) ;
case U12_PRODUCT_ID : //Add U12 stuff Mike F.
return true ;
case BRIDGE_PRODUCT_ID : //Add Wireless bridge stuff Mike F.
return true ;
case T4_PRODUCT_ID :
return true ;
case T5_PRODUCT_ID :
return true ;
case T7_PRODUCT_ID :
return true ;
case DIGIT_PRODUCT_ID :
return true ;
default :
fprintf ( stderr , " Firmware check not supported for product ID %ld \n " , ProductID ) ;
return false ;
}
}
static bool LJUSB_libusb_initialize ( void )
{
if ( ! gIsLibUSBInitialized ) {
int r = libusb_init ( & gLJContext ) ;
if ( r < 0 ) {
fprintf ( stderr , " failed to initialize libusb \n " ) ;
LJUSB_libusbError ( r ) ;
return false ;
}
gIsLibUSBInitialized = true ;
}
return true ;
}
static void LJUSB_libusb_exit ( void )
{
if ( gIsLibUSBInitialized ) {
libusb_exit ( gLJContext ) ;
gLJContext = NULL ;
gIsLibUSBInitialized = false ;
}
}
float LJUSB_GetLibraryVersion ( void )
{
return LJUSB_LIBRARY_VERSION ;
}
static HANDLE LJUSB_OpenSpecificDevice ( libusb_device * dev , const struct libusb_device_descriptor * desc )
{
int r = 1 ;
struct libusb_device_handle * devh = NULL ;
// Open the device to get handle.
r = libusb_open ( dev , & devh ) ;
if ( r < 0 ) {
LJUSB_libusbError ( r ) ;
return NULL ;
}
2024-07-02 21:37:41 -07:00
# if !defined(_WIN32)
2024-06-28 21:24:46 -07:00
// Test if the kernel driver has the U12.
if ( desc - > idProduct = = U12_PRODUCT_ID & & libusb_kernel_driver_active ( devh , 0 ) ) {
# if LJ_DEBUG
fprintf ( stderr , " Kernel Driver was active, detaching... \n " ) ;
# endif
// Detach the U12 from kernel driver.
r = libusb_detach_kernel_driver ( devh , 0 ) ;
// Check the return value
if ( r ! = 0 ) {
libusb_close ( devh ) ;
fprintf ( stderr , " failed to detach from kernel driver. Error Number: %i \n " , r ) ;
return NULL ;
}
}
2024-07-02 21:37:41 -07:00
# endif // _WIN32
2024-06-28 21:24:46 -07:00
r = libusb_claim_interface ( devh , 0 ) ;
if ( r < 0 ) {
LJUSB_libusbError ( r ) ;
libusb_close ( devh ) ;
return NULL ;
}
return ( HANDLE ) devh ;
}
HANDLE LJUSB_OpenDevice ( UINT DevNum , unsigned int dwReserved , unsigned long ProductID )
{
( void ) dwReserved ;
libusb_device * * devs = NULL , * dev = NULL ;
struct libusb_device_descriptor desc ;
ssize_t cnt = 0 ;
int r = 1 ;
unsigned int i = 0 ;
unsigned int ljFoundCount = 0 ;
HANDLE handle = NULL ;
if ( ! LJUSB_libusb_initialize ( ) ) {
return NULL ;
}
cnt = libusb_get_device_list ( gLJContext , & devs ) ;
if ( cnt < 0 ) {
fprintf ( stderr , " failed to get device list \n " ) ;
LJUSB_libusbError ( ( int ) cnt ) ;
LJUSB_libusb_exit ( ) ;
return NULL ;
}
while ( ( dev = devs [ i + + ] ) ! = NULL ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_OpenDevice: calling libusb_get_device_descriptor \n " ) ;
# endif
r = libusb_get_device_descriptor ( dev , & desc ) ;
if ( r < 0 ) {
fprintf ( stderr , " failed to get device descriptor \n " ) ;
libusb_free_device_list ( devs , 1 ) ;
LJUSB_libusbError ( r ) ;
LJUSB_libusb_exit ( ) ;
return NULL ;
}
if ( LJ_VENDOR_ID = = desc . idVendor & & ProductID = = desc . idProduct ) {
ljFoundCount + + ;
if ( ljFoundCount = = DevNum ) {
handle = LJUSB_OpenSpecificDevice ( dev , & desc ) ;
# if LJ_DEBUG
if ( handle ) {
fprintf ( stderr , " LJUSB_OpenDevice: Found handle for product ID %ld \n " , ProductID ) ;
}
# endif
break ;
}
}
}
libusb_free_device_list ( devs , 1 ) ;
if ( handle ! = NULL ) {
//We found a device, now check if it meets the minimum firmware requirement
if ( ! LJUSB_isMinFirmware ( handle , ProductID ) ) {
//Does not meet the requirement. Close device and return an invalid handle.
LJUSB_CloseDevice ( handle ) ;
return NULL ;
}
}
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_OpenDevice: Returning handle \n " ) ;
# endif
return handle ;
}
int LJUSB_OpenAllDevices ( HANDLE * devHandles , UINT * productIds , UINT maxDevices )
{
libusb_device * * devs = NULL , * dev = NULL ;
struct libusb_device_descriptor desc ;
ssize_t cnt = 0 ;
int r = 1 ;
unsigned int i = 0 , ljFoundCount = 0 ;
HANDLE handle = NULL ;
if ( ! LJUSB_libusb_initialize ( ) ) {
return - 1 ;
}
cnt = libusb_get_device_list ( gLJContext , & devs ) ;
if ( cnt < 0 ) {
fprintf ( stderr , " failed to get device list \n " ) ;
LJUSB_libusbError ( ( int ) cnt ) ;
LJUSB_libusb_exit ( ) ;
return - 1 ;
}
while ( ( dev = devs [ i + + ] ) ! = NULL ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_OpenAllDevices: calling libusb_get_device_descriptor \n " ) ;
# endif
r = libusb_get_device_descriptor ( dev , & desc ) ;
if ( r < 0 ) {
fprintf ( stderr , " failed to get device descriptor \n " ) ;
libusb_free_device_list ( devs , 1 ) ;
LJUSB_libusbError ( r ) ;
LJUSB_libusb_exit ( ) ;
return - 1 ;
}
if ( LJ_VENDOR_ID = = desc . idVendor ) {
handle = LJUSB_OpenSpecificDevice ( dev , & desc ) ;
if ( handle = = NULL ) {
// Not a valid handle
continue ;
}
else if ( ljFoundCount < maxDevices ) {
if ( LJUSB_isMinFirmware ( handle , desc . idProduct ) ) {
devHandles [ ljFoundCount ] = handle ;
productIds [ ljFoundCount ] = desc . idProduct ;
ljFoundCount + + ;
} else {
// Not high enough firmware, keep moving.
libusb_close ( handle ) ;
}
} else {
// Too many devices have been found.
libusb_close ( handle ) ;
break ;
}
}
}
libusb_free_device_list ( devs , 1 ) ;
return ljFoundCount ;
}
int LJUSB_OpenAllDevicesOfProductId ( UINT productId , HANDLE * * devHandles )
{
// Always pre-clear result to NULL since there are early returns below.
* devHandles = NULL ;
if ( ! LJUSB_libusb_initialize ( ) ) {
return - 1 ;
}
libusb_device * * devs = NULL ;
ssize_t cnt = libusb_get_device_list ( gLJContext , & devs ) ;
if ( cnt < 0 ) {
fprintf ( stderr , " LJUSB_OpenAllDevicesOfProductId: failed to get device list \n " ) ;
LJUSB_libusbError ( ( int ) cnt ) ;
LJUSB_libusb_exit ( ) ;
return - 1 ;
} else if ( cnt = = 0 ) {
// No devices founds, that's fine, we're done.
return 0 ;
}
* devHandles = calloc ( cnt , sizeof ( HANDLE ) ) ;
if ( * devHandles = = NULL ) {
fprintf ( stderr , " LJUSB_OpenAllDevicesOfProductId: calloc failed \n " ) ;
libusb_free_device_list ( devs , 1 ) ;
return - 1 ;
}
ssize_t i = 0 , successCount = 0 ;
libusb_device * dev = NULL ;
while ( ( dev = devs [ i + + ] ) ! = NULL ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_OpenAllDevicesOfProductId: calling libusb_get_device_descriptor \n " ) ;
# endif
struct libusb_device_descriptor desc ;
int r = libusb_get_device_descriptor ( dev , & desc ) ;
if ( r < 0 ) {
fprintf ( stderr , " LJUSB_OpenAllDevicesOfProductId: failed to get a device descriptor, so skipping it \n " ) ;
} else if ( LJ_VENDOR_ID = = desc . idVendor & &
( productId = = desc . idProduct | | 0 = = productId ) ) {
HANDLE handle = LJUSB_OpenSpecificDevice ( dev , & desc ) ;
if ( handle = = NULL ) {
fprintf ( stderr , " LJUSB_OpenAllDevicesOfProductId: failed to open a device, so skipping it \n " ) ;
} else {
if ( LJUSB_isMinFirmware ( handle , desc . idProduct ) ) {
( * devHandles ) [ successCount ] = handle ;
successCount + + ;
} else {
// Not high enough firmware, keep moving.
libusb_close ( handle ) ;
}
}
}
}
libusb_free_device_list ( devs , 1 ) ;
return successCount ;
}
bool LJUSB_ResetConnection ( HANDLE hDevice )
{
int r ;
if ( LJUSB_isNullHandle ( hDevice ) ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_ResetConnection: returning false. hDevice is NULL. \n " ) ;
# endif
return false ;
}
r = libusb_reset_device ( hDevice ) ;
if ( r ! = 0 )
{
LJUSB_libusbError ( r ) ;
return false ;
}
return true ; //Success
}
static unsigned long LJUSB_DoTransfer ( HANDLE hDevice , unsigned char endpoint , BYTE * pBuff , unsigned long count , unsigned int timeout , bool isBulk )
{
int r = 0 ;
int transferred = 0 ;
# if LJ_DEBUG
fprintf ( stderr , " Calling LJUSB_DoTransfer with endpoint = 0x%x, count = %lu, and isBulk = %d. \n " , endpoint , count , isBulk ) ;
# endif
if ( count > 65535 /*UINT16_MAX*/ ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_DoTransfer: returning 0. count is too large. \n " ) ;
# endif
return 0 ;
}
if ( LJUSB_isNullHandle ( hDevice ) ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_DoTransfer: returning 0. hDevice is NULL. \n " ) ;
# endif
return 0 ;
}
if ( isBulk & & endpoint ! = 1 & & endpoint < 0x81 ) {
fprintf ( stderr , " LJUSB_DoTransfer warning: Got endpoint = %d, however this not a known endpoint. Please verify you are using the header file provided in /usr/local/include/labjackusb.h and not an older header file. \n " , endpoint ) ;
}
if ( isBulk ) {
r = libusb_bulk_transfer ( hDevice , endpoint , pBuff , ( int ) count , & transferred , timeout ) ;
}
else {
if ( endpoint = = 0 ) {
//HID feature request.
r = libusb_control_transfer ( hDevice , 0xa1 , 0x01 , 0x0300 , 0x0000 , pBuff , ( uint16_t ) count , timeout ) ;
if ( r < 0 ) {
LJUSB_libusbError ( r ) ;
return 0 ;
}
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_DoTransfer: returning control transferred = %d. \n " , r ) ;
# endif
return r ;
}
else {
r = libusb_interrupt_transfer ( hDevice , endpoint , pBuff , ( int ) count , & transferred , timeout ) ;
}
}
if ( r = = LIBUSB_ERROR_TIMEOUT ) {
//Timeout occurred but may have received partial data. Setting errno but
//returning the number of bytes transferred which may be > 0.
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_DoTransfer: Transfer timed out. Returning. \n " ) ;
# endif
errno = ETIMEDOUT ;
return transferred ;
}
else if ( r ! = 0 ) {
LJUSB_libusbError ( r ) ;
return 0 ;
}
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_DoTransfer: returning transferred = %d. \n " , transferred ) ;
# endif
return transferred ;
}
// Automatically uses the correct endpoint and transfer method (bulk or interrupt)
static unsigned long LJUSB_SetupTransfer ( HANDLE hDevice , BYTE * pBuff , unsigned long count , unsigned int timeout , enum LJUSB_TRANSFER_OPERATION operation )
{
libusb_device * dev = NULL ;
struct libusb_device_descriptor desc ;
bool isBulk = true ;
unsigned char endpoint = 0 ;
int r = 0 ;
# if LJ_DEBUG
fprintf ( stderr , " Calling LJUSB_SetupTransfer with count = %lu and operation = %d. \n " , count , operation ) ;
# endif
if ( LJUSB_isNullHandle ( hDevice ) ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_SetupTransfer: returning 0. hDevice is NULL. \n " ) ;
# endif
return 0 ;
}
//First determine the device from handle.
dev = libusb_get_device ( hDevice ) ;
r = libusb_get_device_descriptor ( dev , & desc ) ;
if ( r < 0 ) {
LJUSB_libusbError ( r ) ;
return 0 ;
}
switch ( desc . idProduct ) {
/* These devices use bulk transfers */
case UE9_PRODUCT_ID :
isBulk = true ;
switch ( operation ) {
case LJUSB_WRITE :
endpoint = UE9_PIPE_EP1_OUT ;
break ;
case LJUSB_READ :
endpoint = UE9_PIPE_EP1_IN ;
break ;
case LJUSB_STREAM :
endpoint = UE9_PIPE_EP2_IN ;
break ;
default :
errno = EINVAL ;
return 0 ;
}
break ;
case U3_PRODUCT_ID :
isBulk = true ;
switch ( operation ) {
case LJUSB_WRITE :
endpoint = U3_PIPE_EP1_OUT ;
break ;
case LJUSB_READ :
endpoint = U3_PIPE_EP2_IN ;
break ;
case LJUSB_STREAM :
endpoint = U3_PIPE_EP3_IN ;
break ;
default :
errno = EINVAL ;
return 0 ;
}
break ;
case U6_PRODUCT_ID :
isBulk = true ;
switch ( operation ) {
case LJUSB_WRITE :
endpoint = U6_PIPE_EP1_OUT ;
break ;
case LJUSB_READ :
endpoint = U6_PIPE_EP2_IN ;
break ;
case LJUSB_STREAM :
endpoint = U6_PIPE_EP3_IN ;
break ;
default :
errno = EINVAL ;
return 0 ;
}
break ;
case BRIDGE_PRODUCT_ID :
isBulk = true ;
switch ( operation ) {
case LJUSB_WRITE :
endpoint = BRIDGE_PIPE_EP1_OUT ;
break ;
case LJUSB_READ :
endpoint = BRIDGE_PIPE_EP2_IN ;
break ;
case LJUSB_STREAM :
endpoint = BRIDGE_PIPE_EP3_IN ;
break ;
default :
errno = EINVAL ;
return 0 ;
}
break ;
case T4_PRODUCT_ID :
isBulk = true ;
switch ( operation ) {
case LJUSB_WRITE :
endpoint = T4_PIPE_EP1_OUT ;
break ;
case LJUSB_READ :
endpoint = T4_PIPE_EP2_IN ;
break ;
case LJUSB_STREAM :
endpoint = T4_PIPE_EP3_IN ;
break ;
default :
errno = EINVAL ;
return 0 ;
}
break ;
case T5_PRODUCT_ID :
isBulk = true ;
switch ( operation ) {
case LJUSB_WRITE :
endpoint = T5_PIPE_EP1_OUT ;
break ;
case LJUSB_READ :
endpoint = T5_PIPE_EP2_IN ;
break ;
case LJUSB_STREAM :
endpoint = T5_PIPE_EP3_IN ;
break ;
default :
errno = EINVAL ;
return 0 ;
}
break ;
case T7_PRODUCT_ID :
isBulk = true ;
switch ( operation ) {
case LJUSB_WRITE :
endpoint = T7_PIPE_EP1_OUT ;
break ;
case LJUSB_READ :
endpoint = T7_PIPE_EP2_IN ;
break ;
case LJUSB_STREAM :
endpoint = T7_PIPE_EP3_IN ;
break ;
default :
errno = EINVAL ;
return 0 ;
}
break ;
case DIGIT_PRODUCT_ID :
isBulk = true ;
switch ( operation ) {
case LJUSB_WRITE :
endpoint = DIGIT_PIPE_EP1_OUT ;
break ;
case LJUSB_READ :
endpoint = DIGIT_PIPE_EP2_IN ;
break ;
case LJUSB_STREAM :
default :
//No streaming interface
errno = EINVAL ;
return 0 ;
}
break ;
/* These devices use interrupt transfers */
case U12_PRODUCT_ID :
isBulk = false ;
switch ( operation ) {
case LJUSB_READ :
endpoint = U12_PIPE_EP1_IN ;
break ;
case LJUSB_WRITE :
endpoint = U12_PIPE_EP2_OUT ;
break ;
case LJUSB_STREAM :
endpoint = U12_PIPE_EP0 ;
break ;
default :
errno = EINVAL ;
return 0 ;
}
break ;
default :
// Error, not a labjack device
errno = EINVAL ;
return 0 ;
}
return LJUSB_DoTransfer ( hDevice , endpoint , pBuff , count , timeout , isBulk ) ;
}
// Deprecated: Kept for backwards compatibility
unsigned long LJUSB_BulkRead ( HANDLE hDevice , unsigned char endpoint , BYTE * pBuff , unsigned long count )
{
return LJUSB_DoTransfer ( hDevice , endpoint , pBuff , count , LJ_LIBUSB_TIMEOUT_DEFAULT , true ) ;
}
// Deprecated: Kept for backwards compatibility
unsigned long LJUSB_BulkWrite ( HANDLE hDevice , unsigned char endpoint , BYTE * pBuff , unsigned long count )
{
return LJUSB_DoTransfer ( hDevice , endpoint , pBuff , count , LJ_LIBUSB_TIMEOUT_DEFAULT , true ) ;
}
unsigned long LJUSB_Write ( HANDLE hDevice , const BYTE * pBuff , unsigned long count )
{
return LJUSB_SetupTransfer ( hDevice , ( BYTE * ) pBuff , count , LJ_LIBUSB_TIMEOUT_DEFAULT , LJUSB_WRITE ) ;
}
unsigned long LJUSB_Read ( HANDLE hDevice , BYTE * pBuff , unsigned long count )
{
return LJUSB_SetupTransfer ( hDevice , pBuff , count , LJ_LIBUSB_TIMEOUT_DEFAULT , LJUSB_READ ) ;
}
unsigned long LJUSB_Stream ( HANDLE hDevice , BYTE * pBuff , unsigned long count )
{
return LJUSB_SetupTransfer ( hDevice , pBuff , count , LJ_LIBUSB_TIMEOUT_DEFAULT , LJUSB_STREAM ) ;
}
unsigned long LJUSB_WriteTO ( HANDLE hDevice , const BYTE * pBuff , unsigned long count , unsigned int timeout )
{
return LJUSB_SetupTransfer ( hDevice , ( BYTE * ) pBuff , count , timeout , LJUSB_WRITE ) ;
}
unsigned long LJUSB_ReadTO ( HANDLE hDevice , BYTE * pBuff , unsigned long count , unsigned int timeout )
{
return LJUSB_SetupTransfer ( hDevice , pBuff , count , timeout , LJUSB_READ ) ;
}
unsigned long LJUSB_StreamTO ( HANDLE hDevice , BYTE * pBuff , unsigned long count , unsigned int timeout )
{
return LJUSB_SetupTransfer ( hDevice , pBuff , count , timeout , LJUSB_STREAM ) ;
}
void LJUSB_CloseDevice ( HANDLE hDevice )
{
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_CloseDevice \n " ) ;
# endif
if ( LJUSB_isNullHandle ( hDevice ) ) {
return ;
}
//Release
int r = libusb_release_interface ( hDevice , 0 ) ;
if ( r < 0 ) {
fprintf ( stderr , " LJUSB_CloseDevice: failed to release interface \n " ) ;
}
//Close
libusb_close ( hDevice ) ;
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_CloseDevice: closed \n " ) ;
# endif
}
unsigned int LJUSB_GetDevCount ( unsigned long ProductID )
{
libusb_device * * devs = NULL ;
ssize_t cnt = 0 ;
int r = 1 ;
unsigned int i = 0 ;
unsigned int ljFoundCount = 0 ;
if ( ! LJUSB_libusb_initialize ( ) ) {
return 0 ;
}
cnt = libusb_get_device_list ( gLJContext , & devs ) ;
if ( cnt < 0 ) {
fprintf ( stderr , " failed to get device list \n " ) ;
LJUSB_libusbError ( ( int ) cnt ) ;
LJUSB_libusb_exit ( ) ;
return 0 ;
}
libusb_device * dev = NULL ;
// Loop over all USB devices and count the ones with the LabJack
// vendor ID and the passed in product ID.
while ( ( dev = devs [ i + + ] ) ! = NULL ) {
struct libusb_device_descriptor desc ;
r = libusb_get_device_descriptor ( dev , & desc ) ;
if ( r < 0 ) {
fprintf ( stderr , " failed to get device descriptor \n " ) ;
libusb_free_device_list ( devs , 1 ) ;
LJUSB_libusbError ( r ) ;
LJUSB_libusb_exit ( ) ;
return 0 ;
}
if ( LJ_VENDOR_ID = = desc . idVendor & & ProductID = = desc . idProduct ) {
ljFoundCount + + ;
}
}
libusb_free_device_list ( devs , 1 ) ;
return ljFoundCount ;
}
unsigned int LJUSB_GetDevCounts ( UINT * productCounts , UINT * productIds , UINT n )
{
libusb_device * * devs = NULL , * dev = NULL ;
ssize_t cnt = 0 ;
int r = 1 ;
unsigned int i = 0 ;
unsigned int u3ProductCount = 0 , u6ProductCount = 0 ;
unsigned int ue9ProductCount = 0 , u12ProductCount = 0 ;
unsigned int bridgeProductCount = 0 , t7ProductCount = 0 ;
unsigned int digitProductCount = 0 , t4ProductCount = 0 ;
unsigned int t5ProductCount = 0 , allProductCount = 0 ;
if ( ! LJUSB_libusb_initialize ( ) ) {
return 0 ;
}
cnt = libusb_get_device_list ( gLJContext , & devs ) ;
if ( cnt < 0 ) {
fprintf ( stderr , " failed to get device list \n " ) ;
LJUSB_libusbError ( ( int ) cnt ) ;
LJUSB_libusb_exit ( ) ;
return 0 ;
}
// Loop over all USB devices and count the ones with the LabJack
// vendor ID.
while ( ( dev = devs [ i + + ] ) ! = NULL ) {
struct libusb_device_descriptor desc ;
r = libusb_get_device_descriptor ( dev , & desc ) ;
if ( r < 0 ) {
fprintf ( stderr , " failed to get device descriptor \n " ) ;
libusb_free_device_list ( devs , 1 ) ;
LJUSB_libusbError ( r ) ;
LJUSB_libusb_exit ( ) ;
return 0 ;
}
if ( LJ_VENDOR_ID = = desc . idVendor ) {
switch ( desc . idProduct ) {
case U3_PRODUCT_ID :
u3ProductCount + + ;
break ;
case U6_PRODUCT_ID :
u6ProductCount + + ;
break ;
case UE9_PRODUCT_ID :
ue9ProductCount + + ;
break ;
case U12_PRODUCT_ID :
u12ProductCount + + ;
break ;
case BRIDGE_PRODUCT_ID :
bridgeProductCount + + ;
break ;
case T4_PRODUCT_ID :
t4ProductCount + + ;
break ;
case T5_PRODUCT_ID :
t5ProductCount + + ;
break ;
case T7_PRODUCT_ID :
t7ProductCount + + ;
break ;
case DIGIT_PRODUCT_ID :
digitProductCount + + ;
break ;
}
}
}
libusb_free_device_list ( devs , 1 ) ;
for ( i = 0 ; i < n ; i + + ) {
switch ( i ) {
case 0 :
productCounts [ i ] = u3ProductCount ;
productIds [ i ] = U3_PRODUCT_ID ;
allProductCount + = u3ProductCount ;
break ;
case 1 :
productCounts [ i ] = u6ProductCount ;
productIds [ i ] = U6_PRODUCT_ID ;
allProductCount + = u6ProductCount ;
break ;
case 2 :
productCounts [ i ] = ue9ProductCount ;
productIds [ i ] = UE9_PRODUCT_ID ;
allProductCount + = ue9ProductCount ;
break ;
case 3 :
productCounts [ i ] = u12ProductCount ;
productIds [ i ] = U12_PRODUCT_ID ;
allProductCount + = u12ProductCount ;
break ;
case 4 :
productCounts [ i ] = bridgeProductCount ;
productIds [ i ] = BRIDGE_PRODUCT_ID ;
allProductCount + = bridgeProductCount ;
break ;
case 5 :
productCounts [ i ] = t7ProductCount ;
productIds [ i ] = T7_PRODUCT_ID ;
allProductCount + = t7ProductCount ;
break ;
case 6 :
productCounts [ i ] = digitProductCount ;
productIds [ i ] = DIGIT_PRODUCT_ID ;
allProductCount + = digitProductCount ;
break ;
case 7 :
productCounts [ i ] = t4ProductCount ;
productIds [ i ] = T4_PRODUCT_ID ;
allProductCount + = t4ProductCount ;
break ;
case 8 :
productCounts [ i ] = t5ProductCount ;
productIds [ i ] = T5_PRODUCT_ID ;
allProductCount + = t5ProductCount ;
break ;
}
}
return allProductCount ;
}
bool LJUSB_IsHandleValid ( HANDLE hDevice )
{
uint8_t config = 0 ;
int r = 1 ;
if ( LJUSB_isNullHandle ( hDevice ) ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_IsHandleValid: returning false. hDevice is NULL. \n " ) ;
# endif
return false ;
}
// If we can call get configuration without getting an error,
// the handle is still valid.
// Note that libusb_get_configuration() will return a cached value,
// so we replace this call
// r = libusb_get_configuration(hDevice, &config);
// to the actual control tranfser, from the libusb source
r = libusb_control_transfer ( hDevice , LIBUSB_ENDPOINT_IN ,
LIBUSB_REQUEST_GET_CONFIGURATION , 0 , 0 , & config , 1 , LJ_LIBUSB_TIMEOUT_DEFAULT ) ;
if ( r < 0 ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_IsHandleValid: returning false. Return value from libusb_get_configuration was: %d \n " , r ) ;
# endif
LJUSB_libusbError ( r ) ;
return false ;
} else {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_IsHandleValid: returning true. \n " ) ;
# endif
return true ;
}
}
unsigned short LJUSB_GetDeviceDescriptorReleaseNumber ( HANDLE hDevice )
{
libusb_device * dev = NULL ;
struct libusb_device_descriptor desc ;
int r = 0 ;
if ( LJUSB_isNullHandle ( hDevice ) ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_GetDeviceDescriptorReleaseNumber: returning 0. hDevice is NULL. \n " ) ;
# endif
return 0 ;
}
dev = libusb_get_device ( hDevice ) ;
r = libusb_get_device_descriptor ( dev , & desc ) ;
if ( r < 0 ) {
LJUSB_libusbError ( r ) ;
return 0 ;
}
return desc . bcdDevice ;
}
unsigned long LJUSB_GetHIDReportDescriptor ( HANDLE hDevice , BYTE * pBuff , unsigned long count )
{
libusb_device * dev = NULL ;
struct libusb_device_descriptor desc ;
int r = 0 ;
if ( count > UINT16_MAX ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_GetHIDReportDescriptor: returning 0. count is too large. \n " ) ;
# endif
return 0 ;
}
if ( LJUSB_isNullHandle ( hDevice ) ) {
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_GetHIDReportDescriptor: returning 0. hDevice is NULL. \n " ) ;
# endif
return 0 ;
}
//Determine the device from handle.
dev = libusb_get_device ( hDevice ) ;
r = libusb_get_device_descriptor ( dev , & desc ) ;
if ( r < 0 ) {
LJUSB_libusbError ( r ) ;
return 0 ;
}
if ( desc . idProduct ! = U12_PRODUCT_ID ) {
//Only U12 supported
errno = EINVAL ;
return 0 ;
}
r = libusb_control_transfer ( hDevice , 0x81 , 0x06 , 0x2200 , 0x0000 , pBuff , ( uint16_t ) count , LJ_LIBUSB_TIMEOUT_DEFAULT ) ;
if ( r < 0 ) {
LJUSB_libusbError ( r ) ;
return 0 ;
}
# if LJ_DEBUG
fprintf ( stderr , " LJUSB_GetHIDReportDescriptor: returning control transferred = %d. \n " , r ) ;
# endif
return r ;
}
//not supported
bool LJUSB_AbortPipe ( HANDLE hDevice , unsigned long Pipe )
{
( void ) hDevice ;
( void ) Pipe ;
errno = ENOSYS ;
return false ;
}