AppleUSBOHCI_Interrupts.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOService.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include "AppleUSBOHCIMemoryBlocks.h"
#include "AppleUSBOHCI.h"
#define nil (0)
#define DEBUGGING_LEVEL 0 // 1 = low; 2 = high; 3 = extreme
#define super IOUSBController
#define self this
void AppleUSBOHCI::PollInterrupts(IOUSBCompletionAction safeAction)
{
UInt64 timeElapsed;
AbsoluteTime timeStop;
IOReturn err = kIOReturnSuccess;
if (_writeDoneHeadInterrupt & kOHCIHcInterrupt_WDH)
{
_writeDoneHeadInterrupt = 0;
UIMProcessDoneQueue(safeAction);
}
if (_resumeDetectedInterrupt & kOHCIHcInterrupt_RD)
{
_resumeDetectedInterrupt = 0;
_remote_wakeup_occurred = true;
USBLog(3,"AppleUSBOHCI[%p] ResumeDetected Interrupt on bus %ld", this, _busNumber );
if ( _idleSuspend )
setPowerState(kOHCISetPowerLevelRunning, self);
}
if (_unrecoverableErrorInterrupt & kOHCIHcInterrupt_UE)
{
USBError(1,"USB Controller on bus %ld received and unrecoverable error interrupt. Attempting to recover (%d,%d,%d)", _busNumber,_onCardBus, _pcCardEjected, isInactive() );
_unrecoverableErrorInterrupt = 0;
_errors.unrecoverableError++;
if ( _onCardBus )
{
USBError(1,"Ignoring unrecoverable error on PC Card" );
}
else
{
if ( !(_errataBits & kErrataNECOHCIIsochWraparound ) )
{
_pOHCIRegisters->hcCommandStatus = HostToUSBLong(kOHCIHcCommandStatus_HCR);
delay(10 * MICROSECOND);
_pOHCIRegisters->hcControl = HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase) | kOHCIHcControl_PLE);
}
else
{
if ( _rootHubDevice )
{
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
}
SuspendUSBBus();
UIMFinalizeForPowerDown();
_ohciAvailable = false;
IOSleep(100);
UIMInitializeForPowerUp();
_ohciAvailable = true; _ohciBusState = kOHCIBusStateRunning;
if ( _rootHubDevice == NULL )
{
err = CreateRootHubDevice( _device, &_rootHubDevice );
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBOHCI[%p] Could not create root hub device upon wakeup (%x)!", this, err);
}
else
{
_rootHubDevice->registerService(kIOServiceRequired | kIOServiceSynchronous);
}
}
}
}
}
if (_rootHubStatusChangeInterrupt & kOHCIHcInterrupt_RHSC)
{
_rootHubStatusChangeInterrupt = 0;
_remote_wakeup_occurred = true;
USBLog(3,"AppleUSBOHCI[%p] RootHub Status Change Interrupt on bus %ld", this, _busNumber );
UIMRootHubStatusChange( false );
LastRootHubPortStatusChanged ( true );
}
}
void
AppleUSBOHCI::InterruptHandler(OSObject *owner, IOInterruptEventSource * , int )
{
register AppleUSBOHCI *controller = (AppleUSBOHCI *) owner;
if (!controller || controller->isInactive() || (controller->_onCardBus && controller->_pcCardEjected) || !controller->_ohciAvailable)
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->_onCardBus && controller->_pcCardEjected) || !controller->_ohciAvailable)
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;
AbsoluteTime 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))
{
if (activeInterrupts & kOHCIHcInterrupt_FNO)
{
_errors.frameNumberOverflow++;
if ( (USBToHostWord(*(UInt16*)(_pHCCA + kHCCAFrameNumberOffset)) & kOHCIFmNumberMask) < kOHCIBit15 )
_frameNumber += kOHCIFrameOverflowBit;
_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);
}
}
}
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)
{
clock_get_uptime(&timeStamp);
physicalAddress = (UInt32) USBToHostLong(*(UInt32 *)(_pHCCA + kHCCADoneHeadOffset));
physicalAddress &= kOHCIHeadPMask;
oldHead = _savedDoneQueueHead;
cachedHead = physicalAddress;
if ( physicalAddress == NULL )
pHCDoneTD = NULL;
else
{
if ( _onCardBus )
{
if (!IsValidPhysicalAddress( physicalAddress & kOHCIPageMask) )
{
USBLog(1, "Bad phys addr #1 0x%lx", physicalAddress);
pHCDoneTD = NULL;
}
else
{
pHCDoneTD = AppleUSBOHCIgtdMemoryBlock::GetGTDFromPhysical(physicalAddress);
}
}
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
{
if ( _onCardBus )
{
if (!IsValidPhysicalAddress( physicalAddress & kOHCIPageMask) )
{
nextTD = NULL;
}
else
nextTD = AppleUSBOHCIgtdMemoryBlock::GetGTDFromPhysical(physicalAddress);
}
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 = 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;
}
}
}
else
{
if ( (pHCDoneTD->pType != kOHCIIsochronousInType) &&
(pHCDoneTD->pType != kOHCIIsochronousOutType) )
{
pHCDoneTD->command->SetTimeStamp( 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;
}