#include "RBC9-SpaceNav.h"
#pragma warning(disable : 4996)

NTSTATUS RBC9_usb_start(IN PDEVICE_OBJECT pDev_obj)
{
	NTSTATUS				Status;
	PDEVICE_EXTENSION		pDevExt;

	DBG_ENTER(("%p", pDev_obj));

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	/* initialize usb */
	Status = RBC9_usb_reset(pDev_obj);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: usb_reset failed", Status));
		return Status;
	}

	Status = RBC9_usb_get_dev_desc(pDev_obj);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: usb_get_dev_desc failed", Status));
		return Status;
	}

	Status = RBC9_usb_select_conf(pDev_obj);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: usb_select_conf failed", Status));
		return Status;
	}

	Status = RBC9_usb_get_hid_desc(pDev_obj);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: usb_get_hid_desc failed", Status));
		return Status;
	}

	/* ready to start */
	pDevExt->Pnp_State = DEVICE_STATE_STARTED;

	/* start usb worker */
	{
		HANDLE thread;

		/* create thread and start */
		pDevExt->usb_worker_thread_obj = NULL;
		Status = PsCreateSystemThread(&thread,
			THREAD_ALL_ACCESS, NULL, NULL, NULL,
			RBC9_usb_worker, pDev_obj);
		if(! NT_SUCCESS(Status)) {
			DBG_LEAVE(("%x: PsCreateSystemThread failed", Status));
			return Status;
		}

		ObReferenceObjectByHandle(thread, THREAD_ALL_ACCESS, NULL, KernelMode,
			&pDevExt->usb_worker_thread_obj, NULL);
	}


	/* initialize hid part */
	Status = RBC9_hid_start(pDev_obj);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: hid_start failed", Status));
		return Status;
	}

	DBG_LEAVE(("%x", Status));
	return STATUS_SUCCESS;
}

NTSTATUS RBC9_usb_remove(IN PDEVICE_OBJECT pDev_obj)
{

	PDEVICE_EXTENSION		pDevExt;


	DBG_ENTER(("%p", pDev_obj));

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);
	

	/* wait for usb worker stop */
	if(pDevExt->usb_worker_thread_obj != NULL) {
		KeWaitForSingleObject(pDevExt->usb_worker_thread_obj,
			Executive, KernelMode, FALSE, NULL);
		ObDereferenceObject(pDevExt->usb_worker_thread_obj);
		pDevExt->usb_worker_thread_obj = NULL;
	}
	/* finalize hid part */
	RBC9_hid_remove(pDev_obj);

	DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}


static NTSTATUS RBC9_usb_reset(IN PDEVICE_OBJECT pDev_obj)
{
	NTSTATUS Status;
	ULONG port_status;

	DBG_ENTER(("%p", pDev_obj));

	Status = RBC9_usb_get_port_status(pDev_obj, &port_status);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: usb_get_port_status failed", Status));
		return Status;
	}

	/* not connected */
	if((port_status & USBD_PORT_CONNECTED) == 0) {
		DBG_LEAVE(("STATUS_NO_SUCH_DEVICE"));
		return STATUS_NO_SUCH_DEVICE;
	}

	/* already enabled */
	if((port_status & USBD_PORT_ENABLED) != 0) {
		//DBG_LEAVE(("STATUS_SUCCESS"));
		return STATUS_SUCCESS;
	}

	/* reset port */
	Status = RBC9_usb_reset_port(pDev_obj);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: usb_reset_port failed", Status));
		return Status;
	}

	Status = RBC9_usb_get_port_status(pDev_obj, &port_status);
	if(! NT_SUCCESS(Status)) {
		DBG_LEAVE(("%x: usb_get_port_status failed", Status));
		return Status;
	}

	if((port_status & USBD_PORT_CONNECTED) == 0 ||
		(port_status & USBD_PORT_ENABLED) == 0) {
			DBG_LEAVE(("STATUS_NO_SUCH_DEVICE"));
			return STATUS_NO_SUCH_DEVICE;
	}

	DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}

static NTSTATUS RBC9_usb_get_port_status(IN PDEVICE_OBJECT pDev_obj,OUT ULONG *port_status)
{
	NTSTATUS Status;
//	DBG_ENTER(("%p, %u", pDev_obj, *port_status));

	Status = RBC9_usb_call(pDev_obj, IOCTL_INTERNAL_USB_GET_PORT_STATUS,port_status, NULL);

//	DBG_LEAVE(("%x", Status));
	return Status;
}

static NTSTATUS RBC9_usb_call(IN PDEVICE_OBJECT pDev_obj, IN ULONG ioctl_code, IN PVOID arg1, IN PVOID arg2)
{
	IO_STATUS_BLOCK io_status;
	PIRP pIrp;
	PIO_STACK_LOCATION pStack;
	KEVENT evt;
	NTSTATUS Status;

	//DBG_ENTER(("%p, %u, %p, %p", pDev_obj, ioctl_code, arg1, arg2));

	KeInitializeEvent(&evt, NotificationEvent, FALSE);

	pIrp = IoBuildDeviceIoControlRequest(ioctl_code,
		GET_LOWER_DEVICE_OBJECT(pDev_obj),
		NULL, 0,
		NULL, 0,
		TRUE,
		&evt, &io_status);

	pStack = IoGetNextIrpStackLocation(pIrp);
	pStack->Parameters.Others.Argument1 = arg1;
	pStack->Parameters.Others.Argument2 = arg2;

	Status = IoCallDriver(GET_LOWER_DEVICE_OBJECT(pDev_obj), pIrp);
	if(Status == STATUS_PENDING) {
		//DBG_OUT(("STATUS_PENDING"));
		KeWaitForSingleObject(&evt, Executive, KernelMode, FALSE, NULL);
		Status = io_status.Status;
	}

	//DBG_LEAVE(("%x", Status));
	return Status;
}

static NTSTATUS RBC9_usb_reset_port(IN PDEVICE_OBJECT pDev_obj)
{
	NTSTATUS Status;

	//DBG_ENTER(("%p", pDev_obj));

	Status = RBC9_usb_call(pDev_obj, IOCTL_INTERNAL_USB_RESET_PORT, NULL, NULL);

	//DBG_LEAVE(("%x", Status));
	return Status;
}



static NTSTATUS RBC9_usb_get_dev_desc(IN PDEVICE_OBJECT pDev_obj)
{
	NTSTATUS				Status;
	PDEVICE_EXTENSION		pDevExt;
	PURB					urb;

	DBG_ENTER(("%p", pDev_obj));

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
	if(urb == NULL) {
		DBG_LEAVE(("urb STATUS_INSUFFICIENT_RESOURCES"));
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	UsbBuildGetDescriptorRequest(urb, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
		USB_DEVICE_DESCRIPTOR_TYPE, 0, 0,
		&pDevExt->DeviceDescriptor, NULL, sizeof(USB_DEVICE_DESCRIPTOR),
		NULL);

	Status = RBC9_usb_call(pDev_obj, IOCTL_INTERNAL_USB_SUBMIT_URB, urb, NULL);
	if(! NT_SUCCESS(Status) || ! USBD_SUCCESS(urb->UrbHeader.Status)) {
		ExFreePool(urb);
		DBG_LEAVE(("STATUS_UNSUCCESSFUL"));
		return STATUS_UNSUCCESSFUL;
	}

	pDevExt->usb_control_pipe = urb->UrbControlTransfer.PipeHandle;

	ExFreePool(urb);

	DBG_OUT(("VendorID %X, ProductID %X, Version %X ", pDevExt->DeviceDescriptor.idVendor, pDevExt->DeviceDescriptor.idProduct, pDevExt->DeviceDescriptor.bcdDevice  ));
	DBG_LEAVE(("%x", Status));

	return Status;
}

static NTSTATUS RBC9_usb_select_conf(IN PDEVICE_OBJECT pDev_obj)
{
	NTSTATUS						Status;
	PDEVICE_EXTENSION				pDevExt;
	PUSB_CONFIGURATION_DESCRIPTOR	conf_desc;
	PUSB_INTERFACE_DESCRIPTOR		id;
	USBD_INTERFACE_LIST_ENTRY		iface_list[5];
	PURB							urb;
	PUSBD_INTERFACE_INFORMATION		iface_info;
	PUSBD_PIPE_INFORMATION			pipe_info;
	ULONG							size;

	DBG_ENTER(("%p", pDev_obj));

	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	Status = RBC9_usb_get_conf_desc(pDev_obj, &conf_desc, &size, 0);
	if(! NT_SUCCESS(Status)) {

		DBG_LEAVE(("%x: usb_get_conf_desc failed", Status));
		return Status;
	}

	id = USBD_ParseConfigurationDescriptorEx(conf_desc, conf_desc, -1, -1, -1, -1, -1);
	if(id == NULL) {
		ExFreePool(conf_desc);
		DBG_LEAVE(("USBD_ParseConfigurationDescriptorEx STATUS_NO_SUCH_DEVICE"));
		return STATUS_NO_SUCH_DEVICE;
	}
/*
	DBG_OUT(("USB_INTERFACE_DESCRIPTOR"));
	DBG_INFO(("bLength %x",id->bLength));
	DBG_INFO(("bDescriptorType  %x %x",id->bDescriptorType ));
	DBG_INFO(("bInterfaceNumber  %x",id->bInterfaceNumber ));
	DBG_INFO(("bAlternateSetting  %x",id->bAlternateSetting ));
	DBG_INFO(("bNumEndpoints   %x",id->bNumEndpoints  ));
	DBG_INFO(("bInterfaceClass   %x",id->bInterfaceClass  ));
	DBG_INFO(("bInterfaceSubClass  %x",id->bInterfaceSubClass ));
	DBG_INFO(("bInterfaceProtocol  %x",id->bInterfaceProtocol ));
	DBG_INFO(("iInterface  %x",id->iInterface ));
	DBG_INFO((""));
*/
	iface_list[0].InterfaceDescriptor = id;
	iface_list[0].Interface =NULL;
	iface_list[1].InterfaceDescriptor = NULL;

//	DBG_OUT(("Selecting the configuration"));
	urb = USBD_CreateConfigurationRequestEx(conf_desc, iface_list);

	Status = RBC9_usb_call(pDev_obj, IOCTL_INTERNAL_USB_SUBMIT_URB, urb, NULL);
	if(! NT_SUCCESS(Status) || ! USBD_SUCCESS(urb->UrbHeader.Status)) {
		ExFreePool(urb);
		ExFreePool(conf_desc);
		DBG_LEAVE(("USBD_CreateConfigurationRequestEx STATUS_UNSUCCESSFUL"));
		return STATUS_UNSUCCESSFUL;
	}

	pDevExt->usb_conf_handle = urb->UrbSelectConfiguration.ConfigurationHandle;
	iface_info = &urb->UrbSelectConfiguration.Interface;
/*	DBG_OUT(("Interface: InterfaceNumber %u, AlternateSetting %u, Class %u, SubClass %u, Protocol %u, NumberOfPipes %u",
		iface_info->InterfaceNumber, iface_info->AlternateSetting,
		iface_info->Class, iface_info->SubClass,
		iface_info->Protocol, iface_info->NumberOfPipes));
*/

	pDevExt->usb_input_pipe = NULL;
	pDevExt->usb_output_pipe = NULL;

	switch (iface_info->NumberOfPipes)
	{
	case 2:
		{
			pipe_info = &iface_info->Pipes[0];
			pDevExt->usb_input_pipe = pipe_info->PipeHandle;

			DBG_OUT(("Pipe 0 : MaxTransfer %d, MaxPckSize %d, PipeType %d, Interval %d, Handle %d Address %d", 
				pipe_info->MaximumTransferSize, pipe_info->MaximumPacketSize, 
				pipe_info->PipeType, pipe_info->Interval, 
				pipe_info->PipeHandle, pipe_info->EndpointAddress));

			pipe_info = &iface_info->Pipes[1];
			pDevExt->usb_output_pipe = pipe_info->PipeHandle;

			DBG_OUT(("Pipe 1 : MaxTransfer %d, MaxPckSize %d, PipeType %d, Interval %d, Handle %d Address %d", 
				pipe_info->MaximumTransferSize, pipe_info->MaximumPacketSize, 
				pipe_info->PipeType, pipe_info->Interval, 
				pipe_info->PipeHandle, pipe_info->EndpointAddress));
			break;
		}
	case 1:
		{
			pipe_info = &iface_info->Pipes[0];
			pDevExt->usb_input_pipe = pipe_info->PipeHandle;

	/*		DBG_OUT(("Pipe 0 : MaxTransfer %d, MaxPckSize %d, PipeType %d, Interval %d, Handle %d Address %d", 
				pipe_info->MaximumTransferSize, pipe_info->MaximumPacketSize, 
				pipe_info->PipeType, pipe_info->Interval, 
				pipe_info->PipeHandle, pipe_info->EndpointAddress));
		*/	break;
		}
	default:
		{
			DBG_OUT(("No Additionnal Pipe - Only Cntrol Pipe"));
		}
	}


	ExFreePool(urb);
	ExFreePool(conf_desc);

	DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}
static NTSTATUS RBC9_usb_get_conf_desc(IN PDEVICE_OBJECT pDev_obj,
									   OUT PUSB_CONFIGURATION_DESCRIPTOR *conf_desc,
									   OUT ULONG *size,
									   IN UCHAR conf_idx)
{
	NTSTATUS						Status;
	ULONG							desc_size;
	PURB							urb;
	PUSB_CONFIGURATION_DESCRIPTOR	desc;

	DBG_ENTER(("%p, %p, %u, %u", pDev_obj, conf_desc, *size, conf_idx));

	urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
	if(urb == NULL) {
		DBG_LEAVE(("urb STATUS_INSUFFICIENT_RESOURCES"));
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	desc = (PUSB_CONFIGURATION_DESCRIPTOR)ExAllocatePool(NonPagedPool, sizeof(USB_CONFIGURATION_DESCRIPTOR));
	if(desc == NULL) {
		ExFreePool(urb);
		DBG_LEAVE(("desc STATUS_INSUFFICIENT_RESOURCES"));
		return STATUS_INSUFFICIENT_RESOURCES;
	}
	DBG_OUT(("RBC9_usb_get_conf_desc - getting configuration descriptor Part 1"));
	UsbBuildGetDescriptorRequest(urb, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
		USB_CONFIGURATION_DESCRIPTOR_TYPE,
		conf_idx, 0,
		desc, NULL, sizeof(USB_CONFIGURATION_DESCRIPTOR),
		NULL);

	Status = RBC9_usb_call(pDev_obj, IOCTL_INTERNAL_USB_SUBMIT_URB, urb, NULL);
	if(! NT_SUCCESS(Status) || ! USBD_SUCCESS(urb->UrbHeader.Status)) {
		ExFreePool(urb);
		ExFreePool(desc);
		DBG_LEAVE(("STATUS_UNSUCCESSFUL descriptor 1"));
		return STATUS_UNSUCCESSFUL;
	}

	desc_size = desc->wTotalLength;
	ExFreePool(desc);
	desc = (PUSB_CONFIGURATION_DESCRIPTOR)ExAllocatePool(NonPagedPool, desc_size + 16); /* TODO: what is 16 ? */
	if(desc == NULL) {
		ExFreePool(urb);
		DBG_LEAVE(("descriptor 2 STATUS_INSUFFICIENT_RESOURCES"));
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	DBG_OUT(("Getting second part of configuration descriptor"));
	UsbBuildGetDescriptorRequest(urb, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
		USB_CONFIGURATION_DESCRIPTOR_TYPE,
		conf_idx, 0,
		desc, NULL, desc_size,
		NULL);

	Status = RBC9_usb_call(pDev_obj, IOCTL_INTERNAL_USB_SUBMIT_URB, urb, NULL);
	if(! NT_SUCCESS(Status) || ! USBD_SUCCESS(urb->UrbHeader.Status)) {
		ExFreePool(urb);
		ExFreePool(desc);
		DBG_LEAVE(("STATUS_UNSUCCESSFUL"));
		return STATUS_UNSUCCESSFUL;
	}

	*size = urb->UrbControlDescriptorRequest.TransferBufferLength;
	*conf_desc = desc;

//	DBG_OUT(("USB_CONFIGURATION_DESCRIPTOR \nbLength %x \nbDescriptorType  %x \nwTotalLength %x \nbNumInterfaces %x \niConfiguration  %x \nbConfigurationValue %x",
//		desc->bLength ,desc->bDescriptorType  ,desc->wTotalLength  ,desc->bNumInterfaces  ,desc->iConfiguration  ,desc->bConfigurationValue ));

	ExFreePool(urb);

	DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}
static NTSTATUS RBC9_usb_get_hid_desc(IN PDEVICE_OBJECT pDev_obj)
{
	NTSTATUS				Status;
	PDEVICE_EXTENSION		pDevExt	= GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	UCHAR					*BufDesc;
	ULONG					BufSize;

	DBG_ENTER(("%p", pDev_obj));

	BufSize = 999;

	BufDesc = (UCHAR *)ExAllocatePool(NonPagedPool, BufSize);
	if(BufDesc == NULL) {
		DBG_LEAVE(("BufDesc STATUS_INSUFFICIENT_RESOURCES"));
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	Status = RBC9_usb_get_control_transfer(pDev_obj, BufDesc, &BufSize,NAV_PACKET_DESCRIPTOR);
	if(!NT_SUCCESS(Status)) 
	{
		ExFreePool(BufDesc);
		DBG_LEAVE(("STATUS_UNSUCCESSFUL"));
		return STATUS_UNSUCCESSFUL;
	}

	pDevExt->Size_HID_Report_Space = (USHORT) BufSize;
	pDevExt->HID_Report_Space = (UCHAR *)ExAllocatePool(NonPagedPool, BufSize);
	RtlCopyMemory(pDevExt->HID_Report_Space, BufDesc, BufSize);

	
	DBG_OUT(("Report Space %d",BufSize));

	ExFreePool(BufDesc);
	
	DBG_LEAVE(("%x", Status));

	return Status;
}

#pragma PAGEDCODE
static VOID RBC9_usb_worker(IN PVOID ctxt)
{
	PDEVICE_OBJECT				pDev_obj;
	PDEVICE_EXTENSION			pDevExt;
	RAW_INPUT buf;
	ULONG buf_size;


	pDev_obj = (PDEVICE_OBJECT)ctxt;
	pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj);

	DBG_ENTER(("%p - DEVICE_STATE_STARTED 1 = %d ?", ctxt,pDevExt->Pnp_State));

	KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);

	while(pDevExt->Pnp_State == DEVICE_STATE_STARTED) {
		NTSTATUS Status;

		/* read from usb */
		buf_size = sizeof(buf);
		RtlZeroMemory(&buf, buf_size);

		Status = RBC9_usb_get_interrupt_transfer(pDev_obj, &buf, &buf_size);
		if(! NT_SUCCESS(Status)) 
		{
		//	DBG_OUT(("RBC9_usb_worker - get interrupt fail: %x", Status));
			buf = pDevExt->hwInData;

			{
				LARGE_INTEGER time;

				time.QuadPart = -10 * 1000 * 10;
				KeDelayExecutionThread(KernelMode, FALSE, &time);
			}
		}

		/* set raw input */
		pDevExt->hwInData = buf;

		//	DBG_OUT(("RBC9_usb_worker - ReportId %x",pDevExt->hwInData.ReportId));

		/* indicate to process data */
		KeSetEvent(&pDevExt->hid_worker_wakeup_event, IO_NO_INCREMENT, FALSE);
	}

	//DBG_OUT(("already stop/removed"));

	DBG_LEAVE(("STATUS_SUCCESS"));
	PsTerminateSystemThread(STATUS_SUCCESS);
}


#pragma LOCKEDCODE
NTSTATUS RBC9_usb_get_interrupt_transfer(IN PDEVICE_OBJECT pDev_obj, IN PVOID buf, ULONG *buf_size)
{
	// DBG_ENTER((" and leave: %p, %p, %u", pDev_obj, buf, *buf_size));
	return RBC9_usb_interrupt_transfer(pDev_obj, buf, buf_size,
		GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj)->usb_input_pipe,
		(USBD_TRANSFER_DIRECTION_IN |
		USBD_SHORT_TRANSFER_OK));
}

NTSTATUS RBC9_usb_put_interrupt_transfer(IN PDEVICE_OBJECT pDev_obj, IN PVOID buf, ULONG *buf_size)
{
//	DBG_ENTER((" and leave: %p, %p, %u", pDev_obj, buf, *buf_size));
	return RBC9_usb_interrupt_transfer(pDev_obj, buf, buf_size,
		GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj)->usb_output_pipe,
		0 );
}

NTSTATUS RBC9_usb_put_control_transfer(IN PDEVICE_OBJECT pDev_obj, IN PVOID buf, ULONG *buf_size, IN PVOID packet)
{
	//DBG_ENTER((" and leave: %p, %p, %u", pDev_obj, buf, *buf_size));
	return RBC9_usb_control_transfer(pDev_obj, buf, buf_size,
		GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj)->usb_control_pipe,
		(USBD_DEFAULT_PIPE_TRANSFER|
		USBD_SHORT_TRANSFER_OK), packet );
}

NTSTATUS RBC9_usb_get_control_transfer(IN PDEVICE_OBJECT pDev_obj, IN PVOID buf, ULONG *buf_size, IN PVOID packet)
{
	DBG_ENTER((" and leave: %p, %p, %u", pDev_obj, buf, *buf_size));
	return RBC9_usb_control_transfer(pDev_obj, buf, buf_size,
		GET_MINIDRIVER_DEVICE_EXTENSION(pDev_obj)->usb_control_pipe,
		(USBD_TRANSFER_DIRECTION_IN |
		USBD_DEFAULT_PIPE_TRANSFER|
		USBD_SHORT_TRANSFER_OK), packet );
}

static NTSTATUS RBC9_usb_control_transfer(IN PDEVICE_OBJECT pDev_obj,
												IN PVOID buf, ULONG *buf_size,
												USBD_PIPE_HANDLE pipe, ULONG flags, IN PVOID packet)
{
	PURB urb;
	NTSTATUS Status;

	//DBG_ENTER(("%p, %p, %u, %p, %u", pDev_obj, buf, *buf_size, pipe, flags));

	if(pipe == NULL) {
		DBG_LEAVE(("STATUS_INVALID_HANDLE"));
		return STATUS_INVALID_HANDLE;
	}

	if(buf == NULL || *buf_size < 1) {
		DBG_LEAVE(("STATUS_INVALID_PARAMETER"));
		return STATUS_INVALID_PARAMETER;
	}

	urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_TRANSFER));
	if(urb == NULL) {
		DBG_LEAVE(("STATUS_INSUFFICIENT_RESOURCES"));
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	RtlZeroMemory(urb, sizeof(struct _URB_CONTROL_TRANSFER));


	urb->UrbControlTransfer.Hdr.Function			= URB_FUNCTION_CONTROL_TRANSFER;
	urb->UrbControlTransfer.Hdr.Length				= sizeof(struct _URB_CONTROL_TRANSFER);
	urb->UrbControlTransfer.Hdr.Status				= 0;
	urb->UrbControlTransfer.PipeHandle				= 0;
	urb->UrbControlTransfer.TransferFlags 			= flags;
	urb->UrbControlTransfer.TransferBufferLength 	= *buf_size;
	urb->UrbControlTransfer.TransferBuffer 			= buf;
	urb->UrbControlTransfer.TransferBufferMDL 		= NULL;
	urb->UrbControlTransfer.UrbLink 				= NULL;
	
	RtlCopyMemory(urb->UrbControlTransfer.SetupPacket, packet, 8);

	Status = RBC9_usb_call(pDev_obj, IOCTL_INTERNAL_USB_SUBMIT_URB, urb, NULL);
	
	*buf_size = urb->UrbControlTransfer.TransferBufferLength;

//	DBG_OUT(("Buf Len %x",urb->UrbControlTransfer.TransferBufferLength));

	if(! NT_SUCCESS(Status) || ! USBD_SUCCESS(urb->UrbHeader.Status)) {
		ExFreePool(urb);
		DBG_LEAVE(("STATUS_UNSUCCESSFUL"));
		return STATUS_UNSUCCESSFUL;
	}

	ExFreePool(urb);

	// DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}



static NTSTATUS RBC9_usb_interrupt_transfer(IN PDEVICE_OBJECT pDev_obj,
											IN PVOID buf, ULONG *buf_size,
											USBD_PIPE_HANDLE pipe, ULONG flags)
{
	PURB urb;
	NTSTATUS Status;

	//DBG_ENTER(("%p, %p, %u, %p, %u", pDev_obj, buf, *buf_size, pipe, flags));

	if(pipe == NULL) {
		DBG_LEAVE(("STATUS_INVALID_HANDLE"));
		return STATUS_INVALID_HANDLE;
	}

	if(buf == NULL || *buf_size < 1) {
		DBG_LEAVE(("STATUS_INVALID_PARAMETER"));
		return STATUS_INVALID_PARAMETER;
	}

	urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
	if(urb == NULL) {
		DBG_LEAVE(("STATUS_INSUFFICIENT_RESOURCES"));
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	UsbBuildInterruptOrBulkTransferRequest(urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
		pipe,
		buf, NULL, *buf_size,
		flags,
		NULL);
	Status = RBC9_usb_call(pDev_obj, IOCTL_INTERNAL_USB_SUBMIT_URB, urb, NULL);
	if(! NT_SUCCESS(Status) || ! USBD_SUCCESS(urb->UrbHeader.Status)) {
		ExFreePool(urb);
	//	DBG_LEAVE(("STATUS_UNSUCCESSFUL"));
		return STATUS_UNSUCCESSFUL;
	}

	*buf_size = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;

	ExFreePool(urb);

	// DBG_LEAVE(("STATUS_SUCCESS"));
	return STATUS_SUCCESS;
}
