diff options
Diffstat (limited to 'linux/cusblinux-1.0.c')
-rw-r--r-- | linux/cusblinux-1.0.c | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/linux/cusblinux-1.0.c b/linux/cusblinux-1.0.c new file mode 100644 index 0000000..4b43b1d --- /dev/null +++ b/linux/cusblinux-1.0.c @@ -0,0 +1,659 @@ +/* + * cusblinux.cpp + * + * Filled in by Daniel Risacher on 6/21/05 + * Filled in more by Patrick McNeil on 6/27/05 + * Copyright 2005 Phidgets Inc. All rights reserved. + * + */ + +#include "stdafx.h" +#include "cusb.h" +#include <libusb-1.0/libusb.h> + +int CUSBCloseHandle(CPhidgetHandle phid) { + int ret = 0; + int result = EPHIDGET_OK; + + if (!phid) + return EPHIDGET_INVALIDARG; + + CPhidget_clearStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock); + + if (phid->deviceHandle == NULL) + return EPHIDGET_NOTATTACHED; + + CThread_join(&phid->readThread); + + if((ret = libusb_release_interface((libusb_device_handle *) phid->deviceHandle, phid->deviceDef->pdd_iid)) != 0) + { + switch(ret) + { + case LIBUSB_ERROR_NO_DEVICE: + //usb_release_interface called after the device was unplugged + LOG(PHIDGET_LOG_WARNING, "libusb_release_interface called on unplugged device."); + break; + default: + LOG(PHIDGET_LOG_ERROR, "libusb_release_interface failed with error code: %d", ret); + } + } + + //if we notice that PHIDGET_USB_ERROR_FLAG is set, then reset this device before closing + //this gives us a better chance of getting it back if something has gone wrong. + if(CPhidget_statusFlagIsSet(phid->status, PHIDGET_USB_ERROR_FLAG)) + { + LOG(PHIDGET_LOG_WARNING,"PHIDGET_USB_ERROR_FLAG is set - resetting device."); + if((ret = libusb_reset_device((libusb_device_handle *) phid->deviceHandle)) != 0) + { + LOG(PHIDGET_LOG_ERROR, "libusb_reset_device failed with error code: %d", ret); + result = EPHIDGET_UNEXPECTED; + } + } + + libusb_close((libusb_device_handle *) phid->deviceHandle); + + phid->deviceHandle = NULL; + + return result; +} + +int CUSBSendPacket(CPhidgetHandle phid, unsigned char *buffer) { + int BytesWritten = 0, ret; + + if (!phid) + return EPHIDGET_INVALIDARG; + + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + if (phid->deviceHandle == NULL) + { + LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid"); + return EPHIDGET_UNEXPECTED; + } + + if(phid->interruptOutEndpoint) + { + ret = libusb_interrupt_transfer((libusb_device_handle *)phid->deviceHandle, + LIBUSB_ENDPOINT_OUT | phid->deviceDef->pdd_iid+1, + (char *)buffer, + phid->outputReportByteLength, /* size */ + &BytesWritten, + 500); /* FIXME? timeout */ + } + else + { + BytesWritten = libusb_control_transfer((libusb_device_handle *)phid->deviceHandle, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + LIBUSB_REQUEST_SET_CONFIGURATION, + 0x0200, /* value */ + phid->deviceDef->pdd_iid, /* index*/ + (char *)buffer, + phid->outputReportByteLength, /* size */ + 500); /* FIXME? timeout */ + ret = BytesWritten; + } + + if(ret < 0) + { + switch(ret) + { + case LIBUSB_ERROR_TIMEOUT: //important case? + return EPHIDGET_TIMEOUT; + case LIBUSB_ERROR_NO_DEVICE: + //device is gone - unplugged. + LOG(PHIDGET_LOG_INFO, "Device was unplugged - detach."); + return EPHIDGET_NOTATTACHED; + default: + LOG(PHIDGET_LOG_ERROR, "libusb_control_msg failed with error code: %d", ret); + return EPHIDGET_UNEXPECTED; + } + } + + if (BytesWritten != phid->outputReportByteLength) + { + LOG(PHIDGET_LOG_WARNING,"Failure in CUSBSendPacket - Report Length" + ": %d, bytes written: %d", + (int)phid->outputReportByteLength, (int)BytesWritten); + return EPHIDGET_UNEXPECTED; + } + + return EPHIDGET_OK; +} + +int CUSBSetLabel(CPhidgetHandle phid, char *buffer) { + int BytesWritten = 0; + int size = buffer[0]; + + if(size>22) return EPHIDGET_INVALID; + + if (!phid) + return EPHIDGET_INVALIDARG; + + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + if (phid->deviceHandle == NULL) + { + LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid"); + return EPHIDGET_UNEXPECTED; + } + + BytesWritten = libusb_control_transfer((libusb_device_handle *)phid->deviceHandle, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_SET_DESCRIPTOR, + 0x0304, /* value */ + 0x0409, /* index*/ + (char *)buffer, + size, /* size */ + 500); /* FIXME? timeout */ + + if(BytesWritten < 0) + { + switch(BytesWritten) + { + case LIBUSB_ERROR_TIMEOUT: //important case? + default: + LOG(PHIDGET_LOG_INFO, "usb_control_msg failed with error code: %d", BytesWritten); + return EPHIDGET_UNSUPPORTED; + } + } + + if (BytesWritten != size) + { + LOG(PHIDGET_LOG_WARNING,"Failure in CUSBSetLabel - Report Length" + ": %d, bytes written: %d", + size, (int)BytesWritten); + return EPHIDGET_UNEXPECTED; + } + + return EPHIDGET_OK; +} + +/* Buffer should be at least 8 bytes long */ +int CUSBReadPacket(CPhidgetHandle phid, unsigned char *buffer) { + int BytesRead = 0, ret; + + if (!phid) + return EPHIDGET_INVALIDARG; + + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + if (phid->deviceHandle == NULL) + { + LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid"); + return EPHIDGET_UNEXPECTED; + } + + ret = libusb_interrupt_transfer((libusb_device_handle *)phid->deviceHandle, + LIBUSB_ENDPOINT_IN | phid->deviceDef->pdd_iid+1, + (char *)buffer, + phid->inputReportByteLength, + &BytesRead, + 500); + + if (ret != 0) + { + switch(ret) + { + // A timeout occured, but we'll just try again + case LIBUSB_ERROR_TIMEOUT: + LOG(PHIDGET_LOG_VERBOSE, "libusb_interrupt_transfer timeout"); + return EPHIDGET_TIMEOUT; + case LIBUSB_ERROR_BUSY: + //This happens when someone else calls claim_interface on this interface (a manager for ex.) - basically just wait until they release it. + //This will happen if an open occurs in another app which (for some reason) can steal the interface from this one. + LOG(PHIDGET_LOG_INFO, "Device is busy on Read - try again."); + return EPHIDGET_TRYAGAIN; + case LIBUSB_ERROR_NO_DEVICE: + //device is gone - unplugged. + LOG(PHIDGET_LOG_INFO, "Device was unplugged - detach."); + return EPHIDGET_NOTATTACHED; + case LIBUSB_ERROR_PIPE: + case LIBUSB_ERROR_OVERFLOW: + default: + LOG(PHIDGET_LOG_ERROR, "libusb_interrupt_transfer returned: %d \"%s\"", ret); + goto tryagain; + } + } + + if (BytesRead != phid->inputReportByteLength) + { + //Generally means the device was unplugged, but can mean that there is not enough Interrupt bandwidth + //We keep trying and we'll get data, just not all data + LOG(PHIDGET_LOG_WARNING,"Failure in CUSBReadPacket - Report Length" + ": %d, bytes read: %d. Probably trying to use too many Phidgets at once, and some data is being lost.", + (int)phid->inputReportByteLength, (int)BytesRead); + goto tryagain; + } + + phid->tryAgainCounter = 0; + return EPHIDGET_OK; + + //if we see too many tryagains in a row, then we assume something has actually gone wrong and reset the device +tryagain: + phid->tryAgainCounter++; + if(phid->tryAgainCounter > 30) //this will be hit in < 1 second for all devices + { + LOG(PHIDGET_LOG_ERROR, "CUSBReadPacket returned EPHIDGET_TRYAGAIN too many times in a row - reset device."); + phid->tryAgainCounter = 0; + return EPHIDGET_UNEXPECTED; + } + return EPHIDGET_TRYAGAIN; +} + +static int getLabelString(CPhidgetHandle phid, struct libusb_device_handle *handle) +{ + int len = 0; + char labelBuf[22]; + memset(labelBuf, 0, sizeof(labelBuf)); + + //Note that this returns the whole descriptor, including the length and type bytes + len = libusb_get_string_descriptor(handle, 4, 0, (char *)labelBuf, 22); + + if(len < 0) + { + switch(len) + { + case LIBUSB_ERROR_TIMEOUT: //important case? + default: + LOG(PHIDGET_LOG_INFO, "libusb_get_string_descriptor failed in CUSBGetDeviceCapabilities with error code: %d while reading label\ + - this probably just means the device doesn't support labels, so this is fine.", len); + } + phid->label[0]='\0'; + return EPHIDGET_OK; + } + else + return decodeLabelString(labelBuf, phid->label, phid->serialNumber); +} + +int CUSBRefreshLabelString(CPhidgetHandle phid) +{ + return getLabelString(phid, (struct libusb_device_handle *)phid->deviceHandle); +} + +int CUSBGetDeviceCapabilities(CPhidgetHandle phid, struct libusb_device *dev, struct libusb_device_handle *udev) { + unsigned char buf[255]; + int len = 0, i = 0, j, ret; + const struct libusb_interface_descriptor *interfaceDesc; + struct libusb_config_descriptor *configDesc = NULL; + + memset(buf, 0, sizeof(buf)); + + //Get config descriptor + if((ret = libusb_get_active_config_descriptor(dev, &configDesc)) == 0) + { + interfaceDesc = NULL; + + //Find the interface Descriptor + for(i=0; i<configDesc->bNumInterfaces; i++) + { + for(j=0; j<configDesc->interface[i].num_altsetting; j++) + { + if(configDesc->interface[i].altsetting[j].bInterfaceNumber == phid->deviceDef->pdd_iid) + { + interfaceDesc = &configDesc->interface[i].altsetting[j]; + break; + } + } + } + + if(interfaceDesc == NULL) + { + LOG(PHIDGET_LOG_ERROR, "Couldn't find interface descriptor!"); + ret = EPHIDGET_UNEXPECTED; + goto done; + } + + if(interfaceDesc->bNumEndpoints == 2) + { + LOG(PHIDGET_LOG_INFO, "Using Interrupt OUT Endpoint for Host->Device communication."); + phid->interruptOutEndpoint = PTRUE; + } + else + { + LOG(PHIDGET_LOG_INFO, "Using Control Endpoint for Host->Device communication."); + phid->interruptOutEndpoint = PFALSE; + } + } + else + { + LOG(PHIDGET_LOG_ERROR, "libusb_get_active_config_descriptor failed in CUSBGetDeviceCapabilities with error code: %d", ret); + ret = EPHIDGET_UNEXPECTED; + goto done; + } + + //Get a HID descriptor + len = libusb_control_transfer(udev, LIBUSB_ENDPOINT_IN+1, + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_REPORT << 8) + 0, phid->deviceDef->pdd_iid, (char*)buf, + sizeof(buf), 500 /* ms timeout */); + + if(len < 0) + { + switch(len) + { + case LIBUSB_ERROR_TIMEOUT: //important case? + default: + LOG(PHIDGET_LOG_ERROR, "usb_control_msg failed in CUSBGetDeviceCapabilities with error code: %d", len); + ret = EPHIDGET_UNEXPECTED; + goto done; + } + } + + if(len >= 10) + { + for(i=10;i<len;i++) { + if(buf[i]==0x81 && buf[i-2]==0x95) + phid->inputReportByteLength=buf[i-1]; + else if(buf[i]==0x81 && buf[i-4]==0x95) + phid->inputReportByteLength=buf[i-3]; + if(buf[i]==0x91 && buf[i-2]==0x95) + phid->outputReportByteLength=buf[i-1]; + else if(buf[i]==0x91 && buf[i-4]==0x95) + phid->outputReportByteLength=buf[i-3]; + } + } + else + { + LOG(PHIDGET_LOG_ERROR, "Couldn't get report lengths in CUSBGetDeviceCapabilities"); + ret = EPHIDGET_UNEXPECTED; + goto done; + } + + ret = getLabelString(phid, udev); + +done: + if(configDesc) + libusb_free_config_descriptor(configDesc); + return ret; +} + +libusb_context * libusbContext = NULL; +void CUSBUninit() +{ + if(libusbContext) + { + LOG(PHIDGET_LOG_INFO, "Deinitializing libusb"); + libusb_exit(libusbContext); + libusbContext = NULL; + } +} + +int CUSBBuildList(CPhidgetList **curList) { + int MemberIndex, i, j, ret = EPHIDGET_OK, found; + unsigned long Length; + CPhidgetList *traverse; + Length = 0; + MemberIndex = 0; + CPhidgetHandle phid; + char unique_name[20]; + libusb_device **list = NULL; + + TESTPTR(curList) + + if(!libusbContext) + { + LOG(PHIDGET_LOG_INFO, "Initializing libusb"); + if((ret = libusb_init(&libusbContext)) != 0) + { + LOG(PHIDGET_LOG_ERROR, "libusb_init failed with error code: %d", ret); + libusbContext = NULL; + goto done; + } + } + + ssize_t cnt = libusb_get_device_list(NULL, &list); + if(cnt < 0) + { + LOG(PHIDGET_LOG_ERROR, "libusb_get_device_list failed with error code: %d", cnt); + goto done; + } + + //search through all USB devices + for (j = 0; j < cnt; j++) + { + libusb_device *device = list[j]; + + snprintf(unique_name,20,"%d/%d",libusb_get_bus_number(device), libusb_get_device_address(device)); + + found = PFALSE; + if (AttachedDevices) { + // we need to loop all the way through because composite devices will appear twice in the list with the same 'unique' name + for (traverse = AttachedDevices; traverse; traverse=traverse->next) { + if (!strcmp((char *)traverse->phid->CPhidgetFHandle, unique_name)) { + CList_addToList((CListHandle *)curList, traverse->phid, CPhidget_areEqual); + found = PTRUE; + } + } + if(found) goto next; + } + + struct libusb_device_descriptor desc; + if((ret = libusb_get_device_descriptor(device, &desc)) != 0) + { + LOG(PHIDGET_LOG_ERROR, "libusb_get_device_descriptor failed with error code: %d", ret); + goto next; + } + + //LOG(PHIDGET_LOG_DEBUG, "Device %d: %04x %04x", j, desc.idVendor, desc.idProduct); + + for (i = 1; i<PHIDGET_DEVICE_COUNT; i++) { + if ((desc.idVendor == Phid_Device_Def[i].pdd_vid) && + (desc.idProduct == Phid_Device_Def[i].pdd_pid)) + { + if (!(phid = (CPhidgetHandle)malloc(sizeof (*phid)))) + { + ret = EPHIDGET_NOMEMORY; + goto done; + } + ZEROMEM(phid, sizeof(*phid)); + + //LOG(PHIDGET_LOG_DEBUG,"New Device: %s",(char *)Phid_DeviceName[Phid_Device_Def[i].pdd_did]); + + libusb_device_handle *handle = NULL; + if (libusb_open(device, &handle) == 0) + { + if (desc.bcdDevice < 0x100) + phid->deviceVersion = desc.bcdDevice * 100; + else + phid->deviceVersion = ((desc.bcdDevice >> 8) * 100) + ((desc.bcdDevice & 0xff)); + phid->deviceType = (char *)Phid_DeviceName[Phid_Device_Def[i].pdd_did]; + + CPhidget_setStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock); + phid->deviceIDSpec = Phid_Device_Def[i].pdd_sdid; + phid->deviceUID = CPhidget_getUID(phid->deviceIDSpec, phid->deviceVersion); + phid->deviceDef = &Phid_Device_Def[i]; + phid->deviceID = Phid_Device_Def[i].pdd_did; + phid->ProductID = desc.idProduct; + phid->VendorID = desc.idVendor; + + if (desc.iSerialNumber) { + char string[256]; + memset(string, 0, 256); + if((ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string))) < 0) + { + LOG(PHIDGET_LOG_ERROR, "libusb_get_string_descriptor_ascii failed with error code: %d", ret); + LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root"); + libusb_close(handle); + free(phid); + goto next; + } + else + { + phid->serialNumber = atol(string); + getLabelString(phid, handle); + } + } + phid->specificDevice = TRUE; + phid->attr = Phid_Device_Def[i].pdd_attr; + + if(!(phid->CPhidgetFHandle = strdup(unique_name))) + { + ret = EPHIDGET_NOMEMORY; + goto done; + } + + LOG(PHIDGET_LOG_INFO, "New device in CUSBBuildList: %s", (char *)phid->CPhidgetFHandle); + + libusb_close(handle); + CList_addToList((CListHandle *)curList, phid, CPhidget_areEqual); + } //if(udev) + else + { + free(phid); + libusb_close(handle); + } + } //vendor, product ids match + } /* iterate over phidget device table */ +next: ; + } /* iterate over USB devices */ + +done: + if(list) + libusb_free_device_list(list, 1); + return ret; +} + +void CUSBCleanup(void) +{ + ; +} +/* + CUSBOpenHandle takes a CPhidgetInfo structure, with + ProductID/VendorID/SerialNumber filled in. + + Serial number is always filled in. +*/ +int CUSBOpenHandle(CPhidgetHandle phid) +{ + int idVendor; + int idProduct; + int serial = 0; + int i, j,ret = EPHIDGET_OK; + libusb_device **list = NULL; + + ssize_t cnt = libusb_get_device_list(NULL, &list); + + if(cnt < 0) + { + LOG(PHIDGET_LOG_ERROR, "libusb_get_device_list failed with error code: %d", cnt); + goto done; + } + + //search through all USB devices + for (j = 0; j < cnt; j++) + { + libusb_device *device = list[j]; + + struct libusb_device_descriptor desc; + if((ret = libusb_get_device_descriptor(device, &desc)) != 0) + { + LOG(PHIDGET_LOG_ERROR, "libusb_get_device_descriptor failed with error code: %d", ret); + goto next; + } + + for (i = 1; i<PHIDGET_DEVICE_COUNT; i++) { + if (Phid_Device_Def[i].pdd_did == phid->deviceID) { + idVendor = Phid_Device_Def[i].pdd_vid; + idProduct = Phid_Device_Def[i].pdd_pid; + if ((desc.idVendor == idVendor) && (desc.idProduct == idProduct)) { + + /* the vend/prod matches! */ + libusb_device_handle *handle = NULL; + if (libusb_open(device, &handle) == 0) + { + serial = -1; + if (desc.iSerialNumber) { + char string[256]; + if((ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string))) < 0) + { + LOG(PHIDGET_LOG_WARNING, "libusb_get_string_descriptor_ascii failed with error code: %d", ret); + LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root"); + libusb_close(handle); + goto next; + } + else + { + serial = atol(string); + } + } + if (serial == phid->serialNumber) { + //Detach any Kernel Drivers + if((ret = libusb_kernel_driver_active(handle, Phid_Device_Def[i].pdd_iid)) < 0) + { + LOG(PHIDGET_LOG_WARNING, "libusb_kernel_driver_active failed with error code: %d", ret); + } + else if(ret == 1) + { + LOG(PHIDGET_LOG_INFO, "Kernel driver is active - will attempt a detach"); + if((ret = libusb_detach_kernel_driver(handle, Phid_Device_Def[i].pdd_iid)) != 0) + { + LOG(PHIDGET_LOG_WARNING, "libusb_detach_kernel_driver failed with error code: %d", ret); + } + else + { + LOG(PHIDGET_LOG_INFO, "Successfully detached kernel driver"); + } + } + + if((ret = libusb_claim_interface(handle, Phid_Device_Def[i].pdd_iid)) != 0) + { + LOG(PHIDGET_LOG_WARNING, "libusb_claim_interface failed with error code: %d", ret); + libusb_close(handle); + } + else + { + /* the serialnum is okay */ + + phid->deviceHandle = (HANDLE)handle; + phid->deviceIDSpec = Phid_Device_Def[i].pdd_sdid; + phid->deviceDef = &Phid_Device_Def[i]; + phid->deviceType = (char *)Phid_DeviceName[Phid_Device_Def[i].pdd_did]; + + phid->ProductID = idProduct; + phid->VendorID = idVendor; + if (desc.bcdDevice < 0x100) + phid->deviceVersion = desc.bcdDevice * 100; + else + phid->deviceVersion = ((desc.bcdDevice >> 8) * 100) + ((desc.bcdDevice & 0xff)); + + phid->deviceUID = CPhidget_getUID(phid->deviceIDSpec, phid->deviceVersion); + phid->serialNumber = serial; + + if((ret = CUSBGetDeviceCapabilities(phid, device, handle))) + { + LOG(PHIDGET_LOG_ERROR, "CUSBGetDeviceCapabilities returned nonzero code: %d", ret); + } + + phid->attr = Phid_Device_Def[i].pdd_attr; + + ret = EPHIDGET_OK; + goto done; + } /* usb_claim_interface */ + } /* serial matches */ + else + { + libusb_close(handle); + } + } /* udev open */ + else + { + libusb_close(handle); + LOG(PHIDGET_LOG_WARNING, "usb_open failed - bad permission or what?"); + } + } /* vendor/product match */ + } /* deviceID matches in table */ + } /* iterate over phidget device table */ +next: ; + } /* iterate over USB devices */ +done: + if(list) + libusb_free_device_list(list, 1); + + return ret; +} |