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.
594 lines
17 KiB
594 lines
17 KiB
/*++
|
|
|
|
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;
|
|
}
|