/** minimal terminal using pollcat * * This file is part of the Pollcat Library. * Copyright (C) 2022 Expatria Technologies Inc. * Contact: Morgan Hughes * * The Pollcat Library is free software: you can redistribute it and/or modify it under * the terms of the the GNU Lesser 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 copies of the GNU General Public License and the GNU Lesser * General Public License along with the Pollcat Library. If not, see * https://www.gnu.org/licenses/ * * vim:ts=4:noexpandtab */ #include #include #include #include #include #include #include #include "terminal.h" static int fd; static void service (struct pollcat *cat) { char buff[4096]; int ret; int idx; if ( !(pollcat_revents(fd) & POLLIN) ) return; while ( (ret = read(fd, buff, sizeof(buff))) > 0 ) tty_write(buff, ret); } static const struct pollcat_ops ops = { .service = service, }; static struct pollcat cat = { .ops = &ops, }; static int serial_fl; static int serial_fd; static struct termios serial_tio; int serial_init (const char *node, const char *setup) { speed_t spd; char *mod; if ( (fd = open(node, O_RDWR, 0)) < 0 ) return -1; // set stdin nonblocking if ( (serial_fl = fcntl(fd, F_GETFL)) < 0 ) return -1; if ( fcntl(fd, F_SETFL, serial_fl | O_NONBLOCK) < 0 ) return -1; // set stdin nonblocking if ( (serial_fd = fcntl(fd, F_GETFD)) < 0 ) return -1; if ( fcntl(fd, F_SETFD, serial_fd | FD_CLOEXEC) < 0 ) return -1; // save old terminal modes and make raw if ( tcgetattr(fd, &serial_tio) ) return -1; struct termios tio = serial_tio; cfmakeraw(&tio); switch ( strtoul(setup, &mod, 10) ) { case 50: spd = B50; break; case 75: spd = B75; break; case 110: spd = B110; break; case 134: spd = B134; break; case 150: spd = B150; break; case 200: spd = B200; break; case 300: spd = B300; break; case 600: spd = B600; break; case 1200: spd = B1200; break; case 1800: spd = B1800; break; case 2400: spd = B2400; break; case 4800: spd = B4800; break; case 9600: spd = B9600; break; case 19200: spd = B19200; break; case 38400: spd = B38400; break; case 57600: spd = B57600; break; case 115200: spd = B115200; break; #ifdef B230400 case 230400: spd = B230400; break; #endif #ifdef B460800 case 460800: spd = B460800; break; #endif #ifdef B500000 case 500000: spd = B500000; break; #endif #ifdef B576000 case 576000: spd = B576000; break; #endif #ifdef B921600 case 921600: spd = B921600; break; #endif #ifdef B1000000 case 1000000: spd = B1000000; break; #endif #ifdef B1152000 case 1152000: spd = B1152000; break; #endif #ifdef B1500000 case 1500000: spd = B1500000; break; #endif #ifdef B2000000 case 2000000: spd = B2000000; break; #endif #ifdef B2500000 case 2500000: spd = B2500000; break; #endif #ifdef B3000000 case 3000000: spd = B3000000; break; #endif #ifdef B3500000 case 3500000: spd = B3500000; break; #endif #ifdef B4000000 case 4000000: spd = B4000000; break; #endif default: errno = EINVAL; return -1; } if ( cfsetispeed(&tio, spd) || cfsetospeed(&tio, spd) ) return -1; while ( *mod ) { switch ( toupper(*mod) ) { // data bits case '8': tio.c_cflag &= ~CSIZE; tio.c_cflag |= CS8; break; case '7': tio.c_cflag &= ~CSIZE; tio.c_cflag |= CS7; break; case '6': tio.c_cflag &= ~CSIZE; tio.c_cflag |= CS6; break; case '5': tio.c_cflag &= ~CSIZE; tio.c_cflag |= CS5; break; // stop bits case '2': tio.c_cflag |= CSTOPB; break; case '1': tio.c_cflag &= ~CSTOPB; break; // no parity case 'N': tio.c_iflag |= IGNPAR; tio.c_iflag &= ~INPCK; tio.c_cflag &= ~(PARENB | PARODD | CMSPAR); break; // odd parity case 'O': tio.c_iflag |= INPCK | PARMRK; tio.c_iflag &= ~IGNPAR; tio.c_cflag |= PARENB | PARODD; tio.c_cflag &= ~CMSPAR; break; // even parity case 'E': tio.c_iflag |= INPCK | PARMRK; tio.c_iflag &= ~IGNPAR; tio.c_cflag |= PARENB; tio.c_cflag &= ~(PARODD | CMSPAR); break; // mark parity case 'M': tio.c_iflag |= INPCK | PARMRK; tio.c_iflag &= ~IGNPAR; tio.c_cflag |= PARENB | CMSPAR | PARODD; break; // space parity case 'S': tio.c_iflag |= INPCK | PARMRK; tio.c_iflag &= ~IGNPAR; tio.c_cflag |= PARENB | CMSPAR; tio.c_cflag &= ~PARODD; break; default: errno = EINVAL; return -1; } mod++; } if ( tcsetattr(fd, TCSANOW, &tio) ) return -1; pollcat_fd_add(fd, POLLIN); pollcat_iter_add(&cat); return 0; } void serial_done (void) { pollcat_iter_remove(&cat); pollcat_fd_remove(fd); if ( fcntl(fd, F_SETFL, serial_fl) < 0 ) perror("fcntl"); if ( fcntl(fd, F_SETFD, serial_fd) < 0 ) perror("fcntl"); if ( tcsetattr(fd, TCSANOW, &serial_tio) ) perror("tcsetattr"); close(fd); } void serial_write (const void *buff, int len) { write(fd, buff, len); }