From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- spectro/hidio.c | 866 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 866 insertions(+) create mode 100644 spectro/hidio.c (limited to 'spectro/hidio.c') diff --git a/spectro/hidio.c b/spectro/hidio.c new file mode 100644 index 0000000..595b707 --- /dev/null +++ b/spectro/hidio.c @@ -0,0 +1,866 @@ + + /* General USB HID I/O support */ + +/* + * Argyll Color Correction System + * + * Author: Graeme W. Gill + * Date: 2007/10/10 + * + * Copyright 2006 - 2013 Graeme W. Gill + * All rights reserved. + * + * (Based on usbio.c) + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + */ + +/* + * TTBD: + * As usual, Apple seem to have deprecated the routines used here. + * See IOHIDDeviceGetReportWithCallback & IOHIDDeviceSetReportWithCallback + * for info on the replacements. + */ + +/* These routines supliement the class code in ntio.c and unixio.c */ +/* with HID specific access routines for devices running on operating */ +/* systems where this is desirable. */ + +#include +#include +#include +#include +#if defined(UNIX) +#include +#include +#include +#endif +#ifndef SALONEINSTLIB +#include "copyright.h" +#include "aconfig.h" +#else +#include "sa_config.h" +#endif +#include "numsup.h" +#include "xspect.h" +#include "insttypes.h" +#include "conv.h" +#include "icoms.h" + +#if defined(NT) +#include +#endif + +#if defined(UNIX_X11) +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#include +#else /* assume Linux */ +# include +# include +#endif +#ifndef HID_MAX_USAGES /* Workaround Linux Bug ? */ +# define HID_MAX_USAGES 1024 +#endif +#endif + +#if defined(NT) + +/* Declartions to enable HID access without using the DDK */ + +typedef struct _HIDD_ATTRIBUTES { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + +void (WINAPI *pHidD_GetHidGuid)(OUT LPGUID HidGuid) = NULL; +BOOL (WINAPI *pHidD_GetAttributes)(IN HANDLE HidDeviceObject, OUT PHIDD_ATTRIBUTES Attributes) = NULL; + +/* See if we can get the wanted function calls */ +/* Return nz if OK */ +static int setup_dyn_calls() { + static int dyn_inited = 0; + + if (dyn_inited == 0) { + dyn_inited = 1; + + pHidD_GetHidGuid = (void (WINAPI*)(LPGUID)) + GetProcAddress(LoadLibrary("HID"), "HidD_GetHidGuid"); + pHidD_GetAttributes = (BOOL (WINAPI*)(HANDLE, PHIDD_ATTRIBUTES)) + GetProcAddress(LoadLibrary("HID"), "HidD_GetAttributes"); + + if (pHidD_GetHidGuid == NULL + || pHidD_GetAttributes == NULL) + dyn_inited = 0; + } + return dyn_inited; +} + +#endif + + +/* Add paths to USB connected instruments, to the existing */ +/* icompath paths in the icoms structure. */ +/* return com error */ +int hid_get_paths(icompaths *p) { + + a1logd(p->log, 8, "hid_get_paths: called\n"); + +#if defined(NT) + { + GUID HidGuid; + HDEVINFO hdinfo; + SP_DEVICE_INTERFACE_DATA did; +#define DIDD_BUFSIZE sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (sizeof(TCHAR)*MAX_PATH) + char *buf[DIDD_BUFSIZE]; + PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)buf; + SP_DEVINFO_DATA dinfod; + int i; + unsigned int VendorID = 0, ProductID = 0; + + /* Make sure we've dynamically linked */ + if (setup_dyn_calls() == 0) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() Dynamic linking to hid.dll failed\n"); + return ICOM_SYS; + } + + /* Get the device interface GUID for HIDClass devices */ + (*pHidD_GetHidGuid)(&HidGuid); + + /* Return device information for all devices of the HID class */ + hdinfo = SetupDiGetClassDevs(&HidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (hdinfo == INVALID_HANDLE_VALUE) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() SetupDiGetClassDevs failed\n"); + return ICOM_SYS; + } + + /* Get each devices interface data in turn */ + did.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)buf; + pdidd->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + dinfod.cbSize = sizeof(SP_DEVINFO_DATA); + for (i = 0; ; i++) { + instType itype; + + if (SetupDiEnumDeviceInterfaces(hdinfo, NULL, &HidGuid, i, &did) == 0) { + if (GetLastError() == ERROR_NO_MORE_ITEMS) { + break; + } + a1loge(p->log, ICOM_SYS, "hid_get_paths() SetupDiEnumDeviceInterfaces failed\n"); + return ICOM_SYS; + } + if (SetupDiGetDeviceInterfaceDetail(hdinfo, &did, pdidd, DIDD_BUFSIZE, NULL, &dinfod) + == 0) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() SetupDiGetDeviceInterfaceDetail failed\n"); + return ICOM_SYS; + } + + /* Extract the vid and pid from the device path */ + { + int gotid; + char *cp, buf[20]; + + for(gotid = 0;;) { + if ((cp = strchr(pdidd->DevicePath, 'v')) == NULL) + break; + if (strlen(cp) < 8) + break; + if (cp[1] != 'i' || cp[2] != 'd' || cp[3] != '_') + break; + memcpy(buf, cp + 4, 4); + buf[4] = '\000'; + if (sscanf(buf, "%x", &VendorID) != 1) + break; + if ((cp = strchr(pdidd->DevicePath, 'p')) == NULL) + break; + if (strlen(cp) < 8) + break; + if (cp[1] != 'i' || cp[2] != 'd' || cp[3] != '_') + break; + memcpy(buf, cp + 4, 4); + buf[4] = '\000'; + if (sscanf(buf, "%x", &ProductID) != 1) + break; + gotid = 1; + break; + } + if (!gotid) { + a1logd(p->log, 1, "found HID device '%s', inst %d but unable get PID and VID\n",pdidd->DevicePath, dinfod.DevInst); + continue; + } + } + + /* If it's a device we're looking for */ + if ((itype = inst_usb_match(VendorID, ProductID, 0)) != instUnknown) { + struct hid_idevice *hidd; + char pname[400]; + + /* Create human readable path/identification */ + sprintf(pname,"hid:/%d (%s)", dinfod.DevInst, inst_name(itype)); + + a1logd(p->log, 2, "found HID device '%s', inst %d that we're looking for\n",pdidd->DevicePath, dinfod.DevInst); + + if ((hidd = (struct hid_idevice *) calloc(sizeof(struct hid_idevice), 1)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() calloc failed!\n"); + return ICOM_SYS; + } + if ((hidd->dpath = strdup(pdidd->DevicePath)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() calloc failed!\n"); + return ICOM_SYS; + } + + /* Add the path to the list */ + p->add_hid(p, pname, VendorID, ProductID, 0, hidd, itype); + + } else { + a1logd(p->log, 6, "found HID device '%s', inst %d but not one we're looking for\n",pdidd->DevicePath, dinfod.DevInst); + } + } + + /* Now we're done with the hdifo */ + if (SetupDiDestroyDeviceInfoList(hdinfo) == 0) { + a1loge(p->log, ICOM_SYS, "SetupDiDestroyDeviceInfoList failed\n"); + return ICOM_SYS; + } + } +#endif /* NT */ + +#ifdef __APPLE__ + { + kern_return_t kstat; + CFMutableDictionaryRef sdict; /* HID Device dictionary */ + io_iterator_t mit; /* Matching itterator */ + + /* Get dictionary of HID devices */ + if ((sdict = IOServiceMatching(kIOHIDDeviceKey)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() IOServiceMatching returned a NULL dictionary\n"); + return ICOM_SYS; + } + + /* Init itterator to find matching types. Consumes sdict reference */ + if ((kstat = IOServiceGetMatchingServices(kIOMasterPortDefault, sdict, &mit)) + != KERN_SUCCESS) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() IOServiceGetMatchingServices returned %d\n", kstat); + return ICOM_SYS; + } + + /* Find all the matching HID devices */ + for (;;) { + io_object_t ioob; /* HID object found */ + CFNumberRef vref, pref; /* HID Vendor and Product ID propeties */ + CFNumberRef lidpref; /* Location ID properties */ + unsigned int vid = 0, pid = 0, lid = 0; + instType itype; + + if ((ioob = IOIteratorNext(mit)) == 0) + break; + + /* Get the two properies we need. [ Doing IORegistryEntryCreateCFProperty() is much faster */ + /* than IORegistryEntryCreateCFProperties() in some cases.] */ + if ((vref = IORegistryEntryCreateCFProperty(ioob, CFSTR(kIOHIDVendorIDKey), + kCFAllocatorDefault,kNilOptions)) != 0) { + CFNumberGetValue(vref, kCFNumberIntType, &vid); + CFRelease(vref); + } + if ((pref = IORegistryEntryCreateCFProperty(ioob, CFSTR(kIOHIDProductIDKey), + kCFAllocatorDefault,kNilOptions)) != 0) { + CFNumberGetValue(pref, kCFNumberIntType, &pid); + CFRelease(pref); + } + if ((lidpref = IORegistryEntryCreateCFProperty(ioob, CFSTR("LocationID"), + kCFAllocatorDefault,kNilOptions)) != 0) { + CFNumberGetValue(lidpref, kCFNumberIntType, &lid); + CFRelease(lidpref); + } + + /* If it's a device we're looking for */ + if ((itype = inst_usb_match(vid, pid, 0)) != instUnknown) { + struct hid_idevice *hidd; + char pname[400]; + + a1logd(p->log, 2, "found HID device '%s' lid 0x%x that we're looking for\n",inst_name(itype), lid); + + /* Create human readable path/identification */ + sprintf(pname,"hid%d: (%s)", lid >> 20, inst_name(itype)); + + if ((hidd = (struct hid_idevice *)calloc(sizeof(struct hid_idevice), 1)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_get_paths calloc failed!\n"); + return ICOM_SYS; + } + hidd->lid = lid; + hidd->ioob = ioob; + ioob = 0; /* Don't release it */ + + /* Add the path to the list */ + p->add_hid(p, pname, vid, pid, 0, hidd, itype); + } + if (ioob != 0) /* If we haven't kept it */ + IOObjectRelease(ioob); /* Release found object */ + } + IOObjectRelease(mit); /* Release the itterator */ + } +#endif /* __APPLE__ */ + +#if defined(UNIX_X11) + + /* This is how we'd go about adding HID support for Linux, IF it */ + /* was actually capable of communicating application composed reports - */ + /* which it is not, so HID seems pretty busted on Linux.. */ +#ifdef NEVER + /* We need to scan for /dev/hiddev* or /dev/usb/hiddev* device names, */ + /* and then read their vid & pid */ + { + int i; + char *devds[] = { /* Typical locations hiddev will appear under */ + "/dev", + "/dev/usb", + "" + }; + + /* For each possible device directory */ + for (i = 0;;i++) { + DIR *dir; + struct dirent *dentry; + + if (devds[i][0] == '\000') + break; + if ((dir = opendir(devds[i])) == NULL) + continue; + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "hiddev", 6) == 0) { + char dpath[PATH_MAX]; + int fd; + + strcpy(dpath, devds[i]); + strcat(dpath, "/"); + strcat(dpath, dentry->d_name); +// a1logd(p->log, 8, "found hid device '%s'\n",dpath); + /* Open it and see what VID and PID it is */ + if ((fd = open(dpath, O_RDONLY)) >= 0) { + struct hiddev_devinfo hidinfo; + if (ioctl(fd, HIDIOCGDEVINFO, &hidinfo) < 0) + a1logd(p->log, 1, "Unable to get HID info for '%s'\n",dpath); + else { +// a1logd(p->log, 8,"busnum = %d, devnum = %d, vid = 0x%x, pid = 0x%x\n", +// hidinfo.busnum, hidinfo.devnum, hidinfo.vendor, hidinfo.product); + } + close(fd); + } +// More hotplug/udev magic needed to make the device accesible ! +//else a1logd(p->log, 8,"failed to open '%s' err %d\n",dpath,fd); + } + } + closedir(dir); + } + } +#endif /* NEVER */ +#endif /* UNIX_X11 */ + + a1logd(p->log, 8, "icoms_get_paths: returning %d paths and ICOM_OK\n",p->npaths); + + return ICOM_OK; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Close an open HID port */ +/* If we don't do this, the port and/or the device may be left in an unusable state. */ +void hid_close_port(icoms *p) { + + a1logd(p->log, 8, "hid_close_port: called\n"); + + if (p->is_open && p->hidd != NULL) { + +#if defined(NT) + CloseHandle(p->hidd->ols.hEvent); + CloseHandle(p->hidd->fh); +#endif /* NT */ + +#ifdef __APPLE__ + IOObjectRelease(p->hidd->port); + p->hidd->port = 0; + + if (p->hidd->evsrc != NULL) + CFRelease(p->hidd->evsrc); + p->hidd->evsrc = NULL; + + p->hidd->rlr = NULL; + + if ((*p->hidd->device)->close(p->hidd->device) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "hid_close_port: closing HID port '%s' failed",p->name); + return; + } + + if ((*p->hidd->device)->Release(p->hidd->device) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "hid_close_port: Releasing HID port '%s' failed",p->name); + } + p->hidd->device = NULL; +#endif /* __APPLE__ */ + + p->is_open = 0; + a1logd(p->log, 8, "hid_close_port: has been released and closed\n"); + } + + /* Find it and delete it from our static cleanup list */ + usb_delete_from_cleanup_list(p); +} + + +/* Open an HID port for all our uses. */ +/* This always re-opens the port */ +/* return icom error */ +static int hid_open_port( +icoms *p, +icomuflags hidflags, /* Any special handling flags */ +int retries, /* > 0 if we should retry set_configuration (100msec) */ +char **pnames /* List of process names to try and kill before opening */ +) { + /* Make sure the port is open */ + if (!p->is_open) { + a1logd(p->log, 8, "hid_open_port: about to open HID port '%s'\n",p->name); + + p->uflags = hidflags; + +#if defined(NT) + { + int tries = 0; + + for (tries = 0; retries >= 0; retries--, tries++) { + /* Open the device */ + if ((p->hidd->fh = CreateFile(p->hidd->dpath, GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)) + != INVALID_HANDLE_VALUE) { + memset(&p->hidd->ols,0,sizeof(OVERLAPPED)); + if ((p->hidd->ols.hEvent = CreateEvent(NULL, 0, 0, NULL)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_open_port: Failed to create HID " + "Event with %d'\n",GetLastError()); + return ICOM_SYS; + } + break; + } + if (tries > 0 && pnames != NULL) { + /* Open failed. This could be the i1ProfileTray.exe */ + kill_nprocess(pnames, p->log); + msec_sleep(100); + } + } + if (p->hidd->fh == INVALID_HANDLE_VALUE) { + a1loge(p->log, ICOM_SYS, "hid_open_port: Failed to open " + "HID '%s' with %d\n",GetLastError()); + return ICOM_SYS; + } + } +#endif /* NT */ + +#ifdef __APPLE__ + { + IOCFPlugInInterface **piif = NULL; + IOReturn result; + SInt32 score; + + if ((result = IOCreatePlugInInterfaceForService(p->hidd->ioob, kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &piif, &score) != kIOReturnSuccess || piif == NULL)) { + a1loge(p->log, ICOM_SYS, "hid_open_port: Failed to get piif for " + "HID device '%s', result 0x%x, piif 0x%x\n",p->name,result,piif); + return ICOM_SYS; + } + + p->hidd->device = NULL; + if ((*piif)->QueryInterface(piif, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122), (LPVOID)&p->hidd->device) + != kIOReturnSuccess || p->hidd->device == NULL) { + a1loge(p->log, ICOM_SYS, "hid_open_port: Getting HID device '%s' failed",p->name); + return ICOM_SYS; + } + (*piif)->Release(piif); /* delete intermediate object */ + + if ((*p->hidd->device)->open(p->hidd->device, kIOHIDOptionsTypeSeizeDevice) + != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "hid_open_port: Opening HID device '%s' failed",p->name); + return ICOM_SYS; + } + + /* Setup to handle interrupt read callbacks */ + p->hidd->port = 0; + if ((*(p->hidd->device))->createAsyncPort(p->hidd->device, &p->hidd->port) + != kIOReturnSuccess || p->hidd->port == 0) { + a1loge(p->log, ICOM_SYS, "hid_open_port: Creating port on " + "HID device '%s' failed",p->name); + return ICOM_SYS; + } + + p->hidd->evsrc = NULL; + if ((*(p->hidd->device))->createAsyncEventSource(p->hidd->device, &p->hidd->evsrc) + != kIOReturnSuccess || p->hidd->evsrc == NULL) { + a1loge(p->log, ICOM_SYS, "hid_open_port: Creating event source on " + "HID device '%s' failed",p->name); + return ICOM_SYS; + } + + } +#endif /* __APPLE__ */ + + p->is_open = 1; + a1logd(p->log, 8, "hid_open_port: HID port is now open\n"); + } + + /* Install the cleanup signal handlers, and add to our cleanup list */ + usb_install_signal_handlers(p); + + return ICOM_OK; +} + +/* ========================================================= */ + +#ifdef __APPLE__ + +/* HID Interrupt callback for OS X */ +static void hid_read_callback( +void *target, +IOReturn result, +void *refcon, +void *sender, +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 +uint32_t size +#else +UInt32 size +#endif +) { + icoms *p = (icoms *)target; + + a1logd(p->log, 8, "HID callback called with size %d, result 0x%x\n",size,result); + p->hidd->result = result; + p->hidd->bread = size; + CFRunLoopStop(p->hidd->rlr); /* We're done */ +} + +#endif /* __APPLE__ */ + +/* HID Interrupt pipe Read */ +/* Don't retry on a short read, return ICOM_SHORT. */ +/* [Probably uses the control pipe. We need a different */ +/* call for reading messages from the interrupt pipe] */ +static int +icoms_hid_read(icoms *p, + unsigned char *rbuf, /* Read buffer */ + int bsize, /* Bytes to read */ + int *breadp, /* Bytes read */ + double tout /* Timeout in seconds */ +) { + int retrv = ICOM_OK; /* Returned error value */ + int bread = 0; + + if (!p->is_open) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: device not initialised\n"); + return ICOM_SYS; + } + +#if defined(NT) + { + unsigned char *rbuf2; + + /* Create a copy of the data recieved with one more byte */ + if ((rbuf2 = malloc(bsize + 1)) == NULL) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: malloc failed\n"); + return ICOM_SYS; + } + rbuf2[0] = 0; + if (ReadFile(p->hidd->fh, rbuf2, bsize+1, (LPDWORD)&bread, &p->hidd->ols) == 0) { + if (GetLastError() != ERROR_IO_PENDING) { + retrv = ICOM_USBR; + } else { + int res; + res = WaitForSingleObject(p->hidd->ols.hEvent, (int)(tout * 1000.0 + 0.5)); + if (res == WAIT_FAILED) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: HID wait on read failed\n"); + return ICOM_SYS; + } else if (res == WAIT_TIMEOUT) { + CancelIo(p->hidd->fh); + retrv = ICOM_TO; + bread = 0; + } else { + bread = p->hidd->ols.InternalHigh; + } + } + } + if (bread > 0) + bread--; + memmove(rbuf,rbuf2+1,bsize); + free(rbuf2); + } +#endif /* NT */ + +#ifdef __APPLE__ +#ifndef NEVER + { + IOReturn result; + + /* Setup for callback */ + p->hidd->result = -1; + p->hidd->bread = 0; + + if ((*(p->hidd->device))->setInterruptReportHandlerCallback(p->hidd->device, + rbuf, bsize, hid_read_callback, (void *)p, NULL) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: Setting callback handler for " + "HID '%s' failed\n", p->name); + return ICOM_SYS; + } + /* Call runloop, but exit after handling one callback */ + p->hidd->rlr = CFRunLoopGetCurrent(); + CFRunLoopAddSource(p->hidd->rlr, p->hidd->evsrc, kCFRunLoopDefaultMode); + + if ((*(p->hidd->device))->startAllQueues(p->hidd->device) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: Starting queues for " + "HID '%s' failed\n", p->name); + return ICOM_SYS; + } + + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, tout, false); + + if ((*(p->hidd->device))->stopAllQueues(p->hidd->device) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: Stopping queues for " + "HID '%s' failed\n", p->name); + return ICOM_SYS; + } + if (result == kCFRunLoopRunTimedOut) { + retrv = ICOM_TO; + } else if (result != kCFRunLoopRunStopped) { + retrv = ICOM_USBR; + } + CFRunLoopRemoveSource(p->hidd->rlr, p->hidd->evsrc, kCFRunLoopDefaultMode); + + if (p->hidd->result == -1) { /* Callback wasn't called */ + retrv = ICOM_TO; + } else if (p->hidd->result != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: Callback for " + "HID '%s' got unexpected return value\n", p->name); + return ICOM_SYS; + } + bread = p->hidd->bread; + } +#else // NEVER + /* This doesn't work. Don't know why */ + /* Returns 0xe000404f = kIOUSBPipeStalled, Pipe has stalled, error needs to be cleared */ + { + IOReturn result; + if ((result = (*p->hidd->device)->getReport( + p->hidd->device, + kIOHIDReportTypeInput, + 9, /* Bulk or Interrupt transfer */ + (void *)rbuf, + (UInt32 *)&bsize, + (unsigned int)(tout * 1000.0 + 0.5), + NULL, NULL, NULL) + ) != kIOReturnSuccess) { + /* (We could detect other error codes and translate them to ICOM) */ + if (result == kIOReturnTimeout) + retrv = ICOM_TO; + else + retrv = ICOM_USBW; + } else { + bread = bsize; + } + } +#endif // NEVER +#endif /* __APPLE__ */ + + if (breadp != NULL) + *breadp = bread; + + a1logd(p->log, 8, "icoms_hid_read: About to return hid read %d bytes, ICOM err 0x%x\n", + bread, retrv); + + return retrv; +} + +/* - - - - - - - - - - - - - */ + +/* HID Command Pipe Write */ +/* Don't retry on a short read, return ICOM_SHORT. */ +static int +icoms_hid_write(icoms *p, + unsigned char *wbuf, /* Write buffer */ + int bsize, /* Bytes to write */ + int *bwrittenp, /* Bytes written */ + double tout /* Timeout in seconds */ +) { + int retrv = ICOM_OK; /* Returned error value */ + int bwritten = 0; + + if (!p->is_open) { + a1loge(p->log, ICOM_SYS, "icoms_hid_write: device not initialised\n"); + return ICOM_SYS; + } + +#if defined(NT) + { + unsigned char *wbuf2; + + /* Create a copy of the data to send with one more byte */ + if ((wbuf2 = malloc(bsize + 1)) == NULL) { + a1loge(p->log, ICOM_SYS, "icoms_hid_write: malloc failed\n"); + return ICOM_SYS; + } + memmove(wbuf2+1,wbuf,bsize); + wbuf2[0] = 0; /* Extra report ID byte (why ?) */ + if (WriteFile(p->hidd->fh, wbuf2, bsize+1, (LPDWORD)&bwritten, &p->hidd->ols) == 0) { + if (GetLastError() != ERROR_IO_PENDING) { + retrv = ICOM_USBW; + } else { + int res; + res = WaitForSingleObject(p->hidd->ols.hEvent, (int)(tout * 1000.0 + 0.5)); + if (res == WAIT_FAILED) { + a1loge(p->log, ICOM_SYS, "icoms_hid_write: HID wait on write failed\n"); + return ICOM_SYS; + } + else if (res == WAIT_TIMEOUT) { + CancelIo(p->hidd->fh); + retrv = ICOM_TO; + bwritten = 0; + } else { + bwritten = p->hidd->ols.InternalHigh; + } + } + } + if (bwritten > 0) + bwritten--; + + free(wbuf2); + } +#endif /* NT */ + +#ifdef __APPLE__ + { + IOReturn result; + if ((result = (*p->hidd->device)->setReport( + p->hidd->device, + kIOHIDReportTypeOutput, + 9, /* Bulk or Interrupt transfer */ + (void *)wbuf, + bsize, + (unsigned int)(tout * 1000.0 + 0.5), + NULL, NULL, NULL)) != kIOReturnSuccess) { + /* (We could detect other error codes and translate them to ICOM) */ + if (result == kIOReturnTimeout) + retrv = ICOM_TO; + else + retrv = ICOM_USBW; + } else { + bwritten = bsize; + } + } +#endif /* __APPLE__ */ + + if (bwrittenp != NULL) + *bwrittenp = bwritten; + + a1logd(p->log, 8, "icoms_hid_write: wrote %d bytes, ICOM err 0x%x\n",bwritten, retrv); + + return retrv; +} + +/* ------------------------------------------------- */ +/* Set the hid port number and characteristics. */ +/* This may be called to re-establish a connection that has failed */ +/* return icom error */ +static int +icoms_set_hid_port( +icoms *p, +icomuflags hidflags, /* Any special handling flags */ +int retries, /* > 0 if we should retry set_configuration (100msec) */ +char **pnames /* List of process names to try and kill before opening */ +) { + int rv = ICOM_OK; + + a1logd(p->log, 8, "icoms_set_hid_port: About to set HID port characteristics\n"); + + if (p->is_open) + p->close_port(p); + + if (p->port_type(p) == icomt_hid) { + + if ((rv = hid_open_port(p, hidflags, retries, pnames)) != ICOM_OK) + return rv; + + p->write = NULL; + p->read = NULL; + + } + a1logd(p->log, 8, "icoms_set_hid_port: HID port characteristics set ok\n"); + + return rv; +} + +/* Copy hid_idevice contents from icompaths to icom */ +/* return icom error */ +int hid_copy_hid_idevice(icoms *d, icompath *s) { + + if (s->hidd == NULL) { + d->hidd = NULL; + return ICOM_OK; + } + + if ((d->hidd = calloc(sizeof(struct hid_idevice), 1)) == NULL) { + a1loge(d->log, ICOM_SYS, "hid_copy_hid_idevice: malloc failed\n"); + return ICOM_SYS; + } + +#if defined(NT) + if ((d->hidd->dpath = strdup(s->hidd->dpath)) == NULL) { + a1loge(d->log, ICOM_SYS, "hid_copy_hid_idevice: malloc\n"); + return ICOM_SYS; + } +#endif +#if defined(__APPLE__) + d->hidd->ioob = s->hidd->ioob; + IOObjectRetain(d->hidd->ioob); +#endif +#if defined (UNIX_X11) +#endif + return ICOM_OK; +} + +/* Cleanup and then free a hid_del_hid_idevice */ +void hid_del_hid_idevice(struct hid_idevice *hidd) { + if (hidd == NULL) + return; + +#if defined(NT) + if (hidd->dpath != NULL) + free(hidd->dpath); +#endif +#if defined(__APPLE__) + if (hidd->ioob != 0) + IOObjectRelease(hidd->ioob); +#endif +#if defined (UNIX_X11) +#endif + free(hidd); +} + +/* Cleanup any HID specific icoms info */ +void hid_del_hid(icoms *p) { + hid_del_hid_idevice(p->hidd); +} + + +/* ---------------------------------------------------------------------------------*/ + +/* Set the HID specific icoms methods */ +void hid_set_hid_methods( +icoms *p +) { + p->set_hid_port = icoms_set_hid_port; + p->hid_read = icoms_hid_read; + p->hid_write = icoms_hid_write; +} + +/* ---------------------------------------------------------------------------------*/ -- cgit v1.2.3