/*-*-c++-*- * $Id$ * * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "link.h" #include "packet.h" #include "ncp.h" #include "main.h" extern "C" { static void *expire_check(void *arg) { Link *l = (Link *)arg; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); while (1) { usleep(l->retransTimeout() * 500); l->retransmit(); } } }; using namespace std; ENUM_DEFINITION_BEGIN(Link::link_type, Link::LINK_TYPE_UNKNOWN) stringRep.add(Link::LINK_TYPE_UNKNOWN, N_("Unknown")); stringRep.add(Link::LINK_TYPE_SIBO, N_("SIBO")); stringRep.add(Link::LINK_TYPE_EPOC, N_("EPOC")); ENUM_DEFINITION_END(Link::link_type) Link::Link(const char *fname, int baud, ncp *_ncp, unsigned short _verbose) : p(0) { theNCP = _ncp; verbose = _verbose; txSequence = 1; rxSequence = -1; failed = false; seqMask = 7; maxOutstanding = 1; linkType = LINK_TYPE_UNKNOWN; for (int i = 0; i < 256; i++) xoff[i] = false; // generate magic number for sendReqCon() srandom(time(NULL)); conMagic = random(); p = new packet(fname, baud, this, _verbose); pthread_mutex_init(&queueMutex, NULL); pthread_create(&checkthread, NULL, expire_check, this); // submit a link request sendReqReq(); } Link::~Link() { flush(); pthread_cancel(checkthread); pthread_mutex_destroy(&queueMutex); delete p; } unsigned long Link:: retransTimeout() { return ((unsigned long)getSpeed() * 1000 / 13200) + 200; } void Link:: reset() { txSequence = 1; rxSequence = -1; failed = false; seqMask = 7; maxOutstanding = 1; linkType = LINK_TYPE_UNKNOWN; purgeAllQueues(); for (int i = 0; i < 256; i++) xoff[i] = false; p->reset(); // submit a link request sendReqReq(); } unsigned short Link:: getVerbose() { return verbose; } void Link:: setVerbose(unsigned short _verbose) { verbose = _verbose; p->setVerbose(verbose); } void Link:: send(const bufferStore & buff) { if (buff.getLen() > 300) { failed = true; } else transmit(buff); } void Link:: purgeAllQueues() { pthread_mutex_lock(&queueMutex); ackWaitQueue.clear(); holdQueue.clear(); pthread_mutex_unlock(&queueMutex); } void Link:: purgeQueue(int channel) { pthread_mutex_lock(&queueMutex); vector::iterator i; for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (i->data.getByte(0) == channel) { ackWaitQueue.erase(i); i--; } vector::iterator j; for (j = holdQueue.begin(); j != holdQueue.end(); j++) if (j->getByte(0) == channel) { holdQueue.erase(j); j--; } pthread_mutex_unlock(&queueMutex); } void Link:: sendAck(int seq) { if (hasFailed()) return; bufferStore tmp; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> ack seq=" << seq << endl; if (seq > 7) { int hseq = seq >> 3; int lseq = (seq & 7) | 8; seq = (hseq << 8) + lseq; tmp.prependWord(seq); } else tmp.prependByte(seq); p->send(tmp); } void Link:: sendReqCon() { if (hasFailed()) return; bufferStore tmp; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> con seq=4" << endl; tmp.addByte(0x24); tmp.addDWord(conMagic); ackWaitQueueElement e; e.seq = 0; // expected ACK is 0, _NOT_ 4! gettimeofday(&e.stamp, NULL); e.data = tmp; e.txcount = 4; pthread_mutex_lock(&queueMutex); ackWaitQueue.push_back(e); pthread_mutex_unlock(&queueMutex); p->send(tmp); } void Link:: sendReqReq() { if (hasFailed()) return; bufferStore tmp; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> con seq=1" << endl; tmp.addByte(0x21); ackWaitQueueElement e; e.seq = 0; // expected response is Ack with seq=0 or ReqCon gettimeofday(&e.stamp, NULL); e.data = tmp; e.txcount = 4; pthread_mutex_lock(&queueMutex); ackWaitQueue.push_back(e); pthread_mutex_unlock(&queueMutex); p->send(tmp); } void Link:: sendReq() { if (hasFailed()) return; bufferStore tmp; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> con seq=1" << endl; tmp.addByte(0x20); // No Ack expected for this, so no new entry in ackWaitQueue p->send(tmp); } void Link:: receive(bufferStore buff) { if (!p) return; vector::iterator i; bool ackFound; bool conFound; int type = buff.getByte(0); int seq = type & 0x0f; type &= 0xf0; // Support for incoming extended sequence numbers if (seq & 8) { int tseq = buff.getByte(1); buff.discardFirstBytes(2); seq = (tseq << 3) + (seq & 0x07); } else buff.discardFirstBytes(1); switch (type) { case 0x30: // Normal data if (verbose & LNK_DEBUG_LOG) { lout << "Link: << dat seq=" << seq ; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff << endl; else lout << " len=" << buff.getLen() << endl; } if (((rxSequence + 1) & seqMask) == seq) { rxSequence++; rxSequence &= seqMask; sendAck(rxSequence); // Must check for XOFF/XON ncp frames HERE! if ((buff.getLen() == 3) && (buff.getByte(0) == 0)) { switch (buff.getByte(2)) { case 1: // XOFF xoff[buff.getByte(1)] = true; if (verbose & LNK_DEBUG_LOG) lout << "Link: got XOFF for channel " << buff.getByte(1) << endl; break; case 2: // XON xoff[buff.getByte(1)] = false; if (verbose & LNK_DEBUG_LOG) lout << "Link: got XON for channel " << buff.getByte(1) << endl; // Transmit packets on hold queue transmitHoldQueue(buff.getByte(1)); break; default: theNCP->receive(buff); } } else theNCP->receive(buff); } else { sendAck(rxSequence); if (verbose & LNK_DEBUG_LOG) lout << "Link: DUP\n"; } break; case 0x00: // Incoming ack // Find corresponding packet in ackWaitQueue ackFound = false; struct timeval refstamp; pthread_mutex_lock(&queueMutex); for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (i->seq == seq) { ackFound = true; refstamp = i->stamp; ackWaitQueue.erase(i); if (verbose & LNK_DEBUG_LOG) { lout << "Link: << ack seq=" << seq ; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff; lout << endl; } break; } pthread_mutex_unlock(&queueMutex); if (ackFound) { if ((linkType == LINK_TYPE_UNKNOWN) && (seq == 0)) { // If the remote device runs SIBO protocol, this ACK // should be 0 (the Ack on our ReqReq request, which is // treated as a normal Req by the SIBO machine. failed = false; linkType = LINK_TYPE_SIBO; seqMask = 7; maxOutstanding = 1; rxSequence = 0; txSequence = 1; purgeAllQueues(); p->setEpoc(false); if (verbose & LNK_DEBUG_LOG) lout << "Link: 1-linkType set to " << linkType << endl; } // Older packets implicitely ack'ed multiAck(refstamp); // Transmit waiting packets transmitWaitQueue(); } else { // If packet with seq+1 is in ackWaitQueue, resend it immediately // (Receiving an ack for a packet not on our wait queue is a // hint by the Psion about which was the last packet it // received successfully.) pthread_mutex_lock(&queueMutex); struct timeval now; gettimeofday(&now, NULL); bool nextFound = false; for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (i->seq == seq+1) { nextFound = true; if (i->txcount-- == 0) { // timeout, remove packet if (verbose & LNK_DEBUG_LOG) lout << "Link: >> TRANSMIT timeout seq=" << i->seq << endl; ackWaitQueue.erase(i); i--; } else { // retransmit it i->stamp = now; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> RETRANSMIT seq=" << i->seq << endl; p->send(i->data); } break; } pthread_mutex_unlock(&queueMutex); if ((verbose & LNK_DEBUG_LOG) && (!nextFound)) { lout << "Link: << UNMATCHED ack seq=" << seq; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff; lout << endl; } } break; case 0x20: // New link conFound = false; if (seq > 3) { // May be a link confirm packet (EPOC) pthread_mutex_lock(&queueMutex); for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if ((i->seq == 0) && (i->data.getByte(0) == 0x21)) { ackWaitQueue.erase(i); linkType = LINK_TYPE_EPOC; if (verbose & LNK_DEBUG_LOG) lout << "Link: 2-linkType set to " << linkType << endl; conFound = true; failed = false; // EPOC can handle extended sequence numbers seqMask = 0x7ff; // EPOC can handle up to 8 unacknowledged packets maxOutstanding = 8; p->setEpoc(true); if (verbose & LNK_DEBUG_LOG) { lout << "Link: << con seq=" << seq ; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff; lout << endl; } break; } pthread_mutex_unlock(&queueMutex); } if (conFound) { rxSequence = 0; txSequence = 1; sendAck(rxSequence); } else { if (verbose & LNK_DEBUG_LOG) { lout << "Link: << req seq=" << seq; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff; lout << endl; } rxSequence = txSequence = 0; if (seq > 0) { linkType = LINK_TYPE_EPOC; if (verbose & LNK_DEBUG_LOG) lout << "Link: 3-linkType set to " << linkType << endl; // EPOC can handle extended sequence numbers seqMask = 0x7ff; // EPOC can handle up to 8 unacknowledged packets maxOutstanding = 8; p->setEpoc(true); failed = false; sendReqCon(); } else { // SIBO linkType = LINK_TYPE_SIBO; failed = false; seqMask = 7; maxOutstanding = 1; if (verbose & LNK_DEBUG_LOG) lout << "Link: 4-linkType set to " << linkType << endl; rxSequence = 0; txSequence = 1; // Our ReqReq was seq 0 purgeAllQueues(); p->setEpoc(false); sendAck(rxSequence); } } break; case 0x10: // Disconnect if (verbose & LNK_DEBUG_LOG) lout << "Link: << DISC" << endl; failed = true; break; default: lerr << "Link: FATAL: Unknown packet type " << type << endl; } } void Link:: transmitHoldQueue(int channel) { vector tmpQueue; vector::iterator i; // First, move desired packets to a temporary queue pthread_mutex_lock(&queueMutex); for (i = holdQueue.begin(); i != holdQueue.end(); i++) if (i->getByte(0) == channel) { tmpQueue.push_back(*i); holdQueue.erase(i); i--; } pthread_mutex_unlock(&queueMutex); // ... then transmit the moved packets for (i = tmpQueue.begin(); i != tmpQueue.end(); i++) transmit(*i); } void Link:: transmitWaitQueue() { vector tmpQueue; vector::iterator i; // First, move desired packets to a temporary queue for (i = waitQueue.begin(); i != waitQueue.end(); i++) tmpQueue.push_back(*i); waitQueue.clear(); // transmit the moved packets. If the backlock gets // full, they are put into waitQueue again. for (i = tmpQueue.begin(); i != tmpQueue.end(); i++) transmit(*i); } void Link:: transmit(bufferStore buf) { if (hasFailed()) return; int remoteChan = buf.getByte(0); if (xoff[remoteChan]) { pthread_mutex_lock(&queueMutex); holdQueue.push_back(buf); pthread_mutex_unlock(&queueMutex); } else { // If backlock is full, put on waitQueue int ql; pthread_mutex_lock(&queueMutex); ql = ackWaitQueue.size(); pthread_mutex_unlock(&queueMutex); if (ql >= maxOutstanding) { waitQueue.push_back(buf); return; } ackWaitQueueElement e; e.seq = txSequence++; txSequence &= seqMask; gettimeofday(&e.stamp, NULL); // An empty buffer is considered a new link request if (buf.empty()) { // Request for new link e.txcount = 4; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> req seq=" << e.seq << endl; buf.prependByte(0x20 + e.seq); } else { e.txcount = 8; if (verbose & LNK_DEBUG_LOG) { lout << "Link: >> dat seq=" << e.seq; if (verbose & LNK_DEBUG_DUMP) lout << " " << buf; lout << endl; } if (e.seq > 7) { int hseq = e.seq >> 3; int lseq = 0x30 + ((e.seq & 7) | 8); int seq = (hseq << 8) + lseq; buf.prependWord(seq); } else buf.prependByte(0x30 + e.seq); } e.data = buf; pthread_mutex_lock(&queueMutex); ackWaitQueue.push_back(e); pthread_mutex_unlock(&queueMutex); p->send(buf); } } static void timesub(struct timeval *tv, unsigned long millisecs) { uint64_t micros = tv->tv_sec; uint64_t sub = millisecs; micros <<= 32; micros += tv->tv_usec; micros -= (sub * 1000); tv->tv_usec = micros & 0xffffffff; tv->tv_sec = (micros >>= 32) & 0xffffffff; } static bool olderthan(struct timeval t1, struct timeval t2) { uint64_t m1 = t1.tv_sec; uint64_t m2 = t2.tv_sec; m1 <<= 32; m2 <<= 32; m1 += t1.tv_usec; m2 += t2.tv_usec; return (m1 < m2); } void Link:: multiAck(struct timeval refstamp) { vector::iterator i; pthread_mutex_lock(&queueMutex); for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (olderthan(i->stamp, refstamp)) { ackWaitQueue.erase(i); i--; } pthread_mutex_unlock(&queueMutex); } void Link:: retransmit() { if (hasFailed()) { purgeAllQueues(); return; } pthread_mutex_lock(&queueMutex); vector::iterator i; struct timeval now; gettimeofday(&now, NULL); struct timeval expired = now; timesub(&expired, retransTimeout()); for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (olderthan(i->stamp, expired)) { if (i->txcount-- == 0) { // timeout, remove packet if (verbose & LNK_DEBUG_LOG) lout << "Link: >> TRANSMIT timeout seq=" << i->seq << endl; ackWaitQueue.erase(i); failed = true; i--; } else { // retransmit it i->stamp = now; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> RETRANSMIT seq=" << i->seq << endl; p->send(i->data); } } pthread_mutex_unlock(&queueMutex); } void Link:: flush() { while (stuffToSend()) sleep(1); } bool Link:: stuffToSend() { return ((!failed) && (!ackWaitQueue.empty())); } bool Link:: hasFailed() { bool lfailed = p->linkFailed(); if (failed || lfailed) { if (verbose & LNK_DEBUG_LOG) lout << "Link: hasFailed: " << failed << ", " << lfailed << endl; } failed |= lfailed; return failed; } Enum Link:: getLinkType() { return linkType; } int Link:: getSpeed() { return p->getSpeed(); } /* * Local variables: * c-basic-offset: 4 * End: */