@@ -62,7 +62,21 @@ static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
.tm_year = rtc_time->tm_year,
};
- return mktime(&tm_time);
+ return timegm(&tm_time);
+}
+
+static void timestamp_to_rtc_time(time_t timestamp, struct rtc_time *rtc_time)
+{
+ struct tm tm_time;
+
+ gmtime_r(×tamp, &tm_time);
+
+ rtc_time->tm_sec = tm_time.tm_sec;
+ rtc_time->tm_min = tm_time.tm_min;
+ rtc_time->tm_hour = tm_time.tm_hour;
+ rtc_time->tm_mday = tm_time.tm_mday;
+ rtc_time->tm_mon = tm_time.tm_mon;
+ rtc_time->tm_year = tm_time.tm_year;
}
static void nanosleep_with_retries(long ns)
@@ -228,6 +242,71 @@ TEST_F(rtc, alarm_alm_set) {
ASSERT_EQ(new, secs);
}
+TEST_F_TIMEOUT(rtc, alarm_read_loop, READ_LOOP_DURATION_SEC + 2) {
+ int rc;
+ long iter_count = 0;
+ struct rtc_time tm;
+ struct timeval start, curr;
+ time_t secs;
+
+ rc = ioctl(self->fd, RTC_RD_TIME, &tm);
+ ASSERT_NE(-1, rc);
+
+ // 60s is for clocks that only support minute resolution of RTC alarm
+ secs = rtc_time_to_timestamp(&tm) + READ_LOOP_DURATION_SEC + 60 + 2;
+ timestamp_to_rtc_time(secs, &tm);
+
+ rc = ioctl(self->fd, RTC_ALM_SET, &tm);
+ if (rc == -1) {
+ ASSERT_EQ(EINVAL, errno);
+ TH_LOG("skip alarms are not supported.");
+ return;
+ }
+
+ rc = ioctl(self->fd, RTC_AIE_ON, NULL);
+ ASSERT_NE(-1, rc);
+
+ TH_LOG("Continuously reading RTC alarm time for %ds (with %dms breaks after every read).",
+ READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
+
+ gettimeofday(&start, NULL);
+
+ secs = 0;
+
+ do {
+ // TODO: use appropriate directory
+ // TODO: fail gracefully if the kernel support does not exist
+ FILE *wkalarm_file = fopen("/sys/kernel/debug/rtc/rtc0/wakealarm_raw", "r");
+ long long wkalarm_time;
+
+ ASSERT_NE(wkalarm_file, NULL);
+
+ rc = fscanf(wkalarm_file, "%lld", &wkalarm_time);
+ fclose(wkalarm_file);
+
+ ASSERT_EQ(rc, 1);
+ ASSERT_NE(secs, -1);
+ // TODO: use another value as placeholder,
+ // TODO: check if wkalarm_time matches alarm time we set
+ // previously
+ if (secs == 0)
+ secs = wkalarm_time;
+
+ ASSERT_EQ(wkalarm_time, secs);
+
+ // Sleep 11ms to avoid killing / overheating the RTC
+ nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
+
+ gettimeofday(&curr, NULL);
+ iter_count++;
+ } while (curr.tv_sec <= start.tv_sec + READ_LOOP_DURATION_SEC);
+
+ TH_LOG("Performed %ld RTC alarm time reads.", iter_count);
+
+ rc = ioctl(self->fd, RTC_AIE_OFF, NULL);
+ ASSERT_NE(-1, rc);
+}
+
TEST_F(rtc, alarm_wkalm_set) {
struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
struct rtc_wkalrm alarm = { 0 };
@@ -1 +1 @@
-timeout=210
+timeout=240
This is the userspace part of the test unit. This patch depends on commit 2aaa36e95ea5 ("selftests/rtc: continuously read RTC in a loop for 30s") Existing code casts "struct rtc_time *" to "struct tm *", like so: gmtime_r(&secs, (struct tm *)&tm); This is incorrect, because sizeof(struct tm) > sizeof(struct rtc_time) (on Ubuntu 20.04 at least) and gmtime_r overwrites part of the stack. I'll submit a fix for this in mainline soon, but for now I'm defining timestamp_to_rtc_time() here. TODO: - some logic improvements, without much impact on the core algorithm. Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Shuah Khan <shuah@kernel.org> --- tools/testing/selftests/rtc/rtctest.c | 81 ++++++++++++++++++++++++++- tools/testing/selftests/rtc/settings | 2 +- 2 files changed, 81 insertions(+), 2 deletions(-)