| /**************************************************************************** |
| * apps/examples/pty_test/pty_test.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <termios.h> |
| #include <poll.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <sys/ioctl.h> |
| |
| #include <nuttx/serial/pty.h> |
| |
| #include "nshlib/nshlib.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_EXAMPLES_PTYTEST_SERIALDEV |
| # define CONFIG_EXAMPLES_PTYTEST_SERIALDEV "/dev/ttyS1" |
| #endif |
| |
| #ifndef CONFIG_EXAMPLES_PTYTEST_DAEMONPRIO |
| # define CONFIG_EXAMPLES_PTYTEST_DAEMONPRIO SCHED_PRIORITY_DEFAULT |
| #endif |
| |
| #ifndef CONFIG_EXAMPLES_PTYTEST_STACKSIZE |
| # define CONFIG_EXAMPLES_PTYTEST_STACKSIZE 2048 |
| #endif |
| |
| #define POLL_TIMEOUT 200 |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| struct term_pair_s |
| { |
| int fd_uart; |
| int fd_pty; |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: serial_in |
| * |
| * Description: |
| * Read data from serial and write to pts |
| * |
| ****************************************************************************/ |
| |
| static void serial_in(struct term_pair_s *tp) |
| { |
| #ifdef CONFIG_EXAMPLES_PTYTEST_POLL |
| struct pollfd fdp; |
| #endif |
| char buffer[16]; |
| int ret; |
| |
| if (!tp) |
| { |
| fprintf(stderr, "ERROR: terminal pair struct is NULL!\n"); |
| return; |
| } |
| |
| #ifdef CONFIG_EXAMPLES_PTYTEST_POLL |
| fdp.fd = tp->fd_uart; |
| fdp.events = POLLIN; |
| #endif |
| |
| /* Run forever */ |
| |
| while (true) |
| { |
| #ifdef CONFIG_EXAMPLES_PTYTEST_POLL |
| ret = poll((struct pollfd *)&fdp, 1, POLL_TIMEOUT); |
| if (ret > 0) |
| { |
| if ((fdp.revents & POLLIN) != 0) |
| #endif |
| { |
| ssize_t len; |
| |
| len = read(tp->fd_uart, buffer, 16); |
| if (len < 0) |
| { |
| fprintf(stderr, |
| "ERROR Failed to read from serial: %d\n", |
| errno); |
| } |
| else if (len > 0) |
| { |
| ret = write(tp->fd_pty, buffer, len); |
| if (ret < 0) |
| { |
| fprintf(stderr, |
| "Failed to write to serial: %d\n", |
| errno); |
| } |
| } |
| } |
| #ifdef CONFIG_EXAMPLES_PTYTEST_POLL |
| } |
| |
| /* Timeout */ |
| |
| else if (ret == 0) |
| { |
| continue; |
| } |
| |
| /* Poll error */ |
| |
| else if (ret < 0) |
| { |
| fprintf(stderr, "ERROR: poll failed: %d\n", errno); |
| continue; |
| } |
| #endif |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: serial_out |
| * |
| * Description: |
| * Read data from pts and write to serial |
| * |
| ****************************************************************************/ |
| |
| static void serial_out(struct term_pair_s *tp) |
| { |
| #ifdef CONFIG_EXAMPLES_PTYTEST_POLL |
| struct pollfd fdp; |
| #endif |
| char buffer[16]; |
| int ret; |
| |
| if (!tp) |
| { |
| fprintf(stderr, "Error: terminal pair struct is NULL!\n"); |
| return; |
| } |
| |
| #ifdef CONFIG_EXAMPLES_PTYTEST_POLL |
| fdp.fd = tp->fd_pty; |
| fdp.events = POLLIN; |
| #endif |
| |
| /* Run forever */ |
| |
| while (true) |
| { |
| #ifdef CONFIG_EXAMPLES_PTYTEST_POLL |
| ret = poll((struct pollfd *)&fdp, 1, POLL_TIMEOUT); |
| if (ret > 0) |
| { |
| if ((fdp.revents & POLLIN) != 0) |
| #endif |
| { |
| ssize_t len; |
| |
| len = read(tp->fd_pty, buffer, 16); |
| if (len < 0) |
| { |
| fprintf(stderr, |
| "ERROR: Failed to read from pseudo-terminal: %d\n", |
| errno); |
| } |
| else if (len > 0) |
| { |
| ret = write(tp->fd_uart, buffer, len); |
| if (ret < 0) |
| { |
| fprintf(stderr, |
| "ERROR: Failed to write to serial: %d\n", |
| errno); |
| } |
| } |
| } |
| #ifdef CONFIG_EXAMPLES_PTYTEST_POLL |
| } |
| |
| /* Timeout */ |
| |
| else if (ret == 0) |
| { |
| continue; |
| } |
| |
| /* poll error */ |
| |
| else if (ret < 0) |
| { |
| fprintf(stderr, "ERROR: poll failed: %d\n", errno); |
| continue; |
| } |
| #endif |
| } |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: pty_test_main |
| ****************************************************************************/ |
| |
| int main(int argc, FAR char *argv[]) |
| { |
| struct term_pair_s termpair; |
| struct termios tio; |
| pthread_t si_thread; |
| pthread_t so_thread; |
| char buffer[16]; |
| pid_t pid; |
| int fd_pts; |
| int ret; |
| |
| printf("Create pseudo-terminal\n"); |
| |
| /* Open pseudo-terminal master (pts factory) */ |
| |
| termpair.fd_pty = open("/dev/ptmx", O_RDWR | O_NOCTTY); |
| if (termpair.fd_pty < 0) |
| { |
| fprintf(stderr, "ERROR: Failed to opening /dev/ptmx: %d\n", |
| errno); |
| return EXIT_FAILURE; |
| } |
| |
| /* Grant access and unlock the slave PTY */ |
| |
| ret = grantpt(termpair.fd_pty); |
| if (ret < 0) |
| { |
| fprintf(stderr, "ERROR: grantpt() failed: %d\n", errno); |
| goto error_pts; |
| } |
| |
| ret = unlockpt(termpair.fd_pty); |
| if (ret < 0) |
| { |
| fprintf(stderr, "ERROR: unlockpt() failed: %d\n", errno); |
| goto error_pts; |
| } |
| |
| /* Get the create pts file-name */ |
| |
| ret = ptsname_r(termpair.fd_pty, buffer, 16); |
| if (ret < 0) |
| { |
| fprintf(stderr, "ERROR: ptsname_r() failed: %d\n", errno); |
| goto error_pts; |
| } |
| |
| /* Open the created pts */ |
| |
| fd_pts = open(buffer, O_RDWR); |
| if (fd_pts < 0) |
| { |
| fprintf(stderr, "ERROR: Failed to open %s: %d\n", buffer, errno); |
| goto error_pts; |
| } |
| |
| /* Open the second serial port to create a new console there */ |
| |
| termpair.fd_uart = open(CONFIG_EXAMPLES_PTYTEST_SERIALDEV, O_RDWR); |
| if (termpair.fd_uart < 0) |
| { |
| #ifdef CONFIG_EXAMPLES_PTYTEST_WAIT_CONNECTED |
| /* if ENOTCONN is received, re-attempt to open periodically */ |
| |
| if (errno == ENOTCONN) |
| { |
| fprintf(stderr, "ERROR: device not connected, will continue" |
| " trying\n"); |
| } |
| |
| while (termpair.fd_uart < 0 && errno == ENOTCONN) |
| { |
| sleep(1); |
| |
| termpair.fd_uart = open(CONFIG_EXAMPLES_PTYTEST_SERIALDEV, O_RDWR); |
| } |
| |
| /* if we exited due to an error different than ENOTCONN */ |
| |
| if (termpair.fd_uart < 0) |
| { |
| fprintf(stderr, "Failed to open %s: %i\n", |
| CONFIG_EXAMPLES_PTYTEST_SERIALDEV, errno); |
| goto error_serial; |
| } |
| #else |
| fprintf(stderr, "Failed to open %s: %i\n", |
| CONFIG_EXAMPLES_PTYTEST_SERIALDEV, errno); |
| goto error_serial; |
| #endif |
| } |
| |
| /* Enable \n -> \r\n conversion during write */ |
| |
| ret = tcgetattr(termpair.fd_uart, &tio); |
| if (ret) |
| { |
| fprintf(stderr, "ERROR: tcgetattr() failed: %d\n", errno); |
| goto error_serial; |
| } |
| |
| tio.c_oflag = OPOST | ONLCR; |
| ret = tcsetattr(termpair.fd_uart, TCSANOW, &tio); |
| if (ret) |
| { |
| fprintf(stderr, "ERROR: tcsetattr() failed: %d\n", errno); |
| goto error_serial; |
| } |
| |
| printf("Starting a new NSH Session using %s\n", buffer); |
| |
| /* Close default stdin, stdout and stderr */ |
| |
| close(0); |
| close(1); |
| close(2); |
| |
| /* Use this pts file as stdin, stdout, and stderr */ |
| |
| dup2(fd_pts, 0); |
| dup2(fd_pts, 1); |
| dup2(fd_pts, 2); |
| |
| /* Create a new console using this /dev/pts/N */ |
| |
| pid = task_create("NSH Console", CONFIG_EXAMPLES_PTYTEST_DAEMONPRIO, |
| CONFIG_EXAMPLES_PTYTEST_STACKSIZE, nsh_consolemain, |
| &argv[1]); |
| if (pid < 0) |
| { |
| /* Can't do output because stdout and stderr are redirected */ |
| |
| goto error_console; |
| } |
| |
| /* Start a thread to read from serial */ |
| |
| ret = pthread_create(&si_thread, NULL, |
| (pthread_startroutine_t)serial_in, |
| (pthread_addr_t)&termpair); |
| if (ret != 0) |
| { |
| /* Can't do output because stdout and stderr are redirected */ |
| |
| goto error_thread1; |
| } |
| |
| /* Start a thread to write to serial */ |
| |
| ret = pthread_create(&so_thread, NULL, |
| (pthread_startroutine_t)serial_out, |
| (pthread_addr_t)&termpair); |
| if (ret != 0) |
| { |
| /* Can't do output because stdout and stderr are redirected */ |
| |
| goto error_thread2; |
| } |
| |
| /* Stay here to keep the threads running */ |
| |
| while (true) |
| { |
| /* Nothing to do, then sleep to avoid eating all cpu time */ |
| |
| pause(); |
| } |
| |
| return EXIT_SUCCESS; |
| |
| error_thread2: |
| error_thread1: |
| error_console: |
| close(termpair.fd_uart); |
| error_serial: |
| close(fd_pts); |
| error_pts: |
| close(termpair.fd_pty); |
| |
| return EXIT_FAILURE; |
| } |