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"
#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)
{
if (_hostErrorInterrupt & kEHCIHostErrorIntBit)
{
_hostErrorInterrupt = 0;
if ( ++_errors.hostSystemError == (UInt32) (1L << _errors.displayed) )
{
USBLog(1, "AppleUSBEHCI[%p]::PollInterrupts - Host System Error Occurred - not restarted", this);
_errors.displayed++;
}
#if 0
if ( !_onCardBus )
{
if ( _rootHubDevice )
{
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
}
SuspendUSBBus();
UIMFinalizeForPowerDown();
_ehciAvailable = false; IOSleep(100);
UIMInitializeForPowerUp();
_ehciBusState = kEHCIBusStateRunning;
_ehciAvailable = true; if ( _rootHubDevice == NULL )
{
IOReturn err = super::CreateRootHubDevice( _device, &_rootHubDevice );
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBEHCI[%p] Could not create root hub device upon wakeup (%x)!", this, err);
}
}
}
#endif
}
if (_errorInterrupt & kEHCIErrorIntBit)
{
_errorInterrupt = 0;
USBLog(7, "AppleUSBEHCI[%p]::PollInterrupts - completion (_errorInterrupt) interrupt", this);
scavengeCompletedTransactions(safeAction);
}
if (_completeInterrupt & kEHCICompleteIntBit)
{
_completeInterrupt = 0;
USBLog(7, "AppleUSBEHCI[%p]::PollInterrupts - completion (_completeInterrupt) interrupt", this);
scavengeCompletedTransactions(safeAction);
}
if (_portChangeInterrupt & kEHCIPortChangeIntBit)
{
UInt32 port, numPorts;
_portChangeInterrupt = 0;
USBLog(6,"AppleUSBEHCI[%p]::PollInterrupts - Port Change Interrupt on bus %ld - ensuring usability", this, _busNumber );
EnsureUsability();
numPorts = USBToHostLong(_pEHCICapRegisters->HCSParams) & kEHCINumPortsMask;
for (port = 0; port < numPorts; port++)
{
if (!_rhPortBeingResumed[port])
{
UInt32 portStatus = USBToHostLong(_pEHCIRegisters->PortSC[port]);
if (portStatus & kEHCIPortSC_Resume)
{
USBLog(5, "AppleUSBEHCI[%p]::PollInterrupts - port %d appears to be resuming from a remote wakeup - spawning thread to resume", this, (int)port+1);
_rhPortBeingResumed[port] = true;
thread_call_enter1(_rhResumePortTimerThread[port], (void*)(port+1));
}
}
}
}
if (_asyncAdvanceInterrupt & kEHCIAAEIntBit)
{
_asyncAdvanceInterrupt = 0;
USBLog(6, "AppleUSBEHCI[%p]::PollInterrupts - async advance interrupt", this);
}
if (_frameRolloverInterrupt & kEHCIFrListRolloverIntBit)
{
_frameRolloverInterrupt = 0;
_anchorTime = _tempAnchorTime;
_anchorFrame = _tempAnchorFrame;
}
}
void
AppleUSBEHCI::InterruptHandler(OSObject *owner, IOInterruptEventSource * , int )
{
register AppleUSBEHCI *controller = (AppleUSBEHCI *) owner;
static Boolean emitted;
if (!controller || controller->isInactive() || (controller->_onCardBus && controller->_pcCardEjected) || !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->_onCardBus && controller->_pcCardEjected) || !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;
AbsoluteTime timeStamp;
enabledInterrupts = USBToHostLong(_pEHCIRegisters->USBIntr);
activeInterrupts = enabledInterrupts & USBToHostLong(_pEHCIRegisters->USBSTS);
if (activeInterrupts != 0)
{
if (activeInterrupts & kEHCIFrListRolloverIntBit)
{
UInt32 frindex;
uint64_t tempTime;
frindex = USBToHostLong(_pEHCIRegisters->FRIndex);
if (frindex < kEHCIFRIndexRolloverBit)
_frameNumber += kEHCIFrameNumberIncrement;
_tempAnchorFrame = _frameNumber + (frindex >> 3);
clock_get_uptime(&_tempAnchorTime);
_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)
{
clock_get_uptime(&timeStamp);
_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 = _logicalPeriodicList[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)
{
_logicalPeriodicList[testSlot] = nextThing;
_periodicList[testSlot] = HostToUSBLong(thing->GetPhysicalLink());
}
else
{
prevThing->_logicalNext = nextThing;
prevThing->SetPhysicalLink(thing->GetPhysicalLink());
}
if (isochEl->_lowLatency)
isochEl->UpdateFrameList(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;
}