From: Bastian Krause Date: Fri, 21 Jan 2022 15:09:43 +0000 (+0100) Subject: hwclock: add --param-set option X-Git-Tag: v2.38-rc1~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b22b78b1be6df917d5c449d2f5e45d0a67f14502;p=thirdparty%2Futil-linux.git hwclock: add --param-set option Implement the RTC_PARAM_SET 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-set hwclock function accepts aliases for parameters currently existent (Kernel v5.16). They can be extended later on. As fallback and for values, hexadecimal (if prefixed with 0x) and decimal values, as defined in [2], are accepted. Example: $ hwclock --param-set bsm=0x0 [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 --- diff --git a/bash-completion/hwclock b/bash-completion/hwclock index 06e0ce79e8..6b4c01724d 100644 --- a/bash-completion/hwclock +++ b/bash-completion/hwclock @@ -23,6 +23,10 @@ _hwclock_module() COMPREPLY=( $(compgen -W "param" -- $cur) ) return 0 ;; + '--param-set') + COMPREPLY=( $(compgen -W "param=value" -- $cur) ) + return 0 + ;; '-h'|'-?'|'--help'|'-v'|'-V'|'--version') return 0 ;; @@ -49,6 +53,7 @@ _hwclock_module() --delay --epoch --param-get + --param-set --update-drift --noadjfile --adjfile diff --git a/sys-utils/hwclock-rtc.c b/sys-utils/hwclock-rtc.c index 92e1ab7ac1..becfc7b83f 100644 --- a/sys-utils/hwclock-rtc.c +++ b/sys-utils/hwclock-rtc.c @@ -509,3 +509,63 @@ int get_param_rtc(const struct hwclock_control *ctl, struct rtc_param *param) return 0; } + +/* + * Set the Hardware Clock parameter in the kernel. + */ +int set_param_rtc(const struct hwclock_control *ctl) +{ + char *tok = NULL, *end = NULL; + int rtc_fd, base; + struct rtc_param param; + + memset(¶m, 0, sizeof(param)); + + /* handle name */ + tok = strtok(ctl->param_set_option, "="); + if (resolve_rtc_param_alias(tok, ¶m.param)) { + base = strncmp(tok, "0x", 2) ? 10 : 16; + + errno = 0; + param.param = strtoull(tok, &end, base); + if (errno || !end || *end) { + warnx(_("could not convert parameter name to number")); + return 1; + } + } + + /* handle value */ + tok = strtok(NULL, "="); + if (!tok) { + warnx(_("expected =")); + return 1; + } + + base = strncmp(tok, "0x", 2) ? 10 : 16; + end = NULL; + errno = 0; + param.uvalue = strtoull(tok, &end, base); + if (errno || !end || *end) { + warnx(_("could not convert parameter value to number")); + return 1; + } + + /* set parameter */ + rtc_fd = open_rtc(ctl); + if (rtc_fd < 0) { + warnx(_("cannot open %s"), rtc_dev_name); + return 1; + } + + if (ioctl(rtc_fd, RTC_PARAM_SET, ¶m) == -1) { + warn(_("ioctl(%d, RTC_PARAM_SET, param) to %s failed"), + rtc_fd, rtc_dev_name); + return 1; + } + + if (ctl->verbose) + printf(_("ioctl(%d, RTC_PARAM_SET, 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 28f88d221a..520c64230c 100644 --- a/sys-utils/hwclock.c +++ b/sys-utils/hwclock.c @@ -1167,6 +1167,12 @@ manipulate_rtc_param(const struct hwclock_control *ctl) printf(_("The RTC parameter 0x%llx is set to 0x%llx.\n"), param.param, param.uvalue); + + } else if (ctl->param_set_option) { + if (ctl->testing) + return 0; + + return set_param_rtc(ctl); } return 1; @@ -1201,6 +1207,7 @@ usage(void) puts(_(" --setepoch set the RTC epoch according to --epoch")); #endif puts(_(" --param-get display the RTC parameter")); + puts(_(" --param-set = set 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")); @@ -1235,9 +1242,9 @@ usage(void) param++; } - puts(_(" See Kernel's include/uapi/linux/rtc.h for parameters.")); + puts(_(" See Kernel's include/uapi/linux/rtc.h for parameters and values.")); fputs(USAGE_ARG_SEPARATOR, stdout); - puts(_(" accepts hexadecimal values if prefixed with 0x, otherwise decimal.")); + puts(_(" and accept hexadecimal values if prefixed with 0x, otherwise decimal.")); printf(USAGE_MAN_TAIL("hwclock(8)")); exit(EXIT_SUCCESS); @@ -1269,6 +1276,7 @@ int main(int argc, char **argv) OPT_GETEPOCH, OPT_NOADJFILE, OPT_PARAM_GET, + OPT_PARAM_SET, OPT_PREDICT, OPT_SET, OPT_SETEPOCH, @@ -1296,6 +1304,7 @@ int main(int argc, char **argv) { "epoch", required_argument, NULL, OPT_EPOCH }, #endif { "param-get", required_argument, NULL, OPT_PARAM_GET }, + { "param-set", required_argument, NULL, OPT_PARAM_SET }, { "noadjfile", no_argument, NULL, OPT_NOADJFILE }, { "directisa", no_argument, NULL, OPT_DIRECTISA }, { "test", no_argument, NULL, OPT_TEST }, @@ -1413,6 +1422,11 @@ int main(int argc, char **argv) ctl.param_get_option = optarg; ctl.show = 0; break; + case OPT_PARAM_SET: + ctl.param_set_option = optarg; + ctl.show = 0; + ctl.hwaudit_on = 1; + break; case OPT_NOADJFILE: ctl.noadjfile = 1; break; @@ -1506,7 +1520,7 @@ int main(int argc, char **argv) } } - if (ctl.param_get_option) { + if (ctl.param_get_option || ctl.param_set_option) { if (manipulate_rtc_param(&ctl)) hwclock_exit(&ctl, EXIT_FAILURE); diff --git a/sys-utils/hwclock.h b/sys-utils/hwclock.h index 7dd67110ba..8af7e3fdf6 100644 --- a/sys-utils/hwclock.h +++ b/sys-utils/hwclock.h @@ -31,6 +31,7 @@ struct hwclock_control { char *rtc_dev_name; #endif char *param_get_option; + char *param_set_option; unsigned int hwaudit_on:1, adjust:1, @@ -88,6 +89,7 @@ struct rtc_param { }; #define RTC_PARAM_GET _IOW('p', 0x13, struct rtc_param) +#define RTC_PARAM_SET _IOW('p', 0x14, struct rtc_param) #define RTC_PARAM_FEATURES 0 #define RTC_PARAM_CORRECTION 1 @@ -107,6 +109,7 @@ static struct hwclock_param hwclock_params[] = { }; extern int get_param_rtc(const struct hwclock_control *ctl, struct rtc_param *param); +extern int set_param_rtc(const struct hwclock_control *ctl); extern void __attribute__((__noreturn__)) hwclock_exit(const struct hwclock_control *ctl, int status);