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