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