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