]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
hwclock: add support for RTC_VL_READ/RTC_VL_CLR ioctls
authorRasmus Villemoes <rasmus.villemoes@prevas.dk>
Tue, 13 Jun 2023 10:14:28 +0000 (12:14 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 22 Jun 2023 08:27:36 +0000 (10:27 +0200)
Implement a way for userspace to query the status of the backup
battery, if supported by the hardware and driver.

The RTC_VL_* bits are a somewhat recent addition (3431ca4837bf, but
really only from b0efe0281234) to the uapi header,
so provide our own definition if the build host's header doesn't.

Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
sys-utils/hwclock-rtc.c
sys-utils/hwclock.c
sys-utils/hwclock.h

index 7094cd063644b925bec61678532817d4ea4ead9f..f918272a14c9a40316b0d4467a33fc6a404feb11 100644 (file)
@@ -518,3 +518,89 @@ done:
        free(opt);
        return rc;
 }
+
+#ifndef RTC_VL_DATA_INVALID
+#define RTC_VL_DATA_INVALID     0x1
+#endif
+#ifndef RTC_VL_BACKUP_LOW
+#define RTC_VL_BACKUP_LOW       0x2
+#endif
+#ifndef RTC_VL_BACKUP_EMPTY
+#define RTC_VL_BACKUP_EMPTY     0x4
+#endif
+#ifndef RTC_VL_ACCURACY_LOW
+#define RTC_VL_ACCURACY_LOW     0x8
+#endif
+#ifndef RTC_VL_BACKUP_SWITCH
+#define RTC_VL_BACKUP_SWITCH    0x10
+#endif
+
+int rtc_vl_read(const struct hwclock_control *ctl)
+{
+       unsigned int vl;
+       int rtc_fd;
+       size_t i;
+       static const struct vl_bit {
+               unsigned int bit;
+               const char *desc;
+       } vl_bits[] = {
+               { RTC_VL_DATA_INVALID,  N_("Voltage too low, RTC data is invalid") },
+               { RTC_VL_BACKUP_LOW,    N_("Backup voltage is low") },
+               { RTC_VL_BACKUP_EMPTY,  N_("Backup empty or not present") },
+               { RTC_VL_ACCURACY_LOW,  N_("Voltage is low, RTC accuracy is reduced") },
+               { RTC_VL_BACKUP_SWITCH, N_("Backup switchover happened") },
+       };
+
+       rtc_fd = open_rtc(ctl);
+       if (rtc_fd < 0) {
+               warnx(_("cannot open %s"), rtc_dev_name);
+               return 1;
+       }
+
+       if (ioctl(rtc_fd, RTC_VL_READ, &vl) == -1) {
+               warn(_("ioctl(%d, RTC_VL_READ) on %s failed"),
+                    rtc_fd, rtc_dev_name);
+               return 1;
+       }
+
+       if (ctl->verbose) {
+               printf(_("ioctl(%d, RTC_VL_READ) on %s returned 0x%x\n"),
+                      rtc_fd, rtc_dev_name, vl);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(vl_bits); ++i) {
+               const struct vl_bit *vlb = &vl_bits[i];
+
+               if (vl & vlb->bit) {
+                       printf("0x%02x - %s\n", vlb->bit, vlb->desc);
+                       vl &= ~vlb->bit;
+               }
+       }
+       if (vl)
+               printf("0x%02x - unknown bit(s)\n", vl);
+
+       return 0;
+}
+
+int rtc_vl_clear(const struct hwclock_control *ctl)
+{
+       int rtc_fd;
+
+       rtc_fd = open_rtc(ctl);
+       if (rtc_fd < 0) {
+               warnx(_("cannot open %s"), rtc_dev_name);
+               return 1;
+       }
+
+       if (ioctl(rtc_fd, RTC_VL_CLR) == -1) {
+               warn(_("ioctl(%d, RTC_VL_CLEAR) on %s failed"),
+                    rtc_fd, rtc_dev_name);
+               return 1;
+       }
+
+       if (ctl->verbose)
+               printf(_("ioctl(%d, RTC_VL_CLEAR) on %s succeeded.\n"),
+                      rtc_fd, rtc_dev_name);
+
+       return 0;
+}
index 2a1844309bbcda07062d3182675fa40a6057e6a1..b043646b3c5a7caa2f0ecced1149bd31ba7931a2 100644 (file)
@@ -1180,6 +1180,20 @@ manipulate_rtc_param(const struct hwclock_control *ctl)
 
        return 1;
 }
+
+static int
+manipulate_rtc_voltage_low(const struct hwclock_control *ctl)
+{
+       if (ctl->vl_read) {
+               if (rtc_vl_read(ctl))
+                       return 1;
+       }
+       if (ctl->vl_clear) {
+               if (rtc_vl_clear(ctl))
+                       return 1;
+       }
+       return 0;
+}
 #endif
 
 static void out_version(void)
@@ -1215,6 +1229,8 @@ usage(void)
 #ifdef __linux__
        puts(_("     --param-get <param>         display the RTC parameter"));
        puts(_("     --param-set <param>=<value> set the RTC parameter"));
+       puts(_("     --vl-read                   read voltage low information"));
+       puts(_("     --vl-clear                  clear voltage low information"));
 #endif
        puts(_("     --predict                   predict the drifted RTC time according to --date"));
        fputs(USAGE_OPTIONS, stdout);
@@ -1286,6 +1302,8 @@ int main(int argc, char **argv)
                OPT_NOADJFILE,
                OPT_PARAM_GET,
                OPT_PARAM_SET,
+               OPT_VL_READ,
+               OPT_VL_CLEAR,
                OPT_PREDICT,
                OPT_SET,
                OPT_SETEPOCH,
@@ -1315,6 +1333,8 @@ int main(int argc, char **argv)
 #ifdef __linux__
                { "param-get",    required_argument, NULL, OPT_PARAM_GET  },
                { "param-set",    required_argument, NULL, OPT_PARAM_SET  },
+               { "vl-read",      no_argument,       NULL, OPT_VL_READ    },
+               { "vl-clear",     no_argument,       NULL, OPT_VL_CLEAR   },
 #endif
                { "noadjfile",    no_argument,       NULL, OPT_NOADJFILE  },
                { "directisa",    no_argument,       NULL, OPT_DIRECTISA  },
@@ -1439,6 +1459,14 @@ int main(int argc, char **argv)
                        ctl.show = 0;
                        ctl.hwaudit_on = 1;
                        break;
+               case OPT_VL_READ:
+                       ctl.vl_read = 1;
+                       ctl.show = 0;
+                       break;
+               case OPT_VL_CLEAR:
+                       ctl.vl_clear = 1;
+                       ctl.show = 0;
+                       break;
 #endif
                case OPT_NOADJFILE:
                        ctl.noadjfile = 1;
@@ -1540,6 +1568,13 @@ int main(int argc, char **argv)
 
                hwclock_exit(&ctl, EXIT_SUCCESS);
        }
+
+       if (ctl.vl_read || ctl.vl_clear) {
+               if (manipulate_rtc_voltage_low(&ctl))
+                       hwclock_exit(&ctl, EXIT_FAILURE);
+
+               hwclock_exit(&ctl, EXIT_SUCCESS);
+       }
 #endif
 
 #if defined(__linux__) && defined(__alpha__)
index b5b72d45d71080df105b18187ee8a7f2aab4ebcf..a690e717bc368035de47e970a97bfce5387523c7 100644 (file)
@@ -53,6 +53,8 @@ struct hwclock_control {
                set:1,
                update:1,
                universal:1,    /* will store hw_clock_is_utc() return value */
+               vl_read:1,
+               vl_clear:1,
                verbose:1;
 };
 
@@ -88,6 +90,9 @@ extern int get_param_rtc(const struct hwclock_control *ctl,
                        const char *name, uint64_t *id, uint64_t *value);
 extern int set_param_rtc(const struct hwclock_control *ctl, const char *name);
 
+extern int rtc_vl_read(const struct hwclock_control *ctl);
+extern int rtc_vl_clear(const struct hwclock_control *ctl);
+
 extern void __attribute__((__noreturn__))
 hwclock_exit(const struct hwclock_control *ctl, int status);