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