1264 lines
38 KiB
C
1264 lines
38 KiB
C
|
|
/* General USB HID I/O support */
|
|
|
|
/*
|
|
* Argyll Color Management 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,
|
|
* and reccommend a new API for 10.5 and latter, so we should
|
|
* switch to using this if compiled on 10.6 or latter.
|
|
* See IOHIDDeviceGetReportWithCallback & IOHIDDeviceSetReportWithCallback
|
|
* <https://developer.apple.com/library/mac/technotes/tn2187/_index.html>
|
|
* for info on the replacements.
|
|
*/
|
|
|
|
/*
|
|
New 10.5 and later code is not completely developed/debugged
|
|
- the read and write routines simply error out.
|
|
|
|
~~ It turns out that doing CFRetain() on the CFRunLoopRef and CFRunLoopSourceRef
|
|
is important (see usbio_ox.c). Make sure that is correct.
|
|
|
|
Perhaps we should try using
|
|
|
|
IOHIDDeviceRegisterInputReportCallback()
|
|
+ Handle_IOHIDDeviceIOHIDReportCallback()
|
|
+ what triggers it ?? How is this related to GetReport ?
|
|
|
|
+ OHIDDeviceGetReportWithCallback()
|
|
+ Handle_IOHIDDeviceGetReportCallback()
|
|
|
|
+ IOHIDDeviceSetReportWithCallback()
|
|
+ Handle_IOHIDDeviceSetReportCallback()
|
|
|
|
Plus IOHIDQueueScheduleWithRunLoop()
|
|
IOHIDQueueUnscheduleFromRunLoop()
|
|
|
|
+ we probably have to call the run loop ?
|
|
|
|
*/
|
|
#undef USE_NEW_OSX_CODE /* Not fully implemented */
|
|
|
|
/* These routines supliment 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#if defined(UNIX)
|
|
#include <termios.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#endif
|
|
#ifndef SALONEINSTLIB
|
|
#include "copyright.h"
|
|
#include "aconfig.h"
|
|
#else
|
|
#include "sa_config.h"
|
|
#endif
|
|
#include "numsup.h"
|
|
#include "cgats.h"
|
|
#include "xspect.h"
|
|
#include "insttypes.h"
|
|
#include "conv.h"
|
|
#include "icoms.h"
|
|
|
|
#if defined(NT)
|
|
#include <setupapi.h>
|
|
#endif
|
|
|
|
#if defined(UNIX_X11)
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
|
|
#include <sys/types.h>
|
|
#include <usbhid.h>
|
|
#else /* assume Linux */
|
|
# include <asm/types.h>
|
|
# include <linux/hiddev.h>
|
|
#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;
|
|
BOOL (WINAPI *pHidD_GetSerialNumberString)(IN HANDLE HidDeviceObject, OUT PVOID Buffer, IN ULONG BufferLength) = 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");
|
|
pHidD_GetSerialNumberString = (BOOL (WINAPI*)(HANDLE, PVOID, ULONG))
|
|
GetProcAddress(LoadLibrary("HID"), "HidD_GetSerialNumberString");
|
|
|
|
if (pHidD_GetHidGuid == NULL
|
|
|| pHidD_GetAttributes == NULL
|
|
|| pHidD_GetSerialNumberString == 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++) {
|
|
devType 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 = calloc(1, strlen(pdidd->DevicePath)+2)) == NULL) {
|
|
a1loge(p->log, ICOM_SYS, "hid_get_paths() calloc failed!\n");
|
|
return ICOM_SYS;
|
|
}
|
|
/* Windows 10 seems to return paths without the leading '\\' */
|
|
/* (Or are the structure layouts not matching ?) */
|
|
if (pdidd->DevicePath[0] == '\\' &&
|
|
pdidd->DevicePath[1] != '\\')
|
|
strcpy(hidd->dpath, "\\");
|
|
strcat(hidd->dpath, pdidd->DevicePath);
|
|
|
|
/* 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 UNIX_APPLE
|
|
# if defined(USE_NEW_OSX_CODE) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
|
{
|
|
CFAllocatorContext alocctx;
|
|
IOHIDManagerRef mref;
|
|
CFSetRef setref;
|
|
CFIndex count, i;
|
|
IOHIDDeviceRef *values;
|
|
|
|
/* Create HID Manager reference */
|
|
if ((mref = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone)) == NULL) {
|
|
a1loge(p->log, ICOM_SYS, "hid_get_paths() IOHIDManagerCreate returned NULL\n");
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
/* Set to match all HID devices */
|
|
IOHIDManagerSetDeviceMatching(mref, NULL);
|
|
|
|
/* Enumerate the devices (doesn't seem to have to open) */
|
|
if ((setref = IOHIDManagerCopyDevices(mref)) == NULL) {
|
|
a1loge(p->log, ICOM_SYS, "hid_get_paths() IOHIDManagerCopyDevices failed\n");
|
|
CFRelease(mref);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
count = CFSetGetCount(setref);
|
|
if ((values = (IOHIDDeviceRef *)calloc(sizeof(IOHIDDeviceRef), count)) == NULL) {
|
|
a1loge(p->log, ICOM_SYS, "hid_get_paths() calloc failed!\n");
|
|
CFRelease(setref);
|
|
CFRelease(mref);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
CFSetGetValues(setref, (const void **)values);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
CFNumberRef vref, pref; /* HID Vendor and Product ID propeties */
|
|
CFNumberRef lidpref; /* Location ID properties */
|
|
unsigned int vid = 0, pid = 0, lid = 0;
|
|
devType itype;
|
|
IOHIDDeviceRef ioob = values[i]; /* HID object found */
|
|
|
|
if ((vref = IOHIDDeviceGetProperty(ioob, CFSTR(kIOHIDVendorIDKey))) != NULL) {
|
|
CFNumberGetValue(vref, kCFNumberSInt32Type, &vid);
|
|
CFRelease(vref);
|
|
}
|
|
if ((pref = IOHIDDeviceGetProperty(ioob, CFSTR(kIOHIDProductIDKey))) != NULL) {
|
|
CFNumberGetValue(pref, kCFNumberSInt32Type, &pid);
|
|
CFRelease(vref);
|
|
}
|
|
if ((lidpref = IOHIDDeviceGetProperty(ioob, CFSTR("LocationID"))) != NULL) {
|
|
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;
|
|
CFRetain(ioob); /* We're retaining it */
|
|
hidd->ioob = ioob;
|
|
|
|
/* Add the path to the list */
|
|
p->add_hid(p, pname, vid, pid, 0, hidd, itype);
|
|
}
|
|
}
|
|
/* Clean up */
|
|
free(values);
|
|
CFRelease(setref);
|
|
CFRelease(mref);
|
|
}
|
|
#else /* < 1060 */
|
|
{
|
|
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;
|
|
devType 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);
|
|
} else {
|
|
a1logd(p->log, 4, "skipping HID device VID 0x%x PID 0x%x lid 0x%x that we don't want\n",vid, pid, lid);
|
|
}
|
|
if (ioob != 0) /* If we haven't kept it */
|
|
IOObjectRelease(ioob); /* Release found object */
|
|
}
|
|
IOObjectRelease(mit); /* Release the itterator */
|
|
}
|
|
#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
|
#endif /* UNIX_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->ndpaths[dtix_combined]);
|
|
|
|
return ICOM_OK;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#ifdef UNIX_APPLE
|
|
|
|
/* HID Interrupt callback for OS X */
|
|
/* This seems to only get called when the run loop is active. */
|
|
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;
|
|
if (p->hidd->rlr != NULL)
|
|
CFRunLoopStop(p->hidd->rlr); /* We're done */
|
|
else
|
|
a1logd(p->log, 8, "HID callback has no run loop\n");
|
|
}
|
|
|
|
#endif /* UNIX_APPLE */
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
|
|
/* 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) {
|
|
#if defined(NT)
|
|
a1logd(p->log, 8, "hid_open_port: about to open HID port '%s' path '%s'\n",p->name,p->hidd->dpath);
|
|
#else
|
|
a1logd(p->log, 8, "hid_open_port: about to open HID port '%s'\n",p->name);
|
|
#endif
|
|
|
|
p->uflags = hidflags;
|
|
|
|
#if defined(NT)
|
|
{
|
|
int tries = 0;
|
|
int last_err = 0;
|
|
icmUTF16 buf[IUSB_DESC_TYPE_STRING_MAX_SIZE/2];
|
|
|
|
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;
|
|
} else
|
|
last_err = GetLastError();
|
|
|
|
if (tries > 0 && pnames != NULL) {
|
|
/* Open failed. This could be the i1ProfileTray.exe */
|
|
kill_nprocess(pnames, p->log); /* Try and kill it once */
|
|
msec_sleep(100);
|
|
}
|
|
}
|
|
if (p->hidd->fh == INVALID_HANDLE_VALUE) {
|
|
a1loge(p->log, ICOM_SYS, "hid_open_port: Failed to open path "
|
|
"'%s' with err %d\n",p->hidd->dpath, last_err);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
/* If possible, read the device serial no */
|
|
if (!pHidD_GetSerialNumberString(p->hidd->fh, buf, IUSB_DESC_TYPE_STRING_MAX_SIZE)) {
|
|
a1logd(p->log, 1, "hid_open_port failed to read device serial no '%s'\n",p->hidd->dpath);
|
|
} else {
|
|
size_t s8len;
|
|
|
|
/* Convert to 16 bit native endian and add nul terminator */
|
|
s8len =icmUTF16toUTF8(NULL, NULL, buf); /* Compute UTF8 size */
|
|
|
|
if ((p->hidd->SerialNumber = (char *) calloc(s8len, 1)) == NULL) {
|
|
a1loge(p->log, ICOM_SYS, "hid_open_port calloc failed!\n");
|
|
hid_close_port(p);
|
|
return ICOM_SYS;
|
|
}
|
|
icmUTF16toUTF8(NULL, p->hidd->SerialNumber, buf); /* Convert to UTF8 */
|
|
a1logd(p->log, 6, "hid_open_port got USB serial no '%s'\n",p->hidd->SerialNumber);
|
|
}
|
|
}
|
|
#endif /* NT */
|
|
|
|
#ifdef UNIX_APPLE
|
|
# if defined(USE_NEW_OSX_CODE) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
|
{
|
|
/* Open the device */
|
|
if (IOHIDDeviceOpen(p->hidd->ioob, kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) {
|
|
a1loge(p->log, ICOM_SYS, "hid_open_port: Opening HID device '%s' failed",p->name);
|
|
return ICOM_SYS;
|
|
}
|
|
/* Try and get the USB serial number */
|
|
{
|
|
CFStringRef sref; /* Serial number propety */
|
|
char serno[IUSB_DESC_TYPE_STRING_MAX_SIZE/2];
|
|
|
|
if ((sref = IORegistryEntryCreateCFProperty(p->hidd->ioob, CFSTR(kIOHIDSerialNumberKey),
|
|
kCFAllocatorDefault,kNilOptions)) != 0) {
|
|
/* Convert from CF string to C string */
|
|
if (CFStringGetCString(sref, serno, IUSB_DESC_TYPE_STRING_MAX_SIZE/2,
|
|
kCFStringEncodingUTF8)) {
|
|
if ((p->hidd->SerialNumber = strdup(serno)) == NULL) {
|
|
a1loge(p->log, 0, "hid_open_port: open '%s' malloc failed\n",p->name);
|
|
hid_close_port(p);
|
|
return ICOM_SYS;
|
|
}
|
|
} else {
|
|
a1logd(p->log, 6, "hid_open_port: CFStringGetCString failed\n");
|
|
}
|
|
CFRelease(sref);
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
#else /* < 1060 */
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* Setup for read callback */
|
|
p->hidd->result = -1;
|
|
p->hidd->bread = 0;
|
|
|
|
/* And set read callback routine */
|
|
if ((*(p->hidd->device))->setInterruptReportHandlerCallback(p->hidd->device,
|
|
p->hidd->rbuf, 256, hid_read_callback, (void *)p, NULL) != kIOReturnSuccess) {
|
|
a1loge(p->log, ICOM_SYS, "hid_open_port: Setting callback handler for "
|
|
"HID '%s' failed\n", p->name);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if ((*(p->hidd->device))->startAllQueues(p->hidd->device) != kIOReturnSuccess) {
|
|
a1loge(p->log, ICOM_SYS, "hid_open_port: Starting queues for "
|
|
"HID '%s' failed\n", p->name);
|
|
return ICOM_SYS;
|
|
}
|
|
}
|
|
|
|
p->is_open = 1; /* Set it open so that ->usb_control will work... */
|
|
|
|
/* Try and get the USB serial number */
|
|
{
|
|
CFStringRef sref; /* Serial number propety */
|
|
char serno[IUSB_DESC_TYPE_STRING_MAX_SIZE/2];
|
|
|
|
if ((sref = IORegistryEntryCreateCFProperty(p->hidd->ioob, CFSTR(kIOHIDSerialNumberKey),
|
|
kCFAllocatorDefault,kNilOptions)) != 0) {
|
|
/* Convert from CF string to C string */
|
|
if (CFStringGetCString(sref, serno, IUSB_DESC_TYPE_STRING_MAX_SIZE/2,
|
|
kCFStringEncodingUTF8)) {
|
|
if ((p->hidd->SerialNumber = strdup(serno)) == NULL) {
|
|
a1loge(p->log, 0, "hid_open_port: open '%s' malloc failed\n",p->name);
|
|
hid_close_port(p);
|
|
return ICOM_SYS;
|
|
}
|
|
} else {
|
|
a1logd(p->log, 6, "hid_open_port: CFStringGetCString failed\n");
|
|
}
|
|
CFRelease(sref);
|
|
} else {
|
|
}
|
|
}
|
|
#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
|
#endif /* UNIX_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;
|
|
}
|
|
|
|
/* 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 UNIX_APPLE
|
|
# if defined(USE_NEW_OSX_CODE) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
|
if (IOHIDDeviceClose(p->hidd->ioob, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
|
|
a1loge(p->log, ICOM_SYS, "hid_close_port: closing HID port '%s' failed\n",p->name);
|
|
}
|
|
#else /* < 1060 */
|
|
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);
|
|
}
|
|
|
|
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\n",p->name);
|
|
}
|
|
|
|
/* Example code shows a Release being needed, but recent versions of OS X return error */
|
|
/* It's not clear from the empty Apple documentation what to do here. */
|
|
//if ((*p->hidd->device)->Release(p->hidd->device) != kIOReturnSuccess) {
|
|
// a1logw(p->log, "Warning: hid_close_port, Releasing HID port '%s' failed\n",p->name);
|
|
//}
|
|
|
|
p->hidd->device = NULL;
|
|
#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
|
#endif /* UNIX_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);
|
|
}
|
|
|
|
|
|
/* ========================================================= */
|
|
|
|
/* 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 id /* If nz, then first byte of buffer is HID ID */
|
|
) {
|
|
int retrv = ICOM_OK; /* Returned error value */
|
|
int bread = 0;
|
|
|
|
a1logd(p->log, 8, "icoms_hid_read: %d bytes, tout %f\n",bsize,tout);
|
|
|
|
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;
|
|
int bsize2 = bsize;
|
|
|
|
/* Create a copy of the data received with room for one more byte */
|
|
if ((rbuf2 = malloc(bsize + 1)) == NULL) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_hid_read: malloc failed\n");
|
|
return ICOM_SYS;
|
|
}
|
|
if (id == 0) { /* Add no id ID */
|
|
rbuf2[0] = 0;
|
|
bsize2++;
|
|
}
|
|
if (ReadFile(p->hidd->fh, rbuf2, bsize2, (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 (id == 0) { /* Remove dummy HID ID */
|
|
if (bread > 0)
|
|
bread--;
|
|
memmove(rbuf,rbuf2+1,bread);
|
|
} else {
|
|
memmove(rbuf,rbuf2,bread);
|
|
}
|
|
free(rbuf2);
|
|
}
|
|
#endif /* NT */
|
|
|
|
#ifdef UNIX_APPLE
|
|
# if defined(USE_NEW_OSX_CODE) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
|
{
|
|
IOReturn result;
|
|
CFIndex lbsize = bsize;
|
|
|
|
/* Normally the _write should have set the ->rlr */
|
|
/* but this may not be so if we are doing a flush ? */
|
|
if (p->hidd->rlr == NULL) {
|
|
p->hidd->rlr = CFRunLoopGetCurrent();
|
|
p->hidd->result = -1;
|
|
p->hidd->bread = 0;
|
|
}
|
|
|
|
IOHIDDeviceScheduleWithRunLoop(p->hidd->ioob, p->hidd->rlr, kCFRunLoopDefaultMode);
|
|
|
|
#ifndef NEVER
|
|
/* This doesn't work. Don't know why */
|
|
/* Returns 0xe000404f = kIOUSBPipeStalled, Pipe has stalled, error needs to be cleared */
|
|
if ((result = IOHIDDeviceGetReportWithCallback(
|
|
p->hidd->ioob,
|
|
kIOHIDReportTypeInput,
|
|
9, /* Bulk or Interrupt transfer ??? - or wbuf[0] ?? */
|
|
(uint8_t *)rbuf,
|
|
&lbsize,
|
|
tout,
|
|
NULL, NULL)) != kIOReturnSuccess) {
|
|
printf("~1 IOHIDDeviceGetReportWithCallback returned 0x%x\n",result);
|
|
/* (We could detect other error codes and translate them to ICOM) */
|
|
if (result == kIOReturnTimeout)
|
|
retrv = ICOM_TO;
|
|
else
|
|
retrv = ICOM_USBR;
|
|
} else {
|
|
bsize = lbsize;
|
|
bread = bsize;
|
|
}
|
|
#else
|
|
/* This doesn't work. Don't know why */
|
|
/* Returns 0xe000404f = kIOUSBPipeStalled, Pipe has stalled, error needs to be cleared */
|
|
if ((result = IOHIDDeviceGetReport(
|
|
p->hidd->ioob,
|
|
kIOHIDReportTypeInput,
|
|
9, /* Bulk or Interrupt transfer ??? - or wbuf[0] ?? */
|
|
(uint8_t *)rbuf,
|
|
&lbsize)) != kIOReturnSuccess) {
|
|
printf("~1 IOHIDDeviceGet returned 0x%x\n",result);
|
|
/* (We could detect other error codes and translate them to ICOM) */
|
|
if (result == kIOReturnTimeout)
|
|
retrv = ICOM_TO;
|
|
else
|
|
retrv = ICOM_USBR;
|
|
} else {
|
|
bsize = lbsize;
|
|
bread = bsize;
|
|
}
|
|
#endif
|
|
|
|
IOHIDDeviceUnscheduleFromRunLoop(p->hidd->ioob, p->hidd->rlr, kCFRunLoopDefaultMode);
|
|
p->hidd->rlr = NULL;
|
|
}
|
|
#else /* < 1060 */
|
|
#ifndef NEVER /* This works */
|
|
{
|
|
IOReturn result;
|
|
|
|
if (bsize > HID_RBUF_SIZE) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_hid_read: Requested more bytes that HID_RBUF_SIZE\n", p->name);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
/* Normally the _write should have set the ->rlr */
|
|
/* but this may not be so if we are doing a flush ? */
|
|
if (p->hidd->rlr == NULL) {
|
|
p->hidd->rlr = CFRunLoopGetCurrent();
|
|
p->hidd->result = -1;
|
|
p->hidd->bread = 0;
|
|
}
|
|
|
|
CFRunLoopAddSource(p->hidd->rlr, p->hidd->evsrc, kCFRunLoopDefaultMode);
|
|
|
|
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, tout, false);
|
|
|
|
if (result == kCFRunLoopRunTimedOut) {
|
|
retrv = ICOM_TO;
|
|
} else if (result != kCFRunLoopRunStopped) {
|
|
retrv = ICOM_USBR;
|
|
}
|
|
CFRunLoopRemoveSource(p->hidd->rlr, p->hidd->evsrc, kCFRunLoopDefaultMode);
|
|
p->hidd->rlr = NULL; /* Callback is invalid now */
|
|
|
|
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);
|
|
p->hidd->result = -1; /* Result is now invalid */
|
|
p->hidd->bread = 0;
|
|
return ICOM_SYS;
|
|
}
|
|
bread = p->hidd->bread;
|
|
if (bread > 0) {
|
|
memcpy(rbuf, p->hidd->rbuf, bread > HID_RBUF_SIZE ? HID_RBUF_SIZE : 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 /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
|
#endif /* UNIX_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 id /* If nz, then first byte of buffer is HID ID */
|
|
) {
|
|
int retrv = ICOM_OK; /* Returned error value */
|
|
int bwritten = 0;
|
|
|
|
a1logd(p->log, 8, "icoms_hid_write: %d bytes, tout %f\n",bsize,tout);
|
|
|
|
if (!p->is_open) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_hid_write: device not initialised\n");
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
#if defined(NT)
|
|
/* Could use HidD_SetOutputReport() ? - but this doesn't support a timeout. */
|
|
{
|
|
unsigned char *wbuf2;
|
|
int bsize2 = bsize;
|
|
|
|
/* Create a copy of the data to send with possible extra byte */
|
|
if ((wbuf2 = malloc(bsize + 1)) == NULL) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_hid_write: malloc failed\n");
|
|
return ICOM_SYS;
|
|
}
|
|
if (id == 0) {
|
|
memmove(wbuf2+1,wbuf,bsize);
|
|
wbuf2[0] = 0; /* No id ID */
|
|
bsize2++;
|
|
} else {
|
|
memmove(wbuf2,wbuf,bsize);
|
|
}
|
|
if (WriteFile(p->hidd->fh, wbuf2, bsize2, (LPDWORD)&bwritten, &p->hidd->ols) == 0) {
|
|
if (GetLastError() != ERROR_IO_PENDING) {
|
|
a1logd(p->log, 8, "icoms_hid_write error: '%s'\n",GetLastErrorMessage());
|
|
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 (id == 0 && bwritten > 0) {
|
|
bwritten--;
|
|
}
|
|
|
|
free(wbuf2);
|
|
}
|
|
#endif /* NT */
|
|
|
|
#ifdef UNIX_APPLE
|
|
# if defined(USE_NEW_OSX_CODE) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
|
{
|
|
IOReturn result;
|
|
#ifdef NEVER
|
|
/* This doesn't work. Don't know why */
|
|
/* Returns 0xe000404f = kIOUSBPipeStalled, Pipe has stalled, error needs to be cleared */
|
|
/* if we did a read that has timed out */
|
|
/* Returns 0xe00002c7 = kIOReturnUnsupported, without the failed read
|
|
if ((result = IOHIDDeviceSetReportWithCallback(
|
|
p->hidd->ioob,
|
|
kIOHIDReportTypeOutput,
|
|
9, /* Bulk or Interrupt transfer ??? - or wbuf[0] ?? */
|
|
(uint8_t *)wbuf,
|
|
bsize,
|
|
tout,
|
|
NULL, NULL)) != kIOReturnSuccess) {
|
|
printf("~1 IOHIDDeviceSetReportWithCallback returned 0x%x\n",result);
|
|
/* (We could detect other error codes and translate them to ICOM) */
|
|
if (result == kIOReturnTimeout)
|
|
retrv = ICOM_TO;
|
|
else
|
|
retrv = ICOM_USBW;
|
|
} else {
|
|
bwritten = bsize;
|
|
}
|
|
#else
|
|
/* This works, but has no timeout */
|
|
if ((result = IOHIDDeviceSetReport(
|
|
p->hidd->ioob,
|
|
kIOHIDReportTypeOutput,
|
|
9, /* Bulk or Interrupt transfer ??? - or wbuf[0] ?? */
|
|
(uint8_t *)wbuf,
|
|
bsize)) != 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
|
|
}
|
|
#else /* < 1060 */
|
|
{
|
|
/* Clear any existing read message */
|
|
/* (We're assuming it's all write then read measages !!) */
|
|
p->hidd->rlr = CFRunLoopGetCurrent(); // Our thread's run loop
|
|
p->hidd->result = -1;
|
|
p->hidd->bread = 0;
|
|
|
|
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 /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
|
#endif /* UNIX_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(UNIX_APPLE)
|
|
# if defined(USE_NEW_OSX_CODE) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
|
d->hidd->ioob = s->hidd->ioob;
|
|
CFRetain(d->hidd->ioob);
|
|
#else
|
|
d->hidd->ioob = s->hidd->ioob;
|
|
IOObjectRetain(d->hidd->ioob);
|
|
#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
|
#endif /* UNIX_APPLE */
|
|
#if defined (UNIX_X11)
|
|
#endif
|
|
if (s->hidd->SerialNumber != NULL
|
|
&& (d->hidd->SerialNumber = strdup(s->hidd->SerialNumber)) == NULL) {
|
|
a1loge(d->log, ICOM_SYS, "hid_copy_usb_idevice: malloc\n");
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
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(UNIX_APPLE)
|
|
# if defined(USE_NEW_OSX_CODE) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
|
if (hidd->ioob != 0)
|
|
CFRelease(hidd->ioob);
|
|
#else
|
|
if (hidd->ioob != 0)
|
|
IOObjectRelease(hidd->ioob);
|
|
#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
|
#endif /* UNIX_APPLE */
|
|
#if defined (UNIX_X11)
|
|
#endif
|
|
if (hidd->SerialNumber != NULL)
|
|
free(hidd->SerialNumber);
|
|
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;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------*/
|