From dd95a49fc9077ac219494f866de7ce385c539210 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 26 Apr 2020 09:21:18 +0200 Subject: [PATCH] Added local library for serial port access --- .gitignore | 1 + configure.ac | 1 + src/Makefile.am | 3 +- src/libserial/Makefile.am | 6 + src/libserial/serial.c | 460 ++++++++++++++++++++++++++++++++++++++ src/libserial/serial.h | 38 ++++ 6 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 src/libserial/Makefile.am create mode 100755 src/libserial/serial.c create mode 100755 src/libserial/serial.h diff --git a/.gitignore b/.gitignore index 76a9190..eff76be 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ src/libsdr/libsdr.a src/libsample/libsample.a src/libam/libam.a src/libclipper/libclipper.a +src/libserial/libserial.a src/anetz/libgermanton.a src/anetz/anetz src/bnetz/bnetz diff --git a/configure.ac b/configure.ac index 5c37545..542a91e 100644 --- a/configure.ac +++ b/configure.ac @@ -84,6 +84,7 @@ AC_OUTPUT( src/libsdr/Makefile src/libsample/Makefile src/libclipper/Makefile + src/libserial/Makefile src/anetz/Makefile src/bnetz/Makefile src/cnetz/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index d07bc39..94c3f6c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,7 +25,8 @@ SUBDIRS = \ libwave \ libfft \ libmncc \ - libclipper + libclipper \ + libserial if HAVE_ALSA SUBDIRS += \ diff --git a/src/libserial/Makefile.am b/src/libserial/Makefile.am new file mode 100644 index 0000000..04fe1e9 --- /dev/null +++ b/src/libserial/Makefile.am @@ -0,0 +1,6 @@ +AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) + +noinst_LIBRARIES = libserial.a + +libserial_a_SOURCES = \ + serial.c diff --git a/src/libserial/serial.c b/src/libserial/serial.c new file mode 100755 index 0000000..076686a --- /dev/null +++ b/src/libserial/serial.c @@ -0,0 +1,460 @@ +/* serial port access + * + * (C) 2001-2018 by Andreas Eversberg + * All Rights Reserved + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "serial.h" + +/* +serial = serial_open("/dev/ttyS0",9600,8,n,1,'d','e',1,2); + +the return value will be a "serial handle" (!NOT THE FILE HANDLE) for all routines. on failure, a NULL will be returned. + +the parameters are as followed: + +char *device; tty name +int baud; baud rate (bits/s) +int databits; 5-7 +char parity; n=no,e=even,o=odd +int stopbits; 1-2 +char xonxoff; e=disable,e=enable +char rtscts; e=disable,e=enable +float txtimeout; seconds timeout for transmit +float rxtimeout; seconds timeout for receive + +to access the real tty handle use: + +serial->handle + +NOTE: the device is in non-blocking mode. +*/ +serial_t *serial_open(const char *serial_device, int serial_baud, int serial_databits, char serial_parity, int serial_stopbits, char serial_xonxoff, char serial_rtscts, int serial_getbreak, float serial_txtimeout, float serial_rxtimeout) +{ + int baud; + int flags; + int handshake_lines; + serial_t *serial; + + if (serial_databits < 5 || serial_databits > 8) { + serial_errno = -EINVAL; + serial_errnostr = "Only 5 trough 8 stopbits supported."; + return NULL; + } + if (serial_stopbits < 1 || serial_stopbits > 2) { + serial_errno = -EINVAL; + serial_errnostr = "Only 1 trough 2 stopbits supported."; + return NULL; + } + if (serial_xonxoff !='e' && serial_xonxoff != 'd') { + serial_errno = -EINVAL; + serial_errnostr = "Enable or disable Xon/Xoff?"; + return NULL; + } + if (serial_rtscts !='e' && serial_rtscts != 'd') { + serial_errno = -EINVAL; + serial_errnostr = "Enable or disable RTS/CTS?"; + return NULL; + } + if (serial_parity != 'n' && serial_parity != 'e' && serial_parity != 'o') { + serial_errno = -EINVAL; + serial_errnostr = "Unsopported parity."; + return NULL; + } + switch (serial_baud) { + case 50: baud = B50; break; + case 75: baud = B75; break; + case 110: baud = B110; break; + case 134: baud = B134; break; + case 150: baud = B150; break; + case 200: baud = B200; break; + case 300: baud = B300; break; + case 600: baud = B600; break; + case 1200: baud = B1200; break; + case 2400: baud = B2400; break; + case 4800: baud = B4800; break; + case 9600: baud = B9600; break; + case 19200: baud = B19200; break; + case 38400: baud = B38400; break; + case 57600: baud = B57600; break; + case 115200: baud = B115200; break; + case 230400: baud = B230400; break; + default: + serial_errno = -EINVAL; + serial_errnostr = "Baudrate not supported."; + return NULL; + } + + /* allocate handle */ + if ((serial = calloc(1, sizeof(*serial)))) { + + /* set parameters */ + serial->device = serial_device; + serial->baud = serial_baud; + serial->databits = serial_databits; + serial->parity = serial_parity; + serial->stopbits = serial_stopbits; + serial->xonxoff = serial_xonxoff; + serial->rtscts = serial_rtscts; + serial->txtimeout = serial_txtimeout; + serial->rxtimeout = serial_rxtimeout; + + + if ((serial->handle = open(serial->device, O_RDWR | O_NONBLOCK)) >= 0) { + if (isatty(serial->handle)) { + /* get termios */ + tcgetattr(serial->handle, &serial->old_termios); + tcgetattr(serial->handle, &serial->com_termios); + /* set flags */ + serial->com_termios.c_iflag = + ((serial->databits == 7) ? ISTRIP : 0) | + ((serial->xonxoff == 'e') ? (IXON | IXOFF) : 0) ; + if (serial_getbreak) + serial->com_termios.c_iflag |= (PARMRK | INPCK); + else + serial->com_termios.c_iflag |= (IGNBRK | IGNPAR); + serial->com_termios.c_oflag = 0; + serial->com_termios.c_cflag = + CREAD | + HUPCL | + ((serial->databits == 5) ? CS5 : 0) | + ((serial->databits == 6) ? CS6 : 0) | + ((serial->databits == 7) ? CS7 : 0) | + ((serial->databits == 8) ? CS8 : 0) | + ((serial->parity == 'e') ? PARENB : 0) | + ((serial->parity == 'o') ? (PARENB | PARODD) : 0) | + ((serial->stopbits == 2) ? CSTOPB : 0) | + ((serial->rtscts =='e' ) ? CRTSCTS : 0) | + ((serial->rtscts =='d' ) ? CLOCAL : 0) ; + serial->com_termios.c_lflag = 0; + serial->com_termios.c_cc[VSTART] = 0x11; + serial->com_termios.c_cc[VSTOP] = 0x13; + + /* set baud */ + serial->com_termios.c_cflag |= baud; + cfsetispeed(&serial->com_termios, baud); + cfsetospeed(&serial->com_termios, baud); + + serial->com_termios.c_cc[VMIN] = 0; + serial->com_termios.c_cc[VTIME] = (int)(serial->rxtimeout / 0.1 + 0.5); + + if (tcsetattr(serial->handle, TCSANOW, &serial->com_termios) >= 0) { + handshake_lines = TIOCM_DTR; + if (serial->rtscts == 'd') { + handshake_lines |= TIOCM_RTS; + } + if (ioctl(serial->handle, TIOCMBIS, &handshake_lines) >= 0) { + if ((flags = fcntl(serial->handle, F_GETFL, 0)) >= 0) { + flags &= ~O_NONBLOCK; + if (fcntl(serial->handle, F_SETFL, flags) >= 0) { + serial_errno = 0; + serial_errnostr = "ok"; + return serial; + } else { + serial_errno = -EIO; + serial_errnostr = "Cannot set fcntl."; + } + } else { + serial_errno = -EIO; + serial_errnostr = "Cannot read fnctl."; + } + } else { + serial_errno = -EIO; + serial_errnostr = "Cannot set handshake lines."; + } + } else { + serial_errno = -EIO; + serial_errnostr = "TTY refuses settings."; + } + } else { + serial_errno = -EIO; + serial_errnostr = "Device is not a tty!"; + } + tcsetattr(serial->handle, TCSANOW, &serial->old_termios); + close(serial->handle); + } else { + serial_errno = -EIO; + serial_errnostr = "Error opening serial interface."; + } + free(serial); + } else { + serial_errno = -ENOMEM; + serial_errnostr = "not enougth memory for handle"; + } + + return NULL; +} + + +/* +serial_close(serial); + +closes the com port and frees all memory used by "serial" +*/ +void serial_close(serial_t *serial) +{ + serial_errno = 0; + if (serial == 0) + return; + + tcsetattr(serial->handle,TCSANOW,&serial->old_termios); + close(serial->handle); + free(serial); +} + +/* +read = serial_read(serial, &buffer, size); + +reads until buffer "size" has reached or until timeout has occured. +"read" gives the number of bytes read. +*/ + +int serial_read(serial_t *serial, uint8_t *buffer, int size) +{ + int n; + + serial_errno = 0; + if (!serial) + return 0; + + n = read(serial->handle, buffer, size); + if (n < 0) { + serial_errno = n; + n = 0; + } + + return n; +} + +/* +wrote = serial_write(serial, &buffer, size); + +writes until buffer "size" has reached or until timeout has occured. +"wrote" gives the number of bytes written. +*/ + +int serial_write(serial_t *serial, uint8_t *buffer, int size) +{ + int n; + fd_set Desc; + struct timeval Timeout; + + serial_errno = 0; + if (!serial) + return 0; + + Timeout.tv_usec = (int)(serial->txtimeout * 1000000) % 1000000; + Timeout.tv_sec = serial->txtimeout; + + + FD_ZERO (&Desc); + FD_SET (serial->handle, &Desc); + + // Use select to check if write is possible + + if (select(serial->handle + 1, NULL, &Desc, NULL, &Timeout)) { + // Descriptor is ready for writing + n = write(serial->handle, buffer, size); + if (n < 0) { + serial_errno = n; + n = 0; + } + return n; + } else { + // Timeout or signal. Return an timeout error code when a signal + // occurs + return 0; + } +} + +/* +ok = serial_timeout(serial, transmit, receive); + +will set the transmit and receive timeout. note that the receive timeout +will only use the nearest 1/10s. to disable timeout, use 0 for any value. +*/ +int serial_timeout(serial_t *serial, double serial_txtimeout, double serial_rxtimeout) +{ + serial_errno = 0; + if (!serial) + return 0; + + serial->rxtimeout = serial_rxtimeout; + serial->txtimeout = serial_txtimeout; + + serial->com_termios.c_cc[VMIN] = 0; + serial->com_termios.c_cc[VTIME] = (int)(serial->rxtimeout / 0.1 + 0.5); + if (tcsetattr(serial->handle, TCSANOW, &serial->com_termios) < 0) { + serial_errno = - EIO; + serial_errnostr = "TTY refuses settings."; + return -1; + } + + return 0; +} + +/* +serial_errno +serial_errnostr + +the last call will give an errno while opening, which is described below: + +0: ok +also use serial_errnostr for description +*/ +int serial_errno = 0; +char *serial_errnostr = ""; + +int serial_cts(serial_t *serial) +{ + int status = 0; + + serial_errno = 0; + if (!serial) + return 0; + + if(ioctl(serial->handle, TIOCMGET, &status) < 0) { + serial_errno = -EIO; + serial_errnostr = "Cannot get ioctl."; + return -1; + } + return (status & TIOCM_CTS) != 0; +} + +int serial_dsr(serial_t *serial) +{ + int status = 0; + + serial_errno = 0; + if (!serial) + return 0; + + if(ioctl(serial->handle, TIOCMGET, &status) < 0) { + serial_errno = -EIO; + serial_errnostr = "Cannot get ioctl."; + return -1; + } + return (status & TIOCM_DSR) != 0; +} + +/* +ok = serial_dtron(serial); +ok = serial_rtson(serial); +ok = serial_dtroff(serial); +ok = serial_rtsoff(serial); + +turn on or off: rts, dtr +*/ + +int serial_dtron(serial_t *serial) +{ + int status = TIOCM_DTR; + + serial_errno = 0; + if (!serial) + return 0; + + if(ioctl(serial->handle, TIOCMBIS, &status) < 0) { + serial_errno = -EIO; + serial_errnostr = "Cannot set ioctl."; + return -1; + } + return 0; +} +int serial_dtroff(serial_t *serial) +{ + int status = TIOCM_DTR; + + serial_errno = 0; + if (!serial) + return 0; + + if(ioctl(serial->handle, TIOCMBIC, &status) < 0) { + serial_errno = -EIO; + serial_errnostr = "Cannot set ioctl."; + return -1; + } + return 0; +} +int serial_rtson(serial_t *serial) +{ + int status = TIOCM_RTS; + + serial_errno = 0; + if (!serial) + return 0; + + if(ioctl(serial->handle, TIOCMBIS, &status) < 0) { + serial_errno = -EIO; + serial_errnostr = "Cannot set ioctl."; + return -1; + } + return 0; +} +int serial_rtsoff(serial_t *serial) +{ + int status = TIOCM_RTS; + + serial_errno = 0; + if (!serial) + return 0; + + if(ioctl(serial->handle, TIOCMBIC, &status) < 0) { + serial_errno = -EIO; + serial_errnostr = "Cannot set ioctl."; + return -1; + } + return 0; +} + +int serial_break(serial_t *serial, int on) +{ + serial_errno = 0; + if (!serial) + return 0; + + if (ioctl(serial->handle, on ? TIOCSBRK : TIOCCBRK, 0) < 0) { + serial_errno = -EIO; + serial_errnostr = "Cannot set ioctl."; + return -1; + } + return 0; +} + + +/* +handle = serial_handle(serial); + +get real file handle by giving the serial handle +*/ + +int serial_handle(serial_t *serial) +{ + if (!serial) + return 0; + + return serial->handle; +} + diff --git a/src/libserial/serial.h b/src/libserial/serial.h new file mode 100755 index 0000000..17300dc --- /dev/null +++ b/src/libserial/serial.h @@ -0,0 +1,38 @@ + +#include + +extern int serial_errno; +extern char *serial_errnostr; + +typedef struct _serial { + /* parameters */ + const char *device; + int baud; + int databits; + char parity; + int stopbits; + char xonxoff; + char rtscts; + float txtimeout; + float rxtimeout; + + /* internal variables */ + int handle; + struct termios com_termios; + struct termios old_termios; +} serial_t; + +serial_t *serial_open(const char *serial_device, int serial_baud, int serial_databits, char serial_parity, int serial_stopbits, char serial_xonxoff, char serial_rtscts, int serial_getbreak, float serial_txtimeout, float serial_rxtimeout); +void serial_close(serial_t *serial); +int serial_read(serial_t *serial, uint8_t *buffer, int size); +int serial_write(serial_t *serial, uint8_t *buffer, int size); +int serial_timeout(serial_t *serial, double serial_txtimeout, double serial_rxtimeout); +int serial_cts(serial_t *serial); +int serial_dsr(serial_t *serial); +int serial_dtron(serial_t *serial); +int serial_dtroff(serial_t *serial); +int serial_rtson(serial_t *serial); +int serial_rtsoff(serial_t *serial); +int serial_break(serial_t *serial, int on); +int serial_handle(serial_t *serial); +