|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
ocrw.c
Abstract:
Author:
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include <stdio.h>
#include "stddef.h"
#include "wdm.h"
#include "usbscan.h"
#include "usbd_api.h"
#include "private.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, USOpen)
#pragma alloc_text(PAGE, USClose)
#pragma alloc_text(PAGE, USFlush)
#pragma alloc_text(PAGE, USRead)
#pragma alloc_text(PAGE, USWrite)
#pragma alloc_text(PAGE, USGetPipeIndexToUse)
#pragma alloc_text(PAGE, USTransfer)
#endif
NTSTATUS USOpen( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++
Routine Description:
This routine is called to establish a connection to the device class driver. It does no more than return STATUS_SUCCESS.
Arguments: pDeviceObject - Device object for a device. pIrp - Open request packet
Return Value: NT Status - STATUS_SUCCESS
--*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; PFILE_OBJECT fileObject; PUSBSCAN_FILE_CONTEXT pFileContext; PIO_STACK_LOCATION irpStack; PKEY_VALUE_PARTIAL_INFORMATION pValueInfo; ULONG nameLen, ix;
PAGED_CODE(); DebugTrace(TRACE_PROC_ENTER,("USOpen: Enter..\n"));
//
// Check arguments.
//
if( (NULL == pDeviceObject) || (NULL == pDeviceObject->DeviceExtension) || (NULL == pIrp) ) { DebugTrace(TRACE_ERROR,("USOpen: ERROR!! Invalid parameter passed.\n")); Status = STATUS_INVALID_PARAMETER; DebugTrace(TRACE_PROC_LEAVE,("USOpen: Leaving.. Status = %x.\n", Status)); return Status; }
//
// Increment I/O processing counter.
//
USIncrementIoCount( pDeviceObject );
//
// Initialize locals.
//
pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension; irpStack = IoGetCurrentIrpStackLocation (pIrp); fileObject = irpStack->FileObject; pValueInfo = NULL;
Status = STATUS_SUCCESS;
//
// Initialize file context.
//
fileObject->FsContext = NULL; //
// Check if it's accepting requests.
//
if (FALSE == pde -> AcceptingRequests) { DebugTrace(TRACE_WARNING,("USOpen: WARNING!! Device isn't accepting request.\n")); Status = STATUS_DELETE_PENDING; goto USOpen_return; }
//
// Check device power state.
//
if (PowerDeviceD0 != pde -> CurrentDevicePowerState) { DebugTrace(TRACE_WARNING,("USOpen: WARNING!! Device is suspended.\n")); Status = STATUS_DELETE_PENDING; goto USOpen_return; }
//
// Allocate file context buffer.
//
pFileContext = USAllocatePool(NonPagedPool, sizeof(USBSCAN_FILE_CONTEXT)); if(NULL == pFileContext){ DebugTrace(TRACE_CRITICAL,("USOpen: ERROR!! Can't allocate file context\n")); Status = STATUS_INSUFFICIENT_RESOURCES; goto USOpen_return; } RtlZeroMemory(pFileContext, sizeof(USBSCAN_FILE_CONTEXT)); //
// Set allocated buffer to the context.
//
fileObject->FsContext = pFileContext;
//
// Check the length of CreateFile name to see if pipe is specified by prefix.
//
nameLen = fileObject->FileName.Length; DebugTrace(TRACE_STATUS,("USOpen: CreateFile name=%ws, Length=%d.\n", fileObject->FileName.Buffer, nameLen));
if (0 == nameLen) {
//
// Use default pipe
//
pFileContext->PipeIndex = -1;
} else {
//
// Pipe number must be '\' + one digit , like '\0'.
// length would be 4.
//
if( (4 != nameLen) || (fileObject->FileName.Buffer[1] < (WCHAR) '0') || (fileObject->FileName.Buffer[1] > (WCHAR) '9') ) { DebugTrace(TRACE_ERROR,("USOpen: ERROR!! Invalid CreateFile Name\n")); Status = STATUS_INVALID_PARAMETER; } else { pFileContext->PipeIndex = (LONG)(fileObject->FileName.Buffer[1] - (WCHAR) '0');
//
// Check if pipe index is lower than maximum
//
if(pFileContext->PipeIndex > (LONG)pde->NumberOfPipes){ DebugTrace(TRACE_ERROR,("USOpen: ERROR!! Invalid pipe index(0x%x). Use default.\n", pFileContext->PipeIndex)); pFileContext->PipeIndex = -1; Status = STATUS_INVALID_PARAMETER; } } }
//
// Read default timeout value from registry. If not exist, then set default.
//
// Timeout for Read.
Status = UsbScanReadDeviceRegistry(pde, USBSCAN_REG_TIMEOUT_READ, &pValueInfo); if(NT_SUCCESS(Status)){ if(NULL != pValueInfo){ pFileContext->TimeoutRead = *((PULONG)pValueInfo->Data); USFreePool(pValueInfo); pValueInfo = NULL; } else { DebugTrace(TRACE_ERROR,("USOpen: ERROR!! UsbScanReadDeviceRegistry(1) succeeded but pValueInfo is NULL.\n")); pFileContext->TimeoutRead = USBSCAN_TIMEOUT_READ; } } else { pFileContext->TimeoutRead = USBSCAN_TIMEOUT_READ; } DebugTrace(TRACE_STATUS,("USOpen: Default Read timeout=0x%xsec.\n", pFileContext->TimeoutRead));
// Timeout for Write.
Status = UsbScanReadDeviceRegistry(pde, USBSCAN_REG_TIMEOUT_WRITE, &pValueInfo); if(NT_SUCCESS(Status)){ if(NULL != pValueInfo){ pFileContext->TimeoutWrite = *((PULONG)pValueInfo->Data); USFreePool(pValueInfo); pValueInfo = NULL; } else { DebugTrace(TRACE_ERROR,("USOpen: ERROR!! UsbScanReadDeviceRegistry(2) succeeded but pValueInfo is NULL.\n")); pFileContext->TimeoutRead = USBSCAN_TIMEOUT_WRITE; } } else { pFileContext->TimeoutWrite = USBSCAN_TIMEOUT_WRITE;
} DebugTrace(TRACE_STATUS,("USOpen: Default Write timeout=0x%xsec.\n", pFileContext->TimeoutWrite));
// Timeout for Event.
Status = UsbScanReadDeviceRegistry(pde, USBSCAN_REG_TIMEOUT_EVENT, &pValueInfo); if(NT_SUCCESS(Status)){ if(NULL != pValueInfo){ pFileContext->TimeoutEvent = *((PULONG)pValueInfo->Data); USFreePool(pValueInfo); pValueInfo = NULL; } else { DebugTrace(TRACE_ERROR,("USOpen: ERROR!! UsbScanReadDeviceRegistry(3) succeeded but pValueInfo is NULL.\n")); pFileContext->TimeoutRead = USBSCAN_TIMEOUT_EVENT; } } else { pFileContext->TimeoutEvent = USBSCAN_TIMEOUT_EVENT; } DebugTrace(TRACE_STATUS,("USOpen: Default Event timeout=0x%xsec.\n", pFileContext->TimeoutEvent)); //
// Return successfully.
//
Status = STATUS_SUCCESS;
USOpen_return:
pIrp -> IoStatus.Information = 0; pIrp -> IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NO_INCREMENT);
USDecrementIoCount(pDeviceObject); DebugTrace(TRACE_PROC_LEAVE,("USOpen: Leaving.. Status = %x.\n", Status)); return Status;
} // end USOpen()
NTSTATUS USFlush( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++
Routine Description:
Arguments: pDeviceObject - Device object for a device. pIrp - Close request packet
Return Value: NT Status - STATUS_SUCCESS
--*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; ULONG i;
PAGED_CODE(); DebugTrace(TRACE_PROC_ENTER,("USFlush: Enter..\n"));
//
// Check arguments.
//
if( (NULL == pDeviceObject) || (NULL == pDeviceObject->DeviceExtension) || (NULL == pIrp) ) { DebugTrace(TRACE_ERROR,("USFlush: ERROR!! Invalid parameter passed.\n")); Status = STATUS_INVALID_PARAMETER; DebugTrace(TRACE_PROC_LEAVE,("USFlush: Leaving.. Status = %x.\n", Status)); return Status; }
USIncrementIoCount( pDeviceObject );
pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;
Status = STATUS_SUCCESS; for(i = 0; i < pde->NumberOfPipes; i++){ if( (pde->PipeInfo[i].PipeType == UsbdPipeTypeBulk) && (pde->PipeInfo[i].EndpointAddress & BULKIN_FLAG) ) { DebugTrace(TRACE_STATUS,("USFlush: Flushing Buffer[%d].\n",i));
if (pde->ReadPipeBuffer[i].RemainingData > 0) { DebugTrace(TRACE_STATUS,("USFlush: Buffer[%d] 0x%p -> 0x%p.\n", i, pde->ReadPipeBuffer[i].pBuffer, pde->ReadPipeBuffer[i].pStartBuffer)); pde->ReadPipeBuffer[i].pBuffer = pde->ReadPipeBuffer[i].pStartBuffer; pde->ReadPipeBuffer[i].RemainingData = 0; } } }
pIrp -> IoStatus.Information = 0; pIrp -> IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NO_INCREMENT);
USDecrementIoCount(pDeviceObject); DebugTrace(TRACE_PROC_LEAVE,("USFlush: Leaving.. Status = %x.\n", Status)); return Status;
} // end USFlush()
NTSTATUS USClose( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++
Routine Description:
Arguments: pDeviceObject - Device object for a device. pIrp - Close request packet
Return Value: NT Status - STATUS_SUCCESS
--*/ { NTSTATUS Status; PFILE_OBJECT fileObject; PUSBSCAN_FILE_CONTEXT pFileContext; PIO_STACK_LOCATION pIrpStack;
PAGED_CODE(); DebugTrace(TRACE_PROC_ENTER,("USClose: Enter..\n"));
//
// Check arguments.
//
if( (NULL == pDeviceObject) || (NULL == pDeviceObject->DeviceExtension) || (NULL == pIrp) ) { DebugTrace(TRACE_ERROR,("USClose: ERROR!! Invalid parameter passed.\n")); Status = STATUS_INVALID_PARAMETER; DebugTrace(TRACE_PROC_LEAVE,("USClose: Leaving.. Status = %x.\n", Status)); return Status; }
USIncrementIoCount( pDeviceObject );
//
// Initialize locals.
//
pIrpStack = IoGetCurrentIrpStackLocation (pIrp); fileObject = pIrpStack->FileObject; pFileContext = fileObject->FsContext;
//
// Free context buffer.
//
ASSERT(NULL != pFileContext); USFreePool(pFileContext); pFileContext = NULL;
//
// Complete.
//
Status = STATUS_SUCCESS;
pIrp -> IoStatus.Information = 0; pIrp -> IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NO_INCREMENT);
USDecrementIoCount(pDeviceObject); DebugTrace(TRACE_PROC_LEAVE,("USClose: Leaving.. Status = %x.\n", Status)); return Status;
} // end USClose()
NTSTATUS USRead( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++
Routine Description:
Arguments: pDeviceObject - Device object for a device. pIrp - Read request packet
Return Value: NT Status - STATUS_SUCCESS
--*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; PIO_STACK_LOCATION pIrpStack; PFILE_OBJECT fileObject; PUSBSCAN_FILE_CONTEXT pFileContext; ULONG Timeout; PULONG pTimeout;
PAGED_CODE(); DebugTrace(TRACE_PROC_ENTER,("USRead: Enter..\n"));
//
// Check arguments.
//
if( (NULL == pDeviceObject) || (NULL == pDeviceObject->DeviceExtension) || (NULL == pIrp) ) { DebugTrace(TRACE_ERROR,("USRead: ERROR!! Invalid parameter passed.\n")); Status = STATUS_INVALID_PARAMETER; DebugTrace(TRACE_PROC_LEAVE,("USRead: Leaving.. Status = %x.\n", Status)); return Status; }
ASSERT(pIrp -> MdlAddress);
USIncrementIoCount( pDeviceObject );
//
// Initialize locals.
//
pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;
//
// Check if it's accepting requests.
//
if (pde -> AcceptingRequests == FALSE) { DebugTrace(TRACE_ERROR,("USRead: ERROR!! Read issued after device stopped/removed!\n")); Status = STATUS_FILE_CLOSED; pIrp -> IoStatus.Information = 0; pIrp -> IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NO_INCREMENT); goto USRead_return; }
//
// Check device power state.
//
if (PowerDeviceD0 != pde -> CurrentDevicePowerState) { DebugTrace(TRACE_WARNING,("USRead: WARNING!! Device is suspended.\n")); Status = STATUS_FILE_CLOSED; pIrp -> IoStatus.Information = 0; pIrp -> IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NO_INCREMENT); goto USRead_return; }
pIrpStack = IoGetCurrentIrpStackLocation (pIrp); fileObject = pIrpStack->FileObject; pFileContext = fileObject->FsContext;
//
// Copy timeout value for Read from file context.
//
Timeout = pFileContext->TimeoutRead; //
// If timeout value is 0, then never timeout.
//
if(0 == Timeout){ pTimeout = NULL; } else { DebugTrace(TRACE_STATUS,("USRead: Timeout is set to 0x%x sec.\n", Timeout)); pTimeout = &Timeout; }
//
// Call worker funciton.
//
Status = USTransfer(pDeviceObject, pIrp, pde -> IndexBulkIn, NULL, pIrp -> MdlAddress, pIrpStack -> Parameters.Read.Length, pTimeout); //
// IRP should be completed in USTransfer or its completion routine.
//
USRead_return: USDecrementIoCount(pDeviceObject); DebugTrace(TRACE_PROC_LEAVE,("USRead: Leaving.. Status = %x.\n", Status)); return Status; }
NTSTATUS USWrite( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++
Routine Description:
Arguments: pDeviceObject - Device object for a device. pIrp - Write request packet
Return Value: NT Status - STATUS_SUCCESS
--*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; PIO_STACK_LOCATION pIrpStack; PFILE_OBJECT fileObject; PUSBSCAN_FILE_CONTEXT pFileContext; ULONG Timeout; PULONG pTimeout;
PAGED_CODE();
DebugTrace(TRACE_PROC_ENTER,("USWrite: Enter..\n"));
//
// Check arguments.
//
if( (NULL == pDeviceObject) || (NULL == pDeviceObject->DeviceExtension) || (NULL == pIrp) ) { DebugTrace(TRACE_ERROR,("USWrite: ERROR!! Invalid parameter passed.\n")); Status = STATUS_INVALID_PARAMETER; DebugTrace(TRACE_PROC_LEAVE,("USWrite: Leaving.. Status = %x.\n", Status)); return Status; }
USIncrementIoCount( pDeviceObject );
//
// Initialize locals.
//
pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;
//
// Check if it's accepting requests.
//
if (pde -> AcceptingRequests == FALSE) { DebugTrace(TRACE_ERROR,("USWrite: ERROR!! Write issued after device stopped/removed!\n")); Status = STATUS_FILE_CLOSED; pIrp -> IoStatus.Information = 0; pIrp -> IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NO_INCREMENT); goto USWrite_return; }
//
// Check device power state.
//
if (PowerDeviceD0 != pde -> CurrentDevicePowerState) { DebugTrace(TRACE_WARNING,("USWrite: WARNING!! Device is suspended.\n")); Status = STATUS_FILE_CLOSED; pIrp -> IoStatus.Information = 0; pIrp -> IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NO_INCREMENT); goto USWrite_return; }
pIrpStack = IoGetCurrentIrpStackLocation (pIrp); fileObject = pIrpStack->FileObject; pFileContext = fileObject->FsContext;
//
// Copy timeout value for Write from file context.
//
Timeout = pFileContext->TimeoutWrite; //
// If timeout value is 0, then never timeout.
//
if(0 == Timeout){ pTimeout = NULL; } else { DebugTrace(TRACE_STATUS,("USWrite: Timeout is set to 0x%x sec.\n", Timeout)); pTimeout = &Timeout; }
//
// Call worker funciton.
//
#if DBG
{ PUCHAR pDumpBuf = NULL;
if (NULL != pIrp -> MdlAddress) { pIrp -> MdlAddress -> MdlFlags |= MDL_MAPPING_CAN_FAIL; pDumpBuf = MmGetSystemAddressForMdl(pIrp -> MdlAddress); }
if(NULL != pDumpBuf){ MyDumpMemory(pDumpBuf, pIrpStack -> Parameters.Write.Length, FALSE); } } #endif // DBG
Status = USTransfer(pDeviceObject, pIrp, pde -> IndexBulkOut, NULL, pIrp -> MdlAddress, pIrpStack -> Parameters.Write.Length, pTimeout);
//
// IRP should be completed in USTransfer or its completion routine.
//
USWrite_return: USDecrementIoCount(pDeviceObject); DebugTrace(TRACE_PROC_LEAVE,("USWrite: Leaving.. Status = %x.\n", Status)); return Status; }
NTSTATUS USTransfer( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN ULONG Index, IN PVOID pBuffer, // Either pBuffer or pMdl
IN PMDL pMdl, // must be passed in.
IN ULONG TransferSize, IN PULONG pTimeout ) /*++
Routine Description:
Arguments: pDeviceObject - Device object for a device. pOrigianlIrp - Original IRP to Read/Write.
Return Value: NT Status - STATUS_SUCCESS
--*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; PIO_STACK_LOCATION pNextIrpStack; PTRANSFER_CONTEXT pTransferContext; PURB pUrb; PUSBSCAN_PACKETS pPackets; ULONG siz = 0; ULONG MaxPacketSize; ULONG MaxTransferSize; ULONG PipeIndex; BOOLEAN fNextReadBlocked; BOOLEAN fBulkIn; BOOLEAN fNeedCompletion; PAGED_CODE();
DebugTrace(TRACE_PROC_ENTER,("USTransfer: Enter..\n"));
//
// Initialize status etc..
//
Status = STATUS_SUCCESS; fNeedCompletion = TRUE;
pde = NULL; pNextIrpStack = NULL; pTransferContext = NULL; pUrb = NULL; pPackets = NULL;;
//
// Check the arguments.
//
if( (NULL == pIrp) || ( (NULL == pBuffer) && (NULL == pMdl) && (0 != TransferSize) ) || (Index > MAX_NUM_PIPES) ) { DebugTrace(TRACE_ERROR,("USTransfer: ERROR!! Invalid argment.\n")); Status = STATUS_INVALID_PARAMETER; goto USTransfer_return; }
//
// Initialize status etc..
//
pIrp -> IoStatus.Information = 0; pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;
pNextIrpStack = IoGetNextIrpStackLocation(pIrp);
//
// Pickup PipeIndex to use
//
PipeIndex = USGetPipeIndexToUse(pDeviceObject, pIrp, Index);
DebugTrace(TRACE_STATUS,("USTransfer: Transfer [pipe %d] called. size = %d, pBuffer = 0x%p, Mdl = 0x%p \n", PipeIndex, TransferSize, pBuffer, pMdl ));
MaxTransferSize = pde -> PipeInfo[PipeIndex].MaximumTransferSize; MaxPacketSize = pde -> PipeInfo[PipeIndex].MaximumPacketSize;
fBulkIn = ((pde->PipeInfo[PipeIndex].PipeType == UsbdPipeTypeBulk) && (pde->PipeInfo[PipeIndex].EndpointAddress & BULKIN_FLAG));
#if DBG
if (TransferSize > MaxTransferSize) { DebugTrace(TRACE_STATUS,("USTransfer: Transfer > max transfer size.\n")); } #endif
ASSERT(PipeIndex <= MAX_NUM_PIPES);
fNextReadBlocked = FALSE;
if (fBulkIn) {
//
// Get exclusive access to each read buffer by using event
//
DebugTrace(TRACE_STATUS,("USTransfer: Waiting for Sync event for Pipe %d...\n", PipeIndex));
if(NULL != pTimeout){ LARGE_INTEGER Timeout; Timeout = RtlConvertLongToLargeInteger(-10*1000*1000*(*pTimeout)); Status = KeWaitForSingleObject(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, Executive, KernelMode, FALSE, &Timeout); } else { Status = KeWaitForSingleObject(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, Executive, KernelMode, FALSE, 0); } if(STATUS_SUCCESS != Status){ KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); DebugTrace(TRACE_ERROR,("USTransfer: ERROR!! KeWaitForSingleObject() failed. Status=0x%x.\n", Status)); if(STATUS_TIMEOUT == Status){ Status = STATUS_IO_TIMEOUT; } else { Status = STATUS_UNSUCCESSFUL; } goto USTransfer_return; } // if(STATUS_SUCCESS != Status)
DebugTrace(TRACE_STATUS,("USTransfer: Get access to Pipe %d !!\n", PipeIndex));
fNextReadBlocked = TRUE;
//
// If there is remaining data in the read pipe buffer, copy it into the irp transfer buffer.
// Update the irp transfer pointer, number of bytes left to transfer, the read pipe buffer pointer
// and the remaining number of bytes left in the read pipe buffer.
//
if (pde -> ReadPipeBuffer[PipeIndex].RemainingData > 0) { DebugTrace(TRACE_STATUS,("USTransfer: Copying %d buffered bytes into irp\n", pde -> ReadPipeBuffer[PipeIndex].RemainingData)); siz = min(pde -> ReadPipeBuffer[PipeIndex].RemainingData, TransferSize); if (NULL == pBuffer) {
//
// There's no buffer. Try to use Mdl instead.
//
if(NULL == pMdl){
//
// Error: Both Buffer and Mdl are NULL.
//
Status = STATUS_INVALID_PARAMETER; KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); DebugTrace(TRACE_ERROR,("USTransfer: ERROR!! Both Buffer&Mdl=NULL.\n")); goto USTransfer_return;
} else { pMdl->MdlFlags |= MDL_MAPPING_CAN_FAIL; pBuffer = MmGetSystemAddressForMdl(pMdl); if(NULL == pBuffer){ Status = STATUS_INSUFFICIENT_RESOURCES; KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); DebugTrace(TRACE_ERROR,("USTransfer: ERROR!! MmGetSystemAddressForMdl failed.\n")); goto USTransfer_return; } pMdl = NULL; } } ASSERT(siz > 0); ASSERT(pBuffer); ASSERT(pde -> ReadPipeBuffer[PipeIndex].pBuffer); RtlCopyMemory(pBuffer,pde -> ReadPipeBuffer[PipeIndex].pBuffer, siz); pde -> ReadPipeBuffer[PipeIndex].pBuffer += siz; pde -> ReadPipeBuffer[PipeIndex].RemainingData -= siz; ASSERT((LONG)pde -> ReadPipeBuffer[PipeIndex].RemainingData >= 0); if (0 == pde -> ReadPipeBuffer[PipeIndex].RemainingData) { DebugTrace(TRACE_STATUS,("USTransfer: read buffer emptied.\n")); pde -> ReadPipeBuffer[PipeIndex].pBuffer = pde -> ReadPipeBuffer[PipeIndex].pStartBuffer; } (PUCHAR)(pBuffer) += siz; TransferSize -= siz; ASSERT((LONG)TransferSize >= 0);
// If the read irp was completely satisfied from data in the read buffer, then
// unblock the next pending read and return success.
if (0 == TransferSize) { pIrp -> IoStatus.Information = siz; Status = STATUS_SUCCESS; KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); DebugTrace(TRACE_STATUS,("USTransfer: Irp satisfied from ReadBuffer.\n")); goto USTransfer_return; } } // if (pde -> ReadPipeBuffer[PipeIndex].RemainingData > 0)
//
// If this read is an integer number of usb packets, it will not affect
// the state of the read buffer. Unblock the next waiting read in this case.
//
if (0 == TransferSize % MaxPacketSize) { DebugTrace(MAX_TRACE,("USTransfer: Unblocking next read.\n")); KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); fNextReadBlocked = FALSE; } } // if (fBulkIn)
//
// Allocate and initialize Transfer Context
//
pTransferContext = USAllocatePool(NonPagedPool, sizeof(TRANSFER_CONTEXT)); if (NULL == pTransferContext) { DebugTrace(TRACE_CRITICAL,("USTransfer: ERROR!! cannot allocated Transfer Context\n")); DEBUG_BREAKPOINT(); Status = STATUS_INSUFFICIENT_RESOURCES; if (fNextReadBlocked) { KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); } goto USTransfer_return; } RtlZeroMemory(pTransferContext, sizeof(TRANSFER_CONTEXT));
//
// Allocate and initialize URB
//
pUrb = USAllocatePool(NonPagedPool, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER)); if (NULL == pUrb) { DebugTrace(TRACE_CRITICAL,("USTransfer: ERROR!! cannot allocated URB\n")); DEBUG_BREAKPOINT(); Status = STATUS_INSUFFICIENT_RESOURCES; if (fNextReadBlocked) { KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); } goto USTransfer_return; } RtlZeroMemory(pUrb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
ASSERT(pUrb); ASSERT(pTransferContext);
pTransferContext -> fDestinedForReadBuffer = FALSE; pTransferContext -> fNextReadBlocked = fNextReadBlocked; pTransferContext -> RemainingTransferLength = TransferSize; pTransferContext -> ChunkSize = TransferSize; pTransferContext -> PipeIndex = PipeIndex; pTransferContext -> pTransferBuffer = pBuffer; pTransferContext -> pTransferMdl = pMdl; pTransferContext -> NBytesTransferred = siz; pTransferContext -> pUrb = pUrb; pTransferContext -> pThisIrp = pIrp; pTransferContext -> pDeviceObject = pDeviceObject;
//
// IF the transfer is > MaxTransferSize, OR
// IF the transfer is not a multiple of a USB packet AND it is a read transfer THEN
// Check if we have been passed an MDL. If so, we need to turn it into a pointer so
// that we can advance it when the transfer is broken up into smaller transfers.
//
if( (pTransferContext -> ChunkSize > MaxTransferSize) || ( (0 != pTransferContext -> ChunkSize % MaxPacketSize) && (fBulkIn) ) ) { if (NULL == pTransferContext -> pTransferBuffer) { DebugTrace(TRACE_STATUS,("USTransfer: Converting MDL to buffer pointer.\n")); ASSERT(pTransferContext -> pTransferMdl); pTransferContext -> pTransferMdl ->MdlFlags |= MDL_MAPPING_CAN_FAIL;
pTransferContext -> pTransferBuffer = MmGetSystemAddressForMdl(pTransferContext -> pTransferMdl); pTransferContext -> pTransferMdl = NULL; ASSERT(pTransferContext -> pTransferBuffer); if(NULL == pTransferContext -> pTransferBuffer){ Status = STATUS_INSUFFICIENT_RESOURCES; if (fNextReadBlocked) { KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); } goto USTransfer_return; } } }
//
// If chunksize is bigger than MaxTransferSize, then set it to MaxTransferSize. The
// transfer completion routine will issue additional transfers until the total size has
// been transferred.
//
if (pTransferContext -> ChunkSize > MaxTransferSize) { pTransferContext -> ChunkSize = MaxTransferSize; }
if (fBulkIn) {
//
// If this read is smaller than a USB packet, then issue a request for a
// whole usb packet and make sure it goes into the read buffer first.
//
if (pTransferContext -> ChunkSize < MaxPacketSize) { DebugTrace(TRACE_STATUS,("USTransfer: Request is < packet size - transferring whole packet into read buffer.\n")); pTransferContext -> fDestinedForReadBuffer = TRUE; pTransferContext -> pOriginalTransferBuffer = pTransferContext -> pTransferBuffer; // save off original transfer ptr.
pTransferContext -> pTransferBuffer = pde -> ReadPipeBuffer[PipeIndex].pBuffer; pTransferContext -> ChunkSize = MaxPacketSize; }
//
// Truncate the size of the read to an integer number of packets. If necessary,
// the completion routine will handle any fractional remaining packets (with the read buffer).
//
pTransferContext -> ChunkSize = (pTransferContext -> ChunkSize / MaxPacketSize) * MaxPacketSize; }
// ASSERT(pTransferContext -> RemainingTransferLength);
// ASSERT((pTransferContext -> pTransferBuffer) || (pTransferContext -> pTransferMdl));
ASSERT(pTransferContext -> pUrb);
//
// Initialize URB
//
UsbBuildInterruptOrBulkTransferRequest(pUrb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), pde ->PipeInfo[PipeIndex].PipeHandle, pTransferContext -> pTransferBuffer, pTransferContext -> pTransferMdl, pTransferContext -> ChunkSize, USBD_SHORT_TRANSFER_OK, NULL);
//
// Setup stack location for lower driver
//
pNextIrpStack -> MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; pNextIrpStack -> MinorFunction = 0; pNextIrpStack -> Parameters.DeviceIoControl.IoControlCode = (ULONG)IOCTL_INTERNAL_USB_SUBMIT_URB; pNextIrpStack -> Parameters.Others.Argument1 = pUrb;
if(NULL != pTimeout){ pTransferContext -> Timeout = RtlConvertLongToLargeInteger(-10*1000*1000*(*pTimeout));
//
// Initialize timer and DPC.
//
KeInitializeTimer(&(pTransferContext->Timer)); KeInitializeDpc(&(pTransferContext->TimerDpc), (PKDEFERRED_ROUTINE)USTimerDpc, (PVOID)pIrp); //
// Enqueue timer object for timeout.
//
DebugTrace(TRACE_STATUS,("USTransfer: Set timeout(0x%x x 100n sec).\n", -(pTransferContext -> Timeout.QuadPart))); if(KeSetTimer(&(pTransferContext->Timer), pTransferContext -> Timeout, &(pTransferContext->TimerDpc))) { DebugTrace(TRACE_ERROR,("USTransfer: Timer object already exist.\n")); } } else { DebugTrace(TRACE_STATUS,("USTransfer: No timeout for this IRP.\n")); }
//
// Increment processing I/O count, will be decremented in completion.
//
USIncrementIoCount( pDeviceObject );
//
// Mark pending to IRP.
//
IoMarkIrpPending(pIrp);
//
// Set Completion Routine.
//
IoSetCompletionRoutine(pIrp, USTransferComplete, pTransferContext, TRUE, TRUE, TRUE);
//
// Call down.
//
fNeedCompletion = FALSE; Status = IoCallDriver(pde -> pStackDeviceObject, pIrp); if(STATUS_PENDING != Status){ DebugTrace(TRACE_ERROR,("USTransfer: ERROR!! Lower driver returned 0x%x.\n", Status)); }
//
// Must return STATUS_PENDING.
//
Status = STATUS_PENDING;
USTransfer_return:
if(fNeedCompletion){ if(NULL != pIrp){ DebugTrace(TRACE_STATUS,("USTransfer: Completeing IRP now.\n")); //
// Error or data satisfied from buffer.
//
pIrp->IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } // if(NULL != pIrp)
if(NULL != pUrb){ USFreePool(pUrb); } if(NULL != pTransferContext){ USFreePool(pTransferContext); } } DebugTrace(TRACE_PROC_LEAVE,("USTransfer: Leaving.. Status = 0x%x.\n", Status)); return Status; }
NTSTATUS USTransferComplete( IN PDEVICE_OBJECT pPassedDeviceObject, IN PIRP pIrp, IN PTRANSFER_CONTEXT pTransferContext ) /*++
Routine Description:
Arguments: pPassedDeviceObject - Device object for a device. pIrp - Read/write request packet pTransferContext - context info for transfer
Return Value: NT Status - STATUS_SUCCESS
--*/ { NTSTATUS Status; PIO_STACK_LOCATION pIrpStack; PIO_STACK_LOCATION pNextIrpStack; PUSBSCAN_DEVICE_EXTENSION pde; PDEVICE_OBJECT pDeviceObject; PURB pUrb; ULONG CompletedTransferLength; NTSTATUS CompletedTransferStatus; ULONG MaxPacketSize; BOOLEAN fShortTransfer = FALSE; BOOLEAN fBulkIn; ULONG PipeIndex;
DebugTrace(TRACE_PROC_ENTER,("USTransferComplete: Enter.. - called. irp = 0x%p\n",pIrp));
ASSERT(pIrp); ASSERT(pTransferContext);
Status = pIrp -> IoStatus.Status; pIrp -> IoStatus.Information = 0;
if(NULL == pPassedDeviceObject){ pDeviceObject = pTransferContext->pDeviceObject; } else { pDeviceObject = pPassedDeviceObject; }
pIrpStack = IoGetCurrentIrpStackLocation(pIrp); pNextIrpStack = IoGetNextIrpStackLocation(pIrp);
pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension; PipeIndex = pTransferContext -> PipeIndex; MaxPacketSize = pde -> PipeInfo[PipeIndex].MaximumPacketSize;
fBulkIn = ((pde->PipeInfo[PipeIndex].PipeType == UsbdPipeTypeBulk) && (pde->PipeInfo[PipeIndex].EndpointAddress & BULKIN_FLAG));
pUrb = pTransferContext -> pUrb; CompletedTransferLength = pUrb -> UrbBulkOrInterruptTransfer.TransferBufferLength; CompletedTransferStatus = pUrb -> UrbBulkOrInterruptTransfer.Hdr.Status;
if( (STATUS_SUCCESS == CompletedTransferStatus) && (STATUS_SUCCESS == Status) ) {
if (CompletedTransferLength < pTransferContext -> ChunkSize) { DebugTrace(TRACE_STATUS,("USTransferComplete: Short transfer received. Length = %d, ChunkSize = %d\n", CompletedTransferLength, pTransferContext -> ChunkSize)); fShortTransfer = TRUE; }
//
// If this transfer went into the read buffer, then this should be the final read
// of either a multipart larger read, or a single very small read (< single usb packet).
// In either case, we need to copy the appropriate amount of data into the user's irp, update the
// read buffer variables, and complete the user's irp.
//
if (pTransferContext -> fDestinedForReadBuffer) { DebugTrace(TRACE_STATUS,("USTransferComplete: Read transfer completed. size = %d\n", CompletedTransferLength)); ASSERT(CompletedTransferLength <= MaxPacketSize); ASSERT(pTransferContext -> pOriginalTransferBuffer); ASSERT(pTransferContext -> pTransferBuffer); ASSERT(pde -> ReadPipeBuffer[PipeIndex].pBuffer == pTransferContext -> pTransferBuffer); ASSERT(pTransferContext -> RemainingTransferLength < MaxPacketSize);
pde -> ReadPipeBuffer[PipeIndex].RemainingData = CompletedTransferLength; CompletedTransferLength = min(pTransferContext -> RemainingTransferLength, pde -> ReadPipeBuffer[PipeIndex].RemainingData); ASSERT(CompletedTransferLength < MaxPacketSize); RtlCopyMemory(pTransferContext -> pOriginalTransferBuffer, pde -> ReadPipeBuffer[PipeIndex].pBuffer, CompletedTransferLength); pde -> ReadPipeBuffer[PipeIndex].pBuffer += CompletedTransferLength; pde -> ReadPipeBuffer[PipeIndex].RemainingData -= CompletedTransferLength;
if (0 == pde -> ReadPipeBuffer[PipeIndex].RemainingData) { DebugTrace(TRACE_STATUS,("USTransferComplete: Read buffer emptied.\n")); pde -> ReadPipeBuffer[PipeIndex].pBuffer = pde -> ReadPipeBuffer[PipeIndex].pStartBuffer; } pTransferContext -> pTransferBuffer = pTransferContext -> pOriginalTransferBuffer; }
//
// Update the number of bytes transferred, remaining bytes to transfer
// and advance the transfer buffer pointer appropriately.
//
pTransferContext -> NBytesTransferred += CompletedTransferLength; if (pTransferContext -> pTransferBuffer) { pTransferContext -> pTransferBuffer += CompletedTransferLength; } pTransferContext -> RemainingTransferLength -= CompletedTransferLength;
//
// If there is still data to transfer and the previous transfer was NOT a
// short transfer, then issue another request to move the next chunk of data.
//
if (pTransferContext -> RemainingTransferLength > 0) { if (!fShortTransfer) {
DebugTrace(TRACE_STATUS,("USTransferComplete: Queuing next chunk. RemainingSize = %d, pBuffer = 0x%p\n", pTransferContext -> RemainingTransferLength, pTransferContext -> pTransferBuffer ));
if (pTransferContext -> RemainingTransferLength < pTransferContext -> ChunkSize) { pTransferContext -> ChunkSize = pTransferContext -> RemainingTransferLength; }
//
// Reinitialize URB
//
// If the next transfer is < than 1 packet, change it's destination to be
// the read buffer. When this transfer completes, the appropriate amount of data will be
// copied out of the read buffer and into the user's irp. Left over data in the read buffer
// will be available for subsequent reads.
//
if (fBulkIn) { if (pTransferContext -> ChunkSize < MaxPacketSize) { pTransferContext -> fDestinedForReadBuffer = TRUE; pTransferContext -> pOriginalTransferBuffer = pTransferContext -> pTransferBuffer; pTransferContext -> pTransferBuffer = pde -> ReadPipeBuffer[PipeIndex].pBuffer; pTransferContext -> ChunkSize = MaxPacketSize; } pTransferContext -> ChunkSize = (pTransferContext -> ChunkSize / MaxPacketSize) * MaxPacketSize; }
ASSERT(pTransferContext -> ChunkSize >= MaxPacketSize); ASSERT(0 == pTransferContext -> ChunkSize % MaxPacketSize); UsbBuildInterruptOrBulkTransferRequest(pUrb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), pde -> PipeInfo[PipeIndex].PipeHandle, pTransferContext -> pTransferBuffer, NULL, pTransferContext -> ChunkSize, USBD_SHORT_TRANSFER_OK, NULL); IoSetCompletionRoutine(pIrp, USTransferComplete, pTransferContext, TRUE, TRUE, FALSE);
//
// Setup stack location for lower driver
//
pNextIrpStack -> MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; pNextIrpStack -> MinorFunction = 0; pNextIrpStack -> Parameters.DeviceIoControl.IoControlCode = (ULONG)IOCTL_INTERNAL_USB_SUBMIT_URB; pNextIrpStack -> Parameters.Others.Argument1 = pUrb;
IoCallDriver(pde -> pStackDeviceObject, pIrp); Status = STATUS_MORE_PROCESSING_REQUIRED; goto USTransferComplete_return;
} // if (!fShortTransfer)
} // if (pTransferContext -> RemainingTransferLength > 0)
DebugTrace(TRACE_STATUS,("USTransferComplete: Completing transfer request. nbytes transferred = %d, irp = 0x%p\n", pTransferContext -> NBytesTransferred, pIrp));
pIrp -> IoStatus.Information = pTransferContext -> NBytesTransferred;
#if DBG
{ PUCHAR pDumpBuf = NULL;
if(NULL != pTransferContext -> pTransferBuffer){ pDumpBuf = pTransferContext -> pTransferBuffer; } else if (NULL != pTransferContext -> pTransferMdl) { pTransferContext -> pTransferMdl ->MdlFlags |= MDL_MAPPING_CAN_FAIL; pDumpBuf = MmGetSystemAddressForMdl(pTransferContext -> pTransferMdl); }
if(NULL != pDumpBuf){ MyDumpMemory(pDumpBuf, pTransferContext -> NBytesTransferred, TRUE); } } #endif // DBG
} else {
DebugTrace(TRACE_ERROR,("USTransferComplete: ERROR!! Transfer error. USB status = 0x%X, status = 0x%X\n", CompletedTransferStatus, Status)); if (USBD_STATUS_CANCELED == CompletedTransferStatus) { Status = STATUS_CANCELLED; } }
//
// Running here means IRP is completed.
//
pIrp -> IoStatus.Status = Status;
if (pTransferContext -> fNextReadBlocked) { KeSetEvent(&pde -> ReadPipeBuffer[PipeIndex].ReadSyncEvent, 1, FALSE); }
//
// Dequeue timer object if exist.
//
if( (0 != pTransferContext -> Timeout.QuadPart) && (!KeReadStateTimer(&(pTransferContext->Timer))) ) { KeCancelTimer(&(pTransferContext->Timer)); }
//
// Clean-up
//
if(pTransferContext->pUrb){ USFreePool(pTransferContext->pUrb); } USDecrementIoCount(pTransferContext->pDeviceObject); USFreePool(pTransferContext);
USTransferComplete_return: DebugTrace(TRACE_PROC_LEAVE,("USTransferComplete: Leaving.. Status=%x.\n", Status)); return Status; }
ULONG USGetPipeIndexToUse( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN ULONG PipeIndex ) /*++
Routine Description:
Arguments: pDeviceObject - Device object for a device. pIrp - request packet PipeIndex - Default pipe to use
Return Value: ULONG - PipeIndex to use
--*/ { PIO_STACK_LOCATION pIrpStack; PUSBSCAN_DEVICE_EXTENSION pde; PFILE_OBJECT fileObject; PUSBSCAN_FILE_CONTEXT pFileContext; LONG StoredIndex; ULONG IndexToUse;
PAGED_CODE();
DebugTrace(TRACE_PROC_ENTER,("USGetPipeIndexToUse: Enter..\n"));
pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;
pIrpStack = IoGetCurrentIrpStackLocation (pIrp); fileObject = pIrpStack->FileObject; pFileContext = fileObject->FsContext;
ASSERT(NULL != pFileContext);
StoredIndex = pFileContext->PipeIndex;
if( (StoredIndex >= 0) && (StoredIndex < MAX_NUM_PIPES) ){ if(pde->PipeInfo[PipeIndex].PipeType == pde->PipeInfo[StoredIndex].PipeType){ IndexToUse = (ULONG)StoredIndex; } else { IndexToUse = PipeIndex; } } else { if(-1 != StoredIndex){ DebugTrace(TRACE_WARNING,("USGetPipeIndexToUse: WARINING!! Specified pipe index(0x%X) is incorrect. Using default." ,StoredIndex)); } IndexToUse = PipeIndex; } DebugTrace(TRACE_PROC_LEAVE,("USGetPipeIndexToUse: Leaving.. passed=%d, returning=%d.\n",PipeIndex, IndexToUse)); return IndexToUse; }
VOID USTimerDpc( IN PKDPC pDpc, IN PVOID pIrp, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++
Routine Description:
DPC callback routine for timer.
Arguments: pDpc - Pointer to DPC object. pIrp - Passed context. SystemArgument1 - system reserved. SystemArgument2 - system reserved.
Return Value: VOID
--*/ { DebugTrace(TRACE_WARNING,("USTimerDpc: IRP(0x%x) timeout.\n", pIrp)); IoCancelIrp((PIRP)pIrp); }
|