1372 lines
39 KiB
C
1372 lines
39 KiB
C
|
//---------------------------------------------------------------------------
|
||
|
//
|
||
|
// 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
|
||
|
|
||
|
// Set to 0 for no debug logging or 1 for logging.
|
||
|
#define LJ_DEBUG 0
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|