#include <IOKit/IOTypes.h>
#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOService.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/pwr_mgt/IOPMPrivate.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOLocks.h>
#include <IOKit/IOPlatformExpert.h>
#include "IOPlatformFunction.h"
#include <IOI2C/IOI2CDevice.h>
#include <IOKit/IOUserClient.h>
#if (defined(I2C_DEBUG) && I2C_DEBUG)
#define DLOG(fmt, args...) kprintf(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define I2C_ERRLOG 1
#if (defined(I2C_ERRLOG) && I2C_ERRLOG)
#define ERRLOG(fmt, args...) do{kprintf(fmt, ## args);IOLog(fmt, ## args);}while(0)
#else
#define ERRLOG(fmt, args...)
#endif
#define I2C_DLOGPWR 1
#if (defined(I2C_DLOGPWR) && I2C_DLOGPWR)
#define DLOGPWR(fmt, args...) kprintf(fmt, ## args)
#else
#define DLOGPWR(fmt, args...)
#endif
#define I2C_DEBUG_VERBOSE 1
#if (defined(I2C_DEBUG_VERBOSE) && I2C_DEBUG_VERBOSE)
#define DLOGI2C(opt, fmt, args...) do{if(opt&kI2COption_VerboseLog)kprintf(fmt, ## args);}while(0)
#else
#define DLOGI2C(opt, fmt, args...)
#endif
#ifdef kUSE_IOLOCK
#define I2CLOCK IOLockLock(fClientLock)
#define I2CUNLOCK {IOLockUnlock(fClientLock);IOSleep(0);}
#else
#define I2CLOCK semaphore_wait(fClientSem)
#define I2CUNLOCK semaphore_signal(fClientSem)
#endif
#pragma mark
#pragma mark *** IOI2CDevice class ***
#pragma mark
#define super IOService
OSDefineMetaClassAndStructors( IOI2CDevice, IOService )
bool IOI2CDevice::init(
OSDictionary *dict)
{
DLOG("IOI2CDevice::init\n");
if (!super::init(dict))
return false;
fDeviceOffline = TRUE;
fPlatformFuncArray = 0;
if (0 == (reserved = (ExpansionData *)IOMalloc(sizeof(struct ExpansionData))))
return false;
return true;
}
bool
IOI2CDevice::start(
IOService *provider)
{
OSData *regprop;
IOReturn status;
DLOG("+IOI2CDevice::start\n");
if (false == super::start(provider))
return false;
fProvider = provider;
if (regprop = OSDynamicCast(OSData, fProvider->getProperty("reg")))
{
fI2CAddress = *((UInt32 *)regprop->getBytesNoCopy());
if (false == isI2C10BitAddress(fI2CAddress))
fI2CAddress &= 0xff;
}
else
{
ERRLOG("-IOI2CDevice::start no \"reg\" property\n");
return false;
}
if (fProvider->getProperty("AAPL,no-power"))
fStateFlags |= kStateFlags_DISABLE_PM;
if (kIOReturnSuccess != (status = initI2CResources()))
{
ERRLOG("-IOI2CDevice@%lx::start initI2CResources failed:0x%lx\n", fI2CAddress, (UInt32)status);
freeI2CResources();
return false;
}
AbsoluteTime deadline, currentTime;
clock_interval_to_deadline(3, kSecondScale, &deadline);
while (isI2COffline())
{
IOSleep(10);
clock_get_uptime(¤tTime);
if ( CMP_ABSOLUTETIME(¤tTime, &deadline) > 0 )
{
ERRLOG("-IOI2CDevice@%lx::start timed out waiting to power on\n", fI2CAddress);
freeI2CResources();
return false;
}
}
const char *name;
if ( (name = getName()) && (0 == strcmp(name, "IOI2CDevice")) )
{
DLOG("%s@%lx::start enabling on demand PF functions\n", name, fI2CAddress);
fEnableOnDemandPlatformFunctions = true;
registerService();
}
DLOG("-IOI2CDevice@%lx::start\n",fI2CAddress);
return true;
}
void
IOI2CDevice::stop (IOService * provider)
{
DLOG("IOI2CDevice@%lx::stop\n",fI2CAddress);
freeI2CResources();
super::stop(provider);
}
void
IOI2CDevice::free ( void )
{
DLOG("IOI2CDevice@%lx::free\n",fI2CAddress);
freeI2CResources();
if (reserved) { IOFree(reserved, sizeof(struct ExpansionData)); reserved = 0; }
super::free();
}
#pragma mark
#pragma mark *** I2C resources init and free methods ***
#pragma mark
IOReturn
IOI2CDevice::initI2CResources(void)
{
IOReturn status;
DLOG("+IOI2CDevice@%lx::initI2CResources\n",fI2CAddress);
symLockI2CBus = OSSymbol::withCStringNoCopy(kLockI2Cbus);
symUnlockI2CBus = OSSymbol::withCStringNoCopy(kUnlockI2Cbus);
symWriteI2CBus = OSSymbol::withCStringNoCopy(kWriteI2Cbus);
symReadI2CBus = OSSymbol::withCStringNoCopy(kReadI2Cbus);
symClientWrite = OSSymbol::withCStringNoCopy(kIOI2CClientWrite);
symClientRead = OSSymbol::withCStringNoCopy(kIOI2CClientRead);
symPowerInterest = OSSymbol::withCStringNoCopy("IOI2CPowerStateInterest");
#ifdef kUSE_IOLOCK
fClientLock = IOLockAlloc();
#else
if (kIOReturnSuccess != (status = semaphore_create(current_task(), (semaphore**)&fClientSem, SYNC_POLICY_FIFO, 1)))
return status;
#endif
if (!symLockI2CBus || !symUnlockI2CBus || !symWriteI2CBus || !symReadI2CBus
#ifdef kUSE_IOLOCK
|| !fClientLock
#else
|| !fClientSem
#endif
)
return kIOReturnNoMemory;
if (kIOReturnSuccess != (status = InitializePlatformFunctions()))
return status;
if (fStateFlags & kStateFlags_DISABLE_PM)
fDeviceOffline = FALSE;
else
{
if (kIOReturnSuccess != (status = InitializePowerManagement()))
return status;
}
DLOG("-IOI2CDevice@%lx::initI2CResources\n",fI2CAddress);
return status;
}
void
IOI2CDevice::freeI2CResources(void)
{
if (fStateFlags & kStateFlags_TEARDOWN)
return;
fStateFlags |= kStateFlags_TEARDOWN;
DLOG("+IOI2CDevice@%lx::freeI2CResources %x\n",fI2CAddress, fStateFlags);
#ifdef kUSE_IOLOCK
if(fClientLock)
#else
if(fClientSem)
#endif
I2CLOCK;
fDeviceOffline = TRUE;
#ifdef kUSE_IOLOCK
if(fClientLock)
#else
if(fClientSem)
#endif
I2CUNLOCK;
DLOG("+IOI2CDevice@%lx::freeI2CResources\n",fI2CAddress);
if (initialized)
{
DLOGPWR("+IOI2CDevice@%lx::freeI2CResources requesting power OFF\n",fI2CAddress);
changePowerStateTo(kIOI2CPowerState_OFF);
AbsoluteTime deadline, currentTime;
clock_interval_to_deadline(20, kSecondScale, &deadline);
while (fCurrentPowerState != kIOI2CPowerState_OFF)
{
IOSleep(10);
clock_get_uptime(¤tTime);
if ( CMP_ABSOLUTETIME(¤tTime, &deadline) > 0 )
{
ERRLOG("IOI2CDevice@%lx::freeI2CResources timed out waiting to power off\n", fI2CAddress);
break;
}
}
DLOGPWR("+IOI2CDevice@%lx::freeI2CResources calling PMStop\n",fI2CAddress);
PMstop();
}
DLOG("IOI2CDevice@%lx::freeI2CResources 1\n",fI2CAddress);
if (fPowerStateThreadCall)
{
thread_call_cancel(fPowerStateThreadCall);
thread_call_free(fPowerStateThreadCall);
fPowerStateThreadCall = 0;
}
fProvider->callPlatformFunction("IOI2CPowerStateInterest", FALSE, (void *)this, (void *)false, 0, 0);
DLOG("IOI2CDevice@%lx::freeI2CResources 2\n",fI2CAddress);
if (symLockI2CBus) { symLockI2CBus->release(); symLockI2CBus = 0; }
if (symUnlockI2CBus) { symUnlockI2CBus->release(); symUnlockI2CBus = 0; }
if (symWriteI2CBus) { symWriteI2CBus->release(); symWriteI2CBus = 0; }
if (symReadI2CBus) { symReadI2CBus->release(); symReadI2CBus = 0; }
DLOG("IOI2CDevice@%lx::freeI2CResources 3\n",fI2CAddress);
#ifdef kUSE_IOLOCK
if (fClientLock) { IOLockFree(fClientLock); fClientLock = 0; }
#else
if (fClientSem) { semaphore_destroy(current_task(), fClientSem); fClientSem = 0; }
#endif
DLOG("IOI2CDevice@%lx::freeI2CResources 4\n",fI2CAddress);
if (reserved)
{
if (symClientRead) { symClientRead->release(); symClientRead = 0; }
if (symClientWrite) { symClientWrite->release(); symClientWrite = 0; }
if (symPowerInterest) { symPowerInterest->release(); symPowerInterest = 0; }
}
DLOG("-IOI2CDevice@%lx::freeI2CResources\n",fI2CAddress);
}
#pragma mark
#pragma mark *** IOI2CDevice user client creation ***
#pragma mark
IOReturn
IOI2CDevice::newUserClient(
task_t owningTask,
void *securityID,
UInt32 type,
OSDictionary *properties,
IOUserClient **handler)
{
IOUserClient *client;
OSObject *temp;
DLOG("%s::newUserClient\n", getName());
if (type != kIOI2CUserClientType)
return super::newUserClient(owningTask,securityID,type,properties,handler);
if (IOUserClient::clientHasPrivilege(securityID, "root") != kIOReturnSuccess)
{
ERRLOG("%s::newUserClient: Can't create user client, not privileged\n", getName());
return kIOReturnNotPrivileged;
}
temp = OSMetaClass::allocClassWithName("IOI2CUserClient");
if (!temp)
return kIOReturnNoMemory;
if (OSDynamicCast(IOUserClient, temp))
client = (IOUserClient *) temp;
else
{
temp->release();
return kIOReturnUnsupported;
}
if ( !client->initWithTask(owningTask, securityID, type, properties) )
{
client->release();
return kIOReturnBadArgument;
}
if ( !client->attach(this) )
{
client->release();
return kIOReturnUnsupported;
}
if ( !client->start(this) )
{
client->detach(this);
client->release();
return kIOReturnUnsupported;
}
*handler = client;
return kIOReturnSuccess;
}
#pragma mark
#pragma mark *** power management methods ***
#pragma mark
IOReturn
IOI2CDevice::InitializePowerManagement(void)
{
IOReturn status;
static const IOPMPowerState ourPowerStates[kIOI2CPowerState_COUNT] =
{
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{1, kIOPMSleepCapability, kIOPMSleep, kIOPMSleep, 0, 0, 0, 0, 0, 0, 0, 0},
{1, kIOPMDeviceUsable, kIOPMDoze, kIOPMDoze, 0, 0, 0, 0, 0, 0, 0, 0},
{1, kIOPMDeviceUsable, kIOPMPowerOn, kIOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0}
};
DLOG("IOI2CDevice@%lx::InitializePowerManagement\n", fI2CAddress);
PMinit();
fProvider->joinPMtree( this);
if (0 == (fPowerStateThreadCall = thread_call_allocate(&IOI2CDevice::sPowerStateThreadCall, (thread_call_param_t) this)))
{
ERRLOG("IOI2CDevice Failed to allocate threadcall.\n");
return kIOReturnNoResources;
}
if (kIOReturnSuccess != (status = fProvider->callPlatformFunction("IOI2CPowerStateInterest", FALSE, (void *)this, (void *)true, NULL, NULL)))
{
ERRLOG("IOI2CDevice register IOI2CPowerStateInterest falied\n");
return status;
}
if (kIOReturnSuccess != (status = registerPowerDriver( this, (IOPMPowerState *) ourPowerStates, kIOI2CPowerState_COUNT )))
{
ERRLOG("IOI2CDevice Failed to registerPowerDriver.\n");
return status;
}
return status;
}
unsigned long
IOI2CDevice::maxCapabilityForDomainState(
IOPMPowerFlags domainState)
{
unsigned long maxCapability = super::maxCapabilityForDomainState (domainState );
DLOG("IOI2CDevice@%lx:maxCapabilityForDomainState(0x%lx) == 0x%lx\n", fI2CAddress, (UInt32)domainState, (UInt32)maxCapability);
return maxCapability;
}
IOReturn
IOI2CDevice::message(
UInt32 type,
IOService *provider,
void *argument)
{
if (type == 0x1012c)
{
if (provider == NULL)
return kIOReturnBadArgument;
if (fPowerStateThreadCall == 0)
return kIOReturnUnsupported;
if (fStateFlags & kStateFlags_TEARDOWN)
return kIOReturnUnsupported;
fSysPowerRef = provider;
thread_call_enter1(fPowerStateThreadCall, (thread_call_param_t)(kIOI2CPowerState_OFF));
return kIOReturnSuccess;
}
return super::message(type, provider, argument);
}
IOReturn IOI2CDevice::setPowerState(
unsigned long newPowerState,
IOService *dontCare)
{
kprintf("IOI2CDevice@%lx::setPowerState called with state:%lu\n", fI2CAddress, newPowerState);
if (pm_vars == NULL)
return IOPMAckImplied;
if (fPowerStateThreadCall == 0)
return IOPMAckImplied;
if (fCurrentPowerState == newPowerState)
return IOPMAckImplied;
thread_call_enter1(fPowerStateThreadCall, (thread_call_param_t)newPowerState);
return kSetPowerStateTimeout;
}
void
IOI2CDevice::sPowerStateThreadCall(
thread_call_param_t p0,
thread_call_param_t p1)
{
IOI2CDevice *self;
unsigned long newPowerState = (unsigned long)p1;
self = OSDynamicCast(IOI2CDevice, (OSMetaClassBase *)p0);
if (self == NULL)
panic("IOI2CDevice::sPowerStateThreadCall with unknown \"this\" type\n");
DLOG("IOI2CDevice@%lx::sPowerStateThreadCall called with state:%lu\n", self->fI2CAddress, newPowerState);
self->powerStateThreadCall(newPowerState);
}
void
IOI2CDevice::powerStateThreadCall(
unsigned long newPowerState)
{
if (newPowerState == kIOI2CPowerState_OFF)
{
DLOGPWR("IOI2CDevice@%lx transition to OFF\n", fI2CAddress);
I2CLOCK;
fPowerThreadID = current_thread(); fClientIOBlocked = TRUE;
I2CUNLOCK;
if (fSysPowerRef)
{
DLOGPWR("IOI2CDevice@%lx process SHUTDOWN event\n", fI2CAddress);
processPowerEvent(kI2CPowerEvent_SHUTDOWN);
}
else
{
DLOGPWR("IOI2CDevice@%lx process power OFF event\n", fI2CAddress);
if (0 == (fStateFlags & kStateFlags_TEARDOWN))
processPowerEvent(kI2CPowerEvent_OFF);
}
fDeviceOffline = TRUE; fClientIOBlocked = FALSE;
DLOGPWR("IOI2CDevice@%lx device OFFLINE\n------------------\n", fI2CAddress);
}
else
if (newPowerState == kIOI2CPowerState_SLEEP)
{
DLOGPWR("IOI2CDevice@%lx transition to SLEEP\n", fI2CAddress);
I2CLOCK;
fPowerThreadID = current_thread(); fClientIOBlocked = TRUE;
I2CUNLOCK;
if (fCurrentPowerState == kIOI2CPowerState_ON)
{
DLOGPWR("IOI2CDevice@%lx process SLEEP event\n", fI2CAddress);
performFunctionsWithFlags(kIOPFFlagOnSleep);
processPowerEvent(kI2CPowerEvent_SLEEP);
}
fDeviceOffline = TRUE; fClientIOBlocked = FALSE;
DLOGPWR("IOI2CDevice@%lx device OFFLINE\n------------------\n", fI2CAddress);
}
else
if (newPowerState == kIOI2CPowerState_DOZE)
{
DLOGPWR("IOI2CDevice@%lx transition to DOZE\n", fI2CAddress);
processPowerEvent(kI2CPowerEvent_DOZE);
}
else
if (newPowerState == kIOI2CPowerState_ON)
{
DLOGPWR("IOI2CDevice@%lx transition to ON\n", fI2CAddress);
I2CLOCK;
fPowerThreadID = current_thread(); fClientIOBlocked = TRUE; fDeviceOffline = FALSE;
I2CUNLOCK;
if (fCurrentPowerState == kIOI2CPowerState_SLEEP)
{
DLOGPWR("IOI2CDevice@%lx process WAKE event\n", fI2CAddress);
performFunctionsWithFlags(kIOPFFlagOnWake);
processPowerEvent(kI2CPowerEvent_WAKE);
}
else
{
if (0 == (fStateFlags & kStateFlags_STARTUP))
{
fStateFlags |= kStateFlags_STARTUP;
DLOGPWR("IOI2CDevice@%lx process STARTUP event\n", fI2CAddress);
performFunctionsWithFlags(kIOPFFlagOnInit);
processPowerEvent(kI2CPowerEvent_STARTUP);
}
else
{
DLOGPWR("IOI2CDevice@%lx process power ON event\n", fI2CAddress);
processPowerEvent(kI2CPowerEvent_ON);
}
}
DLOGPWR("IOI2CDevice@%lx::setPowerState device ONLINE\n------------------\n", fI2CAddress);
fClientIOBlocked = FALSE; }
else
{
DLOGPWR("IOI2CDevice@%lx ERROR transition to invalid state:%lu\n", fI2CAddress, newPowerState);
return;
}
fCurrentPowerState = newPowerState;
if (fSysPowerRef)
{
IOService *service = OSDynamicCast(IOService, (OSMetaClassBase *)fSysPowerRef);
if (service)
{
kprintf("IOI2CDevice@%lx acknowledgeNotification\n", fI2CAddress);
service->acknowledgeNotification(this, 0);
}
fSysPowerRef = NULL;
}
else
{
kprintf("IOI2CDevice@%lx acknowledgeSetPowerState: %lu\n", fI2CAddress, fCurrentPowerState);
acknowledgeSetPowerState();
}
}
#pragma mark
#pragma mark *** IOI2CDevice subclass client API methods ***
#pragma mark
void IOI2CDevice::processPowerEvent(UInt32 eventType) {}
bool
IOI2CDevice::isI2COffline(void)
{
if (fDeviceOffline || (fClientIOBlocked && (fPowerThreadID != current_thread())))
return true;
return false;
}
UInt32
IOI2CDevice::getI2CAddress(void)
{
return fI2CAddress;
}
IOReturn
IOI2CDevice::lockI2CBus(
UInt32 *clientKeyRef)
{
IOReturn status = kIOReturnSuccess;
UInt32 clientLockKey;
if (clientKeyRef == NULL)
{
ERRLOG("IOI2CDevice@%lx::lockI2CBus bad args\n", fI2CAddress);
return kIOReturnBadArgument;
}
if (ml_at_interrupt_context())
{
ERRLOG("IOI2CDevice@%lx::lockI2CBus from primary interrupt context not permitted\n", fI2CAddress);
*clientKeyRef = kIOI2C_CLIENT_KEY_INVALID;
return kIOReturnNotPermitted;
}
if (isI2COffline())
{
ERRLOG("IOI2CDevice@%lx::lockI2CBus device is offline\n", fI2CAddress);
*clientKeyRef = kIOI2C_CLIENT_KEY_INVALID;
status = kIOReturnOffline;
}
I2CLOCK;
if (isI2COffline())
{
ERRLOG("IOI2CDevice@%lx::lockI2CBus lock canceled: device is offline\n", fI2CAddress);
I2CUNLOCK;
*clientKeyRef = kIOI2C_CLIENT_KEY_INVALID;
return kIOReturnOffline;
}
status = fProvider->callPlatformFunction(symLockI2CBus, false, (void *)0, (void *)&clientLockKey, (void *)0, (void *)0);
if (kIOReturnSuccess != status)
{
ERRLOG("IOI2CDevice@%lx::lockI2CBus - lock canceled: controller lockI2CBus failed:0x%lx\n", fI2CAddress, (UInt32)status);
I2CUNLOCK;
*clientKeyRef = kIOI2C_CLIENT_KEY_INVALID;
return status;
}
*clientKeyRef = clientLockKey;
return status;
}
IOReturn
IOI2CDevice::unlockI2CBus(
UInt32 clientKey)
{
IOReturn status;
if (ml_at_interrupt_context())
{
ERRLOG("IOI2CDevice@%lx::unlockI2CBus from primary interrupt context not permitted\n", fI2CAddress);
return kIOReturnNotPermitted;
}
if (isI2COffline())
{
ERRLOG("IOI2CDevice@%lx::unlockI2CBus device is offline\n", fI2CAddress);
return kIOReturnOffline;
}
status = fProvider->callPlatformFunction(symUnlockI2CBus, false, (void *)0, (void *)clientKey, (void *)0, (void *)0);
if (kIOReturnSuccess != status)
{
ERRLOG("IOI2CDevice@%lx::unlockI2CBus controller unlock failed key:%lx status:0x%x\n", fI2CAddress, clientKey, status);
return status;
}
I2CUNLOCK;
return status;
}
IOReturn
IOI2CDevice::readI2C(
IOI2CCommand *cmd,
UInt32 clientKey)
{
IOReturn status;
if (cmd == NULL)
return kIOReturnBadArgument;
if (isI2COffline())
{
ERRLOG("IOI2CDevice@%lx::readI2C device is offline\n", fI2CAddress);
status = kIOReturnOffline;
}
else
if (clientKey == kIOI2C_CLIENT_KEY_DEFAULT)
{
if (kIOReturnSuccess == (status = lockI2CBus(&clientKey)))
{
cmd->address = getI2CAddress();
DLOGI2C((cmd->options), "IOI2CDevice@%lx::readI2C cmd key:%lx, B:%lx, A:%lx S:%lx, L:%lx, M:%lx\n",
fI2CAddress, clientKey, cmd->bus, cmd->address, cmd->subAddress, cmd->count, cmd->mode);
status = fProvider->callPlatformFunction(symReadI2CBus, false, (void *)cmd, (void *)clientKey, (void *)0, (void *)0);
unlockI2CBus(clientKey);
}
}
else
{
cmd->address = getI2CAddress();
DLOGI2C((cmd->options), "IOI2CDevice@%lx::readI2C cmd key:%lx, B:%lx, A:%lx S:%lx, L:%lx, M:%lx\n",
fI2CAddress, clientKey, cmd->bus, cmd->address, cmd->subAddress, cmd->count, cmd->mode);
status = fProvider->callPlatformFunction(symReadI2CBus, false, (void *)cmd, (void *)clientKey, (void *)0, (void *)0);
}
return status;
}
IOReturn
IOI2CDevice::writeI2C(
IOI2CCommand *cmd,
UInt32 clientKey)
{
IOReturn status;
if (cmd == NULL)
return kIOReturnBadArgument;
if (isI2COffline())
{
ERRLOG("IOI2CDevice@%lx::writeI2C device is offline\n", fI2CAddress);
status = kIOReturnOffline;
}
else
if (clientKey == kIOI2C_CLIENT_KEY_DEFAULT)
{
if (kIOReturnSuccess == (status = lockI2CBus(&clientKey)))
{
cmd->address = getI2CAddress();
DLOGI2C((cmd->options), "IOI2CDevice@%lx::writeI2C cmd key:%lx, B:%lx, A:%lx S:%lx, L:%lx, M:%lx\n",
fI2CAddress, clientKey, cmd->bus, cmd->address, cmd->subAddress, cmd->count, cmd->mode);
status = fProvider->callPlatformFunction(symWriteI2CBus, false, (void *)cmd, (void *)clientKey, (void *)0, (void *)0);
unlockI2CBus(clientKey);
}
}
else
{
cmd->address = getI2CAddress();
DLOGI2C((cmd->options), "IOI2CDevice@%lx::writeI2C cmd key:%lx, B:%lx, A:%lx S:%lx, L:%lx, M:%lx\n",
fI2CAddress, clientKey, cmd->bus, cmd->address, cmd->subAddress, cmd->count, cmd->mode);
status = fProvider->callPlatformFunction(symWriteI2CBus, false, (void *)cmd, (void *)clientKey, (void *)0, (void *)0);
}
return status;
}
IOReturn
IOI2CDevice::writeI2C(
UInt32 subAddress,
UInt8 *data,
UInt32 count,
UInt32 clientKey,
UInt32 mode,
UInt32 retries,
UInt32 timeout_uS,
UInt32 options)
{
IOI2CCommand cmd = {0};
cmd.command = kI2CCommand_Write;
cmd.mode = mode;
cmd.subAddress = subAddress;
cmd.count = count;
cmd.buffer = data;
cmd.retries = retries;
cmd.timeout_uS = timeout_uS;
cmd.options = options;
return writeI2C(&cmd, clientKey);
}
IOReturn
IOI2CDevice::readI2C(
UInt32 subAddress,
UInt8 *data,
UInt32 count,
UInt32 clientKey,
UInt32 mode,
UInt32 retries,
UInt32 timeout_uS,
UInt32 options)
{
IOI2CCommand cmd = {0};
cmd.command = kI2CCommand_Read;
cmd.mode = mode;
cmd.subAddress = subAddress;
cmd.count = count;
cmd.buffer = data;
cmd.retries = retries;
cmd.timeout_uS = timeout_uS;
cmd.options = options;
return readI2C(&cmd, clientKey);
}
#pragma mark
#pragma mark *** External client callPlatformFunction method ***
#pragma mark
IOReturn
IOI2CDevice::callPlatformFunction(
const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4 )
{
IOReturn status;
if (0 == functionName)
return kIOReturnBadArgument;
if (0 == (fStateFlags & kStateFlags_TEARDOWN))
{
if (symReadI2CBus->isEqualTo(functionName))
return readI2C((IOI2CCommand *)param1, (UInt32)param2);
else
if (symWriteI2CBus->isEqualTo(functionName))
return writeI2C((IOI2CCommand *)param1, (UInt32)param2);
else
if (symLockI2CBus->isEqualTo(functionName))
return lockI2CBus((UInt32 *)param2);
else
if (symUnlockI2CBus->isEqualTo(functionName))
return unlockI2CBus((UInt32)param2);
else
if (symClientRead->isEqualTo(functionName))
return readI2C((UInt32)param1, (UInt8 *)param2, (UInt32)param3, (UInt32)param4);
else
if (symClientWrite->isEqualTo(functionName))
return writeI2C((UInt32)param1, (UInt8 *)param2, (UInt32)param3, (UInt32)param4);
if (fEnableOnDemandPlatformFunctions)
{
const char *cstr;
if ((cstr = functionName->getCStringNoCopy()) && (0 == strncmp("platform-do-", cstr, strlen("platform-do-"))))
{
IOPlatformFunction *pfFunc;
if (kIOReturnSuccess == (status = getPlatformFunction(functionName, &pfFunc, kIOPFFlagOnDemand)))
return performFunction (pfFunc, param1, param2, param3, param4);
if (kIOReturnNotFound != status)
return status;
}
}
}
return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
}
#pragma mark
#pragma mark *** IOPlatformFunction API methods ***
#pragma mark
IOReturn
IOI2CDevice::InitializePlatformFunctions(void)
{
IOReturn status = kIOReturnSuccess;
const OSSymbol *temp_sym;
if (fPlatformFuncArray)
return kIOReturnSuccess;
IOPlatformExpert *pexpert = OSDynamicCast(IOPlatformExpert, getPlatform());
if (0 == pexpert)
{
ERRLOG("IOI2CDevice::InitializePlatformFunctions ERROR no platform expert\n");
return kIOReturnSuccess;
}
if (temp_sym = OSSymbol::withCString("InstantiatePlatformFunctions"))
{
status = pexpert->callPlatformFunction(temp_sym, false, (void *)fProvider, (void *)&fPlatformFuncArray, NULL, NULL);
temp_sym->release();
}
DLOG("IOI2CDevice::InitializePlatformFunctions returned from platform expert:%x\n", status);
if ((status == kIOReturnSuccess) && fPlatformFuncArray)
{
IOPlatformFunction *func;
UInt32 i;
UInt32 count;
UInt32 flags;
count = fPlatformFuncArray->getCount();
for (i = 0; i < count; i++)
{
if (func = OSDynamicCast(IOPlatformFunction, fPlatformFuncArray->getObject(i)))
{
flags = func->getCommandFlags();
if ((flags & kIOPFFlagOnDemand) || (flags & kIOPFFlagIntGen))
func->publishPlatformFunction(this);
}
}
}
return status;
}
IOReturn
IOI2CDevice::getPlatformFunction (
const OSSymbol *functionSym,
IOPlatformFunction **funcRef,
UInt32 flags)
{
IOReturn status = kIOReturnNotFound;
UInt32 count, i;
IOPlatformFunction *func;
#define kIOI2CPFFlagsMask (kIOPFFlagOnInit|kIOPFFlagOnTerm|kIOPFFlagOnSleep|kIOPFFlagOnWake|kIOPFFlagOnDemand)
if (flags == 0)
flags = kIOI2CPFFlagsMask;
if (fPlatformFuncArray)
{
count = fPlatformFuncArray->getCount();
for (i = 0; i < count; i++)
{
if (func = OSDynamicCast(IOPlatformFunction, fPlatformFuncArray->getObject(i)))
{
if (functionSym->isEqualTo(func->getPlatformFunctionName()))
{
if (func->getCommandFlags() & flags)
{
status = kIOReturnSuccess;
*funcRef = func;
break;
}
}
}
}
}
return status;
}
void
IOI2CDevice::performFunctionsWithFlags(
UInt32 flags)
{
UInt32 count, i;
IOPlatformFunction *func;
if (0 == fPlatformFuncArray)
return;
count = fPlatformFuncArray->getCount();
for (i = 0; i < count; i++)
{
if (func = OSDynamicCast(IOPlatformFunction, fPlatformFuncArray->getObject(i)))
{
if (func->getCommandFlags() & flags)
performFunction(func);
}
}
}
IOReturn
IOI2CDevice::performFunction (
const char *funcName,
void *pfParam1,
void *pfParam2,
void *pfParam3,
void *pfParam4)
{
IOReturn status;
const OSSymbol *funcSym;
if (0 == (funcSym = OSSymbol::withCString(funcName)))
return kIOReturnNoMemory;
status = performFunction(funcSym, pfParam1, pfParam2, pfParam3, pfParam4);
funcSym->release();
return status;
}
IOReturn
IOI2CDevice::performFunction (
const OSSymbol *funcSym,
void *pfParam1,
void *pfParam2,
void *pfParam3,
void *pfParam4)
{
IOReturn status;
IOPlatformFunction *func;
if (kIOReturnSuccess != (status = getPlatformFunction (funcSym, &func, kIOPFFlagOnDemand)))
return status;
return performFunction(func, pfParam1, pfParam2, pfParam3, pfParam4);
}
IOReturn
IOI2CDevice::performFunction(
IOPlatformFunction *func,
void *pfParam1,
void *pfParam2,
void *pfParam3,
void *pfParam4)
{
IOReturn status = kIOReturnSuccess;
IOPlatformFunctionIterator *iter;
UInt8 scratchBuffer[kI2CPF_READ_BUFFER_LEN] = {0};
UInt8 readBuffer[kI2CPF_READ_BUFFER_LEN] = {0};
UInt32 mode = kI2CMode_Unspecified;
UInt8 *maskBytes, *valueBytes;
unsigned delayMS;
UInt32 cmd, cmdLen, param1, param2, param3, param4, param5,
param6, param7, param8, param9, param10;
UInt32 key = 0;
bool i2cIsLocked = FALSE;
bool isI2CFunction = FALSE;
DLOG ("IOI2CDevice::performFunction(%lx) - entered\n", fI2CAddress);
if (!func)
return kIOReturnBadArgument;
if (!(iter = func->getCommandIterator()))
return kIOReturnNotFound;
while (iter->getNextCommand (&cmd, &cmdLen, ¶m1, ¶m2, ¶m3, ¶m4,
¶m5, ¶m6, ¶m7, ¶m8, ¶m9, ¶m10, (UInt32 *)&status)
&& (status != kIOPFNoError))
{
if ((cmd == kCommandReadI2CSubAddr) || (cmd == kCommandWriteI2CSubAddr))
{
isI2CFunction = TRUE;
break;
}
}
if (status == kIOReturnSuccess)
{
if (isI2CFunction)
{
if (kIOReturnSuccess == (status = lockI2CBus(&key)))
i2cIsLocked = TRUE;
}
}
if (status == kIOReturnSuccess)
{
iter->reset();
while (iter->getNextCommand (&cmd, &cmdLen, ¶m1, ¶m2, ¶m3, ¶m4,
¶m5, ¶m6, ¶m7, ¶m8, ¶m9, ¶m10, (UInt32 *)&status)
&& (status != kIOPFNoError))
{
DLOG ("IOI2CDevice::performFunction(%lx) - 1)0x%lx, 2)0x%lx, 3)0x%lx, 4)0x%lx, 5)0x%lx,"
"6)0x%lx, 7)0x%lx, 8)0x%lx, 9)0x%lx, 10)0x%lx\n", getI2CAddress(), param1, param2, param3,
param4, param5, param6, param7, param8, param9, param10);
switch (cmd)
{
case kCommandDelay:
delayMS = param1 / 1000; DLOG("IOI2CDevice::performFunction(%lx) delay %u\n", getI2CAddress(), delayMS);
if (delayMS != 0)
IOSleep(delayMS);
break;
case kCommandReadI2CSubAddr:
if (param2 > kI2CPF_READ_BUFFER_LEN)
{
status = kIOPFBadCmdLength;
ERRLOG("IOI2CDevice::performFunction(%lx) r-sub operation too big!\n", getI2CAddress());
break;
}
if (mode == kI2CMode_Unspecified)
{
status = kIOReturnUnsupportedMode;
ERRLOG("IOI2CDevice::performFunction(%lx) Rd Unspecified I2C mode\n", getI2CAddress());
break;
}
status = readI2C(param1, readBuffer, param2, key, mode);
break;
case kCommandWriteI2CSubAddr:
if (mode == kI2CMode_Unspecified)
{
status = kIOReturnUnsupportedMode;
ERRLOG("IOI2CDevice::performFunction(%lx) Wt Unspecified I2C mode\n", getI2CAddress());
break;
}
DLOG("IOI2CDevice::performFunction(%lx) w-sub %lx len %lx data", getI2CAddress(), param1, param2);
status = writeI2C((UInt32) param1, (UInt8 *) param3, (UInt32) param2, key, mode);
break;
case kCommandI2CMode:
switch (param1)
{
default:
case kPFMode_Dumb: status = kIOReturnUnsupportedMode; break;
case kPFMode_Standard: mode = kI2CMode_Standard; break;
case kPFMode_Subaddress: mode = kI2CMode_StandardSub; break;
case kPFMode_Combined: mode = kI2CMode_Combined; break;
}
DLOG("IOI2CDevice::performFunction(%lx) PF mode %lx\n", getI2CAddress(), mode);
break;
case kCommandRMWI2CSubAddr:
if ((param2 > kI2CPF_READ_BUFFER_LEN) || (param3 > kI2CPF_READ_BUFFER_LEN) || (param4 > kI2CPF_READ_BUFFER_LEN) || (param3 > param2)) {
ERRLOG("IOI2CDevice::performFunction(%lx) invalid mw-sub cycle\n", getI2CAddress());
status = kIOReturnAborted;
break;
}
if (mode == kI2CMode_Unspecified)
{
status = kIOReturnUnsupportedMode;
ERRLOG("IOI2CDevice::performFunction(%lx) RMW Unspecified I2C mode\n", getI2CAddress());
break;
}
maskBytes = (UInt8 *) param5;
valueBytes = (UInt8 *) param6;
for (unsigned int index = 0; index < param2; index++) {
scratchBuffer[index] = ((valueBytes[index] & maskBytes[index]) |
(readBuffer[index] & ~maskBytes[index]));
}
DLOG("IOI2CDevice::performFunction(%lx) mw-sub %lx len %lx data", getI2CAddress(), param1, param4);
status = writeI2C((UInt8) param1, scratchBuffer, (UInt16) param4, key, mode);
break;
default:
ERRLOG ("IOI2CDevice::performFunction - bad command %ld\n", cmd);
status = kIOReturnAborted;
break;
}
if (status != kIOReturnSuccess)
break;
}
}
if (iter)
iter->release();
if (i2cIsLocked)
unlockI2CBus(key);
DLOG ("IOI2CDevice::performFunction - done status: %x\n", status);
return status;
}
#pragma mark
#pragma mark *** Space reserved for future IOI2CDevice expansion ***
#pragma mark
OSMetaClassDefineReservedUsed ( IOI2CDevice, 0 );
IOReturn
IOI2CDevice::getPlatformFunction (
const char *functionName,
IOPlatformFunction **funcRef,
UInt32 flags)
{
IOReturn status;
const OSSymbol *functionSym = OSSymbol::withCStringNoCopy(functionName);
status = getPlatformFunction(functionSym, funcRef, flags);
functionSym->release();
return status;
}
OSMetaClassDefineReservedUnused ( IOI2CDevice, 1 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 2 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 3 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 4 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 5 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 6 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 7 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 8 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 9 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 10 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 11 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 12 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 13 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 14 );
OSMetaClassDefineReservedUnused ( IOI2CDevice, 15 );