Files
argyllcms/spectro/usbio_dk.c
2026-08-20 20:28:40 +01:00

879 lines
27 KiB
C

/* General USB I/O support for MSWin using the UsbDk system driver. */
/* This file is conditionaly #included into usbio.c */
/* Based on usbio_w0.c */
# pragma message("######### !!!!! usbio_usbdk.c compiled !!!!! ########")
/*
* Argyll Color Management System
*
* Author: Graeme W. Gill
* Date: 2023/9/25
*
* Copyright 2006 - 2023 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <windows.h>
#include <winioctl.h>
#include <setupapi.h>
#include "conv.h"
#pragma message("!!!!!!!!!!!!!!!!!!!! usbio_dk defined !!!!!!!!!!!!!!!!!!!!!!!!!!!!")
#undef DEBUG /* Turn on debug messages */
// See example d:/src/libusb/libusb_1026/libusb/os/windows_usbdk.c
/* --------------------------------------------------- */
/* UsbDk functions */
static UsbDk_inited = 0;
static UsbDk_InstallDriver_t UsbDk_InstallDriver = NULL;
static UsbDk_UninstallDriver_t UsbDk_UninstallDriver = NULL;
static UsbDk_GetDevicesList_t UsbDk_GetDevicesList = NULL;
static UsbDk_ReleaseDevicesList_t UsbDk_ReleaseDevicesList = NULL;
static UsbDk_GetConfigurationDescriptor_t UsbDk_GetConfigurationDescriptor = NULL;
static UsbDk_ReleaseConfigurationDescriptor_t UsbDk_ReleaseConfigurationDescriptor = NULL;
static UsbDk_StartRedirect_t UsbDk_StartRedirect = NULL;
static UsbDk_StopRedirect_t UsbDk_StopRedirect = NULL;
static UsbDk_WritePipe_t UsbDk_WritePipe = NULL;
static UsbDk_ReadPipe_t UsbDk_ReadPipe = NULL;
static UsbDk_AbortPipe_t UsbDk_AbortPipe = NULL;
static UsbDk_ResetPipe_t UsbDk_ResetPipe = NULL;
static UsbDk_SetAltsetting_t UsbDk_SetAltsetting = NULL;
static UsbDk_ResetDevice_t UsbDk_ResetDevice = NULL;
static UsbDk_GetRedirectorSystemHandle_t UsbDk_GetRedirectorSystemHandle = NULL;
/* Make sure UsbDk is installed, and get the function pointers. */
/* Return nz on failure */
static int init_UsbDk(icompaths *p) {
char *pf = NULL;
char path[1024];
HMODULE lib;
if (UsbDk_inited)
return 0;
a1logd(p->log, 6, "init_UsbDk called:\n");
// ~8 ???
// Should check registry for a UsbDk install, and check the version.
// If it is not a version with bug fixes, uninstall it
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\UsbDk
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\UsbDk\Parameters\Wdf
/* Look for the UsbDk .dll */
if ((pf = getenv("PROGRAMFILES")) == NULL)
pf = "C:\\Program Files";
strcpy(path, pf);
strcat(path, "/UsbDk Runtime Library/UsbDkHelper.dll");
if ((lib = LoadLibrary(path)) == NULL) {
// ~8 Should try and install ../usb/UsbDk_1.0.22_x64.msi or ../usb/UsbDk_1.0.22_x86.msi
// try and install it without the drivers running
a1logd(p->log, 1, "init_UsbDk didn't find library '%s'\n",path);
return 1;
}
a1logd(p->log, 7, "init_UsbDk found library\n");
UsbDk_InstallDriver = (UsbDk_InstallDriver_t)GetProcAddress(lib, "UsbDk_InstallDriver");
UsbDk_UninstallDriver = (UsbDk_UninstallDriver_t)GetProcAddress(lib, "UsbDk_UninstallDriver");
UsbDk_GetDevicesList = (UsbDk_GetDevicesList_t)GetProcAddress(lib, "UsbDk_GetDevicesList");
UsbDk_ReleaseDevicesList = (UsbDk_ReleaseDevicesList_t)
GetProcAddress(lib, "UsbDk_ReleaseDevicesList");
UsbDk_GetConfigurationDescriptor = (UsbDk_GetConfigurationDescriptor_t)
GetProcAddress(lib, "UsbDk_GetConfigurationDescriptor");
UsbDk_ReleaseConfigurationDescriptor = (UsbDk_ReleaseConfigurationDescriptor_t)
GetProcAddress(lib, "UsbDk_ReleaseConfigurationDescriptor");
UsbDk_StartRedirect = (UsbDk_StartRedirect_t)GetProcAddress(lib, "UsbDk_StartRedirect");
UsbDk_StopRedirect = (UsbDk_StopRedirect_t)GetProcAddress(lib, "UsbDk_StopRedirect");
UsbDk_WritePipe = (UsbDk_WritePipe_t)GetProcAddress(lib, "UsbDk_WritePipe");
UsbDk_ReadPipe = (UsbDk_ReadPipe_t)GetProcAddress(lib, "UsbDk_ReadPipe");
UsbDk_AbortPipe = (UsbDk_AbortPipe_t)GetProcAddress(lib, "UsbDk_AbortPipe");
UsbDk_ResetPipe = (UsbDk_ResetPipe_t)GetProcAddress(lib, "UsbDk_ResetPipe");
UsbDk_SetAltsetting = (UsbDk_SetAltsetting_t)GetProcAddress(lib, "UsbDk_SetAltsetting");
UsbDk_ResetDevice = (UsbDk_ResetDevice_t)GetProcAddress(lib, "UsbDk_ResetDevice");
UsbDk_GetRedirectorSystemHandle = (UsbDk_GetRedirectorSystemHandle_t)
GetProcAddress(lib, "UsbDk_GetRedirectorSystemHandle");
/* Did all that work ? */
if (UsbDk_InstallDriver == NULL
|| UsbDk_UninstallDriver == NULL
|| UsbDk_GetDevicesList == NULL
|| UsbDk_ReleaseDevicesList == NULL
|| UsbDk_GetConfigurationDescriptor == NULL
|| UsbDk_ReleaseConfigurationDescriptor == NULL
|| UsbDk_StartRedirect == NULL
|| UsbDk_StopRedirect == NULL
|| UsbDk_WritePipe == NULL
|| UsbDk_ReadPipe == NULL
|| UsbDk_AbortPipe == NULL
|| UsbDk_ResetPipe == NULL
|| UsbDk_SetAltsetting == NULL
|| UsbDk_ResetDevice == NULL
|| UsbDk_GetRedirectorSystemHandle == NULL) {
a1logd(p->log, 1, "init_UsbDk failed to find all function\n");
return 1;
}
// ~8 should check if the system driver is running ???
// Can do this by checking service manager ?
// offer to start drivers. If no, fail init
// Start drivers using UsbDk_InstallDriver() ?
// Ask for confirm, if no confirm in 10 seconds, uninstall driver and fail init
a1logd(p->log, 6, "init_UsbDk done\n");
return 0;
}
/* Return a diagnostic string from the ID */
/* The returned buffer is static. */
static char *ID2str(USB_DK_DEVICE_ID *ID) {
static char buf[200];
if ((icmUTF16toUTF8(NULL, NULL, ID->DeviceID)
+ icmUTF16toUTF8(NULL, NULL, ID->InstanceID)
+ 1) > 200) {
sprintf(buf, "USBDK_ID too long for buffer!");
return buf;
}
icmUTF16toUTF8(NULL, buf, ID->DeviceID);
strcat(buf, "-");
icmUTF16toUTF8(NULL, buf + strlen(buf), ID->InstanceID);
return buf;
}
/* Add an OVERLAP cleanup record */
static add_olr(icoms *p, struct usbdk_olr *olr) {
EnterCriticalSection(&p->usbd->lock);
olr->next = p->usbd->olr;
if (p->usbd->olr != NULL)
p->usbd->olr->last = &olr->next;
p->usbd->olr = olr;
olr->last = &p->usbd->olr;
LeaveCriticalSection(&p->usbd->lock);
}
/* Remove an OVERLAP cleanup record */
static rem_olr(icoms *p, struct usbdk_olr *olr) {
EnterCriticalSection(&p->usbd->lock);
*olr->last = olr->next;
if (olr->next != NULL)
olr->next->last = olr->last;
olr->last = NULL;
olr->next = NULL;
LeaveCriticalSection(&p->usbd->lock);
}
/* --------------------------------------------------- */
/* Add paths to USB connected device */
/* Return an icom error */
int usb_get_paths_dk(
icompaths *p
) {
struct usb_idevice *usbd = NULL;
USB_DK_DEVICE_INFO *DevicesArray;
ULONG i, NumberDevices;
int upto;
a1logd(p->log, 5, "usb_get_paths_dk:\n");
if (init_UsbDk(p)) {
return ICOM_SYS;
}
if (!UsbDk_GetDevicesList(&DevicesArray, &NumberDevices)) {
a1logd(p->log, 1, "UsbDk_GetDevicesList failed\n");
return ICOM_SYS;
}
a1logd(p->log, 6, "UsbDk_GetDevicesList got %d devices\n",NumberDevices);
// USB_DK_DEVICE_ID ID;
// ULONG64 FilterID;
// ULONG64 Port;
// ULONG64 Speed;
// USB_DEVICE_DESCRIPTOR DeviceDescriptor;
//USB_DEVICE_DESCRIPTOR
// UCHAR bLength;
// UCHAR bDescriptorType;
// USHORT bcdUSB;
// UCHAR bDeviceClass;
// UCHAR bDeviceSubClass;
// UCHAR bDeviceProtocol;
// UCHAR bMaxPacketSize0;
// USHORT idVendor;
// USHORT idProduct;
// USHORT bcdDevice;
// UCHAR iManufacturer;
// UCHAR iProduct;
// UCHAR iSerialNumber;
// UCHAR bNumConfigurations;
upto = p->get_cur_count(p);
for (i = 0; i < NumberDevices; i++) {
unsigned int vid, pid, nep10 = 0xffff;
unsigned int nconfig, configix;
devType itype;
vid = DevicesArray[i].DeviceDescriptor.idVendor;
pid = DevicesArray[i].DeviceDescriptor.idProduct;
nconfig = DevicesArray[i].DeviceDescriptor.bNumConfigurations;
/*
iManufacturer = DevicesArray[i].DeviceDescriptor.iManufacturer;
iProduct = DevicesArray[i].DeviceDescriptor.iProduct;
iSerialNumber = DevicesArray[i].DeviceDescriptor.iSerialNumber;
*/
# pragma message("######### usbio_dk.c Missing SerialNumber read code !!!!! ########")
a1logd(p->log, 6, "usb_get_paths_dk: 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_dk: instrument not reconized\n");
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_dk: calloc failed!\n");
return ICOM_SYS;
}
usbd->nconfig = nconfig;
usbd->is_dk = 1;
usbd->ID = DevicesArray[i].ID;
/* 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++) {
USB_DK_CONFIG_DESCRIPTOR_REQUEST Request;
USB_CONFIGURATION_DESCRIPTOR *Descriptor;
ULONG Length;
int configno, totlen;
unsigned char *bp, *zp;
unsigned int nifce, ifcount, nep;
/* Read the configuration descriptor */
Request.ID = DevicesArray[i].ID;
Request.Index = configix;
if (!UsbDk_GetConfigurationDescriptor(&Request, &Descriptor, &Length)) {
a1logd(p->log, 1, "usb_get_paths_dk: '%s' failed to read configix %d descriptor\n",
ID2str(&usbd->ID), configix);
free(usbd);
break;
}
// USB_CONFIGURATION_DESCRIPTOR
// UCHAR bLength;
// UCHAR bDescriptorType;
// USHORT wTotalLength;
// UCHAR bNumInterfaces;
// UCHAR bConfigurationValue;
// UCHAR iConfiguration; Index of string descriptor
// UCHAR bmAttributes;
// UCHAR MaxPower;
// UCHAR .... Interface & Endpoint descriptors
nifce = Descriptor->bNumInterfaces; /* number of interfaces */
configno = Descriptor->bConfigurationValue; /* Configuration number */
if (configno != 1) {
UsbDk_ReleaseConfigurationDescriptor(Descriptor);
continue;
}
if ((totlen = Descriptor->wTotalLength) < 6) {
a1logd(p->log, 1, "usb_get_paths_dk: '%s' config desc size strange\n",ID2str(&usbd->ID));
UsbDk_ReleaseConfigurationDescriptor(Descriptor);
free(usbd);
break;
}
bp = ((unsigned char *)Descriptor) + Descriptor->bLength; /* Skip coniguration tag */
zp = ((unsigned char *)Descriptor) + totlen; /* Past last bytes */
/* We are at the first configuration. */
/* Just read tags and keep track of where we are */
ifcount = 0;
nep = 0;
usbd->nifce = nifce; /* number of interfaces */
usbd->config = configno; /* 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) {
ifcount++;
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) { /* (if isn't actually needed...) */
int ad = bp[2];
nep10 = nep;
usbd->EPINFO(ad).valid = 1;
usbd->EPINFO(ad).addr = ad;
usbd->EPINFO(ad).packetsize = usb2ushort(bp + 4);
usbd->EPINFO(ad).type = bp[3] & IUSB_ENDPOINT_TYPE_MASK;
usbd->EPINFO(ad).ifaceno = 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 */
}
UsbDk_ReleaseConfigurationDescriptor(Descriptor);
/* We only get here on the first config, so we're done. */
break;
} /* Next configuration */
if (nep10 == 0xffff) { /* Hmm. Failed to find number of end points */
a1logd(p->log, 1, "usb_get_paths_dk: failed to find number of end points\n");
free(usbd);
continue;
}
/* Found a known instrument, and not found by a previous driver ? */
if ((itype = inst_usb_match(vid, pid, nep10)) != instUnknown
&& !p->check_usb_upto(p, upto, itype)) {
char pname[400];
int rv;
a1logd(p->log, 1, "usb_get_paths_dk: found instrument vid 0x%04x, pid 0x%04x\n",vid,pid);
/* Create a path/identification */
sprintf(pname,"%s (%s)", ID2str(&usbd->ID), inst_name(itype));
/* Add the path and ep info to the list */
if ((rv = p->add_usb(p, pname, vid, pid, nep10, usbd, itype)) != ICOM_OK) {
free(usbd);
UsbDk_ReleaseDevicesList(DevicesArray);
return rv;
}
} else {
free(usbd);
}
} /* Next device */
UsbDk_ReleaseDevicesList(DevicesArray);
return ICOM_OK;
}
/* Copy usb_idevice contents from icompaths to icom */
/* return icom error */
int usb_copy_usb_idevice_dk(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_dk: malloc\n");
return ICOM_SYS;
}
d->usbd->is_dk = s->usbd->is_dk;
d->usbd->ID = s->usbd->ID;
if ((d->usbd->SerialNumber = strdup(s->usbd->SerialNumber)) == 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_dk(struct usb_idevice *usbd) {
if (usbd == NULL)
return;
if (usbd->SerialNumber != NULL)
free(usbd->SerialNumber);
free(usbd);
}
/* Cleanup any USB specific icoms state */
void usb_del_usb_dk(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_dk(icoms *p) {
a1logd(p->log, 6, "usb_close_port_dk: called\n");
if (p->is_open && p->usbd != NULL) {
HANDLE handle = p->usbd->handle;
int i;
struct usbdk_olr *olr;
p->usbd->handle = INVALID_HANDLE_VALUE;
/* We don't need to release interfaces */
/* Be nice and clean up. This helps workaround issue #105... */
/* Make sure any outstanding transfers are cancelled. */
a1logd(p->log, 6, "usb_close_port_dk: cancelling transfers\n");
for (i = 0; i < 32; i++) {
if (!p->ep[i].valid)
continue;
UsbDk_AbortPipe(handle, (ULONG64)p->ep[i].addr);
}
/* If there are pending operations, complete the OVERLAPPED, */
/* or else the driver may lock up and the system might need a re-boot... */
for (olr = p->usbd->olr; olr != NULL; olr = olr->next) {
DWORD xlength;
if (olr->olaps.hEvent != NULL) {
a1logd(p->log, 6, "usb_close_port_dk: doing GetOverlappedResult\n");
GetOverlappedResult(p->usbd->syshandle, &olr->olaps, &xlength, TRUE);
CloseHandle(olr->olaps.hEvent);
}
}
#ifdef NEVER /* We don't need to do this, since UsbDk CYCLEs the port anyway... */
/* Workaround for some bugs - reset device on close */
if (p->uflags & icomuf_reset_before_close) {
a1logd(p->log, 6, "usb_close_port_dk: icomuf_reset_before_close\n");
if (!UsbDk_ResetDevice(handle)) {
a1logd(p->log, 1, "usb_close_port_dk: reset failed\n");
}
msec_sleep(500); /* Things foul up unless we wait for the reset... */
}
#endif
if (!UsbDk_StopRedirect(handle)) {
a1logd(p->log, 1, "usb_close_port_dk failed\n");
}
a1logd(p->log, 6, "usb_close_port_dk: 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_dk(
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_dk: 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;
if (config != 1) {
/* NT defaults to config 1, and nothing currently needs any other config, */
/* so we haven't implemented selecting a config yet... */
a1loge(p->log, ICOM_NOTS, "usb_open_port_dk: native driver cant handle config %d\n",config);
return ICOM_NOTS;
msec_sleep(50); /* Allow device some time to configure */
}
InitializeCriticalSection(&p->usbd->lock);
/* Do open retries */
for (tries = 0; retries >= 0; retries--, tries++) {
a1logd(p->log, 8, "usb_open_port_dk: About to open USB port '%s'\n",ID2str(&p->usbd->ID));
if (tries > 0) {
//msec_sleep(i_rand(50,100));
msec_sleep(77);
}
if ((p->usbd->handle = UsbDk_StartRedirect(&p->usbd->ID)) == INVALID_HANDLE_VALUE) {
a1logd(p->log, 8, "usb_open_port_dk: open '%s' config %d failed (%d) (Device being used ?)\n",ID2str(&p->usbd->ID),config,GetLastError());
if (retries <= 0) {
if (kpc != NULL) {
if (kpc->th->result < 0)
a1logw(p->log, "usb_open_port_dk: killing competing processes failed\n");
kpc->del(kpc);
}
a1logw(p->log, "usb_open_port_dk: open '%s' config %d failed (%d) (Device being used ?)\n",ID2str(&p->usbd->ID),config,GetLastError());
return ICOM_SYS;
}
continue;
} else if (p->debug)
a1logd(p->log, 2, "usb_open_port_dk: open port '%s' succeeded\n",ID2str(&p->usbd->ID));
p->usbd->syshandle = UsbDk_GetRedirectorSystemHandle(p->usbd->handle);
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. */
/* For the moment, assume that the configuration is correct... */
/* UsbDk doesn't seem to support claiming of interfaces. */
/* Perhaps this is because it is not set up to share a USB device */
/* - it grabs exclusive use. */
/* 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_dk: 'serial' read quanta = packet size = %d\n",p->rd_qa);
p->is_open = 1;
a1logd(p->log, 8, "usb_open_port_dk: 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 (non-control pipe) USB transfer function, used for rd/wr. */
/* It appears that we may return a timeout with valid characters. */
static int icoms_usb_transaction_dk(
icoms *p,
usb_cancelt *cancelt, /* if !NULL, allows signalling another thread that op has started */
int *transferred,
icom_usb_trantype ttype, /* transfer type */
unsigned char endpoint, /* 0x80 for write, 0x00 for read */
unsigned char *buffer,
int length, /* Length to write/expected length to read */
unsigned int timeout /* In msec */
) {
USB_DK_TRANSFER_REQUEST Request = { 0 };
TransferResult result;
int rv = ICOM_OK;
struct usbdk_olr olr;
int dirw = (endpoint & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT ? 1 : 0;
DWORD xlength;
int retsz = 0;
in_usb_rw++;
a1logd(p->log, 8, "icoms_usb_transaction_dk: 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;
}
Request.EndpointAddress = endpoint;
Request.Buffer = buffer;
Request.BufferLength = length;
Request.TransferType = icom_usb_trantype_interrutpt ? InterruptTransferType : BulkTransferType;
memset(&olr, 0, sizeof(struct usbdk_olr));
if ((olr.olaps.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
return ICOM_SYS;
add_olr(p, &olr);
if (dirw)
result = UsbDk_WritePipe(p->usbd->handle, &Request, &olr.olaps);
else
result = UsbDk_ReadPipe(p->usbd->handle, &Request, &olr.olaps);
if (result == TransferFailure) {
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(olr.olaps.hEvent, timeout) == WAIT_TIMEOUT) {
/* Cancel the operation, because it timed out */
UsbDk_AbortPipe(p->usbd->handle, (ULONG64)endpoint);
rv = ICOM_TO;
}
if (!GetOverlappedResult(p->usbd->handle, &olr.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(olr.olaps.hEvent);
olr.olaps.hEvent = NULL;
rem_olr(p, &olr);
/* xlength doesn't seem to correspond with what UsbDk did, BytesTransferred does. */
retsz = (int)Request.Result.GenResult.BytesTransferred;
if (transferred != NULL)
*transferred = retsz;
/* The requested size wasn't transferred */
if (rv == ICOM_OK && retsz != length)
rv |= ICOM_SHORT;
if (in_usb_rw < 0) { /* We're being cleaned up */
a1logd(p->log, 8, "coms_usb_transaction: aborting because in_usb_rw = %dn",in_usb_rw);
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. */
/* It's basically up to the caller to set parameters that make up a valid request, */
/* as they are fed directly into the control setup. */
/* Return error icom error code */
static int icoms_usb_control_msg_dk(
icoms *p,
int *transferred,
int requesttype, /* Setup values */
int request,
int value,
int index,
unsigned char *bytes, /* Data to read or write */
int size, /* Size of data to read or write */
int timeout
) {
USB_DK_TRANSFER_REQUEST Request = { 0 };
TransferResult result;
struct usbdk_olr olr;
int rv = ICOM_OK;
int dirw = (requesttype & IUSB_REQ_DIR_MASK) == IUSB_REQ_HOST_TO_DEV ? 1 : 0;
int tsize;
unsigned char *tbuf = NULL;
DWORD xlength;
int retsz = 0;
a1logd(p->log, 8, "icoms_usb_control_msg_dk: type 0x%x req 0x%x size %d\n",requesttype,request,size);
if (transferred != NULL)
*transferred = 0;
/* We're guessing that the first 8 bytes of the buffer are the setup, */
/* with anything after that being the read/write data... */
/* Allocate buffer that includes setup */
tsize = 8 + size;
if ((tbuf = calloc(1, tsize)) == NULL) {
a1loge(p->log, ICOM_SYS, "icoms_usb_control_msg_dk: calloc failed\n");
return ICOM_SYS;
}
short2usb(&tbuf[6], size);
if (dirw)
memcpy(tbuf + 8, bytes, size); /* Copy write data */
/* Fill in the setup */
tbuf[0] = requesttype;
tbuf[1] = request;
short2usb(&tbuf[2], value);
short2usb(&tbuf[4], index);
Request.EndpointAddress = 0; /* Control pipe */
Request.Buffer = tbuf;
Request.BufferLength = tsize;
Request.TransferType = ControlTransferType;
memset(&olr, 0, sizeof(struct usbdk_olr));
if ((olr.olaps.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
return ICOM_SYS;
add_olr(p, &olr);
if (dirw) {
result = UsbDk_WritePipe(p->usbd->handle, &Request, &olr.olaps);
} else {
result = UsbDk_ReadPipe(p->usbd->handle, &Request, &olr.olaps);
}
if (result == TransferFailure) {
CloseHandle(olr.olaps.hEvent);
return ICOM_USBW;
}
/* Hmm. If we don't do this, the UsbDk driver locks up on */
/* this device, and the system needs a re-boot. */
if (!GetOverlappedResult(p->usbd->syshandle, &olr.olaps, &xlength, TRUE)) {
CloseHandle(olr.olaps.hEvent);
olr.olaps.hEvent = NULL;
rem_olr(p, &olr);
return ICOM_USBR;
}
CloseHandle(olr.olaps.hEvent);
olr.olaps.hEvent = NULL;
rem_olr(p, &olr);
/* xlength doesn't seem to correspond with what UsbDk did, BytesTransferred does. */
retsz = (int)Request.Result.GenResult.BytesTransferred;
if (!dirw && retsz > 0) {
memcpy(bytes, tbuf + 8, retsz); /* Copy read data */
}
if (transferred != NULL)
*transferred = retsz;
/* The requested size wasn't transferred */
if (rv == ICOM_OK && retsz != size)
rv |= ICOM_SHORT;
a1logd(p->log, 8, "icoms_usb_control_msg_dk: returning err 0x%x and %d bytes\n",rv, retsz);
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 */
/* achieve what we want. */
int icoms_usb_cancel_io_dk(
icoms *p,
usb_cancelt *cancelt
) {
int rv = ICOM_OK;
amutex_lock(cancelt->cmtx);
if (cancelt->hcancel != NULL) {
int ep = *((unsigned char *)cancelt->hcancel);
if (!UsbDk_AbortPipe(p->usbd->handle, (ULONG64)ep)) {
a1logd(p->log, 1, "icoms_usb_cancel_io_dk failed\n");
rv = ICOM_SYS;
}
}
amutex_unlock(cancelt->cmtx);
return rv;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Reset and end point data toggle to 0 */
int icoms_usb_resetep_dk(
icoms *p,
int ep /* End point address */
) {
if (!UsbDk_ResetPipe(p->usbd->handle, (ULONG64)ep)) {
a1logd(p->log, 1, "icoms_usb_resetep_dk failed\n");
return ICOM_SYS;
}
return ICOM_OK;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Clear a halt on an end point */
/* (Actually does a resetep) */
int icoms_usb_clearhalt_dk(
icoms *p,
int ep /* End point address */
) {
if (!UsbDk_ResetPipe(p->usbd->handle, (ULONG64)ep)) {
a1logd(p->log, 1, "icoms_resetpipe/clearhalt_dk failed\n");
return ICOM_SYS;
}
return ICOM_OK;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - */