1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/kernel.h>
7 #include <errno.h>
8 #include <string.h>
9 #include <zephyr/sys/printk.h>
10 #include <zephyr/posix/signal.h>
11 #include <zephyr/posix/time.h>
12 
13 #define ACTIVE 1
14 #define NOT_ACTIVE 0
15 
16 static void zephyr_timer_wrapper(struct k_timer *ztimer);
17 
18 struct timer_obj {
19 	struct k_timer ztimer;
20 	void (*sigev_notify_function)(union sigval val);
21 	union sigval val;
22 	struct timespec interval;	/* Reload value */
23 	uint32_t reload;			/* Reload value in ms */
24 	uint32_t status;
25 };
26 
27 K_MEM_SLAB_DEFINE(posix_timer_slab, sizeof(struct timer_obj),
28 		  CONFIG_MAX_TIMER_COUNT, 4);
29 
zephyr_timer_wrapper(struct k_timer * ztimer)30 static void zephyr_timer_wrapper(struct k_timer *ztimer)
31 {
32 	struct timer_obj *timer;
33 
34 	timer = (struct timer_obj *)ztimer;
35 
36 	if (timer->reload == 0U) {
37 		timer->status = NOT_ACTIVE;
38 	}
39 
40 	(timer->sigev_notify_function)(timer->val);
41 }
42 
43 /**
44  * @brief Create a per-process timer.
45  *
46  * This API does not accept SIGEV_THREAD as valid signal event notification
47  * type.
48  *
49  * See IEEE 1003.1
50  */
timer_create(clockid_t clockid,struct sigevent * evp,timer_t * timerid)51 int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid)
52 {
53 	struct timer_obj *timer;
54 	const k_timeout_t alloc_timeout = K_MSEC(CONFIG_TIMER_CREATE_WAIT);
55 
56 	if (clockid != CLOCK_MONOTONIC || evp == NULL ||
57 	    (evp->sigev_notify != SIGEV_NONE &&
58 	     evp->sigev_notify != SIGEV_SIGNAL)) {
59 		errno = EINVAL;
60 		return -1;
61 	}
62 
63 	if (k_mem_slab_alloc(&posix_timer_slab, (void **)&timer, alloc_timeout) == 0) {
64 		(void)memset(timer, 0, sizeof(struct timer_obj));
65 	} else {
66 		errno = ENOMEM;
67 		return -1;
68 	}
69 
70 	timer->sigev_notify_function = evp->sigev_notify_function;
71 	timer->val = evp->sigev_value;
72 	timer->interval.tv_sec = 0;
73 	timer->interval.tv_nsec = 0;
74 	timer->reload = 0U;
75 	timer->status = NOT_ACTIVE;
76 
77 	if (evp->sigev_notify == SIGEV_NONE) {
78 		k_timer_init(&timer->ztimer, NULL, NULL);
79 	} else {
80 		k_timer_init(&timer->ztimer, zephyr_timer_wrapper, NULL);
81 	}
82 
83 	*timerid = (timer_t)timer;
84 
85 	return 0;
86 }
87 
88 /**
89  * @brief Get amount of time left for expiration on a per-process timer.
90  *
91  * See IEEE 1003.1
92  */
timer_gettime(timer_t timerid,struct itimerspec * its)93 int timer_gettime(timer_t timerid, struct itimerspec *its)
94 {
95 	struct timer_obj *timer = (struct timer_obj *)timerid;
96 	int32_t remaining, leftover;
97 	int64_t   nsecs, secs;
98 
99 	if (timer == NULL) {
100 		errno = EINVAL;
101 		return -1;
102 	}
103 
104 	if (timer->status == ACTIVE) {
105 		remaining = k_timer_remaining_get(&timer->ztimer);
106 		secs =  remaining / MSEC_PER_SEC;
107 		leftover = remaining - (secs * MSEC_PER_SEC);
108 		nsecs = (int64_t)leftover * NSEC_PER_MSEC;
109 		its->it_value.tv_sec = (int32_t) secs;
110 		its->it_value.tv_nsec = (int32_t) nsecs;
111 	} else {
112 		/* Timer is disarmed */
113 		its->it_value.tv_sec = 0;
114 		its->it_value.tv_nsec = 0;
115 	}
116 
117 	/* The interval last set by timer_settime() */
118 	its->it_interval = timer->interval;
119 	return 0;
120 }
121 
122 /**
123  * @brief Sets expiration time of per-process timer.
124  *
125  * See IEEE 1003.1
126  */
timer_settime(timer_t timerid,int flags,const struct itimerspec * value,struct itimerspec * ovalue)127 int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
128 		  struct itimerspec *ovalue)
129 {
130 	struct timer_obj *timer = (struct timer_obj *) timerid;
131 	uint32_t duration, current;
132 
133 	if (timer == NULL ||
134 	    value->it_interval.tv_nsec < 0 ||
135 	    value->it_interval.tv_nsec >= NSEC_PER_SEC ||
136 	    value->it_value.tv_nsec < 0 ||
137 	    value->it_value.tv_nsec >= NSEC_PER_SEC) {
138 		errno = EINVAL;
139 		return -1;
140 	}
141 
142 	/*  Save time to expire and old reload value. */
143 	if (ovalue != NULL) {
144 		timer_gettime(timerid, ovalue);
145 	}
146 
147 	/* Stop the timer if the value is 0 */
148 	if ((value->it_value.tv_sec == 0) && (value->it_value.tv_nsec == 0)) {
149 		if (timer->status == ACTIVE) {
150 			k_timer_stop(&timer->ztimer);
151 		}
152 
153 		timer->status = NOT_ACTIVE;
154 		return 0;
155 	}
156 
157 	/* Calculate timer period */
158 	timer->reload = _ts_to_ms(&value->it_interval);
159 	timer->interval.tv_sec = value->it_interval.tv_sec;
160 	timer->interval.tv_nsec = value->it_interval.tv_nsec;
161 
162 	/* Calculate timer duration */
163 	duration = _ts_to_ms(&(value->it_value));
164 	if ((flags & TIMER_ABSTIME) != 0) {
165 		current = k_timer_remaining_get(&timer->ztimer);
166 
167 		if (current >= duration) {
168 			duration = 0U;
169 		} else {
170 			duration -= current;
171 		}
172 	}
173 
174 	if (timer->status == ACTIVE) {
175 		k_timer_stop(&timer->ztimer);
176 	}
177 
178 	timer->status = ACTIVE;
179 	k_timer_start(&timer->ztimer, K_MSEC(duration), K_MSEC(timer->reload));
180 	return 0;
181 }
182 
183 /**
184  * @brief Returns the timer expiration overrun count.
185  *
186  * See IEEE 1003.1
187  */
timer_getoverrun(timer_t timerid)188 int timer_getoverrun(timer_t timerid)
189 {
190 	struct timer_obj *timer = (struct timer_obj *) timerid;
191 
192 	if (timer == NULL) {
193 		errno = EINVAL;
194 		return -1;
195 	}
196 
197 	int overruns = k_timer_status_get(&timer->ztimer) - 1;
198 
199 	if (overruns > CONFIG_TIMER_DELAYTIMER_MAX) {
200 		overruns = CONFIG_TIMER_DELAYTIMER_MAX;
201 	}
202 
203 	return overruns;
204 }
205 
206 /**
207  * @brief Delete a per-process timer.
208  *
209  * See IEEE 1003.1
210  */
timer_delete(timer_t timerid)211 int timer_delete(timer_t timerid)
212 {
213 	struct timer_obj *timer = (struct timer_obj *) timerid;
214 
215 	if (timer == NULL) {
216 		errno = EINVAL;
217 		return -1;
218 	}
219 
220 	if (timer->status == ACTIVE) {
221 		timer->status = NOT_ACTIVE;
222 		k_timer_stop(&timer->ztimer);
223 	}
224 
225 	k_mem_slab_free(&posix_timer_slab, (void *)timer);
226 
227 	return 0;
228 }
229