1/* 2 File: ttp3.c 3 4 Contains: Most of the real work of TinyTP. Checking rd/wr queues et al. 5 6*/ 7 8#include "ttp.h" 9#include "ttppdu.h" 10#include "CBufferSegment.h" 11 12int queueDepth(QHdr *qh); // debugging ... return depth of a queue 13 14#if (hasTracing > 0 && hasTTP3Tracing > 0) 15 16enum IrTinyTP3TraceCodes 17{ 18 kAppendTail = 1, 19 kFlushQueue, 20 kGetSegment, 21 kCheckTheQs, 22 kCheckTheQs2, 23 kSendDataless, 24 kCheckTxQ, 25 kCheckRxQ, 26 kReassemble, 27 kGotSmallPacket, 28 kPendingPut, 29 kCurrentSendCredit 30}; 31 32static 33EventTraceCauseDesc gTraceEvents[] = { 34 {kAppendTail, "TinyTP3: append tail"}, 35 {kFlushQueue, "TinyTP3: flush queue"}, 36 {kGetSegment, "TinyTP3: Get segment"}, 37 {kCheckTheQs, "TinyTP3: Check Qs"}, 38 {kCheckTheQs2, "TinyTP3: Qs. Remote/Avail credit"}, 39 {kSendDataless, "TinyTP3: Send Dataless"}, 40 {kCheckTxQ, "TinyTP3: Check TxQ"}, 41 {kCheckRxQ, "TinyTP3: Check RxQ"}, 42 {kReassemble, "TinyTP3: Reassemble"}, 43 {kGotSmallPacket, "TinyTP3: Passing up small packet"}, 44 {kPendingPut, "TinyTP3: put delay pending complete"}, 45 {kCurrentSendCredit, "TinyTP3: current SendCredit"} 46}; 47 48#define XTRACE(x, y, z) IrDALogAdd( x, y, z, gTraceEvents, true ) 49 50#else 51 #define XTRACE(x, y, z) ((void)0) 52#endif 53 54 55void 56TTinyTP::AppendTail(QHdr *qhdr, TTPMsg msg, int r, TTPBuf *buf) 57{ TTPq *tq; 58 IrDAErr err; 59 60 require(qhdr, Fail); 61 /*** 62 if (0) { 63 if (qhdr == &RxQueue) XTRACE(kAppendTail, 1, msg); 64 else if (qhdr == &TxQueue) XTRACE(kAppendTail, 2, msg); 65 else { 66 DebugStr("\pLogic err. TTinyTP::AppendTail called w/unknown queue hdr"); 67 return; 68 } 69 } 70 ***/ 71 72 tq = (TTPq *)AvailQueue.qHead; // get available 73 require(tq, Fail); // what to do if run out? 74 75 err = Dequeue((QElem *)tq, &AvailQueue); 76 nrequire(err, Fail); 77 78 tq->qLink = nil; 79 tq->qType = msg; 80 tq->reason = r; 81 tq->buf = buf; 82 Enqueue((QElem *)tq, qhdr); // put it on the real queue 83 84 XTRACE(kAppendTail, 3, queueDepth(qhdr)); 85Fail: 86 return; 87} 88 89void 90TTinyTP::FlushQueue(QHdr *q) 91{ TTPq *tq; 92 93 check(q); 94 if (q == &RxQueue) {XTRACE(kFlushQueue, 1, queueDepth(q));} 95 else if (q == &TxQueue) {XTRACE(kFlushQueue, 2, queueDepth(q));} 96 else DebugLog("Logic err. TTinyTP::FlushQueue called w/unknown queue hdr"); 97 98 while ((tq = (TTPq *)q->qHead) != 0) { 99 IrDAErr err; 100 err = Dequeue((QElem *)tq, q); 101 ncheck(err); 102 if (tq->buf != 0) BufFree(tq->buf); // be careful ... 103 tq->buf = nil; 104 tq->qLink = nil; 105 Enqueue((QElem *)tq, &AvailQueue); 106 } 107} 108 109// Get the N'th segment out of the large packet that's about to 110// be sent. Index is 1 thru Number packets. All but the last 111// packet will be MaxSegSize long. 112// Rewrite 113TTPBuf * 114TTinyTP::GetSegment(int i, TTPBuf *ttpbuf) // extract segment 115{ 116 TTPBuf *seg; // the segment wrapper 117 unsigned char *newBase; // pointer to start of segment 118 int length; // length of the segment 119 120 check(ttpbuf); 121 XTRACE(kGetSegment, i, ttpbuf->GetSize()); 122 123 i = i - 1; // turn to zero-based index 124 length = BufSize(ttpbuf) - (i * MaxSegSize); // amount left 125 if (length > MaxSegSize) // if too much for one packet 126 length = MaxSegSize; // max length of a segment 127 check(length); 128 129 newBase = BufBase(ttpbuf) + (i * MaxSegSize); // starting point 130 seg = CBufferSegment::New(newBase, length); // wrap a cbuffer around the block 131 check(seg); 132 133 return seg; 134} 135 136// 137//************** Queue checkers 138// 139#define QueueEmpty(x) (x.qHead==nil) 140#define QueueNotEmpty(x) (x.qHead != nil) 141#define LowThreshold 2 // Check this ********* 142void 143TTinyTP::CheckTheQueues() 144{ 145 TTinyTP *curTTP; 146 147 curTTP = this; 148 { 149 // start of 'original' CheckTheQueues' 150 Boolean check = true; // true until no changes after queue checks 151 int rxDepth, txDepth; // debug only 152 rxDepth = queueDepth(&curTTP->RxQueue); // debug only 153 txDepth = queueDepth(&curTTP->TxQueue); // debug only 154 //if (txDepth > 10) // shouldn't happen now 155 // DebugStr("\pTx Queue wedged"); 156 157 XTRACE(kCheckTheQs, rxDepth, txDepth); 158 XTRACE(kCheckTheQs2, curTTP->RemoteCredit, curTTP->AvailCredit); 159 XTRACE(kCurrentSendCredit, curTTP->initial_credit, curTTP->SendCredit); 160 161 while (check) { // loop until no state changes 162 check = false; 163 // if nothing to send or can't send due to lack of credit AND 164 if ((QueueEmpty(curTTP->TxQueue) || curTTP->SendCredit == 0) && 165 // remote is almost dry or we have "lots" to extend AND 166 (curTTP->RemoteCredit <= LowThreshold || curTTP->AvailCredit > 3) && 167 // we have something to extend AND we're connnected 168 curTTP->AvailCredit > 0 && curTTP->Connected) { 169 curTTP->SendDataless(); // send dataless flow PDU 170 // check = true; // goes right on out, not q'd 171 } 172 // hmm, if lots is q'd, should I empty one before going 173 // to the next? 174 if (QueueNotEmpty(curTTP->TxQueue) && 175 curTTP->CheckTxQueue()) check = true; 176 177 if (QueueNotEmpty(curTTP->RxQueue) && 178 curTTP->CheckRxQueue()) check = true; 179 } 180 } 181} 182 183// Send a dataless data PDU 184// Used to advance credit in the absence of any userdata to send 185void 186TTinyTP::SendDataless() 187{ TTPBuf *data; 188 int n; 189 190 XTRACE(kSendDataless, RemoteCredit, AvailCredit); 191 192 n = AvailCredit; 193 AvailCredit = 0; 194 if (n > 127) { AvailCredit = n - 127; n = 127; } 195 RemoteCredit += n; 196 data = ttp_pdu_data(false, n, NULL); 197 require(data, NoMem); 198 this->DataPut(data); // tell LSAP to send it off 199 return; 200NoMem: 201 RemoteCredit -= n; // didn't make it, back off unsent credit 202 AvailCredit += n; 203 return; 204} 205 206Boolean 207TTinyTP::CheckTxQueue() 208{ TTPq *tq; 209 IrDAErr err; 210 211 XTRACE(kCheckTxQ, 0, 0); 212 213 tq = (TTPq *)TxQueue.qHead; // get first off the Xmit list 214 if (tq == 0) return false; // nothing there, nothing changed 215 216 // First case -- disconnect request was queued (on the 217 // transmit queue, allowing pending puts to complete first 218 if (tq->qType == TTP_Disconnect) { 219 Connected = false; // no longer connected state 220 err = Dequeue((QElem *)tq, &TxQueue); // remove current elem 221 ncheck(err); 222 FlushQueue(&TxQueue); // should be empty anyway 223 FlushQueue(&RxQueue); 224 txQDepth = 0; 225 226 // send off disconnect request, with reason=UserRequested 227 //do_lmp_disconnect_request(this, kIrUserRequestedDisconnect, tq->buf); 228 this->Disconnect(); // tell LSAP to hangup 229 230 tq->qLink = nil; 231 Enqueue((QElem *)tq, &AvailQueue); // put back on avail list 232 return true; // something changed 233 } 234 235 // Second case -- data queued and available send credit 236 if ((tq->qType == TTP_Segment || tq->qType == TTP_Segment_Last) && 237 SendCredit > 0) { 238 int n; 239 Boolean m; 240 241 n = AvailCredit; 242 if (n > 127) { 243 AvailCredit = n - 127; 244 n = 127; 245 } else AvailCredit = 0; 246 RemoteCredit += n; 247 err = Dequeue((QElem *)tq, &TxQueue); // remove current elem 248 ncheck(err); 249 txQDepth--; 250 SendCredit--; // we're consuming a transmit credit 251 252 //*** DO lmp_data_request( data ttp pdu, deltacredit = n, userdata = data) 253 m = (tq->qType == TTP_Segment); // set "More" flag 254 ttp_pdu_data_setbyte(m, n, tq->buf); // update the ttp flags 255 this->DataPut(tq->buf); // send off the data PDU 256 // we free it when we get back put complete 257 tq->buf = nil; 258 tq->qLink = nil; 259 Enqueue((QElem *)tq, &AvailQueue); // put back on avail list 260 return true; 261 } 262 return false; // didn't do anything this time through 263} 264 265Boolean 266TTinyTP::CheckRxQueue() 267{ TTPq *tq; 268 IrDAErr err; 269 270 XTRACE(kCheckRxQ, 0, 0); 271 272 tq = (TTPq *)RxQueue.qHead; // get first off the Input list 273 if (tq == 0) return false; // nothing there, nothing changed 274 if (RxSdu.busy) return false; // factored out, can't do anything if blocked 275 276 if (RxMaxSduSize == 0 && // If SAR is *not* being used 277 (tq->qType == TTP_Segment || // and M=1 or M=0 278 tq->qType == TTP_Segment_Last)) { // (spec says to ignore M bit if !SAR) 279 // *** send off tq->buf directly 280 281 err = Dequeue((QElem *)tq, &RxQueue); // remove current elem 282 ncheck(err); 283 284 TTPDataIndication(tq->buf, TTP_Data_Ok); // virtual callback 285 // CLIENT frees the packet buffer 286 tq->buf = nil; 287 tq->qLink = nil; 288 Enqueue((QElem *)tq, &AvailQueue); // put back on avail list 289 //AvailCredit++; // ready for some more (CHECK) 290 return true; 291 } 292 293 // If it's a data packet, we know SAR is in force below here 294 295 // jdg added: check to see if SAR is in effect, but not needed! 296 // if last segment && no reassembly buffer .... 297 if ((tq->qType == TTP_Segment_Last) && // if "last" buffer of a sequence 298 (RxSdu.sarbuf == nil)) { // and no previous packet 299 check (tq->buf); 300 XTRACE(kGotSmallPacket, 0, BufSize(tq->buf)); 301 302 err = Dequeue((QElem *)tq, &RxQueue); // remove current elem 303 ncheck(err); 304 305 TTPDataIndication(tq->buf, TTP_Data_Ok); // just pass it up! 306 tq->buf = nil; // CLIENT must free it 307 tq->qLink = nil; // finally, done w/rx q element 308 Enqueue((QElem *)tq, &AvailQueue); // put back on avail list 309 //AvailCredit++; // ready for some more 310 return true; 311 } 312 313 // Ok, if we have data here, then we really are doing SAR, should 314 // have a big packet ... 315 // Data (terminal or not) 316 if ((tq->qType == TTP_Segment) || // data w/more to follow 317 (tq->qType == TTP_Segment_Last)) { // data w/out more to follow 318 319 err = Dequeue((QElem *)tq, &RxQueue); // remove current elem 320 ncheck(err); 321 322 if (RxSdu.sarbuf == nil) { // first time we've needed an SAR? 323 check( RxMaxSduSize ); 324 RxSdu.sarbuf = BufAlloc(RxMaxSduSize); 325 require(RxSdu.sarbuf, NoMem); // drop packet 326 } 327 // ok, double check that the thing will fit 328 if ((BufUsed(RxSdu.sarbuf) + BufSize(tq->buf)) <= RxMaxSduSize) { 329 Reassemble(RxSdu.sarbuf, tq->buf); 330 BufFree(tq->buf); // done with the partial buffer 331 AvailCredit++; // new style //** check this. 332 } 333 // else drop it on the floor! spec error here???? 334 else DebugLog("TinyTP3: Very confused in check RxBuffer"); 335 336 if (tq->qType == TTP_Segment_Last) { // if last buffer in segment 337 //**** send it up, status = ok 338 BufHideRest(RxSdu.sarbuf); // ok, set buffer size & rewind 339 TTPDataIndication(RxSdu.sarbuf, TTP_Data_Ok); // virtual callback 340 // CLIENT must free buffer 341 RxSdu.sarbuf = nil; // no longer have sar buffer 342 // am giving it to the client 343 } 344 NoMem: 345 tq->buf = nil; 346 tq->qLink = nil; // finally, done w/rx q element 347 Enqueue((QElem *)tq, &AvailQueue); // put back on avail list 348 //AvailCredit++; // ready for some more 349 return true; 350 } 351 352 353 if (tq->qType == TTP_Disconnect) { 354 err = Dequeue((QElem *)tq, &RxQueue); // remove current elem 355 ncheck(err); 356 FlushQueue(&RxQueue); // should be empty already 357 ///*** do disconnect indication, reason=r, data=data 358 TTPDisconnectIndication(tq->reason, tq->buf); // virtual callback 359 tq->buf = nil; // CLIENT frees buffer 360 tq->qLink = nil; 361 Enqueue((QElem *)tq, &AvailQueue); // put back on avail list 362 return true; 363 } 364 365 return false; // fell though, nothing done 366} 367 368void 369TTinyTP::Reassemble(TTPBuf *dest, TTPBuf *src) 370{ 371 check(dest); 372 check(src); 373 XTRACE(kReassemble, BufSize(src), BufUsed(dest)); 374 375 // copy from src to end of dest -- REWRITE!! 376 int len; 377 len = BufSize(src); 378 while (len--) { 379 BufPut(dest, BufGet(src)); 380 } 381} 382 383int 384queueDepth(QHdr *qh) 385{ 386 int count = 0; 387 QElemPtr link; 388 link = qh->qHead; 389 while (link) { 390 count++; 391 link = link->qLink; 392 } 393 return count; 394} 395 396// 397// Temp until something better is found 398// 399// Note I don't need the atomic stuff that I did under OS-9 400// 401IrDAErr Enqueue(QElemPtr qElement, QHdrPtr qHeader) 402{ 403 require(qHeader, Fail); 404 require(qElement, Fail); 405 406 // first, check and verify it's not already on the list 407 if (1) { 408 QElemPtr t; 409 for (t = qHeader->qHead ; t != nil; t = t->qLink) 410 require(t != qElement, Fail); 411 } 412 413 qElement->qLink = qHeader->qHead; // put new one at front 414 qHeader->qHead = qElement; 415 if (qHeader->qTail == nil) // if only one, then new tail 416 qHeader->qTail = qElement; 417 418 return noErr; 419 420Fail: 421 return kIrDAErrGeneric; 422} 423 424IrDAErr Dequeue(QElemPtr qElement, QHdrPtr qHeader) 425{ 426 QElemPtr t, prev; 427 int old_depth, new_depth; // debug 428 429 require(qHeader, Fail); 430 require(qElement, Fail); 431 prev = nil; 432 433 old_depth = queueDepth(qHeader); 434 435 for (t = qHeader->qHead; t != nil; t = t->qLink) { 436 if (t == qElement) { // found it 437 if (prev) // if not first 438 prev->qLink = t->qLink; // remove from chain 439 else // else has new head 440 qHeader->qHead = t->qLink; 441 442 if (qHeader->qTail == t) // if we were the old tail 443 qHeader->qTail = prev; // then there's a new tail 444 445 { 446 new_depth = queueDepth(qHeader); 447 check(new_depth == old_depth -1); 448 } 449 return noErr; 450 } 451 prev = t; 452 } 453 454Fail: 455 return kIrDAErrGeneric; 456} 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472