#include <IOKit/IOTimerEventSource.h>
#include <IOKit/IOCommandGate.h>
#include "IrDAComm.h"
#include "IrGlue.h"
#include "IrComm.h"
#include "CTimer.h"
#include "IrDALog.h"
#if (hasTracing > 0 && hasIrDACommTracing > 0)
enum tracecodes
{
kLogNew = 1,
kLogFree,
kLogInit,
kLogStop,
kLogStop1,
kLogStopThread,
kLogTxBufferAvailable,
kLogWrite,
kLogReadComplete,
kLogReturnCredit,
kLogConnectionStatus,
kLogTransmitComplete,
kLogSetSpeedComplete,
kLogBackEnable,
kLogTimer,
kLogTimerFinished,
kLogXmitCompleteErr,
kLogReturnCreditErr,
kLogDoSomething,
kLogStateChange
};
static
EventTraceCauseDesc gTraceEvents[] = {
{kLogNew, "IrDAComm: new, obj="},
{kLogFree, "IrDAComm: free, obj="},
{kLogInit, "IrDAComm: init, obj="},
{kLogStop, "IrDAComm: stop, obj="},
{kLogStop1, "IrDAComm: stop waiting for disconnect"},
{kLogStopThread, "IrDAComm: stop thread"},
{kLogTxBufferAvailable, "IrDAComm: tx buffer available"},
{kLogWrite, "IrDAComm: write, state=,length="},
{kLogReadComplete, "IrDAComm: pkt read complete, length="},
{kLogReturnCredit, "IrDAComm: return credit, bytecount="},
{kLogConnectionStatus, "IrDAComm: new connection status, state=, connected="},
{kLogTransmitComplete, "IrDAComm: pkt transmit complete, worked="},
{kLogSetSpeedComplete, "IrDAComm: set speed complete"},
{kLogBackEnable, "IrDAComm: back enable, write active="},
{kLogTimer, "IrDAComm: timer routine entry, state="},
{kLogTimerFinished, "IrDAComm: timer routine exit"},
{kLogXmitCompleteErr, "IrDAComm: ERROR. transmit complete while stack is active"},
{kLogReturnCreditErr, "IrDAComm: ERROR. return credit while stack is active"},
{kLogDoSomething, "IrDAComm: run command in gate, cmd code="},
{kLogStateChange, "IrDAComm: state change entry, event=, current state="}
};
#define XTRACE(x, y, z) IrDALogAdd ( x, y, z, gTraceEvents, true)
#else
#define XTRACE(x, y, z) ((void)0)
#endif
extern "C" void timeoutRoutine(OSObject *owner, IOTimerEventSource *sender);
enum { cmdTxBufferAvailable,
cmdWrite,
cmdReturnCredit,
cmdStop,
cmdStopEvent,
cmdReadComplete,
cmdXmitComplete,
cmdSetSpeedComplete
};
#define super OSObject
OSDefineMetaClassAndStructors(IrDAComm, OSObject);
IrDAComm *
IrDAComm::irDAComm(AppleIrDASerial *driver, AppleIrDA *appleirda)
{
IrDAComm *obj = new IrDAComm;
XTRACE(kLogNew, (int)obj >> 16, (short)obj);
if (obj && !obj->init(driver, appleirda)) {
obj->release();
obj = nil;
}
return obj;
}
void IrDAComm::free()
{
IOWorkLoop *workloop;
XTRACE(kLogFree, (int)this >> 16, (short)this);
this->Stop();
if (fDriver) {
workloop = fDriver->getWorkLoop();
if (workloop) {
if (fGate)
workloop->removeEventSource(fGate);
}
}
#define FREE(x) { if (x) { (x)->release(); x = nil; }}
FREE(fGate);
FREE(fTimer);
FREE(fIrComm); FREE(fIrDA);
#undef FREE
#define THREAD_FREE(x) do { if (x) { \
thread_call_cancel(x); \
thread_call_free(x); \
x = NULL; } } while(0)
THREAD_FREE(fStop_thread);
#undef THREAD_FREE
super::free(); }
bool IrDAComm::init(AppleIrDASerial *driver, AppleIrDA *appleirda)
{
IOReturn rc;
IOWorkLoop *workloop;
XTRACE(kLogInit, (int)this >> 16, (short)this);
#if (hasTracing > 0)
DebugLog("log info at 0x%lx", (UInt32)IrDALogGetInfo());
#endif
require(driver, Fail);
fState = kIrDACommStateStart;
fDriver = driver;
fTimer = nil;
fQoS = nil;
fIrDA = nil;
fIrComm = nil;
fWriteBusy = false;
fGate = nil;
fStartCounter = 0; fStop_thread = nil;
if (!super::init())
return false;
fQoS = driver->GetIrDAQoS();
require(fQoS, Fail);
workloop = fDriver->getWorkLoop();
require(workloop, Fail);
fStop_thread = thread_call_allocate(stop_thread, this);
require(fStop_thread, Fail);
fIrDA = TIrGlue::tIrGlue(fDriver, appleirda, workloop, fQoS); require(fIrDA, Fail);
fIrComm = IrComm::irComm(fIrDA, this); require(fIrComm, Fail);
fGate = IOCommandGate::commandGate(this, 0); require(fGate, Fail);
rc = workloop->addEventSource(fGate); require(rc == kIOReturnSuccess, Fail);
fTimer = CTimer::cTimer(workloop, this, &IrDAComm::TimerRoutine);
require(fTimer, Fail);
fTimer->StartTimer(100, 0);
return true;
Fail:
return false;
}
IOReturn IrDAComm::Stop(void)
{
int i;
IOReturn rc = kIOReturnSuccess;
XTRACE(kLogStop, (int)this >> 16, (short)this);
require(fGate, Fail);
require(fIrDA, Fail); require(fDriver, Fail);
if (fState != kIrDACommStateStopped) { boolean_t bt;
if (fDriver->getWorkLoop()->inGate()) { rc = fGate->runAction(&DoSomething, (void *)cmdStopEvent, nil, nil, nil);
check(rc == kIOReturnSuccess);
}
else { bt = thread_call_enter(fStop_thread); check(bt == false);
for (i = 0 ; i < 10; i++) { XTRACE(kLogStop1, i, fState);
if (fState == kIrDACommStateStopped && fIrDA->IsLAPConnected() == false) break; IOSleep(100); }
}
check(fState == kIrDACommStateStopped);
rc = fGate->runAction(&DoSomething, (void *)cmdStop, nil, nil, nil);
check(rc == kIOReturnSuccess);
}
Fail:
fState = kIrDACommStateStopped; XTRACE(kLogStop, 0xffff, 0xffff);
return rc;
}
size_t IrDAComm::TXBufferAvailable()
{
IOReturn rc;
size_t result = 0;
XTRACE(kLogTxBufferAvailable, 0, 0);
if (fIrComm && fGate) {
rc = fGate->runAction(&DoSomething, (void *)cmdTxBufferAvailable, &result);
check(rc == kIOReturnSuccess);
}
XTRACE(kLogTxBufferAvailable, 0xffff, result);
return result;
}
size_t IrDAComm::Write(UInt8 *buf, size_t length)
{
UInt32 result = length;
IOReturn rc;
XTRACE(kLogWrite, fState, (short)length);
if (fState == kIrDACommStateConnected && fIrComm && fGate) {
rc = fGate->runAction(&DoSomething, (void *)cmdWrite, buf, (void *)length, &result);
check(rc == kIOReturnSuccess);
}
return result;
}
IOReturn IrDAComm::ReadComplete(UInt8 *buf, size_t length)
{
IOReturn rc;
XTRACE(kLogReadComplete, length >> 16, (short)length);
if (fGate && fIrComm && fIrDA) {
rc = fGate->runAction(&DoSomething, (void *)cmdReadComplete, buf, (void *)length, nil);
}
return rc;
}
void
IrDAComm::ReturnCredit(size_t byte_count) {
IOReturn rc;
XTRACE(kLogReturnCredit, byte_count >> 16, (short)byte_count);
if (fState == kIrDACommStateConnected && fIrComm && fGate) {
rc = fGate->runAction(&DoSomething, (void *)cmdReturnCredit, (void *)byte_count);
check(rc == kIOReturnSuccess);
}
return;
}
void
IrDAComm::Transmit_Complete(Boolean worked)
{
IOReturn rc;
XTRACE(kLogTransmitComplete, 0, worked);
if (fGate && fIrDA) {
rc = fGate->runAction(&DoSomething, (void *)cmdXmitComplete, (void *)worked);
}
XTRACE(kLogTransmitComplete, 0xffff, 0xffff);
}
void
IrDAComm::SetSpeedComplete(Boolean worked)
{
IOReturn rc;
XTRACE(kLogSetSpeedComplete, 0, worked);
if (fGate && fIrDA) {
rc = fGate->runAction(&DoSomething, (void *)cmdSetSpeedComplete, (void *)worked);
}
XTRACE(kLogSetSpeedComplete, 0xffff, 0xffff);
}
void
IrDAComm::IrCommDataRead(UInt8 *buf, UInt32 length) {
if (fDriver) fDriver->Add_RXBytes(buf, length); else
DebugLog("IrDAComm data read but no driver");
}
void
IrDAComm::BackEnable(void)
{
XTRACE(kLogBackEnable, 0, fWriteBusy);
if (fDriver ) fDriver->SetUpTransmit(); }
void
IrDAComm::StateChange(int event)
{
XTRACE(kLogStateChange, event, fState);
require(fIrComm, Fail);
require(fIrDA, Fail);
switch (fState) {
case kIrDACommStateStart: switch (event) {
case kIrDACommEventTimer: check(fStartCounter == 0); if (fStartCounter == 0) {
fIrDA->Start(); fIrComm->TryConnect(1); }
break;
case kIrDACommEventConnected: fState = kIrDACommStateConnected;
break;
case kIrDACommEventDisconnected: if (fStartCounter++ < 3)
fIrComm->TryConnect(1);
else
fState = kIrDACommStateIdle; break;
case kIrDACommEventStop:
if (fStartCounter == 0) fState = kIrDACommStateStopped; else { fState = kIrDACommStateStopping2; fIrComm->Disconnect(); }
break;
}
break;
case kIrDACommStateIdle: switch (event) {
case kIrDACommEventTimer: fState = kIrDACommStateConnecting;
fIrComm->TryConnect(6);
break;
case kIrDACommEventConnected: DebugLog("logic error event=%d, state=%d", event, fState);
break;
case kIrDACommEventDisconnected: DebugLog("logic error event=%d, state=%d", event, fState);
break;
case kIrDACommEventStop: fState = kIrDACommStateStopped; break;
}
break;
case kIrDACommStateConnecting: switch (event) {
case kIrDACommEventTimer: DebugLog("connect timing out");
break;
case kIrDACommEventConnected:
fState = kIrDACommStateConnected; break;
case kIrDACommEventDisconnected: fState = kIrDACommStateListening;
fIrComm->Listen();
break;
case kIrDACommEventStop: fState = kIrDACommStateStopping2; fIrComm->Disconnect(); break;
}
break;
case kIrDACommStateListening: switch (event) {
case kIrDACommEventTimer: fState = kIrDACommStateStoppingListen; fIrComm->Disconnect(); break;
case kIrDACommEventConnected: fState = kIrDACommStateConnected;
break;
case kIrDACommEventDisconnected: fIrComm->Listen(); break;
case kIrDACommEventStop: fState = kIrDACommStateStopping2; fIrComm->Disconnect(); break;
}
break;
case kIrDACommStateStoppingListen: switch (event) {
case kIrDACommEventTimer: DebugLog("stopping listen timer fired, are we stuck?");
break;
case kIrDACommEventConnected: DebugLog("listen disconnect race condition");
break;
case kIrDACommEventDisconnected: fState = kIrDACommStateDisconnecting;
break;
case kIrDACommEventStop: fState = kIrDACommStateStopping2; break;
}
break;
case kIrDACommStateDisconnecting: switch (event) {
case kIrDACommEventTimer: DebugLog("disconnect timing out?");
break;
case kIrDACommEventConnected: DebugLog("disconnect race condition");
break;
case kIrDACommEventDisconnected: fState = kIrDACommStateConnecting; fIrComm->TryConnect(6); break;
case kIrDACommEventStop: fState = kIrDACommStateStopping; break;
}
break;
case kIrDACommStateConnected: switch (event) {
case kIrDACommEventTimer: break;
case kIrDACommEventConnected: DebugLog("logic error event=%d, state=%d", event, fState);
break;
case kIrDACommEventDisconnected: fState = kIrDACommStateIdle; break;
case kIrDACommEventStop: fState = kIrDACommStateStopping; fIrComm->Disconnect(); break;
}
break;
case kIrDACommStateStopping2:
switch (event) {
case kIrDACommEventTimer: DebugLog("logic error event=%d, state=%d", event, fState);
break;
case kIrDACommEventConnected: fState = kIrDACommStateStopping; break;
case kIrDACommEventDisconnected:
fState = kIrDACommStateStopping; break;
case kIrDACommEventStop:
DebugLog("logic error event=%d, state=%d", event, fState);
break;
}
break;
case kIrDACommStateStopping:
switch (event) {
case kIrDACommEventTimer:
DebugLog("logic error event=%d, state=%d", event, fState);
break;
case kIrDACommEventConnected:
DebugLog("logic error event=%d, state=%d", event, fState);
break;
case kIrDACommEventDisconnected:
fState = kIrDACommStateStopped; if (fIrDA->IsLAPConnected()) fIrDA->DoIdleDisconnect(); break;
case kIrDACommEventStop:
DebugLog("logic error event=%d, state=%d", event, fState);
break;
}
break;
case kIrDACommStateStopped: DebugLog("logic error event=%d, state=%d", event, fState);
break;
}
Fail:
return;
}
void
IrDAComm::ConnectionStatus(Boolean connected)
{
static Boolean last_connected = false;
XTRACE(kLogConnectionStatus, fState, connected);
if (connected != last_connected) {
DebugLog("connection status %d", connected);
last_connected = connected;
}
check(fState != kIrDACommStateIdle);
if (connected) StateChange(kIrDACommEventConnected);
else StateChange(kIrDACommEventDisconnected);
}
void
IrDAComm::TimerRoutine(OSObject *owner, IrDATimerEventSource *iotimer)
{
IrDAComm *obj;
XTRACE(kLogTimer, 0, 0);
obj = OSDynamicCast(IrDAComm, owner);
require(obj, Fail);
XTRACE(kLogTimer, 0x1111, obj->fState);
obj->StateChange(kIrDACommEventTimer);
if (obj->fIrDA) { obj->fIrDA->RunQueue();
}
require(obj->fTimer, Fail);
obj->fTimer->StartTimer(5 * 1000, 0);
XTRACE(kLogTimerFinished, 0, 0);
return;
Fail:
XTRACE(kLogTimerFinished, 0xdead, 0xbeef);
return;
}
IOReturn
IrDAComm::DoSomething(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4)
{
IrDAComm *obj;
int cmd = (int)arg1;
XTRACE(kLogDoSomething, 0, (short)arg1);
obj = OSDynamicCast(IrDAComm, owner);
require(obj, Fail);
switch (cmd) {
case cmdTxBufferAvailable:
{
UInt32 count = 0;
UInt32 *result = (UInt32 *)arg2;
if (obj->fIrComm)
count = obj->fIrComm->TxBufferAvailable();
obj->fWriteBusy = (count == 0);
if (result)
*result = count;
}
break;
case cmdWrite:
{
UInt8 *buf = (UInt8 *)arg2;
UInt32 length = (UInt32)arg3;
UInt32 *result = (UInt32 *)arg4;
if (result)
*result = obj->fIrComm->Write(buf, length);
}
break;
case cmdReturnCredit:
{
UInt32 byte_count = (UInt32)arg2;
if (obj->fIrComm)
obj->fIrComm->ReturnCredit(byte_count);
}
break;
case cmdStop:
{
if (obj->fTimer) {
obj->fTimer->StopTimer();
obj->fTimer->release();
obj->fTimer = nil;
}
if (obj->fIrDA) {
obj->fIrDA->Stop(); }
}
break;
case cmdStopEvent:
check(obj->fTimer);
if (obj->fTimer) {
obj->fTimer->StopTimer(); obj->StateChange(kIrDACommEventStop); }
if (obj->fIrDA) { obj->fIrDA->RunQueue();
}
break;
case cmdReadComplete:
{
UInt8 *buf = (UInt8 *)arg2;
UInt32 length = (UInt32)arg3;
if (obj->fIrDA) {
obj->fIrDA->ReadComplete(buf, length);
obj->fIrDA->RunQueue();
}
}
break;
case cmdXmitComplete:
{
bool worked = (bool)arg2;
if (obj->fIrDA) {
obj->fIrDA->TransmitComplete(worked);
XTRACE(kLogTransmitComplete, 0x1111, 0x1111);
obj->fIrDA->RunQueue();
XTRACE(kLogTransmitComplete, 0x2222, 0x2222);
}
}
break;
case cmdSetSpeedComplete:
{
bool worked = (bool)arg2;
if (obj->fIrDA) {
obj->fIrDA->SetSpeedComplete(worked);
XTRACE(kLogSetSpeedComplete, 0x1111, 0x1111);
obj->fIrDA->RunQueue();
XTRACE(kLogSetSpeedComplete, 0x2222, 0x2222);
}
}
break;
default:
check(0);
break;
}
return kIOReturnSuccess;
Fail:
return kIOReturnBadArgument;
}
void
IrDAComm::GetIrDAStatus(IrDAStatus *status)
{
if (fIrDA && status)
fIrDA->GetIrDAStatus(status);
}
bool
IrDAComm::Starting()
{
return fState == kIrDACommStateStart;
}
void IrDAComm::stop_thread(thread_call_param_t param0, thread_call_param_t param1)
{
IrDAComm *obj;
IOReturn rc;
XTRACE(kLogStopThread, 0, 0);
require(param0, Fail);
obj = OSDynamicCast(IrDAComm, (OSObject *)param0);
require(obj, Fail);
rc = obj->fGate->runAction(&DoSomething, (void *)cmdStopEvent, nil, nil, nil);
check(rc == kIOReturnSuccess);
Fail:
return;
}