plptools
Loading...
Searching...
No Matches
link.cc
Go to the documentation of this file.
1/*
2 * This file is part of plptools.
3 *
4 * Copyright (C) 1999 Philip Proudman <philip.proudman@btinternet.com>
5 * Copyright (C) 1999-2002 Fritz Elfert <felfert@to.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19 *
20 */
21#include "config.h"
22
23#include <iostream>
24
25#include <stdint.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <stdio.h>
29#include <sys/time.h>
30
31#include "bufferstore.h"
32#include "bufferarray.h"
33
34#include "link.h"
35#include "ncp_log.h"
36#include "ncp.h"
37#include "packet.h"
38
39extern "C" {
40 static void *expire_check(void *arg)
41 {
42 Link *l = (Link *)arg;
43 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
44 while (1) {
45 usleep(l->retransTimeout() * 500);
46 l->retransmit();
47 }
48 }
49};
50
51using namespace std;
52
54 stringRep.add(Link::LINK_TYPE_UNKNOWN, N_("Unknown"));
55 stringRep.add(Link::LINK_TYPE_SIBO, N_("SIBO"));
56 stringRep.add(Link::LINK_TYPE_EPOC, N_("EPOC"));
57ENUM_DEFINITION_END(Link::link_type)
58
59Link::Link(const char *fname, int baud, NCP *_ncp, unsigned short _verbose, const int cancellationFd)
60 : p(0)
61{
62 theNCP = _ncp;
63 verbose = _verbose;
64 txSequence = 1;
65 rxSequence = -1;
66 failed = false;
67 seqMask = 7;
68 maxOutstanding = 1;
69 linkType = LINK_TYPE_UNKNOWN;
70 for (int i = 0; i < 256; i++)
71 xoff[i] = false;
72 // generate magic number for sendReqCon()
73 srandom(time(NULL));
74 conMagic = random();
75
76 p = new packet(fname, baud, this, _verbose, cancellationFd);
77
78 pthread_mutex_init(&queueMutex, NULL);
79 pthread_create(&checkthread, NULL, expire_check, this);
80
81 // submit a link request
82 sendReqReq();
83}
84
86{
87 flush();
88 pthread_cancel(checkthread);
89 pthread_join(checkthread, NULL);
90 pthread_mutex_destroy(&queueMutex);
91 delete p;
92}
93
94unsigned long Link::
96{
97 return ((unsigned long)getSpeed() * 1000 / 13200) + 200;
98}
99
101reset() {
102 txSequence = 1;
103 rxSequence = -1;
104 failed = false;
105 seqMask = 7;
106 maxOutstanding = 1;
109 for (int i = 0; i < 256; i++)
110 xoff[i] = false;
111 p->reset();
112 // submit a link request
113 sendReqReq();
114}
115
116unsigned short Link::
118{
119 return verbose;
120}
121
123setVerbose(unsigned short _verbose)
124{
125 verbose = _verbose;
127}
128
130send(const bufferStore & buff)
131{
132 if (buff.getLen() > 300) {
133 failed = true;
134 } else
135 transmit(buff);
136}
137
140{
141 pthread_mutex_lock(&queueMutex);
142 ackWaitQueue.clear();
143 holdQueue.clear();
144 pthread_mutex_unlock(&queueMutex);
145}
146
149{
150 pthread_mutex_lock(&queueMutex);
151 vector<ackWaitQueueElement>::iterator i;
152 for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++)
153 if (i->data.getByte(0) == channel) {
154 ackWaitQueue.erase(i);
155 i--;
156 }
157 vector<bufferStore>::iterator j;
158 for (j = holdQueue.begin(); j != holdQueue.end(); j++)
159 if (j->getByte(0) == channel) {
160 holdQueue.erase(j);
161 j--;
162 }
163 pthread_mutex_unlock(&queueMutex);
164}
165
167sendAck(int seq)
168{
169 if (hasFailed())
170 return;
171 bufferStore tmp;
173 lout << "Link: >> ack seq=" << seq << endl;
174 if (seq > 7) {
175 int hseq = seq >> 3;
176 int lseq = (seq & 7) | 8;
177 seq = (hseq << 8) + lseq;
178 tmp.prependWord(seq);
179 } else
180 tmp.prependByte(seq);
181 p->send(tmp);
182}
183
186{
187 if (hasFailed())
188 return;
189 bufferStore tmp;
191 lout << "Link: >> con seq=4" << endl;
192 tmp.addByte(0x24);
193 tmp.addDWord(conMagic);
195 e.seq = 0; // expected ACK is 0, _NOT_ 4!
196 gettimeofday(&e.stamp, NULL);
197 e.data = tmp;
198 e.txcount = 4;
199 pthread_mutex_lock(&queueMutex);
200 ackWaitQueue.push_back(e);
201 pthread_mutex_unlock(&queueMutex);
202 p->send(tmp);
203}
204
207{
208 if (hasFailed())
209 return;
210 bufferStore tmp;
212 lout << "Link: >> con seq=1" << endl;
213 tmp.addByte(0x21);
215 e.seq = 0; // expected response is Ack with seq=0 or ReqCon
216 gettimeofday(&e.stamp, NULL);
217 e.data = tmp;
218 e.txcount = 4;
219 pthread_mutex_lock(&queueMutex);
220 ackWaitQueue.push_back(e);
221 pthread_mutex_unlock(&queueMutex);
222 p->send(tmp);
223}
224
226sendReq()
227{
228 if (hasFailed())
229 return;
230 bufferStore tmp;
232 lout << "Link: >> con seq=1" << endl;
233 tmp.addByte(0x20);
234 // No Ack expected for this, so no new entry in ackWaitQueue
235 p->send(tmp);
236}
237
240{
241 if (!p)
242 return;
243
244 vector<ackWaitQueueElement>::iterator i;
245 bool ackFound;
246 bool conFound;
247 int type = buff.getByte(0);
248 int seq = type & 0x0f;
249
250 type &= 0xf0;
251 // Support for incoming extended sequence numbers
252 if (seq & 8) {
253 int tseq = buff.getByte(1);
254 buff.discardFirstBytes(2);
255 seq = (tseq << 3) + (seq & 0x07);
256 } else
257 buff.discardFirstBytes(1);
258
259 switch (type) {
260 case 0x30:
261 // Normal data
262 if (verbose & LNK_DEBUG_LOG) {
263 lout << "Link: << dat seq=" << seq ;
265 lout << " " << buff << endl;
266 else
267 lout << " len=" << buff.getLen() << endl;
268 }
269
270 if (((rxSequence + 1) & seqMask) == seq) {
271 rxSequence++;
273
275 // Must check for XOFF/XON ncp frames HERE!
276 if ((buff.getLen() == 3) && (buff.getByte(0) == 0)) {
277 switch (buff.getByte(2)) {
278 case 1:
279 // XOFF
280 xoff[buff.getByte(1)] = true;
282 lout << "Link: got XOFF for channel "
283 << buff.getByte(1) << endl;
284 break;
285 case 2:
286 // XON
287 xoff[buff.getByte(1)] = false;
289 lout << "Link: got XON for channel "
290 << buff.getByte(1) << endl;
291 // Transmit packets on hold queue
292 transmitHoldQueue(buff.getByte(1));
293 break;
294 default:
295 theNCP->receive(buff);
296 }
297 } else
298 theNCP->receive(buff);
299
300 } else {
303 lout << "Link: DUP\n";
304 }
305 break;
306
307 case 0x00:
308 // Incoming ack
309 // Find corresponding packet in ackWaitQueue
310 ackFound = false;
311 struct timeval refstamp;
312 pthread_mutex_lock(&queueMutex);
313 for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++)
314 if (i->seq == seq) {
315 ackFound = true;
316 refstamp = i->stamp;
317 ackWaitQueue.erase(i);
318 if (verbose & LNK_DEBUG_LOG) {
319 lout << "Link: << ack seq=" << seq ;
321 lout << " " << buff;
322 lout << endl;
323 }
324 break;
325 }
326 pthread_mutex_unlock(&queueMutex);
327 if (ackFound) {
328 if ((linkType == LINK_TYPE_UNKNOWN) && (seq == 0)) {
329 // If the remote device runs SIBO protocol, this ACK
330 // should be 0 (the Ack on our ReqReq request, which is
331 // treated as a normal Req by the SIBO machine.
332 failed = false;
334 seqMask = 7;
335 maxOutstanding = 1;
336 rxSequence = 0;
337 txSequence = 1;
339 p->setEpoc(false);
341 lout << "Link: 1-linkType set to " << linkType << endl;
342 }
343 // Older packets implicitely ack'ed
344 multiAck(refstamp);
345 // Transmit waiting packets
347 } else {
348 // If packet with seq+1 is in ackWaitQueue, resend it immediately
349 // (Receiving an ack for a packet not on our wait queue is a
350 // hint by the Psion about which was the last packet it
351 // received successfully.)
352 pthread_mutex_lock(&queueMutex);
353 struct timeval now;
354 gettimeofday(&now, NULL);
355 bool nextFound = false;
356 for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++)
357 if (i->seq == seq+1) {
358 nextFound = true;
359 if (i->txcount-- == 0) {
360 // timeout, remove packet
362 lout << "Link: >> TRANSMIT timeout seq=" <<
363 i->seq << endl;
364 ackWaitQueue.erase(i);
365 i--;
366 } else {
367 // retransmit it
368 i->stamp = now;
370 lout << "Link: >> RETRANSMIT seq=" << i->seq
371 << endl;
372 p->send(i->data);
373 }
374 break;
375 }
376 pthread_mutex_unlock(&queueMutex);
377 if ((verbose & LNK_DEBUG_LOG) && (!nextFound)) {
378 lout << "Link: << UNMATCHED ack seq=" << seq;
380 lout << " " << buff;
381 lout << endl;
382 }
383 }
384 break;
385
386 case 0x20:
387 // New link
388 conFound = false;
389 if (seq > 3) {
390 // May be a link confirm packet (EPOC)
391 pthread_mutex_lock(&queueMutex);
392 for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++)
393 if ((i->seq == 0) && (i->data.getByte(0) == 0x21)) {
394 ackWaitQueue.erase(i);
397 lout << "Link: 2-linkType set to " << linkType << endl;
398 conFound = true;
399 failed = false;
400 // EPOC can handle extended sequence numbers
401 seqMask = 0x7ff;
402 // EPOC can handle up to 8 unacknowledged packets
403 maxOutstanding = 8;
404 p->setEpoc(true);
405 if (verbose & LNK_DEBUG_LOG) {
406 lout << "Link: << con seq=" << seq ;
408 lout << " " << buff;
409 lout << endl;
410 }
411 break;
412 }
413 pthread_mutex_unlock(&queueMutex);
414 }
415 if (conFound) {
416 rxSequence = 0;
417 txSequence = 1;
419 } else {
420 if (verbose & LNK_DEBUG_LOG) {
421 lout << "Link: << req seq=" << seq;
423 lout << " " << buff;
424 lout << endl;
425 }
427 if (seq > 0) {
430 lout << "Link: 3-linkType set to " << linkType << endl;
431 // EPOC can handle extended sequence numbers
432 seqMask = 0x7ff;
433 // EPOC can handle up to 8 unacknowledged packets
434 maxOutstanding = 8;
435 p->setEpoc(true);
436 failed = false;
437 sendReqCon();
438 } else {
439 // SIBO
441 failed = false;
442 seqMask = 7;
443 maxOutstanding = 1;
445 lout << "Link: 4-linkType set to " << linkType << endl;
446 rxSequence = 0;
447 txSequence = 1; // Our ReqReq was seq 0
449 p->setEpoc(false);
451 }
452 }
453 break;
454
455 case 0x10:
456 // Disconnect
458 lout << "Link: << DISC" << endl;
459 failed = true;
460 break;
461
462 default:
463 lerr << "Link: FATAL: Unknown packet type " << type << endl;
464 }
465}
466
469{
470 vector<bufferStore> tmpQueue;
471 vector<bufferStore>::iterator i;
472
473 // First, move desired packets to a temporary queue
474 pthread_mutex_lock(&queueMutex);
475 for (i = holdQueue.begin(); i != holdQueue.end(); i++)
476 if (i->getByte(0) == channel) {
477 tmpQueue.push_back(*i);
478 holdQueue.erase(i);
479 i--;
480 }
481 pthread_mutex_unlock(&queueMutex);
482
483 // ... then transmit the moved packets
484 for (i = tmpQueue.begin(); i != tmpQueue.end(); i++)
485 transmit(*i);
486}
487
490{
491 vector<bufferStore> tmpQueue;
492 vector<bufferStore>::iterator i;
493
494 // First, move desired packets to a temporary queue
495 for (i = waitQueue.begin(); i != waitQueue.end(); i++)
496 tmpQueue.push_back(*i);
497 waitQueue.clear();
498 // transmit the moved packets. If the backlock gets
499 // full, they are put into waitQueue again.
500 for (i = tmpQueue.begin(); i != tmpQueue.end(); i++)
501 transmit(*i);
502}
503
506{
507 if (hasFailed())
508 return;
509
510 int remoteChan = buf.getByte(0);
511 if (xoff[remoteChan]) {
512 pthread_mutex_lock(&queueMutex);
513 holdQueue.push_back(buf);
514 pthread_mutex_unlock(&queueMutex);
515 } else {
516
517 // If backlock is full, put on waitQueue
518 int ql;
519 pthread_mutex_lock(&queueMutex);
520 ql = ackWaitQueue.size();
521 pthread_mutex_unlock(&queueMutex);
522 if (ql >= maxOutstanding) {
523 waitQueue.push_back(buf);
524 return;
525 }
526
528 e.seq = txSequence++;
530 gettimeofday(&e.stamp, NULL);
531 // An empty buffer is considered a new link request
532 if (buf.empty()) {
533 // Request for new link
534 e.txcount = 4;
536 lout << "Link: >> req seq=" << e.seq << endl;
537 buf.prependByte(0x20 + e.seq);
538 } else {
539 e.txcount = 8;
540 if (verbose & LNK_DEBUG_LOG) {
541 lout << "Link: >> dat seq=" << e.seq;
543 lout << " " << buf;
544 lout << endl;
545 }
546 if (e.seq > 7) {
547 int hseq = e.seq >> 3;
548 int lseq = 0x30 + ((e.seq & 7) | 8);
549 int seq = (hseq << 8) + lseq;
550 buf.prependWord(seq);
551 } else
552 buf.prependByte(0x30 + e.seq);
553 }
554 e.data = buf;
555 pthread_mutex_lock(&queueMutex);
556 ackWaitQueue.push_back(e);
557 pthread_mutex_unlock(&queueMutex);
558 p->send(buf);
559 }
560}
561
562static void
563timesub(struct timeval *tv, unsigned long millisecs)
564{
565 uint64_t micros = tv->tv_sec;
566 uint64_t sub = millisecs;
567
568 micros <<= 32;
569 micros += tv->tv_usec;
570 micros -= (sub * 1000);
571 tv->tv_usec = micros & 0xffffffff;
572 tv->tv_sec = (micros >>= 32) & 0xffffffff;
573}
574
575static bool
576olderthan(struct timeval t1, struct timeval t2)
577{
578 uint64_t m1 = t1.tv_sec;
579 uint64_t m2 = t2.tv_sec;
580 m1 <<= 32;
581 m2 <<= 32;
582 m1 += t1.tv_usec;
583 m2 += t2.tv_usec;
584 return (m1 < m2);
585}
586
588multiAck(struct timeval refstamp)
589{
590 vector<ackWaitQueueElement>::iterator i;
591 pthread_mutex_lock(&queueMutex);
592 for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++)
593 if (olderthan(i->stamp, refstamp)) {
594 ackWaitQueue.erase(i);
595 i--;
596 }
597 pthread_mutex_unlock(&queueMutex);
598}
599
602{
603
604 if (hasFailed()) {
606 return;
607 }
608
609 pthread_mutex_lock(&queueMutex);
610 vector<ackWaitQueueElement>::iterator i;
611 struct timeval now;
612 gettimeofday(&now, NULL);
613 struct timeval expired = now;
614 timesub(&expired, retransTimeout());
615 for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++)
616 if (olderthan(i->stamp, expired)) {
617 if (i->txcount-- == 0) {
618 // timeout, remove packet
620 lout << "Link: >> TRANSMIT timeout seq=" << i->seq << endl;
621 ackWaitQueue.erase(i);
622 failed = true;
623 i--;
624 } else {
625 // retransmit it
626 i->stamp = now;
628 lout << "Link: >> RETRANSMIT seq=" << i->seq << endl;
629 p->send(i->data);
630 }
631 }
632 pthread_mutex_unlock(&queueMutex);
633}
634
636flush() {
637 while (stuffToSend())
638 sleep(1);
639}
640
643{
644 return ((!failed) && (!ackWaitQueue.empty()));
645}
646
648hasFailed()
649{
650 bool lfailed = p->linkFailed();
651 if (failed || lfailed) {
653 lout << "Link: hasFailed: " << failed << ", " << lfailed << endl;
654 }
655 failed |= lfailed;
656 return failed;
657}
658
661{
662 return linkType;
663}
664
666getSpeed()
667{
668 return p->getSpeed();
669}
#define ENUM_DEFINITION_END(EnumName)
Definition: Enum.h:304
#define ENUM_DEFINITION_BEGIN(EnumName, initWith)
Helper macro to construct an enumeration wrapper Enum<E> for a specific enum type.
Definition: Enum.h:299
Wrapper class featuring range-checking and string representation of enumerated values.
Definition: Enum.h:136
Definition: ncp.h:54
void receive(bufferStore s)
Definition: ncp.cc:118
A generic container for an array of bytes.
Definition: bufferstore.h:37
void discardFirstBytes(int len=0)
Removes bytes from the start of the buffer.
Definition: bufferstore.cc:143
void addByte(unsigned char c)
Appends a byte to the content of this instance.
Definition: bufferstore.cc:159
void prependByte(unsigned char c)
Prepends a byte to the content of this instance.
Definition: bufferstore.cc:212
void prependWord(int w)
Prepends a word to the content of this instance.
Definition: bufferstore.cc:218
bool empty() const
Tests if the bufferStore is empty.
Definition: bufferstore.h:258
void addDWord(long dw)
Appends a dword to the content of this instance.
Definition: bufferstore.cc:199
unsigned long getLen() const
Retrieves the length of a bufferStore.
Definition: bufferstore.cc:94
unsigned char getByte(long pos=0) const
Retrieves the byte at index pos.
Definition: bufferstore.cc:98
Definition: packet.h:38
int getSpeed()
Definition: packet.cc:293
void send(bufferStore &b)
Send a buffer out to serial line.
Definition: packet.cc:299
void setEpoc(bool)
Definition: packet.cc:287
bool linkFailed()
Definition: packet.cc:486
void reset()
Definition: packet.cc:225
void setVerbose(short int)
Definition: packet.cc:281
Definition: doctest.h:522
std::ostream lerr
#define LNK_DEBUG_LOG
Definition: ncp_log.h:31
std::ostream lout
#define LNK_DEBUG_DUMP
Definition: ncp_log.h:32
#define N_(String)
Definition: plpintl.h:36
int verbose
Definition: plpprintd.cc:58
Describes a transmitted packet which has not yet been acknowledged by the peer.
Definition: link.h:40
struct timeval stamp
Time of last transmit.
Definition: link.h:52
int seq
Original sequence number.
Definition: link.h:44
bufferStore data
Packet content.
Definition: link.h:56
int txcount
Number of remaining transmit retries.
Definition: link.h:48