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