diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2019-01-31 11:05:13 -0500 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2020-04-11 18:02:26 +0200 |
commit | 6984a7ce1535422891ce4de57070cc55a486d352 (patch) | |
tree | 1f585e50cac0dd3ef4b713c2156d6c501b82f7bf | |
parent | 02d347bc6f6e37ead72986436a6fffdeafeec484 (diff) | |
download | rockbox-6984a7ce1535422891ce4de57070cc55a486d352.tar.gz rockbox-6984a7ce1535422891ce4de57070cc55a486d352.zip |
RTC: Add support RTC alarms on hosted targets
Only AGPTeck Rocker is enabled for now, and it doesn't work properly:
* No generic way to determine wakeup reason under Linux
* No generic way to be asynchronously notified if the alarm is
triggered when we're already awake
* Shutting down may clobber RTC wakeup (driver/etc dependent)
And finally:
* AGPTek kernel's RTC driver has some 24h clock and
some timezone-related issues.
So, the infrastructure is arguably useful, but the only applicable
hardware I have is pathologically brain-dead.
Change-Id: Iac6a26a9b6e4efec5d0b3030b87f456eb23fc01d
-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 @@ /* define this if you have a real-time clock */ #define CONFIG_RTC APPLICATION +/* Define if the device can wake from an RTC alarm */ +#define HAVE_RTC_ALARM + /* The number of bytes reserved for loadable codecs */ #define CODEC_SIZE 0x80000 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 @@ #include <linux/rtc.h> #include <fcntl.h> #include <unistd.h> +#include <stdbool.h> #endif + +#include "config.h" + void rtc_init(void) { + tzset(); } int rtc_read_datetime(struct tm *tm) @@ -46,8 +51,6 @@ int rtc_write_datetime(const struct tm *tm) struct timeval tv; struct tm *tm_time; - int rtc = open("/dev/rtc0", O_WRONLY); - tv.tv_sec = mktime((struct tm *)tm); tv.tv_usec = 0; @@ -58,12 +61,147 @@ int rtc_write_datetime(const struct tm *tm) time_t now = time(NULL); tm_time = gmtime(&now); - ioctl(rtc, RTC_SET_TIME, (struct rtc_time *)tm_time); - close(rtc); - - return 0; + /* Try to write the HW RTC, if present. */ + int rtc = open("/dev/rtc0", O_WRONLY); + if (rtc > 0) { + ioctl(rtc, RTC_SET_TIME, (struct rtc_time *)tm_time); + close(rtc); + } #else - (void)tm; - return -1; + (void)(*tm); #endif + return 0; +} + +#if defined(HAVE_RTC_ALARM) && !defined(SIMULATOR) +void rtc_set_alarm(int h, int m) +{ + struct rtc_time tm; + long sec; + + int rtc = open("/dev/rtc0", O_WRONLY); + if (rtc < 0) + return; + + /* Get RTC time */ + ioctl(rtc, RTC_RD_TIME, &tm); + + /* Convert to seconds into the GMT day. Can be negative! */ + sec = h * 3600 + m * 60 + timezone; + h = sec / 3600; + sec -= h * 3600; + m = sec / 60; + + /* Handle negative or positive wraps */ + while (m < 0) { + m += 60; + h--; + } + while (m > 59) { + m -= 60; + h++; + } + while (h < 0) { + h += 24; + tm.tm_mday--; + } + while (h > 23) { + h -= 24; + tm.tm_mday++; + } + + /* Update the struct */ + tm.tm_sec = 0; + tm.tm_hour = h; + tm.tm_min = m; + + ioctl(rtc, RTC_ALM_SET, &tm); + close(rtc); +} + +void rtc_get_alarm(int *h, int *m) +{ + struct rtc_time tm; + long sec; + + int rtc = open("/dev/rtc0", O_WRONLY); + if (rtc < 0) + return; + + ioctl(rtc, RTC_ALM_READ, &tm); + close(rtc); + + /* Convert RTC from UTC to local time zone.. */ + sec = (tm.tm_min * 60) + (tm.tm_hour * 3600) - timezone; + + /* Handle wrapping and negative offsets */ + *h = (sec / 3600); + sec -= *h * 3600; + *m = sec / 60; + + while (*m < 0) { + *m = *m + 60; + *h = *h - 1; + } + while (*m > 59) { + *m = *m - 60; + *h = *h + 1; + } + while (*h < 0) { + *h = *h + 24; + } + while (*h > 23) { + *h = *h - 24; + } +} + +void rtc_enable_alarm(bool enable) +{ + int rtc = open("/dev/rtc0", O_WRONLY); + if (rtc < 0) + return; + + ioctl(rtc, enable ? RTC_AIE_ON : RTC_AIE_OFF, NULL); + close(rtc); + + /* XXX Note that this may or may not work; Linux may need to be suspended + or shut down in a special way to keep the RTC alarm active */ } + +/* Returns true if alarm was the reason we started up */ +bool rtc_check_alarm_started(bool release_alarm) +{ + int rtc = open("/dev/rtc0", O_WRONLY); + if (rtc < 0) + return false; + + /* XXX There is no generic way of determining wakeup reason. Will + likely need a target-specific hook. */ + + /* Disable alarm if requested */ + if (release_alarm) + ioctl(rtc, RTC_AIE_OFF, NULL); + + close(rtc); + return false; +} + +/* See if we received an alarm. */ +bool rtc_check_alarm_flag(void) +{ + struct rtc_wkalrm alrm; + + int rtc = open("/dev/rtc0", O_WRONLY); + if (rtc < 0) + return false; + + alrm.pending = 0; + /* XXX Documented as "mostly useless on Linux" except with EFI RTCs + Will likely need a target-specific hook. */ + ioctl(rtc, RTC_WKALM_RD, &alrm); + close(rtc); + + return alrm.pending; +} + +#endif /* HAVE_RTC_ALARM */ |