Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1700 lines
65 KiB

/***************************************************************************
Copyright (c) 1995 Microsoft Corporation
Module Name:
iso.c
Abstract:
Environment:
kernel mode only
Notes:
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
Revision History:
****************************************************************************/
#define DRIVER
#pragma warning(disable:4214) // bitfield nonstd
#include "wdm.h"
#pragma warning(default:4214)
#include "stdarg.h"
#include "stdio.h"
#pragma warning(disable:4200) //non std struct used
#include "usbdi.h"
#pragma warning(default:4200)
#include "usbdlib.h"
#include "usb.h"
#include "ioctl.h"
#include "isoperf.h"
#include "iso.h"
#define MAX_URBS_PER_PIPE 16
UCHAR ucIsoInterface = 0;
// Memory Leak detection global counters
ULONG gulBytesAllocated = 0;
ULONG gulBytesFreed = 0;
NTSTATUS
ISOPERF_RefreshIsoUrb(
PURB urb,
USHORT packetSize,
USBD_PIPE_HANDLE pipeHandle,
PVOID pvDataBuffer,
ULONG ulDataBufferLen
)
/*++
Routine Description:
Refreshes an Iso Usb Request Block in prep for resubmission to USB stack.
Arguments:
urb - pointer to urb to refresh
packetsize - max packet size for the endpoint for which this urb is intended
pipeHandle - Usbd pipe handle for the Urb
pvDataBuff - pointer to a data buffer that will contain data or will receive data
ulDataBufLen- length of data buffer
Return Value:
NT status code:
STATUS_SUCCESS indicates Urb successfully refreshed
Other status codes indicate error (most likely is a bad parameter passed in, which
would result in STATUS_INVALID_PARAMETER to be returned)
--*/
{
NTSTATUS ntStatus = STATUS_SUCCESS;
ULONG siz = 0;
ULONG i = 0;
ULONG numPackets = 0;
// Calculate the number of packets in this buffer
numPackets = ulDataBufferLen/packetSize;
// Adjust num packets by one if data buffer can accommodate it
if (numPackets*packetSize < ulDataBufferLen) {
numPackets++;
}
//
// Use macro from provided by stack to figure out Urb length for given size of packets. This is
// necessary since Urb for iso transfers depends on the number of packets in the data buffer
// since per-packet information is passed on the usbd interface
//
siz = GET_ISO_URB_SIZE(numPackets);
// Clear out any garbage that may have gotten put in the urb by the last transfer
RtlZeroMemory(urb, siz);
// Now fill in the Urb
urb->UrbIsochronousTransfer.Length = (USHORT) siz;
urb->UrbIsochronousTransfer.Function = URB_FUNCTION_ISOCH_TRANSFER;
urb->UrbIsochronousTransfer.PipeHandle = pipeHandle;
urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN;
urb->UrbIsochronousTransfer.TransferBufferMDL = NULL;
urb->UrbIsochronousTransfer.TransferBuffer = pvDataBuffer;
urb->UrbIsochronousTransfer.TransferBufferLength = numPackets * packetSize;
ASSERT (ulDataBufferLen >= numPackets*packetSize);
// This flag tells the stack to start sending/receiving right away
urb->UrbIsochronousTransfer.TransferFlags |= USBD_START_ISO_TRANSFER_ASAP;
urb->UrbIsochronousTransfer.StartFrame = 0;
urb->UrbIsochronousTransfer.NumberOfPackets = numPackets;
urb->UrbIsochronousTransfer.ReservedMBZ = 0;
for (i=0; i< urb->UrbIsochronousTransfer.NumberOfPackets; i++) {
urb->UrbIsochronousTransfer.IsoPacket[i].Offset
= i * packetSize;
}//for
return ntStatus;
}//ISOPERF_RefreshIsoUrb
NTSTATUS
ISOPERF_IsoInCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This is the completion routine that is called at the end of an iso Irp/Urb. It is
called when the Usb stack completes the Irp.
NOTE: IoCompletion routine always runs at IRQL DISPATCH_LEVEL.
Arguments:
DeviceObject - pointer to the device object that represents this USB iso test device
DeviceObject is obtained from context due to old, old bug
in initial WDM implemenations
Irp - pointer to the Irp that was completed by the usb stack
Context - caller-supplied context that is passed in for use by this completion routine
Return Value:
NT status code
--*/
{
PIsoTxterContext pIsoContext = Context;
PDEVICE_OBJECT myDeviceObject = pIsoContext->DeviceObject;
PDEVICE_EXTENSION deviceExtension = myDeviceObject->DeviceExtension;
pConfig_Stat_Info pStatInfo = deviceExtension->pConfig_Stat_Information;
PIO_STACK_LOCATION nextStack = NULL;
PURB urb = pIsoContext->urb;
PISOPERF_WORKITEM isoperfWorkItem = NULL;
PDEVICE_EXTENSION MateDeviceExtension = NULL;
ULONG unew,lnew,uold,lold;
char * pcWork = NULL; //a worker pointer
ULONG i = 0;
char tempStr[32];
ISO_ASSERT (pIsoContext != NULL);
ISO_ASSERT (pStatInfo != NULL);
ISO_ASSERT (deviceExtension != NULL);
ISOPERF_KdPrint_MAXDEBUG (("In IsoInCompletionRoutine %x %x %x\n",
DeviceObject, Irp, Context));
// Check the USBD status code and only proceed in resubmission if the Urb was successful
// or if the device extension flag that indicates the device is gone is FALSE
if ( (USBD_SUCCESS(urb->UrbHeader.Status)) && ((deviceExtension->StopTransfers) == FALSE) ) {
ISOPERF_GetUrbTimeStamp (urb, &lold, &uold); //Get the Urb's timestamp
GET_PENTIUM_CLOCK_COUNT(unew,lnew); //Get the time now
pStatInfo->ulUrbDeltaClockCount = lnew-lold; //Compute & store the delta
// Check that data is incrementing from last data pattern received on this pipe
if ((ISOPERF_IsDataGood(pIsoContext))== TRUE) {
// Data was good
pStatInfo->ulSuccessfulIrps++;
pStatInfo->ulBytesTransferredIn += pIsoContext->ulBufferLen;
} else {
// An error occured, so stop the test by setting the flag to stop the test
deviceExtension->bStopIsoTest = TRUE;
deviceExtension->ulCountDownToStop = 0;
// Put this information in the status area for the device so the app can see it when it asks
pStatInfo->erError = DataCompareFailed;
pStatInfo->bStopped = 1;
}//else data was bad
if ( (deviceExtension->bStopIsoTest == TRUE) && (deviceExtension->ulCountDownToStop > 0) ) {
// User has requested a stop to the Iso Test so, decrement the countdown value
(deviceExtension->ulCountDownToStop)--;
}//if user requested a stop
// If device extension says to keep going on this test then continue on
if ((deviceExtension->ulCountDownToStop) > 0) {
// Refresh this Urb before we resubmit it to the stack
ISOPERF_RefreshIsoUrb (pIsoContext->urb,
pIsoContext->PipeInfo->MaximumPacketSize,
pIsoContext->PipeInfo->PipeHandle,
pIsoContext->pvBuffer,
pIsoContext->ulBufferLen
);
// If this iso in device has a mate, then start a thread to have it use this data buffer
if (deviceExtension->MateDeviceObject) {
MateDeviceExtension = deviceExtension->MateDeviceObject->DeviceExtension;
// Check if the mate device is up and running
if ( (MateDeviceExtension->Stopped == FALSE) && (MateDeviceExtension->StopTransfers == FALSE) ) {
//start a Work Item to use this buffer which will only do so when the buffer has arrived
isoperfWorkItem = ISOPERF_ExAllocatePool(NonPagedPool, sizeof(ISOPERF_WORKITEM),&gulBytesAllocated);
isoperfWorkItem->DeviceObject = deviceExtension->MateDeviceObject;
isoperfWorkItem->pvBuffer = pIsoContext->pvBuffer;
isoperfWorkItem->ulBufferLen = pIsoContext->ulBufferLen;
isoperfWorkItem->bFirstUrb = pIsoContext->bFirstUrb;
isoperfWorkItem->InMaxPacket = pIsoContext->PipeInfo->MaximumPacketSize;
isoperfWorkItem->ulNumberOfFrames = urb->UrbIsochronousTransfer.NumberOfPackets;
// Call the OUT pipe transfer routine
ISOPERF_StartIsoOutTest (isoperfWorkItem);
// Since the firstUrb flag is used to tell the outpipe whether it's a virgin or not,
// and since this Urb can be recycled through here again, we have to de-virginize the
// flag so this Urb doesn't always cause the outpipe to think it's dealing with a virgin Urb.
if (pIsoContext->bFirstUrb == TRUE) {
pIsoContext->bFirstUrb = FALSE;
}//if this was the first Urb
}//if mate device is ok
}//if there is a mate device in the system (that this driver runs)
// Get the next lower driver's stack
nextStack = IoGetNextIrpStackLocation(Irp);
ASSERT(nextStack != NULL);
//
// Set up the next lower driver's stack parameters which is where that
// driver will go to get its parameters for this internal IOCTL Irp
//
nextStack->Parameters.Others.Argument1 = urb;
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;
IoSetCompletionRoutine(Irp,
ISOPERF_IsoInCompletion,
pIsoContext,
TRUE,
TRUE,
FALSE); //INVOKE ON CANCEL
// Time stamp the Urb before we send it down the stack
ISOPERF_TimeStampUrb (urb, &unew, &lnew);
// Call the driver
IoCallDriver (deviceExtension->StackDeviceObject,
Irp);
// Tell the IO Manager that we want to handle this Irp from here on...
return (STATUS_MORE_PROCESSING_REQUIRED);
} else {
ISOPERF_KdPrint (("Stopped Iso test (did not resubmit Irp/Urb due to device extension flag)\n"));
}/* else the tests should be stopped */
}//if Urb status was successful
// Otherwise, the Urb was unsuccessful
else {
pStatInfo->erError = UsbdErrorInCompletion;
pStatInfo->ulUnSuccessfulIrps++;
FIRE_OFF_CATC;
if (!(USBD_SUCCESS(urb->UrbHeader.Status))) {
ISOPERF_KdPrint (("Urb unsuccessful (status: %#x)\n",urb->UrbHeader.Status));
// Dump out the status for the packets
for (i=0; i< urb->UrbIsochronousTransfer.NumberOfPackets; i++) {
ISOPERF_KdPrint (("Packet %d: Status: [%#X]\n",
i,
urb->UrbIsochronousTransfer.IsoPacket[i].Status));
// Put the last known bad packet status code into this space in the stat area
if (!USBD_SUCCESS(urb->UrbIsochronousTransfer.IsoPacket[i].Status)) {
pStatInfo->UsbdPacketStatCode = urb->UrbIsochronousTransfer.IsoPacket[i].Status;
}//if hit a fail code
sprintf (tempStr, "Data: %x", *((PULONG)(urb->UrbIsochronousTransfer.TransferBuffer)));
ISOPERF_KdPrint (("First data in buffer: %s\n",tempStr));
}//for all the packets
}else {
ISOPERF_KdPrint (("Urb successful, but StopTransfers flag is set (%d)\n",deviceExtension->StopTransfers));
}
}//Urb was unsuccessful, so don't repost it and fall thru to cleanup code
// Clean up section. This only executes if:
// --Urb (bus transfer) was unsuccessful (stop the test)
// --Buffer didn't look right (a hiccup was detected, etc.)
// --User requested tests to stop
ISOPERF_KdPrint (("Stopping Iso In Stream and Cleaning Up...U:%x|C:%x|B:%x\n",
urb,
pIsoContext,
pIsoContext->pvBuffer));
// Free up the memory created for this transfer that we are retiring
if (urb) {
// We can't free the Urb itself, since it has some junk before it, so we have to roll back the
// pointer to get to the beginning of the block that we originally allocated, and then try to free it.
pcWork = (char*)urb; //get the urb
urb = (PURB) (pcWork - (2*sizeof(ULONG))); //the original pointer is 2 DWORDs behind the Urb
ISOPERF_KdPrint (("Freeing urb %x\n",urb));
ISOPERF_ExFreePool (urb, &gulBytesFreed); //Free that buffer
}//if
if (pIsoContext) {
// Free the data buffer
if (pIsoContext->pvBuffer) {
ISOPERF_KdPrint (("Freeing databuff %x\n",pIsoContext->pvBuffer));
ISOPERF_ExFreePool (pIsoContext->pvBuffer, &gulBytesFreed);
}
// Free the Iso Context
ISOPERF_KdPrint (("Freeing pIsoContext %x\n",pIsoContext));
ISOPERF_ExFreePool (pIsoContext, &gulBytesFreed);
}//if valid Iso Context
//Decrement the number of outstanding Irps since this one is being retired
(deviceExtension->ulNumberOfOutstandingIrps)--;
// If this is the last Irp we are retiring, then the device should be marked as not busy
if (deviceExtension->ulNumberOfOutstandingIrps == 0) {
deviceExtension->DeviceIsBusy = FALSE;
pStatInfo->bDeviceRunning = FALSE;
}//if not irps left outstanding on this device
//Free the IoStatus block that we allocated for this Irp
if (Irp->UserIosb) {
ISOPERF_KdPrint (("Freeing My IoStatusBlock %x\n",Irp->UserIosb));
ISOPERF_ExFreePool(Irp->UserIosb, &gulBytesFreed);
} else {
//Bad thing...no IoStatus block pointer??
ISOPERF_KdPrint (("ERROR: Irp's IoStatus block is apparently NULL!\n"));
TRAP();
}//else bad iostatus pointer
//Free the Irp here and return STATUS_MORE_PROCESSING_REQUIRED instead of SUCCESS
ISOPERF_KdPrint (("Freeing Irp %x\n",Irp));
IoFreeIrp(Irp);
// Tell the IO Manager that we want to handle this Irp from here on...
ISOPERF_KdPrint (("Returning STATUS_MORE_PROCESSING_REQUIRED from IsoInCompletion\n"));
return (STATUS_MORE_PROCESSING_REQUIRED);
}//ISOPERF_IsoInCompletion
PURB
ISOPERF_BuildIsoRequest(
IN PDEVICE_OBJECT DeviceObject,
IN PUSBD_PIPE_INFORMATION pPipeInfo,
IN BOOLEAN Read,
IN ULONG length,
IN ULONG ulFrameNumber,
IN PVOID pvTransferBuffer,
IN PMDL pMDL
)
/*++
Routine Description:
Allocates and initializes most of a URB. The caller must initialize the FLAGS field
with any further flags they desire after this function is called.
Arguments:
DeviceObject - pointer to the device extension for this instance of the device
pPipeInfo - ptr to pipe descr for which to build the iso request (urb)
Read - if TRUE, it's a read, FALSE it's a write
length - length of the data buffer or MDL, used for packetizing the buffer for iso txfer
ulframeNumber- if non-zero, use this frame number to build in the urb and don't set flags
pvTransferBuffer - Non-NULL: this is the TB ; NULL: an MDL is specified
ulTransferBufferLength - len of buffer specified in pvTransferBuffer
pMDL - if an MDL is being used, this is a ptr to it
Return Value:
Almost initialized iso urb. Caller must fill in the flags after this fn is called.
--*/
{
ULONG siz;
ULONG packetSize,numPackets, i;
PURB urb = NULL;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
char * pcWork = NULL;
ISO_ASSERT(pPipeInfo!=NULL)
packetSize = pPipeInfo->MaximumPacketSize;
numPackets = length/packetSize;
if (numPackets*packetSize < length) {
numPackets++;
}
siz = GET_ISO_URB_SIZE(numPackets);
//Add room for a URB time stamp at the beginning of the Urb by allocating more than
//siz by 4 DWORDs. The pentium clock count macro can then be used by the caller of
//this routine to fill in the clock count stamp. We allocate an extra 2 DWORDs more than
//we need to allow some room for stuff to put at the end of the URB in the future.
pcWork = ISOPERF_ExAllocatePool(NonPagedPool, (siz+(4*sizeof(ULONG))), &gulBytesAllocated);
urb = (PURB) (pcWork + (2*sizeof(ULONG))); //Push pass the clock count area to the urb area
if (urb) {
RtlZeroMemory(urb, siz);
urb->UrbIsochronousTransfer.Length = (USHORT) siz;
urb->UrbIsochronousTransfer.Function = URB_FUNCTION_ISOCH_TRANSFER;
urb->UrbIsochronousTransfer.PipeHandle = pPipeInfo->PipeHandle;
urb->UrbIsochronousTransfer.TransferFlags = Read ? USBD_TRANSFER_DIRECTION_IN : 0;
urb->UrbIsochronousTransfer.TransferBufferMDL = pMDL;
urb->UrbIsochronousTransfer.TransferBuffer = pvTransferBuffer;
urb->UrbIsochronousTransfer.TransferBufferLength = length;
urb->UrbIsochronousTransfer.Function = URB_FUNCTION_ISOCH_TRANSFER;
if (ulFrameNumber==0) {
//No frame number specified, just start asap
urb->UrbIsochronousTransfer.TransferFlags |= USBD_START_ISO_TRANSFER_ASAP;
}else{
//use specific starting framenumber & don't set (unset) the asap flag
urb->UrbIsochronousTransfer.StartFrame = ulFrameNumber;
urb->UrbIsochronousTransfer.TransferFlags &= ~USBD_START_ISO_TRANSFER_ASAP;
}//if framenumber was specified
urb->UrbIsochronousTransfer.NumberOfPackets = numPackets;
urb->UrbIsochronousTransfer.ReservedMBZ = 0;
// Fill in the packet size array
for (i=0; i< urb->UrbIsochronousTransfer.NumberOfPackets; i++) {
urb->UrbIsochronousTransfer.IsoPacket[i].Offset
= i * packetSize;
} //for
}//if urb
return urb;
}
PVOID
ISOPERF_GetBuff (
PDEVICE_OBJECT DeviceObject,
ULONG ulPipeNumber,
ULONG ulInterfaceNumber,
ULONG ulNumberOfFrames,
PULONG pulBufferSize
)
/*++
ISOPERF_GetBuff
Routine Description:
Creates a buffer as specified by input params. If the input params specify a buffer
that is too big to be submitted to this pipe, then this request will fail.
Arguments:
DeviceObject - pointer to the device extension for this instance of the device
Return Value:
Returns a pointer to the allocated data buffer
--*/
{
PDEVICE_EXTENSION deviceExtension = NULL;
ULONG ulMaxPacketSize = 0;
ULONG ulBufferSize = 0;
ULONG ulMaxTxferSize = 0;
PVOID pvBuffer = NULL;
deviceExtension = DeviceObject->DeviceExtension;
ulMaxPacketSize = deviceExtension->Interface[ulInterfaceNumber]->Pipes[ulPipeNumber].MaximumPacketSize;
ulMaxTxferSize = deviceExtension->Interface[ulInterfaceNumber]->Pipes[ulPipeNumber].MaximumTransferSize;
ulBufferSize = *pulBufferSize = ulMaxPacketSize * ulNumberOfFrames;
// Check if this buffer is too big to submit on this pipe (per its initial setup)
if (ulBufferSize > ulMaxTxferSize) {
*pulBufferSize = 0;
return (NULL);
} else {
return ( ISOPERF_ExAllocatePool (NonPagedPool, ulBufferSize, &gulBytesAllocated) );
}
}//ISOPERF_GetBuff
ULONG
ISOPERF_StartIsoInTest (
PDEVICE_OBJECT DeviceObject,
PIRP pIrp
)
/*++
ISOPERF_StartIsoInTest
Routine Description:
Starts the Iso In Test
Arguments:
DeviceObject - pointer to the device extension for this instance of the device
pIrp - pointer to the Irp from the Ioctl
Return Value:
--*/
{
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
PDEVICE_EXTENSION deviceExtension = NULL;
PURB urb = NULL;
ULONG ulFrameNumber = 0;
ULONG UrbNumber = 0;
ULONG NumberOfFrames = 0;
PUSBD_INTERFACE_INFORMATION pInterfaceInfo = NULL;
PUSBD_PIPE_INFORMATION pUsbdPipeInfo = NULL;
PUSBD_INTERFACE_INFORMATION pMateInterfaceInfo = NULL;
PUSBD_PIPE_INFORMATION pMateUsbdPipeInfo = NULL;
ULONG ulBufferSize = 0;
IsoTxferContext * pIsoContext = NULL;
PVOID pvBuff = NULL;
BOOLEAN bFirstUrb = FALSE;
BOOLEAN bHaveMate = FALSE;
ULONG Upper, Lower;
pConfig_Stat_Info configStatInfo = NULL;
ULONG Max_Urbs_Per_Pipe = 0;
PDEVICE_OBJECT mateDeviceObject = NULL;
PDEVICE_EXTENSION mateDeviceExtension = NULL;
pConfig_Stat_Info mateConfigStatInfo = NULL;
char * pcWork = NULL;
ISOPERF_KdPrint (("Enter ISOPERF_StartIsoInTest (%x) (%x)\n",DeviceObject, pIrp));
deviceExtension = DeviceObject->DeviceExtension;
pInterfaceInfo = deviceExtension->Interface[ucIsoInterface];
ISO_ASSERT (pInterfaceInfo!=NULL)
//make sure this is an IN Iso device
if (deviceExtension->dtTestDeviceType != Iso_In_With_Pattern) {
ISOPERF_KdPrint (("Error: not an Iso IN device! (%d)\n",deviceExtension->dtTestDeviceType));
TRAP();
return (ULONG)STATUS_INVALID_PARAMETER;
}//if it's NOT an ISO IN then bounce it back
// Get the config info for the In Iso Device
configStatInfo = deviceExtension->pConfig_Stat_Information;
ASSERT (configStatInfo != NULL);
// Check out the offset provided to make sure it's not too big
if (configStatInfo->ulFrameOffset >= USBD_ISO_START_FRAME_RANGE) {
ISOPERF_KdPrint (("Error: Detected a FrameOffset Larger than allowed! (%d)\n",configStatInfo->ulFrameOffset));
TRAP();
return (ULONG)STATUS_INVALID_PARAMETER;
}//if bad frame offset
if (configStatInfo) {
Max_Urbs_Per_Pipe = configStatInfo->ulMax_Urbs_Per_Pipe;
NumberOfFrames = configStatInfo->ulNumberOfFrames;
}//if configstatinfo is not null
// Only set up the output device if the config info indicates a desire to do the In->Out test
if (configStatInfo->ulDoInOutTest) {
//If there is an OUT Iso device, and we are trying to do a IN->OUT test, then set that device object up
ntStatus = ISOPERF_FindMateDevice (DeviceObject);
if (ntStatus == STATUS_SUCCESS) {
bHaveMate = TRUE;
mateDeviceObject = deviceExtension->MateDeviceObject;
mateDeviceExtension = mateDeviceObject->DeviceExtension;
pMateInterfaceInfo = mateDeviceExtension->Interface[ucIsoInterface];
mateConfigStatInfo = mateDeviceExtension->pConfig_Stat_Information;
mateConfigStatInfo->ulFrameOffset = configStatInfo->ulFrameOffsetMate;
}//if status success
// Reset the pipe on the mate device if it's also here
// NOTE: we only reset the first pipe on the mate device here
if (bHaveMate == TRUE) {
ASSERT (pMateInterfaceInfo!=NULL);
pMateUsbdPipeInfo = &(pMateInterfaceInfo->Pipes[0]);
ASSERT (pMateUsbdPipeInfo != NULL);
ISOPERF_ResetPipe(deviceExtension->MateDeviceObject,pMateUsbdPipeInfo);
}// if mate
} else {
// Set the mate dev obj to NULL so completion routine knows not to use it
deviceExtension->MateDeviceObject = NULL;
}//if In->Out test is not being requested
// Reset the first pipe on the IN device
pUsbdPipeInfo= &(pInterfaceInfo->Pipes[0]);
ISOPERF_ResetPipe(DeviceObject, pUsbdPipeInfo);
// Untrigger the CATC so if we trigger it later it will go off
RESTART_CATC;
// Get the current frame number
ulFrameNumber = ISOPERF_GetCurrentFrame(DeviceObject);
//Save away the current frame number so the app can peek at it
configStatInfo->ulFrameNumberAtStart = ulFrameNumber;
// See what the user wants to do wrt starting frame number by looking at the config info
if (configStatInfo->ulFrameOffset == 0) {
// This means start ASAP
ulFrameNumber = 0;
}else {
//Add the offset that the User wants to add to this frame number (it better be less than 1024!)
ulFrameNumber += configStatInfo->ulFrameOffset;
} //else
//Save away the starting frame number so the app can peek at it
configStatInfo->ulStartingFrameNumber = ulFrameNumber;
// Set flag to indicate first Urb
bFirstUrb = TRUE;
// Get the pipe info for the first pipe
pUsbdPipeInfo= &(pInterfaceInfo->Pipes[0]);
// Build all the urbs for each pipe (note we send multiple Urbs down the stack)
for (UrbNumber=0;UrbNumber<Max_Urbs_Per_Pipe;UrbNumber++) {
//
// Calculate the buffer size required and get a pointer to the buffer
// Note: this buffer needs to be freed when the StopIsoInTest ioctl is
// received. The completion routine will free the buffer when it
// sees that it is time to stop the iso in test (the pointer is grabbed
// from the irp).
//
pvBuff = ISOPERF_GetBuff (DeviceObject,
0, // pipe number
0, // interface number
NumberOfFrames, // Nbr of mS worth of data to post
&ulBufferSize);
ISO_ASSERT (pvBuff!=NULL);
//
// Create the context for this transfer out of the nonpaged pool since it survives
// this routine and is used in the completion routine
//
pIsoContext = ISOPERF_ExAllocatePool (NonPagedPool, sizeof (IsoTxferContext), &gulBytesAllocated);
//build the urb
urb = ISOPERF_BuildIsoRequest(DeviceObject,
pUsbdPipeInfo, //Pipe info struct
TRUE, //READ
ulBufferSize, //Data buffer size
bFirstUrb ? ulFrameNumber : 0, //Frame Nbr
pvBuff, //Data buffer
NULL //no MDL used
);
if (urb) {
// Fill in the iso context
pIsoContext->urb = urb;
pIsoContext->DeviceObject = DeviceObject;
pIsoContext->PipeInfo = pUsbdPipeInfo;
pIsoContext->irp = pIrp;
pIsoContext->pvBuffer = pvBuff;
pIsoContext->ulBufferLen = ulBufferSize;
pIsoContext->PipeNumber = UrbNumber;
pIsoContext->NumPackets = urb->UrbIsochronousTransfer.NumberOfPackets;
pIsoContext->bFirstUrb = bFirstUrb;
ISOPERF_KdPrint_MAXDEBUG (("Urb %d pvBuff %x ulBuffSz %d NumPackts %d pIsoCont %x Urb: %x\n",
UrbNumber,pvBuff,ulBufferSize,pIsoContext->NumPackets,pIsoContext,urb));
// Time stamp the Urb before we send it down the stack
ISOPERF_TimeStampUrb(urb, &Lower, &Upper);
//Create our own Irp for the device and call the usb stack w/ our urb/irp
ntStatus = ISOPERF_CallUSBDEx ( DeviceObject,
urb,
FALSE, //Don't block
ISOPERF_IsoInCompletion, //Completion routine
pIsoContext, //pvContext
FALSE); //don't want timeout
// Set the busy flag if the Urb/Irp succeed
if (NT_SUCCESS(ntStatus)) {
deviceExtension->DeviceIsBusy = TRUE;
//Set to some huge value; ioctl to stop the test will set this to a smaller value
deviceExtension->ulCountDownToStop = 0xFFFF;
deviceExtension->bStopIsoTest = FALSE;
deviceExtension->StopTransfers = FALSE;
configStatInfo->erError = NoError;
configStatInfo->bDeviceRunning = TRUE;
} else {
deviceExtension->DeviceIsBusy = FALSE;
deviceExtension->bStopIsoTest = TRUE;
deviceExtension->StopTransfers = TRUE;
configStatInfo->erError = ErrorInPostingUrb;
configStatInfo->UrbStatusCode = urb->UrbHeader.Status;
if (bFirstUrb) {
//since this is the first Urb, we know for sure the device isn't running
configStatInfo->bDeviceRunning = FALSE;
configStatInfo->bStopped = TRUE;
}//if first Urb
} //else FAILED calling USB stack
}//if urb[UrbNumber] exists
// Reset the flag that indicates it's the first Urb
bFirstUrb = FALSE;
}//for all the urbs per pipe (UrbNumber)
ISOPERF_KdPrint (("Exit ISOPERF_StartIsoInTest (%x)\n",ntStatus));
return ntStatus;
}//StartIsoInTest
NTSTATUS
ISOPERF_ResetPipe(
IN PDEVICE_OBJECT DeviceObject,
IN USBD_PIPE_INFORMATION * pPipeInfo
)
/*++
Routine Description:
Resets the given Pipe by calling a USBD function
Arguments:
DeviceObject - pointer to dev obj for this instance of usb device
pPipeInfo - pointer to usbd pipe info struct
Return Value:
--*/
{
NTSTATUS ntStatus;
PURB urb;
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
ISO_ASSERT (pPipeInfo!=NULL)
urb = ISOPERF_ExAllocatePool(NonPagedPool,((sizeof(struct _URB_PIPE_REQUEST))+64), &gulBytesAllocated);
if (urb) {
urb->UrbHeader.Length = (USHORT) sizeof (struct _URB_PIPE_REQUEST);
urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
urb->UrbPipeRequest.PipeHandle = pPipeInfo->PipeHandle;
ntStatus = ISOPERF_CallUSBD ( DeviceObject,
urb);
} else {
ntStatus = STATUS_NO_MEMORY;
}
ISOPERF_KdPrint (("Freeing urb in RESET_PIPE: %x\n",urb));
// Free the urb we created since we blocked on this function
if (urb) {
ISOPERF_ExFreePool (urb, &gulBytesFreed);
}//if urb
return ntStatus;
}
ULONG
ISOPERF_GetCurrentFrame(
IN PDEVICE_OBJECT DeviceObject
)
/*++
ISOPERF_GetCurrentFrame
Arguments:
DeviceExtension - pointer to the device extension for this instance
Return Value:
Current Frame Number
--*/
{
NTSTATUS ntStatus;
PURB urb;
ULONG currentUSBFrame = 0;
ISOPERF_KdPrint_MAXDEBUG (("In ISOPERF_GetCurrentFrame: (%x)\n",DeviceObject));
urb = ISOPERF_ExAllocatePool(NonPagedPool,sizeof(struct _URB_GET_CURRENT_FRAME_NUMBER), &gulBytesAllocated);
if (urb) {
urb->UrbHeader.Length = (USHORT) sizeof (struct _URB_GET_CURRENT_FRAME_NUMBER);
urb->UrbHeader.Function = URB_FUNCTION_GET_CURRENT_FRAME_NUMBER;
ntStatus = ISOPERF_CallUSBD ( DeviceObject,
urb
);
if (NT_SUCCESS(ntStatus) && USBD_SUCCESS(URB_STATUS(urb))) {
currentUSBFrame = urb->UrbGetCurrentFrameNumber.FrameNumber;
// Since a ZERO for the USBFrameNumber indicates an error, and if it really is zero by chance, then just bump it by one
if (currentUSBFrame==0) {
currentUSBFrame++;
}
}
ISOPERF_ExFreePool(urb, &gulBytesFreed);
} else {
ntStatus = STATUS_NO_MEMORY;
}
ISOPERF_KdPrint_MAXDEBUG (("Exit ISOPERF_GetCurrentFrame: (%x)\n",currentUSBFrame));
return currentUSBFrame;
}
NTSTATUS
ISOPERF_StopIsoInTest (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Stops the Iso IN test by setting fields in the device extension so that the completion
routine no longer does checks and resubmits Irps to keep the iso stream running.
This call causes no USB stack calls nor USB bus traffic.
Arguments:
DeviceObject - pointer to the device extension for this instance of the device
Irp - pointer to the Irp created in the Ioctl
Return Value:
Always returns NT status of STATUS_SUCCESS
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
ULONG Upper, Lower;
GET_PENTIUM_CLOCK_COUNT(Upper,Lower);
ISOPERF_KdPrint (("Enter StopIsoInTest\n"));
ISOPERF_KdPrint (("Upper:Lower -- %ld : %ld\n",Upper,Lower));
ISO_ASSERT (deviceExtension != NULL);
deviceExtension->bStopIsoTest = TRUE;
deviceExtension->ulCountDownToStop = 100;
ISOPERF_KdPrint (("Stopped: %d\n", deviceExtension->Stopped));
ISOPERF_KdPrint (("StopTransfers: %d\n", deviceExtension->StopTransfers));
ISOPERF_KdPrint (("DeviceIsBusy: %d\n", deviceExtension->DeviceIsBusy));
ISOPERF_KdPrint (("NeedCleanup: %d\n", deviceExtension->NeedCleanup));
ISOPERF_KdPrint (("bStopIsoTest: %d\n", deviceExtension->bStopIsoTest));
ISOPERF_KdPrint (("ulNumberOfOutstandingIrps: %d\n", deviceExtension->ulNumberOfOutstandingIrps));
ISOPERF_KdPrint (("ulCountDownToStop: %d\n", deviceExtension->ulCountDownToStop));
ISOPERF_KdPrint (("Exit StopIsoInTest\n"));
return (STATUS_SUCCESS);
}//ISOPERF_StopIsoInTest
BOOLEAN
ISOPERF_IsDataGood(PIsoTxterContext pIsoContext
)
/*++
Routine Description:
Checks the data in the buffer for an incrementing pattern in every "maxpacketsize" granule
of bytes.
Arguments:
pIsoContext - pointer to the Iso context that contains what this routine needs to process the buffer
Return Value:
TRUE - indicates buffer looks good
FALSE - buffer has an error
--*/
{
PUCHAR pchWork, pchEnd;
UCHAR cCurrentValue, cNextValue;
ULONG ulMaxPacketSize;
pchWork = pIsoContext->pvBuffer;
ulMaxPacketSize = pIsoContext->PipeInfo->MaximumPacketSize;
if (pchWork==NULL) {
ISOPERF_KdPrint (("Bad pchWork in IsDataGood (%x)\n",pchWork));
return (FALSE);
}
if (ulMaxPacketSize >= 1024) {
ISOPERF_KdPrint (("Bad MaxPacketSize in IsDataGood (%x)\n",ulMaxPacketSize));
return (FALSE);
}
pchEnd = pchWork + ((pIsoContext->ulBufferLen) - ulMaxPacketSize);
if (pchEnd > (pchWork + ((pIsoContext->NumPackets)*(ulMaxPacketSize)))) {
ISOPERF_KdPrint (("Buffer Problem in IsDataGood: Base: %x | End: %x | NumPackts: %d | MaxPacktSz: %d\n",
pchWork, pchEnd, pIsoContext->NumPackets, ulMaxPacketSize));
}
ASSERT (pchEnd <= (pchWork + ((pIsoContext->NumPackets)*ulMaxPacketSize)));
cCurrentValue = *pchWork;
while (pchWork < pchEnd) {
// Get the next frame's byte value
cNextValue = *(pchWork + ulMaxPacketSize);
if (cNextValue == (cCurrentValue + 1)) {
//Success, go on to next packet
pchWork+=ulMaxPacketSize;
cCurrentValue = *pchWork;
}else{
// Maybe this is the rollover case, so check for it and don't fail it if it is
if (cNextValue==0) {
if (cCurrentValue==0xFF) {
//Success, go on to next packet
pchWork+=ulMaxPacketSize;
cCurrentValue = *pchWork;
continue;
}
}
ISOPERF_KdPrint (("Fail data compare: pchWork: %x | cNextValue: %x | *pchWork: %x | Base: %x\n",
pchWork, cNextValue, *pchWork, pIsoContext->pvBuffer));
FIRE_OFF_CATC;
RESTART_CATC;
return (FALSE);
}
} //while
return (TRUE);
}//ISOPERF_IsDataGood
NTSTATUS
ISOPERF_GetStats (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN OUT pConfig_Stat_Info pStatInfoOut,
IN ULONG ulBufferLen
)
/*++
Routine Description:
Copies the existing Iso traffic counter values into buffer supplied by caller. The
values of the counter variables are defined in IOCTL.H in the pIsoStats structure.
Arguments:
DeviceObject - pointer to the device extension for this instance of the device
Irp - pointer to the Irp created in the Ioctl
pIsoStats - pointer to buffer where stats will go
ulBufferLen - len of above output buffer
Return Value:
NT status of STATUS_SUCCESS means copy was successful
NT status of STATUS_INVALID_PARAMETER means a param (usually a pointer) was bad
--*/
{
ULONG Upper, Lower;
pConfig_Stat_Info pStatInfo = NULL;
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
ISOPERF_KdPrint_MAXDEBUG (("In GetStats (%x) (%x) (%x) (%d)\n",
DeviceObject,Irp,pStatInfoOut,ulBufferLen));
ASSERT (deviceExtension != NULL);
if (deviceExtension) {
pStatInfo = deviceExtension->pConfig_Stat_Information;
}
ASSERT (pStatInfo!= NULL);
GET_PENTIUM_CLOCK_COUNT(Upper,Lower);
if ( (pStatInfoOut) && (ulBufferLen >= sizeof(Config_Stat_Info)) ) {
// Only copy the stat info from the dev ext area if it exists
if (pStatInfo) {
memcpy (pStatInfoOut, pStatInfo, sizeof (Config_Stat_Info));
}/* if dev ext's stat/config info exists */
// Get the global mem alloc/free info
pStatInfoOut->ulBytesAllocated = gulBytesAllocated;
pStatInfoOut->ulBytesFreed = gulBytesFreed;
// Get the current countdown value
pStatInfoOut->ulCountDownToStop= deviceExtension->ulCountDownToStop;
// Get the Pentium counter values
pStatInfoOut->ulUpperClockCount=Upper;
pStatInfoOut->ulLowerClockCount=Lower;
// Get the device type
pStatInfoOut->DeviceType = deviceExtension->dtTestDeviceType;
Irp->IoStatus.Information = sizeof (Config_Stat_Info);
} else {
ntStatus = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Information = 0;
}/* else */
return (ntStatus);
}//ISOPERF_GetStats
NTSTATUS
ISOPERF_SetDriverConfig (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN OUT pConfig_Stat_Info pConfInfoIn,
IN ULONG ulBufferLen
)
/*++
Routine Description:
Sets the driver's test parameters. These are only checked by this driver when the
Iso tests are started. So, if a user mode app tries to set these after a test has started
they will take effect on the next "start" of the test.
Arguments:
DeviceObject - pointer to the device extension for this instance of the device
Irp - pointer to the Irp created in the Ioctl
pIsoStats - pointer to buffer where config info is located
ulBufferLen - len of above input buffer
Return Value:
NT status of STATUS_SUCCESS means setting of params was successful
NT status of STATUS_INVALID_PARAMETER means a param (usually a pointer) was bad
--*/
{
pConfig_Stat_Info pDriverConfigInfo = NULL;
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
ASSERT (deviceExtension != NULL);
if (deviceExtension) {
pDriverConfigInfo = deviceExtension->pConfig_Stat_Information;
}else{
ntStatus = STATUS_INVALID_PARAMETER;
}//else bad dev ext
ASSERT (pDriverConfigInfo!= NULL);
if ( (pConfInfoIn) && (ulBufferLen >= sizeof(Config_Stat_Info)) ) {
// Only copy the stat info from the dev ext area if it exists
if (pDriverConfigInfo) {
//Set the config info in the driver's config/stat area
pDriverConfigInfo->ulNumberOfFrames = pConfInfoIn->ulNumberOfFrames;
pDriverConfigInfo->ulMax_Urbs_Per_Pipe = pConfInfoIn->ulMax_Urbs_Per_Pipe;
pDriverConfigInfo->ulDoInOutTest = pConfInfoIn->ulDoInOutTest;
pDriverConfigInfo->ulFrameOffset = pConfInfoIn->ulFrameOffset;
pDriverConfigInfo->ulFrameOffsetMate = pConfInfoIn->ulFrameOffsetMate;
ISOPERF_KdPrint (("Setting Driver Config-Number of Frames: %d | MaxUrbsPerPipe: %d | DoInOut: %d | FrameOffset %d | MateOffset %d\n",
pDriverConfigInfo->ulNumberOfFrames,
pDriverConfigInfo->ulMax_Urbs_Per_Pipe,
pDriverConfigInfo->ulDoInOutTest,
pDriverConfigInfo->ulFrameOffset,
pDriverConfigInfo->ulFrameOffsetMate));
}/* if dev ext's stat/config info exists */
else {
ntStatus = STATUS_INVALID_PARAMETER;
}//else bad dev extension
Irp->IoStatus.Information = 0;
} else {
ntStatus = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Information = 0;
}/* else bad buffer passed in (pointer was bad, or length was not correct) */
return (ntStatus);
}//ISOPERF_SetDriverConfig
NTSTATUS
ISOPERF_FindMateDevice (
PDEVICE_OBJECT DeviceObject
)
/*++
Searches the linked list of device objects looking for the long lost mate device for the
given device object. By practicing safe device mating rituals (ie., checking that the device
object is a known type, etc.) it then puts the mate's device object pointer into the device extension
of the given device object.
If no mate is found, sadly, then this routine puts a NULL in the mate deviceobject field in the dev extension,
and returns a STATUS_NOT_FOUND result code
++*/
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PDRIVER_OBJECT driverObject = DeviceObject->DriverObject;
PDEVICE_OBJECT NextDeviceObject = DeviceObject->NextDevice;
PDEVICE_EXTENSION NextDeviceExtension = NULL;
dtDeviceType dtMateTestDeviceTypeA = Unknown_Device_Type;
dtDeviceType dtMateTestDeviceTypeB = Unknown_Device_Type;
ASSERT (DeviceObject != NULL);
ASSERT (deviceExtension!=NULL);
if (NextDeviceObject == NULL) {
ISOPERF_KdPrint(("FindMateDevice: No Next Device\n"));
// NOTE: (old code)
// WDM appears to be loading an entirely separate driver image for the same venid/prodid.
// This makes the approach where we look at the device chain to find the next device in the
// chain not feasible, so as a shortterm fix we'll put the Device Object for a Output Iso device
// in a global variable (which does seem to persist across dd loads) and we'll see if that is a
// mate. kjaff 12/24/96
if (gMyOutputDevice!=NULL) {
ISOPERF_KdPrint(("There is a global device us to check...(%x)\n",gMyOutputDevice));
NextDeviceObject = gMyOutputDevice;
NextDeviceExtension = NextDeviceObject->DeviceExtension;
} else {
ISOPERF_KdPrint (("No global device found either...exiting...\n"));
return STATUS_NOT_FOUND;
}//else no global either
}else{
NextDeviceExtension = NextDeviceObject->DeviceExtension;
ISOPERF_KdPrint(("Found a NextDevice: %x\n",NextDeviceObject));
}//else there is a next device
// The Mate for the device is dependent on the device type
switch (deviceExtension->dtTestDeviceType) {
case Iso_In_With_Pattern:
//This dev can have 2 mate types
ISOPERF_KdPrint (("Looking for a mate for Iso_In_With_Pattern %d\n",deviceExtension->dtTestDeviceType));
dtMateTestDeviceTypeA = Iso_Out_With_Interrupt_Feedback;
dtMateTestDeviceTypeB = Iso_Out_Without_Feedback;
break;
case Iso_Out_With_Interrupt_Feedback:
break;
case Iso_Out_Without_Feedback:
break;
case Unknown_Device_Type:
break;
default:
break;
}//switch on device type
ntStatus = STATUS_NOT_FOUND; //assume we haven't found the mate
while (NextDeviceObject != NULL) {
NextDeviceExtension = NextDeviceObject->DeviceExtension;
// Is this the mate?
if ( (NextDeviceExtension->dtTestDeviceType == dtMateTestDeviceTypeA) ||
(NextDeviceExtension->dtTestDeviceType == dtMateTestDeviceTypeB) ) {
ISOPERF_KdPrint (("Found a mate for Dev %X : %X\n",DeviceObject, NextDeviceObject));
//Found a mate, so fill in this dev object into the given dev obj's ext
deviceExtension->MateDeviceObject = NextDeviceObject;
ntStatus = STATUS_SUCCESS;
break; //drop out of the while
} else {
ISOPERF_KdPrint (("%x is not a mate for Dev %X (type:%d\n",
NextDeviceObject,
DeviceObject,
NextDeviceExtension->dtTestDeviceType));
// Get the next device object
NextDeviceObject = NextDeviceObject->NextDevice;
}//else go on to next device object
}//while next dev obj
ISOPERF_KdPrint (("FindMateDevice exiting...\n"));
return ntStatus;
}//ISOPERF_FindMateDevice
VOID
ISOPERF_StartIsoOutTest (
IN PISOPERF_WORKITEM IsoperfWorkItem
)
/*++
Queues an Irp to the USB stack on the given device object.
Inputs:
IsoperfWorkItem - This routine is designed to be fired offf either directly or via a Work Item. The
work item structure only allows for one parameter to be passed to the work item
routine, hence this single workitem parameter.
Return Value:
None
++*/
{
PDEVICE_OBJECT deviceObject = NULL;
PDEVICE_EXTENSION deviceExtension = NULL;
PUSBD_INTERFACE_INFORMATION pInterfaceInfo = NULL;
PUSBD_PIPE_INFORMATION pUsbdPipeInfo = NULL;
pConfig_Stat_Info configStatInfo = NULL;
PVOID pvBuff = NULL;
PURB urb = NULL;
ULONG ulBufferSize = 0;
NTSTATUS ntStatus = STATUS_SUCCESS;
IsoTxferContext * pIsoContext = NULL;
ULONG ulFrameNumber = 0;
ULONG NumberOfFrames = 0;
ULONG i = 0;
deviceObject = IsoperfWorkItem->DeviceObject;
pvBuff = IsoperfWorkItem->pvBuffer;
ulBufferSize = IsoperfWorkItem->ulBufferLen;
deviceExtension = deviceObject->DeviceExtension;
pInterfaceInfo = deviceExtension->Interface[ucIsoInterface];
configStatInfo = deviceExtension->pConfig_Stat_Information;
ASSERT (pInterfaceInfo!=NULL);
ASSERT (configStatInfo != NULL);
ASSERT (deviceObject != NULL);
if ( (configStatInfo==NULL) || (pInterfaceInfo==NULL) || (deviceObject==NULL) ) {
ISOPERF_KdPrint (("Bad Parameter Received: configInf: %x | InterfInfo: %x | DevObj: %x\n",
configStatInfo, pInterfaceInfo, deviceObject));
TRAP();
return;
}//if any params are bad
// Check out the offset provided to make sure it's not too big
if (configStatInfo->ulFrameOffset >= USBD_ISO_START_FRAME_RANGE) {
ISOPERF_KdPrint (("ISOOUT: Error-Detected a FrameOffset Larger than allowed! (%d)\n",configStatInfo->ulFrameOffset));
TRAP();
return;
}//if bad frame offset
if (IsoperfWorkItem->bFirstUrb) {
// We have to match the endpoint maxpacket sizes for this to work (we can't assume that this is the
// case since the Out device sometimes seems to have a larger maxpacket than the In device
// DESIGNDESIGN This may be OK if we later on want to do some rate-matching emulation here.
if ( (deviceExtension->Interface[0]->Pipes[0].MaximumPacketSize) >= IsoperfWorkItem->InMaxPacket ) {
deviceExtension->Interface[0]->Pipes[0].MaximumPacketSize = IsoperfWorkItem->InMaxPacket;
} else {
//if the OUT device's maxpacket is smaller than the IN device's this seems incorrect so do nothing
return;
}//else the endpoint sizes seem out of whack
//#if 0
// Since this is the first Urb, we need the frame number, so get it
ulFrameNumber = ISOPERF_GetCurrentFrame(deviceObject);
ISOPERF_KdPrint (("StartISOOut got Frame Number: %x | Offset: %d\n",ulFrameNumber,configStatInfo->ulFrameOffset));
if (ulFrameNumber==0) {
ISOPERF_KdPrint (("Got Bad Frame Number (%x) ...exiting...\n",ulFrameNumber));
deviceExtension->StopTransfers = TRUE; //set flag so further transfers stop
return;
}//if bad frame #
//Save away the current frame number so the app can peek at it
configStatInfo->ulFrameNumberAtStart = ulFrameNumber;
// See what the user wants to do wrt starting frame number by looking at the config info
if (configStatInfo->ulFrameOffset == 0) {
// This means start ASAP
ulFrameNumber = 0;
}else {
//Add the offset that the User wants to add to this frame number (it better be less than 1024!)
ulFrameNumber += configStatInfo->ulFrameOffset;
} //else
//Save away the starting frame number so the app can peek at it
configStatInfo->ulStartingFrameNumber = ulFrameNumber;
ISOPERF_KdPrint (("StartISOOut using Frame Number: %x\n",ulFrameNumber));
//#endif
}//if this is the first Urb
else
{
ISOPERF_KdPrint (("StartISOOut _NOT_ the First URB!\n"));
}//else not first Urb
// Get the pipe info for the first pipe
pUsbdPipeInfo= &(pInterfaceInfo->Pipes[0]);
ASSERT(pUsbdPipeInfo != NULL);
if (deviceExtension->StopTransfers != TRUE) {
//We get the Urbs to submit and the Nbr of frames from the workitem because we want to
//make sure this is the same as the setting for the In device (this is filled in by InComplRoutine)
NumberOfFrames = IsoperfWorkItem->ulNumberOfFrames;
ASSERT (NumberOfFrames > 0);
//We get our own buffer for now...in the future this will be passed in if we decide to reuse the
//input device's buffer
pvBuff = ISOPERF_GetBuff (deviceObject,
0,//pipe number
0,//interfacenumber
NumberOfFrames,
&ulBufferSize
);
if (pvBuff==NULL){
return;
}else{
//Copy the input buffer into our output buffer (they should be the same size)
ASSERT (IsoperfWorkItem->ulBufferLen == ulBufferSize);
RtlCopyMemory (pvBuff, IsoperfWorkItem->pvBuffer, ulBufferSize);
}//if pvbuff
//...to here
//build the urb
urb = ISOPERF_BuildIsoRequest(deviceObject,
pUsbdPipeInfo, //Pipe info struct
FALSE, //WRITE
ulBufferSize, //Data buffer size
IsoperfWorkItem->bFirstUrb ? ulFrameNumber : 0,
pvBuff, //Data buffer
NULL //no MDL used
);
ISOPERF_KdPrint_MAXDEBUG (("OUT Urb: %x\n",urb));
pIsoContext = ISOPERF_ExAllocatePool (NonPagedPool, sizeof (IsoTxferContext), &gulBytesAllocated);
// Fill in the iso context
pIsoContext->urb = urb;
pIsoContext->DeviceObject = deviceObject;
pIsoContext->PipeInfo = pUsbdPipeInfo;
pIsoContext->pvBuffer = pvBuff; //NOTE: set this to NULL if you're using the IN device's buffer
pIsoContext->ulBufferLen = ulBufferSize;
pIsoContext->PipeNumber = 0;
pIsoContext->NumPackets = urb->UrbIsochronousTransfer.NumberOfPackets;
pIsoContext->bFirstUrb = IsoperfWorkItem->bFirstUrb;
//Create our own Irp for the device and call the usb stack w/ our urb/irp
ntStatus = ISOPERF_CallUSBDEx ( deviceObject,
urb,
FALSE, //Don't block
ISOPERF_IsoOutCompletion,//Completion routine
pIsoContext, //pvContext
FALSE); //don't want timeout
if (NT_SUCCESS(ntStatus)) {
deviceExtension->DeviceIsBusy = TRUE;
deviceExtension->bStopIsoTest = FALSE;
deviceExtension->StopTransfers = FALSE;
configStatInfo->erError = NoError;
configStatInfo->bDeviceRunning = 1;
} else {
// An error occurred, so stop things and report it thru config/stat info
deviceExtension->DeviceIsBusy = FALSE;
deviceExtension->bStopIsoTest = TRUE;
deviceExtension->StopTransfers = TRUE;
configStatInfo->erError = ErrorInPostingUrb;
configStatInfo->bDeviceRunning = 0;
// If the URB status code got filled in then extract the info returned
if (!(USBD_SUCCESS(urb->UrbHeader.Status))) {
ISOPERF_KdPrint (("StartIsoOut -- Urb unsuccessful (status: %#x)\n",urb->UrbHeader.Status));
configStatInfo->UrbStatusCode = urb->UrbHeader.Status;
// Dump out the status for the packets
for (i=0; i< urb->UrbIsochronousTransfer.NumberOfPackets; i++) {
ISOPERF_KdPrint (("Packet %d: Status: [%#X]\n",
i,
urb->UrbIsochronousTransfer.IsoPacket[i].Status));
// Put the last known bad packet status code into this space in the stat area
if (!USBD_SUCCESS(urb->UrbIsochronousTransfer.IsoPacket[i].Status)) {
configStatInfo->UsbdPacketStatCode = urb->UrbIsochronousTransfer.IsoPacket[i].Status;
}//if hit a fail code
}//for all the packets
}// if bad Urb status code
}//else there was an error
}else{
ISOPERF_KdPrint_MAXDEBUG (("IsoOut not submitting Urbs because StopTransfers is asserted!\n"));
}//else we are being asked to stoptransfers
//Free the work item junk
ISOPERF_ExFreePool (IsoperfWorkItem, &gulBytesFreed);
ISOPERF_KdPrint_MAXDEBUG (("Exit StartIsoOut\n"));
return;
}//ISOPERF_StartIsoOutTest
NTSTATUS
ISOPERF_IsoOutCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
This is the completion routine for the Iso OUT transfer
Inputs:
Device Object - the device object
Irp - The Irp we posted that is being completed
Context - pointer to our defined context which has our DeviceObject since it comes back as NULL here
DeviceObject is obtained from context due to old, old bug
in initial WDM implemenations
Return Value:
ntStatus
++*/
{
PIsoTxterContext pIsoContext;
PDEVICE_OBJECT myDeviceObject;
PDEVICE_EXTENSION deviceExtension;
PURB urb;
pConfig_Stat_Info pStatInfo;
char * pcWork = NULL; //a worker pointer
ULONG i = 0;
ISOPERF_KdPrint_MAXDEBUG (("In IsoOUTCompletion\n"));
TRAP();
pIsoContext = Context;
myDeviceObject = pIsoContext->DeviceObject;
deviceExtension = myDeviceObject->DeviceExtension;
urb = pIsoContext->urb;
pStatInfo = deviceExtension->pConfig_Stat_Information;
// Check the USBD status code and only proceed in resubmission if the Urb was successful
// or if the device extension flag that indicates the device is gone is FALSE
if (!(USBD_SUCCESS(urb->UrbHeader.Status))) {
ISOPERF_KdPrint (("IsoOUT Urb %x unsuccessful (status: %x)\n",urb,(urb->UrbHeader.Status)));
// Don't let it go on w/ the tests
deviceExtension->StopTransfers = TRUE;
// Fill in stat info
if (pStatInfo) {
pStatInfo->erError = UsbdErrorInCompletion;
pStatInfo->bStopped = 1;
pStatInfo->UrbStatusCode = urb->UrbHeader.Status;
}//if pStatInfo
// Dump out the status for the packets
for (i=0; i< urb->UrbIsochronousTransfer.NumberOfPackets; i++) {
ISOPERF_KdPrint (("Packet %d: Status: [%#X]\n",
i,
urb->UrbIsochronousTransfer.IsoPacket[i].Status));
// Put the last known bad packet status code into this space in the stat area
if (!USBD_SUCCESS(urb->UrbIsochronousTransfer.IsoPacket[i].Status)) {
pStatInfo->UsbdPacketStatCode = urb->UrbIsochronousTransfer.IsoPacket[i].Status;
}//if hit a fail code
}//for all the packets
}//if failure detected
// Free up the URB memory created for this transfer that we are retiring
if (urb) {
// We can't free the Urb itself, since it has some junk before it, so we have to roll back the
// pointer to get to the beginning of the block that we originally allocated, and then try to free it.
pcWork = (char*)urb; //get the urb
urb = (PURB) (pcWork - (2*sizeof(ULONG))); //the original pointer is 2 DWORDs behind the Urb
ISOPERF_KdPrint_MAXDEBUG (("Freeing Urb: %x\n",urb));
ISOPERF_ExFreePool (urb, &gulBytesFreed);
}//if
if (pIsoContext) {
// Free the data buffer
// NOTE: This only can be done if we own this data buffer. If the same data buffer that
// the IN device is using is being used here, then we don't want to free this buffer.
// The presence of a pointer to the data buffer will tell us that
if (pIsoContext->pvBuffer) {
ISOPERF_KdPrint_MAXDEBUG (("Freeing pvBuffer: %x\n",pIsoContext->pvBuffer));
ISOPERF_ExFreePool(pIsoContext->pvBuffer, &gulBytesFreed);
}//if pvbuffer
// Free the Iso Context
ISOPERF_KdPrint_MAXDEBUG (("Freeing pIsoContext: %x\n",pIsoContext));
ISOPERF_ExFreePool (pIsoContext, &gulBytesFreed);
}//if valid Iso Context
//Free the IoStatus block
if (Irp->UserIosb) {
ISOPERF_KdPrint_MAXDEBUG (("Freeing My IoStatus Block: %x\n",Irp->UserIosb));
ISOPERF_ExFreePool(Irp->UserIosb, &gulBytesFreed);
} else {
//Bad thing...no IoStatus block pointer??
ISOPERF_KdPrint (("ERROR: Irp's IoStatus block is apparently NULL!\n"));
TRAP();
}//else bad iostatus pointer
// Free the Irp we created
ISOPERF_KdPrint (("Freeing Irp %x\n",Irp));
IoFreeIrp(Irp);
ISOPERF_KdPrint_MAXDEBUG (("Exit IsoOUTCompletion\n"));
return (STATUS_MORE_PROCESSING_REQUIRED); //Leave the Irp alone, IOS, since we're the top level driver
}//ISOPERF_IsoOutCompletion
NTSTATUS
ISOPERF_TimeStampUrb ( PVOID urb,
PULONG pulLower,
PULONG pulUpper
)
/*++
Routine Description:
Puts a Pentium Clock count time stamp 2 DWORDS before the given pointer (usually a urb,
hence the name of the function).
This function can also be used to simply get the Pentium clock count, although there
is already a Macro to do that.
Inputs:
PVOID urb - pointer to a chunk of memory that ususally contains a Urb
PULONG puLower - pointer to a ULONG that gets the current upper CPU clock count (eax)
PULONG puUpper - same as lower, but the upper ULONG value (edx)
Return Value:
ntStatus indiciating success/fail
--*/
{
char * pcWork;
ULONG u,l;
GET_PENTIUM_CLOCK_COUNT (u,l);
// Time stamp the Urb before we send it down the stack
pcWork = (char*) (urb);
//Backup 2 DWORDS
pcWork -= (2*sizeof(ULONG));
//First DWORD is the Upper value (edx)
*((PULONG)pcWork) = u;
//Goto next DWORD
pcWork += sizeof(ULONG);
//Second DWORD is the Lower value (eax)
*((PULONG)pcWork) = l;
// Copy the values to the caller's supplied area
*pulUpper = u;
*pulLower = l;
return STATUS_SUCCESS;
}
NTSTATUS
ISOPERF_GetUrbTimeStamp ( PVOID urb,
PULONG pulLower,
PULONG pulUpper
)
/* ++
Routine Description:
Gets the Pentium Clock count time stamp 2 DWORDS before the given pointer (usually a urb,
hence the name of the function).
Inputs:
PVOID urb - pointer to a chunk of memory that ususally contains a Urb
PULONG puLower - pointer to a ULONG that gets the upper CPU clock count from the timestamp area(eax)
PULONG puUpper - same as lower, but the upper ULONG value (edx)
Return Value:
ntStatus indiciating success/fail
--*/
{
char * pcWork;
//Get the Urb's pointer
pcWork = (char *) urb;
//The first DW before the Urb is the lower DW of the clk cnt...
pcWork -= (sizeof(ULONG));
//...when the Urb was posted to the Usb stack
*pulLower = *((ULONG*)pcWork);
//NOTE: we don't care about the upper value right now...
*pulUpper = 0;
return STATUS_SUCCESS;
}//ISOPERF_GetUrbTimeStamp