#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOEventSource.h>
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOTimeStamp.h>
#include <libkern/OSDebug.h>
#define super OSObject
OSDefineMetaClassAndStructors(IOWorkLoop, OSObject);
OSMetaClassDefineReservedUsed(IOWorkLoop, 0);
OSMetaClassDefineReservedUsed(IOWorkLoop, 1);
OSMetaClassDefineReservedUnused(IOWorkLoop, 2);
OSMetaClassDefineReservedUnused(IOWorkLoop, 3);
OSMetaClassDefineReservedUnused(IOWorkLoop, 4);
OSMetaClassDefineReservedUnused(IOWorkLoop, 5);
OSMetaClassDefineReservedUnused(IOWorkLoop, 6);
OSMetaClassDefineReservedUnused(IOWorkLoop, 7);
enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 };
#ifdef __ppc__
static inline void SETP(void *addr, unsigned int flag)
{ unsigned int *num = (unsigned int *) addr; *num |= flag; }
static inline void CLRP(void *addr, unsigned int flag)
{ unsigned int *num = (unsigned int *) addr; *num &= ~flag; }
static inline bool ISSETP(void *addr, unsigned int flag)
{ unsigned int *num = (unsigned int *) addr; return (*num & flag) != 0; }
#else
static inline void SETP(void *addr, unsigned int flag)
{ unsigned char *num = (unsigned char *) addr; *num |= flag; }
static inline void CLRP(void *addr, unsigned int flag)
{ unsigned char *num = (unsigned char *) addr; *num &= ~flag; }
static inline bool ISSETP(void *addr, unsigned int flag)
{ unsigned char *num = (unsigned char *) addr; return (*num & flag) != 0; }
#endif
#define fFlags loopRestart
bool IOWorkLoop::init()
{
if ( !super::init() )
return false;
if ( gateLock == NULL ) {
if ( !( gateLock = IORecursiveLockAlloc()) )
return false;
}
if ( workToDoLock == NULL ) {
if ( !(workToDoLock = IOSimpleLockAlloc()) )
return false;
IOSimpleLockInit(workToDoLock);
workToDo = false;
}
if ( controlG == NULL ) {
controlG = IOCommandGate::commandGate(
this,
OSMemberFunctionCast(
IOCommandGate::Action,
this,
&IOWorkLoop::_maintRequest));
if ( !controlG )
return false;
controlG->setWorkLoop(this);
if (addEventSource(controlG) != kIOReturnSuccess)
return false;
}
if ( workThread == NULL ) {
IOThreadFunc cptr = OSMemberFunctionCast(
IOThreadFunc,
this,
&IOWorkLoop::threadMain);
workThread = IOCreateThread(cptr, this);
if (!workThread)
return false;
}
return true;
}
IOWorkLoop *
IOWorkLoop::workLoop()
{
return IOWorkLoop::workLoopWithOptions(0);
}
IOWorkLoop *
IOWorkLoop::workLoopWithOptions(IOOptionBits options)
{
IOWorkLoop *me = new IOWorkLoop;
if (me && options) {
me->reserved = IONew(ExpansionData, 1);
if (!me->reserved) {
me->release();
return 0;
}
me->reserved->options = options;
}
if (me && !me->init()) {
me->release();
return 0;
}
return me;
}
void IOWorkLoop::free()
{
if (workThread) {
IOInterruptState is;
closeGate();
disableAllEventSources();
is = IOSimpleLockLockDisableInterrupt(workToDoLock);
SETP(&fFlags, kLoopTerminate);
thread_wakeup_one((void *) &workToDo);
IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
openGate();
}
else {
IOEventSource *event, *next;
for (event = eventChain; event; event = next) {
next = event->getNext();
event->setWorkLoop(0);
event->setNext(0);
event->release();
}
eventChain = 0;
if (controlG) {
controlG->release();
controlG = 0;
}
if (workToDoLock) {
IOSimpleLockFree(workToDoLock);
workToDoLock = 0;
}
if (gateLock) {
IORecursiveLockFree(gateLock);
gateLock = 0;
}
if (reserved) {
IODelete(reserved, ExpansionData, 1);
reserved = 0;
}
super::free();
}
}
IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
{
return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
}
IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
{
return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
}
void IOWorkLoop::enableAllEventSources() const
{
IOEventSource *event;
for (event = eventChain; event; event = event->getNext())
event->enable();
}
void IOWorkLoop::disableAllEventSources() const
{
IOEventSource *event;
for (event = eventChain; event; event = event->getNext())
if (event != controlG) event->disable();
}
void IOWorkLoop::enableAllInterrupts() const
{
IOEventSource *event;
for (event = eventChain; event; event = event->getNext())
if (OSDynamicCast(IOInterruptEventSource, event))
event->enable();
}
void IOWorkLoop::disableAllInterrupts() const
{
IOEventSource *event;
for (event = eventChain; event; event = event->getNext())
if (OSDynamicCast(IOInterruptEventSource, event))
event->disable();
}
#if KDEBUG
#define IOTimeClientS() \
do { \
IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \
(unsigned int) this, (unsigned int) event); \
} while(0)
#define IOTimeClientE() \
do { \
IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \
(unsigned int) this, (unsigned int) event); \
} while(0)
#define IOTimeWorkS() \
do { \
IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \
} while(0)
#define IOTimeWorkE() \
do { \
IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this); \
} while(0)
#else
#define IOTimeClientS()
#define IOTimeClientE()
#define IOTimeWorkS()
#define IOTimeWorkE()
#endif
bool IOWorkLoop::runEventSources()
{
bool res = false;
closeGate();
if (ISSETP(&fFlags, kLoopTerminate))
goto abort;
IOTimeWorkS();
bool more;
do {
CLRP(&fFlags, kLoopRestart);
more = false;
IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
workToDo = false;
IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
for (IOEventSource *evnt = eventChain; evnt; evnt = evnt->getNext()) {
IOTimeClientS();
more |= evnt->checkForWork();
IOTimeClientE();
if (ISSETP(&fFlags, kLoopTerminate))
goto abort;
else if (fFlags & kLoopRestart) {
more = true;
break;
}
}
} while (more);
res = true;
IOTimeWorkE();
abort:
openGate();
return res;
}
void IOWorkLoop::threadMain()
{
restartThread:
do {
if ( !runEventSources() )
goto exitThread;
IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
assert_wait((void *) &workToDo, false);
IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
thread_continue_t cptr = NULL;
if (!reserved || !(kPreciousStack & reserved->options))
cptr = OSMemberFunctionCast(
thread_continue_t, this, &IOWorkLoop::threadMain);
thread_block_parameter(cptr, this);
goto restartThread;
}
IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
} while(workToDo);
exitThread:
workThread = 0; free();
IOExitThread();
}
IOThread IOWorkLoop::getThread() const
{
return workThread;
}
bool IOWorkLoop::onThread() const
{
return (IOThreadSelf() == workThread);
}
bool IOWorkLoop::inGate() const
{
return IORecursiveLockHaveLock(gateLock);
}
void IOWorkLoop::signalWorkAvailable()
{
if (workToDoLock) {
IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
workToDo = true;
thread_wakeup_one((void *) &workToDo);
IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
}
}
void IOWorkLoop::openGate()
{
IORecursiveLockUnlock(gateLock);
}
void IOWorkLoop::closeGate()
{
IORecursiveLockLock(gateLock);
}
bool IOWorkLoop::tryCloseGate()
{
return IORecursiveLockTryLock(gateLock) != 0;
}
int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
{
return IORecursiveLockSleep(gateLock, event, interuptibleType);
}
void IOWorkLoop::wakeupGate(void *event, bool oneThread)
{
IORecursiveLockWakeup(gateLock, event, oneThread);
}
IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
void *arg0, void *arg1,
void *arg2, void *arg3)
{
IOReturn res;
closeGate();
res = (*inAction)(target, arg0, arg1, arg2, arg3);
openGate();
return res;
}
IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
{
maintCommandEnum command = (maintCommandEnum) (vm_address_t) inC;
IOEventSource *inEvent = (IOEventSource *) inD;
IOReturn res = kIOReturnSuccess;
switch (command)
{
case mAddEvent:
if (!inEvent->getWorkLoop()) {
SETP(&fFlags, kLoopRestart);
inEvent->retain();
inEvent->setWorkLoop(this);
inEvent->setNext(0);
if (!eventChain)
eventChain = inEvent;
else {
IOEventSource *event, *next;
for (event = eventChain; (next = event->getNext()); event = next)
;
event->setNext(inEvent);
}
}
break;
case mRemoveEvent:
if (inEvent->getWorkLoop()) {
if (eventChain == inEvent)
eventChain = inEvent->getNext();
else {
IOEventSource *event, *next;
event = eventChain;
while ((next = event->getNext()) && next != inEvent)
event = next;
if (!next) {
res = kIOReturnBadArgument;
break;
}
event->setNext(inEvent->getNext());
}
inEvent->setWorkLoop(0);
inEvent->setNext(0);
inEvent->release();
SETP(&fFlags, kLoopRestart);
}
break;
default:
return kIOReturnUnsupported;
}
return res;
}