mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-05-21 00:56:38 +08:00
feat(vfs): Bring vfs from esp-idf
Commit ID: e1e82c89
This commit is contained in:
49
components/esp8266/include/driver/uart_select.h
Normal file
49
components/esp8266/include/driver/uart_select.h
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
// 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.
|
||||
|
||||
#ifndef _UART_SELECT_H_
|
||||
#define _UART_SELECT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "driver/uart.h"
|
||||
|
||||
typedef enum {
|
||||
UART_SELECT_READ_NOTIF,
|
||||
UART_SELECT_WRITE_NOTIF,
|
||||
UART_SELECT_ERROR_NOTIF,
|
||||
} uart_select_notif_t;
|
||||
|
||||
typedef void (*uart_select_notif_callback_t)(uart_port_t uart_num, uart_select_notif_t uart_select_notif, BaseType_t *task_woken);
|
||||
|
||||
/**
|
||||
* @brief Set notification callback function for select() events
|
||||
* @param uart_num UART port number
|
||||
* @param uart_select_notif_callback callback function
|
||||
*/
|
||||
void uart_set_select_notif_callback(uart_port_t uart_num, uart_select_notif_callback_t uart_select_notif_callback);
|
||||
|
||||
/**
|
||||
* @brief Get mutex guarding select() notifications
|
||||
*/
|
||||
portMUX_TYPE *uart_get_selectlock();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //_UART_SELECT_H_
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2017 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.
|
||||
@ -12,20 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int fcntl(int fd, int request, ...)
|
||||
{
|
||||
int val, ret;
|
||||
va_list va;
|
||||
void esp_vfs_lwip_sockets_register();
|
||||
|
||||
va_start(va, request);
|
||||
|
||||
val = va_arg(va, int);
|
||||
ret = lwip_fcntl(fd, request, val);
|
||||
|
||||
va_end(va);
|
||||
|
||||
return ret;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
74
components/lwip/port/vfs_lwip.c
Normal file
74
components/lwip/port/vfs_lwip.c
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2017 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 <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_dev.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/uart_struct.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
_Static_assert(MAX_FDS >= CONFIG_LWIP_MAX_SOCKETS, "MAX_FDS < CONFIG_LWIP_MAX_SOCKETS");
|
||||
|
||||
static void lwip_stop_socket_select()
|
||||
{
|
||||
sys_sem_signal(sys_thread_sem_get()); //socket_select will return
|
||||
}
|
||||
|
||||
static void lwip_stop_socket_select_isr(BaseType_t *woken)
|
||||
{
|
||||
if (sys_sem_signal_isr(sys_thread_sem_get()) && woken) {
|
||||
*woken = pdTRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static int lwip_fcntl_r_wrapper(int fd, int cmd, va_list args)
|
||||
{
|
||||
return lwip_fcntl_r(fd, cmd, va_arg(args, int));
|
||||
}
|
||||
|
||||
static int lwip_ioctl_r_wrapper(int fd, int cmd, va_list args)
|
||||
{
|
||||
return lwip_ioctl_r(fd, cmd, va_arg(args, void *));
|
||||
}
|
||||
|
||||
void esp_vfs_lwip_sockets_register()
|
||||
{
|
||||
esp_vfs_t vfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
.write = &lwip_write_r,
|
||||
.open = NULL,
|
||||
.fstat = NULL,
|
||||
.close = &lwip_close_r,
|
||||
.read = &lwip_read_r,
|
||||
.fcntl = &lwip_fcntl_r_wrapper,
|
||||
.ioctl = &lwip_ioctl_r_wrapper,
|
||||
.socket_select = &lwip_select,
|
||||
.stop_socket_select = &lwip_stop_socket_select,
|
||||
.stop_socket_select_isr = &lwip_stop_socket_select_isr,
|
||||
};
|
||||
/* Non-LWIP file descriptors are from 0 to (LWIP_SOCKET_OFFSET-1). LWIP
|
||||
* file descriptors are registered from LWIP_SOCKET_OFFSET to
|
||||
* MAX_FDS-1.
|
||||
*/
|
||||
|
||||
ESP_ERROR_CHECK(esp_vfs_register_fd_range(&vfs, NULL, LWIP_SOCKET_OFFSET, MAX_FDS));
|
||||
}
|
296
components/newlib/newlib/port/include/sys/termios.h
Normal file
296
components/newlib/newlib/port/include/sys/termios.h
Normal 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__
|
64
components/newlib/newlib/port/select.c
Normal file
64
components/newlib/newlib/port/select.c
Normal file
@ -0,0 +1,64 @@
|
||||
// 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 <sys/select.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef CONFIG_USE_ONLY_LWIP_SELECT
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
#ifdef CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_NONE
|
||||
#endif //CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "newlib_select";
|
||||
|
||||
static void log_fd_set(const char *fds_name, const fd_set *fds)
|
||||
{
|
||||
if (fds_name && fds) {
|
||||
ESP_LOGD(TAG, "FDs in %s =", fds_name);
|
||||
for (int i = 0; i < MAX_FDS; ++i) {
|
||||
if (FD_ISSET(i, fds)) {
|
||||
ESP_LOGD(TAG, "%d", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //CONFIG_USE_ONLY_LWIP_SELECT
|
||||
|
||||
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
|
||||
{
|
||||
#ifdef CONFIG_USE_ONLY_LWIP_SELECT
|
||||
ESP_LOGD(TAG, "lwip_select starts with nfds = %d", nfds);
|
||||
if (timeout) {
|
||||
ESP_LOGD(TAG, "timeout is %lds + %ldus", timeout->tv_sec, timeout->tv_usec);
|
||||
}
|
||||
log_fd_set("readfds", readfds);
|
||||
log_fd_set("writefds", writefds);
|
||||
log_fd_set("errorfds", errorfds);
|
||||
|
||||
int ret = lwip_select(nfds, readfds, writefds, errorfds, timeout);
|
||||
|
||||
ESP_LOGD(TAG, "lwip_select returns %d", ret);
|
||||
log_fd_set("readfds", readfds);
|
||||
log_fd_set("writefds", writefds);
|
||||
log_fd_set("errorfds", errorfds);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return esp_vfs_select(nfds, readfds, writefds, errorfds, timeout);
|
||||
#endif
|
||||
}
|
54
components/newlib/newlib/port/termios.c
Normal file
54
components/newlib/newlib/port/termios.c
Normal 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
|
7
components/vfs/CMakeLists.txt
Normal file
7
components/vfs/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
set(COMPONENT_SRCS "vfs.c"
|
||||
"vfs_uart.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "include")
|
||||
|
||||
set(COMPONENT_REQUIRES)
|
||||
|
||||
register_component()
|
18
components/vfs/Kconfig
Normal file
18
components/vfs/Kconfig
Normal file
@ -0,0 +1,18 @@
|
||||
menu "Virtual file system"
|
||||
|
||||
config SUPPRESS_SELECT_DEBUG_OUTPUT
|
||||
bool "Suppress select() related debug outputs"
|
||||
default y
|
||||
help
|
||||
Select() related functions might produce an unconveniently lot of
|
||||
debug outputs when one sets the default log level to DEBUG or higher.
|
||||
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
|
170
components/vfs/README.rst
Normal file
170
components/vfs/README.rst
Normal file
@ -0,0 +1,170 @@
|
||||
Virtual filesystem component
|
||||
============================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Virtual filesystem (VFS) component provides a unified interface for drivers which can perform operations on file-like objects. This can be a real filesystems (FAT, SPIFFS, etc.), or device drivers which exposes file-like interface.
|
||||
|
||||
This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At high level, each FS driver is associated with some path prefix. When one of C library functions needs to open a file, VFS component searches for the FS driver associated with the file's path, and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver.
|
||||
|
||||
For example, one can register a FAT filesystem driver with ``/fat`` prefix, and call ``fopen("/fat/file.txt", "w")``. VFS component will then call ``open`` function of FAT driver and pass ``/file.txt`` argument to it (and appropriate mode flags). All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver.
|
||||
|
||||
FS registration
|
||||
---------------
|
||||
|
||||
|
||||
|
||||
To register an FS driver, application needs to define in instance of :cpp:type:`esp_vfs_t` structure and populate it with function pointers to FS APIs:
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
esp_vfs_t myfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
.write = &myfs_write,
|
||||
.open = &myfs_open,
|
||||
.fstat = &myfs_fstat,
|
||||
.close = &myfs_close,
|
||||
.read = &myfs_read,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL));
|
||||
|
||||
Depending on the way FS driver declares its APIs, either ``read``, ``write``, etc., or ``read_p``, ``write_p``, etc. should be used.
|
||||
|
||||
Case 1: API functions are declared without an extra context pointer (FS driver is a singleton)::
|
||||
|
||||
ssize_t myfs_write(int fd, const void * data, size_t size);
|
||||
|
||||
// In definition of esp_vfs_t:
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
.write = &myfs_write,
|
||||
// ... other members initialized
|
||||
|
||||
// When registering FS, context pointer (third argument) is NULL:
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL));
|
||||
|
||||
Case 2: API functions are declared with an extra context pointer (FS driver supports multiple instances)::
|
||||
|
||||
ssize_t myfs_write(myfs_t* fs, int fd, const void * data, size_t size);
|
||||
|
||||
// In definition of esp_vfs_t:
|
||||
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
|
||||
.write_p = &myfs_write,
|
||||
// ... other members initialized
|
||||
|
||||
// When registering FS, pass the FS context pointer into the third argument
|
||||
// (hypothetical myfs_mount function is used for illustrative purposes)
|
||||
myfs_t* myfs_inst1 = myfs_mount(partition1->offset, partition1->size);
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/data1", &myfs, myfs_inst1));
|
||||
|
||||
// Can register another instance:
|
||||
myfs_t* myfs_inst2 = myfs_mount(partition2->offset, partition2->size);
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/data2", &myfs, myfs_inst2));
|
||||
|
||||
Synchronous input/output multiplexing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you want to use synchronous input/output multiplexing by :cpp:func:`select`
|
||||
then you need to register the VFS with :cpp:func:`start_select` and
|
||||
:cpp:func:`end_select` functions similarly to the following example:
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
// In definition of esp_vfs_t:
|
||||
.start_select = &uart_start_select,
|
||||
.end_select = &uart_end_select,
|
||||
// ... other members initialized
|
||||
|
||||
:cpp:func:`start_select` is called for setting up the environment for
|
||||
detection of read/write/error conditions on file descriptors belonging to the
|
||||
given VFS. :cpp:func:`end_select` is called to stop/deinitialize/free the
|
||||
environment which was setup by :cpp:func:`start_select`. Please refer to the
|
||||
reference implementation for the UART peripheral in
|
||||
:component_file:`vfs/vfs_uart.c` and most particularly to functions
|
||||
:cpp:func:`esp_vfs_dev_uart_register`, :cpp:func:`uart_start_select` and
|
||||
:cpp:func:`uart_end_select`.
|
||||
|
||||
Examples demonstrating the use of :cpp:func:`select` with VFS file descriptors
|
||||
are the :example:`peripherals/uart_select` and the :example:`system/select`
|
||||
examples.
|
||||
|
||||
If :cpp:func:`select` is used for socket file descriptors only then one can
|
||||
enable the :envvar:`CONFIG_USE_ONLY_LWIP_SELECT` option which can reduce the code
|
||||
size and improve performance.
|
||||
|
||||
Paths
|
||||
-----
|
||||
|
||||
Each registered FS has a path prefix associated with it. This prefix may be considered a "mount point" of this partition.
|
||||
|
||||
In case when mount points are nested, the mount point with the longest matching path prefix is used when opening the file. For instance, suppose that the following filesystems are registered in VFS:
|
||||
|
||||
- FS 1 on /data
|
||||
- FS 2 on /data/static
|
||||
|
||||
Then:
|
||||
|
||||
- FS 1 will be used when opening a file called ``/data/log.txt``
|
||||
- FS 2 will be used when opening a file called ``/data/static/index.html``
|
||||
- Even if ``/index.html"`` doesn't exist in FS 2, FS 1 will *not* be searched for ``/static/index.html``.
|
||||
|
||||
As a general rule, mount point names must start with the path separator (``/``) and must contain at least one character after path separator. However an empty mount point name is also supported, and may be used in cases when application needs to provide "fallback" filesystem, or override VFS functionality altogether. Such filesystem will be used if no prefix matches the path given.
|
||||
|
||||
VFS does not handle dots (``.``) in path names in any special way. VFS does not treat ``..`` as a reference to the parent directory. I.e. in the above example, using a path ``/data/static/../log.txt`` will not result in a call to FS 1 to open ``/log.txt``. Specific FS drivers (such as FATFS) may handle dots in file names differently.
|
||||
|
||||
When opening files, FS driver will only be given relative path to files. For example:
|
||||
|
||||
- ``myfs`` driver is registered with ``/data`` as path prefix
|
||||
- and application calls ``fopen("/data/config.json", ...)``
|
||||
- then VFS component will call ``myfs_open("/config.json", ...)``.
|
||||
- ``myfs`` driver will open ``/config.json`` file
|
||||
|
||||
VFS doesn't impose a limit on total file path length, but it does limit FS path prefix to ``ESP_VFS_PATH_MAX`` characters. Individual FS drivers may have their own filename length limitations.
|
||||
|
||||
File descriptors
|
||||
----------------
|
||||
|
||||
File descriptors are small positive integers from ``0`` to ``FD_SETSIZE - 1`` where ``FD_SETSIZE`` is defined in newlib's ``sys/types.h``. The largest file descriptors (configured by ``CONFIG_LWIP_MAX_SOCKETS``) are reserved for sockets. The VFS component contains a lookup-table called ``s_fd_table`` for mapping global file descriptors to VFS driver indexes registered in the ``s_vfs`` array.
|
||||
|
||||
Standard IO streams (stdin, stdout, stderr)
|
||||
-------------------------------------------
|
||||
|
||||
If "UART for console output" menuconfig option is not set to "None", then ``stdin``, ``stdout``, and ``stderr`` are configured to read from, and write to, a UART. It is possible to use UART0 or UART1 for standard IO. By default, UART0 is used, with 115200 baud rate, TX pin is GPIO1 and RX pin is GPIO3. These parameters can be changed in menuconfig.
|
||||
|
||||
Writing to ``stdout`` or ``stderr`` will send characters to the UART transmit FIFO. Reading from ``stdin`` will retrieve characters from the UART receive FIFO.
|
||||
|
||||
By default, VFS uses simple functions for reading from and writing to UART. Writes busy-wait until all data is put into UART FIFO, and reads are non-blocking, returning only the data present in the FIFO. Because of this non-blocking read behavior, higher level C library calls, such as ``fscanf("%d\n", &var);`` may not have desired results.
|
||||
|
||||
Applications which use UART driver may instruct VFS to use the driver's interrupt driven, blocking read and write functions instead. This can be done using a call to ``esp_vfs_dev_uart_use_driver`` function. It is also possible to revert to the basic non-blocking functions using a call to ``esp_vfs_dev_uart_use_nonblocking``.
|
||||
|
||||
VFS also provides optional newline conversion feature for input and output. Internally, most applications send and receive lines terminated by LF (''\n'') character. Different terminal programs may require different line termination, such as CR or CRLF. Applications can configure this separately for input and output either via menuconfig, or by calls to ``esp_vfs_dev_uart_set_rx_line_endings`` and ``esp_vfs_dev_uart_set_tx_line_endings`` functions.
|
||||
|
||||
|
||||
|
||||
Standard streams and FreeRTOS tasks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``FILE`` objects for ``stdin``, ``stdout``, and ``stderr`` are shared between all FreeRTOS tasks, but the pointers to these objects are are stored in per-task ``struct _reent``. The following code:
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
fprintf(stderr, "42\n");
|
||||
|
||||
actually is translated to to this (by the preprocessor)::
|
||||
|
||||
fprintf(__getreent()->_stderr, "42\n");
|
||||
|
||||
where the ``__getreent()`` function returns a per-task pointer to ``struct _reent`` (:component_file:`newlib/include/sys/reent.h#L370-L417`). This structure is allocated on the TCB of each task. When a task is initialized, ``_stdin``, ``_stdout`` and ``_stderr`` members of ``struct _reent`` are set to the values of ``_stdin``, ``_stdout`` and ``_stderr`` of ``_GLOBAL_REENT`` (i.e. the structure which is used before FreeRTOS is started).
|
||||
|
||||
Such a design has the following consequences:
|
||||
|
||||
- It is possible to set ``stdin``, ``stdout``, and ``stderr`` for any given task without affecting other tasks, e.g. by doing ``stdin = fopen("/dev/uart/1", "r")``.
|
||||
- Closing default ``stdin``, ``stdout``, or ``stderr`` using ``fclose`` will close the ``FILE`` stream object — this will affect all other tasks.
|
||||
- To change the default ``stdin``, ``stdout``, ``stderr`` streams for new tasks, modify ``_GLOBAL_REENT->_stdin`` (``_stdout``, ``_stderr``) before creating the task.
|
4
components/vfs/component.mk
Executable file → Normal file
4
components/vfs/component.mk
Executable file → Normal file
@ -1,7 +1,5 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
|
386
components/vfs/include/esp_vfs.h
Normal file
386
components/vfs/include/esp_vfs.h
Normal file
@ -0,0 +1,386 @@
|
||||
// Copyright 2015-2016 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.
|
||||
|
||||
#ifndef __ESP_VFS_H__
|
||||
#define __ESP_VFS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_err.h"
|
||||
#include <sys/types.h>
|
||||
#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" {
|
||||
#endif
|
||||
|
||||
#ifndef _SYS_TYPES_FD_SET
|
||||
#error "VFS should be used with FD_SETSIZE and FD_SET from sys/types.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Maximum number of (global) file descriptors.
|
||||
*/
|
||||
#define MAX_FDS FD_SETSIZE /* for compatibility with fd_set and select() */
|
||||
|
||||
/**
|
||||
* Maximum length of path prefix (not including zero terminator)
|
||||
*/
|
||||
#define ESP_VFS_PATH_MAX 15
|
||||
|
||||
/**
|
||||
* Default value of flags member in esp_vfs_t structure.
|
||||
*/
|
||||
#define ESP_VFS_FLAG_DEFAULT 0
|
||||
|
||||
/**
|
||||
* Flag which indicates that FS needs extra context pointer in syscalls.
|
||||
*/
|
||||
#define ESP_VFS_FLAG_CONTEXT_PTR 1
|
||||
|
||||
/*
|
||||
* @brief VFS identificator used for esp_vfs_register_with_id()
|
||||
*/
|
||||
typedef int esp_vfs_id_t;
|
||||
|
||||
/**
|
||||
* @brief VFS definition structure
|
||||
*
|
||||
* This structure should be filled with pointers to corresponding
|
||||
* FS driver functions.
|
||||
*
|
||||
* VFS component will translate all FDs so that the filesystem implementation
|
||||
* sees them starting at zero. The caller sees a global FD which is prefixed
|
||||
* with an pre-filesystem-implementation.
|
||||
*
|
||||
* Some FS implementations expect some state (e.g. pointer to some structure)
|
||||
* to be passed in as a first argument. For these implementations,
|
||||
* populate the members of this structure which have _p suffix, set
|
||||
* flags member to ESP_VFS_FLAG_CONTEXT_PTR and provide the context pointer
|
||||
* to esp_vfs_register function.
|
||||
* If the implementation doesn't use this extra argument, populate the
|
||||
* members without _p suffix and set flags member to ESP_VFS_FLAG_DEFAULT.
|
||||
*
|
||||
* If the FS driver doesn't provide some of the functions, set corresponding
|
||||
* members to NULL.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int flags; /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT */
|
||||
union {
|
||||
ssize_t (*write_p)(void* p, int fd, const void * data, size_t size);
|
||||
ssize_t (*write)(int fd, const void * data, size_t size);
|
||||
};
|
||||
union {
|
||||
off_t (*lseek_p)(void* p, int fd, off_t size, int mode);
|
||||
off_t (*lseek)(int fd, off_t size, int mode);
|
||||
};
|
||||
union {
|
||||
ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size);
|
||||
ssize_t (*read)(int fd, void * dst, size_t size);
|
||||
};
|
||||
union {
|
||||
int (*open_p)(void* ctx, const char * path, int flags, int mode);
|
||||
int (*open)(const char * path, int flags, int mode);
|
||||
};
|
||||
union {
|
||||
int (*close_p)(void* ctx, int fd);
|
||||
int (*close)(int fd);
|
||||
};
|
||||
union {
|
||||
int (*fstat_p)(void* ctx, int fd, struct stat * st);
|
||||
int (*fstat)(int fd, struct stat * st);
|
||||
};
|
||||
union {
|
||||
int (*stat_p)(void* ctx, const char * path, struct stat * st);
|
||||
int (*stat)(const char * path, struct stat * st);
|
||||
};
|
||||
union {
|
||||
int (*link_p)(void* ctx, const char* n1, const char* n2);
|
||||
int (*link)(const char* n1, const char* n2);
|
||||
};
|
||||
union {
|
||||
int (*unlink_p)(void* ctx, const char *path);
|
||||
int (*unlink)(const char *path);
|
||||
};
|
||||
union {
|
||||
int (*rename_p)(void* ctx, const char *src, const char *dst);
|
||||
int (*rename)(const char *src, const char *dst);
|
||||
};
|
||||
union {
|
||||
DIR* (*opendir_p)(void* ctx, const char* name);
|
||||
DIR* (*opendir)(const char* name);
|
||||
};
|
||||
union {
|
||||
struct dirent* (*readdir_p)(void* ctx, DIR* pdir);
|
||||
struct dirent* (*readdir)(DIR* pdir);
|
||||
};
|
||||
union {
|
||||
int (*readdir_r_p)(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
|
||||
int (*readdir_r)(DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
|
||||
};
|
||||
union {
|
||||
long (*telldir_p)(void* ctx, DIR* pdir);
|
||||
long (*telldir)(DIR* pdir);
|
||||
};
|
||||
union {
|
||||
void (*seekdir_p)(void* ctx, DIR* pdir, long offset);
|
||||
void (*seekdir)(DIR* pdir, long offset);
|
||||
};
|
||||
union {
|
||||
int (*closedir_p)(void* ctx, DIR* pdir);
|
||||
int (*closedir)(DIR* pdir);
|
||||
};
|
||||
union {
|
||||
int (*mkdir_p)(void* ctx, const char* name, mode_t mode);
|
||||
int (*mkdir)(const char* name, mode_t mode);
|
||||
};
|
||||
union {
|
||||
int (*rmdir_p)(void* ctx, const char* name);
|
||||
int (*rmdir)(const char* name);
|
||||
};
|
||||
union {
|
||||
int (*fcntl_p)(void* ctx, int fd, int cmd, va_list args);
|
||||
int (*fcntl)(int fd, int cmd, va_list args);
|
||||
};
|
||||
union {
|
||||
int (*ioctl_p)(void* ctx, int fd, int cmd, va_list args);
|
||||
int (*ioctl)(int fd, int cmd, va_list args);
|
||||
};
|
||||
union {
|
||||
int (*fsync_p)(void* ctx, int fd);
|
||||
int (*fsync)(int fd);
|
||||
};
|
||||
union {
|
||||
int (*access_p)(void* ctx, const char *path, int amode);
|
||||
int (*access)(const char *path, int amode);
|
||||
};
|
||||
union {
|
||||
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 */
|
||||
int (*socket_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
|
||||
/** called by VFS to interrupt the socket_select call when select is activated from a non-socket VFS driver; set only for the socket driver */
|
||||
void (*stop_socket_select)();
|
||||
/** stop_socket_select which can be called from ISR; set only for the socket driver */
|
||||
void (*stop_socket_select_isr)(BaseType_t *woken);
|
||||
/** end_select is called to stop the I/O multiplexing and deinitialize the environment created by start_select for the given VFS */
|
||||
void (*end_select)();
|
||||
} esp_vfs_t;
|
||||
|
||||
|
||||
/**
|
||||
* Register a virtual filesystem for given path prefix.
|
||||
*
|
||||
* @param base_path file path prefix associated with the filesystem.
|
||||
* Must be a zero-terminated C string, up to ESP_VFS_PATH_MAX
|
||||
* characters long, and at least 2 characters long.
|
||||
* Name must start with a "/" and must not end with "/".
|
||||
* For example, "/data" or "/dev/spi" are valid.
|
||||
* These VFSes would then be called to handle file paths such as
|
||||
* "/data/myfile.txt" or "/dev/spi/0".
|
||||
* @param vfs Pointer to esp_vfs_t, a structure which maps syscalls to
|
||||
* the filesystem driver functions. VFS component doesn't
|
||||
* assume ownership of this pointer.
|
||||
* @param ctx If vfs->flags has ESP_VFS_FLAG_CONTEXT_PTR set, a pointer
|
||||
* which should be passed to VFS functions. Otherwise, NULL.
|
||||
*
|
||||
* @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
|
||||
* registered.
|
||||
*/
|
||||
esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx);
|
||||
|
||||
|
||||
/**
|
||||
* Special case function for registering a VFS that uses a method other than
|
||||
* open() to open new file descriptors from the interval <min_fd; max_fd).
|
||||
*
|
||||
* This is a special-purpose function intended for registering LWIP sockets to VFS.
|
||||
*
|
||||
* @param vfs Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register().
|
||||
* @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register().
|
||||
* @param min_fd The smallest file descriptor this VFS will use.
|
||||
* @param max_fd Upper boundary for file descriptors this VFS will use (the biggest file descriptor plus one).
|
||||
*
|
||||
* @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
|
||||
* registered, ESP_ERR_INVALID_ARG if the file descriptor boundaries
|
||||
* are incorrect.
|
||||
*/
|
||||
esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, int max_fd);
|
||||
|
||||
/**
|
||||
* Special case function for registering a VFS that uses a method other than
|
||||
* open() to open new file descriptors. In comparison with
|
||||
* esp_vfs_register_fd_range, this function doesn't pre-registers an interval
|
||||
* of file descriptors. File descriptors can be registered later, by using
|
||||
* esp_vfs_register_fd.
|
||||
*
|
||||
* @param vfs Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register().
|
||||
* @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register().
|
||||
* @param vfs_id Here will be written the VFS ID which can be passed to
|
||||
* esp_vfs_register_fd for registering file descriptors.
|
||||
*
|
||||
* @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
|
||||
* registered, ESP_ERR_INVALID_ARG if the file descriptor boundaries
|
||||
* are incorrect.
|
||||
*/
|
||||
esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t *vfs_id);
|
||||
|
||||
/**
|
||||
* Unregister a virtual filesystem for given path prefix
|
||||
*
|
||||
* @param base_path file prefix previously used in esp_vfs_register call
|
||||
* @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for given prefix
|
||||
* hasn't been registered
|
||||
*/
|
||||
esp_err_t esp_vfs_unregister(const char* base_path);
|
||||
|
||||
/**
|
||||
* Special function for registering another file descriptor for a VFS registered
|
||||
* by esp_vfs_register_with_id.
|
||||
*
|
||||
* @param vfs_id VFS identificator returned by esp_vfs_register_with_id.
|
||||
* @param fd The registered file descriptor will be written to this address.
|
||||
*
|
||||
* @return ESP_OK if the registration is successful,
|
||||
* ESP_ERR_NO_MEM if too many file descriptors are registered,
|
||||
* ESP_ERR_INVALID_ARG if the arguments are incorrect.
|
||||
*/
|
||||
esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd);
|
||||
|
||||
/**
|
||||
* Special function for unregistering a file descriptor belonging to a VFS
|
||||
* registered by esp_vfs_register_with_id.
|
||||
*
|
||||
* @param vfs_id VFS identificator returned by esp_vfs_register_with_id.
|
||||
* @param fd File descriptor which should be unregistered.
|
||||
*
|
||||
* @return ESP_OK if the registration is successful,
|
||||
* ESP_ERR_INVALID_ARG if the arguments are incorrect.
|
||||
*/
|
||||
esp_err_t esp_vfs_unregister_fd(esp_vfs_id_t vfs_id, int fd);
|
||||
|
||||
/**
|
||||
* These functions are to be used in newlib syscall table. They will be called by
|
||||
* newlib when it needs to use any of the syscalls.
|
||||
*/
|
||||
/**@{*/
|
||||
ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size);
|
||||
off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode);
|
||||
ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size);
|
||||
int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode);
|
||||
int esp_vfs_close(struct _reent *r, int fd);
|
||||
int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st);
|
||||
int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st);
|
||||
int esp_vfs_link(struct _reent *r, const char* n1, const char* n2);
|
||||
int esp_vfs_unlink(struct _reent *r, const char *path);
|
||||
int esp_vfs_rename(struct _reent *r, const char *src, const char *dst);
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* @brief Synchronous I/O multiplexing which implements the functionality of POSIX select() for VFS
|
||||
* @param nfds Specifies the range of descriptors which should be checked.
|
||||
* The first nfds descriptors will be checked in each set.
|
||||
* @param readfds If not NULL, then points to a descriptor set that on input
|
||||
* specifies which descriptors should be checked for being
|
||||
* ready to read, and on output indicates which descriptors
|
||||
* are ready to read.
|
||||
* @param writefds If not NULL, then points to a descriptor set that on input
|
||||
* specifies which descriptors should be checked for being
|
||||
* ready to write, and on output indicates which descriptors
|
||||
* are ready to write.
|
||||
* @param errorfds If not NULL, then points to a descriptor set that on input
|
||||
* specifies which descriptors should be checked for error
|
||||
* conditions, and on output indicates which descriptors
|
||||
* have error conditions.
|
||||
* @param timeout If not NULL, then points to timeval structure which
|
||||
* specifies the time period after which the functions should
|
||||
* time-out and return. If it is NULL, then the function will
|
||||
* not time-out.
|
||||
*
|
||||
* @return The number of descriptors set in the descriptor sets, or -1
|
||||
* when an error (specified by errno) have occurred.
|
||||
*/
|
||||
int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
|
||||
|
||||
/**
|
||||
* @brief Notification from a VFS driver about a read/write/error condition
|
||||
*
|
||||
* This function is called when the VFS driver detects a read/write/error
|
||||
* condition as it was requested by the previous call to start_select.
|
||||
*
|
||||
* @param signal_sem semaphore handle which was passed to the driver by the start_select call
|
||||
*/
|
||||
void esp_vfs_select_triggered(SemaphoreHandle_t *signal_sem);
|
||||
|
||||
/**
|
||||
* @brief Notification from a VFS driver about a read/write/error condition (ISR version)
|
||||
*
|
||||
* This function is called when the VFS driver detects a read/write/error
|
||||
* condition as it was requested by the previous call to start_select.
|
||||
*
|
||||
* @param signal_sem semaphore handle which was passed to the driver by the start_select call
|
||||
* @param woken is set to pdTRUE if the function wakes up a task with higher priority
|
||||
*/
|
||||
void esp_vfs_select_triggered_isr(SemaphoreHandle_t *signal_sem, BaseType_t *woken);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif //__ESP_VFS_H__
|
89
components/vfs/include/esp_vfs_dev.h
Normal file
89
components/vfs/include/esp_vfs_dev.h
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2015-2017 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_vfs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Line ending settings
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_LINE_ENDINGS_CRLF,//!< CR + LF
|
||||
ESP_LINE_ENDINGS_CR, //!< CR
|
||||
ESP_LINE_ENDINGS_LF, //!< LF
|
||||
} esp_line_endings_t;
|
||||
|
||||
/**
|
||||
* @brief add /dev/uart virtual filesystem driver
|
||||
*
|
||||
* This function is called from startup code to enable serial output
|
||||
*/
|
||||
void esp_vfs_dev_uart_register();
|
||||
|
||||
/**
|
||||
* @brief Set the line endings expected to be received on UART
|
||||
*
|
||||
* This specifies the conversion between line endings received on UART and
|
||||
* newlines ('\n', LF) passed into stdin:
|
||||
*
|
||||
* - ESP_LINE_ENDINGS_CRLF: convert CRLF to LF
|
||||
* - ESP_LINE_ENDINGS_CR: convert CR to LF
|
||||
* - ESP_LINE_ENDINGS_LF: no modification
|
||||
*
|
||||
* @note this function is not thread safe w.r.t. reading from UART
|
||||
*
|
||||
* @param mode line endings expected on UART
|
||||
*/
|
||||
void esp_vfs_dev_uart_set_rx_line_endings(esp_line_endings_t mode);
|
||||
|
||||
/**
|
||||
* @brief Set the line endings to sent to UART
|
||||
*
|
||||
* This specifies the conversion between newlines ('\n', LF) on stdout and line
|
||||
* endings sent over UART:
|
||||
*
|
||||
* - ESP_LINE_ENDINGS_CRLF: convert LF to CRLF
|
||||
* - ESP_LINE_ENDINGS_CR: convert LF to CR
|
||||
* - ESP_LINE_ENDINGS_LF: no modification
|
||||
*
|
||||
* @note this function is not thread safe w.r.t. writing to UART
|
||||
*
|
||||
* @param mode line endings to send to UART
|
||||
*/
|
||||
void esp_vfs_dev_uart_set_tx_line_endings(esp_line_endings_t mode);
|
||||
|
||||
/**
|
||||
* @brief set VFS to use simple functions for reading and writing UART
|
||||
* Read is non-blocking, write is busy waiting until TX FIFO has enough space.
|
||||
* These functions are used by default.
|
||||
* @param uart_num UART peripheral number
|
||||
*/
|
||||
void esp_vfs_dev_uart_use_nonblocking(int uart_num);
|
||||
|
||||
/**
|
||||
* @brief set VFS to use UART driver for reading and writing
|
||||
* @note application must configure UART driver before calling these functions
|
||||
* With these functions, read and write are blocking and interrupt-driven.
|
||||
* @param uart_num UART peripheral number
|
||||
*/
|
||||
void esp_vfs_dev_uart_use_driver(int uart_num);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
55
components/vfs/include/sys/dirent.h
Normal file
55
components/vfs/include/sys/dirent.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2015-2016 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* This header file provides POSIX-compatible definitions of directory
|
||||
* access functions and related data types.
|
||||
* See http://pubs.opengroup.org/onlinepubs/7908799/xsh/dirent.h.html
|
||||
* for reference.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Opaque directory structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t dd_vfs_idx; /*!< VFS index, not to be used by applications */
|
||||
uint16_t dd_rsv; /*!< field reserved for future extension */
|
||||
/* remaining fields are defined by VFS implementation */
|
||||
} DIR;
|
||||
|
||||
/**
|
||||
* @brief Directory entry structure
|
||||
*/
|
||||
struct dirent {
|
||||
int d_ino; /*!< file number */
|
||||
uint8_t d_type; /*!< not defined in POSIX, but present in BSD and Linux */
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_REG 1
|
||||
#define DT_DIR 2
|
||||
char d_name[256]; /*!< zero-terminated file name */
|
||||
};
|
||||
|
||||
DIR* opendir(const char* name);
|
||||
struct dirent* readdir(DIR* pdir);
|
||||
long telldir(DIR* pdir);
|
||||
void seekdir(DIR* pdir, long loc);
|
||||
void rewinddir(DIR* pdir);
|
||||
int closedir(DIR* pdir);
|
||||
int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
|
||||
|
@ -18,10 +18,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#undef ioctl
|
||||
|
||||
int ioctl(int fd, int request, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -1,33 +0,0 @@
|
||||
// Copyright 2018-2019 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 <stdarg.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
int ioctl(int fd, int request, ...)
|
||||
{
|
||||
int ret;
|
||||
void *p;
|
||||
va_list va;
|
||||
|
||||
va_start(va, request);
|
||||
|
||||
p = va_arg(va, void *);
|
||||
ret = lwip_ioctl(fd, request, p);
|
||||
|
||||
va_end(va);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
1098
components/vfs/vfs.c
Normal file
1098
components/vfs/vfs.c
Normal file
File diff suppressed because it is too large
Load Diff
940
components/vfs/vfs_uart.c
Normal file
940
components/vfs/vfs_uart.c
Normal file
@ -0,0 +1,940 @@
|
||||
// Copyright 2015-2017 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 <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/param.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_dev.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/uart_struct.h"
|
||||
#include "driver/uart.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "driver/uart_select.h"
|
||||
|
||||
// TODO: make the number of UARTs chip dependent
|
||||
#define UART_NUM 3
|
||||
|
||||
// Token signifying that no character is available
|
||||
#define NONE -1
|
||||
|
||||
// UART write bytes function type
|
||||
typedef void (*tx_func_t)(int, int);
|
||||
// UART read bytes function type
|
||||
typedef int (*rx_func_t)(int);
|
||||
|
||||
// Basic functions for sending and receiving bytes over UART
|
||||
static void uart_tx_char(int fd, int c);
|
||||
static int uart_rx_char(int fd);
|
||||
|
||||
// Functions for sending and receiving bytes which use UART driver
|
||||
static void uart_tx_char_via_driver(int fd, int c);
|
||||
static int uart_rx_char_via_driver(int fd);
|
||||
|
||||
// Pointers to UART peripherals
|
||||
static uart_dev_t* s_uarts[UART_NUM] = {&UART0, &UART1, &UART2};
|
||||
// per-UART locks, lazily initialized
|
||||
static _lock_t s_uart_read_locks[UART_NUM];
|
||||
static _lock_t s_uart_write_locks[UART_NUM];
|
||||
// One-character buffer used for newline conversion code, per UART
|
||||
static int s_peek_char[UART_NUM] = { NONE, NONE, NONE };
|
||||
// Per-UART non-blocking flag. Note: default implementation does not honor this
|
||||
// flag, all reads are non-blocking. This option becomes effective if UART
|
||||
// driver is used.
|
||||
static bool s_non_blocking[UART_NUM];
|
||||
|
||||
/* Lock ensuring that uart_select is used from only one task at the time */
|
||||
static _lock_t s_one_select_lock;
|
||||
|
||||
static SemaphoreHandle_t *_signal_sem = NULL;
|
||||
static fd_set *_readfds = NULL;
|
||||
static fd_set *_writefds = NULL;
|
||||
static fd_set *_errorfds = NULL;
|
||||
static fd_set *_readfds_orig = NULL;
|
||||
static fd_set *_writefds_orig = NULL;
|
||||
static fd_set *_errorfds_orig = NULL;
|
||||
|
||||
// Newline conversion mode when transmitting
|
||||
static esp_line_endings_t s_tx_mode =
|
||||
#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
|
||||
ESP_LINE_ENDINGS_CRLF;
|
||||
#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR
|
||||
ESP_LINE_ENDINGS_CR;
|
||||
#else
|
||||
ESP_LINE_ENDINGS_LF;
|
||||
#endif
|
||||
|
||||
// Newline conversion mode when receiving
|
||||
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
|
||||
#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
|
||||
ESP_LINE_ENDINGS_CR
|
||||
#else
|
||||
ESP_LINE_ENDINGS_LF
|
||||
#endif
|
||||
};
|
||||
|
||||
static void uart_end_select();
|
||||
|
||||
// Functions used to write bytes to UART. Default to "basic" functions.
|
||||
static tx_func_t s_uart_tx_func[UART_NUM] = {
|
||||
&uart_tx_char, &uart_tx_char, &uart_tx_char
|
||||
};
|
||||
|
||||
// Functions used to read bytes from UART. Default to "basic" functions.
|
||||
static rx_func_t s_uart_rx_func[UART_NUM] = {
|
||||
&uart_rx_char, &uart_rx_char, &uart_rx_char
|
||||
};
|
||||
|
||||
|
||||
static int uart_open(const char * path, int flags, int mode)
|
||||
{
|
||||
// this is fairly primitive, we should check if file is opened read only,
|
||||
// and error out if write is requested
|
||||
int fd = -1;
|
||||
|
||||
if (strcmp(path, "/0") == 0) {
|
||||
fd = 0;
|
||||
} else if (strcmp(path, "/1") == 0) {
|
||||
fd = 1;
|
||||
} else if (strcmp(path, "/2") == 0) {
|
||||
fd = 2;
|
||||
} else {
|
||||
errno = ENOENT;
|
||||
return fd;
|
||||
}
|
||||
|
||||
s_non_blocking[fd] = ((flags & O_NONBLOCK) == O_NONBLOCK);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void uart_tx_char(int fd, int c)
|
||||
{
|
||||
uart_dev_t* uart = s_uarts[fd];
|
||||
while (uart->status.txfifo_cnt >= 127) {
|
||||
;
|
||||
}
|
||||
uart->fifo.rw_byte = c;
|
||||
}
|
||||
|
||||
static void uart_tx_char_via_driver(int fd, int c)
|
||||
{
|
||||
char ch = (char) c;
|
||||
uart_write_bytes(fd, &ch, 1);
|
||||
}
|
||||
|
||||
static int uart_rx_char(int fd)
|
||||
{
|
||||
uart_dev_t* uart = s_uarts[fd];
|
||||
if (uart->status.rxfifo_cnt == 0) {
|
||||
return NONE;
|
||||
}
|
||||
return uart->fifo.rw_byte;
|
||||
}
|
||||
|
||||
static int uart_rx_char_via_driver(int fd)
|
||||
{
|
||||
uint8_t c;
|
||||
int timeout = s_non_blocking[fd] ? 0 : portMAX_DELAY;
|
||||
int n = uart_read_bytes(fd, &c, 1, timeout);
|
||||
if (n <= 0) {
|
||||
return NONE;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static ssize_t uart_write(int fd, const void * data, size_t size)
|
||||
{
|
||||
assert(fd >=0 && fd < 3);
|
||||
const char *data_c = (const char *)data;
|
||||
/* Even though newlib does stream locking on each individual stream, we need
|
||||
* a dedicated UART lock if two streams (stdout and stderr) point to the
|
||||
* same UART.
|
||||
*/
|
||||
_lock_acquire_recursive(&s_uart_write_locks[fd]);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
int c = data_c[i];
|
||||
if (c == '\n' && s_tx_mode != ESP_LINE_ENDINGS_LF) {
|
||||
s_uart_tx_func[fd](fd, '\r');
|
||||
if (s_tx_mode == ESP_LINE_ENDINGS_CR) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
s_uart_tx_func[fd](fd, c);
|
||||
}
|
||||
_lock_release_recursive(&s_uart_write_locks[fd]);
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Helper function which returns a previous character or reads a new one from
|
||||
* UART. Previous character can be returned ("pushed back") using
|
||||
* uart_return_char function.
|
||||
*/
|
||||
static int uart_read_char(int fd)
|
||||
{
|
||||
/* return character from peek buffer, if it is there */
|
||||
if (s_peek_char[fd] != NONE) {
|
||||
int c = s_peek_char[fd];
|
||||
s_peek_char[fd] = NONE;
|
||||
return c;
|
||||
}
|
||||
return s_uart_rx_func[fd](fd);
|
||||
}
|
||||
|
||||
/* Push back a character; it will be returned by next call to uart_read_char */
|
||||
static void uart_return_char(int fd, int c)
|
||||
{
|
||||
assert(s_peek_char[fd] == NONE);
|
||||
s_peek_char[fd] = c;
|
||||
}
|
||||
|
||||
static ssize_t uart_read(int fd, void* data, size_t size)
|
||||
{
|
||||
assert(fd >=0 && fd < 3);
|
||||
char *data_c = (char *) data;
|
||||
size_t received = 0;
|
||||
_lock_acquire_recursive(&s_uart_read_locks[fd]);
|
||||
while (received < size) {
|
||||
int c = uart_read_char(fd);
|
||||
if (c == '\r') {
|
||||
if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CR) {
|
||||
c = '\n';
|
||||
} else if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CRLF) {
|
||||
/* look ahead */
|
||||
int c2 = uart_read_char(fd);
|
||||
if (c2 == NONE) {
|
||||
/* could not look ahead, put the current character back */
|
||||
uart_return_char(fd, c);
|
||||
break;
|
||||
}
|
||||
if (c2 == '\n') {
|
||||
/* this was \r\n sequence. discard \r, return \n */
|
||||
c = '\n';
|
||||
} else {
|
||||
/* \r followed by something else. put the second char back,
|
||||
* it will be processed on next iteration. return \r now.
|
||||
*/
|
||||
uart_return_char(fd, c2);
|
||||
}
|
||||
}
|
||||
} else if (c == NONE) {
|
||||
break;
|
||||
}
|
||||
data_c[received] = (char) c;
|
||||
++received;
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_lock_release_recursive(&s_uart_read_locks[fd]);
|
||||
if (received > 0) {
|
||||
return received;
|
||||
}
|
||||
errno = EWOULDBLOCK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int uart_fstat(int fd, struct stat * st)
|
||||
{
|
||||
assert(fd >=0 && fd < 3);
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uart_close(int fd)
|
||||
{
|
||||
assert(fd >=0 && fd < 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uart_fcntl(int fd, int cmd, va_list args)
|
||||
{
|
||||
assert(fd >=0 && fd < 3);
|
||||
int result = 0;
|
||||
if (cmd == F_GETFL) {
|
||||
if (s_non_blocking[fd]) {
|
||||
result |= O_NONBLOCK;
|
||||
}
|
||||
} else if (cmd == F_SETFL) {
|
||||
int arg = va_arg(args, int);
|
||||
s_non_blocking[fd] = (arg & O_NONBLOCK) != 0;
|
||||
} else {
|
||||
// unsupported operation
|
||||
result = -1;
|
||||
errno = ENOSYS;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int uart_access(const char *path, int amode)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (strcmp(path, "/0") == 0 || strcmp(path, "/1") == 0 || strcmp(path, "/2") == 0) {
|
||||
if (F_OK == amode) {
|
||||
ret = 0; //path exists
|
||||
} else {
|
||||
if ((((amode & R_OK) == R_OK) || ((amode & W_OK) == W_OK)) && ((amode & X_OK) != X_OK)) {
|
||||
ret = 0; //path is readable and/or writable but not executable
|
||||
} else {
|
||||
errno = EACCES;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errno = ENOENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void select_notif_callback(uart_port_t uart_num, uart_select_notif_t uart_select_notif, BaseType_t *task_woken)
|
||||
{
|
||||
switch (uart_select_notif) {
|
||||
case UART_SELECT_READ_NOTIF:
|
||||
if (FD_ISSET(uart_num, _readfds_orig)) {
|
||||
FD_SET(uart_num, _readfds);
|
||||
esp_vfs_select_triggered_isr(_signal_sem, task_woken);
|
||||
}
|
||||
break;
|
||||
case UART_SELECT_WRITE_NOTIF:
|
||||
if (FD_ISSET(uart_num, _writefds_orig)) {
|
||||
FD_SET(uart_num, _writefds);
|
||||
esp_vfs_select_triggered_isr(_signal_sem, task_woken);
|
||||
}
|
||||
break;
|
||||
case UART_SELECT_ERROR_NOTIF:
|
||||
if (FD_ISSET(uart_num, _errorfds_orig)) {
|
||||
FD_SET(uart_num, _errorfds);
|
||||
esp_vfs_select_triggered_isr(_signal_sem, task_woken);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, SemaphoreHandle_t *signal_sem)
|
||||
{
|
||||
if (_lock_try_acquire(&s_one_select_lock)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
const int max_fds = MIN(nfds, UART_NUM);
|
||||
|
||||
portENTER_CRITICAL(uart_get_selectlock());
|
||||
|
||||
if (_readfds || _writefds || _errorfds || _readfds_orig || _writefds_orig || _errorfds_orig || _signal_sem) {
|
||||
portEXIT_CRITICAL(uart_get_selectlock());
|
||||
uart_end_select();
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if ((_readfds_orig = malloc(sizeof(fd_set))) == NULL) {
|
||||
portEXIT_CRITICAL(uart_get_selectlock());
|
||||
uart_end_select();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if ((_writefds_orig = malloc(sizeof(fd_set))) == NULL) {
|
||||
portEXIT_CRITICAL(uart_get_selectlock());
|
||||
uart_end_select();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if ((_errorfds_orig = malloc(sizeof(fd_set))) == NULL) {
|
||||
portEXIT_CRITICAL(uart_get_selectlock());
|
||||
uart_end_select();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
//uart_set_select_notif_callback set the callbacks in UART ISR
|
||||
for (int i = 0; i < max_fds; ++i) {
|
||||
if (FD_ISSET(i, readfds) || FD_ISSET(i, writefds) || FD_ISSET(i, exceptfds)) {
|
||||
uart_set_select_notif_callback(i, select_notif_callback);
|
||||
}
|
||||
}
|
||||
|
||||
_signal_sem = signal_sem;
|
||||
|
||||
_readfds = readfds;
|
||||
_writefds = writefds;
|
||||
_errorfds = exceptfds;
|
||||
|
||||
*_readfds_orig = *readfds;
|
||||
*_writefds_orig = *writefds;
|
||||
*_errorfds_orig = *exceptfds;
|
||||
|
||||
FD_ZERO(readfds);
|
||||
FD_ZERO(writefds);
|
||||
FD_ZERO(exceptfds);
|
||||
|
||||
for (int i = 0; i < max_fds; ++i) {
|
||||
if (FD_ISSET(i, _readfds_orig)) {
|
||||
size_t buffered_size;
|
||||
if (uart_get_buffered_data_len(i, &buffered_size) == ESP_OK && buffered_size > 0) {
|
||||
// signalize immediately when data is buffered
|
||||
FD_SET(i, _readfds);
|
||||
esp_vfs_select_triggered(_signal_sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL(uart_get_selectlock());
|
||||
// s_one_select_lock is not released on successfull exit - will be
|
||||
// released in uart_end_select()
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void uart_end_select()
|
||||
{
|
||||
portENTER_CRITICAL(uart_get_selectlock());
|
||||
for (int i = 0; i < UART_NUM; ++i) {
|
||||
uart_set_select_notif_callback(i, NULL);
|
||||
}
|
||||
|
||||
_signal_sem = NULL;
|
||||
|
||||
_readfds = NULL;
|
||||
_writefds = NULL;
|
||||
_errorfds = NULL;
|
||||
|
||||
if (_readfds_orig) {
|
||||
free(_readfds_orig);
|
||||
_readfds_orig = NULL;
|
||||
}
|
||||
|
||||
if (_writefds_orig) {
|
||||
free(_writefds_orig);
|
||||
_writefds_orig = NULL;
|
||||
}
|
||||
|
||||
if (_errorfds_orig) {
|
||||
free(_errorfds_orig);
|
||||
_errorfds_orig = NULL;
|
||||
}
|
||||
portEXIT_CRITICAL(uart_get_selectlock());
|
||||
_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;
|
||||
}
|
||||
|
||||
/* FALLTHRU */
|
||||
|
||||
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 = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
.write = &uart_write,
|
||||
.open = &uart_open,
|
||||
.fstat = &uart_fstat,
|
||||
.close = &uart_close,
|
||||
.read = &uart_read,
|
||||
.fcntl = &uart_fcntl,
|
||||
.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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
s_tx_mode = mode;
|
||||
}
|
||||
|
||||
void esp_vfs_dev_uart_use_nonblocking(int uart_num)
|
||||
{
|
||||
_lock_acquire_recursive(&s_uart_read_locks[uart_num]);
|
||||
_lock_acquire_recursive(&s_uart_write_locks[uart_num]);
|
||||
s_uart_tx_func[uart_num] = uart_tx_char;
|
||||
s_uart_rx_func[uart_num] = uart_rx_char;
|
||||
_lock_release_recursive(&s_uart_write_locks[uart_num]);
|
||||
_lock_release_recursive(&s_uart_read_locks[uart_num]);
|
||||
}
|
||||
|
||||
void esp_vfs_dev_uart_use_driver(int uart_num)
|
||||
{
|
||||
_lock_acquire_recursive(&s_uart_read_locks[uart_num]);
|
||||
_lock_acquire_recursive(&s_uart_write_locks[uart_num]);
|
||||
s_uart_tx_func[uart_num] = uart_tx_char_via_driver;
|
||||
s_uart_rx_func[uart_num] = uart_rx_char_via_driver;
|
||||
_lock_release_recursive(&s_uart_write_locks[uart_num]);
|
||||
_lock_release_recursive(&s_uart_read_locks[uart_num]);
|
||||
}
|
6
examples/peripherals/uart_select/CMakeLists.txt
Normal file
6
examples/peripherals/uart_select/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(uart_select)
|
8
examples/peripherals/uart_select/Makefile
Normal file
8
examples/peripherals/uart_select/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := uart_select
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
13
examples/peripherals/uart_select/README.md
Normal file
13
examples/peripherals/uart_select/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# UART Select Example
|
||||
|
||||
The UART select example is for demonstrating the use of `select()` for
|
||||
synchronous I/O multiplexing on the UART interface. The example waits for a
|
||||
character from UART using `select()` until a blocking read without delay or a
|
||||
successful non-blocking read is possible.
|
||||
|
||||
Please note that the same result can be achieved by using `uart_read_bytes()`
|
||||
but the use of `select()` allows to use it together with other virtual
|
||||
file system (VFS) drivers, e.g. LWIP sockets. For a more comprehensive example
|
||||
please refer to `system/select`.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
4
examples/peripherals/uart_select/main/CMakeLists.txt
Normal file
4
examples/peripherals/uart_select/main/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "uart_select_example_main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
3
examples/peripherals/uart_select/main/component.mk
Normal file
3
examples/peripherals/uart_select/main/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
@ -0,0 +1,93 @@
|
||||
/* UART Select Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_dev.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
static const char* TAG = "uart_select_example";
|
||||
|
||||
static void uart_select_task()
|
||||
{
|
||||
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_0, &uart_config);
|
||||
uart_driver_install(UART_NUM_0, 2*1024, 0, 0, NULL, 0);
|
||||
|
||||
while (1) {
|
||||
int fd;
|
||||
|
||||
if ((fd = open("/dev/uart/0", O_RDWR)) == -1) {
|
||||
ESP_LOGE(TAG, "Cannot open UART");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a driver now installed so set up the read/write functions to use driver also.
|
||||
esp_vfs_dev_uart_use_driver(0);
|
||||
|
||||
while (1) {
|
||||
int s;
|
||||
fd_set rfds;
|
||||
struct timeval tv = {
|
||||
.tv_sec = 5,
|
||||
.tv_usec = 0,
|
||||
};
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
|
||||
s = select(fd + 1, &rfds, NULL, NULL, &tv);
|
||||
|
||||
if (s < 0) {
|
||||
ESP_LOGE(TAG, "Select failed: errno %d", errno);
|
||||
break;
|
||||
} else if (s == 0) {
|
||||
ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");
|
||||
} else {
|
||||
if (FD_ISSET(fd, &rfds)) {
|
||||
char buf;
|
||||
if (read(fd, &buf, 1) > 0) {
|
||||
ESP_LOGI(TAG, "Received: %c", buf);
|
||||
// Note: Only one character was read even the buffer contains more. The other characters will
|
||||
// be read one-by-one by subsequent calls to select() which will then return immediately
|
||||
// without timeout.
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UART read error");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No FD has been set in select()");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreate(uart_select_task, "uart_select_task", 4*1024, NULL, 5, NULL);
|
||||
}
|
Reference in New Issue
Block a user