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