Example #1
0
FORCEINLINE
ULONG
FxIrp::GetParameterIoctlCodeBufferMethod(
    VOID
    )
{
    return METHOD_FROM_CTL_CODE(GetParameterIoctlCode());
}
Example #2
0
NTSTATUS
DMDeviceControl(
	PDEVICE_OBJECT	DeviceObject,
	PIRP			Irp
	)
{
	NTSTATUS			status;
	PIO_STACK_LOCATION	IrpStack;
	PVOID				InputBuffer;
	ULONG				InputLength;
	PVOID				OutputBuffer;
	ULONG				OutputLength;
	PDEVICE_ENTRY		DevEntry;
	ULONG				IoControlCode;

	ENTER_DISPATCH;
	IrpStack = IoGetCurrentIrpStackLocation(Irp);
	IoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;

	if ( DeviceObject == g_pDeviceObject )
	{
		InputBuffer = Irp->AssociatedIrp.SystemBuffer;
		InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
		OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
		OutputBuffer = Irp->AssociatedIrp.SystemBuffer;

		if ( METHOD_FROM_CTL_CODE(IoControlCode) == METHOD_NEITHER )
		{
			OutputBuffer = Irp->UserBuffer;
		}

		status = DMDeviceIoCtl(Irp, InputBuffer, InputLength, OutputBuffer, OutputLength, IoControlCode);
	}
	else
	{
		DevEntry = LookupEntryByDevObj(DeviceObject);
		if ( DevEntry && g_bStartMon)
		{
			//log it
			KdPrint(("%u-%u: %s\n", DevEntry->DiskNumber, DevEntry->PartitionNumber, "IRP_MJ_DEVICE_CONTROL"));
		}

		status = DefaultDispatch(DeviceObject, Irp);
		// hook new dev
		if (IoControlCode == IOCTL_DISK_FIND_NEW_DEVICES ||
			IoControlCode == IOCTL_DISK_SET_DRIVE_LAYOUT)
		{
			if ( NT_SUCCESS(status) )
			{
				HookDispatch(DeviceObject->DriverObject, 0);
			}
		}
	}

	LEAVE_DISPATCH;
	return status;
}
NTSTATUS
WDFEXPORT(WdfDmaTransactionInitializeUsingRequest)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFDMATRANSACTION DmaTransaction,
    __in
    WDFREQUEST Request,
    __in
    PFN_WDF_PROGRAM_DMA EvtProgramDmaFunction,
    __in
    WDF_DMA_DIRECTION DmaDirection
    )
{
    NTSTATUS status;
    FxDmaTransactionBase* pDmaTrans;
    FxRequest* pReqObj;
    MDL* mdl = NULL;
    PIO_STACK_LOCATION stack;
    ULONG reqLength;
    PFX_DRIVER_GLOBALS  pFxDriverGlobals;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   DmaTransaction,
                                   FX_TYPE_DMA_TRANSACTION,
                                   (PVOID *) &pDmaTrans,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, EvtProgramDmaFunction);

    if (DmaDirection != WdfDmaDirectionReadFromDevice &&
        DmaDirection != WdfDmaDirectionWriteToDevice) {
        status = STATUS_INVALID_PARAMETER;
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
            "Initialization of WDFDMATRANSACTION 0x%p using WDFREQUEST %p, "
            "DmaDirection 0x%x is an invalid value, %!STATUS!",
            DmaTransaction, Request, DmaDirection, status);
        return status;
    }

    FxObjectHandleGetPtr(pFxDriverGlobals,
                         Request,
                         FX_TYPE_REQUEST,
                         (PVOID *) &pReqObj);

    reqLength = 0;

    stack = pReqObj->GetFxIrp()->GetCurrentIrpStackLocation();

    //
    // Get the MDL and Length from the request.
    //
    switch (stack->MajorFunction) {

    case IRP_MJ_READ:

        if (DmaDirection != WdfDmaDirectionReadFromDevice) {
            status = STATUS_INVALID_DEVICE_REQUEST;

            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
                                "Dma direction %!WDF_DMA_DIRECTION! of WDFTRANSACTION "
                                "0x%p doesn't match with the WDFREQUEST 0x%p type "
                                "%!WDF_REQUEST_TYPE! %!STATUS!",
                                DmaDirection, DmaTransaction, Request,
                                stack->MajorFunction, status);

            return status;
        }

        reqLength = stack->Parameters.Read.Length;

        status = pReqObj->GetMdl(&mdl);
        break;

    case IRP_MJ_WRITE:

        if (DmaDirection != WdfDmaDirectionWriteToDevice) {
            status = STATUS_INVALID_DEVICE_REQUEST;

            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
                                "Dma direction %!WDF_DMA_DIRECTION! of WDFTRANSACTION "
                                "0x%p doesn't match with the WDFREQUEST 0x%p type "
                                "%!WDF_REQUEST_TYPE! %!STATUS!",
                                DmaDirection, DmaTransaction, Request,
                                stack->MajorFunction, status);

            return status;
        }

        reqLength = stack->Parameters.Write.Length;

        status = pReqObj->GetMdl(&mdl);
        break;

    case IRP_MJ_DEVICE_CONTROL:
    case IRP_MJ_INTERNAL_DEVICE_CONTROL:

        switch (METHOD_FROM_CTL_CODE(stack->Parameters.DeviceIoControl.IoControlCode)) {
            case METHOD_BUFFERED:

                if (DmaDirection == WdfDmaDirectionWriteToDevice) {
                    reqLength = stack->Parameters.DeviceIoControl.InputBufferLength;
                } else {
                    reqLength = stack->Parameters.DeviceIoControl.OutputBufferLength;
                }

                //
                // In this case both input buffer and output buffer map
                // to the same MDL and it's probed for read & write access.
                // So it's okay for DMA transfer in either direction.
                //
                status = pReqObj->GetMdl(&mdl);
                break;

            case METHOD_IN_DIRECT:
                //
                // For this type, the output buffer is probed for read access.
                // So the direction of DMA transfer is WdfDmaDirectionWriteToDevice.
                //
                if (DmaDirection != WdfDmaDirectionWriteToDevice) {

                    status = STATUS_INVALID_DEVICE_REQUEST;

                    DoTraceLevelMessage(pFxDriverGlobals,
                                        TRACE_LEVEL_ERROR, TRACINGDMA,
                                        "Dma direction %!WDF_DMA_DIRECTION! of WDFTRANSACTION "
                                        "0x%p doesn't match with WDFREQUEST 0x%p ioctl type "
                                        "METHOD_IN_DIRECT %!STATUS!",
                                        DmaDirection, DmaTransaction, Request, status);
                    return status;
                }

                reqLength = stack->Parameters.DeviceIoControl.OutputBufferLength;

                status = pReqObj->GetDeviceControlOutputMdl(&mdl);

                break;

            case METHOD_OUT_DIRECT:
                //
                // For this type, the output buffer is probed for write access.
                // So the direction of DMA transfer is WdfDmaDirectionReadFromDevice.
                //
                if (DmaDirection != WdfDmaDirectionReadFromDevice) {

                    status = STATUS_INVALID_DEVICE_REQUEST;

                    DoTraceLevelMessage(pFxDriverGlobals,
                                        TRACE_LEVEL_ERROR, TRACINGDMA,
                                        "Dma direction %!WDF_DMA_DIRECTION! of WDFTRANSACTION "
                                        "0x%p doesn't match with WDFREQUEST 0x%p ioctl type "
                                        "METHOD_OUT_DIRECT %!STATUS!",
                                        DmaDirection, DmaTransaction, Request, status);

                    return status;
                }

                reqLength = stack->Parameters.DeviceIoControl.OutputBufferLength;

                status = pReqObj->GetDeviceControlOutputMdl(&mdl);

                break;
            default:

                status = STATUS_INVALID_DEVICE_REQUEST;

                DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
                                    "Invalid ioctl code in WDFREQUEST 0x%p %!STATUS!",
                                    Request, status);

                FxVerifierDbgBreakPoint(pFxDriverGlobals);
                break;

        }// End of switch(ioctType)
        break;

    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;

    }

    if (!NT_SUCCESS(status)) {
        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
                            "Couldn't retrieve mdl from WDFREQUEST 0x%p for "
                            "WDFTRANSACTION 0x%p %!STATUS!",
                            Request, DmaTransaction, status);
        return status;
    }

    if (reqLength == 0) {
        status = STATUS_INVALID_DEVICE_REQUEST;
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
            "Zero length request, %!STATUS!", status);
        return status;
    }

    //
    // If the DMA enabler is packet based, make sure the virtual address and
    // the length of transfer are within bounds. Basically, we are checking
    // to see if the length of data to be transferred doesn't span multiple
    // MDLs, because packet based DMA doesn't support chained MDLs.
    //
    if (pDmaTrans->GetDmaEnabler()->SupportsChainedMdls() == FALSE) {
        ULONG  length;

        length = MmGetMdlByteCount(mdl);

        if (reqLength > length) {
            status = STATUS_INVALID_PARAMETER;
            DoTraceLevelMessage(
                pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
                "WDFREQUEST %p transfer length (%d) is out of bounds of MDL "
                "Byte count (%d), %!STATUS!",
                Request, reqLength, length, status);

            return status;
        }
    }

    //
    // Parms appear OK, so initialize this instance.
    //
    status = pDmaTrans->Initialize(EvtProgramDmaFunction,
                                   DmaDirection,
                                   mdl,
                                   0,
                                   reqLength);

    if (!NT_SUCCESS(status)) {
        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
                            "WDFTANSACTION 0x%p initialization failed: "
                            "%!STATUS!", DmaTransaction, status);
        return status;
    }

    //
    // Set this Request in the new DmaTransaction.  The request will 
    // take a reference on this request when it starts executing.
    //
    pDmaTrans->SetRequest(pReqObj);

    return STATUS_SUCCESS;
}
Example #4
0
//----- (08001F19) --------------------------------------------------------
NTSTATUS 
DMDeviceControl(
	PDEVICE_OBJECT	DeviceObject, 
	PIRP			Irp
	)
{
	PIO_STACK_LOCATION	IrpStack;
	PVOID				OutputBuffer;
	PVOID				InputBuffer;
	ULONG				InputLength;
	ULONG				OutputLength;
	NTSTATUS			status;
	PDEVICE_ENTRY		DevEntry;
	LARGE_INTEGER		CurrentTime;
	LARGE_INTEGER		PerfCount; 
	ULONG				seq;
	char				IoctlName[64] = "";
	PCHAR				pIoCtlName;
	ULONG				IoControlCode;

	IrpStack = IoGetCurrentIrpStackLocation(Irp);
	IoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;

	if ( DeviceObject == g_pDeviceObject )
	{
		InputBuffer = Irp->AssociatedIrp.SystemBuffer;
		InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
		OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
		OutputBuffer = Irp->AssociatedIrp.SystemBuffer;

		if ( METHOD_FROM_CTL_CODE(IoControlCode) == METHOD_NEITHER )
		{
			OutputBuffer = Irp->UserBuffer;
		}

		status = DMDeviceIoCtl(Irp, InputBuffer, InputLength, OutputBuffer, OutputLength, IoControlCode);
	}
	else
	{
		if ( g_bUsePerfCounter )
		{
			CurrentTime.QuadPart = 0;
			PerfCount = KeQueryPerformanceCounter(0);
		}
		else
		{
			KeQuerySystemTime(&CurrentTime);
			PerfCount.QuadPart = 0;
		}
		seq = InterlockedIncrement(&Sequence);
		DevEntry = LookupEntryByDevObj(DeviceObject);
		if ( DevEntry )
		{
			pIoCtlName = GetIoctlName(IoctlName, IrpStack->Parameters.DeviceIoControl.IoControlCode);
			LogRecord(
				seq,
				&CurrentTime,
				DevEntry->DiskNumber,
				"%s(%s)",
				IrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? "IRP_MJ_DEVICE_CONTROL" : "IRP_MJ_INTERNAL_DEVICE_CONTROL",
				IoctlName);
		}

		status = DefaultDispatch(DeviceObject, Irp, seq, g_bUsePerfCounter, &PerfCount);
		if ( IoControlCode == IOCTL_DISK_FIND_NEW_DEVICES || IoControlCode == IOCTL_DISK_SET_DRIVE_LAYOUT )
		{
			if ( NT_SUCCESS(status) )
			{
				HookDispatch(DeviceObject->DriverObject, 0);
			}
		}
	}

	return status;
}
VOID XferCtrl (
    __in WDFQUEUE Queue,
    __in WDFREQUEST Request,
    __in size_t InputBufferLength,
    __in size_t OutputBufferLength)
{
	NTSTATUS                status;
	PDEVICE_CONTEXT         deviceContext;
	PREQUEST_CONTEXT        requestContext;
	WDFMEMORY				transferMemory;
	PWDF_USB_CONTROL_SETUP_PACKET setupPacket;
	WDF_REQUEST_SEND_OPTIONS sendOptions;
	WDFMEMORY_OFFSET		_transferOffset;
	PWDFMEMORY_OFFSET		transferOffset = &_transferOffset;

	UNREFERENCED_PARAMETER(InputBufferLength);
	UNREFERENCED_PARAMETER(OutputBufferLength);

	deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue));
	requestContext = GetRequestContext(Request);

	setupPacket = (PWDF_USB_CONTROL_SETUP_PACKET)&requestContext->IoControlRequest.control;
	USBDBG("bmDir=%s bmType=%s bmRecipient=%s bmReserved=%03u bRequest=%u wIndex=%u wValue=%u wLength=%u\n",
	       GetBmRequestDirString(setupPacket->Packet.bm.Request.Dir),
	       GetBmRequestTypeString(setupPacket->Packet.bm.Request.Type),
	       GetBmRequestRecipientString(setupPacket->Packet.bm.Request.Recipient),
	       setupPacket->Packet.bm.Request.Reserved,
	       setupPacket->Packet.bRequest,
	       setupPacket->Packet.wIndex.Value,
	       setupPacket->Packet.wValue.Value,
	       setupPacket->Packet.wLength);

	// If the device and config descriptor requests are not handled they will be the "true"
	// descriptors directly from the device. i.e. if the device has two unassociated interfaces
	// the composite layer will split it into two but each virtual device interface would show
	// both interface deacriptors.
	//
	if (setupPacket->Packet.bm.Request.Dir == BMREQUEST_DEVICE_TO_HOST &&
	        setupPacket->Packet.bm.Request.Type == BMREQUEST_STANDARD &&
	        setupPacket->Packet.bRequest == USB_REQUEST_GET_DESCRIPTOR)
	{
		UCHAR descriptorType = setupPacket->Packet.wValue.Bytes.HiByte;
		// UCHAR descriptorIndex = setupPacket->Packet.wValue.Bytes.LowByte;
		ULONG descriptorSize = 0;
		PVOID descriptorIn = NULL;
		PVOID outputBuffer = NULL;
		size_t outputBufferLength = 0;

		if (requestContext->IoControlCode == LIBUSB_IOCTL_GET_DESCRIPTOR)
		{
			switch(descriptorType)
			{
			case USB_DESCRIPTOR_TYPE_DEVICE:
				descriptorSize = sizeof(deviceContext->UsbDeviceDescriptor);
				descriptorIn = &deviceContext->UsbDeviceDescriptor;
				break;
			case USB_DESCRIPTOR_TYPE_CONFIGURATION:
				if (setupPacket->Packet.wValue.Bytes.LowByte == 0)
				{
					descriptorSize = deviceContext->ConfigurationDescriptorSize;
					descriptorIn = deviceContext->UsbConfigurationDescriptor;
				}
				else
				{
					// we only support the one for now. ;)
					WdfRequestCompleteWithInformation(Request, STATUS_NO_MORE_ENTRIES, 0);
					return;
				}
				break;
			}

			if (descriptorIn && descriptorSize)
			{
				// handle (or fail) this standard request here.
				status = WdfRequestRetrieveOutputBuffer(Request, 2, &outputBuffer, &outputBufferLength);
				if (NT_SUCCESS(status))
				{
					descriptorSize = (ULONG)min(descriptorSize, outputBufferLength);
					RtlCopyMemory(outputBuffer, descriptorIn, descriptorSize);
					WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, descriptorSize);
					return;
				}
				USBERR("WdfRequestRetrieveOutputBuffer failed. status=%Xh\n", status);
				WdfRequestCompleteWithInformation(Request, status, 0);
				return;
			}
		}
	}

	if (METHOD_FROM_CTL_CODE(requestContext->IoControlCode) == METHOD_BUFFERED &&
	        requestContext->RequestType == WdfRequestTypeWrite)
	{
		// support for some of the legacy LIBUSB_IOCTL codes which place the data input
		// buffer at the end of the libusb_request structure.
		status = WdfRequestRetrieveInputMemory(Request, &transferMemory);
		if (!NT_SUCCESS(status))
		{
			USBERR("WdfRequestRetrieveInputMemory failed. status=%Xh\n", status);
			goto Exit;
		}

		if (requestContext->Length < sizeof(libusb_request))
		{
			// this can never happen because the input buffer length is checked for
			// this by the default IoControl event.
			status = STATUS_BUFFER_TOO_SMALL;
			USBERR("input buffer length is less than sizeof(libusb_request) status=%Xh\n", status);
			goto Exit;
		}

		transferOffset->BufferOffset = sizeof(libusb_request);
		transferOffset->BufferLength = requestContext->Length - sizeof(libusb_request);

		if (transferOffset->BufferLength == 0)
		{
			// this is okay but no input data means transferOffset->BufferOffset is pointing
			// to invalid memory; because the length is also zero it is still most likely safe.
			transferOffset = NULL;
			transferMemory = NULL;
		}
	}
	else
	{
		// native control transfers are direct; data comes from/goes to the out buffer whether reading or writing.
		transferOffset = NULL;
		status = WdfRequestRetrieveOutputMemory(Request, &transferMemory);
		if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL)
		{
			USBERR("WdfRequestRetrieveOutputMemory failed. status=%Xh\n", status);
			goto Exit;
		}

		if (status == STATUS_BUFFER_TOO_SMALL)
		{
			// zero length transfer buffer, this is okay.
			transferMemory = NULL;
			USBMSG("zero-length transfer buffer\n");
		}
	}



	status = WdfUsbTargetDeviceFormatRequestForControlTransfer(
	             deviceContext->WdfUsbTargetDevice,
	             Request,
	             setupPacket,
	             transferMemory,
	             transferOffset);

	if (!NT_SUCCESS(status))
	{
		USBERR("WdfUsbTargetDeviceFormatRequestForControlTransfer failed. status=%Xh\n", status);
		goto Exit;
	}

	WdfRequestSetCompletionRoutine(Request,
	                               XferCtrlComplete,
	                               NULL);

	WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, 0);
	status = SetRequestTimeout(requestContext, Request, &sendOptions);
	if (!NT_SUCCESS(status))
	{
		USBERR("SetRequestTimeout failed. status=%Xh\n", status);
		goto Exit;
	}

	if (!WdfRequestSend(Request,
	                    WdfUsbTargetDeviceGetIoTarget(deviceContext->WdfUsbTargetDevice),
	                    &sendOptions))
	{
		status = WdfRequestGetStatus(Request);
		USBERR("WdfRequestSend failed. status=%Xh\n", status);
	}
	else
	{
		USBMSGN("[Ok] status=%Xh", status);
		return;
	}


Exit:
	if (!NT_SUCCESS(status))
	{
		WdfRequestCompleteWithInformation(Request, status, 0);
	}

	return;
}
Example #6
0
NTSTATUS DriverControlUtilityHandler( __in PDEVICE_OBJECT DeviceObject, __in PIRP Irp )
{
	NTSTATUS			nsStatus	= STATUS_INVALID_DEVICE_REQUEST;
	PIO_STACK_LOCATION	IrpSp		= IoGetCurrentIrpStackLocation (Irp);
	ULONG				IOCTL		= IrpSp->Parameters.DeviceIoControl.IoControlCode;
	PVOID				pInBuffer	= NULL;
	PVOID				pOutBuffer	= NULL;
	ULONG				ulInformation		= 0;
	ULONG				ulIOCTLIndex		= 0;
	ULONG				ulInBufferLength	= IrpSp->Parameters.DeviceIoControl.InputBufferLength;
	ULONG				ulOutBufferLength	= IrpSp->Parameters.DeviceIoControl.OutputBufferLength;

	switch(METHOD_FROM_CTL_CODE(IOCTL))
	{
	case METHOD_BUFFERED:
		{
			pInBuffer = Irp->AssociatedIrp.SystemBuffer;
			pOutBuffer= Irp->AssociatedIrp.SystemBuffer;
		}
		break;

	case METHOD_NEITHER:
		{
			pInBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
			pOutBuffer= Irp->UserBuffer;
		}
		break;

	default:
		{
			ASSERT(FALSE);
			nsStatus = STATUS_INVALID_PARAMETER;
		}
	}

	if ( ExGetPreviousMode () == UserMode && g_osVersionCheck < WINDOWS_VERSION_8 )
	{
		for (ulIOCTLIndex = 0; ulIOCTLIndex < _countof(aIOCTLHandlers); ++ulIOCTLIndex)
		{
			if ( (ULONG)aIOCTLHandlers[ulIOCTLIndex].IOCTL == IOCTL )
			{
				nsStatus = aIOCTLHandlers[ulIOCTLIndex].IoctlFunc(
					DeviceObject,
					pInBuffer,
					ulInBufferLength,
					pOutBuffer,
					ulOutBufferLength,
					&ulInformation
					);
				if ( pOutBuffer != NULL && ulInformation > 0 )
				{
					((PCOMMON_HEAD_OUT)pOutBuffer)->IoStatus.Status = nsStatus;
					((PCOMMON_HEAD_OUT)pOutBuffer)->IoStatus.Information = ulInformation;
				}

				break;
			}
		}
	}

	nsStatus = (nsStatus == STATUS_PENDING)? nsStatus : STATUS_SUCCESS;
	Irp->IoStatus.Status = nsStatus;
	Irp->IoStatus.Information = ulInformation;

	return nsStatus;
}
Example #7
0
static VOID UsbChief_EvtIoDeviceControl(IN WDFQUEUE Queue, IN WDFREQUEST Request,
				 IN size_t OutputBufferLength, IN size_t InputBufferLength,
				 IN ULONG IoControlCode)
{
	NTSTATUS Status;
	size_t Length = 0;
	PIOCTL_DATA data;
	PDEVICE_CONTEXT pDeviceContext;
	UCHAR test[4096];
	UCHAR *config;
	WORD *version;
	URB Urb;
	WDF_USB_INTERFACE_SELECT_SETTING_PARAMS interfaceParams;
	ULONG i;
	UNREFERENCED_PARAMETER(OutputBufferLength);
	UNREFERENCED_PARAMETER(InputBufferLength);
	UNREFERENCED_PARAMETER(Queue);

	PAGED_CODE();

	pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue));

	Length = 0;
	switch(IoControlCode) {
	case IOCTL_VENDOR_WRITE:

		if (InputBufferLength != sizeof(*data)) {
			UsbChief_DbgPrint(0, ("Invalid InputBuffer Size: %d/%d\n", InputBufferLength, sizeof(*data)));
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		Status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &data, &Length);
		if (!NT_SUCCESS(Status))
			goto out;

		UsbChief_DbgPrint(DEBUG_IOCTL, ("EvtIoDeviceControl: IOCTL_VENDOR_WRITE %x, Index %x, Value %x, Max Length %d, Buf %p\n",
				      data->Request, data->Index, data->Value, data->Length, data->Buffer));

		if (Length != sizeof(*data)) {
			UsbChief_DbgPrint(0, ("Failed to retrieve buffer: %d/%d\n", Length, sizeof(*data)));
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		memset(&Urb, 0, sizeof(Urb));
		Urb.UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;
		Urb.UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
		Urb.UrbControlVendorClassRequest.RequestTypeReservedBits = 0x40;
		Urb.UrbControlVendorClassRequest.TransferBufferLength = data->Length;
		Urb.UrbControlVendorClassRequest.TransferBuffer = (PVOID)data->Buffer;
		Urb.UrbControlVendorClassRequest.Request = data->Request;
		Urb.UrbControlVendorClassRequest.Value = data->Value;
		Urb.UrbControlVendorClassRequest.Index = data->Index;

		if (DebugLevel & DEBUG_IOCTL) {
			for(i = 0; i < data->Length; i++)
				DbgPrint("%02X ", ((PUCHAR)data->Buffer)[i]);
			DbgPrint("\n");
		}
		Status = WdfUsbTargetDeviceSendUrbSynchronously(pDeviceContext->WdfUsbTargetDevice, NULL, NULL, &Urb);

		if (!NT_SUCCESS(Status)) {
			UsbChief_DbgPrint(0, ("WdfUsbTargetDeviceSendControlTransferSynchronously failed\n"));
		}
		break;

	case IOCTL_VENDOR_READ:
		if (InputBufferLength != sizeof(*data)) {
			UsbChief_DbgPrint(0, ("Invalid InputBuffer Size: %d/%d\n", InputBufferLength, sizeof(*data)));
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		Status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &data, &Length);
		if (!NT_SUCCESS(Status))
			goto out;

		UsbChief_DbgPrint(DEBUG_IOCTL, ("EvtIoDeviceControl: IOCTL_VENDOR_READ %x, Index %x, Value %x, Max Length %d, Buf %p\n",
				      data->Request, data->Index, data->Value, data->Length, data->Buffer));

		if (Length != sizeof(*data)) {
			UsbChief_DbgPrint(0, ("Failed to retrieve buffer: %d/%d\n", Length, sizeof(*data)));
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		if (data->Length > sizeof(test)) {
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		memset(&Urb, 0, sizeof(Urb));
		Urb.UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;
		Urb.UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
		Urb.UrbControlVendorClassRequest.RequestTypeReservedBits = 0xc0;
		Urb.UrbControlVendorClassRequest.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
		Urb.UrbControlVendorClassRequest.TransferBufferLength = data->Length;
		Urb.UrbControlVendorClassRequest.TransferBuffer = test;
		Urb.UrbControlVendorClassRequest.Request = data->Request;
		Urb.UrbControlVendorClassRequest.Value = data->Value;
		Urb.UrbControlVendorClassRequest.Index = data->Index;

		Status = WdfUsbTargetDeviceSendUrbSynchronously(pDeviceContext->WdfUsbTargetDevice, NULL, NULL, &Urb);

		if (DebugLevel & DEBUG_IOCTL && NT_SUCCESS(Status)) {
			for(i = 0; i < Urb.UrbControlVendorClassRequest.TransferBufferLength; i++)
				DbgPrint("%02X ", ((PUCHAR)test)[i]);
			DbgPrint("\n");
		}

		if (Urb.UrbControlVendorClassRequest.TransferBufferLength)
			memcpy((PVOID)data->Buffer, test, Urb.UrbControlVendorClassRequest.TransferBufferLength);
		Length = Urb.UrbControlVendorClassRequest.TransferBufferLength;
		break;

	case IOCTL_SELECT_CONFIGURATION:

		if (InputBufferLength != sizeof(*config)) {
			UsbChief_DbgPrint(0, ("Invalid InputBuffer Size: %d/%d\n", InputBufferLength, sizeof(*config)));
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		config = 0;
		Status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, (PVOID)&config, &Length);
		if (!NT_SUCCESS(Status))
			goto out;

		if (Length != sizeof(*config)) {
			UsbChief_DbgPrint(0, ("Invalid Length: %d/%d\n", Length, sizeof(*config)));
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		UsbChief_DbgPrint(DEBUG_IOCTL, ("EvtDeviceControl: IOCTL_SELECT_CONFIGURATION %x\n", *config));

		if (!pDeviceContext->UsbInterface) {
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		WDF_USB_INTERFACE_SELECT_SETTING_PARAMS_INIT_SETTING (&interfaceParams, *config);

		Status = WdfUsbInterfaceSelectSetting(pDeviceContext->UsbInterface, WDF_NO_OBJECT_ATTRIBUTES,
						      &interfaceParams);
		break;

	case IOCTL_GET_FIRMWARE_VERSION:
		UsbChief_DbgPrint(DEBUG_IOCTL, ("EvtDeviceControl: GET_FIRMWARE_VERSION\n"));

		Status = WdfRequestRetrieveOutputBuffer(Request, OutputBufferLength, &version, &Length);
		if (!NT_SUCCESS(Status))
			goto out;

		if (Length != sizeof(*version)) {
			UsbChief_DbgPrint(0, ("Invalid Length: %d/%d\n", Length, sizeof(*version)));
			Status = STATUS_INVALID_DEVICE_REQUEST;
			goto out;
		}

		*version = pDeviceContext->UsbDeviceDescriptor.bcdDevice;
		break;

	default:
		UsbChief_DbgPrint(DEBUG_IOCTL, ("EvtDeviceControl %08x (Device %x, Method %x) unknown\n",
				      IoControlCode, DEVICE_TYPE_FROM_CTL_CODE(IoControlCode),
				      METHOD_FROM_CTL_CODE(IoControlCode)));
		Status = STATUS_INVALID_DEVICE_REQUEST;
		break;
	}
out:
	UsbChief_DbgPrint(DEBUG_IOCTL, ("EvtDeviceControl: Status %08x, Length %d\n", Status, Length));
	WdfRequestCompleteWithInformation(Request, Status, Length);
}