#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include "MacIOGPIO.h"
#define super IOService
OSDefineMetaClassAndStructors(MacIOGPIO, IOService)
bool MacIOGPIO::init(OSDictionary *dict)
{
fKeyLargoDrv = 0;
fGPIOBaseAddress = 0;
fClients = 0;
fClientLock = 0;
fWorkLoop = 0;
return super::init(dict);
}
void MacIOGPIO::free(void)
{
super::free();
}
IOService *MacIOGPIO::probe(IOService *provider, SInt32 *score)
{
return(this);
}
bool MacIOGPIO::start(IOService *provider)
{
OSData *regprop;
if (!super::start(provider)) return(false);
if ((regprop = OSDynamicCast(OSData, provider->getProperty("reg"))) == 0)
return(false);
else
{
fGPIOBaseAddress = *(UInt32 *)regprop->getBytesNoCopy();
}
fKeyLargoDrv = waitForService(serviceMatching("KeyLargo"));
if (!fKeyLargoDrv) return(false);
fWorkLoop = getWorkLoop();
fClientLock = IOLockAlloc();
if (!fClientLock) return(false);
fSymKeyLargoSafeWriteRegUInt8 = OSSymbol::withCString(kKeyLargoSafeWriteRegUInt8);
fSymKeyLargoSafeReadRegUInt8 = OSSymbol::withCString(kKeyLargoSafeReadRegUInt8);
publishBelow(provider);
return(true);
}
void MacIOGPIO::stop(IOService *provider)
{
MacIOGPIOCallbackInfo *thisClient, *nextClient;
IOLockLock(fClientLock);
if (fClients)
{
thisClient = fClients;
while (thisClient)
{
nextClient = thisClient->next;
thisClient->eventSource->disable();
fWorkLoop->removeEventSource(thisClient->eventSource);
thisClient->eventSource->release();
IOFree(thisClient, sizeof(MacIOGPIOCallbackInfo));
thisClient = nextClient;
}
}
fClients = 0;
IOLockUnlock(fClientLock);
IOLockFree(fClientLock);
fClientLock = 0;
fWorkLoop = 0;
fGPIOBaseAddress = 0;
super::stop(provider);
}
IOReturn MacIOGPIO::callPlatformFunction(const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4)
{
const char *functionNameStr;
IOReturn result = kIOReturnUnsupported;
UInt32 offset;
UInt8 regval;
if (functionName == NULL) return kIOReturnBadArgument;
functionNameStr = functionName->getCStringNoCopy();
DLOG("MacIOGPIO::callPlatformFunction %s %s %08lx %08lx %08lx %08lx\n",
functionNameStr, waitForFunction ? "TRUE" : "FALSE",
(UInt32)param1, (UInt32)param2, (UInt32)param3, (UInt32)param4);
if (strcmp(functionNameStr, kSymGPIOParentIntCapable) == 0)
{
*((UInt32 *)param1) = 1;
result = kIOReturnSuccess;
}
else if (strcmp(functionNameStr, kSymGPIOParentWriteGPIO) == 0)
{
offset = (UInt32)param1;
offset += fGPIOBaseAddress;
result = fKeyLargoDrv->callPlatformFunction(fSymKeyLargoSafeWriteRegUInt8,
false, (void *)offset, (void *)param3, (void *)param2, 0);
}
else if (strcmp(functionNameStr, kSymGPIOParentReadGPIO) == 0)
{
offset = (UInt32)param1;
offset += fGPIOBaseAddress;
result = fKeyLargoDrv->callPlatformFunction(fSymKeyLargoSafeReadRegUInt8,
false, (void *)offset, (void *)®val, 0, 0);
if (result == kIOReturnSuccess)
{
DLOG("MacIOGPIO::callPlatformFunction read value 0x%02x\n", regval);
*(UInt32 *)param2 = (UInt32)regval;
}
}
else if (strcmp(functionNameStr, kSymGPIOParentRegister) == 0)
{
if (registerClient(param1, param2, param3, param4))
result = kIOReturnSuccess;
else
result = kIOReturnError;
}
else if (strcmp(functionNameStr, kSymGPIOParentUnregister) == 0)
{
if (unregisterClient(param1, param2, param3, param4))
result = kIOReturnSuccess;
else
result = kIOReturnBadArgument;
}
else if (strcmp(functionNameStr, kSymGPIOParentEvtEnable) == 0)
{
if (enableClient(param1, param2, param3, param4))
result = kIOReturnSuccess;
else
result = kIOReturnBadArgument;
}
else if (strcmp(functionNameStr, kSymGPIOParentEvtDisable) == 0)
{
if (disableClient(param1, param2, param3, param4))
result = kIOReturnSuccess;
else
result = kIOReturnBadArgument;
}
else
{
DLOG("MacIOGPIO::callPlatformFunction didn't recognize function\n");
result = super::callPlatformFunction(functionName, waitForFunction,
param1, param2, param3, param4);
}
return result;
}
IOReturn MacIOGPIO::callPlatformFunction(const char *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4 )
{
return(super::callPlatformFunction(functionName,
waitForFunction, param1, param2, param3, param4));
}
IOService *MacIOGPIO::createNub( IORegistryEntry * from )
{
IOService *nub;
nub = new MacIOGPIODevice;
if (nub && !nub->init( from, gIODTPlane ))
{
nub->free();
nub = 0;
}
return(nub);
}
void MacIOGPIO::processNub(IOService *myNub)
{
}
void MacIOGPIO::publishBelow(IOService *root)
{
OSCollectionIterator *kids;
IORegistryEntry *next;
IOService *nub;
OSData *compat;
bool gpio;
int strLen;
const char *strStart, *strCur;
bool publishAll;
publishAll = root->getProperty("preserveIODeviceTree") != 0;
if(publishAll)
kids = IODTFindMatchingEntries( root, kIODTRecursive, NULL);
else
kids = IODTFindMatchingEntries( root, kIODTRecursive | kIODTExclusive,
"('programmer-switch')");
if (kids)
{
while((next = (IORegistryEntry *)kids->getNextObject()) != 0)
{
gpio = false;
compat = OSDynamicCast(OSData, next->getProperty("compatible"));
if (compat)
{
strLen = compat->getLength();
strStart = strCur = (const char *)compat->getBytesNoCopy();
while ((strCur - strStart) < strLen)
{
if (strcmp(strCur, "gpio") == 0)
{
gpio = true;
break; }
strCur += strlen(strCur) + 1;
}
}
if(!gpio || ((nub = createNub(next)) == 0))
{
if(publishAll)
root->callPlatformFunction("mac-io-publishChild", false, this, next, 0, 0);
else
DLOG("Not creating nub for %s\n", next->getName());
continue;
}
nub->attach(this);
processNub(nub);
nub->registerService();
}
kids->release();
}
}
bool MacIOGPIO::registerClient(void *param1, void *param2,
void *param3, void *param4)
{
MacIOGPIOCallbackInfo *newClient, *tmpClient;
IOInterruptEventSource *eventSource;
int intType;
IOService *gpio;
IOReturn status;
DLOG("MacIOGPIO::registerClient %08lx %08lx %08lx %08lx\n",
(UInt32)param1, (UInt32)param2, (UInt32)param3, (UInt32)param4);
if ((param2 == 0) || (param3 == 0) || (param4 == 0)) return(false);
if ((gpio = OSDynamicCast(IOService, (OSMetaClassBase *) param2)) == NULL)
return(false);
status = gpio->getInterruptType( 0, &intType );
if (status != kIOInterruptTypeEdge && status != kIOInterruptTypeLevel)
return(false);
eventSource = IOInterruptEventSource::interruptEventSource(this,
(IOInterruptEventAction) &MacIOGPIO::interruptOccurred,
(IOService *)param2, 0);
if (eventSource == 0) return(false);
if (fWorkLoop->addEventSource(eventSource) != kIOReturnSuccess)
{
eventSource->release();
return(false);
}
if ((newClient =
(MacIOGPIOCallbackInfo *)IOMalloc(sizeof(MacIOGPIOCallbackInfo))) == 0)
{
fWorkLoop->removeEventSource(eventSource);
eventSource->release();
return(false);
}
newClient->eventSource = eventSource;
newClient->handler = (GPIOEventHandler)param3;
newClient->self = param4;
newClient->next = 0;
IOLockLock(fClientLock);
if (!fClients)
{
fClients = newClient;
}
else
{
tmpClient = fClients;
while (tmpClient->next != 0) tmpClient = tmpClient->next;
tmpClient->next = newClient;
}
IOLockUnlock(fClientLock);
DLOG("MacIOGPIO::registerClient succeeded!\n");
return(true);
}
#ifdef CLIENT_MATCH
#undef CLIENT_MATCH
#endif
#define CLIENT_MATCH(client) \
(((client)->handler == (GPIOEventHandler)param3) && \
((client)->self == param4))
bool MacIOGPIO::unregisterClient(void *param1, void *param2,
void *param3, void *param4)
{
MacIOGPIOCallbackInfo *prevClient, *thisClient;
bool found = false;
DLOG("MacIOGPIO::unregisterClient %08lx %08lx %08lx %08lx\n",
(UInt32)param1, (UInt32)param2, (UInt32)param3, (UInt32)param4);
if (fClients == 0) return(false);
if (CLIENT_MATCH(fClients))
{
thisClient = fClients;
IOLockLock(fClientLock);
fClients = fClients->next;
IOLockUnlock(fClientLock);
found = true;
}
else
{
thisClient = fClients;
while (thisClient->next != 0)
{
prevClient = thisClient;
thisClient = thisClient->next;
if (CLIENT_MATCH(thisClient))
{
IOLockLock(fClientLock);
prevClient->next = thisClient->next;
IOLockUnlock(fClientLock);
found = true;
break;
}
}
}
if (found)
{
thisClient->eventSource->disable();
fWorkLoop->removeEventSource(thisClient->eventSource);
thisClient->eventSource->release();
IOFree(thisClient, sizeof(MacIOGPIOCallbackInfo));
DLOG("MacIOGPIO::unregisterClient succeeded!\n");
}
return(found);
}
bool MacIOGPIO::enableClient(void *param1, void *param2, void *param3,
void *param4)
{
MacIOGPIOCallbackInfo *client;
bool found = false;
client = fClients;
while (client)
{
if (CLIENT_MATCH(client))
{
client->eventSource->enable();
found = true;
break;
}
client = client->next;
}
return(found);
}
bool MacIOGPIO::disableClient(void *param1, void *param2, void *param3,
void *param4)
{
MacIOGPIOCallbackInfo *client;
bool found = false;
client = fClients;
while (client)
{
if (CLIENT_MATCH(client))
{
client->eventSource->disable();
found = true;
break;
}
client = client->next;
}
return(found);
}
void MacIOGPIO::handleInterrupt(IOInterruptEventSource *source, int count)
{
MacIOGPIOCallbackInfo *client;
GPIOEventHandler handler;
DLOG("MacIOGPIO::handleInterrupt got event!!\n");
client = fClients;
while (client != 0)
{
if (client->eventSource == source)
{
handler = client->handler;
handler(client->self, 0, 0, 0);
break;
}
client = client->next;
}
}
void MacIOGPIO::interruptOccurred(OSObject *me, IOInterruptEventSource *source,
int count)
{
DLOG("MacIOGPIO::interruptOccurred got callback!!\n");
MacIOGPIO *self = (MacIOGPIO *)me;
if (self) self->handleInterrupt(source, count);
}
#ifdef super
#undef super
#endif
#define super IOService
OSDefineMetaClassAndStructors(MacIOGPIODevice, IOService)
bool MacIOGPIODevice::compareName(OSString *name, OSString **matched = 0) const
{
return(IODTCompareNubName(this, name, matched)
|| super::compareName(name, matched));
}