Merge branch 'feature/add-eventfd' into 'master'

vfs: add eventfd support

See merge request espressif/esp-idf!12573
This commit is contained in:
Ivan Grokhotkov
2021-04-16 10:33:06 +00:00
18 changed files with 1282 additions and 33 deletions

View File

@@ -411,7 +411,7 @@ UT_001:
UT_002:
extends: .unit_test_esp32_template
parallel: 13
parallel: 15
tags:
- ESP32_IDF
- UT_T1_1

View File

@@ -1,4 +1,5 @@
idf_component_register(SRCS "vfs.c"
"vfs_eventfd.c"
"vfs_uart.c"
"vfs_semihost.c"
INCLUDE_DIRS include)

View File

@@ -235,3 +235,14 @@ 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, which 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.
Event fds
-------------------------------------------
``eventfd()`` call is a powerful tool to notify a ``select()`` based loop of custom events. The ``eventfd()`` implementation in ESP-IDF is generally the same as described in ``man(2) eventfd`` except for:
- ``esp_vfs_eventfd_register()`` has to be called before calling ``eventfd()``
- Options ``EFD_CLOEXEC``, ``EFD_NONBLOCK`` and ``EFD_SEMAPHORE`` are not supported in flags.
- Option ``EFD_SUPPORT_ISR`` has been added in flags. This flag is required to read and the write the eventfd in an interrupt handler.
Note that creating an eventfd with ``EFD_SUPPORT_ISR`` will cause interrupts to be temporarily disabled when reading, writing the file and during the beginning and the ending of the ``select()`` when this file is set.

View File

@@ -325,6 +325,15 @@ esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t
*/
esp_err_t esp_vfs_unregister(const char* base_path);
/**
* Unregister a virtual filesystem with the given index
*
* @param vfs_id The VFS ID returned by esp_vfs_register_with_id
* @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for the given index
* hasn't been registered
*/
esp_err_t esp_vfs_unregister_with_id(esp_vfs_id_t vfs_id);
/**
* Special function for registering another file descriptor for a VFS registered
* by esp_vfs_register_with_id.
@@ -338,6 +347,21 @@ esp_err_t esp_vfs_unregister(const char* base_path);
*/
esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd);
/**
* Special function for registering another file descriptor with given local_fd
* for a VFS registered by esp_vfs_register_with_id.
*
* @param vfs_id VFS identificator returned by esp_vfs_register_with_id.
* @param local_fd The fd in the local vfs. Passing -1 will set the local fd as the (*fd) value.
* @param permanent Whether the fd should be treated as permannet (not removed after close())
* @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_with_local_fd(esp_vfs_id_t vfs_id, int local_fd, bool permanent, int *fd);
/**
* Special function for unregistering a file descriptor belonging to a VFS
* registered by esp_vfs_register_with_id.

View File

@@ -0,0 +1,71 @@
// Copyright 2021 Espressif Systems (Shanghai) CO 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 <sys/types.h>
#include "esp_err.h"
#define EFD_SUPPORT_ISR (1 << 4)
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Eventfd vfs initialization settings
*/
typedef struct {
size_t max_fds; /*!< The maxinum number of eventfds supported */
} esp_vfs_eventfd_config_t;
#define ESP_VFS_EVENTD_CONFIG_DEFAULT() (esp_vfs_eventfd_config_t) { \
.max_fds = 5, \
};
/**
* @brief Registers the event vfs.
*
* @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
* registered.
*/
esp_err_t esp_vfs_eventfd_register(const esp_vfs_eventfd_config_t *config);
/**
* @brief Unregisters the event vfs.
*
* @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for given prefix
* hasn't been registered
*/
esp_err_t esp_vfs_eventfd_unregister(void);
/*
* @brief Creates an event file descirptor.
*
* The behavior of read, write and select is the same as man(2) eventfd with
* EFD_SEMAPHORE **NOT** specified. A new flag EFD_SUPPORT_ISR has been added.
* This flag is required to write to event fds in interrupt handlers. Accessing
* the control blocks of event fds with EFD_SUPPORT_ISR will cause interrupts to
* be temporarily blocked (e.g. during read, write and beginning and ending of
* the * select).
*
* @return The file descriptor if successful, -1 if error happens.
*/
int eventfd(unsigned int initval, int flags);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,357 @@
// Copyright 2021 Espressif Systems (Shanghai) CO 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 "esp_vfs_eventfd.h"
#include <errno.h>
#include <sys/select.h>
#include "driver/timer.h"
#include "esp_vfs.h"
#include "freertos/FreeRTOS.h"
#include "unity.h"
TEST_CASE("eventfd create and close", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
TEST_ASSERT_EQUAL(0, close(fd));
fd = eventfd(0, EFD_SUPPORT_ISR);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("eventfd reject unknown flags", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 1);
TEST_ASSERT_LESS_THAN(0, fd);
TEST_ASSERT_EQUAL(EINVAL, errno);
fd = eventfd(0, INT_MAX);
TEST_ASSERT_LESS_THAN(0, fd);
TEST_ASSERT_EQUAL(EINVAL, errno);
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("eventfd read", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
unsigned int initval = 123;
int fd = eventfd(initval, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
uint64_t val = 0;
TEST_ASSERT_EQUAL(sizeof(val), read(fd, &val, sizeof(val)));
TEST_ASSERT_EQUAL(initval, val);
TEST_ASSERT_EQUAL(sizeof(val), read(fd, &val, sizeof(val)));
TEST_ASSERT_EQUAL(0, val);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("eventfd read invalid size", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
uint32_t val = 0;
TEST_ASSERT_LESS_THAN(0, read(fd, &val, sizeof(val)));
TEST_ASSERT_EQUAL(EINVAL, errno);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("eventfd write invalid size", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
uint32_t val = 0;
TEST_ASSERT_LESS_THAN(0, write(fd, &val, sizeof(val)));
TEST_ASSERT_EQUAL(EINVAL, errno);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("eventfd write then read", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
uint64_t val = 123;
TEST_ASSERT_EQUAL(sizeof(val), write(fd, &val, sizeof(val)));
TEST_ASSERT_EQUAL(sizeof(val), read(fd, &val, sizeof(val)));
TEST_ASSERT_EQUAL(123, val);
val = 4;
TEST_ASSERT_EQUAL(sizeof(val), write(fd, &val, sizeof(val)));
val = 5;
TEST_ASSERT_EQUAL(sizeof(val), write(fd, &val, sizeof(val)));
TEST_ASSERT_EQUAL(sizeof(val), read(fd, &val, sizeof(val)));
TEST_ASSERT_EQUAL(9, val);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
TEST_CASE("eventfd instant select", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
struct timeval zero_time;
fd_set read_fds, write_fds, error_fds;
zero_time.tv_sec = 0;
zero_time.tv_usec = 0;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
int ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &zero_time);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT(!FD_ISSET(fd, &read_fds));
uint64_t val = 1;
TEST_ASSERT_EQUAL(sizeof(val), write(fd, &val, sizeof(val)));
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &zero_time);
TEST_ASSERT_EQUAL(1, ret);
TEST_ASSERT(FD_ISSET(fd, &read_fds));
TEST_ASSERT_EQUAL(sizeof(val), read(fd, &val, sizeof(val)));
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &zero_time);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT(!FD_ISSET(fd, &read_fds));
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
static void signal_task(void *arg)
{
int fd = *((int *)arg);
vTaskDelay(pdMS_TO_TICKS(1000));
uint64_t val = 1;
TEST_ASSERT_EQUAL(sizeof(val), write(fd, &val, sizeof(val)));
vTaskDelete(NULL);
}
TEST_CASE("eventfd signal from task", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd0 = eventfd(0, 0);
int fd1 = eventfd(0, 0);
int max_fd = fd1 > fd0 ? fd1 : fd0;
TEST_ASSERT_GREATER_OR_EQUAL(0, fd0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd1);
xTaskCreate(signal_task, "signal_task", 2048, &fd0, 5, NULL);
struct timeval wait_time;
struct timeval zero_time;
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(fd0, &read_fds);
FD_SET(fd1, &read_fds);
wait_time.tv_sec = 2;
wait_time.tv_usec = 0;
zero_time.tv_sec = 0;
zero_time.tv_usec = 0;
int ret = select(max_fd + 1, &read_fds, NULL, NULL, &wait_time);
TEST_ASSERT_EQUAL(1, ret);
TEST_ASSERT(FD_ISSET(fd0, &read_fds));
uint64_t val = 1;
TEST_ASSERT_EQUAL(sizeof(val), write(fd1, &val, sizeof(val)));
FD_ZERO(&read_fds);
FD_SET(fd0, &read_fds);
FD_SET(fd1, &read_fds);
ret = select(max_fd + 1, &read_fds, NULL, NULL, &zero_time);
TEST_ASSERT_EQUAL(2, ret);
TEST_ASSERT(FD_ISSET(fd0, &read_fds));
TEST_ASSERT(FD_ISSET(fd1, &read_fds));
TEST_ASSERT_EQUAL(0, close(fd0));
TEST_ASSERT_EQUAL(0, close(fd1));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
static void eventfd_select_test_isr(void *arg)
{
int fd = *((int *)arg);
uint64_t val = 1;
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
int ret = write(fd, &val, sizeof(val));
assert(ret == sizeof(val));
}
TEST_CASE("eventfd signal from ISR", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, EFD_SUPPORT_ISR);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
timer_config_t timer_config = {
.divider = 16,
.counter_dir = TIMER_COUNT_UP,
.counter_en = TIMER_PAUSE,
.alarm_en = TIMER_ALARM_EN,
.auto_reload = false,
};
TEST_ESP_OK(timer_init(TIMER_GROUP_0, TIMER_0, &timer_config));
TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL));
TEST_ESP_OK(timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, TIMER_BASE_CLK / 16));
TEST_ESP_OK(timer_enable_intr(TIMER_GROUP_0, TIMER_0));
TEST_ESP_OK(timer_isr_register(TIMER_GROUP_0, TIMER_0, eventfd_select_test_isr,
&fd, ESP_INTR_FLAG_LOWMED, NULL));
TEST_ESP_OK(timer_start(TIMER_GROUP_0, TIMER_0));
struct timeval wait_time;
fd_set read_fds, write_fds, error_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
wait_time.tv_sec = 2;
wait_time.tv_usec = 0;
FD_SET(fd, &read_fds);
int ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &wait_time);
TEST_ASSERT_EQUAL(1, ret);
TEST_ASSERT(FD_ISSET(fd, &read_fds));
timer_deinit(TIMER_GROUP_0, TIMER_0);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
static void close_task(void *arg)
{
int fd = *((int *)arg);
vTaskDelay(pdMS_TO_TICKS(1000));
TEST_ASSERT_EQUAL(0, close(fd));
vTaskDelete(NULL);
}
TEST_CASE("eventfd select closed fd", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
xTaskCreate(close_task, "close_task", 2048, &fd, 5, NULL);
struct timeval wait_time;
fd_set read_fds, write_fds, error_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(fd, &read_fds);
FD_SET(fd, &error_fds);
wait_time.tv_sec = 2;
wait_time.tv_usec = 0;
int ret = select(fd + 1, &read_fds, &write_fds, &error_fds, &wait_time);
TEST_ASSERT_EQUAL(1, ret);
TEST_ASSERT(FD_ISSET(fd, &error_fds));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}
typedef struct {
xQueueHandle queue;
int fd;
} select_task_args_t;
static void select_task(void *arg)
{
select_task_args_t *select_arg = (select_task_args_t *)arg;
int fd = select_arg->fd;
struct timeval wait_time;
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
wait_time.tv_sec = 2;
wait_time.tv_usec = 0;
int ret = select(fd + 1, &read_fds, NULL, NULL, &wait_time);
assert(ret == 1);
xQueueSend(select_arg->queue, select_arg, 0);
vTaskDelete(NULL);
}
TEST_CASE("eventfd multiple selects", "[vfs][eventfd]")
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
TEST_ESP_OK(esp_vfs_eventfd_register(&config));
int fd = eventfd(0, 0);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
select_task_args_t args = {
.queue = xQueueCreate(10, sizeof(select_task_args_t)),
.fd = fd,
};
select_task_args_t ret_args;
xTaskCreate(select_task, "select_task0", 2048, &args, 5, NULL);
xTaskCreate(select_task, "select_task1", 2048, &args, 5, NULL);
uint64_t val = 1;
TEST_ASSERT_EQUAL(sizeof(val), write(fd, &val, sizeof(val)));
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ASSERT(xQueueReceive(args.queue, &ret_args, 0));
TEST_ASSERT_EQUAL(ret_args.fd, fd);
TEST_ASSERT(xQueueReceive(args.queue, &ret_args, 0));
TEST_ASSERT_EQUAL(ret_args.fd, fd);
vQueueDelete(args.queue);
TEST_ASSERT_EQUAL(0, close(fd));
TEST_ESP_OK(esp_vfs_eventfd_unregister());
}

View File

@@ -172,6 +172,27 @@ esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t
return esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, vfs_id);
}
esp_err_t esp_vfs_unregister_with_id(esp_vfs_id_t vfs_id)
{
if (vfs_id < 0 || vfs_id >= MAX_FDS || s_vfs[vfs_id] == NULL) {
return ESP_ERR_INVALID_ARG;
}
vfs_entry_t* vfs = s_vfs[vfs_id];
free(vfs);
s_vfs[vfs_id] = NULL;
_lock_acquire(&s_fd_table_lock);
// Delete all references from the FD lookup-table
for (int j = 0; j < VFS_MAX_COUNT; ++j) {
if (s_fd_table[j].vfs_index == vfs_id) {
s_fd_table[j] = FD_TABLE_ENTRY_UNUSED;
}
}
_lock_release(&s_fd_table_lock);
return ESP_OK;
}
esp_err_t esp_vfs_unregister(const char* base_path)
{
const size_t base_path_len = strlen(base_path);
@@ -182,28 +203,22 @@ esp_err_t esp_vfs_unregister(const char* base_path)
}
if (base_path_len == vfs->path_prefix_len &&
memcmp(base_path, vfs->path_prefix, vfs->path_prefix_len) == 0) {
free(vfs);
s_vfs[i] = NULL;
_lock_acquire(&s_fd_table_lock);
// Delete all references from the FD lookup-table
for (int j = 0; j < MAX_FDS; ++j) {
if (s_fd_table[j].vfs_index == i) {
s_fd_table[j] = FD_TABLE_ENTRY_UNUSED;
}
}
_lock_release(&s_fd_table_lock);
return ESP_OK;
return esp_vfs_unregister_with_id(i);
}
}
return ESP_ERR_INVALID_STATE;
}
esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd)
{
return esp_vfs_register_fd_with_local_fd(vfs_id, -1, true, fd);
}
esp_err_t esp_vfs_register_fd_with_local_fd(esp_vfs_id_t vfs_id, int local_fd, bool permanent, int *fd)
{
if (vfs_id < 0 || vfs_id >= s_vfs_count || fd == NULL) {
ESP_LOGD(TAG, "Invalid arguments for esp_vfs_register_fd(%d, 0x%x)", vfs_id, (int) fd);
ESP_LOGD(TAG, "Invalid arguments for esp_vfs_register_fd_with_local_fd(%d, %d, %d, 0x%p)",
vfs_id, local_fd, permanent, fd);
return ESP_ERR_INVALID_ARG;
}
@@ -211,9 +226,13 @@ esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd)
_lock_acquire(&s_fd_table_lock);
for (int i = 0; i < MAX_FDS; ++i) {
if (s_fd_table[i].vfs_index == -1) {
s_fd_table[i].permanent = true;
s_fd_table[i].permanent = permanent;
s_fd_table[i].vfs_index = vfs_id;
s_fd_table[i].local_fd = i;
if (local_fd >= 0) {
s_fd_table[i].local_fd = local_fd;
} else {
s_fd_table[i].local_fd = i;
}
*fd = i;
ret = ESP_OK;
break;
@@ -221,7 +240,8 @@ esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd)
}
_lock_release(&s_fd_table_lock);
ESP_LOGD(TAG, "esp_vfs_register_fd(%d, 0x%x) finished with %s", vfs_id, (int) fd, esp_err_to_name(ret));
ESP_LOGD(TAG, "esp_vfs_register_fd_with_local_fd(%d, %d, %d, 0x%p) finished with %s",
vfs_id, local_fd, permanent, fd, esp_err_to_name(ret));
return ret;
}
@@ -806,21 +826,23 @@ static int set_global_fd_sets(const fds_triple_t *vfs_fds_triple, int size, fd_s
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 && esp_vfs_safe_fd_isset(local_fd, &item->readfds)) {
ESP_LOGD(TAG, "FD %d in readfds was set from VFS ID %d", fd, i);
FD_SET(fd, readfds);
++ret;
}
if (writefds && esp_vfs_safe_fd_isset(local_fd, &item->writefds)) {
ESP_LOGD(TAG, "FD %d in writefds was set from VFS ID %d", fd, i);
FD_SET(fd, writefds);
++ret;
}
if (errorfds && esp_vfs_safe_fd_isset(local_fd, &item->errorfds)) {
ESP_LOGD(TAG, "FD %d in errorfds was set from VFS ID %d", fd, i);
FD_SET(fd, errorfds);
++ret;
if (s_fd_table[fd].vfs_index == i) {
const int local_fd = s_fd_table[fd].local_fd; // single read -> no locking is required
if (readfds && esp_vfs_safe_fd_isset(local_fd, &item->readfds)) {
ESP_LOGD(TAG, "FD %d in readfds was set from VFS ID %d", fd, i);
FD_SET(fd, readfds);
++ret;
}
if (writefds && esp_vfs_safe_fd_isset(local_fd, &item->writefds)) {
ESP_LOGD(TAG, "FD %d in writefds was set from VFS ID %d", fd, i);
FD_SET(fd, writefds);
++ret;
}
if (errorfds && esp_vfs_safe_fd_isset(local_fd, &item->errorfds)) {
ESP_LOGD(TAG, "FD %d in errorfds was set from VFS ID %d", fd, i);
FD_SET(fd, errorfds);
++ret;
}
}
}
}

View File

@@ -0,0 +1,465 @@
// Copyright 2021 Espressif Systems (Shanghai) CO 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 "esp_vfs_eventfd.h"
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/lock.h>
#include <sys/select.h>
#include <sys/types.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "soc/spinlock.h"
#define FD_INVALID -1
#define FD_PENDING_SELECT -2
/*
* About the event_select_args_t linked list
*
* Each event_select_args_t structure records a pending select from a select call
* on a file descriptor.
*
* For each select() call, we form a linked list in end_select_args containing
* all the pending selects in this select call.
*
* For each file descriptor, we form a double linked list in event_context_t::select_args.
* This list contains all the pending selects on this file descriptor from
* different select() calls.
*
*/
typedef struct event_select_args_t {
int fd;
fd_set *read_fds;
fd_set *error_fds;
esp_vfs_select_sem_t signal_sem;
// linked list node in event_context_t::select_args
struct event_select_args_t *prev_in_fd;
struct event_select_args_t *next_in_fd;
// linked list node in end_select_arg
struct event_select_args_t *next_in_args;
} event_select_args_t;
typedef struct {
int fd;
bool support_isr;
volatile bool is_set;
volatile uint64_t value;
// a double-linked list for all pending select args with this fd
event_select_args_t *select_args;
_lock_t lock;
// only for event fds that support ISR.
spinlock_t data_spin_lock;
} event_context_t;
esp_vfs_id_t s_eventfd_vfs_id = -1;
static size_t s_event_size;
static event_context_t *s_events;
static void trigger_select_for_event(event_context_t *event)
{
event_select_args_t *select_args = event->select_args;
while (select_args != NULL) {
esp_vfs_select_triggered(select_args->signal_sem);
select_args = select_args->next_in_fd;
}
}
static void trigger_select_for_event_isr(event_context_t *event, BaseType_t *task_woken)
{
event_select_args_t *select_args = event->select_args;
while (select_args != NULL) {
BaseType_t local_woken;
esp_vfs_select_triggered_isr(select_args->signal_sem, &local_woken);
*task_woken = (local_woken || *task_woken);
select_args = select_args->next_in_fd;
}
}
#ifdef CONFIG_VFS_SUPPORT_SELECT
static esp_err_t event_start_select(int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
esp_vfs_select_sem_t signal_sem,
void **end_select_args)
{
esp_err_t error = ESP_OK;
bool should_trigger = false;
nfds = nfds < s_event_size ? nfds : (int)s_event_size;
event_select_args_t *select_args_list = NULL;
// FIXME: end_select_args should be a list to all select args
for (int i = 0; i < nfds; i++) {
_lock_acquire_recursive(&s_events[i].lock);
if (s_events[i].fd == i) {
if (s_events[i].support_isr) {
portENTER_CRITICAL(&s_events[i].data_spin_lock);
}
event_select_args_t *event_select_args =
(event_select_args_t *)malloc(sizeof(event_select_args_t));
event_select_args->fd = i;
event_select_args->signal_sem = signal_sem;
if (FD_ISSET(i, exceptfds)) {
FD_CLR(i, exceptfds);
event_select_args->error_fds = exceptfds;
} else {
event_select_args->error_fds = NULL;
}
FD_CLR(i, exceptfds);
// event fds are always writable
if (FD_ISSET(i, writefds)) {
should_trigger = true;
}
if (FD_ISSET(i, readfds)) {
event_select_args->read_fds = readfds;
if (s_events[i].is_set) {
should_trigger = true;
} else {
FD_CLR(i, readfds);
}
} else {
event_select_args->read_fds = NULL;
}
event_select_args->prev_in_fd = NULL;
event_select_args->next_in_fd = s_events[i].select_args;
if (s_events[i].select_args) {
s_events[i].select_args->prev_in_fd = event_select_args;
}
event_select_args->next_in_args = select_args_list;
select_args_list = event_select_args;
s_events[i].select_args = event_select_args;
if (s_events[i].support_isr) {
portEXIT_CRITICAL(&s_events[i].data_spin_lock);
}
}
_lock_release_recursive(&s_events[i].lock);
}
*end_select_args = select_args_list;
if (should_trigger) {
esp_vfs_select_triggered(signal_sem);
}
return error;
}
static esp_err_t event_end_select(void *end_select_args)
{
event_select_args_t *select_args = (event_select_args_t *)end_select_args;
while (select_args != NULL) {
event_context_t *event = &s_events[select_args->fd];
_lock_acquire_recursive(&event->lock);
if (event->support_isr) {
portENTER_CRITICAL(&event->data_spin_lock);
}
if (event->fd != select_args->fd) { // already closed
if (select_args->error_fds) {
FD_SET(select_args->fd, select_args->error_fds);
}
} else {
if (select_args->read_fds && event->is_set) {
FD_SET(select_args->fd, select_args->read_fds);
}
}
event_select_args_t *prev_in_fd = select_args->prev_in_fd;
event_select_args_t *next_in_fd = select_args->next_in_fd;
event_select_args_t *next_in_args = select_args->next_in_args;
if (prev_in_fd != NULL) {
prev_in_fd->next_in_fd = next_in_fd;
} else {
event->select_args = next_in_fd;
}
if (next_in_fd != NULL) {
next_in_fd->prev_in_fd = prev_in_fd;
}
if (prev_in_fd == NULL && next_in_fd == NULL) { // The last pending select
if (event->fd == FD_PENDING_SELECT) {
event->fd = FD_INVALID;
}
}
if (event->support_isr) {
portEXIT_CRITICAL(&event->data_spin_lock);
}
_lock_release_recursive(&event->lock);
free(select_args);
select_args = next_in_args;
}
return ESP_OK;
}
#endif // CONFIG_VFS_SUPPORT_SELECT
static ssize_t signal_event_fd_from_isr(int fd, const void *data, size_t size)
{
BaseType_t task_woken = pdFALSE;
const uint64_t *val = (const uint64_t *)data;
ssize_t ret = size;
portENTER_CRITICAL_ISR(&s_events[fd].data_spin_lock);
if (s_events[fd].fd == fd) {
s_events[fd].is_set = true;
s_events[fd].value += *val;
trigger_select_for_event_isr(&s_events[fd], &task_woken);
} else {
errno = EBADF;
ret = -1;
}
portEXIT_CRITICAL_ISR(&s_events[fd].data_spin_lock);
if (task_woken) {
portYIELD_FROM_ISR();
}
return ret;
}
static ssize_t event_write(int fd, const void *data, size_t size)
{
ssize_t ret = -1;
if (fd >= s_event_size || data == NULL || size != sizeof(uint64_t)) {
errno = EINVAL;
return ret;
}
if (size != sizeof(uint64_t)) {
errno = EINVAL;
return ret;
}
if (xPortInIsrContext()) {
ret = signal_event_fd_from_isr(fd, data, size);
} else {
const uint64_t *val = (const uint64_t *)data;
_lock_acquire_recursive(&s_events[fd].lock);
if (s_events[fd].support_isr) {
portENTER_CRITICAL(&s_events[fd].data_spin_lock);
}
if (s_events[fd].fd == fd) {
s_events[fd].is_set = true;
s_events[fd].value += *val;
ret = size;
trigger_select_for_event(&s_events[fd]);
if (s_events[fd].support_isr) {
portEXIT_CRITICAL(&s_events[fd].data_spin_lock);
}
} else {
errno = EBADF;
ret = -1;
}
_lock_release_recursive(&s_events[fd].lock);
}
return ret;
}
static ssize_t event_read(int fd, void *data, size_t size)
{
ssize_t ret = -1;
if (fd >= s_event_size || data == NULL || size != sizeof(uint64_t)) {
errno = EINVAL;
return ret;
}
uint64_t *val = (uint64_t *)data;
_lock_acquire_recursive(&s_events[fd].lock);
if (s_events[fd].support_isr) {
portENTER_CRITICAL(&s_events[fd].data_spin_lock);
}
if (s_events[fd].fd == fd) {
*val = s_events[fd].value;
s_events[fd].is_set = false;
ret = size;
s_events[fd].value = 0;
} else {
errno = EBADF;
ret = -1;
}
if (s_events[fd].support_isr) {
portEXIT_CRITICAL(&s_events[fd].data_spin_lock);
}
_lock_release_recursive(&s_events[fd].lock);
return ret;
}
static int event_close(int fd)
{
int ret = -1;
if (fd >= s_event_size) {
errno = EINVAL;
return ret;
}
_lock_acquire_recursive(&s_events[fd].lock);
if (s_events[fd].fd == fd) {
if (s_events[fd].support_isr) {
portENTER_CRITICAL(&s_events[fd].data_spin_lock);
}
if (s_events[fd].select_args == NULL) {
s_events[fd].fd = FD_INVALID;
} else {
s_events[fd].fd = FD_PENDING_SELECT;
trigger_select_for_event(&s_events[fd]);
}
s_events[fd].value = 0;
if (s_events[fd].support_isr) {
portEXIT_CRITICAL(&s_events[fd].data_spin_lock);
}
ret = 0;
} else {
errno = EBADF;
}
_lock_release_recursive(&s_events[fd].lock);
return ret;
}
esp_err_t esp_vfs_eventfd_register(const esp_vfs_eventfd_config_t *config)
{
if (config == NULL || config->max_fds >= MAX_FDS) {
return ESP_ERR_INVALID_ARG;
}
if (s_eventfd_vfs_id != -1) {
return ESP_ERR_INVALID_STATE;
}
s_event_size = config->max_fds;
s_events = (event_context_t *)calloc(s_event_size, sizeof(event_context_t));
for (size_t i = 0; i < s_event_size; i++) {
_lock_init_recursive(&s_events[i].lock);
s_events[i].fd = FD_INVALID;
}
esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_DEFAULT,
.write = &event_write,
.close = &event_close,
.read = &event_read,
#ifdef CONFIG_VFS_SUPPORT_SELECT
.start_select = &event_start_select,
.end_select = &event_end_select,
#endif
};
return esp_vfs_register_with_id(&vfs, NULL, &s_eventfd_vfs_id);
}
esp_err_t esp_vfs_eventfd_unregister(void)
{
if (s_eventfd_vfs_id == -1) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t error = esp_vfs_unregister_with_id(s_eventfd_vfs_id);
if (error == ESP_OK) {
s_eventfd_vfs_id = -1;
}
for (size_t i = 0; i < s_event_size; i++) {
_lock_close_recursive(&s_events[i].lock);
}
free(s_events);
return error;
}
int eventfd(unsigned int initval, int flags)
{
int fd = FD_INVALID;
int global_fd = FD_INVALID;
esp_err_t error = ESP_OK;
if ((flags & (~EFD_SUPPORT_ISR)) != 0) {
errno = EINVAL;
return FD_INVALID;
}
if (s_eventfd_vfs_id == -1) {
errno = EACCES;
return FD_INVALID;
}
for (size_t i = 0; i < s_event_size; i++) {
_lock_acquire_recursive(&s_events[i].lock);
if (s_events[i].fd == FD_INVALID) {
error = esp_vfs_register_fd_with_local_fd(s_eventfd_vfs_id, i, /*permanent=*/false, &global_fd);
if (error != ESP_OK) {
_lock_release_recursive(&s_events[i].lock);
break;
}
bool support_isr = flags & EFD_SUPPORT_ISR;
fd = i;
s_events[i].fd = i;
s_events[i].support_isr = support_isr;
spinlock_initialize(&s_events[i].data_spin_lock);
if (support_isr) {
portENTER_CRITICAL(&s_events[i].data_spin_lock);
}
s_events[i].is_set = false;
s_events[i].value = initval;
s_events[i].select_args = NULL;
if (support_isr) {
portEXIT_CRITICAL(&s_events[i].data_spin_lock);
}
_lock_release_recursive(&s_events[i].lock);
break;
}
_lock_release_recursive(&s_events[i].lock);
}
switch (error) {
case ESP_OK:
fd = global_fd;
break;
case ESP_ERR_NO_MEM:
errno = ENOMEM;
break;
case ESP_ERR_INVALID_ARG:
errno = EINVAL;
break;
default:
errno = EIO;
break;
}
return fd;
}

View File

@@ -157,6 +157,7 @@ INPUT = \
$(IDF_PATH)/components/openthread/include/openthread-core-esp32x-config.h \
$(IDF_PATH)/components/vfs/include/esp_vfs.h \
$(IDF_PATH)/components/vfs/include/esp_vfs_dev.h \
$(IDF_PATH)/components/vfs/include/esp_vfs_eventfd.h \
$(IDF_PATH)/components/vfs/include/esp_vfs_semihost.h \
$(IDF_PATH)/components/fatfs/vfs/esp_vfs_fat.h \
$(IDF_PATH)/components/fatfs/diskio/diskio_impl.h \

View File

@@ -7,3 +7,4 @@ API Reference
.. include-build-file:: inc/esp_vfs_dev.inc
.. include-build-file:: inc/esp_vfs_eventfd.inc

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

View 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 := eventfd
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,74 @@
# eventfd example
The example demonstrates the use of `eventfd()` to collect events from other tasks and ISRs in a
`select()` based main loop. The example starts two tasks and installs a timer interrupt handler:
1. The first task writes to the first `eventfd` periodically.
2. The timer interrupt handler writes to the second `eventfd`.
3. The second task collects the event from two fds with a `select()` loop.
## How to use example
### Hardware Required
This example should be able to run on any commonly available ESP32, ESP32S2, ESP32S3 or ESP32C3 development board.
### Configure the project
```
idf.py menuconfig
```
The default config will work.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The following log output should appear when the example runs (note that the bootloader log has been omitted).
```
I (4310) eventfd_example: Time: 1.99s
I (4310) eventfd_example: Select timeout
I (4310) eventfd_example: =================================
I (4310) eventfd_example: Select timeouted for 1 times
I (4320) eventfd_example: Timer triggerred for 0 times
I (4320) eventfd_example: Progress triggerred for 0 times
I (4330) eventfd_example: =================================
I (4810) eventfd_example: Time: 2.50s
I (4810) eventfd_example: TimerEvent fd event triggered
I (5810) eventfd_example: Time: 3.49s
I (5810) eventfd_example: Progress fd event triggered
I (7310) eventfd_example: Time: 5.00s
I (7310) eventfd_example: TimerEvent fd event triggered
I (9310) eventfd_example: Time: 6.99s
I (9310) eventfd_example: Select timeout
I (9310) eventfd_example: Time: 6.99s
I (9310) eventfd_example: Progress fd event triggered
I (9810) eventfd_example: Time: 7.50s
I (9810) eventfd_example: TimerEvent fd event triggered
I (11810) eventfd_example: Time: 9.49s
I (11810) eventfd_example: Select timeout
I (12310) eventfd_example: Time: 10.00s
I (12310) eventfd_example: TimerEvent fd event triggered
I (12810) eventfd_example: Time: 10.49s
I (12810) eventfd_example: Progress fd event triggered
I (14810) eventfd_example: Time: 12.49s
I (14810) eventfd_example: Select timeout
I (14810) eventfd_example: =================================
I (14810) eventfd_example: Select timeouted for 4 times
I (14820) eventfd_example: Timer triggerred for 4 times
I (14820) eventfd_example: Progress triggerred for 3 times
I (14830) eventfd_example: =================================
```

View File

@@ -0,0 +1,29 @@
from __future__ import unicode_literals
import os
import ttfw_idf
from tiny_test_fw import Env, Utility
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
def test_examples_eventfd(env, extra_data):
# type: (Env, None) -> None
dut = env.get_dut('eventfd', 'examples/system/eventfd')
dut.start_app()
dut.expect('cpu_start: Starting scheduler', timeout=30)
exp_list = [
'eventfd_example: Select timeouted for 4 times',
'eventfd_example: Timer triggerred for 4 times',
'eventfd_example: Progress triggerred for 3 times',
]
Utility.console_log('Expecting:{}{}'.format(os.linesep, os.linesep.join(exp_list)))
dut.expect_all(*exp_list, timeout=60)
if __name__ == '__main__':
test_examples_eventfd()

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "eventfd_example.c"
LDFRAGMENTS linker.lf
INCLUDE_DIRS ".")

View File

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

View File

@@ -0,0 +1,166 @@
/* eventfd 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/select.h>
#include "driver/timer.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "esp_vfs_eventfd.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/timer_types.h"
#define TIMER_DIVIDER 16
#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER)
#define MS_PER_S 1000
#define TIMER_INTERVAL_SEC 2.5
#define TEST_WITHOUT_RELOAD 0
#define PROGRESS_INTERVAL_MS 3500
#define TIMER_SIGNAL 1
#define PROGRESS_SIGNAL 2
static const char *TAG = "eventfd_example";
int s_timer_fd;
int s_progress_fd;
static void eventfd_timer_group0_isr(void *para)
{
timer_spinlock_take(TIMER_GROUP_0);
int timer_idx = (int) para;
uint32_t timer_intr = timer_group_get_intr_status_in_isr(TIMER_GROUP_0);
uint64_t timer_counter_value = timer_group_get_counter_value_in_isr(TIMER_GROUP_0, timer_idx);
if (timer_intr & TIMER_INTR_T0) {
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
timer_counter_value += (uint64_t) (TIMER_INTERVAL_SEC * TIMER_SCALE);
timer_group_set_alarm_value_in_isr(TIMER_GROUP_0, timer_idx, timer_counter_value);
}
timer_group_enable_alarm_in_isr(TIMER_GROUP_0, timer_idx);
uint64_t signal = TIMER_SIGNAL;
ssize_t val = write(s_timer_fd, &signal, sizeof(signal));
assert(val == sizeof(signal));
timer_spinlock_give(TIMER_GROUP_0);
}
static void eventfd_timer_init(int timer_idx, double timer_interval_sec)
{
timer_config_t config = {
.divider = TIMER_DIVIDER,
.counter_dir = TIMER_COUNT_UP,
.counter_en = TIMER_PAUSE,
.alarm_en = TIMER_ALARM_EN,
.auto_reload = false,
};
ESP_ERROR_CHECK(timer_init(TIMER_GROUP_0, timer_idx, &config));
ESP_ERROR_CHECK(timer_set_counter_value(TIMER_GROUP_0, timer_idx, 0x00000000ULL));
ESP_ERROR_CHECK(timer_set_alarm_value(TIMER_GROUP_0, timer_idx, timer_interval_sec * TIMER_SCALE));
ESP_ERROR_CHECK(timer_enable_intr(TIMER_GROUP_0, timer_idx));
ESP_ERROR_CHECK(timer_isr_register(TIMER_GROUP_0, timer_idx, eventfd_timer_group0_isr,
NULL, ESP_INTR_FLAG_IRAM, NULL));
ESP_ERROR_CHECK(timer_start(TIMER_GROUP_0, timer_idx));
}
static void worker_task(void *arg)
{
while (true) {
vTaskDelay(pdMS_TO_TICKS(PROGRESS_INTERVAL_MS));
uint64_t signal = PROGRESS_SIGNAL;
ssize_t val = write(s_progress_fd, &signal, sizeof(signal));
assert(val == sizeof(signal));
}
vTaskDelete(NULL);
}
static void collector_task(void *arg)
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&config));
s_timer_fd = eventfd(0, EFD_SUPPORT_ISR);
s_progress_fd = eventfd(0, 0);
assert(s_timer_fd > 0);
assert(s_progress_fd > 0);
int maxFd = s_progress_fd > s_timer_fd ? s_progress_fd : s_timer_fd;
int select_timeout_count = 0;
int timer_trigger_count = 0;
int progress_trigger_count = 0;
for (size_t i = 0; ; i++) {
struct timeval timeout;
uint64_t signal;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(s_timer_fd, &readfds);
FD_SET(s_progress_fd, &readfds);
int num_triggered = select(maxFd + 1, &readfds, NULL, NULL, &timeout);
assert(num_triggered >= 0);
uint64_t task_counter_value;
timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &task_counter_value);
ESP_LOGI(TAG, "Time: %.2fs", (double)task_counter_value / TIMER_SCALE);
if (FD_ISSET(s_progress_fd, &readfds)) {
ssize_t ret = read(s_progress_fd, &signal, sizeof(signal));
assert(ret == sizeof(signal));
assert(signal == PROGRESS_SIGNAL);
progress_trigger_count++;
ESP_LOGI(TAG, "Progress fd event triggered");
}
if (FD_ISSET(s_timer_fd, &readfds)) {
ssize_t ret = read(s_timer_fd, &signal, sizeof(signal));
assert(ret == sizeof(signal));
assert(signal == TIMER_SIGNAL);
timer_trigger_count++;
ESP_LOGI(TAG, "TimerEvent fd event triggered");
}
if (num_triggered == 0) {
select_timeout_count++;
ESP_LOGI(TAG, "Select timeout");
}
if (i % 10 == 0) {
ESP_LOGI(TAG, "=================================");
ESP_LOGI(TAG, "Select timeouted for %d times", select_timeout_count);
ESP_LOGI(TAG, "Timer triggerred for %d times", timer_trigger_count);
ESP_LOGI(TAG, "Progress triggerred for %d times", progress_trigger_count);
ESP_LOGI(TAG, "=================================");
}
}
timer_deinit(TIMER_GROUP_0, TIMER_0);
close(s_timer_fd);
close(s_progress_fd);
esp_vfs_eventfd_unregister();
vTaskDelete(NULL);
}
void app_main(void)
{
eventfd_timer_init(TIMER_0, TIMER_INTERVAL_SEC);
xTaskCreate(worker_task, "worker_task", 4 * 1024, NULL, 5, NULL);
xTaskCreate(collector_task, "collector_task", 4 * 1024, NULL, 5, NULL);
}

View File

@@ -0,0 +1,4 @@
[mapping:main]
archive: libmain.a
entries:
eventfd_example:eventfd_timer_group0_isr (noflash)