diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/config/agptekrocker.h | 3 | ||||
-rw-r--r-- | firmware/target/hosted/rtc.c | 154 |
2 files changed, 149 insertions, 8 deletions
diff --git a/firmware/export/config/agptekrocker.h b/firmware/export/config/agptekrocker.h index 372287f4b5..6b012730c0 100644 --- a/firmware/export/config/agptekrocker.h +++ b/firmware/export/config/agptekrocker.h | |||
@@ -66,6 +66,9 @@ | |||
66 | /* define this if you have a real-time clock */ | 66 | /* define this if you have a real-time clock */ |
67 | #define CONFIG_RTC APPLICATION | 67 | #define CONFIG_RTC APPLICATION |
68 | 68 | ||
69 | /* Define if the device can wake from an RTC alarm */ | ||
70 | #define HAVE_RTC_ALARM | ||
71 | |||
69 | /* The number of bytes reserved for loadable codecs */ | 72 | /* The number of bytes reserved for loadable codecs */ |
70 | #define CODEC_SIZE 0x80000 | 73 | #define CODEC_SIZE 0x80000 |
71 | 74 | ||
diff --git a/firmware/target/hosted/rtc.c b/firmware/target/hosted/rtc.c index 488531c77c..ced298a5c8 100644 --- a/firmware/target/hosted/rtc.c +++ b/firmware/target/hosted/rtc.c | |||
@@ -27,9 +27,14 @@ | |||
27 | #include <linux/rtc.h> | 27 | #include <linux/rtc.h> |
28 | #include <fcntl.h> | 28 | #include <fcntl.h> |
29 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #include <stdbool.h> | ||
30 | #endif | 31 | #endif |
32 | |||
33 | #include "config.h" | ||
34 | |||
31 | void rtc_init(void) | 35 | void rtc_init(void) |
32 | { | 36 | { |
37 | tzset(); | ||
33 | } | 38 | } |
34 | 39 | ||
35 | int rtc_read_datetime(struct tm *tm) | 40 | int rtc_read_datetime(struct tm *tm) |
@@ -46,8 +51,6 @@ int rtc_write_datetime(const struct tm *tm) | |||
46 | struct timeval tv; | 51 | struct timeval tv; |
47 | struct tm *tm_time; | 52 | struct tm *tm_time; |
48 | 53 | ||
49 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
50 | |||
51 | tv.tv_sec = mktime((struct tm *)tm); | 54 | tv.tv_sec = mktime((struct tm *)tm); |
52 | tv.tv_usec = 0; | 55 | tv.tv_usec = 0; |
53 | 56 | ||
@@ -58,12 +61,147 @@ int rtc_write_datetime(const struct tm *tm) | |||
58 | time_t now = time(NULL); | 61 | time_t now = time(NULL); |
59 | tm_time = gmtime(&now); | 62 | tm_time = gmtime(&now); |
60 | 63 | ||
61 | ioctl(rtc, RTC_SET_TIME, (struct rtc_time *)tm_time); | 64 | /* Try to write the HW RTC, if present. */ |
62 | close(rtc); | 65 | int rtc = open("/dev/rtc0", O_WRONLY); |
63 | 66 | if (rtc > 0) { | |
64 | return 0; | 67 | ioctl(rtc, RTC_SET_TIME, (struct rtc_time *)tm_time); |
68 | close(rtc); | ||
69 | } | ||
65 | #else | 70 | #else |
66 | (void)tm; | 71 | (void)(*tm); |
67 | return -1; | ||
68 | #endif | 72 | #endif |
73 | return 0; | ||
74 | } | ||
75 | |||
76 | #if defined(HAVE_RTC_ALARM) && !defined(SIMULATOR) | ||
77 | void rtc_set_alarm(int h, int m) | ||
78 | { | ||
79 | struct rtc_time tm; | ||
80 | long sec; | ||
81 | |||
82 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
83 | if (rtc < 0) | ||
84 | return; | ||
85 | |||
86 | /* Get RTC time */ | ||
87 | ioctl(rtc, RTC_RD_TIME, &tm); | ||
88 | |||
89 | /* Convert to seconds into the GMT day. Can be negative! */ | ||
90 | sec = h * 3600 + m * 60 + timezone; | ||
91 | h = sec / 3600; | ||
92 | sec -= h * 3600; | ||
93 | m = sec / 60; | ||
94 | |||
95 | /* Handle negative or positive wraps */ | ||
96 | while (m < 0) { | ||
97 | m += 60; | ||
98 | h--; | ||
99 | } | ||
100 | while (m > 59) { | ||
101 | m -= 60; | ||
102 | h++; | ||
103 | } | ||
104 | while (h < 0) { | ||
105 | h += 24; | ||
106 | tm.tm_mday--; | ||
107 | } | ||
108 | while (h > 23) { | ||
109 | h -= 24; | ||
110 | tm.tm_mday++; | ||
111 | } | ||
112 | |||
113 | /* Update the struct */ | ||
114 | tm.tm_sec = 0; | ||
115 | tm.tm_hour = h; | ||
116 | tm.tm_min = m; | ||
117 | |||
118 | ioctl(rtc, RTC_ALM_SET, &tm); | ||
119 | close(rtc); | ||
120 | } | ||
121 | |||
122 | void rtc_get_alarm(int *h, int *m) | ||
123 | { | ||
124 | struct rtc_time tm; | ||
125 | long sec; | ||
126 | |||
127 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
128 | if (rtc < 0) | ||
129 | return; | ||
130 | |||
131 | ioctl(rtc, RTC_ALM_READ, &tm); | ||
132 | close(rtc); | ||
133 | |||
134 | /* Convert RTC from UTC to local time zone.. */ | ||
135 | sec = (tm.tm_min * 60) + (tm.tm_hour * 3600) - timezone; | ||
136 | |||
137 | /* Handle wrapping and negative offsets */ | ||
138 | *h = (sec / 3600); | ||
139 | sec -= *h * 3600; | ||
140 | *m = sec / 60; | ||
141 | |||
142 | while (*m < 0) { | ||
143 | *m = *m + 60; | ||
144 | *h = *h - 1; | ||
145 | } | ||
146 | while (*m > 59) { | ||
147 | *m = *m - 60; | ||
148 | *h = *h + 1; | ||
149 | } | ||
150 | while (*h < 0) { | ||
151 | *h = *h + 24; | ||
152 | } | ||
153 | while (*h > 23) { | ||
154 | *h = *h - 24; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | void rtc_enable_alarm(bool enable) | ||
159 | { | ||
160 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
161 | if (rtc < 0) | ||
162 | return; | ||
163 | |||
164 | ioctl(rtc, enable ? RTC_AIE_ON : RTC_AIE_OFF, NULL); | ||
165 | close(rtc); | ||
166 | |||
167 | /* XXX Note that this may or may not work; Linux may need to be suspended | ||
168 | or shut down in a special way to keep the RTC alarm active */ | ||
69 | } | 169 | } |
170 | |||
171 | /* Returns true if alarm was the reason we started up */ | ||
172 | bool rtc_check_alarm_started(bool release_alarm) | ||
173 | { | ||
174 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
175 | if (rtc < 0) | ||
176 | return false; | ||
177 | |||
178 | /* XXX There is no generic way of determining wakeup reason. Will | ||
179 | likely need a target-specific hook. */ | ||
180 | |||
181 | /* Disable alarm if requested */ | ||
182 | if (release_alarm) | ||
183 | ioctl(rtc, RTC_AIE_OFF, NULL); | ||
184 | |||
185 | close(rtc); | ||
186 | return false; | ||
187 | } | ||
188 | |||
189 | /* See if we received an alarm. */ | ||
190 | bool rtc_check_alarm_flag(void) | ||
191 | { | ||
192 | struct rtc_wkalrm alrm; | ||
193 | |||
194 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
195 | if (rtc < 0) | ||
196 | return false; | ||
197 | |||
198 | alrm.pending = 0; | ||
199 | /* XXX Documented as "mostly useless on Linux" except with EFI RTCs | ||
200 | Will likely need a target-specific hook. */ | ||
201 | ioctl(rtc, RTC_WKALM_RD, &alrm); | ||
202 | close(rtc); | ||
203 | |||
204 | return alrm.pending; | ||
205 | } | ||
206 | |||
207 | #endif /* HAVE_RTC_ALARM */ | ||