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:
parent
ee1e12448e
commit
6097b12df7
4 changed files with 132 additions and 3 deletions
|
@ -19,6 +19,10 @@ _hwclock_module()
|
||||||
COMPREPLY=( $(compgen -W "year" -- $cur) )
|
COMPREPLY=( $(compgen -W "year" -- $cur) )
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
'--param-get')
|
||||||
|
COMPREPLY=( $(compgen -W "param" -- $cur) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
'-h'|'-?'|'--help'|'-v'|'-V'|'--version')
|
'-h'|'-?'|'--help'|'-v'|'-V'|'--version')
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
@ -44,6 +48,7 @@ _hwclock_module()
|
||||||
--date
|
--date
|
||||||
--delay
|
--delay
|
||||||
--epoch
|
--epoch
|
||||||
|
--param-get
|
||||||
--update-drift
|
--update-drift
|
||||||
--noadjfile
|
--noadjfile
|
||||||
--adjfile
|
--adjfile
|
||||||
|
|
|
@ -453,3 +453,59 @@ int set_epoch_rtc(const struct hwclock_control *ctl)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* __alpha__ */
|
#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;
|
||||||
|
}
|
||||||
|
|
|
@ -1154,6 +1154,24 @@ manipulate_epoch(const struct hwclock_control *ctl)
|
||||||
}
|
}
|
||||||
#endif /* __linux__ __alpha__ */
|
#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)
|
static void out_version(void)
|
||||||
{
|
{
|
||||||
printf(UTIL_LINUX_VERSION);
|
printf(UTIL_LINUX_VERSION);
|
||||||
|
@ -1162,6 +1180,8 @@ static void out_version(void)
|
||||||
static void __attribute__((__noreturn__))
|
static void __attribute__((__noreturn__))
|
||||||
usage(void)
|
usage(void)
|
||||||
{
|
{
|
||||||
|
const struct hwclock_param *param = &hwclock_params[0];
|
||||||
|
|
||||||
fputs(USAGE_HEADER, stdout);
|
fputs(USAGE_HEADER, stdout);
|
||||||
printf(_(" %s [function] [option...]\n"), program_invocation_short_name);
|
printf(_(" %s [function] [option...]\n"), program_invocation_short_name);
|
||||||
|
|
||||||
|
@ -1180,6 +1200,7 @@ usage(void)
|
||||||
puts(_(" --getepoch display the RTC epoch"));
|
puts(_(" --getepoch display the RTC epoch"));
|
||||||
puts(_(" --setepoch set the RTC epoch according to --epoch"));
|
puts(_(" --setepoch set the RTC epoch according to --epoch"));
|
||||||
#endif
|
#endif
|
||||||
|
puts(_(" --param-get <param> display the RTC parameter"));
|
||||||
puts(_(" --predict predict the drifted RTC time according to --date"));
|
puts(_(" --predict predict the drifted RTC time according to --date"));
|
||||||
fputs(USAGE_OPTIONS, stdout);
|
fputs(USAGE_OPTIONS, stdout);
|
||||||
puts(_(" -u, --utc the RTC timescale is UTC"));
|
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:"));
|
puts(_(" <param> is either a numeric RTC parameter value or one of these aliases:"));
|
||||||
|
|
||||||
while (param->name) {
|
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++;
|
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);
|
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)"));
|
printf(USAGE_MAN_TAIL("hwclock(8)"));
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
@ -1247,6 +1268,7 @@ int main(int argc, char **argv)
|
||||||
OPT_GET,
|
OPT_GET,
|
||||||
OPT_GETEPOCH,
|
OPT_GETEPOCH,
|
||||||
OPT_NOADJFILE,
|
OPT_NOADJFILE,
|
||||||
|
OPT_PARAM_GET,
|
||||||
OPT_PREDICT,
|
OPT_PREDICT,
|
||||||
OPT_SET,
|
OPT_SET,
|
||||||
OPT_SETEPOCH,
|
OPT_SETEPOCH,
|
||||||
|
@ -1273,6 +1295,7 @@ int main(int argc, char **argv)
|
||||||
{ "setepoch", no_argument, NULL, OPT_SETEPOCH },
|
{ "setepoch", no_argument, NULL, OPT_SETEPOCH },
|
||||||
{ "epoch", required_argument, NULL, OPT_EPOCH },
|
{ "epoch", required_argument, NULL, OPT_EPOCH },
|
||||||
#endif
|
#endif
|
||||||
|
{ "param-get", required_argument, NULL, OPT_PARAM_GET },
|
||||||
{ "noadjfile", no_argument, NULL, OPT_NOADJFILE },
|
{ "noadjfile", no_argument, NULL, OPT_NOADJFILE },
|
||||||
{ "directisa", no_argument, NULL, OPT_DIRECTISA },
|
{ "directisa", no_argument, NULL, OPT_DIRECTISA },
|
||||||
{ "test", no_argument, NULL, OPT_TEST },
|
{ "test", no_argument, NULL, OPT_TEST },
|
||||||
|
@ -1386,6 +1409,10 @@ int main(int argc, char **argv)
|
||||||
ctl.epoch_option = optarg; /* --epoch */
|
ctl.epoch_option = optarg; /* --epoch */
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case OPT_PARAM_GET:
|
||||||
|
ctl.param_get_option = optarg;
|
||||||
|
ctl.show = 0;
|
||||||
|
break;
|
||||||
case OPT_NOADJFILE:
|
case OPT_NOADJFILE:
|
||||||
ctl.noadjfile = 1;
|
ctl.noadjfile = 1;
|
||||||
break;
|
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 defined(__linux__) && defined(__alpha__)
|
||||||
if (ctl.getepoch || ctl.setepoch) {
|
if (ctl.getepoch || ctl.setepoch) {
|
||||||
manipulate_epoch(&ctl);
|
manipulate_epoch(&ctl);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "c.h"
|
#include "c.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "nls.h"
|
||||||
|
|
||||||
#define HWCLOCK_DEBUG_INIT (1 << 0)
|
#define HWCLOCK_DEBUG_INIT (1 << 0)
|
||||||
#define HWCLOCK_DEBUG_RANDOM_SLEEP (1 << 1)
|
#define HWCLOCK_DEBUG_RANDOM_SLEEP (1 << 1)
|
||||||
|
@ -29,6 +30,7 @@ struct hwclock_control {
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
char *rtc_dev_name;
|
char *rtc_dev_name;
|
||||||
#endif
|
#endif
|
||||||
|
char *param_get_option;
|
||||||
unsigned int
|
unsigned int
|
||||||
hwaudit_on:1,
|
hwaudit_on:1,
|
||||||
adjust: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);
|
extern int set_epoch_rtc(const struct hwclock_control *ctl);
|
||||||
#endif
|
#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__))
|
extern void __attribute__((__noreturn__))
|
||||||
hwclock_exit(const struct hwclock_control *ctl, int status);
|
hwclock_exit(const struct hwclock_control *ctl, int status);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue