--- /dev/null
+// 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__
--- /dev/null
+// 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
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
#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" {
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 */
#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"
#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)
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
}
}
}
+
+#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
#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();
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) {
_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 = {
.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)