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")); 
    } 
}