2006-03-14 17:29:56 +03:00
|
|
|
/*
|
2007-10-14 20:55:32 +04:00
|
|
|
* Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
2004-11-26 18:03:28 +03:00
|
|
|
* Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved.
|
|
|
|
*
|
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*/
|
2003-10-24 14:21:10 +04:00
|
|
|
|
|
|
|
|
2004-11-08 14:48:04 +03:00
|
|
|
#include <KernelExport.h>
|
2003-10-24 14:21:10 +04:00
|
|
|
|
|
|
|
#include <arch/real_time_clock.h>
|
2008-01-11 03:36:44 +03:00
|
|
|
#include <commpage.h>
|
2003-10-24 14:21:10 +04:00
|
|
|
#include <real_time_clock.h>
|
2004-11-08 14:48:04 +03:00
|
|
|
#include <real_time_data.h>
|
2006-03-14 23:29:50 +03:00
|
|
|
#include <syscalls.h>
|
2008-01-11 03:36:44 +03:00
|
|
|
#include <thread.h>
|
2004-11-08 14:48:04 +03:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
2003-10-24 14:21:10 +04:00
|
|
|
|
2008-03-24 04:01:51 +03:00
|
|
|
#define TRACE_TIME
|
2004-11-25 01:54:50 +03:00
|
|
|
#ifdef TRACE_TIME
|
|
|
|
# define TRACE(x) dprintf x
|
|
|
|
#else
|
|
|
|
# define TRACE(x)
|
|
|
|
#endif
|
2003-10-24 16:01:25 +04:00
|
|
|
|
2004-11-26 18:03:28 +03:00
|
|
|
|
2007-10-14 21:27:44 +04:00
|
|
|
#define RTC_SECONDS_DAY 86400
|
|
|
|
#define RTC_EPOCH_JULIAN_DAY 2440588
|
|
|
|
// January 1st, 1970
|
|
|
|
|
2004-11-08 14:48:04 +03:00
|
|
|
static struct real_time_data *sRealTimeData;
|
2004-11-23 03:16:24 +03:00
|
|
|
static bool sIsGMT = false;
|
2004-11-26 18:03:28 +03:00
|
|
|
static char sTimezoneFilename[B_PATH_NAME_LENGTH] = "";
|
2004-11-25 01:54:50 +03:00
|
|
|
static bigtime_t sTimezoneOffset = 0;
|
2004-11-23 03:16:24 +03:00
|
|
|
static bool sDaylightSavingTime = false;
|
2003-10-24 16:01:25 +04:00
|
|
|
|
2003-10-24 14:21:10 +04:00
|
|
|
|
2007-10-14 20:55:32 +04:00
|
|
|
/*! Write the system time to CMOS. */
|
2003-10-24 16:01:25 +04:00
|
|
|
static void
|
|
|
|
rtc_system_to_hw(void)
|
|
|
|
{
|
2003-10-24 14:21:10 +04:00
|
|
|
uint32 seconds;
|
2003-10-24 16:01:25 +04:00
|
|
|
|
2007-10-14 20:55:32 +04:00
|
|
|
seconds = (arch_rtc_get_system_time_offset(sRealTimeData) + system_time()
|
2006-05-13 19:41:33 +04:00
|
|
|
+ (sIsGMT ? 0 : sTimezoneOffset)) / 1000000;
|
2007-10-14 20:55:32 +04:00
|
|
|
|
2003-10-24 14:21:10 +04:00
|
|
|
arch_rtc_set_hw_time(seconds);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-14 20:55:32 +04:00
|
|
|
/*! Read the CMOS clock and update the system time accordingly. */
|
2003-10-24 16:01:25 +04:00
|
|
|
static void
|
|
|
|
rtc_hw_to_system(void)
|
|
|
|
{
|
2003-10-24 14:21:10 +04:00
|
|
|
uint32 current_time;
|
2003-10-24 16:01:25 +04:00
|
|
|
|
2003-10-24 14:21:10 +04:00
|
|
|
current_time = arch_rtc_get_hw_time();
|
2004-11-23 03:16:24 +03:00
|
|
|
set_real_time_clock(current_time + (sIsGMT ? 0 : sTimezoneOffset));
|
2003-10-24 14:21:10 +04:00
|
|
|
}
|
|
|
|
|
2003-10-24 16:01:25 +04:00
|
|
|
|
2003-10-24 14:21:10 +04:00
|
|
|
bigtime_t
|
2004-11-29 02:13:22 +03:00
|
|
|
rtc_boot_time(void)
|
2003-10-24 16:01:25 +04:00
|
|
|
{
|
2006-01-04 05:17:59 +03:00
|
|
|
return arch_rtc_get_system_time_offset(sRealTimeData);
|
2003-10-24 14:21:10 +04:00
|
|
|
}
|
2003-10-24 16:09:20 +04:00
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
rtc_debug(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if (argc < 2) {
|
2004-11-26 18:03:28 +03:00
|
|
|
// If no arguments were given, output all useful data.
|
|
|
|
uint32 currentTime;
|
2006-01-04 05:17:59 +03:00
|
|
|
bigtime_t systemTimeOffset
|
|
|
|
= arch_rtc_get_system_time_offset(sRealTimeData);
|
2007-10-14 20:55:32 +04:00
|
|
|
|
2006-01-04 05:17:59 +03:00
|
|
|
currentTime = (systemTimeOffset + system_time()) / 1000000;
|
2004-11-26 18:03:28 +03:00
|
|
|
dprintf("system_time: %Ld\n", system_time());
|
2006-01-04 05:17:59 +03:00
|
|
|
dprintf("system_time_offset: %Ld\n", systemTimeOffset);
|
2004-11-26 18:03:28 +03:00
|
|
|
dprintf("current_time: %lu\n", currentTime);
|
2003-10-24 16:09:20 +04:00
|
|
|
} else {
|
|
|
|
// If there was an argument, reset the system and hw time.
|
2003-10-28 16:12:13 +03:00
|
|
|
set_real_time_clock(strtoul(argv[1], NULL, 10));
|
2003-10-24 16:09:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
2004-11-09 16:50:52 +03:00
|
|
|
rtc_init(kernel_args *args)
|
2003-10-24 16:09:20 +04:00
|
|
|
{
|
2008-01-11 03:36:44 +03:00
|
|
|
sRealTimeData = (struct real_time_data*)allocate_commpage_entry(
|
|
|
|
COMMPAGE_ENTRY_REAL_TIME_DATA, sizeof(struct real_time_data));
|
2004-11-06 20:37:00 +03:00
|
|
|
|
2005-01-26 21:30:50 +03:00
|
|
|
arch_rtc_init(args, sRealTimeData);
|
2003-10-24 16:09:20 +04:00
|
|
|
rtc_hw_to_system();
|
|
|
|
|
2004-11-08 14:48:04 +03:00
|
|
|
add_debugger_command("rtc", &rtc_debug, "Set and test the real-time clock");
|
2003-10-24 16:09:20 +04:00
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
2003-10-24 18:53:02 +04:00
|
|
|
|
2006-03-14 23:22:50 +03:00
|
|
|
// #pragma mark - public kernel API
|
2003-10-24 18:53:02 +04:00
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
set_real_time_clock(uint32 currentTime)
|
|
|
|
{
|
2006-01-04 05:17:59 +03:00
|
|
|
arch_rtc_set_system_time_offset(sRealTimeData,
|
|
|
|
currentTime * 1000000LL - system_time());
|
2004-03-15 00:53:48 +03:00
|
|
|
rtc_system_to_hw();
|
2003-10-24 18:53:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-11-08 05:13:19 +03:00
|
|
|
uint32
|
|
|
|
real_time_clock(void)
|
|
|
|
{
|
2006-01-04 05:17:59 +03:00
|
|
|
return (arch_rtc_get_system_time_offset(sRealTimeData) + system_time())
|
|
|
|
/ 1000000;
|
2003-11-08 05:13:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bigtime_t
|
|
|
|
real_time_clock_usecs(void)
|
|
|
|
{
|
2006-01-04 05:17:59 +03:00
|
|
|
return arch_rtc_get_system_time_offset(sRealTimeData) + system_time();
|
2004-11-22 03:32:22 +03:00
|
|
|
}
|
|
|
|
|
2004-11-26 18:03:28 +03:00
|
|
|
|
2004-12-15 12:23:50 +03:00
|
|
|
status_t
|
|
|
|
get_rtc_info(rtc_info *info)
|
|
|
|
{
|
|
|
|
if (info == NULL)
|
|
|
|
return B_BAD_VALUE;
|
2007-10-14 20:55:32 +04:00
|
|
|
|
2004-12-15 12:23:50 +03:00
|
|
|
info->time = real_time_clock();
|
|
|
|
info->is_gmt = sIsGMT;
|
2006-08-27 23:03:50 +04:00
|
|
|
info->tz_minuteswest = sTimezoneOffset / 1000000LL;
|
2004-12-15 12:23:50 +03:00
|
|
|
info->tz_dsttime = sDaylightSavingTime;
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-03 20:26:37 +03:00
|
|
|
// #pragma mark -
|
|
|
|
|
|
|
|
|
2007-10-14 21:27:44 +04:00
|
|
|
/*! Converts the \a tm data to seconds. Note that the base year is not
|
|
|
|
1900 as in POSIX, but 1970.
|
|
|
|
*/
|
2006-01-03 20:26:37 +03:00
|
|
|
uint32
|
2007-10-14 21:27:44 +04:00
|
|
|
rtc_tm_to_secs(const struct tm *tm)
|
2006-01-03 20:26:37 +03:00
|
|
|
{
|
2007-10-14 21:27:44 +04:00
|
|
|
uint32 days;
|
|
|
|
int year, month;
|
2006-01-03 20:26:37 +03:00
|
|
|
|
2007-10-14 21:27:44 +04:00
|
|
|
month = tm->tm_mon + 1;
|
|
|
|
year = tm->tm_year + RTC_EPOCH_BASE_YEAR;
|
2006-01-03 20:26:37 +03:00
|
|
|
|
2007-10-14 21:27:44 +04:00
|
|
|
// Reference: Fliegel, H. F. and van Flandern, T. C. (1968).
|
|
|
|
// Communications of the ACM, Vol. 11, No. 10 (October, 1968).
|
|
|
|
days = tm->tm_mday - 32075 - RTC_EPOCH_JULIAN_DAY
|
|
|
|
+ 1461 * (year + 4800 + (month - 14) / 12) / 4
|
|
|
|
+ 367 * (month - 2 - 12 * ((month - 14) / 12)) / 12
|
|
|
|
- 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4;
|
2006-01-03 20:26:37 +03:00
|
|
|
|
2007-10-14 21:27:44 +04:00
|
|
|
return days * RTC_SECONDS_DAY + tm->tm_hour * 3600 + tm->tm_min * 60
|
|
|
|
+ tm->tm_sec;
|
2006-01-03 20:26:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
rtc_secs_to_tm(uint32 seconds, struct tm *t)
|
|
|
|
{
|
2007-10-14 21:27:44 +04:00
|
|
|
uint32 year, month, day, l, n;
|
|
|
|
|
|
|
|
// Reference: Fliegel, H. F. and van Flandern, T. C. (1968).
|
|
|
|
// Communications of the ACM, Vol. 11, No. 10 (October, 1968).
|
|
|
|
l = seconds / 86400 + 68569 + RTC_EPOCH_JULIAN_DAY;
|
|
|
|
n = 4 * l / 146097;
|
|
|
|
l = l - (146097 * n + 3) / 4;
|
|
|
|
year = 4000 * (l + 1) / 1461001;
|
|
|
|
l = l - 1461 * year / 4 + 31;
|
|
|
|
month = 80 * l / 2447;
|
|
|
|
day = l - 2447 * month / 80;
|
|
|
|
l = month / 11;
|
|
|
|
month = month + 2 - 12 * l;
|
|
|
|
year = 100 * (n - 49) + year + l;
|
|
|
|
|
|
|
|
t->tm_mday = day;
|
|
|
|
t->tm_mon = month - 1;
|
|
|
|
t->tm_year = year - RTC_EPOCH_BASE_YEAR;
|
|
|
|
|
|
|
|
seconds = seconds % RTC_SECONDS_DAY;
|
2006-01-03 20:26:37 +03:00
|
|
|
t->tm_hour = seconds / 3600;
|
2007-10-14 21:27:44 +04:00
|
|
|
|
2006-01-03 20:26:37 +03:00
|
|
|
seconds = seconds % 3600;
|
|
|
|
t->tm_min = seconds / 60;
|
2007-10-14 21:27:44 +04:00
|
|
|
t->tm_sec = seconds % 60;
|
2006-01-03 20:26:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-10-24 18:53:02 +04:00
|
|
|
// #pragma mark -
|
2006-03-14 23:22:50 +03:00
|
|
|
|
|
|
|
|
2007-10-14 20:55:32 +04:00
|
|
|
/*! This is called from the gettimeofday() implementation that's part of the
|
|
|
|
kernel.
|
|
|
|
*/
|
2006-03-14 23:22:50 +03:00
|
|
|
status_t
|
|
|
|
_kern_get_timezone(time_t *_timezoneOffset, bool *_daylightSavingTime)
|
|
|
|
{
|
|
|
|
*_timezoneOffset = (time_t)(sTimezoneOffset / 1000000LL);
|
|
|
|
*_daylightSavingTime = sDaylightSavingTime;
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// #pragma mark - syscalls
|
2003-10-24 18:53:02 +04:00
|
|
|
|
|
|
|
|
2004-11-26 18:03:28 +03:00
|
|
|
bigtime_t
|
|
|
|
_user_system_time(void)
|
|
|
|
{
|
2008-01-11 03:36:44 +03:00
|
|
|
syscall_64_bit_return_value();
|
|
|
|
|
2004-11-26 18:03:28 +03:00
|
|
|
return system_time();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-11-05 19:08:00 +03:00
|
|
|
status_t
|
2003-10-24 18:53:02 +04:00
|
|
|
_user_set_real_time_clock(uint32 time)
|
|
|
|
{
|
2004-11-05 19:08:00 +03:00
|
|
|
if (geteuid() != 0)
|
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
2003-10-24 18:53:02 +04:00
|
|
|
set_real_time_clock(time);
|
2004-11-05 19:08:00 +03:00
|
|
|
return B_OK;
|
2003-10-24 18:53:02 +04:00
|
|
|
}
|
|
|
|
|
2004-11-22 03:32:22 +03:00
|
|
|
|
|
|
|
status_t
|
2004-11-26 18:03:28 +03:00
|
|
|
_user_set_timezone(time_t timezoneOffset, bool daylightSavingTime)
|
2004-11-22 03:32:22 +03:00
|
|
|
{
|
2004-11-26 18:03:28 +03:00
|
|
|
bigtime_t offset = (bigtime_t)timezoneOffset * 1000000LL;
|
|
|
|
|
2004-11-22 03:32:22 +03:00
|
|
|
if (geteuid() != 0)
|
|
|
|
return B_NOT_ALLOWED;
|
2004-11-26 18:03:28 +03:00
|
|
|
|
2006-01-04 05:17:59 +03:00
|
|
|
TRACE(("old system_time_offset %Ld old %Ld new %Ld gmt %d\n",
|
2007-10-14 20:55:32 +04:00
|
|
|
arch_rtc_get_system_time_offset(sRealTimeData), sTimezoneOffset,
|
|
|
|
offset, sIsGMT));
|
2004-11-26 18:03:28 +03:00
|
|
|
|
|
|
|
// We only need to update our time offset if the hardware clock
|
|
|
|
// does not run in the local timezone.
|
|
|
|
// Since this is shared data, we need to update it atomically.
|
2006-01-04 05:17:59 +03:00
|
|
|
if (!sIsGMT) {
|
|
|
|
arch_rtc_set_system_time_offset(sRealTimeData,
|
2007-10-14 20:55:32 +04:00
|
|
|
arch_rtc_get_system_time_offset(sRealTimeData) + sTimezoneOffset
|
|
|
|
- offset);
|
2006-01-04 05:17:59 +03:00
|
|
|
}
|
2004-11-26 18:03:28 +03:00
|
|
|
|
|
|
|
sTimezoneOffset = offset;
|
|
|
|
sDaylightSavingTime = daylightSavingTime;
|
|
|
|
|
2006-01-04 05:17:59 +03:00
|
|
|
TRACE(("new system_time_offset %Ld\n",
|
|
|
|
arch_rtc_get_system_time_offset(sRealTimeData)));
|
2008-03-24 04:01:51 +03:00
|
|
|
|
2004-11-22 20:14:06 +03:00
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-14 17:29:56 +03:00
|
|
|
status_t
|
|
|
|
_user_get_timezone(time_t *_timezoneOffset, bool *_daylightSavingTime)
|
|
|
|
{
|
|
|
|
time_t offset = (time_t)(sTimezoneOffset / 1000000LL);
|
|
|
|
|
2007-10-14 20:55:32 +04:00
|
|
|
if (!IS_USER_ADDRESS(_timezoneOffset)
|
|
|
|
|| !IS_USER_ADDRESS(_daylightSavingTime)
|
2006-03-14 17:29:56 +03:00
|
|
|
|| user_memcpy(_timezoneOffset, &offset, sizeof(time_t)) < B_OK
|
2007-10-14 20:55:32 +04:00
|
|
|
|| user_memcpy(_daylightSavingTime, &sDaylightSavingTime,
|
|
|
|
sizeof(bool)) < B_OK)
|
2006-03-14 17:29:56 +03:00
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-11-22 20:14:06 +03:00
|
|
|
status_t
|
2004-11-26 18:03:28 +03:00
|
|
|
_user_set_tzfilename(const char *filename, size_t length, bool isGMT)
|
2004-11-22 20:14:06 +03:00
|
|
|
{
|
2008-03-24 04:01:51 +03:00
|
|
|
// store previous value
|
|
|
|
bool wasGMT = sIsGMT;
|
2004-11-22 20:14:06 +03:00
|
|
|
if (geteuid() != 0)
|
|
|
|
return B_NOT_ALLOWED;
|
2004-11-26 18:03:28 +03:00
|
|
|
|
2004-11-22 20:14:06 +03:00
|
|
|
if (!IS_USER_ADDRESS(filename)
|
|
|
|
|| filename == NULL
|
2007-10-14 20:55:32 +04:00
|
|
|
|| user_strlcpy(sTimezoneFilename, filename,
|
|
|
|
B_PATH_NAME_LENGTH) < B_OK)
|
2004-11-22 20:14:06 +03:00
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
2004-11-26 18:03:28 +03:00
|
|
|
sIsGMT = isGMT;
|
2008-03-24 04:01:51 +03:00
|
|
|
|
|
|
|
if (wasGMT != sIsGMT)
|
|
|
|
arch_rtc_set_system_time_offset(sRealTimeData,
|
|
|
|
arch_rtc_get_system_time_offset(sRealTimeData) + (((sIsGMT) ? 1 : -1) * sTimezoneOffset));
|
|
|
|
|
2004-11-22 03:32:22 +03:00
|
|
|
return B_OK;
|
|
|
|
}
|
2004-11-22 20:14:06 +03:00
|
|
|
|
2004-11-23 03:16:24 +03:00
|
|
|
|
|
|
|
status_t
|
2004-11-26 18:03:28 +03:00
|
|
|
_user_get_tzfilename(char *filename, size_t length, bool *_isGMT)
|
2004-11-23 03:16:24 +03:00
|
|
|
{
|
2004-11-26 18:03:28 +03:00
|
|
|
if (filename == NULL || _isGMT == NULL
|
|
|
|
|| !IS_USER_ADDRESS(filename) || !IS_USER_ADDRESS(_isGMT)
|
|
|
|
|| user_strlcpy(filename, sTimezoneFilename, length) < B_OK
|
|
|
|
|| user_memcpy(_isGMT, &sIsGMT, sizeof(bool)) < B_OK)
|
2004-11-23 03:16:24 +03:00
|
|
|
return B_BAD_ADDRESS;
|
2004-11-26 18:03:28 +03:00
|
|
|
|
2004-11-23 03:16:24 +03:00
|
|
|
return B_OK;
|
|
|
|
}
|
2004-12-15 12:23:50 +03:00
|
|
|
|