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.
1850 lines
53 KiB
1850 lines
53 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
isorwr.c
|
|
|
|
Abstract:
|
|
|
|
This file has dispatch routines for read and write.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Notes:
|
|
|
|
Copyright (c) 2000 Microsoft Corporation.
|
|
All Rights Reserved.
|
|
|
|
--*/
|
|
|
|
#include "isousb.h"
|
|
#include "isopnp.h"
|
|
#include "isopwr.h"
|
|
#include "isodev.h"
|
|
#include "isowmi.h"
|
|
#include "isousr.h"
|
|
#include "isorwr.h"
|
|
#include "isostrm.h"
|
|
|
|
NTSTATUS
|
|
IsoUsb_DispatchReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does some validation and
|
|
invokes appropriate function to perform
|
|
Isoch transfer
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object
|
|
Irp - I/O request packet
|
|
|
|
Return Value:
|
|
|
|
NT status value
|
|
|
|
--*/
|
|
{
|
|
ULONG totalLength;
|
|
ULONG packetSize;
|
|
NTSTATUS ntStatus;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PFILE_OBJECT_CONTENT fileObjectContent;
|
|
PUSBD_PIPE_INFORMATION pipeInformation;
|
|
|
|
//
|
|
// initialize vars
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
fileObject = irpStack->FileObject;
|
|
totalLength = 0;
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_DispatchReadWrite - begins\n"));
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_DispatchReadWrite::"));
|
|
IsoUsb_IoIncrement(deviceExtension);
|
|
|
|
if(deviceExtension->DeviceState != Working) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Invalid device state\n"));
|
|
|
|
ntStatus = STATUS_INVALID_DEVICE_STATE;
|
|
goto IsoUsb_DispatchReadWrite_Exit;
|
|
}
|
|
|
|
//
|
|
// make sure that the selective suspend request has been completed.
|
|
//
|
|
if(deviceExtension->SSEnable) {
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
IsoUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));
|
|
|
|
|
|
KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
//
|
|
// obtain the pipe information for read
|
|
// and write from the fileobject.
|
|
//
|
|
if(fileObject && fileObject->FsContext) {
|
|
|
|
fileObjectContent = (PFILE_OBJECT_CONTENT) fileObject->FsContext;
|
|
|
|
pipeInformation = (PUSBD_PIPE_INFORMATION)
|
|
fileObjectContent->PipeInformation;
|
|
}
|
|
else {
|
|
|
|
IsoUsb_DbgPrint(1, ("Invalid device state\n"));
|
|
|
|
ntStatus = STATUS_INVALID_DEVICE_STATE;
|
|
goto IsoUsb_DispatchReadWrite_Exit;
|
|
}
|
|
|
|
if((pipeInformation == NULL) ||
|
|
(UsbdPipeTypeIsochronous != pipeInformation->PipeType)) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Incorrect pipe\n"));
|
|
|
|
ntStatus = STATUS_INVALID_DEVICE_STATE;
|
|
goto IsoUsb_DispatchReadWrite_Exit;
|
|
}
|
|
|
|
if(Irp->MdlAddress) {
|
|
|
|
totalLength = MmGetMdlByteCount(Irp->MdlAddress);
|
|
}
|
|
|
|
if(totalLength == 0) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Transfer data length = 0\n"));
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
goto IsoUsb_DispatchReadWrite_Exit;
|
|
}
|
|
|
|
//
|
|
// each packet can hold this much info
|
|
//
|
|
packetSize = pipeInformation->MaximumPacketSize;
|
|
|
|
if(packetSize == 0) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Invalid parameter\n"));
|
|
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
goto IsoUsb_DispatchReadWrite_Exit;
|
|
}
|
|
|
|
//
|
|
// atleast packet worth of data to be transferred.
|
|
//
|
|
if(totalLength < packetSize) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Atleast packet worth of data..\n"));
|
|
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
goto IsoUsb_DispatchReadWrite_Exit;
|
|
}
|
|
|
|
// perform reset. if there are some active transfers queued up
|
|
// for this endpoint then the reset pipe will fail.
|
|
//
|
|
IsoUsb_ResetPipe(DeviceObject, pipeInformation);
|
|
|
|
if(deviceExtension->IsDeviceHighSpeed) {
|
|
|
|
ntStatus = PerformHighSpeedIsochTransfer(DeviceObject,
|
|
pipeInformation,
|
|
Irp,
|
|
totalLength);
|
|
|
|
}
|
|
else {
|
|
|
|
ntStatus = PerformFullSpeedIsochTransfer(DeviceObject,
|
|
pipeInformation,
|
|
Irp,
|
|
totalLength);
|
|
|
|
}
|
|
|
|
return ntStatus;
|
|
|
|
IsoUsb_DispatchReadWrite_Exit:
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_DispatchReadWrite::"));
|
|
IsoUsb_IoDecrement(deviceExtension);
|
|
|
|
IsoUsb_DbgPrint(3, ("-------------------------------\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
PerformHighSpeedIsochTransfer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUSBD_PIPE_INFORMATION PipeInformation,
|
|
IN PIRP Irp,
|
|
IN ULONG TotalLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
High Speed Isoch Transfer requires packets in multiples of 8.
|
|
(Argument: 8 micro-frames per ms frame)
|
|
Another restriction is that each Irp/Urb pair can be associated
|
|
with a max of 1024 packets.
|
|
|
|
Here is one of the ways of creating Irp/Urb pairs.
|
|
Depending on the characteristics of real-world device,
|
|
the algorithm may be different
|
|
|
|
This algorithm will distribute data evenly among all the packets.
|
|
|
|
Input:
|
|
TotalLength - no. of bytes to be transferred.
|
|
|
|
Other parameters:
|
|
packetSize - max size of each packet for this pipe.
|
|
|
|
Implementation Details:
|
|
|
|
Step 1:
|
|
ASSERT(TotalLength >= 8)
|
|
|
|
Step 2:
|
|
Find the exact number of packets required to transfer all of this data
|
|
|
|
numberOfPackets = (TotalLength + packetSize - 1) / packetSize
|
|
|
|
Step 3:
|
|
Number of packets in multiples of 8.
|
|
|
|
if(0 == (numberOfPackets % 8)) {
|
|
|
|
actualPackets = numberOfPackets;
|
|
}
|
|
else {
|
|
|
|
actualPackets = numberOfPackets +
|
|
(8 - (numberOfPackets % 8));
|
|
}
|
|
|
|
Step 4:
|
|
Determine the min. data in each packet.
|
|
|
|
minDataInEachPacket = TotalLength / actualPackets;
|
|
|
|
Step 5:
|
|
After placing min data in each packet,
|
|
determine how much data is left to be distributed.
|
|
|
|
dataLeftToBeDistributed = TotalLength -
|
|
(minDataInEachPacket * actualPackets);
|
|
|
|
Step 6:
|
|
Start placing the left over data in the packets
|
|
(above the min data already placed)
|
|
|
|
numberOfPacketsFilledToBrim = dataLeftToBeDistributed /
|
|
(packetSize - minDataInEachPacket);
|
|
|
|
Step 7:
|
|
determine if there is any more data left.
|
|
|
|
dataLeftToBeDistributed -= (numberOfPacketsFilledToBrim *
|
|
(packetSize - minDataInEachPacket));
|
|
|
|
Step 8:
|
|
The "dataLeftToBeDistributed" is placed in the packet at index
|
|
"numberOfPacketsFilledToBrim"
|
|
|
|
Algorithm at play:
|
|
|
|
TotalLength = 8193
|
|
packetSize = 8
|
|
Step 1
|
|
|
|
Step 2
|
|
numberOfPackets = (8193 + 8 - 1) / 8 = 1025
|
|
|
|
Step 3
|
|
actualPackets = 1025 + 7 = 1032
|
|
|
|
Step 4
|
|
minDataInEachPacket = 8193 / 1032 = 7 bytes
|
|
|
|
Step 5
|
|
dataLeftToBeDistributed = 8193 - (7 * 1032) = 969.
|
|
|
|
Step 6
|
|
numberOfPacketsFilledToBrim = 969 / (8 - 7) = 969.
|
|
|
|
Step 7
|
|
dataLeftToBeDistributed = 969 - (969 * 1) = 0.
|
|
|
|
Step 8
|
|
Done :)
|
|
|
|
Another algorithm
|
|
Completely fill up (as far as possible) the early packets.
|
|
Place 1 byte each in the rest of them.
|
|
Ensure that the total number of packets is multiple of 8.
|
|
|
|
This routine then
|
|
1. creates a ISOUSB_RW_CONTEXT for each
|
|
read/write to be performed.
|
|
2. creates SUB_CONTEXT for each irp/urb pair.
|
|
(Each irp/urb pair can transfer a max of 1024 packets.)
|
|
3. All the irp/urb pairs are initialized
|
|
4. The subsidiary irps (of the irp/urb pair) are passed
|
|
down the stack at once.
|
|
5. The main Read/Write irp is pending
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object
|
|
Irp - I/O request packet
|
|
|
|
Return Value:
|
|
|
|
NT status value
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG numIrps;
|
|
ULONG stageSize;
|
|
ULONG contextSize;
|
|
ULONG packetSize;
|
|
ULONG numberOfPackets;
|
|
ULONG actualPackets;
|
|
ULONG minDataInEachPacket;
|
|
ULONG dataLeftToBeDistributed;
|
|
ULONG numberOfPacketsFilledToBrim;
|
|
CCHAR stackSize;
|
|
KIRQL oldIrql;
|
|
PUCHAR virtualAddress;
|
|
BOOLEAN read;
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PIO_STACK_LOCATION nextStack;
|
|
PISOUSB_RW_CONTEXT rwContext;
|
|
|
|
//
|
|
// initialize vars
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
if(TotalLength < 8) {
|
|
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
goto PerformHighSpeedIsochTransfer_Exit;
|
|
}
|
|
|
|
//
|
|
// each packet can hold this much info
|
|
//
|
|
packetSize = PipeInformation->MaximumPacketSize;
|
|
|
|
numberOfPackets = (TotalLength + packetSize - 1) / packetSize;
|
|
|
|
if(0 == (numberOfPackets % 8)) {
|
|
|
|
actualPackets = numberOfPackets;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// we need multiple of 8 packets only.
|
|
//
|
|
actualPackets = numberOfPackets +
|
|
(8 - (numberOfPackets % 8));
|
|
}
|
|
|
|
minDataInEachPacket = TotalLength / actualPackets;
|
|
|
|
if(minDataInEachPacket == packetSize) {
|
|
|
|
numberOfPacketsFilledToBrim = actualPackets;
|
|
dataLeftToBeDistributed = 0;
|
|
|
|
IsoUsb_DbgPrint(1, ("TotalLength = %d\n", TotalLength));
|
|
IsoUsb_DbgPrint(1, ("PacketSize = %d\n", packetSize));
|
|
IsoUsb_DbgPrint(1, ("Each of %d packets has %d bytes\n",
|
|
numberOfPacketsFilledToBrim,
|
|
packetSize));
|
|
}
|
|
else {
|
|
|
|
dataLeftToBeDistributed = TotalLength -
|
|
(minDataInEachPacket * actualPackets);
|
|
|
|
numberOfPacketsFilledToBrim = dataLeftToBeDistributed /
|
|
(packetSize - minDataInEachPacket);
|
|
|
|
dataLeftToBeDistributed -= (numberOfPacketsFilledToBrim *
|
|
(packetSize - minDataInEachPacket));
|
|
|
|
|
|
IsoUsb_DbgPrint(1, ("TotalLength = %d\n", TotalLength));
|
|
IsoUsb_DbgPrint(1, ("PacketSize = %d\n", packetSize));
|
|
IsoUsb_DbgPrint(1, ("Each of %d packets has %d bytes\n",
|
|
numberOfPacketsFilledToBrim,
|
|
packetSize));
|
|
if(dataLeftToBeDistributed) {
|
|
|
|
IsoUsb_DbgPrint(1, ("One packet has %d bytes\n",
|
|
minDataInEachPacket + dataLeftToBeDistributed));
|
|
IsoUsb_DbgPrint(1, ("Each of %d packets has %d bytes\n",
|
|
actualPackets - (numberOfPacketsFilledToBrim + 1),
|
|
minDataInEachPacket));
|
|
}
|
|
else {
|
|
IsoUsb_DbgPrint(1, ("Each of %d packets has %d bytes\n",
|
|
actualPackets - numberOfPacketsFilledToBrim,
|
|
minDataInEachPacket));
|
|
}
|
|
}
|
|
|
|
//
|
|
// determine how many stages of transfer needs to be done.
|
|
// in other words, how many irp/urb pairs required.
|
|
// this irp/urb pair is also called the subsidiary irp/urb pair
|
|
//
|
|
numIrps = (actualPackets + 1023) / 1024;
|
|
|
|
IsoUsb_DbgPrint(1, ("PeformHighSpeedIsochTransfer::numIrps = %d\n", numIrps));
|
|
|
|
//
|
|
// for every read/write transfer
|
|
// we create an ISOUSB_RW_CONTEXT
|
|
//
|
|
// initialize the read/write context
|
|
//
|
|
|
|
contextSize = sizeof(ISOUSB_RW_CONTEXT);
|
|
|
|
rwContext = (PISOUSB_RW_CONTEXT) ExAllocatePool(NonPagedPool,
|
|
contextSize);
|
|
|
|
if(rwContext == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Failed to alloc mem for rwContext\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PerformHighSpeedIsochTransfer_Exit;
|
|
}
|
|
|
|
RtlZeroMemory(rwContext, contextSize);
|
|
|
|
//
|
|
// allocate memory for every stage context -
|
|
// subcontext has state information for every irp/urb pair.
|
|
//
|
|
rwContext->SubContext = (PSUB_CONTEXT)
|
|
ExAllocatePool(NonPagedPool,
|
|
numIrps * sizeof(SUB_CONTEXT));
|
|
|
|
if(rwContext->SubContext == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Failed to alloc mem for SubContext\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
ExFreePool(rwContext);
|
|
goto PerformHighSpeedIsochTransfer_Exit;
|
|
}
|
|
|
|
RtlZeroMemory(rwContext->SubContext, numIrps * sizeof(SUB_CONTEXT));
|
|
|
|
rwContext->RWIrp = Irp;
|
|
rwContext->Lock = 2;
|
|
rwContext->NumIrps = numIrps;
|
|
rwContext->IrpsPending = numIrps;
|
|
rwContext->DeviceExtension = deviceExtension;
|
|
KeInitializeSpinLock(&rwContext->SpinLock);
|
|
//
|
|
// save the rwContext pointer in the tail union.
|
|
//
|
|
Irp->Tail.Overlay.DriverContext[0] = (PVOID) rwContext;
|
|
|
|
stackSize = deviceExtension->TopOfStackDeviceObject->StackSize + 1;
|
|
virtualAddress = (PUCHAR) MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|
|
|
for(i = 0; i < numIrps; i++) {
|
|
|
|
PIRP subIrp;
|
|
PURB subUrb;
|
|
PMDL subMdl;
|
|
ULONG nPackets;
|
|
ULONG siz;
|
|
ULONG offset;
|
|
|
|
//
|
|
// for every stage of transfer we need to do the following
|
|
// tasks
|
|
// 1. allocate an irp
|
|
// 2. allocate an urb
|
|
// 3. allocate a mdl.
|
|
//
|
|
// create a subsidiary irp
|
|
//
|
|
subIrp = IoAllocateIrp(stackSize, FALSE);
|
|
|
|
if(subIrp == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context irp\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PerformHighSpeedIsochTransfer_Free;
|
|
}
|
|
|
|
rwContext->SubContext[i].SubIrp = subIrp;
|
|
|
|
if(actualPackets <= 1024) {
|
|
|
|
nPackets = actualPackets;
|
|
actualPackets = 0;
|
|
}
|
|
else {
|
|
|
|
nPackets = 1024;
|
|
actualPackets -= 1024;
|
|
}
|
|
|
|
IsoUsb_DbgPrint(1, ("nPackets = %d for Irp/URB pair %d\n", nPackets, i));
|
|
|
|
ASSERT(nPackets <= 1024);
|
|
|
|
siz = GET_ISO_URB_SIZE(nPackets);
|
|
|
|
//
|
|
// create a subsidiary urb.
|
|
//
|
|
|
|
subUrb = (PURB) ExAllocatePool(NonPagedPool, siz);
|
|
|
|
if(subUrb == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context urb\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PerformHighSpeedIsochTransfer_Free;
|
|
}
|
|
|
|
rwContext->SubContext[i].SubUrb = subUrb;
|
|
|
|
if(nPackets > numberOfPacketsFilledToBrim) {
|
|
|
|
stageSize = packetSize * numberOfPacketsFilledToBrim;
|
|
stageSize += (minDataInEachPacket *
|
|
(nPackets - numberOfPacketsFilledToBrim));
|
|
stageSize += dataLeftToBeDistributed;
|
|
}
|
|
else {
|
|
|
|
stageSize = packetSize * nPackets;
|
|
}
|
|
|
|
//
|
|
// allocate a mdl.
|
|
//
|
|
subMdl = IoAllocateMdl((PVOID) virtualAddress,
|
|
stageSize,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if(subMdl == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context mdl\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PerformHighSpeedIsochTransfer_Free;
|
|
}
|
|
|
|
IoBuildPartialMdl(Irp->MdlAddress,
|
|
subMdl,
|
|
(PVOID) virtualAddress,
|
|
stageSize);
|
|
|
|
rwContext->SubContext[i].SubMdl = subMdl;
|
|
|
|
virtualAddress += stageSize;
|
|
TotalLength -= stageSize;
|
|
|
|
//
|
|
// Initialize the subsidiary urb
|
|
//
|
|
RtlZeroMemory(subUrb, siz);
|
|
|
|
subUrb->UrbIsochronousTransfer.Hdr.Length = (USHORT) siz;
|
|
subUrb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER;
|
|
subUrb->UrbIsochronousTransfer.PipeHandle = PipeInformation->PipeHandle;
|
|
|
|
if(read) {
|
|
|
|
IsoUsb_DbgPrint(1, ("read\n"));
|
|
subUrb->UrbIsochronousTransfer.TransferFlags =
|
|
USBD_TRANSFER_DIRECTION_IN;
|
|
}
|
|
else {
|
|
|
|
IsoUsb_DbgPrint(1, ("write\n"));
|
|
subUrb->UrbIsochronousTransfer.TransferFlags =
|
|
USBD_TRANSFER_DIRECTION_OUT;
|
|
}
|
|
|
|
subUrb->UrbIsochronousTransfer.TransferBufferLength = stageSize;
|
|
subUrb->UrbIsochronousTransfer.TransferBufferMDL = subMdl;
|
|
/*
|
|
This is a way to set the start frame and NOT specify ASAP flag.
|
|
|
|
subUrb->UrbIsochronousTransfer.StartFrame =
|
|
IsoUsb_GetCurrentFrame(DeviceObject, Irp) +
|
|
SOME_LATENCY;
|
|
*/
|
|
subUrb->UrbIsochronousTransfer.TransferFlags |=
|
|
USBD_START_ISO_TRANSFER_ASAP;
|
|
|
|
subUrb->UrbIsochronousTransfer.NumberOfPackets = nPackets;
|
|
subUrb->UrbIsochronousTransfer.UrbLink = NULL;
|
|
|
|
//
|
|
// set the offsets for every packet for reads/writes
|
|
//
|
|
if(read) {
|
|
|
|
offset = 0;
|
|
|
|
for(j = 0; j < nPackets; j++) {
|
|
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Offset = offset;
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = 0;
|
|
|
|
if(numberOfPacketsFilledToBrim) {
|
|
|
|
offset += packetSize;
|
|
numberOfPacketsFilledToBrim--;
|
|
stageSize -= packetSize;
|
|
}
|
|
else if(dataLeftToBeDistributed) {
|
|
|
|
offset += (minDataInEachPacket + dataLeftToBeDistributed);
|
|
stageSize -= (minDataInEachPacket + dataLeftToBeDistributed);
|
|
dataLeftToBeDistributed = 0;
|
|
}
|
|
else {
|
|
|
|
offset += minDataInEachPacket;
|
|
stageSize -= minDataInEachPacket;
|
|
}
|
|
}
|
|
|
|
ASSERT(stageSize == 0);
|
|
}
|
|
else {
|
|
|
|
offset = 0;
|
|
|
|
for(j = 0; j < nPackets; j++) {
|
|
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Offset = offset;
|
|
|
|
if(numberOfPacketsFilledToBrim) {
|
|
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = packetSize;
|
|
offset += packetSize;
|
|
numberOfPacketsFilledToBrim--;
|
|
stageSize -= packetSize;
|
|
}
|
|
else if(dataLeftToBeDistributed) {
|
|
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Length =
|
|
minDataInEachPacket + dataLeftToBeDistributed;
|
|
offset += (minDataInEachPacket + dataLeftToBeDistributed);
|
|
stageSize -= (minDataInEachPacket + dataLeftToBeDistributed);
|
|
dataLeftToBeDistributed = 0;
|
|
|
|
}
|
|
else {
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = minDataInEachPacket;
|
|
offset += minDataInEachPacket;
|
|
stageSize -= minDataInEachPacket;
|
|
}
|
|
}
|
|
|
|
ASSERT(stageSize == 0);
|
|
}
|
|
|
|
IoSetNextIrpStackLocation(subIrp);
|
|
nextStack = IoGetCurrentIrpStackLocation(subIrp);
|
|
|
|
nextStack->DeviceObject = DeviceObject;
|
|
nextStack->Parameters.Others.Argument1 = (PVOID) subUrb;
|
|
nextStack->Parameters.Others.Argument2 = (PVOID) subMdl;
|
|
|
|
nextStack = IoGetNextIrpStackLocation(subIrp);
|
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
nextStack->Parameters.Others.Argument1 = (PVOID) subUrb;
|
|
nextStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
|
|
IoSetCompletionRoutine(subIrp,
|
|
(PIO_COMPLETION_ROUTINE) IsoUsb_SinglePairComplete,
|
|
(PVOID) rwContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
}
|
|
|
|
//
|
|
// while we were busy create subsidiary irp/urb pairs..
|
|
// the main read/write irp may have been cancelled !!
|
|
//
|
|
|
|
KeAcquireSpinLock(&rwContext->SpinLock, &oldIrql);
|
|
|
|
IoSetCancelRoutine(Irp, IsoUsb_CancelReadWrite);
|
|
|
|
if(Irp->Cancel) {
|
|
|
|
//
|
|
// The Cancel flag for the Irp has been set.
|
|
//
|
|
IsoUsb_DbgPrint(3, ("Cancel flag set\n"));
|
|
|
|
ntStatus = STATUS_CANCELLED;
|
|
|
|
if(IoSetCancelRoutine(Irp, NULL)) {
|
|
|
|
//
|
|
// But the I/O manager did not call our cancel routine.
|
|
// we need to free the 1) irp, 2) urb and 3) mdl for every
|
|
// stage and complete the main Irp after releasing the lock
|
|
//
|
|
|
|
IsoUsb_DbgPrint(3, ("cancellation routine NOT run\n"));
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
goto PerformHighSpeedIsochTransfer_Free;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The cancel routine will resume the moment we release the lock.
|
|
//
|
|
for(j = 0; j < numIrps; j++) {
|
|
|
|
if(rwContext->SubContext[j].SubUrb) {
|
|
|
|
ExFreePool(rwContext->SubContext[j].SubUrb);
|
|
rwContext->SubContext[j].SubUrb = NULL;
|
|
}
|
|
|
|
if(rwContext->SubContext[j].SubMdl) {
|
|
|
|
IoFreeMdl(rwContext->SubContext[j].SubMdl);
|
|
rwContext->SubContext[j].SubMdl = NULL;
|
|
}
|
|
}
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
//
|
|
// it is the job of the cancellation routine to free
|
|
// sub-context irps, release rwContext and complete
|
|
// the main readwrite irp
|
|
//
|
|
InterlockedDecrement(&rwContext->Lock);
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// normal processing
|
|
//
|
|
|
|
IsoUsb_DbgPrint(3, ("normal processing\n"));
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
for(j = 0; j < numIrps; j++) {
|
|
|
|
IsoUsb_DbgPrint(3, ("PerformHighSpeedIsochTransfer::"));
|
|
IsoUsb_IoIncrement(deviceExtension);
|
|
|
|
IoCallDriver(deviceExtension->TopOfStackDeviceObject,
|
|
rwContext->SubContext[j].SubIrp);
|
|
}
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
PerformHighSpeedIsochTransfer_Free:
|
|
|
|
for(j = 0; j < numIrps; j++) {
|
|
|
|
if(rwContext->SubContext[j].SubIrp) {
|
|
|
|
IoFreeIrp(rwContext->SubContext[j].SubIrp);
|
|
rwContext->SubContext[j].SubIrp = NULL;
|
|
}
|
|
|
|
if(rwContext->SubContext[j].SubUrb) {
|
|
|
|
ExFreePool(rwContext->SubContext[j].SubUrb);
|
|
rwContext->SubContext[j].SubUrb = NULL;
|
|
}
|
|
|
|
if(rwContext->SubContext[j].SubMdl) {
|
|
|
|
IoFreeMdl(rwContext->SubContext[j].SubMdl);
|
|
rwContext->SubContext[j].SubMdl = NULL;
|
|
}
|
|
}
|
|
|
|
ExFreePool(rwContext->SubContext);
|
|
ExFreePool(rwContext);
|
|
|
|
PerformHighSpeedIsochTransfer_Exit:
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
IsoUsb_DbgPrint(3, ("PerformHighSpeedIsochTransfer::"));
|
|
IsoUsb_IoDecrement(deviceExtension);
|
|
|
|
IsoUsb_DbgPrint(3, ("-------------------------------\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
PerformFullSpeedIsochTransfer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUSBD_PIPE_INFORMATION PipeInformation,
|
|
IN PIRP Irp,
|
|
IN ULONG TotalLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine
|
|
1. creates a ISOUSB_RW_CONTEXT for every
|
|
read/write to be performed.
|
|
2. creates SUB_CONTEXT for each irp/urb pair.
|
|
(Each irp/urb pair can transfer only 255 packets.)
|
|
3. All the irp/urb pairs are initialized
|
|
4. The subsidiary irps (of the irp/urb pair) are passed
|
|
down the stack at once.
|
|
5. The main Read/Write irp is pending
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object
|
|
PipeInformation - USBD_PIPE_INFORMATION
|
|
Irp - I/O request packet
|
|
TotalLength - no. of bytes to be transferred
|
|
|
|
Return Value:
|
|
|
|
NT status value
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG packetSize;
|
|
ULONG numIrps;
|
|
ULONG stageSize;
|
|
ULONG contextSize;
|
|
CCHAR stackSize;
|
|
KIRQL oldIrql;
|
|
PUCHAR virtualAddress;
|
|
BOOLEAN read;
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PIO_STACK_LOCATION nextStack;
|
|
PISOUSB_RW_CONTEXT rwContext;
|
|
|
|
//
|
|
// initialize vars
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer - begins\n"));
|
|
/*
|
|
if(read) {
|
|
|
|
pipeInformation = &deviceExtension->UsbInterface->Pipes[ISOCH_IN_PIPE_INDEX];
|
|
}
|
|
else {
|
|
|
|
pipeInformation = &deviceExtension->UsbInterface->Pipes[ISOCH_OUT_PIPE_INDEX];
|
|
}
|
|
*/
|
|
|
|
//
|
|
// each packet can hold this much info
|
|
//
|
|
packetSize = PipeInformation->MaximumPacketSize;
|
|
|
|
IsoUsb_DbgPrint(3, ("totalLength = %d\n", TotalLength));
|
|
IsoUsb_DbgPrint(3, ("packetSize = %d\n", packetSize));
|
|
|
|
//
|
|
// there is an inherent limit on the number of packets
|
|
// that can be passed down the stack with each
|
|
// irp/urb pair (255)
|
|
// if the number of required packets is > 255,
|
|
// we shall create "required-packets / 255 + 1" number
|
|
// of irp/urb pairs.
|
|
// Each irp/urb pair transfer is also called a stage transfer.
|
|
//
|
|
if(TotalLength > (packetSize * 255)) {
|
|
|
|
stageSize = packetSize * 255;
|
|
}
|
|
else {
|
|
|
|
stageSize = TotalLength;
|
|
}
|
|
|
|
IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::stageSize = %d\n", stageSize));
|
|
|
|
//
|
|
// determine how many stages of transfer needs to be done.
|
|
// in other words, how many irp/urb pairs required.
|
|
// this irp/urb pair is also called the subsidiary irp/urb pair
|
|
//
|
|
numIrps = (TotalLength + stageSize - 1) / stageSize;
|
|
|
|
IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::numIrps = %d\n", numIrps));
|
|
|
|
//
|
|
// for every read/write transfer
|
|
// we create an ISOUSB_RW_CONTEXT
|
|
//
|
|
// initialize the read/write context
|
|
//
|
|
|
|
contextSize = sizeof(ISOUSB_RW_CONTEXT);
|
|
|
|
rwContext = (PISOUSB_RW_CONTEXT) ExAllocatePool(NonPagedPool,
|
|
contextSize);
|
|
|
|
if(rwContext == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Failed to alloc mem for rwContext\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PerformFullSpeedIsochTransfer_Exit;
|
|
}
|
|
|
|
RtlZeroMemory(rwContext, contextSize);
|
|
|
|
//
|
|
// allocate memory for every stage context -
|
|
// subcontext has state information for every irp/urb pair.
|
|
//
|
|
rwContext->SubContext = (PSUB_CONTEXT)
|
|
ExAllocatePool(NonPagedPool,
|
|
numIrps * sizeof(SUB_CONTEXT));
|
|
|
|
if(rwContext->SubContext == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("Failed to alloc mem for SubContext\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
ExFreePool(rwContext);
|
|
goto PerformFullSpeedIsochTransfer_Exit;
|
|
}
|
|
|
|
RtlZeroMemory(rwContext->SubContext, numIrps * sizeof(SUB_CONTEXT));
|
|
|
|
rwContext->RWIrp = Irp;
|
|
rwContext->Lock = 2;
|
|
rwContext->NumIrps = numIrps;
|
|
rwContext->IrpsPending = numIrps;
|
|
rwContext->DeviceExtension = deviceExtension;
|
|
KeInitializeSpinLock(&rwContext->SpinLock);
|
|
//
|
|
// save the rwContext pointer in the tail union.
|
|
//
|
|
Irp->Tail.Overlay.DriverContext[0] = (PVOID) rwContext;
|
|
|
|
stackSize = deviceExtension->TopOfStackDeviceObject->StackSize + 1;
|
|
virtualAddress = (PUCHAR) MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|
|
|
for(i = 0; i < numIrps; i++) {
|
|
|
|
PIRP subIrp;
|
|
PURB subUrb;
|
|
PMDL subMdl;
|
|
ULONG nPackets;
|
|
ULONG siz;
|
|
ULONG offset;
|
|
|
|
//
|
|
// for every stage of transfer we need to do the following
|
|
// tasks
|
|
// 1. allocate an irp
|
|
// 2. allocate an urb
|
|
// 3. allocate a mdl.
|
|
//
|
|
// create a subsidiary irp
|
|
//
|
|
subIrp = IoAllocateIrp(stackSize, FALSE);
|
|
|
|
if(subIrp == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context irp\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PerformFullSpeedIsochTransfer_Free;
|
|
}
|
|
|
|
rwContext->SubContext[i].SubIrp = subIrp;
|
|
|
|
nPackets = (stageSize + packetSize - 1) / packetSize;
|
|
|
|
IsoUsb_DbgPrint(3, ("nPackets = %d for Irp/URB pair %d\n", nPackets, i));
|
|
|
|
ASSERT(nPackets <= 255);
|
|
|
|
siz = GET_ISO_URB_SIZE(nPackets);
|
|
|
|
//
|
|
// create a subsidiary urb.
|
|
//
|
|
|
|
subUrb = (PURB) ExAllocatePool(NonPagedPool, siz);
|
|
|
|
if(subUrb == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context urb\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PerformFullSpeedIsochTransfer_Free;
|
|
}
|
|
|
|
rwContext->SubContext[i].SubUrb = subUrb;
|
|
|
|
//
|
|
// allocate a mdl.
|
|
//
|
|
subMdl = IoAllocateMdl((PVOID) virtualAddress,
|
|
stageSize,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if(subMdl == NULL) {
|
|
|
|
IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context mdl\n"));
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PerformFullSpeedIsochTransfer_Free;
|
|
}
|
|
|
|
IoBuildPartialMdl(Irp->MdlAddress,
|
|
subMdl,
|
|
(PVOID) virtualAddress,
|
|
stageSize);
|
|
|
|
rwContext->SubContext[i].SubMdl = subMdl;
|
|
|
|
virtualAddress += stageSize;
|
|
TotalLength -= stageSize;
|
|
|
|
//
|
|
// Initialize the subsidiary urb
|
|
//
|
|
RtlZeroMemory(subUrb, siz);
|
|
|
|
subUrb->UrbIsochronousTransfer.Hdr.Length = (USHORT) siz;
|
|
subUrb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER;
|
|
subUrb->UrbIsochronousTransfer.PipeHandle = PipeInformation->PipeHandle;
|
|
if(read) {
|
|
|
|
IsoUsb_DbgPrint(3, ("read\n"));
|
|
subUrb->UrbIsochronousTransfer.TransferFlags =
|
|
USBD_TRANSFER_DIRECTION_IN;
|
|
}
|
|
else {
|
|
|
|
IsoUsb_DbgPrint(3, ("write\n"));
|
|
subUrb->UrbIsochronousTransfer.TransferFlags =
|
|
USBD_TRANSFER_DIRECTION_OUT;
|
|
}
|
|
|
|
subUrb->UrbIsochronousTransfer.TransferBufferLength = stageSize;
|
|
subUrb->UrbIsochronousTransfer.TransferBufferMDL = subMdl;
|
|
|
|
/*
|
|
This is a way to set the start frame and NOT specify ASAP flag.
|
|
|
|
subUrb->UrbIsochronousTransfer.StartFrame =
|
|
IsoUsb_GetCurrentFrame(DeviceObject, Irp) +
|
|
SOME_LATENCY;
|
|
*/
|
|
//
|
|
// when the client driver sets the ASAP flag, it basically
|
|
// guarantees that it will make data available to the HC
|
|
// and that the HC should transfer it in the next transfer frame
|
|
// for the endpoint.(The HC maintains a next transfer frame
|
|
// state variable for each endpoint). By resetting the pipe,
|
|
// we make the pipe as virgin. If the data does not get to the HC
|
|
// fast enough, the USBD_ISO_PACKET_DESCRIPTOR - Status is
|
|
// USBD_STATUS_BAD_START_FRAME on uhci. On ohci it is 0xC000000E.
|
|
//
|
|
|
|
subUrb->UrbIsochronousTransfer.TransferFlags |=
|
|
USBD_START_ISO_TRANSFER_ASAP;
|
|
|
|
subUrb->UrbIsochronousTransfer.NumberOfPackets = nPackets;
|
|
subUrb->UrbIsochronousTransfer.UrbLink = NULL;
|
|
|
|
//
|
|
// set the offsets for every packet for reads/writes
|
|
//
|
|
if(read) {
|
|
|
|
offset = 0;
|
|
|
|
for(j = 0; j < nPackets; j++) {
|
|
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Offset = offset;
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = 0;
|
|
|
|
if(stageSize > packetSize) {
|
|
|
|
offset += packetSize;
|
|
stageSize -= packetSize;
|
|
}
|
|
else {
|
|
|
|
offset += stageSize;
|
|
stageSize = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
offset = 0;
|
|
|
|
for(j = 0; j < nPackets; j++) {
|
|
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Offset = offset;
|
|
|
|
if(stageSize > packetSize) {
|
|
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = packetSize;
|
|
offset += packetSize;
|
|
stageSize -= packetSize;
|
|
}
|
|
else {
|
|
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = stageSize;
|
|
offset += stageSize;
|
|
stageSize = 0;
|
|
ASSERT(offset == (subUrb->UrbIsochronousTransfer.IsoPacket[j].Length +
|
|
subUrb->UrbIsochronousTransfer.IsoPacket[j].Offset));
|
|
}
|
|
}
|
|
}
|
|
|
|
IoSetNextIrpStackLocation(subIrp);
|
|
nextStack = IoGetCurrentIrpStackLocation(subIrp);
|
|
|
|
nextStack->DeviceObject = DeviceObject;
|
|
nextStack->Parameters.Others.Argument1 = (PVOID) subUrb;
|
|
nextStack->Parameters.Others.Argument2 = (PVOID) subMdl;
|
|
|
|
nextStack = IoGetNextIrpStackLocation(subIrp);
|
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
nextStack->Parameters.Others.Argument1 = (PVOID) subUrb;
|
|
nextStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
|
|
IoSetCompletionRoutine(subIrp,
|
|
(PIO_COMPLETION_ROUTINE) IsoUsb_SinglePairComplete,
|
|
(PVOID) rwContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
if(TotalLength > (packetSize * 255)) {
|
|
|
|
stageSize = packetSize * 255;
|
|
}
|
|
else {
|
|
|
|
stageSize = TotalLength;
|
|
}
|
|
}
|
|
|
|
//
|
|
// while we were busy create subsidiary irp/urb pairs..
|
|
// the main read/write irp may have been cancelled !!
|
|
//
|
|
|
|
KeAcquireSpinLock(&rwContext->SpinLock, &oldIrql);
|
|
|
|
IoSetCancelRoutine(Irp, IsoUsb_CancelReadWrite);
|
|
|
|
if(Irp->Cancel) {
|
|
|
|
//
|
|
// The Cancel flag for the Irp has been set.
|
|
//
|
|
IsoUsb_DbgPrint(3, ("Cancel flag set\n"));
|
|
|
|
ntStatus = STATUS_CANCELLED;
|
|
|
|
if(IoSetCancelRoutine(Irp, NULL)) {
|
|
|
|
//
|
|
// But the I/O manager did not call our cancel routine.
|
|
// we need to free the 1) irp, 2) urb and 3) mdl for every
|
|
// stage and complete the main Irp after releasing the lock
|
|
//
|
|
|
|
IsoUsb_DbgPrint(3, ("cancellation routine NOT run\n"));
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
goto PerformFullSpeedIsochTransfer_Free;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The cancel routine will resume the moment we release the lock.
|
|
//
|
|
for(j = 0; j < numIrps; j++) {
|
|
|
|
if(rwContext->SubContext[j].SubUrb) {
|
|
|
|
ExFreePool(rwContext->SubContext[j].SubUrb);
|
|
rwContext->SubContext[j].SubUrb = NULL;
|
|
}
|
|
|
|
if(rwContext->SubContext[j].SubMdl) {
|
|
|
|
IoFreeMdl(rwContext->SubContext[j].SubMdl);
|
|
rwContext->SubContext[j].SubMdl = NULL;
|
|
}
|
|
}
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
//
|
|
// it is the job of the cancellation routine to free
|
|
// sub-context irps, release rwContext and complete
|
|
// the main readwrite irp
|
|
//
|
|
InterlockedDecrement(&rwContext->Lock);
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// normal processing
|
|
//
|
|
|
|
IsoUsb_DbgPrint(3, ("normal processing\n"));
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
for(j = 0; j < numIrps; j++) {
|
|
|
|
IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::"));
|
|
IsoUsb_IoIncrement(deviceExtension);
|
|
|
|
IoCallDriver(deviceExtension->TopOfStackDeviceObject,
|
|
rwContext->SubContext[j].SubIrp);
|
|
}
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
PerformFullSpeedIsochTransfer_Free:
|
|
|
|
for(j = 0; j < numIrps; j++) {
|
|
|
|
if(rwContext->SubContext[j].SubIrp) {
|
|
|
|
IoFreeIrp(rwContext->SubContext[j].SubIrp);
|
|
rwContext->SubContext[j].SubIrp = NULL;
|
|
}
|
|
|
|
if(rwContext->SubContext[j].SubUrb) {
|
|
|
|
ExFreePool(rwContext->SubContext[j].SubUrb);
|
|
rwContext->SubContext[j].SubUrb = NULL;
|
|
}
|
|
|
|
if(rwContext->SubContext[j].SubMdl) {
|
|
|
|
IoFreeMdl(rwContext->SubContext[j].SubMdl);
|
|
rwContext->SubContext[j].SubMdl = NULL;
|
|
}
|
|
}
|
|
|
|
ExFreePool(rwContext->SubContext);
|
|
ExFreePool(rwContext);
|
|
|
|
|
|
PerformFullSpeedIsochTransfer_Exit:
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::"));
|
|
IsoUsb_IoDecrement(deviceExtension);
|
|
|
|
IsoUsb_DbgPrint(3, ("-------------------------------\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
IsoUsb_SinglePairComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for the subsidiary irp.
|
|
|
|
For every irp/urb pair, we have allocated
|
|
1. an irp
|
|
2. an urb
|
|
3. a mdl.
|
|
|
|
Case 1:
|
|
we do NOT free the irp on its completion
|
|
we do free the urb and the mdl.
|
|
|
|
Case 1 is executed in Block 3.
|
|
|
|
Case 2:
|
|
when we complete the last of the subsidiary irp,
|
|
we check if the cancel routine for the main Irp
|
|
has run. If not, we free all the irps, release
|
|
the subcontext and the context and complete the
|
|
main Irp.we also free the urb and mdl for this
|
|
stage.
|
|
|
|
Case 2 is executed in Block 2.
|
|
|
|
Case 3:
|
|
when we complete the last of the subsidiary irp,
|
|
we check if the cancel routine for the main Irp
|
|
has run. If yes, we atomically decrement the
|
|
rwContext->Lock field. (the completion routine
|
|
is in race with Cancel routine). If the count is 1,
|
|
the cancel routine will free all the resources.
|
|
we do free the urb and mdl.
|
|
|
|
it is expected of the cancellation routine to free
|
|
all the irps, free the subcontext and the context
|
|
and complete the main irp
|
|
|
|
Case 3 is executed in Block 1b.
|
|
|
|
Case 4:
|
|
when we complete the last of the subsidiary irp,
|
|
we check if the cancel routine for the main Irp
|
|
has run. If yes, we atomically decrement the
|
|
rwContext->Lock field. (the completion routine
|
|
is in race with Cancel routine). If the count is 0,
|
|
we free the irp, subcontext and the context and
|
|
complete the main irp. we also free the urb and
|
|
the mdl for this particular stage.
|
|
|
|
the reason we do not free the subsidiary irp at its
|
|
completion is because the cancellation routine can
|
|
run any time.
|
|
|
|
Case 4 is executed in Block 1a.
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object
|
|
Irp - I/O request packet
|
|
Context - context for the completion routine
|
|
|
|
Return Value:
|
|
|
|
NT status value
|
|
|
|
--*/
|
|
{
|
|
PURB urb;
|
|
PMDL mdl;
|
|
PIRP mainIrp;
|
|
KIRQL oldIrql;
|
|
ULONG i;
|
|
ULONG info;
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PISOUSB_RW_CONTEXT rwContext;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
urb = (PURB) irpStack->Parameters.Others.Argument1;
|
|
mdl = (PMDL) irpStack->Parameters.Others.Argument2;
|
|
info = 0;
|
|
ntStatus = Irp->IoStatus.Status;
|
|
rwContext = (PISOUSB_RW_CONTEXT) Context;
|
|
deviceExtension = rwContext->DeviceExtension;
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_SinglePairComplete - begins\n"));
|
|
|
|
ASSERT(rwContext);
|
|
|
|
KeAcquireSpinLock(&rwContext->SpinLock, &oldIrql);
|
|
|
|
if(NT_SUCCESS(ntStatus) &&
|
|
USBD_SUCCESS(urb->UrbHeader.Status)) {
|
|
|
|
rwContext->NumXfer +=
|
|
urb->UrbIsochronousTransfer.TransferBufferLength;
|
|
|
|
IsoUsb_DbgPrint(1, ("rwContext->NumXfer = %d\n", rwContext->NumXfer));
|
|
}
|
|
else {
|
|
|
|
IsoUsb_DbgPrint(1, ("read-write irp failed with status %X\n", ntStatus));
|
|
IsoUsb_DbgPrint(1, ("urb header status %X\n", urb->UrbHeader.Status));
|
|
}
|
|
|
|
for(i = 0; i < urb->UrbIsochronousTransfer.NumberOfPackets; i++) {
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoPacket[%d].Length = %X IsoPacket[%d].Status = %X\n",
|
|
i,
|
|
urb->UrbIsochronousTransfer.IsoPacket[i].Length,
|
|
i,
|
|
urb->UrbIsochronousTransfer.IsoPacket[i].Status));
|
|
}
|
|
|
|
if(InterlockedDecrement(&rwContext->IrpsPending) == 0) {
|
|
|
|
IsoUsb_DbgPrint(3, ("no more irps pending\n"));
|
|
|
|
if(NT_SUCCESS(ntStatus)) {
|
|
|
|
IsoUsb_DbgPrint(1, ("urb start frame %X\n",
|
|
urb->UrbIsochronousTransfer.StartFrame));
|
|
}
|
|
|
|
mainIrp = (PIRP) InterlockedExchangePointer(&rwContext->RWIrp, NULL);
|
|
|
|
ASSERT(mainIrp);
|
|
|
|
if(IoSetCancelRoutine(mainIrp, NULL) == NULL) {
|
|
|
|
//
|
|
// cancel routine has begun the race
|
|
//
|
|
// Block 1a.
|
|
//
|
|
IsoUsb_DbgPrint(3, ("cancel routine has begun the race\n"));
|
|
|
|
if(InterlockedDecrement(&rwContext->Lock) == 0) {
|
|
|
|
//
|
|
// do the cleanup job ourselves
|
|
//
|
|
IsoUsb_DbgPrint(3, ("losers do the cleanup\n"));
|
|
|
|
for(i = 0; i < rwContext->NumIrps; i++) {
|
|
|
|
IoFreeIrp(rwContext->SubContext[i].SubIrp);
|
|
rwContext->SubContext[i].SubIrp = NULL;
|
|
}
|
|
|
|
info = rwContext->NumXfer;
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
ExFreePool(rwContext->SubContext);
|
|
ExFreePool(rwContext);
|
|
|
|
//
|
|
// if we transferred some data, main Irp completes with success
|
|
//
|
|
|
|
IsoUsb_DbgPrint(1, ("Total data transferred = %X\n", info));
|
|
|
|
IsoUsb_DbgPrint(1, ("***\n"));
|
|
|
|
mainIrp->IoStatus.Status = STATUS_SUCCESS; // ntStatus;
|
|
mainIrp->IoStatus.Information = info;
|
|
|
|
IoCompleteRequest(mainIrp, IO_NO_INCREMENT);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_SinglePairComplete::"));
|
|
IsoUsb_IoDecrement(deviceExtension);
|
|
|
|
IsoUsb_DbgPrint(3, ("-------------------------------\n"));
|
|
|
|
goto IsoUsb_SinglePairComplete_Exit;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Block 1b.
|
|
//
|
|
|
|
IsoUsb_DbgPrint(3, ("cancel routine performs the cleanup\n"));
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Block 2.
|
|
//
|
|
IsoUsb_DbgPrint(3, ("cancel routine has NOT run\n"));
|
|
|
|
for(i = 0; i < rwContext->NumIrps; i++) {
|
|
|
|
IoFreeIrp(rwContext->SubContext[i].SubIrp);
|
|
rwContext->SubContext[i].SubIrp = NULL;
|
|
}
|
|
|
|
info = rwContext->NumXfer;
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
ExFreePool(rwContext->SubContext);
|
|
ExFreePool(rwContext);
|
|
|
|
//
|
|
// if we transferred some data, main Irp completes with success
|
|
//
|
|
IsoUsb_DbgPrint(1, ("Total data transferred = %X\n", info));
|
|
|
|
IsoUsb_DbgPrint(1, ("***\n"));
|
|
|
|
mainIrp->IoStatus.Status = STATUS_SUCCESS; // ntStatus;
|
|
mainIrp->IoStatus.Information = info;
|
|
|
|
IoCompleteRequest(mainIrp, IO_NO_INCREMENT);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_SinglePairComplete::"));
|
|
IsoUsb_IoDecrement(deviceExtension);
|
|
|
|
IsoUsb_DbgPrint(3, ("-------------------------------\n"));
|
|
|
|
goto IsoUsb_SinglePairComplete_Exit;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
IsoUsb_SinglePairComplete_Exit:
|
|
|
|
//
|
|
// Block 3.
|
|
//
|
|
|
|
ExFreePool(urb);
|
|
IoFreeMdl(mdl);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_SinglePairComplete::"));
|
|
IsoUsb_IoDecrement(deviceExtension);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_SinglePairComplete - ends\n"));
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
VOID
|
|
IsoUsb_CancelReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the cancellation routine for the main read/write Irp.
|
|
The policy is as follows:
|
|
|
|
If the cancellation routine is the last to decrement
|
|
rwContext->Lock, then free the irps, subcontext and
|
|
the context. Complete the main irp
|
|
|
|
Otherwise, call IoCancelIrp on each of the subsidiary irp.
|
|
It is valid to call IoCancelIrp on irps for which the
|
|
completion routine has executed, because, we do not free the
|
|
irps in the completion routine.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object
|
|
Irp - I/O request packet
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PIRP mainIrp;
|
|
KIRQL oldIrql;
|
|
ULONG i;
|
|
ULONG info;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PISOUSB_RW_CONTEXT rwContext;
|
|
|
|
//
|
|
// initialize vars
|
|
//
|
|
info = 0;
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_CancelReadWrite - begins\n"));
|
|
|
|
rwContext = (PISOUSB_RW_CONTEXT) Irp->Tail.Overlay.DriverContext[0];
|
|
ASSERT(rwContext);
|
|
deviceExtension = rwContext->DeviceExtension;
|
|
|
|
KeAcquireSpinLock(&rwContext->SpinLock, &oldIrql);
|
|
|
|
if(InterlockedDecrement(&rwContext->Lock)) {
|
|
|
|
IsoUsb_DbgPrint(3, ("about to cancel sub context irps..\n"));
|
|
|
|
for(i = 0; i < rwContext->NumIrps; i++) {
|
|
|
|
if(rwContext->SubContext[i].SubIrp) {
|
|
|
|
IoCancelIrp(rwContext->SubContext[i].SubIrp);
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_CancelReadWrite - ends\n"));
|
|
|
|
return;
|
|
}
|
|
else {
|
|
|
|
for(i = 0; i < rwContext->NumIrps; i++) {
|
|
|
|
IoFreeIrp(rwContext->SubContext[i].SubIrp);
|
|
rwContext->SubContext[i].SubIrp = NULL;
|
|
}
|
|
|
|
mainIrp = (PIRP) InterlockedExchangePointer(&rwContext->RWIrp, NULL);
|
|
|
|
info = rwContext->NumXfer;
|
|
|
|
KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);
|
|
|
|
ExFreePool(rwContext->SubContext);
|
|
ExFreePool(rwContext);
|
|
|
|
//
|
|
// if we transferred some data, main Irp completes with success
|
|
//
|
|
|
|
IsoUsb_DbgPrint(1, ("Total data transferred = %X\n", info));
|
|
|
|
IsoUsb_DbgPrint(1, ("***\n"));
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = info;
|
|
/*
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
*/
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_CancelReadWrite::"));
|
|
IsoUsb_IoDecrement(deviceExtension);
|
|
|
|
IsoUsb_DbgPrint(3, ("-------------------------------\n"));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
IsoUsb_GetCurrentFrame(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine send an irp/urb pair with
|
|
function code URB_FUNCTION_GET_CURRENT_FRAME_NUMBER
|
|
to fetch the current frame
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object
|
|
PIRP - I/O request packet
|
|
|
|
Return Value:
|
|
|
|
Current frame
|
|
|
|
--*/
|
|
{
|
|
KEVENT event;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION nextStack;
|
|
struct _URB_GET_CURRENT_FRAME_NUMBER urb;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// initialize the urb
|
|
//
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_GetCurrentFrame - begins\n"));
|
|
|
|
urb.Hdr.Function = URB_FUNCTION_GET_CURRENT_FRAME_NUMBER;
|
|
urb.Hdr.Length = sizeof(urb);
|
|
urb.FrameNumber = (ULONG) -1;
|
|
|
|
nextStack = IoGetNextIrpStackLocation(Irp);
|
|
nextStack->Parameters.Others.Argument1 = (PVOID) &urb;
|
|
nextStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
KeInitializeEvent(&event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
IsoUsb_StopCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_GetCurrentFrame::"));
|
|
IsoUsb_IoIncrement(deviceExtension);
|
|
|
|
IoCallDriver(deviceExtension->TopOfStackDeviceObject,
|
|
Irp);
|
|
|
|
KeWaitForSingleObject((PVOID) &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_GetCurrentFrame::"));
|
|
IsoUsb_IoDecrement(deviceExtension);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_GetCurrentFrame - ends\n"));
|
|
|
|
return urb.FrameNumber;
|
|
}
|
|
|
|
NTSTATUS
|
|
IsoUsb_StopCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for request to retrieve the frame number
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object
|
|
Irp - I/O request packet
|
|
Context - context passed to the completion routine
|
|
|
|
Return Value:
|
|
|
|
NT status value
|
|
|
|
--*/
|
|
{
|
|
PKEVENT event;
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_StopCompletion - begins\n"));
|
|
|
|
event = (PKEVENT) Context;
|
|
|
|
KeSetEvent(event, IO_NO_INCREMENT, FALSE);
|
|
|
|
IsoUsb_DbgPrint(3, ("IsoUsb_StopCompletion - ends\n"));
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|