From 9d1d9b0a67766b41cd0d83cede49dd0b54cdaf7b Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Mon, 9 Mar 2026 15:31:22 -0400 Subject: [PATCH] Add Battery backup voltage level get/set Signed-off-by: Hugo Villeneuve --- src/main.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/options.c | 42 ++++++++++++++++++++++-------- src/options.h | 2 ++ 3 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/main.c b/src/main.c index 2c6b453..38c2d95 100644 --- a/src/main.c +++ b/src/main.c @@ -70,8 +70,6 @@ static int rtc_param_set_bsm(int fd, int value) par.param = RTC_PARAM_BACKUP_SWITCH_MODE; par.uvalue = value; - printf("Set backup switch mode to: $%02X\n", value); - ret = ioctl(fd, RTC_PARAM_SET, &par); if (ret < 0) { perror("set RTC_PARAM_BACKUP_SWITCH_MODE"); @@ -81,6 +79,64 @@ static int rtc_param_set_bsm(int fd, int value) return 0; } +static int rtc_param_get_vl_backup(int fd) +{ + const char *prefix = "Backup voltage:"; + unsigned long tmp; + int voltage_good; + int ret; + + ret = ioctl(fd, RTC_VL_READ, &tmp); + if (ret < 0) { + perror("get RTC_VL_READ"); + return ret; + } + + voltage_good = true; + + if (tmp & RTC_VL_DATA_INVALID) { + printf("%s too low, RTC data is invalid\n", prefix); + voltage_good = false; + } + + if (tmp & RTC_VL_BACKUP_LOW) { + printf("%s low, check/replace RTC battery\n", prefix); + voltage_good = false; + } + + if (tmp & RTC_VL_BACKUP_EMPTY) { + printf("%s empty or not present\n", prefix); + voltage_good = false; + } + + if (tmp & RTC_VL_ACCURACY_LOW) { + printf("%s low, RTC accuracy is reduced\n", prefix); + voltage_good = false; + } + + if (voltage_good) + printf("%s good\n", prefix); + + if (tmp & RTC_VL_BACKUP_SWITCH) + printf("%s switchover event detected\n", prefix); + + return 0; +} + +static int rtc_param_clear_vl_backup(int fd) +{ + struct rtc_param par; + int ret; + + ret = ioctl(fd, RTC_VL_CLR, &par); + if (ret < 0) { + perror("get RTC_VL_CLR"); + return ret; + } + + return 0; +} + static int rtc_test_uie(int fd) { unsigned long data; @@ -366,6 +422,18 @@ main(int argc, char **argv) } } + if (options.vl_backup) { + rc = rtc_param_get_vl_backup(fd); + if (rc < 0) + exit(rc); + } + + if (options.clear) { + rc = rtc_param_clear_vl_backup(fd); + if (rc < 0) + exit(rc); + } + if (options.test) { rc = rtc_test(fd); if (rc < 0) diff --git a/src/options.c b/src/options.c index e0ee934..930ba0f 100644 --- a/src/options.c +++ b/src/options.c @@ -16,6 +16,7 @@ #include #include #include /* va_start */ +#include #if STDC_HEADERS # include @@ -48,11 +49,20 @@ static const char args_doc[] = ""; */ #define USAGE_KEY 0x123 +#define ARGP_BSM_STR \ + " disabled RTC_BSM_DISABLED\n" \ + " direct RTC_BSM_DIRECT\n" \ + " level RTC_BSM_LEVEL\n" \ + " standby RTC_BSM_STANDBY" + /* The options we understand. */ static struct argp_option argp_options[] = { - {"bsm", 'b', "value", OPTION_ARG_OPTIONAL, "Get/Set Backup Switch Mode (BSM)", 0}, - {"dev", 'd', "path", 0, "RTC device path (optional)", 0}, + {"bsm", 'b', "value", OPTION_ARG_OPTIONAL, "Get/Set Backup Switch Mode (BSM):", 0}, + {0, 0, 0, 0, ARGP_BSM_STR, 0}, + {"clear", 'c', 0, 0, "Clear \"voltage low\" event", 0}, + {"dev", 'd', "path", 0, "RTC device path (optional)", 0}, {"test", 't', 0, 0, "Test RTC", 0}, + {"voltage-level", 'v', 0, 0, "Get battery backup voltage level", 0}, /* * Add -h manually; OPTION_HIDDEN prevents it from appearing twice in --help. * This must be listed after all other options, to prevent an empty line. @@ -87,15 +97,19 @@ void log_fail(const char *format, ...) } static void -decode_int(char *arg, struct argp_state *state, int *dest) +decode_bsm(char *arg, struct argp_state *state) { - char *endptr; - - *dest = strtol(arg, &endptr, 0); - - if (*endptr != '\0') { - log_err("Invalid option: %s\n", arg); - argp_usage(state); + if (STREQ(arg, "disabled")) { + options.bsm_arg = RTC_BSM_DISABLED; + } else if (STREQ(arg, "direct")) { + options.bsm_arg = RTC_BSM_DIRECT; + } else if (STREQ(arg, "level")) { + options.bsm_arg = RTC_BSM_LEVEL; + } else if (STREQ(arg, "standby")) { + options.bsm_arg = RTC_BSM_STANDBY; + } else { + log_fail("Unknown BSM value: %s\n", arg); + argp_usage(state); } } @@ -107,7 +121,10 @@ parse_opt(int key, char *arg, struct argp_state *state) case 'b': options.bsm = true; if (arg) - decode_int(arg, state, &options.bsm_arg); + decode_bsm(arg, state); + break; + case 'c': + options.clear = true; break; case 'd': options.rtc_path = arg; @@ -123,6 +140,9 @@ parse_opt(int key, char *arg, struct argp_state *state) case 't': options.test = true; break; + case 'v': + options.vl_backup = true; + break; default: return ARGP_ERR_UNKNOWN; } diff --git a/src/options.h b/src/options.h index 44c967b..c643ee5 100644 --- a/src/options.h +++ b/src/options.h @@ -19,6 +19,8 @@ struct options_t { char *rtc_path; int bsm; int bsm_arg; + int vl_backup; + int clear; int test; }; -- 2.47.3