1/*
2    File:       IrLAPConn.c
3
4    Contains:   Implementation of the TIrLAPConn class
5
6
7*/
8
9#include "IrLAPConn.h"
10#include "IrLSAPConn.h"
11#include "IrGlue.h"
12#include "IrLAP.h"
13#include "CList.h"
14#include "CListIterator.h"
15#include "IrDALog.h"
16
17#if (hasTracing > 0 && hasLAPConnTracing > 0)
18
19enum IrLAPConnTraceCodes
20{
21    kNullEvent = 1,
22    kDestroy,
23    kInit,
24    kDeInit,
25    kUnexpectedEvent,
26    kLogStateEvent,
27
28    kStandbyConnLstnRequestEvent,
29    kStandbyDisconnectRequestEvent,
30    kStandbyDisconnectReplyEvent,
31    kStandbyDisconnectRequeue,
32
33    kPendingConnLstnRequestEvent,
34    kPendingConnLstnDeferRequest,           // jdg
35    kPendingConnLstnReplyEvent,
36    kPendingDisconnectRequestEvent,
37    kPendingDisconnectReplyEvent,
38    kPendingDisconnectRequeue,              // jdg
39
40    kActiveConnLstnRequestEvent,
41    kActiveConnLstnDeferRequest,            // jdg
42    kActiveGetDataRequestEvent,
43    kActiveCancelGetRequestEvent,
44    kActiveDisconnectRequestEvent,
45    kActiveDisconnectReplyEvent,
46    kActiveDisconnectRequeue,               // jdg
47
48    kDemuxInvalidHeaderEvent,
49    kDemuxGetPendingEvent,
50    kDemuxReplyPostedEvent,
51    kDemuxReplyPostedEvent2,
52    kDemuxNoReceiverEvent,
53    kDemuxReleaseBufferEvent,
54
55    kCancelPendingGetReqEvent,
56    kCleanupPendingRcvdBufEvent,
57
58    kWantToAdd,
59    kAddingToLSAPConnList,
60    kAddingLsapToList,
61
62    kLogStartIdleDisconnectTimer,
63    kLogStopIdleDisconnectTimer,
64    kLogDoIdleDisconnect,
65
66    kLogConnWatchDogFired,
67    kLogIdleDisconnectFired,
68
69    kLogReset,
70    kLogResetLsapConn,
71    kLogResetGetRequest,
72    kLogResetGetReply,
73    kLogResetPendingReq,
74
75    kLogDemux,
76    kLogDemuxCheckingGets1,
77    kLogDemuxCheckingGets2,
78    kLogAddingGetRequest1,
79    kLogAddingGetRequest2,
80    kLogAddingGetRequest3,
81
82    kLogCleanupPendingGetRequestsAndRepliesEntry,
83    kLogCleanupPendingGetRequestsAndReplies2,
84    kLogCleanupPendingGetRequestsAndReplies3,
85
86    kLogCancelPendingGetRequestsEntry,
87    kLogCancelPendingGetRequests,
88    kLogCancelPendingGetRequestsExit,
89
90    kLogFillInLMPDUHeader1,
91    kLogFillInLMPDUHeader2,
92
93    kEnqueueEvent,
94    kDequeueEventStart,
95    kDequeueEventEnd
96};
97
98EventTraceCauseDesc IrLAPConnTraceEvents[] = {
99    {kNullEvent,                    "irlapconn: create obj="},
100    {kDestroy,                      "irlapconn: destroy obj="},
101    {kInit,                         "irlapconn: init"},
102    {kDeInit,                       "irlapconn: deinit"},
103    {kUnexpectedEvent,              "irlapconn: unexpected event"},
104    {kLogStateEvent,                "irlapconn: next state, state=, event="},
105
106    {kStandbyConnLstnRequestEvent,  "irlapconn: standby conn/lstn request"},
107    {kStandbyDisconnectRequestEvent,"irlapconn: standby disconnect request"},
108    {kStandbyDisconnectReplyEvent,  "irlapconn: standby disconnect reply"},
109    {kStandbyDisconnectRequeue,     "irlapconn: standby requeue pending requests"},     // jdg
110
111    {kPendingConnLstnRequestEvent,  "irlapconn: uconnect conn/lstn request"},
112    {kPendingConnLstnDeferRequest,  "irlapconn: uconnect conn/lstn defer request"},     // jdg
113    {kPendingConnLstnReplyEvent,    "irlapconn: uconnect conn/lstn reply"},
114    {kPendingDisconnectRequestEvent,"irlapconn: uconnect disconnect request"},
115    {kPendingDisconnectReplyEvent,  "irlapconn: uconnect disconnect reply"},
116    {kPendingDisconnectRequeue,     "irlapconn: uconnect requeue pending request"},
117
118    {kActiveConnLstnRequestEvent,   "irlapconn: active conn/lstn request"},
119    {kActiveConnLstnDeferRequest,   "irlapconn: active conn/lstn defer request"},       // jdg
120    {kActiveGetDataRequestEvent,    "irlapconn: active get data request"},
121    {kActiveCancelGetRequestEvent,  "irlapconn: active cancel get request"},
122    {kActiveDisconnectRequestEvent, "irlapconn: active disconnect request"},
123    {kActiveDisconnectReplyEvent,   "irlapconn: active disconnect reply"},
124    {kActiveDisconnectRequeue,      "irlapconn: active requeue pending request"},
125
126    {kDemuxInvalidHeaderEvent,      "irlapconn: invalid header"},
127    {kDemuxGetPendingEvent,         "irlapconn: found get pending, event rec="},
128    {kDemuxReplyPostedEvent,        "irlapconn: found lsapconn, no get. buf="},
129    {kDemuxReplyPostedEvent2,       "irlapconn: found lsapconn, no get. lsapconn="},
130    {kDemuxNoReceiverEvent,         "irlapconn: no receiver"},
131    {kDemuxReleaseBufferEvent,      "irlapconn: release buffer"},
132
133    {kCancelPendingGetReqEvent,     "irlapconn: cancel pending get request, lsapconn="},
134    {kCleanupPendingRcvdBufEvent,   "irlapconn: cleaning up pending recd buffer"},
135
136    {kWantToAdd,                    "irlapconn: ** want to add lsapconn to list"},
137    {kAddingToLSAPConnList,         "irlapconn: ** add/del to fLSAPConnList, add=, id="},
138    {kAddingLsapToList,             "irlapconn: ** add/del of lsap="},
139
140    {kLogStartIdleDisconnectTimer,  "irlapconn: starting idle disconnect timer"},
141    {kLogStopIdleDisconnectTimer,   "irlapconn: stoppng idle disconnect timer"},
142    {kLogDoIdleDisconnect,          "irlapconn: doing idle disconnect"},
143
144    {kLogConnWatchDogFired,         "irlapconn: conn watchdog timer fired"},
145    {kLogIdleDisconnectFired,       "irlapconn: idle disconnect timer fired"},
146
147    {kLogReset,                     "irlapconn: reset. fConnected, fState"},
148    {kLogResetLsapConn,             "irlapconn: reset. lsapconn="},
149    {kLogResetGetRequest,           "irlapconn: reset. getrequest="},
150    {kLogResetGetReply,             "irlapconn: reset. getreply="},
151    {kLogResetPendingReq,           "irlapconn: reset. pending request"},
152
153    {kLogDemux,                     "irlapconn: demux input packet, buf="},
154    {kLogDemuxCheckingGets1,        "irlapconn: demux, pending get count="},
155    {kLogDemuxCheckingGets2,        "irlapconn: demux, checking event rec="},
156    {kLogAddingGetRequest1,         "irlapconn: saving get req, event Rec="},
157    {kLogAddingGetRequest2,         "irlapconn: saving get req, lsapconn="},
158    {kLogAddingGetRequest3,         "irlapconn: saving get req, qlen=, lsapid="},
159
160    {kLogCleanupPendingGetRequestsAndRepliesEntry,  "irlapconn: CleanupPendingGetRequestsAndReplies entry, lsapConn="},
161    {kLogCleanupPendingGetRequestsAndReplies2,      "irlapconn: CleanupPendingGetRequestsAndReplies 2"},
162    {kLogCleanupPendingGetRequestsAndReplies3,      "irlapconn: CleanupPendingGetRequestsAndReplies, replyBuffer="},
163
164    {kLogCancelPendingGetRequestsEntry,         "irlapconn: CancelPendingGetRequestsEntry"},
165    {kLogCancelPendingGetRequests,              "irlapconn: CancelPendingGetRequests"},
166    {kLogCancelPendingGetRequestsExit,          "irlapconn: CancelPendingGetRequestsExit"},
167
168    {kLogFillInLMPDUHeader1,        "irlapconn: putRequest"},
169    {kLogFillInLMPDUHeader2,        "irlapconn: buffer"},
170
171
172    {kEnqueueEvent,                 "irlapconn: Event Queued"},
173    {kDequeueEventStart,            "irlapconn: Event Start"},
174    {kDequeueEventEnd,              "irlapconn: Event End"}
175};
176
177#define XTRACE(x, y, z) IrDALogAdd( x, y, (uintptr_t)z & 0xffff, IrLAPConnTraceEvents, true )
178#else
179#define XTRACE(x, y, z) ((void)0)
180#endif
181
182#define GetLAP  (fIrDA->GetLAP())
183
184#define super TIrStream
185	OSDefineMetaClassAndStructors(TIrLAPConn, TIrStream);
186
187//--------------------------------------------------------------------------------
188//      tIrLAPConn
189//--------------------------------------------------------------------------------
190/*static*/
191TIrLAPConn *
192TIrLAPConn::tIrLAPConn(TIrGlue* irda)
193{
194	TIrLAPConn *obj = new TIrLAPConn;
195	XTRACE(kNullEvent, 0, obj);
196
197	if (obj && !obj->Init(irda)) {
198		obj->release();
199		obj = nil;
200	}
201	return obj;
202}
203
204
205
206//--------------------------------------------------------------------------------
207//      free
208//--------------------------------------------------------------------------------
209void
210TIrLAPConn::free()
211{
212    XTRACE(kDestroy, 0, this);
213
214    // Free things allocated by TIrLAPConn
215    StopIdleDisconnectTimer();              // make sure the timer is off
216
217#define FREE(x) { if (x) { (x)->release(); x = nil; } }
218
219    FREE(fLSAPConnList);                // Free the LSAPConn list
220    FREE(fPendingGetRequests);          // Free the pending get requests list
221    FREE(fUnmatchedGetReplys);          // Free the unmatched get replys list
222    FREE(fPendingRequests);             // Free the pending event list
223
224
225    super::free();
226
227} // TIrLAPConn::free
228
229
230//--------------------------------------------------------------------------------
231//      Init
232//--------------------------------------------------------------------------------
233Boolean TIrLAPConn::Init(TIrGlue* irda)
234{
235    XTRACE(kInit, 0, this);
236
237
238    fState = kIrLAPConnStandby;
239    fConnected = false;
240    fPeerDevAddr = 0;
241    fLSAPConnList = nil;
242    fPendingGetRequests = nil;
243    fUnmatchedGetReplys = nil;
244    fPendingRequests = nil;
245
246    fDisconnectPending = false;
247
248
249    // Init IrStream
250#if (hasTracing > 0 && hasLAPConnTracing > 0)
251     if (!super::Init(irda, IrLAPConnTraceEvents, kEnqueueEvent)) return false;
252#else
253    if (!super::Init(irda)) return false;
254#endif
255
256
257    // Init LSAPConn list
258    fLSAPConnList = CList::cList();
259    require(fLSAPConnList, Fail);
260
261    // Init the pending get requests list
262    fPendingGetRequests = CList::cList();
263    require(fPendingGetRequests, Fail);
264
265    // Init the received (but unmatched) get replies
266    fUnmatchedGetReplys = CList::cList();
267    require(fUnmatchedGetReplys, Fail);
268
269    // Init the list of deferred requests
270    fPendingRequests = CList::cList();
271    require(fPendingRequests, Fail);
272
273    return true;
274
275Fail:
276
277    return false;
278
279} // TIrLAPConn::Init
280
281
282//--------------------------------------------------------------------------------
283//      Reset
284//--------------------------------------------------------------------------------
285void TIrLAPConn::Reset()
286{
287
288    XTRACE(kLogReset, fConnected, fState);
289
290    // This will force other asserts in HandleStandbyStateEvent which check
291    // to see that all of the other fields were properly reset at disconnect.
292
293    fState          = kIrLAPConnStandby;    // Using Reset for Aync disconnect.  There
294    fConnected      = false;                // will be no disconnect reply (for now).
295    fPeerDevAddr    = 0;
296
297    StopIdleDisconnectTimer();              // make sure the timer is off
298
299    //
300    // JDG: let's loop over our lsap conn list and purge everything.
301    // this should pick up pending get requests and pending get replies
302    // but for sanities sake, we'll check them too after this iteration.
303    //
304    if (fLSAPConnList && !fLSAPConnList->Empty()) {     // get rid of all pending listen/connects
305	int index;
306	TLSAPConn* lsapConn;
307
308	XTRACE(kLogResetLsapConn, 1, fLSAPConnList->GetArraySize());
309	for (index = fLSAPConnList->GetArraySize() - 1; index >= 0 ; index--) {
310	    lsapConn = (TLSAPConn*)fLSAPConnList->At(index);
311	    XTRACE(kLogResetLsapConn, 0, lsapConn);
312	    // Complete pending get requests and delete any received buffers intended for this conn
313	    CleanupPendingGetRequestsAndReplies(lsapConn, errCancel);
314	}
315	while (!fLSAPConnList->Empty())
316	    fLSAPConnList->RemoveLast();
317	XTRACE(kLogResetLsapConn, 0xffff, 0xffff);
318    }
319
320    if (fPendingGetRequests && !fPendingGetRequests->Empty()) {     // Cancel all pending Get requests
321	CListIterator *iter = CListIterator::cListIterator(fPendingGetRequests);
322	TIrGetRequest *getRequest;
323
324	DebugLog("IrLapConn: reset pending get request. how?"); // shouldn't get here
325	for (getRequest = (TIrGetRequest*)iter->FirstItem();
326	     iter->More(); getRequest = (TIrGetRequest*)iter->NextItem()) {
327	//for (getRequest = OSDynamicCast(TIrGetRequest, (OSObject *)iter->FirstItem());
328	//   iter->More(); getRequest = OSDynamicCast(TIrGetRequest, (OSObject *)iter->NextItem())) {
329	    XTRACE(kLogResetGetRequest, 0, getRequest);
330	    // This is somewhat inefficient, since Cleanup will loop through the pending list
331	    // again to match the LSAP.  But that's ok.
332	    this->CleanupPendingGetRequestsAndReplies( getRequest->fLSAPConn, errCancel);
333	}
334	iter->release();
335    }
336
337    // fUnmatchedGetReplys is a list of CBufferSegments that have come in, discard them
338    if (fUnmatchedGetReplys && !fUnmatchedGetReplys->Empty()) {
339	CListIterator *iter = CListIterator::cListIterator(fUnmatchedGetReplys);
340	CBufferSegment* replyBuffer;
341
342	DebugLog("IrLapConn: reset pending get replies. how?"); // shouldn't get here
343	for (replyBuffer = (CBufferSegment*)iter->FirstItem();
344	     iter->More(); replyBuffer = (CBufferSegment*)iter->NextItem()) {
345	    XTRACE(kLogResetGetReply, 0, replyBuffer);
346	    GetLAP->ReleaseInputBuffer(replyBuffer);    // give the buffer back to lap
347	}
348	while (!fUnmatchedGetReplys->Empty())
349	    fUnmatchedGetReplys->RemoveLast();
350
351	iter->release();
352    }
353
354    // if we had pendingRequests, they were waiting for a disconnect, do 'em now!
355    if (fPendingRequests && !fPendingRequests->Empty()) {
356	CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
357	for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
358	     iter->More(); request = (TIrEvent*)iter->NextItem()) {
359		XTRACE(kLogResetPendingReq, 0, request);
360		check(request->fEvent == kIrListenRequestEvent || request->fEvent == kIrConnectRequestEvent);
361		this->EnqueueEvent(request);
362	}
363	while (!fPendingRequests->Empty())
364	    fPendingRequests->RemoveLast();
365
366	iter->release();
367    }
368    fDisconnectPending = false;         // safe to connect/listen again
369
370    // Whew.  I *think* we've fully reset LapConn.
371
372} // TIrLAPConn::Reset
373
374
375
376//--------------------------------------------------------------------------------
377//      NextState
378//--------------------------------------------------------------------------------
379void TIrLAPConn::NextState(ULong event)
380{
381    XTRACE(kLogStateEvent, fState, event);
382
383    switch (fState) {
384	case kIrLAPConnStandby:
385	    HandleStandbyStateEvent(event);
386	    break;
387
388	case kIrLAPConnConnectOrListen:
389	    HandleConnectOrListenStateEvent(event);
390	    break;
391
392	case kIrLAPConnActive:
393	    HandleActiveStateEvent(event);
394	    break;
395
396	default:
397	    DebugLog("TIrLAPConn::NextState: bad fState");
398	    break;
399    }
400
401} // TIrLAPConn::NextState
402
403
404//--------------------------------------------------------------------------------
405//      HandleStandbyStateEvent
406//--------------------------------------------------------------------------------
407void TIrLAPConn::HandleStandbyStateEvent(ULong event)
408{
409    XASSERT(!fConnected);
410    XASSERT(fPeerDevAddr == 0);
411
412    switch (event) {
413	case kIrConnectRequestEvent:
414	case kIrListenRequestEvent:
415	    {
416		TIrConnLstnRequest* request = (TIrConnLstnRequest*)GetCurrentEvent();
417		XTRACE(kStandbyConnLstnRequestEvent, event, 0);
418
419		XTRACE(kWantToAdd, (uintptr_t)request->fLSAPConn>>16, request->fLSAPConn);
420
421		// Add the lsapConn to the list of pending conns to connect/listen
422		// jdg: the IAS server gets an error on it's listen and immediately issues another
423		// listen request.  Since it didn't do a disconnect, it's LSAP is still on our list.
424		if (!fLSAPConnList->Contains(request->fLSAPConn)) {
425		    fLSAPConnList->InsertLast(request->fLSAPConn);
426		}
427
428		// Keep track of the device that we will be connected to
429		if (event == kIrConnectRequestEvent) {
430		    fPeerDevAddr = request->fDevAddr;
431		}
432
433		// Pass the request on to IrLAP
434		// ***This can be dangerous if the connect can be cancelled separately
435		// from all other pending requests and then the IrLAP will depend on a
436		// buffer that no longer is "allocated".  In other words, maybe a separate
437		// req buffer should be allocated for this purpose.
438		fState = kIrLAPConnConnectOrListen;
439		GetLAP->EnqueueEvent(request);
440	    }
441	    break;
442
443	case kIrDisconnectRequestEvent:
444	    {
445		// Pass the disconnect on to IrLAP ********* JDG: nope, this is not what we want.  lap is already disconnected
446		XTRACE(kStandbyDisconnectRequestEvent, 0, 0);
447
448		if (1) {        // jdg testing
449		    TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)GetCurrentEvent();
450		    // Remove this lsap from the lsap conn list -- if it's still there
451		    if (fLSAPConnList->Contains(disconnectRequest->fLSAPConn)) {    // sigh, it doesn't always ...
452			XTRACE(kAddingToLSAPConnList, 0,
453				disconnectRequest->fLSAPConn->GetMyLSAPId() << 8 | 99);
454			XTRACE(kAddingLsapToList, 0, disconnectRequest->fLSAPConn);        // jdg
455		    IrDAErr removeResult = fLSAPConnList->Remove(disconnectRequest->fLSAPConn);
456		    ncheck(removeResult);
457		    }
458		}
459		//fIrLAP->EnqueueEvent(GetCurrentEvent());      // no, don't do it -- jdg
460		if (1) {        // jdg new
461		    TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)GetCurrentEvent();
462		    disconnectRequest->fEvent = kIrDisconnectReplyEvent;        // turn into a reply
463		    disconnectRequest->fResult = 0;
464		    check(disconnectRequest->fLSAPConn);            // internal disconnect?
465		    disconnectRequest->fLSAPConn->EnqueueEvent(disconnectRequest);  // send it back
466		}
467	    }
468	    break;
469
470	case kIrDisconnectReplyEvent:
471	    {
472		// Use the request block for the reply
473		TIrDisconnectReply* disconnectReply = (TIrDisconnectReply*)GetCurrentEvent();
474		XTRACE(kStandbyDisconnectReplyEvent, 0, 0);
475		// Already disconnected - nothing to do, except return the reply.
476		//check(disconnectReply->fLSAPConn);
477		if (disconnectReply->fLSAPConn == nil)              // if internally generated event
478		    fIrDA->ReleaseEventBlock(disconnectReply);      // then release it here
479		else
480		disconnectReply->fLSAPConn->EnqueueEvent(disconnectReply);
481
482		// jdg: if any deferred events, requeue them now
483		if (fPendingRequests && !fPendingRequests->Empty()) {
484		    CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
485		    for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
486			 iter->More(); request = (TIrEvent*)iter->NextItem()) {
487			    XTRACE(kStandbyDisconnectRequeue, 0, request);
488			    check(request->fEvent == kIrListenRequestEvent || request->fEvent == kIrConnectRequestEvent);
489			    this->EnqueueEvent(request);
490		    }
491		    while (!fPendingRequests->Empty())
492			fPendingRequests->RemoveLast();
493
494		    iter->release();
495		}
496		fDisconnectPending = false;         // safe to connect/listen again
497	    }
498	    break;
499
500	case kIrGetDataRequestEvent:            // if we get a get/put here, just reject it
501	case kIrPutDataRequestEvent:
502	    {
503		XTRACE(kUnexpectedEvent, fState, event);
504		TIrGetRequest* rq = (TIrGetRequest*)GetCurrentEvent();
505		if (rq->fEvent == kIrGetDataRequestEvent)       // turn request into reply (could ++ it)
506		    rq->fEvent = kIrGetDataReplyEvent;
507		else
508		    rq->fEvent = kIrPutDataReplyEvent;
509		rq->fResult = kIrDAErrWrongState;           // better than crashing, but why are we here?
510		check(rq->fLSAPConn);
511		rq->fLSAPConn->EnqueueEvent(rq);            // return to sender, bad destination
512	    }
513	    break;
514
515	default:
516	    XTRACE(kUnexpectedEvent, fState, event);
517	    DebugLog("TIrLAPConn::HandleStandbyStateEvent: bad event");
518	    break;
519    }
520
521} // TIrLAPConn::HandleStandbyStateEvent
522
523
524//--------------------------------------------------------------------------------
525//      HandleConnectOrListenStateEvent
526//--------------------------------------------------------------------------------
527void TIrLAPConn::HandleConnectOrListenStateEvent(ULong event)
528{
529    XASSERT(!fConnected);
530
531    switch (event) {
532	case kIrConnectRequestEvent:
533	case kIrListenRequestEvent:
534	    {
535		TIrConnLstnRequest* request = (TIrConnLstnRequest*)GetCurrentEvent();
536		XTRACE(kPendingConnLstnRequestEvent, event, request->fEvent);
537
538		if (fDisconnectPending) {           // oops, hold off on this request unti the disconnect is done
539		    XTRACE(kPendingConnLstnDeferRequest, 0, GetCurrentEvent());
540		    fPendingRequests->InsertLast(GetCurrentEvent());
541		    check(GetCurrentEvent()->fEvent == kIrListenRequestEvent || GetCurrentEvent()->fEvent == kIrConnectRequestEvent);
542		    return;
543		}
544
545		// ***Here is a problem that probably will have to be dealt with when the
546		// IrDA "stack" is used by a mux tool (i.e. multiple endpoints).  If a listen
547		// was the first request to initiate a listen to LAP and then a connect request
548		// followed, you need to cancel the pending LAP listen request and replace it
549		// with a connect request.
550
551		// Shouldn't be called if connecting to a different device, reject the request if it comes in that way
552		if (event == kIrConnectRequestEvent) {
553		    // This first one checks for the situation described above
554		    //XASSERT(fPeerDevAddr != 0);
555		    //XASSERT(fPeerDevAddr == request->fDevAddr);
556									// JDG: if 1st was connect & this is going to diff device
557		    if (fPeerDevAddr && (fPeerDevAddr != request->fDevAddr)) {  // then reject this connect request
558			request->fEvent = kIrConnectReplyEvent;
559			request->fResult = kIrDAErrGeneric;             // better err available?
560			check(request->fLSAPConn);
561			request->fLSAPConn->EnqueueEvent(request);      // return to sender, bad destination
562			return;
563		    }
564
565		    // JDG hacking  ... won't it ever stop?
566		    // we have a connect request, was the first request a listen?  if so ....
567		    // ask LAP to cancel the pending listen and then send in our connect request.
568		    // Foo.  Bletch.  Ugh.
569		    if (fPeerDevAddr == 0) {                // if 1st request was a listen ...
570			if (GetLAP->CancelPendingListenRequest()) {     // if the lap cancel worked
571			    fPeerDevAddr = request->fDevAddr;           // save the destination address
572			    GetLAP->EnqueueEvent(request);              // then send connect req to lap
573			    // continue and add the lsapconn to our list
574			    // note: the listen request is still on our queue and will finish with the connect
575			}
576			else {      // the cancel of the listen failed, reject the connect request (or?)
577			    request->fEvent = kIrConnectReplyEvent;
578			    request->fResult = kIrDAErrGeneric;             // better err available?
579			    check(request->fLSAPConn);
580			    request->fLSAPConn->EnqueueEvent(request);      // return to sender, bad destination
581			    return;
582			}
583		    }       // end first request a listen
584		}       // end if connect request
585
586		// Add the lsapConn to the list of pending conns to connect/listen
587		XASSERT(!fLSAPConnList->IsEmpty());
588		XASSERT(!fLSAPConnList->Contains(request->fLSAPConn));
589
590		XTRACE(kAddingToLSAPConnList, 1, request->fLSAPConn->GetMyLSAPId() << 8 | 1);
591		XTRACE(kAddingLsapToList, 0, request->fLSAPConn);
592		fLSAPConnList->InsertLast(request->fLSAPConn);
593
594		// No state change
595	    }
596	    break;
597
598	case kIrConnectReplyEvent:
599	case kIrListenReplyEvent:
600	    {
601		TIrConnLstnReply* reply = (TIrConnLstnReply*)GetCurrentEvent();
602		XTRACE(kPendingConnLstnReplyEvent, event, reply->fResult);
603		{
604		    CListIterator *iter = CListIterator::cListIterator(fLSAPConnList);
605
606		    // Complete all pending connect/listen requests that are in the list
607		    for (TLSAPConn* lsapConn = (TLSAPConn*)iter->FirstItem();
608			 iter->More(); lsapConn = (TLSAPConn*)iter->NextItem()) {
609			TIrConnLstnReply* pendingReply = (TIrConnLstnReply*)lsapConn->GetPendConnLstn();
610			//check(pendingReply);      // TEMP TEMP TEMP -- should track this down
611			if (pendingReply) {
612			    // ***Oops, this mess is because some pending buffers are used for the
613			    // ***actual request and others aren't - see other note earlier in the file.
614			    if (pendingReply && pendingReply->fEvent == kIrConnectRequestEvent) {
615				pendingReply->fEvent = kIrConnectReplyEvent;
616			    }
617			    else if (pendingReply->fEvent == kIrListenRequestEvent){
618				pendingReply->fEvent = kIrListenReplyEvent;
619			    }
620			    else {
621				// Assuming that reply has been set by IrLAP.
622				// I.e. this pendingReply == GetCurrentEvent()
623				XASSERT((pendingReply->fEvent == kIrConnectReplyEvent) ||
624					(pendingReply->fEvent == kIrListenReplyEvent));
625			    }
626			    pendingReply->fResult = reply->fResult;
627			    pendingReply->fDevAddr = reply->fDevAddr;
628			    lsapConn->EnqueueEvent(pendingReply);
629			}
630		    }
631		    iter->release();
632
633		} // barney block
634
635		// If connect succeeded, move to the active state (California?)
636		if (reply->fResult == noErr) {
637		    fPeerDevAddr = reply->fDevAddr; // For listen's benefit
638		    fState = kIrLAPConnActive;
639		    fConnected = true;
640		}
641
642		// If connect failed, clean up after responding to the requestors
643		// query: shouldn't the cleanup pending be done first?  and isn't it a nop here?
644		else {
645		    // Removing from end is much faster (and keeps indices valid as items are removed)
646		    XTRACE(kAddingToLSAPConnList, 0, 2);                                    // jdg
647		    for (FastInt index = fLSAPConnList->GetArraySize() - 1; index >= 0 ; index--) {
648			TLSAPConn* lsapConn = (TLSAPConn*)fLSAPConnList->At(index);
649			// Remove this lsap from the lsap conn list
650			XTRACE(kAddingToLSAPConnList, 0,  lsapConn->GetMyLSAPId() << 8 | 2);    // jdg
651			XTRACE(kAddingLsapToList, 0, lsapConn);        // jdg
652			fLSAPConnList->Remove(lsapConn);
653			// Complete pending get requests and delete any received buffers intended for this conn
654			CleanupPendingGetRequestsAndReplies(lsapConn, errCancel);
655		    }
656		    fPeerDevAddr = 0;
657		    fState = kIrLAPConnStandby;
658		}
659	    }
660	    break;
661
662	case kIrDisconnectRequestEvent:
663	    {
664		TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)GetCurrentEvent();
665		XTRACE(kPendingDisconnectRequestEvent, 0, disconnectRequest->fLSAPConn);
666    /*** this didn't work.  Just do a lap disconnect, and we'll return all the pending listen/connect
667	 requests.  Anyone that want's to keep alive can re-issue.
668      ... let's try again
669    ***/
670		// if the requesting lsapconn owns the event we sent to lap, do a lap disconnect
671		// else we're free to just take it off our tables w/out bothering lap
672		check(disconnectRequest->fLSAPConn);
673		if (GetLAP->GetCurrentRequest() == disconnectRequest->fLSAPConn->GetPendConnLstn()) {
674		    fDisconnectPending = true;                  // hold off on any new listen/connect requests
675		    GetLAP->EnqueueEvent(disconnectRequest);    // tell lap to blow off the current listen/conn req
676		}
677		else {              // lap doesn't have the event from this lsapconn
678
679		    XTRACE(kAddingToLSAPConnList, 0, disconnectRequest->fLSAPConn->GetMyLSAPId() << 8 | 3);
680		    XTRACE(kAddingLsapToList, 0, disconnectRequest->fLSAPConn);        // jdg
681		    // Remove this lsap from the lsap conn list and
682		    // complete pending get requests and delete any received buffers intended for this conn
683		    (void) fLSAPConnList->Remove(disconnectRequest->fLSAPConn);
684		    CleanupPendingGetRequestsAndReplies(disconnectRequest->fLSAPConn, errCancel);
685
686		    XTRACE(kPendingDisconnectRequestEvent, 2,  (fLSAPConnList->IsEmpty()) << 1 );
687
688
689		    // now finish off their original listen/connect request
690		    if (disconnectRequest->fLSAPConn->GetPendConnLstn()) {      // if the event is there (should be)
691			TIrConnLstnReply* pendingReply = (TIrConnLstnReply*)disconnectRequest->fLSAPConn->GetPendConnLstn();
692			if (pendingReply->fEvent == kIrConnectRequestEvent) {
693			    pendingReply->fEvent = kIrConnectReplyEvent;
694			}
695			else if (pendingReply->fEvent == kIrListenRequestEvent) {
696				pendingReply->fEvent = kIrListenReplyEvent;
697			}
698			//else check(pendingReply->fEvent == 0x1234);     // force debugger
699			pendingReply->fResult = errCancel;
700			pendingReply->fLSAPConn->EnqueueEvent(pendingReply);
701		    }
702
703		    // Use the request buffer for the reply
704		    disconnectRequest->fEvent = kIrDisconnectReplyEvent;
705		    disconnectRequest->fResult = errCancel;
706		    disconnectRequest->fLSAPConn->EnqueueEvent(disconnectRequest);
707		}
708	    }
709	    break;
710
711	case kIrDisconnectReplyEvent:
712	    {
713		TIrDisconnectReply* disconnectReply = (TIrDisconnectReply*)GetCurrentEvent();
714		XTRACE(kPendingDisconnectReplyEvent, 0, 0);
715		fPeerDevAddr = 0;
716		fConnected = false;
717		fState = kIrLAPConnStandby;
718		//check(disconnectReply->fLSAPConn);            // internal disconnect?
719		if (disconnectReply->fLSAPConn == nil)          // if lapconn generated disconnect
720		    fIrDA->ReleaseEventBlock(disconnectReply);      // then release it here
721		else
722		disconnectReply->fLSAPConn->EnqueueEvent(disconnectReply);
723
724		// jdg: if any deferred events, requeue them now
725		if (fPendingRequests && !fPendingRequests->Empty()) {
726		    CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
727		    for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
728			 iter->More(); request = (TIrEvent*)iter->NextItem()) {
729			    XTRACE(kPendingDisconnectRequeue, 0, request);
730			    check(request->fEvent == kIrListenRequestEvent || request->fEvent == kIrConnectRequestEvent);
731			    this->EnqueueEvent(request);
732		    }
733		    while (!fPendingRequests->Empty())
734			fPendingRequests->RemoveLast();
735
736		    iter->release();
737		}
738		fDisconnectPending = false;         // safe to connect/listen again
739	    }
740	    break;
741
742	default:
743	    XTRACE(kUnexpectedEvent, fState, event);
744	    DebugLog("TIrLAPConn::HandleConnectOrListenStateEvent: bad event");
745	    break;
746    }
747
748} // TIrLAPConn::HandleConnectOrListenStateEvent
749
750
751//--------------------------------------------------------------------------------
752//      HandleActiveStateEvent
753//--------------------------------------------------------------------------------
754void TIrLAPConn::HandleActiveStateEvent(ULong event)
755{
756    XASSERT(fConnected);
757    XASSERT(fPeerDevAddr != 0);
758
759    StopIdleDisconnectTimer();          // always (?) stop the idle timer, we're doing something!
760    switch (event) {
761	case kIrConnectRequestEvent:
762	case kIrListenRequestEvent:
763	    {
764		TIrConnLstnRequest* request = (TIrConnLstnRequest*)GetCurrentEvent();
765		XTRACE(kActiveConnLstnRequestEvent, event, request->fEvent);
766
767		if (fDisconnectPending) {           // oops, hold off on this request unti the disconnect is done
768		    XTRACE(kActiveConnLstnDeferRequest, 0, GetCurrentEvent());
769		    fPendingRequests->InsertLast(GetCurrentEvent());
770		    check(GetCurrentEvent()->fEvent == kIrListenRequestEvent || GetCurrentEvent()->fEvent == kIrConnectRequestEvent);
771		    return;
772		}
773
774		// Shouldn't be called if connecting to a different device
775		// jdg: this should reject the request, not punt.
776		if (event == kIrConnectRequestEvent) {
777		    //XASSERT(fPeerDevAddr == request->fDevAddr);
778		    if (fPeerDevAddr != request->fDevAddr) {
779			// FIXME -- should add logging here of both addresses to see why the confusion ...
780			request->fEvent = kIrConnectReplyEvent;
781			request->fResult = kIrDAErrGeneric;             // better err available?
782			check(request->fLSAPConn);
783			request->fLSAPConn->EnqueueEvent(request);      // return to sender, bad destination
784			break;                                          // done with it now
785		    }
786		}
787
788		// Add the lsapConn to the list of conns associated w/fPeerDevAddr
789		XASSERT(!fLSAPConnList->Contains(request->fLSAPConn));
790		XTRACE(kAddingToLSAPConnList, 1, request->fLSAPConn->GetMyLSAPId() << 8 | 4);                                                       // jdg
791		XTRACE(kAddingLsapToList, 0, request->fLSAPConn);        // jdg
792		fLSAPConnList->InsertLast(request->fLSAPConn);
793
794		// Already connected, reply to the requestor
795		request->fEvent = event == kIrConnectRequestEvent ? kIrConnectReplyEvent : kIrListenReplyEvent;
796		request->fDevAddr = fPeerDevAddr;
797		check(request->fLSAPConn);
798		request->fLSAPConn->EnqueueEvent(request);
799
800		// No state change
801	    }
802	    break;
803
804	case kIrGetDataRequestEvent:
805	    XTRACE(kActiveGetDataRequestEvent, 0, 0);
806	    HandleGetDataRequest();
807	    break;
808
809	case kIrCancelGetRequestEvent:
810	    {
811		TIrCancelGetRequest* cancelGetRequest = (TIrCancelGetRequest*)GetCurrentEvent();
812		XTRACE(kActiveCancelGetRequestEvent, 0, 0);
813		CancelPendingGetRequests(cancelGetRequest->fLSAPConn, kIrDAErrRequestCanceled);
814		// Use the request buffer for the reply
815		cancelGetRequest->fEvent = kIrCancelGetReplyEvent;
816		cancelGetRequest->fResult = noErr;
817		check(cancelGetRequest->fLSAPConn);
818		cancelGetRequest->fLSAPConn->EnqueueEvent(cancelGetRequest);
819	    }
820	    break;
821
822	case kIrDisconnectRequestEvent:
823	    {
824		TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)GetCurrentEvent();
825		XTRACE(kActiveDisconnectRequestEvent, 0, 0);
826
827		XTRACE(kAddingToLSAPConnList, 0, disconnectRequest->fLSAPConn->GetMyLSAPId() << 8 | 5);                                                     // jdg
828		XTRACE(kAddingLsapToList, 0, disconnectRequest->fLSAPConn);        // jdg
829
830		// Remove this lsap from the lsap conn list
831		IrDAErr removeResult = fLSAPConnList->Remove(disconnectRequest->fLSAPConn);
832		// Complete pending get requests and delete any received buffers intended for this conn
833		CleanupPendingGetRequestsAndReplies(disconnectRequest->fLSAPConn, kIrDAErrGeneric);
834
835		// Note: if I do a disconnect after getting a read complete, this gets called twice, once
836		// for the disconnect request, and (I think) once to clean up the pending get request.
837		///////////////////////////////////////////////////////////////////////////
838		// start of ugly hack to kill off name server if it's all that's left
839		//  this gets weird cause it's "the" pending event for LAP.  Grrr.
840		if (removeResult == noErr && fLSAPConnList->GetArraySize() == 1) {  // if only one thing left on the list
841		    TLSAPConn* lsapConn = (TLSAPConn*)fLSAPConnList->At(0);         // grab it
842		    if (lsapConn->GetMyLSAPId() == kNameServerLSAPId) {             // sigh, if the name server
843			TIrConnLstnReply* pendingReply = (TIrConnLstnReply*)lsapConn->GetPendConnLstn();    // get it's event
844			if (pendingReply &&
845			    (pendingReply->fEvent == kIrListenRequestEvent ||           // it was a listen request, but now that
846			     pendingReply->fEvent == kIrGetDataRequestEvent)) {         // we're active, it's a get request
847	    // NEW: let's start a timer here and do the lap disconnect after N seconds of
848	    // nothing going on ...
849			    StartIdleDisconnectTimer();             // start the idle disconnect timer
850		//
851		// Nuke the name server's listen so we can do a LAP disconnect
852		//
853		//          check(pendingReply->fEvent == kIrGetDataRequestEvent);          // just making sure
854		//          removeResult = fLSAPConnList->Remove(lsapConn);                 // get name server off our list
855		//          CancelPendingGetRequests(lsapConn, errCancel);              // this will q up the get response
856			}
857		    }
858		}
859		////////
860		//if ((removeResult == noErr) && fLSAPConnList->IsEmpty() && fIrDA->Disconnecting())
861		if (disconnectRequest->fEvent == kIrDisconnectRequestEvent) {       // if "the" event is still available
862    ////////*** with the idle disconnect timer, we never request a lap disconnect here anymore
863		    if (((removeResult == noErr) && fLSAPConnList->IsEmpty())) {
864		    // If no more connections [and shutting down] - disconnect IrLAP
865			fDisconnectPending = true;                  // hold off on any new listen/connect requests
866		    GetLAP->EnqueueEvent(disconnectRequest);
867		}
868		else {
869		    // Use the request buffer for the reply
870		    disconnectRequest->fEvent = kIrDisconnectReplyEvent;
871		    disconnectRequest->fResult = errCancel;
872		    check(disconnectRequest->fLSAPConn);
873		    disconnectRequest->fLSAPConn->EnqueueEvent(disconnectRequest);
874		}
875		}
876		// else the event was a get and turned into a get reply already by CleanupPendingGetRequestsAndReplies
877	    }
878	    break;
879
880	case kIrDisconnectReplyEvent:
881	    {
882		TIrDisconnectReply* disconnectReply = (TIrDisconnectReply*)GetCurrentEvent();
883		XTRACE(kActiveDisconnectReplyEvent, 0, 0);
884		fPeerDevAddr = 0;
885		fConnected = false;
886		fState = kIrLAPConnStandby;
887
888		// new - if this is a disconnect generated by our idle timer, then fLSAPConn will be nil
889		if (disconnectReply->fLSAPConn == nil) {            // if we allocated the event
890		    fIrDA->ReleaseEventBlock(disconnectReply);      // then release it here
891		    //DebugLog(" disconnect due to idle");
892		}
893		else {      // else send the disconnect complete back to the lsap
894		    disconnectReply->fLSAPConn->EnqueueEvent(disconnectReply);
895		}
896
897		// jdg: if any deferred events, requeue them now
898		if (fPendingRequests && !fPendingRequests->Empty()) {
899		    CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
900		    for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
901			 iter->More(); request = (TIrEvent*)iter->NextItem()) {
902			    XTRACE(kActiveDisconnectRequeue, 0, request);
903			    check(request->fEvent == kIrListenRequestEvent || request->fEvent == kIrConnectRequestEvent);
904			    this->EnqueueEvent(request);
905		    }
906		    while (!fPendingRequests->Empty())
907			fPendingRequests->RemoveLast();
908
909		    iter->release();
910		}
911		fDisconnectPending = false;         // safe to connect/listen again
912	    }
913	    break;
914
915	case kIdleDisconnectEvent:                  // idle disconnect timer fired
916	    {
917		if (fLSAPConnList->GetArraySize() == 1) {               // if only one thing on the list
918		    TLSAPConn* lsapConn = (TLSAPConn*)fLSAPConnList->At(0);         // grab ias server entry
919		    if (lsapConn &&
920			lsapConn->GetMyLSAPId() == kNameServerLSAPId) {             // ifthe name server
921			TIrConnLstnReply* pendingReply = (TIrConnLstnReply*)lsapConn->GetPendConnLstn();    // get it's event
922			if (pendingReply &&                                         // a listen is really a get
923			    (pendingReply->fEvent == kIrGetDataRequestEvent)) {     // we're active, it's a get request
924			    IrDAErr removeResult;
925			    removeResult = fLSAPConnList->Remove(lsapConn);         // get name server off our list
926			    CancelPendingGetRequests(lsapConn, errCancel);      // this will q up the get response
927
928			    TIrDisconnectRequest* disconnectRequest = (TIrDisconnectRequest*)
929						    fIrDA->GrabEventBlock(kIrDisconnectRequestEvent,
930								    sizeof(TIrDisconnectRequest));
931			    check(disconnectRequest);       // pretty sad if no events and idle!
932			    if (disconnectRequest) {
933				disconnectRequest->fLSAPConn = nil;     // lapconn generated event (not LSAPConn)
934				fDisconnectPending = true;              // hold off on any new listen/connect requests
935				GetLAP->EnqueueEvent(disconnectRequest);
936			    }
937			}
938		    }
939		}
940	    }
941	    break;
942
943	default:
944	    XTRACE(kUnexpectedEvent, fState, event);
945	    DebugLog("TIrLAPConn::HandleActiveStateEvent: bad event");
946	    break;
947    }
948
949} // TIrLAPConn::HandleActiveStateEvent
950
951
952//================================ Helper methods ================================
953
954
955//--------------------------------------------------------------------------------
956//      HandleGetDataRequest
957//--------------------------------------------------------------------------------
958void TIrLAPConn::HandleGetDataRequest()
959{
960    Boolean matchFound = false;
961    TIrGetRequest* getRequest = (TIrGetRequest*)GetCurrentEvent();
962
963    // Data may have already arrived and is in the unmatched get replys list
964    // If it is, then the get data request can complete right now
965    CListIterator *iter = CListIterator::cListIterator(fUnmatchedGetReplys);
966    for (CBufferSegment* replyBuffer = (CBufferSegment*)iter->FirstItem();
967	 iter->More(); replyBuffer = (CBufferSegment*)iter->NextItem()) {
968	TLMPDUHeader header;
969	ULong headerLength;
970	Boolean validFormat;
971
972	validFormat = ExtractHeader(replyBuffer, header, headerLength);
973	XASSERT(validFormat);
974
975	if (DataDelivered(getRequest, header, headerLength, replyBuffer)) {
976	    fUnmatchedGetReplys->Remove(replyBuffer);
977	    matchFound = true;
978	    break;
979	}
980    }
981    iter->release();
982
983    // Data is not in one of the buffers, add the request to the pending get requests.
984    if (!matchFound) {
985	XTRACE(kLogAddingGetRequest1, 0, getRequest);
986	XTRACE(kLogAddingGetRequest2, 0, getRequest->fLSAPConn);
987	fPendingGetRequests->InsertLast(getRequest);
988	XTRACE(kLogAddingGetRequest3, fPendingGetRequests->Count(), getRequest->fLSAPConn->GetMyLSAPId());
989    }
990
991} // TIrLAPConn::HandleGetDataRequest
992
993
994//--------------------------------------------------------------------------------
995//      CleanupPendingGetRequestsAndReplies
996//--------------------------------------------------------------------------------
997void TIrLAPConn::CleanupPendingGetRequestsAndReplies(TLSAPConn* lsapConn, IrDAErr returnCode)
998{
999    XTRACE(kLogCleanupPendingGetRequestsAndRepliesEntry, 0, lsapConn);
1000
1001    // Complete any pending get requests with an error.  Default is kIRErrGeneric
1002    CancelPendingGetRequests( lsapConn, returnCode );   // ***FIXME: Better error return?
1003
1004    XTRACE(kLogCleanupPendingGetRequestsAndReplies2, 0, fUnmatchedGetReplys);
1005
1006    // Free any pending received buffers for this lsap connection
1007    if (fUnmatchedGetReplys) {
1008	CListIterator *iter = CListIterator::cListIterator(fUnmatchedGetReplys);
1009	for (CBufferSegment* replyBuffer = (CBufferSegment*)iter->FirstItem();
1010	     iter->More(); replyBuffer = (CBufferSegment*)iter->NextItem()) {
1011	    TLMPDUHeader header;
1012	    ULong headerLength;
1013
1014	    //Boolean validFormat = ExtractHeader(replyBuffer, header, headerLength);
1015	    Boolean validFormat;
1016
1017	    XTRACE(kLogCleanupPendingGetRequestsAndReplies3, 0, replyBuffer);
1018
1019	    validFormat = ExtractHeader(replyBuffer, header, headerLength);
1020	    XASSERT(validFormat);
1021
1022	    // If this buffer is/was for the lsapConn being removed
1023	    if (lsapConn->YourData(header, true /*justChecking*/)) {
1024		XTRACE( kCleanupPendingRcvdBufEvent, 0, returnCode );
1025		fUnmatchedGetReplys->Remove(replyBuffer);
1026		// Release the buffer.
1027		GetLAP->ReleaseInputBuffer(replyBuffer);    // give the buffer back to lap
1028	    }
1029	}
1030	iter->release();
1031    }
1032
1033} // TIrLAPConn::CleanupPendingGetRequestsAndReplies
1034
1035
1036//--------------------------------------------------------------------------------
1037//      CancelPendingGetRequests
1038//--------------------------------------------------------------------------------
1039void TIrLAPConn::CancelPendingGetRequests(TLSAPConn* lsapConn, IrDAErr returnCode)
1040{
1041    // Complete any pending get requests with an error
1042    XTRACE(kLogCancelPendingGetRequestsEntry, 0, lsapConn);
1043
1044    check(lsapConn);
1045    if (fPendingGetRequests && !fPendingGetRequests->Empty()) {
1046	CListIterator *iter = CListIterator::cListIterator(fPendingGetRequests);
1047	for (TIrGetRequest* getRequest = (TIrGetRequest*)iter->FirstItem();
1048	     iter->More(); getRequest = (TIrGetRequest*)iter->NextItem()) {
1049	    if (getRequest->fLSAPConn == lsapConn) {
1050		XTRACE(kLogCancelPendingGetRequests, 0, lsapConn);
1051		fPendingGetRequests->Remove(getRequest);    // get this req off the list
1052		// Send the reply
1053		getRequest->fEvent = kIrGetDataReplyEvent;
1054		getRequest->fResult = returnCode;
1055		lsapConn->EnqueueEvent(getRequest);
1056		// Theoretically there should only be one outstanding get request for
1057		// this lsapConn, so you should be able to break out of the loop here.
1058		// But, who ever believed in theory besides Albert Einstein?
1059		// Could add some debug only logic and an assert to test this...
1060		//
1061		// jdg: I don't trust remove in the middle of an iterate, so let's
1062		// recurse and return instead of finishing the iteration.  Note this
1063		// only recurses when we've found and processed an event for this
1064		// lsapconn, so we're not going to recurse forever.
1065		iter->release();            // we're done with this iteration engine
1066		CancelPendingGetRequests(lsapConn, returnCode);
1067		return;
1068	    }
1069	}
1070	iter->release();
1071    }
1072    XTRACE(kLogCancelPendingGetRequestsExit, 0, lsapConn);
1073
1074} // TIrLAPConn::CancelPendingGetRequests
1075
1076
1077//--------------------------------------------------------------------------------
1078//      Demultiplexor
1079//--------------------------------------------------------------------------------
1080void TIrLAPConn::Demultiplexor(CBufferSegment* inputBuffer)
1081{
1082    TLMPDUHeader header;
1083    ULong headerLength;
1084    Boolean     validFormat;
1085    Boolean     matchFound = false;
1086
1087    XTRACE(kLogDemux, 0, inputBuffer);
1088
1089    validFormat = ExtractHeader(inputBuffer, header, headerLength);
1090
1091    if (!validFormat || ((header.fOpCode & ~kLMPDUReplyFlag) == kLMPDUAccessModeRequest)) {
1092	XTRACE(kDemuxInvalidHeaderEvent, 0, 0);
1093	// We're responding.  Don't keep looking.
1094	matchFound = true;
1095	// Release the buffer
1096	GetLAP->ReleaseInputBuffer(inputBuffer);
1097	// Send a response for access requests (ignore the erroneous access confirms)
1098	if (validFormat && (header.fOpCode == kLMPDUAccessModeRequest) && (header.fMode <= kIrLMPExclusiveMode)) {
1099	    ReplyToInvalidFrame(header, kLMPDUAccessModeReply, kIrLMPDUControlUnsupported);
1100	}
1101    }
1102
1103    // Some received data has arrived.  If a match can be found in the pending get requests
1104    // then that request can be completed and the buffer can be freed up for reuse.
1105    if (!matchFound) {
1106	XTRACE(kLogDemuxCheckingGets1, 0, fPendingGetRequests->Count());
1107	CListIterator *iter = CListIterator::cListIterator(fPendingGetRequests);
1108	for (TIrGetRequest* getRequest = (TIrGetRequest*)iter->FirstItem();
1109	     iter->More(); getRequest = (TIrGetRequest*)iter->NextItem()) {
1110	    XTRACE(kLogDemuxCheckingGets2, 0, getRequest);
1111
1112	    if (DataDelivered(getRequest, header, headerLength, inputBuffer)) {
1113		fPendingGetRequests->Remove(getRequest);
1114		XTRACE(kDemuxGetPendingEvent, 0, getRequest);
1115		matchFound = true;
1116		break;
1117	    }
1118	}
1119	iter->release();
1120    }
1121
1122    // No one is waiting for the data.  Is it a potential reply? - cache it until its requested.
1123    if (!matchFound) {
1124	CListIterator *iter = CListIterator::cListIterator(fLSAPConnList);
1125	for (TLSAPConn* lsapConn = (TLSAPConn*)iter->FirstItem();
1126	     iter->More(); lsapConn = (TLSAPConn*)iter->NextItem()) {
1127	    if (lsapConn->YourData(header, true /*justChecking*/)) {
1128		fUnmatchedGetReplys->InsertLast(inputBuffer);
1129		XTRACE(kDemuxReplyPostedEvent, 0, inputBuffer);
1130		XTRACE(kDemuxReplyPostedEvent2, 0, lsapConn);
1131		matchFound = true;
1132		break;
1133	    }
1134	}
1135	iter->release();
1136    }
1137
1138    // No one connected that this could belong to.
1139    if (!matchFound) {
1140	XTRACE(kDemuxNoReceiverEvent, 0, 0);
1141	// Release the buffer
1142	GetLAP->ReleaseInputBuffer(inputBuffer);
1143	// Send a disconnect response
1144	UByte respCode;
1145	if (header.fOpCode == kLMPDUDataEvent) {
1146	    respCode = kIrDataSentOnDiscLSAPConn;
1147	}
1148	else if (header.fOpCode == kLMPDUConnectRequest) {
1149	    respCode = kIrNoAvailableLMMuxClient;
1150	}
1151	else {
1152	    respCode = kIrUserRequestedDisconnect;
1153	}
1154	ReplyToInvalidFrame(header, kLMPDUDisconnectEvent, respCode);
1155    }
1156
1157} // TIrLAPConn::Demultiplexor
1158
1159
1160//--------------------------------------------------------------------------------
1161//      ReplyToInvalidFrame
1162//--------------------------------------------------------------------------------
1163void TIrLAPConn::ReplyToInvalidFrame(TLMPDUHeader& header, UByte replyOpCode, UByte replyInfo)
1164{
1165    TIrPutRequest* putRequest;
1166
1167    // jdg: note that the check for nil fLSAPConn for this case is
1168    // in the PutComplete routine of IrLAP
1169
1170    putRequest = (TIrPutRequest*)fIrDA->GrabEventBlock(kIrPutDataRequestEvent, sizeof(TIrPutRequest));
1171    // Ignore this if no memory to get request block
1172    if (putRequest != nil) {
1173	putRequest->fLSAPConn = nil;    // Don't respond to putRequest, just free the block
1174	putRequest->fData = nil;
1175	putRequest->fOffset = 0;
1176	putRequest->fLength = 0;
1177	putRequest->fDstLSAPId = header.fSrcLSAPId | kLMPDUControlFlag;
1178	putRequest->fSrcLSAPId = header.fDstLSAPId & ~kLMPDUControlFlag;
1179	putRequest->fCtrlOpCode = replyOpCode;
1180	putRequest->fCtrlInfo = replyInfo;
1181	GetLAP->EnqueueEvent(putRequest);
1182    }
1183
1184} // TIrLAPConn::ReplyToInvalidFrame
1185
1186
1187//--------------------------------------------------------------------------------
1188//      ExtractHeader
1189//--------------------------------------------------------------------------------
1190Boolean TIrLAPConn::ExtractHeader(CBufferSegment* inputBuffer, TLMPDUHeader& header, ULong& length)
1191{
1192    ULong headerLength;
1193
1194    // Need to reseek to 0 as this may be called multiple times
1195    inputBuffer->Seek(0, kPosBeg);
1196
1197    // Get the header info
1198    headerLength = inputBuffer->Getn(&header.fDstLSAPId, sizeof(TLMPDUHeader));
1199    XASSERT(headerLength >= 2);
1200    if (headerLength < 2) {
1201	// LM-PDU header requires dst/src minimum
1202	return false;
1203    }
1204    else if (header.fDstLSAPId & kLMPDUControlFlag) {
1205	header.fDstLSAPId &= ~kLMPDUControlFlag;
1206	if (headerLength == 2) {
1207	    // Control header requires opcode minimum
1208	    return false;
1209	} else if (headerLength == 3) {
1210	    // Set unspecified info field to 0
1211	    header.fInfo = 0;
1212	}
1213	switch (header.fOpCode) {
1214	    case kLMPDUConnectRequest:
1215	    case kLMPDUConnectReply:
1216	    case kLMPDUDisconnectEvent:
1217		headerLength = Min(headerLength, 4);
1218		break;
1219
1220	    case kLMPDUAccessModeRequest:
1221	    case kLMPDUAccessModeReply:
1222		break;
1223
1224	    default:
1225		return false;
1226	}
1227    }
1228    else {
1229	headerLength = 2;
1230	header.fOpCode = kLMPDUDataEvent;
1231	header.fInfo = 0;
1232    }
1233
1234    // Invalid lsap ids
1235    if ((header.fDstLSAPId > kLastValidLSAPId) || (header.fSrcLSAPId > kLastValidLSAPId)) {
1236	return false;
1237    }
1238
1239    length = headerLength;
1240    return true;
1241
1242} // TIrLAPConn::ExtractHeader
1243
1244
1245//--------------------------------------------------------------------------------
1246//      DataDelivered
1247//--------------------------------------------------------------------------------
1248Boolean TIrLAPConn::DataDelivered(TIrGetRequest* getRequest, TLMPDUHeader& header, ULong headerLength, CBufferSegment* dataBuffer)
1249{
1250    if (getRequest->fLSAPConn->YourData(header, false /*justChecking*/)) {
1251	ULong written = 0;
1252
1253	UInt32 dataLength = dataBuffer->GetBufferSize() - headerLength;
1254	if (getRequest->fData && (dataLength > 0)) {
1255	    //if (getRequest->fLength < dataLength) {       // jdg
1256	    //  DebugPrintf("About to die, fLength=%d, datalength %d",
1257	    //      getRequest->fLength, dataLength);
1258	    //}
1259	    XASSERT(getRequest->fLength >= dataLength);
1260	    getRequest->fData->Seek(getRequest->fOffset, kPosBeg);
1261	    written = getRequest->fData->Putn(dataBuffer->GetBufferPtr() + headerLength, dataLength);
1262	    XASSERT(written == dataLength);
1263	}
1264
1265	// Fill in the fields for the reply
1266	getRequest->fEvent = kIrGetDataReplyEvent;
1267	getRequest->fResult = noErr;
1268	getRequest->fLength = written;
1269	getRequest->fCtrlOpCode = header.fOpCode;
1270	getRequest->fCtrlInfo = header.fInfo;
1271
1272	// Send the reply
1273	check(getRequest->fLSAPConn);
1274	getRequest->fLSAPConn->EnqueueEvent(getRequest);
1275
1276	// Release the buffer
1277	XTRACE(kDemuxReleaseBufferEvent, 0, dataBuffer);
1278	GetLAP->ReleaseInputBuffer(dataBuffer);
1279
1280	return true;
1281    }
1282
1283    return false;
1284
1285} // TIrLAPConn::DataDelivered
1286
1287
1288//--------------------------------------------------------------------------------
1289//      FillInLMPDUHeader
1290//--------------------------------------------------------------------------------
1291ULong TIrLAPConn::FillInLMPDUHeader(TIrPutRequest* putRequest, UByte* buffer)
1292{
1293    ULong infoLength;
1294    TLMPDUHeader* lmPDUHeader = (TLMPDUHeader*)buffer;
1295
1296    XTRACE(kLogFillInLMPDUHeader1, 0, putRequest);
1297    XTRACE(kLogFillInLMPDUHeader2, 0, buffer);
1298
1299    // Fill out the info
1300    lmPDUHeader->fDstLSAPId = putRequest->fDstLSAPId;
1301    lmPDUHeader->fSrcLSAPId = putRequest->fSrcLSAPId;
1302
1303    if (putRequest->fCtrlOpCode != kLMPDUDataEvent) {
1304	lmPDUHeader->fDstLSAPId |= kLMPDUControlFlag;
1305	lmPDUHeader->fOpCode = putRequest->fCtrlOpCode;
1306	lmPDUHeader->fInfo = putRequest->fCtrlInfo;
1307	if (putRequest->fCtrlOpCode != kLMPDUAccessModeReply) {
1308	    infoLength = 4;
1309	}
1310	else {
1311	    // Access mode follows "header" for access mode reply frame
1312	    buffer[4] = kIrLMPMultiplexedMode;
1313	    infoLength = 5;
1314	}
1315    }
1316    else {
1317	infoLength = 2;
1318    }
1319
1320    return infoLength;
1321
1322} // TIrLAPConn::FillInLMPDUHeader
1323
1324//--------------------------------------------------------------------------------
1325//      StartIdleDisconnectTimer
1326//--------------------------------------------------------------------------------
1327void TIrLAPConn::StartIdleDisconnectTimer()
1328{
1329    XTRACE(kLogStartIdleDisconnectTimer, 0, 0);
1330    // could just overload timer2, but this keeps sanity a little longer
1331    fIrDA->StartTimer(kTimer_LAPConn, 1 * kSeconds, kIdleDisconnectEvent);
1332
1333} // TIrLAPConn::StartIdleDisconnectTimer
1334
1335
1336//--------------------------------------------------------------------------------
1337//      StopIdleDisconnectTimer
1338//--------------------------------------------------------------------------------
1339void TIrLAPConn::StopIdleDisconnectTimer()
1340{
1341    XTRACE(kLogStopIdleDisconnectTimer, 0, 0);
1342    if (fIrDA)                  // if init'd
1343	fIrDA->StopTimer(kTimer_LAPConn);
1344} // TIrLAPConn::StopIdleDisconnectTimer
1345
1346//
1347// Do idle disconnect ... now, don't wait for the timer
1348//
1349void TIrLAPConn::DoIdleDisconnect()
1350{
1351    XTRACE(kLogDoIdleDisconnect, 0, 0);
1352
1353    StopIdleDisconnectTimer();              // first stop the real timer
1354    if (fState == kIrLAPConnActive)         // and if there is an active connect
1355	NextState(kIdleDisconnectEvent);    // do the idle disconnect logic
1356}
1357
1358//--------------------------------------------------------------------------------
1359//      TimerComplete
1360//--------------------------------------------------------------------------------
1361void TIrLAPConn::TimerComplete(ULong refCon)
1362{
1363//#pragma   unused(refCon)
1364    XASSERT(refCon == kIrConnWatchdogExpiredEvent || refCon == kIdleDisconnectEvent);
1365
1366    if (refCon == kIrConnWatchdogExpiredEvent) {    // one second timer has fired
1367	CListIterator *iter = CListIterator::cListIterator(fLSAPConnList);
1368	XTRACE(kLogConnWatchDogFired, 0, 0);
1369
1370	// Let all of the active lsap conn's know that the timer has fired
1371	for (TLSAPConn* lsapConn = (TLSAPConn*)iter->FirstItem(); iter->More(); lsapConn = (TLSAPConn*)iter->NextItem()) {
1372	    lsapConn->OneSecTickerComplete();
1373	}
1374	iter->release();
1375    }
1376    else {
1377	XTRACE(kLogIdleDisconnectFired, 0, 0);
1378	if (refCon == kIdleDisconnectEvent)         // the idle disconnect timer has fired
1379	    if (fState == kIrLAPConnActive)         // just another sanity check
1380		NextState(refCon);
1381    }
1382
1383} // TIrLAPConn::TimerComplete
1384
1385