diff --git a/build/arm-tools.mk b/build/arm-tools.mk index d372cc4c63..5cb84069e9 100644 --- a/build/arm-tools.mk +++ b/build/arm-tools.mk @@ -55,7 +55,7 @@ quote=" lt=\< dollar=$$ arm_gcc_version_str:=$(strip $(shell $(CC) -dumpversion)) -expected_version:=5.3.1 +expected_version:=9.2.1 #$(info result $(shell test $(quote)$(arm_gcc_version_str)$(quote) $(lt) $(quote)$(expected_version)$(quote);echo $$?)) ifeq ($(shell test $(quote)$(arm_gcc_version_str)$(quote) $(lt) $(quote)$(expected_version)$(quote); echo $$?),0) $(error "ARM gcc version $(expected_version) or later required, but found $(arm_gcc_version_str)") diff --git a/communication/inc/communication_dynalib.h b/communication/inc/communication_dynalib.h index 773a22dca8..dbe7cb7bb4 100644 --- a/communication/inc/communication_dynalib.h +++ b/communication/inc/communication_dynalib.h @@ -25,6 +25,7 @@ #include "dynalib.h" #include "protocol_selector.h" #include "hal_platform.h" +#include "time_compat.h" #ifdef __cplusplus extern "C" { @@ -71,7 +72,7 @@ DYNALIB_FN(BASE_IDX + 2, communication, extract_public_ec_key, int(uint8_t*, siz DYNALIB_FN(BASE_IDX2 + 0, communication, spark_protocol_set_connection_property, int(ProtocolFacade*, unsigned, unsigned, const particle::protocol::connection_properties_t*, void*)) DYNALIB_FN(BASE_IDX2 + 1, communication, spark_protocol_command, int(ProtocolFacade*, ProtocolCommands::Enum, uint32_t, const void*)) DYNALIB_FN(BASE_IDX2 + 2, communication, spark_protocol_time_request_pending, bool(ProtocolFacade*, void*)) -DYNALIB_FN(BASE_IDX2 + 3, communication, spark_protocol_time_last_synced, system_tick_t(ProtocolFacade*, time_t*, void*)) +DYNALIB_FN(BASE_IDX2 + 3, communication, spark_protocol_time_last_synced, system_tick_t(ProtocolFacade*, time32_t*, time_t*)) DYNALIB_FN(BASE_IDX2 + 4, communication, spark_protocol_get_describe_data, int(ProtocolFacade*, spark_protocol_describe_data*, void*)) DYNALIB_FN(BASE_IDX2 + 5, communication, spark_protocol_post_description, int(ProtocolFacade*, int, void*)) DYNALIB_FN(BASE_IDX2 + 6, communication, spark_protocol_to_system_error, int(int)) diff --git a/communication/inc/spark_protocol_functions.h b/communication/inc/spark_protocol_functions.h index 5acea7689f..b67d2c234c 100644 --- a/communication/inc/spark_protocol_functions.h +++ b/communication/inc/spark_protocol_functions.h @@ -34,6 +34,7 @@ #include "system_defs.h" #include "completion_handler.h" #include "hal_platform.h" +#include "time_compat.h" #ifdef __cplusplus extern "C" { @@ -96,7 +97,7 @@ struct SparkCallbacks /** * Sets the time. Time is given in milliseconds since the epoch, UCT. */ - void (*set_time)(time_t t, unsigned int param, void* reserved); + void (*set_time)(uint32_t t, unsigned int param, void* reserved); // size == 40 @@ -191,7 +192,7 @@ void spark_protocol_get_product_details(ProtocolFacade* protocol, product_detail int spark_protocol_set_connection_property(ProtocolFacade* protocol, unsigned property_id, unsigned data, const particle::protocol::connection_properties_t* conn_prop, void* reserved); bool spark_protocol_time_request_pending(ProtocolFacade* protocol, void* reserved=NULL); -system_tick_t spark_protocol_time_last_synced(ProtocolFacade* protocol, time_t* tm, void* reserved=NULL); +system_tick_t spark_protocol_time_last_synced(ProtocolFacade* protocol, time32_t* tm32, time_t* tm); int spark_protocol_to_system_error (int error); diff --git a/communication/src/spark_protocol_functions.cpp b/communication/src/spark_protocol_functions.cpp index 12a009d0d9..7459586d8b 100644 --- a/communication/src/spark_protocol_functions.cpp +++ b/communication/src/spark_protocol_functions.cpp @@ -228,10 +228,17 @@ bool spark_protocol_time_request_pending(ProtocolFacade* protocol, void* reserve (void)reserved; return protocol->time_request_pending(); } -system_tick_t spark_protocol_time_last_synced(ProtocolFacade* protocol, time_t* tm, void* reserved) +system_tick_t spark_protocol_time_last_synced(ProtocolFacade* protocol, time32_t* tm32, time_t* tm) { - (void)reserved; - return protocol->time_last_synced(tm); + time_t t; + auto ms = protocol->time_last_synced(&t); + if (tm32) { + *tm32 = (time32_t)t; + } + if (tm) { + *tm = t; + } + return ms; } int spark_protocol_get_describe_data(ProtocolFacade* protocol, spark_protocol_describe_data* data, void* reserved) diff --git a/communication/src/timesyncmanager.h b/communication/src/timesyncmanager.h index 6202b063d1..63d6be3888 100644 --- a/communication/src/timesyncmanager.h +++ b/communication/src/timesyncmanager.h @@ -35,7 +35,7 @@ class TimeSyncManager } template - bool handle_time_response(time_t tm, system_tick_t mil, Callback set_time) { + bool handle_time_response(uint32_t tm, system_tick_t mil, Callback set_time) { LOG(INFO, "Received TIME response: %lu", (unsigned long)tm); set_time(tm, 0, NULL); expectingResponse_ = false; diff --git a/communication/tests/ConstructorFixture.cpp b/communication/tests/ConstructorFixture.cpp index 2571ac2d1a..db88827920 100644 --- a/communication/tests/ConstructorFixture.cpp +++ b/communication/tests/ConstructorFixture.cpp @@ -137,7 +137,7 @@ bool ConstructorFixture::nothing_to_receive = false; bool ConstructorFixture::function_called = false; int ConstructorFixture::variable_to_get = -98765; bool ConstructorFixture::signal_called_with = false; -time_t ConstructorFixture::set_time_called_with = -1; +int64_t ConstructorFixture::set_time_called_with = -1; EventHandlerCalledWith ConstructorFixture::event_handlers_called_with[2]; ConstructorFixture::ConstructorFixture() @@ -361,7 +361,7 @@ SparkReturnType::Enum ConstructorFixture::mock_variable_type(const char *variabl return SparkReturnType::INT; } -void ConstructorFixture::mock_set_time(time_t t, unsigned int, void*) +void ConstructorFixture::mock_set_time(uint32_t t, unsigned int, void*) { set_time_called_with = t; } diff --git a/communication/tests/ConstructorFixture.h b/communication/tests/ConstructorFixture.h index 68b7be07f2..9f8aeee768 100644 --- a/communication/tests/ConstructorFixture.h +++ b/communication/tests/ConstructorFixture.h @@ -73,8 +73,8 @@ struct ConstructorFixture static system_tick_t mock_millis(void); static bool mock_ota_status_check(void); static SparkReturnType::Enum mock_variable_type(const char *variable_key); - static void mock_set_time(time_t t, unsigned int param, void* reserved); - static time_t set_time_called_with; + static void mock_set_time(uint32_t t, unsigned int param, void* reserved); + static int64_t set_time_called_with; static EventHandlerCalledWith event_handlers_called_with[2]; static void mock_event_handler_0(const char *event_name, const char *data); static void mock_event_handler_1(const char *event_name, const char *data); diff --git a/communication/tests/TestSparkProtocol.cpp b/communication/tests/TestSparkProtocol.cpp index b02137c689..5358a645ba 100644 --- a/communication/tests/TestSparkProtocol.cpp +++ b/communication/tests/TestSparkProtocol.cpp @@ -745,7 +745,7 @@ SUITE(CoreProtocolConstruction) core_protocol.handshake(); bytes_received[0] = bytes_sent[0] = 0; core_protocol.event_loop(); - CHECK_EQUAL(1398367917, set_time_called_with); + CHECK_EQUAL(1398367917LL, set_time_called_with); } TEST(IsInitializedIsFalse) diff --git a/hal/inc/exrtc_hal.h b/hal/inc/exrtc_hal.h index f976b4bb38..11ad93b9b8 100644 --- a/hal/inc/exrtc_hal.h +++ b/hal/inc/exrtc_hal.h @@ -22,6 +22,8 @@ #if HAL_PLATFORM_EXTERNAL_RTC +#include "rtc_hal.h" +#include "system_tick_hal.h" #include #include @@ -29,19 +31,21 @@ extern "C" { #endif -typedef void (*hal_exrtc_alarm_handler_t)(void* context); +typedef hal_rtc_alarm_handler hal_exrtc_alarm_handler; +typedef hal_rtc_alarm_flags hal_exrtc_alarm_flags; -int hal_exrtc_set_unixtime(time_t unixtime, void* reserved); -time_t hal_exrtc_get_unixtime(void* reserved); -int hal_exrtc_set_unix_alarm(time_t unixtime, hal_exrtc_alarm_handler_t handler, void* context, void* reserved); -int hal_exrtc_cancel_unixalarm(void* reserved); +int hal_exrtc_init(void* reserved); +int hal_exrtc_set_time(const struct timeval* tv, void* reserved); +int hal_exrtc_get_time(struct timeval* tv, void* reserved); +int hal_exrtc_set_alarm(const struct timeval* tv, uint32_t flags, hal_exrtc_alarm_handler handler, void* context, void* reserved); +int hal_exrtc_cancel_alarm(void* reserved); bool hal_exrtc_time_is_valid(void* reserved); -int hal_exrtc_enable_watchdog(time_t ms, void* reserved); +int hal_exrtc_enable_watchdog(system_tick_t ms, void* reserved); int hal_exrtc_disable_watchdog(void* reserved); int hal_exrtc_feed_watchdog(void* reserved); -int hal_exrtc_sleep_timer(time_t ms, void* reserved); +int hal_exrtc_sleep_timer(system_tick_t ms, void* reserved); int hal_exrtc_calibrate_xt(int adjValue, void* reserved); diff --git a/hal/inc/hal_dynalib.h b/hal/inc/hal_dynalib.h index 105c1223e0..e601c05a71 100644 --- a/hal/inc/hal_dynalib.h +++ b/hal/inc/hal_dynalib.h @@ -24,6 +24,8 @@ #include "dynalib.h" +#include "time_compat.h" + #ifdef DYNALIB_EXPORT #include "rng_hal.h" #include "eeprom_hal.h" @@ -60,10 +62,13 @@ DYNALIB_FN(BASE_IDX + 1, hal, HAL_Delay_Microseconds, void(uint32_t)) DYNALIB_FN(BASE_IDX + 2, hal, HAL_Timer_Get_Micro_Seconds, system_tick_t(void)) DYNALIB_FN(BASE_IDX + 3, hal, HAL_Timer_Get_Milli_Seconds, system_tick_t(void)) -DYNALIB_FN(BASE_IDX + 4, hal, HAL_RTC_Configuration, void(void)) -DYNALIB_FN(BASE_IDX + 5, hal, HAL_RTC_Get_UnixTime, time_t(void)) -DYNALIB_FN(BASE_IDX + 6, hal, HAL_RTC_Set_UnixTime, void(time_t)) -DYNALIB_FN(BASE_IDX + 7, hal, HAL_RTC_Set_UnixAlarm, void(time_t)) +DYNALIB_FN(BASE_IDX + 4, hal, hal_rtc_init, void(void)) + +// These functions are deprecated +DYNALIB_FN(BASE_IDX + 5, hal, hal_rtc_get_unixtime_deprecated, time32_t(void)) +DYNALIB_FN(BASE_IDX + 6, hal, hal_rtc_set_unixtime_deprecated, void(time32_t)) + +DYNALIB_FN(BASE_IDX + 7, hal, hal_rtc_set_alarm, int(const struct timeval*, uint32_t, hal_rtc_alarm_handler, void*, void*)) DYNALIB_FN(BASE_IDX + 8, hal, HAL_EEPROM_Init, void(void)) DYNALIB_FN(BASE_IDX + 9, hal, HAL_EEPROM_Read, uint8_t(uint32_t)) @@ -72,18 +77,22 @@ DYNALIB_FN(BASE_IDX + 11, hal, HAL_EEPROM_Length, size_t(void)) DYNALIB_FN(BASE_IDX + 12, hal, HAL_disable_irq, int(void)) DYNALIB_FN(BASE_IDX + 13, hal, HAL_enable_irq, void(int)) -DYNALIB_FN(BASE_IDX + 14, hal, HAL_RTC_Cancel_UnixAlarm, void(void)) +DYNALIB_FN(BASE_IDX + 14, hal, hal_rtc_cancel_alarm, void(void)) -DYNALIB_FN(BASE_IDX + 15, hal,HAL_EEPROM_Get, void(uint32_t, void *, size_t)) -DYNALIB_FN(BASE_IDX + 16, hal,HAL_EEPROM_Put, void(uint32_t, const void *, size_t)) -DYNALIB_FN(BASE_IDX + 17, hal,HAL_EEPROM_Clear, void(void)) -DYNALIB_FN(BASE_IDX + 18, hal,HAL_EEPROM_Has_Pending_Erase, bool(void)) -DYNALIB_FN(BASE_IDX + 19, hal,HAL_EEPROM_Perform_Pending_Erase, void(void)) -DYNALIB_FN(BASE_IDX + 20, hal, HAL_RTC_Time_Is_Valid, uint8_t(void*)) +DYNALIB_FN(BASE_IDX + 15, hal, HAL_EEPROM_Get, void(uint32_t, void *, size_t)) +DYNALIB_FN(BASE_IDX + 16, hal, HAL_EEPROM_Put, void(uint32_t, const void *, size_t)) +DYNALIB_FN(BASE_IDX + 17, hal, HAL_EEPROM_Clear, void(void)) +DYNALIB_FN(BASE_IDX + 18, hal, HAL_EEPROM_Has_Pending_Erase, bool(void)) +DYNALIB_FN(BASE_IDX + 19, hal, HAL_EEPROM_Perform_Pending_Erase, void(void)) +DYNALIB_FN(BASE_IDX + 20, hal, hal_rtc_time_is_valid, bool(void*)) DYNALIB_FN(BASE_IDX + 21, hal, hal_timer_millis, uint64_t(void*)) DYNALIB_FN(BASE_IDX + 22, hal, hal_timer_micros, uint64_t(void*)) +DYNALIB_FN(BASE_IDX + 23, hal, hal_rtc_get_time, int(struct timeval*, void*)) +DYNALIB_FN(BASE_IDX + 24, hal, hal_rtc_set_time, int(const struct timeval*, void*)) + + DYNALIB_END(hal) #undef BASE_IDX diff --git a/hal/inc/hal_dynalib_socket_posix.h b/hal/inc/hal_dynalib_socket_posix.h index 886105f582..db55d70c23 100644 --- a/hal/inc/hal_dynalib_socket_posix.h +++ b/hal/inc/hal_dynalib_socket_posix.h @@ -49,10 +49,11 @@ DYNALIB_FN(13, hal_socket, sock_sendto, int(int, const void*, size_t, int, const DYNALIB_FN(14, hal_socket, sock_socket, int(int, int, int)) DYNALIB_FN(15, hal_socket, sock_fcntl, int(int, int, ...)) DYNALIB_FN(16, hal_socket, sock_poll, int(struct pollfd*, nfds_t, int)) -DYNALIB_FN(17, hal_socket, sock_select, int(int, fd_set*, fd_set*, fd_set*, struct timeval*)) +DYNALIB_FN(17, hal_socket, sock_select32, int(int, fd_set*, fd_set*, fd_set*, LIBC_TIMEVAL32*)) DYNALIB_FN(18, hal_socket, sock_recvmsg, int(int, struct msghdr*, int)) DYNALIB_FN(19, hal_socket, sock_sendmsg, int(int, const struct msghdr*, int)) DYNALIB_FN(20, hal_socket, sock_ioctl, int(int, long, void*)) +DYNALIB_FN(21, hal_socket, sock_select, int(int, fd_set*, fd_set*, fd_set*, struct timeval*)) DYNALIB_END(hal_socket) diff --git a/hal/inc/rtc_hal.h b/hal/inc/rtc_hal.h index b4bc7d69a7..24d1001e68 100644 --- a/hal/inc/rtc_hal.h +++ b/hal/inc/rtc_hal.h @@ -29,6 +29,8 @@ #include #include +#include +#include "time_compat.h" /* Exported types ------------------------------------------------------------*/ @@ -42,13 +44,25 @@ extern "C" { #endif -void HAL_RTC_Configuration(void); +typedef void (*hal_rtc_alarm_handler)(void* context); -time_t HAL_RTC_Get_UnixTime(void); -void HAL_RTC_Set_UnixTime(time_t value); -void HAL_RTC_Set_UnixAlarm(time_t value); -void HAL_RTC_Cancel_UnixAlarm(void); -uint8_t HAL_RTC_Time_Is_Valid(void* reserved); +typedef enum hal_rtc_alarm_flags { + HAL_RTC_ALARM_FLAG_IN = 0x01 // In provided amount of time, instead of an absolute timestamp +} hal_rtc_alarm_flags; + +void hal_rtc_init(void); +int hal_rtc_get_time(struct timeval* tv, void* reserved); +int hal_rtc_set_time(const struct timeval* tv, void* reserved); +bool hal_rtc_time_is_valid(void* reserved); +// XXX: only one alarm and its handler can be registered at a time +int hal_rtc_set_alarm(const struct timeval* tv, uint32_t flags, hal_rtc_alarm_handler handler, void* context, void* reserved); +void hal_rtc_cancel_alarm(void); + +// These functions are deprecated and are only used for backwards compatibility +// due to time_t size change +time32_t hal_rtc_get_unixtime_deprecated(void); +void hal_rtc_set_unixtime_deprecated(time32_t value); +// #ifdef __cplusplus } diff --git a/hal/network/lwip/socket_hal.cpp b/hal/network/lwip/socket_hal.cpp index cad8426451..9ad24a2f0a 100644 --- a/hal/network/lwip/socket_hal.cpp +++ b/hal/network/lwip/socket_hal.cpp @@ -46,10 +46,37 @@ int sock_getsockname(int s, struct sockaddr* name, socklen_t* namelen) { } int sock_getsockopt(int s, int level, int optname, void* optval, socklen_t* optlen) { +#ifdef LIBC_64_BIT_TIME_T + if (optname == SO_SNDTIMEO || optname == SO_RCVTIMEO) { + if (optlen && *optlen == sizeof(LIBC_TIMEVAL32) && optval) { + struct timeval tv = {}; + socklen_t sz = sizeof(tv); + int r = lwip_getsockopt(s, level, optname, &tv, &sz); + if (!r) { + LIBC_TIMEVAL32* tv32 = (LIBC_TIMEVAL32*)optval; + tv32->tv_sec = tv.tv_sec; + tv32->tv_usec = tv.tv_usec; + } + return r; + } + } +#endif // LIBC_64_BIT_TIME_T return lwip_getsockopt(s, level, optname, optval, optlen); } int sock_setsockopt(int s, int level, int optname, const void* optval, socklen_t optlen) { +#ifdef LIBC_64_BIT_TIME_T + if (optname == SO_SNDTIMEO || optname == SO_RCVTIMEO) { + if (optlen == sizeof(LIBC_TIMEVAL32) && optval) { + LIBC_TIMEVAL32* tv32 = (LIBC_TIMEVAL32*)optval; + struct timeval tv = { + .tv_sec = tv32->tv_sec, + .tv_usec = tv32->tv_usec + }; + return lwip_setsockopt(s, level, optname, &tv, sizeof(tv)); + } + } +#endif // LIBC_64_BIT_TIME_T return lwip_setsockopt(s, level, optname, optval, optlen); } @@ -104,6 +131,22 @@ int sock_select(int nfds, fd_set* readfds, fd_set* writefds, return lwip_select(nfds, readfds, writefds, exceptfds, timeout); } +#ifdef LIBC_64_BIT_TIME_T +int sock_select32(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, LIBC_TIMEVAL32* timeout) { + struct timeval tv = {}; + if (timeout) { + tv.tv_sec = timeout->tv_sec; + tv.tv_usec = timeout->tv_usec; + return sock_select(nfds, readfds, writefds, exceptfds, &tv); + } + return sock_select(nfds, readfds, writefds, exceptfds, nullptr); +} +#else +int sock_select32(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, LIBC_TIMEVAL32* timeout) __attribute__((alias("sock_select"))); +#endif // LIBC_64_BIT_TIME_T + ssize_t sock_recvmsg(int s, struct msghdr *message, int flags) { return lwip_recvmsg(s, message, flags); } diff --git a/hal/network/lwip/socket_hal_posix_impl.h b/hal/network/lwip/socket_hal_posix_impl.h index a997a24ce1..5af323e8f7 100644 --- a/hal/network/lwip/socket_hal_posix_impl.h +++ b/hal/network/lwip/socket_hal_posix_impl.h @@ -25,6 +25,7 @@ #define SOCKET_HAL_POSIX_IMPL_H #include +#include "time_compat.h" #ifdef __cplusplus extern "C" { @@ -56,6 +57,11 @@ struct sockaddr_ll { u8_t sll_addr[8]; }; +#ifdef LIBC_64_BIT_TIME_T +int sock_select32(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, LIBC_TIMEVAL32* timeout); +#endif // LIBC_64_BIT_TIME_T + /** * @} * diff --git a/hal/shared/time_compat.cpp b/hal/shared/time_compat.cpp new file mode 100644 index 0000000000..82f5f554b9 --- /dev/null +++ b/hal/shared/time_compat.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "time_compat.h" + +#ifndef HAL_TIME_COMPAT_EXCLUDE + +struct tm* localtime32_r(const time32_t* timep, struct tm* result) { + if (!timep) { + return nullptr; + } + time_t tmp = *timep; + return localtime_r(&tmp, result); +} + +time32_t mktime32(struct tm* tm) { + return (time32_t)mktime(tm); +} + +#endif // HAL_TIME_COMPAT_EXCLUDE diff --git a/hal/shared/time_compat.h b/hal/shared/time_compat.h new file mode 100644 index 0000000000..1b1eea60a5 --- /dev/null +++ b/hal/shared/time_compat.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2020 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#pragma once + +#include +#include +#include +#ifndef __cplusplus +#include +#endif // __cplusplus +#include + +#ifdef __NEWLIB__ +#include +#endif // __NEWLIB__ + +// Newlib-specific +#ifdef __NEWLIB__ +#if __NEWLIB__ >= 3 +#ifdef _USE_LONG_TIME_T +#if __LONG_MAX__ > 0x7fffffffL +#define LIBC_64_BIT_TIME_T +#endif // __LONG_MAX__ > 0x7fffffffL +#else +// Newlib has switched to 64-bit time_t by default +#define LIBC_64_BIT_TIME_T +#endif // _USE_LONG_TIME_T +#endif // __NEWLIB__ >= 3 + +#ifndef LIBC_64_BIT_TIME_T +#error "Unsupported newlib version with 32-bit time_t" +#endif // LIBC_64_BIT_TIME_T + +#endif // __NEWLIB__ + +#ifndef __NEWLIB__ +// On all the other platforms assume that 'long' is used +#if __LONG_MAX__ > 0x7fffffffL +#define LIBC_64_BIT_TIME_T +#endif // __LONG_MAX__ > 0x7fffffffL +#endif // __NEWLIB__ + +#ifdef LIBC_64_BIT_TIME_T +// time_t is 64-bit +typedef time_t time64_t; +typedef int32_t time32_t; + +struct timeval32 { + time32_t tv_sec; + int32_t tv_usec; +}; + +#define LIBC_TIMEVAL32 struct timeval32 +#define LIBC_TIMEVAL64 struct timeval + +#else +// time_t is 32-bit +typedef time_t time32_t; +typedef int64_t time64_t; + +struct timeval64 { + time64_t tv_sec; + int64_t tv_usec; +}; + +#define LIBC_TIMEVAL32 struct timeval +#define LIBC_TIMEVAL64 struct timeval64 + +#endif // LIBC_64_BIT_TIME_T + +#ifdef LIBC_64_BIT_TIME_T +static_assert(sizeof(LIBC_TIMEVAL64) == sizeof(struct timeval), "sizeof compat timeval64 does not match libc timeval"); +static_assert(offsetof(LIBC_TIMEVAL64, tv_usec) == offsetof(struct timeval, tv_usec), "offsetof tv_usec int timeval64 does not match libc timeval tv_usec"); +static_assert(sizeof(time64_t) == sizeof(time_t), "sizeof time64_t does not match time_t"); +static_assert(sizeof(struct timeval) == sizeof(time_t) * 2, "sizeof libc timeval does not match expected"); + +static_assert(sizeof(time32_t) == sizeof(int32_t), "sizeof time32_t does not match 32-bit time_t"); +static_assert(sizeof(LIBC_TIMEVAL32) == sizeof(time32_t) * 2, "sizeof compat timeval32 does not match libc timeval with 32-bit time_t"); +static_assert(offsetof(LIBC_TIMEVAL32, tv_usec) == sizeof(time32_t), "sizeof compat timeval32 does not match libc timeval with 32-bit time_t"); +#else +static_assert(sizeof(LIBC_TIMEVAL32) == sizeof(struct timeval), "sizeof compat timeval32 does not match libc timeval"); +static_assert(offsetof(LIBC_TIMEVAL32, tv_usec) == offsetof(struct timeval, tv_usec), "offsetof tv_usec in timeval32 does not match libc timeval tv_usec"); +static_assert(sizeof(time32_t) == sizeof(time_t), "sizeof time32_t does not match time_t"); +static_assert(sizeof(struct timeval) == sizeof(time_t) * 2, "sizeof libc timeval does not match expected"); + +static_assert(sizeof(time64_t) == sizeof(int64_t), "sizeof time64_t does not match 64-bit time_t"); +static_assert(sizeof(LIBC_TIMEVAL64) == sizeof(time64_t) * 2, "sizeof compat timeval32 does not match libc timeval with 32-bit time_t"); +static_assert(offsetof(LIBC_TIMEVAL64, tv_usec) == sizeof(time64_t), "sizeof compat timeval64 does not match libc timeval with 32-bit time_t"); +#endif // LIBC_64_BIT_TIME_T + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +struct tm* localtime32_r(const time32_t* timep, struct tm* result); +time32_t mktime32(struct tm* tm); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/hal/src/electron/rtc_hal.c b/hal/src/electron/rtc_hal.c deleted file mode 100644 index 3ab3676d9c..0000000000 --- a/hal/src/electron/rtc_hal.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - ****************************************************************************** - * Copyright (c) 2015 Particle Industries, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - ****************************************************************************** - */ - -/* Includes -----------------------------------------------------------------*/ - -/* Private typedef ----------------------------------------------------------*/ - -/* Private define -----------------------------------------------------------*/ - -/* Private macro ------------------------------------------------------------*/ - -/* Private variables --------------------------------------------------------*/ - -/* Extern variables ---------------------------------------------------------*/ - -/* Private function prototypes ----------------------------------------------*/ - \ No newline at end of file diff --git a/hal/src/gcc/core_hal.cpp b/hal/src/gcc/core_hal.cpp index a84a6a25c6..f9ddcb1690 100644 --- a/hal/src/gcc/core_hal.cpp +++ b/hal/src/gcc/core_hal.cpp @@ -43,6 +43,7 @@ #include "eeprom_file.h" #include "eeprom_hal.h" +#include "rtc_hal.h" using std::cout; @@ -342,6 +343,7 @@ void HAL_Notify_WDT() void HAL_Core_Init(void) { + hal_rtc_init(); } void HAL_Bootloader_Lock(bool lock) diff --git a/hal/src/gcc/rtc_hal.cpp b/hal/src/gcc/rtc_hal.cpp index f7724e60f8..ddcb14a808 100644 --- a/hal/src/gcc/rtc_hal.cpp +++ b/hal/src/gcc/rtc_hal.cpp @@ -1,45 +1,63 @@ +/* + * Copyright (c) 2020 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ #include "rtc_hal.h" - - #include "boost_posix_time_wrap.h" -#include - +#include "check.h" +#include "system_error.h" +#include +#include + +namespace { + +void ptimeToTimeval(struct timeval* tv, boost::posix_time::ptime pt) { + static boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); + auto diff = pt - epoch; + tv->tv_sec = diff.ticks() / diff.ticks_per_second(); + tv->tv_usec = diff.fractional_seconds(); +} -void HAL_RTC_Configuration(void) -{ } +// anonymous -#if BOOST_VERSION < 105800 -time_t to_time_t(boost::posix_time::ptime t) -{ - using namespace boost::posix_time; - static ptime epoch(boost::gregorian::date(1970,1,1)); - time_duration::sec_type x = (t - epoch).total_seconds(); - return time_t(x); +void hal_rtc_init(void) { + // UTC by default + setenv("TZ", "", 1); + tzset(); } -#endif -time_t HAL_RTC_Get_UnixTime(void) -{ +int hal_rtc_get_time(struct timeval* tv, void* reserved) { + CHECK_TRUE(tv, SYSTEM_ERROR_INVALID_ARGUMENT); auto now = boost::posix_time::microsec_clock::universal_time(); - return to_time_t(now); + ptimeToTimeval(tv, now); + return 0; } -void HAL_RTC_Set_UnixTime(time_t value) -{ - +int hal_rtc_set_time(const struct timeval* tv, void* reserved) { + return 0; } -void HAL_RTC_Set_UnixAlarm(time_t value) -{ +bool hal_rtc_time_is_valid(void* reserved) { + return true; } -void HAL_RTC_Cancel_UnixAlarm(void) -{ +int hal_rtc_set_alarm(const struct timeval* tv, uint32_t flags, hal_rtc_alarm_handler handler, void* context, void* reserved) { + return SYSTEM_ERROR_NOT_SUPPORTED; } -uint8_t HAL_RTC_Time_Is_Valid(void* reserved) -{ - return 1; -} \ No newline at end of file +void hal_rtc_cancel_alarm(void) { +} diff --git a/hal/src/nRF52840/core_hal.c b/hal/src/nRF52840/core_hal.c index 3efb5a1a57..47deab0adc 100644 --- a/hal/src/nRF52840/core_hal.c +++ b/hal/src/nRF52840/core_hal.c @@ -342,7 +342,7 @@ void HAL_Core_Setup(void) { // Initialize stdlib PRNG with a seed from hardware RNG srand(HAL_RNG_GetRandomNumber()); - HAL_RTC_Configuration(); + hal_rtc_init(); #if !defined(MODULAR_FIRMWARE) || !MODULAR_FIRMWARE module_user_init_hook(); diff --git a/hal/src/nRF52840/exrtc_hal.cpp b/hal/src/nRF52840/exrtc_hal.cpp index b662bc6c33..4caf936337 100644 --- a/hal/src/nRF52840/exrtc_hal.cpp +++ b/hal/src/nRF52840/exrtc_hal.cpp @@ -30,45 +30,56 @@ using namespace particle; namespace { const auto UNIX_TIME_201801010000 = 1514764800; // 2018/01/01 00:00:00 -const auto UNIX_TIME_YEAR_BASE = 118; // 2018 - 1900 } // anonymous -int hal_exrtc_set_unixtime(time_t unixtime, void* reserved) { - struct tm* calendar = gmtime(&unixtime); - if (!calendar) { - return SYSTEM_ERROR_INTERNAL; - } - CHECK(Am18x5::getInstance().setCalendar(calendar)); - return SYSTEM_ERROR_NONE; +int hal_exrtc_init(void* reserved) { + (void)Am18x5::getInstance(); + return 0; +} + +int hal_exrtc_set_time(const struct timeval* tv, void* reserved) { + return Am18x5::getInstance().setTime(tv); } -time_t hal_exrtc_get_unixtime(void* reserved) { - struct tm calendar; - CHECK(Am18x5::getInstance().getCalendar(&calendar)); - return mktime(&calendar); +int hal_exrtc_get_time(struct timeval* tv, void* reserved) { + return Am18x5::getInstance().getTime(tv); } -int hal_exrtc_set_unix_alarm(time_t unixtime, hal_exrtc_alarm_handler_t handler, void* context, void* reserved) { - struct tm* calendar = gmtime(&unixtime); - if (!calendar) { - return SYSTEM_ERROR_INTERNAL; +int hal_exrtc_set_alarm(const struct timeval* tv, uint32_t flags, hal_exrtc_alarm_handler handler, void* context, void* reserved) { + CHECK_TRUE(tv, SYSTEM_ERROR_INVALID_ARGUMENT); + struct timeval alarm = *tv; + if (flags & HAL_RTC_ALARM_FLAG_IN) { + struct timeval now; + CHECK(hal_exrtc_get_time(&now, nullptr)); + timeradd(&now, tv, &alarm); } - CHECK(Am18x5::getInstance().setAlarm(calendar)); - return Am18x5::getInstance().enableAlarm(true, handler, context); + CHECK(Am18x5::getInstance().setAlarm(&alarm)); + CHECK(Am18x5::getInstance().enableAlarm(true, handler, context)); + + int res = CHECK(Am18x5::getInstance().getAlarm(&alarm)); + struct timeval now; + CHECK(hal_exrtc_get_time(&now, nullptr)); + // If alarm time is in the past and it hasn't fired + if (timercmp(&alarm, &now, <) && res == 0) { + return SYSTEM_ERROR_TIMEOUT; + } + return 0; } -int hal_exrtc_cancel_unixalarm(void* reserved) { +int hal_exrtc_cancel_alarm(void* reserved) { return Am18x5::getInstance().enableAlarm(false, nullptr, nullptr); } bool hal_exrtc_time_is_valid(void* reserved) { - time_t unixtime = 0; - CHECK(hal_exrtc_get_unixtime(nullptr)); - return unixtime > UNIX_TIME_201801010000; + struct timeval tv; + if (!hal_exrtc_get_time(&tv, nullptr)) { + return tv.tv_sec > UNIX_TIME_201801010000; + } + return false; } -int hal_exrtc_enable_watchdog(time_t ms, void* reserved) { +int hal_exrtc_enable_watchdog(system_tick_t ms, void* reserved) { uint8_t value; // Maximum 31. Am18x5WatchdogFrequency frequency; if (ms < 1937) { // 31 * 1000 / 16 @@ -98,7 +109,7 @@ int hal_exrtc_feed_watchdog(void* reserved) { return Am18x5::getInstance().feedWatchdog(); } -int hal_exrtc_sleep_timer(time_t ms, void* reserved) { +int hal_exrtc_sleep_timer(system_tick_t ms, void* reserved) { uint8_t ticks; Am18x5TimerFrequency frequency; if (ms <= 3984) { // 255 * 1000 / 64 diff --git a/hal/src/nRF52840/posix/syscalls_posix.cpp b/hal/src/nRF52840/posix/syscalls_posix.cpp index 3f3ac91492..337d124bff 100644 --- a/hal/src/nRF52840/posix/syscalls_posix.cpp +++ b/hal/src/nRF52840/posix/syscalls_posix.cpp @@ -354,10 +354,10 @@ pid_t _getpid(void) { } int _gettimeofday(struct timeval* tv, void* tz) { - if (tv) { - time_t t = HAL_RTC_Get_UnixTime(); - tv->tv_sec = t; - tv->tv_usec = 0; + int r = hal_rtc_get_time(tv, nullptr); + if (r) { + errno = EFAULT; + return -1; } // tz argument is obsolete (void)tz; diff --git a/hal/src/nRF52840/rtc_hal.cpp b/hal/src/nRF52840/rtc_hal.cpp index bf5a4e03c2..a07bc6c0d3 100644 --- a/hal/src/nRF52840/rtc_hal.cpp +++ b/hal/src/nRF52840/rtc_hal.cpp @@ -21,6 +21,7 @@ #include "concurrent_hal.h" #include "service_debug.h" #include "hal_platform.h" +#include "check.h" #if HAL_PLATFORM_EXTERNAL_RTC #include "exrtc_hal.h" @@ -33,74 +34,127 @@ extern "C" void HAL_RTCAlarm_Handler(void); namespace { -const auto UNIX_TIME_201801010000 = 1514764800; // 2018/01/01 00:00:00 +const uint64_t UNIX_TIME_201801010000 = 1514764800000000; // 2018/01/01 00:00:00 -time_t s_unix_time_base = 946684800; // Default date/time to 2000/01/01 00:00:00 -uint64_t s_unix_time_base_ms = 0; // Millisecond clock reference to the s_unix_time_base +uint64_t s_unix_time_base = 946684800000000; // Default date/time to 2000/01/01 00:00:00 +uint64_t s_unix_time_base_us = 0; // Microsecond clock reference to the s_unix_time_base -#if HAL_PLATFORM_EXTERNAL_RTC -void exRtcAlarmHandler(void* context) { - HAL_RTCAlarm_Handler(); -} -#else +const uint64_t US_IN_SECONDS = 1000000ULL; + +#if !HAL_PLATFORM_EXTERNAL_RTC os_timer_t s_alarm_timer = nullptr; // software alarm timer -#endif +hal_rtc_alarm_handler s_alarm_handler = nullptr; +void* s_alarm_context = nullptr; +#endif // !HAL_PLATFORM_EXTERNAL_RTC + +uint64_t getUsUnixTime() { + int st = HAL_disable_irq(); + auto unix_time_base = s_unix_time_base; + auto unix_time_base_us = s_unix_time_base_us; + HAL_enable_irq(st); + uint64_t us = hal_timer_micros(nullptr); + uint64_t unixTimeUs = unix_time_base + (us - unix_time_base_us); + return unixTimeUs; +} + +void timevalFromUsUnixtime(struct timeval* tv, uint64_t us) { + tv->tv_sec = us / US_IN_SECONDS; + tv->tv_usec = (us - (tv->tv_sec * US_IN_SECONDS)); +} + +uint64_t usUnixtimeFromTimeval(const struct timeval* tv) { + return (tv->tv_sec * US_IN_SECONDS + tv->tv_usec); +} } // anonymous -void HAL_RTC_Configuration(void) { +void hal_rtc_init(void) { #if HAL_PLATFORM_EXTERNAL_RTC - HAL_RTC_Set_UnixTime(hal_exrtc_get_unixtime(nullptr)); + hal_exrtc_init(nullptr); + struct timeval tv = {}; + if (!hal_exrtc_get_time(&tv, nullptr)) { + hal_rtc_set_time(&tv, nullptr); + } #else // Do nothing #endif } -void HAL_RTC_Set_UnixTime(time_t value) { - uint64_t ms = hal_timer_millis(nullptr); - int st = HAL_disable_irq(); - s_unix_time_base = value; - s_unix_time_base_ms = ms; - HAL_enable_irq(st); -#if HAL_PLATFORM_EXTERNAL_RTC - hal_exrtc_set_unixtime(value, nullptr); -#endif +bool hal_rtc_time_is_valid(void* reserved) { + return s_unix_time_base > UNIX_TIME_201801010000; +} + +int hal_rtc_get_time(struct timeval* tv, void* reserved) { + CHECK_TRUE(tv, SYSTEM_ERROR_INVALID_ARGUMENT); + auto unixTimeUs = getUsUnixTime(); + timevalFromUsUnixtime(tv, unixTimeUs); + return 0; } -time_t HAL_RTC_Get_UnixTime(void) { +int hal_rtc_set_time(const struct timeval* tv, void* reserved) { + CHECK_TRUE(tv, SYSTEM_ERROR_INVALID_ARGUMENT); + uint64_t us = hal_timer_micros(nullptr); int st = HAL_disable_irq(); - auto unix_time_base = s_unix_time_base; - auto unix_time_base_ms = s_unix_time_base_ms; + s_unix_time_base = usUnixtimeFromTimeval(tv); + s_unix_time_base_us = us; HAL_enable_irq(st); - uint64_t ms = hal_timer_millis(nullptr); - return unix_time_base + (time_t)((ms - unix_time_base_ms) / 1000); +#if HAL_PLATFORM_EXTERNAL_RTC + CHECK(hal_exrtc_set_time(tv, nullptr)); +#endif + return 0; } -void HAL_RTC_Set_UnixAlarm(time_t value) { +int hal_rtc_set_alarm(const struct timeval* tv, uint32_t flags, hal_rtc_alarm_handler handler, void* context, void* reserved) { #if HAL_PLATFORM_EXTERNAL_RTC - hal_exrtc_set_unix_alarm(value, exRtcAlarmHandler, nullptr, nullptr); + return hal_exrtc_set_alarm(tv, flags, handler, context, nullptr); #else + CHECK_TRUE(tv, SYSTEM_ERROR_INVALID_ARGUMENT); + struct timeval alarm = *tv; + if (flags & HAL_RTC_ALARM_FLAG_IN) { + struct timeval now; + CHECK(hal_rtc_get_time(&now, nullptr)); + timeradd(&alarm, &now, &alarm); + } + auto unixTimeMs = getUsUnixTime() / 1000; + auto alarmTimeMs = usUnixtimeFromTimeval(&alarm) / 1000; + if (alarmTimeMs <= unixTimeMs) { + // Too late to set such an alarm + return SYSTEM_ERROR_TIMEOUT; + } + // This implementation is only used for System.sleep(seconds) (network sleep) // on Gen3 devices. if (!s_alarm_timer) { os_timer_create(&s_alarm_timer, 0, [](os_timer_t timer) { - HAL_RTCAlarm_Handler(); + if (s_alarm_handler) { + s_alarm_handler(s_alarm_context); + } }, nullptr, true, nullptr); SPARK_ASSERT(s_alarm_timer); } - HAL_RTC_Cancel_UnixAlarm(); + hal_rtc_cancel_alarm(); + s_alarm_context = context; + s_alarm_handler = handler; + + unsigned diffMs = (unsigned)(alarmTimeMs - unixTimeMs); // NOTE: changing the period of a timer in a dormant state will also // start the timer. - os_timer_change(s_alarm_timer, OS_TIMER_CHANGE_PERIOD, false, value * 1000, + int r = os_timer_change(s_alarm_timer, OS_TIMER_CHANGE_PERIOD, false, diffMs, 0xffffffff, nullptr); + + if (r != 0) { + return SYSTEM_ERROR_INTERNAL; + } + + return r; #endif } -void HAL_RTC_Cancel_UnixAlarm(void) { +void hal_rtc_cancel_alarm(void) { #if HAL_PLATFORM_EXTERNAL_RTC - hal_exrtc_cancel_unixalarm(nullptr); + hal_exrtc_cancel_alarm(nullptr); #else // This implementation is only used for System.sleep(seconds) (network sleep) // on Gen3 devices. @@ -110,6 +164,17 @@ void HAL_RTC_Cancel_UnixAlarm(void) { #endif } -uint8_t HAL_RTC_Time_Is_Valid(void* reserved) { - return HAL_RTC_Get_UnixTime() > UNIX_TIME_201801010000; +// These are deprecated due to time_t size changes +void hal_rtc_set_unixtime_deprecated(time32_t value) { + struct timeval tv = { + .tv_sec = value, + .tv_usec = 0 + }; + hal_rtc_set_time(&tv, nullptr); +} + +time32_t hal_rtc_get_unixtime_deprecated(void) { + struct timeval tv = {}; + hal_rtc_get_time(&tv, nullptr); + return (time32_t)tv.tv_sec; } diff --git a/hal/src/nRF52840/sleep_hal.cpp b/hal/src/nRF52840/sleep_hal.cpp index 90248bd575..83e1fc22fe 100644 --- a/hal/src/nRF52840/sleep_hal.cpp +++ b/hal/src/nRF52840/sleep_hal.cpp @@ -598,11 +598,13 @@ static int enterHibernateMode(const hal_sleep_config_t* config, hal_wakeup_sourc auto source = config->wakeup_sources; while (source) { if (source->type == HAL_WAKEUP_SOURCE_TYPE_RTC) { - time_t now = 0; - CHECK(now = hal_exrtc_get_unixtime(nullptr)); - auto rtcWakeup = reinterpret_cast(source); - time_t alarm = now + rtcWakeup->ms / 1000; - CHECK(hal_exrtc_set_unix_alarm(alarm, nullptr, nullptr, nullptr)); + auto rtcWakeup = reinterpret_cast(source); + auto seconds = rtcWakeup->ms / 1000; + struct timeval tv = { + .tv_sec = seconds, + .tv_usec = 0 + }; + CHECK(hal_exrtc_set_alarm(&tv, HAL_RTC_ALARM_FLAG_IN, nullptr, nullptr, nullptr)); } source = source->next; } diff --git a/hal/src/stm32f2xx/core_hal_stm32f2xx.c b/hal/src/stm32f2xx/core_hal_stm32f2xx.c index 06c5599114..94566d5c74 100644 --- a/hal/src/stm32f2xx/core_hal_stm32f2xx.c +++ b/hal/src/stm32f2xx/core_hal_stm32f2xx.c @@ -364,7 +364,7 @@ void HAL_Core_Config(void) HAL_Core_Config_systick_configuration(); - HAL_RTC_Configuration(); + hal_rtc_init(); HAL_RNG_Configuration(); diff --git a/hal/src/stm32f2xx/rtc_hal.c b/hal/src/stm32f2xx/rtc_hal.c index c8f275f48b..78e39df65a 100644 --- a/hal/src/stm32f2xx/rtc_hal.c +++ b/hal/src/stm32f2xx/rtc_hal.c @@ -24,11 +24,19 @@ */ /* Includes ------------------------------------------------------------------*/ + +// For some reason on some platforms this is not defined +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif // _DEFAULT_SOURCE + #include "rtc_hal.h" #include "stm32f2xx_rtc.h" #include "hw_config.h" #include "interrupts_hal.h" #include "debug.h" +#include "system_error.h" +#include /* Private typedef -----------------------------------------------------------*/ @@ -42,22 +50,24 @@ static time_t HAL_RTC_Time_Last_Set = 0; /* Private function prototypes -----------------------------------------------*/ -extern void HAL_RTCAlarm_Handler(void); +static hal_rtc_alarm_handler s_alarm_handler = NULL; +static void* s_alarm_handler_context = NULL; +static volatile bool s_alarm_fired = false; void setRTCTime(RTC_TimeTypeDef* RTC_TimeStructure, RTC_DateTypeDef* RTC_DateStructure) { - /* Configure the RTC time register */ - if(RTC_SetTime(RTC_Format_BIN, RTC_TimeStructure) == ERROR) - { - /* RTC Set Time failed */ - } - - /* Configure the RTC date register */ - if(RTC_SetDate(RTC_Format_BIN, RTC_DateStructure) == ERROR) - { - /* RTC Set Date failed */ - } + /* Configure the RTC time register */ + if(RTC_SetTime(RTC_Format_BIN, RTC_TimeStructure) == ERROR) + { + /* RTC Set Time failed */ + } + + /* Configure the RTC date register */ + if(RTC_SetDate(RTC_Format_BIN, RTC_DateStructure) == ERROR) + { + /* RTC Set Date failed */ + } } /* Set date/time to 2000/01/01 00:00:00 */ @@ -81,206 +91,264 @@ void HAL_RTC_Initialize_UnixTime() } -void HAL_RTC_Configuration(void) +void hal_rtc_init(void) { - RTC_InitTypeDef RTC_InitStructure; - EXTI_InitTypeDef EXTI_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; - - __IO uint32_t AsynchPrediv = 0x7F, SynchPrediv = 0xFF; - - /* Configure EXTI Line17(RTC Alarm) to generate an interrupt on rising edge */ - EXTI_ClearITPendingBit(EXTI_Line17); - EXTI_InitStructure.EXTI_Line = EXTI_Line17; - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; - EXTI_InitStructure.EXTI_LineCmd = ENABLE; - EXTI_Init(&EXTI_InitStructure); - - /* Enable the RTC Alarm Interrupt */ - NVIC_InitStructure.NVIC_IRQChannel = RTC_Alarm_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = RTC_Alarm_IRQ_PRIORITY; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - /* Check if the StandBy flag is set */ - if(PWR_GetFlagStatus(PWR_FLAG_SB) != RESET) - { - /* System resumed from STANDBY mode */ - - /* Wait for RTC APB registers synchronisation */ - RTC_WaitForSynchro(); - - /* Clear the RTC Alarm Flag */ - RTC_ClearFlag(RTC_FLAG_ALRAF); - - /* Clear the EXTI Line 17 Pending bit (Connected internally to RTC Alarm) */ - EXTI_ClearITPendingBit(EXTI_Line17); - - /* No need to configure the RTC as the RTC configuration(clock source, enable, - prescaler,...) is kept after wake-up from STANDBY */ - } - else - { - /* StandBy flag is not set */ - - /* Enable LSE */ - RCC_LSEConfig(RCC_LSE_ON); - - /* Wait till LSE is ready */ - while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) - { - //Do nothing - } - - /* Select LSE as RTC Clock Source */ - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); - - /* Enable RTC Clock */ - RCC_RTCCLKCmd(ENABLE); - - /* Wait for RTC registers synchronization */ - RTC_WaitForSynchro(); - - /* RTC register configuration done only once */ - if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0xC1C1) - { - /* Configure the RTC data register and RTC prescaler */ - RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv; - RTC_InitStructure.RTC_SynchPrediv = SynchPrediv; - RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; - - /* Check on RTC init */ - if (RTC_Init(&RTC_InitStructure) != ERROR) - { + RTC_InitTypeDef RTC_InitStructure; + EXTI_InitTypeDef EXTI_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + __IO uint32_t AsynchPrediv = 0x7F, SynchPrediv = 0xFF; + + /* Configure EXTI Line17(RTC Alarm) to generate an interrupt on rising edge */ + EXTI_ClearITPendingBit(EXTI_Line17); + EXTI_InitStructure.EXTI_Line = EXTI_Line17; + EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; + EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; + EXTI_InitStructure.EXTI_LineCmd = ENABLE; + EXTI_Init(&EXTI_InitStructure); + + /* Enable the RTC Alarm Interrupt */ + NVIC_InitStructure.NVIC_IRQChannel = RTC_Alarm_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = RTC_Alarm_IRQ_PRIORITY; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Check if the StandBy flag is set */ + if(PWR_GetFlagStatus(PWR_FLAG_SB) != RESET) + { + /* System resumed from STANDBY mode */ + + /* Wait for RTC APB registers synchronisation */ + RTC_WaitForSynchro(); + + /* Clear the RTC Alarm Flag */ + RTC_ClearFlag(RTC_FLAG_ALRAF); + + /* Clear the EXTI Line 17 Pending bit (Connected internally to RTC Alarm) */ + EXTI_ClearITPendingBit(EXTI_Line17); + + /* No need to configure the RTC as the RTC configuration(clock source, enable, + prescaler,...) is kept after wake-up from STANDBY */ + } + else + { + /* StandBy flag is not set */ + + /* Enable LSE */ + RCC_LSEConfig(RCC_LSE_ON); + + /* Wait till LSE is ready */ + while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) + { + //Do nothing + } + + /* Select LSE as RTC Clock Source */ + RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); + + /* Enable RTC Clock */ + RCC_RTCCLKCmd(ENABLE); + + /* Wait for RTC registers synchronization */ + RTC_WaitForSynchro(); + + /* RTC register configuration done only once */ + if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0xC1C1) + { + /* Configure the RTC data register and RTC prescaler */ + RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv; + RTC_InitStructure.RTC_SynchPrediv = SynchPrediv; + RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; + + /* Check on RTC init */ + if (RTC_Init(&RTC_InitStructure) != ERROR) + { /* Configure RTC Date and Time Registers if not set - Fixes #480, #580 */ /* Set date/time to 2000/01/01 00:00:00 */ HAL_RTC_Initialize_UnixTime(); /* Indicator for the RTC configuration */ RTC_WriteBackupRegister(RTC_BKP_DR0, 0xC1C1); - } - } - } + } + } + } } -time_t HAL_RTC_Get_UnixTime(void) -{ - RTC_TimeTypeDef RTC_TimeStructure; - RTC_DateTypeDef RTC_DateStructure; +int hal_rtc_get_time(struct timeval* tv, void* reserved) { + if (!tv) { + return SYSTEM_ERROR_INVALID_ARGUMENT; + } + + RTC_TimeTypeDef RTC_TimeStructure; + RTC_DateTypeDef RTC_DateStructure; - /* Get the current Time and Date */ - RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure); - RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure); + /* Get the current Time and Date */ + RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure); + RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure); - struct tm calendar_time; + struct tm calendar_time = {0}; - /* Set calendar_time time struct values */ - calendar_time.tm_hour = RTC_TimeStructure.RTC_Hours; - calendar_time.tm_min = RTC_TimeStructure.RTC_Minutes; - calendar_time.tm_sec = RTC_TimeStructure.RTC_Seconds; + /* Set calendar_time time struct values */ + calendar_time.tm_hour = RTC_TimeStructure.RTC_Hours; + calendar_time.tm_min = RTC_TimeStructure.RTC_Minutes; + calendar_time.tm_sec = RTC_TimeStructure.RTC_Seconds; - /* Set calendar_time date struct values */ - calendar_time.tm_wday = RTC_DateStructure.RTC_WeekDay; - calendar_time.tm_mday = RTC_DateStructure.RTC_Date; - calendar_time.tm_mon = RTC_DateStructure.RTC_Month-1; + /* Set calendar_time date struct values */ + calendar_time.tm_wday = RTC_DateStructure.RTC_WeekDay; + calendar_time.tm_mday = RTC_DateStructure.RTC_Date; + calendar_time.tm_mon = RTC_DateStructure.RTC_Month-1; // STM32F2 year is only 2-digit (00 - 99) - calendar_time.tm_year = RTC_DateStructure.RTC_Year + 100; + calendar_time.tm_year = RTC_DateStructure.RTC_Year + 100; + calendar_time.tm_isdst = -1; - return (time_t)mktime(&calendar_time); + tv->tv_sec = mktime(&calendar_time); + tv->tv_usec = 0; + return 0; } -void HAL_RTC_Set_UnixTime(time_t value) -{ - RTC_TimeTypeDef RTC_TimeStructure; - RTC_DateTypeDef RTC_DateStructure; +int hal_rtc_set_time(const struct timeval* tv, void* reserved) { + if (!tv) { + return SYSTEM_ERROR_INVALID_ARGUMENT; + } - struct tm *calendar_time; - calendar_time = localtime(&value); + RTC_TimeTypeDef RTC_TimeStructure; + RTC_DateTypeDef RTC_DateStructure; + + struct tm calendar_time = {}; + if (!gmtime_r(&tv->tv_sec, &calendar_time)) { + return SYSTEM_ERROR_INTERNAL; + } - /* Get calendar_time time struct values */ - RTC_TimeStructure.RTC_Hours = calendar_time->tm_hour; - RTC_TimeStructure.RTC_Minutes = calendar_time->tm_min; - RTC_TimeStructure.RTC_Seconds = calendar_time->tm_sec; + /* Get calendar_time time struct values */ + RTC_TimeStructure.RTC_Hours = calendar_time.tm_hour; + RTC_TimeStructure.RTC_Minutes = calendar_time.tm_min; + RTC_TimeStructure.RTC_Seconds = calendar_time.tm_sec; - /* Get calendar_time date struct values */ - RTC_DateStructure.RTC_WeekDay = calendar_time->tm_wday; - RTC_DateStructure.RTC_Date = calendar_time->tm_mday; - RTC_DateStructure.RTC_Month = calendar_time->tm_mon+1; + /* Get calendar_time date struct values */ + RTC_DateStructure.RTC_WeekDay = calendar_time.tm_wday; + RTC_DateStructure.RTC_Date = calendar_time.tm_mday; + RTC_DateStructure.RTC_Month = calendar_time.tm_mon+1; // STM32F2 year is only 2-digit (00 - 99) - RTC_DateStructure.RTC_Year = calendar_time->tm_year % 100; + RTC_DateStructure.RTC_Year = calendar_time.tm_year % 100; int32_t state = HAL_disable_irq(); setRTCTime(&RTC_TimeStructure, &RTC_DateStructure); - HAL_RTC_Time_Last_Set = value; + HAL_RTC_Time_Last_Set = tv->tv_sec; HAL_enable_irq(state); + + return 0; } -void HAL_RTC_Set_UnixAlarm(time_t value) -{ - RTC_AlarmTypeDef RTC_AlarmStructure; +int hal_rtc_set_alarm(const struct timeval* tv, uint32_t flags, hal_rtc_alarm_handler handler, void* context, void* reserved) { + if (!tv) { + return SYSTEM_ERROR_INVALID_ARGUMENT; + } + + struct timeval alarm = *tv; + if (flags & HAL_RTC_ALARM_FLAG_IN) { + struct timeval now; + int r = hal_rtc_get_time(&now, NULL); + if (r) { + return r; + } + timeradd(&now, tv, &alarm); + } - time_t alarm_time = HAL_RTC_Get_UnixTime() + value; + struct tm alarm_time_tm; + if (!gmtime_r(&alarm.tv_sec, &alarm_time_tm)) { + return SYSTEM_ERROR_INTERNAL; + } - struct tm *alarm_time_tm; - alarm_time_tm = localtime(&alarm_time); + int32_t state = HAL_disable_irq(); + /* Disable the Alarm A */ + RTC_AlarmCmd(RTC_Alarm_A, DISABLE); - /* Disable the Alarm A */ - RTC_AlarmCmd(RTC_Alarm_A, DISABLE); + s_alarm_handler_context = context; + s_alarm_handler = handler; - RTC_AlarmStructure.RTC_AlarmTime.RTC_Hours = alarm_time_tm->tm_hour; - RTC_AlarmStructure.RTC_AlarmTime.RTC_Minutes = alarm_time_tm->tm_min; - RTC_AlarmStructure.RTC_AlarmTime.RTC_Seconds = alarm_time_tm->tm_sec; - RTC_AlarmStructure.RTC_AlarmDateWeekDay = alarm_time_tm->tm_mday; + RTC_AlarmTypeDef RTC_AlarmStructure; + RTC_AlarmStructure.RTC_AlarmTime.RTC_Hours = alarm_time_tm.tm_hour; + RTC_AlarmStructure.RTC_AlarmTime.RTC_Minutes = alarm_time_tm.tm_min; + RTC_AlarmStructure.RTC_AlarmTime.RTC_Seconds = alarm_time_tm.tm_sec; + RTC_AlarmStructure.RTC_AlarmDateWeekDay = alarm_time_tm.tm_mday; RTC_AlarmStructure.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_Date; RTC_AlarmStructure.RTC_AlarmMask = RTC_AlarmMask_None; - /* Configure the RTC Alarm A register */ - RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &RTC_AlarmStructure); + /* Configure the RTC Alarm A register */ + RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &RTC_AlarmStructure); + + /* Enable the RTC Alarm A Interrupt */ + RTC_ITConfig(RTC_IT_ALRA, ENABLE); + + /* Enable the Alarm A */ + RTC_AlarmCmd(RTC_Alarm_A, ENABLE); - /* Enable the RTC Alarm A Interrupt */ - RTC_ITConfig(RTC_IT_ALRA, ENABLE); + /* Clear RTC Alarm Flag */ + RTC_ClearFlag(RTC_FLAG_ALRAF); + s_alarm_fired = false; + HAL_enable_irq(state); + + struct timeval now; + int r = hal_rtc_get_time(&now, NULL); + if (r) { + return r; + } - /* Enable the Alarm A */ - RTC_AlarmCmd(RTC_Alarm_A, ENABLE); + // Check if for some reason the alarm date is already in the past + // and hasn't fired + if (alarm.tv_sec < now.tv_sec && !s_alarm_fired) { + hal_rtc_cancel_alarm(); + return SYSTEM_ERROR_TIMEOUT; + } - /* Clear RTC Alarm Flag */ - RTC_ClearFlag(RTC_FLAG_ALRAF); + return 0; } -void HAL_RTC_Cancel_UnixAlarm(void) { +void hal_rtc_cancel_alarm(void) { RTC_AlarmCmd(RTC_Alarm_A, DISABLE); RTC_ClearFlag(RTC_FLAG_ALRAF); RTC_WaitForSynchro(); RTC_ClearITPendingBit(RTC_IT_ALRA); EXTI_ClearITPendingBit(EXTI_Line17); + + s_alarm_handler = NULL; + s_alarm_handler_context = NULL; + s_alarm_fired = false; } void RTC_Alarm_irq(void) { - if(RTC_GetITStatus(RTC_IT_ALRA) != RESET) - { - HAL_RTCAlarm_Handler(); - - /* Clear EXTI line17 pending bit */ - EXTI_ClearITPendingBit(EXTI_Line17); - - /* Check if the Wake-Up flag is set */ - if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET) - { - /* Clear Wake Up flag */ - PWR_ClearFlag(PWR_FLAG_WU); - } - - /* Clear RTC Alarm interrupt pending bit */ - RTC_ClearITPendingBit(RTC_IT_ALRA); - } + if(RTC_GetITStatus(RTC_IT_ALRA) != RESET) + { + s_alarm_fired = true; + if (s_alarm_handler) { + s_alarm_handler(s_alarm_handler_context); + } + + /* Clear EXTI line17 pending bit */ + EXTI_ClearITPendingBit(EXTI_Line17); + + /* Check if the Wake-Up flag is set */ + if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET) + { + /* Clear Wake Up flag */ + PWR_ClearFlag(PWR_FLAG_WU); + } + + /* Clear RTC Alarm interrupt pending bit */ + RTC_ClearITPendingBit(RTC_IT_ALRA); + } } -uint8_t HAL_RTC_Time_Is_Valid(void* reserved) -{ - uint8_t valid = 0; +bool hal_rtc_time_is_valid(void* reserved) { + bool valid = false; + + struct timeval tv; + if (hal_rtc_get_time(&tv, NULL)) { + return valid; + } + int32_t state = HAL_disable_irq(); for (;;) { @@ -293,13 +361,27 @@ uint8_t HAL_RTC_Time_Is_Valid(void* reserved) if (RTC_DateStructure.RTC_Year == 0) break; - if (HAL_RTC_Time_Last_Set && HAL_RTC_Get_UnixTime() < HAL_RTC_Time_Last_Set) + if (HAL_RTC_Time_Last_Set && tv.tv_sec < HAL_RTC_Time_Last_Set) break; - valid = 1; + valid = true; break; } HAL_enable_irq(state); return valid; } + +void hal_rtc_set_unixtime_deprecated(time32_t value) { + struct timeval tv = { + .tv_sec = value, + .tv_usec = 0 + }; + hal_rtc_set_time(&tv, NULL); +} + +time32_t hal_rtc_get_unixtime_deprecated(void) { + struct timeval tv = {}; + hal_rtc_get_time(&tv, NULL); + return (time32_t)tv.tv_sec; +} diff --git a/hal/src/stm32f2xx/sleep_hal.cpp b/hal/src/stm32f2xx/sleep_hal.cpp index 036edb4d0a..405b6e41d9 100644 --- a/hal/src/stm32f2xx/sleep_hal.cpp +++ b/hal/src/stm32f2xx/sleep_hal.cpp @@ -129,14 +129,14 @@ static int enterStopMode(const hal_sleep_config_t* config, hal_wakeup_source_bas } else if (wakeupSource->type == HAL_WAKEUP_SOURCE_TYPE_RTC) { auto rtcWakeup = reinterpret_cast(wakeupSource); long seconds = rtcWakeup->ms / 1000; + /* * - To wake up from the Stop mode with an RTC alarm event, it is necessary to: * - Configure the EXTI Line 17 to be sensitive to rising edges (Interrupt * or Event modes) using the EXTI_Init() function. * */ - HAL_RTC_Cancel_UnixAlarm(); - HAL_RTC_Set_UnixAlarm((time_t) seconds); + hal_rtc_cancel_alarm(); // Connect RTC to EXTI line EXTI_InitTypeDef extiInitStructure = {0}; @@ -145,6 +145,12 @@ static int enterStopMode(const hal_sleep_config_t* config, hal_wakeup_source_bas extiInitStructure.EXTI_Trigger = EXTI_Trigger_Rising; extiInitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&extiInitStructure); + + struct timeval tv = { + .tv_sec = seconds, + .tv_usec = 0 + }; + CHECK(hal_rtc_set_alarm(&tv, HAL_RTC_ALARM_FLAG_IN, nullptr, nullptr, nullptr)); } wakeupSource = wakeupSource->next; } @@ -229,7 +235,7 @@ static int enterStopMode(const hal_sleep_config_t* config, hal_wakeup_source_bas if (wakeupSource->type == HAL_WAKEUP_SOURCE_TYPE_RTC) { // No need to detach RTC Alarm from EXTI, since it will be detached in HAL_Interrupts_Restore() // RTC Alarm should be canceled to avoid entering HAL_RTCAlarm_Handler or if we were woken up by pin - HAL_RTC_Cancel_UnixAlarm(); + hal_rtc_cancel_alarm(); } else if (wakeupSource->type == HAL_WAKEUP_SOURCE_TYPE_GPIO) { auto gpioWakeup = reinterpret_cast(wakeupSource); HAL_Interrupts_Detach_Ext(gpioWakeup->pin, 1, nullptr); @@ -256,8 +262,13 @@ static int enterHibernateMode(const hal_sleep_config_t* config, hal_wakeup_sourc while (wakeupSource) { if (wakeupSource->type == HAL_WAKEUP_SOURCE_TYPE_RTC) { long seconds = reinterpret_cast(wakeupSource)->ms / 1000; - HAL_RTC_Cancel_UnixAlarm(); - HAL_RTC_Set_UnixAlarm((time_t) seconds); + + struct timeval tv = { + .tv_sec = seconds, + .tv_usec = 0 + }; + hal_rtc_cancel_alarm(); + CHECK(hal_rtc_set_alarm(&tv, HAL_RTC_ALARM_FLAG_IN, nullptr, nullptr, nullptr)); } else if (wakeupSource->type == HAL_WAKEUP_SOURCE_TYPE_GPIO) { enableWkpPin = true; } diff --git a/hal/src/template/rtc_hal.c b/hal/src/template/rtc_hal.c deleted file mode 100644 index 01eda96e24..0000000000 --- a/hal/src/template/rtc_hal.c +++ /dev/null @@ -1,65 +0,0 @@ -/** - ****************************************************************************** - * @file rtc_hal.c - * @author Satish Nair, Brett Walach - * @version V1.0.0 - * @date 12-Sept-2014 - * @brief - ****************************************************************************** - Copyright (c) 2013-2015 Particle Industries, Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation, either - version 3 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see . - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "rtc_hal.h" - -/* Private typedef -----------------------------------------------------------*/ - -/* Private define ------------------------------------------------------------*/ - -/* Private macro -------------------------------------------------------------*/ - -/* Private variables ---------------------------------------------------------*/ - -/* Extern variables ----------------------------------------------------------*/ - -/* Private function prototypes -----------------------------------------------*/ - -uint32_t HAL_RTC_Get_Counter(void) -{ - return 0; -} - -void HAL_RTC_Set_Counter(uint32_t value) -{ -} - -void HAL_RTC_Set_Alarm(uint32_t value) -{ -} - -void HAL_RTC_Set_UnixAlarm(time_t value) -{ - -} - -void HAL_RTC_Cancel_UnixAlarm(void) -{ -} - -void HAL_RTC_Set_UnixTime(time_t value) -{ -} diff --git a/hal/src/template/rtc_hal.cpp b/hal/src/template/rtc_hal.cpp index 3ff9fdf80b..368be4f819 100644 --- a/hal/src/template/rtc_hal.cpp +++ b/hal/src/template/rtc_hal.cpp @@ -26,29 +26,24 @@ /* Includes ------------------------------------------------------------------*/ #include "rtc_hal.h" -time_t HAL_RTC_Get_UnixTime(void) -{ - return 0; +void hal_rtc_init(void) { } -void HAL_RTC_Set_Alarm(uint32_t value) -{ +int hal_rtc_get_time(struct timeval* tv, void* reserved) { + return 0; } -void HAL_RTC_Set_UnixAlarm(time_t value) -{ - +int hal_rtc_set_time(const struct timeval* tv, void* reserved) { + return 0; } -void HAL_RTC_Cancel_UnixAlarm(void) -{ +bool hal_rtc_time_is_valid(void* reserved) { + return false; } -void HAL_RTC_Set_UnixTime(time_t value) -{ +int hal_rtc_set_alarm(const struct timeval* tv, uint32_t flags, hal_rtc_alarm_handler handler, void* context, void* reserved) { + return 0; } -uint8_t HAL_RTC_Time_Is_Valid(void* reserved) -{ - return 0; -} \ No newline at end of file +void hal_rtc_cancel_alarm(void) { +} diff --git a/hal/src/tracker/am18x5.cpp b/hal/src/tracker/am18x5.cpp index 0fe5fd17eb..b9a5b4a3bd 100644 --- a/hal/src/tracker/am18x5.cpp +++ b/hal/src/tracker/am18x5.cpp @@ -38,6 +38,16 @@ void exRtcInterruptHandler(void* data) { instance->sync(); } +const long MICROS_IN_HUNDREDTH = 10000; +const auto UNIX_TIME_YEAR_BASE = 118; // 2018 - 1900 + +int timevalToCalendar(const struct timeval* tv, struct tm* calendar) { + CHECK_TRUE(tv, SYSTEM_ERROR_INVALID_ARGUMENT); + CHECK_TRUE(gmtime_r(&tv->tv_sec, calendar), SYSTEM_ERROR_INVALID_ARGUMENT); + CHECK_TRUE(calendar->tm_year >= UNIX_TIME_YEAR_BASE, SYSTEM_ERROR_INVALID_ARGUMENT); + return 0; +} + } // anonymous namespace Am18x5::Am18x5() @@ -77,7 +87,7 @@ int Am18x5::begin() { LOG(ERROR, "os_semaphore_create() failed"); return SYSTEM_ERROR_INTERNAL; } - if (os_thread_create(&exRtcWorkerThread_, "IO Expander Thread", OS_THREAD_PRIORITY_NETWORK, exRtcInterruptHandleThread, this, 512)) { + if (os_thread_create(&exRtcWorkerThread_, "exrtc", OS_THREAD_PRIORITY_NETWORK, exRtcInterruptHandleThread, this, 512)) { os_semaphore_destroy(exRtcWorkerSemaphore_); exRtcWorkerSemaphore_ = nullptr; LOG(ERROR, "os_thread_create() failed"); @@ -145,55 +155,67 @@ int Am18x5::getPartNumber(uint16_t* id) const { return SYSTEM_ERROR_NONE; } -int Am18x5::setCalendar(const struct tm* calendar) const { +int Am18x5::setTime(const struct timeval* tv) const { + struct tm calendar; + CHECK(timevalToCalendar(tv, &calendar)); + Am18x5Lock lock(); CHECK_TRUE(initialized_, SYSTEM_ERROR_INVALID_STATE); - CHECK_TRUE(calendar, SYSTEM_ERROR_INVALID_ARGUMENT); - CHECK_TRUE(calendar->tm_year >= UNIX_TIME_YEAR_BASE, SYSTEM_ERROR_INVALID_ARGUMENT); - uint8_t buff[7] = {0}; - buff[0] = CHECK(decToBcd(calendar->tm_sec)); - buff[1] = CHECK(decToBcd(calendar->tm_min)); - buff[2] = CHECK(decToBcd(calendar->tm_hour)); - buff[3] = CHECK(decToBcd(calendar->tm_mday)); - buff[4] = CHECK(decToBcd(calendar->tm_mon + 1)); // Month in tm structure ranges from 0 - 11. - buff[5] = CHECK(decToBcd(calendar->tm_year - UNIX_TIME_YEAR_BASE)); - buff[6] = CHECK(decToBcd(calendar->tm_wday)); - CHECK(writeContinuousRegisters(Am18x5Register::SECONDS, buff, sizeof(buff))); + uint8_t buff[8] = {0}; + buff[0] = CHECK(decToBcd(tv->tv_usec / MICROS_IN_HUNDREDTH)); + buff[1] = CHECK(decToBcd(calendar.tm_sec)); + buff[2] = CHECK(decToBcd(calendar.tm_min)); + buff[3] = CHECK(decToBcd(calendar.tm_hour)); + buff[4] = CHECK(decToBcd(calendar.tm_mday)); + buff[5] = CHECK(decToBcd(calendar.tm_mon + 1)); // Month in tm structure ranges from 0 - 11. + buff[6] = CHECK(decToBcd(calendar.tm_year - UNIX_TIME_YEAR_BASE)); + buff[7] = CHECK(decToBcd(calendar.tm_wday)); + CHECK(writeContinuousRegisters(Am18x5Register::HUNDREDTHS, buff, sizeof(buff))); return SYSTEM_ERROR_NONE; } -int Am18x5::getCalendar(struct tm* calendar) const { +int Am18x5::getTime(struct timeval* tv) const { + CHECK_TRUE(tv, SYSTEM_ERROR_INVALID_ARGUMENT); + Am18x5Lock lock(); CHECK_TRUE(initialized_, SYSTEM_ERROR_INVALID_STATE); - CHECK_TRUE(calendar, SYSTEM_ERROR_INVALID_ARGUMENT); - uint8_t buff[7] = {0}; - CHECK(readContinuousRegisters(Am18x5Register::SECONDS, buff, sizeof(buff))); - calendar->tm_sec = CHECK(bcdToDec(buff[0])); - calendar->tm_min = CHECK(bcdToDec(buff[1])); - calendar->tm_hour = CHECK(bcdToDec(buff[2])); - calendar->tm_mday = CHECK(bcdToDec(buff[3])); - calendar->tm_mon = CHECK(bcdToDec(buff[4])); - calendar->tm_mon -= 1; - calendar->tm_year = CHECK(bcdToDec(buff[5])); - calendar->tm_year += UNIX_TIME_YEAR_BASE; - calendar->tm_wday = CHECK(bcdToDec(buff[6])); + uint8_t buff[8] = {0}; + CHECK(readContinuousRegisters(Am18x5Register::HUNDREDTHS, buff, sizeof(buff))); + + struct tm calendar = {}; + calendar.tm_sec = CHECK(bcdToDec(buff[1])); + calendar.tm_min = CHECK(bcdToDec(buff[2])); + calendar.tm_hour = CHECK(bcdToDec(buff[3])); + calendar.tm_mday = CHECK(bcdToDec(buff[4])); + calendar.tm_mon = CHECK(bcdToDec(buff[5])); + calendar.tm_mon -= 1; + calendar.tm_year = CHECK(bcdToDec(buff[6])); + calendar.tm_year += UNIX_TIME_YEAR_BASE; + calendar.tm_wday = CHECK(bcdToDec(buff[7])); + calendar.tm_isdst = -1; + + tv->tv_sec = mktime(&calendar); + tv->tv_usec = (long)buff[0] * MICROS_IN_HUNDREDTH; return SYSTEM_ERROR_NONE; } -int Am18x5::setAlarm(const struct tm* calendar) { +int Am18x5::setAlarm(const struct timeval* tv) { + struct tm calendar; + CHECK(timevalToCalendar(tv, &calendar)); + Am18x5Lock lock(); CHECK_TRUE(initialized_, SYSTEM_ERROR_INVALID_STATE); - CHECK_TRUE(calendar, SYSTEM_ERROR_INVALID_ARGUMENT); - CHECK_TRUE(calendar->tm_year >= UNIX_TIME_YEAR_BASE, SYSTEM_ERROR_INVALID_ARGUMENT); - uint8_t buff[6] = {0}; - buff[0] = CHECK(decToBcd(calendar->tm_sec)); - buff[1] = CHECK(decToBcd(calendar->tm_min)); - buff[2] = CHECK(decToBcd(calendar->tm_hour)); - buff[3] = CHECK(decToBcd(calendar->tm_mday)); - buff[4] = CHECK(decToBcd(calendar->tm_mon + 1)); // Month in tm structure ranges from 0 - 11. - alarmYear_ = calendar->tm_year - UNIX_TIME_YEAR_BASE; - buff[5] = CHECK(decToBcd(calendar->tm_wday)); - CHECK(writeContinuousRegisters(Am18x5Register::SECONDS_ALARM, buff, sizeof(buff))); + uint8_t buff[7] = {0}; + buff[0] = CHECK(decToBcd(tv->tv_usec / MICROS_IN_HUNDREDTH)); + buff[1] = CHECK(decToBcd(calendar.tm_sec)); + buff[2] = CHECK(decToBcd(calendar.tm_min)); + buff[3] = CHECK(decToBcd(calendar.tm_hour)); + buff[4] = CHECK(decToBcd(calendar.tm_mday)); + buff[5] = CHECK(decToBcd(calendar.tm_mon + 1)); // Month in tm structure ranges from 0 - 11. + alarmYear_ = calendar.tm_year - UNIX_TIME_YEAR_BASE; + buff[6] = CHECK(decToBcd(calendar.tm_wday)); + CHECK(writeContinuousRegisters(Am18x5Register::HUNDREDTHS_ALARM, buff, sizeof(buff))); + return SYSTEM_ERROR_NONE; } @@ -202,10 +224,36 @@ int Am18x5::enableAlarm(bool enable, Am18x5::AlarmHandler handler, void* context CHECK_TRUE(initialized_, SYSTEM_ERROR_INVALID_STATE); alarmHandler_ = handler; alarmHandlerContext_ = context; + CHECK(writeRegister(Am18x5Register::TIMER_CONTROL, 1, false, true, TIMER_CONTROL_RPT_MASK, TIMER_CONTROL_RPT_SHIFT)); return writeRegister(Am18x5Register::INT_MASK, enable, false, true, INTERRUPT_AIE_MASK, INTERRUPT_AIE_SHIFT); } +int Am18x5::getAlarm(struct timeval* tv) const { + if (tv) { + uint8_t buff[7] = {}; + CHECK(readContinuousRegisters(Am18x5Register::HUNDREDTHS_ALARM, buff, sizeof(buff))); + + struct tm calendar = {}; + calendar.tm_sec = CHECK(bcdToDec(buff[1])); + calendar.tm_min = CHECK(bcdToDec(buff[2])); + calendar.tm_hour = CHECK(bcdToDec(buff[3])); + calendar.tm_mday = CHECK(bcdToDec(buff[4])); + calendar.tm_mon = CHECK(bcdToDec(buff[5])); + calendar.tm_mon -= 1; + // NOTE: alarmYear_ needs to be valid + calendar.tm_year = alarmYear_ + UNIX_TIME_YEAR_BASE; + calendar.tm_wday = CHECK(bcdToDec(buff[6])); + calendar.tm_isdst = -1; + + tv->tv_sec = mktime(&calendar); + tv->tv_usec = (long)buff[0] * MICROS_IN_HUNDREDTH; + } + uint8_t alm; + CHECK(readRegister(Am18x5Register::STATUS, &alm, false, STATUS_ALM_MASK)); + return (int)alm; +} + int Am18x5::enableWatchdog(uint8_t value, Am18x5WatchdogFrequency frequency) const { Am18x5Lock lock(); CHECK_TRUE(initialized_, SYSTEM_ERROR_INVALID_STATE); @@ -557,7 +605,6 @@ os_thread_return_t Am18x5::exRtcInterruptHandleThread(void* param) { os_thread_exit(instance->exRtcWorkerThread_); } -constexpr uint16_t Am18x5::UNIX_TIME_YEAR_BASE; constexpr uint16_t Am18x5::PART_NUMBER; #endif // HAL_PLATFORM_EXTERNAL_RTC diff --git a/hal/src/tracker/am18x5.h b/hal/src/tracker/am18x5.h index 2b6c11194b..2d89b0aa67 100644 --- a/hal/src/tracker/am18x5.h +++ b/hal/src/tracker/am18x5.h @@ -131,10 +131,11 @@ class Am18x5 { int end(); int sync(); - int setCalendar(const struct tm* calendar) const; - int getCalendar(struct tm* calendar) const; + int setTime(const struct timeval* tv) const; + int getTime(struct timeval* tv) const; - int setAlarm(const struct tm* calendar); + int setAlarm(const struct timeval* tv); + int getAlarm(struct timeval* tv) const; int enableAlarm(bool enable, AlarmHandler handler, void* context); int enableWatchdog(uint8_t value, Am18x5WatchdogFrequency frequency) const; @@ -202,7 +203,6 @@ class Am18x5 { int readContinuousRegisters(const Am18x5Register start_reg, uint8_t* buff, size_t len) const; static os_thread_return_t exRtcInterruptHandleThread(void* param); - static constexpr uint16_t UNIX_TIME_YEAR_BASE = 118; // 2018 - 1900 static constexpr uint16_t PART_NUMBER = 0x1805; bool initialized_; diff --git a/modules/electron/system-part1/src/time_compat.cpp b/modules/electron/system-part1/src/time_compat.cpp new file mode 100644 index 0000000000..51dff10208 --- /dev/null +++ b/modules/electron/system-part1/src/time_compat.cpp @@ -0,0 +1,2 @@ +// ! +#include "../shared/time_compat.cpp" diff --git a/modules/electron/system-part3/makefile b/modules/electron/system-part3/makefile index 6610c9549e..155034e204 100644 --- a/modules/electron/system-part3/makefile +++ b/modules/electron/system-part3/makefile @@ -11,7 +11,7 @@ LIB_DEPENDENCIES = services-dynalib system wiring crypto-dynalib crypto communic MAKE_DEPENDENCIES = newlib_nano $(LIB_DEPENDENCIES) # exclude hal_cellular and hal_usb -GLOBAL_DEFINES += HAL_CELLULAR_EXCLUDE=1 HAL_USB_EXCLUDE=1 HAL_BOOTLOADER_EXCLUDE=1 MODULE_HAS_SYSTEM_PART3=1 +GLOBAL_DEFINES += HAL_CELLULAR_EXCLUDE=1 HAL_USB_EXCLUDE=1 HAL_BOOTLOADER_EXCLUDE=1 MODULE_HAS_SYSTEM_PART3=1 HAL_TIME_COMPAT_EXCLUDE=1 include ../modular.mk diff --git a/services/inc/services2_dynalib.h b/services/inc/services2_dynalib.h index bb33b4763b..1f6d60e64e 100644 --- a/services/inc/services2_dynalib.h +++ b/services/inc/services2_dynalib.h @@ -8,6 +8,7 @@ #include "printf_export.h" #include #include +#include "time_compat.h" #ifdef __cplusplus extern "C" { @@ -54,10 +55,12 @@ DYNALIB_FN(14, services2, pb_encode_varint, bool(pb_ostream_t*, pb_uint64_t)) DYNALIB_FN(15, services2, _printf_float, int(struct _reent*, struct _prt_data_t*, FILE*, int(*pfunc)(struct _reent* , FILE*, const char*, size_t), va_list*)) DYNALIB_FN(16, services2, _tzset_unlocked_r, void(struct _reent*)) DYNALIB_FN(17, services2, __udivmoddi4, unsigned long(unsigned long, unsigned long, unsigned long*)) -DYNALIB_FN(18, services2, mktime, time_t(struct tm*)) +DYNALIB_FN(18, services2, mktime32, time32_t(struct tm*)) DYNALIB_FN(19, services2, __ssvfscanf_r, int(struct _reent*, FILE*, const char*, va_list)) DYNALIB_FN(20, services2, _printf_i, int(struct _reent*, struct _prt_data_t*, FILE*, int (*pfunc)(struct _reent *, FILE *, const char *, size_t), va_list*)) -DYNALIB_FN(21, services2, localtime_r, struct tm*(const time_t*, struct tm*)) +DYNALIB_FN(21, services2, localtime32_r, struct tm*(const time32_t*, struct tm*)) +DYNALIB_FN(22, services2, localtime_r, struct tm*(const time_t*, struct tm*)) +DYNALIB_FN(23, services2, mktime, time_t(struct tm*)) DYNALIB_END(services2) diff --git a/system/inc/system_cloud.h b/system/inc/system_cloud.h index d30ceb4b79..d68e244ff8 100644 --- a/system/inc/system_cloud.h +++ b/system/inc/system_cloud.h @@ -30,6 +30,7 @@ #include #include #include +#include "time_compat.h" #define DEFAULT_CLOUD_EVENT_TTL 60 @@ -282,7 +283,7 @@ bool spark_subscribe(const char *eventName, EventHandler handler, void* handler_ void spark_unsubscribe(void *reserved); bool spark_sync_time(void *reserved); bool spark_sync_time_pending(void* reserved); -system_tick_t spark_sync_time_last(time_t* tm, void* reserved); +system_tick_t spark_sync_time_last(time32_t* tm32, time_t* tm); void spark_process(void); diff --git a/system/inc/system_dynalib_cloud.h b/system/inc/system_dynalib_cloud.h index ee1e0384d3..aeafb89f01 100644 --- a/system/inc/system_dynalib_cloud.h +++ b/system/inc/system_dynalib_cloud.h @@ -26,6 +26,7 @@ #include "dynalib.h" #include "system_tick_hal.h" +#include "time_compat.h" #ifdef DYNALIB_EXPORT #include "system_cloud.h" @@ -47,7 +48,7 @@ DYNALIB_FN(9, system_cloud, spark_subscribe, bool(const char*, EventHandler, voi DYNALIB_FN(10, system_cloud, spark_unsubscribe, void(void*)) DYNALIB_FN(11, system_cloud, spark_sync_time, bool(void*)) DYNALIB_FN(12, system_cloud, spark_sync_time_pending, bool(void*)) -DYNALIB_FN(13, system_cloud, spark_sync_time_last, system_tick_t(time_t*, void*)) +DYNALIB_FN(13, system_cloud, spark_sync_time_last, system_tick_t(time32_t*, time_t*)) DYNALIB_FN(14, system_cloud, spark_set_connection_property, int(unsigned, unsigned, const void*, void*)) DYNALIB_FN(15, system_cloud, spark_set_random_seed_from_cloud_handler, int(void (*handler)(unsigned int), void*)) DYNALIB_FN(16, system_cloud, spark_publish_vitals, int(system_tick_t, void*)) diff --git a/system/src/system_cloud.cpp b/system/src/system_cloud.cpp index 9fafed163f..ae6cd6c33a 100644 --- a/system/src/system_cloud.cpp +++ b/system/src/system_cloud.cpp @@ -146,10 +146,10 @@ bool spark_sync_time_pending(void* reserved) return spark_protocol_time_request_pending(sp, nullptr); } -system_tick_t spark_sync_time_last(time_t* tm, void* reserved) +system_tick_t spark_sync_time_last(time32_t* tm32, time_t* tm) { - SYSTEM_THREAD_CONTEXT_SYNC(spark_sync_time_last(tm, reserved)); - return spark_protocol_time_last_synced(sp, tm, nullptr); + SYSTEM_THREAD_CONTEXT_SYNC(spark_sync_time_last(tm32, tm)); + return spark_protocol_time_last_synced(sp, tm32, tm); } /** diff --git a/system/src/system_cloud_internal.cpp b/system/src/system_cloud_internal.cpp index 7a6628a57f..efa1ad6870 100644 --- a/system/src/system_cloud_internal.cpp +++ b/system/src/system_cloud_internal.cpp @@ -556,9 +556,13 @@ uint32_t compute_cloud_state_checksum(SparkAppStateSelector::Enum stateSelector, } #endif /* HAL_PLATFORM_CLOUD_UDP */ -void system_set_time(time_t time, unsigned param, void*) +void system_set_time(uint32_t time, unsigned param, void*) { - HAL_RTC_Set_UnixTime(time); + struct timeval tv = { + .tv_sec = (time_t)time, + .tv_usec = 0 + }; + hal_rtc_set_time(&tv, nullptr); system_notify_event(time_changed, time_changed_sync); } @@ -1149,7 +1153,7 @@ int Spark_Handshake(bool presence_announce) publishSafeModeEventIfNeeded(); Send_Firmware_Update_Flags(); - if (!HAL_RTC_Time_Is_Valid(nullptr) && spark_sync_time_last(nullptr, nullptr) == 0) { + if (!hal_rtc_time_is_valid(nullptr) && spark_sync_time_last(nullptr, nullptr) == 0) { spark_protocol_send_time_request(sp); } } diff --git a/system/src/system_cloud_internal.h b/system/src/system_cloud_internal.h index 5d3272a768..fb262a8b04 100644 --- a/system/src/system_cloud_internal.h +++ b/system/src/system_cloud_internal.h @@ -39,7 +39,7 @@ int Spark_Handshake(bool presence_announce); bool Spark_Communication_Loop(void); void Spark_Process_Events(); -void system_set_time(time_t time, unsigned param, void* reserved); +void system_set_time(uint32_t time, unsigned param, void* reserved); String bytes2hex(const uint8_t* buf, unsigned len); diff --git a/system/src/system_sleep_compat.cpp b/system/src/system_sleep_compat.cpp index a7ec3ffd19..0d1f1c05db 100644 --- a/system/src/system_sleep_compat.cpp +++ b/system/src/system_sleep_compat.cpp @@ -73,7 +73,7 @@ static void network_resume() { * Output : None. * Return : None. *******************************************************************************/ -extern "C" void HAL_RTCAlarm_Handler(void) +void system_sleep_rtc_alarm_handler(void* context) { /* Wake up from System.sleep mode(SLEEP_MODE_WLAN) */ network_resume(); @@ -167,7 +167,15 @@ int system_sleep_impl(Spark_Sleep_TypeDef sleepMode, long seconds, uint32_t para case SLEEP_MODE_WLAN: if (seconds) { - HAL_RTC_Set_UnixAlarm((time_t) seconds); + struct timeval tv = { + .tv_sec = seconds, + .tv_usec = 0 + }; + int r = hal_rtc_set_alarm(&tv, HAL_RTC_ALARM_FLAG_IN, system_sleep_rtc_alarm_handler, nullptr, nullptr); + if (r) { + system_sleep_rtc_alarm_handler(nullptr); + return r; + } } break; diff --git a/user/tests/unit/makefile b/user/tests/unit/makefile index ba1a3bd6f3..b4adecbb01 100644 --- a/user/tests/unit/makefile +++ b/user/tests/unit/makefile @@ -73,6 +73,7 @@ CPPSRC += $(call target_files,$(SYSTEM)src/,control_request_handler.cpp) CPPSRC += $(call target_files,$(HAL)src/gcc,filesystem.cpp) CPPSRC += $(call target_files,$(HAL)src/gcc,device_config.cpp) CPPSRC += $(call target_files,$(HAL)src/gcc,core_hal.cpp) +CPPSRC += $(call target_files,$(HAL)src/gcc,rtc_hal.cpp) CPPSRC += $(call target_files,$(HAL)src/gcc,timer_hal.cpp) CPPSRC += $(call target_files,$(HAL)src/gcc,rgbled_hal.cpp) CPPSRC += $(call target_files,$(HAL)src/gcc,wlan_hal.cpp) diff --git a/user/tests/wiring/no_fixture/time.cpp b/user/tests/wiring/no_fixture/time.cpp index 86d6944d60..654cc64813 100644 --- a/user/tests/wiring/no_fixture/time.cpp +++ b/user/tests/wiring/no_fixture/time.cpp @@ -25,6 +25,7 @@ #include "application.h" #include "unit-test/unit-test.h" +#include "rtc_hal.h" test(TIME_01_NowReturnsCorrectUnixTime) { // when @@ -266,3 +267,79 @@ test(TIME_16_TimeChangedEvent) { assertMore(Particle.timeSyncedLast(), syncedLastMillis); assertEqual(s_time_changed_reason, (int)time_changed_sync); } + +test(TIME_17_RtcAlarmFiresCorrectly) { + if (Particle.syncTimePending()) { + waitFor(Particle.syncTimeDone, 60000); + assertTrue(Particle.syncTimeDone()); + assertTrue(Time.isValid()); + } + + // Absolute time + struct timeval now; + assertEqual(0, hal_rtc_get_time(&now, nullptr)); + now.tv_sec += 5; + static volatile bool alarmFired = false; + auto ms = millis(); + int r = hal_rtc_set_alarm(&now, 0, [](void* ctx) -> void { + volatile bool* alarmFired = (volatile bool*)ctx; + *alarmFired = true; + }, (void*)&alarmFired, nullptr); + assertEqual(r, 0); + while (!alarmFired && (millis() - ms) <= 6000) { + delay(1); + } + assertLessOrEqual(millis() - ms, 6000); + hal_rtc_cancel_alarm(); + assertTrue((bool)alarmFired); + + // In some amount of time + alarmFired = false; + now.tv_sec = 5; + now.tv_usec = 0; + ms = millis(); + r = hal_rtc_set_alarm(&now, HAL_RTC_ALARM_FLAG_IN, [](void* ctx) -> void { + volatile bool* alarmFired = (volatile bool*)ctx; + *alarmFired = true; + }, (void*)&alarmFired, nullptr); + assertEqual(r, 0); + while (!alarmFired && (millis() - ms) <= 6000) { + delay(1); + } + assertLessOrEqual(millis() - ms, 6000); + hal_rtc_cancel_alarm(); + assertTrue((bool)alarmFired); +} + +test(TIME_18_RtcAlarmReturnsAnErrorWhenTimeInThePast) { + if (Particle.syncTimePending()) { + waitFor(Particle.syncTimeDone, 60000); + assertTrue(Particle.syncTimeDone()); + assertTrue(Time.isValid()); + } + + // Absolute time + struct timeval now; + assertEqual(0, hal_rtc_get_time(&now, nullptr)); + now.tv_sec -= 60; + static volatile bool alarmFired = false; + int r = hal_rtc_set_alarm(&now, 0, [](void* ctx) -> void { + volatile bool* alarmFired = (volatile bool*)ctx; + *alarmFired = true; + }, (void*)&alarmFired, nullptr); + hal_rtc_cancel_alarm(); + assertFalse((bool)alarmFired); + assertEqual(r, (int)SYSTEM_ERROR_TIMEOUT); + + // In some amount of time + alarmFired = false; + now.tv_sec = -5; + now.tv_usec = 0; + r = hal_rtc_set_alarm(&now, HAL_RTC_ALARM_FLAG_IN, [](void* ctx) -> void { + volatile bool* alarmFired = (volatile bool*)ctx; + *alarmFired = true; + }, (void*)&alarmFired, nullptr); + hal_rtc_cancel_alarm(); + assertFalse((bool)alarmFired); + assertEqual(r, (int)SYSTEM_ERROR_TIMEOUT); +} diff --git a/user/tests/wiring/time_compat/application.cpp b/user/tests/wiring/time_compat/application.cpp new file mode 100644 index 0000000000..7199a89dc4 --- /dev/null +++ b/user/tests/wiring/time_compat/application.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "application.h" +#include "unit-test/unit-test.h" + +SYSTEM_MODE(AUTOMATIC); + +UNIT_TEST_APP(); + +// Enable threading if compiled with "USE_THREADING=y" +#if PLATFORM_THREADING == 1 && USE_THREADING == 1 +SYSTEM_THREAD(ENABLED); +#endif diff --git a/user/tests/wiring/time_compat/import_services2.c b/user/tests/wiring/time_compat/import_services2.c new file mode 100644 index 0000000000..4cffc58dcd --- /dev/null +++ b/user/tests/wiring/time_compat/import_services2.c @@ -0,0 +1,26 @@ + +/* + * Copyright (c) 2020 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "platforms.h" + +// Normally this dynalib is not imported for user part +// We are doing this specifically for the purposes of testing +#if PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION +#define DYNALIB_IMPORT +#include "services2_dynalib.h" +#endif // PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION diff --git a/user/tests/wiring/time_compat/test.mk b/user/tests/wiring/time_compat/test.mk new file mode 100644 index 0000000000..b680973e45 --- /dev/null +++ b/user/tests/wiring/time_compat/test.mk @@ -0,0 +1,18 @@ +INCLUDE_DIRS += $(SOURCE_PATH)/$(USRSRC) # add user sources to include path +# add C and CPP files - if USRSRC is not empty, then add a slash +CPPSRC += $(call target_files,$(USRSRC_SLASH),*.cpp) +CSRC += $(call target_files,$(USRSRC_SLASH),*.c) + +APPSOURCES=$(call target_files,$(USRSRC_SLASH),*.cpp) +APPSOURCES+=$(call target_files,$(USRSRC_SLASH),*.c) +ifeq ($(strip $(APPSOURCES)),) +$(error "No sources found in $(SOURCE_PATH)/$(USRSRC)") +endif + +ifeq ("${USE_THREADING}","y") +USE_THREADING_VALUE=1 +else +USE_THREADING_VALUE=0 +endif + +CFLAGS += -DUSE_THREADING=${USE_THREADING_VALUE} diff --git a/user/tests/wiring/time_compat/time_compat.cpp b/user/tests/wiring/time_compat/time_compat.cpp new file mode 100644 index 0000000000..6a8a07198a --- /dev/null +++ b/user/tests/wiring/time_compat/time_compat.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2019 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "application.h" +#include "unit-test/unit-test.h" +#include "socket_hal.h" +#include "rtc_hal.h" +#include "scope_guard.h" + +namespace { + +bool tmEqual(const struct tm& lh, const struct tm& rh) { + return lh.tm_sec == rh.tm_sec && + lh.tm_min == rh.tm_min && + lh.tm_hour == rh.tm_hour && + lh.tm_mday == rh.tm_mday && + lh.tm_mon == rh.tm_mon && + lh.tm_year == rh.tm_year && + lh.tm_wday == rh.tm_wday && + lh.tm_yday == rh.tm_yday && + lh.tm_isdst == rh.tm_isdst; +} + +#if PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION +// Emulates localtime behavior on older versions of toolchain +struct tm* localtime32(const time32_t* tim_p) { + // _impure_ptr is module/part-specific right now + // In order not to allocate unnecessary stuff, as a hack + // we will here temporarily use what localtime() returned us + time_t tim = static_cast(*tim_p); + auto localTm = localtime(&tim); + + return localtime32_r(tim_p, localTm); +} +#endif // PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION + +} // anonymous + +Serial1LogHandler dbg(460800, LOG_LEVEL_ALL); + +STARTUP({ + Log.trace("Current time: %s", Time.timeStr().c_str()); +}); + +test(TIME_COMPAT_00_TimeIsValid) { + waitFor(Particle.connected, 120000); + assertTrue(Particle.connected()); + if (Particle.syncTimePending()) { + waitFor(Particle.syncTimeDone, 120000); + assertTrue(Particle.syncTimeDone()); + } + assertTrue(Time.isValid()); +} + +test(TIME_COMPAT_01_DynalibNewlib32BitTimeTFunctionsWorkCorrectly) { + // This test only applies to Electron where localtime_r and mktime are exported in + // services2 dynalib +#if PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION + time_t refTime = 1514764800; // 2018/01/01 00:00:00 + time32_t refTime32 = (time32_t)refTime; + uint32_t garbage = 0xffffffff; + (void)garbage; + struct tm tm = {}; + struct tm tm32 = {}; + + assertEqual(refTime, static_cast(refTime32)); + + // localtime_r vs localtime32_r + assertTrue(localtime_r(&refTime, &tm) == &tm); + assertTrue(localtime32_r(&refTime32, &tm32) == &tm32); + assertTrue(tmEqual(tm, tm32)); + + // localtime vs localtime32 + auto localTm = localtime(&refTime); + assertTrue(localTm != nullptr); + memcpy(&tm, localTm, sizeof(struct tm)); + auto localTm32 = localtime32(&refTime32); + assertTrue(localTm32 != nullptr); + memcpy(&tm32, localTm32, sizeof(struct tm)); + assertTrue(tmEqual(tm, tm32)); + + auto mkTimeResult = mktime(&tm); + auto mkTimeResult32 = mktime32(&tm32); + assertEqual(mkTimeResult, static_cast(mkTimeResult32)); +#endif // PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION +} + +#if HAL_USE_SOCKET_HAL_POSIX +test(TIME_COMPAT_02_SocketSelect) { + int s = sock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + SCOPE_GUARD({ + if (s >= 0) { + sock_close(s); + } + }); + assertMoreOrEqual(s, 0); + + struct sockaddr_in sin = {}; + sin.sin_family = AF_INET; + sin.sin_port = 0x1122; + + assertEqual(0, sock_bind(s, (const sockaddr*)&sin, sizeof(sin))); + + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(s, &readfds); + struct timeval tv = { + .tv_sec = 2, + .tv_usec = 0 + }; + + auto ms = millis(); + assertEqual(0, sock_select(s + 1, &readfds, nullptr, nullptr, &tv)); + assertMoreOrEqual(millis() - ms, 2000); + + struct timeval32 tv32 = { + .tv_sec = 2, + .tv_usec = 0 + }; + uint32_t garbage = 0xffffffff; + (void)garbage; + ms = millis(); + assertEqual(0, sock_select32(s + 1, &readfds, nullptr, nullptr, &tv32)); + assertMoreOrEqual(millis() - ms, 2000); +} + +test(TIME_COMPAT_03_SocketSetGetSockOptRcvTimeo) { + int s = sock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + SCOPE_GUARD({ + if (s >= 0) { + sock_close(s); + } + }); + assertMoreOrEqual(s, 0); + + struct sockaddr_in sin = {}; + sin.sin_family = AF_INET; + sin.sin_port = 0x1122; + + assertEqual(0, sock_bind(s, (const sockaddr*)&sin, sizeof(sin))); + + struct timeval tv = { + .tv_sec = 0xbad, + .tv_usec = 0 + }; + assertEqual(0, sock_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))); + + socklen_t size = sizeof(tv); + memset(&tv, 0, sizeof(tv)); + assertEqual(0, sock_getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, &size)); + assertEqual(size, sizeof(tv)); + assertEqual(tv.tv_sec, 0xbad); + assertEqual(tv.tv_usec, 0); + + struct timeval32 tv32 = {}; + uint32_t garbage = 0xffffffff; + (void)garbage; + size = sizeof(tv32); + assertEqual(0, sock_getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv32, &size)); + assertEqual(size, sizeof(tv32)); + assertEqual(tv32.tv_sec, 0xbad); + assertEqual(tv32.tv_usec, 0); + assertEqual(garbage, 0xffffffff); + + tv32.tv_sec = 0xbeef; + tv32.tv_usec = 0; + assertEqual(0, sock_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv32, sizeof(tv32))); + + size = sizeof(tv32); + memset(&tv32, 0, sizeof(tv32)); + assertEqual(0, sock_getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv32, &size)); + assertEqual(size, sizeof(tv32)); + assertEqual(tv32.tv_sec, 0xbeef); + assertEqual(tv32.tv_usec, 0); + assertEqual(garbage, 0xffffffff); +} + +test(TIME_COMPAT_03_SocketSetGetSockOptSndTimeo) { + int s = sock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + SCOPE_GUARD({ + if (s >= 0) { + sock_close(s); + } + }); + assertMoreOrEqual(s, 0); + + struct timeval tv = { + .tv_sec = 0xbad, + .tv_usec = 0 + }; + assertEqual(0, sock_setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))); + + socklen_t size = sizeof(tv); + memset(&tv, 0, sizeof(tv)); + assertEqual(0, sock_getsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, &size)); + assertEqual(size, sizeof(tv)); + assertEqual(tv.tv_sec, 0xbad); + assertEqual(tv.tv_usec, 0); + + struct timeval32 tv32 = {}; + uint32_t garbage = 0xffffffff; + (void)garbage; + size = sizeof(tv32); + assertEqual(0, sock_getsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv32, &size)); + assertEqual(size, sizeof(tv32)); + assertEqual(tv32.tv_sec, 0xbad); + assertEqual(tv32.tv_usec, 0); + assertEqual(garbage, 0xffffffff); + + tv32.tv_sec = 0xbeef; + tv32.tv_usec = 0; + assertEqual(0, sock_setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv32, sizeof(tv32))); + + size = sizeof(tv32); + memset(&tv32, 0, sizeof(tv32)); + assertEqual(0, sock_getsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv32, &size)); + assertEqual(size, sizeof(tv32)); + assertEqual(tv32.tv_sec, 0xbeef); + assertEqual(tv32.tv_usec, 0); + assertEqual(garbage, 0xffffffff); +} + +#endif // HAL_USE_SOCKET_HAL_POSIX + +test(TIME_COMPAT_04_RtcHal) { + time_t refTime = 1577836800; // 2020-01-01 00:00:00 + + struct timeval tv = { + .tv_sec = refTime, + .tv_usec = 0 + }; + assertEqual(0, hal_rtc_set_time(&tv, nullptr)); + memset(&tv, 0, sizeof(tv)); + assertEqual(0, hal_rtc_get_time(&tv, nullptr)); + time32_t t = hal_rtc_get_unixtime_deprecated(); + + assertMoreOrEqual(tv.tv_sec, refTime); + assertLessOrEqual(tv.tv_sec - refTime, 1); + assertMoreOrEqual(static_cast(t), tv.tv_sec); + assertLessOrEqual(static_cast(t) - tv.tv_sec, 1); + + hal_rtc_set_unixtime_deprecated(static_cast(refTime)); + memset(&tv, 0, sizeof(tv)); + assertEqual(0, hal_rtc_get_time(&tv, nullptr)); + t = hal_rtc_get_unixtime_deprecated(); + + assertMoreOrEqual(tv.tv_sec, refTime); + assertLessOrEqual(tv.tv_sec - refTime, 1); + assertMoreOrEqual(static_cast(t), tv.tv_sec); + assertLessOrEqual(static_cast(t) - tv.tv_sec, 1); +} + +test(TIME_COMPAT_05_SyncTime) { + time_t refTime = 1546300800; // 2019-01-01 00:00:00 + + struct timeval tv = { + .tv_sec = refTime, + .tv_usec = 0 + }; + assertEqual(0, hal_rtc_set_time(&tv, nullptr)); + memset(&tv, 0, sizeof(tv)); + assertEqual(0, hal_rtc_get_time(&tv, nullptr)); + assertMoreOrEqual(tv.tv_sec, refTime); + assertLessOrEqual(tv.tv_sec - refTime, 1); + + auto ms = millis(); + Particle.syncTime(); + waitFor(Particle.syncTimeDone, 120000); + assertTrue(Particle.syncTimeDone()); + + assertTrue(Time.isValid()); + + time32_t cloudTime32; + time_t cloudTime; + auto synced = spark_sync_time_last(&cloudTime32, &cloudTime); + assertMoreOrEqual(synced, ms); + assertEqual(static_cast(cloudTime32), cloudTime); + + memset(&tv, 0, sizeof(tv)); + assertEqual(0, hal_rtc_get_time(&tv, nullptr)); + time32_t t = hal_rtc_get_unixtime_deprecated(); + assertMoreOrEqual(tv.tv_sec, cloudTime); + assertMoreOrEqual(t, cloudTime32); +} diff --git a/wiring/inc/spark_wiring_cloud.h b/wiring/inc/spark_wiring_cloud.h index 53f32e6868..bfc0667fba 100644 --- a/wiring/inc/spark_wiring_cloud.h +++ b/wiring/inc/spark_wiring_cloud.h @@ -361,7 +361,7 @@ class CloudClass { system_tick_t timeSyncedLast(time_t& tm) { tm = 0; - return spark_sync_time_last(&tm, nullptr); + return spark_sync_time_last(nullptr, &tm); } static void sleep(long seconds) __attribute__ ((deprecated("Please use System.sleep() instead."))) diff --git a/wiring/src/spark_wiring_time.cpp b/wiring/src/spark_wiring_time.cpp index 5499910175..52ef597ff7 100644 --- a/wiring/src/spark_wiring_time.cpp +++ b/wiring/src/spark_wiring_time.cpp @@ -58,48 +58,16 @@ time_t dst_current_cache = 0; // a cache of the DST offset currently being app /* Time utility functions */ static struct tm Convert_UnixTime_To_CalendarTime(time_t unix_time); -//static time_t Convert_CalendarTime_To_UnixTime(struct tm calendar_time); -//static struct tm Get_CalendarTime(void); -//static void Set_CalendarTime(struct tm t); static void Refresh_UnixTime_Cache(time_t unix_time); /* Convert Unix/RTC time to Calendar time */ static struct tm Convert_UnixTime_To_CalendarTime(time_t unix_time) { - struct tm *calendar_time; - calendar_time = localtime(&unix_time); - calendar_time->tm_year += 1900; - return *calendar_time; -} - -/* Convert Calendar time to Unix/RTC time */ -/* -static time_t Convert_CalendarTime_To_UnixTime(struct tm calendar_time) -{ - calendar_time.tm_year -= 1900; - time_t unix_time = mktime(&calendar_time); - return unix_time; -} -*/ - -/* Get converted Calendar time */ -/* - static struct tm Get_CalendarTime(void) -{ - time_t unix_time = HAL_RTC_Get_UnixTime(); - unix_time += time_zone_cache; - struct tm calendar_time = Convert_UnixTime_To_CalendarTime(unix_time); + struct tm calendar_time; + localtime_r(&unix_time, &calendar_time); + calendar_time.tm_year += 1900; return calendar_time; } - */ - -/* Set Calendar time as Unix/RTC time */ -/* -static void Set_CalendarTime(struct tm calendar_time) -{ - HAL_RTC_Set_UnixTime(Convert_CalendarTime_To_UnixTime(calendar_time)); -} -*/ /* Refresh Unix/RTC time cache */ static void Refresh_UnixTime_Cache(time_t unix_time) @@ -252,12 +220,14 @@ int TimeClass::year(time_t t) time_t TimeClass::now() { (void)isValid(); - return HAL_RTC_Get_UnixTime(); + struct timeval tv = {}; + hal_rtc_get_time(&tv, nullptr); + return tv.tv_sec; } time_t TimeClass::local() { - return HAL_RTC_Get_UnixTime()+time_zone_cache+dst_current_cache; + return now() + time_zone_cache + dst_current_cache; } /* set the time zone (+/-) offset from GMT */ @@ -307,34 +277,42 @@ uint8_t TimeClass::isDST() /* set the given time as unix/rtc time */ void TimeClass::setTime(time_t t) { - HAL_RTC_Set_UnixTime(t); - system_notify_time_changed((uint32_t)time_changed_manually, nullptr, nullptr); + struct timeval tv = { + .tv_sec = t, + .tv_usec = 0 + }; + if (!hal_rtc_set_time(&tv, nullptr)) { + system_notify_time_changed((uint32_t)time_changed_manually, nullptr, nullptr); + } } /* return string representation for the given time */ String TimeClass::timeStr(time_t t) { - t += time_zone_cache; + t += time_zone_cache; t += dst_current_cache; - tm* calendar_time = localtime(&t); - char* ascstr = asctime(calendar_time); - int len = strlen(ascstr); - ascstr[len-1] = 0; // remove final newline - return String(ascstr); + struct tm calendar_time = {}; + localtime_r(&t, &calendar_time); + char ascstr[26] = {}; + asctime_r(&calendar_time, ascstr); + int len = strlen(ascstr); + ascstr[len-1] = 0; // remove final newline + return String(ascstr); } String TimeClass::format(time_t t, const char* format_spec) { - if (format_spec==NULL) + if (format_spec == nullptr) format_spec = this->format_spec; - if (!format_spec || !strcmp(format_spec,TIME_FORMAT_DEFAULT)) { + if (!format_spec || !strcmp(format_spec, TIME_FORMAT_DEFAULT)) { return timeStr(t); } t += time_zone_cache; t += dst_current_cache; - tm* calendar_time = localtime(&t); - return timeFormatImpl(calendar_time, format_spec, time_zone_cache + dst_current_cache); + struct tm calendar_time = {}; + localtime_r(&t, &calendar_time); + return timeFormatImpl(&calendar_time, format_spec, time_zone_cache + dst_current_cache); } String TimeClass::timeFormatImpl(tm* calendar_time, const char* format, int time_zone) @@ -366,20 +344,20 @@ String TimeClass::timeFormatImpl(tm* calendar_time, const char* format, int time } } - char buf[50]; - strftime(buf, 50, format_str, calendar_time); + char buf[50] = {}; + strftime(buf, sizeof(buf), format_str, calendar_time); return String(buf); } bool TimeClass::isValid() { - bool rtcstate = HAL_RTC_Time_Is_Valid(nullptr); + bool rtcstate = hal_rtc_time_is_valid(nullptr); if (rtcstate) return rtcstate; if (System.mode() == AUTOMATIC && system_thread_get_state(nullptr) == spark::feature::DISABLED) { waitUntil(Particle.syncTimeDone); - return HAL_RTC_Time_Is_Valid(nullptr); + return hal_rtc_time_is_valid(nullptr); } return rtcstate; }