| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <kernel.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/printk.h> |
| #include <posix/time.h> |
| |
| #define ACTIVE 1 |
| #define NOT_ACTIVE 0 |
| |
| static void zephyr_timer_wrapper(struct k_timer *ztimer); |
| |
| struct timer_obj { |
| struct k_timer ztimer; |
| void (*sigev_notify_function)(sigval val); |
| sigval val; |
| struct timespec interval; /* Reload value */ |
| uint32_t reload; /* Reload value in ms */ |
| uint32_t status; |
| }; |
| |
| K_MEM_SLAB_DEFINE(posix_timer_slab, sizeof(struct timer_obj), |
| CONFIG_MAX_TIMER_COUNT, 4); |
| |
| static void zephyr_timer_wrapper(struct k_timer *ztimer) |
| { |
| struct timer_obj *timer; |
| |
| timer = (struct timer_obj *)ztimer; |
| |
| if (timer->reload == 0U) { |
| timer->status = NOT_ACTIVE; |
| } |
| |
| (timer->sigev_notify_function)(timer->val); |
| } |
| |
| /** |
| * @brief Create a per-process timer. |
| * |
| * This API does not accept SIGEV_THREAD as valid signal event notification |
| * type. |
| * |
| * See IEEE 1003.1 |
| */ |
| int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) |
| { |
| struct timer_obj *timer; |
| |
| if (clockid != CLOCK_MONOTONIC || evp == NULL || |
| (evp->sigev_notify != SIGEV_NONE && |
| evp->sigev_notify != SIGEV_SIGNAL)) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| if (k_mem_slab_alloc(&posix_timer_slab, (void **)&timer, K_MSEC(100)) == 0) { |
| (void)memset(timer, 0, sizeof(struct timer_obj)); |
| } else { |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| timer->sigev_notify_function = evp->sigev_notify_function; |
| timer->val = evp->sigev_value; |
| timer->interval.tv_sec = 0; |
| timer->interval.tv_nsec = 0; |
| timer->reload = 0U; |
| timer->status = NOT_ACTIVE; |
| |
| if (evp->sigev_notify == SIGEV_NONE) { |
| k_timer_init(&timer->ztimer, NULL, NULL); |
| } else { |
| k_timer_init(&timer->ztimer, zephyr_timer_wrapper, NULL); |
| } |
| |
| *timerid = (timer_t)timer; |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Get amount of time left for expiration on a per-process timer. |
| * |
| * See IEEE 1003.1 |
| */ |
| int timer_gettime(timer_t timerid, struct itimerspec *its) |
| { |
| struct timer_obj *timer = (struct timer_obj *)timerid; |
| int32_t remaining, leftover; |
| int64_t nsecs, secs; |
| |
| if (timer == NULL) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| if (timer->status == ACTIVE) { |
| remaining = k_timer_remaining_get(&timer->ztimer); |
| secs = remaining / MSEC_PER_SEC; |
| leftover = remaining - (secs * MSEC_PER_SEC); |
| nsecs = (int64_t)leftover * NSEC_PER_MSEC; |
| its->it_value.tv_sec = (int32_t) secs; |
| its->it_value.tv_nsec = (int32_t) nsecs; |
| } else { |
| /* Timer is disarmed */ |
| its->it_value.tv_sec = 0; |
| its->it_value.tv_nsec = 0; |
| } |
| |
| /* The interval last set by timer_settime() */ |
| its->it_interval = timer->interval; |
| return 0; |
| } |
| |
| /** |
| * @brief Sets expiration time of per-process timer. |
| * |
| * See IEEE 1003.1 |
| */ |
| int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, |
| struct itimerspec *ovalue) |
| { |
| struct timer_obj *timer = (struct timer_obj *) timerid; |
| uint32_t duration, current; |
| |
| if (timer == NULL || |
| value->it_interval.tv_nsec < 0 || |
| value->it_interval.tv_nsec >= NSEC_PER_SEC || |
| value->it_value.tv_nsec < 0 || |
| value->it_value.tv_nsec >= NSEC_PER_SEC) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| /* Save time to expire and old reload value. */ |
| if (ovalue != NULL) { |
| timer_gettime(timerid, ovalue); |
| } |
| |
| /* Stop the timer if the value is 0 */ |
| if ((value->it_value.tv_sec == 0) && (value->it_value.tv_nsec == 0)) { |
| if (timer->status == ACTIVE) { |
| k_timer_stop(&timer->ztimer); |
| } |
| |
| timer->status = NOT_ACTIVE; |
| return 0; |
| } |
| |
| /* Calculate timer period */ |
| timer->reload = _ts_to_ms(&value->it_interval); |
| timer->interval.tv_sec = value->it_interval.tv_sec; |
| timer->interval.tv_nsec = value->it_interval.tv_nsec; |
| |
| /* Calcaulte timer duration */ |
| duration = _ts_to_ms(&(value->it_value)); |
| if ((flags & TIMER_ABSTIME) != 0) { |
| current = k_timer_remaining_get(&timer->ztimer); |
| |
| if (current >= duration) { |
| duration = 0U; |
| } else { |
| duration -= current; |
| } |
| } |
| |
| if (timer->status == ACTIVE) { |
| k_timer_stop(&timer->ztimer); |
| } |
| |
| timer->status = ACTIVE; |
| k_timer_start(&timer->ztimer, K_MSEC(duration), K_MSEC(timer->reload)); |
| return 0; |
| } |
| |
| /** |
| * @brief Delete a per-process timer. |
| * |
| * See IEEE 1003.1 |
| */ |
| int timer_delete(timer_t timerid) |
| { |
| struct timer_obj *timer = (struct timer_obj *) timerid; |
| |
| if (timer == NULL) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| if (timer->status == ACTIVE) { |
| timer->status = NOT_ACTIVE; |
| k_timer_stop(&timer->ztimer); |
| } |
| |
| k_mem_slab_free(&posix_timer_slab, (void *) &timer); |
| |
| return 0; |
| } |