1/* 2 File: IrComm.cpp 3 4 Contains: IrCOMM protocol layer 5 6*/ 7 8#include "IrComm.h" 9#include "IrDAComm.h" 10#include "IrGlue.h" 11#include "CBufferSegment.h" 12#include "ttppdu.h" 13#include "IrDALog.h" 14 15#pragma mark -- Globals 16 17#if (hasTracing > 0 && hasIrCommTracing > 0) 18 19enum TraceCodes 20{ 21 kLogNew = 1, 22 kLogFree, 23 kLogInit, 24 kLogTxAvail, 25 kLogWrite, 26 kLogReturnCredit, 27 kLogBackEnable, 28 29 kLogTryConnect, 30 kLogListen, 31 kLogDisconnect, 32 kLogDiscoverComplete, 33 kLogLkupDone, 34 kLogConnectConfirm, 35 kLogDisconnected, 36 37 kLogConnectIndication, 38 kLogAcceptDone, 39 40 kLogTTPDataRead, 41 kLogTTPDataRead2, 42 43 kLogDataWrite, 44 kLogDataRead 45}; 46 47static 48EventTraceCauseDesc gTraceEvents[] = { 49 {kLogNew, "IrComm: new, obj="}, 50 {kLogFree, "IrComm: free, obj="}, 51 {kLogInit, "IrComm: init, obj="}, 52 {kLogTxAvail, "IrComm: tx available, returning="}, 53 {kLogWrite, "IrComm: write, count="}, 54 {kLogReturnCredit, "IrComm: return credit. bytes consumed, count="}, 55 {kLogBackEnable, "IrComm: back eable"}, 56 57 {kLogTryConnect, "IrComm: try to connect"}, 58 {kLogListen, "IrComm: listen"}, 59 {kLogDisconnect, "IrComm: disconnect request"}, 60 {kLogDiscoverComplete, "IrComm: discover complete, numfound=, result="}, 61 {kLogLkupDone, "IrComm: lookup done, result=, peerid="}, 62 {kLogConnectConfirm, "IrComm: connect confirm, result="}, 63 {kLogDisconnected, "IrComm: disconnected. reason="}, 64 65 {kLogConnectIndication, "IrComm: listen complete"}, 66 {kLogAcceptDone, "IrComm: accept done"}, 67 68 {kLogTTPDataRead, "IrComm: read complete, result=, len="}, 69 {kLogTTPDataRead2, "IrComm: read ctlLen=, dataLen="}, 70 71 {kLogDataWrite, "IrComm: write data"}, 72 {kLogDataRead, "IrComm: read data"} 73}; 74 75#define XTRACE(x, y, z) IrDALogAdd( x, y, (uintptr_t)z & 0xffff, gTraceEvents, true ) 76#else 77#define XTRACE(x, y, z) ((void)0) 78#endif 79 80#pragma mark -- Prototypes 81 82void InitSizeQueue(void); // init the silly size queue 83void AddPacketSize(short length); // add a packet's size to the fifo 84short FirstPacketSize(void); // return size (remaining) from the first packet in the q 85void ShrinkFirstPacketSize(short length); // mark a few bytes as consumed from the first packet in q 86void NukeFirstPacketSize(void); // first packet in the q has been consumed 87 88extern IrDAErr Dequeue(QElemPtr qElement, QHdrPtr qHeader); // MOVE these too ************** 89extern IrDAErr Enqueue(QElemPtr qElement, QHdrPtr qHeader); 90 91#if (hasTracing > 0 && hasIrCommTracing > 1) 92void IrDALogData(int msg, UInt8 *buf, int count); // log data into irdalog 93#define LOGDATA(msg, buf, count) IrDALogData(msg, buf, count) 94#else 95#define LOGDATA(msg, buf, count) (void)0) 96#endif 97 98 99#define super TTinyTP 100 OSDefineMetaClassAndStructors(IrComm, TTinyTP); 101 102/*static*/ 103IrComm * 104IrComm::irComm(TIrGlue *irda, IrDAComm *irdacomm) 105{ 106 IrComm *obj = new IrComm; 107 108 XTRACE(kLogNew, 0, obj); 109 110 if (obj && !obj->Init(irda, irdacomm)) { 111 obj->release(); 112 obj = nil; 113 } 114 115 return obj; 116} 117 118void 119IrComm::free(void) 120{ 121 XTRACE(kLogFree, 0, this); 122 123 ///xxx; 124 125 super::free(); 126} 127 128Boolean 129IrComm::Init(TIrGlue *irda, IrDAComm *irdacomm) 130{ 131 XTRACE(kLogInit, 0, this); 132 UInt8 *classname = (UInt8 *)"IrDA:IrCOMM"; // same lsap for client and server! 133 134 fIrDAComm = irdacomm; 135 fConnected = false; 136 fMaxPacketSize = 2048; // set for real when connected 137 138 InitSizeQueue(); 139 140 if (!super::TTPInitialize( 141 irda, 142 (UInt32)kTinyTPCredit, // credits (in packets) 143 (UInt32)kAssignDynamicLSAPId, // any old lsap id for now (will register it later?) 144 classname, // unused classname (real one done later) 145 (ULong)kDevInfoHintIrCOMM)) return false; // init tinytp, bail if fails 146 147 //// 148 return true; 149} 150 151 152// Return the number of bytes we can transmit without starting 153// to worry about flow control. 154 155UInt32 156IrComm::TxBufferAvailable(void) 157{ 158 if (fConnected) { 159 UInt32 count; 160 count = TTPXmitQueueSize(fMaxPacketSize) * fMaxPacketSize; 161 XTRACE(kLogTxAvail, count >> 16, count); 162 return count; 163 } 164 XTRACE(kLogTxAvail, 0xffff, 0xffff); 165 return 30*1024; // anything big, we just going to flush it (race condition?) 166} 167 168// 169// break the write up into packet size chucks 170// and send them off to tinytp for writing 171// 172UInt32 173IrComm::Write(UInt8 *buf, UInt32 length) 174{ 175 UInt32 written = 0; 176 177 XTRACE(kLogWrite, length >> 16, length); 178 179 require(buf, Done); 180 require(length > 0, Done); 181 182 while (length > 0) { 183 CBufferSegment *pkt; 184 int rc, len; 185 186 require(TTPXmitQueueSize(fMaxPacketSize) > 0, Done); // should have at least room for one more write 187 188 len = Min(length, fMaxPacketSize); 189 require(len > 0, Done); 190 require(len < 2047, Done); // sanity 191 192 pkt = BufAlloc(len + 1); 193 require(pkt, Done); 194 195 rc = pkt->Put(0); // no ircomm control info in this packet 196 check(rc == 0); // should return the byte we're putting 197 198 rc = pkt->Putn(buf, len); 199 require(rc == len, Done); // should return count of bytes moved (will truncate to fit in buffer) 200 201 BufHideRest(pkt); // hide rest of the buffer (i.e. set the write length) 202 203 DoDataRequest(pkt); // send to tinytp for xmit 204 205 BufFree(pkt); // tinytp copies the data from us, free our copy (keep the cbuf?) 206 207 buf += len; 208 written += len; 209 length -= len; 210 } 211 //IOLog("IrComm: wrote %ld bytes\n", written); 212 213Done: 214 return written; 215} 216 217// 218// our tty client has consumed some bytes, convert the count into 219// a packet count and extend that many credits back to our ttp peer. 220// 221void 222IrComm::ReturnCredit(UInt32 bytecount) 223{ 224 XTRACE(kLogReturnCredit, bytecount >> 16, bytecount); 225 short x; 226 227 while (bytecount > 0) { // loop until we've "processed" what's been returned 228 x = FirstPacketSize(); // get size of first packet in the fifo (what's left anyway) 229 require(x > 0, Fail); // sanity 230 231 if (bytecount >= (UInt32)x) { // if we've consumed all of the first packet 232 NukeFirstPacketSize(); // take it out of the fifo 233 bytecount -= x; // adjust amount consumed 234 TTPRxDone(); // the flow control work: extend tinytp credit by one -- we've consumed a pkt! 235 } 236 else { // first packet size is more than we've consumed 237 ShrinkFirstPacketSize(bytecount); 238 bytecount = 0; // and we're done 239 } 240 } 241Fail: 242 return; 243} 244 245void 246IrComm::TryConnect(int slots) 247{ 248 XTRACE(kLogTryConnect, 0, fConnected); 249 250 if (fConnected == false) 251 DoDiscoverRequest(slots); // look for peer (1,6,8,16 legal slot counts) 252} 253 254void 255IrComm::Listen(void) 256{ 257 CBufferSegment *buf; 258 259 XTRACE(kLogListen, 0, fConnected); 260 require(fConnected == false, Fail); 261 262 buf = BufAlloc(1000); // for initial connect message data 263 require(buf, Fail); 264 265 DoListenRequest(buf); 266 267Fail: 268 return; 269} 270 271void 272IrComm::Disconnect(void) 273{ 274 //int review_disconnect_logic; 275 276 XTRACE(kLogDisconnect, 0, 0); 277 278 // review ... 279 DoDisconnectRequest(nil); 280} 281 282#pragma mark ==== TinyTP Callbacks ==== 283 284void IrComm::TTPDiscoverComplete(int numFound, IrDAErr result) 285{ 286 int peer_index; // init -1 to keep from connnecting to non-ircomm hint devices, else init to 0 287 Boolean found_ircomm_peer = false; 288 289 XTRACE(kLogDiscoverComplete, numFound, result); 290 291#if (hasTracing > 0 && hasIrCommTracing > 1) 292 peer_index = 0; // if debug, allow connect attempt to first device if no ircomm 293 DebugLog("numFound=%d, result=%ld", numFound, result); 294#else 295 peer_index = -1; // if normal build, insist on an ircomm device before connecting 296#endif 297 if (numFound > 0) { 298 int i; 299#if (hasTracing > 0 && hasIrCommTracing > 1) 300 for (i = 0 ; i < numFound; i++) { // log all the discovered peers -- debugging only 301 DebugLog("discovered at %ld nicname '%s' hints 0x%lx", 302 fDiscoverInfo[i].addr, 303 fDiscoverInfo[i].name, 304 fDiscoverInfo[i].serviceHints); 305 } 306#endif 307 for (i = 0 ; i < numFound; i++) { // find a peer with the ircomm hint bit 308 if (fDiscoverInfo[i].serviceHints & kDevInfoHintIrCOMM) { // ircomm hint! 309 peer_index = i; 310 found_ircomm_peer = true; 311 break; 312 } 313 } 314 315 /* now try and do an lsap lookup if our peer had the ircomm hint bit set */ 316 /* or if debug build, then maybe try to connect to an hp netbeamir */ 317 if (peer_index >= 0) { // if we found an ircomm peer, do ias query 318 IrDAErr err; 319 UInt8 *classname = (UInt8 *)"IrDA:IrCOMM"; 320 UInt8 *attrname = (UInt8 *)"IrDA:TinyTP:LsapSel"; 321#if (hasTracing > 0 && hasIrCommTracing > 0) 322 if (found_ircomm_peer == false) classname = (UInt8 *)"HP-BDP"; // jdg's debugging 323#endif 324 fPeerAddress = fDiscoverInfo[peer_index].addr; 325 err = LSAPLookup(classname, attrname, fDiscoverInfo[peer_index].addr); 326 ncheck(err); 327 if (err == noErr) // success 328 return; 329 } 330 } 331 332 // Fall through with any error 333 if (fIrDAComm) 334 fIrDAComm->ConnectionStatus(false); // we're disconnected 335 336} 337 338void IrComm::TTPLookupComplete(IrDAErr result,UInt32 peerLSAPId) 339{ 340 XTRACE(kLogLkupDone, result, peerLSAPId); 341 342#if (hasTracing > 0 && hasIrCommTracing > 0) 343 DebugLog("lookup complete, result=%ld, lsap=%ld", (long int)result, (long int)peerLSAPId); 344#endif 345 if (result == noErr && peerLSAPId > 0) { 346 DoConnectRequest ( // connect to a remote peer 347 fPeerAddress, // peer address 348 peerLSAPId, // remote LSAP id 349 nil, 0, nil); // requested QoS (unimpld), no reassembly, no user level data 350 } 351 else { 352 fConnected = false; 353 if (fIrDAComm) 354 fIrDAComm->ConnectionStatus(false); // we're disconnected 355 } 356} 357 358void 359IrComm::TTPConnectIndication ( // Listen complete 360 IrDAErr result, 361 TTPSAP SAP, // calling TTP SAP 362 TIrQOS *ourQOS, // our QoS (post negotiation) 363 TIrQOS *peerQOS, // peer QoS (post negotiation) 364 int MaxSduSize, // calling MaxSduSize 365 TTPBuf *UserData) // calling UserData 366{ 367 XTRACE(kLogConnectIndication, 0, result); 368 369 require(UserData, Fail); // sanity, we allocated this in listen, should always be there 370 BufFree(UserData); // returned with data from connect msg. should look at it here. 371 372 if (result == noErr) { 373 if (peerQOS) fMaxPacketSize = peerQOS->GetDataSize() - 5; 374 else fMaxPacketSize = 2048; // never happens I hope 375 376 DoConnectResponse ( // reply to a connect indication (i.e. accept call) 377 SAP, // calling TTP SAP 378 0, // ircomm doesn't do tinytp fragmentation (silly) 379 nil); // called UserData 380 } 381 else if (fIrDAComm) 382 fIrDAComm->ConnectionStatus(false); 383Fail: 384 return; 385} 386 387 388void 389IrComm::TTPConnectConfirm ( // Connect complete 390 TTPSAP SAP, // called TTP SAP 391 TIrQOS *ourQOS, // our QoS (post negotiation) 392 TIrQOS *peerQOS, // peer QoS (post negotiation) 393 int MaxSduSize, // called MaxSduSize 394 TTPBuf *UserData) // called UserData 395{ 396 XTRACE(kLogConnectConfirm, 0, 0); 397 398 check(ourQOS); 399 check(peerQOS); 400 if (peerQOS) { 401 fMaxPacketSize = peerQOS->GetDataSize() - 5; 402 //IOLog("ircomm - max packet size read as %d\n", fMaxPacketSize); 403 } 404 else { 405 fMaxPacketSize = 2048; // never happens I hope 406 //IOLog("ircomm - qos nil, max packet set to %d\n", fMaxPacketSize); 407 } 408 409 if (fIrDAComm) { 410 fConnected = true; 411 fIrDAComm->ConnectionStatus(true); 412 } 413} 414 415 416void 417IrComm::TTPDisconnectIndication ( // Disconnect complete 418 int reason, 419 TTPBuf *UserData) 420{ 421 XTRACE(kLogDisconnected, 0, reason); 422 fConnected = false; 423 if (fIrDAComm) 424 fIrDAComm->ConnectionStatus(false); // we're disconnected 425} 426 427 428void 429IrComm::TTPDataIndication(TTPBuf *userdata, TTP_Read_Status status) 430{ 431 UInt32 ctlLen, dataLen, count; 432 UInt8 *ctlBuffer, *dataBuffer, *buf; 433 434 require(userdata, Fail); 435 nrequire(status, Fail); 436 437 XTRACE(kLogTTPDataRead, status, BufSize(userdata)); 438 439 count = BufSize(userdata); 440 buf = BufBase(userdata); 441 442 //IOLog("IrComm got %ld bytes\n", count); 443 444// LOGDATA(kLogDataRead, buf, count); // send all data to irdalog if tracing > 1 445 446 ctlLen = *buf; // first byte is length of control data 447 ctlBuffer = buf + 1; // control data (if any) after ctl length 448 449 dataLen = count - ctlLen - 1; // data is packet - ctlLen byte - ctl buffer 450 dataBuffer = ctlBuffer + ctlLen; // data follows control buffer 451 452 XTRACE(kLogTTPDataRead2, ctlLen, dataLen); 453 // 454 // parse control buffer here (eventually) 455 // 456 if (dataLen) { // if any data to pass up 457 AddPacketSize(dataLen); // keep track of packet lens for flow control 458 if (fIrDAComm) 459 fIrDAComm->IrCommDataRead(dataBuffer, dataLen); // send data to IrDAComm and pseudo tty 460 } 461 else TTPRxDone(); 462 463 BufFree(userdata); // release the cbuffer 464 465Fail: 466 return; 467} 468 469void 470IrComm::TTPUDataIndication(TTPBuf *UserData) 471{ 472 // crash, this isn't impld 473} 474 475 476void 477IrComm::TTPAcceptDoneIndication(IrDAErr result) // connection fully opened 478{ 479 XTRACE(kLogAcceptDone, 0, result); 480 481 fConnected = (result == noErr); 482 483 if (fIrDAComm) 484 fIrDAComm->ConnectionStatus(result == noErr); 485} 486 487 488void 489IrComm::TTPBackEnable(void) // more tinytp buffers are available 490{ 491 XTRACE(kLogBackEnable, 0, 0); 492 if (fIrDAComm) 493 fIrDAComm->BackEnable(); 494} 495 496#pragma mark ==== Really silly packet size FIFO ==== 497 498// 499// This is really silly, but we need to keep track 500// of packet sizes in a race-condition safe manner. 501// at least if we want to try and do flow control 502// in a moderately sane manner (todo: explore insanity) 503// 504#define kSizeQueueLength (kTinyTPCredit+2) // if more, then should never ever run out 505QElem gSizeQueueArray[kSizeQueueLength]; // our queue elements fit in std QElem 506QHdr gAvailSizeQueue; // the free list 507QHdr gSizeQueue; // the "packet size" fifo 508 509void 510InitSizeQueue(void) 511{ 512 int i; 513 514 gAvailSizeQueue.qFlags = gSizeQueue.qFlags = 0; 515 gAvailSizeQueue.qHead = gSizeQueue.qHead = nil; 516 gAvailSizeQueue.qTail = gSizeQueue.qTail = nil; 517 for (i = 0 ; i < kSizeQueueLength; i++) { 518 Enqueue(&gSizeQueueArray[i], &gAvailSizeQueue); 519 } 520} 521 522// 523// caller has a new packet, the size of which we 524// want to save in a fifo for flow control. 525// Can you say "excessively complex"? 526// 527void 528AddPacketSize(short length) 529{ 530 QElemPtr q; 531 IrDAErr err; 532 533 q = gAvailSizeQueue.qHead; 534 require(q, Fail); // really shouldn't happen 535 536 err = Dequeue(q, &gAvailSizeQueue); 537 nrequire(err, Fail); 538 539 q->qLink = nil; 540 q->qType = 123; 541 q->qData[0] = length; 542 Enqueue(q, &gSizeQueue); 543 544Fail: 545 return; 546} 547 548// 549// caller wants to know the size of the first packet 550// 551short 552FirstPacketSize(void) 553{ 554 QElemPtr q; 555 q = gSizeQueue.qHead; 556 if (q == nil) return 0; 557 return q->qData[0]; 558} 559 560// 561// caller has consumed some, but not all, of the 562// data in the first packet. adjust the length 563// 564void 565ShrinkFirstPacketSize(short length) 566{ 567 QElemPtr q; 568 q = gSizeQueue.qHead; 569 require(q, Fail); 570 q->qData[0] -= length; 571 572Fail: 573 return; 574} 575 576// caller has consumed first packet (at least) 577// so pull it off the size queue and add it 578// back to the free list 579void 580NukeFirstPacketSize(void) 581{ 582 QElemPtr q; 583 IrDAErr err; 584 585 q = gSizeQueue.qHead; 586 require(q, Fail); 587 588 err = Dequeue(q, &gSizeQueue); 589 nrequire(err, Fail); 590 591 Enqueue(q, &gAvailSizeQueue); 592 593Fail: 594 return; 595} 596 597 598#if (hasTracing > 0 && hasIrCommTracing > 1) 599 600void IrDALogData(int msg, UInt8 *buf, int count) // log data into irdalog 601{ 602 UInt32 x = 0; 603 int i = 0; // count of bytes in x 604 605 while (count-- > 0) { 606 x = x << 8; 607 x |= *buf++; 608 if (++i == 4) { 609 XTRACE(msg, x >> 16, x); 610 i = 0; 611 x = 0; 612 } 613 } 614 if (i) XTRACE(msg, x >> 16, x); 615} 616 617#endif 618 619