1 /*
2 * Copyright (c) 2018 Intel Corporation
3 * Copyright (c) 2024, Meta
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #undef _POSIX_C_SOURCE
9 #define _POSIX_C_SOURCE 200809L
10
11 #include "posix_clock.h"
12
13 #include <errno.h>
14
15 #include <zephyr/kernel.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/posix/pthread.h>
18 #include <zephyr/posix/signal.h>
19 #include <zephyr/posix/time.h>
20
21 #define ACTIVE 1
22 #define NOT_ACTIVE 0
23
24 LOG_MODULE_REGISTER(posix_timer);
25
26 static void zephyr_timer_wrapper(struct k_timer *ztimer);
27
28 struct timer_obj {
29 struct k_timer ztimer;
30 struct sigevent evp;
31 struct k_sem sem_cond;
32 pthread_t thread;
33 struct timespec interval; /* Reload value */
34 uint32_t reload; /* Reload value in ms */
35 uint32_t status;
36 };
37
38 K_MEM_SLAB_DEFINE(posix_timer_slab, sizeof(struct timer_obj), CONFIG_POSIX_TIMER_MAX,
39 __alignof__(struct timer_obj));
40
zephyr_timer_wrapper(struct k_timer * ztimer)41 static void zephyr_timer_wrapper(struct k_timer *ztimer)
42 {
43 struct timer_obj *timer;
44
45 timer = (struct timer_obj *)ztimer;
46
47 if (timer->reload == 0U) {
48 timer->status = NOT_ACTIVE;
49 LOG_DBG("timer %p not active", timer);
50 }
51
52 if (timer->evp.sigev_notify == SIGEV_NONE) {
53 LOG_DBG("SIGEV_NONE");
54 return;
55 }
56
57 if (timer->evp.sigev_notify_function == NULL) {
58 LOG_DBG("NULL sigev_notify_function");
59 return;
60 }
61
62 LOG_DBG("calling sigev_notify_function %p", timer->evp.sigev_notify_function);
63 (timer->evp.sigev_notify_function)(timer->evp.sigev_value);
64 }
65
zephyr_thread_wrapper(void * arg)66 static void *zephyr_thread_wrapper(void *arg)
67 {
68 int ret;
69 struct timer_obj *timer = (struct timer_obj *)arg;
70
71 ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
72 __ASSERT(ret == 0, "pthread_setcanceltype() failed: %d", ret);
73
74 if (timer->evp.sigev_notify_attributes == NULL) {
75 ret = pthread_detach(pthread_self());
76 __ASSERT(ret == 0, "pthread_detach() failed: %d", ret);
77 }
78
79 while (1) {
80 if (timer->reload == 0U) {
81 timer->status = NOT_ACTIVE;
82 LOG_DBG("timer %p not active", timer);
83 }
84
85 ret = k_sem_take(&timer->sem_cond, K_FOREVER);
86 __ASSERT(ret == 0, "k_sem_take() failed: %d", ret);
87
88 if (timer->evp.sigev_notify_function == NULL) {
89 LOG_DBG("NULL sigev_notify_function");
90 continue;
91 }
92
93 LOG_DBG("calling sigev_notify_function %p", timer->evp.sigev_notify_function);
94 (timer->evp.sigev_notify_function)(timer->evp.sigev_value);
95 }
96
97 return NULL;
98 }
99
zephyr_timer_interrupt(struct k_timer * ztimer)100 static void zephyr_timer_interrupt(struct k_timer *ztimer)
101 {
102 struct timer_obj *timer;
103
104 timer = (struct timer_obj *)ztimer;
105 k_sem_give(&timer->sem_cond);
106 }
107
108 /**
109 * @brief Create a per-process timer.
110 *
111 * This API does not accept SIGEV_THREAD as valid signal event notification
112 * type.
113 *
114 * See IEEE 1003.1
115 */
timer_create(clockid_t clockid,struct sigevent * evp,timer_t * timerid)116 int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid)
117 {
118 int ret = 0;
119 int detachstate;
120 struct timer_obj *timer;
121 const k_timeout_t alloc_timeout = K_MSEC(CONFIG_TIMER_CREATE_WAIT);
122
123 if (evp == NULL || timerid == NULL) {
124 errno = EINVAL;
125 return -1;
126 }
127
128 if (k_mem_slab_alloc(&posix_timer_slab, (void **)&timer, alloc_timeout) != 0) {
129 LOG_DBG("k_mem_slab_alloc() failed: %d", ret);
130 errno = ENOMEM;
131 return -1;
132 }
133
134 *timer = (struct timer_obj){0};
135 timer->evp = *evp;
136 evp = &timer->evp;
137
138 switch (evp->sigev_notify) {
139 case SIGEV_NONE:
140 k_timer_init(&timer->ztimer, NULL, NULL);
141 break;
142 case SIGEV_SIGNAL:
143 k_timer_init(&timer->ztimer, zephyr_timer_wrapper, NULL);
144 break;
145 case SIGEV_THREAD:
146 if (evp->sigev_notify_attributes != NULL) {
147 ret = pthread_attr_getdetachstate(evp->sigev_notify_attributes,
148 &detachstate);
149 if (ret != 0) {
150 LOG_DBG("pthread_attr_getdetachstate() failed: %d", ret);
151 errno = ret;
152 ret = -1;
153 goto free_timer;
154 }
155
156 if (detachstate != PTHREAD_CREATE_DETACHED) {
157 ret = pthread_attr_setdetachstate(evp->sigev_notify_attributes,
158 PTHREAD_CREATE_DETACHED);
159 if (ret != 0) {
160 LOG_DBG("pthread_attr_setdetachstate() failed: %d", ret);
161 errno = ret;
162 ret = -1;
163 goto free_timer;
164 }
165 }
166 }
167
168 ret = k_sem_init(&timer->sem_cond, 0, 1);
169 if (ret != 0) {
170 LOG_DBG("k_sem_init() failed: %d", ret);
171 errno = -ret;
172 ret = -1;
173 goto free_timer;
174 }
175
176 ret = pthread_create(&timer->thread, evp->sigev_notify_attributes,
177 zephyr_thread_wrapper, timer);
178 if (ret != 0) {
179 LOG_DBG("pthread_create() failed: %d", ret);
180 errno = ret;
181 ret = -1;
182 goto free_timer;
183 }
184
185 k_timer_init(&timer->ztimer, zephyr_timer_interrupt, NULL);
186 break;
187 default:
188 ret = -1;
189 errno = EINVAL;
190 goto free_timer;
191 }
192
193 *timerid = (timer_t)timer;
194 goto out;
195
196 free_timer:
197 k_mem_slab_free(&posix_timer_slab, (void *)timer);
198
199 out:
200 return ret;
201 }
202
203 /**
204 * @brief Get amount of time left for expiration on a per-process timer.
205 *
206 * See IEEE 1003.1
207 */
timer_gettime(timer_t timerid,struct itimerspec * its)208 int timer_gettime(timer_t timerid, struct itimerspec *its)
209 {
210 struct timer_obj *timer = (struct timer_obj *)timerid;
211 int32_t remaining, leftover;
212 int64_t nsecs, secs;
213
214 if (timer == NULL) {
215 errno = EINVAL;
216 return -1;
217 }
218
219 if (timer->status == ACTIVE) {
220 remaining = k_timer_remaining_get(&timer->ztimer);
221 secs = remaining / MSEC_PER_SEC;
222 leftover = remaining - (secs * MSEC_PER_SEC);
223 nsecs = (int64_t)leftover * NSEC_PER_MSEC;
224 its->it_value.tv_sec = (int32_t) secs;
225 its->it_value.tv_nsec = (int32_t) nsecs;
226 } else {
227 /* Timer is disarmed */
228 its->it_value.tv_sec = 0;
229 its->it_value.tv_nsec = 0;
230 }
231
232 /* The interval last set by timer_settime() */
233 its->it_interval = timer->interval;
234 return 0;
235 }
236
237 /**
238 * @brief Sets expiration time of per-process timer.
239 *
240 * See IEEE 1003.1
241 */
timer_settime(timer_t timerid,int flags,const struct itimerspec * value,struct itimerspec * ovalue)242 int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
243 struct itimerspec *ovalue)
244 {
245 struct timer_obj *timer = (struct timer_obj *) timerid;
246 uint32_t duration, current;
247
248 if ((timer == NULL) || !timespec_is_valid(&value->it_interval) ||
249 !timespec_is_valid(&value->it_value)) {
250 errno = EINVAL;
251 return -1;
252 }
253
254 /* Save time to expire and old reload value. */
255 if (ovalue != NULL) {
256 timer_gettime(timerid, ovalue);
257 }
258
259 /* Stop the timer if the value is 0 */
260 if ((value->it_value.tv_sec == 0) && (value->it_value.tv_nsec == 0)) {
261 if (timer->status == ACTIVE) {
262 k_timer_stop(&timer->ztimer);
263 }
264
265 timer->status = NOT_ACTIVE;
266 return 0;
267 }
268
269 /* Calculate timer period */
270 timer->reload = ts_to_ms(&value->it_interval);
271 timer->interval.tv_sec = value->it_interval.tv_sec;
272 timer->interval.tv_nsec = value->it_interval.tv_nsec;
273
274 /* Calculate timer duration */
275 duration = ts_to_ms(&(value->it_value));
276 if ((flags & TIMER_ABSTIME) != 0) {
277 current = k_timer_remaining_get(&timer->ztimer);
278
279 if (current >= duration) {
280 duration = 0U;
281 } else {
282 duration -= current;
283 }
284 }
285
286 if (timer->status == ACTIVE) {
287 k_timer_stop(&timer->ztimer);
288 }
289
290 timer->status = ACTIVE;
291 k_timer_start(&timer->ztimer, K_MSEC(duration), K_MSEC(timer->reload));
292 return 0;
293 }
294
295 /**
296 * @brief Returns the timer expiration overrun count.
297 *
298 * See IEEE 1003.1
299 */
timer_getoverrun(timer_t timerid)300 int timer_getoverrun(timer_t timerid)
301 {
302 struct timer_obj *timer = (struct timer_obj *) timerid;
303
304 if (timer == NULL) {
305 errno = EINVAL;
306 return -1;
307 }
308
309 int overruns = k_timer_status_get(&timer->ztimer) - 1;
310
311 if (overruns > CONFIG_POSIX_DELAYTIMER_MAX) {
312 overruns = CONFIG_POSIX_DELAYTIMER_MAX;
313 }
314
315 return overruns;
316 }
317
318 /**
319 * @brief Delete a per-process timer.
320 *
321 * See IEEE 1003.1
322 */
timer_delete(timer_t timerid)323 int timer_delete(timer_t timerid)
324 {
325 struct timer_obj *timer = (struct timer_obj *) timerid;
326
327 if (timer == NULL) {
328 errno = EINVAL;
329 return -1;
330 }
331
332 if (timer->status == ACTIVE) {
333 timer->status = NOT_ACTIVE;
334 k_timer_stop(&timer->ztimer);
335 }
336
337 if (timer->evp.sigev_notify == SIGEV_THREAD) {
338 (void)pthread_cancel(timer->thread);
339 }
340
341 k_mem_slab_free(&posix_timer_slab, (void *)timer);
342
343 return 0;
344 }
345