/* General USB I/O support, MSWin native libusb0.sys 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 #include #include #include #include #include #include #include #include #undef DEBUG /* Turn on debug messages */ #define LIBUSBW1_MAX_DEVICES 255 #define LIBUSBW1_PATH_MAX 512 #define LIBUSBW1_DEFAULT_TIMEOUT 5000 /* USB descriptors are little endian */ /* Take a word sized return buffer, and convert it to an unsigned int */ static unsigned int buf2uint(unsigned char *buf) { unsigned int val; val = buf[3]; val = ((val << 8) + (0xff & buf[2])); val = ((val << 8) + (0xff & buf[1])); val = ((val << 8) + (0xff & buf[0])); return val; } /* Take a short sized return buffer, and convert it to an int */ static unsigned int buf2ushort(unsigned char *buf) { unsigned int val; val = buf[1]; val = ((val << 8) + (0xff & buf[0])); return val; } /* Take an int, and convert it into a byte buffer */ static void int2buf(unsigned char *buf, int inv) { buf[0] = (inv >> 0) & 0xff; buf[1] = (inv >> 8) & 0xff; buf[2] = (inv >> 16) & 0xff; buf[3] = (inv >> 24) & 0xff; } /* Take a short, and convert it into a byte buffer */ static void short2buf(unsigned char *buf, int inv) { buf[0] = (inv >> 0) & 0xff; buf[1] = (inv >> 8) & 0xff; } /* Do a synchronous request. Return an ICOM error */ static int do_sync_io( HANDLE handle, unsigned int ioctl, void *out, int outsz, void *in, int insz, int *retsz) { OVERLAPPED olaps; DWORD xlength; memset(&olaps, 0, sizeof(OVERLAPPED)); if (retsz != NULL) *retsz = 0; if ((olaps.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) return ICOM_SYS; if (!DeviceIoControl(handle, ioctl, out, outsz, in, insz, &xlength, &olaps)) { if (GetLastError() != ERROR_IO_PENDING) { CloseHandle(olaps.hEvent); return ICOM_USBW; } if (!GetOverlappedResult(handle, &olaps, &xlength, TRUE)) { CloseHandle(olaps.hEvent); return ICOM_USBR; } } CloseHandle(olaps.hEvent); if (retsz != NULL) *retsz = (int)xlength; return ICOM_OK; } /* Add paths to USB connected device */ /* Return an icom error */ int usb_get_paths( icompaths *p ) { unsigned int vid, pid, nep10 = 0xffff; unsigned int configix, nconfig, nifce; devType itype; struct usb_idevice *usbd = NULL; int rv, retsz, i; for (i = 0; i < LIBUSBW1_MAX_DEVICES; i++) { libusb_request req; char dpath[LIBUSBW1_PATH_MAX]; HANDLE handle; unsigned char buf[IUSB_DESC_TYPE_DEVICE_SIZE]; _snprintf(dpath, LIBUSBW1_PATH_MAX - 1,"\\\\.\\libusb0-%04d", i+1); if (i < 16) /* Suppress messages on unlikely ports */ a1logd(p->log, 6, "usb_get_paths opening device '%s'\n",dpath); if ((handle = CreateFile(dpath, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)) == INVALID_HANDLE_VALUE) { #ifdef DEBUG a1logd(p->log, 8, "usb_get_paths failed to open device '%s'\n",dpath); #endif continue; } /* Set kernel message debug */ if (p->log->debug >= 6) { req.debug.level = LIBUSB_DEBUG_MAX; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if (do_sync_io(handle, LIBUSB_IOCTL_SET_DEBUG_LEVEL, &req, sizeof(libusb_request), NULL, 0, NULL)) { a1logd(p->log, 1, "usb_get_paths: failed to set driver log leve\n"); } else { a1logd(p->log, 1, "usb_get_paths: turned on kernel debug messages\n"); } } /* Read the device descriptor */ req.descriptor.type = IUSB_DESC_TYPE_DEVICE; req.descriptor.recipient = IUSB_REQ_RECIP_DEVICE; req.descriptor.index = 0; req.descriptor.language_id = 0; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if (do_sync_io(handle, LIBUSB_IOCTL_GET_DESCRIPTOR, &req, sizeof(libusb_request), buf, IUSB_DESC_TYPE_DEVICE_SIZE, &retsz) != ICOM_OK || retsz != IUSB_DESC_TYPE_DEVICE_SIZE) { a1logd(p->log, 1, "usb_get_paths: failed to read device descriptor '%s'\n",dpath); CloseHandle(handle); continue; } /* Extract the vid and pid */ vid = buf2ushort(buf + 8); pid = buf2ushort(buf + 10); nconfig = buf[17]; a1logd(p->log, 6, "usb_get_paths: checking vid 0x%04x, pid 0x%04x\n",vid,pid); /* Do a preliminary match */ if ((itype = inst_usb_match(vid, pid, 0)) == instUnknown) { a1logd(p->log, 6 , "usb_get_paths: instrument not reconized\n"); CloseHandle(handle); 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, "usb_get_paths: calloc failed!\n"); CloseHandle(handle); return ICOM_SYS; } usbd->nconfig = nconfig; /* Read the configuration descriptors looking for the first configuration, first interface, */ /* and extract the number of end points for each configuration */ for (configix = 0; configix < nconfig; configix++) { int configno, totlen; unsigned char *buf2, *bp, *zp; unsigned int ninfaces, inface, nep; /* Read the configuration descriptor */ req.descriptor.type = IUSB_DESC_TYPE_CONFIG; req.descriptor.recipient = IUSB_REQ_RECIP_DEVICE; req.descriptor.index = configix; req.descriptor.language_id = 0; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if ((rv = do_sync_io(handle, LIBUSB_IOCTL_GET_DESCRIPTOR, &req, sizeof(libusb_request), buf, IUSB_DESC_TYPE_CONFIG_SIZE, &retsz)) != ICOM_OK || retsz != IUSB_DESC_TYPE_CONFIG_SIZE) { a1logd(p->log, 1, "usb_get_paths: failed to read configix %d descriptor\n",configix); free(usbd); CloseHandle(handle); break; } nifce = buf[4]; /* number of interfaces */ configno = buf[5]; /* Configuration number */ if (configno != 1) continue; if ((totlen = buf2ushort(buf + 2)) < 6) { a1logd(p->log, 1, "usb_get_paths: '%s' config desc size strange\n",dpath); free(usbd); CloseHandle(handle); break; } if ((buf2 = calloc(1, totlen)) == NULL) { a1loge(p->log, ICOM_SYS, "usb_get_paths: calloc of descriptor failed!\n"); free(usbd); CloseHandle(handle); return ICOM_SYS; } /* Read the whole configuration descriptor */ req.descriptor.type = IUSB_DESC_TYPE_CONFIG; req.descriptor.recipient = IUSB_REQ_RECIP_DEVICE; req.descriptor.index = configix; req.descriptor.language_id = 0; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if (do_sync_io(handle, LIBUSB_IOCTL_GET_DESCRIPTOR, &req, sizeof(libusb_request), buf2, totlen, &retsz) != ICOM_OK || retsz != totlen) { a1logd(p->log, 1, "usb_get_paths: failed to read all configix %d descriptor\n",configix); free(buf2); free(usbd); CloseHandle(handle); break; } bp = buf2 + buf2[0]; /* Skip coniguration tag */ zp = buf2 + totlen; /* Past last bytes */ /* We are at the first configuration. */ /* Just read tags and keep track of where we are */ ninfaces = 0; nep = 0; usbd->nifce = buf2[4]; /* number of interfaces */ usbd->config = configno = buf2[5]; /* this configuration */ for (;bp < zp; bp += bp[0]) { int ifaceno; if ((bp + 1) >= zp) break; /* Hmm - bodgy, give up */ if (bp[1] == IUSB_DESC_TYPE_INTERFACE) { ninfaces++; if ((bp + 2) >= zp) break; /* Hmm - bodgy, give up */ ifaceno = bp[2]; /* Get bInterfaceNumber */ } else if (bp[1] == IUSB_DESC_TYPE_ENDPOINT) { nep++; if ((bp + 5) >= zp) break; /* Hmm - bodgy */ /* At first config - */ /* record current nep and end point details */ if (configno == 1) { int ad = bp[2]; nep10 = nep; usbd->EPINFO(ad).valid = 1; usbd->EPINFO(ad).addr = ad; usbd->EPINFO(ad).packetsize = buf2ushort(bp + 4); usbd->EPINFO(ad).type = bp[3] & IUSB_ENDPOINT_TYPE_MASK; usbd->EPINFO(ad).interface = ifaceno; a1logd(p->log, 6, "set ep ad 0x%x packetsize %d type %d\n",ad,usbd->EPINFO(ad).packetsize,usbd->EPINFO(ad).type); } } /* Ignore other tags */ } free(buf2); } if (nep10 == 0xffff) { /* Hmm. Failed to find number of end points */ a1logd(p->log, 1, "usb_get_paths: failed to find number of end points\n"); free(usbd); CloseHandle(handle); continue; } /* Check that we have an up to date kernel driver */ req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if (do_sync_io(handle, LIBUSB_IOCTL_GET_VERSION, &req, sizeof(libusb_request), &req, sizeof(libusb_request), &retsz) != ICOM_OK || retsz != sizeof(libusb_request)) { a1logd(p->log, 1, "usb_get_paths: failed to read driver version info\n"); free(usbd); CloseHandle(handle); continue; } if (req.version.major < 1 || req.version.major == 1 && (req.version.minor < 2 || req.version.minor == 2 && req.version.micro < 6)) { a1loge(p->log, ICOM_VER, "usb_get_paths: Must update %s System Driver to latest version!\n",inst_name(itype)); free(usbd); CloseHandle(handle); return ICOM_VER; } CloseHandle(handle); /* Found a known instrument ? */ if ((itype = inst_usb_match(vid, pid, nep10)) != instUnknown) { char pname[400]; a1logd(p->log, 1, "usb_get_paths: found instrument vid 0x%04x, pid 0x%04x\n",vid,pid); /* Create a path/identification */ sprintf(pname,"%s (%s)", dpath + 4, inst_name(itype)); if ((usbd->dpath = strdup(dpath)) == NULL) { a1loge(p->log, ICOM_SYS, "usb_get_paths: strdup path failed!\n"); free(usbd); return ICOM_SYS; } /* Add the path and ep info to the list */ if ((rv = p->add_usb(p, pname, vid, pid, nep10, usbd, itype)) != ICOM_OK) return rv; } else { free(usbd); } } 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; } if ((d->usbd->dpath = strdup(s->usbd->dpath)) == NULL) { a1loge(d->log, ICOM_SYS, "usb_copy_usb_idevice: malloc\n"); return ICOM_SYS; } /* Copy the current state & ep info */ d->nconfig = s->usbd->nconfig; d->config = s->usbd->config; d->nifce = s->usbd->nifce; for (i = 0; i < 32; i++) d->ep[i] = s->usbd->ep[i]; /* Struct copy */ 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->dpath != NULL) free(usbd->dpath); free(usbd); } /* Cleanup any USB specific icoms state */ void usb_del_usb(icoms *p) { usb_del_usb_idevice(p->usbd); } /* 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) { int iface, rv; /* Release all the interfaces */ for (iface = 0; iface < p->nifce; iface++) { libusb_request req; memset(&req, 0, sizeof(req)); req.intf.interface_number = iface; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; do_sync_io(p->usbd->handle, LIBUSB_IOCTL_RELEASE_INTERFACE, &req, sizeof(libusb_request), NULL, 0, NULL); } /* Workaround for some bugs - reset device on close */ if (p->uflags & icomuf_reset_before_close) { libusb_request req; memset(&req, 0, sizeof(req)); req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if ((rv = do_sync_io(p->usbd->handle, LIBUSB_IOCTL_RESET_DEVICE, &req, sizeof(libusb_request), NULL, 0, NULL)) != ICOM_OK) { a1logd(p->log, 1, "usb_close_port: reset returned %d\n",rv); } msec_sleep(500); /* Things foul up unless we wait for the reset... */ } CloseHandle(p->usbd->handle); 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 void *urb_reaper(void *context); /* Declare */ /* 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 */ 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 */ ) { int rv, 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) { int rv, i, iface; kkill_nproc_ctx *kpc = NULL; OSVERSIONINFO osver; 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; } /* Do open retries */ for (tries = 0; retries >= 0; retries--, tries++) { a1logd(p->log, 8, "usb_open_port: About to open USB port '%s'\n",p->usbd->dpath); if (tries > 0) { //msec_sleep(i_rand(50,100)); msec_sleep(77); } if ((p->usbd->handle = CreateFile(p->usbd->dpath, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)) == INVALID_HANDLE_VALUE) { a1logd(p->log, 8, "usb_open_port: open '%s' config %d failed (%d) (Device being used ?)\n",p->usbd->dpath,config,GetLastError()); if (retries <= 0) { if (kpc != NULL) { if (kpc->th->result < 0) a1logw(p->log, "usb_open_port: killing competing processes failed\n"); kpc->del(kpc); } a1logw(p->log, "usb_open_port: open '%s' config %d failed (%d) (Device being used ?)\n",p->usbd->dpath,config,GetLastError()); return ICOM_SYS; } continue; } else if (p->debug) a1logd(p->log, 2, "usb_open_port: open port '%s' succeeded\n",p->usbd->dpath); p->uflags = usbflags; /* We're done */ 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. */ osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); osver.dwMajorVersion = 5; GetVersionEx(&osver); if (osver.dwMajorVersion >= 6 && osver.dwMinorVersion >= 2) { p->cconfig = 0; /* Need to do set_congfig(1) on Win8 */ } else { p->cconfig = 1; /* Set by default to config 1 */ } if (p->cconfig != config) { libusb_request req; memset(&req, 0, sizeof(libusb_request)); req.configuration.configuration = config; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if ((rv = do_sync_io(p->usbd->handle, LIBUSB_IOCTL_SET_CONFIGURATION, &req, sizeof(libusb_request), NULL, 0, NULL)) != ICOM_OK) { a1loge(p->log, rv, "usb_open_port: Setting port '%s' to config %d failed with %d\n",p->usbd->dpath,config,rv); return ICOM_SYS; } p->cconfig = config; a1logd(p->log, 6, "usb_open_port: set config %d OK\n",config); } /* Claim all the interfaces */ for (iface = 0; iface < p->nifce; iface++) { libusb_request req; memset(&req, 0, sizeof(libusb_request)); req.intf.interface_number = iface; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if ((rv = do_sync_io(p->usbd->handle, LIBUSB_IOCTL_CLAIM_INTERFACE, &req, sizeof(libusb_request), NULL, 0, NULL)) != ICOM_OK) { a1loge(p->log, rv, "usb_open_port: Claiming USB port '%s' interface %d failed with %d\n",p->usbd->dpath,iface,rv); return ICOM_SYS; } } /* 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)) { 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; } /* -------------------------------------------------------------- */ /* Our universal USB transfer function, used for rd/wr. */ /* It appears that we may return a timeout with valid characters. */ 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 rv = ICOM_OK; int dirw = (endpoint & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT ? 1 : 0; libusb_request req; OVERLAPPED olaps; DWORD xlength = 0; in_usb_rw++; a1logd(p->log, 8, "icoms_usb_transaction: req type 0x%x ep 0x%x size %d\n",ttype,endpoint,length); if (transferred != NULL) *transferred = 0; if (ttype != icom_usb_trantype_interrutpt && ttype != icom_usb_trantype_bulk) { /* We only handle interrupt & bulk, not control */ return ICOM_SYS; } memset(&olaps, 0, sizeof(olaps)); if ((olaps.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) return ICOM_SYS; memset(&req, 0, sizeof(libusb_request)); req.endpoint.endpoint = endpoint; if (!DeviceIoControl(p->usbd->handle, dirw ? LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE : LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ, &req, sizeof(libusb_request), buffer, length, &xlength, &olaps)) { if (GetLastError() != ERROR_IO_PENDING) { rv = dirw ? ICOM_USBW : ICOM_USBR; goto done; } if (cancelt != NULL) { amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)&endpoint; cancelt->state = 1; amutex_unlock(cancelt->condx); /* Signal any thread waiting for IO start */ amutex_unlock(cancelt->cmtx); } if (WaitForSingleObject(olaps.hEvent, timeout) == WAIT_TIMEOUT) { /* Cancel the operation, because it timed out */ memset(&req, 0, sizeof(libusb_request)); req.endpoint.endpoint = endpoint; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; do_sync_io(p->usbd->handle, LIBUSB_IOCTL_ABORT_ENDPOINT, &req, sizeof(libusb_request), NULL, 0, NULL); rv = ICOM_TO; } if (!GetOverlappedResult(p->usbd->handle, &olaps, &xlength, TRUE)) { if (rv == ICOM_OK) { if (GetLastError() == ERROR_OPERATION_ABORTED) rv = ICOM_CANC; else rv = dirw ? ICOM_USBW : ICOM_USBR; } } } done:; if (cancelt != NULL) { amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)NULL; if (cancelt->state == 0) amutex_unlock(cancelt->condx); /* Make sure this gets unlocked */ cancelt->state = 2; amutex_unlock(cancelt->cmtx); } CloseHandle(olaps.hEvent); if (transferred != NULL) *transferred = (int)xlength; /* The requested size wasn't transferred */ if (rv == ICOM_OK && xlength != length) rv = ICOM_SHORT; 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",rv, xlength); return rv; } /* 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 rv = ICOM_OK; int dirw = (requesttype & IUSB_REQ_DIR_MASK) == IUSB_REQ_HOST_TO_DEV ? 1 : 0; libusb_request req; unsigned char *obuf = (unsigned char *)&req; int osize = sizeof(libusb_request); unsigned char *ibuf = bytes; int isize = size; int ioctl = 0; int retsz = 0; a1logd(p->log, 8, "icoms_usb_control_msg: type 0x%x req 0x%x size %d\n",requesttype,request,size); if (transferred != NULL) *transferred = 0; memset(&req, 0, sizeof(libusb_request)); req.timeout = timeout; /* We need to treat each request type as a different IOCTL */ switch (requesttype & IUSB_REQ_TYPE_MASK) { case IUSB_REQ_TYPE_STANDARD: switch (request) { case IUSB_REQ_GET_STATUS: req.status.recipient = requesttype & IUSB_REQ_RECIP_MASK; req.status.index = index; ioctl = LIBUSB_IOCTL_GET_STATUS; break; case IUSB_REQ_CLEAR_FEATURE: req.feature.recipient = requesttype & IUSB_REQ_RECIP_MASK; req.feature.feature = value; req.feature.index = index; ioctl = LIBUSB_IOCTL_CLEAR_FEATURE; break; case IUSB_REQ_SET_FEATURE: req.feature.recipient = requesttype & IUSB_REQ_RECIP_MASK; req.feature.feature = value; req.feature.index = index; ioctl = LIBUSB_IOCTL_SET_FEATURE; break; case IUSB_REQ_GET_DESCRIPTOR: req.descriptor.recipient = requesttype & IUSB_REQ_RECIP_MASK; req.descriptor.type = (value >> 8) & 0xFF; req.descriptor.index = value & 0xFF; req.descriptor.language_id = index; ioctl = LIBUSB_IOCTL_GET_DESCRIPTOR; break; case IUSB_REQ_SET_DESCRIPTOR: req.descriptor.recipient = requesttype & IUSB_REQ_RECIP_MASK; req.descriptor.type = (value >> 8) & 0xFF; req.descriptor.index = value & 0xFF; req.descriptor.language_id = index; ioctl = LIBUSB_IOCTL_SET_DESCRIPTOR; break; case IUSB_REQ_GET_CONFIGURATION: ioctl = LIBUSB_IOCTL_GET_CONFIGURATION; break; case IUSB_REQ_SET_CONFIGURATION: req.configuration.configuration = value; ioctl = LIBUSB_IOCTL_SET_CONFIGURATION; break; case IUSB_REQ_GET_INTERFACE: req.intf.interface_number = index; ioctl = LIBUSB_IOCTL_GET_INTERFACE; break; case IUSB_REQ_SET_INTERFACE: req.intf.interface_number = index; req.intf.altsetting_number = value; ioctl = LIBUSB_IOCTL_SET_INTERFACE; break; default: return ICOM_SYS; } break; case IUSB_REQ_TYPE_VENDOR: case IUSB_REQ_TYPE_CLASS: req.vendor.type = (requesttype & IUSB_REQ_TYPE_MASK) >> IUSB_REQ_TYPE_SHIFT; req.vendor.recipient = requesttype & IUSB_REQ_RECIP_MASK; req.vendor.request = request; req.vendor.value = value; req.vendor.index = index; if (dirw) ioctl = LIBUSB_IOCTL_VENDOR_WRITE; else ioctl = LIBUSB_IOCTL_VENDOR_READ; break; case IUSB_REQ_TYPE_RESERVED: default: return ICOM_SYS; } /* If we're writing the data, append it to the req */ if (dirw) { osize = sizeof(libusb_request) + size; if ((obuf = calloc(1, osize)) == NULL) { a1loge(p->log, ICOM_SYS, "icoms_usb_control_msg: calloc failed\n"); return ICOM_SYS; } memcpy(obuf, &req, sizeof(libusb_request)); memcpy(obuf + sizeof(libusb_request), bytes, size); ibuf = NULL; isize = 0; } if ((rv = do_sync_io(p->usbd->handle, ioctl, obuf, osize, ibuf, isize, &retsz)) != ICOM_OK) { if (dirw) free(obuf); return rv; } if (dirw) { free(obuf); retsz = size; } if (transferred != NULL) /* Adjust for header size requested */ *transferred = retsz; a1logd(p->log, 8, "icoms_usb_control_msg: returning err 0x%x and %d bytes\n",rv, *transferred); return rv; } /* Cancel i/o operation in another thread. */ /* Only Vista has CancelIoEx that can cancel a single operation, */ /* so we cancel the io to the end point, which will */ /* acheive what we want. */ int icoms_usb_cancel_io( icoms *p, usb_cancelt *cancelt ) { int rv = ICOM_OK; amutex_lock(cancelt->cmtx); if (cancelt->hcancel != NULL) { libusb_request req; memset(&req, 0, sizeof(libusb_request)); req.endpoint.endpoint = *((unsigned char *)cancelt->hcancel); req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if ((rv = do_sync_io(p->usbd->handle, LIBUSB_IOCTL_ABORT_ENDPOINT, &req, sizeof(libusb_request), NULL, 0, NULL)) != ICOM_OK) { a1logd(p->log, 1, "icoms_usb_cancel_io: failed with 0x%x\n",rv); } } amutex_unlock(cancelt->cmtx); return rv; } /* - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Reset and end point data toggle to 0 */ int icoms_usb_resetep( icoms *p, int ep /* End point address */ ) { libusb_request req; int rv = ICOM_OK; memset(&req, 0, sizeof(libusb_request)); req.endpoint.endpoint = ep; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if ((rv = do_sync_io(p->usbd->handle, LIBUSB_IOCTL_RESET_ENDPOINT, &req, sizeof(libusb_request), NULL, 0, NULL)) != ICOM_OK) { a1logd(p->log, 1, "icoms_usb_resetep failed with %d\n",rv); return rv; } return rv; } /* - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Clear a halt on an end point */ /* (Actually does a resetep) */ int icoms_usb_clearhalt( icoms *p, int ep /* End point address */ ) { libusb_request req; int rv = ICOM_OK; memset(&req, 0, sizeof(libusb_request)); req.endpoint.endpoint = ep; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if ((rv = do_sync_io(p->usbd->handle, LIBUSB_IOCTL_RESET_ENDPOINT, &req, sizeof(libusb_request), NULL, 0, NULL)) != ICOM_OK) { a1logd(p->log, 1, "icoms_usb_resetep failed with %d\n",rv); return rv; } return rv; } /* - - - - - - - - - - - - - - - - - - - - - - - - - */ #ifdef NEVER libusb_request req; unsigned char buf[1] = { 0xff }; memset(&req, 0, sizeof(libusb_request)); req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; if ((rv = do_sync_io(p->usbd->handle, LIBUSB_IOCTL_GET_CONFIGURATION, &req, sizeof(libusb_request), buf, 1, NULL)) != ICOM_OK) { a1logd(p->log, 1, "usb_open_port: Getting port '%s' configuration failed with %d\n",p->usbd->dpath,rv); /* Ignore error */ } else { a1logd(p->log, 1, "usb_open_port: current config = %d\n",(int)buf[0]); } config = buf[0]; #endif // NEVER