/*****************************************************************************/

/*
 *	usbdrvwinasync.c  --  Windows USB driver interface using Baycomusb Driver, asynchronous.
 *
 *	Copyright (C) 1999-2001
 *          Thomas Sailer (t.sailer@alumni.ethz.ch)
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Please note that the GPL allows you to use the driver, NOT the radio.
 *  In order to use the radio, you need a license from the communications
 *  authority of your country.
 *
 *
 *  History:
 *   0.1  23.06.1999  Created
 *   0.2  16.07.2000  Adapted to new interface
 *
 */

/*****************************************************************************/

#include <windows.h>
//#include "wininc/winioctl.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include "sysdeps.h"

#include "usbdevio.h"
#include "list.h"

#define LOGCTRLBULK

/* --------------------------------------------------------------------- */

#define DEVICE_TYPE DWORD

#define FILE_DEVICE_UNKNOWN             0x00000022

#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)

#define METHOD_BUFFERED                 0
#define METHOD_IN_DIRECT                1
#define METHOD_OUT_DIRECT               2
#define METHOD_NEITHER                  3

#define FILE_ANY_ACCESS                 0
#define FILE_READ_ACCESS          (0x0001) 
#define FILE_WRITE_ACCESS         (0x0002)

#define USBD_STATUS  DWORD

/* --------------------------------------------------------------------- */

#define USBD_STATUS_CRC                      ((USBD_STATUS)0xC0000001L)
#define USBD_STATUS_BTSTUFF                  ((USBD_STATUS)0xC0000002L)
#define USBD_STATUS_DATA_TOGGLE_MISMATCH     ((USBD_STATUS)0xC0000003L)
#define USBD_STATUS_STALL_PID                ((USBD_STATUS)0xC0000004L)
#define USBD_STATUS_DEV_NOT_RESPONDING       ((USBD_STATUS)0xC0000005L)
#define USBD_STATUS_PID_CHECK_FAILURE        ((USBD_STATUS)0xC0000006L)
#define USBD_STATUS_UNEXPECTED_PID           ((USBD_STATUS)0xC0000007L)
#define USBD_STATUS_DATA_OVERRUN             ((USBD_STATUS)0xC0000008L)
#define USBD_STATUS_DATA_UNDERRUN            ((USBD_STATUS)0xC0000009L)
#define USBD_STATUS_RESERVED1                ((USBD_STATUS)0xC000000AL)
#define USBD_STATUS_RESERVED2                ((USBD_STATUS)0xC000000BL)
#define USBD_STATUS_BUFFER_OVERRUN           ((USBD_STATUS)0xC000000CL)
#define USBD_STATUS_BUFFER_UNDERRUN          ((USBD_STATUS)0xC000000DL)
#define USBD_STATUS_NOT_ACCESSED             ((USBD_STATUS)0xC000000FL)
#define USBD_STATUS_FIFO                     ((USBD_STATUS)0xC0000010L)
#define USBD_STATUS_ENDPOINT_HALTED          ((USBD_STATUS)0xC0000030L)
#define USBD_STATUS_NO_MEMORY                ((USBD_STATUS)0x80000100L)
#define USBD_STATUS_INVALID_URB_FUNCTION     ((USBD_STATUS)0x80000200L)
#define USBD_STATUS_INVALID_PARAMETER        ((USBD_STATUS)0x80000300L)
#define USBD_STATUS_ERROR_BUSY               ((USBD_STATUS)0x80000400L)
#define USBD_STATUS_REQUEST_FAILED           ((USBD_STATUS)0x80000500L)
#define USBD_STATUS_INVALID_PIPE_HANDLE      ((USBD_STATUS)0x80000600L)
#define USBD_STATUS_NO_BANDWIDTH             ((USBD_STATUS)0x80000700L)
#define USBD_STATUS_INTERNAL_HC_ERROR        ((USBD_STATUS)0x80000800L)
#define USBD_STATUS_ERROR_SHORT_TRANSFER     ((USBD_STATUS)0x80000900L)
#define USBD_STATUS_BAD_START_FRAME          ((USBD_STATUS)0xC0000A00L)
#define USBD_STATUS_ISOCH_REQUEST_FAILED     ((USBD_STATUS)0xC0000B00L)
#define USBD_STATUS_FRAME_CONTROL_OWNED      ((USBD_STATUS)0xC0000C00L)
#define USBD_STATUS_FRAME_CONTROL_NOT_OWNED  ((USBD_STATUS)0xC0000D00L)
#define USBD_STATUS_CANCELED                 ((USBD_STATUS)0x00010000L)
#define USBD_STATUS_CANCELING                ((USBD_STATUS)0x00020000L)

/* --------------------------------------------------------------------- */

#define NTSTATUS DWORD

#ifndef HasOverlappedIoCompleted
#define HasOverlappedIoCompleted(lpOverlapped) \
    ((lpOverlapped)->Internal != STATUS_PENDING)
#endif

#ifndef STATUS_INVALID_PARAMETER
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
#endif

#ifndef STATUS_INVALID_PARAMETER_1
#define STATUS_INVALID_PARAMETER_1 ((NTSTATUS)0xC00000EFL)
#endif

/* --------------------------------------------------------------------- */

#include "usbdrv.h"

struct usbdevice {
	HANDLE dev;
	HANDLE ioevent;
	CRITICAL_SECTION lock;
	struct list_head urbs;
};

const char usb_devicefs_mountpoint[256] = "\\\\.\\baycomusb";

struct async {
	struct list_head list;
	struct usbdevfs_urb *urb;
        unsigned char type;
	unsigned char ast;
	OVERLAPPED ol1, ol2;
};

#define AST_OL1PENDING  1
#define AST_OL2PENDING  2
#define AST_OL1ERROR    4
#define AST_OL2ERROR    8
#define AST_KILLED     16

struct async_bulk {
	struct async head;
	struct usbdevfs_bulktransfer bulk2;
	struct usbdevfs_bulktransfer bulk;
	/* data */
};

struct async_iso {
	struct async head;
	struct usbdevfs_isotransfer iso;
	/* data */
};

struct async_ctrl {
	struct async head;
	struct usbdevfs_ctrltransfer ctrl;
	/* data */
};


/* --------------------------------------------------------------------- */

extern int snprintf(char *str, size_t n, char const *fmt, ...);
extern int vsnprintf(char *str, size_t n, char const *fmt, va_list args);

/* --------------------------------------------------------------------- */

static void usberrprintf(struct usbdevice *dev, unsigned int usberr, const char *funcname, const char *format, ...)
{
	struct usbe {
		USBD_STATUS err;
		const char *str;
	} usbe[] = {
		{ USBD_STATUS_CRC, "CRC" },
		{ USBD_STATUS_BTSTUFF, "BTSTUFF" },
		{ USBD_STATUS_DATA_TOGGLE_MISMATCH, "DATA_TOGGLE_MISMATCH" },
		{ USBD_STATUS_STALL_PID, "STALL_PID" },
		{ USBD_STATUS_DEV_NOT_RESPONDING, "DEV_NOT_RESPONDING" },
		{ USBD_STATUS_PID_CHECK_FAILURE, "PID_CHECK_FAILURE" },
		{ USBD_STATUS_UNEXPECTED_PID, "UNEXPECTED_PID" },
		{ USBD_STATUS_DATA_OVERRUN, "DATA_OVERRUN" },
		{ USBD_STATUS_DATA_UNDERRUN, "DATA_UNDERRUN" },
		{ USBD_STATUS_RESERVED1, "RESERVED1" },
		{ USBD_STATUS_RESERVED2, "RESERVED2" },
		{ USBD_STATUS_BUFFER_OVERRUN, "BUFFER_OVERRUN" },
		{ USBD_STATUS_BUFFER_UNDERRUN, "BUFFER_UNDERRUN" },
		{ USBD_STATUS_NOT_ACCESSED, "NOT_ACCESSED" },
		{ USBD_STATUS_FIFO, "FIFO" },
		{ USBD_STATUS_ENDPOINT_HALTED, "ENDPOINT_HALTED" },
		{ USBD_STATUS_NO_MEMORY, "NO_MEMORY" },
		{ USBD_STATUS_INVALID_URB_FUNCTION, "INVALID_URB_FUNCTION" },
		{ USBD_STATUS_INVALID_PARAMETER, "INVALID_PARAMETER" },
		{ USBD_STATUS_ERROR_BUSY, "ERROR_BUSY" },
		{ USBD_STATUS_REQUEST_FAILED, "REQUEST_FAILED" },
		{ USBD_STATUS_INVALID_PIPE_HANDLE, "INVALID_PIPE_HANDLE" },
		{ USBD_STATUS_NO_BANDWIDTH, "NO_BANDWIDTH" },
		{ USBD_STATUS_INTERNAL_HC_ERROR, "INTERNAL_HC_ERROR" },
		{ USBD_STATUS_ERROR_SHORT_TRANSFER, "" },
		{ USBD_STATUS_BAD_START_FRAME, "BAD_START_FRAME" },
		{ USBD_STATUS_ISOCH_REQUEST_FAILED, "ISOCH_REQUEST_FAILED" },
		{ USBD_STATUS_FRAME_CONTROL_OWNED, "FRAME_CONTROL_OWNED" },
		{ USBD_STATUS_FRAME_CONTROL_NOT_OWNED, "FRAME_CONTROL_NOT_OWNED" },
		{ USBD_STATUS_CANCELED, "CANCELED" },
		{ USBD_STATUS_CANCELING, "CANCELING" },
		{ 0, "unknown" }
	};
	struct usbe *ep = usbe;
	char buf[256];
	va_list ap;
	const char *usberrst = "unknown";
        DWORD err;

	err = GetLastError();
	while (ep->err != 0 && ep->err != usberr)
		ep++;
	usberrst = ep->str;
	if (format) {
		va_start(ap, format);
		vsnprintf(buf, sizeof(buf), format, ap);
		va_end(ap);
		lprintf(2, "%s: (%s) error 0x%08lx usberror %s (0x%08x)\n", funcname, buf, err, usberrst, usberr);
	} else
		lprintf(2, "%s: error 0x%08lx usberror %s (0x%08x)\n", funcname, err, usberrst, usberr);
}

/* --------------------------------------------------------------------- */

/*
 * Parse and show the different USB descriptors.
 */
void usb_show_device_descriptor(FILE *f, struct usb_device_descriptor *desc)
{
        fprintf(f, "  Length              = %2d%s\n", desc->bLength,
		desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)");
        fprintf(f, "  DescriptorType      = %02x\n", desc->bDescriptorType);
        fprintf(f, "  USB version         = %x.%02x\n",
	       desc->bcdUSB[1], desc->bcdUSB[0]);
        fprintf(f, "  Vendor:Product      = %02x%02x:%02x%02x\n",
	       desc->idVendor[1], desc->idVendor[0], desc->idProduct[1], desc->idProduct[0]);
        fprintf(f, "  MaxPacketSize0      = %d\n", desc->bMaxPacketSize0);
        fprintf(f, "  NumConfigurations   = %d\n", desc->bNumConfigurations);
        fprintf(f, "  Device version      = %x.%02x\n",
		desc->bcdDevice[1], desc->bcdDevice[0]);
        fprintf(f, "  Device Class:SubClass:Protocol = %02x:%02x:%02x\n",
		desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol);
        switch (desc->bDeviceClass) {
        case 0:
                fprintf(f, "    Per-interface classes\n");
                break;
        case 9:
                fprintf(f, "    Hub device class\n");
                break;
        case 0xff:
                fprintf(f, "    Vendor class\n");
                break;
        default:
                fprintf(f, "    Unknown class\n");
        }
}

/* --------------------------------------------------------------------- */

void usb_setmountpoint(const char *mnt)
{
	if (!mnt)
		mnt = "\\\\.\\baycomusb";
	strncpy(usb_devicefs_mountpoint, mnt, sizeof(usb_devicefs_mountpoint));
}

const char *usb_getmountpoint(void)
{
	return usb_devicefs_mountpoint;
}

void usb_close(struct usbdevice *dev)
{
	struct list_head *list;
	struct async *async;

	if (!dev)
		return;
	if (!CancelIo(dev->dev)) {
		lprintf(1, "usb_close: CancelIO error %ld\n", GetLastError());
	}
	EnterCriticalSection(&dev->lock);
	for (list = dev->urbs.next; list != &dev->urbs;) {
		async = list_entry(list, struct async, list);
		list = list->next;
		if (((async->ast & AST_OL1PENDING) && !HasOverlappedIoCompleted(&async->ol1)) ||
		    ((async->ast & AST_OL2PENDING) && !HasOverlappedIoCompleted(&async->ol2))) {
			lprintf(1, "usb_close: IO not completed!\n");
		}
		list_del(&async->list);
		free(async);
	}
	LeaveCriticalSection(&dev->lock);
	CloseHandle(dev->dev);
	CloseHandle(dev->ioevent);
	DeleteCriticalSection(&dev->lock);
	free(dev);
#ifdef LOGCTRLBULK
	lprintf(20, "usb_close\n");
#endif /* LOGCTRLBULK */
}

struct usbdevice *usb_open_bynumber(unsigned int busnum, unsigned int devnum, int vendorid, int productid)
{
	return NULL;
}

struct usbdevice *usb_open(int vendorid, int productid, unsigned int timeout, unsigned int index)
{
	struct usbdevice *dev;
	HANDLE h = INVALID_HANDLE_VALUE;
	HANDLE hevent = INVALID_HANDLE_VALUE;
	char name[256];
        DWORD err;

	snprintf(name, sizeof(name), "%s%u", usb_devicefs_mountpoint, index);
	h = CreateFile(name, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	if (h == INVALID_HANDLE_VALUE) {
		err = GetLastError();
		lprintf(10, "usb_open: cannot open driver \"%s\" error %ld\n", name, err);
                errno = ENOENT;
		return NULL;
	}
	hevent = CreateEvent(NULL, TRUE, FALSE, NULL);  /* must be manual reset */
	if (!hevent) {
		CloseHandle(h);
		errno = ENOMEM;
		return NULL;
	}
      	if (!(dev = malloc(sizeof(struct usbdevice)))) {
		CloseHandle(h);
		CloseHandle(hevent);
                errno = ENOMEM;
		return NULL;
	}
	dev->dev = h;
	dev->ioevent = hevent;
	InitializeCriticalSection(&dev->lock);
	INIT_LIST_HEAD(&dev->urbs);
       	return dev;
}

int usb_control_msg(struct usbdevice *dev, unsigned char requesttype, unsigned char request,
		    unsigned short value, unsigned short index, unsigned short length, void *data, unsigned int timeout)
{
        struct usbdevfs_ctrltransfer *ctrl;
        unsigned int totlen = sizeof(struct usbdevfs_ctrltransfer) + length;
	OVERLAPPED ol;
	BOOLEAN res;
	DWORD val, err;
        int ret;
        
        if (!(ctrl = malloc(totlen))) {
                lprintf(0, "usb_control_msg: out of memory\n");
                errno = ENOMEM;
                return -1;
        }
	ctrl->cookie = 0;
	ctrl->flags = 0;
        ctrl->requesttype = requesttype;
        ctrl->request = request;
        ctrl->value = value;
        ctrl->index = index;
        ctrl->len = length;
        ctrl->timeout = timeout;
        if (!(requesttype & 0x80) && length > 0)
                memcpy((void *)(ctrl + 1), data, length);
	ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!ol.hEvent) {
                lprintf(0, "usb_control_msg: cannot create event\n");
		free(ctrl);
                errno = ENOMEM;
                return -1;
        }
	res = DeviceIoControl(dev->dev, USBDEVFS_CONTROL, ctrl, totlen, ctrl, totlen, &val, &ol);
	if (res != TRUE) {
		err = GetLastError();
		if (err == ERROR_IO_PENDING)
			res = GetOverlappedResult(dev->dev, &ol, &val, TRUE);
	}
	CloseHandle(ol.hEvent);
	if (res != TRUE) {
		usberrprintf(dev, ctrl->usberr, "usb_control", "rqt=0x%x rq=0x%x val=%u idx=%u len=%u", requesttype, request, value, index, length);
                free(ctrl);
                errno = EIO;
		return -1;
	}
        if ((requesttype & 0x80) && ctrl->len > 0)
                memcpy(data, (void *)(ctrl + 1), ctrl->len);
        ret = ctrl->len;
        free(ctrl);
#ifdef LOGCTRLBULK
       lprintf(20, "usb_control: rqt=0x%x rq=0x%x val=%u idx=%u len=%u ret=%d\n", requesttype, request, value, index, length, ret);
#endif /* LOGCTRLBULK */
	return ret;
}

int usb_bulk_msg(struct usbdevice *dev, unsigned int ep, unsigned int dlen, void *data, unsigned int timeout)
{
        struct usbdevfs_bulktransfer *bulk;
        unsigned int totlen = sizeof(struct usbdevfs_bulktransfer) + dlen;
	OVERLAPPED ol;
	BOOLEAN res;
	DWORD val, err;
        int ret, ret2;
        
        if (!(bulk = malloc(totlen))) {
                lprintf(0, "usb_bulk_msg: out of memory\n");
                errno = ENOMEM;
                return -1;
        }
	bulk->cookie = 0;
	bulk->flags = 0;
        bulk->ep = ep;
        bulk->len = dlen;
        bulk->timeout = timeout;
        if (!(ep & 0x80) && dlen > 0)
                memcpy((void *)(bulk + 1), data, dlen);
	ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!ol.hEvent) {
                lprintf(0, "usb_bulk_msg: cannot create event\n");
		free(bulk);
                errno = ENOMEM;
                return -1;
        }
	res = DeviceIoControl(dev->dev, USBDEVFS_BULK, bulk, totlen, bulk, totlen, &val, &ol);
	if (res != TRUE) {
		err = GetLastError();
		if (err == ERROR_IO_PENDING)
			res = GetOverlappedResult(dev->dev, &ol, &val, TRUE);
	}
	CloseHandle(ol.hEvent);
	if (res != TRUE) {
		usberrprintf(dev, bulk->usberr, "usb_bulk", "ep=0x%x len=%u", ep, dlen);
                free(bulk);
                errno = EIO;
		return -1;
	}
        if ((ep & 0x80) && bulk->len > 0)
                memcpy(data, (void *)(bulk + 1), bulk->len);
        ret = bulk->len;
        free(bulk);
        if (!(ep & 0x80) && dlen > 0 && ret == dlen && !(dlen & 0x3f)) {
                ret2 = usb_bulk_msg(dev, ep, 0, NULL, timeout);
                if (ret2 < 0)
                        ret = ret2;
        }
#ifdef LOGCTRLBULK
       lprintf(20, "usb_bulk: ep=0x%x len=%u ret=%d\n", ep, dlen, ret);
#endif /* LOGCTRLBULK */
        return ret;
}

int usb_resetep(struct usbdevice *dev, unsigned int ep)
{
        struct usbdevfs_resetep rstep;
	OVERLAPPED ol;
	DWORD val, err;
	BOOLEAN res;

	ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!ol.hEvent) {
                lprintf(0, "usb_resetep: cannot create event\n");
                errno = ENOMEM;
                return -1;
        }
        rstep.ep = ep;
 	res = DeviceIoControl(dev->dev, USBDEVFS_RESETEP, &rstep, sizeof(rstep), &rstep, sizeof(rstep), &val, &ol);
	if (res != TRUE) {
		err = GetLastError();
		if (err == ERROR_IO_PENDING)
			res = GetOverlappedResult(dev->dev, &ol, &val, TRUE);
	}
	CloseHandle(ol.hEvent);
	if (res != TRUE) {
		usberrprintf(dev, rstep.usberr, "usb_resetep", "ep=0x%x", ep);
                errno = EIO;
                return -1;
	}
#ifdef LOGCTRLBULK
	lprintf(20, "usb_resetep: ep=0x%x\n", ep);
#endif /* LOGCTRLBULK */
	return 0;
}

int usb_setconfiguration(struct usbdevice *dev, unsigned int configuration)
{
        struct usbdevfs_setconfig cfg;
	OVERLAPPED ol;
	DWORD val, err;
	BOOLEAN res;

	ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!ol.hEvent) {
                lprintf(0, "usb_setconfiguration: cannot create event\n");
                errno = ENOMEM;
                return -1;
        }
        cfg.config = configuration;
 	res = DeviceIoControl(dev->dev, USBDEVFS_SETCONFIGURATION, &cfg, sizeof(cfg), &cfg, sizeof(cfg), &val, &ol);
	if (res != TRUE) {
		err = GetLastError();
		if (err == ERROR_IO_PENDING)
			res = GetOverlappedResult(dev->dev, &ol, &val, TRUE);
	}
	CloseHandle(ol.hEvent);
	if (res != TRUE) {
		usberrprintf(dev, cfg.usberr, "usb_setconfiguration", "config=%d", configuration);
                errno = EIO;
		return -1;
	}
#ifdef LOGCTRLBULK
	lprintf(20, "usb_setconfiguration: config=%d\n", configuration);
#endif /* LOGCTRLBULK */
	return 0;
}

int usb_setinterface(struct usbdevice *dev, unsigned int intf, unsigned int altsetting)
{
        struct usbdevfs_setinterface setif;
	OVERLAPPED ol;
	BOOLEAN res;
	DWORD val, err;

	ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!ol.hEvent) {
                lprintf(0, "usb_setinterface: cannot create event\n");
                errno = ENOMEM;
                return -1;
        }
	setif.interf = intf;
	setif.altsetting = altsetting;
	res = DeviceIoControl(dev->dev, USBDEVFS_SETINTERFACE, &setif, sizeof(setif), &setif, sizeof(setif), &val, &ol);
	if (res != TRUE) {
		err = GetLastError();
		if (err == ERROR_IO_PENDING)
			res = GetOverlappedResult(dev->dev, &ol, &val, TRUE);
	}
	CloseHandle(ol.hEvent);
	if (res != TRUE) {
		usberrprintf(dev, setif.usberr, "usb_setinterface", "intf=%u altsetting=%u", intf, altsetting);
                errno = EIO;
		return -1;
	}
#ifdef LOGCTRLBULK
	lprintf(20, "usb_setinterface: intf=%u altsetting=%u\n", intf, altsetting);
#endif /* LOGCTRLBULK */
	return 0;
}

int usb_getdevicedescriptor(struct usbdevice *dev, struct usb_device_descriptor *desc)
{
	int r = usb_control_msg(dev, 0x80, 6, 1, 0, sizeof(*desc), desc, 0);
#if 1
	if (r == -1) {
		Sleep(100);
		r = usb_control_msg(dev, 0x80, 6, 1, 0, sizeof(*desc), desc, 0);
	}
#endif
	if (r < sizeof(*desc))
		return -1;
        return 0;
}

int usb_claiminterface(struct usbdevice *dev, unsigned int intf)
{
	return 0;
}

int usb_releaseinterface(struct usbdevice *dev, unsigned int intf)
{
	return 0;
}

int usb_discsignal(struct usbdevice *dev, unsigned int signr, void *context)
{
	return -1;
}

static int submit_bulk_urb(struct usbdevice *dev, struct usbdevfs_urb *urb)
{
	struct async_bulk *async;
        unsigned int totlen = sizeof(struct usbdevfs_bulktransfer) + urb->buffer_length;
        unsigned int asynclen = sizeof(struct async_bulk) + urb->buffer_length;
	BOOLEAN res;
	DWORD val, err;

	urb->status = 0;
	urb->actual_length = 0;
	urb->error_count = 0;
        if (!(async = malloc(asynclen))) {
                lprintf(0, "usb_submit_bulk_urb: out of memory\n");
                errno = ENOMEM;
		urb->status = -ENOMEM;
		urb->error_count = 1;
                return -1;
        }
	memset(async, 0, asynclen);
	async->head.urb = urb;
	async->head.type = USBDEVFS_URB_TYPE_BULK;
	async->head.ast = 0;
	async->head.ol1.hEvent = dev->ioevent;
	async->head.ol2.hEvent = dev->ioevent;
	async->bulk.cookie = (unsigned int)urb;
        async->bulk.ep = urb->endpoint;
        async->bulk.len = urb->buffer_length;
        async->bulk.timeout = 0;
	async->bulk.flags = urb->flags;
        if (!(async->bulk.ep & 0x80) && urb->buffer_length > 0)
                memcpy((void *)(&async->bulk + 1), urb->buffer, urb->buffer_length);
	res = DeviceIoControl(dev->dev, USBDEVFS_BULK, &async->bulk, totlen, &async->bulk, totlen, &val, &async->head.ol1);
	if (res != TRUE) {
		err = GetLastError();
		if (err != ERROR_IO_PENDING) {
			errno = EIO;
			goto err;
		}
		async->head.ast |= AST_OL1PENDING;
	}
	if (!(urb->endpoint & 0x80) && (urb->flags & USBDEVFS_URB_ZERO_PACKET) && 
	    urb->buffer_length > 0 && !(urb->buffer_length & 0x3f)) {
		async->bulk2.cookie = (unsigned int)urb;
		async->bulk2.ep = urb->endpoint;
		async->bulk2.len = 0;
		async->bulk2.timeout = 0;
		async->bulk2.flags = urb->flags;
		res = DeviceIoControl(dev->dev, USBDEVFS_BULK, &async->bulk2, sizeof(struct usbdevfs_bulktransfer), &async->bulk2, sizeof(struct usbdevfs_bulktransfer), &val, &async->head.ol2);
		if (res != TRUE) {
			err = GetLastError();
			if (err != ERROR_IO_PENDING)
				async->head.ast |= AST_OL2ERROR;
			else
				async->head.ast |= AST_OL2PENDING;
		}
	}
	EnterCriticalSection(&dev->lock);
	list_add(&async->head.list, &dev->urbs);
	if (!(async->head.ast & (AST_OL1PENDING|AST_OL2PENDING)))
		SetEvent(dev->ioevent);
	LeaveCriticalSection(&dev->lock);
#ifdef LOGCTRLBULK
	lprintf(20, "usb async bulk: ep=0x%x len=%u\n", async->bulk.ep, async->bulk.len);
#endif /* LOGCTRLBULK */
	return 0;

  err:
	urb->status = -errno;
	urb->error_count = 1;
	free(async);
#ifdef LOGCTRLBULK
	lprintf(20, "usb_submit_bulk_urb: DeviceIoControl error\n");
#endif /* LOGCTRLBULK */
	return -1;
}

static int submit_iso_urb(struct usbdevice *dev, struct usbdevfs_urb *urb)
{
	struct async_iso *async;
	unsigned int isopktlen = urb->number_of_packets * sizeof(struct usbdevfs_isopacketdesc);
	unsigned int isohdrlen = sizeof(struct usbdevfs_isotransfer) + isopktlen;
	unsigned int isolen = isohdrlen + urb->buffer_length;
        unsigned int asynclen = sizeof(struct async_iso) + isopktlen + urb->buffer_length;
	BOOLEAN res;
	DWORD val, err;
	unsigned int i, offs;
        
	urb->status = 0;
	urb->actual_length = 0;
	urb->error_count = 0;
        if (!(async = malloc(asynclen))) {
                lprintf(0, "usb_submit_iso_urb: out of memory\n");
                errno = ENOMEM;
		urb->status = -ENOMEM;
		urb->error_count = 1;
                return -1;
        }
	memset(async, 0, asynclen);
	async->head.urb = urb;
	async->head.type = USBDEVFS_URB_TYPE_ISO;
	async->head.ast = 0;
	async->head.ol1.hEvent = dev->ioevent;
	async->iso.cookie = (unsigned int)urb;
        async->iso.ep = urb->endpoint;
        async->iso.len = isolen;
        async->iso.timeout = 0;
	async->iso.flags = urb->flags;
	async->iso.start_frame = urb->start_frame;
	async->iso.number_of_packets = urb->number_of_packets;
	offs = isohdrlen;
	for (i = 0; i < urb->number_of_packets; i++) {
		async->iso.iso_frame_desc[i].offset = offs;
		offs += urb->iso_frame_desc[i].length;
		async->iso.iso_frame_desc[i].length = urb->iso_frame_desc[i].length;
	}
	if (offs > asynclen) {
		urb->status = -errno;
		urb->error_count = 1;
		free(async);
 #ifdef LOGCTRLBULK
		lprintf(20, "usb_submit_iso_urb: buffer_length too small\n");
#endif /* LOGCTRLBULK */
		return -1;
	}
	if (!(async->iso.ep & 0x80) && urb->buffer_length > 0)
                memcpy(((unsigned char *)&async->iso) + isohdrlen, urb->buffer, urb->buffer_length);
	res = DeviceIoControl(dev->dev, USBDEVFS_ISO, &async->iso, isolen, &async->iso, isolen, &val, &async->head.ol1);
	if (res != TRUE) {
		err = GetLastError();
		if (err != ERROR_IO_PENDING) {
			errno = EIO;
			goto err;
		}
		async->head.ast |= AST_OL1PENDING;
	}
	EnterCriticalSection(&dev->lock);
	list_add(&async->head.list, &dev->urbs);
	if (async->head.ast & AST_OL1PENDING)
		SetEvent(dev->ioevent);
	LeaveCriticalSection(&dev->lock);
#ifdef LOGCTRLBULK
	lprintf(20, "usb async iso: ep=0x%x len=%u\n", async->iso.ep, async->iso.len);
#endif /* LOGCTRLBULK */
	return 0;

  err:
	urb->status = -errno;
	urb->error_count = 1;
	free(async);
#ifdef LOGCTRLBULK
	lprintf(20, "usb_submit_iso_urb: DeviceIoControl error\n");
#endif /* LOGCTRLBULK */
	return -1;
}

static int submit_control_urb(struct usbdevice *dev, struct usbdevfs_urb *urb)
{
	struct async_ctrl *async;
	struct usb_control_request *creq = urb->buffer;
        unsigned int totlen = sizeof(struct usbdevfs_ctrltransfer) + urb->buffer_length - 8;
        unsigned int asynclen = sizeof(struct async_ctrl) + urb->buffer_length - 8;
	BOOLEAN res;
	DWORD val, err;

	urb->status = 0;
	urb->actual_length = 0;
	urb->error_count = 0;
        if (!(async = malloc(asynclen))) {
                lprintf(0, "usb_submit_control_urb: out of memory\n");
                errno = ENOMEM;
		urb->status = -ENOMEM;
		urb->error_count = 1;
                return -1;
        }
	memset(async, 0, asynclen);
	async->head.urb = urb;
	async->head.type = USBDEVFS_URB_TYPE_CONTROL;
	async->head.ast = 0;
	async->head.ol1.hEvent = dev->ioevent;
	async->head.ol2.hEvent = dev->ioevent;
	async->ctrl.cookie = (unsigned int)urb;
	async->ctrl.flags = urb->flags;
        async->ctrl.timeout = 0;
        async->ctrl.requesttype = creq->requesttype;
        async->ctrl.request = creq->request;
        async->ctrl.value = creq->value[0] | (creq->value[1] << 8);
        async->ctrl.index = creq->index[0] | (creq->index[1] << 8);
        async->ctrl.len = creq->length[0] | (creq->length[1] << 8);
        if (!(async->ctrl.requesttype & 0x80) && urb->buffer_length > 8)
                memcpy((void *)(&async->ctrl + 1), urb->buffer + 8, urb->buffer_length - 8);
	res = DeviceIoControl(dev->dev, USBDEVFS_CONTROL, &async->ctrl, totlen, &async->ctrl, totlen, &val, &async->head.ol1);
	if (res != TRUE) {
		err = GetLastError();
		if (err != ERROR_IO_PENDING) {
			errno = EIO;
			goto err;
		}
		async->head.ast |= AST_OL1PENDING;
	}
	EnterCriticalSection(&dev->lock);
	list_add(&async->head.list, &dev->urbs);
	if (!(async->head.ast & AST_OL1PENDING))
		SetEvent(dev->ioevent);
	LeaveCriticalSection(&dev->lock);
#ifdef LOGCTRLBULK
	lprintf(20, "usb async control: rqt=0x%x rq=0x%x val=%u idx=%u len=%u\n", 
		async->ctrl.requesttype, async->ctrl.request, async->ctrl.value, async->ctrl.index,
		async->ctrl.len);
#endif /* LOGCTRLBULK */
	return 0;

  err:
	urb->status = -errno;
	urb->error_count = 1;
	free(async);
#ifdef LOGCTRLBULK
	lprintf(20, "usb_submit_control_urb: DeviceIoControl error\n");
#endif /* LOGCTRLBULK */
	return -1;
}

int usb_submiturb(struct usbdevice *dev, struct usbdevfs_urb *urb)
{
	if (!dev || !urb)
		return -1;
	switch (urb->type) {
	case USBDEVFS_URB_TYPE_BULK:
		if (urb->buffer_length < 0 || urb->buffer_length > 65536)
			return -1;
		return submit_bulk_urb(dev, urb);

	case USBDEVFS_URB_TYPE_ISO:
		if (urb->buffer_length < 0 || urb->buffer_length > 65536 ||
		    urb->number_of_packets < 1 || urb->number_of_packets > 256)
			return -1;
		return submit_iso_urb(dev, urb);

	case USBDEVFS_URB_TYPE_CONTROL:
		if (urb->buffer_length < 8 || urb->buffer_length > 65536)
			return -1;
		return submit_control_urb(dev, urb);
	}
	return -1;
}

int usb_discardurb(struct usbdevice *dev, struct usbdevfs_urb *urb)
{
	struct list_head *list;
	struct async *async;
	OVERLAPPED ol;
	DWORD val, err;
	BOOLEAN res;
	ULONG cookie = (ULONG)urb;

	if (!dev)
		return -1;
	EnterCriticalSection(&dev->lock);
	for (list = dev->urbs.next; list != &dev->urbs;) {
		async = list_entry(list, struct async, list);
		list = list->next;
		if (async->urb == urb)
			async->ast |= AST_KILLED;
	}
	LeaveCriticalSection(&dev->lock);
	ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!ol.hEvent) {
                lprintf(0, "usb_resetep: cannot create event\n");
                errno = ENOMEM;
                return -1;
        }
 	res = DeviceIoControl(dev->dev, USBDEVFS_CANCELTRANSFER, &cookie, sizeof(cookie), &cookie, sizeof(cookie), &val, &ol);
	if (res != TRUE) {
		err = GetLastError();
		if (err == ERROR_IO_PENDING) {
			res = GetOverlappedResult(dev->dev, &ol, &val, TRUE);
			if (res != TRUE)
				err = GetLastError();
		}
	}
	CloseHandle(ol.hEvent);
	if (res != TRUE) {
		usberrprintf(dev, err, "usb_discardurb", "urb=%p", urb);
                errno = EIO;
		if (err == STATUS_INVALID_PARAMETER_1)
			errno = EINVAL;
                return -1;
	}
#ifdef LOGCTRLBULK
	lprintf(20, "usb_discardurb: urb=%p\n", urb);
#endif /* LOGCTRLBULK */
	return 0;
}

static inline void bulk_complete(struct usbdevice *dev, struct async *as)
{
	struct async_bulk *async = (struct async_bulk *)as;
	struct usbdevfs_urb *urb = async->head.urb;

	if (async->head.ast & (AST_OL1ERROR|AST_OL2ERROR)) {
		usberrprintf(dev, async->bulk.usberr ? async->bulk.usberr : async->bulk2.usberr, "usb_reapurb: bulk", "ep=0x%x", async->bulk.ep);
		urb->status = -EIO;
		urb->error_count = 1;
		return;
	}
        if ((async->bulk.ep & 0x80) && async->bulk.len > 0)
                memcpy(urb->buffer, (void *)(&async->bulk + 1), async->bulk.len);
        urb->actual_length = async->bulk.len;
#ifdef LOGCTRLBULK
	lprintf(20, "usb async bulk complete: ep=0x%x len=%u\n", async->bulk.ep, async->bulk.len);
#endif /* LOGCTRLBULK */
}

static inline void iso_complete(struct usbdevice *dev, struct async *as)
{
	struct async_iso *async = (struct async_iso *)as;
	struct usbdevfs_urb *urb = async->head.urb;
	unsigned int isohdrlen, i;

	isohdrlen = sizeof(struct usbdevfs_isotransfer) + 
		urb->number_of_packets * sizeof(struct usbdevfs_isopacketdesc);
	if (async->head.ast & AST_OL1ERROR) {
		usberrprintf(dev, async->iso.usberr, "usb_reapurb: iso", "ep=0x%x", async->iso.ep);
		urb->status = -EIO;
		urb->error_count = 1;
		return;
	}
        if ((async->iso.ep & 0x80) && async->iso.len > isohdrlen)
                memcpy(urb->buffer, ((unsigned char *)&async->iso) + isohdrlen, async->iso.len - isohdrlen);
        urb->actual_length = async->iso.len;
        urb->error_count = async->iso.error_count;
	for (i = 0; i < urb->number_of_packets; i++) {
		urb->iso_frame_desc[i].actual_length = async->iso.iso_frame_desc[i].length;
		urb->iso_frame_desc[i].status = 0;
		if (async->iso.iso_frame_desc[i].usberr)
			urb->iso_frame_desc[i].status = -EIO;
	}
#ifdef LOGCTRLBULK
	lprintf(20, "usb async iso complete: ep=0x%x len=%u\n", async->iso.ep, async->iso.len);
#endif /* LOGCTRLBULK */
}

static inline void ctrl_complete(struct usbdevice *dev, struct async *as)
{
	struct async_ctrl *async = (struct async_ctrl *)as;
	struct usbdevfs_urb *urb = async->head.urb;

	if (async->head.ast & AST_OL1ERROR) {
		usberrprintf(dev, async->ctrl.usberr, "usb_reapurb: ctrl", "rqt=0x%x rq=0x%x val=%u idx=%u len=%u", 
			     async->ctrl.requesttype, async->ctrl.request, async->ctrl.value, async->ctrl.index, async->ctrl.len);
		urb->status = -EIO;
		urb->error_count = 1;
		return;
	}
        if ((async->ctrl.requesttype & 0x80) && async->ctrl.len > 0)
                memcpy(urb->buffer + 8, (void *)(&async->ctrl + 1), async->ctrl.len);
        urb->actual_length = async->ctrl.len;
#ifdef LOGCTRLBULK
	lprintf(20, "usb async control complete: rqt=0x%x rq=0x%x val=%u idx=%u len=%u\n", 
		async->ctrl.requesttype, async->ctrl.request, async->ctrl.value, async->ctrl.index,
		async->ctrl.len);
#endif /* LOGCTRLBULK */
}

static struct usbdevfs_urb *usb_reapurb_nonblock(struct usbdevice *dev)
{
	struct list_head *list;
	struct async *async;
	struct usbdevfs_urb *urb = NULL;
	BOOLEAN res;
	DWORD val;

#ifdef LOGCTRLBULK
	lprintf(20, "usb_reapurb_nonblock:\n");
#endif /* LOGCTRLBULK */
	ResetEvent(dev->ioevent);
	EnterCriticalSection(&dev->lock);
	for (list = dev->urbs.next; list != &dev->urbs;) {
		async = list_entry(list, struct async, list);
		list = list->next;
		if ((async->ast & AST_OL1PENDING) && HasOverlappedIoCompleted(&async->ol1)) {
			async->ast &= ~AST_OL1PENDING;
			res = GetOverlappedResult(dev->dev, &async->ol1, &val, FALSE);
			if (!res)
				async->ast |= AST_OL1ERROR;
		}
		if ((async->ast & AST_OL2PENDING) && HasOverlappedIoCompleted(&async->ol2)) {
			async->ast &= ~AST_OL2PENDING;
			res = GetOverlappedResult(dev->dev, &async->ol2, &val, FALSE);
			if (!res)
				async->ast |= AST_OL2ERROR;
		}
#ifdef LOGCTRLBULK
		lprintf(20, "  async: ast 0x%02x type %u\n", async->ast, async->type);
#endif /* LOGCTRLBULK */
		if (async->ast & (AST_OL1PENDING|AST_OL2PENDING))
			continue;
		if (async->ast & AST_KILLED) {
			list_del(&async->list);
			free(async);
			continue;
		}			
		switch (async->type) {
		case USBDEVFS_URB_TYPE_BULK:
			bulk_complete(dev, async);
			break;
			
		case USBDEVFS_URB_TYPE_ISO:
			iso_complete(dev, async);
			break;
			
		case USBDEVFS_URB_TYPE_CONTROL:
			ctrl_complete(dev, async);
			break;
		}
		urb = async->urb;
		list_del(&async->list);
		free(async);
		break;
	}
	LeaveCriticalSection(&dev->lock);
#ifdef LOGCTRLBULK
	lprintf(20, "usb_reapurb_nonblock: return %p\n", urb);
#endif /* LOGCTRLBULK */
	return urb;
}

struct usbdevfs_urb *usb_reapurb(struct usbdevice *dev, int timeout)
{
	struct usbdevfs_urb *urb = NULL;
	SYSTEMTIME st;
	FILETIME ft;
	LARGE_INTEGER tm1, tm2;
	DWORD wt;
	LONGLONG diff;

	if (!dev)
		return NULL;
	if (timeout > 0) {
		GetSystemTime(&st);
		SystemTimeToFileTime(&st, &ft);
		tm1._STRUCT_NAME(u.)LowPart = ft.dwLowDateTime;
		tm1._STRUCT_NAME(u.)HighPart = ft.dwHighDateTime;
		tm1.QuadPart += 10000 * timeout;
	}
	for (;;) {
		urb = usb_reapurb_nonblock(dev);
		if (urb)
			return urb;
		if (timeout == 0)
			return NULL;
		else if (timeout < 0) {
			wt = INFINITE;
		} else {
			GetSystemTime(&st);
			SystemTimeToFileTime(&st, &ft);
			tm2._STRUCT_NAME(u.)LowPart = ft.dwLowDateTime;
			tm2._STRUCT_NAME(u.)HighPart = ft.dwHighDateTime;
			diff = tm1.QuadPart - tm2.QuadPart;
			if (diff <= 10000)
				return NULL;
			wt = diff / 10000;
		}
		WaitForSingleObject(dev->ioevent, wt);
	}
}

HANDLE usb_getfd(struct usbdevice *dev)
{
	return dev->ioevent;
}

/* ---------------------------------------------------------------------- */
