diff --git a/userspace/include/hal_exports.h b/userspace/include/hal_exports.h index b7f0017d47013d4fa6f2283c0c4b8d507675736e..8d0708e5a2b54ffe9e350b2bb1fa0ea74da70be4 100644 --- a/userspace/include/hal_exports.h +++ b/userspace/include/hal_exports.h @@ -26,6 +26,7 @@ #define HEXP_LOCK_CMD_START 1 #define HEXP_LOCK_CMD_CHECK 2 #define HEXP_LOCK_CMD_ENABLE_TRACKING 3 +#define HEXP_LOCK_CMD_RESET 4 #define HEXP_LOCK_STATUS_LOCKED 0 #define HEXP_LOCK_STATUS_BUSY 1 diff --git a/userspace/libwr/include/libwr/hal_shmem.h b/userspace/libwr/include/libwr/hal_shmem.h index d96a97f94be1ab1604236fe43583cfe9ce1dec2f..273c66147f7e0a41674e6b8ecf3064681d1f1bac 100644 --- a/userspace/libwr/include/libwr/hal_shmem.h +++ b/userspace/libwr/include/libwr/hal_shmem.h @@ -11,6 +11,7 @@ #define HAL_PORT_STATE_UP 2 #define HAL_PORT_STATE_CALIBRATION 3 #define HAL_PORT_STATE_LOCKING 4 +#define HAL_PORT_STATE_RESET 5 /* Read temperature from SFPs */ #define READ_SFP_DIAG_ENABLE 1 diff --git a/userspace/ppsi b/userspace/ppsi index cb5934e8dac07c21572d335a5691ea714eeebf57..b2f4280d7386dd936e0b62712955d6e5e31d2e94 160000 --- a/userspace/ppsi +++ b/userspace/ppsi @@ -1 +1 @@ -Subproject commit cb5934e8dac07c21572d335a5691ea714eeebf57 +Subproject commit b2f4280d7386dd936e0b62712955d6e5e31d2e94 diff --git a/userspace/tools/Makefile b/userspace/tools/Makefile index 4677ff11b08bf0482fbc3ecbafefd08ee04f509a..9721453dc4eebc4e14f6a0406fa10c03e7a37a37 100644 --- a/userspace/tools/Makefile +++ b/userspace/tools/Makefile @@ -10,6 +10,7 @@ TOOLS += wrs_status_led TOOLS += mkpasswd TOOLS += wrs_sfp_dump TOOLS += wrs_throttling +TOOLS += utc_leap_test PPSI_CONFIG = ../ppsi/include/generated/autoconf.h WR_INSTALL_ROOT ?= /usr/lib/white-rabbit diff --git a/userspace/tools/utc_leap_test.c b/userspace/tools/utc_leap_test.c new file mode 100644 index 0000000000000000000000000000000000000000..bc7f1b739a22401e4a51d108d00f8a3e46d63d44 --- /dev/null +++ b/userspace/tools/utc_leap_test.c @@ -0,0 +1,279 @@ +/* This is just a subset of wr_date, user to test on the host */ +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> +#include <fcntl.h> +#include <time.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/timex.h> + +#ifndef MOD_TAI +#define MOD_TAI 0x80 +#endif + +int opt_verbose = 1; +char *prgname; + +static void unix_time_clear_utc_flags(void) +{ + struct timex t; + + /* + * We have to call adjtime twice here, as kernels + * prior to 6b1859dba01c7 (included in 3.5 and + * -stable), had an issue with the state machine + * and wouldn't clear the STA_INS/DEL flag directly. + */ + t.modes = ADJ_STATUS; + t.status = STA_PLL; + adjtimex(&t); + + /* Clear maxerror, as it can cause UNSYNC to be set */ + t.modes = ADJ_MAXERROR; + t.maxerror = 0; + adjtimex(&t); + + /* Clear the status */ + t.modes = ADJ_STATUS; + t.status = 0; + adjtimex(&t); + +} + +static int unix_time_get_utc_time(int *hours, int *minutes, int *seconds) +{ + int ret; + struct timex t; + time_t now; + struct tm *date; + + /* Get the UTC time */ + memset(&t, 0, sizeof(t)); + ret = adjtimex(&t); + if (ret >= 0) { + now = t.time.tv_sec; + /* use gmtime for correct leap handling */ + date = gmtime(&now); + *hours = date->tm_hour; + *minutes = date->tm_min; + *seconds = date->tm_sec; + return 0; + } else { + *hours = 0; + *minutes = 0; + *seconds = 0; + return -1; + } + + return -1; +} + +static int unix_time_get_utc_offset(int *offset, int *leap59, int *leap61) +{ + int ret; + struct timex t; + int hours, minutes, seconds; + + unix_time_get_utc_time(&hours, &minutes, &seconds); + + /* + * Get the UTC/TAI difference + */ + memset(&t, 0, sizeof(t)); + ret = adjtimex(&t); + if (ret >= 0) { + if (hours >= 12) { + if ((t.status & STA_INS) == STA_INS) { + *leap59 = 0; + *leap61 = 1; + } else if ((t.status & STA_DEL) == STA_DEL) { + *leap59 = 1; + *leap61 = 0; + } else { + *leap59 = 0; + *leap61 = 0; + } + } else { + unix_time_clear_utc_flags(); + *leap59 = 0; + *leap61 = 0; + } + /* + * Our WRS kernel has tai support, but our compiler does not. + * We are 32-bit only, and we know for sure that tai is + * exactly after stbcnt. It's a bad hack, but it works + */ + *offset = *((int *)(&t.stbcnt) + 1); + return 0; + } else { + *leap59 = 0; + *leap61 = 0; + *offset = 0; + return -1; + } +} + +static int unix_time_set_utc_offset(int offset, int leap59, int leap61) +{ + struct timex t; + int ret; + + unix_time_clear_utc_flags(); + + /* get the current flags first */ + memset(&t, 0, sizeof(t)); + ret = adjtimex(&t); + + if (ret >= 0) { + if (leap59) { + t.modes = MOD_STATUS; + t.status |= STA_DEL; + t.status &= ~STA_INS; + printf("set leap59 flag\n"); + } else if (leap61) { + t.modes = MOD_STATUS; + t.status |= STA_INS; + t.status &= ~STA_DEL; + printf("set leap61 flag\n"); + } else { + t.modes = MOD_STATUS; + t.status &= ~STA_INS; + t.status &= ~STA_DEL; + printf("set no leap flags\n"); + } + + if (adjtimex(&t) < 0) { + printf("set UTC flags failed\n"); + return -1; + } + + } else + printf("get UTC flags failed\n"); + + t.modes = MOD_TAI; + t.constant = offset; + if (adjtimex(&t) < 0) { + printf("set UTC offset failed\n"); + return -1; + } else + printf("set UTC offset to: %i\n", offset); + + + return 0; +} + +int main(int argc, char **argv) +{ + int i, j; + int offset, leap59, leap61; + int hours, minutes, seconds; + struct timespec tp; + + for (j=0; j<3; j++) { + + /* Get the UTC time */ + unix_time_get_utc_time(&hours, &minutes, &seconds); + unix_time_get_utc_offset(&offset, &leap59, &leap61); + printf("current time %d:%d:%d, offset=%d, l59=%d, l61=%d\n", hours, minutes, seconds, offset, leap59, leap61); + + + /* Set time to 30. June 2017 23:59:50 */ + tp.tv_sec = 1498867190; + tp.tv_nsec = 0; + clock_settime(CLOCK_REALTIME, &tp); + + + /* Get the UTC time */ + unix_time_get_utc_time(&hours, &minutes, &seconds); + unix_time_get_utc_offset(&offset, &leap59, &leap61); + printf("new current time %d:%d:%d, offset=%d, l59=%d, l61=%d\n", hours, minutes, seconds, offset, leap59, leap61); + + + if (j == 0) { + /* Set leap61 and UTC offset to 37 */ + offset = 37; + leap59 = 0; + leap61 = 1; + unix_time_set_utc_offset(offset, leap59, leap61); + } else if (j == 1) { + /* Set leap59 and UTC offset to 37 */ + offset = 37; + leap59 = 1; + leap61 = 0; + unix_time_set_utc_offset(offset, leap59, leap61); + } else { + /* Set no leaps and UTC offset to 37 */ + offset = 37; + leap59 = 0; + leap61 = 0; + unix_time_set_utc_offset(offset, leap59, leap61); + } + + if (j == 0) { + if ((hours == 23) && (minutes == 59) && (seconds == 50) && (offset == 37) && (leap59 == 0) && (leap61 == 1)) { + printf("pre leap61 handling correct\n"); + } else { + printf("pre leap61 handling failed\n"); + exit(1); + } + } else if (j == 1) { + if ((hours == 23) && (minutes == 59) && (seconds == 50) && (offset == 37) && (leap59 == 1) && (leap61 == 0)) { + printf("pre leap59 handling correct\n"); + } else { + printf("pre leap59 handling failed\n"); + exit(2); + } + } else { + if ((hours == 23) && (minutes == 59) && (seconds == 50) && (offset == 37) && (leap59 == 0) && (leap61 == 0)) { + printf("pre no-leap handling correct\n"); + } else { + printf("pre no-leap handling failed\n"); + exit(3); + } + } + + for (i=0; i< 20; i++) { + /* Get the UTC time */ + unix_time_get_utc_offset(&offset, &leap59, &leap61); + unix_time_get_utc_time(&hours, &minutes, &seconds); + printf("new current time after offset and leap %d:%d:%d, offset=%d, l59=%d, l61=%d\n", hours, minutes, seconds, offset, leap59, leap61); + sleep(1); + } + + unix_time_get_utc_offset(&offset, &leap59, &leap61); + unix_time_get_utc_time(&hours, &minutes, &seconds); + + if (j == 0) { + if ((hours == 0) && (minutes == 0) && (seconds == 9) && (offset == 38) && (leap59 == 0) && (leap61 == 0)) { + printf("post leap61 handling correct\n\n"); + } else { + printf("post leap61 handling failed\n\n"); + exit(1); + } + } else if (j == 1) { + if ((hours == 0) && (minutes == 0) && (seconds == 11) && (offset == 36) && (leap59 == 0) && (leap61 == 0)) { + printf("post leap59 handling correct\n\n"); + } else { + printf("post leap59 handling failed\n\n"); + exit(2); + } + } else { + if ((hours == 0) && (minutes == 0) && (seconds == 10) && (offset == 37) && (leap59 == 0) && (leap61 == 0)) { + printf("post no-leap handling correct\n\n"); + } else { + printf("post no-leap handling failed\n\n"); + exit(3); + } + } + } + + printf("all leap handling tests passed\n\n"); + exit(0); +} diff --git a/userspace/wrsw_hal/hal_exports.c b/userspace/wrsw_hal/hal_exports.c index 0bfad7523ebffd2742d8d40f0e525a521ce1e909..ad07772856fd905b624abf0585b47ef5ee890260 100644 --- a/userspace/wrsw_hal/hal_exports.c +++ b/userspace/wrsw_hal/hal_exports.c @@ -65,6 +65,10 @@ int halexp_lock_cmd(const char *port_name, int command, int priority) else return HEXP_LOCK_STATUS_NONE; break; + + case HEXP_LOCK_CMD_RESET: + return hal_port_reset(port_name); + } return -100; /* fixme: add real error code */ diff --git a/userspace/wrsw_hal/hal_ports.c b/userspace/wrsw_hal/hal_ports.c index b4460aba0be8ad3d39f4d7eb9e23a00bb99f4e6a..989b0daf3b7e1ec8f03129916bdc117b6fbed6d1 100644 --- a/userspace/wrsw_hal/hal_ports.c +++ b/userspace/wrsw_hal/hal_ports.c @@ -423,6 +423,7 @@ static void hal_port_fsm(struct hal_port_state * p) /* Default state - wait until the link goes up */ case HAL_PORT_STATE_LINK_DOWN: + case HAL_PORT_STATE_RESET: { if (link_up) { p->calib.tx_calibrated = 1; @@ -760,6 +761,39 @@ int hal_port_check_lock(const char *port_name) (hs->flags & RTS_REF_LOCKED)); } +int hal_port_reset(const char *port_name) +{ + struct hal_port_state *p = hal_lookup_port(ports, + hal_port_nports, port_name); + + if (!p) + return -1; + + if (p->state != HAL_PORT_STATE_LINK_DOWN + && p->state != HAL_PORT_STATE_DISABLED) { + if (p->locked) { + pr_info("Switching RTS to use local reference\n"); + if (hal_get_timing_mode() + != HAL_TIMING_MODE_GRAND_MASTER) + rts_set_mode(RTS_MODE_GM_FREERUNNING); + } + + /* turn off synced LED */ + set_led_synced(p->hw_index, 0); + + /* turn off link/wrmode LEDs */ + set_led_wrmode(p->hw_index, SFP_LED_WRMODE_OFF); + hal_port_reset_state(p); + p->state = HAL_PORT_STATE_RESET; + + rts_enable_ptracker(p->hw_index, 0); + pr_info("%s: link down\n", p->name); + + return 1; + } + return 0; +} + /* to avoid i2c transfers to set the link LEDs, cache their state */ static void set_led_wrmode(int p_index, int val) { diff --git a/userspace/wrsw_hal/wrsw_hal.h b/userspace/wrsw_hal/wrsw_hal.h index b9a84c7ec2f63545a884255cb2f78fdd535b1d79..dbd60cd5df9aa91c44a6ee0aaa580e557158e1a7 100644 --- a/userspace/wrsw_hal/wrsw_hal.h +++ b/userspace/wrsw_hal/wrsw_hal.h @@ -27,9 +27,10 @@ int hal_update_wripc(int ms_timeout); int hal_add_cleanup_callback(hal_cleanup_callback_t cb); -int hal_port_start_lock(const char *port_name, int priority); -int hal_port_check_lock(const char *port_name); -int hal_port_enable_tracking(const char *port_name); +int hal_port_start_lock(const char *port_name, int priority); +int hal_port_check_lock(const char *port_name); +int hal_port_reset(const char *port_name); +int hal_port_enable_tracking(const char *port_name); int hal_init_timing_mode(void); int hal_init_timing(char *filename);