summaryrefslogtreecommitdiff
path: root/arch/um/drivers/rtc_user.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2021-01-15 15:12:18 +0300
committerRichard Weinberger <richard@nod.at>2021-02-12 23:38:52 +0300
commitdde8b58d512703d396e02427de1053b4d912aa42 (patch)
tree1536dfa429585580bdc252afb9bc0639d937d830 /arch/um/drivers/rtc_user.c
parentbfc58e2b98e99737409cd9f4d86a79677c5b887c (diff)
downloadlinux-dde8b58d512703d396e02427de1053b4d912aa42.tar.xz
um: add a pseudo RTC
Add a pseudo RTC that simply is able to send an alarm signal waking up the system at a given time in the future. Since apparently timerfd_create() FDs don't support SIGIO, we use the sigio-creating helper thread, which just learned to do suspend/resume properly in the previous patch. For time-travel mode, OTOH, just add an event at the specified time in the future, and that's already sufficient to wake up the system at that point in time since suspend will just be in an "endless wait". For s2idle support also call pm_system_wakeup(). Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'arch/um/drivers/rtc_user.c')
-rw-r--r--arch/um/drivers/rtc_user.c80
1 files changed, 80 insertions, 0 deletions
diff --git a/arch/um/drivers/rtc_user.c b/arch/um/drivers/rtc_user.c
new file mode 100644
index 000000000000..4016bc1d577e
--- /dev/null
+++ b/arch/um/drivers/rtc_user.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Intel Corporation
+ * Author: Johannes Berg <johannes@sipsolutions.net>
+ */
+#include <os.h>
+#include <errno.h>
+#include <sched.h>
+#include <unistd.h>
+#include <kern_util.h>
+#include <sys/select.h>
+#include <stdio.h>
+#include <sys/timerfd.h>
+#include "rtc.h"
+
+static int uml_rtc_irq_fds[2];
+
+void uml_rtc_send_timetravel_alarm(void)
+{
+ unsigned long long c = 1;
+
+ CATCH_EINTR(write(uml_rtc_irq_fds[1], &c, sizeof(c)));
+}
+
+int uml_rtc_start(bool timetravel)
+{
+ int err;
+
+ if (timetravel) {
+ int err = os_pipe(uml_rtc_irq_fds, 1, 1);
+ if (err)
+ goto fail;
+ } else {
+ uml_rtc_irq_fds[0] = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
+ if (uml_rtc_irq_fds[0] < 0) {
+ err = -errno;
+ goto fail;
+ }
+
+ /* apparently timerfd won't send SIGIO, use workaround */
+ sigio_broken(uml_rtc_irq_fds[0]);
+ err = add_sigio_fd(uml_rtc_irq_fds[0]);
+ if (err < 0) {
+ close(uml_rtc_irq_fds[0]);
+ goto fail;
+ }
+ }
+
+ return uml_rtc_irq_fds[0];
+fail:
+ uml_rtc_stop(timetravel);
+ return err;
+}
+
+int uml_rtc_enable_alarm(unsigned long long delta_seconds)
+{
+ struct itimerspec it = {
+ .it_value = {
+ .tv_sec = delta_seconds,
+ },
+ };
+
+ if (timerfd_settime(uml_rtc_irq_fds[0], 0, &it, NULL))
+ return -errno;
+ return 0;
+}
+
+void uml_rtc_disable_alarm(void)
+{
+ uml_rtc_enable_alarm(0);
+}
+
+void uml_rtc_stop(bool timetravel)
+{
+ if (timetravel)
+ os_close_file(uml_rtc_irq_fds[1]);
+ else
+ ignore_sigio_fd(uml_rtc_irq_fds[0]);
+ os_close_file(uml_rtc_irq_fds[0]);
+}