2020-05-23 18:09:28 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Friedt Professional Engineering Services, Inc
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <kernel.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <errno.h>
|
|
|
|
/* required for struct timespec */
|
|
|
|
#include <posix/time.h>
|
2020-09-20 20:31:39 +08:00
|
|
|
#include <sys/util.h>
|
2020-05-23 18:09:28 +08:00
|
|
|
#include <sys_clock.h>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Suspend execution for nanosecond intervals.
|
|
|
|
*
|
|
|
|
* See IEEE 1003.1
|
|
|
|
*/
|
|
|
|
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
|
|
|
|
{
|
|
|
|
uint64_t ns;
|
2020-09-20 20:31:39 +08:00
|
|
|
uint64_t us;
|
2020-05-23 18:09:28 +08:00
|
|
|
const bool update_rmtp = rmtp != NULL;
|
|
|
|
|
|
|
|
if (rqtp == NULL) {
|
|
|
|
errno = EFAULT;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0
|
|
|
|
|| rqtp->tv_nsec >= NSEC_PER_SEC) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rqtp->tv_sec == 0 && rqtp->tv_nsec == 0) {
|
|
|
|
goto do_rmtp_update;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(rqtp->tv_sec >= ULLONG_MAX / NSEC_PER_SEC)) {
|
|
|
|
/* If a user passes this in, we could be here a while, but
|
|
|
|
* at least it's technically correct-ish
|
|
|
|
*/
|
2020-09-20 20:31:39 +08:00
|
|
|
ns = rqtp->tv_nsec + NSEC_PER_SEC
|
|
|
|
+ k_sleep(K_SECONDS(rqtp->tv_sec - 1)) * NSEC_PER_MSEC;
|
2020-05-23 18:09:28 +08:00
|
|
|
} else {
|
|
|
|
ns = rqtp->tv_sec * NSEC_PER_SEC + rqtp->tv_nsec;
|
|
|
|
}
|
|
|
|
|
2020-09-20 20:31:39 +08:00
|
|
|
/* TODO: improve upper bound when hr timers are available */
|
|
|
|
us = ceiling_fraction(ns, NSEC_PER_USEC);
|
|
|
|
do {
|
|
|
|
us = k_usleep(us);
|
|
|
|
} while (us != 0);
|
2020-05-23 18:09:28 +08:00
|
|
|
|
|
|
|
do_rmtp_update:
|
|
|
|
if (update_rmtp) {
|
|
|
|
rmtp->tv_sec = 0;
|
|
|
|
rmtp->tv_nsec = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|