1371 lines
39 KiB
C
Raw Normal View History

//---------------------------------------------------------------------------
//
// 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;
}
#if !defined(_WIN32)
// 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;
}
}
#endif // _WIN32
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;
}