diff options
author | Mike Frysinger <vapier@gentoo.org> | 2008-02-15 02:27:19 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2008-02-15 02:27:19 +0000 |
commit | 6b160e490d4d77596c1603d34d0a1ca0579a82da (patch) | |
tree | 653ab55714a0373751b619144e56b5ea163938f2 /util-linux/rtcwake.c | |
parent | be7d2a8ded621a6d62f8caa76f7c51185b166ab1 (diff) | |
download | busybox-w32-6b160e490d4d77596c1603d34d0a1ca0579a82da.tar.gz busybox-w32-6b160e490d4d77596c1603d34d0a1ca0579a82da.tar.bz2 busybox-w32-6b160e490d4d77596c1603d34d0a1ca0579a82da.zip |
split some rtc funcs out of hwclock and into an rtc header/lib so that the new rtcwake applet as well as hwclock can utilize the same code
Diffstat (limited to 'util-linux/rtcwake.c')
-rw-r--r-- | util-linux/rtcwake.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c new file mode 100644 index 000000000..718f43d45 --- /dev/null +++ b/util-linux/rtcwake.c | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | * rtcwake -- enter a system sleep state until specified wakeup time. | ||
3 | * | ||
4 | * This version was taken from util-linux and scrubbed down for busybox. | ||
5 | * | ||
6 | * This uses cross-platform Linux interfaces to enter a system sleep state, | ||
7 | * and leave it no later than a specified time. It uses any RTC framework | ||
8 | * driver that supports standard driver model wakeup flags. | ||
9 | * | ||
10 | * This is normally used like the old "apmsleep" utility, to wake from a | ||
11 | * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most | ||
12 | * platforms can implement those without analogues of BIOS, APM, or ACPI. | ||
13 | * | ||
14 | * On some systems, this can also be used like "nvram-wakeup", waking | ||
15 | * from states like ACPI S4 (suspend to disk). Not all systems have | ||
16 | * persistent media that are appropriate for such suspend modes. | ||
17 | * | ||
18 | * The best way to set the system's RTC is so that it holds the current | ||
19 | * time in UTC. Use the "-l" flag to tell this program that the system | ||
20 | * RTC uses a local timezone instead (maybe you dual-boot MS-Windows). | ||
21 | * That flag should not be needed on systems with adjtime support. | ||
22 | */ | ||
23 | |||
24 | #include "libbb.h" | ||
25 | #include "rtc_.h" | ||
26 | |||
27 | #define SYS_RTC_PATH "/sys/class/rtc/%s/device/power/wakeup" | ||
28 | #define SYS_POWER_PATH "/sys/power/state" | ||
29 | #define DEFAULT_MODE "suspend" | ||
30 | |||
31 | static time_t rtc_time; | ||
32 | |||
33 | static int may_wakeup(const char *rtcname) | ||
34 | { | ||
35 | ssize_t ret; | ||
36 | char buf[128]; | ||
37 | |||
38 | /* strip the '/dev/' from the rtcname here */ | ||
39 | if (!strncmp(rtcname, "/dev/", 5)) | ||
40 | rtcname += 5; | ||
41 | |||
42 | snprintf(buf, sizeof(buf), SYS_RTC_PATH, rtcname); | ||
43 | ret = open_read_close(buf, buf, sizeof(buf)); | ||
44 | if (ret < 0) | ||
45 | return 0; | ||
46 | |||
47 | /* wakeup events could be disabled or not supported */ | ||
48 | return strcmp(buf, "enabled\n") == 0; | ||
49 | } | ||
50 | |||
51 | static void setup_alarm(int fd, time_t *wakeup) | ||
52 | { | ||
53 | struct tm *tm; | ||
54 | struct linux_rtc_wkalrm wake; | ||
55 | |||
56 | /* The wakeup time is in POSIX time (more or less UTC). | ||
57 | * Ideally RTCs use that same time; but PCs can't do that | ||
58 | * if they need to boot MS-Windows. Messy... | ||
59 | * | ||
60 | * When running in utc mode this process's timezone is UTC, | ||
61 | * so we'll pass a UTC date to the RTC. | ||
62 | * | ||
63 | * Else mode is local so the time given to the RTC | ||
64 | * will instead use the local time zone. | ||
65 | */ | ||
66 | tm = localtime(wakeup); | ||
67 | |||
68 | wake.time.tm_sec = tm->tm_sec; | ||
69 | wake.time.tm_min = tm->tm_min; | ||
70 | wake.time.tm_hour = tm->tm_hour; | ||
71 | wake.time.tm_mday = tm->tm_mday; | ||
72 | wake.time.tm_mon = tm->tm_mon; | ||
73 | wake.time.tm_year = tm->tm_year; | ||
74 | /* wday, yday, and isdst fields are unused by Linux */ | ||
75 | wake.time.tm_wday = -1; | ||
76 | wake.time.tm_yday = -1; | ||
77 | wake.time.tm_isdst = -1; | ||
78 | |||
79 | /* many rtc alarms only support up to 24 hours from 'now', | ||
80 | * so use the "more than 24 hours" request only if we must | ||
81 | */ | ||
82 | if ((rtc_time + (24 * 60 * 60)) > *wakeup) { | ||
83 | xioctl(fd, RTC_ALM_SET, &wake.time); | ||
84 | xioctl(fd, RTC_AIE_ON, 0); | ||
85 | } else { | ||
86 | /* avoid an extra AIE_ON call */ | ||
87 | wake.enabled = 1; | ||
88 | xioctl(fd, RTC_WKALM_SET, &wake); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | static void suspend_system(const char *suspend) | ||
93 | { | ||
94 | FILE *f = xfopen(SYS_POWER_PATH, "w"); | ||
95 | fprintf(f, "%s\n", suspend); | ||
96 | fflush(f); | ||
97 | /* this executes after wake from suspend */ | ||
98 | fclose(f); | ||
99 | } | ||
100 | |||
101 | #define RTCWAKE_OPT_AUTO 0x01 | ||
102 | #define RTCWAKE_OPT_LOCAL 0x02 | ||
103 | #define RTCWAKE_OPT_UTC 0x04 | ||
104 | #define RTCWAKE_OPT_DEVICE 0x08 | ||
105 | #define RTCWAKE_OPT_SUSPEND_MODE 0x10 | ||
106 | #define RTCWAKE_OPT_SECONDS 0x20 | ||
107 | #define RTCWAKE_OPT_TIME 0x40 | ||
108 | |||
109 | int rtcwake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
110 | int rtcwake_main(int argc, char **argv) | ||
111 | { | ||
112 | unsigned opt; | ||
113 | const char *rtcname = NULL; | ||
114 | const char *suspend; | ||
115 | const char *opt_seconds; | ||
116 | const char *opt_time; | ||
117 | |||
118 | time_t sys_time; | ||
119 | time_t alarm_time = 0; | ||
120 | unsigned seconds = 0; | ||
121 | int utc = -1; | ||
122 | int fd; | ||
123 | |||
124 | #if ENABLE_GETOPT_LONG | ||
125 | static const char rtcwake_longopts[] ALIGN1 = | ||
126 | "auto\0" No_argument "a" | ||
127 | "local\0" No_argument "l" | ||
128 | "utc\0" No_argument "u" | ||
129 | "device\0" Required_argument "d" | ||
130 | "mode\0" Required_argument "m" | ||
131 | "seconds\0" Required_argument "s" | ||
132 | "time\0" Required_argument "t" | ||
133 | ; | ||
134 | applet_long_options = rtcwake_longopts; | ||
135 | } | ||
136 | #endif | ||
137 | opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time); | ||
138 | |||
139 | /* this is the default | ||
140 | if (opt & RTCWAKE_OPT_AUTO) | ||
141 | utc = -1; | ||
142 | */ | ||
143 | if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL)) | ||
144 | utc = opt & RTCWAKE_OPT_UTC; | ||
145 | if (!(opt & RTCWAKE_OPT_SUSPEND_MODE)) | ||
146 | suspend = DEFAULT_MODE; | ||
147 | if (opt & RTCWAKE_OPT_SECONDS) | ||
148 | /* alarm time, seconds-to-sleep (relative) */ | ||
149 | seconds = xatoi(opt_seconds); | ||
150 | if (opt & RTCWAKE_OPT_TIME) | ||
151 | /* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */ | ||
152 | alarm_time = xatoi(opt_time); | ||
153 | |||
154 | if (!alarm_time && !seconds) | ||
155 | bb_error_msg_and_die("must provide wake time"); | ||
156 | |||
157 | if (utc == -1) | ||
158 | utc = rtc_adjtime_is_utc(); | ||
159 | |||
160 | /* the rtcname is relative to /dev */ | ||
161 | xchdir("/dev"); | ||
162 | |||
163 | if (strcmp(suspend, "on") != 0 && !may_wakeup(rtcname)) | ||
164 | bb_error_msg_and_die("%s not enabled for wakeup events", rtcname); | ||
165 | |||
166 | /* this RTC must exist and (if we'll sleep) be wakeup-enabled */ | ||
167 | fd = rtc_xopen(rtcname, O_RDONLY); | ||
168 | |||
169 | /* relative or absolute alarm time, normalized to time_t */ | ||
170 | sys_time = time(0); | ||
171 | if (sys_time == (time_t)-1) | ||
172 | bb_perror_msg_and_die("read system time"); | ||
173 | rtc_time = rtc_read_time(fd, utc); | ||
174 | |||
175 | if (alarm_time) { | ||
176 | if (alarm_time < sys_time) | ||
177 | bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time)); | ||
178 | alarm_time += sys_time - rtc_time; | ||
179 | } else | ||
180 | alarm_time = rtc_time + seconds + 1; | ||
181 | setup_alarm(fd, &alarm_time); | ||
182 | |||
183 | sync(); | ||
184 | printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time)); | ||
185 | fflush(stdout); | ||
186 | usleep(10 * 1000); | ||
187 | |||
188 | if (!strcmp(suspend, "on")) | ||
189 | suspend_system(suspend); | ||
190 | else { | ||
191 | /* "fake" suspend ... we'll do the delay ourselves */ | ||
192 | unsigned long data; | ||
193 | |||
194 | do { | ||
195 | ssize_t ret = safe_read(fd, &data, sizeof(data)); | ||
196 | if (ret < 0) { | ||
197 | bb_perror_msg("rtc read"); | ||
198 | break; | ||
199 | } | ||
200 | } while (!(data & RTC_AF)); | ||
201 | } | ||
202 | |||
203 | xioctl(fd, RTC_AIE_OFF, 0); | ||
204 | |||
205 | if (ENABLE_FEATURE_CLEAN_UP) | ||
206 | close(fd); | ||
207 | |||
208 | return EXIT_SUCCESS; | ||
209 | } | ||