/**************************************************************************** * apps/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 "system/readline.h" #include "cu.h" #ifdef CONFIG_SYSTEM_CUTERM_DISABLE_ERROR_PRINT # define cu_error(...) #else # define cu_error(...) fprintf(stderr, __VA_ARGS__) #endif /**************************************************************************** * Private Types ****************************************************************************/ enum parity_mode { PARITY_NONE, PARITY_EVEN, PARITY_ODD, }; /**************************************************************************** * Private Data ****************************************************************************/ static struct cu_globals_s g_cu; /**************************************************************************** * Public Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: cu_listener * * Description: * Entry point for the listener thread. * ****************************************************************************/ static FAR void *cu_listener(FAR void *parameter) { FAR struct cu_globals_s *cu = (FAR struct cu_globals_s *)parameter; for (; ; ) { int rc; char ch; rc = read(cu->devfd, &ch, 1); if (rc <= 0) { break; } fputc(ch, stdout); fflush(stdout); } /* Won't get here */ return NULL; } static void sigint(int sig) { g_cu.force_exit = true; } #ifdef CONFIG_SERIAL_TERMIOS static int set_termios(FAR struct cu_globals_s *cu, int rate, enum parity_mode parity, int rtscts, int nocrlf) #else static int set_termios(FAR struct cu_globals_s *cu, int nocrlf) #endif { int rc = 0; int ret; struct termios tio; tio = cu->devtio; #ifdef CONFIG_SERIAL_TERMIOS tio.c_cflag &= ~(PARENB | PARODD | CRTSCTS); switch (parity) { case PARITY_EVEN: tio.c_cflag |= PARENB; break; case PARITY_ODD: tio.c_cflag |= PARENB | PARODD; break; case PARITY_NONE: break; } /* set baudrate */ if (rate != 0) { cfsetspeed(&tio, rate); } if (rtscts) { tio.c_cflag |= CRTS_IFLOW | CCTS_OFLOW; } #endif tio.c_oflag = OPOST; /* enable or disable \n -> \r\n conversion during write */ if (nocrlf == 0) { tio.c_oflag |= ONLCR; } ret = tcsetattr(cu->devfd, TCSANOW, &tio); if (ret) { cu_error("set_termios: ERROR during tcsetattr(): %d\n", errno); rc = -1; goto errout; } /* Let the remote machine to handle all crlf/echo except Ctrl-C */ if (cu->stdfd >= 0) { tio = cu->stdtio; tio.c_iflag = 0; tio.c_oflag = 0; tio.c_lflag &= ~ECHO; ret = tcsetattr(cu->stdfd, TCSANOW, &tio); if (ret) { cu_error("set_termios: ERROR during tcsetattr(): %d\n", errno); rc = -1; } } errout: return rc; } static void retrieve_termios(FAR struct cu_globals_s *cu) { tcsetattr(cu->devfd, TCSANOW, &cu->devtio); if (cu->stdfd >= 0) { tcsetattr(cu->stdfd, TCSANOW, &cu->stdtio); } } static void print_help(void) { printf("Usage: cu [options]\n" " -l: Use named device (default %s)\n" #ifdef CONFIG_SERIAL_TERMIOS " -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" #endif " -c: Disable lf -> crlf conversion (default: off)\n" " -f: Enable endless mode without escape sequence (default: off)\n" " -?: This help\n", CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE #ifdef CONFIG_SERIAL_TERMIOS , CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD #endif ); } 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. * ****************************************************************************/ int main(int argc, FAR char *argv[]) { pthread_attr_t attr; struct sigaction sa; FAR const char *devname = CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE; FAR struct cu_globals_s *cu = &g_cu; #ifdef CONFIG_SERIAL_TERMIOS int baudrate = CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD; enum parity_mode parity = PARITY_NONE; int rtscts = 1; #endif int nocrlf = 0; int nobreak = 0; int option; int ret; int bcmd; int start_of_line = 1; int exitval = EXIT_FAILURE; bool badarg = false; /* Initialize global data */ memset(cu, 0, sizeof(*cu)); /* Install signal handlers */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigint; sigaction(SIGINT, &sa, NULL); optind = 0; /* Global that needs to be reset in FLAT mode */ while ((option = getopt(argc, argv, "l:s:cefhor?")) != ERROR) { switch (option) { case 'l': devname = optarg; break; #ifdef CONFIG_SERIAL_TERMIOS case 's': baudrate = atoi(optarg); break; case 'e': parity = PARITY_EVEN; break; case 'o': parity = PARITY_ODD; break; case 'r': rtscts = 0; break; #endif case 'c': nocrlf = 1; break; case 'f': nobreak = 1; break; case 'h': case '?': print_help(); exitval = EXIT_SUCCESS; /* Go through */ default: badarg = true; break; } } if (badarg) { return exitval; } /* Open the serial device for reading and writing */ cu->devfd = open(devname, O_RDWR); if (cu->devfd < 0) { cu_error("cu_main: ERROR: Failed to open %s for writing: %d\n", devname, errno); goto errout_with_devinit; } /* Remember serial device termios attributes */ ret = tcgetattr(cu->devfd, &cu->devtio); if (ret) { cu_error("cu_main: ERROR during tcgetattr(): %d\n", errno); goto errout_with_devfd; } /* Remember std termios attributes if it is a tty. Try to select * right descriptor that is used to refer to tty */ if (isatty(fileno(stderr))) { cu->stdfd = fileno(stderr); } else if (isatty(fileno(stdout))) { cu->stdfd = fileno(stdout); } else if (isatty(fileno(stdin))) { cu->stdfd = fileno(stdin); } else { cu->stdfd = -1; } if (cu->stdfd >= 0) { tcgetattr(cu->stdfd, &cu->stdtio); } #ifdef CONFIG_SERIAL_TERMIOS if (set_termios(cu, baudrate, parity, rtscts, nocrlf) != 0) #else if (set_termios(cu, nocrlf) != 0) #endif { goto errout_with_devfd_retrieve; } /* Start the serial receiver thread */ ret = pthread_attr_init(&attr); if (ret != OK) { cu_error("cu_main: pthread_attr_init failed: %d\n", ret); goto errout_with_devfd_retrieve; } /* Set priority of listener to configured value */ attr.priority = CONFIG_SYSTEM_CUTERM_PRIORITY; ret = pthread_create(&cu->listener, &attr, cu_listener, cu); pthread_attr_destroy(&attr); if (ret != 0) { cu_error("cu_main: Error in thread creation: %d\n", ret); goto errout_with_devfd_retrieve; } /* Send messages and get responses -- forever */ while (!cu->force_exit) { char ch; int c = getc(stdin); if (c < 0) { continue; } ch = c; if (nobreak == 1) { write(cu->devfd, &ch, 1); continue; } 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(cu->devfd, &ch, 1); continue; } else { if (cu_cmd(bcmd) == 1) { break; } } } /* Normal character */ write(cu->devfd, &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(cu->listener); exitval = EXIT_SUCCESS; /* Error exits */ errout_with_devfd_retrieve: retrieve_termios(cu); errout_with_devfd: close(cu->devfd); errout_with_devinit: return exitval; }