#include #include #include #include #include #include #include #include #include #include #include #include #include /* * This program reads data from a serial port and dumps it into a file. * * Copyright (C) 2018 - 2019 R.J. van der Putten, Leiden, Holland, * rob at sput dot nl. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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. */ #define RECTTY_VERSION "2019-01-31 17:31:28 UTC" int fd; struct termios oldtio; struct my_baud { unsigned int rate; char *str; } my_bauds[] = { \ { B300, "300" }, \ { B600, "600" }, \ { B1200, "1200" }, \ { B2400, "2400" }, \ { B4800, "4800" }, \ { B9600, "9600" }, \ { B19200, "19200" }, \ { B38400, "38400" }, \ { B57600, "57600" }, \ { B115200, "115200" }, \ { 0, NULL } \ }; #define MY_DFLT_SPD "115200" /* inspired by https://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html */ void resetty(void) { tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &oldtio); close(fd); } void hlp(void) { fprintf(stderr, "Writes data from tty to file.\n"); fprintf(stderr, \ "Usage: recvtty -d device [-b baudrate] [-f filename] [-h] [-m] [-v].\n"); fprintf(stderr, "-h: Print help and exit.\n"); fprintf(stderr, "-m: Enable monitoring DSR and DCD;\n"); fprintf(stderr, \ " The program will terminate when one of them is dropped.\n"); fprintf(stderr, "-v: Print version and exit.\n"); fprintf(stderr, "Default baudrate is %s bps.\n", MY_DFLT_SPD); fprintf(stderr, "Default filename is YYYYMMDD-hhmmss.dump, "); fprintf(stderr, "EG: 20181224-135557.dump\n"); fprintf(stderr, "The tty is set to RAW, 8N1, RTS-CTS handshaking.\n"); } void prtvers(void) { printf("Version: %s\n", RECTTY_VERSION); } unsigned int findbaud(char *s) { int i; i = 0; while (my_bauds[i].str != NULL) { if(strcmp(s, my_bauds[i].str) == 0) return(my_bauds[i].rate); i++; } return(0); } void listbauds(void) { int i; fprintf(stderr, "%s", my_bauds[0].str); i = 1; while (my_bauds[i].str != NULL) { fprintf(stderr, ", %s", my_bauds[i].str); i++; } } int main(int argc, char **argv) { int cont, ch, ktot, len, maxfd, opt, retval, tot, wlen; int modfl, modst, status; unsigned int baudrate; char baudstr[16], buf[4096], dev[256], filnam[256]; struct termios newtio; FILE *f1; fd_set readfs; time_t t; struct tm *tim; struct timeval timeout; ch = 0; ktot = 0; len = 0; maxfd = 0; modfl = 0; /* Monitor DSR and DCD if 1 */ modst = 0; /* DSR and DCD status */ opt = 0; retval = 0; status = 0; t = 0; tot = 0; wlen = 0; memset(buf, 0, sizeof(buf)); memset(dev, 0, sizeof(dev)); memset(filnam, 0, sizeof(filnam)); memset(&newtio, 0, sizeof(newtio)); /* This makes the tty rather raw */ f1 = NULL; tim = NULL; strncpy(baudstr, MY_DFLT_SPD, 15); /* Default speed */ cont = 1; /* Continue */ timeout.tv_sec = 2; timeout.tv_usec = 0; /* Command line processing */ while ((opt = getopt(argc, argv, "b:d:f:hmv")) != -1) { switch (opt) { case 'b': strncpy(baudstr, optarg, 15); break; case 'd': strncpy(dev, optarg, 255); break; case 'f': strncpy(filnam, optarg, 255); break; case 'h': hlp(); exit(0); case 'm': /* Use modem control lines */ modfl = 1; break; case 'v': prtvers(); exit(0); default: hlp(); exit(1); } } if ((baudrate = findbaud(baudstr)) == 0) { fprintf(stderr, "Invalid baudrate: %s.\n", baudstr); fprintf(stderr, "baudrate must be one of: "); listbauds(); fprintf(stderr, ".\n"); exit(1); } if (dev[0] == 0) { fprintf(stderr, "Must specify device. EG: -d /dev/ttyS1\n"); exit(1); } /* File name */ if (filnam[0] == 0) { t = time(NULL); tim = localtime(&t); snprintf(filnam, 255, "%04d%02d%02d-%02d%02d%02d.dump", \ tim->tm_year + 1900, tim->tm_mon + 1, tim->tm_mday, \ tim->tm_hour, tim->tm_min, tim->tm_sec); } if ((f1 = fopen(filnam, "a")) == NULL) { fprintf(stderr, "Can't open %s", filnam); exit(1); } /* Open modem device for reading and writing and not as controlling tty because we don't want to get killed if line noise sends CTRL-C. */ if ((fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) { perror(dev); fclose(f1); exit(1); } /* save current serial port settings */ tcgetattr(fd, &oldtio); /* BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed. CRTSCTS: Output hardware flow control. HUPCL: Lower modem control lines (DTR) when done. CLOCAL: Ignore modem control (DSR, DCD) lines. CREAD: Enable receiving characters. CS8: 8 bit, no parity, 1 stopbit. */ newtio.c_cflag = baudrate | CRTSCTS | HUPCL | CLOCAL | CREAD | CS8; /* Input: IGNPAR: ignore bytes with parity errors */ newtio.c_iflag = IGNPAR; /* Blocking read until 1 character arrives. You don't need this with a non blocking read. */ /* newtio.c_cc[VMIN] = 1; */ /* Clean the modem line and activate the settings for the port */ tcflush(fd, TCIOFLUSH); if (tcsetattr(fd, TCSANOW, &newtio) != 0) { perror(dev); resetty(); fclose(f1); exit(1); } printf("Device: %s, speed: %s bps.\n", dev, baudstr); printf("Press to quit.\n"); while (cont != 0) { /* (Re) Initialise select() */ FD_ZERO(&readfs); FD_SET(0, &readfs); FD_SET(fd, &readfs); maxfd = fd + 1; if(modfl == 0) retval = select(maxfd, &readfs, NULL, NULL, NULL); else retval = select(maxfd, &readfs, NULL, NULL, &timeout); if (retval < 0) { if (errno == EINTR) { fprintf(stderr, "Reinitilizing select().\n"); continue; } else { perror("Select failed."); resetty(); fflush(f1); fclose(f1); exit(1); } } /* Block until read */ if (FD_ISSET(0, &readfs)) { /* Read data from keyboard */ read(0, &ch, 1); printf("Quitting\n"); cont = 0; /* Don't continue */ } if (FD_ISSET(fd, &readfs)) { /* Read data from ttySx */ len = read(fd, buf, 4095); if (len < 1) { if (errno == EAGAIN || errno == EINTR) { fprintf(stderr, "Reinitilizing select().\n"); continue; } else { perror("Can't read from tty."); resetty(); fclose(f1); exit(1); } } buf[len] = 0; /* Terminate */ if ((wlen = fwrite(buf, 1, len, f1)) < 1) { fprintf(stderr, "Can't write to output file.\n"); resetty(); fclose(f1); exit(1); } if (wlen != len) { fprintf(stderr, "Write mismatch.\n"); resetty(); fclose(f1); exit(1); } tot = tot + len; if (tot > ktot) { printf("Wrote %d bytes.\n", tot); ktot = ktot + 1024; } /* Done reading, check status */ if (modfl != 0) { ioctl(fd, TIOCMGET, &status); if ((status & TIOCM_DSR) != 0) modst = modst | 1; /* DSR High */ if ((status & TIOCM_CD) != 0) modst = modst | 2; /* DCD High */ } } /* Select() timedout */ if (modfl != 0) { ioctl(fd, TIOCMGET, &status); if ((status & TIOCM_DSR) == 0 && (modst & 1) != 0) { printf("DSR Dropped, terminating.\n"); cont = 0; } else if ((status & TIOCM_CD) == 0 && (modst & 2) != 0) { printf("DCD Dropped, terminating.\n"); cont = 0; } } } /* restore the old port settings */ resetty(); fflush(f1); fclose(f1); printf("Wrote %d bytes to file %s\n", tot, filnam); return(0); }