diff --git a/extras/timekeeping/LICENSE b/extras/timekeeping/LICENSE new file mode 100644 index 0000000..c924453 --- /dev/null +++ b/extras/timekeeping/LICENSE @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2018, Jeff Kletsky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the copyright holder 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 + * HOLDER 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. + */ diff --git a/extras/timekeeping/README.timekeeping.md b/extras/timekeeping/README.timekeeping.md new file mode 100644 index 0000000..77277e7 --- /dev/null +++ b/extras/timekeeping/README.timekeeping.md @@ -0,0 +1,213 @@ +# Overview +`timekeeping` provides an implementation of a clock that can provide monotonic time with microsecond resolution and supports many of the common time-of-day functions in a POSIX-like manner through `gettimeofday()`. It does not supply a clock *discipline*, such as NTP or SNTP, but does implement `settimeofday()` and `adjtime()` to allow implementation of clock discipline. + +The system clock is used to as the time reference. Time is available from boot or wake, referenced to the system clock's "zero" until `settimeofday()` is called. + +Timezone functionality is provided through `tzset()`, with the assumption that the `timekeeping` internal clock is set to UTC. + +**Important Note** + +This code does not alter OS-level behavior related to "ticks". For example, `vTaskDelay(const TickType_t xTicksToDelay)` will continue to result in a delay of `xTicksToDelay` *ticks,* which may not be the same length of time as indicated by calls to `gettimeofday()` or related functions. + +# Supported Functionality +## Standard C Library Calls +### Implemented or Updated + + #include + + int + gettimeofday(struct timeval *restrict tp, void *restrict tzp); + + int + settimeofday(const struct timeval *tp, const struct timezone *tzp); + + int + adjtime(const struct timeval *delta, struct timeval *olddelta); + +### Existing, Unmodified + + #include + + int + setenv(const char *name, const char *value, int overwrite); +
+ + #include + + void + tzset(void); + +Note that POSIX-style clock selection and timers have not been modified at this time. + +## Implementation-specific Details + +See [notes below](#managing-system-clock-wrap) on the need to call `gettimeofday()` or `adjtime()` at least once an hour to detect and compensate for clock "wrap". + +Note that, in keeping with current practice, calls to `settimeofday()` and `gettimeofday()` do not utilize incoming values in the timezone argument to modify the value referenced by the time argument. In this implementation, the time argument for `gettimeofday()` and `settimeofday()` is *always* relative to the internal clock's datum (assumed by this implementation to be UTC). + +With the prototypes in ``, it is either not possible (`const`) or unsafe (`void`) to modify the timezone argument. As a result, this implementation ignores any value passes in the timezone argument entirely. This implementation does not consider it an error to pass a non-null value for the timezone argument. + +It is suggested that time-critical functions be executed from high-priority threads to reduce the likelihood of timing errors that may occur if the thread is swapped out of execution during the calls. + +# Examples + +## Set Time of Day (UTC) + + struct timeval tv; + tv->tv_sec = 1518798027; /* 2018-02-16T16:20:27+00:00 */ + tv->tv_usec = 0; + settimeofday(&tv, NULL); +## Get Time of Day (UTC) + struct timeval tv; + gettimeofday(&tv, NULL); +## Slew Time + struct timeval tv; + tv->tv_sec = 0; + tv->tv_usec = -50 * 1000; /* -50 ms */ + adjtime(&tv, NULL); +## Set Local Time Zone to US Pacific + setenv("TZ", "PST8PDT7,M3.1.0,M11.1.0", 1); + tzset(); +## Set Local Time Zone to UTC + setenv("TZ", "UTC0UTC0", 1); + tzset(); + + +# Timezone Management + +As newlib is typically compiled and supplied with for esp-open-rtos, the timezone can be managed through `setenv()` and `tzset()` using POSIX-style timezone strings. + +For example + + setenv("TZ", "PST8PDT7,M3.1.0,M11.1.0", 1); + tzset(); + +will set United States' Pacific Time rules (as of 2018) for both standard and daylight savings time. As only two rules are implemented in newlib, calculations across changes in timezone rules, or localities that have more than two changes per year are not directly supported by newlib itself. + +Although the source code of `setenv()` appears to call `tzset()` when `TZ` is set, the combination of empirical experience and the POSIX description of `tzset()` +> However, portable applications should call `tzset()` explicitly before using `ctime_r()` or `localtime_r()` because setting timezone information is optional for those functions. + +strongly suggest an explicit call to `tzset()` after changing `TZ`. + +Note that `unsetenv("TZ")` will not "reset" all the internal variables related to timezone implementation. One approach to setting a zero-offset timezone is + + setenv("TZ", "UTC0UTC0"); + tzset(); + +While the timezone rules will still be populated, the global `_daylight` is set to `0` (`DST_NONE`) so that the rules should not be consulted by "compliant" code. Further, the offset in the two rules is set to `0` so that even if the rules are consulted by other code that does not respect the `_daylight` setting there is no offset applied. + +Direct manipulation of the timezone-related variables is not recommended due to multi-threading issues. + +# Implementation Approach +## Hardware Clock +The system clock is used as the underlying clock. A discussion of why the ESP8266 RTC is *not* used may be found in an [appendix](#why-not-the-rtc). Espressif specifies a 15-ppm or better crystal, so the system clock should be accurate to better than 1 ms per minute, or 1.3 seconds per day. For comparison, color-burst crystals are typically in the 30-100 ppm range. + +The system clock is a 32-bit value with one-microsecond resolution. As a result, it will "wrap" every hour and a few minutes. This 32-bit limitation is accommodated for in software. The system clock is allowed to free run; all compensation and adjustments are done in software. + +## Hardware Clock to Internal Clock Conversion + +The "internal clock" is the system's estimate of microseconds since the epoch. As the ESP8266 is a self-contained system and the only consumer of the clock, there is no implementation of a "wall-time CMOS" functionality. + +The value of the internal clock is referred to as `internal_clock` and `system_clock` refers to the current value that would be read using `sdk_system_get_time()`. + + +### Without `adjtime()` Slew in Process +When there is not a pending slew from a call to `adjtime()`, the internal clock is calculated as + + internal_clock = system_clock + clock_offset + +`clock_offset` is a 64-bit, signed integer in units of microseconds and should not overflow until well past the useful lifespan of the ESP8266. `clock_offset` includes the aggregated offset specified by calls to `settimeofday()`, `adjtime()`, as well as the overflow from the [system clock wrapping](#managing-system-clock-wrap). + +### `adjtime()` Implementation + +`adjtime()` is generally used after the initial clock set to slowly slew the time, rather than step it. The reference implementation of NTPv4 limits its slew rate requests to 500 us/s (500 ppm). This rate of slew should be sufficient for the crystal-controlled system clock of the ESP8266. + +The slew rate of the current implementation is fixed 500 us/s by the macro `ADJTIME_SLEW_PERIOD`, measured in units of us of elapsed time per us of slew. By defining it in this way, integer arithmetic can be used, providing significant speed advantages over floating-point calculations. + +When a call to `adjtime()` is made, the current time is captured in `slew_start_time`. The elapsed time in microseconds to slew the requested amount is calculated, added to the current `internal_clock`, and saved as `slew_complete_time`. Until `system_clock + clock_offset` passes `slew_complete_time` there is slew in process and the internal clock is calculated as + + internal_clock = (system_clock + clock_offset) + + ( (system_clock + clock_offset) + - slew_start_time ) + / SIGNED_ADJTIME_SLEW_PERIOD + +where `SIGNED_ADJTIME_SLEW_PERIOD` adopts the sign of the requested `delta`. Use of the un-slewed clock is intentional as it simplifies calculations. + +Once the slew is complete, the amount of slew is added to `clock_offset` and calculation of `internal_clock` resumes as described in the [previous section](#without-adjtime-slew-in-process). See [*Managing System-clock Wrap*](#managing-system-clock-wrap) for how and when this adjustment is made. + +Note that `settimeofday()` is implemented as a "hard set" of the internal clock and will abort any in-progress clock slew. + +Any remaining slew from prior calls to `adjtime()` can be returned by the call in its second argument, *but are overridden by the new value, not added to them.* The remaining slew is calculated as + + olddelta_in_us = (slew_complete_time - (system_clock + clock_offset)) + / SIGNED_ADJTIME_SLEW_PERIOD + +and returned in `struct timeval *olddelta` in normalized form. `adjtime()` can be called with a NULL `delta` and will not modify the slew in that case. + +Note that the magnitude of `delta` cannot exceed `ADJTIME_MAX_SECS_ALLOWED`, presently defined as 2000 (seconds). This is significantly greater than the 128-ms limit within the NTPv4 reference implementation, small enough to prevent overflow of the 32-bit `adjtime_delta` internal, and hopefully larger than any rational use of `adjtime()`. + + +### Managing System-clock Wrap +As previously discussed, the system clock wraps in a little bit more than hour (232 microseconds, ~ 71 minutes). This needs to be detected and accounted for prior to any call to obtain internal time, as well as before it wraps a second time. the internal `_check_system_clock()` manages this, as well as the incorporation of completed slew into `clock_offset`. It is called within the implementations of `settimeofday()`, `adjtime()`, and `gettimeofday()`. + +`_check_system_clock()` operates by recording the last value of the system clock and comparing it to the current value. If the current value is less than the last value, then it is assumed that a single wrap has occurred and the value of `clock_offset` is increased by 232. It also checks to see if there is a completed clock slew. If so, adds the (signed) value of the slew in microseconds to `clock_offset` and resets the internal state variables associated with slew. + +In many situations either or both `adjtime()` or `getttimeofday()` are called at least once an hour. If this is not the case, `gettimeofday(NULL, NULL)` should be called once an hour, using a repeating timer or other appropriate method. + + +# License + +All files in this directory + + Copyright (c) 2018, Jeff Kletsky + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the copyright holder 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 + HOLDER 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. + +Some files may have additional copyright and/or licenses. Consult those files for details. At a minimum, this includes: + +* `lwipopts.h` -- Copyright (c) 2001-2003 Swedish Institute of Computer Science. + +# Appendix + +## Why Not the RTC? + +While the ESP8266 RTC seems a plausible choice for time keeping, it has several chip-level implementation details that make it less desirable than the system clock. + +One issue is resolution. The RTC has only a nominal 6-ms resolution, compared to the 1-ms resolution of the system clock. + +A second issue is stability. It is believed that the ESP8266 RTC is a simple RC oscillator. As such, it is likely to be highly temperature sensitive. + +Accuracy is another concern. While the SDK can provide an estimate of the RTC's period, it is likely measured with respect to the system clock and, as such, can be no better than the accuracy of the system clock. Further, it is a very noisy estimate, with values showing short-term standard deviation in the 500-1000 ppm range, compared to the system clock crystal which is spec-ed by Espressif to be 15 ppm or better. As the system clock crystal is likely used to derive the RF, units that have been FCC certified are likely to meet this specification or better (10 ppm is a common value). + +Finally, according to the Espressif SDK documentation, the RTC is reset under many situations that one would have hoped a "true" RTC would maintain time keeping. It notes that "CHIP_EN (including the deep-sleep wakeup)" results in "RTC memory is random value, RTC timer starts from zero". + +While the RTC's longer period means that counter wrap occurs less frequently (~7 hours for the RTC, compared to a little over an hour for the system clock), this is not deemed a significant advantage. + +As a result, the system clock (or an external RTC) is preferred over the ESP8266 internal RTC. + +Normal memory is used for all state variables, rather than the internal RTC memory. If clock-state information needs to be preserved during sleep, it can be obtained through `gettimeofday(&tv, NULL)` and, if desired, any outstanding slew obtained through `adjtime(NULL, &olddelta)`. No accessors to internal `timekeeping` data are believed required. \ No newline at end of file diff --git a/extras/timekeeping/component.mk b/extras/timekeeping/component.mk new file mode 100644 index 0000000..512c3cc --- /dev/null +++ b/extras/timekeeping/component.mk @@ -0,0 +1,8 @@ +# Component makefile for extras/timekeeping + +INC_DIRS += $(timekeeping_ROOT) + +# args for passing into compile rule generation +timekeeping_SRC_DIR = $(timekeeping_ROOT) + +$(eval $(call component_compile_rules,timekeeping)) diff --git a/extras/timekeeping/tests/quick/Makefile b/extras/timekeeping/tests/quick/Makefile new file mode 100644 index 0000000..6aaf49f --- /dev/null +++ b/extras/timekeeping/tests/quick/Makefile @@ -0,0 +1,6 @@ + +PROGRAM=timekeeping_quick_tests + +EXTRA_COMPONENTS = extras/timekeeping + +include ../../../../common.mk diff --git a/extras/timekeeping/tests/quick/timekeeping_quick_tests.c b/extras/timekeeping/tests/quick/timekeeping_quick_tests.c new file mode 100644 index 0000000..4b4b1fe --- /dev/null +++ b/extras/timekeeping/tests/quick/timekeeping_quick_tests.c @@ -0,0 +1,364 @@ +/* + * Tests of functionality within extras/timekeeping + */ + +/*- + * Copyright (c) 2018, Jeff Kletsky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the copyright holder 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 + * HOLDER 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. + */ + + +#include +#include + +#include +#include + +/* espressif/esp_system.h uses "bool" but fails to #include */ +#include +#include +#include +#include + +/* Not defined in espressif/esp_wifi.h */ +extern bool +sdk_wifi_set_opmode_current(uint8_t opmode); + +/* For sdk_system_restart_in_nmi() */ +#include + +#include +#include + + +#define BLUE_LED 2 +#define LED_ON 0 +#define LED_OFF 1 + +#define WAIT_FOR_UART_SECS 5 /* 2 might be OK */ + +#define TV2LD(TV) ((long double)TV.tv_sec + (long double)TV.tv_usec * 1.e-6) + +void +dump_tzglobals(void) { + + /* + * Defined in newlib-xtensa/newlib/libc/include/time.h + * implemented in newlib-xtensa/newlib/libc/time/tzvars.c + */ + + printf("_timezone = %li\n", _timezone); + printf("_daylight = %i\n", _daylight); + printf("_tzname[0] = %s\n", _tzname[0]); + printf("_tzname[1] = %s\n", _tzname[1]); +} + +void +dump_tzinfo(void) { + + __tzinfo_type *tzinfo; + __tzrule_type tzrule; + + tzinfo = __gettzinfo(); + printf("{%i, %i,\n", tzinfo->__tznorth, tzinfo->__tzyear); + tzrule = tzinfo->__tzrule[0]; + printf("{%c, %i, %i, %i, %i, %li, %li},\n", + tzrule.ch, tzrule.m, tzrule.n, tzrule.d, tzrule.s, (long)tzrule.change, (long)tzrule.offset); + tzrule = tzinfo->__tzrule[1]; + printf("{%c, %i, %i, %i, %i, %li, %li}}\n", + tzrule.ch, tzrule.m, tzrule.n, tzrule.d, tzrule.s, (long)tzrule.change, (long)tzrule.offset); +} + +void +dump_both(void) { + dump_tzglobals(); + dump_tzinfo(); +} + + +void +testTask(void *pvParameters) +{ + char *env_value; + struct timeval tv; + time_t time_secs; + long num_runs; + long idx; + TickType_t base_ticks; + TickType_t increment_ticks; + int seconds_per_report; + int retval; + uint32_t t0; + uint32_t t1; + + /* These variables for calculation-speed tests */ + int64_t internal_clock; + int64_t system_plus_offset; + int64_t slew_start_time; + int64_t seemingly_useful = 0; +#define ADJTIME_SLEW_PERIOD 2000 +#define ADJTIME_SLEW_RATE 500.e-6 + + + gpio_enable(BLUE_LED, GPIO_OUTPUT); + gpio_write(BLUE_LED, LED_OFF); + + vTaskDelay(WAIT_FOR_UART_SECS * 1000 / portTICK_PERIOD_MS); + + gpio_write(BLUE_LED, LED_ON); + + printf("\n"); + printf("===> setenv/getenv test; expect TEST ='test value'\n"); + + setenv("TEST", "test value", 1); + env_value = getenv("TEST"); + printf("TEST = '%s'\n", env_value); + + printf("\n"); + printf("===> Dump tzinfo before change:\n"); + dump_both(); + + + /* "Full" POSIX TZ spec in 2018 is TZ=PST8PDT7,M3.1.0/02:00:00,M11.1.0/02:00:00 */ + + printf("\n"); + printf("===> Change TZ to PST8PDT7,M3.1.0,M11.1.0\n"); + + setenv("TZ", "PST8PDT7,M3.1.0,M11.1.0", 1); + dump_both(); + + printf("\n"); + printf("===> Call tzset() just in case that's the reason it's not working\n"); + tzset(); + dump_both(); + + + printf("\n"); + printf("===> unsetenv TZ and confirm\n"); + unsetenv("TZ"); + env_value = getenv("TZ"); + if (env_value) { + printf("TZ = %s\n", env_value); + } else { + printf("TZ not found (expected)\n"); + } + tzset(); + dump_both(); + printf("##### NOTE: tzset() only changes the globals if TZ is unset\n"); + + printf("\n"); + printf("===> Change TZ to PST8PDT7,M3.1.0,M11.1.0\n"); + + setenv("TZ", "PST8PDT7,M3.1.0,M11.1.0", 1); + tzset(); + dump_both(); + + printf("\n"); + printf("===> Change TZ to UTC (not terribly useful without zoneinfo\n"); + + setenv("TZ", "UTC", 1); + tzset(); + dump_both(); + printf("##### NOTE: just changes name, not any values; 'UTC0UTC0' better\n"); + + + printf("\n"); + printf("===> Change TZ to UTC0\n"); + + setenv("TZ", "UTC0", 1); + tzset(); + dump_both(); + + printf("\n"); + printf("===> Change TZ to UTC0UTC0\n"); + + setenv("TZ", "UTC0UTC0", 1); + tzset(); + dump_both(); + + printf("\n"); + printf("===> Change TZ to UTC0UTC0,0,0\n"); + + setenv("TZ", "UTC0UTC0,0,0", 1); + tzset(); + dump_both(); + + printf("\n"); + printf("===> Change TZ to UTC0UTC0,J0/0,J0/0\n"); + + setenv("TZ", "UTC0UTC0,J0/0,J0/0", 1); + tzset(); + dump_both(); + + + printf("\n"); + printf("===> Change TZ to PST8PDT7,M3.1.0,M11.1.0\n"); + printf("===> PST: 1518754387 Thursday, February 15, 2018 8:13:07 PM GMT-08:00\n"); + setenv("TZ", "PST8PDT7,M3.1.0,M11.1.0", 1); + tzset(); + time_secs = 1518754387; + printf("%s", ctime(&time_secs)); + printf("===> PDT: 1526469187 Wednesday, May 16, 2018 4:13:07 AM GMT-07:00\n"); + time_secs = 1526469187; + printf("%s", ctime(&time_secs)); + + printf("\n"); + printf("===> back-to-back printf of sdk_system_get_time() then look at gettimeofday()\n"); + printf("System clock: %0.6Lf seconds\n", ((long double)sdk_system_get_time())*1e-6); + printf("System clock: %0.6Lf seconds\n", ((long double)sdk_system_get_time())*1e-6); + gettimeofday(&tv, NULL); + printf("now: %0.6Lf seconds\n", TV2LD(tv)); + + setenv("TZ", "UTC0UTC0,0,0", 1); + tzset(); + printf("UTC: %s", ctime(&tv.tv_sec)); /* ctime() includes \n */ + + setenv("TZ", "PST8PDT7,M3.1.0,M11.1.0", 1); + tzset(); + printf("PxT: %s", ctime(&tv.tv_sec)); + + printf("\n"); + printf("===> settimeofday to 1518768000, Friday, February 16, 2018 12:00:00 AM GMT-08:00\n"); + tv.tv_sec = 1518768000; + tv.tv_usec = 0; + settimeofday(&tv, NULL); + tv.tv_sec = 0; + tv.tv_usec = 0; + gettimeofday(&tv, NULL); + printf("PST: %s", ctime(&tv.tv_sec)); + + + num_runs = 1e3; + + printf("\n===== Calculation speed tests, %ld runs each, results in us =====\n", num_runs); + + system_plus_offset = 1518768000; + slew_start_time = system_plus_offset; + + t0 = sdk_system_get_time(); + for (idx = 0; idx < num_runs; idx++) { + internal_clock = + (int64_t)(system_plus_offset + + (system_plus_offset + - slew_start_time) + / ADJTIME_SLEW_PERIOD); + seemingly_useful = seemingly_useful + internal_clock; + system_plus_offset++; /* It's hard to keep this loop from being optimized out! */ + } + t1 = sdk_system_get_time(); + if (seemingly_useful != 0) + printf("internal clock with / ADJTIME_SLEW_PERIOD: %ld for %ld runs\n", (long)t1 - (long)t0, idx); + + t0 = sdk_system_get_time(); + for (idx = 0; idx < num_runs; idx++) { + internal_clock = + (int64_t)(system_plus_offset + + (double)(system_plus_offset + - slew_start_time) + * ADJTIME_SLEW_RATE); + seemingly_useful = seemingly_useful + internal_clock; + system_plus_offset++; + } + t1 = sdk_system_get_time(); + if (seemingly_useful != 0) + printf("internal clock with (double) * ADJTIME_SLEW_RATE: %ld for %ld runs\n", (long)t1 - (long)t0, idx); + + + t0 = sdk_system_get_time(); + for (idx = 0; idx < num_runs; idx++) { + internal_clock = + (int64_t)(system_plus_offset + + (int64_t)((double)(system_plus_offset + - slew_start_time) + * ADJTIME_SLEW_RATE)); + seemingly_useful = seemingly_useful + internal_clock; + system_plus_offset++; + } + t1 = sdk_system_get_time(); + if (seemingly_useful != 0) + printf("internal clock with (unint64_t) and (double) * ADJTIME_SLEW_RATE: %ld for %ld runs\n", (long)t1 - (long)t0, idx); + + printf("\n===== time-display run =====\n"); + + num_runs = 10; + + seconds_per_report = 1; + + increment_ticks = (seconds_per_report * 1000) / portTICK_PERIOD_MS; + + base_ticks = xTaskGetTickCount(); + + for (idx = 0; idx <= num_runs; idx++) { + gettimeofday(&tv, NULL); + printf("%20.6Lf at %ld ticks\n", TV2LD(tv), (long)(xTaskGetTickCount() - base_ticks)); + vTaskDelay((base_ticks - xTaskGetTickCount()) + (increment_ticks * (idx + 1))); + } + + printf("\n===== other tests =====\n"); + printf("===> confirm time() function works\n"); + printf("%li from time(NULL)\n", (long)time(NULL)); + printf("%li and %li from time(&time_secs)\n", (long)time(&time_secs), (long)time_secs); + + printf("\n===> Confirm that too-large slew is rejected\n"); + + tv.tv_sec = 10000; + tv.tv_usec = 0; + retval = adjtime(&tv, NULL); + if (retval == 0) { + printf("adjtime with +10000 s FAILED TO FAIL\n"); + } else { + printf("adjtime with +10000 s properly rejected the change, errno: %d\n", errno); + } + tv.tv_sec = -10000; + tv.tv_usec = 0; + retval = adjtime(&tv, NULL); + if (retval == 0) { + printf("adjtime with -10000 s FAILED TO FAIL\n"); + } else { + printf("adjtime with -10000 s properly rejected the change, errno: %d\n", errno); + } + + + printf("\n"); + printf("All done; reboot imminent\n"); + + gpio_write(BLUE_LED, LED_OFF); + + sdk_system_restart_in_nmi(); +} + + + +void +user_init(void) +{ + uart_set_baud(0, 115200); + sdk_wifi_set_opmode_current(NULL_MODE); /* Temporarily disable */ + xTaskCreate(testTask, "testTask", 512, NULL, 2, NULL); +} diff --git a/extras/timekeeping/tests/slew/Makefile b/extras/timekeeping/tests/slew/Makefile new file mode 100644 index 0000000..9c6aafe --- /dev/null +++ b/extras/timekeeping/tests/slew/Makefile @@ -0,0 +1,6 @@ + +PROGRAM=timekeeping_slew_tests + +EXTRA_COMPONENTS = extras/timekeeping + +include ../../../../common.mk diff --git a/extras/timekeeping/tests/slew/timekeeping_slew_tests.c b/extras/timekeeping/tests/slew/timekeeping_slew_tests.c new file mode 100644 index 0000000..5cb6ed8 --- /dev/null +++ b/extras/timekeeping/tests/slew/timekeeping_slew_tests.c @@ -0,0 +1,199 @@ +/* + * Tests of functionality within extras/timekeeping + */ + +/*- + * Copyright (c) 2018, Jeff Kletsky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the copyright holder 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 + * HOLDER 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. + */ + + +#include +#include + +#include +#include + +/* espressif/esp_system.h uses "bool" but fails to #include */ +#include +#include +#include +#include + +/* Not defined in espressif/esp_wifi.h */ +extern bool +sdk_wifi_set_opmode_current(uint8_t opmode); + +/* For sdk_system_restart_in_nmi() */ +#include + +#include +#include + + +#define BLUE_LED 2 +#define LED_ON 0 +#define LED_OFF 1 + +#define WAIT_FOR_UART_SECS 5 /* 2 might be OK */ + +#define TV2LD(TV) ((long double)TV.tv_sec + (long double)TV.tv_usec * 1.e-6) + +void +testTask(void *pvParameters) +{ + struct timeval tv; + long num_runs; + long idx; + TickType_t base_ticks; + TickType_t increment_ticks; + int seconds_per_report; + struct timeval delta; + struct timeval olddelta; + + + gpio_enable(BLUE_LED, GPIO_OUTPUT); + gpio_write(BLUE_LED, LED_OFF); + + vTaskDelay(WAIT_FOR_UART_SECS * 1000 / portTICK_PERIOD_MS); + + gpio_write(BLUE_LED, LED_ON); + + printf("\n"); + printf("settimeofday to 1000000000\n"); + tv.tv_sec = 1000000000; + tv.tv_usec = 0; + settimeofday(&tv, NULL); + + + printf("\n===== time-display runs =====\n"); + + num_runs = 10; + seconds_per_report = 1; + + increment_ticks = (seconds_per_report * 1000) / portTICK_PERIOD_MS; + + base_ticks = xTaskGetTickCount(); + + for (idx = 0; idx <= num_runs; idx++) { + gettimeofday(&tv, NULL); + printf("%20.6Lf at %ld ticks\n", TV2LD(tv), (long)(xTaskGetTickCount() - base_ticks)); + vTaskDelay((base_ticks - xTaskGetTickCount()) + (increment_ticks * (idx + 1))); + } + + + num_runs = 15; + seconds_per_report = 1; + increment_ticks = (seconds_per_report * 1000) / portTICK_PERIOD_MS; + + delta.tv_sec = 0; + delta.tv_usec = 5000; /* 5 ms -- should take 10 seconds */ + + printf("\n"); + printf("with +5 ms slew requested\n"); + adjtime(&delta, NULL); + base_ticks = xTaskGetTickCount(); + + for (idx = 0; idx <= num_runs; idx++) { + gettimeofday(&tv, NULL); + adjtime(NULL, &olddelta); + printf("%20.6Lf at %ld ticks; %0.6Lf olddelta\n", + TV2LD(tv), (long)(xTaskGetTickCount() - base_ticks), TV2LD(olddelta)); + vTaskDelay((base_ticks - xTaskGetTickCount()) + (increment_ticks * (idx + 1))); + } + + delta.tv_sec = 0; + delta.tv_usec = -5000; /* -5 ms -- should take 10 seconds */ + + printf("\n"); + printf("with -5 ms slew requested\n"); + adjtime(&delta, NULL); + base_ticks = xTaskGetTickCount(); + + for (idx = 0; idx <= num_runs; idx++) { + gettimeofday(&tv, NULL); + adjtime(NULL, &olddelta); + printf("%20.6Lf at %ld ticks; %0.6Lf olddelta\n", + TV2LD(tv), (long)(xTaskGetTickCount() - base_ticks), TV2LD(olddelta)); + vTaskDelay((base_ticks - xTaskGetTickCount()) + (increment_ticks * (idx + 1))); + } + + num_runs = 15; + seconds_per_report = 20; + increment_ticks = (seconds_per_report * 1000) / portTICK_PERIOD_MS; + + delta.tv_sec = 0; + delta.tv_usec = 130 * 1000; /* 130 ms -- 128 ms needed for NTP */ + + printf("\n"); + printf("with 130 ms slew requested (20-s reporting period)\n"); + adjtime(&delta, NULL); + base_ticks = xTaskGetTickCount(); + + for (idx = 0; idx <= num_runs; idx++) { + gettimeofday(&tv, NULL); + adjtime(NULL, &olddelta); + printf("%20.6Lf at %ld ticks; %0.6Lf olddelta\n", + TV2LD(tv), (long)(xTaskGetTickCount() - base_ticks), TV2LD(olddelta)); + vTaskDelay((base_ticks - xTaskGetTickCount()) + (increment_ticks * (idx + 1))); + } + + delta.tv_sec = 0; + delta.tv_usec = -130 * 1000; /* -130 ms -- 128 ms needed for NTP */ + + printf("\n"); + printf("with -130 ms slew requested\n"); + adjtime(&delta, NULL); + base_ticks = xTaskGetTickCount(); + + for (idx = 0; idx <= num_runs; idx++) { + gettimeofday(&tv, NULL); + adjtime(NULL, &olddelta); + printf("%20.6Lf at %ld ticks; %0.6Lf olddelta\n", + TV2LD(tv), (long)(xTaskGetTickCount() - base_ticks), TV2LD(olddelta)); + vTaskDelay((base_ticks - xTaskGetTickCount()) + (increment_ticks * (idx + 1))); + } + + printf("\n"); + printf("All done; reboot imminent\n"); + + gpio_write(BLUE_LED, LED_OFF); + + sdk_system_restart_in_nmi(); +} + + + +void +user_init(void) +{ + uart_set_baud(0, 115200); + sdk_wifi_set_opmode_current(NULL_MODE); /* Temporarily disable */ + xTaskCreate(testTask, "testTask", 512, NULL, 2, NULL); +} diff --git a/extras/timekeeping/tests/sntp-run/Makefile b/extras/timekeeping/tests/sntp-run/Makefile new file mode 100644 index 0000000..6098b35 --- /dev/null +++ b/extras/timekeeping/tests/sntp-run/Makefile @@ -0,0 +1,20 @@ + +PROGRAM=timekeeping_sntp_run + +# Test the use of extras/timekeeping with LWIP SNTP + +EXTRA_COMPONENTS = extras/timekeeping + +# Can work for broadcast or poll +# (assuming you have NTP broadcast already configured on the network) + +# Broadcast / poll and hosts for poll set in timekeeping_sntp_run.c +# SNTP parameters set at the top of lwipopts.h + + +# To set then only log time difference (rather than correct the clock) +# define TIMEKEEPING_SET_AND_MEASURE_ONLY + +# PROGRAM_CFLAGS = $(CFLAGS) -DTIMEKEEPING_SET_AND_MEASURE_ONLY + +include ../../../../common.mk diff --git a/extras/timekeeping/tests/sntp-run/lwipopts.h b/extras/timekeeping/tests/sntp-run/lwipopts.h new file mode 100644 index 0000000..24c718e --- /dev/null +++ b/extras/timekeeping/tests/sntp-run/lwipopts.h @@ -0,0 +1,996 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + + + + +/* + * Local settings and linkage for use of LWIP SNTP + * Additions to original: esp-open-rtos/lwip/include/lwipopts.h + */ + +/* #define SNTP_DEBUG LWIP_DBG_ON */ + +#define SNTP_SERVER_DNS 1 + +/* + * 0 -- off + * 1 -- enables address/port check + * 2 -- adds timestamp check against that sent + * Rest not yet implemented as of 2018-02-08; upstream commit 010b0210 + * 3 -- will add field-level checks + * 4 -- will add root delay/dispersion tests + */ +#define SNTP_CHECK_RESPONSE 2 + +#define SNTP_COMP_ROUNDTRIP 1 + +#define SNTP_STARTUP_DELAY 0 + +/* 60 seconds is lower threshold by NTPv4 spec */ +#define SNTP_UPDATE_DELAY (64 * 1000) + +/* + * Getter has to be a inline #define + * as it modifies arguments in place + * u32_t in lwip/apps/sntp.c + * lwip/include/arch/cc.h:typedef uint32_t u32_t; + */ +#include +#define SNTP_GET_SYSTEM_TIME(S, F) do { \ + struct timeval _tv; \ + gettimeofday(&_tv, NULL); \ + S = (uint32_t)_tv.tv_sec; \ + F = (uint32_t)_tv.tv_usec; \ +} while (0) + +#define SNTP_SET_SYSTEM_TIME_US(S, F) sntp_impl_set_system_time_us(S, F) + +#include "sntp_impl.h" + +/* + * END local settings and linkage for SNTP + */ + + + + +#define ESP_OPEN_RTOS 1 + +/* See tcp.c tcp_alloc(). */ +#ifndef ESP_TIMEWAIT_THRESHOLD +#define ESP_TIMEWAIT_THRESHOLD 10000 +#endif + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#define LWIP_TIMEVAL_PRIVATE 0 + +/* + ----------------------------------------------- + ---------- Platform specific locking ---------- + ----------------------------------------------- +*/ +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#define SYS_LIGHTWEIGHT_PROT 1 + +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#define MEMCPY(dst,src,len) memcpy(dst,src,len) + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) + +/* + ------------------------------------ + ----------- Core locking ----------- + ------------------------------------ +*/ + +/** + * LWIP_TCPIP_CORE_LOCKING + * Creates a global mutex that is held during TCPIP thread operations. + * Can be locked by client code to perform lwIP operations without changing + * into TCPIP thread using callbacks. See LOCK_TCPIP_CORE() and + * UNLOCK_TCPIP_CORE(). + * Your system should provide mutexes supporting priority inversion to use this. + */ +#ifndef LWIP_TCPIP_CORE_LOCKING +#define LWIP_TCPIP_CORE_LOCKING 1 +#endif + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: when LWIP_TCPIP_CORE_LOCKING is enabled, + * this lets tcpip_input() grab the mutex for input packets as well, + * instead of allocating a message and passing it to tcpip_thread. + * + * ATTENTION: this does not work when tcpip_input() is called from + * interrupt context! + */ +#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 +#endif + +/** + * Macro/function to check whether lwIP's threading/locking + * requirements are satisfied during current function call. + * This macro usually calls a function that is implemented in the OS-dependent + * sys layer and performs the following checks: + * - Not in ISR + * - If @ref LWIP_TCPIP_CORE_LOCKING = 1: TCPIP core lock is held + * - If @ref LWIP_TCPIP_CORE_LOCKING = 0: function is called from TCPIP thread + * @see @ref multithreading + */ +#ifndef LWIP_ASSERT_CORE_LOCKED +void sys_check_core_locking(void); +#define LWIP_ASSERT_CORE_LOCKED() sys_check_core_locking() +#endif + +/** + * Called as first thing in the lwIP TCPIP thread. Can be used in conjunction + * with @ref LWIP_ASSERT_CORE_LOCKED to check core locking. + * @see @ref multithreading + */ +#ifndef LWIP_MARK_TCPIP_THREAD +void sys_mark_tcpip_thread(void); +#define LWIP_MARK_TCPIP_THREAD() sys_mark_tcpip_thread() +#endif + +#if LWIP_TCPIP_CORE_LOCKING + +#ifndef LOCK_TCPIP_CORE +void sys_lock_tcpip_core(void); +#define LOCK_TCPIP_CORE() sys_lock_tcpip_core() +#endif + +#ifndef UNLOCK_TCPIP_CORE +void sys_unlock_tcpip_core(void); +#define UNLOCK_TCPIP_CORE() sys_unlock_tcpip_core() +#endif + +#else +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#define MEM_LIBC_MALLOC 1 + +/** + * MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator. + * Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution + * speed (heap alloc can be much slower than pool alloc) and usage from interrupts + * (especially if your netif driver allocates PBUF_POOL pbufs for received frames + * from interrupt)! + * ATTENTION: Currently, this uses the heap for ALL pools (also for private pools, + * not only for internal pools defined in memp_std.h)! + */ +#define MEMP_MEM_MALLOC 1 + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> \#define MEM_ALIGNMENT 4 + * 2 byte alignment -> \#define MEM_ALIGNMENT 2 + */ +#define MEM_ALIGNMENT 4 + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#ifndef MEMP_OVERFLOW_CHECK +#define MEMP_OVERFLOW_CHECK 0 +#endif + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + * This also sets the number of lwip socket descriptors. + */ +#ifndef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 12 +#endif + +/* + -------------------------------- + ---------- ARP options ------- + -------------------------------- +*/ + +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#ifndef LWIP_ARP +#define LWIP_ARP 1 +#endif + +/** + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. + */ +#ifndef ARP_QUEUEING +#define ARP_QUEUEING 1 +#endif + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#ifndef IP_REASSEMBLY +#define IP_REASSEMBLY 1 +#endif + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#ifndef IP_FRAG +#define IP_FRAG 1 +#endif + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#ifndef IP_REASS_MAXAGE +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#ifndef IP_REASS_MAX_PBUFS +#define IP_REASS_MAX_PBUFS 2 +#endif + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#ifndef LWIP_DHCP +#define LWIP_DHCP 1 +#endif + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#ifndef DHCP_DOES_ARP_CHECK +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +#define LWIP_DHCP_BOOTP_FILE 0 + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/* + ---------------------------------- + ----- SNMP MIB2 support ----- + ---------------------------------- +*/ +/* + ---------------------------------- + ----- Multicast/IGMP options ----- + ---------------------------------- +*/ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#ifndef LWIP_IGMP +#define LWIP_IGMP 1 +#endif + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#ifndef LWIP_DNS +#define LWIP_DNS 1 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#ifndef DNS_TABLE_SIZE +#define DNS_TABLE_SIZE 1 +#endif + +/** DNS maximum host name length supported in the name table. */ +#ifndef DNS_MAX_NAME_LENGTH +#define DNS_MAX_NAME_LENGTH 128 +#endif + +/** Set this to 1 to enable querying ".local" names via mDNS + * using a One-Shot Multicast DNS Query */ +#ifndef LWIP_DNS_SUPPORT_MDNS_QUERIES +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 +#endif + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#ifndef TCP_MAXRTX +#define TCP_MAXRTX 6 +#endif + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#ifndef TCP_SYNMAXRTX +#define TCP_SYNMAXRTX 3 +#endif + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#ifndef TCP_QUEUE_OOSEQ +#define TCP_QUEUE_OOSEQ 1 +#endif + +/** + * LWIP_TCP_SACK_OUT==1: TCP will support sending selective acknowledgements (SACKs). + */ +#ifndef LWIP_TCP_SACK_OUT +#define LWIP_TCP_SACK_OUT 1 +#endif + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#ifndef TCP_MSS +#define TCP_MSS 1460 +#endif + +/** + * TCP_OOSEQ_MAX_BYTES: The default maximum number of bytes queued on ooseq per + * pcb if TCP_OOSEQ_BYTES_LIMIT is not defined. Default is 0 (no limit). + * Only valid for TCP_QUEUE_OOSEQ==1. + */ +#ifndef TCP_OOSEQ_MAX_BYTES +#define TCP_OOSEQ_MAX_BYTES (2 * TCP_MSS) +#endif + +/** + * TCP_OOSEQ_BYTES_LIMIT(ooseq): Return the maximum number of bytes to be queued + * on ooseq per pcb, given the pcb. Only valid for TCP_QUEUE_OOSEQ==1. + */ +#if !defined TCP_OOSEQ_BYTES_LIMIT +#define TCP_OOSEQ_BYTES_LIMIT(ooseq) ooseq_bytes_limit(ooseq) +#endif + +/** + * TCP_OOSEQ_MAX_PBUFS: The default maximum number of pbufs queued on ooseq per + * pcb if TCP_OOSEQ_BYTES_LIMIT is not defined. Default is 0 (no limit). + * Only valid for TCP_QUEUE_OOSEQ==1. + */ +#ifndef TCP_OOSEQ_MAX_PBUFS +#define TCP_OOSEQ_MAX_PBUFS 2 +#endif + +/** + * TCP_OOSEQ_PBUFS_LIMIT(ooseq): Return the maximum number of pbufs to be queued + * on ooseq per pcb, given the pcb. Only valid for TCP_QUEUE_OOSEQ==1. + */ +#ifndef TCP_OOSEQ_PBUFS_LIMIT +#define TCP_OOSEQ_PBUFS_LIMIT(ooseq) ooseq_pbufs_limit(ooseq) +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#ifndef TCP_LISTEN_BACKLOG +#define TCP_LISTEN_BACKLOG 1 +#endif + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#ifndef TCP_DEFAULT_LISTEN_BACKLOG +#define TCP_DEFAULT_LISTEN_BACKLOG 2 +#endif + +/** + * TCP_OVERSIZE: The maximum number of bytes that tcp_write may + * allocate ahead of time in an attempt to create shorter pbuf chains + * for transmission. The meaningful range is 0 to TCP_MSS. Some + * suggested values are: + * + * 0: Disable oversized allocation. Each tcp_write() allocates a new + pbuf (old behaviour). + * 1: Allocate size-aligned pbufs with minimal excess. Use this if your + * scatter-gather DMA requires aligned fragments. + * 128: Limit the pbuf/memory overhead to 20%. + * TCP_MSS: Try to create unfragmented TCP packets. + * TCP_MSS/4: Try to create 4 fragments or less per TCP packet. + */ +#ifndef TCP_OVERSIZE +#define TCP_OVERSIZE TCP_MSS +#endif + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + * The timestamp option is currently only used to help remote hosts, it is not + * really used locally. Therefore, it is only enabled when a TS option is + * received in the initial SYN packet from a remote host. + */ +#ifndef LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_TIMESTAMPS 1 +#endif + +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#ifndef TCP_WND_UPDATE_THRESHOLD +#define TCP_WND_UPDATE_THRESHOLD LWIP_MIN((TCP_WND / 4), (TCP_MSS * 4)) +#endif + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ + +/** + * PBUF_LINK_ENCAPSULATION_HLEN: the number of bytes that should be allocated + * for an additional encapsulation header before ethernet headers (e.g. 802.11) + * + * 1. LINK_HLEN 14Byte will be remove in WLAN layer + * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes. + * 3. encryption needs exra 4 bytes ahead of actual data payload, and require + * DAddr and SAddr to be 4-byte aligned. + * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice... + * 5. LCC add 6 bytes more, We don't consider WAPI yet... + * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be + * matter is ether_hdr is not 4B aligned. + * + * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned + * + * 1. lwip + * | empty 30B | eth_hdr (14B) | payload ...| + * total: 44B ahead payload + * 2. net80211 + * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...| + * total: 40B ahead sec_rsv and 44B ahead payload + * + */ +#define PBUF_LINK_ENCAPSULATION_HLEN 36 + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#ifndef LWIP_NETIF_HOSTNAME +#define LWIP_NETIF_HOSTNAME 1 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#ifndef LWIP_NETIF_API +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquisition) + */ +#ifndef LWIP_NETIF_STATUS_CALLBACK +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP *tries* to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + */ +#define LWIP_NETIF_TX_SINGLE_PBUF 1 + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ + +/* + ------------------------------------ + ---------- SLIPIF options ---------- + ------------------------------------ +*/ + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_STACKSIZE +#define TCPIP_THREAD_STACKSIZE 480 +#endif + +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES-5) + +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#define TCPIP_MBOX_SIZE 16 + +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_UDP_RECVMBOX_SIZE 6 + +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_TCP_RECVMBOX_SIZE 6 + +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#define DEFAULT_ACCEPTMBOX_SIZE 6 + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#define LWIP_POSIX_SOCKETS_IO_NAMES 0 + +/** + * LWIP_SOCKET_OFFSET==n: Increases the file descriptor number created by LwIP with n. + * This can be useful when there are multiple APIs which create file descriptors. + * When they all start with a different offset and you won't make them overlap you can + * re implement read/write/close/ioctl/fnctl to send the requested action to the right + * library (sharing select will need more work though). + */ +#ifndef LWIP_SOCKET_OFFSET +#define LWIP_SOCKET_OFFSET 3 +#endif + +/** + * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#ifndef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 1 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 1 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#ifndef LWIP_TCP_KEEPALIVE +#define LWIP_TCP_KEEPALIVE 1 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#ifndef LWIP_SO_RCVBUF +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR option. + */ +#ifndef SO_REUSE +#define SO_REUSE 1 +#endif + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ + +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#ifndef LWIP_STATS +#define LWIP_STATS 0 +#endif + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#ifndef LWIP_STATS_DISPLAY +#define LWIP_STATS_DISPLAY 0 +#endif + +/* + --------------------------------- + ---------- PPP options ---------- + --------------------------------- +*/ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ + +/* + --------------------------------------- + ---------- IPv6 options --------------- + --------------------------------------- +*/ +/** + * LWIP_IPV6==1: Enable IPv6 + */ +#ifndef LWIP_IPV6 +#define LWIP_IPV6 0 +#endif + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/* + --------------------------------------- + ---------- mDNS options --------------- + --------------------------------------- +*/ + +/** + * LWIP_MDNS_RESPONDER_QUEUE_ANNOUNCEMENTS==1: Unsolicited announcements are + * queued and run from a timer callback. + */ +#ifndef LWIP_MDNS_RESPONDER_QUEUE_ANNOUNCEMENTS +#define LWIP_MDNS_RESPONDER_QUEUE_ANNOUNCEMENTS 1 +#endif + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ + +// Uncomment this line, and set the individual debug options you want, for IP stack debug output +//#define LWIP_DEBUG + +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + */ +//#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_WARNING + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + */ +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#define ETHARP_DEBUG LWIP_DBG_OFF + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#define NETIF_DEBUG LWIP_DBG_OFF + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#define PBUF_DEBUG LWIP_DBG_OFF + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#define API_LIB_DEBUG LWIP_DBG_OFF + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#define API_MSG_DEBUG LWIP_DBG_OFF + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#define SOCKETS_DEBUG LWIP_DBG_OFF + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#define ICMP_DEBUG LWIP_DBG_OFF + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#define IGMP_DEBUG LWIP_DBG_OFF + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#define INET_DEBUG LWIP_DBG_OFF + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#define IP_DEBUG LWIP_DBG_OFF + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#define IP_REASS_DEBUG LWIP_DBG_OFF + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#define RAW_DEBUG LWIP_DBG_OFF + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#define MEM_DEBUG LWIP_DBG_OFF + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#define MEMP_DEBUG LWIP_DBG_OFF + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#define SYS_DEBUG LWIP_DBG_OFF + +/** + * TIMERS_DEBUG: Enable debugging in timers.c. + */ +#define TIMERS_DEBUG LWIP_DBG_OFF + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#define TCP_DEBUG LWIP_DBG_OFF + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#define TCP_INPUT_DEBUG LWIP_DBG_OFF + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#define TCP_FR_DEBUG LWIP_DBG_OFF + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#define TCP_RTO_DEBUG LWIP_DBG_OFF + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#define TCP_CWND_DEBUG LWIP_DBG_OFF + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#define TCP_WND_DEBUG LWIP_DBG_OFF + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#define TCP_RST_DEBUG LWIP_DBG_OFF + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#define TCP_QLEN_DEBUG LWIP_DBG_OFF + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#define UDP_DEBUG LWIP_DBG_OFF + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#define TCPIP_DEBUG LWIP_DBG_OFF + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#define SLIP_DEBUG LWIP_DBG_OFF + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#define DHCP_DEBUG LWIP_DBG_OFF + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#define AUTOIP_DEBUG LWIP_DBG_OFF + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#define DNS_DEBUG LWIP_DBG_OFF + +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#define IP6_DEBUG LWIP_DBG_OFF + +/** + * MDNS_DEBUG: Enable debugging for multicast DNS. + */ +#define MDNS_DEBUG LWIP_DBG_OFF + +/* + -------------------------------------------------- + ---------- Performance tracking options ---------- + -------------------------------------------------- +*/ + +#endif /* __LWIPOPTS_H__ */ diff --git a/extras/timekeeping/tests/sntp-run/sntp_impl.c b/extras/timekeeping/tests/sntp-run/sntp_impl.c new file mode 100644 index 0000000..afcc763 --- /dev/null +++ b/extras/timekeeping/tests/sntp-run/sntp_impl.c @@ -0,0 +1,113 @@ +/* + * Test implementation of callback for LWIP SNTP + * + * Production code would likely decide how to handle + * various magnitude changes, be robust to outliers, + * and be brisk about it all + */ + +/*- + * Copyright (c) 2018, Jeff Kletsky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the copyright holder 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 + * HOLDER 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. + */ + + +#include + +#include + +#include "sntp_impl.h" + +/********************************************************** + * IMPORTANT: THIS IS A TEST HARNESS, NOT PRODUCTION CODE * + ********************************************************** + * + * Spikes in delay of over 200 ms have been observed in testing. + * Such spikes would likely cause both a forward and a backward jump + * in time using this simplistic approach. Increasing the threshold to, + * for example, 250 ms would require 2000 * 250 ms = 500 sec ~ 10 min + * for the time to slew back after a "bad set" or series of "bad" packets. + * + * PRODUCTION CODE SHOULD USE AN APPLICATION-APPROPRIATE CLOCK DISCIPLINE + */ +#ifndef SNTP_IMPL_STEP_THRESHOLD +#define SNTP_IMPL_STEP_THRESHOLD 125000 +#endif + + +#define TV2LD(TV) ((long double)TV.tv_sec + (long double)TV.tv_usec * 1.e-6) +#include + + +/* + * Called by lwip/apps/sntp.c through + * #define SNTP_SET_SYSTEM_TIME_US(S, F) sntp_impl_set_system_time_us(S, F) + * u32_t matches lwip/apps/sntp.c + */ +void +sntp_impl_set_system_time_us(uint32_t secs, uint32_t us) { + + struct timeval new; + struct timeval old; + struct timeval dt; + +#ifdef TIMEKEEPING_SET_AND_MEASURE_ONLY + static long double time_has_been_set_at; +#endif + + gettimeofday(&old, NULL); + + new.tv_sec = secs; + new.tv_usec = us; + + timersub(&new, &old, &dt); + +#ifdef TIMEKEEPING_SET_AND_MEASURE_ONLY + + if (time_has_been_set_at == 0) { + settimeofday(&new, NULL); + time_has_been_set_at = TV2LD(new); + } + + printf("SNTP: %20.6Lf delta: %10.3Lf ms %3.1Lf ppm\n", + TV2LD(new), TV2LD(dt)*1e3, (TV2LD(dt) / (TV2LD(new) - time_has_been_set_at))*1e6); + +#else /* Normal operation */ + + if (secs || abs(us) > SNTP_IMPL_STEP_THRESHOLD) { + settimeofday(&new, NULL); + } else { + adjtime(&dt, NULL); + } + + printf("SNTP: %20.6Lf delta: %7.3Lf ms\n", + TV2LD(new), TV2LD(dt)*1e3); + +#endif +} diff --git a/extras/timekeeping/tests/sntp-run/sntp_impl.h b/extras/timekeeping/tests/sntp-run/sntp_impl.h new file mode 100644 index 0000000..4a7628b --- /dev/null +++ b/extras/timekeeping/tests/sntp-run/sntp_impl.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2018, Jeff Kletsky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the copyright holder 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 + * HOLDER 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 EXTRAS_TIMEKEEPING_SNTP_IMPL_H +#define EXTRAS_TIMEKEEPING_SNTP_IMPL_H + +#include + +void +sntp_impl_set_system_time_us(uint32_t secs, uint32_t frac); + +/* See lwipopts.h for #define statements to couple with LWIP SNTP */ + +#endif /* EXTRAS_TIMEKEEPING_SNTP_IMPL_H */ diff --git a/extras/timekeeping/tests/sntp-run/timekeeping_sntp_run.c b/extras/timekeeping/tests/sntp-run/timekeeping_sntp_run.c new file mode 100644 index 0000000..a8195d0 --- /dev/null +++ b/extras/timekeeping/tests/sntp-run/timekeeping_sntp_run.c @@ -0,0 +1,136 @@ +/* + * Simple test using LWIP SNTP + */ + +/*- + * Copyright (c) 2018, Jeff Kletsky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the copyright holder 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 + * HOLDER 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. + */ + + +#include +#include + +#include +#include + +/* espressif/esp_system.h uses "bool" but fails to #include */ +#include +#include +#include + +/* For sdk_system_restart_in_nmi() */ +#include + +#include +#include + +#include + + +#define BLUE_LED 2 +#define LED_ON 0 +#define LED_OFF 1 + +#define WAIT_FOR_UART_SECS 5 /* 2 might be OK */ + +#define TV2LD(TV) ((long double)TV.tv_sec + (long double)TV.tv_usec * 1.e-6) + +void +sntp_task(void *pvParameters) { + + while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP) { + vTaskDelay(10); + }; + + /* + * Set one of SNTP_OPMODE_LISTENONLY or SNTP_OPMODE_POLL + */ + + + /* + * SNTP_OPMODE_LISTENONLY + * just needs the mode set, no server names required + * (requires broadcast NTP on your network) + */ + + sntp_setoperatingmode(SNTP_OPMODE_LISTENONLY); + + /* + * SNTP_OPMODE_POLL + * Needs one or more server names set + * additional servers are "fail over" + * Can use a DNS name or an address literal + * LWIP can also be configured with SNTP_GET_SERVERS_FROM_DHCP + * (DHCP-specified SNTP servers untested at this time) + * + * NOTE: Early testing with polling shows higher deviations + * than seen with broadcast, even with RTT compensation + * Cause unknown at this time, but believed to be within SNTP + * amd not part of timekeeping itself. + */ + +/* + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, "ntp_a.example.com"); + sntp_setservername(1, "ntp_b.example.com"); + sntp_setservername(2, "ntp_c.example.com"); +*/ + + /* Once set up, this is all it takes */ + sntp_init(); + + /* + * Have high-priority thread "parked", might as well use it + * Show calling gettimeofday() once an hour to check for timer wrap + * (the SNTP process itself, if connected, should be sufficient) + */ + while (1) { + vTaskDelay(60 * 60 * (1000 / portTICK_PERIOD_MS)); + printf("gettimeofday(NULL, NULL)\n"); + gettimeofday(NULL, NULL); + } +} + + + +void +user_init(void) +{ + uart_set_baud(0, 115200); + sdk_wifi_set_opmode(STATION_MODE); + /* + * Run at a high enough priority so that the initial time-set doesn't get interrupted + * Later calls and listen-only mode calls run in the high-priority "tcpip_thread" (LWIP) + * + * While 196 heap seemed sufficient for many tests, use of an NTP pool + * with DNS caused heap errors/warnings on starting NTP at 256 heap + */ + xTaskCreate(sntp_task, "SNTP task", 288, NULL, 6, NULL); +} diff --git a/extras/timekeeping/tests/wrap/Makefile b/extras/timekeeping/tests/wrap/Makefile new file mode 100644 index 0000000..0f0a4f9 --- /dev/null +++ b/extras/timekeeping/tests/wrap/Makefile @@ -0,0 +1,6 @@ + +PROGRAM=timekeeping_wrap_test + +EXTRA_COMPONENTS = extras/timekeeping + +include ../../../../common.mk diff --git a/extras/timekeeping/tests/wrap/timekeeping_wrap_test.c b/extras/timekeeping/tests/wrap/timekeeping_wrap_test.c new file mode 100644 index 0000000..70df3e3 --- /dev/null +++ b/extras/timekeeping/tests/wrap/timekeeping_wrap_test.c @@ -0,0 +1,129 @@ +/* + * Tests of functionality within extras/timekeeping + */ + +/*- + * Copyright (c) 2018, Jeff Kletsky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the copyright holder 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 + * HOLDER 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. + */ + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +/* espressif/esp_system.h uses "bool" but fails to #include */ +#include +#include + + +#define BLUE_LED 2 +#define LED_ON 0 +#define LED_OFF 1 + +#define WAIT_FOR_UART_SECS 5 /* 2 might be OK */ + +/* For sdk_system_restart_in_nmi() */ +#include + +/* Not defined in espressif/esp_wifi.h */ +extern bool +sdk_wifi_set_opmode_current(uint8_t opmode); + +#define TV2LD(TV) ((long double)TV.tv_sec + (long double)TV.tv_usec * 1.e-6) + + +void +testTask(void *pvParameters) +{ + struct timeval tv; + TickType_t base_ticks; + TickType_t increment_ticks; + int idx; + long num_runs; + int seconds_per_report; + + gpio_enable(BLUE_LED, GPIO_OUTPUT); + gpio_write(BLUE_LED, LED_OFF); + + vTaskDelay(WAIT_FOR_UART_SECS * 1000 / portTICK_PERIOD_MS); + + gpio_write(BLUE_LED, LED_ON); + + + printf("\n"); + printf("settimeofday to 1000000000\n"); + tv.tv_sec = 1000000000; + tv.tv_usec = 0; + settimeofday(&tv, NULL); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + printf("\n===== 4500-second (75 min) time-display run =====\n"); + printf(" 2^32 = 4294967296; ~4295 seconds\n"); + + num_runs = 450; + seconds_per_report = 10; + + increment_ticks = (seconds_per_report * 1000) / portTICK_PERIOD_MS; + + base_ticks = xTaskGetTickCount(); + + for (idx = 0; idx <= num_runs; idx++) { + gettimeofday(&tv, NULL); + printf("%20.6Lf at %8.3lf sec elapsed; %10lu system clock\n", + TV2LD(tv), (double)(xTaskGetTickCount() - base_ticks)/100, (long unsigned int)sdk_system_get_time()); + vTaskDelay((base_ticks - xTaskGetTickCount()) + (increment_ticks * (idx + 1))); + } + + printf("\n"); + printf("All done; reboot imminent\n"); + + gpio_write(BLUE_LED, LED_OFF); + + sdk_system_restart_in_nmi(); +} + + + +void +user_init(void) +{ + uart_set_baud(0, 115200); + sdk_wifi_set_opmode_current(NULL_MODE); /* Temporarily disable */ + xTaskCreate(testTask, "testTask", 512, NULL, 2, NULL); +} diff --git a/extras/timekeeping/timekeeping.c b/extras/timekeeping/timekeeping.c new file mode 100644 index 0000000..1fb65fd --- /dev/null +++ b/extras/timekeeping/timekeeping.c @@ -0,0 +1,323 @@ +/* + * Basic timekeeping functions + * + * Independent of clock discipline + */ + +/*- + * Copyright (c) 2018, Jeff Kletsky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the copyright holder 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 + * HOLDER 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. + */ + + +#include +#include +#include + +/* espressif/esp_system.h uses "bool" but fails to #include */ +#include +#include + +#include + + +/* #define here for easier use of other system clocks */ +#define GET_SYSTEM_CLOCK_US() sdk_system_get_time() + +typedef uint32_t system_clock_t; +#define SYSTEM_CLOCK_WRAP_US 0x100000000LL + +/* + * Define the slew rate in terms of microseconds of time to slew 1 microsecond + * This allows the use of integer arithmetic + * 2000 us to slew 1 us is 500 us of slew per sec, consistent with NTP usage + */ +#define ADJTIME_SLEW_PERIOD 2000 + +/* + * Try to prevent gross errors in calls to adjtime(), + * such as calling with the desired time + * NTP typically won't try to slew more than 128 ms + * At 500 ppm, can slew 1.8 seconds/hour + * A little less than what an int32_t can represent should be more than enough + * (2**31)/1e6 < 2147 + */ +#define ADJTIME_MAX_SECS_ALLOWED 2000 + + +/* + * Assume that newlib was compiled to implement tz locking + * See newlib-xtensa/newlib/libc/time/local.h + * + * __tz_lock is implemented by newlib and calls + * __tz_lock() calls __lock_acquire() which calls + * xSemaphoreTake() -- NOT RECURSIVE + * Use of __tz_lock() in applications may result in deadlock + * + * As that local.h is not available within esp-open-rtos + * replicate the definitions and provide proto here + */ +#define TZ_LOCK __tz_lock() +#define TZ_UNLOCK __tz_unlock() + +extern void __tz_lock(void); +extern void __tz_unlock(void); + + +/* + * Multi-threading considerations + * + * While the calls related to timekeeping are generally "tight" + * there is the possiblity that the calling process will be swapped out. + * Selection of proper task priority should mitigate this + * + * taskENTER_CRITICAL() and taskEXIT_CRITICAL() might be useful + * for specialized requirements. Before considering their use + * determine if configMAX_SYSCALL_INTERRUPT_PRIORITY is enabled + * and is at a level that permits at least ticks. + * See also configMAX_API_CALL_INTERRUPT_PRIORITY + * + * TIMEKEEPING_LOCK_USE_CRITICAL doesn't make much sense + * if it prevents interrupts related to ticks! + * + * TIMEKEEPING_LOCK_USE_CRITICAL is UNTESTED + */ + +#ifdef TIMEKEEPING_LOCK_USE_CRITICAL +#define TIMEKEEPING_LOCK() do {TZ_LOCK; taskENTER_CRITICAL();} while (0) +#define TIMEKEEPING_UNLOCK() do {taskEXIT_CRITICAL(); TZ_UNLOCK;} while (0) +#else +#define TIMEKEEPING_LOCK() TZ_LOCK +#define TIMEKEEPING_UNLOCK() TZ_UNLOCK +#endif + +#define SIGNED_ADJTIME_SLEW_PERIOD (timekeeping_state.adjtime_delta < 0 \ + ? -ADJTIME_SLEW_PERIOD : ADJTIME_SLEW_PERIOD) + +#define UNUSED_PARAM(X) ((void)X) + +/* + * All units in timekeeping_state are microseconds + * Intentionally *signed* values as, for example, might want to reset to zero + * for a time other than when the system clock started ticking + */ +static struct +{ + int64_t clock_offset; + system_clock_t last_system_clock_value; + int32_t adjtime_delta; + int64_t slew_start_time; + int64_t slew_complete_time; +} timekeeping_state; + + +static inline void +_reset_adjtime_state() { + + /* This should only be called under TIMEKEEPING_LOCK */ + + timekeeping_state.adjtime_delta = 0; + timekeeping_state.slew_start_time = 0; + timekeeping_state.slew_complete_time = 0; +} + +static void +_check_system_clock(void) { + + /* This should only be called under TIMEKEEPING_LOCK */ + + system_clock_t current_system_clock; + + current_system_clock = GET_SYSTEM_CLOCK_US(); + + if (current_system_clock < timekeeping_state.last_system_clock_value) { + timekeeping_state.clock_offset = + timekeeping_state.clock_offset + SYSTEM_CLOCK_WRAP_US; + } + + timekeeping_state.last_system_clock_value = current_system_clock; + + if (timekeeping_state.slew_complete_time + && ((GET_SYSTEM_CLOCK_US() + timekeeping_state.clock_offset) + >= timekeeping_state.slew_complete_time)) { + timekeeping_state.clock_offset = + timekeeping_state.clock_offset + timekeeping_state.adjtime_delta; + _reset_adjtime_state(); + } +} + + +int +_settimeofday_r(struct _reent *r, const struct timeval *tv, const struct timezone *tz) { + + UNUSED_PARAM(tz); + + int retval = 0; + system_clock_t current_system_clock; + int64_t desired_internal_clock; + + /* Effectively a system call, be picky */ + if (tv && (tv->tv_usec < 0 || tv->tv_usec >= 1000000 || tv->tv_sec < 0)) { + retval = -1; + r->_errno = EINVAL; + } + + TIMEKEEPING_LOCK(); + + _check_system_clock(); + + if (tv && !retval) { + + current_system_clock = GET_SYSTEM_CLOCK_US(); + desired_internal_clock = ((int64_t) tv->tv_sec * 1000000) + tv->tv_usec; + + timekeeping_state.clock_offset = + desired_internal_clock - current_system_clock; + + _reset_adjtime_state(); + + } + + TIMEKEEPING_UNLOCK(); + + return (retval); +} + + +/* "Override" the newlib definition used for gettimeofday and variants */ +int +_gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { + + UNUSED_PARAM(r); + UNUSED_PARAM(tz); + + system_clock_t current_system_clock; + int64_t system_plus_offset; + int64_t internal_clock; + + current_system_clock = GET_SYSTEM_CLOCK_US(); + + TIMEKEEPING_LOCK(); + + _check_system_clock(); + + if (tv) { + + system_plus_offset = + current_system_clock + timekeeping_state.clock_offset; + if (!timekeeping_state.slew_complete_time) { + internal_clock = system_plus_offset; + } else { + internal_clock = + (int64_t)(system_plus_offset + + (system_plus_offset + - timekeeping_state.slew_start_time) + / SIGNED_ADJTIME_SLEW_PERIOD); + } + + tv->tv_sec = internal_clock / 1000000; + tv->tv_usec = internal_clock % 1000000; + + } + + TIMEKEEPING_UNLOCK(); + + return (0); +} + + +int +_adjtime_r(struct _reent *r, const struct timeval *delta, struct timeval *olddelta) { + + int retval = 0; + system_clock_t current_system_clock; + int64_t system_plus_offset; + int32_t slew; + + current_system_clock = GET_SYSTEM_CLOCK_US(); + + /* Effectively a system call, be picky */ + if (delta && (delta->tv_usec <= -1000000 || delta->tv_usec >= 1000000 + || delta->tv_sec > ADJTIME_MAX_SECS_ALLOWED + || delta->tv_sec < -ADJTIME_MAX_SECS_ALLOWED)) { + retval = -1; + r->_errno = EINVAL; + } + + TIMEKEEPING_LOCK(); + + _check_system_clock(); + + if (!retval) { + + system_plus_offset = + current_system_clock + timekeeping_state.clock_offset; + + if (olddelta) { + if (timekeeping_state.slew_complete_time) { + slew = (int32_t)((timekeeping_state.slew_complete_time + - system_plus_offset) + / SIGNED_ADJTIME_SLEW_PERIOD); + olddelta->tv_sec = slew / 1000000; + olddelta->tv_usec = slew % 1000000; + } else { + olddelta->tv_sec = 0; + olddelta->tv_usec = 0; + } + } + + if (delta) { + timekeeping_state.adjtime_delta = delta->tv_sec * 1000000 + delta->tv_usec; + timekeeping_state.slew_start_time = system_plus_offset; + timekeeping_state.slew_complete_time = + timekeeping_state.slew_start_time + + (int64_t) timekeeping_state.adjtime_delta + * SIGNED_ADJTIME_SLEW_PERIOD; + } + + } /* if (!retval) */ + + TIMEKEEPING_UNLOCK(); + + return (retval); +} + + + +int +settimeofday(const struct timeval *tv, const struct timezone *tz) { + + return _settimeofday_r(_REENT, tv, tz); +} + +int +adjtime(const struct timeval *delta, struct timeval *olddelta) { + + return _adjtime_r(_REENT, delta, olddelta); +}