コード例 #1
0
ファイル: drv_pipe.c プロジェクト: Noah-p0werd0wn/usb-travis
NTSTATUS Pipe_Reset(__in PDEVICE_CONTEXT deviceContext,
                    __in UCHAR pipeID)
{
	NTSTATUS status = STATUS_INVALID_PARAMETER;
	PPIPE_CONTEXT pipeContext;

	PAGED_CODE();

	pipeContext = GetPipeContextByID(deviceContext, pipeID);
	if (pipeContext->Pipe)
	{
		USBMSG("pipeID=%02Xh\n", pipeID);

		status = WdfUsbTargetPipeResetSynchronously(pipeContext->Pipe, WDF_NO_HANDLE, NULL);
		if (!NT_SUCCESS(status))
		{
			USBERR("WdfUsbTargetPipeResetSynchronously failed pipeID=%02Xh status=%Xh\n", pipeID, status);
		}
		else
		{
			if (pipeContext->Queue)
			{
				PQUEUE_CONTEXT queueContext = GetQueueContext(pipeContext->Queue);
				queueContext->IsFreshPipeReset = TRUE;
			}
		}
	}
	else
	{
		USBERR("pipeID=%02Xh not found\n", pipeID);
	}

	return status;
}
コード例 #2
0
/* EvtIoStop
Invoked for every inflight pipe write request.  The callback executes when:
1) libusbK will stop the queue because of queue policy changes.
2) The system requests stand-by
3) The device is removed
*/
VOID Queue_OnStop(
    __in WDFQUEUE Queue,
    __in WDFREQUEST Request,
    __in ULONG ActionFlags)
{
	PQUEUE_CONTEXT queueContext;
	PREQUEST_CONTEXT requestContext;

	if (Request == NULL || Queue == NULL)
	{
		USBERRN("Invalid wdf object.");
		return;
	}
	if ((queueContext = GetQueueContext(Queue)) == NULL)
	{
		USBERRN("Invalid queue context.");
		return;
	}
	if ((requestContext = GetRequestContext(Request)) == NULL)
	{
		USBERRN("Invalid request context.");
		return;
	}

	// None of the libusbK transfer functions set EvtRequestCancel, hence this should never happen.
	if (ActionFlags & WdfRequestStopRequestCancelable)
	{
		USBERRN("WdfRequestStopRequestCancelable! pipeID=%02Xh", queueContext->Info.EndpointAddress);
		WdfVerifierDbgBreakPoint();
		return;
	}

	if (ActionFlags & WdfRequestStopActionSuspend)
	{

		USBDBGN("StopAcknowledge for ActionSuspend. pipeID=%02Xh request=%p timeout=%d", 
			queueContext->Info.EndpointAddress, Request, requestContext->Timeout);

		WdfRequestStopAcknowledge(Request, FALSE);
	}
	else if(ActionFlags & WdfRequestStopActionPurge)
	{
		USBDBGN("CancelSentRequest for ActionPurge. pipeID=%02Xh request=%p timeout=%d", 
				queueContext->Info.EndpointAddress, Request, requestContext->Timeout);

		WdfRequestCancelSentRequest(Request);
	}
}
コード例 #3
0
/* EvtIoResume
Invoked for pipe write requests which were "StopAcknowledge" in EvtIoStop.
This callback exucutes after the device has re-entered D0(working).
*/
VOID Queue_OnResume(WDFQUEUE Queue, WDFREQUEST Request)
{
	PQUEUE_CONTEXT queueContext;
	PREQUEST_CONTEXT requestContext;
	NTSTATUS status = STATUS_SUCCESS;

	if (Request == NULL || Queue == NULL)
	{
		USBERRN("Invalid wdf object.");
		return;
	}
	if ((queueContext = GetQueueContext(Queue)) == NULL)
	{
		USBERRN("Invalid queue context.");
		return;
	}
	if ((requestContext = GetRequestContext(Request)) == NULL)
	{
		USBERRN("Invalid request context.");
		return;
	}

	mXfer_HandlePipeResetScenarios(status, queueContext, requestContext);
}
コード例 #4
0
ファイル: drv_pipe.c プロジェクト: Noah-p0werd0wn/usb-travis
NTSTATUS Pipe_InitQueue(
    __in PDEVICE_CONTEXT deviceContext,
    __in PPIPE_CONTEXT pipeContext,
    __out WDFQUEUE* queueRef)
{
	WDF_IO_QUEUE_CONFIG queueConfig;
	WDF_OBJECT_ATTRIBUTES objectAttributes;
	NTSTATUS status = STATUS_INVALID_HANDLE;
	WDFQUEUE queue = NULL;
	PQUEUE_CONTEXT queueContext;
	WDF_OBJECT_ATTRIBUTES memAttributes;

	WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes);
	objectAttributes.SynchronizationScope = WdfSynchronizationScopeQueue;

	*queueRef = NULL;

	// All queues get a context. At the very least, they will use a local copy of policy and pipe information
	WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&objectAttributes, QUEUE_CONTEXT);

	queueConfig.DispatchType = WdfIoQueueDispatchInvalid;
	Pipe_InitQueueConfig(pipeContext, &queueConfig);

	if (queueConfig.DispatchType != WdfIoQueueDispatchInvalid)
	{
		status = WdfIoQueueCreate(deviceContext->WdfDevice,
		                          &queueConfig,
		                          &objectAttributes,
		                          &queue);
		if (!NT_SUCCESS(status))
		{
			USBERR("WdfIoQueueCreate failed. pipeID=%02Xh status=%Xh\n", pipeContext->PipeInformation.EndpointAddress, status);
			goto Exit;
		}

		// Create the memory for partial read storage
		queueContext = GetQueueContext(queue);

		queueContext->IsFreshPipeReset = TRUE;

		// SET queueContext->OverOfs
		RtlZeroMemory(&queueContext->OverOfs, sizeof(queueContext->OverOfs));

		// SET queueContext->PipeHandle
		queueContext->PipeHandle = pipeContext->Pipe;

		// SET queueContext->Info
		RtlCopyMemory(&queueContext->Info, &pipeContext->PipeInformation, sizeof(queueContext->Info));

		WDF_OBJECT_ATTRIBUTES_INIT(&memAttributes);
		memAttributes.ParentObject = queue;

		// Only bulk and interrupt pipes have an OverMem buffer and it is only used
		// when the queue type is sequential. (RAW_IO=FALSE)
		if ((queueContext->Info.MaximumPacketSize) &&
		        (queueContext->Info.PipeType == WdfUsbPipeTypeBulk || queueContext->Info.PipeType == WdfUsbPipeTypeInterrupt))
		{
			// SET queueContext->OverMem
			// SET queueContext->OverBuf
			status = WdfMemoryCreate(&memAttributes, NonPagedPool, POOL_TAG, queueContext->Info.MaximumPacketSize, &queueContext->OverMem, &queueContext->OverBuf);
			if (!NT_SUCCESS(status))
			{
				USBERRN("WdfMemoryCreate failed. status=%08Xh", status);
				WdfObjectDelete(queue);
				goto Exit;
			}
		}
	}

	*queueRef = queue;

Exit:
	return status;
}
コード例 #5
0
VOID
EvtIoDeviceControl(
    _In_  WDFQUEUE          Queue,
    _In_  WDFREQUEST        Request,
    _In_  size_t            OutputBufferLength,
    _In_  size_t            InputBufferLength,
    _In_  ULONG             IoControlCode
    )
/*++
Routine Description:

    This event callback function is called when the driver receives an

    (KMDF) IOCTL_HID_Xxx code when handlng IRP_MJ_INTERNAL_DEVICE_CONTROL
    (UMDF) IOCTL_HID_Xxx, IOCTL_UMDF_HID_Xxx when handling IRP_MJ_DEVICE_CONTROL

Arguments:

    Queue - A handle to the queue object that is associated with the I/O request

    Request - A handle to a framework request object.

    OutputBufferLength - The length, in bytes, of the request's output buffer,
            if an output buffer is available.

    InputBufferLength - The length, in bytes, of the request's input buffer, if
            an input buffer is available.

    IoControlCode - The driver or system defined IOCTL associated with the request

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status;
    BOOLEAN                 completeRequest = TRUE;
    WDFDEVICE               device = WdfIoQueueGetDevice(Queue);
    PDEVICE_CONTEXT         deviceContext = NULL;
    PQUEUE_CONTEXT          queueContext = GetQueueContext(Queue);
    UNREFERENCED_PARAMETER  (OutputBufferLength);
    UNREFERENCED_PARAMETER  (InputBufferLength);

    deviceContext = GetDeviceContext(device);

    switch (IoControlCode)
    {
    case IOCTL_HID_GET_DEVICE_DESCRIPTOR:   // METHOD_NEITHER
        //
        // Retrieves the device's HID descriptor.
        //
        _Analysis_assume_(deviceContext->HidDescriptor.bLength != 0);
        status = RequestCopyFromBuffer(Request,
                            &deviceContext->HidDescriptor,
                            deviceContext->HidDescriptor.bLength);
        break;

    case IOCTL_HID_GET_DEVICE_ATTRIBUTES:   // METHOD_NEITHER
        //
        //Retrieves a device's attributes in a HID_DEVICE_ATTRIBUTES structure.
        //
        status = RequestCopyFromBuffer(Request,
                            &queueContext->DeviceContext->HidDeviceAttributes,
                            sizeof(HID_DEVICE_ATTRIBUTES));
        break;

    case IOCTL_HID_GET_REPORT_DESCRIPTOR:   // METHOD_NEITHER
        //
        //Obtains the report descriptor for the HID device.
        //
        status = RequestCopyFromBuffer(Request,
                            deviceContext->ReportDescriptor,
                            deviceContext->HidDescriptor.DescriptorList[0].wReportLength);
        break;

    case IOCTL_HID_READ_REPORT:             // METHOD_NEITHER
        //
        // Returns a report from the device into a class driver-supplied
        // buffer.
        //
        status = ReadReport(queueContext, Request, &completeRequest);
        break;

    case IOCTL_HID_WRITE_REPORT:            // METHOD_NEITHER
        //
        // Transmits a class driver-supplied report to the device.
        //
        status = WriteReport(queueContext, Request);
        break;

#ifdef _KERNEL_MODE

    case IOCTL_HID_GET_FEATURE:             // METHOD_OUT_DIRECT

        status = GetFeature(queueContext, Request);
        break;

    case IOCTL_HID_SET_FEATURE:             // METHOD_IN_DIRECT

        status = SetFeature(queueContext, Request);
        break;

    case IOCTL_HID_GET_INPUT_REPORT:        // METHOD_OUT_DIRECT

        status = GetInputReport(queueContext, Request);
        break;

    case IOCTL_HID_SET_OUTPUT_REPORT:       // METHOD_IN_DIRECT

        status = SetOutputReport(queueContext, Request);
        break;

#else // UMDF specific

    //
    // HID minidriver IOCTL uses HID_XFER_PACKET which contains an embedded pointer.
    //
    //   typedef struct _HID_XFER_PACKET {
    //     PUCHAR reportBuffer;
    //     ULONG  reportBufferLen;
    //     UCHAR  reportId;
    //   } HID_XFER_PACKET, *PHID_XFER_PACKET;
    //
    // UMDF cannot handle embedded pointers when marshalling buffers between processes.
    // Therefore a special driver mshidumdf.sys is introduced to convert such IRPs to
    // new IRPs (with new IOCTL name like IOCTL_UMDF_HID_Xxxx) where:
    //
    //   reportBuffer - passed as one buffer inside the IRP
    //   reportId     - passed as a second buffer inside the IRP
    //
    // The new IRP is then passed to UMDF host and driver for further processing.
    //

    case IOCTL_UMDF_HID_GET_FEATURE:        // METHOD_NEITHER

        status = GetFeature(queueContext, Request);
        break;

    case IOCTL_UMDF_HID_SET_FEATURE:        // METHOD_NEITHER

        status = SetFeature(queueContext, Request);
        break;

    case IOCTL_UMDF_HID_GET_INPUT_REPORT:  // METHOD_NEITHER

        status = GetInputReport(queueContext, Request);
        break;

    case IOCTL_UMDF_HID_SET_OUTPUT_REPORT: // METHOD_NEITHER

        status = SetOutputReport(queueContext, Request);
        break;

#endif // _KERNEL_MODE

    case IOCTL_HID_GET_STRING:                      // METHOD_NEITHER

        status = GetString(Request);
        break;

    case IOCTL_HID_GET_INDEXED_STRING:              // METHOD_OUT_DIRECT

        status = GetIndexedString(Request);
        break;

    case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST:  // METHOD_NEITHER
        //
        // This has the USBSS Idle notification callback. If the lower driver
        // can handle it (e.g. USB stack can handle it) then pass it down
        // otherwise complete it here as not inplemented. For a virtual
        // device, idling is not needed.
        //
        // Not implemented. fall through...
        //
    case IOCTL_HID_ACTIVATE_DEVICE:                 // METHOD_NEITHER
    case IOCTL_HID_DEACTIVATE_DEVICE:               // METHOD_NEITHER
    case IOCTL_GET_PHYSICAL_DESCRIPTOR:             // METHOD_OUT_DIRECT
        //
        // We don't do anything for these IOCTLs but some minidrivers might.
        //
        // Not implemented. fall through...
        //
    default:
        status = STATUS_NOT_IMPLEMENTED;
        break;
    }

    //
    // Complete the request. Information value has already been set by request
    // handlers.
    //
    if (completeRequest) {
        WdfRequestComplete(Request, status);
    }
}
コード例 #6
0
NTSTATUS
QueueCreate(
    _In_  WDFDEVICE         Device,
    _Out_ WDFQUEUE          *Queue
    )
/*++
Routine Description:

    This function creates a default, parallel I/O queue to proces IOCTLs
    from hidclass.sys.

Arguments:

    Device - Handle to a framework device object.

    Queue - Output pointer to a framework I/O queue handle, on success.

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status;
    WDF_IO_QUEUE_CONFIG     queueConfig;
    WDF_OBJECT_ATTRIBUTES   queueAttributes;
    WDFQUEUE                queue;
    PQUEUE_CONTEXT          queueContext;

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
                            &queueConfig,
                            WdfIoQueueDispatchParallel);

#ifdef _KERNEL_MODE
    queueConfig.EvtIoInternalDeviceControl  = EvtIoDeviceControl;
#else
    //
    // HIDclass uses INTERNAL_IOCTL which is not supported by UMDF. Therefore
    // the hidumdf.sys changes the IOCTL type to DEVICE_CONTROL for next stack
    // and sends it down
    //
    queueConfig.EvtIoDeviceControl          = EvtIoDeviceControl;
#endif

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
                            &queueAttributes,
                            QUEUE_CONTEXT);

    status = WdfIoQueueCreate(
                            Device,
                            &queueConfig,
                            &queueAttributes,
                            &queue);

    if( !NT_SUCCESS(status) ) {
        KdPrint(("WdfIoQueueCreate failed 0x%x\n",status));
        return status;
    }

    queueContext = GetQueueContext(queue);
    queueContext->Queue         = queue;
    queueContext->DeviceContext = GetDeviceContext(Device);
    queueContext->OutputReport  = 0;

    *Queue = queue;
    return status;
}
コード例 #7
0
VOID Xfer_ReadBulk (
    __in WDFQUEUE Queue,
    __in WDFREQUEST Request)
{
	NTSTATUS                status;
	PREQUEST_CONTEXT        requestContext = NULL;
	PQUEUE_CONTEXT			queueContext = NULL;
	PDEVICE_CONTEXT         deviceContext;
	ULONG					remainingLength;
	ULONG					stageLength = 0;
	ULONG					remainderLength = 0;
	WDF_REQUEST_SEND_OPTIONS sendOptions;
	PUCHAR					transferBuffer;


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

	VALIDATE_REQUEST_CONTEXT(requestContext, status);
	if (!NT_SUCCESS(status)) goto Exit;

	if ((queueContext = GetQueueContext(Queue)) == NULL)
	{
		status = STATUS_INVALID_DEVICE_REQUEST;
		USBERRN("Invalid queue context");
		goto Exit;
	}

	queueContext->Xfer.Transferred = 0;
	queueContext->Xfer.Length = requestContext->Length;

	if (!queueContext->OverMem)
	{
		status = STATUS_INVALID_DEVICE_REQUEST;
		USBERRN("Invalid queue context:OverMem");
		goto Exit;
	}

	if (!queueContext->Info.MaximumPacketSize)
	{
		status = STATUS_INVALID_BUFFER_SIZE;
		USBERRN("PipeID=%02Xh MaximumPacketSize=0", queueContext->Info.EndpointAddress);
		goto Exit;
	}

	// handle pipe reset scenarios: ResetPipeOnResume, AutoClearStall
	mXfer_HandlePipeResetScenarios(status, queueContext, requestContext);

	// Handle special case scenario where read length = 0
	if (!queueContext->Xfer.Length)
	{
		// if AllowPartialReads complete successfully for 0 bytes
		status = requestContext->Policies.AllowPartialReads ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
		goto Exit;
	}

	// Init queue user memory
	status = GetTransferMemory(Request, requestContext->ActualRequestType, &queueContext->Xfer.UserMem);
	if (!NT_SUCCESS(status))
	{
		USBERR("GetTransferMemory failed. Status=%08Xh\n", status);
		goto Exit;
	}
	queueContext->Xfer.UserOfs.BufferOffset = 0;
	queueContext->Xfer.UserOfs.BufferLength = queueContext->Xfer.Length;

	// Check if there are bytes left over from a previous transfer.
	if (queueContext->OverOfs.BufferLength > 0)
	{
		// Copy partial read bytes bytes into UserMem
		remainingLength = queueContext->Xfer.Length - queueContext->Xfer.Transferred;
		stageLength = (ULONG)queueContext->OverOfs.BufferLength > remainingLength ? remainingLength : (ULONG)queueContext->OverOfs.BufferLength;
		transferBuffer = &queueContext->OverBuf[queueContext->OverOfs.BufferOffset];

		mXfer_CopyPartialReadToUserMemory(status, queueContext, transferBuffer, stageLength, goto Exit);

		USBDBGN("PipeID=%02Xh Transferred %u bytes from a previous partial read.",
		        queueContext->Info.EndpointAddress, queueContext->Xfer.Transferred);

		if (queueContext->Xfer.Transferred >= queueContext->Xfer.Length)
		{
			if (requestContext->Policies.AutoFlush)
			{
				// discard any extra partial read bytes
				queueContext->OverOfs.BufferLength = 0;
				queueContext->OverOfs.BufferOffset = 0;
			}

			USBMSGN("PipeID=%02Xh DoneReason: Transferred==Requested. Transferred=%u",
			        queueContext->Info.EndpointAddress,	queueContext->Xfer.Transferred);
			status = STATUS_SUCCESS;
			goto Exit;
		}

		/*
		It would seem if IgnoreShortPackets=FALSE we would complete the request here.
		However, WinUSB does not handle it this way and will still submit the request.
		*/
#if 0
		else if (!requestContext->Policies.IgnoreShortPackets)
		{
			USBMSGN("PipeID=%02Xh DoneReason: IgnoreShortPackets=FALSE. Transferred=%u",
			        queueContext->Info.EndpointAddress,	queueContext->Xfer.Transferred);
			status = STATUS_SUCCESS;
			goto Exit;
		}
#endif
	}
コード例 #8
0
VOID PipeQueue_OnIoControl(__in WDFQUEUE Queue,
						   __in WDFREQUEST Request,
						   __in size_t OutputBufferLength,
						   __in size_t InputBufferLength,
						   __in ULONG IoControlCode)
{
	NTSTATUS status;
	ULONG length = 0;
	PREQUEST_CONTEXT requestContext = GetRequestContext(Request);
	PQUEUE_CONTEXT queueContext = NULL;

	VALIDATE_REQUEST_CONTEXT(requestContext, status);
	if (!NT_SUCCESS(status)) goto Done;

	if ((queueContext = GetQueueContext(Queue)) == NULL)
	{
		status = STATUS_INVALID_DEVICE_REQUEST;
		USBERRN("Invalid queue context");
		goto Done;
	}

	switch(IoControlCode)
	{
	case LIBUSB_IOCTL_ISOCHRONOUS_READ:
	case LIBUSB_IOCTL_ISOCHRONOUS_WRITE:
	case LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE:
	case LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ:

		switch (queueContext->Info.PipeType)
		{
		case WdfUsbPipeTypeIsochronous:

			if (USB_ENDPOINT_DIRECTION_IN(queueContext->Info.EndpointAddress))
			{
				XferIsoRead(Queue, Request);
				return;
			}
			XferIsoWrite(Queue, Request);
			return;

		case WdfUsbPipeTypeBulk:
		case WdfUsbPipeTypeInterrupt:
			if(requestContext->Policies.RawIO)
			{
				if (USB_ENDPOINT_DIRECTION_IN(queueContext->Info.EndpointAddress))
					Xfer_ReadBulkRaw(Queue, Request);
				else
					Xfer_WriteBulkRaw(Queue, Request);
			}
			else
			{
				if (USB_ENDPOINT_DIRECTION_IN(queueContext->Info.EndpointAddress))
					Xfer_ReadBulk(Queue, Request);
				else
					Xfer_WriteBulk(Queue, Request);
			}
			return;

		default:
			status = STATUS_INVALID_PARAMETER;
			USBERRN("Invalid PipeType=%s\n", GetPipeTypeString(queueContext->Info.PipeType));
			break;
		}

	case LIBUSBK_IOCTL_ISOEX_READ:
	case LIBUSBK_IOCTL_ISOEX_WRITE:
		if (queueContext->Info.PipeType == WdfUsbPipeTypeIsochronous)
		{
			XferIsoEx(Queue, Request);
			return;
		}
		status = STATUS_INVALID_PARAMETER;
		USBERRN("Invalid PipeType=%s\n", GetPipeTypeString(queueContext->Info.PipeType));
		break;

		/*
		case LIBUSBK_IOCTL_AUTOISOEX_READ:
		case LIBUSBK_IOCTL_AUTOISOEX_WRITE:
			if (queueContext->Info.PipeType == WdfUsbPipeTypeIsochronous)
			{
				XferAutoIsoEx(Queue, Request);
				return;
			}
			status = STATUS_INVALID_PARAMETER;
			USBERRN("Invalid PipeType=%s\n", GetPipeTypeString(queueContext->Info.PipeType));
			break;
		*/

	case LIBUSB_IOCTL_SET_FEATURE:
	case LIBUSB_IOCTL_CLEAR_FEATURE:
	case LIBUSB_IOCTL_GET_DESCRIPTOR:
	case LIBUSB_IOCTL_SET_DESCRIPTOR:
	case LIBUSB_IOCTL_VENDOR_WRITE:
	case LIBUSB_IOCTL_VENDOR_READ:
	case LIBUSB_IOCTL_CONTROL_READ:
	case LIBUSB_IOCTL_CONTROL_WRITE:

		XferCtrl(Queue, Request, InputBufferLength, OutputBufferLength);
		return;

	default:

		USBERR("unknown IoControlCode %Xh (function=%04Xh)\n", IoControlCode, FUNCTION_FROM_CTL_CODE(IoControlCode));
		status = STATUS_INVALID_DEVICE_REQUEST;
		WdfRequestCompleteWithInformation(Request, status, 0);

		return;
	}

Done:
	WdfRequestCompleteWithInformation(Request, status, length);
	return;

}
コード例 #9
0
VOID PipeQueue_OnWrite(__in WDFQUEUE Queue,
					   __in WDFREQUEST Request,
					   __in size_t InputBufferLength)
{
	NTSTATUS				status = STATUS_SUCCESS;
	PDEVICE_CONTEXT         deviceContext;
	PREQUEST_CONTEXT		requestContext;
	PQUEUE_CONTEXT queueContext = NULL;

	UNREFERENCED_PARAMETER(InputBufferLength);

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

	VALIDATE_REQUEST_CONTEXT(requestContext, status);
	if (!NT_SUCCESS(status)) goto Done;

	if ((queueContext = GetQueueContext(Queue)) == NULL)
	{
		status = STATUS_INVALID_DEVICE_REQUEST;
		USBERRN("Invalid queue context");
		goto Done;
	}

	if (!queueContext->PipeHandle)
	{
		USBERR("null pipe handle\n");
		status = STATUS_INVALID_HANDLE;
		goto Done;
	}

	switch(queueContext->Info.PipeType)
	{
	case WdfUsbPipeTypeIsochronous:
		if (USB_ENDPOINT_DIRECTION_OUT(queueContext->Info.EndpointAddress))
		{
			XferIsoWrite(Queue, Request);
			return;
		}
		break;

	case WdfUsbPipeTypeBulk:
	case WdfUsbPipeTypeInterrupt:
		if (USB_ENDPOINT_DIRECTION_IN(queueContext->Info.EndpointAddress))
		{
			status = STATUS_INVALID_DEVICE_REQUEST;
			USBERRN("Cannot write to an IN pipe.");
			goto Done;
		}
		if(requestContext->Policies.RawIO)
			Xfer_WriteBulkRaw(Queue, Request);
		else
			Xfer_WriteBulk(Queue, Request);
	}

	status = STATUS_INVALID_DEVICE_REQUEST;
	USBERRN("PipeID=%02Xh Invalid request", queueContext->Info.EndpointAddress);

Done:
	WdfRequestCompleteWithInformation(Request, status, 0);

}