Add basic support for termios.h

Closes https://github.com/espressif/esp-idf/issues/2063
This commit is contained in:
Roland Dobai 2018-08-14 13:39:30 +02:00
parent 499d06746b
commit 5395435816
7 changed files with 1087 additions and 7 deletions

View File

@ -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__

View File

@ -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

View 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

View File

@ -26,8 +26,10 @@
#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 */

View 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

View 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

View 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)