1 /*
2  * Copyright (c) 2018 Friedt Professional Engineering Services, Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 #include <kernel.h>
9 #include <limits.h>
10 #include <errno.h>
11 /* required for struct timespec */
12 #include <posix/time.h>
13 #include <sys/util.h>
14 #include <sys_clock.h>
15 
16 /**
17  * @brief Suspend execution for nanosecond intervals.
18  *
19  * See IEEE 1003.1
20  */
nanosleep(const struct timespec * rqtp,struct timespec * rmtp)21 int nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
22 {
23 	uint64_t ns;
24 	uint64_t us;
25 	const bool update_rmtp = rmtp != NULL;
26 
27 	if (rqtp == NULL) {
28 		errno = EFAULT;
29 		return -1;
30 	}
31 
32 	if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0
33 		|| rqtp->tv_nsec >= NSEC_PER_SEC) {
34 		errno = EINVAL;
35 		return -1;
36 	}
37 
38 	if (rqtp->tv_sec == 0 && rqtp->tv_nsec == 0) {
39 		goto do_rmtp_update;
40 	}
41 
42 	if (unlikely(rqtp->tv_sec >= ULLONG_MAX / NSEC_PER_SEC)) {
43 		/* If a user passes this in, we could be here a while, but
44 		 * at least it's technically correct-ish
45 		 */
46 		ns = rqtp->tv_nsec + NSEC_PER_SEC
47 			+ k_sleep(K_SECONDS(rqtp->tv_sec - 1)) * NSEC_PER_MSEC;
48 	} else {
49 		ns = rqtp->tv_sec * NSEC_PER_SEC + rqtp->tv_nsec;
50 	}
51 
52 	/* TODO: improve upper bound when hr timers are available */
53 	us = ceiling_fraction(ns, NSEC_PER_USEC);
54 	do {
55 		us = k_usleep(us);
56 	} while (us != 0);
57 
58 do_rmtp_update:
59 	if (update_rmtp) {
60 		rmtp->tv_sec = 0;
61 		rmtp->tv_nsec = 0;
62 	}
63 
64 	return 0;
65 }
66