www.pudn.com > bulkUSB.rar > bulkrwr.c


/*++ 
 
Copyright (c) 2000  Microsoft Corporation 
 
Module Name: 
 
    bulkrwr.c 
 
Abstract: 
 
    This file has routines to perform reads and writes. 
    The read and writes are for bulk transfers. 
 
Environment: 
 
    Kernel mode 
 
Notes: 
 
    Copyright (c) 2000 Microsoft Corporation.   
    All Rights Reserved. 
 
--*/ 
 
#include "bulkusb.h" 
#include "bulkpnp.h" 
#include "bulkpwr.h" 
#include "bulkdev.h" 
#include "bulkrwr.h" 
#include "bulkwmi.h" 
#include "bulkusr.h" 
 
PBULKUSB_PIPE_CONTEXT 
BulkUsb_PipeWithName( 
    IN PDEVICE_OBJECT  DeviceObject, 
    IN PUNICODE_STRING FileName 
    ) 
/*++ 
  
Routine Description: 
 
    This routine will pass the string pipe name and 
    fetch the pipe number. 
 
Arguments: 
 
    DeviceObject - pointer to DeviceObject 
    FileName - string pipe name 
 
Return Value: 
 
    The device extension maintains a pipe context for  
    the pipes on 82930 board. 
    This routine returns the pointer to this context in 
    the device extension for the "FileName" pipe. 
 
--*/ 
{ 
    LONG                  ix; 
    ULONG                 uval;  
    ULONG                 nameLength; 
    ULONG                 umultiplier; 
    PDEVICE_EXTENSION     deviceExtension; 
    PBULKUSB_PIPE_CONTEXT pipeContext; 
 
    // 
    // initialize variables 
    // 
    pipeContext = NULL; 
    // 
    // typedef WCHAR *PWSTR; 
    // 
    nameLength = (FileName->Length / sizeof(WCHAR)); 
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; 
 
    BulkUsb_DbgPrint(3, ("BulkUsb_PipeWithName - begins\n")); 
 
    if(nameLength != 0) { 
     
        BulkUsb_DbgPrint(3, ("Filename = %ws nameLength = %d\n", FileName->Buffer, nameLength)); 
 
        // 
        // Parse the pipe# 
        // 
        ix = nameLength - 1; 
 
        // if last char isn't digit, decrement it. 
        while((ix > -1) && 
              ((FileName->Buffer[ix] < (WCHAR) '0')  || 
               (FileName->Buffer[ix] > (WCHAR) '9')))             { 
 
            ix--; 
        } 
 
        if(ix > -1) { 
 
            uval = 0; 
            umultiplier = 1; 
 
            // traversing least to most significant digits. 
 
            while((ix > -1) && 
                  (FileName->Buffer[ix] >= (WCHAR) '0') && 
                  (FileName->Buffer[ix] <= (WCHAR) '9'))          { 
         
                uval += (umultiplier * 
                         (ULONG) (FileName->Buffer[ix] - (WCHAR) '0')); 
 
                ix--; 
                umultiplier *= 10; 
            } 
 
            if(uval < 6 && deviceExtension->PipeContext) { 
         
                pipeContext = &deviceExtension->PipeContext[uval]; 
            } 
        } 
    } 
 
    BulkUsb_DbgPrint(3, ("BulkUsb_PipeWithName - ends\n")); 
 
    return pipeContext; 
} 
 
NTSTATUS 
BulkUsb_DispatchReadWrite( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP           Irp 
    ) 
/*++ 
  
Routine Description: 
 
    Dispatch routine for read and write. 
    This routine creates a BULKUSB_RW_CONTEXT for a read/write. 
    This read/write is performed in stages of BULKUSB_MAX_TRANSFER_SIZE. 
    once a stage of transfer is complete, then the irp is circulated again,  
    until the requested length of tranfer is performed. 
 
Arguments: 
 
    DeviceObject - pointer to device object 
    Irp - I/O request packet 
 
Return Value: 
 
    NT status value 
 
--*/ 
{ 
    PMDL                   mdl; 
    PURB                   urb; 
    ULONG                  totalLength; 
    ULONG                  stageLength; 
    ULONG                  urbFlags; 
    BOOLEAN                read; 
    NTSTATUS               ntStatus; 
    ULONG_PTR              virtualAddress; 
    PFILE_OBJECT           fileObject; 
    PDEVICE_EXTENSION      deviceExtension; 
    PIO_STACK_LOCATION     irpStack; 
    PIO_STACK_LOCATION     nextStack; 
    PBULKUSB_RW_CONTEXT    rwContext; 
    PUSBD_PIPE_INFORMATION pipeInformation; 
 
    // 
    // initialize variables 
    // 
    urb = NULL; 
    mdl = NULL; 
    rwContext = NULL; 
    totalLength = 0; 
    irpStack = IoGetCurrentIrpStackLocation(Irp); 
    fileObject = irpStack->FileObject; 
    read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE; 
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; 
 
    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite - begins\n")); 
 
    if(deviceExtension->DeviceState != Working) { 
 
        BulkUsb_DbgPrint(1, ("Invalid device state\n")); 
 
        ntStatus = STATUS_INVALID_DEVICE_STATE; 
        goto BulkUsb_DispatchReadWrite_Exit; 
    } 
 
    // 
    // It is true that the client driver cancelled the selective suspend 
    // request in the dispatch routine for create Irps. 
    // But there is no guarantee that it has indeed completed. 
    // so wait on the NoIdleReqPendEvent and proceed only if this event 
    // is signalled. 
    // 
    BulkUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n")); 
     
    // 
    // make sure that the selective suspend request has been completed. 
    // 
 
    if(deviceExtension->SSEnable) { 
 
        KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,  
                              Executive,  
                              KernelMode,  
                              FALSE,  
                              NULL); 
    } 
 
    if(fileObject && fileObject->FsContext) { 
 
        pipeInformation = fileObject->FsContext; 
 
        if((UsbdPipeTypeBulk != pipeInformation->PipeType) && 
           (UsbdPipeTypeInterrupt != pipeInformation->PipeType)) { 
             
            BulkUsb_DbgPrint(1, ("Usbd pipe type is not bulk or interrupt\n")); 
 
            ntStatus = STATUS_INVALID_HANDLE; 
            goto BulkUsb_DispatchReadWrite_Exit; 
        } 
    } 
    else { 
 
        BulkUsb_DbgPrint(1, ("Invalid handle\n")); 
 
        ntStatus = STATUS_INVALID_HANDLE; 
        goto BulkUsb_DispatchReadWrite_Exit; 
    } 
 
    rwContext = (PBULKUSB_RW_CONTEXT) 
                ExAllocatePool(NonPagedPool, 
                               sizeof(BULKUSB_RW_CONTEXT)); 
 
    if(rwContext == NULL) { 
         
        BulkUsb_DbgPrint(1, ("Failed to alloc mem for rwContext\n")); 
 
        ntStatus = STATUS_INSUFFICIENT_RESOURCES; 
        goto BulkUsb_DispatchReadWrite_Exit; 
    } 
 
    if(Irp->MdlAddress) { 
 
        totalLength = MmGetMdlByteCount(Irp->MdlAddress); 
    } 
 
    if(totalLength > BULKUSB_TEST_BOARD_TRANSFER_BUFFER_SIZE) { 
 
        BulkUsb_DbgPrint(1, ("Transfer length > circular buffer\n")); 
 
        ntStatus = STATUS_INVALID_PARAMETER; 
 
        ExFreePool(rwContext); 
 
        goto BulkUsb_DispatchReadWrite_Exit; 
    } 
 
    if(totalLength == 0) { 
 
        BulkUsb_DbgPrint(1, ("Transfer data length = 0\n")); 
 
        ntStatus = STATUS_SUCCESS; 
 
        ExFreePool(rwContext); 
 
        goto BulkUsb_DispatchReadWrite_Exit; 
    } 
 
    urbFlags = USBD_SHORT_TRANSFER_OK; 
    virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress); 
 
    if(read) { 
 
        urbFlags |= USBD_TRANSFER_DIRECTION_IN; 
        BulkUsb_DbgPrint(3, ("Read operation\n")); 
    } 
    else { 
 
        urbFlags |= USBD_TRANSFER_DIRECTION_OUT; 
        BulkUsb_DbgPrint(3, ("Write operation\n")); 
    } 
 
    // 
    // the transfer request is for totalLength. 
    // we can perform a max of BULKUSB_MAX_TRANSFER_SIZE 
    // in each stage. 
    // 
    if(totalLength > BULKUSB_MAX_TRANSFER_SIZE) { 
 
        stageLength = BULKUSB_MAX_TRANSFER_SIZE; 
    } 
    else { 
 
        stageLength = totalLength; 
    } 
 
    mdl = IoAllocateMdl((PVOID) virtualAddress, 
                        totalLength, 
                        FALSE, 
                        FALSE, 
                        NULL); 
 
    if(mdl == NULL) { 
     
        BulkUsb_DbgPrint(1, ("Failed to alloc mem for mdl\n")); 
 
        ntStatus = STATUS_INSUFFICIENT_RESOURCES; 
 
        ExFreePool(rwContext); 
 
        goto BulkUsb_DispatchReadWrite_Exit; 
    } 
 
    // 
    // map the portion of user-buffer described by an mdl to another mdl 
    // 
    IoBuildPartialMdl(Irp->MdlAddress, 
                      mdl, 
                      (PVOID) virtualAddress, 
                      stageLength); 
 
    urb = ExAllocatePool(NonPagedPool, 
                         sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER)); 
 
    if(urb == NULL) { 
 
        BulkUsb_DbgPrint(1, ("Failed to alloc mem for urb\n")); 
 
        ntStatus = STATUS_INSUFFICIENT_RESOURCES; 
 
        ExFreePool(rwContext); 
        IoFreeMdl(mdl); 
 
        goto BulkUsb_DispatchReadWrite_Exit; 
    } 
 
    UsbBuildInterruptOrBulkTransferRequest( 
                            urb, 
                            sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), 
                            pipeInformation->PipeHandle, 
                            NULL, 
                            mdl, 
                            stageLength, 
                            urbFlags, 
                            NULL); 
 
    // 
    // set BULKUSB_RW_CONTEXT parameters. 
    // 
     
    rwContext->Urb             = urb; 
    rwContext->Mdl             = mdl; 
    rwContext->Length          = totalLength - stageLength; 
    rwContext->Numxfer         = 0; 
    rwContext->VirtualAddress  = virtualAddress + stageLength; 
    rwContext->DeviceExtension = deviceExtension; 
 
    // 
    // use the original read/write irp as an internal device control irp 
    // 
 
    nextStack = IoGetNextIrpStackLocation(Irp); 
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; 
    nextStack->Parameters.Others.Argument1 = (PVOID) urb; 
    nextStack->Parameters.DeviceIoControl.IoControlCode =  
                                             IOCTL_INTERNAL_USB_SUBMIT_URB; 
 
    IoSetCompletionRoutine(Irp,  
                           (PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion, 
                           rwContext, 
                           TRUE, 
                           TRUE, 
                           TRUE); 
 
    // 
    // since we return STATUS_PENDING call IoMarkIrpPending. 
    // This is the boiler plate code. 
    // This may cause extra overhead of an APC for the Irp completion 
    // but this is the correct thing to do. 
    // 
 
    IoMarkIrpPending(Irp); 
 
    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite::")); 
    BulkUsb_IoIncrement(deviceExtension); 
 
    ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, 
                            Irp); 
 
    if(!NT_SUCCESS(ntStatus)) { 
 
        BulkUsb_DbgPrint(1, ("IoCallDriver fails with status %X\n", ntStatus)); 
 
        // 
        // if the device was yanked out, then the pipeInformation  
        // field is invalid. 
        // similarly if the request was cancelled, then we need not 
        // invoked reset pipe/device. 
        // 
        if((ntStatus != STATUS_CANCELLED) &&  
           (ntStatus != STATUS_DEVICE_NOT_CONNECTED)) { 
             
            ntStatus = BulkUsb_ResetPipe(DeviceObject, 
                                     pipeInformation); 
     
            if(!NT_SUCCESS(ntStatus)) { 
 
                BulkUsb_DbgPrint(1, ("BulkUsb_ResetPipe failed\n")); 
 
                ntStatus = BulkUsb_ResetDevice(DeviceObject); 
            } 
        } 
        else { 
 
            BulkUsb_DbgPrint(3, ("ntStatus is STATUS_CANCELLED or " 
                                 "STATUS_DEVICE_NOT_CONNECTED\n")); 
        } 
    } 
 
    // 
    // we return STATUS_PENDING and not the status returned by the lower layer. 
    // 
    return STATUS_PENDING; 
 
BulkUsb_DispatchReadWrite_Exit: 
 
    Irp->IoStatus.Status = ntStatus; 
    Irp->IoStatus.Information = 0; 
 
    IoCompleteRequest(Irp, IO_NO_INCREMENT); 
 
    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite - ends\n")); 
 
    return ntStatus; 
} 
 
NTSTATUS 
BulkUsb_ReadWriteCompletion( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP           Irp, 
    IN PVOID          Context 
    ) 
/*++ 
  
Routine Description: 
 
    This is the completion routine for reads/writes 
    If the irp completes with success, we check if we 
    need to recirculate this irp for another stage of 
    transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED. 
    if the irp completes in error, free all memory allocs and 
    return the status. 
 
Arguments: 
 
    DeviceObject - pointer to device object 
    Irp - I/O request packet 
    Context - context passed to the completion routine. 
 
Return Value: 
 
    NT status value 
 
--*/ 
{ 
    ULONG               stageLength; 
    NTSTATUS            ntStatus; 
    PIO_STACK_LOCATION  nextStack; 
    PBULKUSB_RW_CONTEXT rwContext; 
 
    // 
    // initialize variables 
    // 
    rwContext = (PBULKUSB_RW_CONTEXT) Context; 
    ntStatus = Irp->IoStatus.Status; 
 
    UNREFERENCED_PARAMETER(DeviceObject); 
    BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion - begins\n")); 
 
    // 
    // successfully performed a stageLength of transfer. 
    // check if we need to recirculate the irp. 
    // 
    if(NT_SUCCESS(ntStatus)) { 
 
        if(rwContext) { 
 
            rwContext->Numxfer +=  
              rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength; 
         
            if(rwContext->Length) { 
 
                // 
                // another stage transfer 
                // 
                BulkUsb_DbgPrint(3, ("Another stage transfer...\n")); 
 
                if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) { 
             
                    stageLength = BULKUSB_MAX_TRANSFER_SIZE; 
                } 
                else { 
                 
                    stageLength = rwContext->Length; 
                } 
 
                // the source MDL is not mapped and so when the lower driver 
                // calls MmGetSystemAddressForMdl(Safe) on Urb->Mdl (target Mdl),  
                // system PTEs are used. 
                // IoFreeMdl calls MmPrepareMdlForReuse to release PTEs (unlock 
                // VA address before freeing any Mdl 
                // Rather than calling IoFreeMdl and IoAllocateMdl each time, 
                // just call MmPrepareMdlForReuse 
                // Not calling MmPrepareMdlForReuse will leak system PTEs 
                //  
                MmPrepareMdlForReuse(rwContext->Mdl); 
 
                IoBuildPartialMdl(Irp->MdlAddress, 
                                  rwContext->Mdl, 
                                  (PVOID) rwContext->VirtualAddress, 
                                  stageLength); 
             
                // 
                // reinitialize the urb 
                // 
                rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength  
                                                                  = stageLength; 
                rwContext->VirtualAddress += stageLength; 
                rwContext->Length -= stageLength; 
 
                nextStack = IoGetNextIrpStackLocation(Irp); 
                nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; 
                nextStack->Parameters.Others.Argument1 = rwContext->Urb; 
                nextStack->Parameters.DeviceIoControl.IoControlCode =  
                                            IOCTL_INTERNAL_USB_SUBMIT_URB; 
 
                IoSetCompletionRoutine(Irp, 
                                       BulkUsb_ReadWriteCompletion, 
                                       rwContext, 
                                       TRUE, 
                                       TRUE, 
                                       TRUE); 
 
                IoCallDriver(rwContext->DeviceExtension->TopOfStackDeviceObject,  
                             Irp); 
 
                return STATUS_MORE_PROCESSING_REQUIRED; 
            } 
            else { 
 
                // 
                // this is the last transfer 
                // 
 
                Irp->IoStatus.Information = rwContext->Numxfer; 
            } 
        } 
    } 
    else { 
 
        BulkUsb_DbgPrint(1, ("ReadWriteCompletion - failed with status = %X\n", ntStatus)); 
    } 
     
    if(rwContext) { 
 
        // 
        // dump rwContext 
        // 
        BulkUsb_DbgPrint(3, ("rwContext->Urb             = %X\n",  
                             rwContext->Urb)); 
        BulkUsb_DbgPrint(3, ("rwContext->Mdl             = %X\n",  
                             rwContext->Mdl)); 
        BulkUsb_DbgPrint(3, ("rwContext->Length          = %d\n",  
                             rwContext->Length)); 
        BulkUsb_DbgPrint(3, ("rwContext->Numxfer         = %d\n",  
                             rwContext->Numxfer)); 
        BulkUsb_DbgPrint(3, ("rwContext->VirtualAddress  = %X\n",  
                             rwContext->VirtualAddress)); 
        BulkUsb_DbgPrint(3, ("rwContext->DeviceExtension = %X\n",  
                             rwContext->DeviceExtension)); 
 
        BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion::")); 
        BulkUsb_IoDecrement(rwContext->DeviceExtension); 
 
        ExFreePool(rwContext->Urb); 
        IoFreeMdl(rwContext->Mdl); 
        ExFreePool(rwContext); 
    } 
 
    BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion - ends\n")); 
 
    return ntStatus; 
}