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 <zephyr/posix/time.h>
9 #include <zephyr/posix/sys/time.h>
10 #include <zephyr/syscall_handler.h>
11 #include <zephyr/spinlock.h>
12
13 /*
14 * `k_uptime_get` returns a timestamp based on an always increasing
15 * value from the system start. To support the `CLOCK_REALTIME`
16 * clock, this `rt_clock_base` records the time that the system was
17 * started. This can either be set via 'clock_settime', or could be
18 * set from a real time clock, if such hardware is present.
19 */
20 static struct timespec rt_clock_base;
21 static struct k_spinlock rt_clock_base_lock;
22
23 /**
24 * @brief Get clock time specified by clock_id.
25 *
26 * See IEEE 1003.1
27 */
z_impl_clock_gettime(clockid_t clock_id,struct timespec * ts)28 int z_impl_clock_gettime(clockid_t clock_id, struct timespec *ts)
29 {
30 struct timespec base;
31 k_spinlock_key_t key;
32
33 switch (clock_id) {
34 case CLOCK_MONOTONIC:
35 base.tv_sec = 0;
36 base.tv_nsec = 0;
37 break;
38
39 case CLOCK_REALTIME:
40 key = k_spin_lock(&rt_clock_base_lock);
41 base = rt_clock_base;
42 k_spin_unlock(&rt_clock_base_lock, key);
43 break;
44
45 default:
46 errno = EINVAL;
47 return -1;
48 }
49
50 uint64_t ticks = k_uptime_ticks();
51 uint64_t elapsed_secs = ticks / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
52 uint64_t nremainder = ticks - elapsed_secs * CONFIG_SYS_CLOCK_TICKS_PER_SEC;
53
54 ts->tv_sec = (time_t) elapsed_secs;
55 /* For ns 32 bit conversion can be used since its smaller than 1sec. */
56 ts->tv_nsec = (int32_t) k_ticks_to_ns_floor32(nremainder);
57
58 ts->tv_sec += base.tv_sec;
59 ts->tv_nsec += base.tv_nsec;
60 if (ts->tv_nsec >= NSEC_PER_SEC) {
61 ts->tv_sec++;
62 ts->tv_nsec -= NSEC_PER_SEC;
63 }
64
65 return 0;
66 }
67
68 #ifdef CONFIG_USERSPACE
z_vrfy_clock_gettime(clockid_t clock_id,struct timespec * ts)69 int z_vrfy_clock_gettime(clockid_t clock_id, struct timespec *ts)
70 {
71 Z_OOPS(Z_SYSCALL_MEMORY_WRITE(ts, sizeof(*ts)));
72 return z_impl_clock_gettime(clock_id, ts);
73 }
74 #include <syscalls/clock_gettime_mrsh.c>
75 #endif
76
77 /**
78 * @brief Set the time of the specified clock.
79 *
80 * See IEEE 1003.1.
81 *
82 * Note that only the `CLOCK_REALTIME` clock can be set using this
83 * call.
84 */
clock_settime(clockid_t clock_id,const struct timespec * tp)85 int clock_settime(clockid_t clock_id, const struct timespec *tp)
86 {
87 struct timespec base;
88 k_spinlock_key_t key;
89
90 if (clock_id != CLOCK_REALTIME) {
91 errno = EINVAL;
92 return -1;
93 }
94
95 uint64_t elapsed_nsecs = k_ticks_to_ns_floor64(k_uptime_ticks());
96 int64_t delta = (int64_t)NSEC_PER_SEC * tp->tv_sec + tp->tv_nsec
97 - elapsed_nsecs;
98
99 base.tv_sec = delta / NSEC_PER_SEC;
100 base.tv_nsec = delta % NSEC_PER_SEC;
101
102 key = k_spin_lock(&rt_clock_base_lock);
103 rt_clock_base = base;
104 k_spin_unlock(&rt_clock_base_lock, key);
105
106 return 0;
107 }
108
109 /**
110 * @brief Get current real time.
111 *
112 * See IEEE 1003.1
113 */
gettimeofday(struct timeval * tv,void * tz)114 int gettimeofday(struct timeval *tv, void *tz)
115 {
116 struct timespec ts;
117 int res;
118
119 /* As per POSIX, "if tzp is not a null pointer, the behavior
120 * is unspecified." "tzp" is the "tz" parameter above. */
121 ARG_UNUSED(tz);
122
123 res = clock_gettime(CLOCK_REALTIME, &ts);
124 tv->tv_sec = ts.tv_sec;
125 tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
126
127 return res;
128 }
129