AppleUSBEHCI_Interrupts.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBHubPolicyMaker.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include "AppleUSBEHCI.h"
#include "USBTracepoints.h"
#define super IOUSBControllerV3
#define self this
#if EHCI_USE_KPRINTF
#undef USBLog
#undef USBError
void kprintf(const char *format, ...)
__attribute__((format(printf, 1, 2)));
#define USBLog( LEVEL, FORMAT, ARGS... ) if ((LEVEL) <= EHCI_USE_KPRINTF) { kprintf( FORMAT "\n", ## ARGS ) ; }
#define USBError( LEVEL, FORMAT, ARGS... ) { kprintf( FORMAT "\n", ## ARGS ) ; }
#endif
void
AppleUSBEHCI::PollInterrupts(IOUSBCompletionAction safeAction)
{
USBTrace_Start( kUSBTEHCIInterrupts, kTPEHCIInterruptsPollInterrupts, (uintptr_t)this, _errorInterrupt, _completeInterrupt, _portChangeInterrupt );
if (_hostErrorInterrupt & kEHCIHostErrorIntBit)
{
_hostErrorInterrupt = 0;
if ( ++_errors.hostSystemError == (UInt32) (1L << _errors.displayed) )
{
USBLog(1, "AppleUSBEHCI[%p]::PollInterrupts - Host System Error Occurred - not restarted", this);
USBTrace( kUSBTEHCIInterrupts, kTPEHCIInterruptsPollInterrupts , (uintptr_t)this, _errors.displayed, 0, 1 );
_errors.displayed++;
}
}
if (_errorInterrupt & kEHCIErrorIntBit)
{
_errorInterrupt = 0;
USBLog(7, "AppleUSBEHCI[%p]::PollInterrupts - completion (_errorInterrupt) interrupt", this);
USBTrace( kUSBTEHCIInterrupts, kTPEHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 4 );
scavengeCompletedTransactions(safeAction);
}
if (_completeInterrupt & kEHCICompleteIntBit)
{
_completeInterrupt = 0;
USBLog(7, "AppleUSBEHCI[%p]::PollInterrupts - completion (_completeInterrupt) interrupt", this);
USBTrace( kUSBTEHCIInterrupts, kTPEHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 3 );
scavengeCompletedTransactions(safeAction);
}
if (_portChangeInterrupt & kEHCIPortChangeIntBit)
{
_portChangeInterrupt = 0;
USBTrace( kUSBTEHCIInterrupts, kTPEHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 5 );
USBLog(6,"AppleUSBEHCI[%p]::PollInterrupts - Port Change Interrupt on bus 0x%x - ensuring usability", this, (uint32_t)_busNumber );
EnsureUsability();
if (_myPowerState == kUSBPowerStateOn)
{
RHCheckForPortResumes();
}
else
{
USBLog(2, "AppleUSBEHCI[%p]::PollInterrupts - deferring checking for RHStatus until we are running again", this);
}
}
if (_asyncAdvanceInterrupt & kEHCIAAEIntBit)
{
_asyncAdvanceInterrupt = 0;
USBTrace( kUSBTEHCIInterrupts, kTPEHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 6 );
USBLog(6, "AppleUSBEHCI[%p]::PollInterrupts - async advance interrupt", this);
}
if (_frameRolloverInterrupt & kEHCIFrListRolloverIntBit)
{
_frameRolloverInterrupt = 0;
_anchorTime = _tempAnchorTime;
_anchorFrame = _tempAnchorFrame;
USBTrace( kUSBTEHCIInterrupts, kTPEHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 7 );
}
USBTrace_End( kUSBTEHCIInterrupts, kTPEHCIInterruptsPollInterrupts, (uintptr_t)this, 0, 0, 0 );
}
void
AppleUSBEHCI::InterruptHandler(OSObject *owner, IOInterruptEventSource * , int )
{
register AppleUSBEHCI *controller = (AppleUSBEHCI *) owner;
static Boolean emitted;
if (!controller || controller->isInactive() || !controller->_controllerAvailable)
{
#if EHCI_USE_KPRINTF
kprintf("AppleUSBEHCI::InterruptHandler - Ignoring interrupt\n");
#endif
return;
}
if (!emitted)
{
emitted = true;
}
controller->PollInterrupts();
}
bool
AppleUSBEHCI::PrimaryInterruptFilter(OSObject *owner, IOFilterInterruptEventSource *source)
{
register AppleUSBEHCI *controller = (AppleUSBEHCI *)owner;
bool result = true;
if (!controller || controller->isInactive() || !controller->_controllerAvailable)
{
#if EHCI_USE_KPRINTF
kprintf("AppleUSBEHCI[%p]::PrimaryInterruptFilter - Ignoring interrupt\n", controller);
#endif
return false;
}
controller->_filterInterruptActive = true;
result = controller->FilterInterrupt(0);
controller->_filterInterruptActive = false;
return result;
}
bool
AppleUSBEHCI::FilterInterrupt(int index)
{
register UInt32 activeInterrupts;
register UInt32 enabledInterrupts;
Boolean needSignal = false;
uint64_t timeStamp;
enabledInterrupts = USBToHostLong(_pEHCIRegisters->USBIntr);
activeInterrupts = enabledInterrupts & USBToHostLong(_pEHCIRegisters->USBSTS);
if (activeInterrupts != 0)
{
USBTrace( kUSBTEHCIInterrupts, kTPEHCIInterruptsPrimaryInterruptFilter, (uintptr_t)this, enabledInterrupts, activeInterrupts, (_frameNumber << 3) + USBToHostLong(_pEHCIRegisters->FRIndex) );
if (activeInterrupts & kEHCIFrListRolloverIntBit)
{
UInt32 frindex;
uint64_t tempTime;
frindex = USBToHostLong(_pEHCIRegisters->FRIndex);
if (frindex < kEHCIFRIndexRolloverBit)
_frameNumber += kEHCIFrameNumberIncrement;
_tempAnchorFrame = _frameNumber + (frindex >> 3);
tempTime = mach_absolute_time();
_tempAnchorTime = *(AbsoluteTime*)&tempTime;
_frameRolloverInterrupt = kEHCIFrListRolloverIntBit;
_pEHCIRegisters->USBSTS = HostToUSBLong(kEHCIFrListRolloverIntBit); IOSync();
}
if (activeInterrupts & kEHCIAAEIntBit)
{
_asyncAdvanceInterrupt = kEHCIAAEIntBit;
_pEHCIRegisters->USBSTS = HostToUSBLong(kEHCIAAEIntBit); IOSync();
needSignal = true;
}
if (activeInterrupts & kEHCIHostErrorIntBit)
{
_hostErrorInterrupt = kEHCIHostErrorIntBit;
_pEHCIRegisters->USBSTS = HostToUSBLong(kEHCIHostErrorIntBit); IOSync();
needSignal = true;
}
if (activeInterrupts & kEHCIPortChangeIntBit)
{
_portChangeInterrupt = kEHCIPortChangeIntBit;
_pEHCIRegisters->USBSTS = HostToUSBLong(kEHCIPortChangeIntBit); IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pEHCIRegisters->USBSTS); while ((count++ < 10) && (newValue & kEHCIPortChangeIntBit))
{
_pEHCIRegisters->USBSTS = HostToUSBLong(kEHCIPortChangeIntBit); IOSync();
newValue = USBToHostLong(_pEHCIRegisters->USBSTS);
}
}
needSignal = true;
}
if (activeInterrupts & kEHCIErrorIntBit)
{
_errorInterrupt = kEHCIErrorIntBit;
_pEHCIRegisters->USBSTS = HostToUSBLong(kEHCIErrorIntBit); IOSync();
needSignal = true;
}
if (activeInterrupts & kEHCICompleteIntBit)
{
timeStamp = mach_absolute_time();
_completeInterrupt = kEHCICompleteIntBit;
_pEHCIRegisters->USBSTS = HostToUSBLong(kEHCICompleteIntBit); IOSync();
needSignal = true;
if (!_inAbortIsochEP && (_pEHCIRegisters->USBCMD & HostToUSBLong(kEHCICMDPeriodicEnable)) && (_outSlot < kEHCIPeriodicListEntries))
{
IOUSBControllerIsochListElement *cachedHead;
UInt32 cachedProducer;
UInt32 frIndex;
UInt16 curSlot, testSlot, nextSlot;
UInt16 curMicroFrame;
frIndex = USBToHostLong(_pEHCIRegisters->FRIndex);
curSlot = (frIndex >> 3) & (kEHCIPeriodicListEntries-1);
curMicroFrame = frIndex & 7;
cachedHead = (IOUSBControllerIsochListElement*)_savedDoneQueueHead;
cachedProducer = _producerCount;
testSlot = _outSlot;
while (testSlot != curSlot)
{
IOUSBControllerListElement *thing, *prevThing, *nextThing;
IOUSBControllerIsochListElement *isochEl;
AppleEHCISplitIsochTransferDescriptor *splitTD;
bool needToRescavenge = false;
nextSlot = (testSlot+1) & (kEHCIPeriodicListEntries-1);
thing = GetPeriodicListLogicalEntry(testSlot);
prevThing = NULL;
while(thing != NULL)
{
nextThing = thing->_logicalNext;
isochEl = OSDynamicCast(IOUSBControllerIsochListElement, thing);
if (!isochEl)
break;
splitTD = OSDynamicCast(AppleEHCISplitIsochTransferDescriptor, isochEl);
if (splitTD && (((AppleEHCIIsochEndpoint*)(splitTD->_pEndpoint))->useBackPtr) && (nextSlot == curSlot) && (curMicroFrame < 2))
{
prevThing = thing;
thing = nextThing;
needToRescavenge = true;
continue;
}
if (!prevThing)
{
SetPeriodicListEntry(testSlot, nextThing);
}
else
{
prevThing->_logicalNext = nextThing;
prevThing->SetPhysicalLink(thing->GetPhysicalLink());
}
if (isochEl->_lowLatency)
isochEl->UpdateFrameList(*(AbsoluteTime*)&timeStamp);
isochEl->_doneQueueLink = cachedHead;
cachedHead = isochEl;
cachedProducer++;
if (isochEl->_pEndpoint)
{
isochEl->_pEndpoint->onProducerQ++;
OSDecrementAtomic( &(isochEl->_pEndpoint->scheduledTDs));
}
thing = nextThing;
}
testSlot = nextSlot;
if (!needToRescavenge)
_outSlot = testSlot;
}
IOSimpleLockLock( _wdhLock );
_savedDoneQueueHead = cachedHead; _producerCount = cachedProducer;
IOSimpleLockUnlock( _wdhLock );
}
}
}
if (needSignal)
_filterInterruptSource->signalInterrupt();
return false;
}