From 0b624384cd52be20e61284551d832b499d7b7707 Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Sat, 14 Apr 2012 12:56:48 +0100 Subject: Imported Upstream version 2.1.8.20120216 --- cphidget.c | 1637 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1637 insertions(+) create mode 100644 cphidget.c (limited to 'cphidget.c') diff --git a/cphidget.c b/cphidget.c new file mode 100644 index 0000000..d8d5cb3 --- /dev/null +++ b/cphidget.c @@ -0,0 +1,1637 @@ +#include "stdafx.h" +#include "cphidget.h" +#include "cusb.h" +#include "csocket.h" +#include "cthread.h" +#include "cphidgetlist.h" +#include "utils.h" + +int print_debug_messages = FALSE; + +CPhidgetListHandle ActiveDevices = 0; +CPhidgetListHandle AttachedDevices = 0; + +/* Protects ActiveDevices and AttachedDevices */ +int phidgetLocksInitialized = PFALSE; +CThread_mutex_t activeDevicesLock; +CThread_mutex_t attachedDevicesLock; + +char +translate_bool_to_ascii(char value) +{ + switch (value) { + case PTRUE: + return '1'; + case PFALSE: + return '0'; + } + return '?'; +} + +int CCONV phidget_type_to_id(const char *name) +{ + int i; + for(i=0;itv_sec) + (double)((now.tv_usec-start->tv_usec)/1000000.0); +#endif + return duration; +} + +void setTimeNow(TIME *now) +{ + #ifdef _WINDOWS + GetSystemTime(now); + #else + gettimeofday(now,NULL); + #endif +} + +/* The is a void pointer that gets malloced only of some platforms */ +void CPhidgetFHandle_free(void *arg) +{ + #if defined(_MACOSX) || defined(WINCE) + return; + #else + free(arg); arg = NULL; + return; + #endif +} + +//Two different Phidget Handles that refer to the same Physical Phidget +int CCONV CPhidget_areEqual(void *arg1, void *arg2) +{ + CPhidgetHandle phid1 = (CPhidgetHandle)arg1; + CPhidgetHandle phid2 = (CPhidgetHandle)arg2; + + if(!phid1 || !phid2) + return PFALSE; + + // If they are not the same device class + if(phid2->deviceID != phid1->deviceID) + return PFALSE; + + // If they both have a device id, but they are different + if(phid1->deviceIDSpec && phid2->deviceIDSpec && (phid2->deviceIDSpec != phid1->deviceIDSpec)) + return PFALSE; + + // Neither device is marked as PHIDGETOPEN_ANY + if(phid1->specificDevice && phid2->specificDevice) + { + // If one is open serial and the other is open label + if(phid1->specificDevice == PHIDGETOPEN_SERIAL && phid2->specificDevice == PHIDGETOPEN_LABEL || + phid1->specificDevice == PHIDGETOPEN_LABEL && phid2->specificDevice == PHIDGETOPEN_SERIAL) + return PFALSE; + + // If one is open serial but they have different serials + if((phid1->specificDevice == PHIDGETOPEN_SERIAL || phid2->specificDevice == PHIDGETOPEN_SERIAL) && + phid2->serialNumber != phid1->serialNumber) + return PFALSE; + + // If one is open label but they have different labels + if((phid1->specificDevice == PHIDGETOPEN_LABEL || phid2->specificDevice == PHIDGETOPEN_LABEL) && + strncmp(phid1->label, phid2->label, MAX_LABEL_STORAGE-1)) + return PFALSE; + } + + return PTRUE; + + //OLD + /*if( + ( + (phid1->specificDevice == 0 || phid2->specificDevice == 0)?1:(phid2->serialNumber == phid1->serialNumber)) + && + ( + (phid1->deviceIDSpec == 0 || phid2->deviceIDSpec == 0)?1:(phid2->deviceIDSpec == phid1->deviceIDSpec) + ) && + (phid2->deviceID == phid1->deviceID)) + return 1; + return 0;*/ +} + +//Two different Phidget Handles that refer to the same Physical Phidget - more stringent +// Same serial, device class and device id +int CCONV CPhidget_areExtraEqual(void *arg1, void *arg2) +{ + CPhidgetHandle phid1 = (CPhidgetHandle)arg1; + CPhidgetHandle phid2 = (CPhidgetHandle)arg2; + + if(!phid1 || !phid2) + return PFALSE; + + if( + (phid2->serialNumber == phid1->serialNumber) && + (phid2->deviceIDSpec == phid1->deviceIDSpec) && + (phid2->deviceID == phid1->deviceID) + ) + return PTRUE; + return PFALSE; +} + +//Two identical Phidget Handles (same pointer) +int CCONV CPhidgetHandle_areEqual(void *arg1, void *arg2) +{ + if(arg1 == arg2) return 1; + return 0; +} + +void CPhidgetErrorEvent_free(void *arg) +{ + free(((void **)arg)[0]); + free(arg); +} + +void CCONV CPhidget_free(void *arg) +{ + CPhidgetHandle phid = (CPhidgetHandle)arg; + if (!phid) + return; + + //Call device specific free function if it exists + if(phid->fptrFree) + phid->fptrFree(phid); + + //this is only malloc-ed on windows and linux, not wince or mac +#if (defined(_WINDOWS) && !defined(WINCE)) || defined(_LINUX) || defined(_ANDROID) + if (phid->CPhidgetFHandle) { + CPhidgetFHandle_free(phid->CPhidgetFHandle); phid->CPhidgetFHandle = NULL; + } +#endif + + CThread_mutex_destroy(&phid->lock); + CThread_mutex_destroy(&phid->openCloseLock); + CThread_mutex_destroy(&phid->writelock); + CThread_mutex_destroy(&phid->outputLock); + CThread_destroy_event(&phid->writeAvailableEvent); + CThread_destroy_event(&phid->writtenEvent); + + CList_emptyList((CListHandle *)&phid->errEventList, PTRUE, CPhidgetErrorEvent_free); + + free(phid); phid = NULL; + return; +} + +int CCONV CPhidget_create(CPhidgetHandle *phid) +{ + CPhidgetHandle temp_phid; + + TESTPTR(phid) + + if(!(temp_phid = malloc(sizeof(CPhidget)))) + return EPHIDGET_NOMEMORY; + ZEROMEM(temp_phid, sizeof(CPhidget)); + + CThread_mutex_init(&temp_phid->lock); + CThread_mutex_init(&temp_phid->openCloseLock); + CThread_mutex_init(&temp_phid->writelock); + CThread_mutex_init(&temp_phid->outputLock); + CThread_create_event(&temp_phid->writeAvailableEvent); + CThread_create_event(&temp_phid->writtenEvent); + + CPhidget_clearStatusFlag(&temp_phid->status, PHIDGET_ATTACHED_FLAG, &temp_phid->lock); + + *phid = temp_phid; + + return EPHIDGET_OK; +} + +const char *CPhidget_strerror(int error) +{ + if ((error < 0) || (error >= PHIDGET_ERROR_CODE_COUNT)) + return Phid_UnknownErrorDescription; + + return Phid_ErrorDescriptions[error]; +} + +int CPhidget_statusFlagIsSet(int status, int flag) +{ + if(status & flag) return PTRUE; + return PFALSE; +} + +//status is a flags variable +int CPhidget_setStatusFlag(int *status, int flag, CThread_mutex_t *lock) +{ + TESTPTR(status) + + if(lock != NULL) CThread_mutex_lock(lock); + *status |= flag; + if(lock != NULL) CThread_mutex_unlock(lock); + + return EPHIDGET_OK; +} + +int CPhidget_clearStatusFlag(int *status, int flag, CThread_mutex_t *lock) +{ + TESTPTR(status) + + if(lock != NULL) CThread_mutex_lock(lock); + *status &= (~flag); + if(lock != NULL) CThread_mutex_unlock(lock); + + return EPHIDGET_OK; +} + +int CPhidget_read(CPhidgetHandle phid) +{ + int result = EPHIDGET_OK; + + TESTPTR(phid) + + if (CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + || CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG)) { + result = CUSBReadPacket((CPhidgetHandle)phid, + phid->lastReadPacket); + if (result) return result; + if (phid->fptrData) + result= phid->fptrData((CPhidgetHandle)phid, + phid->lastReadPacket, phid->inputReportByteLength); + return result; + } + return EPHIDGET_NOTATTACHED; +} + + +int CPhidget_write(CPhidgetHandle phid) +{ + unsigned char buffer[MAX_OUT_PACKET_SIZE]; + unsigned int len; + int result = EPHIDGET_OK; + + TESTPTR(phid) + + ZEROMEM(buffer, sizeof(buffer)); + + CThread_reset_event(&phid->writeAvailableEvent); + + len = MAX_OUT_PACKET_SIZE; + if ((result = phid->fptrGetPacket((CPhidgetHandle)phid, buffer, &len)) + != EPHIDGET_OK) + goto fail; + // XXX len ignored + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != + EPHIDGET_OK) + goto fail; +fail: + /* + * under all circumstances (especially failure), signal + * waiting writers to loop around and check device status. + */ + CThread_set_event(&phid->writtenEvent); + return result; +} +//Begin exported functions +int CCONV +CPhidget_open(CPhidgetHandle phid, int serialNumber) +{ + int result = 0; + TESTPTR(phid) + if (serialNumber < -1) + return EPHIDGET_INVALIDARG; + +#if defined(_IPHONE) + return EPHIDGET_UNSUPPORTED; +#endif + +#if defined(_ANDROID) + if(!ANDROID_USB_GOOD) + return EPHIDGET_UNSUPPORTED; +#endif + + CThread_mutex_lock(&phid->openCloseLock); + if (CPhidget_statusFlagIsSet(phid->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Phidget handle."); + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_OK; + } + + if (serialNumber == -1) + phid->specificDevice = PHIDGETOPEN_ANY; + else + phid->specificDevice = PHIDGETOPEN_SERIAL; + phid->serialNumber = serialNumber; + + result = RegisterLocalDevice(phid); + + CPhidget_setStatusFlag(&phid->status, PHIDGET_OPENED_FLAG, &phid->lock); + CThread_mutex_unlock(&phid->openCloseLock); + + return result; +} + +int CCONV +CPhidget_openLabel(CPhidgetHandle phid, const char *label) +{ + int result = 0; + TESTPTR(phid) + +#if defined(_IPHONE) + return EPHIDGET_UNSUPPORTED; +#endif + +#if defined(_ANDROID) + if(!ANDROID_USB_GOOD) + return EPHIDGET_UNSUPPORTED; +#endif + + if(label != NULL && ((result = encodeLabelString(label, NULL, NULL)) != EPHIDGET_OK)) + return result; + + CThread_mutex_lock(&phid->openCloseLock); + if (CPhidget_statusFlagIsSet(phid->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Phidget handle."); + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_OK; + } + + if (label == NULL) + phid->specificDevice = PHIDGETOPEN_ANY; + else + { + phid->specificDevice = PHIDGETOPEN_LABEL; + memcpy(phid->label, label, strlen(label)+1); + } + + result = RegisterLocalDevice(phid); + + CPhidget_setStatusFlag(&phid->status, PHIDGET_OPENED_FLAG, &phid->lock); + CThread_mutex_unlock(&phid->openCloseLock); + + return result; +} + +int CCONV +CPhidget_close(CPhidgetHandle phid) +{ + int result = EPHIDGET_OK; + + char key[1024], val[6]; + + TESTPTR(phid) + + CThread_mutex_lock(&phid->openCloseLock); + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_OPENED_FLAG)) + { + //Could be .NET finalizer - always calls close even if already called. + LOG(PHIDGET_LOG_INFO, "Close was called on an already closed Phidget handle."); + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_OK; + } + + //Call device specific close function if it exists + if(phid->fptrClose) + phid->fptrClose(phid); + + if(CPhidget_statusFlagIsSet(phid->status, PHIDGET_REMOTE_FLAG)) + { + CThread_mutex_lock(&phid->lock); + if(CPhidget_statusFlagIsSet(phid->status, PHIDGET_SERVER_CONNECTED_FLAG)) + { + struct sockaddr_storage name; + char addr[200], *a; + socklen_t namelen = sizeof(name); + int port, e; + + if(getsockname(phid->networkInfo->server->socket, (struct sockaddr *)&name, &namelen) != 0) + { +#ifndef _WINDOWS + LOG(PHIDGET_LOG_WARNING,"getsockname: %s", strerror(errno)); +#else + LOG(PHIDGET_LOG_WARNING,"getsockname: WSA Error %d", WSAGetLastError()); +#endif + goto netdone; + } + if((e = getnameinfo((struct sockaddr *)&name, namelen, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) + { + LOG(PHIDGET_LOG_WARNING,"getnameinfo: %s", gai_strerror(e)); + goto netdone; + } + port = (int)((struct sockaddr_in *)&name)->sin_port; + escape(addr, strlen(addr), &a); + + if(phid->specificDevice == PHIDGETOPEN_SERIAL) + snprintf(key, sizeof(key), "/PCK/Client/%s/%d%05d/%s/%d", a, phid->networkInfo->uniqueConnectionID, port, Phid_DeviceName[phid->deviceID], phid->serialNumber); + else if(phid->specificDevice == PHIDGETOPEN_LABEL) + { + char *l; + escape(phid->label, strlen(phid->label), &l); + snprintf(key, sizeof(key), "/PCK/Client/%s/%d%05d/%s/-1/%s", a, phid->networkInfo->uniqueConnectionID, port, Phid_DeviceName[phid->deviceID], l); + free(l); + } + else + snprintf(key, sizeof(key), "/PCK/Client/%s/%d%05d/%s", a, phid->networkInfo->uniqueConnectionID, port, Phid_DeviceName[phid->deviceID]); + snprintf(val, sizeof(val), "Close"); + free(a); + //don't care about errors on this, so we don't add the error handler - just close already! + pdc_async_set(phid->networkInfo->server->pdcs, key, val, (int)strlen(val), PTRUE, NULL, NULL); + } +netdone: + CThread_mutex_unlock(&phid->lock); + + result = unregisterRemotePhidget(phid); + + phid->keyCount = 0; + } + else + { + if(!phidgetLocksInitialized) + { + CThread_mutex_init(&activeDevicesLock); + CThread_mutex_init(&attachedDevicesLock); + phidgetLocksInitialized = PTRUE; + } + CThread_mutex_lock(&activeDevicesLock); + CList_removeFromList((CListHandle *)&ActiveDevices, phid, CPhidget_areEqual, FALSE, NULL); + CThread_mutex_unlock(&activeDevicesLock); + if (CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)) { + phid->writeStopFlag = PTRUE; + CThread_join(&phid->writeThread); //join before closing because we want to wait for outstanding writes to complete + result = CUSBCloseHandle(phid); + CThread_join(&phid->readThread); + } + + if(phid->specificDevice == PHIDGETOPEN_ANY_ATTACHED) + { + phid->specificDevice = PHIDGETOPEN_ANY; + phid->serialNumber = -1; + } + + //if there are no more active phidgets or managers, wait for the central thread to exit + if(!ActiveDevices && !ActivePhidgetManagers) + { + JoinCentralThread(); + } + } + CPhidget_clearStatusFlag(&phid->status, PHIDGET_OPENED_FLAG, &phid->lock); + + CThread_mutex_unlock(&phid->openCloseLock); + return result; +} + +//TODO: maybe this should take (CPhidgetHandle *) so that we can nulify the pointer +// (and handle multiple calls to CPhidget_delete) +int CCONV +CPhidget_delete(CPhidgetHandle phid) +{ + CPhidget_free(phid); + return EPHIDGET_OK; +} + +int CCONV +CPhidget_set_OnDetach_Handler(CPhidgetHandle phid, + int(CCONV *fptr)(CPhidgetHandle, void *), void *userPtr) +{ + TESTPTR(phid) + phid->fptrDetach = fptr; + phid->fptrDetachptr = userPtr; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_set_OnAttach_Handler(CPhidgetHandle phid, + int(CCONV *fptr)(CPhidgetHandle, void *), void *userPtr) +{ + TESTPTR(phid) + phid->fptrAttach = fptr; + phid->fptrAttachptr = userPtr; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_set_OnError_Handler(CPhidgetHandle phid, + int (CCONV *fptr) (CPhidgetHandle, void *, int, const char *), void *userPtr) +{ + TESTPTR(phid) + phid->fptrError = fptr; + phid->fptrErrorptr = userPtr; + return EPHIDGET_OK; +} +int CCONV CPhidget_set_OnServerConnect_Handler(CPhidgetHandle phid, int (CCONV *fptr)(CPhidgetHandle phid, void *userPtr), void *userPtr) +{ + TESTPTR(phid) + phid->fptrServerConnect = fptr; + phid->fptrServerConnectptr = userPtr; + return EPHIDGET_OK; +} +int CCONV CPhidget_set_OnServerDisconnect_Handler(CPhidgetHandle phid, int (CCONV *fptr)(CPhidgetHandle phid, void *userPtr), void *userPtr) +{ + TESTPTR(phid) + phid->fptrServerDisconnect = fptr; + phid->fptrServerDisconnectptr = userPtr; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_getDeviceName(CPhidgetHandle phid, const char **buffer) +{ + TESTPTRS(phid, buffer) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_DETACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + *buffer = (char *)phid->deviceDef->pdd_name; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_getSerialNumber(CPhidgetHandle phid, int *serialNumber) +{ + TESTPTRS(phid, serialNumber) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_DETACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + *serialNumber = phid->serialNumber; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_getDeviceVersion(CPhidgetHandle phid, int *devVer) +{ + TESTPTRS(phid, devVer) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_DETACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + *devVer = phid->deviceVersion; + return EPHIDGET_OK; +} + +/* for now this just returns the attached bit of the status variable - this function should probably be renamed*/ +// This CAN be called on closed devices, this should NOT be called internally as it is confusing +int CCONV +CPhidget_getDeviceStatus(CPhidgetHandle phid, int *status) +{ + TESTPTRS(phid, status) + + *status = CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG); + return EPHIDGET_OK; +} + +/* for now this just returns the attached bit of the status variable */ +int CCONV +CPhidget_getServerStatus(CPhidgetHandle phid, int *status) +{ + TESTPTRS(phid, status) + + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_REMOTE_FLAG)) + return EPHIDGET_UNSUPPORTED; + + CThread_mutex_lock(&phid->lock); + if(CPhidget_statusFlagIsSet(phid->status, PHIDGET_SERVER_CONNECTED_FLAG)) + if(phid->networkInfo->server) + *status = CPhidget_statusFlagIsSet(phid->networkInfo->server->status, PHIDGETSOCKET_CONNECTED_FLAG); + else + *status = PFALSE; + else + *status = PFALSE; + CThread_mutex_unlock(&phid->lock); + + return EPHIDGET_OK; +} + + +int CCONV +CPhidget_getLibraryVersion(const char **buffer) +{ + TESTPTR(buffer) + *buffer = LibraryVersion; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_getDeviceType(CPhidgetHandle phid, const char **buffer) +{ + TESTPTRS(phid, buffer) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_DETACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + *buffer = (char *)Phid_DeviceName[phid->deviceID]; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_getDeviceID(CPhidgetHandle phid, CPhidget_DeviceID *deviceID) +{ + TESTPTRS(phid, deviceID) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_DETACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + *deviceID = phid->deviceIDSpec; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_getDeviceClass(CPhidgetHandle phid, CPhidget_DeviceClass *deviceClass) +{ + TESTPTRS(phid, deviceClass) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_DETACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + *deviceClass = phid->deviceID; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_getDeviceLabel(CPhidgetHandle phid, const char **buffer) +{ + TESTPTRS(phid, buffer) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_DETACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + *buffer = (char *)phid->label; + return EPHIDGET_OK; +} + +int CCONV +CPhidget_setDeviceLabel(CPhidgetHandle phid, const char *buffer) +{ + int ret = EPHIDGET_OK; + TESTPTRS(phid, buffer) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)) + return EPHIDGET_NOTATTACHED; + + if(CPhidget_statusFlagIsSet(phid->status, PHIDGET_REMOTE_FLAG)) + { + char key[1024]; + + if((ret = encodeLabelString(buffer, NULL, NULL)) != EPHIDGET_OK) + return ret; + + snprintf(key, sizeof(key), "/PCK/%s/%d/Label", phid->deviceType, phid->serialNumber); + CThread_mutex_lock(&phid->lock); + if(!CPhidget_statusFlagIsSet(phid->status, PHIDGET_SERVER_CONNECTED_FLAG)) + { + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_NETWORK_NOTCONNECTED; + } + pdc_async_set(phid->networkInfo->server->pdcs, key, buffer, (int)strlen(buffer), PFALSE, internal_async_network_error_handler, phid); + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_OK; + } + else + { + +#if defined(_WINDOWS) && !defined(WINCE) + //setLabel not supported on Windows only (Windows CE does support it) + return EPHIDGET_UNSUPPORTED; +#else + int len; + char buffer2[(MAX_LABEL_SIZE * 2) + 2]; + ZEROMEM(buffer2, (MAX_LABEL_SIZE * 2) + 2); + + len = (MAX_LABEL_SIZE * 2); + if((ret = encodeLabelString(buffer, &buffer2[2], &len)) == EPHIDGET_OK) + { + //length of descriptor + buffer2[0] = len+2; + //type of descriptor (string) + buffer2[1] = 3; + + //make sure we're not trying to set a label that will match the wrap-around bug when read back + if(labelHasWrapError(phid->serialNumber, buffer2) == PTRUE) + { + LOG(PHIDGET_LOG_WARNING, "Can't set a label that would match the wraparound bug."); + return EPHIDGET_INVALIDARG; + } + + if ((ret = CUSBSetLabel(phid, buffer2)) == EPHIDGET_OK) + { + int triedUTF8 = PFALSE; + + refresh: + //read back the label and compare it + if ((ret = CUSBRefreshLabelString(phid)) == EPHIDGET_OK) + { + CPhidgetHandle foundPhidget; + + //label read back didn't match + if(strcmp(buffer, phid->label)) + { + //label descriptor is longer then 16 bytes and the first 7 bytes back match; + // almost certainly this is a problem with the wrap around bug. + if(buffer2[0] > 16 && !strncmp(buffer, phid->label, 7) && triedUTF8 == PFALSE) + { + //try setting the label as UTF-8 with 0xFFFF header - we can encode up to 12 bytes + if(strlen(buffer) <= 12) + { + LOG(PHIDGET_LOG_INFO, "Trying to setLabel as UTF-8 because of wrap around bug."); + + //only try this once + triedUTF8 = PTRUE; + + strcpy(&buffer2[4], buffer); + buffer2[0] = strlen(buffer) + 4; + buffer2[2] = 0xFF; + buffer2[3] = 0xFF; + + if ((ret = CUSBSetLabel(phid, buffer2)) == EPHIDGET_OK) + { + //go check it + goto refresh; + } + else //setLabel failed + { + LOG(PHIDGET_LOG_ERROR, "Something unexpected happened trying to set the label (UTF-8). Try again."); + goto clearlabel; + } + } + else //label is too long to be stored, but we have tried to write the label, so we need to clear out anything stored + { + ret = EPHIDGET_INVALIDARG; + LOG(PHIDGET_LOG_ERROR, "This device supports 12-bytes UTF-8 labels. Try again with a shorter string, or pure ASCII."); + goto clearlabel; + } + } + else //label doesn't match and it doesn't look like the wrap around error + { + ret = EPHIDGET_UNEXPECTED; + LOG(PHIDGET_LOG_ERROR, "set label doesn't match read back label: \"%s\" vs. \"%s\"", buffer, phid->label); + goto clearlabel; + } + } + else //label matches, we're good + { + //update label in PhidgetManager + CThread_mutex_lock(&attachedDevicesLock); + if(CList_findInList((CListHandle)AttachedDevices, phid, CPhidget_areEqual, (void **)&foundPhidget) == EPHIDGET_OK) + { + strcpy(foundPhidget->label, buffer); + } + CThread_mutex_unlock(&attachedDevicesLock); + } + } + else + { + LOG(PHIDGET_LOG_ERROR, "Was unable to read back the label after setting."); + goto clearlabel; + } + } + else + { + LOG(PHIDGET_LOG_ERROR, "Something unexpected happened trying to set the label. Try again."); + return ret; + } + } + else + { + LOG(PHIDGET_LOG_ERROR, "Error encoding label string, not setting label."); + return ret; + } + + //Success! + return EPHIDGET_OK; + + //if a setLabel succeeded, but then we got an error verifying, then we should just clear the label so there's nothing funky in there. + clearlabel: + LOG(PHIDGET_LOG_INFO, "Clearing label because of an error during set."); + ZEROMEM(buffer2, (MAX_LABEL_SIZE * 2) + 2); + buffer2[0] = 2; + buffer2[1] = 3; + CUSBSetLabel(phid, buffer2); + return ret; +#endif + } +} + +int CCONV +CPhidget_getServerID(CPhidgetHandle phid, const char **buffer) +{ + TESTPTRS(phid, buffer) + +#ifdef USE_ZEROCONF + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_REMOTE_FLAG)) + return EPHIDGET_UNSUPPORTED; + + CThread_mutex_lock(&phid->lock); + + if(!phid->networkInfo->mdns) //not mDNS - not yet supported + { + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_UNSUPPORTED; + } + + //refresh ONLY if connected - otherwise it might block + if(CPhidget_statusFlagIsSet(phid->status, PHIDGET_SERVER_CONNECTED_FLAG)) + { + if(refreshZeroconfPhidget(phid)) + { + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_NETWORK; + } + } + if(phid->networkInfo->zeroconf_server_id) + { + *buffer = (char *)phid->networkInfo->zeroconf_server_id; + } + else if(phid->networkInfo->requested_serverID) + { + *buffer = (char *)phid->networkInfo->requested_serverID; + } + else + { + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_UNEXPECTED; + } + + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_OK; +#else + return EPHIDGET_UNSUPPORTED; +#endif +} + +int CCONV +CPhidget_getServerAddress(CPhidgetHandle phid, const char **ipAddr, int *port) +{ + TESTPTRS(phid, ipAddr) + TESTPTR(port) + + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_REMOTE_FLAG)) + return EPHIDGET_UNSUPPORTED; + + CThread_mutex_lock(&phid->lock); +#ifdef USE_ZEROCONF + if(phid->networkInfo->mdns) + { + //Look it up again new EVERY time! + if(getZeroconfHostPort(phid->networkInfo)) + { + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_NETWORK; + } + if(!phid->networkInfo->zeroconf_host || !phid->networkInfo->zeroconf_port) + { + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_UNEXPECTED; + } + *ipAddr = (char *)phid->networkInfo->zeroconf_host; + *port = strtol(phid->networkInfo->zeroconf_port, NULL, 10); + } + else +#endif + { + if(CPhidget_statusFlagIsSet(phid->status, PHIDGET_SERVER_CONNECTED_FLAG)) + { + if(!phid->networkInfo->server->address || !phid->networkInfo->server->port) + { + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_UNEXPECTED; + } + *ipAddr = (char *)phid->networkInfo->server->address; + *port = strtol(phid->networkInfo->server->port, NULL, 10); + } + else + { + *ipAddr = (char *)phid->networkInfo->requested_address; + *port = strtol(phid->networkInfo->requested_port, NULL, 10); + } + } + CThread_mutex_unlock(&phid->lock); + return EPHIDGET_OK; +} + +int CCONV CPhidget_getErrorDescription(int ErrorCode, const char **buf) +{ + TESTPTR(buf) + if ((ErrorCode < 0) || (ErrorCode >= PHIDGET_ERROR_CODE_COUNT)) { + *buf = CPhidget_strerror(EPHIDGET_INVALID); + return EPHIDGET_INVALID; + } + *buf = CPhidget_strerror(ErrorCode); + return EPHIDGET_OK; +} + +//expect 6 bytes of data +int CCONV CPhidget_calibrate(CPhidgetHandle phid, unsigned char Offset, unsigned char *data) +{ + int result; + unsigned char buffer[8]; + TESTPTR(phid) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)) + return EPHIDGET_NOTATTACHED; + + ZEROMEM(buffer, sizeof(buffer)); + + buffer[0] = 0x74; + buffer[1] = Offset; + buffer[2] = data[0]; + buffer[3] = data[1]; + buffer[4] = data[2]; + buffer[5] = data[3]; + buffer[6] = data[4]; + buffer[7] = data[5]; + + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + return EPHIDGET_OK; +} + +int CCONV CPhidget_calibrate_gain2offset(CPhidgetHandle phid, int Index, unsigned short offset, unsigned long gain1, unsigned long gain2) +{ + int result; + unsigned char buffer[8]; + TESTPTR(phid) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)) + return EPHIDGET_NOTATTACHED; + + ZEROMEM(buffer, sizeof(buffer)); + + buffer[0] = 0x73; + + buffer[1] = 0 + Index; + buffer[2] = (unsigned char)(offset >> 8); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 1 + Index; + buffer[2] = (unsigned char)(offset & 0xff); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 2 + Index; + buffer[2] = (unsigned char)(gain1 >> 16); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 3 + Index; + buffer[2] = (unsigned char)(gain1 >> 8); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 4 + Index; + buffer[2] = (unsigned char)(gain1); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 5 + Index; + buffer[2] = (unsigned char)(gain2 >> 16); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 6 + Index; + buffer[2] = (unsigned char)(gain2 >> 8); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 7 + Index; + buffer[2] = (unsigned char)(gain2); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + return EPHIDGET_OK; +} + +int CCONV CPhidget_calibrate_gainoffset(CPhidgetHandle phid, int Index, unsigned short offset, unsigned long gain) +{ + //BL:Not sure what this does + int result; + unsigned char buffer[8]; + TESTPTR(phid) + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)) + return EPHIDGET_NOTATTACHED; + + ZEROMEM(buffer, sizeof(buffer)); + + buffer[0] = 0x72; + + //BL: Index can overflow byte + buffer[1] = 0 + Index; + buffer[2] = (unsigned char)(offset >> 8); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 1 + Index; + buffer[2] = (unsigned char)(offset & 0xff); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 2 + Index; + buffer[2] = (unsigned char)(gain >> 16); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 3 + Index; + buffer[2] = (unsigned char)(gain >> 8); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + buffer[1] = 4 + Index; + buffer[2] = (unsigned char)(gain); + if ((result = CUSBSendPacket((CPhidgetHandle)phid, buffer)) != EPHIDGET_OK) + return result; + + return EPHIDGET_OK; +} + +//0 ms = infinite timeout - note that it only checks the attach variable every 10ms +int CCONV CPhidget_waitForAttachment(CPhidgetHandle phid, int milliseconds) +{ + //BL: Should this use constants for infinite timeout rather than just 0? + long duration = 0; + TIME start; + +#ifdef _WINDOWS + TIME now; + FILETIME nowft, oldft, resft; + ULARGE_INTEGER nowul, oldul, resul; +#else + struct timeval now; +#endif + + TESTPTR(phid) + if(milliseconds) + { +#ifdef _WINDOWS + GetSystemTime(&start); +#else + gettimeofday(&start,NULL); +#endif + } + + while(!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)) + { + if(!CPhidget_statusFlagIsSet(phid->status, PHIDGET_OPENED_FLAG)) + return EPHIDGET_CLOSED; + if(milliseconds) + { +#ifdef _WINDOWS + GetSystemTime(&now); + SystemTimeToFileTime(&now, &nowft); + SystemTimeToFileTime(&start, &oldft); + + nowul.HighPart = nowft.dwHighDateTime; + nowul.LowPart = nowft.dwLowDateTime; + oldul.HighPart = oldft.dwHighDateTime; + oldul.LowPart = oldft.dwLowDateTime; + + resul.HighPart = nowul.HighPart - oldul.HighPart; + resul.LowPart = nowul.LowPart - oldul.LowPart; + + resft.dwHighDateTime = resul.HighPart; + resft.dwLowDateTime = resul.LowPart; + + duration = (resft.dwLowDateTime/10000); +#else + gettimeofday(&now, NULL); + duration = (now.tv_sec - start.tv_sec)*1000 + ((now.tv_usec-start.tv_usec)/1000); +#endif + if(duration > milliseconds) + return EPHIDGET_TIMEOUT; + } + SLEEP(10); + } + return EPHIDGET_OK; +} + +void throw_error_event(CPhidgetHandle phid, const char *error, int errcode) +{ + //BL:didn't add checking here since don't want to mess with error propagation. Should? + if(phid && phid->fptrError) + { + phid->fptrError(phid, phid->fptrErrorptr, errcode, error); + return; + } + LOG(PHIDGET_LOG_WARNING,"Got an async error: %d: %s\n\tTip: Set up an error handler to catch this properly.", errcode, error); + //abort(); +} + +int findActiveDevice(CPhidgetHandle attachedDevice) +{ + int result; + CPhidgetList *activeDevice; + CPhidgetList *matches = 0; + + CThread_mutex_lock(&activeDevicesLock); + + /* First search for an active device that matches this specific attached device + * (specific serial number of specific label + */ + for (activeDevice=ActiveDevices; activeDevice; activeDevice = activeDevice->next) + { + /* Don't bother with devices that are already associated with an attached Phidget */ + if(!CPhidget_statusFlagIsSet(activeDevice->phid->status, PHIDGET_ATTACHED_FLAG)) + { + /* Check device ID (Phidget class) */ + if(activeDevice->phid->deviceID == attachedDevice->deviceID) + { + /* Check for either openLabel with matching label, or openSerial with matching serial */ + if(( + activeDevice->phid->specificDevice == PHIDGETOPEN_SERIAL + && (activeDevice->phid->serialNumber == attachedDevice->serialNumber) + ) || ( + activeDevice->phid->specificDevice == PHIDGETOPEN_LABEL + && !strncmp(activeDevice->phid->label, attachedDevice->label, MAX_LABEL_STORAGE-1) + )) + { + /* add to match list */ + CList_addToList((CListHandle *)&matches, activeDevice->phid, CPhidgetHandle_areEqual); + } + } + } + } + + /* If we didn't find a specific match, then check is we have any 'openany' active devices + * that match the device type + */ + for (activeDevice=ActiveDevices; activeDevice; activeDevice = activeDevice->next) + { + /* Don't bother with devices that are already associated with an attached Phidget */ + if(!CPhidget_statusFlagIsSet(activeDevice->phid->status, PHIDGET_ATTACHED_FLAG)) + { + /* Check device ID (Phidget class) */ + if(activeDevice->phid->deviceID == attachedDevice->deviceID) + { + /* Make sure this is an openAny device */ + if(activeDevice->phid->specificDevice == PHIDGETOPEN_ANY) + { + /* add to match list */ + CList_addToList((CListHandle *)&matches, activeDevice->phid, CPhidgetHandle_areEqual); + } + } + } + } + + CThread_mutex_unlock(&activeDevicesLock); + + /* we create a list of possible matches and try to open them till we get success + * this is so we can unlock activeDevicesLock before calling attachActiveDevice. + */ + + /* Found a match, try to associate this Phidget with the actual Phidget and open it. + * This should send the attach event, set the PHIDGET_ATTACHED_FLAG flag to true, + * star the read and write threads and send out initial events + */ + result = EPHIDGET_NOTFOUND; + for (activeDevice=matches; activeDevice; activeDevice = activeDevice->next) + { + /* Prevent close from being called during attachActiveDevice */ + CThread_mutex_lock(&activeDevice->phid->openCloseLock); + if(attachActiveDevice(activeDevice->phid, attachedDevice) == EPHIDGET_OK) + result = EPHIDGET_OK; + CThread_mutex_unlock(&activeDevice->phid->openCloseLock); + + /* if successfull, stop looking. */ + if(result == EPHIDGET_OK) + break; + } + //free list + CList_emptyList((CListHandle *)&matches, PFALSE, NULL); + + return result; +} + +int findActiveDevices() +{ + CPhidgetList *attachedDevice; + int result = 0; + + CThread_mutex_lock(&attachedDevicesLock); + + for (attachedDevice=AttachedDevices; attachedDevice; attachedDevice = attachedDevice->next) + { + result = findActiveDevice(attachedDevice->phid); + } + + CThread_mutex_unlock(&attachedDevicesLock); + + return result; +} + +//active device is in the list of devices waiting for connections, attachedDevice is the device that was just plugged in +int attachActiveDevice(CPhidgetHandle activeDevice, CPhidgetHandle attachedDevice) +{ + int result = 0; + TESTPTRS(activeDevice, attachedDevice) + + if(!CPhidget_statusFlagIsSet(activeDevice->status, PHIDGET_OPENED_FLAG)) + return EPHIDGET_UNEXPECTED; + +#ifdef _WINDOWS +#ifndef WINCE + //open uses this so it doesn't have to enumerate all devices - but ONLY ON WINDOWS! + activeDevice->CPhidgetFHandle = malloc(wcslen(attachedDevice->CPhidgetFHandle)*sizeof(WCHAR)+10); + wcsncpy((WCHAR *)activeDevice->CPhidgetFHandle, attachedDevice->CPhidgetFHandle, wcslen(attachedDevice->CPhidgetFHandle)+1); + activeDevice->deviceIDSpec = attachedDevice->deviceIDSpec; + activeDevice->deviceDef = attachedDevice->deviceDef; +#endif +#endif + +#ifdef _ANDROID + //Android also uses the file handle to open the device + activeDevice->CPhidgetFHandle = strdup(attachedDevice->CPhidgetFHandle); + activeDevice->deviceIDSpec = attachedDevice->deviceIDSpec; + activeDevice->deviceDef = attachedDevice->deviceDef; +#endif + + if(activeDevice->specificDevice == PHIDGETOPEN_ANY) + { + activeDevice->specificDevice = PHIDGETOPEN_ANY_ATTACHED; + } + activeDevice->serialNumber = attachedDevice->serialNumber; + + if ((result = CUSBOpenHandle(activeDevice)) != + EPHIDGET_OK) { + LOG(PHIDGET_LOG_WARNING,"unable to open active device: %d", result); + if(activeDevice->specificDevice == PHIDGETOPEN_ANY_ATTACHED) + { + activeDevice->specificDevice = PHIDGETOPEN_ANY; + activeDevice->serialNumber = -1; + } + activeDevice->deviceIDSpec = 0; + return result; + } + + CThread_mutex_lock(&activeDevice->lock); + CPhidget_setStatusFlag(&activeDevice->status, PHIDGET_ATTACHING_FLAG, NULL); + if((result = activeDevice->fptrInit((CPhidgetHandle)activeDevice))) + { + CPhidget_clearStatusFlag(&activeDevice->status, PHIDGET_ATTACHING_FLAG, NULL); + CThread_mutex_unlock(&activeDevice->lock); + if(activeDevice->specificDevice == PHIDGETOPEN_ANY_ATTACHED) + { + activeDevice->specificDevice = PHIDGETOPEN_ANY; + activeDevice->serialNumber = -1; + } + LOG(PHIDGET_LOG_ERROR, "Device Initialization functions failed: %d", result); + if(result == EPHIDGET_BADVERSION) + { + if (activeDevice->fptrError) + activeDevice->fptrError((CPhidgetHandle)activeDevice, activeDevice->fptrErrorptr, EEPHIDGET_BADVERSION, "This Phidget requires a newer library - please upgrade."); + } + CUSBCloseHandle(activeDevice); + return result; + } + + //make sure the write events are in a good state + activeDevice->writeStopFlag = FALSE; + CThread_reset_event(&activeDevice->writtenEvent); + CThread_reset_event(&activeDevice->writeAvailableEvent); + + //set phidget as attached + CPhidget_clearStatusFlag(&activeDevice->status, PHIDGET_ATTACHING_FLAG, NULL); + CPhidget_setStatusFlag(&activeDevice->status, PHIDGET_ATTACHED_FLAG, NULL); + + //Start write thread + if (CThread_create(&activeDevice->writeThread, + WriteThreadFunction, activeDevice)) { + LOG(PHIDGET_LOG_WARNING,"unable to create write thread"); + CPhidget_clearStatusFlag(&activeDevice->status, PHIDGET_ATTACHED_FLAG, NULL); + CThread_mutex_unlock(&activeDevice->lock); + CUSBCloseHandle(activeDevice); + if(activeDevice->specificDevice == PHIDGETOPEN_ANY_ATTACHED) + { + activeDevice->specificDevice = PHIDGETOPEN_ANY; + activeDevice->serialNumber = -1; + } + return EPHIDGET_UNEXPECTED; + } + activeDevice->writeThread.thread_status = TRUE; + + CThread_mutex_unlock(&activeDevice->lock); + + //Attach event + if (activeDevice->fptrAttach) + { + //printf("Throwing read thread\n"); + activeDevice->fptrAttach(activeDevice, + activeDevice->fptrAttachptr); + } + + activeDevice->fptrEvents((CPhidgetHandle)activeDevice); + + //Start read thread + //We start this after the attach event returns so that we can guarantee no data events + CThread_mutex_lock(&activeDevice->lock); + if (CThread_create(&activeDevice->readThread, + ReadThreadFunction, activeDevice)) { + LOG(PHIDGET_LOG_WARNING,"unable to create read thread"); + CPhidget_clearStatusFlag(&activeDevice->status, PHIDGET_ATTACHED_FLAG, NULL); + CThread_mutex_unlock(&activeDevice->lock); + activeDevice->writeStopFlag = PTRUE; + CThread_join(&activeDevice->writeThread); //join before closing because we want to wait for outstanding writes to complete + CUSBCloseHandle(activeDevice); + if(activeDevice->specificDevice == PHIDGETOPEN_ANY_ATTACHED) + { + activeDevice->specificDevice = PHIDGETOPEN_ANY; + activeDevice->serialNumber = -1; + } + return EPHIDGET_UNEXPECTED; + } + activeDevice->readThread.thread_status = TRUE; + CThread_mutex_unlock(&activeDevice->lock); + + return EPHIDGET_OK; +} + +double round_double(double x, int decimals) +{ + return (double)((double)round(x * (double)(pow(10, decimals))) / (double)(pow(10, decimals))); +} + +//Verifies that the UTF-8 label will fit in 20 UTF-16 bytes +//Also gives you a handle to the output label - since we had to make it anyways... +int encodeLabelString(const char *buffer, char *out, int *outLen) +{ + int len; + char buffer2[(MAX_LABEL_SIZE * 2)]; + +#ifdef USE_INTERNAL_UNICONV + unsigned char *utf8label = (unsigned char *)buffer; + unsigned char *utf8labelEnd = utf8label + strlen(buffer); + unichar *utf16label = (unichar *)buffer2; + unichar *utf16labelEnd = utf16label + (MAX_LABEL_SIZE); + ConversionResult resp; + + resp = NSConvertUTF8toUTF16(&utf8label, utf8labelEnd, &utf16label, utf16labelEnd); + + if(resp != ok) + { + switch(resp) + { + case sourceExhausted: + LOG (PHIDGET_LOG_WARNING, "source exhausted error."); + break; + case targetExhausted: + LOG (PHIDGET_LOG_WARNING, "target exhausted error."); + break; + default: + LOG (PHIDGET_LOG_WARNING, "unexpected error."); + return EPHIDGET_UNEXPECTED; + } + return EPHIDGET_INVALIDARG; + } + len = (size_t)utf16label - (size_t)buffer2; + +#else + + #ifndef _WINDOWS + //Mac and Linux compatible UTF-8 to UTF-16LE conversion + char *utf8label = (char *)buffer; + char *utf16label = buffer2; + size_t inBytes = strlen(buffer); // Up to MAX_LABEL_STORAGE bytes read + size_t outBytes = (MAX_LABEL_SIZE * 2); //UTF-16 characters are two bytes each. + iconv_t conv; + size_t resp; + + conv= iconv_open("UTF-16LE", "UTF-8"); + if (conv == (iconv_t)(-1)) + return EPHIDGET_UNEXPECTED; + + resp = iconv(conv, &utf8label, &inBytes, (char **)&utf16label, &outBytes); + + iconv_close(conv); + + if (resp == (size_t) -1) { + switch (errno) { + case EILSEQ: + case EINVAL: + LOG (PHIDGET_LOG_WARNING, "Malformed UTF8 string used for label."); + break; + case E2BIG: + LOG (PHIDGET_LOG_WARNING, "Label string is too long."); + break; + default: + LOG (PHIDGET_LOG_ERROR, "Unexpected error in parsing label string: %s.", strerror (errno)); + return EPHIDGET_UNEXPECTED; + } + return EPHIDGET_INVALIDARG; + } + //length of descriptor = string length in bytes plus header (buffer[0] and buffer[1]) + len = ((MAX_LABEL_SIZE * 2)-outBytes); + #else + //Windows compatible UTF-8 to UTF-16LE conversion + int ret = EPHIDGET_OK; + int charsWritten=0; + wchar_t *outLabel = (wchar_t *)buffer2; + //don't try to convert a nothing string + if(strlen(buffer) > 0) + { + charsWritten = MultiByteToWideChar(CP_UTF8, 0, buffer, strlen(buffer), outLabel, MAX_LABEL_SIZE); + + //Error + if(!charsWritten) + { + switch (GetLastError()) + { + case ERROR_INSUFFICIENT_BUFFER: + LOG(PHIDGET_LOG_WARNING, "Label string is too long."); + break; + case ERROR_NO_UNICODE_TRANSLATION: + LOG (PHIDGET_LOG_WARNING, "Malformed UTF8 string used for label."); + break; + case ERROR_INVALID_PARAMETER: + default: + LOG (PHIDGET_LOG_ERROR, "Unexpected error in parsing label string."); + return EPHIDGET_UNEXPECTED; + } + return EPHIDGET_INVALIDARG; + } + } + + len = charsWritten*2; + #endif + +#endif + + if(out && outLen) + { + if(len <= *outLen) + *outLen = len; + memcpy(out, buffer2, *outLen); + } + return EPHIDGET_OK; +} + +//detect if this label descriptor exhibits the wraparound error +//ie bytes 16-21 will match bytes 0-5 of the serial number string descriptor +int labelHasWrapError(int serialNumber, char *labelBuf) +{ + char errorBytes[6]; + char serialString[8]; + int serialLen; + int compareSize; + int i; + + //only applies when the label descriptor is > 16 bytes + if(labelBuf[0] <= 16) + return PFALSE; + + //only applies when the first 7 digits are ascii (set by old label functions) + for(i=3; i<16; i+=2) + if(labelBuf[i] != 0x00) + return PFALSE; + + ZEROMEM(errorBytes, 6); + + //construct the 1st 6 bytes of the serial number descriptor + snprintf(serialString, 8, "%d", serialNumber); + serialLen = strlen(serialString); + + errorBytes[0] = serialLen * 2 + 2; //length + errorBytes[1] = 0x03; //type + + //serial number 1st digit + if(serialLen>=1) + errorBytes[2] = serialString[0]; + else + errorBytes[2] = 0x00; + errorBytes[3] = 0x00; + + //serial number 2nd digit + if(serialLen>=2) + errorBytes[4] = serialString[1]; + else + errorBytes[4] = 0x00; + errorBytes[5] = 0x00; + + //compare the end of the label buffer with the string descriptor + compareSize = labelBuf[0] - 16; + if(!strncmp(&labelBuf[16], errorBytes, compareSize)) + return PTRUE; + + return PFALSE; +} + +//takes the label string buffer from the USB device and outputs a UTF-8 version +int decodeLabelString(char *labelBuf, char *out, int serialNumber) +{ + //out NEEDS to be zeroed out, or we'll end up with a UTF-8 string with no terminating NULL + ZEROMEM(out, MAX_LABEL_STORAGE); + + //this returns true only if our descriptor is > 16 bytes and has the error, so we truncate + if (labelHasWrapError(serialNumber, labelBuf)) + { + int i; + for(i=16;i 4 && labelBuf[2] == (char)0xFF && labelBuf[3] == (char)0xFF) + { + LOG(PHIDGET_LOG_DEBUG, "Found a wrap-around bug style label."); + memcpy(out, &labelBuf[4], labelBuf[0]-4); + out[labelBuf[0]-4] = '\0'; + } + //otherwise it's stored as UTF-16LE + else + { +#ifdef USE_INTERNAL_UNICONV + unsigned char *utf8label = (unsigned char *)out; + unsigned char *utf8labelEnd = utf8label + MAX_LABEL_STORAGE; + unichar *utf16label = (unichar *)&labelBuf[2]; + unichar *utf16labelEnd = utf16label + ((labelBuf[0]-2) / 2); + ConversionResult resp; + resp = NSConvertUTF16toUTF8(&utf16label, utf16labelEnd, &utf8label, utf8labelEnd); + + if(resp != ok) + { + switch(resp) + { + case sourceExhausted: + LOG (PHIDGET_LOG_WARNING, "source exhausted error."); + break; + case targetExhausted: + LOG (PHIDGET_LOG_WARNING, "target exhausted error."); + break; + default: + LOG (PHIDGET_LOG_WARNING, "unexpected error."); + return EPHIDGET_UNEXPECTED; + } + return EPHIDGET_INVALIDARG; + } + +#else + + #ifndef _WINDOWS + char *utf16label = &labelBuf[2]; + char *utf8label = (char *)out; + size_t inBytes = labelBuf[0]-2; // Up to MAX_LABEL_STORAGE bytes read + size_t outBytes = (MAX_LABEL_STORAGE); //UTF-16 characters are two bytes each. + iconv_t conv; + size_t resp; + conv= iconv_open("UTF-8", "UTF-16LE"); + if (conv == (iconv_t)(-1)) + return EPHIDGET_UNEXPECTED; + + resp = iconv(conv, &utf16label, &inBytes, &utf8label, &outBytes); + + iconv_close(conv); + + if (resp == (size_t) -1) { + switch (errno) { + case EILSEQ: + case EINVAL: + case E2BIG: + default: + LOG (PHIDGET_LOG_ERROR, "Unexpected error converting label to UTF-8: %s.", strerror (errno)); + return EPHIDGET_UNEXPECTED; + } + } + #else + //labelData in NULL terminated + int bytesWritten = WideCharToMultiByte(CP_UTF8, 0, (wchar_t *)&labelBuf[2], -1, out, MAX_LABEL_STORAGE+1, NULL, NULL); + + //Error + if(!bytesWritten) + { + LOG(PHIDGET_LOG_ERROR, "Unable to convert label to UTF-8!"); + return EPHIDGET_UNEXPECTED; + } + #endif +#endif + } + + return EPHIDGET_OK; +} -- cgit v1.2.3