1/* 2 File: ttp.cpp 3 4 Contains: Client interfaces to Tiny TP 5 6 7*/ 8 9#include "ttp.h" 10#include "ttppdu.h" 11#include "CBufferSegment.h" 12#include "IrDALog.h" 13 14#if (hasTracing > 0 && hasTTPTracing > 0) 15 16enum IrTinyTPTraceCodes 17{ 18 kLogFree = 1, 19 kLogInit, 20 kDiscoverRequest, 21 kLookupRequest, 22 kListenRequest, 23 kConnectRequest, 24 kConnectResponse, 25 kDisconnectRequest, 26 kDataRequest, 27 kFlowControl, 28 kDataRequestDropped, 29 kXmitQueueSize, 30 kXmitQueueSize2, 31 kXmitQueueSize3, 32 kXmitIrDABufsLow, 33 kTTPRxDone, 34 kDiscardPendingPuts 35}; 36 37EventTraceCauseDesc gTraceEvents[] = { 38 {kLogFree, "TinyTP: free obj="}, 39 {kLogInit, "TinyTP: initializing, obj=, credit=, lsap="}, 40 {kDiscoverRequest, "TinyTP: discover request"}, 41 {kLookupRequest, "TinyTP: lookup request"}, 42 {kListenRequest, "TinyTP: listen request"}, 43 {kConnectRequest, "TinyTP: connect request"}, 44 {kConnectResponse, "TinyTP: connect response"}, 45 {kDisconnectRequest, "TinyTP: disconnect request"}, 46 {kDataRequest, "TinyTP: put data request"}, 47 {kFlowControl, "TinyTP: flow control request"}, 48 {kDataRequestDropped, "TinyTP: ** Data request dropped ***"}, 49 {kXmitQueueSize, "TinyTP: XMIT Queue Size, sendcredit=, depth="}, 50 {kXmitQueueSize2, "TinyTP: XMIT Queue Size2, explode_factor, MaxSegSize="}, 51 {kXmitQueueSize3, "TinyTP: XMIT Queue Size3, txQDepth=, depth="}, 52 {kXmitIrDABufsLow, "TinyTP: XMIT - global buffers low, SendCredit=, CBufCnt="}, 53 {kTTPRxDone, "TinyTP: Client says Rx is done"}, 54 {kDiscardPendingPuts, "TinyTP: discard pending puts"} 55}; 56 57#define XTRACE(x, y, z) IrDALogAdd( x, y, (uintptr_t)z & 0xffff, gTraceEvents, true ) 58 59#else 60 #define XTRACE(x, y, z) ((void)0) 61#endif 62 63const UInt8 kIASTinyTPLSAPSelAttrStr[] = "IrDA:TinyTP:LsapSel"; 64 65#define super CIrLSAP 66 OSDefineMetaClassAndAbstractStructors(TTinyTP, CIrLSAP); 67 68void 69TTinyTP::free() 70{ 71 XTRACE(kLogFree, 0, this); 72 73 super::free(); 74} 75 76Boolean 77TTinyTP::TTPInitialize( // do real initialization 78 TIrGlue *irda, // glue 79 UInt32 credit, // number of concurrent read buffers (hmm?) 80 UInt32 desiredLSAPId, // port to get (kAssignDynamicLSAPId if not listening) 81 UInt8 * className, // name to register with IAS 82 ULong hints) 83{ 84 int i; 85 86 XTRACE(kLogInit, 0, this); 87 XTRACE(kLogInit, credit, desiredLSAPId); 88 89 90 TxQueue.qFlags = 0; 91 TxQueue.qHead = TxQueue.qTail = nil; 92 RxQueue.qFlags = 0; 93 RxQueue.qHead = RxQueue.qTail = nil; 94 AvailQueue.qFlags = 0; 95 AvailQueue.qHead = AvailQueue.qTail = nil; 96 97 98 AvailCredit = 0; // lets zero out some fields for sanity's sake 99 RemoteCredit = 0; 100 SendCredit = 0; 101 MaxSegSize = 0; 102 TxMaxSduSize = 0; 103 RxMaxSduSize = 0; 104 RxSdu.sarbuf = nil; 105 RxSdu.busy = false; 106 107 txQDepth = 0; // nothing on tx queue yet 108 Connected = false; 109 discoverPending = false; // no discover request pending yet. 110 111 initial_credit = credit; 112 113 // have number of buffers allocated compiled into IrDA "for now" 114 // allocate "credit" buffers and hand to Ir? 115 //for (i = 0 ; i < (10*credit) ; i++) { 116 for (i = 0 ; i < kMaxTTPRequests ; i++) { 117 TTPq *tq; 118 //tq = (TTPq *)NewPtr(sizeof(TTPq)); 119 //check(tq); // these should work ... 120 tq = &ttpqpool[i]; // grab out of our own pool instead of using NewPtr 121 tq->qLink = nil; 122 tq->qType = 1; 123 Enqueue((QElem *)tq, &(this->AvailQueue)); // Prep my free list 124 } 125 126 return super::Init(irda, desiredLSAPId, className, (UInt8 *) kIASTinyTPLSAPSelAttrStr, hints); // Init LSAP 127} 128 129// 130// thin layer over LMP (turn into inlines after debugged) 131// 132void 133TTinyTP::DoDiscoverRequest (int slots) 134{ 135 XTRACE(kDiscoverRequest, 0, this); 136 if (!discoverPending) { 137 discoverPending = true; 138 this->Discover(slots); // just call LSAP 139 } 140} 141 142void 143TTinyTP::DoLookupRequest ( 144 unsigned char *classname, 145 UInt32 remoteAddr ) // name and device to query 146{ 147 XTRACE(kLookupRequest, 0, this); 148 149 //fPeerAddr = remoteAddr; // FOO. CIrLSAP sets peer addr during lookup 150 151 this->LSAPLookup(classname, ( UInt8 * )kIASTinyTPLSAPSelAttrStr, remoteAddr ); 152} 153 154void 155TTinyTP::DoListenRequest( 156 TTPBuf *userData) // read buffer 157{ 158 XTRACE(kListenRequest, 0, this); 159 Listen(userData); // just call CIrLSAP to start the listen 160} 161 162// ************ 163// ************ Connect Request 164// ************ client can release userData upon return 165// ************ 166 167/* 168void 169TTinyTP::DoConnectRequest ( 170 TTPSAP sap, // peer's lsap 171 TIrQOS *qos, // requested QoS 172 int maxSduSize, 173 TTPBuf *userData) // size of buffer is how much to send 174{ 175 this->DoConnectRequest( 176 fPeerAddr, // supply the missing remote address (ahem) 177 sap, qos, maxSduSize, userData); 178} 179*/ 180void 181TTinyTP::DoConnectRequest ( 182 UInt32 remoteAddr, // peer's address 183 TTPSAP sap, // peer's lsap 184 TIrQOS *qos, // requested QoS 185 int maxSduSize, 186 TTPBuf *userData) // size of buffer is how much to send 187{ 188#pragma unused (qos) 189 int n; 190 TTPBuf *pduBuf; // the buffer for it 191 192 XTRACE(kConnectRequest, 0, this); 193 fPeerAddr = remoteAddr; // save peer's address 194 fPeerSAP = sap; // save peer's sap address (not used yet) 195 196 this->Connected = false; // Not connected to anything yet 197 this->AvailCredit = 0; // (needed?) 198 this->RxMaxSduSize = maxSduSize; // Set max inbound SDU size 199 //this->RxSdu.size = 0; 200 //if (maxSduSize > 0) 201 // this->RxSdu.sarbuf = BufAlloc(maxSduSize); // alloc a reassembly buffer 202 //else 203 this->RxSdu.sarbuf = nil; // else make sure it's nil 204 this->RxSdu.busy = false; // ?? 205 206 n = this->initial_credit; // default buffering 207 this->SendCredit = 0; 208 if (n > 127) { 209 AvailCredit = n - 127; 210 n = 127; 211 } 212 RemoteCredit = n; 213 pduBuf = ttp_pdu_connect ( // return filled in connect TTP PDU 214 (maxSduSize == 0) ? 0 : 1, // P flag (1 if sends maxSduSize) 215 n, maxSduSize, userData); 216 require(pduBuf, NoMem); 217 218 this->Connect(fPeerAddr, sap, pduBuf); // ask LSAP to do a connect 219 this->CheckTheQueues(); // see if we have more work to do 220 221NoMem: 222 return; 223} 224 225// ************ 226// ************ Connect Response (Accept call) 227// ************ client can release userData upon return 228// ************ 229 230void 231TTinyTP::DoConnectResponse ( 232 TTPSAP sap, // calling TTP SAP 233 int maxSduSize, // called MaxSduSize 234 TTPBuf *userData) // called UserData 235{ 236#pragma unused (sap) 237 int n; 238 TTPBuf *pduBuf; // the buf for it 239 240 XTRACE(kConnectResponse, 0, this); 241 XTRACE(kConnectResponse, sap, maxSduSize); 242 XTRACE(kConnectResponse, maxSduSize >> 16, maxSduSize); 243 244 this->AvailCredit = 0; 245 this->RxMaxSduSize = maxSduSize; 246 //this->RxSdu.size = 0; 247 //if (maxSduSize > 0) 248 // this->RxSdu.sarbuf = BufAlloc(maxSduSize); // alloc a reassembly buffer 249 //else 250 this->RxSdu.sarbuf = nil; // else make sure it's nil 251 this->RxSdu.busy = false; 252 253 n = this->initial_credit; // default buffering 254 if (n > 127) { 255 this->AvailCredit = n - 127; 256 n = 127; 257 } 258 this->RemoteCredit = n; // we're extending these many buffers to peer 259 pduBuf = ttp_pdu_connect( 260 (maxSduSize == 0) ? 0 : 1, n, maxSduSize, userData); 261 require(pduBuf, NoMem); 262 263 this->Accept(pduBuf); // tell LSAP to accept (sanity check request parms first?) 264 this->Connected = true; 265 this->CheckTheQueues(); // see if we have more work to do 266NoMem: 267 return; 268} 269 270// ************ 271// ************ Disconnect Request 272// ************ client can release userData upon return 273// ************ 274 275void 276TTinyTP::DoDisconnectRequest ( 277 TTPBuf *userData) // userdata doesn't make it 278{ 279 XTRACE(kDisconnectRequest, 0, this); 280 281 if (Connected == false) { // if already disconnected 282 //TTPDisconnectIndication(0, nil); // just tell 'em so (again?) 283 Disconnect(); // tell cirlsap to disconnect 284 } 285 else { 286 this->AppendTail(&(this->TxQueue), TTP_Disconnect, 0, userData); 287 txQDepth++; 288 this->CheckTheQueues(); // see if we have more work to do 289 } 290} 291 292// ************ 293// ************ Data Request 294// ************ client CAN release userData upon return (until we rewrite it again) 295// ************ 296extern int queueDepth(QHdr *qh); // debugging ... return depth of a queue 297 298void 299TTinyTP::DoDataRequest ( 300 TTPBuf *userData) // data to send 301{ 302 TTPBuf *newbuf; // get rid of this soon .... 303 //static int dropCount = 0; // debugging. count number of drops in a row ... 304 305 XTRACE(kDataRequest, 0, this); 306 307 check (userData); // error if nil buffer ptr 308 if (!userData) return; 309 //check(this->Connected == true); // error if we're not connected 310 check(BufSize(userData)); // error, if zero size send (ahem, why?) 311 if (Connected == false) return; // error, but don't crash in this version .... 312 313 /************************************************************************* 314 if (SendCredit == 0) { // drop if our credit has hit bottom 315 XTRACE(kDataRequestDropped, dropCount, queueDepth(&TxQueue)); 316 dropCount++; 317 if (dropCount > 20) { // 100 was hit 318 DebugStr("\pTTP just dropped 20 sends in a row due to congestion"); 319 dropCount = 0; // may want to keep trying .... 320 } 321 this->CheckTheQueues(); // this shouldn't be needed ... 322 return; // return (drop the request) 323 } 324 else 325 dropCount = 0; // hey, we're still alive! 326 *************************************************************************/ 327 328 if (this->TxMaxSduSize == 0) { // SAR Disabled 329 if (BufSize(userData) > this->MaxSegSize) 330 return; // todo: return error: too big & SAR disabled 331 newbuf = ttp_pdu_data(false, 0, userData); 332 require(newbuf, NoMem); 333 this->AppendTail(&(this->TxQueue), TTP_Segment_Last, 0, newbuf); 334 txQDepth++; 335 } 336 else { // SAR enabled 337 int i, numsegs; 338 if (BufSize(userData) > this->TxMaxSduSize) // error ... 339 return; // todo: return error: too big, even for SAR 340 341 numsegs = ((BufSize(userData) + this->MaxSegSize -1) / this->MaxSegSize); 342 if (numsegs == 1) { // if fits w/out SAR ... (jdg added) 343 newbuf = ttp_pdu_data(false, 0, userData); // copy to data pdu 344 require(newbuf, NoMem); 345 this->AppendTail(&(this->TxQueue), TTP_Segment_Last, 0, newbuf); 346 txQDepth++; 347 } 348 else { // else ... gee let's do SAR and split into lots of packets 349 TTPBuf *seg; // Get segment just returns pointers into the orig buffer 350 for (i = 1 ; i < numsegs; i++) { // first N-1 segments have the more bit set 351 seg = GetSegment(i, userData); // get ptrs to the initial segments 352 require(seg, NoMem); 353 newbuf = ttp_pdu_data(true, 0, seg); // copy to new buffer w/the ttp header byte 354 require(newbuf, NoMem); 355 BufFree(seg); // this just frees the wrapper (I hope) 356 this->AppendTail(&(this->TxQueue), TTP_Segment, 0, newbuf); 357 txQDepth++; 358 } 359 seg = GetSegment(numsegs, userData); // get the last segment 360 require(seg, NoMem); 361 newbuf = ttp_pdu_data(false, 0, seg); // copy the data out 362 require(newbuf, NoMem); 363 BufFree(seg); // free the wrapper 364 this->AppendTail(&(this->TxQueue), TTP_Segment_Last, 0, newbuf); 365 txQDepth++; 366 } 367 } 368 this->CheckTheQueues(); // see if we have work to do 369NoMem: 370 return; 371} 372 373// we're a zombie, discard any queued up data 374void 375TTinyTP::TTPDiscardPendingPuts(void) 376{ 377 TTPq *tq; 378 IrDAErr err; 379 int counter = 0; // debugging only 380 381 XTRACE(kDiscardPendingPuts, 0x1111, 0); 382 // Could do a flushqueue, but I don't want to flush a 383 // disconnect request, just data puts. oh well. 384 //FlushQueue(&TxQueue); // should be empty anyway 385 386 while ((tq = (TTPq *)TxQueue.qHead) != 0) { 387 if (tq->qType != TTP_Segment && // if not a segment put 388 tq->qType != TTP_Segment_Last) break; // then prob a disconnect, stop 389 err = Dequeue((QElem *)tq, &TxQueue); 390 ncheck(err); // really shouldn't happen 391 if (tq->buf != 0) BufFree(tq->buf); // be careful ... 392 tq->buf = nil; 393 tq->qLink = nil; 394 Enqueue((QElem *)tq, &AvailQueue); 395 counter++; 396 } 397 XTRACE(kDiscardPendingPuts, 0xffff, counter); 398} 399 400void 401TTinyTP::DoUdataRequest ( // not really impl'd 402 TTPBuf *userData) // data to send 403{ 404#pragma unused (userData) 405 if (this->Connected == false) return; // error 406 //this->UDataPut(userData); // send off UData 407} 408 409void 410TTinyTP::SetLocalFlow ( 411 TFlowOnOff onOff) // start/stop flow 412{ 413 XTRACE(kFlowControl, 0, 0); 414 415 if (onOff == FlowOn) this->RxSdu.busy = false; 416 else this->RxSdu.busy = true; 417 418 this->CheckTheQueues(); // see if we have work to do 419} 420 421// 422// Return number of packets the client can send to us w/out worrying 423// about running out of send credits (or CBuffers). Note that this 424// is intended to be called from non-def task clients, so there's been 425// some work done to avoid race conditions. 426// 427int 428TTinyTP::TTPXmitQueueSize(int maxPacketSize) 429{ int depth; 430 int explode_factor; // maxPacketSize could turn into this many frags 431 //int REVIEW_Unlimited_Buffer_Allocation; 432 433 // Check against 8 buffers, one for a new write, and 7 for new get's in case 434 // we get a 7-window full set of data packets all at once. 435 /************************** 436 if (CountFreeCBuffers() <= 8) { // first -- is IrDA running out of buffers? 437 XTRACE(kXmitIrDABufsLow, SendCredit, CountFreeCBuffers()); 438 return 0; // if so, then pretend we can't send 439 } 440 *****************************/ 441 442 depth = SendCredit; // first start with currently available SendCredit 443 444// avoid queueDepth so that we can call this from non-dt tasks! 445// depth -= queueDepth(&TxQueue); // now subtract out pending writes 446// // now have actual packets count available to send 447 depth -= txQDepth; // subtract out depth of tx queue 448 449 XTRACE(kXmitQueueSize3, txQDepth, depth); 450 451 check(depth >= 0); // sanity check ... 452 if (depth <= 0) return 0; 453 454 if (this->TxMaxSduSize == 0) { // if SAR Disabled 455 XTRACE(kXmitQueueSize, SendCredit, depth); 456 return depth; // then we're done 457 } 458 459 // if we got this far, we have some credit available, let's see what the 460 // worst-case scenario looks like 461 462 explode_factor = (maxPacketSize + this->MaxSegSize -1) / this->MaxSegSize; 463 // else each send could fragment into lots of packets 464 depth = depth / explode_factor; // assuming max packetsize on all packets ... 465 466 if (depth == 0) { // ahem, special case of hitting the limit, let it! 467 // we know depth started out >= 1 due to previous test 468 depth = 1; // let it send one more and then go busy 469 } 470 471 XTRACE(kXmitQueueSize, SendCredit, depth); 472 XTRACE(kXmitQueueSize2, explode_factor, MaxSegSize); 473 return depth; // could be fancier ... 474} 475 476void 477TTinyTP::TTPRxDone(void) // move this ... 478{ 479 AvailCredit++; // new style credit 480 XTRACE(kTTPRxDone, RemoteCredit, AvailCredit); 481 CheckTheQueues(); // see if we have work to do (eg a dataless credit extension) 482} 483