|
|
/*++
Copyright (c) 1996-1998 Microsoft Corporation
Module Name:
OCRW.C
Abstract:
This source file contains the dispatch routines which handle opening, closing, reading, and writing to the device, i.e.:
IRP_MJ_CREATE IRP_MJ_CLOSE IRP_MJ_READ IRP_MJ_WRITE
Environment:
kernel mode
Revision History:
06-01-98 : started rewrite
--*/
//*****************************************************************************
// I N C L U D E S
//*****************************************************************************
#include <wdm.h>
#include <usbdi.h>
#include <usbdlib.h>
#include "i82930.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, I82930_Create)
#pragma alloc_text(PAGE, I82930_Close)
#pragma alloc_text(PAGE, I82930_ReadWrite)
#pragma alloc_text(PAGE, I82930_BuildAsyncUrb)
#pragma alloc_text(PAGE, I82930_BuildIsoUrb)
#pragma alloc_text(PAGE, I82930_GetCurrentFrame)
#pragma alloc_text(PAGE, I82930_ResetPipe)
#pragma alloc_text(PAGE, I82930_AbortPipe)
#endif
//******************************************************************************
//
// I82930_Create()
//
// Dispatch routine which handles IRP_MJ_CREATE
//
//******************************************************************************
NTSTATUS I82930_Create ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; PFILE_OBJECT fileObject; UCHAR pipeIndex; PI82930_PIPE pipe; NTSTATUS ntStatus;
DBGPRINT(2, ("enter: I82930_Create\n"));
LOGENTRY('CREA', DeviceObject, Irp, 0);
DBGFBRK(DBGF_BRK_CREATE);
deviceExtension = DeviceObject->DeviceExtension;
INCREMENT_OPEN_COUNT(deviceExtension);
if (deviceExtension->AcceptingRequests) { irpStack = IoGetCurrentIrpStackLocation(Irp);
fileObject = irpStack->FileObject;
if (fileObject->FileName.Length != 0) { if ((fileObject->FileName.Length == 3*sizeof(WCHAR)) && (fileObject->FileName.Buffer[0] == '\\') && (fileObject->FileName.Buffer[1] >= '0' ) && (fileObject->FileName.Buffer[1] <= '9' ) && (fileObject->FileName.Buffer[2] >= '0' ) && (fileObject->FileName.Buffer[2] <= '9' )) { pipeIndex = ((fileObject->FileName.Buffer[1] - '0') * 10 + (fileObject->FileName.Buffer[2] - '0'));
if (pipeIndex < deviceExtension->InterfaceInfo->NumberOfPipes) { pipe = &deviceExtension->PipeList[pipeIndex];
#if 0
if (pipe->Opened) { // Pipe already open
//
DBGPRINT(2, ("Pipe already open\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } else #endif
{ // Good to open the pipe
//
DBGPRINT(2, ("Opened pipe %2d %08X\n", pipeIndex, pipe));
pipe->Opened = TRUE;
fileObject->FsContext = pipe;
ntStatus = STATUS_SUCCESS; } } else { // Pipe index too big
//
DBGPRINT(2, ("Pipe index too big\n")); ntStatus = STATUS_NO_SUCH_DEVICE; } } else { // Pipe name bad format
//
DBGPRINT(2, ("Pipe name bad format\n")); ntStatus = STATUS_NO_SUCH_DEVICE; } } else { // Open entire device, not an individual pipe
//
DBGPRINT(2, ("Opened device\n")); fileObject->FsContext = NULL; ntStatus = STATUS_SUCCESS; } } else { ntStatus = STATUS_DELETE_PENDING; }
Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
DBGPRINT(2, ("exit: I82930_Create %08X\n", ntStatus));
LOGENTRY('crea', ntStatus, 0, 0);
if (ntStatus != STATUS_SUCCESS) { DECREMENT_OPEN_COUNT(deviceExtension); }
return ntStatus; }
//******************************************************************************
//
// I82930_Close()
//
// Dispatch routine which handles IRP_MJ_CLOSE
//
//******************************************************************************
NTSTATUS I82930_Close ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; PFILE_OBJECT fileObject; PI82930_PIPE pipe;
DBGPRINT(2, ("enter: I82930_Close\n"));
LOGENTRY('CLOS', DeviceObject, Irp, 0);
DBGFBRK(DBGF_BRK_CLOSE);
deviceExtension = DeviceObject->DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation(Irp);
fileObject = irpStack->FileObject;
pipe = fileObject->FsContext;
if (pipe != NULL) { DBGPRINT(2, ("Closed pipe %2d %08X\n", pipe->PipeIndex, pipe));
pipe->Opened = FALSE; } else { DBGPRINT(2, ("Closed device\n")); }
Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
DBGPRINT(2, ("exit: I82930_Close\n"));
LOGENTRY('clos', 0, 0, 0);
DECREMENT_OPEN_COUNT(deviceExtension);
return STATUS_SUCCESS; }
//******************************************************************************
//
// I82930_ReadWrite()
//
// Dispatch routine which handles IRP_MJ_READ and IRP_MJ_WRITE
//
//******************************************************************************
NTSTATUS I82930_ReadWrite ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; PIO_STACK_LOCATION nextStack; PFILE_OBJECT fileObject; PI82930_PIPE pipe; PURB urb; NTSTATUS ntStatus;
DBGPRINT(2, ("enter: I82930_ReadWrite\n"));
LOGENTRY('RW ', DeviceObject, Irp, 0);
DBGFBRK(DBGF_BRK_READWRITE);
deviceExtension = DeviceObject->DeviceExtension;
if (!deviceExtension->AcceptingRequests) { ntStatus = STATUS_DELETE_PENDING; goto I82930_ReadWrite_Reject; }
irpStack = IoGetCurrentIrpStackLocation(Irp); nextStack = IoGetNextIrpStackLocation(Irp);
fileObject = irpStack->FileObject;
pipe = fileObject->FsContext;
// Only allow Reads and Writes on individual pipes, not the entire device
//
if (pipe == NULL) { ntStatus = STATUS_INVALID_PARAMETER; goto I82930_ReadWrite_Reject; }
// Only allow Reads on IN endpoints and Writes on OUT endpoints
//
if ((USB_ENDPOINT_DIRECTION_OUT(pipe->PipeInfo->EndpointAddress) && irpStack->MajorFunction != IRP_MJ_WRITE) || (USB_ENDPOINT_DIRECTION_IN(pipe->PipeInfo->EndpointAddress) && irpStack->MajorFunction != IRP_MJ_READ)) { ntStatus = STATUS_INVALID_PARAMETER; goto I82930_ReadWrite_Reject; }
// Don't allow a Read or Write on a zero bandwidth endpoint
//
if (pipe->PipeInfo->MaximumPacketSize == 0) { ntStatus = STATUS_INVALID_PARAMETER; goto I82930_ReadWrite_Reject; }
// Build either a URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER
// or a URB_FUNCTION_ISOCH_TRANSFER based on the PipeType
//
switch (pipe->PipeInfo->PipeType) { case UsbdPipeTypeBulk: case UsbdPipeTypeInterrupt: urb = I82930_BuildAsyncUrb(DeviceObject, Irp, pipe); break;
case UsbdPipeTypeIsochronous: urb = I82930_BuildIsoUrb(DeviceObject, Irp, pipe); break;
default: ntStatus = STATUS_INVALID_PARAMETER; goto I82930_ReadWrite_Reject; }
if (urb == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; goto I82930_ReadWrite_Reject; }
// Initialize the Irp stack parameters for the next lower driver
// to submit the URB
//
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
nextStack->Parameters.Others.Argument1 = urb;
// Set a completion routine which will update the Irp->IoStatus.Information
// with the URB TransferBufferLength and then free the URB.
//
IoSetCompletionRoutine(Irp, I82930_ReadWrite_Complete, urb, TRUE, TRUE, TRUE);
// Submit the URB to the next lower driver
//
ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, Irp);
goto I82930_Read_Done;
I82930_ReadWrite_Reject:
Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
I82930_Read_Done:
DBGPRINT(2, ("exit: I82930_ReadWrite %08X\n", ntStatus));
LOGENTRY('rw ', ntStatus, 0, 0);
return ntStatus; }
//******************************************************************************
//
// I82930_ReadWrite_Complete()
//
//******************************************************************************
NTSTATUS I82930_ReadWrite_Complete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PURB urb;
urb = (PURB)Context;
LOGENTRY('RWC1', DeviceObject, Irp, urb); LOGENTRY('RWC2', urb->UrbBulkOrInterruptTransfer.TransferBufferLength, urb->UrbHeader.Status, 0);
DBGPRINT(3, ("ReadWrite_Complete: Length 0x%08X, Urb Status 0x%08X, Irp Status 0x%08X\n", urb->UrbBulkOrInterruptTransfer.TransferBufferLength, urb->UrbHeader.Status, Irp->IoStatus.Status));
// Propagate the pending flag back up the Irp stack
//
if (Irp->PendingReturned) { IoMarkIrpPending(Irp); }
Irp->IoStatus.Information = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
ExFreePool(urb);
return STATUS_SUCCESS; }
//******************************************************************************
//
// I82930_BuildAsyncUrb()
//
// Allocates and initializes a URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER
// request URB
//
//******************************************************************************
PURB I82930_BuildAsyncUrb ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PI82930_PIPE Pipe ) { PIO_STACK_LOCATION irpStack; LARGE_INTEGER byteOffset; ULONG transferLength; USHORT urbSize; PURB urb;
DBGPRINT(2, ("enter: I82930_BuildAsyncUrb\n"));
irpStack = IoGetCurrentIrpStackLocation(Irp);
// We will use the ByteOffset to control the USBD_SHORT_TRANSFER_OK flag
//
byteOffset = irpStack->Parameters.Read.ByteOffset;
// Get the transfer length from the MDL
//
if (Irp->MdlAddress) { transferLength = MmGetMdlByteCount(Irp->MdlAddress); } else { transferLength = 0; }
urbSize = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
urb = ExAllocatePool(NonPagedPool, urbSize);
if (urb) { RtlZeroMemory(urb, urbSize);
urb->UrbHeader.Length = urbSize; urb->UrbHeader.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
urb->UrbBulkOrInterruptTransfer.PipeHandle = Pipe->PipeInfo->PipeHandle;
if (!byteOffset.HighPart) { urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK; }
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = transferLength;
urb->UrbBulkOrInterruptTransfer.TransferBuffer = NULL;
urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = Irp->MdlAddress;
urb->UrbBulkOrInterruptTransfer.UrbLink = NULL; }
DBGPRINT(2, ("exit: I82930_BuildAsyncUrb %08X\n", urb));
return urb; }
//******************************************************************************
//
// I82930_BuildIsoUrb()
//
// Allocates and initializes a URB_FUNCTION_ISOCH_TRANSFER request URB
//
//******************************************************************************
PURB I82930_BuildIsoUrb ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PI82930_PIPE Pipe ) { PIO_STACK_LOCATION irpStack; LARGE_INTEGER byteOffset; ULONG transferLength; ULONG packetSize; ULONG numPackets; ULONG packetIndex; ULONG urbSize; PURB urb;
DBGPRINT(2, ("enter: I82930_BuildIsoUrb\n"));
irpStack = IoGetCurrentIrpStackLocation(Irp);
// We will use the ByteOffset for +/- offset to current frame
//
byteOffset = irpStack->Parameters.Read.ByteOffset;
// Get the transfer length from the MDL
//
if (Irp->MdlAddress) { transferLength = MmGetMdlByteCount(Irp->MdlAddress); } else { transferLength = 0; }
// Calculate the number of Iso packets based on the transfer length
// and the endpoint MaxPacketSize
//
packetSize = Pipe->PipeInfo->MaximumPacketSize;
numPackets = transferLength / packetSize;
if (numPackets * packetSize < transferLength) { numPackets++; }
urbSize = GET_ISO_URB_SIZE(numPackets);
urb = ExAllocatePool(NonPagedPool, urbSize);
if (urb) { RtlZeroMemory(urb, urbSize);
urb->UrbHeader.Length = (USHORT)urbSize; urb->UrbHeader.Function = URB_FUNCTION_ISOCH_TRANSFER;
urb->UrbBulkOrInterruptTransfer.PipeHandle = Pipe->PipeInfo->PipeHandle;
urb->UrbIsochronousTransfer.TransferFlags = 0;
urb->UrbIsochronousTransfer.TransferBufferLength = transferLength;
urb->UrbIsochronousTransfer.TransferBuffer = NULL;
urb->UrbIsochronousTransfer.TransferBufferMDL = Irp->MdlAddress;
urb->UrbIsochronousTransfer.UrbLink = NULL;
// Use the ByteOffset for +/- offset to current frame
//
if (byteOffset.HighPart) { urb->UrbIsochronousTransfer.StartFrame = I82930_GetCurrentFrame(DeviceObject, Irp) + byteOffset.LowPart; } else { urb->UrbIsochronousTransfer.StartFrame = 0;
urb->UrbIsochronousTransfer.TransferFlags |= USBD_START_ISO_TRANSFER_ASAP; }
urb->UrbIsochronousTransfer.NumberOfPackets = numPackets;
for (packetIndex = 0; packetIndex < numPackets; packetIndex++) { urb->UrbIsochronousTransfer.IsoPacket[packetIndex].Offset = packetIndex * packetSize; } }
DBGPRINT(2, ("exit: I82930_BuildIsoUrb %08X\n", urb));
return urb; }
//******************************************************************************
//
// I82930_CompletionStop()
//
// Completion Routine which just stops further completion of the Irp
//
//******************************************************************************
NTSTATUS I82930_CompletionStop ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { return STATUS_MORE_PROCESSING_REQUIRED; }
//******************************************************************************
//
// I82930_GetCurrentFrame()
//
// Returns the current frame on the bus to which the device is attached.
//
// The next stack frame of the Irp is used, but the Irp is not completed.
//
//******************************************************************************
ULONG I82930_GetCurrentFrame ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION nextStack; NTSTATUS ntStatus; struct _URB_GET_CURRENT_FRAME_NUMBER urb;
deviceExtension = DeviceObject->DeviceExtension;
// Initialize the URB
//
urb.Hdr.Function = URB_FUNCTION_GET_CURRENT_FRAME_NUMBER; urb.Hdr.Length = sizeof(urb); urb.FrameNumber = (ULONG)-1;
// Set the IRP parameters to pass the URB down the stack
//
nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->Parameters.Others.Argument1 = &urb;
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
// Since this Irp is borrowed for URB_FUNCTION_GET_CURRENT_FRAME_NUMBER
// before it is passed down later for the real URB request after this
// routine returns, set a completion routine which stop further completion
// of the Irp.
//
IoSetCompletionRoutine( Irp, I82930_CompletionStop, NULL, // Context
TRUE, // InvokeOnSuccess
TRUE, // InvokeOnError
TRUE // InvokeOnCancel
);
// Now pass the Irp down the stack
//
ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, Irp);
ASSERT(ntStatus != STATUS_PENDING);
// Don't need to wait for completion because JD guarantees that
// URB_FUNCTION_GET_CURRENT_FRAME_NUMBER will never return STATUS_PENDING
return urb.FrameNumber; }
//******************************************************************************
//
// I82930_ResetPipe()
//
// This will reset the host pipe to Data0 and should also reset the device
// endpoint to Data0 for Bulk and Interrupt pipes by issuing a Clear_Feature
// Endpoint_Stall to the device endpoint.
//
// For Iso pipes this will set the virgin state of pipe so that ASAP
// transfers begin with the current bus frame instead of the next frame
// after the last transfer occurred.
//
// Iso endpoints do not use the data toggle (all Iso packets are Data0).
// However, it may be useful to issue a Clear_Feature Endpoint_Stall to a
// device Iso endpoint.
//
// Must be called at IRQL <= DISPATCH_LEVEL
//
//******************************************************************************
NTSTATUS I82930_ResetPipe ( IN PDEVICE_OBJECT DeviceObject, IN PI82930_PIPE Pipe, IN BOOLEAN IsoClearStall ) { PURB urb; NTSTATUS ntStatus;
DBGPRINT(2, ("enter: I82930_ResetPipe\n"));
LOGENTRY('RESP', DeviceObject, Pipe, IsoClearStall);
// Allocate URB for RESET_PIPE request
//
urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
if (urb != NULL) { // Initialize RESET_PIPE request URB
//
urb->UrbHeader.Length = sizeof (struct _URB_PIPE_REQUEST); urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE; urb->UrbPipeRequest.PipeHandle = Pipe->PipeInfo->PipeHandle;
// Submit RESET_PIPE request URB
//
ntStatus = I82930_SyncSendUsbRequest(DeviceObject, urb);
// Done with URB for RESET_PIPE request, free it
//
ExFreePool(urb); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; }
// Issue Clear_Feature Endpoint_Stall request for Iso pipe, if desired
//
if (NT_SUCCESS(ntStatus) && IsoClearStall && (Pipe->PipeInfo->PipeType == UsbdPipeTypeIsochronous)) { // Allocate URB for CONTROL_FEATURE request
//
urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_FEATURE_REQUEST));
if (urb != NULL) { // Initialize CONTROL_FEATURE request URB
//
urb->UrbHeader.Length = sizeof (struct _URB_CONTROL_FEATURE_REQUEST); urb->UrbHeader.Function = URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT; urb->UrbControlFeatureRequest.UrbLink = NULL; urb->UrbControlFeatureRequest.FeatureSelector = USB_FEATURE_ENDPOINT_STALL; urb->UrbControlFeatureRequest.Index = Pipe->PipeInfo->EndpointAddress;
// Submit CONTROL_FEATURE request URB
//
ntStatus = I82930_SyncSendUsbRequest(DeviceObject, urb);
// Done with URB for CONTROL_FEATURE request, free it
//
ExFreePool(urb); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } }
DBGPRINT(2, ("exit: I82930_ResetPipe %08X\n", ntStatus));
LOGENTRY('resp', ntStatus, 0, 0);
return ntStatus; }
//******************************************************************************
//
// I82930_AbortPipe()
//
// Must be called at IRQL <= DISPATCH_LEVEL
//
//******************************************************************************
NTSTATUS I82930_AbortPipe ( IN PDEVICE_OBJECT DeviceObject, IN PI82930_PIPE Pipe ) { PURB urb; NTSTATUS ntStatus;
DBGPRINT(2, ("enter: I82930_AbortPipe\n"));
LOGENTRY('ABRT', DeviceObject, Pipe, 0);
// Allocate URB for ABORT_PIPE request
//
urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
if (urb != NULL) { // Initialize ABORT_PIPE request URB
//
urb->UrbHeader.Length = sizeof (struct _URB_PIPE_REQUEST); urb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE; urb->UrbPipeRequest.PipeHandle = Pipe->PipeInfo->PipeHandle;
// Submit ABORT_PIPE request URB
//
ntStatus = I82930_SyncSendUsbRequest(DeviceObject, urb);
// Done with URB for ABORT_PIPE request, free it
//
ExFreePool(urb); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; }
DBGPRINT(2, ("exit: I82930_AbortPipe %08X\n", ntStatus));
LOGENTRY('abrt', ntStatus, 0, 0);
return ntStatus; }
|