#include "IrLAPConn.h"
#include "IrLSAPConn.h"
#include "IrGlue.h"
#include "IrLAP.h"
#include "CList.h"
#include "CListIterator.h"
#include "IrDALog.h"
#if (hasTracing > 0 && hasLAPConnTracing > 0)
enum IrLAPConnTraceCodes
{
kNullEvent = 1,
kDestroy,
kInit,
kDeInit,
kUnexpectedEvent,
kLogStateEvent,
kStandbyConnLstnRequestEvent,
kStandbyDisconnectRequestEvent,
kStandbyDisconnectReplyEvent,
kStandbyDisconnectRequeue,
kPendingConnLstnRequestEvent,
kPendingConnLstnDeferRequest, kPendingConnLstnReplyEvent,
kPendingDisconnectRequestEvent,
kPendingDisconnectReplyEvent,
kPendingDisconnectRequeue,
kActiveConnLstnRequestEvent,
kActiveConnLstnDeferRequest, kActiveGetDataRequestEvent,
kActiveCancelGetRequestEvent,
kActiveDisconnectRequestEvent,
kActiveDisconnectReplyEvent,
kActiveDisconnectRequeue,
kDemuxInvalidHeaderEvent,
kDemuxGetPendingEvent,
kDemuxReplyPostedEvent,
kDemuxReplyPostedEvent2,
kDemuxNoReceiverEvent,
kDemuxReleaseBufferEvent,
kCancelPendingGetReqEvent,
kCleanupPendingRcvdBufEvent,
kWantToAdd,
kAddingToLSAPConnList,
kAddingLsapToList,
kLogStartIdleDisconnectTimer,
kLogStopIdleDisconnectTimer,
kLogDoIdleDisconnect,
kLogConnWatchDogFired,
kLogIdleDisconnectFired,
kLogReset,
kLogResetLsapConn,
kLogResetGetRequest,
kLogResetGetReply,
kLogResetPendingReq,
kLogDemux,
kLogDemuxCheckingGets1,
kLogDemuxCheckingGets2,
kLogAddingGetRequest1,
kLogAddingGetRequest2,
kLogAddingGetRequest3,
kLogCleanupPendingGetRequestsAndRepliesEntry,
kLogCleanupPendingGetRequestsAndReplies2,
kLogCleanupPendingGetRequestsAndReplies3,
kLogCancelPendingGetRequestsEntry,
kLogCancelPendingGetRequests,
kLogCancelPendingGetRequestsExit,
kLogFillInLMPDUHeader1,
kLogFillInLMPDUHeader2,
kEnqueueEvent,
kDequeueEventStart,
kDequeueEventEnd
};
EventTraceCauseDesc IrLAPConnTraceEvents[] = {
{kNullEvent, "irlapconn: create obj="},
{kDestroy, "irlapconn: destroy obj="},
{kInit, "irlapconn: init"},
{kDeInit, "irlapconn: deinit"},
{kUnexpectedEvent, "irlapconn: unexpected event"},
{kLogStateEvent, "irlapconn: next state, state=, event="},
{kStandbyConnLstnRequestEvent, "irlapconn: standby conn/lstn request"},
{kStandbyDisconnectRequestEvent,"irlapconn: standby disconnect request"},
{kStandbyDisconnectReplyEvent, "irlapconn: standby disconnect reply"},
{kStandbyDisconnectRequeue, "irlapconn: standby requeue pending requests"},
{kPendingConnLstnRequestEvent, "irlapconn: uconnect conn/lstn request"},
{kPendingConnLstnDeferRequest, "irlapconn: uconnect conn/lstn defer request"}, {kPendingConnLstnReplyEvent, "irlapconn: uconnect conn/lstn reply"},
{kPendingDisconnectRequestEvent,"irlapconn: uconnect disconnect request"},
{kPendingDisconnectReplyEvent, "irlapconn: uconnect disconnect reply"},
{kPendingDisconnectRequeue, "irlapconn: uconnect requeue pending request"},
{kActiveConnLstnRequestEvent, "irlapconn: active conn/lstn request"},
{kActiveConnLstnDeferRequest, "irlapconn: active conn/lstn defer request"}, {kActiveGetDataRequestEvent, "irlapconn: active get data request"},
{kActiveCancelGetRequestEvent, "irlapconn: active cancel get request"},
{kActiveDisconnectRequestEvent, "irlapconn: active disconnect request"},
{kActiveDisconnectReplyEvent, "irlapconn: active disconnect reply"},
{kActiveDisconnectRequeue, "irlapconn: active requeue pending request"},
{kDemuxInvalidHeaderEvent, "irlapconn: invalid header"},
{kDemuxGetPendingEvent, "irlapconn: found get pending, event rec="},
{kDemuxReplyPostedEvent, "irlapconn: found lsapconn, no get. buf="},
{kDemuxReplyPostedEvent2, "irlapconn: found lsapconn, no get. lsapconn="},
{kDemuxNoReceiverEvent, "irlapconn: no receiver"},
{kDemuxReleaseBufferEvent, "irlapconn: release buffer"},
{kCancelPendingGetReqEvent, "irlapconn: cancel pending get request, lsapconn="},
{kCleanupPendingRcvdBufEvent, "irlapconn: cleaning up pending recd buffer"},
{kWantToAdd, "irlapconn: ** want to add lsapconn to list"},
{kAddingToLSAPConnList, "irlapconn: ** add/del to fLSAPConnList, add=, id="},
{kAddingLsapToList, "irlapconn: ** add/del of lsap="},
{kLogStartIdleDisconnectTimer, "irlapconn: starting idle disconnect timer"},
{kLogStopIdleDisconnectTimer, "irlapconn: stoppng idle disconnect timer"},
{kLogDoIdleDisconnect, "irlapconn: doing idle disconnect"},
{kLogConnWatchDogFired, "irlapconn: conn watchdog timer fired"},
{kLogIdleDisconnectFired, "irlapconn: idle disconnect timer fired"},
{kLogReset, "irlapconn: reset. fConnected, fState"},
{kLogResetLsapConn, "irlapconn: reset. lsapconn="},
{kLogResetGetRequest, "irlapconn: reset. getrequest="},
{kLogResetGetReply, "irlapconn: reset. getreply="},
{kLogResetPendingReq, "irlapconn: reset. pending request"},
{kLogDemux, "irlapconn: demux input packet, buf="},
{kLogDemuxCheckingGets1, "irlapconn: demux, pending get count="},
{kLogDemuxCheckingGets2, "irlapconn: demux, checking event rec="},
{kLogAddingGetRequest1, "irlapconn: saving get req, event Rec="},
{kLogAddingGetRequest2, "irlapconn: saving get req, lsapconn="},
{kLogAddingGetRequest3, "irlapconn: saving get req, qlen=, lsapid="},
{kLogCleanupPendingGetRequestsAndRepliesEntry, "irlapconn: CleanupPendingGetRequestsAndReplies entry, lsapConn="},
{kLogCleanupPendingGetRequestsAndReplies2, "irlapconn: CleanupPendingGetRequestsAndReplies 2"},
{kLogCleanupPendingGetRequestsAndReplies3, "irlapconn: CleanupPendingGetRequestsAndReplies, replyBuffer="},
{kLogCancelPendingGetRequestsEntry, "irlapconn: CancelPendingGetRequestsEntry"},
{kLogCancelPendingGetRequests, "irlapconn: CancelPendingGetRequests"},
{kLogCancelPendingGetRequestsExit, "irlapconn: CancelPendingGetRequestsExit"},
{kLogFillInLMPDUHeader1, "irlapconn: putRequest"},
{kLogFillInLMPDUHeader2, "irlapconn: buffer"},
{kEnqueueEvent, "irlapconn: Event Queued"},
{kDequeueEventStart, "irlapconn: Event Start"},
{kDequeueEventEnd, "irlapconn: Event End"}
};
#define XTRACE(x, y, z) IrDALogAdd( x, y, (uintptr_t)z & 0xffff, IrLAPConnTraceEvents, true )
#else
#define XTRACE(x, y, z) ((void)0)
#endif
#define GetLAP (fIrDA->GetLAP())
#define super TIrStream
OSDefineMetaClassAndStructors(TIrLAPConn, TIrStream);
TIrLAPConn *
TIrLAPConn::tIrLAPConn(TIrGlue* irda)
{
TIrLAPConn *obj = new TIrLAPConn;
XTRACE(kNullEvent, 0, obj);
if (obj && !obj->Init(irda)) {
obj->release();
obj = nil;
}
return obj;
}
void
TIrLAPConn::free()
{
XTRACE(kDestroy, 0, this);
StopIdleDisconnectTimer();
#define FREE(x) { if (x) { (x)->release(); x = nil; } }
FREE(fLSAPConnList); FREE(fPendingGetRequests); FREE(fUnmatchedGetReplys); FREE(fPendingRequests);
super::free();
}
Boolean TIrLAPConn::Init(TIrGlue* irda)
{
XTRACE(kInit, 0, this);
fState = kIrLAPConnStandby;
fConnected = false;
fPeerDevAddr = 0;
fLSAPConnList = nil;
fPendingGetRequests = nil;
fUnmatchedGetReplys = nil;
fPendingRequests = nil;
fDisconnectPending = false;
#if (hasTracing > 0 && hasLAPConnTracing > 0)
if (!super::Init(irda, IrLAPConnTraceEvents, kEnqueueEvent)) return false;
#else
if (!super::Init(irda)) return false;
#endif
fLSAPConnList = CList::cList();
require(fLSAPConnList, Fail);
fPendingGetRequests = CList::cList();
require(fPendingGetRequests, Fail);
fUnmatchedGetReplys = CList::cList();
require(fUnmatchedGetReplys, Fail);
fPendingRequests = CList::cList();
require(fPendingRequests, Fail);
return true;
Fail:
return false;
}
void TIrLAPConn::Reset()
{
XTRACE(kLogReset, fConnected, fState);
fState = kIrLAPConnStandby; fConnected = false; fPeerDevAddr = 0;
StopIdleDisconnectTimer();
if (fLSAPConnList && !fLSAPConnList->Empty()) { int index;
TLSAPConn* lsapConn;
XTRACE(kLogResetLsapConn, 1, fLSAPConnList->GetArraySize());
for (index = fLSAPConnList->GetArraySize() - 1; index >= 0 ; index--) {
lsapConn = (TLSAPConn*)fLSAPConnList->At(index);
XTRACE(kLogResetLsapConn, 0, lsapConn);
CleanupPendingGetRequestsAndReplies(lsapConn, errCancel);
}
while (!fLSAPConnList->Empty())
fLSAPConnList->RemoveLast();
XTRACE(kLogResetLsapConn, 0xffff, 0xffff);
}
if (fPendingGetRequests && !fPendingGetRequests->Empty()) { CListIterator *iter = CListIterator::cListIterator(fPendingGetRequests);
TIrGetRequest *getRequest;
DebugLog("IrLapConn: reset pending get request. how?"); for (getRequest = (TIrGetRequest*)iter->FirstItem();
iter->More(); getRequest = (TIrGetRequest*)iter->NextItem()) {
XTRACE(kLogResetGetRequest, 0, getRequest);
this->CleanupPendingGetRequestsAndReplies( getRequest->fLSAPConn, errCancel);
}
iter->release();
}
if (fUnmatchedGetReplys && !fUnmatchedGetReplys->Empty()) {
CListIterator *iter = CListIterator::cListIterator(fUnmatchedGetReplys);
CBufferSegment* replyBuffer;
DebugLog("IrLapConn: reset pending get replies. how?"); for (replyBuffer = (CBufferSegment*)iter->FirstItem();
iter->More(); replyBuffer = (CBufferSegment*)iter->NextItem()) {
XTRACE(kLogResetGetReply, 0, replyBuffer);
GetLAP->ReleaseInputBuffer(replyBuffer); }
while (!fUnmatchedGetReplys->Empty())
fUnmatchedGetReplys->RemoveLast();
iter->release();
}
if (fPendingRequests && !fPendingRequests->Empty()) {
CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
iter->More(); request = (TIrEvent*)iter->NextItem()) {
XTRACE(kLogResetPendingReq, 0, request);
check(request->fEvent == kIrListenRequestEvent || request->fEvent == kIrConnectRequestEvent);
this->EnqueueEvent(request);
}
while (!fPendingRequests->Empty())
fPendingRequests->RemoveLast();
iter->release();
}
fDisconnectPending = false;
}
void TIrLAPConn::NextState(ULong event)
{
XTRACE(kLogStateEvent, fState, event);
switch (fState) {
case kIrLAPConnStandby:
HandleStandbyStateEvent(event);
break;
case kIrLAPConnConnectOrListen:
HandleConnectOrListenStateEvent(event);
break;
case kIrLAPConnActive:
HandleActiveStateEvent(event);
break;
default:
DebugLog("TIrLAPConn::NextState: bad fState");
break;
}
}
void TIrLAPConn::HandleStandbyStateEvent(ULong event)
{
XASSERT(!fConnected);
XASSERT(fPeerDevAddr == 0);
switch (event) {
case kIrConnectRequestEvent:
case kIrListenRequestEvent:
{
TIrConnLstnRequest* request = (TIrConnLstnRequest*)GetCurrentEvent();
XTRACE(kStandbyConnLstnRequestEvent, event, 0);
XTRACE(kWantToAdd, (uintptr_t)request->fLSAPConn>>16, request->fLSAPConn);
if (!fLSAPConnList->Contains(request->fLSAPConn)) {
fLSAPConnList->InsertLast(request->fLSAPConn);
}
if (event == kIrConnectRequestEvent) {
fPeerDevAddr = request->fDevAddr;
}
fState = kIrLAPConnConnectOrListen;
GetLAP->EnqueueEvent(request);
}
break;
case kIrDisconnectRequestEvent:
{
XTRACE(kStandbyDisconnectRequestEvent, 0, 0);
if (1) { TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)GetCurrentEvent();
if (fLSAPConnList->Contains(disconnectRequest->fLSAPConn)) { XTRACE(kAddingToLSAPConnList, 0,
disconnectRequest->fLSAPConn->GetMyLSAPId() << 8 | 99);
XTRACE(kAddingLsapToList, 0, disconnectRequest->fLSAPConn); IrDAErr removeResult = fLSAPConnList->Remove(disconnectRequest->fLSAPConn);
ncheck(removeResult);
}
}
if (1) { TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)GetCurrentEvent();
disconnectRequest->fEvent = kIrDisconnectReplyEvent; disconnectRequest->fResult = 0;
check(disconnectRequest->fLSAPConn); disconnectRequest->fLSAPConn->EnqueueEvent(disconnectRequest); }
}
break;
case kIrDisconnectReplyEvent:
{
TIrDisconnectReply* disconnectReply = (TIrDisconnectReply*)GetCurrentEvent();
XTRACE(kStandbyDisconnectReplyEvent, 0, 0);
if (disconnectReply->fLSAPConn == nil) fIrDA->ReleaseEventBlock(disconnectReply); else
disconnectReply->fLSAPConn->EnqueueEvent(disconnectReply);
if (fPendingRequests && !fPendingRequests->Empty()) {
CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
iter->More(); request = (TIrEvent*)iter->NextItem()) {
XTRACE(kStandbyDisconnectRequeue, 0, request);
check(request->fEvent == kIrListenRequestEvent || request->fEvent == kIrConnectRequestEvent);
this->EnqueueEvent(request);
}
while (!fPendingRequests->Empty())
fPendingRequests->RemoveLast();
iter->release();
}
fDisconnectPending = false; }
break;
case kIrGetDataRequestEvent: case kIrPutDataRequestEvent:
{
XTRACE(kUnexpectedEvent, fState, event);
TIrGetRequest* rq = (TIrGetRequest*)GetCurrentEvent();
if (rq->fEvent == kIrGetDataRequestEvent) rq->fEvent = kIrGetDataReplyEvent;
else
rq->fEvent = kIrPutDataReplyEvent;
rq->fResult = kIrDAErrWrongState; check(rq->fLSAPConn);
rq->fLSAPConn->EnqueueEvent(rq); }
break;
default:
XTRACE(kUnexpectedEvent, fState, event);
DebugLog("TIrLAPConn::HandleStandbyStateEvent: bad event");
break;
}
}
void TIrLAPConn::HandleConnectOrListenStateEvent(ULong event)
{
XASSERT(!fConnected);
switch (event) {
case kIrConnectRequestEvent:
case kIrListenRequestEvent:
{
TIrConnLstnRequest* request = (TIrConnLstnRequest*)GetCurrentEvent();
XTRACE(kPendingConnLstnRequestEvent, event, request->fEvent);
if (fDisconnectPending) { XTRACE(kPendingConnLstnDeferRequest, 0, GetCurrentEvent());
fPendingRequests->InsertLast(GetCurrentEvent());
check(GetCurrentEvent()->fEvent == kIrListenRequestEvent || GetCurrentEvent()->fEvent == kIrConnectRequestEvent);
return;
}
if (event == kIrConnectRequestEvent) {
if (fPeerDevAddr && (fPeerDevAddr != request->fDevAddr)) { request->fEvent = kIrConnectReplyEvent;
request->fResult = kIrDAErrGeneric; check(request->fLSAPConn);
request->fLSAPConn->EnqueueEvent(request); return;
}
if (fPeerDevAddr == 0) { if (GetLAP->CancelPendingListenRequest()) { fPeerDevAddr = request->fDevAddr; GetLAP->EnqueueEvent(request); }
else { request->fEvent = kIrConnectReplyEvent;
request->fResult = kIrDAErrGeneric; check(request->fLSAPConn);
request->fLSAPConn->EnqueueEvent(request); return;
}
} }
XASSERT(!fLSAPConnList->IsEmpty());
XASSERT(!fLSAPConnList->Contains(request->fLSAPConn));
XTRACE(kAddingToLSAPConnList, 1, request->fLSAPConn->GetMyLSAPId() << 8 | 1);
XTRACE(kAddingLsapToList, 0, request->fLSAPConn);
fLSAPConnList->InsertLast(request->fLSAPConn);
}
break;
case kIrConnectReplyEvent:
case kIrListenReplyEvent:
{
TIrConnLstnReply* reply = (TIrConnLstnReply*)GetCurrentEvent();
XTRACE(kPendingConnLstnReplyEvent, event, reply->fResult);
{
CListIterator *iter = CListIterator::cListIterator(fLSAPConnList);
for (TLSAPConn* lsapConn = (TLSAPConn*)iter->FirstItem();
iter->More(); lsapConn = (TLSAPConn*)iter->NextItem()) {
TIrConnLstnReply* pendingReply = (TIrConnLstnReply*)lsapConn->GetPendConnLstn();
if (pendingReply) {
if (pendingReply && pendingReply->fEvent == kIrConnectRequestEvent) {
pendingReply->fEvent = kIrConnectReplyEvent;
}
else if (pendingReply->fEvent == kIrListenRequestEvent){
pendingReply->fEvent = kIrListenReplyEvent;
}
else {
XASSERT((pendingReply->fEvent == kIrConnectReplyEvent) ||
(pendingReply->fEvent == kIrListenReplyEvent));
}
pendingReply->fResult = reply->fResult;
pendingReply->fDevAddr = reply->fDevAddr;
lsapConn->EnqueueEvent(pendingReply);
}
}
iter->release();
}
if (reply->fResult == noErr) {
fPeerDevAddr = reply->fDevAddr; fState = kIrLAPConnActive;
fConnected = true;
}
else {
XTRACE(kAddingToLSAPConnList, 0, 2); for (FastInt index = fLSAPConnList->GetArraySize() - 1; index >= 0 ; index--) {
TLSAPConn* lsapConn = (TLSAPConn*)fLSAPConnList->At(index);
XTRACE(kAddingToLSAPConnList, 0, lsapConn->GetMyLSAPId() << 8 | 2); XTRACE(kAddingLsapToList, 0, lsapConn); fLSAPConnList->Remove(lsapConn);
CleanupPendingGetRequestsAndReplies(lsapConn, errCancel);
}
fPeerDevAddr = 0;
fState = kIrLAPConnStandby;
}
}
break;
case kIrDisconnectRequestEvent:
{
TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)GetCurrentEvent();
XTRACE(kPendingDisconnectRequestEvent, 0, disconnectRequest->fLSAPConn);
check(disconnectRequest->fLSAPConn);
if (GetLAP->GetCurrentRequest() == disconnectRequest->fLSAPConn->GetPendConnLstn()) {
fDisconnectPending = true; GetLAP->EnqueueEvent(disconnectRequest); }
else {
XTRACE(kAddingToLSAPConnList, 0, disconnectRequest->fLSAPConn->GetMyLSAPId() << 8 | 3);
XTRACE(kAddingLsapToList, 0, disconnectRequest->fLSAPConn); (void) fLSAPConnList->Remove(disconnectRequest->fLSAPConn);
CleanupPendingGetRequestsAndReplies(disconnectRequest->fLSAPConn, errCancel);
XTRACE(kPendingDisconnectRequestEvent, 2, (fLSAPConnList->IsEmpty()) << 1 );
if (disconnectRequest->fLSAPConn->GetPendConnLstn()) { TIrConnLstnReply* pendingReply = (TIrConnLstnReply*)disconnectRequest->fLSAPConn->GetPendConnLstn();
if (pendingReply->fEvent == kIrConnectRequestEvent) {
pendingReply->fEvent = kIrConnectReplyEvent;
}
else if (pendingReply->fEvent == kIrListenRequestEvent) {
pendingReply->fEvent = kIrListenReplyEvent;
}
pendingReply->fResult = errCancel;
pendingReply->fLSAPConn->EnqueueEvent(pendingReply);
}
disconnectRequest->fEvent = kIrDisconnectReplyEvent;
disconnectRequest->fResult = errCancel;
disconnectRequest->fLSAPConn->EnqueueEvent(disconnectRequest);
}
}
break;
case kIrDisconnectReplyEvent:
{
TIrDisconnectReply* disconnectReply = (TIrDisconnectReply*)GetCurrentEvent();
XTRACE(kPendingDisconnectReplyEvent, 0, 0);
fPeerDevAddr = 0;
fConnected = false;
fState = kIrLAPConnStandby;
if (disconnectReply->fLSAPConn == nil) fIrDA->ReleaseEventBlock(disconnectReply); else
disconnectReply->fLSAPConn->EnqueueEvent(disconnectReply);
if (fPendingRequests && !fPendingRequests->Empty()) {
CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
iter->More(); request = (TIrEvent*)iter->NextItem()) {
XTRACE(kPendingDisconnectRequeue, 0, request);
check(request->fEvent == kIrListenRequestEvent || request->fEvent == kIrConnectRequestEvent);
this->EnqueueEvent(request);
}
while (!fPendingRequests->Empty())
fPendingRequests->RemoveLast();
iter->release();
}
fDisconnectPending = false; }
break;
default:
XTRACE(kUnexpectedEvent, fState, event);
DebugLog("TIrLAPConn::HandleConnectOrListenStateEvent: bad event");
break;
}
}
void TIrLAPConn::HandleActiveStateEvent(ULong event)
{
XASSERT(fConnected);
XASSERT(fPeerDevAddr != 0);
StopIdleDisconnectTimer(); switch (event) {
case kIrConnectRequestEvent:
case kIrListenRequestEvent:
{
TIrConnLstnRequest* request = (TIrConnLstnRequest*)GetCurrentEvent();
XTRACE(kActiveConnLstnRequestEvent, event, request->fEvent);
if (fDisconnectPending) { XTRACE(kActiveConnLstnDeferRequest, 0, GetCurrentEvent());
fPendingRequests->InsertLast(GetCurrentEvent());
check(GetCurrentEvent()->fEvent == kIrListenRequestEvent || GetCurrentEvent()->fEvent == kIrConnectRequestEvent);
return;
}
if (event == kIrConnectRequestEvent) {
if (fPeerDevAddr != request->fDevAddr) {
request->fEvent = kIrConnectReplyEvent;
request->fResult = kIrDAErrGeneric; check(request->fLSAPConn);
request->fLSAPConn->EnqueueEvent(request); break; }
}
XASSERT(!fLSAPConnList->Contains(request->fLSAPConn));
XTRACE(kAddingToLSAPConnList, 1, request->fLSAPConn->GetMyLSAPId() << 8 | 4); XTRACE(kAddingLsapToList, 0, request->fLSAPConn); fLSAPConnList->InsertLast(request->fLSAPConn);
request->fEvent = event == kIrConnectRequestEvent ? kIrConnectReplyEvent : kIrListenReplyEvent;
request->fDevAddr = fPeerDevAddr;
check(request->fLSAPConn);
request->fLSAPConn->EnqueueEvent(request);
}
break;
case kIrGetDataRequestEvent:
XTRACE(kActiveGetDataRequestEvent, 0, 0);
HandleGetDataRequest();
break;
case kIrCancelGetRequestEvent:
{
TIrCancelGetRequest* cancelGetRequest = (TIrCancelGetRequest*)GetCurrentEvent();
XTRACE(kActiveCancelGetRequestEvent, 0, 0);
CancelPendingGetRequests(cancelGetRequest->fLSAPConn, kIrDAErrRequestCanceled);
cancelGetRequest->fEvent = kIrCancelGetReplyEvent;
cancelGetRequest->fResult = noErr;
check(cancelGetRequest->fLSAPConn);
cancelGetRequest->fLSAPConn->EnqueueEvent(cancelGetRequest);
}
break;
case kIrDisconnectRequestEvent:
{
TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)GetCurrentEvent();
XTRACE(kActiveDisconnectRequestEvent, 0, 0);
XTRACE(kAddingToLSAPConnList, 0, disconnectRequest->fLSAPConn->GetMyLSAPId() << 8 | 5); XTRACE(kAddingLsapToList, 0, disconnectRequest->fLSAPConn);
IrDAErr removeResult = fLSAPConnList->Remove(disconnectRequest->fLSAPConn);
CleanupPendingGetRequestsAndReplies(disconnectRequest->fLSAPConn, kIrDAErrGeneric);
if (removeResult == noErr && fLSAPConnList->GetArraySize() == 1) { TLSAPConn* lsapConn = (TLSAPConn*)fLSAPConnList->At(0); if (lsapConn->GetMyLSAPId() == kNameServerLSAPId) { TIrConnLstnReply* pendingReply = (TIrConnLstnReply*)lsapConn->GetPendConnLstn(); if (pendingReply &&
(pendingReply->fEvent == kIrListenRequestEvent || pendingReply->fEvent == kIrGetDataRequestEvent)) { StartIdleDisconnectTimer(); }
}
}
if (disconnectRequest->fEvent == kIrDisconnectRequestEvent) { if (((removeResult == noErr) && fLSAPConnList->IsEmpty())) {
fDisconnectPending = true; GetLAP->EnqueueEvent(disconnectRequest);
}
else {
disconnectRequest->fEvent = kIrDisconnectReplyEvent;
disconnectRequest->fResult = errCancel;
check(disconnectRequest->fLSAPConn);
disconnectRequest->fLSAPConn->EnqueueEvent(disconnectRequest);
}
}
}
break;
case kIrDisconnectReplyEvent:
{
TIrDisconnectReply* disconnectReply = (TIrDisconnectReply*)GetCurrentEvent();
XTRACE(kActiveDisconnectReplyEvent, 0, 0);
fPeerDevAddr = 0;
fConnected = false;
fState = kIrLAPConnStandby;
if (disconnectReply->fLSAPConn == nil) { fIrDA->ReleaseEventBlock(disconnectReply); }
else { disconnectReply->fLSAPConn->EnqueueEvent(disconnectReply);
}
if (fPendingRequests && !fPendingRequests->Empty()) {
CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
iter->More(); request = (TIrEvent*)iter->NextItem()) {
XTRACE(kActiveDisconnectRequeue, 0, request);
check(request->fEvent == kIrListenRequestEvent || request->fEvent == kIrConnectRequestEvent);
this->EnqueueEvent(request);
}
while (!fPendingRequests->Empty())
fPendingRequests->RemoveLast();
iter->release();
}
fDisconnectPending = false; }
break;
case kIdleDisconnectEvent: {
if (fLSAPConnList->GetArraySize() == 1) { TLSAPConn* lsapConn = (TLSAPConn*)fLSAPConnList->At(0); if (lsapConn &&
lsapConn->GetMyLSAPId() == kNameServerLSAPId) { TIrConnLstnReply* pendingReply = (TIrConnLstnReply*)lsapConn->GetPendConnLstn(); if (pendingReply && (pendingReply->fEvent == kIrGetDataRequestEvent)) { IrDAErr removeResult;
removeResult = fLSAPConnList->Remove(lsapConn); CancelPendingGetRequests(lsapConn, errCancel);
TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)
fIrDA->GrabEventBlock(kIrDisconnectRequestEvent,
sizeof(TIrDisconnectRequest));
check(disconnectRequest); if (disconnectRequest) {
disconnectRequest->fLSAPConn = nil; fDisconnectPending = true; GetLAP->EnqueueEvent(disconnectRequest);
}
}
}
}
}
break;
default:
XTRACE(kUnexpectedEvent, fState, event);
DebugLog("TIrLAPConn::HandleActiveStateEvent: bad event");
break;
}
}
void TIrLAPConn::HandleGetDataRequest()
{
Boolean matchFound = false;
TIrGetRequest* getRequest = (TIrGetRequest*)GetCurrentEvent();
CListIterator *iter = CListIterator::cListIterator(fUnmatchedGetReplys);
for (CBufferSegment* replyBuffer = (CBufferSegment*)iter->FirstItem();
iter->More(); replyBuffer = (CBufferSegment*)iter->NextItem()) {
TLMPDUHeader header;
ULong headerLength;
Boolean validFormat;
validFormat = ExtractHeader(replyBuffer, header, headerLength);
XASSERT(validFormat);
if (DataDelivered(getRequest, header, headerLength, replyBuffer)) {
fUnmatchedGetReplys->Remove(replyBuffer);
matchFound = true;
break;
}
}
iter->release();
if (!matchFound) {
XTRACE(kLogAddingGetRequest1, 0, getRequest);
XTRACE(kLogAddingGetRequest2, 0, getRequest->fLSAPConn);
fPendingGetRequests->InsertLast(getRequest);
XTRACE(kLogAddingGetRequest3, fPendingGetRequests->Count(), getRequest->fLSAPConn->GetMyLSAPId());
}
}
void TIrLAPConn::CleanupPendingGetRequestsAndReplies(TLSAPConn* lsapConn, IrDAErr returnCode)
{
XTRACE(kLogCleanupPendingGetRequestsAndRepliesEntry, 0, lsapConn);
CancelPendingGetRequests( lsapConn, returnCode );
XTRACE(kLogCleanupPendingGetRequestsAndReplies2, 0, fUnmatchedGetReplys);
if (fUnmatchedGetReplys) {
CListIterator *iter = CListIterator::cListIterator(fUnmatchedGetReplys);
for (CBufferSegment* replyBuffer = (CBufferSegment*)iter->FirstItem();
iter->More(); replyBuffer = (CBufferSegment*)iter->NextItem()) {
TLMPDUHeader header;
ULong headerLength;
Boolean validFormat;
XTRACE(kLogCleanupPendingGetRequestsAndReplies3, 0, replyBuffer);
validFormat = ExtractHeader(replyBuffer, header, headerLength);
XASSERT(validFormat);
if (lsapConn->YourData(header, true )) {
XTRACE( kCleanupPendingRcvdBufEvent, 0, returnCode );
fUnmatchedGetReplys->Remove(replyBuffer);
GetLAP->ReleaseInputBuffer(replyBuffer); }
}
iter->release();
}
}
void TIrLAPConn::CancelPendingGetRequests(TLSAPConn* lsapConn, IrDAErr returnCode)
{
XTRACE(kLogCancelPendingGetRequestsEntry, 0, lsapConn);
check(lsapConn);
if (fPendingGetRequests && !fPendingGetRequests->Empty()) {
CListIterator *iter = CListIterator::cListIterator(fPendingGetRequests);
for (TIrGetRequest* getRequest = (TIrGetRequest*)iter->FirstItem();
iter->More(); getRequest = (TIrGetRequest*)iter->NextItem()) {
if (getRequest->fLSAPConn == lsapConn) {
XTRACE(kLogCancelPendingGetRequests, 0, lsapConn);
fPendingGetRequests->Remove(getRequest); getRequest->fEvent = kIrGetDataReplyEvent;
getRequest->fResult = returnCode;
lsapConn->EnqueueEvent(getRequest);
iter->release(); CancelPendingGetRequests(lsapConn, returnCode);
return;
}
}
iter->release();
}
XTRACE(kLogCancelPendingGetRequestsExit, 0, lsapConn);
}
void TIrLAPConn::Demultiplexor(CBufferSegment* inputBuffer)
{
TLMPDUHeader header;
ULong headerLength;
Boolean validFormat;
Boolean matchFound = false;
XTRACE(kLogDemux, 0, inputBuffer);
validFormat = ExtractHeader(inputBuffer, header, headerLength);
if (!validFormat || ((header.fOpCode & ~kLMPDUReplyFlag) == kLMPDUAccessModeRequest)) {
XTRACE(kDemuxInvalidHeaderEvent, 0, 0);
matchFound = true;
GetLAP->ReleaseInputBuffer(inputBuffer);
if (validFormat && (header.fOpCode == kLMPDUAccessModeRequest) && (header.fMode <= kIrLMPExclusiveMode)) {
ReplyToInvalidFrame(header, kLMPDUAccessModeReply, kIrLMPDUControlUnsupported);
}
}
if (!matchFound) {
XTRACE(kLogDemuxCheckingGets1, 0, fPendingGetRequests->Count());
CListIterator *iter = CListIterator::cListIterator(fPendingGetRequests);
for (TIrGetRequest* getRequest = (TIrGetRequest*)iter->FirstItem();
iter->More(); getRequest = (TIrGetRequest*)iter->NextItem()) {
XTRACE(kLogDemuxCheckingGets2, 0, getRequest);
if (DataDelivered(getRequest, header, headerLength, inputBuffer)) {
fPendingGetRequests->Remove(getRequest);
XTRACE(kDemuxGetPendingEvent, 0, getRequest);
matchFound = true;
break;
}
}
iter->release();
}
if (!matchFound) {
CListIterator *iter = CListIterator::cListIterator(fLSAPConnList);
for (TLSAPConn* lsapConn = (TLSAPConn*)iter->FirstItem();
iter->More(); lsapConn = (TLSAPConn*)iter->NextItem()) {
if (lsapConn->YourData(header, true )) {
fUnmatchedGetReplys->InsertLast(inputBuffer);
XTRACE(kDemuxReplyPostedEvent, 0, inputBuffer);
XTRACE(kDemuxReplyPostedEvent2, 0, lsapConn);
matchFound = true;
break;
}
}
iter->release();
}
if (!matchFound) {
XTRACE(kDemuxNoReceiverEvent, 0, 0);
GetLAP->ReleaseInputBuffer(inputBuffer);
UByte respCode;
if (header.fOpCode == kLMPDUDataEvent) {
respCode = kIrDataSentOnDiscLSAPConn;
}
else if (header.fOpCode == kLMPDUConnectRequest) {
respCode = kIrNoAvailableLMMuxClient;
}
else {
respCode = kIrUserRequestedDisconnect;
}
ReplyToInvalidFrame(header, kLMPDUDisconnectEvent, respCode);
}
}
void TIrLAPConn::ReplyToInvalidFrame(TLMPDUHeader& header, UByte replyOpCode, UByte replyInfo)
{
TIrPutRequest* putRequest;
putRequest = (TIrPutRequest*)fIrDA->GrabEventBlock(kIrPutDataRequestEvent, sizeof(TIrPutRequest));
if (putRequest != nil) {
putRequest->fLSAPConn = nil; putRequest->fData = nil;
putRequest->fOffset = 0;
putRequest->fLength = 0;
putRequest->fDstLSAPId = header.fSrcLSAPId | kLMPDUControlFlag;
putRequest->fSrcLSAPId = header.fDstLSAPId & ~kLMPDUControlFlag;
putRequest->fCtrlOpCode = replyOpCode;
putRequest->fCtrlInfo = replyInfo;
GetLAP->EnqueueEvent(putRequest);
}
}
Boolean TIrLAPConn::ExtractHeader(CBufferSegment* inputBuffer, TLMPDUHeader& header, ULong& length)
{
ULong headerLength;
inputBuffer->Seek(0, kPosBeg);
headerLength = inputBuffer->Getn(&header.fDstLSAPId, sizeof(TLMPDUHeader));
XASSERT(headerLength >= 2);
if (headerLength < 2) {
return false;
}
else if (header.fDstLSAPId & kLMPDUControlFlag) {
header.fDstLSAPId &= ~kLMPDUControlFlag;
if (headerLength == 2) {
return false;
} else if (headerLength == 3) {
header.fInfo = 0;
}
switch (header.fOpCode) {
case kLMPDUConnectRequest:
case kLMPDUConnectReply:
case kLMPDUDisconnectEvent:
headerLength = Min(headerLength, 4);
break;
case kLMPDUAccessModeRequest:
case kLMPDUAccessModeReply:
break;
default:
return false;
}
}
else {
headerLength = 2;
header.fOpCode = kLMPDUDataEvent;
header.fInfo = 0;
}
if ((header.fDstLSAPId > kLastValidLSAPId) || (header.fSrcLSAPId > kLastValidLSAPId)) {
return false;
}
length = headerLength;
return true;
}
Boolean TIrLAPConn::DataDelivered(TIrGetRequest* getRequest, TLMPDUHeader& header, ULong headerLength, CBufferSegment* dataBuffer)
{
if (getRequest->fLSAPConn->YourData(header, false )) {
ULong written = 0;
UInt32 dataLength = dataBuffer->GetBufferSize() - headerLength;
if (getRequest->fData && (dataLength > 0)) {
XASSERT(getRequest->fLength >= dataLength);
getRequest->fData->Seek(getRequest->fOffset, kPosBeg);
written = getRequest->fData->Putn(dataBuffer->GetBufferPtr() + headerLength, dataLength);
XASSERT(written == dataLength);
}
getRequest->fEvent = kIrGetDataReplyEvent;
getRequest->fResult = noErr;
getRequest->fLength = written;
getRequest->fCtrlOpCode = header.fOpCode;
getRequest->fCtrlInfo = header.fInfo;
check(getRequest->fLSAPConn);
getRequest->fLSAPConn->EnqueueEvent(getRequest);
XTRACE(kDemuxReleaseBufferEvent, 0, dataBuffer);
GetLAP->ReleaseInputBuffer(dataBuffer);
return true;
}
return false;
}
ULong TIrLAPConn::FillInLMPDUHeader(TIrPutRequest* putRequest, UByte* buffer)
{
ULong infoLength;
TLMPDUHeader* lmPDUHeader = (TLMPDUHeader*)buffer;
XTRACE(kLogFillInLMPDUHeader1, 0, putRequest);
XTRACE(kLogFillInLMPDUHeader2, 0, buffer);
lmPDUHeader->fDstLSAPId = putRequest->fDstLSAPId;
lmPDUHeader->fSrcLSAPId = putRequest->fSrcLSAPId;
if (putRequest->fCtrlOpCode != kLMPDUDataEvent) {
lmPDUHeader->fDstLSAPId |= kLMPDUControlFlag;
lmPDUHeader->fOpCode = putRequest->fCtrlOpCode;
lmPDUHeader->fInfo = putRequest->fCtrlInfo;
if (putRequest->fCtrlOpCode != kLMPDUAccessModeReply) {
infoLength = 4;
}
else {
buffer[4] = kIrLMPMultiplexedMode;
infoLength = 5;
}
}
else {
infoLength = 2;
}
return infoLength;
}
void TIrLAPConn::StartIdleDisconnectTimer()
{
XTRACE(kLogStartIdleDisconnectTimer, 0, 0);
fIrDA->StartTimer(kTimer_LAPConn, 1 * kSeconds, kIdleDisconnectEvent);
}
void TIrLAPConn::StopIdleDisconnectTimer()
{
XTRACE(kLogStopIdleDisconnectTimer, 0, 0);
if (fIrDA) fIrDA->StopTimer(kTimer_LAPConn);
}
void TIrLAPConn::DoIdleDisconnect()
{
XTRACE(kLogDoIdleDisconnect, 0, 0);
StopIdleDisconnectTimer(); if (fState == kIrLAPConnActive) NextState(kIdleDisconnectEvent); }
void TIrLAPConn::TimerComplete(ULong refCon)
{
XASSERT(refCon == kIrConnWatchdogExpiredEvent || refCon == kIdleDisconnectEvent);
if (refCon == kIrConnWatchdogExpiredEvent) { CListIterator *iter = CListIterator::cListIterator(fLSAPConnList);
XTRACE(kLogConnWatchDogFired, 0, 0);
for (TLSAPConn* lsapConn = (TLSAPConn*)iter->FirstItem(); iter->More(); lsapConn = (TLSAPConn*)iter->NextItem()) {
lsapConn->OneSecTickerComplete();
}
iter->release();
}
else {
XTRACE(kLogIdleDisconnectFired, 0, 0);
if (refCon == kIdleDisconnectEvent) if (fState == kIrLAPConnActive) NextState(refCon);
}
}