1 /*
2 * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3 */
4
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <signal.h>
9 #include <time.h>
10 #include <pthread.h>
11 #include <aos/errno.h>
12 #include <aos/kernel.h>
13
14 #define POSIX_TIMER_ID_MIN 1
15
16 typedef struct timer_list_s {
17 timer_t id;
18 aos_timer_t aos_timer;
19 void *evp; /* The sigevent as the parameter of timer_callback. */
20 struct timer_list_s *next;
21 } timer_list_t;
22
23 timer_list_t *g_timer_list_head = NULL; /* The timer list head. */
24 pthread_mutex_t g_timer_lock = PTHREAD_MUTEX_INITIALIZER; /* The lock to protect timer list. */
25
26 /* Add the unify timer_callback to avoid the argument of callback is an automatic variable,
27 * we malloc a sigevent and store the argument it it when timer_create, and free it when
28 * timer_delete.
29 */
timer_callback(void * timer,void * arg)30 static void timer_callback(void *timer, void *arg)
31 {
32 struct sigevent *evp =(struct sigevent *)arg;
33
34 if ((evp != NULL) && (evp->sigev_notify_function != NULL)) {
35 evp->sigev_notify_function(evp->sigev_value);
36 }
37 }
38
timespec_to_nanosecond(struct timespec * t)39 static inline int64_t timespec_to_nanosecond(struct timespec *t)
40 {
41 return ((uint64_t)t->tv_sec * 1000000000UL + t->tv_nsec);
42 }
43
nanosecond_to_timespec(int64_t ns)44 static inline struct timespec nanosecond_to_timespec(int64_t ns)
45 {
46 struct timespec ret_time;
47
48 ret_time.tv_sec = ns / 1000000000UL;
49 ret_time.tv_nsec = ns % 1000000000UL;
50
51 return ret_time;
52 }
53
timespec_abs_to_relate(struct timespec * time_abs,struct timespec * time_relate)54 static int timespec_abs_to_relate(struct timespec *time_abs, struct timespec *time_relate)
55 {
56 int ret = 0;
57 int64_t ns = 0;
58 struct timespec time_now;
59
60 memset(&time_now, 0, sizeof(time_now));
61
62 ret = clock_gettime(CLOCK_MONOTONIC, &time_now);
63 if (ret != 0) {
64 return -1;
65 }
66
67 ns = timespec_to_nanosecond(time_abs) - timespec_to_nanosecond(&time_now);
68 if (ns < 0) {
69 return -1;
70 }
71
72 *time_relate = nanosecond_to_timespec(ns);
73
74 return 0;
75 }
76
timer_create(clockid_t clockid,struct sigevent * evp,timer_t * timerid)77 int timer_create(clockid_t clockid, struct sigevent * evp, timer_t * timerid)
78 {
79 int ret = 0;
80 timer_list_t *timer_list_m = NULL;
81 timer_list_t *timer_list = NULL;
82
83 if ((evp == NULL) || (timerid == NULL) ||
84 ((clockid != CLOCK_REALTIME) && (clockid != CLOCK_MONOTONIC))) {
85 errno = EINVAL;
86 return -1;
87 }
88
89 /* Only support SIGEV_THREAD. */
90 if (evp->sigev_notify != SIGEV_THREAD) {
91 errno = ENOTSUP;
92 return -1;
93 }
94
95 /* The sigev_notify_function should be set for SIGEV_THREAD */
96 if (evp->sigev_notify_function == NULL) {
97 errno = EINVAL;
98 return -1;
99 }
100
101 /* malloc new timer struct */
102 timer_list_m = (timer_list_t *)malloc(sizeof(timer_list_t));
103 if (timer_list_m == NULL) {
104 return -1;
105 }
106
107 memset(timer_list_m, 0, sizeof(timer_list_t));
108
109 timer_list_m->evp = malloc(sizeof(struct sigevent));
110 if(timer_list_m->evp == NULL) {
111 free(timer_list_m);
112 timer_list_m = NULL;
113 return -1;
114 }
115 memcpy(timer_list_m->evp, evp, sizeof(struct sigevent));
116
117 ret = pthread_mutex_lock(&g_timer_lock);
118 if (ret != 0) {
119 free(timer_list_m->evp);
120 free(timer_list_m);
121 return -1;
122 }
123
124 /* find the last node add the new timer to the list */
125 if (g_timer_list_head == NULL) {
126
127 /* init the id to POSIX_TIMER_ID_MIN */
128 timer_list_m->id = (timer_t) POSIX_TIMER_ID_MIN;
129 g_timer_list_head = timer_list_m;
130 } else {
131 timer_list = g_timer_list_head;
132 while(timer_list->next != NULL) {
133 timer_list = timer_list->next;
134 }
135
136 /* the id of new timer equel to last id plus one */
137 timer_list_m->id = (int)timer_list->id + 1;
138 timer_list->next = timer_list_m;
139 }
140
141 pthread_mutex_unlock(&g_timer_lock);
142
143 /* create a timer */
144 ret = aos_timer_new_ext(&(timer_list_m->aos_timer), timer_callback, timer_list_m->evp,
145 1, 1, 0);
146 if (ret != 0) {
147 return -1;
148 }
149
150 /* update the timerid */
151 *timerid = timer_list_m->id;
152
153 return 0;
154 }
155
timer_delete(timer_t timerid)156 int timer_delete(timer_t timerid)
157 {
158 timer_list_t *timer_list = NULL;
159 timer_list_t *timer_list_l = NULL;
160 int ret = 0;
161
162 /* Timer list is empty. */
163 if (g_timer_list_head == NULL) {
164 return -1;
165 }
166
167 ret = pthread_mutex_lock(&g_timer_lock);
168 if (ret != 0) {
169 return -1;
170 }
171
172 /* Scan the list to find the timer according to timerid. */
173 timer_list = g_timer_list_head;
174 timer_list_l = g_timer_list_head;
175 while ((timer_list != NULL) && (timer_list->id != timerid)) {
176 timer_list_l = timer_list;
177 timer_list = timer_list->next;
178 }
179
180 /* Do not find the timerid. */
181 if (timer_list == NULL) {
182 pthread_mutex_unlock(&g_timer_lock);
183 return -1;
184 }
185
186 /* Stop and detete the timer. */
187 aos_timer_stop(&(timer_list->aos_timer));
188
189 aos_timer_free(&(timer_list->aos_timer));
190 /* Delete the timer from the list and free the timer. */
191 if (timer_list_l == g_timer_list_head) {
192 g_timer_list_head = timer_list->next;
193 } else {
194 timer_list_l->next = timer_list->next;
195 }
196
197 if (timer_list->evp != NULL) {
198 free(timer_list->evp);
199 }
200 free(timer_list);
201
202 pthread_mutex_unlock(&g_timer_lock);
203
204 return ret;
205 }
206
timer_settime(timer_t timerid,int flags,const struct itimerspec * restrict value,struct itimerspec * restrict ovalue)207 int timer_settime(timer_t timerid, int flags, const struct itimerspec *restrict value,
208 struct itimerspec *restrict ovalue)
209 {
210 int ret = 0;
211 int64_t value_ns = 0;
212 int64_t interval_ns = 0;
213 struct timespec value_spec = {0};
214 struct timespec interval_spec = {0};
215 timer_list_t *timer_list = NULL;
216
217 if (value == NULL) {
218 return -1;
219 }
220
221 if (g_timer_list_head == NULL) {
222 return -1;
223 }
224
225 /* If the time is absolute time transform it to relative time. */
226 if((flags & TIMER_ABSTIME) == TIMER_ABSTIME) {
227 ret = timespec_abs_to_relate((struct timespec *)&value->it_value, &value_spec);
228 ret |= timespec_abs_to_relate((struct timespec *)&value->it_interval, &interval_spec);
229 if (ret != 0) {
230 return -1;
231 }
232
233 value_ns = timespec_to_nanosecond(&value_spec);
234 interval_ns = timespec_to_nanosecond(&interval_spec);
235 } else {
236 value_ns = timespec_to_nanosecond((struct timespec *)&value->it_value);
237 interval_ns = timespec_to_nanosecond((struct timespec *)&value->it_interval);
238 }
239
240 /* Get the old parameters of timer if ovalue is not NULL. */
241 if (ovalue != NULL) {
242 ret = timer_gettime(timerid, ovalue);
243 if (ret != 0) {
244 return -1;
245 }
246 }
247
248 ret = pthread_mutex_lock(&g_timer_lock);
249 if (ret != 0) {
250 return -1;
251 }
252
253 /* Scan the list to find the timer according to timerid. */
254 timer_list = g_timer_list_head;
255 while ((timer_list != NULL) && (timer_list->id != timerid)) {
256 timer_list = timer_list->next;
257 }
258
259 /* Do not find the timer. */
260 if (timer_list == NULL) {
261 pthread_mutex_unlock(&g_timer_lock);
262 return -1;
263 }
264
265 aos_timer_stop(&(timer_list->aos_timer));
266 /* TODO: support interval and first round timer different. */
267 ret = aos_timer_change(&(timer_list->aos_timer), value_ns / 1000000);
268 aos_timer_start(&(timer_list->aos_timer));
269
270 pthread_mutex_unlock(&g_timer_lock);
271 return ret;
272 }
273
timer_gettime(timer_t timerid,struct itimerspec * value)274 int timer_gettime(timer_t timerid, struct itimerspec *value)
275 {
276 int ret = 0;
277 uint64_t time_ns[4] = {0};
278 timer_list_t *timer_list = NULL;
279
280 if (value == NULL) {
281 return -1;
282 }
283
284 ret = pthread_mutex_lock(&g_timer_lock);
285 if (ret != 0) {
286 return -1;
287 }
288
289 /* Scan the list to find the timer according to timerid */
290 timer_list = g_timer_list_head;
291 while(timer_list != NULL) {
292 if (timer_list->id == timerid) {
293 break;
294 }
295 timer_list = timer_list->next;
296 }
297
298 if (timer_list == NULL) {
299 /* The timerid is not found. */
300 pthread_mutex_unlock(&g_timer_lock);
301 return -1;
302 }
303
304 ret = aos_timer_gettime(&(timer_list->aos_timer), time_ns);
305 if (ret != 0) {
306 pthread_mutex_unlock(&g_timer_lock);
307 return -1;
308 }
309 value->it_interval.tv_sec = time_ns[0];
310 value->it_interval.tv_nsec = time_ns[1];
311 value->it_value.tv_sec = time_ns[2];
312 value->it_value.tv_nsec = time_ns[3];
313
314 pthread_mutex_unlock(&g_timer_lock);
315 return 0;
316 }
317
timer_getoverrun(timer_t timerid)318 int timer_getoverrun(timer_t timerid)
319 {
320 errno = ENOSYS;
321 return -1;
322 }
323