From 6097b12df708a91651972f73cac2984692196073 Mon Sep 17 00:00:00 2001 From: Bastian Krause Date: Fri, 21 Jan 2022 16:09:35 +0100 Subject: [PATCH] hwclock: add --param-get option Implement the RTC_PARAM_GET RTC ioctl in hwclock. The ioctl interface was introduced with [1], which went mainline in Kernel v5.16. The parameters are independent of hardware/driver. This means we can read and set parameters in a generic way. The new --param-get hwclock function accepts aliases for parameters currently existent (Kernel v5.16). They can be extended later on. As fallback, hexadecimal (if prefixed with 0x) and decimal values, as defined in [2], are accepted. Example: $ hwclock --param-get features [1] https://lore.kernel.org/all/20211018151933.76865-1-alexandre.belloni@bootlin.com/ [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/rtc.h Signed-off-by: Bastian Krause --- bash-completion/hwclock | 5 ++++ sys-utils/hwclock-rtc.c | 56 +++++++++++++++++++++++++++++++++++++++++ sys-utils/hwclock.c | 40 ++++++++++++++++++++++++++--- sys-utils/hwclock.h | 34 +++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 3 deletions(-) diff --git a/bash-completion/hwclock b/bash-completion/hwclock index faf29afe9a..06e0ce79e8 100644 --- a/bash-completion/hwclock +++ b/bash-completion/hwclock @@ -19,6 +19,10 @@ _hwclock_module() COMPREPLY=( $(compgen -W "year" -- $cur) ) return 0 ;; + '--param-get') + COMPREPLY=( $(compgen -W "param" -- $cur) ) + return 0 + ;; '-h'|'-?'|'--help'|'-v'|'-V'|'--version') return 0 ;; @@ -44,6 +48,7 @@ _hwclock_module() --date --delay --epoch + --param-get --update-drift --noadjfile --adjfile diff --git a/sys-utils/hwclock-rtc.c b/sys-utils/hwclock-rtc.c index 07af9c83c3..92e1ab7ac1 100644 --- a/sys-utils/hwclock-rtc.c +++ b/sys-utils/hwclock-rtc.c @@ -453,3 +453,59 @@ int set_epoch_rtc(const struct hwclock_control *ctl) return 0; } #endif /* __alpha__ */ + +static int resolve_rtc_param_alias(const char *alias, uint64_t *value) +{ + const struct hwclock_param *param = &hwclock_params[0]; + + while (param->name) { + if (!strcmp(alias, param->name)) { + *value = param->id; + return 0; + } + param++; + } + + return 1; +} + +/* + * Get the Hardware Clock parameter setting from the kernel. + */ +int get_param_rtc(const struct hwclock_control *ctl, struct rtc_param *param) +{ + int rtc_fd; + + /* handle name */ + if (resolve_rtc_param_alias(ctl->param_get_option, ¶m->param)) { + char *end = NULL; + int base; + + base = strncmp(ctl->param_get_option, "0x", 2) ? 10 : 16; + errno = 0; + param->param = strtoull(ctl->param_get_option, &end, base); + if (errno || !end || *end) { + warnx(_("could not convert parameter name to number")); + return 1; + } + } + + /* get parameter */ + rtc_fd = open_rtc(ctl); + if (rtc_fd < 0) { + warn(_("cannot open %s"), rtc_dev_name); + return 1; + } + + if (ioctl(rtc_fd, RTC_PARAM_GET, param) == -1) { + warn(_("ioctl(%d, RTC_PARAM_GET, param) to %s failed"), + rtc_fd, rtc_dev_name); + return 1; + } + + if (ctl->verbose) + printf(_("ioctl(%d, RTC_PARAM_GET, param) to %s succeeded.\n"), + rtc_fd, rtc_dev_name); + + return 0; +} diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c index 802f66b77d..28f88d221a 100644 --- a/sys-utils/hwclock.c +++ b/sys-utils/hwclock.c @@ -1154,6 +1154,24 @@ manipulate_epoch(const struct hwclock_control *ctl) } #endif /* __linux__ __alpha__ */ +static int +manipulate_rtc_param(const struct hwclock_control *ctl) +{ + if (ctl->param_get_option) { + struct rtc_param param = {}; + + if (get_param_rtc(ctl, ¶m)) { + warnx(_("unable to read the RTC parameter 0x%llx."), param.param); + return 1; + } + + printf(_("The RTC parameter 0x%llx is set to 0x%llx.\n"), + param.param, param.uvalue); + } + + return 1; +} + static void out_version(void) { printf(UTIL_LINUX_VERSION); @@ -1162,6 +1180,8 @@ static void out_version(void) static void __attribute__((__noreturn__)) usage(void) { + const struct hwclock_param *param = &hwclock_params[0]; + fputs(USAGE_HEADER, stdout); printf(_(" %s [function] [option...]\n"), program_invocation_short_name); @@ -1180,6 +1200,7 @@ usage(void) puts(_(" --getepoch display the RTC epoch")); puts(_(" --setepoch set the RTC epoch according to --epoch")); #endif + puts(_(" --param-get display the RTC parameter")); puts(_(" --predict predict the drifted RTC time according to --date")); fputs(USAGE_OPTIONS, stdout); puts(_(" -u, --utc the RTC timescale is UTC")); @@ -1210,13 +1231,13 @@ usage(void) puts(_(" is either a numeric RTC parameter value or one of these aliases:")); while (param->name) { - printf(_(" %1$s: %2$s (0x%3$x)\n"), param->name, param->help, param->id); + printf(_(" - %1$s: %2$s (0x%3$x)\n"), param->name, param->help, param->id); param++; } - puts(_(" See Kernel's include/uapi/linux/rtc.h for paramters and values.")); + puts(_(" See Kernel's include/uapi/linux/rtc.h for parameters.")); fputs(USAGE_ARG_SEPARATOR, stdout); - puts(_(" and accept hexadecimal values if prefixed with 0x, otherwise decimal.")); + puts(_(" accepts hexadecimal values if prefixed with 0x, otherwise decimal.")); printf(USAGE_MAN_TAIL("hwclock(8)")); exit(EXIT_SUCCESS); @@ -1247,6 +1268,7 @@ int main(int argc, char **argv) OPT_GET, OPT_GETEPOCH, OPT_NOADJFILE, + OPT_PARAM_GET, OPT_PREDICT, OPT_SET, OPT_SETEPOCH, @@ -1273,6 +1295,7 @@ int main(int argc, char **argv) { "setepoch", no_argument, NULL, OPT_SETEPOCH }, { "epoch", required_argument, NULL, OPT_EPOCH }, #endif + { "param-get", required_argument, NULL, OPT_PARAM_GET }, { "noadjfile", no_argument, NULL, OPT_NOADJFILE }, { "directisa", no_argument, NULL, OPT_DIRECTISA }, { "test", no_argument, NULL, OPT_TEST }, @@ -1386,6 +1409,10 @@ int main(int argc, char **argv) ctl.epoch_option = optarg; /* --epoch */ break; #endif + case OPT_PARAM_GET: + ctl.param_get_option = optarg; + ctl.show = 0; + break; case OPT_NOADJFILE: ctl.noadjfile = 1; break; @@ -1479,6 +1506,13 @@ int main(int argc, char **argv) } } + if (ctl.param_get_option) { + if (manipulate_rtc_param(&ctl)) + hwclock_exit(&ctl, EXIT_FAILURE); + + hwclock_exit(&ctl, EXIT_SUCCESS); + } + #if defined(__linux__) && defined(__alpha__) if (ctl.getepoch || ctl.setepoch) { manipulate_epoch(&ctl); diff --git a/sys-utils/hwclock.h b/sys-utils/hwclock.h index 627cf51452..7dd67110ba 100644 --- a/sys-utils/hwclock.h +++ b/sys-utils/hwclock.h @@ -9,6 +9,7 @@ #include "c.h" #include "debug.h" +#include "nls.h" #define HWCLOCK_DEBUG_INIT (1 << 0) #define HWCLOCK_DEBUG_RANDOM_SLEEP (1 << 1) @@ -29,6 +30,7 @@ struct hwclock_control { #ifdef __linux__ char *rtc_dev_name; #endif + char *param_get_option; unsigned int hwaudit_on:1, adjust:1, @@ -74,6 +76,38 @@ extern int get_epoch_rtc(const struct hwclock_control *ctl, unsigned long *epoch extern int set_epoch_rtc(const struct hwclock_control *ctl); #endif +struct rtc_param { + uint64_t param; + union { + uint64_t uvalue; + int64_t svalue; + uint64_t ptr; + }; + uint32_t index; + uint32_t __pad; +}; + +#define RTC_PARAM_GET _IOW('p', 0x13, struct rtc_param) + +#define RTC_PARAM_FEATURES 0 +#define RTC_PARAM_CORRECTION 1 +#define RTC_PARAM_BACKUP_SWITCH_MODE 2 + +struct hwclock_param { + int id; + const char *name; + const char *help; +}; + +static struct hwclock_param hwclock_params[] = { + { RTC_PARAM_FEATURES, "features", N_("supported features") }, + { RTC_PARAM_CORRECTION, "correction", N_("time correction") }, + { RTC_PARAM_BACKUP_SWITCH_MODE, "bsm", N_("backup switch mode") }, + { } +}; + +extern int get_param_rtc(const struct hwclock_control *ctl, struct rtc_param *param); + extern void __attribute__((__noreturn__)) hwclock_exit(const struct hwclock_control *ctl, int status); -- 2.47.2