You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1515 lines
43 KiB
1515 lines
43 KiB
/***************************************************************************
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
INTREAD.C
|
|
|
|
Abstract:
|
|
|
|
Generic USB routines - must be called at PASSIVE_LEVEL
|
|
|
|
Environment:
|
|
|
|
Kernel Mode Only
|
|
|
|
Notes:
|
|
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
PURPOSE.
|
|
|
|
Copyright (c) 2001 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
01/08/2001 : created
|
|
|
|
Authors:
|
|
|
|
Tom Green
|
|
|
|
|
|
****************************************************************************/
|
|
|
|
#include "pch.h"
|
|
|
|
#include <usb.h>
|
|
#include <usbdrivr.h>
|
|
#include <usbdlib.h>
|
|
#include <ntstatus.h>
|
|
|
|
#include "usbutil.h"
|
|
#include "usbdbg.h"
|
|
#include "intread.h"
|
|
#include "usbpriv.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, USBCallSyncEx)
|
|
#pragma alloc_text(PAGE, USBVendorRequestEx)
|
|
#pragma alloc_text(PAGE, USBClassRequestEx)
|
|
#pragma alloc_text(PAGE, USBStartInterruptTransfers)
|
|
#pragma alloc_text(PAGE, USBStopInterruptTransfers)
|
|
#pragma alloc_text(PAGE, USBStartSelectiveSuspend)
|
|
#pragma alloc_text(PAGE, USBStopSelectiveSuspend)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapInitializeInterruptReadData
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
|
|
NTSTATUS UsbWrapInitializeInterruptReadData(
|
|
IN PUSB_WRAPPER_EXTENSION WrapExtension,
|
|
IN PUSBD_PIPE_INFORMATION InterruptPipe,
|
|
IN INTERRUPT_CALLBACK DriverCallback,
|
|
IN PVOID DriverContext,
|
|
IN ULONG MaxTransferSize,
|
|
IN ULONG NotificationTypes,
|
|
IN ULONG PingPongCount)
|
|
{
|
|
|
|
WrapExtension->IntReadWrap.InterruptPipe = InterruptPipe;
|
|
WrapExtension->IntReadWrap.MaxTransferSize = MaxTransferSize;
|
|
WrapExtension->IntReadWrap.ClientCallback = DriverCallback;
|
|
WrapExtension->IntReadWrap.ClientContext = DriverContext;
|
|
WrapExtension->IntReadWrap.NotificationTypes = NotificationTypes;
|
|
WrapExtension->IntReadWrap.NumPingPongs = PingPongCount;
|
|
WrapExtension->IntReadWrap.OutstandingRequests = 0;
|
|
WrapExtension->IntReadWrap.WorkItemRunning = 0;
|
|
WrapExtension->IntReadWrap.TransferCount = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapInitializePingPongIrps
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
|
|
NTSTATUS UsbWrapInitializePingPongIrps(PUSB_WRAPPER_EXTENSION WrapExtension)
|
|
{
|
|
|
|
NTSTATUS result = STATUS_SUCCESS;
|
|
ULONG i;
|
|
CCHAR numIrpStackLocations;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapInitializePingPongIrps\n"));
|
|
|
|
numIrpStackLocations = WrapExtension->LowerDeviceObject->StackSize;
|
|
|
|
//
|
|
// Initialize the queues for interrupt read data
|
|
//
|
|
|
|
InitializeListHead(&WrapExtension->IntReadWrap.SavedQueue);
|
|
InitializeListHead(&WrapExtension->IntReadWrap.IncomingQueue);
|
|
KeInitializeSpinLock(&WrapExtension->IntReadWrap.QueueLock);
|
|
|
|
ASSERT(WrapExtension->IntReadWrap.NumPingPongs > 0);
|
|
|
|
WrapExtension->IntReadWrap.PingPongs = ALLOC_MEM(NonPagedPool,
|
|
WrapExtension->IntReadWrap.NumPingPongs*sizeof(USB_WRAPPER_PINGPONG),
|
|
USBWRAP_TAG);
|
|
|
|
if (WrapExtension->IntReadWrap.PingPongs){
|
|
|
|
ULONG transferBufferSize = WrapExtension->IntReadWrap.MaxTransferSize;
|
|
|
|
RtlZeroMemory(WrapExtension->IntReadWrap.PingPongs,
|
|
WrapExtension->IntReadWrap.NumPingPongs*sizeof(USB_WRAPPER_PINGPONG));
|
|
|
|
for (i = 0; i < WrapExtension->IntReadWrap.NumPingPongs; i++) {
|
|
|
|
WrapExtension->IntReadWrap.PingPongs[i].myWrapExt = WrapExtension;
|
|
WrapExtension->IntReadWrap.PingPongs[i].weAreCancelling = 0;
|
|
WrapExtension->IntReadWrap.PingPongs[i].sig = PINGPONG_SIG;
|
|
|
|
|
|
WrapExtension->IntReadWrap.PingPongs[i].irp = IoAllocateIrp(numIrpStackLocations+1, FALSE);
|
|
|
|
if (WrapExtension->IntReadWrap.PingPongs[i].irp){
|
|
|
|
PURB pUrb;
|
|
|
|
|
|
KeInitializeEvent(&WrapExtension->IntReadWrap.PingPongs[i].sentEvent,
|
|
NotificationEvent,
|
|
TRUE); // Set to signaled
|
|
KeInitializeEvent(&WrapExtension->IntReadWrap.PingPongs[i].pumpDoneEvent,
|
|
NotificationEvent,
|
|
TRUE); // Set to signaled
|
|
|
|
pUrb = ALLOC_MEM( NonPagedPool, sizeof(URB), USBWRAP_TAG);
|
|
|
|
if(pUrb) {
|
|
|
|
USHORT size;
|
|
PIO_STACK_LOCATION NextStack = NULL;
|
|
RtlZeroMemory(pUrb, sizeof(URB));
|
|
|
|
|
|
WrapExtension->IntReadWrap.PingPongs[i].urb = pUrb;
|
|
|
|
|
|
} else {
|
|
|
|
result = STATUS_INSUFFICIENT_RESOURCES;
|
|
FREE_MEM(WrapExtension->IntReadWrap.PingPongs[i].irp);
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
result = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapInitializePingPongIrps (0x%x)\n", result));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapSubmitInterruptRead
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS UsbWrapSubmitInterruptRead(
|
|
IN PUSB_WRAPPER_EXTENSION WrapExtension,
|
|
PUSB_WRAPPER_PINGPONG PingPong,
|
|
BOOLEAN *IrpSent)
|
|
{
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KIRQL oldIrql;
|
|
BOOLEAN proceed;
|
|
LONG oldInterlock;
|
|
ULONG transferBufferSize;
|
|
PIRP irp = PingPong->irp;
|
|
PURB urb = PingPong->urb;
|
|
|
|
|
|
ASSERT(irp);
|
|
|
|
*IrpSent = FALSE;
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapSubmitInterruptRead\n"));
|
|
|
|
|
|
while (1) {
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
oldInterlock = InterlockedExchange(&PingPong->ReadInterlock,
|
|
PINGPONG_START_READ);
|
|
ASSERT(oldInterlock == PINGPONG_END_READ);
|
|
|
|
irp->Cancel = FALSE;
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
|
|
|
|
/*
|
|
* Send down the read IRP.
|
|
*/
|
|
KeResetEvent(&PingPong->sentEvent);
|
|
|
|
if (PingPong->weAreCancelling) {
|
|
|
|
InterlockedDecrement(&PingPong->weAreCancelling);
|
|
//
|
|
// Ordering of the next two instructions is crucial, since
|
|
// CancelPingPongs will exit after pumpDoneEvent is set, and the
|
|
// pingPongs could be deleted after that.
|
|
//
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("Pingpong %x cancelled in submit before sending\n", PingPong))
|
|
KeSetEvent (&PingPong->sentEvent, 0, FALSE);
|
|
KeSetEvent(&PingPong->pumpDoneEvent, 0, FALSE);
|
|
status = STATUS_CANCELLED;
|
|
break;
|
|
|
|
} else {
|
|
|
|
PIO_STACK_LOCATION NextStack = NULL;
|
|
|
|
UsbBuildInterruptOrBulkTransferRequest(PingPong->urb,
|
|
(USHORT) sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
|
|
WrapExtension->IntReadWrap.InterruptPipe->PipeHandle,
|
|
NULL,
|
|
NULL,
|
|
WrapExtension->IntReadWrap.MaxTransferSize,
|
|
USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
|
|
NULL);
|
|
|
|
urb->UrbBulkOrInterruptTransfer.TransferBuffer = UsbWrapGetTransferBuffer(WrapExtension);
|
|
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = WrapExtension->IntReadWrap.MaxTransferSize;
|
|
|
|
if (urb->UrbBulkOrInterruptTransfer.TransferBuffer == NULL) {
|
|
|
|
KeSetEvent (&PingPong->sentEvent, 0, FALSE);
|
|
KeSetEvent(&PingPong->pumpDoneEvent, 0, FALSE);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
}
|
|
|
|
InterlockedIncrement(&WrapExtension->IntReadWrap.OutstandingRequests);
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("Sending pingpong %x from Submit\n", PingPong))
|
|
|
|
|
|
|
|
|
|
IoSetCompletionRoutine( irp,
|
|
UsbWrapInterruptReadComplete,
|
|
WrapExtension, // context
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
|
|
|
|
NextStack = IoGetNextIrpStackLocation(PingPong->irp);
|
|
|
|
ASSERT(NextStack);
|
|
|
|
NextStack->Parameters.Others.Argument1 = PingPong->urb;
|
|
NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
NextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
|
|
NextStack->DeviceObject = WrapExtension->LowerDeviceObject;
|
|
|
|
|
|
status = IoCallDriver(WrapExtension->LowerDeviceObject, irp);
|
|
KeSetEvent (&PingPong->sentEvent, 0, FALSE);
|
|
*IrpSent = TRUE;
|
|
|
|
}
|
|
|
|
if (PINGPONG_IMMEDIATE_READ != InterlockedExchange(&PingPong->ReadInterlock,
|
|
PINGPONG_END_READ)) {
|
|
//
|
|
// The read is asynch, will call SubmitInterruptRead from the
|
|
// completion routine
|
|
//
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("read is pending\n"))
|
|
break;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The read was synchronous (probably bytes in the buffer). The
|
|
// completion routine will not call SubmitInterruptRead, so we
|
|
// just loop here. This is to prevent us from running out of stack
|
|
// space if always call StartRead from the completion routine
|
|
//
|
|
status = irp->IoStatus.Status;
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("read is looping with status %x\n", status))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// if (PingPong->weAreCancelling ) {
|
|
|
|
// We are stopping the read pump.
|
|
// set this event and stop resending the pingpong IRP.
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("We are cancelling bit set for pingpong %x\n", PingPong))
|
|
// InterlockedDecrement(&PingPong->weAreCancelling);
|
|
KeSetEvent(&PingPong->pumpDoneEvent, 0, FALSE);
|
|
|
|
break;
|
|
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapSubmitInterruptRead (0x%x)\n", status));
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapIsDeviceConnected
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS
|
|
UsbWrapIsDeviceConnected (
|
|
IN PUSB_WRAPPER_EXTENSION WrapExt
|
|
)
|
|
{
|
|
PIRP irp;
|
|
KEVENT event;
|
|
PIO_STACK_LOCATION nextStack;
|
|
ULONG portStatus;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK iostatus;
|
|
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapIsDeviceConnected\n"));
|
|
|
|
|
|
// Initialize the event we'll wait on.
|
|
//
|
|
KeInitializeEvent(&event,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
// Allocate the Irp
|
|
//
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_GET_PORT_STATUS,
|
|
WrapExt->LowerDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
&event,
|
|
&iostatus);
|
|
|
|
if (irp == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Set the Irp parameters
|
|
//
|
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
nextStack->Parameters.Others.Argument1 = &portStatus;
|
|
|
|
// Pass the Irp down the stack
|
|
//
|
|
status = IoCallDriver(WrapExt->LowerDeviceObject,
|
|
irp);
|
|
|
|
// If the request is pending, block until it completes
|
|
//
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = iostatus.Status;
|
|
}
|
|
|
|
if (NT_SUCCESS(status) && !(portStatus & USBD_PORT_CONNECTED))
|
|
{
|
|
status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapIsDeviceConnected (0x%x)\n", status));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapResetDevice
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS
|
|
UsbWrapResetDevice (
|
|
IN PUSB_WRAPPER_EXTENSION WrapExt
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK iostatus;
|
|
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapResetDevice\n"));
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_RESET_PORT,
|
|
WrapExt->LowerDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
&event,
|
|
&iostatus);
|
|
|
|
if (irp == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
status = IoCallDriver(WrapExt->LowerDeviceObject, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = iostatus.Status;
|
|
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapResetDevice (0x%x)\n", status));
|
|
|
|
|
|
return status;
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapErrorHandlerWorkRoutine
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID
|
|
UsbWrapErrorHandlerWorkRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context)
|
|
{
|
|
|
|
PUSB_WRAPPER_EXTENSION wrapExt = ((PUSB_WRAPPER_WORKITEM_CONTEXT) Context)->WrapExtension;
|
|
PIO_WORKITEM workItem = ((PUSB_WRAPPER_WORKITEM_CONTEXT) Context)->WorkItem;
|
|
PUSB_WRAPPER_PINGPONG pingPong = ((PUSB_WRAPPER_WORKITEM_CONTEXT) Context)->PingPong;
|
|
NTSTATUS status;
|
|
|
|
BOOLEAN errorHandled = FALSE;
|
|
|
|
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapErrorHandlerWorkRoutine\n"));
|
|
|
|
status = IoAcquireRemoveLockEx(wrapExt->RemoveLock,
|
|
DeviceObject,
|
|
__FILE__,
|
|
__LINE__,
|
|
wrapExt->RemLockSize);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DBGPRINT(DBG_USBUTIL_ERROR, ("UsbWrapErrorHandlerWorkRoutine: exiting due to removal\n"));
|
|
goto ErrorWorkItemComplete;
|
|
|
|
}
|
|
// Lets first stop all ping pongs.
|
|
UsbWrapCancelAllPingPongIrps(wrapExt);
|
|
|
|
//
|
|
// Notify Client of the error and give them a chance to handle it
|
|
//
|
|
if (wrapExt->IntReadWrap.NotificationTypes & USBWRAP_NOTIFICATION_READ_ERROR) {
|
|
|
|
status = (wrapExt->IntReadWrap.ClientCallback)(wrapExt->IntReadWrap.ClientContext,
|
|
&pingPong->irp->IoStatus.Status,
|
|
sizeof(NTSTATUS),
|
|
USBWRAP_NOTIFICATION_READ_ERROR,
|
|
&errorHandled);
|
|
|
|
|
|
}
|
|
|
|
if(!errorHandled) {
|
|
|
|
// The client didn't handle it, lets try to fix it ourselves by resetting the pipe
|
|
|
|
ULONG retryCount;
|
|
|
|
// Try the reset up to 3 times
|
|
//
|
|
for (retryCount = 0; retryCount < 3; retryCount++)
|
|
{
|
|
//
|
|
// First figure out if the device is still connected.
|
|
//
|
|
status = UsbWrapIsDeviceConnected(wrapExt);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// Give up if the device is no longer connected.
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The device is still connected, now reset the device.
|
|
//
|
|
status = UsbWrapResetDevice(wrapExt);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
// reset was successful
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
InterlockedExchange(&wrapExt->IntReadWrap.HandlingError, 0);
|
|
|
|
if (NT_SUCCESS(status) && pingPong->irp->IoStatus.Status != STATUS_DEVICE_NOT_CONNECTED) {
|
|
|
|
UsbWrapStartAllPingPongs(wrapExt);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
IoReleaseRemoveLockEx(wrapExt->RemoveLock,
|
|
DeviceObject,
|
|
wrapExt->RemLockSize);
|
|
ErrorWorkItemComplete:
|
|
|
|
FREE_MEM(Context);
|
|
IoFreeWorkItem(workItem);
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapErrorHandlerWorkRoutine\n"));
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapWorkRoutine
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID
|
|
UsbWrapWorkRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context)
|
|
{
|
|
|
|
BOOLEAN QueueData = FALSE;
|
|
PVOID buffer;
|
|
ULONG dataLength;
|
|
NTSTATUS status;
|
|
PUSB_WRAPPER_EXTENSION wrapExtension = ((PUSB_WRAPPER_WORKITEM_CONTEXT) Context)->WrapExtension;
|
|
PIO_WORKITEM workItem = ((PUSB_WRAPPER_WORKITEM_CONTEXT) Context)->WorkItem;
|
|
KIRQL irql;
|
|
|
|
__try
|
|
{
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapWorkRoutine\n"));
|
|
|
|
KeAcquireSpinLock(&wrapExtension->IntReadWrap.QueueLock,
|
|
&irql);
|
|
|
|
if(wrapExtension->IntReadWrap.PumpState == PUMP_STOPPED) {
|
|
|
|
wrapExtension->IntReadWrap.WorkItemRunning--;
|
|
ASSERT(wrapExtension->IntReadWrap.WorkItemRunning == 0);
|
|
|
|
KeReleaseSpinLock(&wrapExtension->IntReadWrap.QueueLock,
|
|
irql);
|
|
|
|
__leave;
|
|
}
|
|
|
|
|
|
|
|
while(!IsListEmpty(&wrapExtension->IntReadWrap.IncomingQueue)) {
|
|
|
|
|
|
UsbWrapDequeueData(wrapExtension,
|
|
&buffer,
|
|
&dataLength,
|
|
&wrapExtension->IntReadWrap.IncomingQueue);
|
|
|
|
if (!buffer) {
|
|
wrapExtension->IntReadWrap.WorkItemRunning--;
|
|
ASSERT(wrapExtension->IntReadWrap.WorkItemRunning == 0);
|
|
}
|
|
|
|
KeReleaseSpinLock(&wrapExtension->IntReadWrap.QueueLock,
|
|
irql);
|
|
|
|
DBGPRINT(DBG_USBUTIL_OTHER_ERROR, (" Dequeued Data buffer: 0x%x\n", buffer));
|
|
|
|
if(!buffer) {
|
|
|
|
//
|
|
// This data had better be there!!!
|
|
//
|
|
__leave;
|
|
|
|
}
|
|
|
|
status = (wrapExtension->IntReadWrap.ClientCallback)(wrapExtension->IntReadWrap.ClientContext,
|
|
buffer,
|
|
dataLength,
|
|
USBWRAP_NOTIFICATION_READ_COMPLETE,
|
|
&QueueData);
|
|
|
|
if(QueueData) {
|
|
|
|
|
|
UsbWrapEnqueueData(wrapExtension,
|
|
buffer,
|
|
dataLength,
|
|
&wrapExtension->IntReadWrap.SavedQueue);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (!(wrapExtension->IntReadWrap.NotificationTypes & USBWRAP_NOTIFICATION_BUFFER_CLIENT_FREE)) {
|
|
|
|
UsbWrapFreeTransferBuffer(wrapExtension,
|
|
buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
KeAcquireSpinLock(&wrapExtension->IntReadWrap.QueueLock,
|
|
&irql);
|
|
}
|
|
|
|
wrapExtension->IntReadWrap.WorkItemRunning--;
|
|
|
|
KeReleaseSpinLock(&wrapExtension->IntReadWrap.QueueLock,
|
|
irql);
|
|
|
|
}
|
|
|
|
__finally
|
|
{
|
|
|
|
if (workItem) {
|
|
|
|
IoFreeWorkItem(workItem);
|
|
|
|
// only want to free the context if this was actually executed in a
|
|
// Worker thread. If it was called directly, then this was allocated
|
|
// on the stack.
|
|
FREE_MEM(Context);
|
|
|
|
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapWorkRoutine\n"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* GetPingPongFromIrp
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
USB_WRAPPER_PINGPONG *GetPingPongFromIrp(
|
|
PUSB_WRAPPER_EXTENSION WrapExt,
|
|
PIRP irp)
|
|
{
|
|
|
|
USB_WRAPPER_PINGPONG *pingPong = NULL;
|
|
ULONG i;
|
|
|
|
for (i = 0; i < WrapExt->IntReadWrap.NumPingPongs; i++){
|
|
|
|
if (WrapExt->IntReadWrap.PingPongs[i].irp == irp){
|
|
|
|
pingPong = &WrapExt->IntReadWrap.PingPongs[i];
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT(pingPong);
|
|
return pingPong;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapInterruptReadComplete
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS UsbWrapInterruptReadComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
|
|
PUSB_WRAPPER_EXTENSION wrapExtension = (PUSB_WRAPPER_EXTENSION)Context;
|
|
PUSB_WRAPPER_PINGPONG pingPong;
|
|
KIRQL oldIrql;
|
|
BOOLEAN startRead;
|
|
BOOLEAN errorHandled = FALSE;
|
|
BOOLEAN startWorkItem = FALSE;
|
|
NTSTATUS status;
|
|
BOOLEAN resend = TRUE;
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapInterruptReadComplete %p\n", Irp));
|
|
|
|
//
|
|
// Track the number of outstanding requests to this device.
|
|
//
|
|
ASSERT(wrapExtension->IntReadWrap.OutstandingRequests > 0 );
|
|
InterlockedDecrement(&wrapExtension->IntReadWrap.OutstandingRequests);
|
|
DeviceObject = wrapExtension->DeviceObject;
|
|
pingPong = GetPingPongFromIrp(wrapExtension, Irp);
|
|
|
|
ASSERT(DeviceObject);
|
|
if (!pingPong) {
|
|
|
|
//
|
|
// Something is terribly wrong, but do nothing. Hopefully
|
|
// just exiting will clear up this pimple.
|
|
//
|
|
DBGPRINT(DBG_USBUTIL_ERROR,("A pingPong structure could not be found!!! Have this looked at!"))
|
|
goto InterruptReadCompleteExit;
|
|
|
|
}
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
|
|
|
|
if (wrapExtension->IntReadWrap.NotificationTypes & (USBWRAP_NOTIFICATION_READ_COMPLETE | USBWRAP_NOTIFICATION_READ_COMPLETE_DIRECT)) {
|
|
|
|
PIO_WORKITEM workItem;
|
|
PUSB_WRAPPER_WORKITEM_CONTEXT workItemContext;
|
|
ULONG count;
|
|
|
|
UsbWrapEnqueueData(wrapExtension,
|
|
pingPong->urb->UrbBulkOrInterruptTransfer.TransferBuffer,
|
|
pingPong->urb->UrbBulkOrInterruptTransfer.TransferBufferLength,
|
|
&wrapExtension->IntReadWrap.IncomingQueue);
|
|
|
|
count = pingPong->urb->UrbBulkOrInterruptTransfer.TransferFlags;
|
|
|
|
DBGPRINT(DBG_USBUTIL_OTHER_ERROR,
|
|
("UsbWrapEnqueueData, %p buffer: 0x%x\n", pingPong,
|
|
pingPong->urb->UrbBulkOrInterruptTransfer.TransferBuffer));
|
|
|
|
|
|
wrapExtension->IntReadWrap.TransferCount = count;
|
|
|
|
pingPong->urb->UrbBulkOrInterruptTransfer.TransferBuffer = NULL;
|
|
|
|
KeAcquireSpinLock(&wrapExtension->IntReadWrap.QueueLock,
|
|
&oldIrql);
|
|
|
|
if (wrapExtension->IntReadWrap.WorkItemRunning) {
|
|
|
|
startWorkItem = FALSE;
|
|
|
|
} else {
|
|
|
|
startWorkItem = TRUE;
|
|
wrapExtension->IntReadWrap.WorkItemRunning++;
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock(&wrapExtension->IntReadWrap.QueueLock,
|
|
oldIrql);
|
|
|
|
if (startWorkItem) {
|
|
|
|
if (wrapExtension->IntReadWrap.NotificationTypes & USBWRAP_NOTIFICATION_READ_COMPLETE_DIRECT) {
|
|
|
|
USB_WRAPPER_WORKITEM_CONTEXT context;
|
|
|
|
context.WorkItem = NULL;
|
|
context.WrapExtension = wrapExtension;
|
|
|
|
|
|
UsbWrapWorkRoutine(DeviceObject,
|
|
&context);
|
|
|
|
} else {
|
|
|
|
workItem = IoAllocateWorkItem(DeviceObject);
|
|
|
|
if (!workItem) {
|
|
|
|
//
|
|
// Insufficient Resources
|
|
//
|
|
|
|
goto InterruptReadCompleteExit;
|
|
|
|
}
|
|
|
|
workItemContext = ALLOC_MEM(NonPagedPool, sizeof(USB_WRAPPER_WORKITEM_CONTEXT), USBWRAP_TAG);
|
|
|
|
if (!workItemContext) {
|
|
|
|
//
|
|
// Insufficient Resources;
|
|
//
|
|
|
|
goto InterruptReadCompleteExit;
|
|
|
|
}
|
|
|
|
workItemContext->WorkItem = workItem;
|
|
workItemContext->WrapExtension = wrapExtension;
|
|
|
|
//
|
|
// Queue work item to notify the client
|
|
//
|
|
|
|
IoQueueWorkItem(workItem,
|
|
UsbWrapWorkRoutine,
|
|
CriticalWorkQueue,
|
|
workItemContext);
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Client doesn't want notification, so queue data in saved queue
|
|
// so it can be read when the client is ready
|
|
//
|
|
|
|
UsbWrapEnqueueData(wrapExtension,
|
|
pingPong->urb->UrbBulkOrInterruptTransfer.TransferBuffer,
|
|
pingPong->urb->UrbBulkOrInterruptTransfer.TransferBufferLength,
|
|
&wrapExtension->IntReadWrap.SavedQueue);
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DBGPRINT(DBG_USBUTIL_ERROR,("irp failed buffer: 0x%x\n",pingPong->urb->UrbBulkOrInterruptTransfer.TransferBuffer ))
|
|
|
|
UsbWrapFreeTransferBuffer(wrapExtension, pingPong->urb->UrbBulkOrInterruptTransfer.TransferBuffer);
|
|
if ((!pingPong->weAreCancelling)) {
|
|
|
|
//
|
|
// The Irp failed.
|
|
//
|
|
|
|
ULONG i;
|
|
PIO_WORKITEM workItem;
|
|
PUSB_WRAPPER_WORKITEM_CONTEXT workItemContext;
|
|
|
|
|
|
DBGPRINT(DBG_USBUTIL_ERROR,("A pingpong irp (0x%x) failed : 0x%x\n",pingPong,Irp->IoStatus.Status ))
|
|
|
|
//
|
|
// First we must stop all Ping Pongs
|
|
//
|
|
KeSetEvent (&pingPong->sentEvent, 0, FALSE);
|
|
KeSetEvent(&pingPong->pumpDoneEvent, 0, FALSE);
|
|
// resend = FALSE;
|
|
|
|
|
|
|
|
if ((Irp->IoStatus.Status != STATUS_DEVICE_NOT_CONNECTED)
|
|
&& (!InterlockedCompareExchange(&wrapExtension->IntReadWrap.HandlingError, 1, 0))) {
|
|
|
|
// Queue a workitem to actually handle the error
|
|
|
|
workItem = IoAllocateWorkItem(DeviceObject);
|
|
|
|
if (!workItem) {
|
|
|
|
//
|
|
// Insufficient Resources
|
|
//
|
|
|
|
goto InterruptReadCompleteExit;
|
|
|
|
|
|
}
|
|
|
|
workItemContext = ALLOC_MEM(NonPagedPool, sizeof(USB_WRAPPER_WORKITEM_CONTEXT), USBWRAP_TAG);
|
|
|
|
if (!workItemContext) {
|
|
|
|
//
|
|
// Insufficient Resources;
|
|
//
|
|
|
|
goto InterruptReadCompleteExit;
|
|
|
|
}
|
|
|
|
workItemContext->WorkItem = workItem;
|
|
workItemContext->WrapExtension = wrapExtension;
|
|
workItemContext->PingPong = pingPong;
|
|
|
|
IoQueueWorkItem(workItem,
|
|
UsbWrapErrorHandlerWorkRoutine,
|
|
CriticalWorkQueue,
|
|
workItemContext);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If ReadInterlock is == START_READ, this func has been completed
|
|
// synchronously. Place IMMEDIATE_READ into the interlock to signify this
|
|
// situation; this will notify StartRead to loop when IoCallDriver returns.
|
|
// Otherwise, we have been completed async and it is safe to call StartRead()
|
|
//
|
|
startRead =
|
|
(PINGPONG_START_READ !=
|
|
InterlockedCompareExchange(&pingPong->ReadInterlock,
|
|
PINGPONG_IMMEDIATE_READ,
|
|
PINGPONG_START_READ));
|
|
|
|
|
|
|
|
//
|
|
// Business as usual.
|
|
//
|
|
if (startRead) {
|
|
|
|
if (pingPong->weAreCancelling){
|
|
|
|
// We are stopping the read pump.
|
|
// Set this event and stop resending the pingpong IRP.
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("We are cancelling bit set for pingpong %x\n", pingPong))
|
|
// InterlockedDecrement(&pingPong->weAreCancelling);
|
|
KeSetEvent(&pingPong->pumpDoneEvent, 0, FALSE);
|
|
|
|
} else {
|
|
|
|
if (Irp->IoStatus.Status == STATUS_SUCCESS) {
|
|
|
|
BOOLEAN irpSent;
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("Submitting pingpong %x from completion routine\n", pingPong))
|
|
UsbWrapSubmitInterruptRead(wrapExtension, pingPong, &irpSent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
InterruptReadCompleteExit:
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapInterruptReadComplete\n"));
|
|
/*
|
|
* ALWAYS return STATUS_MORE_PROCESSING_REQUIRED;
|
|
* otherwise, the irp is required to have a thread.
|
|
*/
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapStartAllPingPongs
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS UsbWrapStartAllPingPongs(PUSB_WRAPPER_EXTENSION WrapExt)
|
|
{
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapStartAllPingPongs\n"));
|
|
ASSERT(WrapExt->IntReadWrap.NumPingPongs > 0);
|
|
|
|
InterlockedExchange(&WrapExt->IntReadWrap.PumpState,
|
|
PUMP_STARTED);
|
|
for (i = 0; i < WrapExt->IntReadWrap.NumPingPongs; i++){
|
|
|
|
BOOLEAN irpSent;
|
|
|
|
// Different threads may be trying to start this pump at the
|
|
// same time due to idle notification. Must only start once.
|
|
if (WrapExt->IntReadWrap.PingPongs[i].pumpDoneEvent.Header.SignalState) {
|
|
|
|
WrapExt->IntReadWrap.PingPongs[i].ReadInterlock = PINGPONG_END_READ;
|
|
KeResetEvent(&WrapExt->IntReadWrap.PingPongs[i].pumpDoneEvent);
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("Starting pingpong %x from UsbWrapStartAllPingPongs\n", &WrapExt->IntReadWrap.PingPongs[i]))
|
|
status = UsbWrapSubmitInterruptRead(WrapExt, &WrapExt->IntReadWrap.PingPongs[i], &irpSent);
|
|
|
|
|
|
if (!NT_SUCCESS(status)){
|
|
|
|
if (irpSent){
|
|
|
|
DBGPRINT(DBG_USBUTIL_USB_ERROR,("Initial read failed with status %xh.", status))
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
DBGPRINT(DBG_USBUTIL_USB_ERROR,("Initial read failed, irp not sent, status = %xh.", status))
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (status == STATUS_PENDING){
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
InterlockedExchange(&WrapExt->IntReadWrap.PumpState,
|
|
PUMP_STOPPED);
|
|
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapStartAllPingPongs (0x%x)\n", status));
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapCancelAllPingPongIrps
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID UsbWrapCancelAllPingPongIrps(PUSB_WRAPPER_EXTENSION WrapExt)
|
|
{
|
|
|
|
ULONG i;
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapCancelAllPingPongIrpss\n"));
|
|
|
|
for (i = 0; i < WrapExt->IntReadWrap.NumPingPongs; i++){
|
|
|
|
USB_WRAPPER_PINGPONG *pingPong = &WrapExt->IntReadWrap.PingPongs[i];
|
|
UsbWrapCancelPingPongIrp(pingPong);
|
|
|
|
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapCancelAllPingPongIrps\n"));
|
|
|
|
}
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapCancelPingPongIrp
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
|
|
VOID UsbWrapCancelPingPongIrp(USB_WRAPPER_PINGPONG *PingPong)
|
|
{
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("Cancelling pingpong %x\n", PingPong))
|
|
ASSERT(PingPong->sig == PINGPONG_SIG);
|
|
|
|
//
|
|
// The order of the following instructions is crucial. We must set
|
|
// the weAreCancelling bit before waiting on the sentEvent, and the
|
|
// last thing that we should wait on is the pumpDoneEvent, which
|
|
// indicates that the read loop has finished all reads and will never
|
|
// run again.
|
|
//
|
|
InterlockedIncrement(&PingPong->weAreCancelling);
|
|
|
|
/*
|
|
* Synchronize with the irp's completion routine.
|
|
*/
|
|
|
|
KeWaitForSingleObject(&PingPong->sentEvent,
|
|
Executive, // wait reason
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL ); // no timeout
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("Pingpong sent event set for pingpong %x\n", PingPong))
|
|
IoCancelIrp(PingPong->irp);
|
|
|
|
/*
|
|
* Cancelling the IRP causes a lower driver to
|
|
* complete it (either in a cancel routine or when
|
|
* the driver checks Irp->Cancel just before queueing it).
|
|
* Wait for the IRP to actually get cancelled.
|
|
*/
|
|
KeWaitForSingleObject( &PingPong->pumpDoneEvent,
|
|
Executive, // wait reason
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL ); // no timeout
|
|
|
|
// // Now clear the cancelling flag so the pingpong could be resent.
|
|
InterlockedDecrement(&PingPong->weAreCancelling);
|
|
DBGPRINT(DBG_USBUTIL_TRACE, ("Pingpong pump done event set for %x\n", PingPong))
|
|
}
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapDestroyPingPongs
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID UsbWrapDestroyPingPongs(PUSB_WRAPPER_EXTENSION WrapExt)
|
|
{
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapDestroyPingPongs\n"));
|
|
|
|
|
|
if (WrapExt && WrapExt->IntReadWrap.PingPongs){
|
|
|
|
ULONG i;
|
|
|
|
UsbWrapCancelAllPingPongIrps(WrapExt);
|
|
|
|
for (i = 0; i < WrapExt->IntReadWrap.NumPingPongs; i++){
|
|
|
|
if (WrapExt->IntReadWrap.PingPongs[i].urb) {
|
|
ExFreePool(WrapExt->IntReadWrap.PingPongs[i].urb);
|
|
WrapExt->IntReadWrap.PingPongs[i].urb = NULL;
|
|
}
|
|
|
|
if (WrapExt->IntReadWrap.PingPongs[i].irp) {
|
|
IoFreeIrp(WrapExt->IntReadWrap.PingPongs[i].irp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ExFreePool(WrapExt->IntReadWrap.PingPongs);
|
|
WrapExt->IntReadWrap.PingPongs = NULL;
|
|
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapDestroyPingPongs\n"));
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapEnqueueData
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID UsbWrapEnqueueData(
|
|
IN PUSB_WRAPPER_EXTENSION WrapExt,
|
|
IN PVOID Data,
|
|
IN ULONG DataLength,
|
|
IN PLIST_ENTRY Queue
|
|
)
|
|
{
|
|
|
|
PUSB_WRAPPER_DATA_BLOCK dataBlock;
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapEnqueueData\n"));
|
|
dataBlock = ALLOC_MEM(NonPagedPool, sizeof(USB_WRAPPER_DATA_BLOCK), USBWRAP_TAG);
|
|
|
|
if (!dataBlock) {
|
|
//
|
|
// D'oh, out of resources
|
|
//
|
|
DBGPRINT(DBG_USBUTIL_ERROR, ("UsbWrapEnqueueData: Failed to allocate dataBlock\n"));
|
|
|
|
// Make sure that we don't leak this memory
|
|
if (Data) {
|
|
FREE_MEM(Data);
|
|
}
|
|
return;
|
|
}
|
|
|
|
dataBlock->Buffer = Data;
|
|
dataBlock->DataLen = DataLength;
|
|
ExInterlockedInsertTailList(
|
|
Queue,
|
|
(PLIST_ENTRY) dataBlock,
|
|
&WrapExt->IntReadWrap.QueueLock);
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapEnqueueData\n"));
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapDequeueData
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID UsbWrapDequeueData(
|
|
IN PUSB_WRAPPER_EXTENSION WrapExt,
|
|
OUT PVOID *Data,
|
|
OUT ULONG *DataLength,
|
|
IN PLIST_ENTRY Queue
|
|
)
|
|
{
|
|
|
|
PUSB_WRAPPER_DATA_BLOCK dataBlock;
|
|
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapDequeueData\n"));
|
|
dataBlock = (PUSB_WRAPPER_DATA_BLOCK) RemoveHeadList(Queue);
|
|
|
|
if(!dataBlock) {
|
|
|
|
*Data = NULL;
|
|
*DataLength = 0;
|
|
|
|
} else {
|
|
|
|
*Data = dataBlock->Buffer;
|
|
*DataLength = dataBlock->DataLen;
|
|
|
|
}
|
|
|
|
FREE_MEM(dataBlock);
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapDequeueData\n"));
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapGetTransferBuffer
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
PVOID UsbWrapGetTransferBuffer(
|
|
IN PUSB_WRAPPER_EXTENSION WrapExt
|
|
)
|
|
{
|
|
|
|
PVOID buffer;
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapGetTransferBuffer\n"));
|
|
buffer = ALLOC_MEM(NonPagedPool, WrapExt->IntReadWrap.MaxTransferSize, USBWRAP_TAG);
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapGetTransferBuffer\n"));
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapFreeTransferBuffer
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID UsbWrapFreeTransferBuffer(
|
|
IN PUSB_WRAPPER_EXTENSION WrapExt,
|
|
PVOID Buffer
|
|
)
|
|
{
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: UsbWrapFreeTransferBuffer\n"));
|
|
|
|
if (Buffer != NULL) {
|
|
|
|
FREE_MEM(Buffer);
|
|
|
|
}
|
|
|
|
DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: UsbWrapFreeTransferBuffer\n"));
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* UsbWrapReadData
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS UsbWrapReadData(
|
|
IN PUSB_WRAPPER_EXTENSION WrapExt,
|
|
IN PVOID Buffer,
|
|
IN ULONG *BufferLength
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PUSB_WRAPPER_DATA_BLOCK dataBlock;
|
|
KIRQL oldIrql;
|
|
ULONG bytesCoppied;
|
|
|
|
|
|
__try{
|
|
|
|
KeAcquireSpinLock(&WrapExt->IntReadWrap.QueueLock,
|
|
&oldIrql);
|
|
|
|
if(IsListEmpty(&WrapExt->IntReadWrap.SavedQueue)) {
|
|
|
|
bytesCoppied = 0;
|
|
status = STATUS_SUCCESS;
|
|
__leave;
|
|
|
|
}
|
|
|
|
|
|
dataBlock = (PUSB_WRAPPER_DATA_BLOCK) WrapExt->IntReadWrap.SavedQueue.Flink;
|
|
|
|
|
|
if(dataBlock->DataLen > *BufferLength) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
bytesCoppied = dataBlock->DataLen;
|
|
|
|
__leave;
|
|
|
|
}
|
|
RemoveHeadList(&WrapExt->IntReadWrap.SavedQueue);
|
|
bytesCoppied = 0;
|
|
while(TRUE) {
|
|
|
|
RtlCopyMemory(Buffer,
|
|
dataBlock->Buffer,
|
|
dataBlock->DataLen);
|
|
|
|
bytesCoppied += dataBlock->DataLen;
|
|
*BufferLength -= dataBlock->DataLen;
|
|
(UCHAR*) Buffer += dataBlock->DataLen;
|
|
|
|
FREE_MEM(dataBlock->Buffer);
|
|
|
|
if(dataBlock->DataLen < WrapExt->IntReadWrap.MaxTransferSize) {
|
|
// Found the end of the data
|
|
FREE_MEM(dataBlock);
|
|
__leave;
|
|
}
|
|
|
|
FREE_MEM(dataBlock);
|
|
|
|
if(IsListEmpty(&WrapExt->IntReadWrap.SavedQueue)) {
|
|
__leave;
|
|
}
|
|
|
|
|
|
dataBlock = (PUSB_WRAPPER_DATA_BLOCK) WrapExt->IntReadWrap.SavedQueue.Flink;
|
|
|
|
|
|
if(dataBlock->DataLen > *BufferLength) {
|
|
|
|
// Not enough buffer left for this transfer
|
|
__leave;
|
|
|
|
}
|
|
|
|
RemoveHeadList(&WrapExt->IntReadWrap.SavedQueue);
|
|
|
|
|
|
}
|
|
|
|
|
|
} __finally {
|
|
|
|
*BufferLength = bytesCoppied;
|
|
KeReleaseSpinLock(&WrapExt->IntReadWrap.QueueLock,
|
|
oldIrql);
|
|
|
|
}
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
VOID
|
|
UsbWrapEmptyQueue(PUSB_WRAPPER_EXTENSION WrapExt,
|
|
PLIST_ENTRY Queue)
|
|
{
|
|
__try
|
|
{
|
|
PUSB_WRAPPER_DATA_BLOCK dataBlock;
|
|
|
|
if (!WrapExt) {
|
|
__leave;
|
|
}
|
|
|
|
|
|
while (!IsListEmpty(Queue)) {
|
|
|
|
dataBlock =
|
|
(PUSB_WRAPPER_DATA_BLOCK) ExInterlockedRemoveHeadList(Queue,
|
|
&WrapExt->IntReadWrap.QueueLock);
|
|
FREE_MEM(dataBlock->Buffer);
|
|
FREE_MEM(dataBlock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
__finally
|
|
{
|
|
}
|
|
|
|
}
|
|
|