1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <errno.h> 18 #include <fcntl.h> 19 #include <linux/rtc.h> 20 #include <sys/ioctl.h> 21 #include <sys/stat.h> 22 #include <sys/types.h> 23 #include <unistd.h> 24 25 #include <gtest/gtest.h> 26 27 static int hwtime(int flag, int request, struct rtc_time *tm) { 28 static const char rtc[] = "/dev/rtc0"; 29 30 int ret = TEMP_FAILURE_RETRY(access(rtc, flag & O_WRONLY) ? W_OK : R_OK); 31 if (ret < 0) { 32 ret = -errno; 33 } 34 if (ret == -EACCES) { 35 return ret; 36 } 37 38 if (flag & O_WRONLY) { 39 struct stat st; 40 ret = TEMP_FAILURE_RETRY(stat(rtc, &st)); 41 if (ret < 0) { 42 ret = -errno; 43 } else if (!(st.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))) { 44 ret = -EACCES; 45 } 46 } 47 if (ret == -EACCES) { 48 return ret; 49 } 50 51 do { 52 ret = TEMP_FAILURE_RETRY(open(rtc, flag)); 53 if (ret < 0) { 54 ret = -errno; 55 } 56 } while (ret == -EBUSY); 57 if (ret < 0) { 58 return ret; 59 } 60 61 int fd = ret; 62 do { 63 ret = TEMP_FAILURE_RETRY(ioctl(fd, request, tm)); 64 if (ret < 0) { 65 ret = -errno; 66 } 67 } while (ret == -EBUSY); 68 close(fd); 69 return ret; 70 } 71 72 static int rd_hwtime(struct rtc_time *tm) { 73 return hwtime(O_RDONLY, RTC_RD_TIME, tm); 74 } 75 76 static int set_hwtime(struct rtc_time *tm) { 77 return hwtime(O_WRONLY, RTC_SET_TIME, tm); 78 } 79 80 static void rtc_rollover(int start, int end) { 81 struct rtc_time roll; 82 memset(&roll, 0, sizeof(roll)); 83 ASSERT_LE(0, rd_hwtime(&roll)); 84 int mday[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 85 mday[1] = (roll.tm_year % 4) ? 28 : 29; 86 ASSERT_LE(0, roll.tm_sec); 87 ASSERT_GT(60, roll.tm_sec); 88 ASSERT_LE(0, roll.tm_min); 89 ASSERT_GT(60, roll.tm_min); 90 ASSERT_LE(0, roll.tm_hour); 91 ASSERT_GT(24, roll.tm_hour); 92 ASSERT_LE(0, roll.tm_mday); 93 ASSERT_GE(mday[roll.tm_mon], roll.tm_mday); 94 ASSERT_LE(0, roll.tm_mon); 95 ASSERT_GT(12, roll.tm_mon); 96 ASSERT_LE(0, roll.tm_year); 97 ASSERT_GT(138, roll.tm_year); 98 99 // Wait for granular clock 100 struct rtc_time save = roll; 101 static const useconds_t timeout_sleep = 10000; 102 static const int timeout_num = 2000000 / timeout_sleep; 103 int timeout; 104 for (timeout = timeout_num; timeout && (roll.tm_year == save.tm_year); --timeout) { 105 ASSERT_LE(0, rd_hwtime(&save)); 106 usleep(timeout_sleep); 107 } 108 109 memset(&roll, 0, sizeof(roll)); 110 roll.tm_sec = 59; 111 roll.tm_min = 59; 112 roll.tm_hour = 23; 113 roll.tm_mday = 31; 114 roll.tm_mon = 11; 115 roll.tm_year = 70; 116 roll.tm_wday = 0; 117 roll.tm_yday = 0; 118 roll.tm_isdst = 0; 119 120 bool eacces = true; 121 for (roll.tm_year = start; roll.tm_year < end; ++roll.tm_year) { 122 struct rtc_time tm = roll; 123 int __set_hwtime = set_hwtime(&tm); 124 // Allowed to be 100% denied for writing 125 if ((__set_hwtime == -EACCES) && (eacces == true)) { 126 continue; 127 } 128 eacces = false; 129 // below 2016, permitted to error out. 130 if ((__set_hwtime == -EINVAL) && (roll.tm_year < 116)) { 131 continue; 132 } 133 ASSERT_LE(0, __set_hwtime); 134 ASSERT_LE(0, rd_hwtime(&tm)); 135 ASSERT_EQ(roll.tm_sec, tm.tm_sec); 136 ASSERT_EQ(roll.tm_min, tm.tm_min); 137 ASSERT_EQ(roll.tm_hour, tm.tm_hour); 138 ASSERT_EQ(roll.tm_mday, tm.tm_mday); 139 ASSERT_EQ(roll.tm_mon, tm.tm_mon); 140 ASSERT_EQ(roll.tm_year, tm.tm_year); 141 for (timeout = timeout_num; timeout && (roll.tm_year == tm.tm_year); --timeout) { 142 ASSERT_LE(0, rd_hwtime(&tm)); 143 usleep(timeout_sleep); 144 } 145 ASSERT_EQ(roll.tm_year + 1, tm.tm_year); 146 EXPECT_LT(timeout_num * 5 / 100, timeout); 147 EXPECT_GT(timeout_num * 95 / 100, timeout); 148 149 // correct saved time to compensate for rollover check 150 if (++save.tm_sec >= 60) { 151 save.tm_sec = 0; 152 if (++save.tm_min >= 60) { 153 save.tm_min = 0; 154 if (++save.tm_hour >= 24) { 155 save.tm_hour = 0; 156 mday[1] = (save.tm_year % 4) ? 28 : 29; 157 if (++save.tm_mday >= mday[save.tm_mon]) { 158 save.tm_mday = 1; 159 if (++save.tm_mon >= 12) { 160 save.tm_mon = 0; 161 ++save.tm_year; 162 } 163 } 164 } 165 } 166 } 167 } 168 169 if (!eacces) { 170 ASSERT_LE(0, set_hwtime(&save)); 171 } 172 ASSERT_LE(0, rd_hwtime(&roll)); 173 174 if (!eacces) { 175 ASSERT_EQ(save.tm_sec, roll.tm_sec); 176 ASSERT_EQ(save.tm_min, roll.tm_min); 177 ASSERT_EQ(save.tm_hour, roll.tm_hour); 178 ASSERT_EQ(save.tm_mday, roll.tm_mday); 179 ASSERT_EQ(save.tm_mon, roll.tm_mon); 180 ASSERT_EQ(save.tm_year, roll.tm_year); 181 } 182 } 183 184 TEST(time, rtc_rollover_1970_1990) { 185 rtc_rollover(70, 90); 186 } 187 188 TEST(time, rtc_rollover_1990_2010) { 189 rtc_rollover(90, 110); 190 } 191 192 TEST(time, rtc_rollover_2010_2030) { 193 rtc_rollover(110, 130); 194 } 195 196 TEST(time, rtc_rollover_2030_2037) { 197 rtc_rollover(130, 137); 198 } 199