Leaked source code of windows server 2003
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

/***************************************************************************
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
{
}
}