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