|
|
/*++
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; }
|