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