aboutsummaryrefslogtreecommitdiffstats
path: root/cphidget.c
diff options
context:
space:
mode:
authorJonathan McCrohan <jmccrohan@gmail.com>2012-04-14 12:56:48 +0100
committerJonathan McCrohan <jmccrohan@gmail.com>2012-04-14 12:56:48 +0100
commit0b624384cd52be20e61284551d832b499d7b7707 (patch)
tree6f95a4bbef47abc9720b96c0722e8f632aef228a /cphidget.c
downloadlibphidget21-0b624384cd52be20e61284551d832b499d7b7707.tar.gz
Imported Upstream version 2.1.8.20120216upstream/2.1.8.20120216
Diffstat (limited to 'cphidget.c')
-rw-r--r--cphidget.c1637
1 files changed, 1637 insertions, 0 deletions
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;i<PHIDGET_DEVICE_CLASS_COUNT;i++)
+ {
+ if(Phid_DeviceName[i] != NULL)
+ {
+ if(!strcmp(Phid_DeviceName[i],name))
+ return i;
+ }
+ }
+ return -1;
+}
+
+//returns ms
+double timestampdiff(CPhidget_Timestamp time1, CPhidget_Timestamp time2)
+{
+ return ((time1.seconds * 1000) + (double)(time1.microseconds / 1000.0)) - ((time2.seconds * 1000) + (double)(time2.microseconds / 1000.0));
+}
+
+double timeSince(TIME *start)
+{
+#ifdef _WINDOWS
+ TIME now;
+ FILETIME nowft, oldft, resft;
+ ULARGE_INTEGER nowul, oldul, resul;
+ double duration;
+ 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 = (double)(resft.dwLowDateTime/10000000.0);
+#else
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ double duration = (now.tv_sec - start->tv_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<labelBuf[0];i++)
+ labelBuf[i] = 0x00;
+ labelBuf[0] = 16;
+ LOG(PHIDGET_LOG_WARNING, "Detected getLabel error - label is being truncated to first 7 characters. Please setLabel again to correct this.");
+ }
+
+ //check if the label is stored as UTF-8 directly
+ if(labelBuf[0] > 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;
+}