Merge branch 'feature/vfs_select' into 'master'

Allow VFS file descriptors in select()

See merge request 
pull/1979/head
Angus Gratton 2018-05-18 15:04:25 +08:00
commit 3c457afca5
24 zmienionych plików z 1250 dodań i 17 usunięć
components
driver
include/driver
lwip
include/lwip/lwip
newlib
include/sys
docs/en/api-reference/peripherals
tools/unit-test-app/main

Wyświetl plik

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

Wyświetl plik

@ -29,6 +29,7 @@
#include "soc/uart_struct.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "driver/uart_select.h"
#define XOFF (char)0x13
#define XON (char)0x11
@ -100,12 +101,14 @@ typedef struct {
uint8_t tx_brk_flg; /*!< Flag to indicate to send a break signal in the end of the item sending procedure */
uint8_t tx_brk_len; /*!< TX break signal cycle length/number */
uint8_t tx_waiting_brk; /*!< Flag to indicate that TX FIFO is ready to send break signal after FIFO is empty, do not push data into TX FIFO right now.*/
uart_select_notif_callback_t uart_select_notif_callback; /*!< Notification about select() events */
} uart_obj_t;
static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0};
/* DRAM_ATTR is required to avoid UART array placed in flash, due to accessed from ISR */
static DRAM_ATTR uart_dev_t* const UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2};
static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED};
static portMUX_TYPE uart_selectlock = portMUX_INITIALIZER_UNLOCKED;
esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit)
{
@ -835,6 +838,11 @@ static void uart_rx_intr_handler_default(void *param)
uart_clear_intr_status(uart_num, UART_RXFIFO_TOUT_INT_CLR_M | UART_RXFIFO_FULL_INT_CLR_M);
uart_event.type = UART_DATA;
uart_event.size = rx_fifo_len;
UART_ENTER_CRITICAL_ISR(&uart_selectlock);
if (p_uart->uart_select_notif_callback) {
p_uart->uart_select_notif_callback(uart_num, UART_SELECT_READ_NOTIF, &HPTaskAwoken);
}
UART_EXIT_CRITICAL_ISR(&uart_selectlock);
}
p_uart->rx_stash_len = rx_fifo_len;
//If we fail to push data to ring buffer, we will have to stash the data, and send next time.
@ -893,15 +901,30 @@ static void uart_rx_intr_handler_default(void *param)
uart_reg->int_clr.rxfifo_ovf = 1;
UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]);
uart_event.type = UART_FIFO_OVF;
UART_ENTER_CRITICAL_ISR(&uart_selectlock);
if (p_uart->uart_select_notif_callback) {
p_uart->uart_select_notif_callback(uart_num, UART_SELECT_ERROR_NOTIF, &HPTaskAwoken);
}
UART_EXIT_CRITICAL_ISR(&uart_selectlock);
} else if(uart_intr_status & UART_BRK_DET_INT_ST_M) {
uart_reg->int_clr.brk_det = 1;
uart_event.type = UART_BREAK;
} else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) {
uart_reg->int_clr.frm_err = 1;
uart_event.type = UART_FRAME_ERR;
UART_ENTER_CRITICAL_ISR(&uart_selectlock);
if (p_uart->uart_select_notif_callback) {
p_uart->uart_select_notif_callback(uart_num, UART_SELECT_ERROR_NOTIF, &HPTaskAwoken);
}
UART_EXIT_CRITICAL_ISR(&uart_selectlock);
} else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) {
uart_reg->int_clr.parity_err = 1;
uart_event.type = UART_PARITY_ERR;
UART_ENTER_CRITICAL_ISR(&uart_selectlock);
if (p_uart->uart_select_notif_callback) {
p_uart->uart_select_notif_callback(uart_num, UART_SELECT_ERROR_NOTIF, &HPTaskAwoken);
}
UART_EXIT_CRITICAL_ISR(&uart_selectlock);
} else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) {
UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]);
uart_reg->conf0.txd_brk = 0;
@ -1259,6 +1282,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b
p_uart_obj[uart_num]->tx_ring_buf = NULL;
p_uart_obj[uart_num]->tx_buf_size = 0;
}
p_uart_obj[uart_num]->uart_select_notif_callback = NULL;
} else {
ESP_LOGE(UART_TAG, "UART driver already installed");
return ESP_FAIL;
@ -1346,3 +1370,15 @@ esp_err_t uart_driver_delete(uart_port_t uart_num)
}
return ESP_OK;
}
void uart_set_select_notif_callback(uart_port_t uart_num, uart_select_notif_callback_t uart_select_notif_callback)
{
if (uart_num < UART_NUM_MAX && p_uart_obj[uart_num]) {
p_uart_obj[uart_num]->uart_select_notif_callback = (uart_select_notif_callback_t) uart_select_notif_callback;
}
}
portMUX_TYPE *uart_get_selectlock()
{
return &uart_selectlock;
}

Wyświetl plik

@ -38,6 +38,7 @@
#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
#include <sys/select.h>
#include <stddef.h> /* for size_t */
#include <string.h> /* for FD_ZERO */
@ -589,8 +590,10 @@ static inline int sendto(int s,const void *dataptr,size_t size,int flags,const s
{ return lwip_sendto_r(s,dataptr,size,flags,to,tolen); }
static inline int socket(int domain,int type,int protocol)
{ return lwip_socket(domain,type,protocol); }
#ifndef ESP_PLATFORM
static inline int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout)
{ return lwip_select(maxfdp1,readset,writeset,exceptset,timeout); }
#endif /* ESP_PLATFORM */
static inline int ioctlsocket(int s,long cmd,void *argp)
{ return lwip_ioctl_r(s,cmd,argp); }
@ -643,8 +646,10 @@ static inline int sendto(int s,const void *dataptr,size_t size,int flags,const s
{ return lwip_sendto(s,dataptr,size,flags,to,tolen); }
static inline int socket(int domain,int type,int protocol)
{ return lwip_socket(domain,type,protocol); }
#ifndef ESP_PLATFORM
static inline int select(int maxfdp1,fd_set t*readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout)
{ return lwip_select(maxfdp1,readset,writeset,exceptset,timeout); }
#endif /* ESP_PLATFORM */
static inline int ioctlsocket(int s,long cmd,void *argp)
{ return lwip_ioctl(s,cmd,argp); }

Wyświetl plik

@ -148,6 +148,10 @@ err_t sys_sem_new(sys_sem_t *sem, u8_t count);
/** Signals a semaphore
* @param sem the semaphore to signal */
void sys_sem_signal(sys_sem_t *sem);
/** Signals a semaphore (ISR version)
* @param sem the semaphore to signal
* @return non-zero if a higher priority task has been woken */
int sys_sem_signal_isr(sys_sem_t *sem);
/** Wait for a semaphore for the specified timeout
* @param sem the semaphore to wait for
* @param timeout timeout in milliseconds to wait (0 = wait forever)

Wyświetl plik

@ -133,6 +133,15 @@ sys_sem_signal(sys_sem_t *sem)
xSemaphoreGive(*sem);
}
/*-----------------------------------------------------------------------------------*/
// Signals a semaphore (from ISR)
int sys_sem_signal_isr(sys_sem_t *sem)
{
BaseType_t woken = pdFALSE;
xSemaphoreGiveFromISR(*sem, &woken);
return woken == pdTRUE;
}
/*-----------------------------------------------------------------------------------*/
/*
Blocks the thread while waiting for the semaphore to be

Wyświetl plik

@ -24,15 +24,31 @@
#include "soc/uart_struct.h"
#include "lwip/sockets.h"
#include "sdkconfig.h"
/* Non-LWIP file descriptors are from 0 to (LWIP_SOCKET_OFFSET-1).
* LWIP file descriptors are from LWIP_SOCKET_OFFSET to FD_SETSIZE-1.
*/
#include "lwip/sys.h"
_Static_assert(MAX_FDS >= CONFIG_LWIP_MAX_SOCKETS, "MAX_FDS < CONFIG_LWIP_MAX_SOCKETS");
static int lwip_fcntl_r_wrapper(int fd, int cmd, va_list args);
static int lwip_ioctl_r_wrapper(int fd, int cmd, va_list args);
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()
{
@ -45,17 +61,14 @@ void esp_vfs_lwip_sockets_register()
.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));
}
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 *));
}

Wyświetl plik

@ -0,0 +1,31 @@
// 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 __ESP_SYS_SELECT_H__
#define __ESP_SYS_SELECT_H__
#include <sys/types.h>
#include <sys/time.h>
#ifdef __cplusplus
extern "C" {
#endif
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //__ESP_SYS_SELECT_H__

Wyświetl plik

@ -0,0 +1,21 @@
// 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"
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
{
return esp_vfs_select(nfds, readfds, writefds, errorfds, timeout);
}

Wyświetl plik

@ -64,6 +64,35 @@ Case 2: API functions are declared with an extra context pointer (FS driver supp
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.
Paths
-----

Wyświetl plik

@ -25,7 +25,9 @@
#include <sys/types.h>
#include <sys/reent.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
@ -167,6 +169,16 @@ typedef struct
int (*access_p)(void* ctx, const char *path, int amode);
int (*access)(const char *path, int amode);
};
/** 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);
/** 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;
@ -235,6 +247,50 @@ 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.
*/
void esp_vfs_select_triggered();
/**
* @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 woken is set to pdTRUE if the function wakes up a task with higher priority
*/
void esp_vfs_select_triggered_isr(BaseType_t *woken);
#ifdef __cplusplus
} // extern "C"
#endif

Wyświetl plik

@ -0,0 +1,284 @@
// 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 <stdio.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/param.h>
#include "unity.h"
#include "soc/uart_struct.h"
#include "freertos/FreeRTOS.h"
#include "driver/uart.h"
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
typedef struct {
int fd;
int delay_ms;
xSemaphoreHandle sem;
} test_task_param_t;
static const char message[] = "Hello world!";
static int socket_init()
{
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_DGRAM,
};
struct addrinfo *res;
int err;
struct sockaddr_in saddr = { 0 };
int socket_fd = -1;
err = getaddrinfo("localhost", "80", &hints, &res);
TEST_ASSERT_EQUAL(err, 0);
TEST_ASSERT_NOT_NULL(res);
socket_fd = socket(res->ai_family, res->ai_socktype, 0);
TEST_ASSERT(socket_fd >= 0);
saddr.sin_family = PF_INET;
saddr.sin_port = htons(80);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
err = bind(socket_fd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in));
TEST_ASSERT(err >= 0);
err = connect(socket_fd, res->ai_addr, res->ai_addrlen);
TEST_ASSERT_EQUAL_MESSAGE(err, 0, "Socket connection failed");
freeaddrinfo(res);
return socket_fd;
}
static void uart1_init()
{
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);
}
static void send_task(void *param)
{
const test_task_param_t *test_task_param = param;
vTaskDelay(test_task_param->delay_ms / portTICK_PERIOD_MS);
write(test_task_param->fd, message, sizeof(message));
if (test_task_param->sem) {
xSemaphoreGive(test_task_param->sem);
}
vTaskDelete(NULL);
}
static inline void start_task(const test_task_param_t *test_task_param)
{
xTaskCreate(send_task, "send_task", 4*1024, (void *) test_task_param, 5, NULL);
}
static void init(int *uart_fd, int *socket_fd)
{
uart1_init();
UART1.conf0.loopback = 1;
*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);
*socket_fd = socket_init();
}
static void deinit(int uart_fd, int socket_fd)
{
esp_vfs_dev_uart_use_nonblocking(1);
close(uart_fd);
UART1.conf0.loopback = 0;
uart_driver_delete(UART_NUM_1);
close(socket_fd);
}
TEST_CASE("UART can do select()", "[vfs]")
{
int uart_fd;
int socket_fd;
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 100000,
};
char recv_message[sizeof(message)];
init(&uart_fd, &socket_fd);
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(uart_fd, &rfds);
//without socket in rfds it will not use the same signalization
const test_task_param_t test_task_param = {
.fd = uart_fd,
.delay_ms = 50,
.sem = xSemaphoreCreateBinary(),
};
TEST_ASSERT_NOT_NULL(test_task_param.sem);
start_task(&test_task_param);
int s = select(uart_fd + 1, &rfds, NULL, NULL, &tv);
TEST_ASSERT_EQUAL(s, 1);
TEST_ASSERT(FD_ISSET(uart_fd, &rfds));
TEST_ASSERT_UNLESS(FD_ISSET(socket_fd, &rfds));
int read_bytes = read(uart_fd, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
FD_ZERO(&rfds);
FD_SET(uart_fd, &rfds);
FD_SET(socket_fd, &rfds);
start_task(&test_task_param);
s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
TEST_ASSERT_EQUAL(s, 1);
TEST_ASSERT(FD_ISSET(uart_fd, &rfds));
TEST_ASSERT_UNLESS(FD_ISSET(socket_fd, &rfds));
read_bytes = read(uart_fd, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
vSemaphoreDelete(test_task_param.sem);
deinit(uart_fd, socket_fd);
}
TEST_CASE("socket can do select()", "[vfs]")
{
int uart_fd;
int socket_fd;
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 100000,
};
char recv_message[sizeof(message)];
init(&uart_fd, &socket_fd);
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(uart_fd, &rfds);
FD_SET(socket_fd, &rfds);
const test_task_param_t test_task_param = {
.fd = socket_fd,
.delay_ms = 50,
.sem = xSemaphoreCreateBinary(),
};
TEST_ASSERT_NOT_NULL(test_task_param.sem);
start_task(&test_task_param);
const int s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
TEST_ASSERT_EQUAL(s, 1);
TEST_ASSERT_UNLESS(FD_ISSET(uart_fd, &rfds));
TEST_ASSERT(FD_ISSET(socket_fd, &rfds));
int read_bytes = read(socket_fd, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
vSemaphoreDelete(test_task_param.sem);
deinit(uart_fd, socket_fd);
}
TEST_CASE("select() timeout", "[vfs]")
{
int uart_fd;
int socket_fd;
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 100000,
};
init(&uart_fd, &socket_fd);
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(uart_fd, &rfds);
FD_SET(socket_fd, &rfds);
int s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
TEST_ASSERT_EQUAL(s, 0);
TEST_ASSERT_UNLESS(FD_ISSET(uart_fd, &rfds));
TEST_ASSERT_UNLESS(FD_ISSET(socket_fd, &rfds));
FD_ZERO(&rfds);
s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
TEST_ASSERT_EQUAL(s, 0);
TEST_ASSERT_UNLESS(FD_ISSET(uart_fd, &rfds));
TEST_ASSERT_UNLESS(FD_ISSET(socket_fd, &rfds));
deinit(uart_fd, socket_fd);
}
static void select_task(void *param)
{
const test_task_param_t *test_task_param = param;
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 100000,
};
int s = select(1, NULL, NULL, NULL, &tv);
TEST_ASSERT_EQUAL(s, 0); //timeout
if (test_task_param->sem) {
xSemaphoreGive(test_task_param->sem);
}
vTaskDelete(NULL);
}
TEST_CASE("concurent select() fails", "[vfs]")
{
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 100000,//irrelevant
};
const test_task_param_t test_task_param = {
.sem = xSemaphoreCreateBinary(),
};
TEST_ASSERT_NOT_NULL(test_task_param.sem);
xTaskCreate(select_task, "select_task", 4*1024, (void *) &test_task_param, 5, NULL);
vTaskDelay(10 / portTICK_PERIOD_MS); //make sure the task has started and waits in select()
int s = select(1, NULL, NULL, NULL, &tv);
TEST_ASSERT_EQUAL(s, -1); //this select should fail because the other one is "waiting"
TEST_ASSERT_EQUAL(errno, EINTR);
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
vSemaphoreDelete(test_task_param.sem);
}

Wyświetl plik

@ -20,7 +20,10 @@
#include <sys/ioctl.h>
#include <sys/unistd.h>
#include <sys/lock.h>
#include <sys/param.h>
#include <dirent.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_vfs.h"
#include "esp_log.h"
@ -49,12 +52,28 @@ typedef struct vfs_entry_ {
int offset; // index of this structure in s_vfs array
} vfs_entry_t;
typedef struct {
bool isset; // none or at least one bit is set in the following 3 fd sets
fd_set readfds;
fd_set writefds;
fd_set errorfds;
} fds_triple_t;
static vfs_entry_t* s_vfs[VFS_MAX_COUNT] = { 0 };
static size_t s_vfs_count = 0;
static fd_table_t s_fd_table[MAX_FDS] = { [0 ... MAX_FDS-1] = FD_TABLE_ENTRY_UNUSED };
static _lock_t s_fd_table_lock;
/* Semaphore used for waiting select events from other VFS drivers when socket
* select is not used (not registered or socket FDs are not observed by the
* given call of select)
*/
static SemaphoreHandle_t s_select_sem = NULL;
/* Lock ensuring that select is called from only one task at the time */
static _lock_t s_one_select_lock;
static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, int *vfs_index)
{
if (len != LEN_PATH_PREFIX_IGNORED) {
@ -637,3 +656,214 @@ int access(const char *path, int amode)
CHECK_AND_CALL(ret, r, vfs, access, path_within_vfs, amode);
return ret;
}
static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple)
{
for (int i = 0; i < end_index; ++i) {
const vfs_entry_t *vfs = get_vfs_for_index(i);
const fds_triple_t *item = &vfs_fds_triple[i];
if (vfs && vfs->vfs.end_select && item->isset) {
vfs->vfs.end_select();
}
}
}
static int set_global_fd_sets(const fds_triple_t *vfs_fds_triple, int size, fd_set *readfds, fd_set *writefds, fd_set *errorfds)
{
int ret = 0;
for (int i = 0; i < size; ++i) {
const fds_triple_t *item = &vfs_fds_triple[i];
if (item->isset) {
for (int fd = 0; fd < MAX_FDS; ++fd) {
const int local_fd = s_fd_table[fd].local_fd; // single read -> no locking is required
if (readfds && FD_ISSET(local_fd, &item->readfds)) {
FD_SET(fd, readfds);
++ret;
}
if (writefds && FD_ISSET(local_fd, &item->writefds)) {
FD_SET(fd, writefds);
++ret;
}
if (errorfds && FD_ISSET(local_fd, &item->errorfds)) {
FD_SET(fd, errorfds);
++ret;
}
}
}
}
return ret;
}
int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
{
int ret = 0;
struct _reent* r = __getreent();
if (nfds > MAX_FDS || nfds < 0) {
__errno_r(r) = EINVAL;
return -1;
}
if (_lock_try_acquire(&s_one_select_lock)) {
__errno_r(r) = EINTR;
return -1;
}
fds_triple_t *vfs_fds_triple;
if ((vfs_fds_triple = calloc(s_vfs_count, sizeof(fds_triple_t))) == NULL) {
__errno_r(r) = ENOMEM;
_lock_release(&s_one_select_lock);
return -1;
}
int (*socket_select)(int, fd_set *, fd_set *, fd_set *, struct timeval *) = NULL;
for (int fd = 0; fd < nfds; ++fd) {
_lock_acquire(&s_fd_table_lock);
const bool is_socket_fd = s_fd_table[fd].permanent;
const int vfs_index = s_fd_table[fd].vfs_index;
const int local_fd = s_fd_table[fd].local_fd;
_lock_release(&s_fd_table_lock);
if (vfs_index < 0) {
continue;
}
if (!socket_select && is_socket_fd) {
// no socket_select found yet and the fd is for a socket so take a look
if ((readfds && FD_ISSET(fd, readfds)) ||
(writefds && FD_ISSET(fd, writefds)) ||
(errorfds && FD_ISSET(fd, errorfds))) {
const vfs_entry_t *vfs = s_vfs[vfs_index];
socket_select = vfs->vfs.socket_select;
}
continue;
}
fds_triple_t *item = &vfs_fds_triple[vfs_index]; // FD sets for VFS which belongs to fd
if (readfds && FD_ISSET(fd, readfds)) {
item->isset = true;
FD_SET(local_fd, &item->readfds);
FD_CLR(fd, readfds);
}
if (writefds && FD_ISSET(fd, writefds)) {
item->isset = true;
FD_SET(local_fd, &item->writefds);
FD_CLR(fd, writefds);
}
if (errorfds && FD_ISSET(fd, errorfds)) {
item->isset = true;
FD_SET(local_fd, &item->errorfds);
FD_CLR(fd, errorfds);
}
}
// all non-socket VFSs have their FD sets in vfs_fds_triple
// the global readfds, writefds and errorfds contain only socket FDs (if
// there any)
if (!socket_select) {
// There is no socket VFS registered or select() wasn't called for
// any socket. Therefore, we will use our own signalization.
if ((s_select_sem = xSemaphoreCreateBinary()) == NULL) {
free(vfs_fds_triple);
__errno_r(r) = ENOMEM;
_lock_release(&s_one_select_lock);
return -1;
}
}
for (int i = 0; i < s_vfs_count; ++i) {
const vfs_entry_t *vfs = get_vfs_for_index(i);
fds_triple_t *item = &vfs_fds_triple[i];
if (vfs && vfs->vfs.start_select && item->isset) {
// call start_select for all non-socket VFSs with has at least one FD set in readfds, writefds, or errorfds
// note: it can point to socket VFS but item->isset will be false for that
esp_err_t err = vfs->vfs.start_select(nfds, &item->readfds, &item->writefds, &item->errorfds);
if (err != ESP_OK) {
call_end_selects(i, vfs_fds_triple);
(void) set_global_fd_sets(vfs_fds_triple, s_vfs_count, readfds, writefds, errorfds);
if (s_select_sem) {
vSemaphoreDelete(s_select_sem);
s_select_sem = NULL;
}
free(vfs_fds_triple);
__errno_r(r) = ENOMEM;
_lock_release(&s_one_select_lock);
return -1;
}
}
}
if (socket_select) {
ret = socket_select(nfds, readfds, writefds, errorfds, timeout);
} else {
if (readfds) {
FD_ZERO(readfds);
}
if (writefds) {
FD_ZERO(writefds);
}
if (errorfds) {
FD_ZERO(errorfds);
}
TickType_t ticks_to_wait = portMAX_DELAY;
if (timeout) {
uint32_t timeout_ms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
ticks_to_wait = timeout_ms / portTICK_PERIOD_MS;
}
xSemaphoreTake(s_select_sem, ticks_to_wait);
}
call_end_selects(s_vfs_count, vfs_fds_triple); // for VFSs for start_select was called before
if (ret >= 0) {
ret += set_global_fd_sets(vfs_fds_triple, s_vfs_count, readfds, writefds, errorfds);
}
if (s_select_sem) {
vSemaphoreDelete(s_select_sem);
s_select_sem = NULL;
}
free(vfs_fds_triple);
_lock_release(&s_one_select_lock);
return ret;
}
void esp_vfs_select_triggered()
{
if (s_select_sem) {
xSemaphoreGive(s_select_sem);
} else {
// Another way would be to go through s_fd_table and find the VFS
// which has a permanent FD. But in order to avoid to lock
// s_fd_table_lock we go through the VFS table.
for (int i = 0; i < s_vfs_count; ++i) {
const vfs_entry_t *vfs = s_vfs[i];
if (vfs != NULL && vfs->vfs.stop_socket_select != NULL) {
vfs->vfs.stop_socket_select();
break;
}
}
}
}
void esp_vfs_select_triggered_isr(BaseType_t *woken)
{
if (s_select_sem) {
xSemaphoreGiveFromISR(s_select_sem, woken);
} else {
// Another way would be to go through s_fd_table and find the VFS
// which has a permanent FD. But in order to avoid to lock
// s_fd_table_lock we go through the VFS table.
for (int i = 0; i < s_vfs_count; ++i) {
const vfs_entry_t *vfs = s_vfs[i];
if (vfs != NULL && vfs->vfs.stop_socket_select_isr != NULL) {
vfs->vfs.stop_socket_select_isr(woken);
break;
}
}
}
}

Wyświetl plik

@ -18,12 +18,14 @@
#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
@ -56,6 +58,13 @@ static int s_peek_char[UART_NUM] = { NONE, NONE, NONE };
// driver is used.
static bool s_non_blocking[UART_NUM];
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
@ -288,6 +297,108 @@ static int uart_access(const char *path, int amode)
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(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(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(task_woken);
}
break;
}
}
static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
{
const int max_fds = MIN(nfds, UART_NUM);
taskENTER_CRITICAL(uart_get_selectlock());
if (_readfds || _writefds || _errorfds || _readfds_orig || _writefds_orig || _errorfds_orig) {
taskEXIT_CRITICAL(uart_get_selectlock());
return ESP_ERR_INVALID_STATE;
}
if ((_readfds_orig = malloc(sizeof(fd_set))) == NULL) {
taskEXIT_CRITICAL(uart_get_selectlock());
return ESP_ERR_NO_MEM;
}
if ((_writefds_orig = malloc(sizeof(fd_set))) == NULL) {
taskEXIT_CRITICAL(uart_get_selectlock());
return ESP_ERR_NO_MEM;
}
if ((_errorfds_orig = malloc(sizeof(fd_set))) == NULL) {
taskEXIT_CRITICAL(uart_get_selectlock());
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);
}
}
_readfds = readfds;
_writefds = writefds;
_errorfds = exceptfds;
*_readfds_orig = *readfds;
*_writefds_orig = *writefds;
*_errorfds_orig = *exceptfds;
FD_ZERO(readfds);
FD_ZERO(writefds);
FD_ZERO(exceptfds);
taskEXIT_CRITICAL(uart_get_selectlock());
return ESP_OK;
}
static void uart_end_select()
{
taskENTER_CRITICAL(uart_get_selectlock());
for (int i = 0; i < UART_NUM; ++i) {
uart_set_select_notif_callback(i, 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;
}
taskEXIT_CRITICAL(uart_get_selectlock());
}
void esp_vfs_dev_uart_register()
{
esp_vfs_t vfs = {
@ -299,6 +410,8 @@ void esp_vfs_dev_uart_register()
.read = &uart_read,
.fcntl = &uart_fcntl,
.access = &uart_access,
.start_select = &uart_start_select,
.end_select = &uart_end_select,
};
ESP_ERROR_CHECK(esp_vfs_register("/dev/uart", &vfs, NULL));
}

Wyświetl plik

@ -142,6 +142,8 @@ Demonstration of how to report various communication events and how to use pater
Transmitting and receiveing with the same UART in two separate FreeRTOS tasks: :example:`peripherals/uart_async_rxtxtasks`.
Using synchronous I/O multiplexing for UART file descriptors: :example:`peripherals/uart_select`.
API Reference
-------------

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

Wyświetl plik

@ -0,0 +1,90 @@
/* 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);
} 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);
}

Wyświetl plik

@ -420,7 +420,7 @@ static void mcast_example_task(void *pvParameters)
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
int s = lwip_select(sock + 1, &rfds, NULL, NULL, &tv);
int s = select(sock + 1, &rfds, NULL, NULL, &tv);
if (s < 0) {
ESP_LOGE(TAG, "Select failed: errno %d", errno);
err = -1;

Wyświetl plik

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := select-example
include $(IDF_PATH)/make/project.mk

Wyświetl plik

@ -0,0 +1,11 @@
# Synchronous I/O multiplexing example
The example demonstrates the use of synchronous I/O multiplexing by the select()
function with UART and socket file descriptors. The example starts three tasks:
1. The first task writes periodically to the UART1 file descriptor.
2. The second task writes periodically to the socket descriptor.
3. Both UART1 and the socket are configured to act as loopbacks. The third
task detects by the use of select() whether it is possible to read from
UART1 or the socket, and receives the sent messages from the other tasks.
See the README.md file in the upper level 'examples' directory for more information about examples.

Wyświetl plik

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

Wyświetl plik

@ -0,0 +1,207 @@
/* 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/param.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"
#include "tcpip_adapter.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
static const char* TAG = "uart_select_example";
static int uart_fd = -1;
static int socket_fd = -1;
static void socket_deinit()
{
close(socket_fd);
socket_fd = -1;
}
static void socket_init()
{
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_DGRAM,
};
struct addrinfo *res;
int err;
struct sockaddr_in saddr = { 0 };
tcpip_adapter_init();
err = getaddrinfo("localhost", "80", &hints, &res);
if (err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed: %d", errno);
return;
}
socket_fd = socket(res->ai_family, res->ai_socktype, 0);
if (socket_fd < 0) {
ESP_LOGE(TAG, "Failed to allocate socket.");
freeaddrinfo(res);
return;
}
saddr.sin_family = PF_INET;
saddr.sin_port = htons(80);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
err = bind(socket_fd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in));
if (err < 0) {
ESP_LOGE(TAG, "Failed to bind socket");
freeaddrinfo(res);
socket_deinit();
return;
}
if (connect(socket_fd, res->ai_addr, res->ai_addrlen) != 0) {
ESP_LOGE(TAG, "Socket connection failed: %d", errno);
freeaddrinfo(res);
socket_deinit();
return;
}
freeaddrinfo(res);
}
static void uart1_deinit()
{
close(uart_fd);
uart_fd = -1;
uart_driver_delete(UART_NUM_1);
UART1.conf0.loopback = 0;
}
static void uart1_init()
{
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, 0, 0, NULL, 0);
UART1.conf0.loopback = 1;
if ((uart_fd = open("/dev/uart/1", O_RDWR | O_NONBLOCK)) == -1) {
ESP_LOGE(TAG, "Cannot open UART1");
uart1_deinit();
}
esp_vfs_dev_uart_use_driver(1);
}
static void uart1_write_task(void *param)
{
char buf[20];
uart1_init();
for (uint8_t i = 1;; ++i) {
vTaskDelay(4000 / portTICK_PERIOD_MS);
snprintf(buf, sizeof(buf), "UART message U%d", i);
int write_bytes = write(uart_fd, buf, strlen(buf));
if (write_bytes < 0) {
ESP_LOGE(TAG, "UART1 write error");
} else {
ESP_LOGI(TAG, "%d bytes were sent to UART1: %s", write_bytes, buf);
}
}
uart1_deinit(uart_fd);
vTaskDelete(NULL);
}
static void socket_write_task(void *param)
{
char buf[20];
socket_init();
for (uint8_t i = 1;; ++i) {
vTaskDelay(3000 / portTICK_PERIOD_MS);
snprintf(buf, sizeof(buf), "Socket message S%d", i);
int write_bytes = write(socket_fd, buf, strlen(buf));
if (write_bytes < 0) {
ESP_LOGE(TAG, "Socket write error");
} else {
ESP_LOGI(TAG, "%d bytes were written to socket: %s", write_bytes, buf);
}
}
socket_deinit();
vTaskDelete(NULL);
}
static void check_and_print(int fd, const fd_set *rfds, const char *src_msg)
{
char buf[100];
int read_bytes;
if (FD_ISSET(fd, rfds)) {
if ((read_bytes = read(fd, buf, sizeof(buf)-1)) > 0) {
buf[read_bytes] = '\0';
ESP_LOGI(TAG, "%d bytes were received through %s: %s", read_bytes, src_msg, buf);
} else {
ESP_LOGE(TAG, "%s read error", src_msg);
}
}
}
static void select_task(void *param)
{
while (1) {
int s;
fd_set rfds;
struct timeval tv = {
.tv_sec = 1,
.tv_usec = 0,
};
FD_ZERO(&rfds);
FD_SET(uart_fd, &rfds);
FD_SET(socket_fd, &rfds);
s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
if (s < 0) {
ESP_LOGE(TAG, "Select failed: errno %d", errno);
} else if (s == 0) {
ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");
} else {
check_and_print(socket_fd, &rfds, "socket");
check_and_print(uart_fd, &rfds, "UART1");
}
}
vTaskDelete(NULL);
}
void app_main()
{
xTaskCreate(uart1_write_task, "uart1_write_task", 4*1024, NULL, 5, NULL);
xTaskCreate(socket_write_task, "socket_write_task", 4*1024, NULL, 5, NULL);
vTaskDelay(1000 / portTICK_PERIOD_MS);
xTaskCreate(select_task, "select_task", 4*1024, NULL, 5, NULL);
}

Wyświetl plik

@ -3,6 +3,7 @@
#include "freertos/task.h"
#include "unity.h"
#include "unity_config.h"
#include "tcpip_adapter.h"
void unityTask(void *pvParameters)
{
@ -12,6 +13,10 @@ void unityTask(void *pvParameters)
void app_main()
{
// TCP/IP adapter is initialized here because it leaks memory so the
// initialization in test cases would make the test fail because of leak.
tcpip_adapter_init();
// Note: if unpinning this task, change the way run times are calculated in
// unity_platform
xTaskCreatePinnedToCore(unityTask, "unityTask", 8192, NULL,