summaryrefslogtreecommitdiff
path: root/spectro/usbio_ox.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/usbio_ox.c')
-rw-r--r--spectro/usbio_ox.c986
1 files changed, 986 insertions, 0 deletions
diff --git a/spectro/usbio_ox.c b/spectro/usbio_ox.c
new file mode 100644
index 0000000..24498fb
--- /dev/null
+++ b/spectro/usbio_ox.c
@@ -0,0 +1,986 @@
+
+/* General USB I/O support, OS X native implementation. */
+/* This file is conditionaly #included into usbio.c */
+
+/*
+ * Argyll Color Correction System
+ *
+ * Author: Graeme W. Gill
+ * Date: 2006/22/4
+ *
+ * Copyright 2006 - 2013 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
+ * see the License2.txt file for licencing details.
+ */
+
+#include <sys/time.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOTypes.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOCFPlugIn.h>
+//#include <IOKit/IOCFBundle.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+# include <objc/objc-auto.h>
+#endif
+
+/* Add paths to USB connected instruments */
+/* Return an icom error */
+int usb_get_paths(
+icompaths *p
+) {
+ kern_return_t kstat;
+ CFMutableDictionaryRef sdict; /* USB Device dictionary */
+ io_iterator_t mit; /* Matching itterator */
+ int vid, pid;
+ struct usb_idevice *usbd = NULL;
+
+ a1logd(p->log, 6, "usb_get_paths: about to look through devices:\n");
+
+ /* Get dictionary of USB devices */
+ if ((sdict = IOServiceMatching(kIOUSBDeviceClassName)) == NULL) {
+ a1loge(p->log, ICOM_SYS, "usb_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, "usb_get_paths() IOServiceGetMatchingServices returned %d\n", kstat);
+ return ICOM_SYS;
+ }
+
+ /* Find all the matching USB devices */
+ for (;;) {
+ io_object_t ioob; /* USB object found */
+ io_iterator_t it1; /* Child level 1 */
+ CFMutableDictionaryRef usbprops = 0; /* USB Device properties */
+ CFNumberRef vref, pref; /* USB Vendor and Product ID propeties */
+ CFNumberRef nconfref; /* No configurations */
+ CFNumberRef nepref, lidpref; /* No ep's, Location ID properties */
+ unsigned int vid = 0, pid = 0, nep, tnep, nconfig = 0, lid = 0;
+ instType itype;
+
+ if ((ioob = IOIteratorNext(mit)) == 0)
+ break;
+
+ /* Get the two properies we need. */
+ if ((vref = IORegistryEntryCreateCFProperty(ioob, CFSTR(kUSBVendorID),
+ kCFAllocatorDefault,kNilOptions)) != 0) {
+ CFNumberGetValue(vref, kCFNumberIntType, &vid);
+ CFRelease(vref);
+ }
+ if ((pref = IORegistryEntryCreateCFProperty(ioob, CFSTR(kUSBProductID),
+ kCFAllocatorDefault,kNilOptions)) != 0) {
+ CFNumberGetValue(pref, kCFNumberIntType, &pid);
+ CFRelease(pref);
+ }
+
+ if ((nconfref = IORegistryEntryCreateCFProperty(ioob, CFSTR("bNumConfigurations"),
+ kCFAllocatorDefault,kNilOptions)) != 0) {
+ CFNumberGetValue(nconfref, kCFNumberIntType, &nconfig);
+ CFRelease(nconfref);
+ }
+
+ if ((lidpref = IORegistryEntryCreateCFProperty(ioob, CFSTR("locationID"),
+ kCFAllocatorDefault,kNilOptions)) != 0) {
+ CFNumberGetValue(lidpref, kCFNumberIntType, &lid);
+ CFRelease(lidpref);
+ }
+
+ a1logd(p->log, 6, "usb_check_and_add: checking vid 0x%04x, pid 0x%04x, lid 0x%x\n",vid,pid,lid);
+
+ /* Do a preliminary match */
+ if ((itype = inst_usb_match(vid, pid, 0)) == instUnknown) {
+ a1logd(p->log, 6 , "usb_check_and_add: 0x%04x 0x%04x not reconized\n",vid,pid);
+ IOObjectRelease(ioob); /* Release found object */
+ continue;
+ }
+
+ /* Allocate an idevice so that we can fill in the end point information */
+ if ((usbd = (struct usb_idevice *) calloc(sizeof(struct usb_idevice), 1)) == NULL) {
+ a1loge(p->log, ICOM_SYS, "icoms: calloc failed!\n");
+ return ICOM_SYS;
+ }
+
+ usbd->nconfig = nconfig;
+ usbd->config = 1; /* We are only interested in config 1 */
+
+ /* Go through all the Interfaces, adding up the number of end points */
+ tnep = 0;
+ if ((kstat = IORegistryEntryCreateIterator(ioob, kIOServicePlane,
+ kIORegistryIterateRecursively, &it1)) != KERN_SUCCESS) {
+ IOObjectRelease(ioob);
+ IOObjectRelease(mit);
+ a1loge(p->log, kstat, "usb_check_and_add: IORegistryEntryCreateIterator() with %d\n",kstat);
+ return ICOM_SYS;
+ }
+ usbd->nifce = 0;
+ for (;;) {
+ io_object_t ch1; /* Child object 1 */
+ if ((ch1 = IOIteratorNext(it1)) == 0)
+ break;
+ if (IOObjectConformsTo(ch1, "IOUSBInterface")) {
+ unsigned int config = 0;
+
+ /* Get the configuration number */
+ if ((nconfref = IORegistryEntryCreateCFProperty(ch1, CFSTR(kUSBNumEndpoints),
+ kCFAllocatorDefault,kNilOptions)) != 0) {
+ CFNumberGetValue(nconfref, kCFNumberIntType, &config);
+ CFRelease(nconfref);
+ }
+
+ if (config != 1)
+ continue;
+
+ usbd->nifce++;
+
+ /* Get the number of end points */
+ if ((nepref = IORegistryEntryCreateCFProperty(ch1, CFSTR(kUSBNumEndpoints),
+ kCFAllocatorDefault,kNilOptions)) != 0) {
+ CFNumberGetValue(nepref, kCFNumberIntType, &nep);
+ CFRelease(nepref);
+ tnep += nep;
+ }
+ IOObjectRelease(ch1);
+ }
+ }
+ IOObjectRelease(it1);
+
+ if ((itype = inst_usb_match(vid, pid, tnep)) != instUnknown) {
+ int i;
+ char pname[400];
+ int rv;
+
+ /* If this device is HID, it will have already added to the paths list, */
+ /* so check for this and skip this device if it is already there. */
+ for (i = 0; i < p->npaths; i++) {
+ if (p->paths[i]->vid == vid
+ && p->paths[i]->pid == pid
+ && p->paths[i]->hidd != NULL
+ && p->paths[i]->hidd->lid == lid) {
+ a1logd(p->log, 1, "usb_check_and_add: Ignoring device because it is already in list as HID\n");
+ break;
+ }
+ }
+ if (i < p->npaths) {
+ IOObjectRelease(ioob); /* Release found object */
+ free(usbd);
+
+ } else {
+
+ a1logd(p->log, 1, "usb_check_and_add: found instrument vid 0x%04x, pid 0x%04x\n",vid,pid);
+ usbd->lid = lid;
+ usbd->ioob = ioob;
+
+ /* Create a path/identification */
+ sprintf(pname,"usb%d: (%s)", lid >> 20, inst_name(itype));
+
+ /* Add the path and ep info to the list */
+ if ((rv = p->add_usb(p, pname, vid, pid, tnep, usbd, itype)) != ICOM_OK) {
+ IOObjectRelease(ioob); /* Release found object */
+ free(usbd);
+ IOObjectRelease(mit); /* Release the itterator */
+ return rv;
+ }
+ }
+ } else {
+ IOObjectRelease(ioob); /* Release found object */
+ free(usbd);
+ }
+ }
+ IOObjectRelease(mit); /* Release the itterator */
+
+ a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->npaths);
+ return ICOM_OK;
+}
+
+/* Copy usb_idevice contents from icompaths to icom */
+/* return icom error */
+int usb_copy_usb_idevice(icoms *d, icompath *s) {
+ int i;
+ if (s->usbd == NULL) {
+ d->usbd = NULL;
+ return ICOM_OK;
+ }
+ if ((d->usbd = calloc(sizeof(struct usb_idevice), 1)) == NULL) {
+ a1loge(d->log, ICOM_SYS, "usb_copy_usb_idevice: malloc\n");
+ return ICOM_SYS;
+ }
+ d->nconfig = s->usbd->nconfig;
+ d->config = s->usbd->config;
+ d->nifce = s->usbd->nifce;
+ d->usbd->ioob = s->usbd->ioob;
+ IOObjectRetain(d->usbd->ioob);
+ return ICOM_OK;
+}
+
+/* Cleanup and then free a usb dev entry */
+void usb_del_usb_idevice(struct usb_idevice *usbd) {
+
+ if (usbd == NULL)
+ return;
+
+ if (usbd->ioob != 0)
+ IOObjectRelease(usbd->ioob);
+
+ free(usbd);
+}
+
+/* Cleanup any USB specific icoms state */
+void usb_del_usb(icoms *p) {
+
+ usb_del_usb_idevice(p->usbd);
+}
+
+/* Clean up anything allocated/created in p->usbd, but don't free p->usbd itself */
+static void cleanup_device(icoms *p) {
+
+ if (p->usbd != NULL) {
+ if (p->usbd->device != NULL) { /* device is open */
+ int i;
+
+ /* Stop the RunLoop and wait for it */
+ if (p->usbd->cfrunloop != NULL) {
+
+ a1logd(p->log, 6, "usb_close_port: waiting for RunLoop thread to exit\n");
+ CFRunLoopStop(p->usbd->cfrunloop);
+ CFRelease(p->usbd->cfrunloop);
+ if (p->usbd->thread != NULL)
+ pthread_join(p->usbd->thread, NULL);
+ }
+
+ /* Release all the interfaces */
+ for (i = 0; i < p->usbd->nifce; i++) {
+ if (p->usbd->interfaces[i] != NULL) {
+ (*p->usbd->interfaces[i])->USBInterfaceClose(p->usbd->interfaces[i]);
+ (*p->usbd->interfaces[i])->Release(p->usbd->interfaces[i]);
+ }
+ }
+
+ /* Close the device */
+ (*(p->usbd->device))->USBDeviceClose(p->usbd->device);
+ (*(p->usbd->device))->Release(p->usbd->device);
+ }
+ pthread_cond_destroy(&p->usbd->cond);
+ pthread_mutex_destroy(&p->usbd->lock);
+
+ memset(p->usbd, 0, sizeof(struct usb_idevice));
+ }
+}
+
+/* Close an open USB port */
+/* If we don't do this, the port and/or the device may be left in an unusable state. */
+void usb_close_port(icoms *p) {
+
+ a1logd(p->log, 6, "usb_close_port: called\n");
+
+ if (p->is_open && p->usbd != NULL) {
+
+ /* Workaround for some bugs - reset device on close */
+ if (p->uflags & icomuf_reset_before_close) {
+ IOReturn rv;
+ if ((rv = (*(p->usbd->device))->ResetDevice(p->usbd->device)) != kIOReturnSuccess) {
+ a1logd(p->log, 1, "usb_close_port: ResetDevice failed with 0x%x\n",rv);
+ }
+ }
+
+ /* Close down and free everything in p->usbd */
+ cleanup_device(p);
+ if (p->usbd != NULL) {
+ free(p->usbd);
+ p->usbd = NULL;
+ }
+ a1logd(p->log, 6, "usb_close_port: usb port has been released and closed\n");
+ }
+ p->is_open = 0;
+
+ /* Find it and delete it from our static cleanup list */
+ usb_delete_from_cleanup_list(p);
+}
+
+static int icoms_usb_control_msg(icoms *p, int *transferred, int requesttype, int request,
+ int value, int index, unsigned char *bytes, int size, int timeout);
+static void *io_runloop(void *context);
+
+/* Open a USB port for all our uses. */
+/* This always re-opens the port */
+/* return icom error */
+static int usb_open_port(
+icoms *p,
+int config, /* Configuration number, (1 based) */
+int wr_ep, /* Write end point */
+int rd_ep, /* Read end point */
+icomuflags usbflags,/* 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 */
+) {
+ IOReturn rv;
+ int tries = 0;
+ a1logd(p->log, 8, "usb_open_port: Make sure USB port is open, tries %d\n",retries);
+
+ if (p->is_open)
+ p->close_port(p);
+
+ /* Make sure the port is open */
+ if (!p->is_open) {
+ kkill_nproc_ctx *kpc = NULL;
+
+ if (config != 1) {
+ /* Nothing currently needs it, so we haven't implemented it yet... */
+ a1loge(p->log, ICOM_NOTS, "usb_open_port: native driver cant handle config %d\n",config);
+ return ICOM_NOTS;
+ }
+
+ pthread_mutex_init(&p->usbd->lock, NULL);
+ pthread_cond_init(&p->usbd->cond, NULL);
+
+ /* Do open retries */
+ for (tries = 0; retries >= 0; retries--, tries++) {
+ IOCFPlugInInterface **piif = NULL;
+ SInt32 score;
+
+ a1logd(p->log, 8, "usb_open_port: About to open USB port '%s'\n",p->name);
+
+ if (tries > 0) {
+ //msec_sleep(i_rand(50,100));
+ msec_sleep(77);
+ }
+ if (tries > 0 && pnames != NULL && kpc == NULL) {
+ if ((kpc = kkill_nprocess(pnames, p->log)) == NULL) {
+ a1logd(p->log, 1, "kkill_nprocess returned error!\n");
+ }
+ }
+
+ if ((rv = IOCreatePlugInInterfaceForService(p->usbd->ioob,
+ kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
+ &piif, &score)) != kIOReturnSuccess || piif == NULL) {
+ a1loge(p->log, rv, "usb_open_port: Failed to get piif for "
+ "USB device '%s', result 0x%x, piif 0x%x\n",p->name,rv,piif);
+ return ICOM_SYS;
+ }
+
+ p->usbd->device = NULL;
+ if ((rv = (*piif)->QueryInterface(piif,
+ CFUUIDGetUUIDBytes(DeviceInterfaceID), (void *)&p->usbd->device))
+ != kIOReturnSuccess || p->usbd->device == NULL) {
+ a1loge(p->log, rv, "usb_open_port: QueryInterface '%s' failed with 0%x\n",p->name, rv);
+ (*piif)->Release(piif);
+ return ICOM_SYS;
+ }
+ (*piif)->Release(piif); /* delete intermediate object */
+
+ if ((rv = (*p->usbd->device)->USBDeviceOpenSeize(p->usbd->device)) != kIOReturnSuccess) {
+ a1logd(p->log, 8, "usb_open_port: open '%s' config %d failed (0x%x) (Device being used ?)\n",p->name,config,rv);
+ if (retries <= 0) {
+ if (kpc != NULL)
+ kpc->del(kpc);
+ a1loge(p->log, rv, "usb_open_port: open '%s' config %d failed (0x%x) (Device being used ?)\n",p->name, config, rv);
+ (*p->usbd->device)->Release(p->usbd->device);
+ return ICOM_SYS;
+ }
+ continue;
+ } else if (p->debug)
+ a1logd(p->log, 1, "usb_open_port: open port '%s' succeeded\n",p->name);
+
+ p->uflags = usbflags;
+
+ /* We're done retries */
+ break;
+ }
+
+ if (kpc != NULL)
+ kpc->del(kpc);
+
+ /* We should only do a set configuration if the device has more than one */
+ /* possible configuration and it is currently not the desired configuration, */
+ /* but we should avoid doing a set configuration if the OS has already */
+ /* selected the configuration we want, since two set configs seem to */
+ /* mess up the Spyder2, BUT we can't do a get config because this */
+ /* messes up the i1pro-D. */
+
+ /* OS X doesn't do a set_configuration() by default */
+ p->cconfig = 0;
+
+ if (p->cconfig != config) {
+ if ((rv = (*(p->usbd->device))->SetConfiguration(
+ p->usbd->device, config)) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: SetConfiguration failed with 0x%x\n",rv);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ p->cconfig = config;
+ a1logd(p->log, 6, "usb_open_port: SetConfiguration %d OK\n",config);
+ }
+
+ /* Get interfaces */
+ {
+ int i, j;
+ IOUSBFindInterfaceRequest req;
+ io_iterator_t ioit;
+ io_service_t iface;
+ IOCFPlugInInterface **pluginref = NULL;
+ SInt32 score;
+
+ req.bInterfaceClass =
+ req.bInterfaceSubClass =
+ req.bInterfaceProtocol =
+ req.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ if ((rv = (*(p->usbd->device))->CreateInterfaceIterator(
+ p->usbd->device, &req, &ioit)) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: CreateInterfaceIterator failed with 0x%x\n",rv);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ iface = IOIteratorNext(ioit);
+ for (i = 0; iface != 0; i++, iface = IOIteratorNext(ioit)) {
+ u_int8_t nep;
+
+ if ((rv = IOCreatePlugInInterfaceForService(iface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &pluginref, &score)) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: IOCreatePlugInInterfaceForService failed with 0x%x\n",rv);
+ IOObjectRelease(iface);
+ IOObjectRelease(ioit);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ IOObjectRelease(iface);
+ if ((rv = (*pluginref)->QueryInterface(pluginref,
+ CFUUIDGetUUIDBytes(InterfaceInterfaceID),
+ (void *)&p->usbd->interfaces[i])) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: QueryInterface failed with 0x%x\n",rv);
+ (*pluginref)->Stop(pluginref);
+ IODestroyPlugInInterface(pluginref);
+ IOObjectRelease(ioit);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ (*pluginref)->Stop(pluginref);
+ IODestroyPlugInInterface(pluginref);
+
+ if ((rv = (*(p->usbd->interfaces[i]))->USBInterfaceOpen(
+ p->usbd->interfaces[i])) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: USBInterfaceOpen failed with 0x%x\n",rv);
+ IOObjectRelease(ioit);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+
+ /* Get the end point details, and set reference to pipe no and interfece ix */
+ if ((rv = (*(p->usbd->interfaces[i]))->GetNumEndpoints(
+ p->usbd->interfaces[i], &nep)) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: GetNumEndpoints failed with 0x%x\n",rv);
+ IOObjectRelease(ioit);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ for (j = 0; j < nep; j++) {
+ UInt8 direction, pnumber, transferType, interval;
+ UInt16 maxPacketSize;
+ int ad;
+ if ((rv = (*(p->usbd->interfaces[i]))->GetPipeProperties(
+ p->usbd->interfaces[i], (UInt8)(j+1), &direction, &pnumber,
+ &transferType, &maxPacketSize, &interval)) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: GetPipeProperties failed with 0x%x\n",rv);
+ IOObjectRelease(ioit);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ ad = ((direction << IUSB_ENDPOINT_DIR_SHIFT) & IUSB_ENDPOINT_DIR_MASK)
+ | (pnumber & IUSB_ENDPOINT_NUM_MASK);
+ p->EPINFO(ad).valid = 1;
+ p->EPINFO(ad).addr = ad;
+ p->EPINFO(ad).packetsize = maxPacketSize;
+ p->EPINFO(ad).type = transferType & IUSB_ENDPOINT_TYPE_MASK;
+ p->EPINFO(ad).interface = i;
+ p->EPINFO(ad).pipe = j+1;
+ a1logd(p->log, 6, "set ep ad 0x%x packetsize %d type %d, if %d, pipe %d\n",ad,maxPacketSize,transferType & IUSB_ENDPOINT_TYPE_MASK,i,j);
+ }
+ }
+ p->usbd->nifce = i; /* Just in case.. */
+
+ IOObjectRelease(ioit);
+ }
+ {
+ /* Setup the RunLoop thread */
+ a1logd(p->log, 6, "usb_open_port: Starting RunLoop thread\n");
+ if ((rv = pthread_create(&p->usbd->thread, NULL, io_runloop, (void*)p)) < 0) {
+ a1loge(p->log, ICOM_SYS, "usb_open_port: creating RunLoop thread failed with %s\n",rv);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+
+ /* Wait for the runloop thread to start and create a cfrunloop */
+ pthread_mutex_lock(&p->usbd->lock);
+ while (p->usbd->cfrunloop == NULL)
+ pthread_cond_wait(&p->usbd->cond, &p->usbd->lock);
+ pthread_mutex_unlock(&p->usbd->lock);
+ if (p->usbd->thrv != kIOReturnSuccess) { /* Thread failed */
+ pthread_join(p->usbd->thread, NULL);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ CFRetain(p->usbd->cfrunloop);
+ a1logd(p->log, 6, "usb_open_port: RunLoop thread started\n");
+ }
+
+ /* Clear any errors */
+ /* (Some I/F seem to hang if we do this, some seem to hang if we don't !) */
+ if (!(p->uflags & icomuf_no_open_clear)) {
+ int i;
+ for (i = 0; i < 32; i++) {
+ if (!p->ep[i].valid)
+ continue;
+ p->usb_clearhalt(p, p->ep[i].addr);
+ }
+ }
+
+ /* Set "serial" coms values */
+ p->wr_ep = wr_ep;
+ p->rd_ep = rd_ep;
+ p->rd_qa = p->EPINFO(rd_ep).packetsize;
+ if (p->rd_qa == 0)
+ p->rd_qa = 8;
+ a1logd(p->log, 8, "usb_open_port: 'serial' read quanta = packet size = %d\n",p->rd_qa);
+
+ p->is_open = 1;
+ a1logd(p->log, 8, "usb_open_port: USB port is now open\n");
+ }
+
+ /* Install the cleanup signal handlers, and add to our cleanup list */
+ usb_install_signal_handlers(p);
+
+ return ICOM_OK;
+}
+
+/* -------------------------------------------------------------- */
+
+/* The run loop thread */
+static void *io_runloop(void *context) {
+ icoms *p = (icoms *)context;
+ int i;
+
+ /* Register this thread with the Objective-C garbage collector */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+ objc_registerThreadWithCollector();
+#endif
+
+ a1logd(p->log, 6, "io_runloop: thread started\n");
+
+ p->usbd->cfrunloop = CFRunLoopGetCurrent(); /* Get this threads RunLoop */
+ CFRetain(p->usbd->cfrunloop);
+
+ /* Add a device event source */
+ if ((p->usbd->thrv = (*(p->usbd->device))->CreateDeviceAsyncEventSource(
+ p->usbd->device, &p->usbd->cfsource)) != kIOReturnSuccess) {
+ a1loge(p->log, p->usbd->thrv, "io_runloop: CreateDeviceAsyncEventSource failed with 0x%x\n",p->usbd->thrv);
+ } else {
+ CFRunLoopAddSource(p->usbd->cfrunloop, p->usbd->cfsource, kCFRunLoopDefaultMode);
+ }
+
+ /* Create an async event source for the interfaces */
+ for (i = 0; p->usbd->thrv == kIOReturnSuccess && i < p->usbd->nifce; i++) {
+ if ((p->usbd->thrv = (*(p->usbd->interfaces[i]))->CreateInterfaceAsyncEventSource(
+ p->usbd->interfaces[i], &p->usbd->cfsources[i])) != kIOReturnSuccess) {
+ a1loge(p->log, p->usbd->thrv, "io_runloop: CreateInterfaceAsyncEventSource failed with 0x%x\n",p->usbd->thrv);
+ } else {
+ /* Add it to the RunLoop */
+ CFRunLoopAddSource(p->usbd->cfrunloop, p->usbd->cfsources[i], kCFRunLoopDefaultMode);
+ }
+ }
+
+ /* Signal main thread that we've started */
+ pthread_mutex_lock(&p->usbd->lock);
+ pthread_cond_signal(&p->usbd->cond);
+ pthread_mutex_unlock(&p->usbd->lock);
+
+ /* Run the loop, or exit on error */
+ if (p->usbd->thrv == kIOReturnSuccess) {
+ CFRunLoopRun(); /* Run the loop and deliver events */
+ }
+
+ /* Delete the interfaces async event sources */
+ for (i = 0; i < p->usbd->nifce; i++) {
+ if (p->usbd->cfsources[i] != NULL) {
+ CFRunLoopRemoveSource(p->usbd->cfrunloop, p->usbd->cfsources[i], kCFRunLoopDefaultMode);
+ CFRelease(p->usbd->cfsources[i]);
+ }
+ }
+
+ /* Delete the devices event sources */
+ if (p->usbd->cfsource != NULL) {
+ CFRunLoopRemoveSource(p->usbd->cfrunloop, p->usbd->cfsource, kCFRunLoopDefaultMode);
+ CFRelease(p->usbd->cfsource);
+ }
+
+ CFRelease(p->usbd->cfrunloop);
+
+ a1logd(p->log, 6, "io_runloop: thread done\n");
+ return NULL;
+}
+
+/* I/O structures */
+
+typedef struct _usbio_req {
+ icoms *p;
+ int iix; /* Interface index */
+ UInt8 pno; /* pipe index */
+ volatile int done; /* Done flag */
+ pthread_mutex_t lock; /* Protect req & callback access */
+ pthread_cond_t cond; /* Signal to thread waiting on req */
+ int xlength; /* Bytes transferred */
+ IOReturn result; /* Result of transaction */
+} usbio_req;
+
+
+/* Async completion callback - called by RunLoop thread */
+static void io_callback(void *refcon, IOReturn result, void *arg0) {
+ usbio_req *req = (usbio_req *)refcon;
+
+ a1logd(req->p->log, 1, "io_callback: result 0x%x, length %d\n",result,(int)(long)arg0);
+
+ req->xlength = (int)(long)arg0;
+ req->result = result;
+ req->done = 1;
+ pthread_mutex_lock(&req->lock);
+ pthread_cond_signal(&req->cond);
+ pthread_mutex_unlock(&req->lock);
+}
+
+/* Our universal USB transfer function */
+static int icoms_usb_transaction(
+ icoms *p,
+ usb_cancelt *cancelt,
+ int *transferred,
+ icom_usb_trantype ttype, /* transfer type */
+ unsigned char endpoint, /* 0x80 for control write, 0x00 for control read */
+ unsigned char *buffer,
+ int length,
+ unsigned int timeout /* In msec */
+) {
+ int reqrv = ICOM_OK, rv = 0;
+ int dirw = (endpoint & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT ? 1 : 0;
+ usbio_req req;
+ IOReturn result;
+ int iix = p->EPINFO(endpoint).interface;
+ UInt8 pno = (UInt8)p->EPINFO(endpoint).pipe;
+
+ in_usb_rw++;
+
+ a1logd(p->log, 8, "icoms_usb_transaction: req type 0x%x ep 0x%x size %d\n",ttype,endpoint,length);
+
+ if (ttype != icom_usb_trantype_interrutpt
+ && ttype != icom_usb_trantype_bulk) {
+ /* We only handle interrupt & bulk, not control */
+ return ICOM_SYS;
+ }
+
+ req.p = p;
+ req.iix = iix;
+ req.pno = pno;
+ req.xlength = 0;
+ req.done = 0;
+ pthread_mutex_init(&req.lock, NULL);
+ pthread_cond_init(&req.cond, NULL);
+
+ if (dirw)
+ result = (*p->usbd->interfaces[iix])->WritePipeAsync(p->usbd->interfaces[iix],
+ pno, buffer, length, io_callback, &req);
+ else
+ result = (*p->usbd->interfaces[iix])->ReadPipeAsync(p->usbd->interfaces[iix],
+ pno, buffer, length, io_callback, &req);
+
+ if (result != kIOReturnSuccess) {
+ a1loge(p->log, ICOM_SYS, "icoms_usb_transaction: %sPipeAsync failed with 0x%x\n",dirw ? "Write" : "Read", result);
+ reqrv = ICOM_SYS;
+ goto done;
+ }
+
+ if (cancelt != NULL) {
+ usb_lock_cancel(cancelt);
+ cancelt->hcancel = (void *)&req;
+ usb_unlock_cancel(cancelt);
+ }
+
+ /* Wait for the callback to complete */
+ pthread_mutex_lock(&req.lock);
+ if (!req.done) {
+ struct timeval tv;
+ struct timespec ts;
+
+ // this is unduly complicated...
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec + timeout/1000;
+ ts.tv_nsec = (tv.tv_usec + (timeout % 1000) * 1000) * 1000L;
+ if (ts.tv_nsec > 1000000000L) {
+ ts.tv_nsec -= 1000000000L;
+ ts.tv_sec++;
+ }
+
+ for(;;) { /* Ignore spurious wakeups */
+ if ((rv = pthread_cond_timedwait(&req.cond, &req.lock, &ts)) != 0) {
+ if (rv != ETIMEDOUT) {
+ a1logd(p->log, 1, "coms_usb_transaction: pthread_cond_timedwait failed with %d\n",rv);
+ (*p->usbd->interfaces[iix])->AbortPipe(p->usbd->interfaces[iix], pno);
+ req.result = kIOReturnAborted;
+ reqrv = ICOM_SYS;
+ break;
+ }
+
+ /* Timed out */
+ a1logd(p->log, 8, "coms_usb_transaction: time out - aborting io\n");
+ (*p->usbd->interfaces[iix])->AbortPipe(p->usbd->interfaces[iix], pno);
+ reqrv = ICOM_TO;
+ /* Wait for the cancelled io to be signalled */
+ if ((rv = pthread_cond_wait(&req.cond, &req.lock)) != 0) {
+ pthread_mutex_unlock(&req.lock);
+ a1logd(p->log, 1, "coms_usb_transaction: pthread_cond_wait failed with %d\n",rv);
+ req.result = kIOReturnAborted;
+ reqrv = ICOM_SYS;
+ break;
+ }
+ break;
+ }
+ if (req.done) /* Ignore spurious wakeups */
+ break;
+ }
+ }
+ pthread_mutex_unlock(&req.lock);
+
+ a1logd(p->log, 8, "coms_usb_transaction: completed with reqrv 0x%x and xlength %d\n",req.result,req.xlength);
+
+ /* If io was aborted, ClearPipeStall */
+ if (req.result == kIOReturnAborted) {
+#if defined(NEVER) && (InterfaceVersion > 182)
+ (*p->usbd->interfaces[iix])->ClearPipeStallBothEnds(p->usbd->interfaces[iix], pno);
+#else
+ (*p->usbd->interfaces[iix])->ClearPipeStall(p->usbd->interfaces[iix], pno);
+ icoms_usb_control_msg(p, NULL, IUSB_REQ_HOST_TO_DEV | IUSB_REQ_TYPE_STANDARD
+ | IUSB_REQ_RECIP_ENDPOINT, IUSB_REQ_CLEAR_FEATURE,
+ IUSB_FEATURE_EP_HALT, endpoint, NULL, 0, 200);
+#endif
+ if (reqrv == ICOM_OK) /* If not aborted for a known reason, must be cancelled */
+ reqrv = ICOM_CANC;
+ }
+
+ /* If normal completion - not timed out or aborted */
+ if (reqrv == ICOM_OK) { /* Completed OK */
+ if (req.result == kIOReturnSuccess) {
+ if (req.xlength != length)
+ reqrv = ICOM_SHORT;
+ } else {
+ if (dirw)
+ reqrv = ICOM_USBW;
+ else
+ reqrv = ICOM_USBR;
+ }
+ }
+
+ if (transferred != NULL)
+ *transferred = req.xlength;
+
+done:;
+ if (cancelt != NULL) {
+ usb_lock_cancel(cancelt);
+ cancelt->hcancel = (void *)NULL;
+ usb_unlock_cancel(cancelt);
+ }
+
+ pthread_cond_destroy(&req.cond);
+ pthread_mutex_destroy(&req.lock);
+
+ if (in_usb_rw < 0)
+ exit(0);
+
+ in_usb_rw--;
+
+ a1logd(p->log, 8, "coms_usb_transaction: returning err 0x%x and %d bytes\n",reqrv, req.xlength);
+
+ return reqrv;
+}
+
+
+/* Our control message routine */
+/* Return error icom error code */
+static int icoms_usb_control_msg(
+icoms *p,
+int *transferred,
+int requesttype, int request,
+int value, int index, unsigned char *bytes, int size,
+int timeout) {
+ int reqrv = ICOM_OK;
+ int dirw = (requesttype & IUSB_REQ_DIR_MASK) == IUSB_REQ_HOST_TO_DEV ? 1 : 0;
+ IOReturn rv;
+ IOUSBDevRequestTO req;
+
+ a1logd(p->log, 8, "icoms_usb_control_msg: type 0x%x req 0x%x size %d\n",requesttype,request,size);
+ bzero(&req, sizeof(req));
+ req.bmRequestType = requesttype;
+ req.bRequest = request;
+ req.wValue = value;
+ req.wIndex = index;
+ req.wLength = size;
+ req.pData = bytes;
+ req.completionTimeout = timeout;
+ req.noDataTimeout = timeout;
+
+ if (transferred != NULL)
+ *transferred = 0;
+
+ if ((rv = (*(p->usbd->device))->DeviceRequestTO(p->usbd->device, &req)) != kIOReturnSuccess) {
+ if (rv == kIOUSBTransactionTimeout) {
+ reqrv = ICOM_TO;
+ } else {
+ if (dirw)
+ reqrv = ICOM_USBW;
+ else
+ reqrv = ICOM_USBR;
+ }
+ } else {
+ if (transferred != NULL)
+ *transferred = req.wLenDone;
+ }
+
+ a1logd(p->log, 8, "icoms_usb_control_msg: returning err 0x%x and %d bytes\n",reqrv, transferred != NULL ? *transferred : -1);
+ return reqrv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Time out error return value */
+
+//#define USBIO_ERROR_TIMEOUT -ETIMEDOUT
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Cancel i/o in another thread */
+int icoms_usb_cancel_io(
+ icoms *p,
+ usb_cancelt *cancelt
+) {
+ int reqrv = ICOM_OK;
+
+ usb_lock_cancel(cancelt);
+ usbio_req *req = (usbio_req *)cancelt->hcancel;
+ if (req != NULL) {
+ IOReturn rv;
+
+ if ((rv = (*p->usbd->interfaces[req->iix])->AbortPipe(
+ p->usbd->interfaces[req->iix], req->pno)) != kIOReturnSuccess) {
+ reqrv = ICOM_USBW;
+ }
+ }
+ usb_unlock_cancel(cancelt);
+
+ return reqrv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Reset and end point data toggle to 0 */
+int icoms_usb_resetep(
+ icoms *p,
+ int ep /* End point address */
+) {
+ int reqrv = ICOM_OK;
+ int iix = p->EPINFO(ep).interface;
+ UInt8 pno = (UInt8)p->EPINFO(ep).pipe;
+ IOReturn rv;
+
+ if ((rv = (*p->usbd->interfaces[iix])->ResetPipe(
+ p->usbd->interfaces[iix], pno)) != kIOReturnSuccess) {
+ a1logd(p->log, 1, "icoms_usb_resetep failed with 0x%x\n",rv);
+ reqrv = ICOM_USBW;
+ }
+ return reqrv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Clear a halt on an end point */
+int icoms_usb_clearhalt(
+ icoms *p,
+ int ep /* End point address */
+) {
+ int reqrv = ICOM_OK;
+ int iix = p->EPINFO(ep).interface;
+ UInt8 pno = (UInt8)p->EPINFO(ep).pipe;
+ IOReturn rv;
+ int irv;
+
+#if defined(NEVER) && (InterfaceVersion > 182)
+ if ((rv = (*p->usbd->interfaces[iix])->ClearPipeStallBothEnds(
+ p->usbd->interfaces[iix], pno)) != kIOReturnSuccess) {
+ a1logd(p->log, 1, "icoms_usb_clearhalt failed with 0x%x\n",rv);
+ reqrv = ICOM_USBW;
+ }
+#else
+ if ((rv = (*p->usbd->interfaces[iix])->ClearPipeStall(
+ p->usbd->interfaces[iix], pno)) != kIOReturnSuccess) {
+ a1logd(p->log, 1, "icoms_usb_clearhalt failed with 0x%x\n",rv);
+ reqrv = ICOM_USBW;
+ }
+ if ((irv = icoms_usb_control_msg(p, NULL, IUSB_REQ_HOST_TO_DEV | IUSB_REQ_TYPE_STANDARD
+ | IUSB_REQ_RECIP_ENDPOINT, IUSB_REQ_CLEAR_FEATURE,
+ IUSB_FEATURE_EP_HALT, ep, NULL, 0, 200)) != ICOM_OK) {
+ a1logd(p->log, 1, "icoms_usb_clearhalt far end failed with 0x%x\n",irv);
+ reqrv = ICOM_USBW;
+ }
+#endif
+ return reqrv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifdef NEVER
+ {
+ IOUSBFindInterfaceRequest req;
+ io_iterator_t ioit;
+
+ /* See if we can find any interfaces */
+ req.bInterfaceClass =
+ req.bInterfaceSubClass =
+ req.bInterfaceProtocol =
+ req.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ if ((rv = (*(p->usbd->device))->CreateInterfaceIterator(
+ p->usbd->device, &req, &ioit)) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: CreateInterfaceIterator failed with 0x%x\n",rv);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ if (IOIteratorNext(ioit) == 0) { /* Configure the device */
+ IOUSBConfigurationDescriptorPtr confdesc;
+
+ if ((rv = (*(p->usbd->device))->GetConfigurationDescriptorPtr(
+ p->usbd->device, config-1, &confdesc)) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: GetConfigurationDescriptorPtr failed with 0x%x\n",rv);
+ IOObjectRelease(ioit);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ if ((rv = (*(p->usbd->device))->SetConfiguration(
+ p->usbd->device, confdesc->bConfigurationValue)) != kIOReturnSuccess) {
+ a1loge(p->log, rv, "usb_open_port: SetConfiguration failed with 0x%x\n",rv);
+ IOObjectRelease(ioit);
+ cleanup_device(p);
+ return ICOM_SYS;
+ }
+ a1logd(p->log, 6, "usb_open_port: SetConfiguration %d OK\n",confdesc->bConfigurationValue);
+
+ } else { /* Some diagnostics */
+ UInt8 confno;
+ if ((rv = (*(p->usbd->device))->GetConfiguration(
+ p->usbd->device, &confno)) != kIOReturnSuccess) {
+ a1logd(p->log, 6, "usb_open_port: GetConfiguration failed with 0x%x\n",rv);
+ } else {
+ a1logd(p->log, 6, "usb_open_port: Device didn't need configuring - currently %d\n",confno);
+ }
+ }
+ IOObjectRelease(ioit);
+
+ }
+#endif /* NEVER */