/* * Saitek X52 Pro MFD & LED driver - Clock manager * * Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org) * * SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0 */ #include "config.h" #include #include #include #include #include #include "pinelog.h" #include "x52d_config.h" #include "x52d_clock.h" #include "x52d_const.h" #include "x52d_device.h" static bool clock_enabled = false; static int clock_primary_is_local = false; void x52d_cfg_set_Clock_Enabled(bool enabled) { PINELOG_DEBUG(_("Setting clock enable to %s"), enabled ? _("on") : _("off")); clock_enabled = enabled; } void x52d_cfg_set_Clock_PrimaryIsLocal(bool param) { PINELOG_DEBUG(_("Setting %s clock timezone to %s"), libx52_clock_id_to_str(LIBX52_CLOCK_1), param ? _("local") : _("UTC")); clock_primary_is_local = !!param; } static int get_tz_offset(const char *tz) { char *orig_tz = NULL; char *orig_tz_copy = NULL; time_t t; struct tm tmp; struct tm *timeval; char *new_tz = NULL; size_t new_tz_len; int offset = 0; new_tz_len = strlen(tz) + 2; new_tz = malloc(new_tz_len); if (new_tz == NULL) { PINELOG_WARN(_("Unable to allocate memory for timezone. Falling back to UTC")); goto cleanup; } snprintf(new_tz, new_tz_len, ":%s", tz); orig_tz = getenv("TZ"); if (orig_tz != NULL) { /* TZ was set in the environment */ orig_tz_copy = strdup(orig_tz); if (orig_tz_copy == NULL) { PINELOG_WARN(_("Unable to backup timezone environment. Falling back to UTC")); goto cleanup; } } setenv("TZ", new_tz, true); t = time(NULL); tzset(); timeval = localtime_r(&t, &tmp); if (timeval != NULL) { #if HAVE_STRUCT_TM_TM_GMTOFF /* If valid, then timeval.tm_gmtoff contains the offset in seconds east * of GMT. Divide by 60 to get the offset in minutes east of GMT. */ offset = (int)(timeval->tm_gmtoff / 60); #else /* The compiler does not provide tm_gmtoff. Fallback to using the * timezone variable, which is in seconds west of GMT. Divide by -60 to * get the offset in minutes east of GMT. * * ============ * XXX NOTE XXX * ============ * timezone is always the default (non-summer) timezone offset from GMT. * Therefore, this may not be accurate during the summer time months * for the region in question. */ offset = (int)(timezone / -60); #endif } cleanup: if (orig_tz == NULL) { unsetenv("TZ"); } else { setenv("TZ", orig_tz_copy, true); free(orig_tz_copy); } if (new_tz != NULL) { free(new_tz); } tzset(); PINELOG_TRACE("Offset for timezone '%s' is %d", tz, offset); return offset; } static void set_clock_offset(libx52_clock_id id, const char *param) { if (clock_enabled) { PINELOG_DEBUG(_("Setting %s clock timezone to %s"), libx52_clock_id_to_str(id), param); x52d_dev_set_clock_timezone(id, get_tz_offset(param)); } } void x52d_cfg_set_Clock_Secondary(char* param) { set_clock_offset(LIBX52_CLOCK_2, param); } void x52d_cfg_set_Clock_Tertiary(char* param) { set_clock_offset(LIBX52_CLOCK_3, param); } static void set_clock_format(libx52_clock_id id, libx52_clock_format fmt) { if (clock_enabled) { PINELOG_DEBUG(_("Setting %s clock format to %s"), libx52_clock_id_to_str(id), libx52_clock_format_to_str(fmt)); x52d_dev_set_clock_format(id, fmt); } } void x52d_cfg_set_Clock_FormatPrimary(libx52_clock_format fmt) { set_clock_format(LIBX52_CLOCK_1, fmt); } void x52d_cfg_set_Clock_FormatSecondary(libx52_clock_format fmt) { set_clock_format(LIBX52_CLOCK_2, fmt); } void x52d_cfg_set_Clock_FormatTertiary(libx52_clock_format fmt) { set_clock_format(LIBX52_CLOCK_3, fmt); } void x52d_cfg_set_Clock_DateFormat(libx52_date_format fmt) { if (clock_enabled) { PINELOG_DEBUG(_("Setting date format to %s"), libx52_date_format_to_str(fmt)); x52d_dev_set_date_format(fmt); } } static pthread_t clock_thr; static void * x52_clock_thr(void *param) { int rc; PINELOG_INFO(_("Starting X52 clock manager thread")); for (;;) { time_t cur_time; sleep(1); if (!clock_enabled) { /* Clock thread is disabled, check again next time */ continue; } if (time(&cur_time) < 0) { PINELOG_WARN(_("Error %d retrieving current time: %s"), errno, strerror(errno)); continue; } rc = x52d_dev_set_clock(cur_time, clock_primary_is_local); if (rc == LIBX52_SUCCESS) { // Device manager will update the clock, this is only for debugging PINELOG_TRACE("Setting X52 clock to %ld", cur_time); } } return NULL; } void x52d_clock_init(void) { int rc; PINELOG_TRACE("Initializing clock manager"); rc = pthread_create(&clock_thr, NULL, x52_clock_thr, NULL); if (rc != 0) { PINELOG_FATAL(_("Error %d initializing clock thread: %s"), rc, strerror(rc)); } } void x52d_clock_exit(void) { PINELOG_INFO(_("Shutting down X52 clock manager thread")); pthread_cancel(clock_thr); }