plptools
Loading...
Searching...
No Matches
packet.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-2001 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 <pthread.h>
24#include <string>
25#include <cstring>
26#include <fstream>
27#include <iomanip>
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <sys/param.h>
32#include <unistd.h>
33#include <sys/time.h>
34#include <sys/types.h>
35#include <errno.h>
36#include <sys/ioctl.h>
37#include <termios.h>
38#include <signal.h>
39
40#include "link.h"
41#include "mp_serial.h"
42#include "ncp_log.h"
43#include "packet.h"
44
45#define BUFLEN 4096 // Must be a power of 2
46#define BUFMASK (BUFLEN-1)
47#define hasSpace(dir) (((dir##Write + 1) & BUFMASK) != dir##Read)
48#define hasData(dir) (dir##Write != dir##Read)
49#define inca(idx,amount) do { \
50 idx = (idx + amount) & BUFMASK; \
51} while (0)
52#define inc1(idx) inca(idx, 1)
53#define normalize(idx) do { idx &= BUFMASK; } while (0)
54
55static unsigned short pumpverbose = 0;
57extern "C" {
63static void usr1handler(int sig)
64{
65 signal(SIGUSR1, usr1handler);
66}
67
68
69// TODO: `fd` isn't thread-safe.
70static void *pump_run(void *arg)
71{
72 packet *p = (packet *)arg;
73 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
74 while (1) {
75 if (p->fd == -1) {
76 fd_set r_set;
77 FD_ZERO(&r_set);
78 FD_SET(p->cancellationFd, &r_set);
79 struct timeval tv = {1, 0};
80 select(p->cancellationFd + 1, &r_set, NULL, NULL, &tv);
81 } else {
82 fd_set r_set;
83 fd_set w_set;
84 int res;
85 int count;
86
87 FD_ZERO(&r_set);
88 w_set = r_set;
89 FD_SET(p->cancellationFd, &r_set);
90 if (hasSpace(p->in))
91 FD_SET(p->fd, &r_set);
92 if (hasData(p->out))
93 FD_SET(p->fd, &w_set);
94 struct timeval tv = {1, 0};
95 res = select(MAX(p->fd, p->cancellationFd) + 1, &r_set, &w_set, NULL, &tv);
96 switch (res) {
97 case 0:
98 break;
99 case -1:
100 break;
101 default:
102 if (FD_ISSET(p->fd, &w_set)) {
103 count = p->outWrite - p->outRead;
104 if (count < 0)
105 count = (BUFLEN - p->outRead);
106 res = write(p->fd, &p->outBuffer[p->outRead], count);
107 if (res > 0) {
109 int i;
110 printf("pump: wrote %d bytes: (", res);
111 for (i = 0; i<res; i++)
112 printf("%02x ",
113 p->outBuffer[p->outRead + i]);
114 printf(")\n");
115 }
116 int hadSpace = hasSpace(p->out);
117 inca(p->outRead, res);
118 if (!hadSpace)
119 pthread_kill(p->thisThread, SIGUSR1);
120 }
121 }
122 if (FD_ISSET(p->fd, &r_set)) {
123 count = p->inRead - p->inWrite;
124 if (count <= 0)
125 count = (BUFLEN - p->inWrite);
126 res = read(p->fd, &p->inBuffer[p->inWrite], count);
127 if (res > 0) {
129 int i;
130 printf("pump: read %d bytes: (", res);
131 for (i = 0; i<res; i++)
132 printf("%02x ", p->inBuffer[p->inWrite + i]);
133 printf(")\n");
134 }
135 inca(p->inWrite, res);
136 p->findSync();
137 }
138 } else {
139 if (hasData(p->in))
140 p->findSync();
141 }
142 break;
143 }
144 }
145 }
146}
147
148};
149
150static const int baud_table[] = {
151 115200,
152 57600,
153 38400,
154 19200,
155 9600,
156 // Lower rates don't make sense ?!
157};
158#define BAUD_TABLE_SIZE (sizeof(baud_table) / sizeof(int))
159
160using namespace std;
161
163packet(const char *fname, int _baud, Link *_link, unsigned short _verbose, const int _cancellationFd)
164: cancellationFd(_cancellationFd)
165{
166 verbose = pumpverbose = _verbose;
167 devname = strdup(fname);
168 assert(devname);
169 baud = _baud;
170 theLINK = _link;
171 isEPOC = false;
172 justStarted = true;
173
174 // Initialize CRC table
175 crc_table[0] = 0;
176 for (int i = 0; i < 128; i++) {
177 unsigned int carry = crc_table[i] & 0x8000;
178 unsigned int tmp = (crc_table[i] << 1) & 0xffff;
179 crc_table[i * 2 + (carry ? 0 : 1)] = tmp ^ 0x1021;
180 crc_table[i * 2 + (carry ? 1 : 0)] = tmp;
181 }
182
183 inRead = inWrite = outRead = outWrite = 0;
184 inBuffer = new unsigned char[BUFLEN + 1];
185 outBuffer = new unsigned char[BUFLEN + 1];
186 assert(inBuffer);
187 assert(outBuffer);
188
189 esc = false;
190 lastFatal = false;
191 serialStatus = -1;
192 lastSYN = startPkt = -1;
193 crcIn = crcOut = 0;
194
195 thisThread = pthread_self();
196 realBaud = baud;
197 if (baud < 0) {
198 baud_index = 1;
199 realBaud = baud_table[0];
200 }
202 if (fd == -1)
203 lastFatal = true;
204 else {
205 signal(SIGUSR1, usr1handler);
206 pthread_create(&datapump, NULL, pump_run, this);
207 }
208}
209
211~packet()
212{
213 if (fd != -1) {
214 pthread_cancel(datapump);
215 pthread_join(datapump, NULL);
216 ser_exit(fd);
217 }
218 fd = -1;
219 delete []inBuffer;
220 delete []outBuffer;
221 free(devname);
222}
223
225reset()
226{
227 // This method stops the data pump thread and restarts it, performing a pthread_join. Given this, it's unsafe to be
228 // called from the data pump itself. This is a belt and braces check to ensure we don't do that (spoiler: we were).
229 assert(pthread_self() != datapump);
230 if (fd != -1) {
231 pthread_cancel(datapump);
232 pthread_join(datapump, NULL);
233 }
235 if (fd != -1) {
236 pthread_create(&datapump, NULL, pump_run, this);
237 realWrite();
238 }
239}
240
243{
245 lout << "resetting serial connection" << endl;
246 if (fd != -1) {
247 ser_exit(fd);
248 fd = -1;
249 }
250 usleep(100000);
251 outRead = outWrite = 0;
252 inRead = inWrite = 0;
253 esc = false;
254 lastFatal = false;
255 serialStatus = -1;
256 lastSYN = startPkt = -1;
257 crcIn = crcOut = 0;
258 realBaud = baud;
259 justStarted = true;
260 if (baud < 0) {
263 baud_index = 0;
264 }
265
268 lout << "serial connection set to " << dec << realBaud
269 << " baud, fd=" << fd << endl;
270 if (fd != -1)
271 lastFatal = false;
272}
273
274short int packet::
276{
277 return verbose;
278}
279
281setVerbose(short int _verbose)
282{
283 verbose = pumpverbose = _verbose;
284}
285
287setEpoc(bool _epoc)
288{
289 isEPOC = _epoc;
290}
291
293getSpeed()
294{
295 return realBaud;
296}
297
300{
301 opByte(0x16);
302 opByte(0x10);
303 opByte(0x02);
304
305 crcOut = 0;
306 long len = b.getLen();
307
308 if (verbose & PKT_DEBUG_LOG) {
309 lout << "packet: >> ";
311 lout << b;
312 else
313 lout << " len=" << dec << len;
314 lout << endl;
315 }
316
317 for (int i = 0; i < len; i++) {
318 unsigned char c = b.getByte(i);
319 switch (c) {
320 case 0x03:
321 if (isEPOC) {
322 opByte(0x10);
323 opByte(0x04);
324 addToCrc(0x03, &crcOut);
325 } else
326 opCByte(c, &crcOut);
327 break;
328 case 0x10:
329 opByte(0x10);
330 // fall thru
331 default:
332 opCByte(c, &crcOut);
333 }
334 }
335 opByte(0x10);
336 opByte(0x03);
337 opByte(crcOut >> 8);
338 opByte(crcOut & 0xff);
339 realWrite();
340}
341
343opByte(unsigned char a)
344{
345 if (!hasSpace(out))
346 realWrite();
348 inc1(outWrite);
349}
350
352opCByte(unsigned char a, unsigned short *crc)
353{
354 addToCrc(a, crc);
355 if (!hasSpace(out))
356 realWrite();
358 inc1(outWrite);
359}
360
362realWrite()
363{
364 pthread_kill(datapump, SIGUSR1);
365 while (!hasSpace(out)) {
366 sigset_t sigs;
367 int dummy;
368 sigemptyset(&sigs);
369 sigaddset(&sigs, SIGUSR1);
370 sigwait(&sigs, &dummy);
371 }
372}
373
375findSync()
376{
377 int inw = inWrite;
378 int p;
379
380outerLoop:
381 p = (lastSYN >= 0) ? lastSYN : inRead;
382 if (startPkt < 0) {
383 while (p != inw) {
384 normalize(p);
385 if (inBuffer[p++] != 0x16)
386 continue;
387 lastSYN = p - 1;
388 normalize(p);
389 if (p == inw)
390 break;
391 if (inBuffer[p++] != 0x10)
392 continue;
393 normalize(p);
394 if (p == inw)
395 break;
396 if (inBuffer[p++] != 0x02)
397 continue;
398 normalize(p);
399 lastSYN = startPkt = p;
400 crcIn = inCRCstate = 0;
401 rcv.init();
402 esc = false;
403 break;
404 }
405 }
406 if (startPkt >= 0) {
407 justStarted = false;
408 while (p != inw) {
409 unsigned char c = inBuffer[p];
410 switch (inCRCstate) {
411 case 0:
412 if (esc) {
413 esc = false;
414 switch (c) {
415 case 0x03:
416 inCRCstate = 1;
417 break;
418 case 0x04:
419 addToCrc(0x03, &crcIn);
420 rcv.addByte(0x03);
421 break;
422 default:
423 addToCrc(c, &crcIn);
424 rcv.addByte(c);
425 break;
426 }
427 } else {
428 if (c == 0x10)
429 esc = true;
430 else {
431 addToCrc(c, &crcIn);
432 rcv.addByte(c);
433 }
434 }
435 break;
436 case 1:
437 receivedCRC = c;
438 receivedCRC <<= 8;
439 inCRCstate = 2;
440 break;
441 case 2:
442 receivedCRC |= c;
443 inc1(p);
444 inRead = p;
445 startPkt = lastSYN = -1;
446 inCRCstate = 0;
447 if (receivedCRC != crcIn) {
449 lout << "packet: BAD CRC" << endl;
450 } else {
451 if (verbose & PKT_DEBUG_LOG) {
452 lout << "packet: << ";
454 lout << rcv;
455 else
456 lout << "len=" << dec << rcv.getLen();
457 lout << endl;
458 }
460 }
461 rcv.init();
462 if (hasData(out))
463 return;
464 goto outerLoop;
465 }
466 inc1(p);
467 }
468 lastSYN = p;
469 } else {
470 // If we get here, no sync was found.
471 // If we are just started and the amount of received data exceeds
472 // 15 bytes, the baudrate is obviously wrong.
473 // (or the connected device is not an EPOC device). Reset the
474 // serial connection and try next baudrate, if auto-baud is set.
475 if (justStarted) {
476 int rx_amount = (inw > inRead) ?
477 inw - inRead : BUFLEN - inRead + inw;
478 if (rx_amount > 15) {
480 }
481 }
482 }
483}
484
487{
488 int arg;
489 int res;
490 bool failed = false;
491
492 if (fd == -1)
493 return false;
494 res = ioctl(fd, TIOCMGET, &arg);
495 if (res < 0)
496 lastFatal = true;
497 if ((serialStatus == -1) || (arg != serialStatus)) {
499 lout << "packet: < DTR:" << ((arg & TIOCM_DTR)?1:0)
500 << " RTS:" << ((arg & TIOCM_RTS)?1:0)
501 << " DCD:" << ((arg & TIOCM_CAR)?1:0)
502 << " DSR:" << ((arg & TIOCM_DSR)?1:0)
503 << " CTS:" << ((arg & TIOCM_CTS)?1:0) << endl;
504 if (!((arg & TIOCM_RTS) && (arg & TIOCM_DTR))) {
505 arg |= (TIOCM_DTR | TIOCM_RTS);
506 res = ioctl(fd, TIOCMSET, &arg);
507 if (res < 0)
508 lastFatal = true;
510 lout << "packet: > DTR:" << ((arg & TIOCM_DTR)?1:0)
511 << " RTS:" << ((arg & TIOCM_RTS)?1:0)
512 << " DCD:" << ((arg & TIOCM_CAR)?1:0)
513 << " DSR:" << ((arg & TIOCM_DSR)?1:0)
514 << " CTS:" << ((arg & TIOCM_CTS)?1:0) << endl;
515 }
516 serialStatus = arg;
517 }
518 // TODO: Check for a solution on Solaris.
519 if ((arg & TIOCM_DSR) == 0) {
520 failed = true;
521 }
523 lout << "packet: linkFATAL\n";
524 if ((verbose & PKT_DEBUG_LOG) && failed)
525 lout << "packet: linkFAILED\n";
526 return (lastFatal || failed);
527}
A generic container for an array of bytes.
Definition: bufferstore.h:37
void init()
Initializes the bufferStore.
Definition: bufferstore.cc:77
void addByte(unsigned char c)
Appends a byte to the content of this instance.
Definition: bufferstore.cc:159
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
unsigned char * inBuffer
Definition: packet.h:78
Link * theLINK
Definition: packet.h:68
bool esc
Definition: packet.h:97
int inWrite
Definition: packet.h:79
unsigned int crc_table[256]
Definition: packet.h:71
int lastSYN
Definition: packet.h:87
unsigned short receivedCRC
Definition: packet.h:75
int getSpeed()
Definition: packet.cc:293
~packet()
Definition: packet.cc:211
bufferStore rcv
Definition: packet.h:90
void send(bufferStore &b)
Send a buffer out to serial line.
Definition: packet.cc:299
friend void * pump_run(void *)
Definition: packet.cc:70
void setEpoc(bool)
Definition: packet.cc:287
bool isEPOC
Definition: packet.h:99
void internalReset()
Definition: packet.cc:242
bool linkFailed()
Definition: packet.cc:486
int baud
Definition: packet.h:103
int outWrite
Definition: packet.h:83
int fd
Definition: packet.h:92
unsigned short crcOut
Definition: packet.h:73
packet(const char *fname, int baud, Link *_link, unsigned short verbose, const int cancellationFd)
Definition: packet.cc:163
int inRead
Definition: packet.h:80
pthread_t datapump
Definition: packet.h:69
void reset()
Definition: packet.cc:225
unsigned char * outBuffer
Definition: packet.h:82
pthread_t thisThread
Definition: packet.h:70
int startPkt
Definition: packet.h:86
unsigned short inCRCstate
Definition: packet.h:76
int serialStatus
Definition: packet.h:93
bool justStarted
Definition: packet.h:100
void setVerbose(short int)
Definition: packet.cc:281
bool lastFatal
Definition: packet.h:98
void realWrite()
Definition: packet.cc:362
short int getVerbose()
Definition: packet.cc:275
const int cancellationFd
Definition: packet.h:105
int outRead
Definition: packet.h:84
void opByte(unsigned char a)
Definition: packet.cc:343
short int verbose
Definition: packet.h:96
int baud_index
Definition: packet.h:94
void opCByte(unsigned char a, unsigned short *crc)
Definition: packet.cc:352
void findSync()
Definition: packet.cc:375
unsigned short crcIn
Definition: packet.h:74
void addToCrc(unsigned char a, unsigned short *crc)
Definition: packet.h:58
int realBaud
Definition: packet.h:95
char * devname
Definition: packet.h:102
void ser_exit(int fd)
Definition: mp_serial.c:172
int init_serial(const char *dev, int speed, int debug)
Definition: mp_serial.c:58
Definition: doctest.h:522
#define PKT_DEBUG_HANDSHAKE
Definition: ncp_log.h:35
std::ostream lout
#define PKT_DEBUG_DUMP
Definition: ncp_log.h:34
#define PKT_DEBUG_LOG
Definition: ncp_log.h:33
static unsigned short pumpverbose
Definition: packet.cc:55
static void * pump_run(void *arg)
Definition: packet.cc:70
static const int baud_table[]
Definition: packet.cc:150
static void usr1handler(int sig)
Signal handler does nothing.
Definition: packet.cc:63
#define hasData(dir)
Definition: packet.cc:48
#define normalize(idx)
Definition: packet.cc:53
#define BAUD_TABLE_SIZE
Definition: packet.cc:158
#define inc1(idx)
Definition: packet.cc:52
#define inca(idx, amount)
Definition: packet.cc:49
#define BUFLEN
Definition: packet.cc:45
#define hasSpace(dir)
Definition: packet.cc:47
static rfsv * a
Definition: main.cc:53