diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 87282302762..50153eb3cba 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -225,6 +225,7 @@ config ARCH_CHIP_STM32 select ARCH_HAVE_I2CRESET select ARCH_HAVE_HEAPCHECK select ARCH_HAVE_TICKLESS + select ARCH_HAVE_TIMEKEEPING select ARMV7M_HAVE_STACKCHECK ---help--- STMicro STM32 architectures (ARM Cortex-M3/4). diff --git a/arch/arm/src/stm32/stm32_freerun.c b/arch/arm/src/stm32/stm32_freerun.c index 836df043ad7..38133c7c365 100644 --- a/arch/arm/src/stm32/stm32_freerun.c +++ b/arch/arm/src/stm32/stm32_freerun.c @@ -53,7 +53,7 @@ #ifdef CONFIG_STM32_FREERUN /**************************************************************************** - * Private Functions + * Private Data ****************************************************************************/ static struct stm32_freerun_s *g_freerun; @@ -80,6 +80,7 @@ static struct stm32_freerun_s *g_freerun; * ****************************************************************************/ +#ifndef CONFIG_CLOCK_TIMEKEEPING static int stm32_freerun_handler(int irq, void *context) { struct stm32_freerun_s *freerun = g_freerun; @@ -90,6 +91,7 @@ static int stm32_freerun_handler(int irq, void *context) STM32_TIM_ACKINT(freerun->tch, 0); return OK; } +#endif /* CONFIG_CLOCK_TIMEKEEPING */ /**************************************************************************** * Public Functions @@ -140,15 +142,21 @@ int stm32_freerun_initialize(struct stm32_freerun_s *freerun, int chan, * success. */ - freerun->chan = chan; - freerun->running = false; - freerun->overflow = 0; + freerun->chan = chan; + freerun->running = false; - g_freerun = freerun; +#ifdef CONFIG_CLOCK_TIMEKEEPING + freerun->counter_mask = 0xffffffffull; +#endif + +#ifndef CONFIG_CLOCK_TIMEKEEPING + freerun->overflow = 0; + g_freerun = freerun; /* Set up to receive the callback when the counter overflow occurs */ STM32_TIM_SETISR(freerun->tch, stm32_freerun_handler, 0); +#endif /* Set timer period */ @@ -157,8 +165,11 @@ int stm32_freerun_initialize(struct stm32_freerun_s *freerun, int chan, /* Start the counter */ STM32_TIM_SETMODE(freerun->tch, STM32_TIM_MODE_UP); + +#ifndef CONFIG_CLOCK_TIMEKEEPING STM32_TIM_ACKINT(freerun->tch, 0); STM32_TIM_ENABLEINT(freerun->tch, 0); +#endif return OK; } @@ -182,6 +193,8 @@ int stm32_freerun_initialize(struct stm32_freerun_s *freerun, int chan, * ****************************************************************************/ +#ifndef CONFIG_CLOCK_TIMEKEEPING + int stm32_freerun_counter(struct stm32_freerun_s *freerun, struct timespec *ts) { @@ -257,6 +270,16 @@ int stm32_freerun_counter(struct stm32_freerun_s *freerun, return OK; } +#else /* CONFIG_CLOCK_TIMEKEEPING */ + +int stm32_freerun_counter(struct stm32_freerun_s *freerun, uint64_t *counter) +{ + *counter = (uint64_t)STM32_TIM_GETCOUNTER(freerun->tch); + return OK; +} + +#endif /* CONFIG_CLOCK_TIMEKEEPING */ + /**************************************************************************** * Name: stm32_freerun_uninitialize * diff --git a/arch/arm/src/stm32/stm32_freerun.h b/arch/arm/src/stm32/stm32_freerun.h index 08dd1786da7..bc7609666cf 100644 --- a/arch/arm/src/stm32/stm32_freerun.h +++ b/arch/arm/src/stm32/stm32_freerun.h @@ -64,9 +64,16 @@ struct stm32_freerun_s { uint8_t chan; /* The timer/counter in use */ bool running; /* True: the timer is running */ - uint32_t overflow; /* Timer counter overflow */ FAR struct stm32_tim_dev_s *tch; /* Handle returned by stm32_tim_init() */ uint32_t frequency; + +#ifndef CONFIG_CLOCK_TIMEKEEPING + uint32_t overflow; /* Timer counter overflow */ +#endif + +#ifdef CONFIG_CLOCK_TIMEKEEPING + uint64_t counter_mask; +#endif }; /**************************************************************************** @@ -127,9 +134,18 @@ int stm32_freerun_initialize(struct stm32_freerun_s *freerun, int chan, * ****************************************************************************/ +#ifndef CONFIG_CLOCK_TIMEKEEPING + int stm32_freerun_counter(struct stm32_freerun_s *freerun, struct timespec *ts); +#else /* CONFIG_CLOCK_TIMEKEEPING */ + +int stm32_freerun_counter(struct stm32_freerun_s *freerun, + uint64_t *counter); + +#endif /* CONFIG_CLOCK_TIMEKEEPING */ + /**************************************************************************** * Name: stm32_freerun_uninitialize * diff --git a/arch/arm/src/stm32/stm32_tickless.c b/arch/arm/src/stm32/stm32_tickless.c index eadef5ce90d..82d7a593d25 100644 --- a/arch/arm/src/stm32/stm32_tickless.c +++ b/arch/arm/src/stm32/stm32_tickless.c @@ -272,11 +272,44 @@ void up_timer_initialize(void) * ****************************************************************************/ +#ifndef CONFIG_CLOCK_TIMEKEEPING + int up_timer_gettime(FAR struct timespec *ts) { return stm32_freerun_counter(&g_tickless.freerun, ts); } +#else + +int up_timer_getcounter(FAR uint64_t *cycles) +{ + return stm32_freerun_counter(&g_tickless.freerun, cycles); +} + +#endif /* CONFIG_CLOCK_TIMEKEEPING */ + +/**************************************************************************** + * Name: up_timer_getmask + * + * Description: + * To be provided + * + * Input Parameters: + * mask - Location to return the 64-bit mask + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_CLOCK_TIMEKEEPING +void up_timer_getmask(FAR uint64_t *mask) +{ + DEBUGASSERT(mask != NULL); + *mask = g_tickless.freerun.counter_mask; +} +#endif /* CONFIG_CLOCK_TIMEKEEPING */ + /**************************************************************************** * Name: up_timer_cancel * diff --git a/include/nuttx/arch.h b/include/nuttx/arch.h index 24925c05ec6..93a595a44ae 100644 --- a/include/nuttx/arch.h +++ b/include/nuttx/arch.h @@ -1496,10 +1496,15 @@ void up_timer_initialize(void); * ****************************************************************************/ -#ifdef CONFIG_SCHED_TICKLESS +#if defined(CONFIG_SCHED_TICKLESS) && !defined(CONFIG_CLOCK_TIMEKEEPING) int up_timer_gettime(FAR struct timespec *ts); #endif +#ifdef CONFIG_CLOCK_TIMEKEEPING +int up_timer_getcounter(FAR uint64_t *cycles); +void up_timer_getmask(FAR uint64_t *mask); +#endif + /**************************************************************************** * Name: up_alarm_cancel * diff --git a/include/sys/syscall.h b/include/sys/syscall.h index 5f36242b619..2fba44de34f 100644 --- a/include/sys/syscall.h +++ b/include/sys/syscall.h @@ -215,7 +215,12 @@ #define SYS_clock_getres (__SYS_clock+1) #define SYS_clock_gettime (__SYS_clock+2) #define SYS_clock_settime (__SYS_clock+3) -#define __SYS_timers (__SYS_clock+4) +#ifdef CONFIG_CLOCK_TIMEKEEPING +# define SYS_adjtime (__SYS_clock+4) +# define __SYS_timers (__SYS_clock+5) +#else +# define __SYS_timers (__SYS_clock+4) +#endif /* The following are defined only if POSIX timers are supported */ diff --git a/include/sys/time.h b/include/sys/time.h index b36e30fca9a..7b1e15e9b5f 100644 --- a/include/sys/time.h +++ b/include/sys/time.h @@ -1,7 +1,7 @@ /**************************************************************************** * include/sys/time.h * - * Copyright (C) 2009, 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2009, 2015-2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -112,6 +112,7 @@ /**************************************************************************** * Public Type Definitions ****************************************************************************/ + /* struct timeval represents time as seconds plus microseconds */ struct timeval @@ -190,6 +191,46 @@ int gettimeofday(FAR struct timeval *tv, FAR struct timezone *tz); int settimeofday(FAR const struct timeval *tv, FAR struct timezone *tz); +/**************************************************************************** + * Name: adjtime + * + * Description: + * The adjtime() function gradually adjusts the system clock (as returned + * by gettimeofday(2)). The amount of time by which the clock is to be + * adjusted is specified in the structure pointed to by delta. + * + * This structure has the following form: + * + * struct timeval + * { + * time_t tv_sec; (seconds) + * suseconds_t tv_usec; (microseconds) + * }; + * + * If the adjustment in delta is positive, then the system clock is + * speeded up by some small percentage (i.e., by adding a small amount of + * time to the clock value in each second) until the adjustment has been + * completed. If the adjustment in delta is negative, then the clock is + * slowed down in a similar fashion. + * + * If a clock adjustment from an earlier adjtime() call is already in + * progress at the time of a later adjtime() call, and delta is not NULL + * for the later call, then the earlier adjustment is stopped, but any + * already completed part of that adjustment is not undone. + * + * If olddelta is not NULL, then the buffer that it points to is used to + * return the amount of time remaining from any previous adjustment that + * has not yet been completed. + * + * NOTE: This is not a POSIX interface but derives from 4.3BSD, System V. + * It is also supported for Linux compatibility. + * + ****************************************************************************/ + +#ifdef CONFIG_CLOCK_TIMEKEEPING +int adjtime(FAR const struct timeval *delta, FAR struct timeval *olddelta); +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/sched/Kconfig b/sched/Kconfig index 864220286ab..731baa801ef 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -167,6 +167,17 @@ config CLOCK_MONOTONIC The value of the CLOCK_MONOTONIC clock cannot be set via clock_settime(). +config ARCH_HAVE_TIMEKEEPING + bool + default n + +config CLOCK_TIMEKEEPING + bool "Support timekeeping algorithms" + default n + depends on EXPERIMENTAL && ARCH_HAVE_TIMEKEEPING + ---help--- + CLOCK_TIMEKEEPING enables experimental time management algorithms. + config JULIAN_TIME bool "Enables Julian time conversions" default n diff --git a/sched/clock/Make.defs b/sched/clock/Make.defs index c0bc23f5101..078f445e3e8 100644 --- a/sched/clock/Make.defs +++ b/sched/clock/Make.defs @@ -38,6 +38,10 @@ CSRCS += clock_time2ticks.c clock_abstime2ticks.c clock_ticks2time.c CSRCS += clock_systimer.c clock_systimespec.c clock_timespec_add.c CSRCS += clock_timespec_subtract.c +ifeq ($(CONFIG_CLOCK_TIMEKEEPING),y +CSRCS += clock_timekeeping.c +endif + # Include clock build support DEPPATH += --dep-path clock diff --git a/sched/clock/clock.h b/sched/clock/clock.h index 8a5c55a3e76..a3286dfc31f 100644 --- a/sched/clock/clock.h +++ b/sched/clock/clock.h @@ -79,7 +79,9 @@ extern volatile uint32_t g_system_timer; # endif #endif +#ifndef CONFIG_CLOCK_TIMEKEEPING extern struct timespec g_basetime; +#endif /******************************************************************************** * Public Function Prototypes diff --git a/sched/clock/clock_gettime.c b/sched/clock/clock_gettime.c index e014984f491..74ffea5be8b 100644 --- a/sched/clock/clock_gettime.c +++ b/sched/clock/clock_gettime.c @@ -49,6 +49,9 @@ #include #include "clock/clock.h" +#ifdef CONFIG_CLOCK_TIMEKEEPING +# include "clock/clock_timekeeping.h" +#endif /**************************************************************************** * Public Functions @@ -90,7 +93,9 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp) * reset. */ -#ifdef CONFIG_SCHED_TICKLESS +#if defined(CONFIG_CLOCK_TIMEKEEPING) + ret = clock_timekeeping_get_monotonic_time(tp); +#elif defined(CONFIG_SCHED_TICKLESS) ret = up_timer_gettime(tp); #else ret = clock_systimespec(tp); @@ -113,7 +118,15 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp) * last set. */ +#if defined(CONFIG_CLOCK_TIMEKEEPING) + ret = clock_timekeeping_get_wall_time(tp); +#elif defined(CONFIG_SCHED_TICKLESS) + ret = up_timer_gettime(&ts); +#else ret = clock_systimespec(&ts); +#endif + +#ifndef CONFIG_CLOCK_TIMEKEEPING if (ret == OK) { /* Add the base time to this. The base time is the time-of-day @@ -138,6 +151,7 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp) tp->tv_sec = ts.tv_sec; tp->tv_nsec = ts.tv_nsec; } +#endif /* CONFIG_CLOCK_TIMEKEEPING */ } else { diff --git a/sched/clock/clock_initialize.c b/sched/clock/clock_initialize.c index 6e653e78e7f..9e5d9f93f4b 100644 --- a/sched/clock/clock_initialize.c +++ b/sched/clock/clock_initialize.c @@ -54,6 +54,9 @@ #include #include "clock/clock.h" +#ifdef CONFIG_CLOCK_TIMEKEEPING +# include "clock/clock_timekeeping.h" +#endif /**************************************************************************** * Pre-processor Definitions @@ -172,10 +175,16 @@ static void clock_inittime(void) { /* (Re-)initialize the time value to match the RTC */ - (void)clock_basetime(&g_basetime); +#ifndef CONFIG_CLOCK_TIMEKEEPING +#ifndef CONFIG_RTC_HIRES + clock_basetime(&g_basetime); +#endif #ifndef CONFIG_SCHED_TICKLESS g_system_timer = 0; #endif +#else + clock_inittimekeeping(); +#endif } /**************************************************************************** diff --git a/sched/clock/clock_settime.c b/sched/clock/clock_settime.c index 8f269079d89..ec0c1d85be9 100644 --- a/sched/clock/clock_settime.c +++ b/sched/clock/clock_settime.c @@ -48,6 +48,9 @@ #include #include "clock/clock.h" +#ifdef CONFIG_CLOCK_TIMEKEEPING +# include "clock/clock_timekeeping.h" +#endif /**************************************************************************** * Public Functions @@ -76,6 +79,7 @@ int clock_settime(clockid_t clock_id, FAR const struct timespec *tp) if (clock_id == CLOCK_REALTIME) { +#ifndef CONFIG_CLOCK_TIMEKEEPING /* Interrupts are disabled here so that the in-memory time * representation and the RTC setting will be as close as * possible. @@ -123,6 +127,9 @@ int clock_settime(clockid_t clock_id, FAR const struct timespec *tp) sinfo("basetime=(%ld,%lu) bias=(%ld,%lu)\n", (long)g_basetime.tv_sec, (unsigned long)g_basetime.tv_nsec, (long)bias.tv_sec, (unsigned long)bias.tv_nsec); +#else + ret = clock_timekeeping_set_wall_time(tp); +#endif } else { diff --git a/sched/clock/clock_systimer.c b/sched/clock/clock_systimer.c index 3e8e978ce99..6c9645fbf86 100644 --- a/sched/clock/clock_systimer.c +++ b/sched/clock/clock_systimer.c @@ -83,7 +83,11 @@ systime_t clock_systimer(void) /* Get the time from the platform specific hardware */ +#ifndef CONFIG_CLOCK_TIMEKEEPING (void)up_timer_gettime(&ts); +#else + (void)clock_timekeeping_get_monotonic_time(&ts); +#endif /* Convert to a 64-bit value in microseconds, then in clock tick units */ @@ -96,7 +100,11 @@ systime_t clock_systimer(void) /* Get the time from the platform specific hardware */ +#ifndef CONFIG_CLOCK_TIMEKEEPING (void)up_timer_gettime(&ts); +#else + (void)clock_timekeeping_get_monotonic_time(&ts); +#endif /* Convert to a 64- then a 32-bit value */ diff --git a/sched/clock/clock_timekeeping.c b/sched/clock/clock_timekeeping.c new file mode 100644 index 00000000000..c8fb361c6fe --- /dev/null +++ b/sched/clock/clock_timekeeping.c @@ -0,0 +1,329 @@ +/************************************************************************ + * sched/clock/clock_timekeeping.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Max Neklyudov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************/ + +/************************************************************************ + * Included Files + ************************************************************************/ + +#include + +#ifdef CONFIG_CLOCK_TIMEKEEPING + +#include +#include +#include +#include +#include + +#include +#include + +#include "clock/clock.h" + +/************************************************************************ + * Pre-processor Definitions + ************************************************************************/ + +#define NTP_MAX_ADJUST 500 + +/********************************************************************** + * Private Data + **********************************************************************/ + +static struct timespec g_clock_wall_time; +static struct timespec g_clock_monotonic_time; +static uint64_t g_clock_last_counter; +static uint64_t g_clock_mask; +static long g_clock_adjust; + +/************************************************************************ + * Private Functions + ************************************************************************/ + +/************************************************************************ + * Name: clock_get_current_time + ************************************************************************/ + +static int clock_get_current_time(FAR struct timespec *ts, + FAR struct timespec *base) +{ + irqstate_t flags; + uint64_t counter; + uint64_t offset; + uint64_t nsec; + time_t sec; + int ret; + + flags = enter_critical_section(); + + ret = up_timer_getcounter(&counter); + if (ret < 0) + { + goto errout_in_critical_section; + } + + offset = (counter - g_clock_last_counter) & g_clock_mask; + nsec = offset * NSEC_PER_TICK; + sec = nsec / NSEC_PER_SEC; + nsec -= sec * NSEC_PER_SEC; + + nsec += base->tv_nsec; + if (nsec > NSEC_PER_SEC) + { + nsec -= NSEC_PER_SEC; + sec += 1; + } + + ts->tv_nsec = nsec; + ts->tv_sec = base->tv_sec + sec; + +errout_in_critical_section: + leave_critical_section(flags); + return ret; +} + +/************************************************************************ + * Public Functions + ************************************************************************/ + +/************************************************************************ + * Name: clock_timekeeping_get_monotonic_time + ************************************************************************/ + +int clock_timekeeping_get_monotonic_time(FAR struct timespec *ts) +{ + return clock_get_current_time(ts, &g_clock_monotonic_time); +} + +/************************************************************************ + * Name: clock_timekeeping_get_wall_time + ************************************************************************/ + +int clock_timekeeping_get_wall_time(FAR struct timespec *ts) +{ + return clock_get_current_time(ts, &g_clock_wall_time); +} + +/************************************************************************ + * Name: clock_timekeeping_set_wall_time + ************************************************************************/ + +int clock_timekeeping_set_wall_time(FAR struct timespec *ts) +{ + irqstate_t flags; + uint64_t counter; + int ret; + + flags = enter_critical_section(); + + ret = up_timer_getcounter(&counter); + if (ret < 0) + { + goto errout_in_critical_section; + } + + g_clock_wall_time = *ts; + g_clock_adjust = 0; + g_clock_last_counter = counter; + +errout_in_critical_section: + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: adjtime + * + * Description: + * The adjtime() function gradually adjusts the system clock (as returned + * by gettimeofday(2)). The amount of time by which the clock is to be + * adjusted is specified in the structure pointed to by delta. + * + * This structure has the following form: + * + * struct timeval + * { + * time_t tv_sec; (seconds) + * suseconds_t tv_usec; (microseconds) + * }; + * + * If the adjustment in delta is positive, then the system clock is + * speeded up by some small percentage (i.e., by adding a small amount of + * time to the clock value in each second) until the adjustment has been + * completed. If the adjustment in delta is negative, then the clock is + * slowed down in a similar fashion. + * + * If a clock adjustment from an earlier adjtime() call is already in + * progress at the time of a later adjtime() call, and delta is not NULL + * for the later call, then the earlier adjustment is stopped, but any + * already completed part of that adjustment is not undone. + * + * If olddelta is not NULL, then the buffer that it points to is used to + * return the amount of time remaining from any previous adjustment that + * has not yet been completed. + * + * NOTE: This is not a POSIX interface but derives from 4.3BSD, System V. + * It is also supported for Linux compatibility. + * + ****************************************************************************/ + +int adjtime(FAR const struct timeval *delta, FAR struct timeval *olddelta) +{ + irqstate_t flags; + long adjust_usec; + + if (!delta) + { + set_errno(EINVAL); + return -1; + } + + flags = enter_critical_section(); + + adjust_usec = delta->tv_sec * USEC_PER_SEC + delta->tv_usec; + + if (olddelta) + { + olddelta->tv_usec = g_clock_adjust; + } + + g_clock_adjust = adjust_usec; + + leave_critical_section(flags); + + return OK; +} + +/************************************************************************ + * Name: clock_update_wall_time + ************************************************************************/ + +void clock_update_wall_time(void) +{ + irqstate_t flags; + uint64_t counter; + uint64_t offset; + int64_t nsec; + time_t sec; + int ret; + + flags = enter_critical_section(); + + ret = up_timer_getcounter(&counter); + if (ret < 0) + { + goto errout_in_critical_section; + } + + offset = (counter - g_clock_last_counter) & g_clock_mask; + if (offset == 0) + { + goto errout_in_critical_section; + } + + nsec = offset * NSEC_PER_TICK; + sec = nsec / NSEC_PER_SEC; + nsec -= sec * NSEC_PER_SEC; + + g_clock_monotonic_time.tv_sec += sec; + g_clock_monotonic_time.tv_nsec += nsec; + if (g_clock_monotonic_time.tv_nsec > NSEC_PER_SEC) + { + g_clock_monotonic_time.tv_nsec -= NSEC_PER_SEC; + g_clock_monotonic_time.tv_sec += 1; + } + + nsec += g_clock_wall_time.tv_nsec; + if (nsec > NSEC_PER_SEC) + { + nsec -= NSEC_PER_SEC; + sec += 1; + } + + if (g_clock_adjust != 0 && sec > 0) + { + long adjust = NTP_MAX_ADJUST * (long)sec; + if (g_clock_adjust < adjust && g_clock_adjust > -adjust) + { + adjust = g_clock_adjust; + } + + nsec += adjust * NSEC_PER_USEC; + + while (nsec < 0) + { + nsec += NSEC_PER_SEC; + sec -= 1; + } + + while (nsec > NSEC_PER_SEC) + { + nsec -= NSEC_PER_SEC; + sec += 1; + } + } + + g_clock_wall_time.tv_sec += sec; + g_clock_wall_time.tv_nsec = (long)nsec; + + g_clock_last_counter = counter; + +errout_in_critical_section: + leave_critical_section(flags); +} + +/************************************************************************ + * Name: clock_inittimekeeping + ************************************************************************/ + +void clock_inittimekeeping(void) +{ + struct tm rtctime; + + up_timer_getmask(&g_clock_mask); + + /* Get the broken-errout_in_critical_section time from the date/time RTC. */ + + (void)up_rtc_getdatetime(&rtctime); + + /* And use the broken-errout_in_critical_section time to initialize the system time */ + + g_clock_wall_time.tv_sec = mktime(&rtctime); + g_clock_wall_time.tv_nsec = 0; + + memset(&g_clock_monotonic_time, 0, sizeof(g_clock_monotonic_time)); +} + +#endif /* CONFIG_CLOCK_TIMEKEEPING */ diff --git a/sched/clock/clock_timekeeping.h b/sched/clock/clock_timekeeping.h new file mode 100644 index 00000000000..e86613a3a44 --- /dev/null +++ b/sched/clock/clock_timekeeping.h @@ -0,0 +1,62 @@ +/******************************************************************************** + * sched/clock/clock_timekeeping.h + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Max Neklyudov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ********************************************************************************/ + +#ifndef __SCHED_CLOCK_CLOCK_TIMEKEEPING_H +#define __SCHED_CLOCK_CLOCK_TIMEKEEPING_H + +/******************************************************************************** + * Included Files + ********************************************************************************/ + +#include +#include + +#include + +#include + +/******************************************************************************** + * Public Function Prototypes + ********************************************************************************/ + +int clock_timekeeping_get_monotonic_time(FAR struct timespec *ts); +int clock_timekeeping_get_wall_time(FAR struct timespec *ts); +int clock_timekeeping_set_wall_time(FAR struct timespec *ts); + +void clock_update_wall_time(void); + +void clock_inittimekeeping(void); + +#endif /* __SCHED_CLOCK_CLOCK_TIMEKEEPING_H */ diff --git a/sched/sched/sched_processtimer.c b/sched/sched/sched_processtimer.c index 33c3f583ac8..9b64abed1b8 100644 --- a/sched/sched/sched_processtimer.c +++ b/sched/sched/sched_processtimer.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/sched/sched_processtimer.c * - * Copyright (C) 2007, 2009, 2014-2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2014-2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -144,6 +144,12 @@ static inline void sched_process_scheduler(void) void sched_process_timer(void) { +#ifdef CONFIG_CLOCK_TIMEKEEPING + /* Process wall time */ + + clock_update_wall_time(); +#endif + /* Increment the system time (if in the link) */ #ifdef CONFIG_HAVE_WEAKFUNCTIONS diff --git a/sched/sched/sched_timerexpiration.c b/sched/sched/sched_timerexpiration.c index a0ebcff8c9c..309b1a705d1 100644 --- a/sched/sched/sched_timerexpiration.c +++ b/sched/sched/sched_timerexpiration.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/sched/sched_timerexpiration.c * - * Copyright (C) 2014-2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2014-2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -53,6 +53,10 @@ #include "wdog/wdog.h" #include "clock/clock.h" +#ifdef CONFIG_CLOCK_TIMEKEEPING +# include "clock/clock_timekeeping.h" +#endif + #ifdef CONFIG_SCHED_TICKLESS /**************************************************************************** @@ -262,6 +266,12 @@ static unsigned int sched_timer_process(unsigned int ticks, bool noswitches) unsigned int rettime = 0; unsigned int tmp; +#ifdef CONFIG_CLOCK_TIMEKEEPING + /* Process wall time */ + + clock_update_wall_time(); +#endif + /* Process watchdogs */ tmp = wd_timer(ticks); diff --git a/syscall/syscall.csv b/syscall/syscall.csv index 49842f3f304..c1fdd2e2916 100644 --- a/syscall/syscall.csv +++ b/syscall/syscall.csv @@ -1,4 +1,5 @@ "_exit","unistd.h","","void","int" +"adjtime","sys/time.h","defined(CONFIG_CLOCK_TIMEKEEPING)","int","FAR const struct timeval *","FAR struct timeval *" "aio_cancel","aio.h","defined(CONFIG_FS_AIO)","int","int","FAR struct aiocb *" "aio_fsync","aio.h","defined(CONFIG_FS_AIO)","int","int","FAR struct aiocb *" "aio_read","aio.h","defined(CONFIG_FS_AIO)","int","FAR struct aiocb *" diff --git a/syscall/syscall_lookup.h b/syscall/syscall_lookup.h index 58602a19ace..d0f304fbadd 100644 --- a/syscall/syscall_lookup.h +++ b/syscall/syscall_lookup.h @@ -152,6 +152,9 @@ SYSCALL_LOOKUP(up_assert, 2, STUB_up_assert) SYSCALL_LOOKUP(clock_getres, 2, STUB_clock_getres) SYSCALL_LOOKUP(clock_gettime, 2, STUB_clock_gettime) SYSCALL_LOOKUP(clock_settime, 2, STUB_clock_settime) +#ifdef CONFIG_CLOCK_TIMEKEEPING + SYSCALL_LOOKUP(adjtime, 2, STUB_adjtime) +#endif /* The following are defined only if POSIX timers are supported */ diff --git a/syscall/syscall_stublookup.c b/syscall/syscall_stublookup.c index a5cace0e37c..868eca6f132 100644 --- a/syscall/syscall_stublookup.c +++ b/syscall/syscall_stublookup.c @@ -154,6 +154,7 @@ uintptr_t STUB_clock_systimer(int nbr); uintptr_t STUB_clock_getres(int nbr, uintptr_t parm1, uintptr_t parm2); uintptr_t STUB_clock_gettime(int nbr, uintptr_t parm1, uintptr_t parm2); uintptr_t STUB_clock_settime(int nbr, uintptr_t parm1, uintptr_t parm2); +uintptr_t STUB_adjtime(int nbr, uintptr_t parm1, uintptr_t parm2); /* The following are defined only if POSIX timers are supported */