/*
 * Include files needed for WDM driver support
 */
#include <wdm.h>
#include "stdarg.h"
#include "stdio.h"

/*
 * Include files needed for USB support
 */
#include "usbdi.h"
#include "usbdlib.h"

/*
 * Include file for the baycomusb Device
 */
#include "bcusb.h"

#include "usbdevio.h"

#if 0
#define VLOG(x)  do { x; } while (0)
#else
#define VLOG(x)  do {  } while (0)
#endif

#if 1
#define ELOG(x)  do { x; } while (0)
#else
#define ELOG(x)  do {  } while (0)
#endif

/*
 * Helper structures for synchronous calls to USBD with timeout
 */

#define USBDEVFS_INTERNAL_CONTROL      CTL_CODE(FILE_DEVICE_USBDEVIO,256,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define USBDEVFS_INTERNAL_BULK         CTL_CODE(FILE_DEVICE_USBDEVIO,257,METHOD_BUFFERED,FILE_ANY_ACCESS)

struct internal_ctrlbulk {
        unsigned int timeout; /* in milliseconds */
        PURB urb;
};


/*
 * Synchronous call to usbd; note: no timeout
 */

NTSTATUS call_usbd(PDEVICE_OBJECT fdo, PURB urb)
{
	NTSTATUS ret;
	pdabusb_t s;
	PIRP irp;
	KEVENT event;
	IO_STATUS_BLOCK ioStatus;
	PIO_STACK_LOCATION ns;
        
	s = fdo->DeviceExtension;
        if(s->device_state == _removed || s->CurrentDevicePowerState == PowerDeviceD3)
                return STATUS_DELETE_PENDING;
	KeInitializeEvent(&event, NotificationEvent, FALSE);
	irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB, s->sdo,
                                            NULL, 0, NULL, 0, TRUE, /* INTERNAL */
                                            &event, &ioStatus);
	ns = IoGetNextIrpStackLocation(irp);
	ASSERT(ns != NULL);
	ns->Parameters.Others.Argument1 = urb;
	ret = IoCallDriver(s->sdo, irp);
	if (ret == STATUS_PENDING) {
		KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
		ret = ioStatus.Status;
	}
	if(!NT_SUCCESS(ret) || !USBD_SUCCESS(urb->UrbHeader.Status))
		ELOG(printk(MODSTR"call_usbd: NT-Status: %08X URB-Status: %08X\n", ret, urb->UrbHeader.Status));
	return ret;
}

/*
 * Synchronous call to usbd; with timeout
 */

static NTSTATUS callusbdtimeoutcompletion(PDEVICE_OBJECT devobj, PIRP irp, PKEVENT event)
{
        KeSetEvent(event, 0, FALSE);
        return STATUS_MORE_PROCESSING_REQUIRED;
}

NTSTATUS call_usbd_timeout(PDEVICE_OBJECT fdo, PURB urb, unsigned int tmo)
{
	NTSTATUS ret;
	pdabusb_t s;
	PIRP irp;
	KEVENT event;
	IO_STATUS_BLOCK ioStatus;
	PIO_STACK_LOCATION ns;
        LARGE_INTEGER timeout;
        
	s = fdo->DeviceExtension;
        if(s->device_state == _removed || s->CurrentDevicePowerState == PowerDeviceD3)
                return STATUS_DELETE_PENDING;
	KeInitializeEvent(&event, NotificationEvent, FALSE);
        irp = IoAllocateIrp((CCHAR)(s->sdo->StackSize+1), FALSE);
        if (!irp)
                return STATUS_INSUFFICIENT_RESOURCES;
	ns = IoGetNextIrpStackLocation(irp);
        ns->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
        ns->Parameters.DeviceIoControl.OutputBufferLength = 0;
        ns->Parameters.DeviceIoControl.InputBufferLength = 0;
        ns->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
        irp->AssociatedIrp.SystemBuffer = NULL;
	ns->Parameters.Others.Argument1 = urb;
        IoSetCompletionRoutine(irp, callusbdtimeoutcompletion, &event, TRUE, TRUE, TRUE);
	ret = IoCallDriver(s->sdo, irp);
        if (!NT_SUCCESS(ret))
                goto out;
        if (ret != STATUS_PENDING)
                goto out;
        if (tmo)
                timeout.QuadPart = -10000 * (LONGLONG)tmo;
        ret = KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, tmo ? &timeout : NULL);
        if (ret == STATUS_TIMEOUT) {
                IoCancelIrp(irp);
                KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        }
        ret = irp->IoStatus.Status;
        /* STATUS_TIMEOUT is not an error condition... */
#if 0
        if (ret == STATUS_CANCELLED)
                ret = STATUS_TIMEOUT;
#endif
        
  out:
        IoFreeIrp(irp);
	if(!NT_SUCCESS(ret) || !USBD_SUCCESS(urb->UrbHeader.Status))
		ELOG(printk(MODSTR"call_usbd: NT-Status: %08X URB-Status: %08X\n", ret, urb->UrbHeader.Status));
	return ret;
}

/*
 * create URB's for control and bulk transfers
 */

static NTSTATUS make_control_urb(PURB *rurb, PDEVICE_OBJECT fdo, unsigned int flags, unsigned char requesttype, unsigned char request,
				 unsigned short value, unsigned short index, unsigned short length, void *data, PMDL mdl)
{
    	pdabusb_t s = fdo->DeviceExtension;
        USHORT func;
        PURB urb;
	unsigned int flg;

	*rurb = NULL;
        if (request == 6 && requesttype == 0x80 && value >= 1 && value <= 3) {
                if (!(urb = kmalloc(sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST))))
                        return STATUS_NO_MEMORY;
                UsbBuildGetDescriptorRequest(urb, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                             (value == 1) ? USB_DEVICE_DESCRIPTOR_TYPE :
                                             (value == 2) ? USB_CONFIGURATION_DESCRIPTOR_TYPE : USB_STRING_DESCRIPTOR_TYPE,
                                             index, 0, data, mdl, length, NULL);
		*rurb = urb;
		return STATUS_SUCCESS;
	}
        switch (requesttype & 0x60) {
        case 0x20:  /* CLASS */
                switch (requesttype & 0x1f) {
                case 0:
                        func = URB_FUNCTION_CLASS_DEVICE;
                        break;

                case 1:
                        func = URB_FUNCTION_CLASS_INTERFACE;
                        break;

                case 2:
                        func = URB_FUNCTION_CLASS_ENDPOINT;
                        break;

                case 3:
                        func = URB_FUNCTION_CLASS_OTHER;
                        break;

                default:
                        return STATUS_INVALID_PARAMETER;
                }
                break;

        case 0x40: /* VENDOR */
                switch (requesttype & 0x1f) {
                case 0:
                        func = URB_FUNCTION_VENDOR_DEVICE;
                        break;

                case 1:
                        func = URB_FUNCTION_VENDOR_INTERFACE;
                        break;

                case 2:
                        func = URB_FUNCTION_VENDOR_ENDPOINT;
                        break;

                case 3:
                        func = URB_FUNCTION_VENDOR_OTHER;
                        break;

                default:
                        return STATUS_INVALID_PARAMETER;
                }
                break;

        default:
                return STATUS_INVALID_PARAMETER;
        }
        if (!(urb = kmalloc(sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST))))
                return STATUS_NO_MEMORY;
	flg = ((requesttype & 0x80) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
	if (!(flags & USBDEVFS_URB_DISABLE_SPD))
		flg |= USBD_SHORT_TRANSFER_OK;
        UsbBuildVendorRequest(urb, func, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
                              flg, 0, request, value, index, data, mdl, length, NULL);
	*rurb = urb;
	return STATUS_SUCCESS;
}

static NTSTATUS make_bulk_urb(PURB *rurb, PDEVICE_OBJECT fdo, unsigned int flags, unsigned int ep, unsigned int dlen, void *data, PMDL mdl)
{
    	pdabusb_t s = fdo->DeviceExtension;
        PURB urb, urb2 = NULL;
        unsigned int maxpktsz = 0, flg;

	*rurb = NULL;
        if (ep & 0x80)
                ep = (ep & 15) | 16;
        else
                ep &= 15;
        if (!s->eptopipeh[ep])
                return STATUS_INVALID_PARAMETER;
#if 0
        /* URB link does not work by design, according to Microsoft */
        if (ep < 16)
                maxpktsz = s->epmaxpkt[ep];
        /* manually append NULL packet to conserve packet boundaries for BULK OUT */
        if ((maxpktsz > 0) && (dlen > 0) && !(dlen % maxpktsz)) {
                VLOG(printk(MODSTR"make_bulk_urb: add zero length URB\n"));
                if (!(urb2 = kmalloc(sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER))))
                        return STATUS_NO_MEMORY;
                UsbBuildInterruptOrBulkTransferRequest(urb2, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
                                                       s->eptopipeh[ep], data, mdl, 0, USBD_SHORT_TRANSFER_OK, NULL);
        }
#endif
        if (!(urb = kmalloc(sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER)))) {
                if (urb2)
                        kfree(urb2);
                return STATUS_NO_MEMORY;
        }
	flg = 0;
	if (!(flags & USBDEVFS_URB_DISABLE_SPD))
		flg |= USBD_SHORT_TRANSFER_OK;
        UsbBuildInterruptOrBulkTransferRequest(urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
                                               s->eptopipeh[ep], data, mdl, dlen, flg, urb2);
	*rurb = urb;
	return STATUS_SUCCESS;
}

static NTSTATUS make_iso_urb(PURB *rurb, PDEVICE_OBJECT fdo, struct usbdevfs_isotransfer *iso, void *data, PMDL mdl)
{
    	pdabusb_t s = fdo->DeviceExtension;
        PURB urb;
        unsigned int ep, sz, i;
        
	*rurb = NULL;
	if (!iso || !data)
		return STATUS_INVALID_PARAMETER;
	ep = iso->ep;
        if (ep & 0x80)
                ep = (ep & 15) | 16;
        else
                ep &= 15;
        if (!s->eptopipeh[ep])
                return STATUS_INVALID_PARAMETER;
	if (iso->number_of_packets < 1 || iso->number_of_packets > 256)
		return STATUS_INVALID_PARAMETER;
	sz = GET_ISO_URB_SIZE(iso->number_of_packets);
        if (!(urb = kmalloc(sz)))
                return STATUS_NO_MEMORY;
	memset(urb, 0, sz);
        urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER;
	urb->UrbIsochronousTransfer.Hdr.Length = (USHORT)sz;
        urb->UrbIsochronousTransfer.PipeHandle = s->eptopipeh[ep];
        urb->UrbIsochronousTransfer.TransferFlags = (ep & 0x10) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT;
        urb->UrbIsochronousTransfer.TransferBuffer = data;
        urb->UrbIsochronousTransfer.TransferBufferMDL = mdl;
        urb->UrbIsochronousTransfer.TransferBufferLength = iso->len;
        if (iso->flags & USBDEVFS_URB_ISO_ASAP)
		urb->UrbIsochronousTransfer.TransferFlags |= USBD_START_ISO_TRANSFER_ASAP;
        urb->UrbIsochronousTransfer.NumberOfPackets = iso->number_of_packets;
        urb->UrbIsochronousTransfer.UrbLink = NULL;
	urb->UrbIsochronousTransfer.StartFrame = iso->start_frame;
 
	for (i = 0; i < iso->number_of_packets; i++) {
		urb->UrbIsochronousTransfer.IsoPacket[i].Offset = iso->iso_frame_desc[i].offset;
		urb->UrbIsochronousTransfer.IsoPacket[i].Length = iso->iso_frame_desc[i].length;
	}
	*rurb = urb;
	return STATUS_SUCCESS;
}

static NTSTATUS make_resetep_urb(PURB *rurb, PDEVICE_OBJECT fdo, USHORT func, unsigned int ep)
{
    	pdabusb_t s = fdo->DeviceExtension;
        PURB urb;
        
	*rurb = NULL;
        if (ep & 0x80)
                ep = (ep & 15) | 16;
        else
                ep &= 15;
        if (!s->eptopipeh[ep])
                return STATUS_INVALID_PARAMETER;
        if (!(urb = kmalloc(sizeof(struct _URB_PIPE_REQUEST))))
                return STATUS_NO_MEMORY;
        RtlZeroMemory(urb, sizeof(struct _URB_PIPE_REQUEST));
        urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST);
        urb->UrbHeader.Function = func;
        urb->UrbPipeRequest.PipeHandle = s->eptopipeh[ep];
	*rurb = urb;
	return STATUS_SUCCESS;
}

/*
 * completion handler
 */

#define COMPL_CONTROL           0
#define COMPL_BULK              1
#define COMPL_ISO               2
#define COMPL_ERRRETURN         3
#define COMPL_INTERNALCTRL      4
#define COMPL_INTERNALBULK      5

struct completionstate {
        PDEVICE_OBJECT fdo;
	unsigned int ctype;
        KDPC timeout_dpc;
	KTIMER timeout_timer;
        PIRP irp;
        PURB urb;
        NTSTATUS status;
	unsigned int cookie;
	struct list_head cancel_list;
};

static void async_timeout(PKDPC dpc, PVOID dc, PVOID SystemArgument1, PVOID SystemArgument2)
{
        struct completionstate *st = (struct completionstate *)dc;
        PDEVICE_OBJECT fdo = st->fdo;
    	pdabusb_t s = fdo->DeviceExtension;

        if (!IoCancelIrp(st->irp))
		ELOG(printk(MODSTR"async_timeout: IoCancelIrp failed\n"));
        ELOG(printk(MODSTR"async_timeout: st 0x%08x ctype %d\n", (unsigned int)st, st->ctype));
}

static NTSTATUS async_complete(PDEVICE_OBJECT DevObj, PIRP irp, PVOID dc)
{
        struct completionstate *st = (struct completionstate *)dc;
        PDEVICE_OBJECT fdo = st->fdo;
    	pdabusb_t s = fdo->DeviceExtension;
	PURB urb = st->urb;
	NTSTATUS ret = STATUS_SUCCESS;
        struct usbdevfs_ctrltransfer *ctrl;
        struct usbdevfs_bulktransfer *bulk;
	struct usbdevfs_isotransfer *iso;
        unsigned int *uip;
	unsigned int i;
        KIRQL irql;

        KeCancelTimer(&st->timeout_timer);
	/* take out of cookie list */
	KeAcquireSpinLock(&s->cancel_list_lock, &irql);
	list_del(&st->cancel_list);
	INIT_LIST_HEAD(&st->cancel_list);
	KeReleaseSpinLock(&s->cancel_list_lock, irql);
#if 1
        VLOG(printk(MODSTR"async_complete: st 0x%08x ntstat 0x%08x, usbd stat: 0x%08x  len: %u\n",
                    (unsigned int)st, irp->IoStatus.Status, urb->UrbHeader.Status,
                    (st->ctype == COMPL_CONTROL) ? urb->UrbControlVendorClassRequest.TransferBufferLength :
                    (st->ctype == COMPL_BULK) ? urb->UrbBulkOrInterruptTransfer.TransferBufferLength : 0));
#else
        if (!NT_SUCCESS(irp->IoStatus.Status) || !USBD_SUCCESS(urb->UrbHeader.Status))
                ELOG(printk(MODSTR"async_complete: ntstat 0x%08x, usbd stat: 0x%08x\n",
                       irp->IoStatus.Status, urb->UrbHeader.Status));
#endif
        /* If the lower driver returned PENDING, mark our stack location as pending also. */
        if (irp->PendingReturned)
                IoMarkIrpPending(irp);
	/* completion type specific processing */
	irp->IoStatus.Information = 0;
	switch (st->ctype) {
	case COMPL_CONTROL:
                ctrl = (struct usbdevfs_ctrltransfer *)irp->AssociatedIrp.SystemBuffer;
                ctrl->usberr = urb->UrbHeader.Status;
                ctrl->len = urb->UrbControlVendorClassRequest.TransferBufferLength;
                irp->IoStatus.Information = sizeof(struct usbdevfs_ctrltransfer);
		if (NT_SUCCESS(irp->IoStatus.Status) && USBD_SUCCESS(urb->UrbHeader.Status)) {
                        if (ctrl->requesttype & 0x80)
                                irp->IoStatus.Information = sizeof(struct usbdevfs_ctrltransfer) + ctrl->len;
		} else {
			s->lastusberror = urb->UrbHeader.Status;
			ret = STATUS_UNSUCCESSFUL;
		}
		break;

	case COMPL_BULK:
                bulk = (struct usbdevfs_bulktransfer *)irp->AssociatedIrp.SystemBuffer;
                bulk->usberr = urb->UrbHeader.Status;
                bulk->len = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
                if (urb->UrbBulkOrInterruptTransfer.UrbLink)
                        kfree(urb->UrbBulkOrInterruptTransfer.UrbLink);
                urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
		if (NT_SUCCESS(irp->IoStatus.Status) && USBD_SUCCESS(urb->UrbHeader.Status)) {
                        if (bulk->ep & 0x80)
                                irp->IoStatus.Information = sizeof(struct usbdevfs_bulktransfer) + bulk->len;
		} else {
			s->lastusberror = urb->UrbHeader.Status;
			ret = STATUS_UNSUCCESSFUL;
		}
		break;

	case COMPL_ISO:
                iso = (struct usbdevfs_isotransfer *)irp->AssociatedIrp.SystemBuffer;
                iso->usberr = urb->UrbHeader.Status;
		iso->error_count = urb->UrbIsochronousTransfer.ErrorCount;
		for (i = 0; i < iso->number_of_packets; i++) {
			iso->iso_frame_desc[i].usberr = urb->UrbIsochronousTransfer.IsoPacket[i].Status;
			iso->iso_frame_desc[i].length = urb->UrbIsochronousTransfer.IsoPacket[i].Length;
		}
		if (urb->UrbIsochronousTransfer.UrbLink)
                        kfree(urb->UrbIsochronousTransfer.UrbLink);
                urb->UrbIsochronousTransfer.UrbLink = NULL;
		if (NT_SUCCESS(irp->IoStatus.Status) && USBD_SUCCESS(urb->UrbHeader.Status)) {
                        if (iso->ep & 0x80)
                                irp->IoStatus.Information = iso->len;
		} else {
			s->lastusberror = urb->UrbHeader.Status;
			ret = STATUS_UNSUCCESSFUL;
		}
		break;

	case COMPL_ERRRETURN:
                uip = (unsigned int *)irp->AssociatedIrp.SystemBuffer;
                *uip = urb->UrbHeader.Status;
                irp->IoStatus.Information = sizeof(unsigned int);
		if (!NT_SUCCESS(irp->IoStatus.Status) || !USBD_SUCCESS(urb->UrbHeader.Status)) {
			s->lastusberror = urb->UrbHeader.Status;
			ret = STATUS_UNSUCCESSFUL;
		}
		break;

        case COMPL_INTERNALCTRL:
                irp->IoStatus.Information = urb->UrbControlVendorClassRequest.TransferBufferLength;
		if (!NT_SUCCESS(irp->IoStatus.Status) || !USBD_SUCCESS(urb->UrbHeader.Status))
                        ret = STATUS_UNSUCCESSFUL;
                break;
                
        case COMPL_INTERNALBULK:
                irp->IoStatus.Information = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
		if (!NT_SUCCESS(irp->IoStatus.Status) || !USBD_SUCCESS(urb->UrbHeader.Status))
                        ret = STATUS_UNSUCCESSFUL;
                break;
                
	default:
		ELOG(printk(MODSTR"unknown completion type: %d\n", st->ctype));
		ret = STATUS_UNSUCCESSFUL;
	}
	irp->IoStatus.Status = ret;
	kfree(urb);
	kfree(st);
	dabusb_decrement_io_count(fdo);
        return (ret == STATUS_SUCCESS) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
};

static NTSTATUS async_usbd(PDEVICE_OBJECT fdo, PIRP irp, PURB urb, unsigned int tmo, unsigned int ctype, unsigned int cookie)
{
    	pdabusb_t s = fdo->DeviceExtension;
        struct completionstate *st = NULL;
	NTSTATUS ret;
	PIO_STACK_LOCATION ns;
        LARGE_INTEGER timeout;
	KIRQL irql;

        if(s->device_state == _removed || s->CurrentDevicePowerState == PowerDeviceD3) {
                ret = STATUS_DELETE_PENDING;
                goto out;
        }
        if (!urb) {
                ret = STATUS_INVALID_PARAMETER;
                goto out;
        }
        if (!(st = kmalloc(sizeof(struct completionstate)))) {
		ret = STATUS_INSUFFICIENT_RESOURCES;
		goto out;
	}
	dabusb_increment_io_count(fdo);
        /* set up callusbd state structure */
        st->fdo = fdo;
        st->urb = urb;
	st->irp = irp;
        st->ctype = ctype;
        KeInitializeDpc(&st->timeout_dpc, async_timeout, st);
        KeInitializeTimerEx(&st->timeout_timer, NotificationTimer);
	INIT_LIST_HEAD(&st->cancel_list);
	st->cookie = cookie;
	/* setup next IRP location */
	ns = IoGetNextIrpStackLocation(irp);
	ASSERT(ns != NULL);
        ns->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
        ns->Parameters.Others.Argument1 = urb;
        ns->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
        IoSetCompletionRoutine(irp, async_complete, st, TRUE, TRUE, TRUE);
	/* set timer if requested */
        if (tmo) {
                timeout.QuadPart = -10000 * (LONGLONG)tmo;
		VLOG(printk(MODSTR"async_usbd: timeout %ums setting timeout %d\n", tmo, timeout.QuadPart));
                KeSetTimer(&st->timeout_timer, timeout, &st->timeout_dpc);
        }
	/* put into cookie list */
	KeAcquireSpinLock(&s->cancel_list_lock, &irql);
	list_add_tail(&st->cancel_list, &s->cancel_list);
	KeReleaseSpinLock(&s->cancel_list_lock, irql);
	ret = IoCallDriver(s->sdo, irp);
	if (ret == STATUS_PENDING)
		return ret;
	if (NT_SUCCESS(ret))
		VLOG(printk(MODSTR"async_usbd: IoCallDriver successfully returned 0x%08x\n", ret));
	else
		ELOG(printk(MODSTR"async_usbd: IoCallDriver error 0x%08x\n", ret));
	KeCancelTimer(&st->timeout_timer);
	KeAcquireSpinLock(&s->cancel_list_lock, &irql);
	list_del(&st->cancel_list);
	INIT_LIST_HEAD(&st->cancel_list);
	KeReleaseSpinLock(&s->cancel_list_lock, irql);
	kfree(urb);
	kfree(st);
	irp->IoStatus.Status = ret;
	irp->IoStatus.Information = 0;
	IoCompleteRequest(irp, IO_NO_INCREMENT); 
	dabusb_decrement_io_count(fdo);
	return ret;

  out:
        if (urb)
                kfree(urb);
	if (st)
		kfree(st);
	irp->IoStatus.Status = ret;
	irp->IoStatus.Information = 0;
	IoCompleteRequest(irp, IO_NO_INCREMENT);
	return ret;
}

static NTSTATUS async_cancel(PDEVICE_OBJECT fdo, unsigned int cookie)
{
    	pdabusb_t s = fdo->DeviceExtension;
        struct completionstate *st;
	struct list_head *list;
	KIRQL irql;
	PIRP irp;
	unsigned int killed = 0, killerr = 0;

	if (!cookie) {
	        VLOG(printk(MODSTR"async_cancel: invalid cookie %u\n", cookie));
		return STATUS_INVALID_PARAMETER;
	}
	for (;;) {
   	        irp = NULL;
		KeAcquireSpinLock(&s->cancel_list_lock, &irql);
		for (list = s->cancel_list.next; list != &s->cancel_list;) {
			st = list_entry(list, struct completionstate, cancel_list);
			list = list->next;
			if (st->cookie == cookie) {
				irp = st->irp;
				list_del(&st->cancel_list);
				INIT_LIST_HEAD(&st->cancel_list);
				break;
			}
		}
		KeReleaseSpinLock(&s->cancel_list_lock, irql);
		if (!irp)
			break;
		/* race with normal/timeout completion?? */
		if (IoCancelIrp(irp))
			killed++;
		else
			killerr++;
		VLOG(printk(MODSTR"async_cancel: found killable irp: %p killed: %u killerr: %u\n",
			    irp, killed, killerr));
	}
	if (killerr) {
	        VLOG(printk(MODSTR"async_cancel: unsuccessful\n"));
		return STATUS_UNSUCCESSFUL;
	}
	else if (killed) {
	        VLOG(printk(MODSTR"async_cancel: success\n"));
		return STATUS_SUCCESS;
	}
	VLOG(printk(MODSTR"async_cancel: no killable irps with cookie %u found\n", cookie));
	return STATUS_INVALID_PARAMETER_1;
}

NTSTATUS async_cancel_all(PDEVICE_OBJECT fdo)
{
    	pdabusb_t s = fdo->DeviceExtension;
        struct completionstate *st;
	struct list_head *list;
	KIRQL irql;
	PIRP irp;
	NTSTATUS ret = STATUS_SUCCESS;

	VLOG(printk(MODSTR"async_cancel_all: entering\n"));
	for (;;) {
		irp = NULL;
		KeAcquireSpinLock(&s->cancel_list_lock, &irql);
		list = s->cancel_list.next;
		if (list != &s->cancel_list) {
			st = list_entry(list, struct completionstate, cancel_list);
			list_del(&st->cancel_list);
			INIT_LIST_HEAD(&st->cancel_list);
			irp = st->irp;
		}
		KeReleaseSpinLock(&s->cancel_list_lock, irql);
		if (!irp)
			break;
		VLOG(printk(MODSTR"async_cancel_all: cancelling IRP 0x%08x\n", (unsigned long)irp));
		if (!IoCancelIrp(irp)) {
			ret = STATUS_UNSUCCESSFUL;
			ELOG(printk(MODSTR"async_cancel_all: IoCancelIrp failed\n"));
		}
	}
	VLOG(printk(MODSTR"async_cancel_all: leaving 0x%08x\n", ret));
	return ret;
}

/*
 * Synchronous calls
 */

int usb_control_msg(PDEVICE_OBJECT fdo, unsigned char requesttype, unsigned char request,
                    unsigned short value, unsigned short index, unsigned short length, void *data, unsigned int timeout)
{
    	pdabusb_t s = fdo->DeviceExtension;
        PURB urb;
        NTSTATUS ret;

	ret = make_control_urb(&urb, fdo, 0, requesttype, request, value, index, length, data, NULL);
	if (!NT_SUCCESS(ret))
		return ret;
        //ret = call_usbd(fdo, urb);
        ret = call_usbd_timeout(fdo, urb, timeout);
        if (NT_SUCCESS(ret)) {
                if (USBD_SUCCESS(urb->UrbHeader.Status))
                        ret = urb->UrbControlVendorClassRequest.TransferBufferLength;
                else
                        ret = STATUS_UNSUCCESSFUL;
        }
        kfree(urb);
        return ret;
}

int usb_bulk_msg(PDEVICE_OBJECT fdo, unsigned int ep, unsigned int dlen, void *data, unsigned int timeout)
{
    	pdabusb_t s = fdo->DeviceExtension;
        PURB urb;
        NTSTATUS ret;
        
	ret = make_bulk_urb(&urb, fdo, 0, ep, dlen, data, NULL);
	if (!NT_SUCCESS(ret))
		return ret;
        //ret = call_usbd(fdo, urb);
        ret = call_usbd_timeout(fdo, urb, timeout);
        if (NT_SUCCESS(ret)) {
                if (USBD_SUCCESS(urb->UrbHeader.Status))
                        ret = urb->UrbControlVendorClassRequest.TransferBufferLength;
                else
                        ret = STATUS_UNSUCCESSFUL;
        }
        kfree(urb);
        return ret;
}

NTSTATUS abort_pipes(PDEVICE_OBJECT fdo)
{
	pdabusb_t s = fdo->DeviceExtension;
        PURB urb;
	unsigned int i;
	NTSTATUS ret;

	VLOG(printk(MODSTR"abort_pipes: enter\n"));
	if (!(urb = kmalloc(sizeof(struct _URB_PIPE_REQUEST)))) {
		ELOG(printk(MODSTR"abort_pipes: out of memory\n"));
                return STATUS_NO_MEMORY;
	}
	for (i = 0; i < 32; i++) {
		if (!s->eptopipeh[i])
			continue;
		VLOG(printk(MODSTR"abort_pipes: aborting pipe %u\n", i));
		RtlZeroMemory(urb, sizeof(struct _URB_PIPE_REQUEST));
		urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST);
		urb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
		urb->UrbPipeRequest.PipeHandle = s->eptopipeh[i];
		ret = call_usbd_timeout(fdo, urb, 100);
		if (!NT_SUCCESS(ret) || !USBD_SUCCESS(urb->UrbHeader.Status))
			ELOG(printk(MODSTR"abort_pipes: pipe %u abort error 0x%08x, 0x%08x\n", 
				    ret, urb->UrbHeader.Status));
		RtlZeroMemory(urb, sizeof(struct _URB_PIPE_REQUEST));
		urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST);
		urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
		urb->UrbPipeRequest.PipeHandle = s->eptopipeh[i];
		ret = call_usbd_timeout(fdo, urb, 100);
		if (!NT_SUCCESS(ret) || !USBD_SUCCESS(urb->UrbHeader.Status))
			ELOG(printk(MODSTR"abort_pipes: pipe %u reset error 0x%08x, 0x%08x\n", 
				    ret, urb->UrbHeader.Status));
	}
	kfree(urb);
	VLOG(printk(MODSTR"abort_pipes: exit\n"));
	return STATUS_SUCCESS;
}

int usb_resetep(PDEVICE_OBJECT fdo, unsigned int ep)
{
    	pdabusb_t s = fdo->DeviceExtension;
        PURB urb;
        NTSTATUS ret;
        
        ret = make_resetep_urb(&urb, fdo, URB_FUNCTION_RESET_PIPE, ep);
	if (!NT_SUCCESS(ret))
		return ret;
        //ret = call_usbd(fdo, urb);
        ret = call_usbd_timeout(fdo, urb, 100);
        if (NT_SUCCESS(ret)) {
                if (USBD_SUCCESS(urb->UrbHeader.Status))
                        ret = urb->UrbControlVendorClassRequest.TransferBufferLength;
                else
                        ret = STATUS_UNSUCCESSFUL;
        }
        kfree(urb);
        return ret;
}

static PUSB_CONFIGURATION_DESCRIPTOR getcfgdesc(PDEVICE_OBJECT fdo, unsigned int index)
{
     	pdabusb_t s = fdo->DeviceExtension;
        PUSB_CONFIGURATION_DESCRIPTOR cfgdesc;
        PURB urb;
        NTSTATUS ret;
        unsigned int sz;

        if (!(urb = kmalloc(sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST))))
                return NULL;
        sz = sizeof(USB_CONFIGURATION_DESCRIPTOR) + 16;
        if (!(cfgdesc = kmalloc(sz))) {
                kfree(urb);
                return NULL;
        }
        UsbBuildGetDescriptorRequest(urb, sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                     USB_CONFIGURATION_DESCRIPTOR_TYPE, index, 0,
                                     cfgdesc, NULL,
                                     sizeof(USB_CONFIGURATION_DESCRIPTOR), /* Get only the configuration descriptor */
                                     NULL);
        //ret = call_usbd(fdo, urb);
        ret = call_usbd_timeout(fdo, urb, 100);
        if (!NT_SUCCESS(ret)) {
                kfree(cfgdesc);
                kfree(urb);
                return NULL;
        }
        sz = cfgdesc->wTotalLength + 16;
        kfree(cfgdesc);
        if (!(cfgdesc = kmalloc(sz))) {
                kfree(urb);
                return NULL;
        }
        UsbBuildGetDescriptorRequest(urb, sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                     USB_CONFIGURATION_DESCRIPTOR_TYPE, index, 0,
                                     cfgdesc, NULL, sz, NULL);
        //ret = call_usbd(fdo, urb);
        ret = call_usbd_timeout(fdo, urb, 100);
        if (!NT_SUCCESS(ret)) {
                kfree(cfgdesc);
                kfree(urb);
                return NULL;
        }
        kfree(urb);
        return cfgdesc;
}


int usb_setconfiguration(PDEVICE_OBJECT fdo, unsigned int configuration)
{
    	pdabusb_t s = fdo->DeviceExtension;
        PUSB_CONFIGURATION_DESCRIPTOR cfgdesc;
        USBD_INTERFACE_LIST_ENTRY iflist;
        PURB urb;
        NTSTATUS ret;
        int ret2;
        unsigned int index;

	for (index = 0; index < 256; index++) {
		cfgdesc = getcfgdesc(fdo, index);
		if (!cfgdesc)
			break;
		if (cfgdesc->bConfigurationValue == configuration)
			break;
		kfree(cfgdesc);
		cfgdesc = NULL;
	}
	if (!cfgdesc) {
		ELOG(printk(MODSTR"failure to get configuration descriptor\n"));
		return STATUS_INVALID_PARAMETER;
	}
	abort_pipes(fdo);
        memset(&s->eptopipeh, 0, sizeof(s->eptopipeh));
        memset(&s->epmaxpkt, 0, sizeof(s->epmaxpkt));
        kfree(cfgdesc);
        return STATUS_SUCCESS;
}

int usb_setinterface(PDEVICE_OBJECT fdo, unsigned int intf, unsigned int altsetting)
{
     	pdabusb_t s = fdo->DeviceExtension;
        PURB urb;
        PUSB_CONFIGURATION_DESCRIPTOR cfgdesc;
        PUSBD_INTERFACE_INFORMATION ifobj;
        USBD_INTERFACE_LIST_ENTRY iflist[2];
        NTSTATUS ret;
        unsigned int i, j, ep;

	abort_pipes(fdo);
        memset(&s->eptopipeh, 0, sizeof(s->eptopipeh));
        memset(&s->epmaxpkt, 0, sizeof(s->epmaxpkt));
        cfgdesc = getcfgdesc(fdo, s->cfgindex);
        if (!cfgdesc) {
                ELOG(printk(MODSTR"failure to get configuration descriptor\n"));
                return STATUS_INVALID_PARAMETER;
        }
        iflist[0].InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(cfgdesc, cfgdesc, intf, altsetting, -1, -1, -1);
        if (!iflist[0].InterfaceDescriptor) {
                ELOG(printk(MODSTR"invalid interface %u altsetting %u\n", intf, altsetting));
                kfree(cfgdesc);
                return STATUS_INVALID_PARAMETER;
        }
        iflist[1].InterfaceDescriptor = NULL;
        iflist[1].Interface = NULL;
        urb = USBD_CreateConfigurationRequestEx(cfgdesc, &iflist[0]);
        if (!urb) {
                ELOG(printk(MODSTR "USBD_CreateConfigurationRequestEx failed\n"));
                kfree(cfgdesc);
                return STATUS_INVALID_PARAMETER;
        }
        //ret = call_usbd(fdo, urb);
        ret = call_usbd_timeout(fdo, urb, 100);
        if (!NT_SUCCESS(ret) || !USBD_SUCCESS(urb->UrbHeader.Status)) {
                kfree(cfgdesc);
                kfree(urb);
                ELOG(printk(MODSTR"Error in configuration request: ntstatus %d urb status %d\n", ret, urb->UrbHeader.Status));
                return NT_SUCCESS(ret) ? STATUS_UNSUCCESSFUL : ret;
        }
        ifobj = (PUSBD_INTERFACE_INFORMATION)(&(urb->UrbSelectConfiguration.Interface));
        /* save in our EP->pipehandle translation table */
        for (i = 0; i < ifobj->NumberOfPipes; i++) {
                ep = ifobj->Pipes[i].EndpointAddress & 15;
                if (ifobj->Pipes[i].EndpointAddress & 0x80)
                        ep |= 16;
                else
                        s->epmaxpkt[ep] = ifobj->Pipes[i].MaximumPacketSize;
                s->eptopipeh[ep] = ifobj->Pipes[i].PipeHandle;
        }
        kfree(cfgdesc);
        kfree(urb);
        return STATUS_SUCCESS;
}

int usb_claiminterface(PDEVICE_OBJECT fdo, unsigned int intf)
{
        return STATUS_SUCCESS;
}

int usb_releaseinterface(PDEVICE_OBJECT fdo, unsigned int intf)
{
        return STATUS_SUCCESS;
}

NTSTATUS baycomusb_internal_ioctl(IN PDEVICE_OBJECT fdo, IN PIRP irp)
{
     	pdabusb_t s = fdo->DeviceExtension;
        NTSTATUS ret = STATUS_INVALID_PARAMETER;
        PIO_STACK_LOCATION irpSp;
        struct internal_ctrlbulk *cb;
        
        irpSp = IoGetCurrentIrpStackLocation(irp);
        VLOG(printk(MODSTR"ioctlinternal>>>> code 0x%08x\n", irpSp->Parameters.DeviceIoControl.IoControlCode));
        switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
        case USBDEVFS_INTERNAL_CONTROL:
                cb = irpSp->Parameters.Others.Argument1;
                ret = async_usbd(fdo, irp, cb->urb, cb->timeout, COMPL_INTERNALCTRL, 0);
                break;
                
        case USBDEVFS_INTERNAL_BULK:
                cb = irpSp->Parameters.Others.Argument1;
                ret = async_usbd(fdo, irp, cb->urb, cb->timeout, COMPL_INTERNALBULK, 0);
                break;
                
        default:
                return baycomusb_ioctl(fdo, irp);
        }
        VLOG(printk(MODSTR"ioctlinternal<<<< async: status 0x%08x retstatus 0x%08x info %d  usberr 0x%08x\n", irp->IoStatus.Status, ret, irp->IoStatus.Information, s->lastusberror));
        return ret;        
}

NTSTATUS baycomusb_ioctl(IN PDEVICE_OBJECT fdo, IN PIRP irp)
{
     	pdabusb_t s = fdo->DeviceExtension;
        NTSTATUS ret = STATUS_INVALID_PARAMETER;
        PIO_STACK_LOCATION irpSp;
        struct usbdevfs_ctrltransfer *ctrl;
        struct usbdevfs_bulktransfer *bulk;
	struct usbdevfs_isotransfer *iso;
        struct usbdevfs_setinterface *setif;
        struct usbdevfs_setconfig *setcfg;
        struct usbdevfs_resetep *rstep;
	PURB urb = NULL;
        PULONG ulptr;
	unsigned int i, j, k;

        irpSp = IoGetCurrentIrpStackLocation(irp);
        VLOG(printk(MODSTR"ioctl>>>> code 0x%08x\n", irpSp->Parameters.DeviceIoControl.IoControlCode));
        switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
        case USBDEVFS_CONTROL:
                ctrl = (struct usbdevfs_ctrltransfer *)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_ctrltransfer) ||
                    irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(struct usbdevfs_ctrltransfer) ||
                    ((ctrl->requesttype & 0x80) && irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(struct usbdevfs_ctrltransfer) + ctrl->len) ||
                    (!(ctrl->requesttype & 0x80) && irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_ctrltransfer) + ctrl->len))
                        goto outseterr;
                VLOG(printk(MODSTR"     >>>> rq 0x%02x rqt 0x%02x val 0x%04x idx 0x%04x len %u tmo %u flg 0x%x\n",
                            ctrl->request, ctrl->requesttype, ctrl->value, ctrl->index, ctrl->len, ctrl->timeout, ctrl->flags));
		/* async version */
		ret = make_control_urb(&urb, fdo, ctrl->flags, ctrl->requesttype, ctrl->request, ctrl->value, ctrl->index, (unsigned short)ctrl->len, (void *)(ctrl + 1), NULL);
		if (!NT_SUCCESS(ret))
			goto outseterr;
		ret = async_usbd(fdo, irp, urb, ctrl->timeout, COMPL_CONTROL, ctrl->cookie);
		goto outasyncok;

        case USBDEVFS_BULK:
                bulk = (struct usbdevfs_bulktransfer *)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_bulktransfer) ||
                    irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(struct usbdevfs_bulktransfer) ||
                    ((bulk->ep & 0x80) && irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(struct usbdevfs_bulktransfer) + bulk->len) ||
                    (!(bulk->ep & 0x80) && irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_bulktransfer) + bulk->len))
                        goto outseterr;
                VLOG(printk(MODSTR"     >>>> ep 0x%02x len %u tmo %u flg 0x%x\n",
			    bulk->ep, bulk->len, bulk->timeout, bulk->flags));
		/* async version */
		ret = make_bulk_urb(&urb, fdo, bulk->flags, bulk->ep, bulk->len, (void *)(bulk + 1), NULL);
		if (!NT_SUCCESS(ret))
			goto outseterr;
		ret = async_usbd(fdo, irp, urb, bulk->timeout, COMPL_BULK, bulk->cookie);
		goto outasyncok;

        case USBDEVFS_ISO:
                iso = (struct usbdevfs_isotransfer *)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_isotransfer) ||
                    irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(struct usbdevfs_isotransfer))
			goto outseterr;
		i = sizeof(struct usbdevfs_isotransfer) + iso->number_of_packets * sizeof(struct usbdevfs_isopacketdesc);
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < i ||
                    irpSp->Parameters.DeviceIoControl.OutputBufferLength < i)
			goto outseterr;
		if (iso->len > irpSp->Parameters.DeviceIoControl.InputBufferLength ||
		    iso->len > irpSp->Parameters.DeviceIoControl.OutputBufferLength)
			goto outseterr;
		for (i = j = 0; j < iso->number_of_packets; j++) {
			k = iso->iso_frame_desc[j].offset + iso->iso_frame_desc[j].length;
			if (k < iso->iso_frame_desc[j].offset)
				goto outseterr;
			if (i < k)
				i = k;
		}
		if (i > iso->len)
			goto outseterr;
                VLOG(printk(MODSTR"     >>>> ep 0x%02x len %u flg 0x%x\n", iso->ep, i, iso->flags));
		/* async version */
		ret = make_iso_urb(&urb, fdo, iso, iso, NULL);
		if (!NT_SUCCESS(ret))
			goto outseterr;
		ret = async_usbd(fdo, irp, urb, iso->timeout, COMPL_ISO, iso->cookie);
		goto outasyncok;

        case USBDEVFS_RESETEP:
                rstep = (struct usbdevfs_resetep *)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_resetep) ||
                    irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(unsigned int))
                        goto outseterr;
                VLOG(printk(MODSTR"     >>>> ep 0x%02x\n", rstep->ep));
		/* async version */
		ret = make_resetep_urb(&urb, fdo, URB_FUNCTION_RESET_PIPE, rstep->ep);
		if (!NT_SUCCESS(ret))
			goto outseterr;
		ret = async_usbd(fdo, irp, urb, 1000, COMPL_ERRRETURN, 0);
		goto outasyncok;

        case USBDEVFS_ABORTEP:
                rstep = (struct usbdevfs_resetep *)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_resetep) ||
                    irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(unsigned int))
                        goto outseterr;
                VLOG(printk(MODSTR"     >>>> ep 0x%02x\n", rstep->ep));
		/* async version */
		ret = make_resetep_urb(&urb, fdo, URB_FUNCTION_ABORT_PIPE, rstep->ep);
		if (!NT_SUCCESS(ret))
			goto outseterr;
		ret = async_usbd(fdo, irp, urb, 1000, COMPL_ERRRETURN, 0);
		goto outasyncok;

        case USBDEVFS_SETINTERFACE:
                setif = (struct usbdevfs_setinterface *)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_setinterface)) {
                        ret = STATUS_INVALID_PARAMETER;
                        break;
                }
                VLOG(printk(MODSTR"     >>>> interface %u altsetting %u\n", setif->interf, setif->altsetting));
                ret = usb_setinterface(fdo, setif->interf, setif->altsetting);
                if (!NT_SUCCESS(ret))
			goto outseterr;
		irp->IoStatus.Status = STATUS_SUCCESS;
		irp->IoStatus.Information = ret;
		goto outcompl;

        case USBDEVFS_SETCONFIGURATION:
                setcfg = (struct usbdevfs_setconfig *)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct usbdevfs_setconfig)) {
                        ret = STATUS_INVALID_PARAMETER;
                        break;
                }
                ret = usb_setconfiguration(fdo, setcfg->config);
                if (!NT_SUCCESS(ret))
			goto outseterr;
		irp->IoStatus.Status = STATUS_SUCCESS;
		irp->IoStatus.Information = ret;
		goto outcompl;
                
        case USBDEVFS_CLAIMINTERFACE:
                irp->IoStatus.Status = STATUS_SUCCESS;
                irp->IoStatus.Information = 0;
                goto outcompl;

        case USBDEVFS_RELEASEINTERFACE:
                irp->IoStatus.Status = STATUS_SUCCESS;
                irp->IoStatus.Information = 0;
                goto outcompl;

        case USBDEVFS_GETLASTERROR:
                ulptr = (PULONG)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*ulptr)) {
                        irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
                        irp->IoStatus.Information = 0;
                        goto outcompl;
                }
                *ulptr = s->lastusberror;
                irp->IoStatus.Status = STATUS_SUCCESS;
                irp->IoStatus.Information = sizeof(*ulptr);
                goto outcompl;

	case USBDEVFS_CANCELTRANSFER:
                ulptr = (PULONG)irp->AssociatedIrp.SystemBuffer;
                if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*ulptr)) {
                        irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
                        irp->IoStatus.Information = 0;
                        goto outcompl;
                }
                irp->IoStatus.Status = async_cancel(fdo, *ulptr);
                irp->IoStatus.Information = 0;
                goto outcompl;

        default:
                ret = STATUS_NOT_IMPLEMENTED;
		goto outseterr;
        }
        goto outseterr;

  outseterr:
	irp->IoStatus.Status = ret;
	irp->IoStatus.Information = 0;
  outcompl:
	IoCompleteRequest(irp, IO_NO_INCREMENT);
	VLOG(printk(MODSTR"ioctl<<<< status 0x%08x info %d  usberr 0x%08x\n", irp->IoStatus.Status, irp->IoStatus.Information, s->lastusberror));
	return irp->IoStatus.Status;

  outasyncok:
	VLOG(printk(MODSTR"ioctl<<<< async: status 0x%08x retstatus 0x%08x info %d  usberr 0x%08x\n", irp->IoStatus.Status, ret, irp->IoStatus.Information, s->lastusberror));
	return ret;
}
