AppleUSBEHCI_Interrupts.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include "AppleUSBEHCI.h"
#define super IOUSBControllerV2
#define self this
void
AppleUSBEHCI::PollInterrupts(IOUSBCompletionAction safeAction)
{
if (_hostErrorInterrupt & kEHCIHostErrorIntBit)
{
_hostErrorInterrupt = 0;
if ( ++_errors.hostSystemError == (UInt32) (1L << _errors.displayed) )
{
USBLog(1, "%s[%p]::PollInterrupts - Host System Error Occurred - not restarted", getName(), 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 = CreateRootHubDevice( _device, &_rootHubDevice );
if ( err != kIOReturnSuccess )
{
USBError(1,"%s[%p] Could not create root hub device upon wakeup (%x)!",getName(), this, err);
}
else
{
_rootHubDevice->registerService(kIOServiceRequired | kIOServiceSynchronous);
}
}
}
#endif
}
if (_errorInterrupt & kEHCIErrorIntBit)
{
_errorInterrupt = 0;
USBLog(7, "%s[%p]::PollInterrupts - completion (_errorInterrupt) interrupt", getName(), this);
scavengeCompletedTransactions(safeAction);
}
if (_completeInterrupt & kEHCICompleteIntBit)
{
_completeInterrupt = 0;
USBLog(7, "%s[%p]::PollInterrupts - completion (_completeInterrupt) interrupt", getName(), this);
scavengeCompletedTransactions(safeAction);
}
if (_portChangeInterrupt & kEHCIPortChangeIntBit)
{
_portChangeInterrupt = 0;
_remote_wakeup_occurred = true;
USBLog(3, "%s[%p]::PollInterrupts - port change detect interrupt", getName(), this);
if ( _idleSuspend )
{
USBLog(1, "%s[%p]::PollInterrupts - port change detect interrupt while in idlesuspend - restarting bus", getName(), this);
setPowerState(kEHCISetPowerLevelRunning, self);
}
UIMRootHubStatusChange();
}
if (_asyncAdvanceInterrupt & kEHCIAAEIntBit)
{
_asyncAdvanceInterrupt = 0;
_errors.ownershipChange++;
_pEHCIRegisters->USBSTS = USBToHostLong(kEHCIAAEIntBit);
USBLog(3, "%s[%p]::PollInterrupts - async advance interrupt", getName(), this);
}
}
void
AppleUSBEHCI::InterruptHandler(OSObject *owner, IOInterruptEventSource * , int )
{
register AppleUSBEHCI *controller = (AppleUSBEHCI *) owner;
static Boolean emitted;
if (!controller || controller->isInactive() || (controller->_onCardBus && controller->_pcCardEjected) || !controller->_ehciAvailable)
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->_ehciAvailable)
return false;
if (controller->_ehciBusState == kEHCIBusStateSuspended)
{
return true;
}
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;
frindex = USBToHostLong(_pEHCIRegisters->FRIndex);
if (frindex < kEHCIFRIndexRolloverBit)
_frameNumber += kEHCIFrameNumberIncrement;
_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))
{
AppleEHCIIsochListElement * cachedHead;
UInt32 cachedProducer;
UInt32 frIndex;
UInt16 curSlot, testSlot, nextSlot;
UInt16 curMicroFrame;
frIndex = USBToHostLong(_pEHCIRegisters->FRIndex);
curSlot = (frIndex >> 3) & (kEHCIPeriodicListEntries-1);
curMicroFrame = frIndex & 7;
cachedHead = (AppleEHCIIsochListElement*)_savedDoneQueueHead;
cachedProducer = _producerCount;
testSlot = _outSlot;
while (testSlot != curSlot)
{
AppleEHCIListElement *thing, *prevThing, *nextThing;
AppleEHCIIsochListElement *isochEl;
AppleEHCISplitIsochTransferDescriptor *splitTD;
bool needToRescavenge = false;
nextSlot = (testSlot+1) & (kEHCIPeriodicListEntries-1);
thing = _logicalPeriodicList[testSlot];
prevThing = NULL;
while(thing != NULL)
{
nextThing = thing->_logicalNext;
isochEl = OSDynamicCast(AppleEHCIIsochListElement, thing);
if (!isochEl)
break;
splitTD = OSDynamicCast(AppleEHCISplitIsochTransferDescriptor, isochEl);
if (splitTD && (splitTD->_pEndpoint->useBackPtr) && (nextSlot == curSlot) && (curMicroFrame < 2))
{
prevThing = thing;
thing = nextThing;
needToRescavenge = true;
continue;
}
if (!prevThing)
{
_logicalPeriodicList[testSlot] = nextThing;
_periodicList[testSlot] = 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++;
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;
}