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 <bst@pengutronix.de>
This commit is contained in:
Bastian Krause 2022-01-21 16:09:35 +01:00 committed by Karel Zak
parent ee1e12448e
commit 6097b12df7
4 changed files with 132 additions and 3 deletions

View file

@ -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

View file

@ -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, &param->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;
}

View file

@ -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, &param)) {
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 <param> 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(_(" <param> 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(_(" <param> and <value> accept hexadecimal values if prefixed with 0x, otherwise decimal."));
puts(_(" <param> 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);

View file

@ -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);