Leaked source code of windows server 2003
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

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