1/*
2    File:       IrLMP.cpp
3
4    Contains:   Implementation of the TIrLMP class
5
6*/
7
8#include "IrGlue.h"
9#include "IrLMP.h"
10#include "IrLAP.h"
11#include "IrLAPConn.h"
12#include "CList.h"
13#include "CListIterator.h"
14#include "IrDiscovery.h"
15#include "IrDscInfo.h"
16
17#if (hasTracing > 0 && hasLMPTracing > 0)
18
19enum IrLMPTraceCodes
20{
21    kCreate = 1,
22    kInit,
23    kLogFree,
24    kUnexpectedEvent,
25
26    kStnCntlReadyDiscoverEvent,
27    kStnCntlReadyConnectEvent,
28    kStnCntlReadyListenEvent,
29    kStnCntlReadyDiscRequestEvent,
30    kStnCntlReadyDiscReplyEvent,
31
32    kStnCntlDiscoverDiscoverEvent,
33    kStnCntlResolveDiscoverEvent,
34
35    kLogLMPResetEntry,
36    kLogLMPResetExit,
37    kLogFillInLMPDUHeader,
38    kLogStartOneSecTicker,
39
40    kEnqueueEvent,
41    kDequeueEventStart,
42    kDequeueEventEnd
43};
44
45EventTraceCauseDesc IrLMPTraceEvents[] = {
46    {kCreate,                       "irlmp: create obj="},
47    {kInit,                         "irlmp: init, lmp="},
48    {kLogFree,                      "irlmp: free"},
49    {kUnexpectedEvent,              "irlmp: unexpected event"},
50
51    {kStnCntlReadyDiscoverEvent,    "irlmp: Ready discover request"},
52    {kStnCntlReadyConnectEvent,     "irlmp: Ready connect request"},
53    {kStnCntlReadyListenEvent,      "irlmp: Ready listen request"},
54    {kStnCntlReadyDiscRequestEvent, "irlmp: Ready disconnect request"},
55    {kStnCntlReadyDiscReplyEvent,   "irlmp: Ready disconnect reply"},
56
57    {kStnCntlDiscoverDiscoverEvent, "irlmp: Discv discover reply"},
58    {kStnCntlResolveDiscoverEvent,  "irlmp: Reslv discover reply"},
59
60    {kLogLMPResetEntry,             "irlmp: reset entry"},
61    {kLogLMPResetExit,              "irlmp: reset finished"},
62    {kLogFillInLMPDUHeader,         "irlmp: Fill In LMP PDU Header"},
63    {kLogStartOneSecTicker,         "irlmp: StartOneSecTicker"},
64
65    {kEnqueueEvent,                 "irlmp: Event Queued"},
66    {kDequeueEventStart,            "irlmp: Event Start"},
67    {kDequeueEventEnd,              "irlmp: Event End"}
68};
69
70#define XTRACE(x, y, z) IrDALogAdd( x, y, (uintptr_t)z & 0xffff, IrLMPTraceEvents, true )
71
72#else
73#define XTRACE(x, y, z) ((void)0)
74#endif
75
76#define GetLAP          (fIrDA->GetLAP())
77#define GetLAPConn      (fIrDA->GetLAPConn())
78#define GetDiscovery    (fIrDA->GetDiscovery())
79
80//--------------------------------------------------------------------------------
81#define super TIrStream
82    OSDefineMetaClassAndStructors(TIrLMP, TIrStream);
83
84//--------------------------------------------------------------------------------
85//      TIrLMP
86//--------------------------------------------------------------------------------
87/*static*/
88TIrLMP * TIrLMP::tIrLMP(TIrGlue* irda)
89{
90    TIrLMP *obj = new TIrLMP;
91
92    XTRACE(kCreate, 0, obj);
93
94    if (obj && !obj->Init(irda)) {
95	obj->release();
96	obj = nil;
97    }
98    return obj;
99}
100
101void TIrLMP::free(void)
102{
103    XTRACE(kLogFree, 0, this);
104
105    if (fPendingRequests) {     // cleanup pending event list
106	fPendingRequests->release();
107	fPendingRequests = nil;
108    }
109
110    super::free();
111}
112
113
114//--------------------------------------------------------------------------------
115//      Init
116//--------------------------------------------------------------------------------
117Boolean TIrLMP::Init(TIrGlue* irda)
118{
119    XTRACE(kInit, 0, this);
120
121
122    fState = kIrLMPReady;
123    fTimerClients = 0;
124
125    fNumAddrConflicts = 0;
126    bzero(fAddrConflicts, sizeof(fAddrConflicts));
127
128    fPendingRequests = nil;
129
130
131#if (hasTracing > 0 && hasLMPTracing > 0)
132    if (!super::Init(irda, IrLMPTraceEvents, kEnqueueEvent)) return false;
133#else
134    if (!super::Init(irda)) return false;
135#endif
136
137
138    fPendingRequests = CList::cList();      // make pending requests list
139    require(fPendingRequests, Fail);
140
141    return true;
142
143Fail:
144
145    return false;
146
147} // TIrLMP::Init
148
149
150//--------------------------------------------------------------------------------
151//      Reset
152//--------------------------------------------------------------------------------
153void TIrLMP::Reset()
154{
155    XTRACE(kLogLMPResetEntry, 0, 0);
156    // This is only intended for orderly reset (see IrGlue.c DisconnectComplete).
157
158    fState = kIrLMPReady;
159    fTimerClients = 0;
160
161    if (GetLAPConn != nil) {
162	GetLAPConn->Reset();
163    }
164    XTRACE(kLogLMPResetExit, 0, 0);
165
166} // TIrLMP::Reset
167
168
169
170//--------------------------------------------------------------------------------
171//      NextState
172//
173//      Station Control state transitions (IrLMP 3.5.2.3.1)
174//      Only supports Ready, Discover, and Resolve Address states
175//--------------------------------------------------------------------------------
176void TIrLMP::NextState(ULong event)
177{
178    switch (fState) {
179	case kIrLMPReady:
180	    HandleReadyStateEvent(event);
181	    break;
182
183	case kIrLMPDiscover:
184	    HandleDiscoverStateEvent(event);
185	    break;
186
187	case kIrLMPResolveAddress:
188	    HandleResolveAddressStateEvent(event);
189	    break;
190
191	default:
192	    DebugLog("TIrLMP::NextState: bad fState");
193	    break;
194    }
195
196} // TIrLMP::NextState
197
198
199//--------------------------------------------------------------------------------
200//      HandleReadyStateEvent
201//
202//      Station Control Ready state transitions (IrLMP 3.5.2.3.2)
203//      Not all states are supported (see above comment)
204//--------------------------------------------------------------------------------
205void TIrLMP::HandleReadyStateEvent(ULong event)
206{
207    switch (event) {
208	case kIrDiscoverRequestEvent:
209	    {
210		XTRACE(kStnCntlReadyDiscoverEvent, 0, 0);
211		TIrDiscoverRequest* discoverRequest = (TIrDiscoverRequest*)GetCurrentEvent();
212		discoverRequest->fConflictDevAddr = kIrLAPBroadcastDevAddr;
213		fState = kIrLMPDiscover;
214		GetLAP->EnqueueEvent(discoverRequest);
215	    }
216	    break;
217
218	case kIrConnectRequestEvent:
219	    XTRACE(kStnCntlReadyConnectEvent, 0, 0);
220	    // Forward connect request to LAPConn
221	    GetLAPConn->EnqueueEvent(GetCurrentEvent());
222	    break;
223
224	case kIrListenRequestEvent:
225	    XTRACE(kStnCntlReadyListenEvent, 0, 0);
226	    // Forward listen request to LAPConn
227	    GetLAPConn->EnqueueEvent(GetCurrentEvent());
228	    break;
229
230	case kIrConnectReplyEvent:
231	case kIrListenReplyEvent:
232	case kIrGetDataRequestEvent:
233	case kIrCancelGetRequestEvent:
234	    // Forward these to IrLAPConn
235	    GetLAPConn->EnqueueEvent(GetCurrentEvent());
236	    break;
237
238	case kIrPutDataRequestEvent:
239	case kIrCancelPutRequestEvent:
240	    // Forward these to IrLAP
241	    GetLAP->EnqueueEvent(GetCurrentEvent());
242	    break;
243
244	case kIrDisconnectRequestEvent:
245	    // If request came from IrDA (IrGlue) pass it directly to IrLAP
246	    //if (((TIrDisconnectEvent*)GetCurrentEvent())->fLSAPConn == nil) {
247	    //  XTRACE(kStnCntlReadyDiscRequestEvent, 0, 0);
248	    //  GetLAP->EnqueueEvent(GetCurrentEvent());
249	    //}
250
251	    // else (request came from an LSAPConn), pass it on to IrLAPConn
252	    //else {
253		XTRACE(kStnCntlReadyDiscRequestEvent, 1, 0);
254		GetLAPConn->EnqueueEvent(GetCurrentEvent());
255	    //}
256	    break;
257
258	case kIrDisconnectReplyEvent:
259	    // If request came from IrDA (IrGlue) pass it back
260	    //if (((TIrDisconnectEvent*)GetCurrentEvent())->fLSAPConn == nil) {
261	    //  XTRACE(kStnCntlReadyDiscReplyEvent, 0, 0);
262	    //  fIrDA->EnqueueEvent(GetCurrentEvent());
263	    //}
264
265	    // else (request came from an LSAPConn), pass it on to IrLAPConn
266	    //else {
267		XTRACE(kStnCntlReadyDiscReplyEvent, 1, 0);
268		GetLAPConn->EnqueueEvent(GetCurrentEvent());
269	    //}
270	    break;
271
272	case kIrDiscoverReplyEvent:                 // jdg: this can happen now if we're doing
273	    HandleDiscoverStateEvent(event);        // lots of discovers while connected and
274	    break;                                  // then get an async disconnect (lmp reset)
275
276	default:
277	    XTRACE(kUnexpectedEvent, fState, event);
278	    DebugLog("TIrLMP::HandleReadyStateEvent: bad event");
279	    break;
280    }
281
282} // TIrLMP::HandleReadyStateEvent
283
284
285//--------------------------------------------------------------------------------
286//      HandleDiscoverStateEvent
287//--------------------------------------------------------------------------------
288void TIrLMP::HandleDiscoverStateEvent(ULong event)
289{
290    switch (event) {
291	case kIrDiscoverReplyEvent:
292	    {
293		TIrDiscoverReply* discoverReply = (TIrDiscoverReply*)GetCurrentEvent();
294		// If returned with no error and addr conflicts, do addr conflict resolution
295		if ((discoverReply->fResult == noErr) &&
296		    (!discoverReply->fPassiveDiscovery) &&
297		    (AddrConflicts(discoverReply->fDiscoveredDevices, true))) {
298		    // Initiate the first of the resolution calls
299		    XTRACE(kStnCntlDiscoverDiscoverEvent, 0, 1);
300		    XASSERT(fNumAddrConflicts > 0);
301		    // Reuse the reply for the request to resolve the address conflict.
302		    discoverReply->fEvent = kIrDiscoverRequestEvent;
303		    discoverReply->fConflictDevAddr = fAddrConflicts[--fNumAddrConflicts];
304		    fState = kIrLMPResolveAddress;
305		    GetLAP->EnqueueEvent(discoverReply);
306		}
307		else {
308		    // Either some error or no addr conflicts so discovery is complete
309		    XTRACE(kStnCntlDiscoverDiscoverEvent, 0, 2);
310		    fState = kIrLMPReady;
311		    GetDiscovery->EnqueueEvent(discoverReply);
312		    // JDG: queue up any pending requests for us (via normal queue, sigh)
313		    if (fPendingRequests && !fPendingRequests->Empty()) {
314			CListIterator *iter = CListIterator::cListIterator(fPendingRequests);
315			for (TIrEvent* request = (TIrEvent*)iter->FirstItem();
316			     iter->More(); request = (TIrEvent*)iter->NextItem()) {
317				XTRACE(kStnCntlDiscoverDiscoverEvent, request->fEvent, 3);
318				// Send the reply to ourselves (sigh)
319				this->EnqueueEvent(request);
320			}
321			// now that we've processed the list, empty it (backwards first for speed)
322			while (!fPendingRequests->Empty())
323			    fPendingRequests->RemoveLast();     // remove last til no more left
324			iter->release();
325		    }
326		}
327	    }
328	    break;
329
330	//case kIrDisconnectRequestEvent:
331	//  // This is only expected from IrDA (IrGlue) as all discovering/address resolution
332	//  // should be done before any connections are established.
333	//  XASSERT(((TIrDisconnectRequest*)GetCurrentEvent())->fLSAPConn == nil);
334	//  // Pass request on to IrLAP
335	//  GetLAP->EnqueueEvent(GetCurrentEvent());
336	//  break;
337
338	//case kIrDisconnectReplyEvent:
339	//  // Pass reply back to IrDA
340	//  GetDiscovery->EnqueueEvent(GetCurrentEvent());
341	//  break;
342
343	case kIrConnectReplyEvent:          // JDG ADDED THESE for listen/disconnect/discover
344	case kIrListenReplyEvent:
345	case kIrGetDataRequestEvent:
346	case kIrCancelGetRequestEvent:
347	    // Forward these to IrLAPConn
348	    GetLAPConn->EnqueueEvent(GetCurrentEvent());
349	    break;
350
351	case kIrConnectRequestEvent:            // jdg hacking
352	case kIrListenRequestEvent:
353	case kIrDisconnectRequestEvent:
354	case kIrPutDataRequestEvent:
355	case kIrCancelPutRequestEvent:
356	    // save until we get back to ready state
357	    fPendingRequests->InsertLast(GetCurrentEvent());
358	    break;
359
360	default:
361	    XTRACE(kUnexpectedEvent, fState, event);
362	    DebugLog("TIrLMP::HandleDiscoverStateEvent: bad event");
363	    break;
364    }
365
366} // TIrLMP::HandleDiscoverStateEvent
367
368
369//--------------------------------------------------------------------------------
370//      HandleResolveAddressStateEvent
371//--------------------------------------------------------------------------------
372void TIrLMP::HandleResolveAddressStateEvent(ULong event)
373{
374    switch (event) {
375	case kIrDiscoverReplyEvent:
376	    {
377		XTRACE(kStnCntlResolveDiscoverEvent, 0, 0);
378		TIrDiscoverReply* discoverReply = (TIrDiscoverReply*)GetCurrentEvent();
379
380		// Any addr conflicts in current set of responses?  If so this rtn removes them
381		(void)AddrConflicts(discoverReply->fDiscoveredDevices, false);
382
383		// If an error was returned or we're finished resolving the original conflicts...
384		if ((discoverReply->fResult != noErr) || (fNumAddrConflicts == 0)) {
385		    fState = kIrLMPReady;
386		    GetDiscovery->EnqueueEvent(discoverReply);
387		}
388
389		// Still some more conflicting addresses to resolve
390		else {
391		    XASSERT(fNumAddrConflicts > 0);
392		    // Reuse the reply for the request to resolve the address conflict.
393		    discoverReply->fConflictDevAddr = fAddrConflicts[--fNumAddrConflicts];
394		    GetLAP->EnqueueEvent(discoverReply);
395		}
396	    }
397	    break;
398
399	case kIrDisconnectRequestEvent:
400	    // This is only expected from IrDA (IrGlue) as all discovering/address resolution
401	    // should be done before any connections are established.
402	    XASSERT(((TIrDisconnectRequest*)GetCurrentEvent())->fLSAPConn == nil);
403	    // Pass request on to IrLAP
404	    GetLAP->EnqueueEvent(GetCurrentEvent());
405	    break;
406
407	case kIrDisconnectReplyEvent:
408	    // Pass reply back to IrDA
409	    GetDiscovery->EnqueueEvent(GetCurrentEvent());
410	    break;
411
412	default:
413	    XTRACE(kUnexpectedEvent, fState, event);
414	    DebugLog("TIrLMP::HandleResolveAddressStateEvent: bad event");
415	    break;
416    }
417
418} // TIrLMP::HandleResolveAddressStateEvent
419
420
421//================================ Helper methods ================================
422
423
424//--------------------------------------------------------------------------------
425//      AddrConflicts
426//--------------------------------------------------------------------------------
427Boolean TIrLMP::AddrConflicts(CList* discoveredDevices, Boolean setAddrConflicts)
428{
429    // This looks for any address conflicts between my address and all of the addresses
430    // represented in the discoveredDevices list.  Any discoveredDevices with
431    // conflicting addresses are removed from the list of discoveredDevices.
432    // If setAddrConflicts is true then the conflicting addresses are saved in
433    // fAddrConflicts, which is then used for address resolution.
434
435    ULong addrToCheck;
436    ULong uniqueCount = 0;
437    Boolean thisConflicts;
438    Boolean conflicts = false;
439    ULong uniqueAddrs[kMaxReturnedAddrs+1];     // Extra 1 for my address
440
441    // Initialize unique addrs to my address to weed out conlicts with my address too.
442    uniqueAddrs[uniqueCount++] = GetLAP->GetMyDevAddr();
443
444    // Init addr conflict fields if setting addr conflict info
445    if (setAddrConflicts) {
446	fNumAddrConflicts = 0;
447    }
448
449    // No conflicts if no entries returned
450    if (discoveredDevices->GetArraySize() > 0) {
451	// Go backwards so entries don't shift down (and also faster if entry removed from end)
452	for (FastInt index1 = discoveredDevices->GetArraySize() - 1; index1 >= 0 ; index1--) {
453	    TIrDscInfo* discoveryInfo = (TIrDscInfo*)discoveredDevices->At(index1);
454	    // Now check to see if this entries addr is already in the uniqueAddrs list
455	    thisConflicts = false;
456	    addrToCheck = discoveryInfo->GetDeviceAddr();
457	    for (ULong index2 = 0; index2 < uniqueCount; index2++) {
458		if (addrToCheck == uniqueAddrs[index2]) {
459		    // There is an address conflict, maybe add to conflicts, remove it
460		    conflicts = thisConflicts = true;
461		    if (setAddrConflicts) {
462			if (fNumAddrConflicts < kMaxAddrConflicts) {
463			    fAddrConflicts[fNumAddrConflicts++] = addrToCheck;
464			}
465		    }
466		    discoveredDevices->RemoveAt(index1);
467		    discoveryInfo->release();
468		    break;
469		}
470	    }
471	    // If no conflicts then this address is also unique, add it to the unique addr list.
472	    if (!thisConflicts) {
473		if (uniqueCount < (kMaxReturnedAddrs+1)) {
474		    uniqueAddrs[uniqueCount++] = addrToCheck;
475		}
476		else {
477		    DebugLog("TIrLMP::AddrConflicts: too many unique addrs");
478		}
479	    }
480	}
481    }
482
483    return conflicts;
484
485} // TIrLMP::AddrConflicts
486
487
488//--------------------------------------------------------------------------------
489//      Demultiplexor
490//--------------------------------------------------------------------------------
491void TIrLMP::Demultiplexor(CBufferSegment* inputBuffer)
492{
493    // All of the real work is done in IrLAPConn - pass it on
494    GetLAPConn->Demultiplexor(inputBuffer);
495
496} // TIrLMP::Demultiplexor
497
498
499//--------------------------------------------------------------------------------
500//      FillInLMPDUHeader
501//--------------------------------------------------------------------------------
502ULong TIrLMP::FillInLMPDUHeader(TIrPutRequest* putRequest, UByte* buffer)
503{
504    XTRACE(kLogFillInLMPDUHeader, 0, 0);
505    // All of the real work is done in IrLAPConn - pass it on
506    return GetLAPConn->FillInLMPDUHeader(putRequest, buffer);
507
508} // TIrLMP::FillInLMPDUHeader
509
510
511//--------------------------------------------------------------------------------
512//      StartOneSecTicker
513//--------------------------------------------------------------------------------
514void TIrLMP::StartOneSecTicker()
515{
516    XTRACE(kLogStartOneSecTicker, 0, fTimerClients);
517
518    // NOTE: This is exclusively used for the LSAPConn connect watchdog timer(s)
519    // I'm trying to keep it small and simple.  If there are other timers that
520    // can take advantage of this 1 second tick counter then they may be able to
521    // be piggy-backed on top of this timer.
522
523    if (fTimerClients++ == 0) {
524	fIrDA->StartTimer(kTimer_LMP, 1 * kSeconds, kIrConnWatchdogExpiredEvent);
525    }
526
527} // TIrLMP::StartOneSecTicker
528
529
530//--------------------------------------------------------------------------------
531//      StopOneSecTicker
532//--------------------------------------------------------------------------------
533void TIrLMP::StopOneSecTicker()
534{
535    // NOTE: This is exclusively used for the LSAPConn connect watchdog timer(s)
536
537    if (fTimerClients > 0) {
538	if (--fTimerClients == 0) {
539	    fIrDA->StopTimer(kTimer_LMP);
540	}
541    }
542
543} // TIrLMP::StopOneSecTicker
544
545
546//--------------------------------------------------------------------------------
547//      TimerComplete
548//--------------------------------------------------------------------------------
549void TIrLMP::TimerComplete(ULong refCon)
550{
551    // All of the real work is done in IrLAPConn - pass it on
552    GetLAPConn->TimerComplete(refCon);
553
554    // If one sec ticker is still active, retrigger the ticker
555    if (refCon == kIrConnWatchdogExpiredEvent && fTimerClients > 0) {
556	fIrDA->StartTimer(kTimer_LMP, 1 * kSeconds, kIrConnWatchdogExpiredEvent);
557    }
558
559} // TIrLMP::TimerComplete
560
561