Util-Linux Archive mirror
 help / color / mirror / Atom feed
From: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
To: util-linux@vger.kernel.org
Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Subject: [PATCH] hwclock: add support for RTC_VL_READ/RTC_VL_CLR ioctls
Date: Tue, 13 Jun 2023 12:14:28 +0200	[thread overview]
Message-ID: <20230613101428.294827-1-rasmus.villemoes@prevas.dk> (raw)

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 | 86 +++++++++++++++++++++++++++++++++++++++++
 sys-utils/hwclock.c     | 35 +++++++++++++++++
 sys-utils/hwclock.h     |  5 +++
 3 files changed, 126 insertions(+)

diff --git a/sys-utils/hwclock-rtc.c b/sys-utils/hwclock-rtc.c
index 7094cd063..f918272a1 100644
--- a/sys-utils/hwclock-rtc.c
+++ b/sys-utils/hwclock-rtc.c
@@ -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;
+}
diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c
index 2a1844309..b043646b3 100644
--- a/sys-utils/hwclock.c
+++ b/sys-utils/hwclock.c
@@ -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__)
diff --git a/sys-utils/hwclock.h b/sys-utils/hwclock.h
index b5b72d45d..a690e717b 100644
--- a/sys-utils/hwclock.h
+++ b/sys-utils/hwclock.h
@@ -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);
 
-- 
2.37.2


             reply	other threads:[~2023-06-13 10:15 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-13 10:14 Rasmus Villemoes [this message]
2023-06-20  9:26 ` [PATCH] hwclock: add support for RTC_VL_READ/RTC_VL_CLR ioctls Karel Zak
2023-06-20  9:30   ` Rasmus Villemoes
2023-06-20 10:22     ` Karel Zak
2023-06-20 11:22       ` Rasmus Villemoes
2023-06-21 20:18 ` [PATCH] hwclock: add --vl-read, --vl-clear documentation and bash-completion Rasmus Villemoes
2023-06-22  8:29   ` [PATCH] hwclock: add support for RTC_VL_READ/RTC_VL_CLR ioctls Karel Zak

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230613101428.294827-1-rasmus.villemoes@prevas.dk \
    --to=rasmus.villemoes@prevas.dk \
    --cc=util-linux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).