xbox-kernel/private/ntos/dd/usb/ohcd/isr.c
2020-09-30 17:17:25 +02:00

1315 lines
42 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
isr.c
Abstract:
Implementation of the OpenHCI interrupt service routine and related code
including the DPC.
Environment:
Designed for XBOX.
Notes:
Revision History:
01-24-00 created by Mitchell Dernis (mitchd)
--*/
//
// Pull in OS headers
//
#include <ntos.h>
//
// Setup the debug information for this file (see ..\inc\debug.h)
//
#define MODULE_POOL_TAG 'DCHO'
#include <debug.h>
DEFINE_USB_DEBUG_FUNCTIONS("OHCD");
int IsrEntryCount=0;
//
// Pull in usb headers
//
#include <hcdi.h> //Interface between USBD and HCD
#include "ohcd.h" //Private OHCD stuff
//----------------------------------------------------------------------------
// Forward declaration of functions defined and used only this in this module
//----------------------------------------------------------------------------
VOID
FASTCALL
OHCD_fProcessDoneTD(
POHCD_DEVICE_EXTENSION DeviceExtension,
POHCD_TRANSFER_DESCRIPTOR TD
);
VOID
FASTCALL
OHCD_fProcessFailedTD(
POHCD_DEVICE_EXTENSION DeviceExtension,
POHCD_TRANSFER_DESCRIPTOR TD
);
VOID
FASTCALL
OHCD_fCompleteRequest(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN POHCD_ENDPOINT Endpoint,
IN PURB Urb
);
VOID
FASTCALL
OHCD_fCreditTDQuota(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN POHCD_ENDPOINT Endpoint
);
BOOLEAN
FASTCALL
OHCD_fProcessCancelPendingList(
POHCD_DEVICE_EXTENSION DeviceExtension,
ULONG CurrentFrame
);
BOOLEAN
FASTCALL
OHCD_fProcessAbortPendingList(
POHCD_DEVICE_EXTENSION DeviceExtension,
ULONG CurrentFrame
);
BOOLEAN
FASTCALL
OHCD_fProcessClosePendingList(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
ULONG CurrentFrame
);
VOID
FASTCALL
OHCD_fCleanEndpointTDs(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN POHCD_ENDPOINT Endpoint
);
//----------------------------------------------------------------------------
// Implementation of function called from other modules:
// OHCD_InterruptService
// OHCD_IsrDpc
// OHCD_Get32BitFrameNumber
//----------------------------------------------------------------------------
BOOLEAN
OHCD_InterruptService(
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext
)
{
POHCD_DEVICE_EXTENSION deviceExtension = (POHCD_DEVICE_EXTENSION)ServiceContext;
ULONG interruptsSignaled;
ULONG interruptEnable;
POHCI_OPERATIONAL_REGISTERS HcRegs = deviceExtension->OperationalRegisters;
IsrEntryCount++;
//
// Get the interrupts which are actually signaled on USB. According to OpenHCI 7.1.5
// the ine bits set in HcInterruptEnable and in HcInterrupt are actually signaled.
//
//
interruptEnable = READ_REGISTER_ULONG(&HcRegs->HcInterruptEnable);
interruptsSignaled = interruptEnable & READ_REGISTER_ULONG(&HcRegs->HcInterruptStatus);
//
// If an enabled interrupt was not signaled, or if the master interrupt
// is off this interrupt couldn't have been for us.
//
if( (0 == interruptsSignaled) || !(interruptEnable&HCINT_MasterInterruptEnable) ) return FALSE;
//
// Disable Interrupts on Host Controller
//
WRITE_REGISTER_ULONG(&HcRegs->HcInterruptDisable, HCINT_MasterInterruptEnable);
//**
//** In sequence check and handle the following conditions:
//**
//** SchedulingOverrun
//** FrameNumberOverflow
//**
//** The following conditions are handled only in the DPC:
//**
//** WritebackDoneHead
//** StartOfFrame
//** UnrecoverableError
//** RootHubStatusChange
//**
//
// Today we ack but otherwise ignore a SchedulingOverrun interrupt.
// In the future we may want to do something as suggested in section
// 5.2.10.1 of the OpenHCI specification.
//
if (interruptsSignaled & HCINT_SchedulingOverrun)
{
WRITE_REGISTER_ULONG(&HcRegs->HcInterruptStatus, HCINT_SchedulingOverrun);
//
// record that we handled the interrupt
//
interruptsSignaled &= ~HCINT_SchedulingOverrun;
}
//
// Check for Frame Number Overflow The following insures that the 32 bit
// frame never runs backward.
//
if (interruptsSignaled & HCINT_FrameNumberOverflow)
{
//
// Fix up our high part.
//
deviceExtension->FrameHighPart += 0x10000 -
(0x8000 & (deviceExtension->HCCA->FrameNumber ^ deviceExtension->FrameHighPart));
//
// Clear the status flag, we handled it.
//
WRITE_REGISTER_ULONG(&HcRegs->HcInterruptStatus, HCINT_FrameNumberOverflow);
//
// record that we handled the interrupt
//
interruptsSignaled &= ~HCINT_FrameNumberOverflow;
};
//
// If there is something left to do,
// queue a DPC item, let the DPC reenable interrupts
// when it is done.
//
if(interruptsSignaled)
{
deviceExtension->IsrDpc_Context.InterruptsSignaled = interruptsSignaled;
KeInsertQueueDpc(&deviceExtension->IsrDpc, NULL, NULL);
}
else
{
//
// If we didn't queue anything than reenable interrupts.
//
WRITE_REGISTER_ULONG(&deviceExtension->OperationalRegisters->HcInterruptEnable, HCINT_MasterInterruptEnable);
}
return TRUE;
}
VOID
OHCD_IsrDpc(
PKDPC Dpc,
PVOID DeviceExtensionPtr,
PVOID Bogus1,
PVOID Bogus2
)
/*++
Routine Description:
The OHCD_InterruptService will have queued the DPC, if any of the following
conditions occured:
WritebackDoneHead
StartOfFrame
UnrecoverableError
RootHubStatusChange
We handled any and all of these conditions which are present, and then
reenable interrupts for the host controller.
Arguments:
Dpc - pointer to our DPC object
DeviceExtensionPtr - pointer to device extension for our HC.
Bogus1 - These are arguments that the ISR
Bogus2 - could have supplied, but we don't need them.
Return Value:
Noned
--*/
{
//
// Declare and initialize a whole bunch of convenience variables.
//
POHCD_DEVICE_EXTENSION deviceExtension = (POHCD_DEVICE_EXTENSION)DeviceExtensionPtr;
POHCI_OPERATIONAL_REGISTERS hcRegs = deviceExtension->OperationalRegisters;
ULONG interruptsSignaled = deviceExtension->IsrDpc_Context.InterruptsSignaled;
ULONG doneHead;
POHCD_TRANSFER_DESCRIPTOR completedTDs = NULL;
POHCD_TRANSFER_DESCRIPTOR tempTD = 0;
BOOLEAN fItemsStillPending = FALSE;
ULONG currentFrame;
USB_DBG_ENTRY_PRINT(("Entering OHCD_IsrDpc"));
//
// The pause routine checks this to see if it
// is between an ISR and the DPC executing.
//
deviceExtension->IsrDpc_Context.InterruptsSignaled = 0;
//
// Check for UnrecoverableError first, because there is no point.
// in doing anything else, if this has occured.
//
if (interruptsSignaled & HCINT_UnrecoverableError)
{
ASSERT(FALSE && "Wow, what do we do here? Try reseting USB? Reset the whole box?");
}
//
// Check for WritebackDoneHead, to see if we have TDs on the done list
//
if(interruptsSignaled & HCINT_WritebackDoneHead)
{
USB_DBG_TRACE_PRINT(("HCINT_WritebackDoneHead is set: processing . . ."));
doneHead = deviceExtension->HCCA->DoneHead;
doneHead &= 0xFFFFFFF0; //LSb's can be used to indicate other interrupt conditions, get rid of them
deviceExtension->HCCA->DoneHead = 0;
#ifdef SILVER
//
// If the done head was lost, try to find it.
//
if(0==doneHead)
{
doneHead = OHCD_fPoolFindLostDoneHead(deviceExtension);
}
#else
ASSERT(doneHead && "Xbox hardware should not have this bug");
#endif
//
// The Done Queue can be treated as a LIFO. Furthermore, they are linked only
// by physical address pointers. So in one fell swoop we:
// 1) dequeue them from the head
// 2) reverse the links
// 3) switch the physical addresses to virtual addresses.
//
while(doneHead)
{
//
// Get a virtual pointer to a TD
//
tempTD = OHCD_PoolTDFromPhysicalAddress(doneHead);
ASSERT(tempTD);
//
// Dequeue TD from Host Controller Queue
//
doneHead = tempTD->HcTransferDescriptor.NextTD;
//
// Queue TD at head our CompletedTDs list
//
tempTD->HcTransferDescriptor.NextTDVirtual = (POHCI_TRANSFER_DESCRIPTOR)completedTDs;
completedTDs = tempTD;
}
//
// Now step through each TD in the order of completion
// and deal with it.
//
while(completedTDs)
{
//
// Dequeue a TD off the head
//
tempTD = completedTDs;
completedTDs = (POHCD_TRANSFER_DESCRIPTOR)tempTD->HcTransferDescriptor.NextTDVirtual;
//
// Process the TD
//
if(OHCD_IS_ISOCH_TD(tempTD))
{
OHCD_PROCESS_ISOCHRONOUS_TD(deviceExtension, tempTD);
} else
{
OHCD_fProcessDoneTD(deviceExtension, tempTD);
}
}
//
// Tell the Host Controller we are done
//
WRITE_REGISTER_ULONG(&hcRegs->HcInterruptStatus, HCINT_WritebackDoneHead);
interruptsSignaled &= ~HCINT_WritebackDoneHead;
//
// This shouldn't be necessary! However, in some cases we added to the schedule while interrupts were
// still disabled and the operation completed before this DPC routine finished. The interrupt seems to
// be lost in these cases. Hitting these flags after restoring the interrupt gets things going again.
// I would call this a hardware bug. Not worth complaining about though, this fix is pretty straightforward.
//
WRITE_REGISTER_ULONG((PULONG)&deviceExtension->OperationalRegisters->HcCommandStatus, HCCS_ControlListFilled | HCCS_BulkListFilled);
}
//
// Check for StartOfFrame
//
if (interruptsSignaled & HCINT_StartOfFrame)
{
//
// Why did we ask for a SOF? Probably to guarantee an interrupt so
// that we could deal with closing endpoints, or canceling requests
//
//
// We treat SOF interrupts as one time events, so shut them
// back off.
WRITE_REGISTER_ULONG(&hcRegs->HcInterruptStatus, HCINT_StartOfFrame);
WRITE_REGISTER_ULONG(&hcRegs->HcInterruptDisable, HCINT_StartOfFrame);
interruptsSignaled &= ~HCINT_StartOfFrame;
}
//
// Handle pending cancels
//
currentFrame = OHCD_Get32BitFrameNumber(deviceExtension);
if(OHCD_fProcessCancelPendingList(deviceExtension, currentFrame))
{
fItemsStillPending = TRUE;
}
//
// Handle pending aborts
//
// IT IS IMPORTANT TO HANDLE PENDING ABORTS BEFORE PENDING
// CLOSES, SO THAT WE DO NOT CLOSE AN ENDPOINT WITH AN ABORT
// PENDING.
//
if(OHCD_fProcessAbortPendingList(deviceExtension, currentFrame))
{
fItemsStillPending = TRUE;
}
//
// Handle pending closes
//
if(OHCD_fProcessClosePendingList(deviceExtension, currentFrame))
{
fItemsStillPending = TRUE;
}
//
// If there anything (cancels, aborts, or closes)
// are still pending, then set an interrupt at
// the next start of frame.
//
if(fItemsStillPending)
{
USB_DBG_TRACE_PRINT(("Items still pending setting an SOF interrupt."));
WRITE_REGISTER_ULONG(&deviceExtension->OperationalRegisters->HcInterruptStatus, HCINT_StartOfFrame);
WRITE_REGISTER_ULONG(&deviceExtension->OperationalRegisters->HcInterruptEnable, HCINT_StartOfFrame);
}
//
// Process Root Hub changes
//
if(interruptsSignaled & HCINT_RootHubStatusChange)
{
USB_DBG_TRACE_PRINT(("Root hub interrupt signaled"));
OHCD_RootHubProcessInterrupt(deviceExtension);
WRITE_REGISTER_ULONG(&hcRegs->HcInterruptStatus, HCINT_RootHubStatusChange);
interruptsSignaled &= ~HCINT_RootHubStatusChange;
}
//
// If some interrupt occured and we haven't
// handled it, we obviously don't care about it, so disable
// it.
if(interruptsSignaled)
{
ASSERT(FALSE && "Not expecting interrupts that we don't handle.");
WRITE_REGISTER_ULONG(&hcRegs->HcInterruptStatus, interruptsSignaled);
interruptsSignaled = 0;
}
//
// Turn interrupts back on.
//
WRITE_REGISTER_ULONG(&hcRegs->HcInterruptEnable, HCINT_MasterInterruptEnable);
USB_DBG_EXIT_PRINT(("Exiting OHCD_IsrDpc"));
}
ULONG
FASTCALL
OHCD_Get32BitFrameNumber(
POHCD_DEVICE_EXTENSION DeviceExtension
)
{
ULONG hp, fn, n;
/* This code accounts for the fact that HccaFrameNumber is updated by the
* HC before the HCD gets an interrupt that will adjust FrameHighPart. No
* synchronization is necessary due to great cleaverness. */
hp = DeviceExtension->FrameHighPart;
fn = DeviceExtension->HCCA->FrameNumber;
n = ((fn & 0x7FFF) | hp) + ((fn ^ hp) & 0x8000);
return n;
}
//----------------------------------------------------------------------------
// Implementation of functions used only locally:
// OHCD_ProcessDoneTD
// OHCD_ProcessFailedTD
// OHCD_ProcessCancelPendingList
//----------------------------------------------------------------------------
VOID
FASTCALL
OHCD_fProcessDoneTD(
POHCD_DEVICE_EXTENSION DeviceExtension,
POHCD_TRANSFER_DESCRIPTOR TD
)
/*++
Routine Description:
Called from the IsrDpc to cleanup each TD that was
in the done queue. There are a large number of cases.
Arguments:
DeviceExtension - pointer to device extension for our HC.
TD - Transfer Descriptor that completed.
Return Value:
None
--*/
{
PURB urb = TD->Urb;
POHCD_ENDPOINT endpoint = TD->Endpoint;
POHCD_TRANSFER_DESCRIPTOR setupBlock;
BOOLEAN isLastTD;
USB_DBG_ENTRY_PRINT(("Entering OHCD_ProcessDoneTD"));
//
// Success or failure, if this is a setup TD, we need to free the
// setupBlock.
//
if(OHCD_TD_TYPE_SETUP == TD->Type)
{
USB_DBG_TRACE_PRINT(("TD is a setup TD, so free the descriptor used for the data payload."));
//
// Clean up the setup packet which we stored in a descriptor block.
// Get the virtual address of the setup packet (BufferEnd points
// to the physical address of the last byte of the 8 byte setup
// packet).
//
setupBlock = OHCD_PoolTDFromPhysicalAddress(TD->HcTransferDescriptor.BufferEnd - 7);
//
// With Virtual address in hand we can free it.
//
OHCD_PoolFreeTD(setupBlock);
OHCD_PoolCreditControlTDQuota(1);
}
//
// If this TD completed with an error, than
// let OHCD_ProcessFailedTD take over.
//
if(OHCI_CC_NO_ERROR != TD->HcTransferDescriptor.ConditionCode)
{
USB_DBG_TRACE_PRINT(("TD @0x%0.8x had an error. CC = %x", TD, TD->HcTransferDescriptor.ConditionCode));
ASSERT(TD->Type != OHCD_TD_TYPE_DUMMY);
OHCD_fProcessFailedTD(DeviceExtension, TD);
return;
}
//
// Calculate bytes transfered.
//
if(TD->HcTransferDescriptor.CurrentBufferPointer)
{
//
// We do everything signed to account for page wrap
//
LONG currentBufferPointerOffset = (LONG)BYTE_OFFSET(TD->HcTransferDescriptor.CurrentBufferPointer);
LONG endBufferPointerOffset = (LONG)BYTE_OFFSET(TD->HcTransferDescriptor.BufferEnd);
LONG bytesTransfered;
if(endBufferPointerOffset >= currentBufferPointerOffset)
{
bytesTransfered = TD->Bytes - (endBufferPointerOffset - currentBufferPointerOffset);
} else
{
bytesTransfered = TD->Bytes - (PAGE_SIZE - (currentBufferPointerOffset - endBufferPointerOffset));
}
bytesTransfered--;
ASSERT(bytesTransfered >= 0);
urb->CommonTransfer.TransferBufferLength += bytesTransfered;
} else
{
urb->CommonTransfer.TransferBufferLength += TD->Bytes;
}
//
// Record whether this was the last TD in an URB
//
isLastTD = TD->Flags & OHCD_TD_FLAG_LAST_TD;
//
// Free the TD
//
OHCD_PoolFreeTD(TD);
//
// Credit the proper quota
//
OHCD_fCreditTDQuota(DeviceExtension, endpoint);
//
// If that TD completed the URB then we need to process
// the URB
//
if(isLastTD)
{
USB_DBG_TRACE_PRINT(("TD was last in an URB."));
urb->Header.Status = USBD_STATUS_SUCCESS;
OHCD_fCompleteRequest(DeviceExtension, endpoint, urb);
}
USB_DBG_EXIT_PRINT(("Exiting OHCD_ProcessDoneTD"));
//
// Nothing else to do
//
return;
}
VOID
FASTCALL
OHCD_fProcessFailedTD(
POHCD_DEVICE_EXTENSION DeviceExtension,
POHCD_TRANSFER_DESCRIPTOR TD
)
/*++
Routine Description:
Called from the OHCD_ProcessDoneTD (in the context of IsrDPC),
because the condition code indicated an error. Not called if
TD is for an isochronous endpoint.
Our job is to deal with that condition. Firstly, the
associated endpoint has definately been halted, so we
can party on the endpoint. In most cases we do
the following:
1) Copy the error condition into the URB's status field.
2) Clean up all the TD's associated with that URB.
5) Complete the URB.
There is one exceptions for DataUnderrun when ShortXferOK. There
is a slight difference in the way control endpoints are handled.
For Bulk and Interrupt endpoints we proceed as follows:
1) Clean up all the remaining TD's associated with the URB.
2) Complete the URB successfully.
For control endpoints we do the following:
1) Clean up all the remaining TD's associated with the URB, except
the status TD.
When we are done the endpoint should be ready to go again. The
caller must clear the halted bit though.
Arguments:
DeviceExtension - pointer to device extension for our HC.
TD - Transfer Descriptor that completed.
Return Value:
None
--*/
{
PURB urb = TD->Urb;
POHCD_ENDPOINT endpoint = TD->Endpoint;
BOOLEAN errorCondition = TRUE;
BOOLEAN isLastTD;
ULONG tempTDPhysical;
POHCD_TRANSFER_DESCRIPTOR tempTD;
ASSERT(OHCI_CC_NO_ERROR != TD->HcTransferDescriptor.ConditionCode);
//
// DataUnderrun when USBD_SHORT_TRANSFER_OK, is not an error
// mark it as success, and record the bytes that were transfered
//
if(
(OHCI_CC_DATA_UNDERRUN == TD->HcTransferDescriptor.ConditionCode) &&
(urb->CommonTransfer.ShortTransferOK)
)
{
if(TD->HcTransferDescriptor.CurrentBufferPointer)
{
//
// We do everything signed to account for page wrap
//
LONG currentBufferPointerOffset = (LONG)BYTE_OFFSET(TD->HcTransferDescriptor.CurrentBufferPointer);
LONG endBufferPointerOffset = (LONG)BYTE_OFFSET(TD->HcTransferDescriptor.BufferEnd);
LONG bytesTransfered;
if(endBufferPointerOffset >= currentBufferPointerOffset)
{
bytesTransfered = TD->Bytes - (endBufferPointerOffset - currentBufferPointerOffset);
} else
{
bytesTransfered = TD->Bytes - (PAGE_SIZE - (currentBufferPointerOffset - endBufferPointerOffset));
}
bytesTransfered--;
ASSERT(bytesTransfered >= 0);
urb->CommonTransfer.TransferBufferLength += bytesTransfered;
} else
{
urb->CommonTransfer.TransferBufferLength += TD->Bytes;
}
USB_DBG_TRACE_PRINT(("OHCI_CC_DATA_UNDERRUN with ShortTransferOK"));
urb->Header.Status = USBD_STATUS_SUCCESS;
errorCondition = FALSE;
}
else
//
// It really is an error, convert the condition code to a USBD_STATUS_XXXX code.
//
{
if(OHCI_CC_NOT_ACCESSED2 == TD->HcTransferDescriptor.ConditionCode)
{
//
// TDs are initialized with OHCD_CC_DATA_NOT_ACCESSED. If the condition
// code is OHCD_CC_DATA_NOT_ACCESSED2, it is because the OHCD_ProcessCancelPendingList
// called OHCD_ProcessDoneTD to finish off its dirty work.
//
urb->Header.Status = USBD_STATUS_CANCELED;
}
urb->Header.Status = OHCI_CC_TO_USBD_STATUS(TD->HcTransferDescriptor.ConditionCode);
}
tempTD = TD;
//
// tempTDPhysical will be kept one TD ahead of tempTD
// on the queue. This is because, we need to get the
// first one from the endpoints programmed queue (it hasn't
// failed yet, so it was not in the done queue). Subsequent
// tempTDPhysical values are obtained in the loop, by walking the
// queue.
//
tempTDPhysical = READ_HEADP(&endpoint->HcEndpointDescriptor);
//
// Loop until all the TDs of the failed URB are freed.
//
USB_DBG_TRACE_PRINT(("Freeing TDs from failed Transfer:"));
do
{
//
// Before freeing the TD, record whether or
// not it is the last one in the URB.
//
isLastTD = tempTD->Flags & OHCD_TD_FLAG_LAST_TD;
//
// Free the TD
//
USB_DBG_TRACE_PRINT(("0x%0.8x", tempTD));
OHCD_PoolFreeTD(tempTD);
//
// Credit the proper quota
//
OHCD_fCreditTDQuota(DeviceExtension, endpoint);
//
// update tempTD for the next iteration.
//
tempTD = OHCD_PoolTDFromPhysicalAddress(tempTDPhysical);
tempTDPhysical = tempTD->HcTransferDescriptor.NextTD;
//
// If tempTD is a OHCD_TD_TYPE_STATUS and we
// did not really fail, than we should break out
// before freeing it.
//
if( (OHCD_TD_TYPE_STATUS == tempTD->Type) && !errorCondition)
{
USB_DBG_TRACE_PRINT(("Leaving Status TD (0x%0.8x) on ED", tempTD));
break;
}
} while(!isLastTD);
//
// Update the Queue to reflect that we freed all of them TDs
// (remember that tempTDPhysical was one TD ahead of the last
// one we didn't free)
tempTDPhysical = tempTD->PhysicalAddress;
WRITE_HEADP(&endpoint->HcEndpointDescriptor, tempTDPhysical);
//
// If we cleaned up a whole URB (i.e. it was not
// a short control transfer) then complete the Urb.
//
if(isLastTD)
{
OHCD_fCompleteRequest(DeviceExtension, endpoint, urb);
}
//
// For control endpoints and short transfers, automatically clear the
// halted condition.
//
if(USB_ENDPOINT_TYPE_CONTROL == endpoint->EndpointType || !errorCondition)
{
CLEAR_HALTED(&endpoint->HcEndpointDescriptor);
}
}
VOID
FASTCALL
OHCD_fCompleteRequest(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN POHCD_ENDPOINT Endpoint,
IN PURB Urb
)
/*++
Routine Description:
Helper method for OHCD_fProcessDoneTD and OHCD_fProcessFailedTD.
It just pulls some duplicate code out, and puts it in one place.
Actions Performed:
1) Clean up Urb out of endpoint.
2) If the Urb is pending cancel, do maintenance
on the pending cancel list and endpoint.
3) Mark Urb Completed.
4) Complete URb.
It does not set the Urb status, the caller does that.
--*/
{
Endpoint->ProgrammedUrbCount--;
//
// Unlock buffer pages if needed
//
if(Urb->CommonTransfer.TransferBuffer)
{
MmLockUnlockBufferPages(Urb->CommonTransfer.TransferBuffer, Urb->CommonTransfer.Hca.HcdOriginalLength, TRUE);
#ifdef DVTSNOOPBUG
OHCD_PoolEndDoubleBufferTransfer(Urb);
#endif
}
//
// If OHCD_URB_FLAG_CANCELED is set, it may mean
// that the URB is sitting in our device extension's
// pending cancel list. If so we have some work to do.
//
if(OHCD_URB_FLAG_CANCELED&Urb->CommonTransfer.Hca.HcdUrbFlags)
{
//
// Search the pending cancel list for the Urb.
//
PURB urbPrev=NULL;
PURB urbCur=DeviceExtension->PendingCancels;
while(urbCur && Urb!=urbCur)
{
urbPrev = urbCur;
urbCur = urbPrev->CommonTransfer.Hca.HcdUrbLink;
}
//
// If we find it, better clean up.
//
if(urbCur)
{
ASSERT(urbCur==Urb);
//
// Splice the Urb out of the pending cancel list.
//
if(!urbPrev)
{
DeviceExtension->PendingCancels = urbCur->CommonTransfer.Hca.HcdUrbLink;
} else
{
urbPrev->CommonTransfer.Hca.HcdUrbLink = urbCur->CommonTransfer.Hca.HcdUrbLink;
}
urbCur->CommonTransfer.Hca.HcdUrbLink = NULL;
//
// Handle the endpoint pause associated with the Urb pending cancel.
//
if( 0 == --Endpoint->PendingPauseCount)
{
Endpoint->Flags &= ~OHCD_ENDPOINT_FLAG_PAUSING;
Endpoint->HcEndpointDescriptor.Control.Skip = 0;
}
} else
//
// There is only one legitimate reason that the Urb would
// not be in the pending cancel list. That is if
// OHCD_fProcessCancelPendingList is in our call stack. If that
// is the case Urb->Header.Status will already be set to
// USBD_STATUS_CANCELED.
//
{
ASSERT(USBD_STATUS_CANCELED == Urb->Header.Status);
}
}
//
// Complete the request.
//
Urb->CommonTransfer.Hca.HcdUrbFlags |= OHCD_URB_FLAG_COMPLETED;
USBD_CompleteRequest(Urb);
}
VOID
FASTCALL
OHCD_fCreditTDQuota(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN POHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Called whenever a TD is retired. It credits the proper
quota (control, bulk or interrupt), and jogs the appropriate
queue that may be waiting to fit under the quota.
DeviceExtension - pointer to device extension for our HC.
Endpoint - endpoint the TD was associated with.
--*/
{
switch(Endpoint->EndpointType)
{
case USB_ENDPOINT_TYPE_CONTROL:
OHCD_PoolCreditControlTDQuota(1);
OHCD_fProgramControlTransfer(DeviceExtension);
break;
case USB_ENDPOINT_TYPE_BULK:
OHCD_PoolCreditBulkTDQuota(1);
OHCD_fProgramBulkTransfer(DeviceExtension);
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
Endpoint->TDInUseCount--;
OHCD_fProgramInterruptTransfer(DeviceExtension, Endpoint);
break;
default:
//ISOCH endpoint never expected here.
USB_DBG_ERROR_PRINT(("Unrecognized endpoint type in OHCD_ProcessDoneTD"));
//This is a serious error, please dump the stack, bug it, and reboot.
}
}
BOOLEAN
FASTCALL
OHCD_fProcessCancelPendingList(
POHCD_DEVICE_EXTENSION DeviceExtension,
ULONG CurrentFrame
)
/*++
Routine Description:
Called from the IsrDpc to cleanup any pending cancels.
The DPC has to do it, because the endpoint must be paused.
Arguments:
DeviceExtension - pointer to device extension for our HC.
Return Value:
None
--*/
{
PURB stillPending = NULL;
PURB urb;
POHCD_ENDPOINT endpoint;
ASSERT_DISPATCH_LEVEL();
//
// Pop the first URB in each request pending cancel off
// the Pending Cancel queue and deal with it. Note that
// we have an inner loop to deal with URBs of the same request.
//
while(DeviceExtension->PendingCancels)
{
//
// pop an urb off the pending cancels queue
//
urb = DeviceExtension->PendingCancels;
DeviceExtension->PendingCancels = urb->CommonTransfer.Hca.HcdUrbLink;
//
// Get the endpoint for the urb
//
endpoint = (POHCD_ENDPOINT)urb->CommonTransfer.EndpointHandle;
//
// If the endpoint is not paused yet, hold the urb aside on
// the stillPending list.
//
if(CurrentFrame < endpoint->PauseFrame)
{
urb->CommonTransfer.Hca.HcdUrbLink = stillPending;
stillPending = urb;
} else if(endpoint->Flags&OHCD_ENDPOINT_FLAG_DELAY_PAUSE)
{
// Change the pause frame so that OHCD_fProcessAbortPendingList
// and OHCD_fProcessClosePendingList won't process anything for
// this endpoint until the next interrupt.
endpoint->PauseFrame = CurrentFrame+1;
// Clear the delay pause flag.
endpoint->Flags &= ~OHCD_ENDPOINT_FLAG_DELAY_PAUSE;
// Put the Urb on the still pending list
urb->CommonTransfer.Hca.HcdUrbLink = stillPending;
stillPending = urb;
} else
{
//
// The endpoint is paused, we need to cleanup all the TDs associated with
// the Urb and complete it. We can leverage the OHCD_ProcessDoneTD routine,
// here's how:
//
// 1) Find our URB on the endpoints queue.
// 2) Save physical pointers to the original first TD, and the
// last TD prior to the first TD of our URB.
// 3) Set the head of the queue to point the second TD of our URB.
// 4) Set the condition code of the first TD is set to
// OHCI_CC_NOT_ACCESSED2.
// 5) Call OHCD_ProcessDoneTD.
// 6) Restore the portion of the queue prior to our URB
//
ULONG firstTDPhysical;
ULONG priorTDPhysical;
ULONG currentTDPhysical;
POHCD_TRANSFER_DESCRIPTOR currentTD;
//
// ASSERT that the Urb is not yet completed.
//
ASSERT(!(urb->CommonTransfer.Hca.HcdUrbFlags & OHCD_URB_FLAG_COMPLETED));
//
// Loop until we find the first TD of our URB, or until we found the end
// of the queue.
// Steps 1) and 2)
//
priorTDPhysical = 0;
firstTDPhysical =
currentTDPhysical = READ_HEADP(&endpoint->HcEndpointDescriptor);
currentTD = OHCD_PoolTDFromPhysicalAddress(currentTDPhysical);
while(
(urb != currentTD->Urb) &&
(currentTDPhysical != endpoint->HcEndpointDescriptor.TailP)
)
{
priorTDPhysical = currentTDPhysical;
currentTDPhysical = currentTD->HcTransferDescriptor.NextTD;
currentTD = OHCD_PoolTDFromPhysicalAddress(currentTDPhysical);
}
//
// ASSERT that we found the Urb. Basically, we should
// always find it. If we didn't, it got lost, and that
// would be very bad.
//
ASSERT(urb == currentTD->Urb);
//
// 3) Adjusting queue for OHCD_ProcessDoneTD
//
WRITE_HEADP(&endpoint->HcEndpointDescriptor, currentTD->HcTransferDescriptor.NextTD);
//
// 4) Verify that the condition code of the first TD is set to
// OHCI_CC_NOT_ACCESSED2.
currentTD->HcTransferDescriptor.ConditionCode = OHCI_CC_NOT_ACCESSED2;
//
// 5) Call OHCD_ProcessDoneTD.
//
OHCD_fProcessDoneTD(DeviceExtension, currentTD);
//
// 6) Restore endpoint's queue
//
if(0 != priorTDPhysical)
{
currentTD = OHCD_PoolTDFromPhysicalAddress(priorTDPhysical);
currentTD->HcTransferDescriptor.NextTD = READ_HEADP(&endpoint->HcEndpointDescriptor);
WRITE_HEADP(&endpoint->HcEndpointDescriptor, firstTDPhysical);
}
//
// Decrement pending pause count, and restart the endpoint
// if it is time.
//
if( 0 == --endpoint->PendingPauseCount)
{
endpoint->Flags &= ~OHCD_ENDPOINT_FLAG_PAUSING;
endpoint->HcEndpointDescriptor.Control.Skip = 0;
}
}
}//Loop over all URBs pending cancel
//
// Put all the cancels we passed over back on the
// pending list.
//
DeviceExtension->PendingCancels = stillPending;
return stillPending ? TRUE : FALSE;
}
BOOLEAN
FASTCALL
OHCD_fProcessAbortPendingList(
POHCD_DEVICE_EXTENSION DeviceExtension,
ULONG CurrentFrame
)
/*++
Routine Description:
Called from the IsrDpc to cleanup any pending aborts.
The DPC has to do it, because the endpoint must be paused.
Arguments:
DeviceExtension - pointer to device extension for our HC.
Return Value:
None
--*/
{
PURB stillPending = NULL;
PURB urb;
POHCD_ENDPOINT endpoint;
ASSERT_DISPATCH_LEVEL();
//
// Pop the first URB in each request pending cancel off
// the Pending Cancel queue and deal with it. Note that
// we have an inner loop to deal with URBs of the same request.
//
while(DeviceExtension->PendingAborts)
{
//
// pop an urb off the pending aborts queue
//
urb = DeviceExtension->PendingAborts;
DeviceExtension->PendingAborts = urb->CommonTransfer.Hca.HcdUrbLink;
//
// Get the endpoint for the urb
//
endpoint = (POHCD_ENDPOINT)urb->CommonTransfer.EndpointHandle;
//
// If the endpoint is not paused yet, hold the urb aside on
// the stillPending list.
if(CurrentFrame < endpoint->PauseFrame)
{
urb->AbortEndpoint.HcdNextAbort = stillPending;
stillPending = urb;
} else if(endpoint->Flags&OHCD_ENDPOINT_FLAG_DELAY_PAUSE)
{
// Change the pause frame so that OHCD_fProcessClosePendingList won't
// process anything for this endpoint until the next interrupt.
endpoint->PauseFrame = CurrentFrame+1;
// Clear the delay pause flag.
endpoint->Flags &= ~OHCD_ENDPOINT_FLAG_DELAY_PAUSE;
// Put the Urb on the still pending list
urb->AbortEndpoint.HcdNextAbort = stillPending;
stillPending = urb;
} else
//
// The endpoint is paused.
//
{
OHCD_fCleanEndpointTDs(DeviceExtension, endpoint);
//
// Decrement PendingPauseCount, and restart endpoint if necessary.
//
if(0 == --endpoint->PendingPauseCount)
{
endpoint->Flags &= ~OHCD_ENDPOINT_FLAG_PAUSING;
endpoint->HcEndpointDescriptor.Control.Skip = 0;
}
//
// Complete the Abort URB
//
urb->Header.Status = USBD_STATUS_SUCCESS;
USBD_CompleteRequest(urb);
}
}//Loop over all URBs pending cancel
//
// Put all the cancels we passed over back on the
// pending list.
//
DeviceExtension->PendingAborts = stillPending;
return stillPending ? TRUE : FALSE;
}
BOOLEAN
FASTCALL
OHCD_fProcessClosePendingList(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
ULONG CurrentFrame
)
{
PURB_CLOSE_ENDPOINT urb;
PURB_CLOSE_ENDPOINT notClosedYet = NULL;
POHCD_ENDPOINT endpoint;
while(DeviceExtension->PendingCloses)
{
//
// Remove an endpoint from the queue.
//
urb = &DeviceExtension->PendingCloses->CloseEndpoint;
DeviceExtension->PendingCloses = urb->HcdNextClose;
//
// Retrieve the endpoint from the URB
//
endpoint = (POHCD_ENDPOINT)urb->EndpointHandle;
//
// If the endpoint is not paused yet, hold the endpoint aside on
// the notClosedYet list.
//
if(CurrentFrame < endpoint->PauseFrame)
{
urb->HcdNextClose = (PURB)notClosedYet;
notClosedYet = urb;
} else if(endpoint->Flags&OHCD_ENDPOINT_FLAG_DELAY_PAUSE)
{
// Clear the delay pause flag.
endpoint->Flags &= ~OHCD_ENDPOINT_FLAG_DELAY_PAUSE;
// Put the Urb on the still pending list
urb->HcdNextClose = (PURB)notClosedYet;
notClosedYet = urb;
}
//
// Otherwise, cleanup the endpoint and complete the URB
//
else
{
if(OHCD_IS_ISOCH_CLOSE(urb))
{
OHCD_ISOCH_COMPLETE_CLOSE_ENDPOINT(DeviceExtension, urb);
} else
{
//
// Cleanup any straggling TDS, canceling any pending transfers
// in the process.
//
OHCD_fCleanEndpointTDs(DeviceExtension, endpoint);
//
// Serious programming error if we haven't cleaned everything.
//
ASSERT(0==endpoint->QueuedUrbCount);
ASSERT(0==endpoint->ProgrammedUrbCount);
ASSERT(0==--endpoint->PendingPauseCount);
//
// Update the data toggle bits in the close URB
//
if(urb->DataToggleBits)
{
ULONG dataToggleMask = 1 << endpoint->HcEndpointDescriptor.Control.EndpointAddress;
if(endpoint->HcEndpointDescriptor.Control.Direction == 2)
{
dataToggleMask <<= 16;
}
if(GET_TOGGLECARRY(&endpoint->HcEndpointDescriptor))
{
*urb->DataToggleBits |= dataToggleMask;
} else
{
*urb->DataToggleBits &= ~dataToggleMask;
}
}
OHCD_PoolFreeEndpoint(endpoint);
urb->Hdr.Status = USBD_STATUS_SUCCESS;
USBD_CompleteRequest((PURB)urb);
}
}
}
//
// Place the endpoints that weren't closed yet, back on
// the PendingCloses list. So what if we reversed the order.
//
DeviceExtension->PendingCloses = (PURB)notClosedYet;
return notClosedYet ? TRUE : FALSE;
}
VOID
FASTCALL
OHCD_fCleanEndpointTDs(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN POHCD_ENDPOINT Endpoint
)
{
ULONG TDPhysical;
POHCD_TRANSFER_DESCRIPTOR TD = NULL;
BOOLEAN done = FALSE;
//
// 1) Pull the TDs off one at a time.
// 2) Make sure that the ConditionCode is OHCI_CC_NOT_ACCESSED2.
// 3) Call OHCD_ProcessDoneTD, which will call OHCD_ProcessFailedTD,
// which will:
// a) Clean all the TDs associated with the same URB.
// b) Complete the URB as USBD_STATUS_CANCELED
// 4) Clean up the dummy TD when we get to it.
//
while(!done)
{
TDPhysical = READ_HEADP(&Endpoint->HcEndpointDescriptor);
if(0 == TDPhysical) break;
TD = OHCD_PoolTDFromPhysicalAddress(TDPhysical);
if(TDPhysical != Endpoint->HcEndpointDescriptor.TailP)
{
ASSERT(OHCD_TD_TYPE_DUMMY != TD->Type);
TD->HcTransferDescriptor.ConditionCode = OHCI_CC_NOT_ACCESSED2;
WRITE_HEADP(&Endpoint->HcEndpointDescriptor, TD->HcTransferDescriptor.NextTD);
OHCD_fProcessDoneTD(DeviceExtension, TD);
}else
{
ASSERT(OHCD_TD_TYPE_DUMMY == TD->Type);
WRITE_HEADP(&Endpoint->HcEndpointDescriptor, 0);
Endpoint->HcEndpointDescriptor.TailP = 0;
OHCD_PoolFreeTD(TD);
done = TRUE;
}
}
}