GNU Linux-libre 6.1.90-gnu
[releases.git] / arch / um / drivers / rtc_user.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 Intel Corporation
4  * Author: Johannes Berg <johannes@sipsolutions.net>
5  */
6 #include <stdbool.h>
7 #include <os.h>
8 #include <errno.h>
9 #include <sched.h>
10 #include <unistd.h>
11 #include <kern_util.h>
12 #include <sys/select.h>
13 #include <stdio.h>
14 #include <sys/timerfd.h>
15 #include "rtc.h"
16
17 static int uml_rtc_irq_fds[2];
18
19 void uml_rtc_send_timetravel_alarm(void)
20 {
21         unsigned long long c = 1;
22
23         CATCH_EINTR(write(uml_rtc_irq_fds[1], &c, sizeof(c)));
24 }
25
26 int uml_rtc_start(bool timetravel)
27 {
28         int err;
29
30         if (timetravel) {
31                 int err = os_pipe(uml_rtc_irq_fds, 1, 1);
32                 if (err)
33                         goto fail;
34         } else {
35                 uml_rtc_irq_fds[0] = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
36                 if (uml_rtc_irq_fds[0] < 0) {
37                         err = -errno;
38                         goto fail;
39                 }
40
41                 /* apparently timerfd won't send SIGIO, use workaround */
42                 sigio_broken(uml_rtc_irq_fds[0]);
43                 err = add_sigio_fd(uml_rtc_irq_fds[0]);
44                 if (err < 0) {
45                         close(uml_rtc_irq_fds[0]);
46                         goto fail;
47                 }
48         }
49
50         return uml_rtc_irq_fds[0];
51 fail:
52         uml_rtc_stop(timetravel);
53         return err;
54 }
55
56 int uml_rtc_enable_alarm(unsigned long long delta_seconds)
57 {
58         struct itimerspec it = {
59                 .it_value = {
60                         .tv_sec = delta_seconds,
61                 },
62         };
63
64         if (timerfd_settime(uml_rtc_irq_fds[0], 0, &it, NULL))
65                 return -errno;
66         return 0;
67 }
68
69 void uml_rtc_disable_alarm(void)
70 {
71         uml_rtc_enable_alarm(0);
72 }
73
74 void uml_rtc_stop(bool timetravel)
75 {
76         if (timetravel)
77                 os_close_file(uml_rtc_irq_fds[1]);
78         else
79                 ignore_sigio_fd(uml_rtc_irq_fds[0]);
80         os_close_file(uml_rtc_irq_fds[0]);
81 }