IOInterruptController.cpp [plain text]
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOInterrupts.h>
#include <IOKit/IOInterruptController.h>
#include <IOKit/IOKitDebug.h>
#include <IOKit/IOTimeStamp.h>
#define super IOService
OSDefineMetaClassAndAbstractStructors(IOInterruptController, IOService);
OSMetaClassDefineReservedUnused(IOInterruptController, 0);
OSMetaClassDefineReservedUnused(IOInterruptController, 1);
OSMetaClassDefineReservedUnused(IOInterruptController, 2);
OSMetaClassDefineReservedUnused(IOInterruptController, 3);
OSMetaClassDefineReservedUnused(IOInterruptController, 4);
OSMetaClassDefineReservedUnused(IOInterruptController, 5);
IOReturn IOInterruptController::registerInterrupt(IOService *nub, int source,
void *target,
IOInterruptHandler handler,
void *refCon)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
int wasDisabledSoft;
IOReturn error;
OSData *vectorData;
IOOptionBits options;
bool canBeShared, shouldBeShared, wasAlreadyRegisterd;
IOService *originalNub = NULL; int originalSource = 0;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(IOInterruptVectorNumber *)vectorData->getBytesNoCopy();
vector = &vectors[vectorNumber];
IOLockLock(vector->interruptLock);
canBeShared = vectorCanBeShared(vectorNumber, vector);
IODTGetInterruptOptions(nub, source, &options);
#if defined(__i386__) || defined(__x86_64__)
int interruptType;
if (OSDynamicCast(IOPlatformDevice, getProvider()) &&
(getInterruptType(nub, source, &interruptType) == kIOReturnSuccess) &&
(kIOInterruptTypeLevel & interruptType))
{
options |= kIODTInterruptShared;
}
#endif
shouldBeShared = canBeShared && (options & kIODTInterruptShared);
wasAlreadyRegisterd = vector->interruptRegistered;
if (wasAlreadyRegisterd && !canBeShared) {
IOLockUnlock(vector->interruptLock);
return kIOReturnNoResources;
}
if (wasAlreadyRegisterd || shouldBeShared) {
if (vector->sharedController == 0) {
vector->sharedController = new IOSharedInterruptController;
if (vector->sharedController == 0) {
IOLockUnlock(vector->interruptLock);
return kIOReturnNoMemory;
}
if (wasAlreadyRegisterd) {
originalNub = vector->nub;
originalSource = vector->source;
disableVectorHard(vectorNumber, vector);
vector->interruptDisabledHard = 0;
}
error = vector->sharedController->initInterruptController(this, vectorData);
if (error != kIOReturnSuccess) {
if (wasAlreadyRegisterd) enableInterrupt(originalNub, originalSource);
vector->sharedController->release();
vector->sharedController = 0;
IOLockUnlock(vector->interruptLock);
return error;
}
if (wasAlreadyRegisterd) {
error = vector->sharedController->registerInterrupt(originalNub,
originalSource,
vector->target,
vector->handler,
vector->refCon);
if (error != kIOReturnSuccess) {
wasDisabledSoft = vector->interruptDisabledSoft;
vector->interruptDisabledSoft = 1;
vector->interruptDisabledHard = 1;
if (!wasDisabledSoft) originalNub->enableInterrupt(originalSource);
enableInterrupt(originalNub, originalSource);
vector->sharedController->release();
vector->sharedController = 0;
IOLockUnlock(vector->interruptLock);
return error;
}
}
vector->handler = (IOInterruptHandler)vector->sharedController->getInterruptHandlerAddress();
vector->nub = vector->sharedController;
vector->source = 0;
vector->target = vector->sharedController;
vector->refCon = 0;
if (wasAlreadyRegisterd) wasDisabledSoft = vector->interruptDisabledSoft;
else wasDisabledSoft = true;
if (!wasAlreadyRegisterd) initVector(vectorNumber, vector);
vector->interruptDisabledSoft = 1;
vector->interruptDisabledHard = 1;
vector->interruptRegistered = 1;
if (!wasDisabledSoft) originalNub->enableInterrupt(originalSource);
}
error = vector->sharedController->registerInterrupt(nub, source, target,
handler, refCon);
IOLockUnlock(vector->interruptLock);
return error;
}
vector->handler = handler;
vector->nub = nub;
vector->source = source;
vector->target = target;
vector->refCon = refCon;
initVector(vectorNumber, vector);
vector->interruptDisabledHard = 1;
vector->interruptDisabledSoft = 1;
vector->interruptRegistered = 1;
IOLockUnlock(vector->interruptLock);
return kIOReturnSuccess;
}
IOReturn IOInterruptController::unregisterInterrupt(IOService *nub, int source)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
OSData *vectorData;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(IOInterruptVectorNumber *)vectorData->getBytesNoCopy();
vector = &vectors[vectorNumber];
IOLockLock(vector->interruptLock);
if (!vector->interruptRegistered) {
IOLockUnlock(vector->interruptLock);
return kIOReturnSuccess;
}
disableInterrupt(nub, source);
disableVectorHard(vectorNumber, vector);
vector->interruptActive = 0;
vector->interruptDisabledSoft = 0;
vector->interruptDisabledHard = 0;
vector->interruptRegistered = 0;
vector->nub = 0;
vector->source = 0;
vector->handler = 0;
vector->target = 0;
vector->refCon = 0;
IOLockUnlock(vector->interruptLock);
return kIOReturnSuccess;
}
IOReturn IOInterruptController::getInterruptType(IOService *nub, int source,
int *interruptType)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
OSData *vectorData;
if (interruptType == 0) return kIOReturnBadArgument;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(IOInterruptVectorNumber *)vectorData->getBytesNoCopy();
vector = &vectors[vectorNumber];
*interruptType = getVectorType(vectorNumber, vector);
return kIOReturnSuccess;
}
IOReturn IOInterruptController::enableInterrupt(IOService *nub, int source)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
OSData *vectorData;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(IOInterruptVectorNumber *)vectorData->getBytesNoCopy();
vector = &vectors[vectorNumber];
if (vector->interruptDisabledSoft) {
vector->interruptDisabledSoft = 0;
#if !defined(__i386__) && !defined(__x86_64__)
OSMemoryBarrier();
#endif
if (!getPlatform()->atInterruptLevel()) {
while (vector->interruptActive)
{}
}
if (vector->interruptDisabledHard) {
vector->interruptDisabledHard = 0;
enableVector(vectorNumber, vector);
}
}
return kIOReturnSuccess;
}
IOReturn IOInterruptController::disableInterrupt(IOService *nub, int source)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
OSData *vectorData;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(IOInterruptVectorNumber *)vectorData->getBytesNoCopy();
vector = &vectors[vectorNumber];
vector->interruptDisabledSoft = 1;
#if !defined(__i386__) && !defined(__x86_64__)
OSMemoryBarrier();
#endif
if (!getPlatform()->atInterruptLevel()) {
while (vector->interruptActive)
{}
}
return kIOReturnSuccess;
}
IOReturn IOInterruptController::causeInterrupt(IOService *nub, int source)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
OSData *vectorData;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(IOInterruptVectorNumber *)vectorData->getBytesNoCopy();
vector = &vectors[vectorNumber];
causeVector(vectorNumber, vector);
return kIOReturnSuccess;
}
IOInterruptAction IOInterruptController::getInterruptHandlerAddress(void)
{
return 0;
}
IOReturn IOInterruptController::handleInterrupt(void *refCon, IOService *nub,
int source)
{
return kIOReturnInvalid;
}
bool IOInterruptController::vectorCanBeShared(IOInterruptVectorNumber ,
IOInterruptVector *)
{
return false;
}
void IOInterruptController::initVector(IOInterruptVectorNumber ,
IOInterruptVector *)
{
}
int IOInterruptController::getVectorType(IOInterruptVectorNumber ,
IOInterruptVector *)
{
return kIOInterruptTypeEdge;
}
void IOInterruptController::disableVectorHard(IOInterruptVectorNumber ,
IOInterruptVector *)
{
}
void IOInterruptController::enableVector(IOInterruptVectorNumber ,
IOInterruptVector *)
{
}
void IOInterruptController::causeVector(IOInterruptVectorNumber ,
IOInterruptVector *)
{
}
#undef super
#define super IOInterruptController
OSDefineMetaClassAndStructors(IOSharedInterruptController, IOInterruptController);
OSMetaClassDefineReservedUnused(IOSharedInterruptController, 0);
OSMetaClassDefineReservedUnused(IOSharedInterruptController, 1);
OSMetaClassDefineReservedUnused(IOSharedInterruptController, 2);
OSMetaClassDefineReservedUnused(IOSharedInterruptController, 3);
#define kIOSharedInterruptControllerDefaultVectors (128)
IOReturn IOSharedInterruptController::initInterruptController(IOInterruptController *parentController, OSData *parentSource)
{
int cnt, interruptType;
IOReturn error;
reserved = NULL;
if (!super::init())
return kIOReturnNoResources;
provider = this;
_interruptSources = (IOInterruptSource *)IOMalloc(sizeof(IOInterruptSource));
if (_interruptSources == 0) return kIOReturnNoMemory;
_numInterruptSources = 1;
parentController->retain();
parentSource->retain();
_interruptSources[0].interruptController = parentController;
_interruptSources[0].vectorData = parentSource;
sourceIsLevel = false;
error = provider->getInterruptType(0, &interruptType);
if (error == kIOReturnSuccess) {
if (interruptType & kIOInterruptTypeLevel)
sourceIsLevel = true;
}
numVectors = kIOSharedInterruptControllerDefaultVectors; vectors = (IOInterruptVector *)IOMalloc(numVectors * sizeof(IOInterruptVector));
if (vectors == NULL) {
IOFree(_interruptSources, sizeof(IOInterruptSource));
return kIOReturnNoMemory;
}
bzero(vectors, numVectors * sizeof(IOInterruptVector));
controllerLock = IOSimpleLockAlloc();
if (controllerLock == 0) return kIOReturnNoResources;
for (cnt = 0; cnt < numVectors; cnt++) {
vectors[cnt].interruptLock = IOLockAlloc();
if (vectors[cnt].interruptLock == NULL) {
for (cnt = 0; cnt < numVectors; cnt++) {
if (vectors[cnt].interruptLock != NULL)
IOLockFree(vectors[cnt].interruptLock);
}
return kIOReturnNoResources;
}
}
numVectors = 0; vectorsRegistered = 0;
vectorsEnabled = 0;
controllerDisabled = 1;
return kIOReturnSuccess;
}
IOReturn IOSharedInterruptController::registerInterrupt(IOService *nub,
int source,
void *target,
IOInterruptHandler handler,
void *refCon)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector = 0;
OSData *vectorData;
IOInterruptState interruptState;
interruptSources = nub->_interruptSources;
vectorNumber = kIOSharedInterruptControllerDefaultVectors;
while (vectorsRegistered != kIOSharedInterruptControllerDefaultVectors) {
for (vectorNumber = 0; vectorNumber < kIOSharedInterruptControllerDefaultVectors; vectorNumber++) {
vector = &vectors[vectorNumber];
IOLockLock(vector->interruptLock);
if (!vector->interruptRegistered) break;
IOLockUnlock(vector->interruptLock);
}
if (vectorNumber != kIOSharedInterruptControllerDefaultVectors) break;
}
if (vectorNumber == kIOSharedInterruptControllerDefaultVectors) {
return kIOReturnNoResources;
}
vectorData = OSData::withBytes(&vectorNumber, sizeof(vectorNumber));
if (vectorData == 0) {
IOLockUnlock(vector->interruptLock);
return kIOReturnNoMemory;
}
interruptSources[source].interruptController = this;
interruptSources[source].vectorData = vectorData;
vector->handler = handler;
vector->nub = nub;
vector->source = source;
vector->target = target;
vector->refCon = refCon;
vector->interruptDisabledSoft = 1;
vector->interruptRegistered = 1;
interruptState = IOSimpleLockLockDisableInterrupt(controllerLock);
if (++vectorsRegistered > numVectors) numVectors = vectorsRegistered;
IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
IOLockUnlock(vector->interruptLock);
return kIOReturnSuccess;
}
IOReturn IOSharedInterruptController::unregisterInterrupt(IOService *nub,
int source)
{
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
IOInterruptState interruptState;
for (vectorNumber = 0; vectorNumber < kIOSharedInterruptControllerDefaultVectors; vectorNumber++) {
vector = &vectors[vectorNumber];
IOLockLock(vector->interruptLock);
if (!vector->interruptRegistered
|| (vector->nub != nub) || (vector->source != source)) {
IOLockUnlock(vector->interruptLock);
continue;
}
disableInterrupt(nub, source);
vector->interruptActive = 0;
vector->interruptDisabledSoft = 0;
vector->interruptDisabledHard = 0;
vector->interruptRegistered = 0;
vector->nub = 0;
vector->source = 0;
vector->handler = 0;
vector->target = 0;
vector->refCon = 0;
interruptState = IOSimpleLockLockDisableInterrupt(controllerLock);
vectorsRegistered--;
IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
IOLockUnlock(vector->interruptLock);
}
if (vectorsEnabled == vectorsRegistered) {
controllerDisabled = 0;
provider->enableInterrupt(0);
}
return kIOReturnSuccess;
}
IOReturn IOSharedInterruptController::getInterruptType(IOService *,
int ,
int *interruptType)
{
return provider->getInterruptType(0, interruptType);
}
IOReturn IOSharedInterruptController::enableInterrupt(IOService *nub,
int source)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
OSData *vectorData;
IOInterruptState interruptState;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(IOInterruptVectorNumber *)vectorData->getBytesNoCopy();
vector = &vectors[vectorNumber];
interruptState = IOSimpleLockLockDisableInterrupt(controllerLock);
if (!vector->interruptDisabledSoft) {
IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
return kIOReturnSuccess;
}
vector->interruptDisabledSoft = 0;
vectorsEnabled++;
IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
if (controllerDisabled && (vectorsEnabled == vectorsRegistered)) {
controllerDisabled = 0;
provider->enableInterrupt(0);
}
return kIOReturnSuccess;
}
IOReturn IOSharedInterruptController::disableInterrupt(IOService *nub,
int source)
{
IOInterruptSource *interruptSources;
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
OSData *vectorData;
IOInterruptState interruptState;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(IOInterruptVectorNumber *)vectorData->getBytesNoCopy();
vector = &vectors[vectorNumber];
interruptState = IOSimpleLockLockDisableInterrupt(controllerLock);
if (!vector->interruptDisabledSoft) {
vector->interruptDisabledSoft = 1;
#if !defined(__i386__) && !defined(__x86_64__)
OSMemoryBarrier();
#endif
vectorsEnabled--;
}
IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
if (!getPlatform()->atInterruptLevel()) {
while (vector->interruptActive)
{}
}
return kIOReturnSuccess;
}
IOInterruptAction IOSharedInterruptController::getInterruptHandlerAddress(void)
{
return OSMemberFunctionCast(IOInterruptAction,
this, &IOSharedInterruptController::handleInterrupt);
}
IOReturn IOSharedInterruptController::handleInterrupt(void * ,
IOService * nub,
int )
{
IOInterruptVectorNumber vectorNumber;
IOInterruptVector *vector;
for (vectorNumber = 0; vectorNumber < numVectors; vectorNumber++) {
vector = &vectors[vectorNumber];
vector->interruptActive = 1;
#if !defined(__i386__) && !defined(__x86_64__)
OSMemoryBarrier();
#endif
if (!vector->interruptDisabledSoft) {
if (vector->interruptRegistered) {
bool trace = (gIOKitTrace & kIOTraceInterrupts) ? true : false;
if (trace)
IOTimeStampStartConstant(IODBG_INTC(IOINTC_HANDLER),
(uintptr_t) vectorNumber, (uintptr_t) vector->handler, (uintptr_t)vector->target);
vector->handler(vector->target, vector->refCon, vector->nub, vector->source);
if (trace)
IOTimeStampEndConstant(IODBG_INTC(IOINTC_HANDLER),
(uintptr_t) vectorNumber, (uintptr_t) vector->handler, (uintptr_t)vector->target);
}
}
vector->interruptActive = 0;
}
IOSimpleLockLock(controllerLock);
if (vectorsEnabled != vectorsRegistered) {
nub->disableInterrupt(0);
controllerDisabled = 1;
}
IOSimpleLockUnlock(controllerLock);
return kIOReturnSuccess;
}