]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
hwclock: add --param-set option
authorBastian Krause <bst@pengutronix.de>
Fri, 21 Jan 2022 15:09:43 +0000 (16:09 +0100)
committerKarel Zak <kzak@redhat.com>
Fri, 28 Jan 2022 10:37:47 +0000 (11:37 +0100)
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 <bst@pengutronix.de>
bash-completion/hwclock
sys-utils/hwclock-rtc.c
sys-utils/hwclock.c
sys-utils/hwclock.h

index 06e0ce79e8de002c0276f4de487f6c07cf5b39ee..6b4c01724dd7e58ef05e35806765313ce90d8776 100644 (file)
@@ -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
index 92e1ab7ac1c35e2b7c5a38fcd54dfef81406b257..becfc7b83f281c3679cb85224d72e7af99449cfe 100644 (file)
@@ -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(&param, 0, sizeof(param));
+
+       /* handle name */
+       tok = strtok(ctl->param_set_option, "=");
+       if (resolve_rtc_param_alias(tok, &param.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 <param>=<value>"));
+               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, &param) == -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;
+}
index 28f88d221a2b06644e0f977c58bed7b9ba500515..520c64230c68d5b40631388ec67135cff8d0a779 100644 (file)
@@ -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 <param>         display the RTC parameter"));
+       puts(_("     --param-set <param>=<value> 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(_(" <param> accepts hexadecimal values if prefixed with 0x, otherwise decimal."));
+       puts(_(" <param> and <value> 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);
 
index 7dd67110ba5c4b97ca572f661a54c8a32c2085c2..8af7e3fdf600b749ff7205cf40860a2b8e2eadb5 100644 (file)
@@ -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);