/*************************************************************************** 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;UrbNumberurb = 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