#include "IrGlue.h"
#include "IrLMP.h"
#include "IrLAP.h"
#include "IrLAPConn.h"
#include "CList.h"
#include "CListIterator.h"
#include "IrDiscovery.h"
#include "IrDscInfo.h"
#if (hasTracing > 0 && hasLMPTracing > 0)
enum IrLMPTraceCodes
{
kCreate = 1,
kInit,
kLogFree,
kUnexpectedEvent,
kStnCntlReadyDiscoverEvent,
kStnCntlReadyConnectEvent,
kStnCntlReadyListenEvent,
kStnCntlReadyDiscRequestEvent,
kStnCntlReadyDiscReplyEvent,
kStnCntlDiscoverDiscoverEvent,
kStnCntlResolveDiscoverEvent,
kLogLMPResetEntry,
kLogLMPResetExit,
kLogFillInLMPDUHeader,
kLogStartOneSecTicker,
kEnqueueEvent,
kDequeueEventStart,
kDequeueEventEnd
};
EventTraceCauseDesc IrLMPTraceEvents[] = {
{kCreate, "irlmp: create obj="},
{kInit, "irlmp: init, lmp="},
{kLogFree, "irlmp: free"},
{kUnexpectedEvent, "irlmp: unexpected event"},
{kStnCntlReadyDiscoverEvent, "irlmp: Ready discover request"},
{kStnCntlReadyConnectEvent, "irlmp: Ready connect request"},
{kStnCntlReadyListenEvent, "irlmp: Ready listen request"},
{kStnCntlReadyDiscRequestEvent, "irlmp: Ready disconnect request"},
{kStnCntlReadyDiscReplyEvent, "irlmp: Ready disconnect reply"},
{kStnCntlDiscoverDiscoverEvent, "irlmp: Discv discover reply"},
{kStnCntlResolveDiscoverEvent, "irlmp: Reslv discover reply"},
{kLogLMPResetEntry, "irlmp: reset entry"},
{kLogLMPResetExit, "irlmp: reset finished"},
{kLogFillInLMPDUHeader, "irlmp: Fill In LMP PDU Header"},
{kLogStartOneSecTicker, "irlmp: StartOneSecTicker"},
{kEnqueueEvent, "irlmp: Event Queued"},
{kDequeueEventStart, "irlmp: Event Start"},
{kDequeueEventEnd, "irlmp: Event End"}
};
#define XTRACE(x, y, z) IrDALogAdd( x, y, (uintptr_t)z & 0xffff, IrLMPTraceEvents, true )
#else
#define XTRACE(x, y, z) ((void)0)
#endif
#define GetLAP (fIrDA->GetLAP())
#define GetLAPConn (fIrDA->GetLAPConn())
#define GetDiscovery (fIrDA->GetDiscovery())
#define super TIrStream
OSDefineMetaClassAndStructors(TIrLMP, TIrStream);
TIrLMP * TIrLMP::tIrLMP(TIrGlue* irda)
{
TIrLMP *obj = new TIrLMP;
XTRACE(kCreate, 0, obj);
if (obj && !obj->Init(irda)) {
obj->release();
obj = nil;
}
return obj;
}
void TIrLMP::free(void)
{
XTRACE(kLogFree, 0, this);
if (fPendingRequests) { fPendingRequests->release();
fPendingRequests = nil;
}
super::free();
}
Boolean TIrLMP::Init(TIrGlue* irda)
{
XTRACE(kInit, 0, this);
fState = kIrLMPReady;
fTimerClients = 0;
fNumAddrConflicts = 0;
bzero(fAddrConflicts, sizeof(fAddrConflicts));
fPendingRequests = nil;
#if (hasTracing > 0 && hasLMPTracing > 0)
if (!super::Init(irda, IrLMPTraceEvents, kEnqueueEvent)) return false;
#else
if (!super::Init(irda)) return false;
#endif
fPendingRequests = CList::cList(); require(fPendingRequests, Fail);
return true;
Fail:
return false;
}
void TIrLMP::Reset()
{
XTRACE(kLogLMPResetEntry, 0, 0);
fState = kIrLMPReady;
fTimerClients = 0;
if (GetLAPConn != nil) {
GetLAPConn->Reset();
}
XTRACE(kLogLMPResetExit, 0, 0);
}
void TIrLMP::NextState(ULong event)
{
switch (fState) {
case kIrLMPReady:
HandleReadyStateEvent(event);
break;
case kIrLMPDiscover:
HandleDiscoverStateEvent(event);
break;
case kIrLMPResolveAddress:
HandleResolveAddressStateEvent(event);
break;
default:
DebugLog("TIrLMP::NextState: bad fState");
break;
}
}
void TIrLMP::HandleReadyStateEvent(ULong event)
{
switch (event) {
case kIrDiscoverRequestEvent:
{
XTRACE(kStnCntlReadyDiscoverEvent, 0, 0);
TIrDiscoverRequest* discoverRequest = (TIrDiscoverRequest*)GetCurrentEvent();
discoverRequest->fConflictDevAddr = kIrLAPBroadcastDevAddr;
fState = kIrLMPDiscover;
GetLAP->EnqueueEvent(discoverRequest);
}
break;
case kIrConnectRequestEvent:
XTRACE(kStnCntlReadyConnectEvent, 0, 0);
GetLAPConn->EnqueueEvent(GetCurrentEvent());
break;
case kIrListenRequestEvent:
XTRACE(kStnCntlReadyListenEvent, 0, 0);
GetLAPConn->EnqueueEvent(GetCurrentEvent());
break;
case kIrConnectReplyEvent:
case kIrListenReplyEvent:
case kIrGetDataRequestEvent:
case kIrCancelGetRequestEvent:
GetLAPConn->EnqueueEvent(GetCurrentEvent());
break;
case kIrPutDataRequestEvent:
case kIrCancelPutRequestEvent:
GetLAP->EnqueueEvent(GetCurrentEvent());
break;
case kIrDisconnectRequestEvent:
XTRACE(kStnCntlReadyDiscRequestEvent, 1, 0);
GetLAPConn->EnqueueEvent(GetCurrentEvent());
break;
case kIrDisconnectReplyEvent:
XTRACE(kStnCntlReadyDiscReplyEvent, 1, 0);
GetLAPConn->EnqueueEvent(GetCurrentEvent());
break;
case kIrDiscoverReplyEvent: HandleDiscoverStateEvent(event); break;
default:
XTRACE(kUnexpectedEvent, fState, event);
DebugLog("TIrLMP::HandleReadyStateEvent: bad event");
break;
}
}
void TIrLMP::HandleDiscoverStateEvent(ULong event)
{
switch (event) {
case kIrDiscoverReplyEvent:
{
TIrDiscoverReply* discoverReply = (TIrDiscoverReply*)GetCurrentEvent();
if ((discoverReply->fResult == noErr) &&
(!discoverReply->fPassiveDiscovery) &&
(AddrConflicts(discoverReply->fDiscoveredDevices, true))) {
XTRACE(kStnCntlDiscoverDiscoverEvent, 0, 1);
XASSERT(fNumAddrConflicts > 0);
discoverReply->fEvent = kIrDiscoverRequestEvent;
discoverReply->fConflictDevAddr = fAddrConflicts[--fNumAddrConflicts];
fState = kIrLMPResolveAddress;
GetLAP->EnqueueEvent(discoverReply);
}
else {
XTRACE(kStnCntlDiscoverDiscoverEvent, 0, 2);
fState = kIrLMPReady;
GetDiscovery->EnqueueEvent(discoverReply);
if (fPendingRequests && !fPendingRequests->Empty()) {
CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
iter->More(); request = (TIrEvent*)iter->NextItem()) {
XTRACE(kStnCntlDiscoverDiscoverEvent, request->fEvent, 3);
this->EnqueueEvent(request);
}
while (!fPendingRequests->Empty())
fPendingRequests->RemoveLast(); iter->release();
}
}
}
break;
case kIrConnectReplyEvent: case kIrListenReplyEvent:
case kIrGetDataRequestEvent:
case kIrCancelGetRequestEvent:
GetLAPConn->EnqueueEvent(GetCurrentEvent());
break;
case kIrConnectRequestEvent: case kIrListenRequestEvent:
case kIrDisconnectRequestEvent:
case kIrPutDataRequestEvent:
case kIrCancelPutRequestEvent:
fPendingRequests->InsertLast(GetCurrentEvent());
break;
default:
XTRACE(kUnexpectedEvent, fState, event);
DebugLog("TIrLMP::HandleDiscoverStateEvent: bad event");
break;
}
}
void TIrLMP::HandleResolveAddressStateEvent(ULong event)
{
switch (event) {
case kIrDiscoverReplyEvent:
{
XTRACE(kStnCntlResolveDiscoverEvent, 0, 0);
TIrDiscoverReply* discoverReply = (TIrDiscoverReply*)GetCurrentEvent();
(void)AddrConflicts(discoverReply->fDiscoveredDevices, false);
if ((discoverReply->fResult != noErr) || (fNumAddrConflicts == 0)) {
fState = kIrLMPReady;
GetDiscovery->EnqueueEvent(discoverReply);
}
else {
XASSERT(fNumAddrConflicts > 0);
discoverReply->fConflictDevAddr = fAddrConflicts[--fNumAddrConflicts];
GetLAP->EnqueueEvent(discoverReply);
}
}
break;
case kIrDisconnectRequestEvent:
XASSERT(((TIrDisconnectRequest*)GetCurrentEvent())->fLSAPConn == nil);
GetLAP->EnqueueEvent(GetCurrentEvent());
break;
case kIrDisconnectReplyEvent:
GetDiscovery->EnqueueEvent(GetCurrentEvent());
break;
default:
XTRACE(kUnexpectedEvent, fState, event);
DebugLog("TIrLMP::HandleResolveAddressStateEvent: bad event");
break;
}
}
Boolean TIrLMP::AddrConflicts(CList* discoveredDevices, Boolean setAddrConflicts)
{
ULong addrToCheck;
ULong uniqueCount = 0;
Boolean thisConflicts;
Boolean conflicts = false;
ULong uniqueAddrs[kMaxReturnedAddrs+1];
uniqueAddrs[uniqueCount++] = GetLAP->GetMyDevAddr();
if (setAddrConflicts) {
fNumAddrConflicts = 0;
}
if (discoveredDevices->GetArraySize() > 0) {
for (FastInt index1 = discoveredDevices->GetArraySize() - 1; index1 >= 0 ; index1--) {
TIrDscInfo* discoveryInfo = (TIrDscInfo*)discoveredDevices->At(index1);
thisConflicts = false;
addrToCheck = discoveryInfo->GetDeviceAddr();
for (ULong index2 = 0; index2 < uniqueCount; index2++) {
if (addrToCheck == uniqueAddrs[index2]) {
conflicts = thisConflicts = true;
if (setAddrConflicts) {
if (fNumAddrConflicts < kMaxAddrConflicts) {
fAddrConflicts[fNumAddrConflicts++] = addrToCheck;
}
}
discoveredDevices->RemoveAt(index1);
discoveryInfo->release();
break;
}
}
if (!thisConflicts) {
if (uniqueCount < (kMaxReturnedAddrs+1)) {
uniqueAddrs[uniqueCount++] = addrToCheck;
}
else {
DebugLog("TIrLMP::AddrConflicts: too many unique addrs");
}
}
}
}
return conflicts;
}
void TIrLMP::Demultiplexor(CBufferSegment* inputBuffer)
{
GetLAPConn->Demultiplexor(inputBuffer);
}
ULong TIrLMP::FillInLMPDUHeader(TIrPutRequest* putRequest, UByte* buffer)
{
XTRACE(kLogFillInLMPDUHeader, 0, 0);
return GetLAPConn->FillInLMPDUHeader(putRequest, buffer);
}
void TIrLMP::StartOneSecTicker()
{
XTRACE(kLogStartOneSecTicker, 0, fTimerClients);
if (fTimerClients++ == 0) {
fIrDA->StartTimer(kTimer_LMP, 1 * kSeconds, kIrConnWatchdogExpiredEvent);
}
}
void TIrLMP::StopOneSecTicker()
{
if (fTimerClients > 0) {
if (--fTimerClients == 0) {
fIrDA->StopTimer(kTimer_LMP);
}
}
}
void TIrLMP::TimerComplete(ULong refCon)
{
GetLAPConn->TimerComplete(refCon);
if (refCon == kIrConnWatchdogExpiredEvent && fTimerClients > 0) {
fIrDA->StartTimer(kTimer_LMP, 1 * kSeconds, kIrConnWatchdogExpiredEvent);
}
}