www.pudn.com > usbfx2lk_v1.1.zip > usbfx2lk_io.cpp
///////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright 2005 OSR Open Systems Resources, Inc.
// All Rights Reserved
//
// This sofware is supplied for instructional purposes only.
//
// OSR Open Systems Resources, Inc. (OSR) expressly disclaims any warranty
// for this software. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
// THE IMPLIED WARRANTIES OF MECHANTABILITY OR FITNESS FOR A PARTICULAR
// PURPOSE. THE ENTIRE RISK ARISING FROM THE USE OF THIS SOFTWARE REMAINS
// WITH YOU. OSR's entire liability and your exclusive remedy shall not
// exceed the price paid for this material. In no event shall OSR or its
// suppliers be liable for any damages whatsoever (including, without
// limitation, damages for loss of business profit, business interruption,
// loss of business information, or any other pecuniary loss) arising out
// of the use or inability to use this software, even if OSR has been
// advised of the possibility of such damages. Because some states/
// jurisdictions do not allow the exclusion or limitation of liability for
// consequential or incidental damages, the above limitation may not apply
// to you.
//
// OSR Open Systems Resources, Inc.
// 105 Route 101A Suite 19
// Amherst, NH 03031 (603) 595-6500 FAX: (603) 595-6503
// email bugs to: bugs@osr.com
//
//
// MODULE:
//
// USBFx2LK_io.cpp
//
// ABSTRACT:
//
// This file contains the routines that handle IRP_MJ_READ and
// IRP_MJ_WRITE processing for the OSR USB FX2 Learning Kit Device
//
// AUTHOR(S):
//
// OSR Open Systems Resources, Inc.
//
///////////////////////////////////////////////////////////////////////////////
#include "usbfx2lk.h"
#ifdef WPP_TRACING
//
// Include the necessary tmh file - this is
// just a matter of course if you're using WPP tracing.
//
extern "C" {
#include "usbfx2lk_io.tmh"
}
#endif
//
// Forward Definitions
//
NTSTATUS UsbFx2LkReadCompletionRoutine(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context);
NTSTATUS UsbFx2LkWriteCompletionRoutine(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context);
VOID IssueUsbReadRequest(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context);
VOID IssueUsbWriteRequest(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context);
///////////////////////////////////////////////////////////////////////////////
//
// ProcessNextIrpInQueue
//
// This routine processes the Next Irp in the Io Queue, if we are in a state
// to process Irps. If not, the Irps will remain in the queue until the state
// of the device changes to either a state where the Irps can be processed or where
// the Irps will be removed from the queue.
//
// INPUTS:
//
// DevExt - Address of our Device Extension.
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// None
//
// IRQL:
//
// IRQL == PASSIVE_LEVEL
//
// CONTEXT:
//
// User Context
//
// NOTES:
//
///////////////////////////////////////////////////////////////////////////////
VOID ProcessNextIrpInQueue(PUSBFX2LK_EXT DevExt)
{
KIRQL oldIrql;
PIRP irp;
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE, ("ProcessNextIrpInQueue Entered...\n"));
//
// Check the state of the device. If we not processing irps
// we will just return.
//
KeAcquireSpinLock(&DevExt->IoStateLock,&oldIrql);
if(DevExt->DevicePnPState >= STATE_ALL_ABOVE_QUEUE) {
KeReleaseSpinLock(&DevExt->IoStateLock,oldIrql);
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("ProcessNextIrpInQueue: Exit Queuing Irps, Not Processing\n"));
return;
}
KeReleaseSpinLock(&DevExt->IoStateLock,oldIrql);
//
// See if the device is currently suspended. If it
// is, this routine will attempt to asynchronously power
// the device back up and will return TRUE. In that
// case, the power up code will restart our queues and
// we can simply exit here. However, if this routine
// returns FALSE the device is already powered up
// and we can attempt to start the next IRP in
// the queue.
//
if (!SSPowerDeviceIfSuspendedAsync(DevExt)) {
//
// Attempt to remove an IRP from the Queue.
//
irp = IoCsqRemoveNextIrp(&DevExt->CancelSafeIoQueue,NULL);
//
// See if we got an IRP. If not, then we exit.
//
if(!irp) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("ProcessNextIrpInQueue Exit No Irps in Queue\n"));
return;
}
//
// Got an IRP, submit it to the Device Below us.
//
(VOID)IoCallDriver(DevExt->DeviceToSendIrpsTo,irp);
}
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE, ("ProcessNextIrpInQueue Exit...\n"));
return;
}
///////////////////////////////////////////////////////////////////////////////
//
// UsbFx2LkRead
//
// This routine is called by the IO Manager to process a IRP_MJ_READ
// Irp.
//
//
// INPUTS:
//
// DeviceObject - One of our Device Objects.
// Irp - The Irp to process.
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// STATUS_PENDING, or an ERROR.
//
// IRQL:
//
// IRQL == PASSIVE_LEVEL
//
// CONTEXT:
//
// User Context
//
// NOTES:
//
// READ requests are always serviced by the bulk input pipe
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS UsbFx2LkRead(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
PUSBFX2LK_EXT devExt = (PUSBFX2LK_EXT)DeviceObject->DeviceExtension;
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
KIRQL oldIrql;
PUSBFX2LK_IO_CONTEXT pFx2Context = NULL;
NTSTATUS status;
PMDL pMdl = NULL;
ULONG totalLength = 0;
ULONG urbFlags = 0;
PUCHAR virtualAddress = 0;
ULONG stageLength = 0;
PURB urb = NULL;
PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
//
// We should never be here and not at PASSIVE_LEVEL
//
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
//
// Major issue is we haven't setup our bulk input pipe yet
//
ASSERT(devExt->BulkInPipe);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE, ("UsbFx2LkRead Entered...\n"));
//
// Increment the number of outstanding IOs queued to the device.
//
OsrIncrementOutstandingIoCount(devExt,__FILE__,__LINE__);
//
// See what sort of state we're in.
//
KeAcquireSpinLock(&devExt->IoStateLock,&oldIrql);
if (devExt->DevicePnPState < STATE_ALL_BELOW_FAIL) {
KeReleaseSpinLock(&devExt->IoStateLock,oldIrql);
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkRead: Failing request due to Pnp State! Current PnP state - %s\n",
OsrPrintState(devExt)));
status = STATUS_INVALID_DEVICE_STATE;
goto UsbFx2LkRead_Exit;
}
KeReleaseSpinLock(&devExt->IoStateLock,oldIrql);
//
// We do not support zero length operations in this driver
//
if(!ioStack->Parameters.Read.Length || !Irp->MdlAddress) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkRead: Invalid Parameters to READ \n"));
status = STATUS_INVALID_PARAMETER;
goto UsbFx2LkRead_Exit;
}
//
// Get the total length of the transfer we are asked to perform.
//
totalLength = ioStack->Parameters.Read.Length;
//
// See if the transfer length is greater than the maximum packet
// size of the pipe * the amount of buffering that the firmware
// provides. If the transfer is too large, we will reject it here
// without further processing
//
if(totalLength >
(ULONG)(devExt->BulkInPipe->PipeInformation.MaximumPacketSize * \
USBFX2LK_BULK_TRANSFER_FW_BUFFERING)) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkRead: Length exceeds %d\n",
devExt->BulkInPipe->PipeInformation.MaximumPacketSize * USBFX2LK_BULK_TRANSFER_FW_BUFFERING));
status = STATUS_INVALID_PARAMETER;
goto UsbFx2LkRead_Exit;
}
//
// Allocate a USBFX2 I/O context. This is a driver specific
// structure that we'll use to contain all the necessary
// information about this particular request while we
// own that request
//
pFx2Context = (PUSBFX2LK_IO_CONTEXT) ExAllocatePoolWithTag(NonPagedPool,sizeof(USBFX2LK_IO_CONTEXT),'ciuO');
if(pFx2Context == NULL) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkRead: Failed to allocate IO Context\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto UsbFx2LkRead_Exit;
}
//
// Get the user's base virtual address from the MDL. This
// will be used as a cookie to indicate what area of the
// user's data buffer we are currently copying data into
//
virtualAddress = (PUCHAR) MmGetMdlVirtualAddress(Irp->MdlAddress);
//
// the transfer request is for totalLength. We can perform a maximum of
// the bulk input pipe's PipeInformation.MaximumPacketSize in each stage.
//
pFx2Context->MaxmimumStageSize = devExt->BulkInPipe->PipeInformation.MaximumPacketSize;
if(totalLength > devExt->BulkInPipe->PipeInformation.MaximumPacketSize) {
//
// Total length is bigger that we can send, so we will break up
// the transfer into smaller pieces.
//
stageLength = pFx2Context->MaxmimumStageSize;
} else {
//
// We can send this packet in one transfer.
//
stageLength = totalLength;
}
//
// We asked the I/O manager to supply reads and writes from user
// mode components in the form of MDLs by setting the DO_DIRECT_IO
// bit in our device object during AddDevice. This will result in
// the I/O manager sending our driver MDLs that describe the entire
// length of the user's buffer for the entire operation.
//
// However, we may need to split this single user transfer up
// into multiple "stages" so that we don't send our device
// I/O operations that exceed the maximum supported single
// transfer size. To do this, we will build what are called
// "partial MDLs" that describe only the portion of the user's
// data buffer that we will be transferring at each individual
// stage.
//
// The first step in building partial MDLs will be allocating
// a new MDL that is large enough to describe the passed in MDL.
//
pMdl = IoAllocateMdl((PVOID)virtualAddress,
totalLength,
FALSE,
FALSE,
NULL);
if(pMdl == NULL) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,("UsbFx2LkRead: Failed to Allocate Mdl\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(pFx2Context);
goto UsbFx2LkRead_Exit;
}
//
// Now, build a partial MDL out of the newly allocated
// MDL. This will map the first stageLength bytes of
// the MDL in the IRP into the newly allocated MDL
//
IoBuildPartialMdl(Irp->MdlAddress,
pMdl,
(PVOID)virtualAddress,
stageLength);
//
// Allocate an URB to be used for the transfer.
//
urb = (PURB) ExAllocatePoolWithTag(NonPagedPool,sizeof(URB),'bruO');
if(urb == NULL) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,("UsbFx2LkRead: Failed to allocate URB\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(pFx2Context);
IoFreeMdl(pMdl);
goto UsbFx2LkRead_Exit;
}
//
// Build the Transfer URB.
//
//
// Indicate that this is a read function and that we will allow short
// transfers of data.
//
urbFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN;
UsbBuildInterruptOrBulkTransferRequest(
urb,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
devExt->BulkInPipe->PipeInformation.PipeHandle,
NULL,
pMdl,
stageLength,
urbFlags,
NULL);
//
// Set up our USBFX2 I/O context so that when the URB completes we can
// resume from where we left off.
//
pFx2Context->Urb = urb;
pFx2Context->Irp = Irp;
pFx2Context->Mdl = pMdl;
//
// Indicate how much more data there is to process
//
pFx2Context->RemainingLength = totalLength - stageLength;
pFx2Context->Numxfer = 0;
//
// Update the VirtualAddress pointer. We pass this pointer to
// IoBuildPartialMdl each time we want to map a portion of the
// MDL to indicate the portion of the MDL that we want to map.
// It must be adjusted after each transfer stage.
//
pFx2Context->VirtualAddress = virtualAddress + stageLength;
pFx2Context->DevExt = devExt;
pFx2Context->TotalLength = totalLength;
//
// We will need a workitem in order to resubmit this IRP in
// the case of a multistage transfer.
//
pFx2Context->PWorkItem = IoAllocateWorkItem(devExt->FunctionalDeviceObject);
if(!pFx2Context->PWorkItem) {
status = STATUS_INSUFFICIENT_RESOURCES;
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,("UsbFx2LkRead: Failed IoAllocateWorkItem\n"));
ExFreePool(pFx2Context);
IoFreeMdl(pMdl);
ExFreePool(urb);
goto UsbFx2LkRead_Exit;
}
//
// use the Users Irp as an internal device control irp, which
// is what is used to submit URBs to the host controller
//
nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
//
// The USB drivers expect the URB to be in
// Parameters.Others.Argument1
//
nextStack->Parameters.Others.Argument1 = (PVOID) urb;
//
// Setup the I/O control code
//
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;
//
// We need a completion routine for this transfer.
//
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)UsbFx2LkReadCompletionRoutine,
pFx2Context,
TRUE,
TRUE,
TRUE);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("UsbFx2LkRead: Queuing Read Irp\n"));
//
// While we have the Irp in our queue, store the address of the IO Context in our
// DriverContext field of the Irp. We do this so that if the Irp is canceled while
// in our queue, we have enough information to do the correct cleanup.
//
Irp->Tail.Overlay.DriverContext[0] = pFx2Context;
//
// Mark the IRP as being in-progress before queueing it,
// because we're going to be returning STATUS_PENDING
// from this entry point
//
IoMarkIrpPending(Irp);
//
// Queue the using the appropriate cancel safe queue routin
//
#ifdef W2K3
//
// IoCsqInsertIrpEx returns whatever we return from OsrCsqInsertIoIrpEx,
// which for us is STATUS_SUCCESS.
//
status = IoCsqInsertIrpEx(&devExt->CancelSafeIoQueue,Irp,NULL,devExt);
ASSERT(status == STATUS_SUCCESS);
#else // W2K3
IoCsqInsertIrp(&devExt->CancelSafeIoQueue,Irp,NULL);
#endif // W2K3
//
// We have queued this IRP onto our internal queue.
// Now call our helper routine ProcessNextIrpInQueue
// to pass this request (or possibly another request
// that was running parallel and beat us to the queue)
// to the host controller.
//
// ProcessNextIrpInQueue will pass the request on to the
// host if we are in an applicable PnP state otherwise the
// request will just stay in the queue.
//
ProcessNextIrpInQueue(devExt);
return STATUS_PENDING;
UsbFx2LkRead_Exit:
//
// If we're here then we are failing the request.
// Decrement our outstanding I/O count
//
ASSERT(!NT_SUCCESS(status));
OsrDecrementOutstandingIoCount(devExt,__FILE__,__LINE__);
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// UsbFx2LkReadCompletionRoutine
//
// This routine is called by the IO Manager when a USB Read Completes
//
//
// INPUTS:
//
// DeviceObject - One of our Device Objects.
// Irp - The Irp to process.
// Context -
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// None
//
// IRQL:
//
// IRQL <= DISPATCH_LEVEL
//
// CONTEXT:
//
// User Context
//
// NOTES:
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS UsbFx2LkReadCompletionRoutine(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context)
{
NTSTATUS status = Irp->IoStatus.Status;
PUSBFX2LK_IO_CONTEXT pFx2Context = (PUSBFX2LK_IO_CONTEXT) Context;
ULONG stageLength = 0;
UNREFERENCED_PARAMETER(DeviceObject);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("UsbFx2LkReadCompletionRoutine: Entered.\n"));
//
// There's no reason why this pointer
// shouldn't be valid
//
ASSERT(pFx2Context);
//
// successfully performed a stageLength of transfer.
// check if we need to recirculate the irp.
//
if(NT_SUCCESS(status)) {
//
// The transfer completed successfully, see if we need to continue transfering.
//
//
// Calculate how much data has been recently received.
//
pFx2Context->Numxfer += pFx2Context->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
//
// Determine if there is more to transfer.
//
if(pFx2Context->RemainingLength) {
//
// Set up the next stage of the transfer.
//
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: Another stage transfer %d of %d\n",
pFx2Context->Numxfer,pFx2Context->TotalLength));
//
// Compare the remaining length to our maximum stage size.
// If it is larger, then this transfer will require at
// least two more sub-transfers
//
if(pFx2Context->RemainingLength > pFx2Context->MaxmimumStageSize) {
stageLength = pFx2Context->MaxmimumStageSize;
} else {
stageLength = pFx2Context->RemainingLength;
}
//
// Prepare the partial MDL to be reused for the next part of the transfer.
// This function unmaps any pages that may have been previously mapped
// by this MDL before reusing it.
//
MmPrepareMdlForReuse(pFx2Context->Mdl);
//
// Setup the partial MDL again, but this time we
// use the updated VirtualAddress pointer from the
// original MDL. This is the cookie that the memory
// manager uses to figure out which portion of the
// MDL needs to be mapped into the partial MDL.
//
IoBuildPartialMdl(Irp->MdlAddress,
pFx2Context->Mdl,
(PVOID)pFx2Context->VirtualAddress,
stageLength);
//
// reinitialize the urb
//
pFx2Context->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = stageLength;
//
// And update the VirtualAddress and remaining transfer length in the
// FX2 I/O context.
//
pFx2Context->VirtualAddress += stageLength;
pFx2Context->RemainingLength -= stageLength;
//
// We cannot simply resubmit the IRP here with IoCallDriver for two
// reasons:
//
// 1) We may be running at IRQL == DISPATCH_LEVEL and the USB stack
// is not explicitly documented to be called at its dispatch
// entry points at IRQL DISPATCH_LEVEL
//
// 2) If the request is completed in the lower driver's dispatch entry
// point, we will be called back here at our completion routine
// recursively. If we then submit the IRP back down inline, we
// can continue doing this recursion until we finally run out
// stack space
//
// For those two reasons, we will queue a work item (guaranteeing an
// IRQL of PASSIVE_LEVEL and a fresh stack) and resubmit the IRP
// from the work item.
//
IoQueueWorkItem(pFx2Context->PWorkItem,IssueUsbReadRequest,CriticalWorkQueue,pFx2Context);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("UsbFx2LkReadCompletionRoutine: Next Stage.\n"));
//
// We are going to be resubmitting this IRP to the host in our
// work item, so we need to indicate to the I/O manager
// that we are still processing the IRP by returning the
// special STATUS_MORE_PROCESSING_REQUIRED status
//
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
//
// this is the last transfer
//
//
// Update Statistics
//
pFx2Context->DevExt->WmiStatistics.TotalBulkBytesRead += pFx2Context->Numxfer;
pFx2Context->DevExt->WmiStatistics.TotalBulkReadIrpCount++;
//
// Indicate the number of bytes transfered.
//
Irp->IoStatus.Information = pFx2Context->Numxfer;
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: Receive Complete.\n"));
}
} else {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: failed status = %08.8x %s\n", status,OsrNtStatusToString(status)));
}
//
// dump Fx2Context
//
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: pFx2Context->Urb = %p\n",
pFx2Context->Urb));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: pFx2Context->Mdl = %p\n",
pFx2Context->Mdl));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: pFx2Context->RemainingLength = %d\n",
pFx2Context->RemainingLength));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: pFx2Context->Numxfer = %d\n",
pFx2Context->Numxfer));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: pFx2Context->VirtualAddress = %p\n",
pFx2Context->VirtualAddress));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkReadCompletionRoutine: pFx2Context->DevExt = %p\n",
pFx2Context->DevExt));
//
// Decrement the outstanding IO count
//
OsrDecrementOutstandingIoCount(pFx2Context->DevExt,__FILE__,__LINE__);
//
// Clean up all of the resources used for this transfer
//
IoFreeWorkItem(pFx2Context->PWorkItem);
ExFreePool(pFx2Context->Urb);
IoFreeMdl(pFx2Context->Mdl);
ExFreePool(pFx2Context);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("UsbFx2LkReadCompletionRoutine: Exit.\n"));
return STATUS_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
//
// IssueUsbReadRequest
//
// This routine is called in response to our UsbFx2LkReadCompletionRoutine
// queuing the Work Item to do the next stage of the Read request.
//
//
// INPUTS:
//
// DeviceObject - One of our Device Objects.
// Context - our pFx2Context
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// None
//
// IRQL:
//
// IRQL == PASSIVE_LEVEL
//
// CONTEXT:
//
// System Context
//
// NOTES:
//
///////////////////////////////////////////////////////////////////////////////
VOID IssueUsbReadRequest(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)
{
PUSBFX2LK_IO_CONTEXT pFx2Context = (PUSBFX2LK_IO_CONTEXT) Context;
PIO_STACK_LOCATION nextStack;
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("IssueUsbReadRequest: Entered.\n"));
//
// Prepare the next stack location so that we can resubmit the irp
// to do the next part of the transfer. Note that this step is
// required because the I/O manager zeroes the lower stack
// locations as part of completion processing.
//
nextStack = IoGetNextIrpStackLocation(pFx2Context->Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = pFx2Context->Urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;
//
// Set our completion routine to be called when this stage of
// the read completes.
//
IoSetCompletionRoutine(pFx2Context->Irp,
UsbFx2LkReadCompletionRoutine,
pFx2Context,
TRUE,
TRUE,
TRUE);
//
// Send the request to the USB Driver.
//
IoCallDriver(pFx2Context->DevExt->DeviceToSendIrpsTo,
pFx2Context->Irp);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("IssueUsbReadRequest: Exit.\n"));
}
///////////////////////////////////////////////////////////////////////////////
//
// UsbFx2LkWrite
//
// This routine is called by the IO Manager to process a IRP_MJ_WRITE
// Irp.
//
//
// INPUTS:
//
// DeviceObject - One of our Device Objects.
// Irp - The Irp to process.
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// STATUS_PENDING, or an ERROR.
//
// IRQL:
//
// IRQL == PASSIVE_LEVEL
//
// CONTEXT:
//
// User Context
//
// NOTES:
//
// WRITE requests are always serviced by the bulk out pipe
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS UsbFx2LkWrite(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
PUSBFX2LK_EXT devExt = (PUSBFX2LK_EXT)DeviceObject->DeviceExtension;
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
KIRQL oldIrql;
PUSBFX2LK_IO_CONTEXT pFx2Context = NULL;
NTSTATUS status;
PMDL pMdl = NULL;
ULONG totalLength = 0;
ULONG urbFlags = 0;
PUCHAR virtualAddress = 0;
ULONG stageLength = 0;
PURB urb = NULL;
PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
//
// We should never be here and not at PASSIVE_LEVEL
//
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
//
// Major issue is we haven't setup our bulk output pipe yet
//
ASSERT(devExt->BulkOutPipe);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE, ("UsbFx2LkWrite Entered...\n"));
//
// Increment the number of outstanding IOs queued to the device.
//
OsrIncrementOutstandingIoCount(devExt,__FILE__,__LINE__);
//
// See what sort of state we're in.
//
KeAcquireSpinLock(&devExt->IoStateLock,&oldIrql);
if (devExt->DevicePnPState < STATE_ALL_BELOW_FAIL) {
KeReleaseSpinLock(&devExt->IoStateLock,oldIrql);
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkWrite: Failing request due to Pnp State! Current PnP state - %s\n",
OsrPrintState(devExt)));
status = STATUS_INVALID_DEVICE_STATE;
goto UsbFx2LkWrite_Exit;
}
KeReleaseSpinLock(&devExt->IoStateLock,oldIrql);
//
//
// We do not support zero length operations in this driver
//
if(!ioStack->Parameters.Write.Length || !Irp->MdlAddress) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkWrite: Invalid Parameters to Write \n"));
status = STATUS_INVALID_PARAMETER;
goto UsbFx2LkWrite_Exit;
}
//
// Get the total length of the transfer we are asked to perform.
//
totalLength = ioStack->Parameters.Write.Length;
//
// See if the transfer length is greater than the maximum packet
// size of the pipe * the amount of buffering that the firmware
// provides. If the transfer is too large, we will reject it here
// without further processing
//
if(totalLength >
(ULONG)(devExt->BulkOutPipe->PipeInformation.MaximumPacketSize * \
USBFX2LK_BULK_TRANSFER_FW_BUFFERING)) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkWrite: Length exceeds %d\n",
devExt->BulkOutPipe->PipeInformation.MaximumPacketSize * USBFX2LK_BULK_TRANSFER_FW_BUFFERING));
status = STATUS_INVALID_PARAMETER;
goto UsbFx2LkWrite_Exit;
}
//
// Allocate a USBFX2 I/O context. This is a driver specific
// structure that we'll use to contain all the necessary
// information about this particular request while we
// own that request
//
pFx2Context = (PUSBFX2LK_IO_CONTEXT) ExAllocatePoolWithTag(NonPagedPool,sizeof(USBFX2LK_IO_CONTEXT),'ciuO');
if(pFx2Context == NULL) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,("UsbFx2LkWrite: Failed to allocate IO Context\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto UsbFx2LkWrite_Exit;
}
//
// Get the user's base virtual address from the MDL. This
// will be used as a cookie to indicate what area of the
// user's data buffer we are currently copying data into
//
virtualAddress = (PUCHAR) MmGetMdlVirtualAddress(Irp->MdlAddress);
//
// the transfer request is for totalLength. We can perform a maximum of
// the BulkOut pipe's PipeInformation.MaximumPacketSize in each stage.
//
pFx2Context->MaxmimumStageSize = devExt->BulkInPipe->PipeInformation.MaximumPacketSize;
if(totalLength > pFx2Context->MaxmimumStageSize) {
//
// Total length is bigger that we can send, so we will break up
// the transfer into smaller pieces.
//
stageLength = pFx2Context->MaxmimumStageSize;
} else {
//
// We can send this packet in one transfer.
//
stageLength = totalLength;
}
//
// We asked the I/O manager to supply reads and writes from user
// mode components in the form of MDLs by setting the DO_DIRECT_IO
// bit in our device object during AddDevice. This will result in
// the I/O manager sending our driver MDLs that describe the entire
// length of the user's buffer for the entire operation.
//
// However, we may need to split this single user transfer up
// into multiple "stages" so that we don't send our device
// I/O operations that exceed the maximum supported single
// transfer size. To do this, we will build what are called
// "partial MDLs" that describe only the portion of the user's
// data buffer that we will be transferring at each individual
// stage.
//
// The first step in building partial MDLs will be allocating
// a new MDL that is large enough to describe the passed in MDL.
//
pMdl = IoAllocateMdl((PVOID)virtualAddress,
totalLength,
FALSE,
FALSE,
NULL);
if(pMdl == NULL) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,("UsbFx2LkWrite: Failed to Allocate Mdl\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(pFx2Context);
goto UsbFx2LkWrite_Exit;
}
//
// Now, build a partial MDL out of the newly allocated
// MDL. This will map the first stageLength bytes of
// the MDL in the IRP into the newly allocated MDL
//
IoBuildPartialMdl(Irp->MdlAddress,
pMdl,
(PVOID)virtualAddress,
stageLength);
//
// Allocate an URB to be used for the transfer.
//
urb = (PURB) ExAllocatePoolWithTag(NonPagedPool,sizeof(URB),'bruO');
if(urb == NULL) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,("UsbFx2LkWrite: Failed to allocate URB\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(pFx2Context);
IoFreeMdl(pMdl);
goto UsbFx2LkWrite_Exit;
}
//
// Build the Transfer URB.
//
//
// Indicate that this is a write function and that we will allow short
// transfers of data.
//
urbFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_OUT;
UsbBuildInterruptOrBulkTransferRequest(
urb,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
devExt->BulkOutPipe->PipeInformation.PipeHandle,
NULL,
pMdl,
stageLength,
urbFlags,
NULL);
//
// Set up our USBFX2 I/O context so that when the URB completes we can
// resume from where we left off.
//
pFx2Context->Urb = urb;
pFx2Context->Irp = Irp;
pFx2Context->Mdl = pMdl;
//
// Indicate how much more data there is to process
//
pFx2Context->RemainingLength = totalLength - stageLength;
pFx2Context->Numxfer = 0;
//
// Update the VirtualAddress pointer. We pass this pointer to
// IoBuildPartialMdl each time we want to map a portion of the
// MDL to indicate the portion of the MDL that we want to map.
// It must be adjusted after each transfer stage.
//
pFx2Context->VirtualAddress = virtualAddress + stageLength;
pFx2Context->DevExt = devExt;
pFx2Context->TotalLength = totalLength;
//
// We will need a workitem in order to resubmit this IRP in
// the case of a multistage transfer.
//
pFx2Context->PWorkItem = IoAllocateWorkItem(devExt->FunctionalDeviceObject);
if(!pFx2Context->PWorkItem) {
status = STATUS_INSUFFICIENT_RESOURCES;
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,("UsbFx2LkWrite: Failed IoAllocateWorkItem\n"));
ExFreePool(pFx2Context);
IoFreeMdl(pMdl);
ExFreePool(urb);
goto UsbFx2LkWrite_Exit;
}
//
// use the Users Irp as an internal device control irp, which
// is what is used to submit URBs to the host controller
//
nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
//
// The USB drivers expect the URB to be in
// Parameters.Others.Argument1
//
nextStack->Parameters.Others.Argument1 = (PVOID) urb;
//
// Setup the I/O control code
//
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;
//
// We need a completion routine for this transfer.
//
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)UsbFx2LkWriteCompletionRoutine,
pFx2Context,
TRUE,
TRUE,
TRUE);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("UsbFx2LkWrite: Queuing Write Irp\n"));
//
// While we have the Irp in our queue, store the address of the IO Context in our
// DriverContext field of the Irp. We do this so that if the Irp is canceled while
// in our queue, we have enough information to do the correct cleanup.
//
Irp->Tail.Overlay.DriverContext[0] = pFx2Context;
//
// Mark the IRP as being in-progress before queueing it,
// because we're going to be returning STATUS_PENDING
// from this entry point
//
IoMarkIrpPending(Irp);
//
// Queue the using the appropriate cancel safe queue routin
//
#ifdef W2K3
//
// IoCsqInsertIrpEx returns whatever we return from OsrCsqInsertIoIrpEx,
// which for us is STATUS_SUCCESS.
//
status = IoCsqInsertIrpEx(&devExt->CancelSafeIoQueue,Irp,NULL,devExt);
ASSERT(status == STATUS_SUCCESS);
#else // W2K3
IoCsqInsertIrp(&devExt->CancelSafeIoQueue,Irp,NULL);
#endif // W2K3
//
// We have queued this IRP onto our internal queue.
// Now call our helper routine ProcessNextIrpInQueue
// to pass this request (or possibly another request
// that was running parallel and beat us to the queue)
// to the host controller.
//
// ProcessNextIrpInQueue will pass the request on to the
// host if we are in an applicable PnP state otherwise the
// request will just stay in the queue.
//
ProcessNextIrpInQueue(devExt);
return STATUS_PENDING;
UsbFx2LkWrite_Exit:
//
// If we're here then we are failing the request.
// Decrement our outstanding I/O count
//
ASSERT(!NT_SUCCESS(status));
OsrDecrementOutstandingIoCount(devExt,__FILE__,__LINE__);
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// UsbFx2LkWriteCompletionRoutine
//
// This routine is called by the IO Manager when a USB Write Completes
//
//
// INPUTS:
//
// DeviceObject - One of our Device Objects.
// Irp - The Irp to process.
// Context -
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// None
//
// IRQL:
//
// IRQL <= DISPATCH_LEVEL
//
// CONTEXT:
//
// User Context
//
// NOTES:
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS UsbFx2LkWriteCompletionRoutine(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context)
{
NTSTATUS status = Irp->IoStatus.Status;
PUSBFX2LK_IO_CONTEXT pFx2Context = (PUSBFX2LK_IO_CONTEXT) Context;
ULONG stageLength = 0;
UNREFERENCED_PARAMETER(DeviceObject);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("UsbFx2LkWriteCompletionRoutine: Entered.\n"));
//
// There's no reason why this pointer
// shouldn't be valid
//
ASSERT(pFx2Context);
//
// See if we successfully performed a stageLength of transfer.
// check if we need to recirculate the irp.
//
if(NT_SUCCESS(status)) {
//
// The transfer completed successfully, see if we need to continue transfering.
//
//
// Calculate how much data has been recently received.
//
pFx2Context->Numxfer += pFx2Context->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
//
// Determine if there is more to transfer.
//
if(pFx2Context->RemainingLength) {
//
// Set up the next stage of the transfer.
//
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: Another stage transfer %d of %d\n",
pFx2Context->Numxfer,pFx2Context->TotalLength));
//
// Compare the remaining length to our maximum stage size.
// If it is larger, then this transfer will require at
// least two more sub-transfers
//
if(pFx2Context->RemainingLength > pFx2Context->MaxmimumStageSize) {
stageLength = pFx2Context->MaxmimumStageSize;
} else {
stageLength = pFx2Context->RemainingLength;
}
//
// Prepare the MDL to be reused for the next part of the transfer.
// This function unmaps any pages that may have been previously mapped
// by this MDL before reusing it.
//
MmPrepareMdlForReuse(pFx2Context->Mdl);
//
// Setup the partial MDL again, but this time we
// use the updated VirtualAddress pointer from the
// original MDL. This is the cookie that the memory
// manager uses to figure out which portion of the
// MDL needs to be mapped into the partial MDL.
//
IoBuildPartialMdl(Irp->MdlAddress,
pFx2Context->Mdl,
(PVOID) pFx2Context->VirtualAddress,
stageLength);
//
// reinitialize the urb
//
pFx2Context->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = stageLength;
//
// And update the VirtualAddress and remaining transfer length in the
// FX2 I/O context.
//
pFx2Context->VirtualAddress += stageLength;
pFx2Context->RemainingLength -= stageLength;
//
// We cannot simply resubmit the IRP here with IoCallDriver for two
// reasons:
//
// 1) We may be running at IRQL == DISPATCH_LEVEL and the USB stack
// is not explicitly documented to be called at its dispatch
// entry points at IRQL DISPATCH_LEVEL
//
// 2) If the request is completed in the lower driver's dispatch entry
// point, we will be called back here at our completion routine
// recursively. If we then submit the IRP back down inline, we
// can continue doing this recursion until we finally run out
// stack space
//
// For those two reasons, we will queue a work item (guaranteeing an
// IRQL of PASSIVE_LEVEL and a fresh stack) and resubmit the IRP
// from the work item.
//
IoQueueWorkItem(pFx2Context->PWorkItem,IssueUsbWriteRequest,CriticalWorkQueue,pFx2Context);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("UsbFx2LkWriteCompletionRoutine: Next Stage.\n"));
//
// We are going to be resubmitting this IRP to the host in our
// work item, so we need to indicate to the I/O manager
// that we are still processing the IRP by returning the
// special STATUS_MORE_PROCESSING_REQUIRED status
//
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
//
// this is the last transfer
//
//
// Update Statistics
//
pFx2Context->DevExt->WmiStatistics.TotalBulkBytesWritten += pFx2Context->Numxfer ;
pFx2Context->DevExt->WmiStatistics.TotalBulkWriteIrpCount++;
//
// Indicate number of bytes transfered.
//
Irp->IoStatus.Information = pFx2Context->Numxfer;
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: Write Complete.\n"));
}
} else {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: failed status = %08.8x %s\n", status,OsrNtStatusToString(status)));
}
//
// dump Fx2Context
//
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: pFx2Context->Urb = %p\n",
pFx2Context->Urb));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: pFx2Context->Mdl = %p\n",
pFx2Context->Mdl));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: pFx2Context->RemainingLength = %d\n",
pFx2Context->RemainingLength));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: pFx2Context->Numxfer = %d\n",
pFx2Context->Numxfer));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: pFx2Context->VirtualAddress = %p\n",
pFx2Context->VirtualAddress));
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,
("UsbFx2LkWriteCompletionRoutine: pFx2Context->DevExt = %p\n",
pFx2Context->DevExt));
//
// Decrement the outstanding IO count
//
OsrDecrementOutstandingIoCount(pFx2Context->DevExt,__FILE__,__LINE__);
//
// Clean up all of the resources used for this transfer
//
IoFreeWorkItem(pFx2Context->PWorkItem);
ExFreePool(pFx2Context->Urb);
IoFreeMdl(pFx2Context->Mdl);
ExFreePool(pFx2Context);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("UsbFx2LkWriteCompletionRoutine: Exit.\n"));
return STATUS_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
//
// IssueUsbWriteRequest
//
// This routine is called in response to our UsbFx2LkWriteCompletionRoutine
// queuing the Work Item to do the next stage of the write request.
//
//
// INPUTS:
//
// DeviceObject - One of our Device Objects.
// Context - our pFx2Context
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// None
//
// IRQL:
//
// IRQL == PASSIVE_LEVEL
//
// CONTEXT:
//
// System Context
//
// NOTES:
//
///////////////////////////////////////////////////////////////////////////////
VOID IssueUsbWriteRequest(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)
{
PUSBFX2LK_IO_CONTEXT pFx2Context = (PUSBFX2LK_IO_CONTEXT) Context;
PIO_STACK_LOCATION nextStack;
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("IssueUsbWriteRequest: Entered.\n"));
//
// Prepare the next stack location so that we can resubmit the irp
// to do the next part of the transfer. Note that this step is
// required because the I/O manager zeroes the lower stack
// locations as part of completion processing.
//
nextStack = IoGetNextIrpStackLocation(pFx2Context->Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = pFx2Context->Urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;
//
// Set our completion routine to be called when this stage of
// the read completes.
//
IoSetCompletionRoutine(pFx2Context->Irp,
UsbFx2LkWriteCompletionRoutine,
pFx2Context,
TRUE,
TRUE,
TRUE);
//
// Send the write request to the Bus Driver.
//
IoCallDriver(pFx2Context->DevExt->DeviceToSendIrpsTo,
pFx2Context->Irp);
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("IssueUsbWriteRequest: Exit.\n"));
}
///////////////////////////////////////////////////////////////////////////////
//
// OsrProcessQueuedRequests
//
// This interface checks the current state of the read and write
// queues, and starts requests on the device if either are not
// busy.
//
// INPUTS:
//
// DevExt - Pointer to device extension of device on which to
// start the transfers
//
// OUTPUTS:
//
// None.
//
// RETURNS:
//
// None.
//
// IRQL:
//
// This routine is called at IRQL <= DISPATCH_LEVEL
//
// CONTEXT:
//
// Arbitrary
//
// NOTES:
//
///////////////////////////////////////////////////////////////////////////////
VOID OsrProcessQueuedRequests(PUSBFX2LK_EXT DevExt)
{
PIRP irp = NULL;
KIRQL oldIrql;
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,("OsrProcessQueuedRequests: Entered.\n"));
//
// We should never be here and not at <= DISPATCH_LEVEL
//
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Try to start up any queued I/O on the
// device.
//
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_READWRITE,("OsrProcessQueuedRequests: PnP State = %s\n",
OsrPrintState(DevExt)));
//
// Are we in a proper PnP/Power state?
//
KeAcquireSpinLock(&DevExt->IoStateLock,&oldIrql);
if (DevExt->DevicePnPState < STATE_ALL_BELOW_DRAIN) {
KeReleaseSpinLock(&DevExt->IoStateLock,oldIrql);
//
// We're good to go. Attempt to remove an
// IRP off of the queue
//
while((irp = IoCsqRemoveNextIrp(&DevExt->CancelSafeIoQueue, NULL))) {
(VOID)IoCallDriver(DevExt->DeviceToSendIrpsTo,irp);
}
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,("OsrProcessQueuedRequests: Exit. Irps Queued.\n"));
} else {
KeReleaseSpinLock(&DevExt->IoStateLock,oldIrql);
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_READWRITE,("OsrProcessQueuedRequests: Exit. Not Processing Queue.\n"));
}
}