]> granicus.if.org Git - esp-idf/commitdiff
Add basic support for termios.h
authorRoland Dobai <dobai.roland@gmail.com>
Tue, 14 Aug 2018 11:39:30 +0000 (13:39 +0200)
committerRoland Dobai <dobai.roland@gmail.com>
Thu, 30 Aug 2018 12:29:20 +0000 (14:29 +0200)
Closes https://github.com/espressif/esp-idf/issues/2063

components/newlib/platform_include/sys/termios.h [new file with mode: 0644]
components/newlib/termios.c [new file with mode: 0644]
components/vfs/Kconfig
components/vfs/include/esp_vfs.h
components/vfs/test/test_vfs_uart.c
components/vfs/vfs.c
components/vfs/vfs_uart.c

diff --git a/components/newlib/platform_include/sys/termios.h b/components/newlib/platform_include/sys/termios.h
new file mode 100644 (file)
index 0000000..fd0eb5c
--- /dev/null
@@ -0,0 +1,296 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed 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.
+//
+// This header file is based on the termios header of
+// "The Single UNIX (r) Specification, Version 2, Copyright (c)  1997 The Open Group".
+
+#ifndef __ESP_SYS_TERMIOS_H__
+#define __ESP_SYS_TERMIOS_H__
+
+// ESP-IDF NOTE: This header provides only a compatibility layer for macros and functions defined in sys/termios.h.
+// Not everything has a defined meaning for ESP-IDF (e.g. process leader IDs) and therefore are likely to be stubbed
+// in actual implementations.
+
+
+#include <stdint.h>
+#include <sys/types.h>
+#include "sdkconfig.h"
+
+#ifdef CONFIG_SUPPORT_TERMIOS
+
+// subscripts for the array c_cc:
+#define VEOF                        0           /** EOF character */
+#define VEOL                        1           /** EOL character */
+#define VERASE                      2           /** ERASE character */
+#define VINTR                       3           /** INTR character */
+#define VKILL                       4           /** KILL character */
+#define VMIN                        5           /** MIN value */
+#define VQUIT                       6           /** QUIT character */
+#define VSTART                      7           /** START character */
+#define VSTOP                       8           /** STOP character */
+#define VSUSP                       9           /** SUSP character */
+#define VTIME                      10           /** TIME value */
+#define NCCS                       (VTIME + 1)  /** Size of the array c_cc for control characters */
+
+// input modes for use as flags in the c_iflag field
+#define BRKINT      (1u << 0)       /** Signal interrupt on break. */
+#define ICRNL       (1u << 1)       /** Map CR to NL on input. */
+#define IGNBRK      (1u << 2)       /** Ignore break condition. */
+#define IGNCR       (1u << 3)       /** Ignore CR. */
+#define IGNPAR      (1u << 4)       /** Ignore characters with parity errors. */
+#define INLCR       (1u << 5)       /** Map NL to CR on input. */
+#define INPCK       (1u << 6)       /** Enable input parity check. */
+#define ISTRIP      (1u << 7)       /** Strip character. */
+#define IUCLC       (1u << 8)       /** Map upper-case to lower-case on input (LEGACY). */
+#define IXANY       (1u << 9)       /** Enable any character to restart output. */
+#define IXOFF       (1u << 10)      /** Enable start/stop input control. */
+#define IXON        (1u << 11)      /** Enable start/stop output control. */
+#define PARMRK      (1u << 12)      /** Mark parity errors. */
+
+// output Modes for use as flags in the c_oflag field
+#define OPOST       (1u <<  0)       /** Post-process output */
+#define OLCUC       (1u <<  1)       /** Map lower-case to upper-case on output (LEGACY). */
+#define ONLCR       (1u <<  2)       /** Map NL to CR-NL on output. */
+#define OCRNL       (1u <<  3)       /** Map CR to NL on output. */
+#define ONOCR       (1u <<  4)       /** No CR output at column 0. */
+#define ONLRET      (1u <<  5)       /** NL performs CR function. */
+#define OFILL       (1u <<  6)       /** Use fill characters for delay. */
+#define NLDLY       (1u <<  7)       /** Select newline delays: */
+#define   NL0       (0u <<  7)       /** Newline character type 0. */
+#define   NL1       (1u <<  7)       /** Newline character type 1. */
+#define CRDLY       (3u <<  8)       /** Select carriage-return delays: */
+#define   CR0       (0u <<  8)       /** Carriage-return delay type 0. */
+#define   CR1       (1u <<  8)       /** Carriage-return delay type 1. */
+#define   CR2       (2u <<  8)       /** Carriage-return delay type 2. */
+#define   CR3       (3u <<  8)       /** Carriage-return delay type 3. */
+#define TABDLY      (3u << 10)       /** Select horizontal-tab delays: */
+#define   TAB0      (0u << 10)       /** Horizontal-tab delay type 0. */
+#define   TAB1      (1u << 10)       /** Horizontal-tab delay type 1. */
+#define   TAB2      (2u << 10)       /** Horizontal-tab delay type 2. */
+#define   TAB3      (3u << 10)       /** Expand tabs to spaces. */
+#define BSDLY       (1u << 12)       /** Select backspace delays: */
+#define   BS0       (0u << 12)       /** Backspace-delay type 0. */
+#define   BS1       (1u << 12)       /** Backspace-delay type 1. */
+#define VTDLY       (1u << 13)       /** Select vertical-tab delays: */
+#define   VT0       (0u << 13)       /** Vertical-tab delay type 0. */
+#define   VT1       (1u << 13)       /** Vertical-tab delay type 1. */
+#define FFDLY       (1u << 14)       /** Select form-feed delays: */
+#define   FF0       (0u << 14)       /** Form-feed delay type 0. */
+#define   FF1       (1u << 14)       /** Form-feed delay type 1. */
+
+// Baud Rate Selection. Valid values for objects of type speed_t:
+// CBAUD range B0 - B38400
+#define B0          0   /** Hang up */
+#define B50         1
+#define B75         2
+#define B110        3
+#define B134        4
+#define B150        5
+#define B200        6
+#define B300        7
+#define B600        8
+#define B1200       9
+#define B1800      10
+#define B2400      11
+#define B4800      12
+#define B9600      13
+#define B19200     14
+#define B38400     15
+// CBAUDEX range B57600 - B4000000
+#define B57600     16
+#define B115200    17
+#define B230400    18
+#define B460800    19
+#define B500000    20
+#define B576000    21
+#define B921600    22
+#define B1000000   23
+#define B1152000   24
+#define B1500000   25
+#define B2000000   26
+#define B2500000   27
+#define B3000000   28
+#define B3500000   29
+#define B4000000   30
+
+// Control Modes for the c_cflag field:
+#define CSIZE      (3u << 0)        /* Character size: */
+#define   CS5      (0u << 0)        /** 5 bits. */
+#define   CS6      (1u << 0)        /** 6 bits. */
+#define   CS7      (2u << 0)        /** 7 bits. */
+#define   CS8      (3u << 0)        /** 8 bits. */
+#define CSTOPB     (1u << 2)        /** Send two stop bits, else one. */
+#define CREAD      (1u << 3)        /** Enable receiver. */
+#define PARENB     (1u << 4)        /** Parity enable. */
+#define PARODD     (1u << 5)        /** Odd parity, else even. */
+#define HUPCL      (1u << 6)        /** Hang up on last close. */
+#define CLOCAL     (1u << 7)        /** Ignore modem status lines. */
+#define CBAUD      (1u << 8)        /** Use baud rates defined by B0-B38400 macros. */
+#define CBAUDEX    (1u << 9)        /** Use baud rates defined by B57600-B4000000 macros. */
+#define BOTHER     (1u << 10)       /** Use custom baud rates */
+
+// Local Modes for c_lflag field:
+#define ECHO       (1u << 0)        /** Enable echo. */
+#define ECHOE      (1u << 1)        /** Echo erase character as error-correcting backspace. */
+#define ECHOK      (1u << 2)        /** Echo KILL. */
+#define ECHONL     (1u << 3)        /** Echo NL. */
+#define ICANON     (1u << 4)        /** Canonical input (erase and kill processing). */
+#define IEXTEN     (1u << 5)        /** Enable extended input character processing. */
+#define ISIG       (1u << 6)        /** Enable signals. */
+#define NOFLSH     (1u << 7)        /** Disable flush after interrupt or quit. */
+#define TOSTOP     (1u << 8)        /** Send SIGTTOU for background output. */
+#define XCASE      (1u << 9)        /** Canonical upper/lower presentation (LEGACY). */
+
+// Attribute Selection constants for use with tcsetattr():
+#define TCSANOW    0                /** Change attributes immediately. */
+#define TCSADRAIN  1                /** Change attributes when output has drained. */
+#define TCSAFLUSH  2                /** Change attributes when output has drained; also flush pending input. */
+
+// Line Control constants for use with tcflush():
+#define TCIFLUSH    0               /** Flush pending input. Flush untransmitted output. */
+#define TCIOFLUSH   1               /** Flush both pending input and untransmitted output. */
+#define TCOFLUSH    2               /** Flush untransmitted output. */
+
+// constants for use with tcflow():
+#define TCIOFF      0               /** Transmit a STOP character, intended to suspend input data. */
+#define TCION       1               /** Transmit a START character, intended to restart input data. */
+#define TCOOFF      2               /** Suspend output. */
+#define TCOON       3               /** Restart output. */
+
+typedef uint8_t cc_t;
+typedef uint32_t speed_t;
+typedef uint16_t tcflag_t;
+
+struct termios
+{
+    tcflag_t c_iflag;    /** Input modes */
+    tcflag_t c_oflag;    /** Output modes */
+    tcflag_t c_cflag;    /** Control modes */
+    tcflag_t c_lflag;    /** Local modes */
+    cc_t     c_cc[NCCS]; /** Control characters */
+    speed_t  c_ispeed;   /** input baud rate */
+    speed_t  c_ospeed;   /** output baud rate */
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Extracts the input baud rate from the input structure exactly (without interpretation).
+ *
+ * @param p input termios structure
+ * @return input baud rate
+ */
+speed_t cfgetispeed(const struct termios *p);
+
+/**
+ * @brief Extracts the output baud rate from the input structure exactly (without interpretation).
+ *
+ * @param p input termios structure
+ * @return output baud rate
+ */
+speed_t cfgetospeed(const struct termios *p);
+
+/**
+ * @brief Set input baud rate in the termios structure
+ *
+ * There is no effect in hardware until a subsequent call of tcsetattr().
+ *
+ * @param p input termios structure
+ * @param sp input baud rate
+ * @return 0 when successful, -1 otherwise with errno set
+ */
+int cfsetispeed(struct termios *p, speed_t sp);
+
+/**
+ * @brief Set output baud rate in the termios structure
+ *
+ * There is no effect in hardware until a subsequent call of tcsetattr().
+ *
+ * @param p input termios structure
+ * @param sp output baud rate
+ * @return 0 when successful, -1 otherwise with errno set
+ */
+int cfsetospeed(struct termios *p, speed_t sp);
+
+/**
+ * @brief Wait for transmission of output
+ *
+ * @param fd file descriptor of the terminal
+ * @return 0 when successful, -1 otherwise with errno set
+ */
+int tcdrain(int fd);
+
+/**
+ * @brief Suspend or restart the transmission or reception of data
+ *
+ * @param fd file descriptor of the terminal
+ * @param action selects actions to do
+ * @return 0 when successful, -1 otherwise with errno set
+ */
+int tcflow(int fd, int action);
+
+/**
+ * @brief Flush non-transmitted output data and non-read input data
+ *
+ * @param fd file descriptor of the terminal
+ * @param select selects what should be flushed
+ * @return 0 when successful, -1 otherwise with errno set
+ */
+int tcflush(int fd, int select);
+
+/**
+ * @brief Gets the parameters of the terminal
+ *
+ * @param fd file descriptor of the terminal
+ * @param p output termios structure
+ * @return 0 when successful, -1 otherwise with errno set
+ */
+int tcgetattr(int fd, struct termios *p);
+
+/**
+ * @brief Get process group ID for session leader for controlling terminal
+ *
+ * @param fd file descriptor of the terminal
+ * @return process group ID when successful, -1 otherwise with errno set
+ */
+pid_t tcgetsid(int fd);
+
+/**
+ * @brief Send a break for a specific duration
+ *
+ * @param fd file descriptor of the terminal
+ * @param duration duration of break
+ * @return 0 when successful, -1 otherwise with errno set
+ */
+int tcsendbreak(int fd, int duration);
+
+/**
+ * @brief Sets the parameters of the terminal
+ *
+ * @param fd file descriptor of the terminal
+ * @param optional_actions optional actions
+ * @param p input termios structure
+ * @return 0 when successful, -1 otherwise with errno set
+ */
+int tcsetattr(int fd, int optional_actions, const struct termios *p);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // CONFIG_SUPPORT_TERMIOS
+
+#endif //__ESP_SYS_TERMIOS_H__
diff --git a/components/newlib/termios.c b/components/newlib/termios.c
new file mode 100644 (file)
index 0000000..bccd5bf
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed 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.
+
+#include "sdkconfig.h"
+
+#ifdef CONFIG_SUPPORT_TERMIOS
+
+#include <sys/termios.h>
+#include <sys/errno.h>
+
+speed_t cfgetispeed(const struct termios *p)
+{
+    return p ? p->c_ispeed : B0;
+}
+
+speed_t cfgetospeed(const struct termios *p)
+{
+    return p ? p->c_ospeed : B0;
+}
+
+int cfsetispeed(struct termios *p, speed_t sp)
+{
+    if (p) {
+        p->c_ispeed = sp;
+        return 0;
+    } else {
+        errno = EINVAL;
+        return -1;
+    }
+}
+
+int cfsetospeed(struct termios *p, speed_t sp)
+{
+    if (p) {
+        p->c_ospeed = sp;
+        return 0;
+    } else {
+        errno = EINVAL;
+        return -1;
+    }
+}
+
+#endif // CONFIG_SUPPORT_TERMIOS
index d3d4ae9ad2b083791612e1a6b41794d5bf5c240b..19a565b2bcb7cf6bf8b6513f24a97419aa7522eb 100644 (file)
@@ -9,4 +9,10 @@ config SUPPRESS_SELECT_DEBUG_OUTPUT
         It is possible to suppress these debug outputs by enabling this
         option.
 
+config SUPPORT_TERMIOS
+    bool "Add support for termios.h"
+    default y
+    help
+        Disabling this option can save memory when the support for termios.h is not required.
+
 endmenu
index 4d847274b4732097ebfb04e144e4b2dabdd26c06..d7467d227f54b922ec66f610028c2970258f1a18 100644 (file)
 #include <sys/reent.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/termios.h>
 #include <dirent.h>
 #include <string.h>
+#include "sdkconfig.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -178,6 +180,37 @@ typedef struct
         int (*truncate_p)(void* ctx, const char *path, off_t length);
         int (*truncate)(const char *path, off_t length);
     };
+#ifdef CONFIG_SUPPORT_TERMIOS
+    union {
+        int (*tcsetattr_p)(void *ctx, int fd, int optional_actions, const struct termios *p);
+        int (*tcsetattr)(int fd, int optional_actions, const struct termios *p);
+    };
+    union {
+        int (*tcgetattr_p)(void *ctx, int fd, struct termios *p);
+        int (*tcgetattr)(int fd, struct termios *p);
+    };
+    union {
+        int (*tcdrain_p)(void *ctx, int fd);
+        int (*tcdrain)(int fd);
+    };
+    union {
+        int (*tcflush_p)(void *ctx, int fd, int select);
+        int (*tcflush)(int fd, int select);
+    };
+    union {
+        int (*tcflow_p)(void *ctx, int fd, int action);
+        int (*tcflow)(int fd, int action);
+    };
+    union {
+        pid_t (*tcgetsid_p)(void *ctx, int fd);
+        pid_t (*tcgetsid)(int fd);
+    };
+    union {
+        int (*tcsendbreak_p)(void *ctx, int fd, int duration);
+        int (*tcsendbreak)(int fd, int duration);
+    };
+#endif // CONFIG_SUPPORT_TERMIOS
+
     /** start_select is called for setting up synchronous I/O multiplexing of the desired file descriptors in the given VFS */
     esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, SemaphoreHandle_t *signal_sem);
     /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */
index 20684cc2b425e321a714e5581605dd50d6a5c11c..2e45d76bf495b01cdf8027b3edb8127c6078dec5 100644 (file)
@@ -15,6 +15,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <fcntl.h>
+#include <sys/termios.h>
+#include <sys/errno.h>
 #include "unity.h"
 #include "rom/uart.h"
 #include "soc/uart_struct.h"
@@ -23,6 +26,7 @@
 #include "freertos/semphr.h"
 #include "driver/uart.h"
 #include "esp_vfs_dev.h"
+#include "esp_vfs.h"
 #include "sdkconfig.h"
 
 static void fwrite_str_loopback(const char* str, size_t size)
@@ -198,3 +202,131 @@ TEST_CASE("can write to UART while another task is reading", "[vfs]")
     vSemaphoreDelete(read_arg.done);
     vSemaphoreDelete(write_arg.done);
 }
+
+#ifdef CONFIG_SUPPORT_TERMIOS
+TEST_CASE("Can use termios for UART", "[vfs]")
+{
+    uart_config_t uart_config = {
+        .baud_rate = 115200,
+        .data_bits = UART_DATA_8_BITS,
+        .parity    = UART_PARITY_DISABLE,
+        .stop_bits = UART_STOP_BITS_1,
+        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
+    };
+    uart_param_config(UART_NUM_1, &uart_config);
+    uart_driver_install(UART_NUM_1, 256, 256, 0, NULL, 0);
+
+    const int uart_fd = open("/dev/uart/1", O_RDWR);
+    TEST_ASSERT_NOT_EQUAL_MESSAGE(uart_fd, -1, "Cannot open UART");
+    esp_vfs_dev_uart_use_driver(1);
+
+    TEST_ASSERT_EQUAL(-1, tcgetattr(uart_fd, NULL));
+    TEST_ASSERT_EQUAL(EINVAL, errno);
+
+    struct termios tios, tios_result;
+
+    TEST_ASSERT_EQUAL(-1, tcgetattr(-1, &tios));
+    TEST_ASSERT_EQUAL(EBADF, errno);
+
+    TEST_ASSERT_EQUAL(0, tcgetattr(uart_fd, &tios));
+
+    TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSADRAIN, &tios));
+    TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSAFLUSH, &tios));
+
+    tios.c_iflag |= IGNCR;
+    TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSANOW, &tios));
+    tios.c_iflag &= (~IGNCR);
+    TEST_ASSERT_EQUAL(0, tcgetattr(uart_fd, &tios_result));
+    TEST_ASSERT_EQUAL(IGNCR, tios_result.c_iflag & IGNCR);
+    memset(&tios_result, 0xFF, sizeof(struct termios));
+
+    tios.c_iflag |= ICRNL;
+    TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSANOW, &tios));
+    tios.c_iflag &= (~ICRNL);
+    TEST_ASSERT_EQUAL(0, tcgetattr(uart_fd, &tios_result));
+    TEST_ASSERT_EQUAL(ICRNL, tios_result.c_iflag & ICRNL);
+    memset(&tios_result, 0xFF, sizeof(struct termios));
+
+    {
+        uart_word_length_t data_bit;
+        uart_stop_bits_t stop_bits;
+        uart_parity_t parity_mode;
+
+        tios.c_cflag &= (~CSIZE);
+        tios.c_cflag &= (~CSTOPB);
+        tios.c_cflag &= (~PARENB);
+        tios.c_cflag |= CS6;
+        TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSANOW, &tios));
+        tios.c_cflag &= (~CSIZE);
+        TEST_ASSERT_EQUAL(0, tcgetattr(uart_fd, &tios_result));
+        TEST_ASSERT_EQUAL(CS6, tios_result.c_cflag & CS6);
+        TEST_ASSERT_EQUAL(ESP_OK, uart_get_word_length(UART_NUM_1, &data_bit));
+        TEST_ASSERT_EQUAL(UART_DATA_6_BITS, data_bit);
+        TEST_ASSERT_EQUAL(0, tios_result.c_cflag & CSTOPB);
+        TEST_ASSERT_EQUAL(ESP_OK, uart_get_stop_bits(UART_NUM_1, &stop_bits));
+        TEST_ASSERT_EQUAL(UART_STOP_BITS_1, stop_bits);
+        TEST_ASSERT_EQUAL(ESP_OK, uart_get_parity(UART_NUM_1, &parity_mode));
+        TEST_ASSERT_EQUAL(UART_PARITY_DISABLE, parity_mode);
+        memset(&tios_result, 0xFF, sizeof(struct termios));
+    }
+
+    {
+        uart_stop_bits_t stop_bits;
+        uart_parity_t parity_mode;
+
+        tios.c_cflag |= CSTOPB;
+        tios.c_cflag |= (PARENB | PARODD);
+        TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSANOW, &tios));
+        tios.c_cflag &= (~(CSTOPB | PARENB | PARODD));
+        TEST_ASSERT_EQUAL(0, tcgetattr(uart_fd, &tios_result));
+        TEST_ASSERT_EQUAL(CSTOPB, tios_result.c_cflag & CSTOPB);
+        TEST_ASSERT_EQUAL(ESP_OK, uart_get_stop_bits(UART_NUM_1, &stop_bits));
+        TEST_ASSERT_EQUAL(UART_STOP_BITS_2, stop_bits);
+        TEST_ASSERT_EQUAL(ESP_OK, uart_get_parity(UART_NUM_1, &parity_mode));
+        TEST_ASSERT_EQUAL(UART_PARITY_ODD, parity_mode);
+        memset(&tios_result, 0xFF, sizeof(struct termios));
+    }
+
+    {
+        uint32_t baudrate;
+
+        tios.c_cflag &= (~BOTHER);
+        tios.c_cflag |= CBAUD;
+        tios.c_ispeed = tios.c_ospeed = B38400;
+        TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSANOW, &tios));
+        TEST_ASSERT_EQUAL(0, tcgetattr(uart_fd, &tios_result));
+        TEST_ASSERT_EQUAL(CBAUD, tios_result.c_cflag & CBAUD);
+        TEST_ASSERT_EQUAL(B38400, tios_result.c_ispeed);
+        TEST_ASSERT_EQUAL(B38400, tios_result.c_ospeed);
+        TEST_ASSERT_EQUAL(ESP_OK, uart_get_baudrate(UART_NUM_1, &baudrate));
+        TEST_ASSERT_EQUAL(38400, baudrate);
+
+        tios.c_cflag |= CBAUDEX;
+        tios.c_ispeed = tios.c_ospeed = B230400;
+        TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSANOW, &tios));
+        TEST_ASSERT_EQUAL(0, tcgetattr(uart_fd, &tios_result));
+        TEST_ASSERT_EQUAL(BOTHER, tios_result.c_cflag & BOTHER);
+        // Setting the speed to 230400 will set it actually to 230423
+        TEST_ASSERT_EQUAL(230423, tios_result.c_ispeed);
+        TEST_ASSERT_EQUAL(230423, tios_result.c_ospeed);
+        TEST_ASSERT_EQUAL(ESP_OK, uart_get_baudrate(UART_NUM_1, &baudrate));
+        TEST_ASSERT_EQUAL(230423, baudrate);
+
+        tios.c_cflag |= BOTHER;
+        tios.c_ispeed = tios.c_ospeed = 213;
+        TEST_ASSERT_EQUAL(0, tcsetattr(uart_fd, TCSANOW, &tios));
+        TEST_ASSERT_EQUAL(0, tcgetattr(uart_fd, &tios_result));
+        TEST_ASSERT_EQUAL(BOTHER, tios_result.c_cflag & BOTHER);
+        TEST_ASSERT_EQUAL(213, tios_result.c_ispeed);
+        TEST_ASSERT_EQUAL(213, tios_result.c_ospeed);
+        TEST_ASSERT_EQUAL(ESP_OK, uart_get_baudrate(UART_NUM_1, &baudrate));
+        TEST_ASSERT_EQUAL(213, baudrate);
+
+        memset(&tios_result, 0xFF, sizeof(struct termios));
+    }
+
+    esp_vfs_dev_uart_use_nonblocking(1);
+    close(uart_fd);
+    uart_driver_delete(UART_NUM_1);
+}
+#endif // CONFIG_SUPPORT_TERMIOS
index f0a195992d08118a7df0996040062b7dc80af248..e8b99eff2ccbccac421cd2551717cb1d2f454bb1 100644 (file)
@@ -994,3 +994,103 @@ void esp_vfs_select_triggered_isr(SemaphoreHandle_t *signal_sem, BaseType_t *wok
         }
     }
 }
+
+#ifdef CONFIG_SUPPORT_TERMIOS
+int tcgetattr(int fd, struct termios *p)
+{
+    const vfs_entry_t* vfs = get_vfs_for_fd(fd);
+    const int local_fd = get_local_fd(vfs, fd);
+    struct _reent* r = __getreent();
+    if (vfs == NULL || local_fd < 0) {
+        __errno_r(r) = EBADF;
+        return -1;
+    }
+    int ret;
+    CHECK_AND_CALL(ret, r, vfs, tcgetattr, local_fd, p);
+    return ret;
+}
+
+int tcsetattr(int fd, int optional_actions, const struct termios *p)
+{
+    const vfs_entry_t* vfs = get_vfs_for_fd(fd);
+    const int local_fd = get_local_fd(vfs, fd);
+    struct _reent* r = __getreent();
+    if (vfs == NULL || local_fd < 0) {
+        __errno_r(r) = EBADF;
+        return -1;
+    }
+    int ret;
+    CHECK_AND_CALL(ret, r, vfs, tcsetattr, local_fd, optional_actions, p);
+    return ret;
+}
+
+int tcdrain(int fd)
+{
+    const vfs_entry_t* vfs = get_vfs_for_fd(fd);
+    const int local_fd = get_local_fd(vfs, fd);
+    struct _reent* r = __getreent();
+    if (vfs == NULL || local_fd < 0) {
+        __errno_r(r) = EBADF;
+        return -1;
+    }
+    int ret;
+    CHECK_AND_CALL(ret, r, vfs, tcdrain, local_fd);
+    return ret;
+}
+
+int tcflush(int fd, int select)
+{
+    const vfs_entry_t* vfs = get_vfs_for_fd(fd);
+    const int local_fd = get_local_fd(vfs, fd);
+    struct _reent* r = __getreent();
+    if (vfs == NULL || local_fd < 0) {
+        __errno_r(r) = EBADF;
+        return -1;
+    }
+    int ret;
+    CHECK_AND_CALL(ret, r, vfs, tcflush, local_fd, select);
+    return ret;
+}
+
+int tcflow(int fd, int action)
+{
+    const vfs_entry_t* vfs = get_vfs_for_fd(fd);
+    const int local_fd = get_local_fd(vfs, fd);
+    struct _reent* r = __getreent();
+    if (vfs == NULL || local_fd < 0) {
+        __errno_r(r) = EBADF;
+        return -1;
+    }
+    int ret;
+    CHECK_AND_CALL(ret, r, vfs, tcflow, local_fd, action);
+    return ret;
+}
+
+pid_t tcgetsid(int fd)
+{
+    const vfs_entry_t* vfs = get_vfs_for_fd(fd);
+    const int local_fd = get_local_fd(vfs, fd);
+    struct _reent* r = __getreent();
+    if (vfs == NULL || local_fd < 0) {
+        __errno_r(r) = EBADF;
+        return -1;
+    }
+    int ret;
+    CHECK_AND_CALL(ret, r, vfs, tcgetsid, local_fd);
+    return ret;
+}
+
+int tcsendbreak(int fd, int duration)
+{
+    const vfs_entry_t* vfs = get_vfs_for_fd(fd);
+    const int local_fd = get_local_fd(vfs, fd);
+    struct _reent* r = __getreent();
+    if (vfs == NULL || local_fd < 0) {
+        __errno_r(r) = EBADF;
+        return -1;
+    }
+    int ret;
+    CHECK_AND_CALL(ret, r, vfs, tcsendbreak, local_fd, duration);
+    return ret;
+}
+#endif // CONFIG_SUPPORT_TERMIOS
index d53de7597687ae3180ba6b0c503334fd3510184c..e407de4757defc5c8bdf1aab2ebafe864db04934 100644 (file)
@@ -80,14 +80,15 @@ static esp_line_endings_t s_tx_mode =
 #endif
 
 // Newline conversion mode when receiving
-static esp_line_endings_t s_rx_mode =
+static esp_line_endings_t s_rx_mode[UART_NUM] = { [0 ... UART_NUM-1] =
 #if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF
-        ESP_LINE_ENDINGS_CRLF;
+        ESP_LINE_ENDINGS_CRLF
 #elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
-        ESP_LINE_ENDINGS_CR;
+        ESP_LINE_ENDINGS_CR
 #else
-        ESP_LINE_ENDINGS_LF;
+        ESP_LINE_ENDINGS_LF
 #endif
+};
 
 static void uart_end_select();
 
@@ -213,9 +214,9 @@ static ssize_t uart_read(int fd, void* data, size_t size)
     while (received < size) {
         int c = uart_read_char(fd);
         if (c == '\r') {
-            if (s_rx_mode == ESP_LINE_ENDINGS_CR) {
+            if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CR) {
                 c = '\n';
-            } else if (s_rx_mode == ESP_LINE_ENDINGS_CRLF) {
+            } else if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CRLF) {
                 /* look ahead */
                 int c2 = uart_read_char(fd);
                 if (c2 == NONE) {
@@ -420,6 +421,456 @@ static void uart_end_select()
     _lock_release(&s_one_select_lock);
 }
 
+#ifdef CONFIG_SUPPORT_TERMIOS
+static int uart_tcsetattr(int fd, int optional_actions, const struct termios *p)
+{
+    if (fd < 0 || fd >= UART_NUM) {
+        errno = EBADF;
+        return -1;
+    }
+
+    if (p == NULL) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    switch (optional_actions) {
+        case TCSANOW:
+            // nothing to do
+            break;
+        case TCSADRAIN:
+            if (uart_wait_tx_done(fd, portMAX_DELAY) != ESP_OK) {
+                errno = EINVAL;
+                return -1;
+            }
+            // intentional fall-through to the next case
+        case TCSAFLUSH:
+            if (uart_flush_input(fd) != ESP_OK) {
+                errno = EINVAL;
+                return -1;
+            }
+            break;
+        default:
+            errno = EINVAL;
+            return -1;
+    }
+
+    if (p->c_iflag & IGNCR) {
+        s_rx_mode[fd] = ESP_LINE_ENDINGS_CRLF;
+    } else if (p->c_iflag & ICRNL) {
+        s_rx_mode[fd] = ESP_LINE_ENDINGS_CR;
+    } else {
+        s_rx_mode[fd] = ESP_LINE_ENDINGS_LF;
+    }
+
+    // output line endings are not supported because there is no alternative in termios for converting LF to CR
+
+    {
+        uart_word_length_t data_bits;
+        const tcflag_t csize_bits = p->c_cflag & CSIZE;
+
+        switch (csize_bits) {
+            case CS5:
+                data_bits = UART_DATA_5_BITS;
+                break;
+            case CS6:
+                data_bits = UART_DATA_6_BITS;
+                break;
+            case CS7:
+                data_bits = UART_DATA_7_BITS;
+                break;
+            case CS8:
+                data_bits = UART_DATA_8_BITS;
+                break;
+            default:
+                errno = EINVAL;
+                return -1;
+        }
+
+        if (uart_set_word_length(fd, data_bits) != ESP_OK) {
+            errno = EINVAL;
+            return -1;
+        }
+    }
+
+    if (uart_set_stop_bits(fd, (p->c_cflag & CSTOPB) ? UART_STOP_BITS_2 : UART_STOP_BITS_1) != ESP_OK) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if (uart_set_parity(fd, (p->c_cflag & PARENB) ?
+                ((p->c_cflag & PARODD) ? UART_PARITY_ODD : UART_PARITY_EVEN)
+                :
+                UART_PARITY_DISABLE) != ESP_OK) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if (p->c_cflag & (CBAUD | CBAUDEX)) {
+        if (p->c_ispeed != p->c_ospeed) {
+            errno = EINVAL;
+            return -1;
+        } else {
+            uint32_t b;
+            if (p->c_cflag & BOTHER) {
+                b = p->c_ispeed;
+            } else {
+                switch (p->c_ispeed) {
+                    case B0:
+                        b = 0;
+                        break;
+                    case B50:
+                        b = 50;
+                        break;
+                    case B75:
+                        b = 75;
+                        break;
+                    case B110:
+                        b = 110;
+                        break;
+                    case B134:
+                        b = 134;
+                        break;
+                    case B150:
+                        b = 150;
+                        break;
+                    case B200:
+                        b = 200;
+                        break;
+                    case B300:
+                        b = 300;
+                        break;
+                    case B600:
+                        b = 600;
+                        break;
+                    case B1200:
+                        b = 1200;
+                        break;
+                    case B1800:
+                        b = 1800;
+                        break;
+                    case B2400:
+                        b = 2400;
+                        break;
+                    case B4800:
+                        b = 4800;
+                        break;
+                    case B9600:
+                        b = 9600;
+                        break;
+                    case B19200:
+                        b = 19200;
+                        break;
+                    case B38400:
+                        b = 38400;
+                        break;
+                    case B57600:
+                        b = 57600;
+                        break;
+                    case B115200:
+                        b = 115200;
+                        break;
+                    case B230400:
+                        b = 230400;
+                        break;
+                    case B460800:
+                        b = 460800;
+                        break;
+                    case B500000:
+                        b = 500000;
+                        break;
+                    case B576000:
+                        b = 576000;
+                        break;
+                    case B921600:
+                        b = 921600;
+                        break;
+                    case B1000000:
+                        b = 1000000;
+                        break;
+                    case B1152000:
+                        b = 1152000;
+                        break;
+                    case B1500000:
+                        b = 1500000;
+                        break;
+                    case B2000000:
+                        b = 2000000;
+                        break;
+                    case B2500000:
+                        b = 2500000;
+                        break;
+                    case B3000000:
+                        b = 3000000;
+                        break;
+                    case B3500000:
+                        b = 3500000;
+                        break;
+                    case B4000000:
+                        b = 4000000;
+                        break;
+                    default:
+                        errno = EINVAL;
+                        return -1;
+                }
+            }
+
+            if (uart_set_baudrate(fd, b) != ESP_OK) {
+                errno = EINVAL;
+                return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int uart_tcgetattr(int fd, struct termios *p)
+{
+    if (fd < 0 || fd >= UART_NUM) {
+        errno = EBADF;
+        return -1;
+    }
+
+    if (p == NULL) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    memset(p, 0, sizeof(struct termios));
+
+    if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CRLF) {
+        p->c_iflag |= IGNCR;
+    } else if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CR) {
+        p->c_iflag |= ICRNL;
+    }
+
+    {
+        uart_word_length_t data_bits;
+
+        if (uart_get_word_length(fd, &data_bits) != ESP_OK) {
+            errno = EINVAL;
+            return -1;
+        }
+
+        p->c_cflag &= (~CSIZE);
+
+        switch (data_bits) {
+            case UART_DATA_5_BITS:
+                p->c_cflag |= CS5;
+                break;
+            case UART_DATA_6_BITS:
+                p->c_cflag |= CS6;
+                break;
+            case UART_DATA_7_BITS:
+                p->c_cflag |= CS7;
+                break;
+            case UART_DATA_8_BITS:
+                p->c_cflag |= CS8;
+                break;
+            default:
+                errno = ENOSYS;
+                return -1;
+        }
+    }
+
+    {
+        uart_stop_bits_t stop_bits;
+        if (uart_get_stop_bits(fd, &stop_bits) != ESP_OK) {
+            errno = EINVAL;
+            return -1;
+        }
+
+        switch (stop_bits) {
+            case UART_STOP_BITS_1:
+                // nothing to do
+                break;
+            case UART_STOP_BITS_2:
+                p->c_cflag |= CSTOPB;
+                break;
+            default:
+                // UART_STOP_BITS_1_5 is unsupported by termios
+                errno = ENOSYS;
+                return -1;
+        }
+    }
+
+    {
+        uart_parity_t parity_mode;
+        if (uart_get_parity(fd, &parity_mode) != ESP_OK) {
+            errno = EINVAL;
+            return -1;
+        }
+
+        switch (parity_mode) {
+            case UART_PARITY_EVEN:
+                p->c_cflag |= PARENB;
+                break;
+            case UART_PARITY_ODD:
+                p->c_cflag |= (PARENB | PARODD);
+                break;
+            case UART_PARITY_DISABLE:
+                // nothing to do
+                break;
+            default:
+                errno = ENOSYS;
+                return -1;
+        }
+    }
+
+    {
+        uint32_t baudrate;
+        if (uart_get_baudrate(fd, &baudrate) != ESP_OK) {
+            errno = EINVAL;
+            return -1;
+        }
+
+        p->c_cflag |= (CBAUD | CBAUDEX);
+
+        speed_t sp;
+        switch (baudrate) {
+            case 0:
+                sp = B0;
+                break;
+            case 50:
+                sp = B50;
+                break;
+            case 75:
+                sp = B75;
+                break;
+            case 110:
+                sp = B110;
+                break;
+            case 134:
+                sp = B134;
+                break;
+            case 150:
+                sp = B150;
+                break;
+            case 200:
+                sp = B200;
+                break;
+            case 300:
+                sp = B300;
+                break;
+            case 600:
+                sp = B600;
+                break;
+            case 1200:
+                sp = B1200;
+                break;
+            case 1800:
+                sp = B1800;
+                break;
+            case 2400:
+                sp = B2400;
+                break;
+            case 4800:
+                sp = B4800;
+                break;
+            case 9600:
+                sp = B9600;
+                break;
+            case 19200:
+                sp = B19200;
+                break;
+            case 38400:
+                sp = B38400;
+                break;
+            case 57600:
+                sp = B57600;
+                break;
+            case 115200:
+                sp = B115200;
+                break;
+            case 230400:
+                sp = B230400;
+                break;
+            case 460800:
+                sp = B460800;
+                break;
+            case 500000:
+                sp = B500000;
+                break;
+            case 576000:
+                sp = B576000;
+                break;
+            case 921600:
+                sp = B921600;
+                break;
+            case 1000000:
+                sp = B1000000;
+                break;
+            case 1152000:
+                sp = B1152000;
+                break;
+            case 1500000:
+                sp = B1500000;
+                break;
+            case 2000000:
+                sp = B2000000;
+                break;
+            case 2500000:
+                sp = B2500000;
+                break;
+            case 3000000:
+                sp = B3000000;
+                break;
+            case 3500000:
+                sp = B3500000;
+                break;
+            case 4000000:
+                sp = B4000000;
+                break;
+            default:
+                p->c_cflag |= BOTHER;
+                sp = baudrate;
+                break;
+        }
+
+        p->c_ispeed = p->c_ospeed = sp;
+    }
+
+    return 0;
+}
+
+static int uart_tcdrain(int fd)
+{
+    if (fd < 0 || fd >= UART_NUM) {
+        errno = EBADF;
+        return -1;
+    }
+
+    if (uart_wait_tx_done(fd, portMAX_DELAY) != ESP_OK) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return 0;
+}
+
+static int uart_tcflush(int fd, int select)
+{
+    if (fd < 0 || fd >= UART_NUM) {
+        errno = EBADF;
+        return -1;
+    }
+
+    if (select == TCIFLUSH) {
+        if (uart_flush_input(fd) != ESP_OK) {
+            errno = EINVAL;
+            return -1;
+        }
+    } else {
+        // output flushing is not supported
+        errno = EINVAL;
+        return -1;
+    }
+
+    return 0;
+}
+#endif // CONFIG_SUPPORT_TERMIOS
+
 void esp_vfs_dev_uart_register()
 {
     esp_vfs_t vfs = {
@@ -433,13 +884,21 @@ void esp_vfs_dev_uart_register()
         .access = &uart_access,
         .start_select = &uart_start_select,
         .end_select = &uart_end_select,
+#ifdef CONFIG_SUPPORT_TERMIOS
+        .tcsetattr = &uart_tcsetattr,
+        .tcgetattr = &uart_tcgetattr,
+        .tcdrain = &uart_tcdrain,
+        .tcflush = &uart_tcflush,
+#endif // CONFIG_SUPPORT_TERMIOS
     };
     ESP_ERROR_CHECK(esp_vfs_register("/dev/uart", &vfs, NULL));
 }
 
 void esp_vfs_dev_uart_set_rx_line_endings(esp_line_endings_t mode)
 {
-    s_rx_mode = mode;
+    for (int i = 0; i < UART_NUM; ++i) {
+        s_rx_mode[i] = mode;
+    }
 }
 
 void esp_vfs_dev_uart_set_tx_line_endings(esp_line_endings_t mode)