#include "ttp.h"
#include "ttppdu.h"
#include "CBufferSegment.h"
int queueDepth(QHdr *qh);
#if (hasTracing > 0 && hasTTP3Tracing > 0)
enum IrTinyTP3TraceCodes
{
kAppendTail = 1,
kFlushQueue,
kGetSegment,
kCheckTheQs,
kCheckTheQs2,
kSendDataless,
kCheckTxQ,
kCheckRxQ,
kReassemble,
kGotSmallPacket,
kPendingPut,
kCurrentSendCredit
};
static
EventTraceCauseDesc gTraceEvents[] = {
{kAppendTail, "TinyTP3: append tail"},
{kFlushQueue, "TinyTP3: flush queue"},
{kGetSegment, "TinyTP3: Get segment"},
{kCheckTheQs, "TinyTP3: Check Qs"},
{kCheckTheQs2, "TinyTP3: Qs. Remote/Avail credit"},
{kSendDataless, "TinyTP3: Send Dataless"},
{kCheckTxQ, "TinyTP3: Check TxQ"},
{kCheckRxQ, "TinyTP3: Check RxQ"},
{kReassemble, "TinyTP3: Reassemble"},
{kGotSmallPacket, "TinyTP3: Passing up small packet"},
{kPendingPut, "TinyTP3: put delay pending complete"},
{kCurrentSendCredit, "TinyTP3: current SendCredit"}
};
#define XTRACE(x, y, z) IrDALogAdd( x, y, z, gTraceEvents, true )
#else
#define XTRACE(x, y, z) ((void)0)
#endif
void
TTinyTP::AppendTail(QHdr *qhdr, TTPMsg msg, int r, TTPBuf *buf)
{ TTPq *tq;
IrDAErr err;
require(qhdr, Fail);
tq = (TTPq *)AvailQueue.qHead; require(tq, Fail);
err = Dequeue((QElem *)tq, &AvailQueue);
nrequire(err, Fail);
tq->qLink = nil;
tq->qType = msg;
tq->reason = r;
tq->buf = buf;
Enqueue((QElem *)tq, qhdr);
XTRACE(kAppendTail, 3, queueDepth(qhdr));
Fail:
return;
}
void
TTinyTP::FlushQueue(QHdr *q)
{ TTPq *tq;
check(q);
if (q == &RxQueue) {XTRACE(kFlushQueue, 1, queueDepth(q));}
else if (q == &TxQueue) {XTRACE(kFlushQueue, 2, queueDepth(q));}
else DebugLog("Logic err. TTinyTP::FlushQueue called w/unknown queue hdr");
while ((tq = (TTPq *)q->qHead) != 0) {
IrDAErr err;
err = Dequeue((QElem *)tq, q);
ncheck(err);
if (tq->buf != 0) BufFree(tq->buf); tq->buf = nil;
tq->qLink = nil;
Enqueue((QElem *)tq, &AvailQueue);
}
}
TTPBuf *
TTinyTP::GetSegment(int i, TTPBuf *ttpbuf) {
TTPBuf *seg; unsigned char *newBase; int length;
check(ttpbuf);
XTRACE(kGetSegment, i, ttpbuf->GetSize());
i = i - 1; length = BufSize(ttpbuf) - (i * MaxSegSize); if (length > MaxSegSize) length = MaxSegSize; check(length);
newBase = BufBase(ttpbuf) + (i * MaxSegSize); seg = CBufferSegment::New(newBase, length); check(seg);
return seg;
}
#define QueueEmpty(x) (x.qHead==nil)
#define QueueNotEmpty(x) (x.qHead != nil)
#define LowThreshold 2 // Check this *********
void
TTinyTP::CheckTheQueues()
{
TTinyTP *curTTP;
curTTP = this;
{
Boolean check = true; int rxDepth, txDepth; rxDepth = queueDepth(&curTTP->RxQueue); txDepth = queueDepth(&curTTP->TxQueue);
XTRACE(kCheckTheQs, rxDepth, txDepth);
XTRACE(kCheckTheQs2, curTTP->RemoteCredit, curTTP->AvailCredit);
XTRACE(kCurrentSendCredit, curTTP->initial_credit, curTTP->SendCredit);
while (check) { check = false;
if ((QueueEmpty(curTTP->TxQueue) || curTTP->SendCredit == 0) &&
(curTTP->RemoteCredit <= LowThreshold || curTTP->AvailCredit > 3) &&
curTTP->AvailCredit > 0 && curTTP->Connected) {
curTTP->SendDataless(); }
if (QueueNotEmpty(curTTP->TxQueue) &&
curTTP->CheckTxQueue()) check = true;
if (QueueNotEmpty(curTTP->RxQueue) &&
curTTP->CheckRxQueue()) check = true;
}
}
}
void
TTinyTP::SendDataless()
{ TTPBuf *data;
int n;
XTRACE(kSendDataless, RemoteCredit, AvailCredit);
n = AvailCredit;
AvailCredit = 0;
if (n > 127) { AvailCredit = n - 127; n = 127; }
RemoteCredit += n;
data = ttp_pdu_data(false, n, NULL);
require(data, NoMem);
this->DataPut(data); return;
NoMem:
RemoteCredit -= n; AvailCredit += n;
return;
}
Boolean
TTinyTP::CheckTxQueue()
{ TTPq *tq;
IrDAErr err;
XTRACE(kCheckTxQ, 0, 0);
tq = (TTPq *)TxQueue.qHead; if (tq == 0) return false;
if (tq->qType == TTP_Disconnect) {
Connected = false; err = Dequeue((QElem *)tq, &TxQueue); ncheck(err);
FlushQueue(&TxQueue); FlushQueue(&RxQueue);
txQDepth = 0;
this->Disconnect();
tq->qLink = nil;
Enqueue((QElem *)tq, &AvailQueue); return true; }
if ((tq->qType == TTP_Segment || tq->qType == TTP_Segment_Last) &&
SendCredit > 0) {
int n;
Boolean m;
n = AvailCredit;
if (n > 127) {
AvailCredit = n - 127;
n = 127;
} else AvailCredit = 0;
RemoteCredit += n;
err = Dequeue((QElem *)tq, &TxQueue); ncheck(err);
txQDepth--;
SendCredit--;
m = (tq->qType == TTP_Segment); ttp_pdu_data_setbyte(m, n, tq->buf); this->DataPut(tq->buf); tq->buf = nil;
tq->qLink = nil;
Enqueue((QElem *)tq, &AvailQueue); return true;
}
return false; }
Boolean
TTinyTP::CheckRxQueue()
{ TTPq *tq;
IrDAErr err;
XTRACE(kCheckRxQ, 0, 0);
tq = (TTPq *)RxQueue.qHead; if (tq == 0) return false; if (RxSdu.busy) return false;
if (RxMaxSduSize == 0 && (tq->qType == TTP_Segment || tq->qType == TTP_Segment_Last)) {
err = Dequeue((QElem *)tq, &RxQueue); ncheck(err);
TTPDataIndication(tq->buf, TTP_Data_Ok); tq->buf = nil;
tq->qLink = nil;
Enqueue((QElem *)tq, &AvailQueue); return true;
}
if ((tq->qType == TTP_Segment_Last) && (RxSdu.sarbuf == nil)) { check (tq->buf);
XTRACE(kGotSmallPacket, 0, BufSize(tq->buf));
err = Dequeue((QElem *)tq, &RxQueue); ncheck(err);
TTPDataIndication(tq->buf, TTP_Data_Ok); tq->buf = nil; tq->qLink = nil; Enqueue((QElem *)tq, &AvailQueue); return true;
}
if ((tq->qType == TTP_Segment) || (tq->qType == TTP_Segment_Last)) {
err = Dequeue((QElem *)tq, &RxQueue); ncheck(err);
if (RxSdu.sarbuf == nil) { check( RxMaxSduSize );
RxSdu.sarbuf = BufAlloc(RxMaxSduSize);
require(RxSdu.sarbuf, NoMem); }
if ((BufUsed(RxSdu.sarbuf) + BufSize(tq->buf)) <= RxMaxSduSize) {
Reassemble(RxSdu.sarbuf, tq->buf);
BufFree(tq->buf); AvailCredit++; }
else DebugLog("TinyTP3: Very confused in check RxBuffer");
if (tq->qType == TTP_Segment_Last) { BufHideRest(RxSdu.sarbuf); TTPDataIndication(RxSdu.sarbuf, TTP_Data_Ok); RxSdu.sarbuf = nil; }
NoMem:
tq->buf = nil;
tq->qLink = nil; Enqueue((QElem *)tq, &AvailQueue); return true;
}
if (tq->qType == TTP_Disconnect) {
err = Dequeue((QElem *)tq, &RxQueue); ncheck(err);
FlushQueue(&RxQueue); TTPDisconnectIndication(tq->reason, tq->buf); tq->buf = nil; tq->qLink = nil;
Enqueue((QElem *)tq, &AvailQueue); return true;
}
return false; }
void
TTinyTP::Reassemble(TTPBuf *dest, TTPBuf *src)
{
check(dest);
check(src);
XTRACE(kReassemble, BufSize(src), BufUsed(dest));
int len;
len = BufSize(src);
while (len--) {
BufPut(dest, BufGet(src));
}
}
int
queueDepth(QHdr *qh)
{
int count = 0;
QElemPtr link;
link = qh->qHead;
while (link) {
count++;
link = link->qLink;
}
return count;
}
IrDAErr Enqueue(QElemPtr qElement, QHdrPtr qHeader)
{
require(qHeader, Fail);
require(qElement, Fail);
if (1) {
QElemPtr t;
for (t = qHeader->qHead ; t != nil; t = t->qLink)
require(t != qElement, Fail);
}
qElement->qLink = qHeader->qHead; qHeader->qHead = qElement;
if (qHeader->qTail == nil) qHeader->qTail = qElement;
return noErr;
Fail:
return kIrDAErrGeneric;
}
IrDAErr Dequeue(QElemPtr qElement, QHdrPtr qHeader)
{
QElemPtr t, prev;
int old_depth, new_depth;
require(qHeader, Fail);
require(qElement, Fail);
prev = nil;
old_depth = queueDepth(qHeader);
for (t = qHeader->qHead; t != nil; t = t->qLink) {
if (t == qElement) { if (prev) prev->qLink = t->qLink; else qHeader->qHead = t->qLink;
if (qHeader->qTail == t) qHeader->qTail = prev;
{
new_depth = queueDepth(qHeader);
check(new_depth == old_depth -1);
}
return noErr;
}
prev = t;
}
Fail:
return kIrDAErrGeneric;
}