btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / real_time_clock.cpp
blob7db88c24f964ac44423cb9a4b3101f04e4be38e8
1 /*
2 * Copyright 2004-2009, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
9 #include <KernelExport.h>
11 #include <arch/real_time_clock.h>
12 #include <commpage.h>
13 #include <real_time_clock.h>
14 #include <real_time_data.h>
15 #include <syscalls.h>
16 #include <thread.h>
18 #include <stdlib.h>
20 //#define TRACE_TIME
21 #ifdef TRACE_TIME
22 # define TRACE(x) dprintf x
23 #else
24 # define TRACE(x)
25 #endif
28 #define RTC_SECONDS_DAY 86400
29 #define RTC_EPOCH_JULIAN_DAY 2440588
30 // January 1st, 1970
32 static struct real_time_data *sRealTimeData;
33 static bool sIsGMT = false;
34 static bigtime_t sTimezoneOffset = 0;
35 static char sTimezoneName[B_FILE_NAME_LENGTH] = "GMT";
38 static void
39 real_time_clock_changed()
41 timer_real_time_clock_changed();
42 user_timer_real_time_clock_changed();
46 /*! Write the system time to CMOS. */
47 static void
48 rtc_system_to_hw(void)
50 uint32 seconds;
52 seconds = (arch_rtc_get_system_time_offset(sRealTimeData) + system_time()
53 + (sIsGMT ? 0 : sTimezoneOffset)) / 1000000;
55 arch_rtc_set_hw_time(seconds);
59 /*! Read the CMOS clock and update the system time accordingly. */
60 static void
61 rtc_hw_to_system(void)
63 uint32 current_time;
65 current_time = arch_rtc_get_hw_time();
66 set_real_time_clock(current_time + (sIsGMT ? 0 : sTimezoneOffset));
70 bigtime_t
71 rtc_boot_time(void)
73 return arch_rtc_get_system_time_offset(sRealTimeData);
77 static int
78 rtc_debug(int argc, char **argv)
80 if (argc < 2) {
81 // If no arguments were given, output all useful data.
82 uint32 currentTime;
83 bigtime_t systemTimeOffset
84 = arch_rtc_get_system_time_offset(sRealTimeData);
86 currentTime = (systemTimeOffset + system_time()) / 1000000;
87 dprintf("system_time: %" B_PRId64 "\n", system_time());
88 dprintf("system_time_offset: %" B_PRId64 "\n", systemTimeOffset);
89 dprintf("current_time: %" B_PRIu32 "\n", currentTime);
90 } else {
91 // If there was an argument, reset the system and hw time.
92 set_real_time_clock(strtoul(argv[1], NULL, 10));
95 return 0;
99 status_t
100 rtc_init(kernel_args *args)
102 sRealTimeData = (struct real_time_data*)allocate_commpage_entry(
103 COMMPAGE_ENTRY_REAL_TIME_DATA, sizeof(struct real_time_data));
105 arch_rtc_init(args, sRealTimeData);
106 rtc_hw_to_system();
108 add_debugger_command("rtc", &rtc_debug, "Set and test the real-time clock");
109 return B_OK;
113 // #pragma mark - public kernel API
116 void
117 set_real_time_clock_usecs(bigtime_t currentTime)
119 arch_rtc_set_system_time_offset(sRealTimeData, currentTime - system_time());
120 rtc_system_to_hw();
121 real_time_clock_changed();
125 void
126 set_real_time_clock(unsigned long currentTime)
128 set_real_time_clock_usecs((bigtime_t)currentTime * 1000000);
132 unsigned long
133 real_time_clock(void)
135 return (arch_rtc_get_system_time_offset(sRealTimeData) + system_time())
136 / 1000000;
140 bigtime_t
141 real_time_clock_usecs(void)
143 return arch_rtc_get_system_time_offset(sRealTimeData) + system_time();
147 uint32
148 get_timezone_offset(void)
150 return (time_t)(sTimezoneOffset / 1000000LL);
154 // #pragma mark -
157 /*! Converts the \a tm data to seconds. Note that the base year is not
158 1900 as in POSIX, but 1970.
160 uint32
161 rtc_tm_to_secs(const struct tm *tm)
163 uint32 days;
164 int year, month;
166 month = tm->tm_mon + 1;
167 year = tm->tm_year + RTC_EPOCH_BASE_YEAR;
169 // Reference: Fliegel, H. F. and van Flandern, T. C. (1968).
170 // Communications of the ACM, Vol. 11, No. 10 (October, 1968).
171 days = tm->tm_mday - 32075 - RTC_EPOCH_JULIAN_DAY
172 + 1461 * (year + 4800 + (month - 14) / 12) / 4
173 + 367 * (month - 2 - 12 * ((month - 14) / 12)) / 12
174 - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4;
176 return days * RTC_SECONDS_DAY + tm->tm_hour * 3600 + tm->tm_min * 60
177 + tm->tm_sec;
181 void
182 rtc_secs_to_tm(uint32 seconds, struct tm *t)
184 uint32 year, month, day, l, n;
186 // Reference: Fliegel, H. F. and van Flandern, T. C. (1968).
187 // Communications of the ACM, Vol. 11, No. 10 (October, 1968).
188 l = seconds / 86400 + 68569 + RTC_EPOCH_JULIAN_DAY;
189 n = 4 * l / 146097;
190 l = l - (146097 * n + 3) / 4;
191 year = 4000 * (l + 1) / 1461001;
192 l = l - 1461 * year / 4 + 31;
193 month = 80 * l / 2447;
194 day = l - 2447 * month / 80;
195 l = month / 11;
196 month = month + 2 - 12 * l;
197 year = 100 * (n - 49) + year + l;
199 t->tm_mday = day;
200 t->tm_mon = month - 1;
201 t->tm_year = year - RTC_EPOCH_BASE_YEAR;
203 seconds = seconds % RTC_SECONDS_DAY;
204 t->tm_hour = seconds / 3600;
206 seconds = seconds % 3600;
207 t->tm_min = seconds / 60;
208 t->tm_sec = seconds % 60;
212 // #pragma mark - syscalls
215 bigtime_t
216 _user_system_time(void)
218 syscall_64_bit_return_value();
220 return system_time();
224 status_t
225 _user_set_real_time_clock(bigtime_t time)
227 if (geteuid() != 0)
228 return B_NOT_ALLOWED;
230 set_real_time_clock_usecs(time);
231 return B_OK;
235 status_t
236 _user_set_timezone(int32 timezoneOffset, const char *name, size_t nameLength)
238 bigtime_t offset = (bigtime_t)timezoneOffset * 1000000LL;
240 if (geteuid() != 0)
241 return B_NOT_ALLOWED;
243 TRACE(("old system_time_offset %Ld old %Ld new %Ld gmt %d\n",
244 arch_rtc_get_system_time_offset(sRealTimeData), sTimezoneOffset,
245 offset, sIsGMT));
247 if (name != NULL && nameLength > 0) {
248 if (!IS_USER_ADDRESS(name)
249 || user_strlcpy(sTimezoneName, name, sizeof(sTimezoneName)) < 0)
250 return B_BAD_ADDRESS;
253 // We only need to update our time offset if the hardware clock
254 // does not run in the local timezone.
255 // Since this is shared data, we need to update it atomically.
256 if (!sIsGMT) {
257 arch_rtc_set_system_time_offset(sRealTimeData,
258 arch_rtc_get_system_time_offset(sRealTimeData) + sTimezoneOffset
259 - offset);
260 real_time_clock_changed();
263 sTimezoneOffset = offset;
265 TRACE(("new system_time_offset %Ld\n",
266 arch_rtc_get_system_time_offset(sRealTimeData)));
268 return B_OK;
272 status_t
273 _user_get_timezone(int32 *_timezoneOffset, char *userName, size_t nameLength)
275 int32 offset = (int32)(sTimezoneOffset / 1000000LL);
277 if (_timezoneOffset != NULL
278 && (!IS_USER_ADDRESS(_timezoneOffset)
279 || user_memcpy(_timezoneOffset, &offset, sizeof(time_t)) < B_OK))
280 return B_BAD_ADDRESS;
282 if (userName != NULL
283 && (!IS_USER_ADDRESS(userName)
284 || user_strlcpy(userName, sTimezoneName, nameLength) < 0))
285 return B_BAD_ADDRESS;
287 return B_OK;
291 status_t
292 _user_set_real_time_clock_is_gmt(bool isGMT)
294 // store previous value
295 bool wasGMT = sIsGMT;
296 if (geteuid() != 0)
297 return B_NOT_ALLOWED;
299 sIsGMT = isGMT;
301 if (wasGMT != sIsGMT) {
302 arch_rtc_set_system_time_offset(sRealTimeData,
303 arch_rtc_get_system_time_offset(sRealTimeData)
304 + (sIsGMT ? 1 : -1) * sTimezoneOffset);
305 real_time_clock_changed();
308 return B_OK;
312 status_t
313 _user_get_real_time_clock_is_gmt(bool *_userIsGMT)
315 if (_userIsGMT == NULL)
316 return B_BAD_VALUE;
318 if (_userIsGMT != NULL
319 && (!IS_USER_ADDRESS(_userIsGMT)
320 || user_memcpy(_userIsGMT, &sIsGMT, sizeof(bool)) != B_OK))
321 return B_BAD_ADDRESS;
323 return B_OK;