/****************************************************************************
* 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 <termios.h>
#include <debug.h>
#include <apps/readline.h>
#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;
}