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