From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- spectro/usbio_nt.c | 898 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 898 insertions(+) create mode 100644 spectro/usbio_nt.c (limited to 'spectro/usbio_nt.c') diff --git a/spectro/usbio_nt.c b/spectro/usbio_nt.c new file mode 100644 index 0000000..1711f0d --- /dev/null +++ b/spectro/usbio_nt.c @@ -0,0 +1,898 @@ + +/* 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 + +#define 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 instruments */ +/* Return an icom error */ +int usb_get_paths( +icompaths *p +) { + unsigned int vid, pid, nep10 = 0xffff; + unsigned int configix, nconfig, nifce; + instType 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); + 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_check_and_add: 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); + } + } + CloseHandle(p->usbd->handle); + + free(p->usbd->dpath); + 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 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) + kpc->del(kpc); + a1loge(p->log, ICOM_SYS, "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 */ +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 (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; + + if (cancelt != NULL) { + usb_lock_cancel(cancelt); + cancelt->hcancel = (void *)&endpoint; + usb_unlock_cancel(cancelt); + } + + 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 (WaitForSingleObject(olaps.hEvent, timeout) == WAIT_TIMEOUT) { + + /* Cancel the operation */ + 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) { + usb_lock_cancel(cancelt); + cancelt->hcancel = (void *)NULL; + usb_unlock_cancel(cancelt); + } + + 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); + + 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; + usb_lock_cancel(cancelt); + 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); + } + } + usb_unlock_cancel(cancelt); + + 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 + -- cgit v1.2.3