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@xxxxx> Cc: Alessandro Zummo <a.zummo@xxxxxxxxxxxx> Cc: Alexandre Belloni <alexandre.belloni@xxxxxxxxxxx> Cc: Shuah Khan <shuah@xxxxxxxxxx> --- tools/testing/selftests/rtc/rtctest.c | 81 ++++++++++++++++++++++++++- tools/testing/selftests/rtc/settings | 2 +- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/rtc/rtctest.c b/tools/testing/selftests/rtc/rtctest.c index 2b9d929a24ed..7e5f2f1c00ef 100644 --- a/tools/testing/selftests/rtc/rtctest.c +++ b/tools/testing/selftests/rtc/rtctest.c @@ -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 }; diff --git a/tools/testing/selftests/rtc/settings b/tools/testing/selftests/rtc/settings index 0c1a2075d5f3..b478e684846a 100644 --- a/tools/testing/selftests/rtc/settings +++ b/tools/testing/selftests/rtc/settings @@ -1 +1 @@ -timeout=210 +timeout=240 -- 2.25.1