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.
9 #include <KernelExport.h>
11 #include <arch/real_time_clock.h>
13 #include <real_time_clock.h>
14 #include <real_time_data.h>
22 # define TRACE(x) dprintf x
28 #define RTC_SECONDS_DAY 86400
29 #define RTC_EPOCH_JULIAN_DAY 2440588
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";
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. */
48 rtc_system_to_hw(void)
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. */
61 rtc_hw_to_system(void)
65 current_time
= arch_rtc_get_hw_time();
66 set_real_time_clock(current_time
+ (sIsGMT
? 0 : sTimezoneOffset
));
73 return arch_rtc_get_system_time_offset(sRealTimeData
);
78 rtc_debug(int argc
, char **argv
)
81 // If no arguments were given, output all useful data.
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
);
91 // If there was an argument, reset the system and hw time.
92 set_real_time_clock(strtoul(argv
[1], NULL
, 10));
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
);
108 add_debugger_command("rtc", &rtc_debug
, "Set and test the real-time clock");
113 // #pragma mark - public kernel API
117 set_real_time_clock_usecs(bigtime_t currentTime
)
119 arch_rtc_set_system_time_offset(sRealTimeData
, currentTime
- system_time());
121 real_time_clock_changed();
126 set_real_time_clock(unsigned long currentTime
)
128 set_real_time_clock_usecs((bigtime_t
)currentTime
* 1000000);
133 real_time_clock(void)
135 return (arch_rtc_get_system_time_offset(sRealTimeData
) + system_time())
141 real_time_clock_usecs(void)
143 return arch_rtc_get_system_time_offset(sRealTimeData
) + system_time();
148 get_timezone_offset(void)
150 return (time_t)(sTimezoneOffset
/ 1000000LL);
157 /*! Converts the \a tm data to seconds. Note that the base year is not
158 1900 as in POSIX, but 1970.
161 rtc_tm_to_secs(const struct tm
*tm
)
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
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
;
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;
196 month
= month
+ 2 - 12 * l
;
197 year
= 100 * (n
- 49) + year
+ l
;
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
216 _user_system_time(void)
218 syscall_64_bit_return_value();
220 return system_time();
225 _user_set_real_time_clock(bigtime_t time
)
228 return B_NOT_ALLOWED
;
230 set_real_time_clock_usecs(time
);
236 _user_set_timezone(int32 timezoneOffset
, const char *name
, size_t nameLength
)
238 bigtime_t offset
= (bigtime_t
)timezoneOffset
* 1000000LL;
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
,
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.
257 arch_rtc_set_system_time_offset(sRealTimeData
,
258 arch_rtc_get_system_time_offset(sRealTimeData
) + sTimezoneOffset
260 real_time_clock_changed();
263 sTimezoneOffset
= offset
;
265 TRACE(("new system_time_offset %Ld\n",
266 arch_rtc_get_system_time_offset(sRealTimeData
)));
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
;
283 && (!IS_USER_ADDRESS(userName
)
284 || user_strlcpy(userName
, sTimezoneName
, nameLength
) < 0))
285 return B_BAD_ADDRESS
;
292 _user_set_real_time_clock_is_gmt(bool isGMT
)
294 // store previous value
295 bool wasGMT
= sIsGMT
;
297 return B_NOT_ALLOWED
;
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();
313 _user_get_real_time_clock_is_gmt(bool *_userIsGMT
)
315 if (_userIsGMT
== NULL
)
318 if (_userIsGMT
!= NULL
319 && (!IS_USER_ADDRESS(_userIsGMT
)
320 || user_memcpy(_userIsGMT
, &sIsGMT
, sizeof(bool)) != B_OK
))
321 return B_BAD_ADDRESS
;