AppleUSBOHCI_Interrupts.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOService.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBHubPolicyMaker.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include "AppleUSBOHCIMemoryBlocks.h"
#include "AppleUSBOHCI.h"
#include "USBTracepoints.h"
#define nil (0)
#define DEBUGGING_LEVEL 0 // 1 = low; 2 = high; 3 = extreme
#define super IOUSBControllerV3
#define self this
void AppleUSBOHCI::PollInterrupts(IOUSBCompletionAction safeAction)
{
UInt64 timeElapsed;
AbsoluteTime timeStop;
IOReturn err = kIOReturnSuccess;
USBTrace_Start( kUSBTOHCIInterrupts, kTPOHCIInterruptsPollInterrupts, (uintptr_t)this, 0, 0, 0 );
if (_writeDoneHeadInterrupt & kOHCIHcInterrupt_WDH)
{
_writeDoneHeadInterrupt = 0;
USBTrace( kUSBTOHCIInterrupts, kTPOHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 1 );
UIMProcessDoneQueue(safeAction);
}
if (_resumeDetectedInterrupt & kOHCIHcInterrupt_RD)
{
_resumeDetectedInterrupt = 0;
USBTrace( kUSBTOHCIInterrupts, kTPOHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 2 );
_remote_wakeup_occurred = true;
USBLog(3,"AppleUSBOHCI[%p]::PollInterrupts - ResumeDetected Interrupt on bus %d - ensuring usability", this, (uint32_t)_busNumber );
EnsureUsability();
}
if (_unrecoverableErrorInterrupt & kOHCIHcInterrupt_UE)
{
USBError(1,"USB Controller on bus %d received an unrecoverable error interrupt. Attempting to fix (%d)", (uint32_t) _busNumber, isInactive() );
_unrecoverableErrorInterrupt = 0;
_errors.unrecoverableError++;
USBTrace( kUSBTOHCIInterrupts, kTPOHCIInterruptsPollInterrupts , (uintptr_t)this, (uintptr_t)_errors.unrecoverableError, 0, 3 );
if ( !(_errataBits & kErrataNECOHCIIsochWraparound ) )
{
USBLog(2,"AppleUSBOHCI[%p]::PollInterrupts - setting kOHCIHcCommandStatus_HCR to reset controller on bus %d", this, (uint32_t)_busNumber );
_pOHCIRegisters->hcCommandStatus = HostToUSBLong(kOHCIHcCommandStatus_HCR);
IODelay(10); _pOHCIRegisters->hcControl = HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase) | kOHCIHcControl_PLE);
}
else
{
USBLog(2,"AppleUSBOHCI[%p]::PollInterrupts - ignoring unrecoverable error on bus %d", this, (uint32_t)_busNumber );
}
}
if (_rootHubStatusChangeInterrupt & kOHCIHcInterrupt_RHSC)
{
_rootHubStatusChangeInterrupt = 0;
_remote_wakeup_occurred = true;
USBTrace( kUSBTOHCIInterrupts, kTPOHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 4 );
if ( _myPowerState == kUSBPowerStateLowPower )
{
USBLog(5,"AppleUSBOHCI[%p]::PollInterrupts - RootHubStatusChange Interrupt on bus %d while in lowPower -- setting _rootHubStatuschangedInterruptReceived and ensuring usability", this, (uint32_t)_busNumber );
_rootHubStatuschangedInterruptReceived = true;
}
else
{
USBLog(5,"AppleUSBOHCI[%p]::PollInterrupts - RootHubStatusChange Interrupt on bus %d - ensuring usability", this, (uint32_t)_busNumber );
}
EnsureUsability();
_needToReEnableRHSCInterrupt = false;
for (int port = 1; port <= _rootHubNumPorts; port++)
{
if ((USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port-1]) & kOHCIHcRhPortStatus_Change) != 0)
{
_needToReEnableRHSCInterrupt = true;
break;
}
}
if (!_needToReEnableRHSCInterrupt)
{
UInt32 interrupts;
USBLog(5,"AppleUSBOHCI[%p]::PollInterrupts - RootHubStatusChange Spurious interrupt, re-enabling RHSC", this );
_pOHCIRegisters->hcInterruptEnable = HostToUSBLong (kOHCIHcInterrupt_MIE | kOHCIHcInterrupt_RHSC);
IOSync();
}
}
if (_frameNumberOverflowInterrupt & kOHCIHcInterrupt_FNO)
{
_frameNumberOverflowInterrupt = 0;
_anchorTime = _tempAnchorTime;
_anchorFrame = _tempAnchorFrame;
USBTrace( kUSBTOHCIInterrupts, kTPOHCIInterruptsPollInterrupts , (uintptr_t)this, 0, 0, 5 );
USBLog(5, "AppleUSBOHCI[%p]::PollInterrupts - frame rollover interrupt frame (0x08%qx)", this, _anchorFrame);
}
USBTrace_End( kUSBTOHCIInterrupts, kTPOHCIInterruptsPollInterrupts, (uintptr_t)this, 0, 0, 0 );
}
void
AppleUSBOHCI::InterruptHandler(OSObject *owner, IOInterruptEventSource * , int )
{
register AppleUSBOHCI *controller = (AppleUSBOHCI *) owner;
if (!controller || controller->isInactive() || !controller->_controllerAvailable)
return;
controller->finishPending();
controller->PollInterrupts();
controller->_filterInterruptCount = 0;
}
bool
AppleUSBOHCI::PrimaryInterruptFilter(OSObject *owner, IOFilterInterruptEventSource *source)
{
register AppleUSBOHCI *controller = (AppleUSBOHCI *)owner;
bool result = true;
if (!controller || controller->isInactive() || !controller->_controllerAvailable)
return false;
result = controller->FilterInterrupt(0);
return result;
}
bool
AppleUSBOHCI::IsValidPhysicalAddress(IOPhysicalAddress pageAddr)
{
AppleUSBOHCIitdMemoryBlock *itdMemBlock = _itdMBHead;
AppleUSBOHCIgtdMemoryBlock *gtdMemBlock = _gtdMBHead;
while (gtdMemBlock)
{
if ( pageAddr == (gtdMemBlock->GetSharedPhysicalPtr(0) & kOHCIPageMask) )
return true;
gtdMemBlock = gtdMemBlock->GetNextBlock();
}
while (itdMemBlock)
{
if ( pageAddr == (itdMemBlock->GetSharedPhysicalPtr(0) & kOHCIPageMask) )
return true;
itdMemBlock = itdMemBlock->GetNextBlock();
}
return false;
}
bool
AppleUSBOHCI::FilterInterrupt(int index)
{
register UInt32 activeInterrupts;
register UInt32 enabledInterrupts;
IOPhysicalAddress physicalAddress;
AppleOHCIGeneralTransferDescriptorPtr pHCDoneTD = NULL;
AppleOHCIGeneralTransferDescriptorPtr nextTD = NULL, prevTD = NULL;
uint64_t timeStamp;
UInt32 numberOfTDs = 0;
IOPhysicalAddress oldHead;
IOPhysicalAddress cachedHead;
UInt32 cachedProducer;
Boolean needSecondary = false;
enabledInterrupts = USBToHostLong(_pOHCIRegisters->hcInterruptEnable);
activeInterrupts = enabledInterrupts & USBToHostLong(_pOHCIRegisters->hcInterruptStatus);
if ((enabledInterrupts & kOHCIHcInterrupt_MIE) && (activeInterrupts != 0))
{
USBTrace( kUSBTOHCIInterrupts, kTPOHCIInterruptsPrimaryInterruptFilter , (uintptr_t)this, enabledInterrupts, activeInterrupts, 0 );
if (activeInterrupts & kOHCIHcInterrupt_FNO)
{
uint64_t tempTime;
UInt16 framenumber16;
framenumber16 = USBToHostWord(*(UInt16*)(_pHCCA + 0x80));
_errors.frameNumberOverflow++;
if ( (USBToHostWord(*(UInt16*)(_pHCCA + kHCCAFrameNumberOffset)) & kOHCIFmNumberMask) < kOHCIBit15 )
_frameNumber += kOHCIFrameOverflowBit;
_tempAnchorFrame = _frameNumber + framenumber16;
tempTime = mach_absolute_time();
_tempAnchorTime = *(AbsoluteTime*)&tempTime;
_frameNumberOverflowInterrupt = kOHCIHcInterrupt_FNO;
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_FNO);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pOHCIRegisters->hcInterruptStatus); while ((count++ < 10) && (newValue & kOHCIHcInterrupt_FNO))
{
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_FNO);
IOSync();
newValue = USBToHostLong(_pOHCIRegisters->hcInterruptStatus);
}
}
needSecondary = true;
}
if (activeInterrupts & kOHCIHcInterrupt_SO)
{
_errors.scheduleOverrun++;
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_SO);
IOSync();
}
if (activeInterrupts & kOHCIHcInterrupt_SF)
{
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_SF);
IOSync();
_pOHCIRegisters->hcInterruptDisable = HostToUSBLong(kOHCIHcInterrupt_SF);
IOSync();
}
if (activeInterrupts & kOHCIHcInterrupt_OC)
{
_errors.ownershipChange++;
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_OC);
IOSync();
}
if (activeInterrupts & kOHCIHcInterrupt_RHSC)
{
_rootHubStatusChangeInterrupt = kOHCIHcInterrupt_RHSC;
_pOHCIRegisters->hcInterruptDisable = HostToUSBLong(kOHCIHcInterrupt_RHSC);
IOSync();
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_RHSC);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pOHCIRegisters->hcInterruptStatus); while ((count++ < 10) && (newValue & kOHCIHcInterrupt_RHSC))
{
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_RHSC);
IOSync();
newValue = USBToHostLong(_pOHCIRegisters->hcInterruptStatus);
}
}
needSecondary = true;
}
if (activeInterrupts & kOHCIHcInterrupt_UE)
{
_errors.unrecoverableError++;
_unrecoverableErrorInterrupt = kOHCIHcInterrupt_UE;
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_UE);
IOSync();
needSecondary = true;
}
if (activeInterrupts & kOHCIHcInterrupt_RD)
{
_resumeDetectedInterrupt = kOHCIHcInterrupt_RD;
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_RD);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pOHCIRegisters->hcInterruptStatus); while ((count++ < 10) && (newValue & kOHCIHcInterrupt_RD))
{
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_RD);
IOSync();
newValue = USBToHostLong(_pOHCIRegisters->hcInterruptStatus);
}
}
needSecondary = true;
}
if (activeInterrupts & kOHCIHcInterrupt_WDH)
{
timeStamp = mach_absolute_time();
physicalAddress = (UInt32) USBToHostLong(*(UInt32 *)(_pHCCA + kHCCADoneHeadOffset));
physicalAddress &= kOHCIHeadPMask;
oldHead = _savedDoneQueueHead;
cachedHead = physicalAddress;
if ( physicalAddress == NULL )
pHCDoneTD = NULL;
else
{
pHCDoneTD = AppleUSBOHCIgtdMemoryBlock::GetGTDFromPhysical(physicalAddress);
}
*(UInt32 *)(_pHCCA + kHCCADoneHeadOffset) = 0L;
_pOHCIRegisters->hcInterruptStatus = HostToUSBLong(kOHCIHcInterrupt_WDH);
IOSync();
_writeDoneHeadInterrupt = kOHCIHcInterrupt_WDH;
prevTD = NULL;
while (pHCDoneTD != NULL)
{
AppleOHCIIsochTransferDescriptorPtr pITD;
IOUSBLowLatencyIsocFrame * pFrames;
IOReturn errStatus;
UInt32 control;
UInt32 transferStatus;
UInt32 frameCount;
UInt32 i;
numberOfTDs++;
physicalAddress = USBToHostLong(pHCDoneTD->pShared->nextTD) & kOHCIHeadPMask;
if ( physicalAddress == NULL )
nextTD = NULL;
else
{
nextTD = AppleUSBOHCIgtdMemoryBlock::GetGTDFromPhysical(physicalAddress);
}
if ( (pHCDoneTD->pType == kOHCIIsochronousInLowLatencyType) ||
(pHCDoneTD->pType == kOHCIIsochronousOutLowLatencyType) )
{
_lowLatencyIsochTDsProcessed++;
pITD = (AppleOHCIIsochTransferDescriptorPtr) pHCDoneTD;
pFrames = (IOUSBLowLatencyIsocFrame *) pITD->pIsocFrame;
control = USBToHostLong(pHCDoneTD->pShared->ohciFlags);
transferStatus = (control & kOHCIGTDControl_CC) >> kOHCIGTDControl_CCPhase;
errStatus = TranslateStatusToUSBError(transferStatus);
frameCount = (USBToHostLong(pITD->pShared->flags) & kOHCIITDControl_FC) >> kOHCIITDControl_FCPhase;
for (i = 0; i <= frameCount; i++)
{
UInt16 frActCount;
UInt32 frStatus;
_framesUpdated++;
if ( pFrames[pITD->frameNum + i].frStatus != (IOReturn) kUSBLowLatencyIsochTransferKey )
_framesError++;
pFrames[pITD->frameNum + i].frTimeStamp = *(AbsoluteTime *)&timeStamp;
UInt16 offset = USBToHostWord(pITD->pShared->offset[i]);
if ( ((offset & kOHCIITDOffset_CC) >> kOHCIITDOffset_CCPhase) == kOHCIITDOffsetConditionNotAccessed)
{
frActCount = 0;
frStatus = TranslateStatusToUSBError(kOHCIITDConditionNotAccessedReturn);
}
else
{
IOReturn tdStatus = TranslateStatusToUSBError( (offset & kOHCIITDPSW_CC) >> kOHCIITDPSW_CCPhase);
if ((kIOReturnSuccess == tdStatus) && (pITD->pType == kOHCIIsochronousOutLowLatencyType))
frActCount = pFrames[pITD->frameNum + i].frReqCount;
else
frActCount = offset & kOHCIITDPSW_Size;
frStatus = tdStatus;
}
if ( pITD->requestFromRosettaClient )
{
pFrames[pITD->frameNum + i].frReqCount = OSSwapInt16(pFrames[pITD->frameNum + i].frReqCount);
pFrames[pITD->frameNum + i].frActCount = OSSwapInt16(frActCount);
pFrames[pITD->frameNum + i].frStatus = OSSwapInt32(frStatus);
}
else
{
pFrames[pITD->frameNum + i].frActCount = frActCount;
pFrames[pITD->frameNum + i].frStatus = frStatus;
#ifdef __LP64__
USBTrace( kUSBTOHCIInterrupts, kTPOHCIUpdateFrameList , 0, (uintptr_t)&pFrames[pITD->frameNum + i], (uintptr_t)frActCount, (uintptr_t)timeStamp );
#else
USBTrace( kUSBTOHCIInterrupts, kTPOHCIUpdateFrameList , 0, (uintptr_t)&pFrames[pITD->frameNum + i], (uintptr_t)(pFrames[pITD->frameNum + i].frTimeStamp.hi), (uintptr_t)pFrames[pITD->frameNum + i].frTimeStamp.lo );
#endif
}
}
}
else
{
if ( (pHCDoneTD->pType != kOHCIIsochronousInType) &&
(pHCDoneTD->pType != kOHCIIsochronousOutType) )
{
pHCDoneTD->command->SetTimeStamp( *(AbsoluteTime *)&timeStamp );
}
}
prevTD = pHCDoneTD;
pHCDoneTD = nextTD;
}
cachedProducer = _producerCount;
cachedProducer += numberOfTDs;
if ( prevTD != NULL )
prevTD->pShared->nextTD = HostToUSBLong(oldHead);
IOSimpleLockLock( _wdhLock );
_savedDoneQueueHead = cachedHead; _producerCount = cachedProducer;
IOSimpleLockUnlock( _wdhLock );
needSecondary = true;
}
}
if (needSecondary)
_filterInterruptSource->signalInterrupt();
return false;
}