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