1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Real Time Clock Driver Test Program 4 * 5 * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com> 6 */ 7 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <linux/rtc.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <sys/ioctl.h> 14 #include <sys/time.h> 15 #include <sys/types.h> 16 #include <time.h> 17 #include <unistd.h> 18 19 #include "../kselftest_harness.h" 20 21 #define NUM_UIE 3 22 #define ALARM_DELTA 3 23 24 static char *rtc_file = "/dev/rtc0"; 25 26 FIXTURE(rtc) { 27 int fd; 28 }; 29 30 FIXTURE_SETUP(rtc) { 31 self->fd = open(rtc_file, O_RDONLY); 32 ASSERT_NE(-1, self->fd); 33 } 34 35 FIXTURE_TEARDOWN(rtc) { 36 close(self->fd); 37 } 38 39 TEST_F(rtc, date_read) { 40 int rc; 41 struct rtc_time rtc_tm; 42 43 /* Read the RTC time/date */ 44 rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm); 45 ASSERT_NE(-1, rc); 46 47 TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.", 48 rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900, 49 rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); 50 } 51 52 TEST_F(rtc, uie_read) { 53 int i, rc, irq = 0; 54 unsigned long data; 55 56 /* Turn on update interrupts */ 57 rc = ioctl(self->fd, RTC_UIE_ON, 0); 58 if (rc == -1) { 59 ASSERT_EQ(EINVAL, errno); 60 TH_LOG("skip update IRQs not supported."); 61 return; 62 } 63 64 for (i = 0; i < NUM_UIE; i++) { 65 /* This read will block */ 66 rc = read(self->fd, &data, sizeof(data)); 67 ASSERT_NE(-1, rc); 68 irq++; 69 } 70 71 EXPECT_EQ(NUM_UIE, irq); 72 73 rc = ioctl(self->fd, RTC_UIE_OFF, 0); 74 ASSERT_NE(-1, rc); 75 } 76 77 TEST_F(rtc, uie_select) { 78 int i, rc, irq = 0; 79 unsigned long data; 80 81 /* Turn on update interrupts */ 82 rc = ioctl(self->fd, RTC_UIE_ON, 0); 83 if (rc == -1) { 84 ASSERT_EQ(EINVAL, errno); 85 TH_LOG("skip update IRQs not supported."); 86 return; 87 } 88 89 for (i = 0; i < NUM_UIE; i++) { 90 struct timeval tv = { .tv_sec = 2 }; 91 fd_set readfds; 92 93 FD_ZERO(&readfds); 94 FD_SET(self->fd, &readfds); 95 /* The select will wait until an RTC interrupt happens. */ 96 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 97 ASSERT_NE(-1, rc); 98 ASSERT_NE(0, rc); 99 100 /* This read won't block */ 101 rc = read(self->fd, &data, sizeof(unsigned long)); 102 ASSERT_NE(-1, rc); 103 irq++; 104 } 105 106 EXPECT_EQ(NUM_UIE, irq); 107 108 rc = ioctl(self->fd, RTC_UIE_OFF, 0); 109 ASSERT_NE(-1, rc); 110 } 111 112 TEST_F(rtc, alarm_alm_set) { 113 struct timeval tv = { .tv_sec = ALARM_DELTA + 2 }; 114 unsigned long data; 115 struct rtc_time tm; 116 fd_set readfds; 117 time_t secs, new; 118 int rc; 119 120 rc = ioctl(self->fd, RTC_RD_TIME, &tm); 121 ASSERT_NE(-1, rc); 122 123 secs = timegm((struct tm *)&tm) + ALARM_DELTA; 124 gmtime_r(&secs, (struct tm *)&tm); 125 126 rc = ioctl(self->fd, RTC_ALM_SET, &tm); 127 if (rc == -1) { 128 ASSERT_EQ(EINVAL, errno); 129 TH_LOG("skip alarms are not supported."); 130 return; 131 } 132 133 rc = ioctl(self->fd, RTC_ALM_READ, &tm); 134 ASSERT_NE(-1, rc); 135 136 TH_LOG("Alarm time now set to %02d:%02d:%02d.", 137 tm.tm_hour, tm.tm_min, tm.tm_sec); 138 139 /* Enable alarm interrupts */ 140 rc = ioctl(self->fd, RTC_AIE_ON, 0); 141 ASSERT_NE(-1, rc); 142 143 FD_ZERO(&readfds); 144 FD_SET(self->fd, &readfds); 145 146 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 147 ASSERT_NE(-1, rc); 148 ASSERT_NE(0, rc); 149 150 /* Disable alarm interrupts */ 151 rc = ioctl(self->fd, RTC_AIE_OFF, 0); 152 ASSERT_NE(-1, rc); 153 154 rc = read(self->fd, &data, sizeof(unsigned long)); 155 ASSERT_NE(-1, rc); 156 TH_LOG("data: %lx", data); 157 158 rc = ioctl(self->fd, RTC_RD_TIME, &tm); 159 ASSERT_NE(-1, rc); 160 161 new = timegm((struct tm *)&tm); 162 ASSERT_EQ(new, secs); 163 } 164 165 TEST_F(rtc, alarm_wkalm_set) { 166 struct timeval tv = { .tv_sec = ALARM_DELTA + 2 }; 167 struct rtc_wkalrm alarm = { 0 }; 168 struct rtc_time tm; 169 unsigned long data; 170 fd_set readfds; 171 time_t secs, new; 172 int rc; 173 174 rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time); 175 ASSERT_NE(-1, rc); 176 177 secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA; 178 gmtime_r(&secs, (struct tm *)&alarm.time); 179 180 alarm.enabled = 1; 181 182 rc = ioctl(self->fd, RTC_WKALM_SET, &alarm); 183 if (rc == -1) { 184 ASSERT_EQ(EINVAL, errno); 185 TH_LOG("skip alarms are not supported."); 186 return; 187 } 188 189 rc = ioctl(self->fd, RTC_WKALM_RD, &alarm); 190 ASSERT_NE(-1, rc); 191 192 TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.", 193 alarm.time.tm_mday, alarm.time.tm_mon + 1, 194 alarm.time.tm_year + 1900, alarm.time.tm_hour, 195 alarm.time.tm_min, alarm.time.tm_sec); 196 197 FD_ZERO(&readfds); 198 FD_SET(self->fd, &readfds); 199 200 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 201 ASSERT_NE(-1, rc); 202 ASSERT_NE(0, rc); 203 204 rc = read(self->fd, &data, sizeof(unsigned long)); 205 ASSERT_NE(-1, rc); 206 207 rc = ioctl(self->fd, RTC_RD_TIME, &tm); 208 ASSERT_NE(-1, rc); 209 210 new = timegm((struct tm *)&tm); 211 ASSERT_EQ(new, secs); 212 } 213 214 TEST_F(rtc, alarm_alm_set_minute) { 215 struct timeval tv = { .tv_sec = 62 }; 216 unsigned long data; 217 struct rtc_time tm; 218 fd_set readfds; 219 time_t secs, new; 220 int rc; 221 222 rc = ioctl(self->fd, RTC_RD_TIME, &tm); 223 ASSERT_NE(-1, rc); 224 225 secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec; 226 gmtime_r(&secs, (struct tm *)&tm); 227 228 rc = ioctl(self->fd, RTC_ALM_SET, &tm); 229 if (rc == -1) { 230 ASSERT_EQ(EINVAL, errno); 231 TH_LOG("skip alarms are not supported."); 232 return; 233 } 234 235 rc = ioctl(self->fd, RTC_ALM_READ, &tm); 236 ASSERT_NE(-1, rc); 237 238 TH_LOG("Alarm time now set to %02d:%02d:%02d.", 239 tm.tm_hour, tm.tm_min, tm.tm_sec); 240 241 /* Enable alarm interrupts */ 242 rc = ioctl(self->fd, RTC_AIE_ON, 0); 243 ASSERT_NE(-1, rc); 244 245 FD_ZERO(&readfds); 246 FD_SET(self->fd, &readfds); 247 248 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 249 ASSERT_NE(-1, rc); 250 ASSERT_NE(0, rc); 251 252 /* Disable alarm interrupts */ 253 rc = ioctl(self->fd, RTC_AIE_OFF, 0); 254 ASSERT_NE(-1, rc); 255 256 rc = read(self->fd, &data, sizeof(unsigned long)); 257 ASSERT_NE(-1, rc); 258 TH_LOG("data: %lx", data); 259 260 rc = ioctl(self->fd, RTC_RD_TIME, &tm); 261 ASSERT_NE(-1, rc); 262 263 new = timegm((struct tm *)&tm); 264 ASSERT_EQ(new, secs); 265 } 266 267 TEST_F(rtc, alarm_wkalm_set_minute) { 268 struct timeval tv = { .tv_sec = 62 }; 269 struct rtc_wkalrm alarm = { 0 }; 270 struct rtc_time tm; 271 unsigned long data; 272 fd_set readfds; 273 time_t secs, new; 274 int rc; 275 276 rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time); 277 ASSERT_NE(-1, rc); 278 279 secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec; 280 gmtime_r(&secs, (struct tm *)&alarm.time); 281 282 alarm.enabled = 1; 283 284 rc = ioctl(self->fd, RTC_WKALM_SET, &alarm); 285 if (rc == -1) { 286 ASSERT_EQ(EINVAL, errno); 287 TH_LOG("skip alarms are not supported."); 288 return; 289 } 290 291 rc = ioctl(self->fd, RTC_WKALM_RD, &alarm); 292 ASSERT_NE(-1, rc); 293 294 TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.", 295 alarm.time.tm_mday, alarm.time.tm_mon + 1, 296 alarm.time.tm_year + 1900, alarm.time.tm_hour, 297 alarm.time.tm_min, alarm.time.tm_sec); 298 299 FD_ZERO(&readfds); 300 FD_SET(self->fd, &readfds); 301 302 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 303 ASSERT_NE(-1, rc); 304 ASSERT_NE(0, rc); 305 306 rc = read(self->fd, &data, sizeof(unsigned long)); 307 ASSERT_NE(-1, rc); 308 309 rc = ioctl(self->fd, RTC_RD_TIME, &tm); 310 ASSERT_NE(-1, rc); 311 312 new = timegm((struct tm *)&tm); 313 ASSERT_EQ(new, secs); 314 } 315 316 static void __attribute__((constructor)) 317 __constructor_order_last(void) 318 { 319 if (!__constructor_order) 320 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; 321 } 322 323 int main(int argc, char **argv) 324 { 325 switch (argc) { 326 case 2: 327 rtc_file = argv[1]; 328 /* FALLTHROUGH */ 329 case 1: 330 break; 331 default: 332 fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]); 333 return 1; 334 } 335 336 return test_harness_run(argc, argv); 337 } 338