diff --git a/components/vfs/test/test_vfs_eventfd_integration.c b/components/vfs/test/test_vfs_eventfd_integration.c deleted file mode 100644 index 50e19dbafe..0000000000 --- a/components/vfs/test/test_vfs_eventfd_integration.c +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2021 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 "esp_vfs_eventfd.h" - -#include -#include -#include - -#include "driver/periph_ctrl.h" -#include "driver/timer.h" -#include "esp_err.h" -#include "esp_types.h" -#include "esp_vfs.h" -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#include "freertos/task.h" -#include "hal/timer_types.h" -#include "unity.h" - -#define TIMER_DIVIDER 16 -#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) -#define TIMER_INTERVAL0_SEC (0.25) -#define TEST_WITHOUT_RELOAD 0 -#define PROGRESS_INTERVAL_MS 350 -#define TIMER_SIGNAL 1 -#define PROGRESS_SIGNAL 2 - -int s_timer_fd; -int s_progress_fd; - -/* - * A simple helper function to print the raw timer counter value - * and the counter value converted to seconds - */ -static void inline print_timer_counter(uint64_t counter_value) -{ - printf("Counter: 0x%08x%08x\n", (uint32_t) (counter_value >> 32), - (uint32_t) (counter_value)); - printf("Time : %.8f s\n", (double) counter_value / TIMER_SCALE); -} - -static void IRAM_ATTR 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_INTERVAL0_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, - }; - TEST_ESP_OK(timer_init(TIMER_GROUP_0, timer_idx, &config)); - - TEST_ESP_OK(timer_set_counter_value(TIMER_GROUP_0, timer_idx, 0x00000000ULL)); - - TEST_ESP_OK(timer_set_alarm_value(TIMER_GROUP_0, timer_idx, timer_interval_sec * TIMER_SCALE)); - TEST_ESP_OK(timer_enable_intr(TIMER_GROUP_0, timer_idx)); - TEST_ESP_OK(timer_isr_register(TIMER_GROUP_0, timer_idx, eventfd_timer_group0_isr, - (void *) timer_idx, ESP_INTR_FLAG_IRAM, NULL)); - - TEST_ESP_OK(timer_start(TIMER_GROUP_0, timer_idx)); -} - -static void eventfd_timer_deinit(int timer_idx) -{ - timer_pause(TIMER_GROUP_0, timer_idx); - timer_deinit(TIMER_GROUP_0, timer_idx); -} - -static void worker_task(void *arg) -{ - for (int i = 0; i < 3; i++) { - 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); -} - -TEST_CASE("Test eventfd triggered correctly", "[vfs][eventfd]") -{ - xTaskCreate(worker_task, "worker_task", 1024, NULL, 5, NULL); - esp_vfs_eventfd_config_t config = { - .eventfd_max_num = 5, - }; - TEST_ESP_OK(esp_vfs_eventfd_register(&config)); - s_timer_fd = eventfd(0, EFD_SUPPORT_ISR); - s_progress_fd = eventfd(0, 0); - int maxFd = s_progress_fd > s_timer_fd ? s_progress_fd : s_timer_fd; - printf("Timer fd %d progress fd %d\n", s_timer_fd, s_progress_fd); - TEST_ASSERT_GREATER_OR_EQUAL(0, s_timer_fd); - TEST_ASSERT_GREATER_OR_EQUAL(0, s_progress_fd); - eventfd_timer_init(TIMER_0, TIMER_INTERVAL0_SEC); - - int select_timeout_count = 0; - int timer_trigger_count = 0; - int progress_trigger_count = 0; - - for (size_t i = 0; i < 10; i++) { - struct timeval timeout; - uint64_t signal; - - timeout.tv_sec = 0; - timeout.tv_usec = 200 * 1000; - - fd_set readfds; - fd_set writefds; - fd_set errorfds; - - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&errorfds); - - FD_SET(s_timer_fd, &readfds); - FD_SET(s_progress_fd, &readfds); - - select(maxFd + 1, &readfds, &writefds, &errorfds, &timeout); - - printf("-------- TASK TIME --------\n"); - uint64_t task_counter_value; - TEST_ESP_OK(timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &task_counter_value)); - print_timer_counter(task_counter_value); - - if (FD_ISSET(s_progress_fd, &readfds)) { - ssize_t ret = read(s_progress_fd, &signal, sizeof(signal)); - TEST_ASSERT_EQUAL(ret, sizeof(signal)); - TEST_ASSERT_EQUAL(signal, PROGRESS_SIGNAL); - progress_trigger_count++; - printf("Progress fd\n"); - } else if (FD_ISSET(s_timer_fd, &readfds)) { - ssize_t ret = read(s_timer_fd, &signal, sizeof(signal)); - TEST_ASSERT(ret == sizeof(signal)); - TEST_ASSERT(signal == TIMER_SIGNAL); - timer_trigger_count++; - printf("TimerEvent fd\n"); - } else { - select_timeout_count++; - printf("Select timeout\n"); - } - } - - printf("Select timeout: %d\n", select_timeout_count); - printf("Timer trigger: %d\n", timer_trigger_count); - printf("Progress trigger: %d\n", progress_trigger_count); - TEST_ASSERT_EQUAL(3, select_timeout_count); - TEST_ASSERT_EQUAL(4, timer_trigger_count); - TEST_ASSERT_EQUAL(3, progress_trigger_count); - TEST_ASSERT_EQUAL(0, close(s_progress_fd)); - TEST_ASSERT_EQUAL(0, close(s_timer_fd)); - eventfd_timer_deinit(TIMER_0); - TEST_ESP_OK(esp_vfs_eventfd_unregister()); -} diff --git a/examples/system/eventfd/CMakeLists.txt b/examples/system/eventfd/CMakeLists.txt new file mode 100644 index 0000000000..25c37e595f --- /dev/null +++ b/examples/system/eventfd/CMakeLists.txt @@ -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) diff --git a/examples/system/eventfd/Makefile b/examples/system/eventfd/Makefile new file mode 100644 index 0000000000..e70931d40b --- /dev/null +++ b/examples/system/eventfd/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := select + +include $(IDF_PATH)/make/project.mk diff --git a/examples/system/eventfd/README.md b/examples/system/eventfd/README.md new file mode 100644 index 0000000000..9bcfc0acaa --- /dev/null +++ b/examples/system/eventfd/README.md @@ -0,0 +1,9 @@ +# 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. + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/system/eventfd/main/CMakeLists.txt b/examples/system/eventfd/main/CMakeLists.txt new file mode 100644 index 0000000000..52e676a338 --- /dev/null +++ b/examples/system/eventfd/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "eventfd_example.c" + INCLUDE_DIRS ".") diff --git a/examples/system/eventfd/main/component.mk b/examples/system/eventfd/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/system/eventfd/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/system/eventfd/main/eventfd_example.c b/examples/system/eventfd/main/eventfd_example.c new file mode 100644 index 0000000000..67e7745436 --- /dev/null +++ b/examples/system/eventfd/main/eventfd_example.c @@ -0,0 +1,168 @@ +/* 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 +#include + +#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 IRAM_ATTR 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 = { + .eventfd_max_num = 2, + }; + 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); +}