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) )
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 <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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue