vfs: add example for eventfd

This commit is contained in:
Jiacheng Guo 2021-03-08 15:48:30 +08:00
parent 7b911e4641
commit 37a992bbde
7 changed files with 197 additions and 186 deletions

View File

@ -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 <stdint.h>
#include <stdio.h>
#include <sys/select.h>
#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());
}

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

View File

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

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "eventfd_example.c"
INCLUDE_DIRS ".")

View File

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

View File

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