/**************************************************************************** * system/cu/cu_main.c * * Copyright (C) 2014 sysmocom - s.f.m.c. GmbH. All rights reserved. * Author: Harald Welte * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cu.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define SIGINT 2 #define SIGKILL 9 #define SIGTERM 15 enum parity_mode { PARITY_NONE, PARITY_EVEN, PARITY_ODD, }; /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Public Data ****************************************************************************/ /* terminal state data */ struct cu_globals_s g_cu; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: cu_listener * * Description: * Entry point for the listener thread. * ****************************************************************************/ static FAR void *cu_listener(FAR void *parameter) { for (;;) { int rc; char ch; rc = read(g_cu.infd, &ch, 1); if (rc <= 0) { break; } fputc(ch, stdout); fflush(stdout); } /* Won't get here */ return NULL; } static void sigint(int sig) { pthread_cancel(g_cu.listener); close(g_cu.outfd); close(g_cu.infd); exit(0); } static int enable_crlf_conversion(int fd) { #ifdef CONFIG_SERIAL_TERMIOS int rc = 0; int ret; struct termios tio; /* enable \n -> \r\n conversion during write */ ret = tcgetattr(fd, &tio); if (ret) { fprintf(stderr, "en_crlf_conv: ERROR during tcgetattr(): %d\n", errno); rc = -1; } tio.c_oflag = OPOST | ONLCR; ret = tcsetattr(fd, TCSANOW, &tio); if (ret) { fprintf(stderr, "en_crlf_conv: ERROR during tcsetattr(): %d\n", errno); rc = -1; } return rc; #else return -1; #endif } static int set_baudrate(int fd, int rate, enum parity_mode parity, int rtscts) { #ifdef CONFIG_SERIAL_TERMIOS int rc = 0; int ret; struct termios tio; /* enable \n -> \r\n conversion during write */ ret = tcgetattr(fd, &tio); if (ret) { fprintf(stderr, "set_baudrate: ERROR during tcgetattr(): %d\n", errno); rc = -1; } if (rate != 0) { cfsetspeed(&tio, rate); } switch (parity) { case PARITY_EVEN: tio.c_cflag = PARENB; break; case PARITY_ODD: tio.c_cflag = PARENB | PARODD; break; case PARITY_NONE: break; } if (rtscts) { tio.c_cflag |= CRTS_IFLOW | CCTS_OFLOW; } ret = tcsetattr(fd, TCSANOW, &tio); if (ret) { fprintf(stderr, "set_baudrate: ERROR during tcsetattr(): %d\n", errno); rc = -1; } return rc; #else if (rate == 0) { return 0; } return -1; #endif } static void print_help(void) { printf("Usage: cu [options]\n" " -l: Use named device (default %s)\n" " -e: Set even parity\n" " -o: Set odd parity\n" " -s: Use given speed (default %d)\n" " -r: Disable RTS/CTS flow control (default: on)\n" " -?: This help\n", CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE, CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD); } static void print_escape_help(void) { printf("[Escape sequences]\n" "[~. hangup]\n" ); } static int cu_cmd(char bcmd) { switch (bcmd) { case '?': print_escape_help(); break; case '.': return 1; /* FIXME: implement other commands such as send/receive file */ } return 0; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: cu_main * * Description: * Main entry point for the serial terminal example. * ****************************************************************************/ #ifdef CONFIG_BUILD_KERNEL int main(int argc, FAR char *argv[]) #else int cu_main(int argc, FAR char *argv[]) #endif { pthread_attr_t attr; struct sigaction sa; FAR char *devname = CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE; int baudrate = CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD; enum parity_mode parity = PARITY_NONE; int rtscts = 1; int option; int ret; int bcmd; int start_of_line = 1; int exitval = EXIT_FAILURE; /* Initialize global data */ memset(&g_cu, 0, sizeof(struct cu_globals_s)); /* Install signal handlers */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigint; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGKILL, &sa, NULL); while ((option = getopt(argc, argv, "l:s:eor?")) != ERROR) { switch (option) { case 'l': devname = optarg; break; case 's': baudrate = atoi(optarg); break; case 'e': parity = PARITY_ODD; break; case 'o': parity = PARITY_EVEN; break; case 'r': rtscts = 0; break; case '?': print_help(); return EXIT_SUCCESS; default: return EXIT_FAILURE; } } /* Open the serial device for writing */ g_cu.outfd = open(devname, O_WRONLY); if (g_cu.outfd < 0) { fprintf(stderr, "cu_main: ERROR: Failed to open %s for writing: %d\n", devname, errno); goto errout_with_devinit; } enable_crlf_conversion(g_cu.outfd); set_baudrate(g_cu.outfd, baudrate, parity, rtscts); /* Open the serial device for reading. Since we are already connected, this * should not fail. */ g_cu.infd = open(devname, O_RDONLY); if (g_cu.infd < 0) { fprintf(stderr, "cu_main: ERROR: Failed to open %s for reading: %d\n", devname, errno); goto errout_with_outfd; } /* Start the serial receiver thread */ ret = pthread_attr_init(&attr); if (ret != OK) { fprintf(stderr, "cu_main: pthread_attr_init failed: %d\n", ret); goto errout_with_fds; } ret = pthread_create(&g_cu.listener, &attr, cu_listener, (pthread_addr_t)0); if (ret != 0) { fprintf(stderr, "cu_main: Error in thread creation: %d\n", ret); goto errout_with_fds; } /* Send messages and get responses -- forever */ for (;;) { int ch = getc(stdin); if (ch <= 0) { break; } if (start_of_line == 1 && ch == '~') { /* We've seen and escape (~) character, echo it to local * terminal and read the next char from serial */ fputc(ch, stdout); bcmd = getc(stdin); if (bcmd == ch) { /* Escaping a tilde: handle like normal char */ write(g_cu.outfd, &ch, 1); continue; } else { if (cu_cmd(bcmd) == 1) { break; } } } /* Normal character */ write(g_cu.outfd, &ch, 1); /* Determine if we are now at the start of a new line or not */ if (ch == '\n' || ch == '\r') { start_of_line = 1; } else { start_of_line = 0; } } pthread_cancel(g_cu.listener); pthread_attr_destroy(&attr); exitval = EXIT_SUCCESS; /* Error exits */ errout_with_fds: close(g_cu.infd); errout_with_outfd: close(g_cu.outfd); errout_with_devinit: return exitval; }