| // 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]); |
| } |