I will probably spend more time on this than I plan to, though I am not really writing this to run on Windows. At this point, Windows compilation works but when the driver attempts to read the HID descriptor, the response is only 45 bytes instead of the expected 75 (which is what it gets when run on Linux). I was under the impression that this response was just raw data from the device itself, but it's clearly handled differently on the different platforms, so I think it will be somewhat interesting to dig into what is different between the two and why. It's possible I may actually learn something along the way, unfortunately.
1371 lines
39 KiB
C
1371 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
|
|
|
|
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;
|
|
}
|