You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1386 lines
45 KiB
1386 lines
45 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1999 - 2001
|
|
|
|
Module Name:
|
|
|
|
ocrw.c
|
|
|
|
Abstract:
|
|
|
|
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",pIrp));
|
|
|
|
//
|
|
// 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",pIrp));
|
|
|
|
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",pIrp));
|
|
|
|
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",pIrp));
|
|
// DebugTrace(TRACE_ERROR,("USRead: Enter.. size=0x%x\n",pIrpStack -> Parameters.Write.Length));
|
|
|
|
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",pIrp));
|
|
// DebugTrace(TRACE_ERROR,("USWrite: Enter.. size=0x%x\n",pIrpStack -> Parameters.Write.Length));
|
|
|
|
// 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,("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));
|
|
goto USTransfer_return;
|
|
}
|
|
|
|
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){
|
|
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 != 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);
|
|
}
|
|
|