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;
+}
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)
#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);
OPT_NOADJFILE,
OPT_PARAM_GET,
OPT_PARAM_SET,
+ OPT_VL_READ,
+ OPT_VL_CLEAR,
OPT_PREDICT,
OPT_SET,
OPT_SETEPOCH,
#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 },
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;
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__)
set:1,
update:1,
universal:1, /* will store hw_clock_is_utc() return value */
+ vl_read:1,
+ vl_clear:1,
verbose:1;
};
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);