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