Skip to content

Commit 209b806

Browse files
committed
posix: add timerfd interface
This is a wrapper around zvfs_timerfd, so we provide a standard POSIX "compatible" timerfd interface. Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
1 parent 1f04ad1 commit 209b806

File tree

7 files changed

+170
-0
lines changed

7 files changed

+170
-0
lines changed

include/zephyr/posix/sys/timerfd.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2020 Tobias Svehagen
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_POSIX_SYS_TIMERFD_H_
8+
#define ZEPHYR_INCLUDE_POSIX_SYS_TIMERFD_H_
9+
10+
#include <zephyr/zvfs/timerfd.h>
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
#define TFD_NONBLOCK ZVFS_TFD_NONBLOCK
17+
18+
#define TFD_IOC_SET_TICKS ZVFS_TFD_IOC_SET_TICKS
19+
20+
#define TFD_TIMER_ABSTIME 1
21+
22+
typedef zvfs_timerfd_t timerfd_t;
23+
24+
/**
25+
* @brief Create a file descriptor for ZVFS timer notification
26+
*
27+
* The returned file descriptor can be used with POSIX read calls or
28+
* with the @ref zvfs_timerfd_settime or @ref zvfs_timerfd_gettime functions.
29+
*
30+
* It also supports polling and by including an timerfd in a call to poll,
31+
* it is possible to signal and wake the polling thread by simply writing to
32+
* the timerfd.
33+
*
34+
* When using read() on a ZVFS timerfd, the size must always be at
35+
* least 8 bytes or the operation will fail with EINVAL.
36+
*
37+
* @return New ZVFS timerfd file descriptor on success, -1 on error
38+
*/
39+
int timerfd_create(int clockid, int flags);
40+
41+
/**
42+
* @brief Get the itimespec configuration ZVFS timerfd
43+
*
44+
* @param fd File descriptor
45+
* @param curr_value Pointer to struct itimerspec to store current value
46+
*
47+
* @return 0 on success, -1 on error
48+
*/
49+
int timerfd_gettime(int fd, struct itimerspec *curr_value);
50+
51+
/**
52+
* @brief Set the itimespec configuration ZVFS timerfd
53+
*
54+
* The current value is stored in old_value if it is not NULL.
55+
*
56+
* @param fd File descriptor
57+
* @param new_value Pointer to struct itimerspec to set new value
58+
* @param old_value Pointer to struct itimerspec to store old value
59+
*
60+
* @return 0 on success, -1 on error
61+
*/
62+
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,
63+
struct itimerspec *old_value);
64+
65+
#ifdef __cplusplus
66+
}
67+
#endif
68+
69+
#endif /* ZEPHYR_INCLUDE_POSIX_SYS_TIMERFD_H_ */

lib/posix/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# zephyr-keep-sorted-start
44
add_subdirectory_ifdef(CONFIG_EVENTFD eventfd)
5+
add_subdirectory_ifdef(CONFIG_TIMERFD timerfd)
56
add_subdirectory_ifdef(CONFIG_POSIX_C_LANG_SUPPORT_R c_lang_support_r)
67
add_subdirectory_ifdef(CONFIG_POSIX_C_LIB_EXT c_lib_ext)
78
add_subdirectory_ifdef(CONFIG_POSIX_SHELL shell)

lib/posix/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,6 @@ endmenu
4242

4343
# Eventfd Support (not officially POSIX)
4444
rsource "eventfd/Kconfig"
45+
46+
# Timerfd Support (not officially POSIX)
47+
rsource "timerfd/Kconfig"

lib/posix/Kconfig.profile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ config POSIX_API
1515
select POSIX_AEP_REALTIME_MINIMAL # CLOCK_MONOTONIC, pthread_attr_setstack(), etc
1616
select POSIX_NETWORKING if NETWORKING # inet_ntoa(), socket(), etc
1717
imply EVENTFD # eventfd(), eventfd_read(), eventfd_write()
18+
imply TIMERFD # timerfd_create(), timerfd_settime(), timerfd_gettime()
1819
imply POSIX_FD_MGMT # open(), close(), read(), write()
1920
imply POSIX_MULTI_PROCESS # sleep(), getpid(), etc
2021
imply XSI_SINGLE_PROCESS # gettimeofday()

lib/posix/timerfd/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
zephyr_library()
4+
zephyr_library_sources(timerfd.c)

lib/posix/timerfd/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2025 Atym, Inc.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config TIMERFD
6+
bool "Support for timerfd"
7+
select ZVFS
8+
select ZVFS_TIMERFD
9+
help
10+
Enable support for timer file descriptors, timerfd. A timerfd can
11+
be used as an timer waiting mechanism together with POSIX calls
12+
like read, write and poll.

lib/posix/timerfd/timerfd.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (c) 2024, Tenstorrent AI ULC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "../options/posix_clock.h"
8+
#include <string.h>
9+
10+
#include <zephyr/posix/sys/timerfd.h>
11+
#include <zephyr/zvfs/timerfd.h>
12+
13+
int timerfd_create(int clockid, int flags)
14+
{
15+
return zvfs_timerfd(clockid, flags);
16+
};
17+
18+
int timerfd_gettime(int fd, struct itimerspec *curr_value)
19+
{
20+
int ret;
21+
uint32_t value, period;
22+
int32_t leftover;
23+
int64_t nsecs, secs;
24+
25+
ret = zvfs_timerfd_gettime(fd, &value, &period);
26+
if (ret != 0) {
27+
return ret;
28+
}
29+
30+
/* converts value */
31+
secs = value / MSEC_PER_SEC;
32+
leftover = value - (secs * MSEC_PER_SEC);
33+
nsecs = (int64_t)leftover * NSEC_PER_MSEC;
34+
curr_value->it_value.tv_sec = (int32_t) secs;
35+
curr_value->it_value.tv_nsec = (int32_t) nsecs;
36+
37+
/* converts period */
38+
secs = period / MSEC_PER_SEC;
39+
leftover = period - (secs * MSEC_PER_SEC);
40+
nsecs = (int64_t)leftover * NSEC_PER_MSEC;
41+
curr_value->it_interval.tv_sec = (int32_t) secs;
42+
curr_value->it_interval.tv_nsec = (int32_t) nsecs;
43+
44+
return 0;
45+
};
46+
47+
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,
48+
struct itimerspec *old_value)
49+
{
50+
uint32_t period, duration, current;
51+
52+
if (!timespec_is_valid(&new_value->it_interval) ||
53+
!timespec_is_valid(&new_value->it_value)) {
54+
errno = EINVAL;
55+
return -1;
56+
}
57+
58+
/* Save time to expire and old reload value. */
59+
if (old_value != NULL) {
60+
timerfd_gettime(fd, old_value);
61+
}
62+
63+
/* Calculate timer duration */
64+
duration = ts_to_ms(&(new_value->it_value));
65+
66+
/* Calculate timer period */
67+
period = ts_to_ms(&(new_value->it_interval));
68+
69+
if ((flags & TFD_TIMER_ABSTIME) != 0) {
70+
zvfs_timerfd_gettime(fd, &current, NULL);
71+
72+
if (current >= duration) {
73+
duration = 0U;
74+
} else {
75+
duration -= current;
76+
}
77+
}
78+
79+
return zvfs_timerfd_settime(fd, K_MSEC(duration), K_MSEC(period));
80+
}

0 commit comments

Comments
 (0)