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

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