IOSerialSessionSync.cpp [plain text]
#include <string.h>
#include <IOKit/assert.h>
#include <IOKit/system.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOConditionLock.h>
#include "IOSerialSessionSync.h"
#define STREAM ((IOSerialStreamSync *) fProvider)
#define super IOSerialStreamSync
OSDefineMetaClassAndStructors(IOSerialSessionSync, IOSerialStreamSync)
struct IOSerialSessionSyncEntry {
queue_chain_t fLink;
IOConditionLock *fSleepLock; IOConditionLock *fHandOverLock; IOSerialStreamSync *stream;
IOSerialSessionSync *session; int fRefCount; PTTypeT type; UInt8 waitP, waitN; };
class IOSerialSessionSyncGlobals {
private:
IOLock *fLock; queue_head_t fStreamSyncList;
public:
IOSerialSessionSyncGlobals();
~IOSerialSessionSyncGlobals();
inline bool isValid();
IOSerialSessionSyncEntry *createEntry(IOSerialStreamSync *stream, bool *cyclePortP);
void releaseEntry(IOSerialSessionSyncEntry *entry);
};
static IOSerialSessionSyncGlobals sSessionSyncGlobals;
IOSerialSessionSyncGlobals::IOSerialSessionSyncGlobals()
{
queue_init(& fStreamSyncList);
fLock = IOLockAlloc();
if (fLock)
IOLockInit(fLock);
}
IOSerialSessionSyncGlobals::~IOSerialSessionSyncGlobals()
{
queue_init(&fStreamSyncList);
if (fLock)
IOLockFree(fLock);
}
bool IOSerialSessionSyncGlobals::isValid()
{
return (fLock != 0);
}
IOSerialSessionSyncEntry *IOSerialSessionSyncGlobals::
createEntry(IOSerialStreamSync *stream, bool *cyclePortP)
{
IOSerialSessionSyncEntry *entry;
IOConditionLock *sleepLock = 0, *handOverLock = 0;
bool undoAcquire = false;
IOTakeLock(fLock);
do {
queue_iterate(&fStreamSyncList, entry, IOSerialSessionSyncEntry *, fLink) {
if (stream == entry->stream) {
entry->fRefCount++;
break;
}
}
if ( !queue_end(&fStreamSyncList, (queue_entry_t) entry)) {
*cyclePortP = true;
break;
}
*cyclePortP = false;
entry = 0;
if (stream->acquirePort(false) != kIOReturnSuccess)
break;
undoAcquire = true;
sleepLock = new IOConditionLock;
handOverLock = new IOConditionLock;
entry = (IOSerialSessionSyncEntry *) IOMalloc(sizeof(*entry));
if (!sleepLock || !handOverLock || !entry)
break;
undoAcquire = false;
sleepLock->initWithCondition(false);
handOverLock->initWithCondition(true);
bzero(entry, sizeof(*entry));
entry->fRefCount = 1;
entry->stream = stream;
entry->type = None;
entry->fSleepLock = sleepLock; sleepLock = 0;
entry->fHandOverLock = handOverLock; handOverLock = 0;
queue_enter(&fStreamSyncList, entry, IOSerialSessionSyncEntry *, fLink);
} while(false);
if (handOverLock)
handOverLock->release();
if (sleepLock)
sleepLock->release();
if (undoAcquire)
stream->releasePort();
IOUnlock(fLock);
return entry;
}
void IOSerialSessionSyncGlobals::
releaseEntry(IOSerialSessionSyncEntry *entry)
{
int refCount;
IOTakeLock(fLock);
refCount = --(entry->fRefCount);
if (refCount)
IOUnlock(fLock);
else {
queue_remove(&fStreamSyncList, entry, IOSerialSessionSyncEntry *, fLink);
IOUnlock(fLock);
entry->fSleepLock->release();
entry->fHandOverLock->release();
entry->stream->releasePort();
IOFree(entry, sizeof(*entry));
}
}
bool IOSerialSessionSync::initWithStreamSync(IOSerialStreamSync *nub)
{
if (!super::init())
return false;
if (!sSessionSyncGlobals.isValid())
return false;
if (!OSDynamicCast(IOSerialStreamSync, nub))
return false;
STREAM = nub;
fLockError = kIOReturnNotOpen; fStreamSyncEntry = 0;
return true;
}
IOSerialSessionSync * IOSerialSessionSync::withStreamSync(IOSerialStreamSync *nub)
{
IOSerialSessionSync *me = new IOSerialSessionSync;
if (me && !me->initWithStreamSync(nub)) {
me->release();
me = 0;
}
return me;
}
IOReturn IOSerialSessionSync::acquireAudit(bool sleep)
{
return acquireSession(Preempt, sleep);
}
IOReturn IOSerialSessionSync::acquirePort(bool sleep)
{
return acquireSession(NonPreempt, sleep);
}
IOReturn IOSerialSessionSync::releasePort()
{
if (fStreamSyncEntry)
{
requestType(None, false);
releaseSessionSync();
}
return kIOReturnSuccess;
}
IOReturn IOSerialSessionSync::
setState(UInt32 state, UInt32 mask)
{
if (fLockError)
return fLockError;
return STREAM->setState(state, mask);
}
UInt32 IOSerialSessionSync::getState()
{
return STREAM->getState();
}
IOReturn IOSerialSessionSync::watchState(UInt32 *state, UInt32 mask)
{
IOReturn result;
if (fLockError)
return fLockError;
result = STREAM->watchState(state, mask);
if (fLockError)
return fLockError;
return result;
}
UInt32 IOSerialSessionSync::nextEvent()
{
if (fLockError != kIOReturnSuccess)
return PD_E_EOQ;
return STREAM->nextEvent();
}
IOReturn IOSerialSessionSync::executeEvent(UInt32 event, UInt32 data)
{
if (fLockError)
return fLockError;
return STREAM->executeEvent(event, data);
}
IOReturn IOSerialSessionSync::requestEvent(UInt32 event, UInt32 *data)
{
if (fLockError)
return fLockError;
return STREAM->requestEvent(event, data);
}
IOReturn IOSerialSessionSync::enqueueEvent(UInt32 event, UInt32 data, bool sleep)
{
IOReturn result;
if (fLockError)
return fLockError;
result = STREAM->enqueueEvent(event, data, sleep);
if (fLockError)
return fLockError;
return result;
}
IOReturn IOSerialSessionSync::dequeueEvent(UInt32 *event, UInt32 *data, bool sleep)
{
IOReturn result;
if (fLockError)
return fLockError;
result = STREAM->dequeueEvent(event, data, sleep);
if (fLockError)
return fLockError;
return result;
}
IOReturn IOSerialSessionSync::
enqueueData(UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep)
{
IOReturn result;
if (fLockError)
return fLockError;
result = STREAM->enqueueData(buffer, size, count, sleep);
if (fLockError)
return fLockError;
return result;
}
IOReturn IOSerialSessionSync::
dequeueData(UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min)
{
IOReturn result;
if (fLockError)
return fLockError;
result = STREAM->dequeueData(buffer, size, count, min);
if (fLockError)
return fLockError;
return result;
}
IOReturn IOSerialSessionSync::acquireSession(PTTypeT req_type, bool sleep)
{
bool cyclePort;
if (fStreamSyncEntry)
cyclePort = (req_type == Preempt && fStreamSyncEntry->waitN);
else {
fStreamSyncEntry = sSessionSyncGlobals.createEntry(STREAM, &cyclePort);
if (!fStreamSyncEntry)
return kIOReturnExclusiveAccess;
}
fLockError = requestType(req_type, sleep);
if (fLockError != kIOReturnSuccess)
releaseSessionSync();
else if (cyclePort) {
fStreamSyncEntry->stream->releasePort();
fLockError = fStreamSyncEntry->stream->acquirePort(false);
assert(fLockError == kIOReturnSuccess);
}
return fLockError;
}
void IOSerialSessionSync::releaseSessionSync()
{
assert(fStreamSyncEntry);
if (fStreamSyncEntry->session == this)
{
(void) fStreamSyncEntry->stream->executeEvent(PD_E_ACTIVE, false);
}
sSessionSyncGlobals.releaseEntry(fStreamSyncEntry);
fStreamSyncEntry = (IOSerialSessionSyncEntry *) NULL;
fLockError = kIOReturnNotOpen;
}
IOReturn IOSerialSessionSync::getType(PTTypeT want_type, bool sleep)
{
UInt8 *counter;
IOReturn result = kIOReturnSuccess;
assert(want_type == Preempt || want_type == NonPreempt);
assert(fStreamSyncEntry);
if (!sleep)
result = kIOReturnCannotLock;
else {
counter = (want_type == Preempt)
? & fStreamSyncEntry->waitP : & fStreamSyncEntry->waitN;
(*counter)++;
if (fStreamSyncEntry->fSleepLock->lockWhen(want_type))
result = kIOReturnIPCError;
fStreamSyncEntry->fHandOverLock->lock();
if (fStreamSyncEntry->fHandOverLock->getCondition())
fStreamSyncEntry->fHandOverLock->unlock();
else
fStreamSyncEntry->fHandOverLock->unlockWith(true);
(*counter)--;
if (!result)
{
fStreamSyncEntry->type = want_type;
fStreamSyncEntry->session = this;
}
}
return result;
}
IOReturn IOSerialSessionSync::requestType(PTTypeT new_type, bool sleep)
{
IOReturn result = kIOReturnBadArgument;
assert(fStreamSyncEntry);
if (new_type == None)
{
assert(fStreamSyncEntry->type != None);
fStreamSyncEntry->type = None;
fStreamSyncEntry->session = NULL;
if (fStreamSyncEntry->waitN)
new_type = NonPreempt;
else if (fStreamSyncEntry->waitP)
new_type = Preempt;
else
new_type = None;
if (new_type != None)
{
fStreamSyncEntry->fHandOverLock->lock();
fStreamSyncEntry->fHandOverLock->unlockWith(false);
}
fStreamSyncEntry->fSleepLock->unlockWith(new_type);
if (new_type != None)
{
fStreamSyncEntry->fHandOverLock->lockWhen(true);
fStreamSyncEntry->fHandOverLock->unlock();
}
result = kIOReturnSuccess;
}
else if (fStreamSyncEntry->session == this)
{
switch (new_type)
{
case Preempt:
if (fStreamSyncEntry->waitN)
{
requestType(None, false);
result = getType(Preempt, sleep);
break;
}
case NonPreempt:
fStreamSyncEntry->type = new_type;
result = kIOReturnSuccess;
break;
default: break;
}
}
else switch (fStreamSyncEntry->type)
{
case None:
if (fStreamSyncEntry->fSleepLock->lock())
result = kIOReturnIPCError;
else
{
fStreamSyncEntry->type = new_type;
fStreamSyncEntry->session = this;
result = kIOReturnSuccess;
}
break;
case NonPreempt:
switch (new_type)
{
case Preempt:
case NonPreempt:
result = getType(new_type, sleep);
break;
default: break;
}
break;
case Preempt:
switch (new_type)
{
case NonPreempt:
fStreamSyncEntry->session->releaseSessionSync();
fStreamSyncEntry->type = new_type;
fStreamSyncEntry->session = this;
result = kIOReturnSuccess;
break;
case Preempt:
result = getType(new_type, sleep);
break;
default: break;
}
break; }
return result;
}