* apps/system/cu/cu_main.c
*
* Copyright (C) 2014 sysmocom - s.f.m.c. GmbH. All rights reserved.
* Author: Harald Welte <hwelte@sysmocom.de>
*
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <debug.h>
#include <stdint.h>
#include <stdbool.h>
#include <semaphore.h>
#include <termios.h>
#include "system/readline.h"
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE
# define CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE "/dev/ttyS0"
#endif
#ifndef CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD
# define CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD 115200
#endif
#ifdef CONFIG_SYSTEM_CUTERM_DISABLE_ERROR_PRINT
# define cu_error(...)
#else
# define cu_error(...) dprintf(STDERR_FILENO, __VA_ARGS__)
#endif
* Private Types
****************************************************************************/
enum parity_mode
{
PARITY_NONE,
PARITY_EVEN,
PARITY_ODD,
};
* name conflicts with other global symbols -- a poor man's name space.
*/
struct cu_globals_s
{
int devfd;
int stdfd;
int escape;
struct termios devtio;
struct termios stdtio;
pthread_t listener;
bool force_exit;
};
* Private Data
****************************************************************************/
* Public Data
****************************************************************************/
* Private Functions
****************************************************************************/
#ifndef CONFIG_DISABLE_SIGNALS
static void listener_sigusr1(int sig)
{
}
#endif
* 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;
#ifndef CONFIG_DISABLE_SIGNALS
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = listener_sigusr1;
sigaction(SIGUSR1, &sa, NULL);
#endif
while (!cu->force_exit)
{
char buf[CONFIG_LINE_MAX];
int rc;
rc = read(cu->devfd, buf, sizeof(buf));
if (rc <= 0)
{
break;
}
rc = write(STDOUT_FILENO, buf, rc);
if (rc <= 0)
{
break;
}
}
return NULL;
}
#ifndef CONFIG_DISABLE_SIGNALS
static void cu_exit(int signo, FAR siginfo_t *siginfo, FAR void *context)
{
FAR struct cu_globals_s *cu = siginfo->si_user;
cu->force_exit = true;
}
#endif
#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 ret;
struct termios tio;
if (isatty(cu->devfd) && isatty(cu->stdfd))
{
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;
}
if (rate != 0)
{
cfsetspeed(&tio, rate);
}
if (rtscts)
{
tio.c_cflag |= CRTS_IFLOW | CCTS_OFLOW;
}
#endif
tio.c_oflag = OPOST;
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);
return ret;
}
tio = cu->stdtio;
tio.c_iflag = 0;
tio.c_oflag = 0;
tio.c_lflag &= ~(ECHO | ICANON);
ret = tcsetattr(cu->stdfd, TCSANOW, &tio);
if (ret)
{
cu_error("set_termios: ERROR during tcsetattr(): %d\n", errno);
return ret;
}
}
return 0;
}
static void retrieve_termios(FAR struct cu_globals_s *cu)
{
if (isatty(cu->devfd) && isatty(cu->stdfd))
{
tcsetattr(cu->devfd, TCSANOW, &cu->devtio);
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"
" -f: Disable RTS/CTS flow control (default: on)\n"
#endif
" -c: Disable lf -> crlf conversion (default: off)\n"
" -E: Set the escape character (default: ~).\n"
" To eliminate the escape character, use -E ''\n"
" -?: This help\n",
CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE
#ifdef CONFIG_SERIAL_TERMIOS
, CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD
#endif
);
}
static void print_escape_help(FAR struct cu_globals_s *cu)
{
printf("[Escape sequences]\n[%c. hangup]\n", cu->escape);
}
static int cu_cmd(FAR struct cu_globals_s *cu, char bcmd)
{
switch (bcmd)
{
case '?':
print_escape_help(cu);
break;
case '.':
return 1;
}
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;
#ifndef CONFIG_DISABLE_SIGNALS
struct sigaction sa;
#endif
struct cu_globals_s cu;
FAR const char *devname = CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE;
#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 option;
int ret;
int start_of_line = 1;
int exitval = EXIT_FAILURE;
bool badarg = false;
bool escaping = false;
memset(&cu, 0, sizeof(struct cu_globals_s));
cu.escape = '~';
#ifndef CONFIG_DISABLE_SIGNALS
memset(&sa, 0, sizeof(sa));
sa.sa_user = &cu;
sa.sa_sigaction = cu_exit;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGINT, &sa, NULL) < 0)
{
cu_error("cu_main: ERROR during setup cu_exit sigaction(): %d\n",
errno);
return EXIT_FAILURE;
}
#endif
optind = 0;
while ((option = getopt(argc, argv, "l:s:ceE:fho?")) != 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 'f':
rtscts = 0;
break;
#endif
case 'c':
nocrlf = 1;
break;
case 'E':
cu.escape = atoi(optarg);
break;
case 'h':
case '?':
print_help();
exitval = EXIT_SUCCESS;
default:
badarg = true;
break;
}
}
if (badarg)
{
return exitval;
}
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;
}
if (isatty(cu.devfd))
{
ret = tcgetattr(cu.devfd, &cu.devtio);
if (ret)
{
cu_error("cu_main: ERROR during tcgetattr(): %d\n", errno);
goto errout_with_devfd;
}
}
* right descriptor that is used to refer to tty
*/
if (isatty(STDERR_FILENO))
{
cu.stdfd = STDERR_FILENO;
}
else if (isatty(STDOUT_FILENO))
{
cu.stdfd = STDOUT_FILENO;
}
else if (isatty(STDIN_FILENO))
{
cu.stdfd = STDIN_FILENO;
}
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;
}
ret = pthread_attr_init(&attr);
if (ret != OK)
{
cu_error("cu_main: pthread_attr_init failed: %d\n", ret);
goto errout_with_devfd_retrieve;
}
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;
}
while (!cu.force_exit)
{
char buf[CONFIG_LINE_MAX];
ssize_t nwrite = 0;
ssize_t nread;
ssize_t i;
nread = read(STDIN_FILENO, buf, sizeof(buf));
if (nread <= 0)
{
continue;
}
for (i = 0; i < nread; i++)
{
char ch = buf[i];
if (escaping)
{
* now process the command character
*/
escaping = false;
if (cu_cmd(&cu, ch) == 1)
{
cu.force_exit = true;
nread = i;
break;
}
}
if (start_of_line == 1 && ch == cu.escape)
{
if (i > nwrite)
{
write(cu.devfd, &buf[nwrite], i - nwrite);
}
nwrite = i + 1;
* terminal and read the next char from serial
*/
write(STDOUT_FILENO, &ch, 1);
start_of_line = 0;
escaping = true;
continue;
}
if (ch == '\n' || ch == '\r')
{
start_of_line = 1;
}
else
{
start_of_line = 0;
}
}
if (nread > nwrite)
{
write(cu.devfd, &buf[nwrite], nread - nwrite);
}
}
pthread_kill(cu.listener, SIGUSR1);
pthread_join(cu.listener, NULL);
exitval = EXIT_SUCCESS;
errout_with_devfd_retrieve:
retrieve_termios(&cu);
errout_with_devfd:
close(cu.devfd);
errout_with_devinit:
return exitval;
}