|
@@ -1,7 +1,7 @@
|
|
|
/*
|
|
|
* clock.c
|
|
|
*
|
|
|
- * Copyright (C) 2013 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
|
|
|
+ * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
|
|
|
*
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
* it under the terms of the GNU Affero General Public License as
|
|
@@ -22,86 +22,77 @@
|
|
|
#include <syscalls.h>
|
|
|
#include <irq.h>
|
|
|
#include <user.h>
|
|
|
+#include <timer.h>
|
|
|
+#include <log.h>
|
|
|
|
|
|
-static clock_time_t clock_current_time;
|
|
|
-static byte_t clock_settings;
|
|
|
+#define RTC_IRQ 8
|
|
|
|
|
|
-void clock_irq(registers_t *regs, byte_t irq_num)
|
|
|
-{
|
|
|
- bool_t pm = FALSE;
|
|
|
-
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_SECONDS_REG);
|
|
|
- byte_t seconds = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_MINUTES_REG);
|
|
|
- byte_t minutes = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_HOURS_REG);
|
|
|
- byte_t hours = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_DAY_REG);
|
|
|
- byte_t day = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_MONTH_REG);
|
|
|
- byte_t month = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_YEAR_REG);
|
|
|
- byte_t year = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
-
|
|
|
- if (hours & CLOCK_PM_BIT) pm = TRUE;
|
|
|
- hours &= ~CLOCK_PM_BIT;
|
|
|
-
|
|
|
- if (!(clock_settings & CLOCK_BINARY_MODE))
|
|
|
- {
|
|
|
- hours = (hours >> 4) * 10 + (hours & 0x0F);
|
|
|
- minutes = (minutes >> 4) * 10 + (minutes & 0x0F);
|
|
|
- seconds = (seconds >> 4) * 10 + (seconds & 0x0F);
|
|
|
- day = (day >> 4) * 10 + (day & 0x0F);
|
|
|
- month = (month >> 4) * 10 + (month & 0x0F);
|
|
|
- year = (year >> 4) * 10 + (year & 0x0F);
|
|
|
- }
|
|
|
+#define RTC_STA_UPDATING (1 << 7)
|
|
|
|
|
|
- if (!(clock_settings & CLOCK_24HOUR_MODE))
|
|
|
- {
|
|
|
- if (pm) hours += 12;
|
|
|
- else if (hours == 12) hours = 0;
|
|
|
- }
|
|
|
+#define RTC_HOURS_PM_BIT (1 << 7)
|
|
|
|
|
|
- clock_current_time.seconds = seconds;
|
|
|
- clock_current_time.minutes = minutes;
|
|
|
- clock_current_time.hours = hours;
|
|
|
- clock_current_time.day = day;
|
|
|
- clock_current_time.month = month;
|
|
|
- clock_current_time.year = year;
|
|
|
+#define RTC_STB_DST (1 << 0)
|
|
|
+#define RTC_STB_24HOUR (1 << 1)
|
|
|
+#define RTC_STB_BINARY (1 << 2)
|
|
|
+#define RTC_STB_SQUARE_WAVE (1 << 3)
|
|
|
+#define RTC_STB_UPDATE_INT (1 << 4)
|
|
|
+#define RTC_STB_ALARM_INT (1 << 5)
|
|
|
+#define RTC_STB_PERIODIC_INT (1 << 6)
|
|
|
+#define RTC_STB_DISABLE (1 << 7)
|
|
|
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_STC_REG);
|
|
|
- cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
-}
|
|
|
+#define RTC_SECONDS_REG 0x00
|
|
|
+#define RTC_MINUTES_REG 0x02
|
|
|
+#define RTC_HOURS_REG 0x04
|
|
|
+#define RTC_DAY_REG 0x07
|
|
|
+#define RTC_MONTH_REG 0x08
|
|
|
+#define RTC_YEAR_REG 0x09
|
|
|
+#define RTC_STA_REG 0x0A
|
|
|
+#define RTC_STB_REG 0x0B
|
|
|
+#define RTC_STC_REG 0x0C
|
|
|
+
|
|
|
+#define BCD_TO_BINARY(x) (((x) >> 4) * 10 + ((x) & 0x0F))
|
|
|
|
|
|
-bool_t clock_check_time(clock_time_t *time)
|
|
|
+typedef struct
|
|
|
{
|
|
|
- byte_t max_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
|
|
- if (time->year % 4 == 0 && (time->year % 100 != 0 || time->year % 400 == 0)) max_days[1]++;
|
|
|
+ byte_t second, minute, hour, day, month, year;
|
|
|
+} rtc_time_t;
|
|
|
|
|
|
- if (time->seconds >= 60) return FALSE;
|
|
|
- if (time->minutes >= 60) return FALSE;
|
|
|
- if (time->hours >= 24) return FALSE;
|
|
|
- if (time->month > 12 || time->month == 0) return FALSE;
|
|
|
- if (time->day > max_days[time->month - 1]) return FALSE;
|
|
|
+static clock_time_t clock_startup_timestamp = 0;
|
|
|
+static clock_time_t clock_adjustment = 0;
|
|
|
+static byte_t rtc_settings;
|
|
|
|
|
|
- return TRUE;
|
|
|
+static void read_rtc(rtc_time_t *time)
|
|
|
+{
|
|
|
+ critical_t critical;
|
|
|
+ enter_critical(&critical);
|
|
|
+
|
|
|
+ cpu_write_port_byte(CMOS_CMD_PORT, RTC_STA_REG);
|
|
|
+ for (;;) if (!(cpu_read_port_byte(CMOS_DATA_PORT) & RTC_STA_UPDATING)) break;
|
|
|
+
|
|
|
+ cpu_write_port_byte(CMOS_CMD_PORT, RTC_SECONDS_REG);
|
|
|
+ time->second = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
+ cpu_write_port_byte(CMOS_CMD_PORT, RTC_MINUTES_REG);
|
|
|
+ time->minute = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
+ cpu_write_port_byte(CMOS_CMD_PORT, RTC_HOURS_REG);
|
|
|
+ time->hour = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
+ cpu_write_port_byte(CMOS_CMD_PORT, RTC_DAY_REG);
|
|
|
+ time->day = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
+ cpu_write_port_byte(CMOS_CMD_PORT, RTC_MONTH_REG);
|
|
|
+ time->month = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
+ cpu_write_port_byte(CMOS_CMD_PORT, RTC_YEAR_REG);
|
|
|
+ time->year = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
+
|
|
|
+ leave_critical(&critical);
|
|
|
}
|
|
|
|
|
|
sysret_t syscall_clock_get_time(clock_time_t *time)
|
|
|
{
|
|
|
- if (get_previous_mode() != KERNEL_MODE && !check_usermode(time, sizeof(clock_time_t)))
|
|
|
- {
|
|
|
- return ERR_BADPTR;
|
|
|
- }
|
|
|
+ if (get_previous_mode() != KERNEL_MODE && !check_usermode(time, sizeof(clock_time_t))) return ERR_BADPTR;
|
|
|
+ int64_t microseconds = (int64_t)(timer_get_nanoseconds() / 1000ULL);
|
|
|
|
|
|
EH_TRY
|
|
|
{
|
|
|
- time->seconds = clock_current_time.seconds;
|
|
|
- time->minutes = clock_current_time.minutes;
|
|
|
- time->hours = clock_current_time.hours;
|
|
|
- time->day = clock_current_time.day;
|
|
|
- time->month = clock_current_time.month;
|
|
|
- time->year = clock_current_time.year;
|
|
|
+ *time = clock_startup_timestamp + clock_adjustment + microseconds;
|
|
|
}
|
|
|
EH_CATCH
|
|
|
{
|
|
@@ -114,7 +105,7 @@ sysret_t syscall_clock_get_time(clock_time_t *time)
|
|
|
|
|
|
sysret_t syscall_clock_set_time(clock_time_t *time)
|
|
|
{
|
|
|
- clock_time_t safe_time = {0};
|
|
|
+ clock_time_t safe_time;
|
|
|
|
|
|
if (get_previous_mode() == USER_MODE)
|
|
|
{
|
|
@@ -131,77 +122,75 @@ sysret_t syscall_clock_set_time(clock_time_t *time)
|
|
|
safe_time = *time;
|
|
|
}
|
|
|
|
|
|
- if (!clock_check_time(&safe_time)) return ERR_INVALID;
|
|
|
+ int64_t microseconds = (int64_t)(timer_get_nanoseconds() / 1000ULL);
|
|
|
+ clock_time_t new_timestamp = safe_time;
|
|
|
+ clock_time_t old_timestamp = clock_startup_timestamp + clock_adjustment + microseconds;
|
|
|
+ clock_adjustment = new_timestamp - old_timestamp;
|
|
|
+
|
|
|
+ // TODO: Update the RTC
|
|
|
+ return ERR_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+void clock_init()
|
|
|
+{
|
|
|
+ cpu_write_port_byte(CMOS_CMD_PORT, RTC_STB_REG);
|
|
|
+ rtc_settings = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
|
|
|
- byte_t seconds = safe_time.seconds;
|
|
|
- byte_t minutes = safe_time.minutes;
|
|
|
- byte_t hours = safe_time.hours;
|
|
|
- byte_t day = safe_time.day;
|
|
|
- byte_t month = safe_time.month;
|
|
|
- byte_t year = safe_time.year;
|
|
|
+ rtc_time_t time;
|
|
|
+ clock_time_t adjustment;
|
|
|
|
|
|
- if (!(clock_settings & CLOCK_BINARY_MODE))
|
|
|
+ for (;;)
|
|
|
{
|
|
|
- hours = ((hours / 10) << 4) + (hours % 10);
|
|
|
- minutes = ((minutes / 10) << 4) + (minutes % 10);
|
|
|
- seconds = ((seconds / 10) << 4) + (seconds % 10);
|
|
|
- day = ((day / 10) << 4) + (day % 10);
|
|
|
- month = ((month / 10) << 4) + (month % 10);
|
|
|
- year = ((year / 10) << 4) + (year % 10);
|
|
|
+ rtc_time_t check;
|
|
|
+ read_rtc(&check);
|
|
|
+ adjustment = (clock_time_t)(timer_get_nanoseconds() / 1000ULL);
|
|
|
+ read_rtc(&time);
|
|
|
+ if (memcmp(&time, &check, sizeof(rtc_time_t))) break;
|
|
|
}
|
|
|
|
|
|
- if (!(clock_settings & CLOCK_24HOUR_MODE) && (hours > 12)) hours |= CLOCK_PM_BIT;
|
|
|
+ if (!(rtc_settings & RTC_STB_BINARY))
|
|
|
+ {
|
|
|
+ time.second = BCD_TO_BINARY(time.second);
|
|
|
+ time.minute = BCD_TO_BINARY(time.minute);
|
|
|
+ time.hour = BCD_TO_BINARY(time.hour & ~RTC_HOURS_PM_BIT) | (time.hour & RTC_HOURS_PM_BIT);
|
|
|
+ time.day = BCD_TO_BINARY(time.day);
|
|
|
+ time.month = BCD_TO_BINARY(time.month);
|
|
|
+ time.year = BCD_TO_BINARY(time.year);
|
|
|
+ }
|
|
|
|
|
|
- critical_t critical;
|
|
|
- enter_critical(&critical);
|
|
|
+ if (!(rtc_settings & RTC_STB_24HOUR))
|
|
|
+ {
|
|
|
+ time.hour = (time.hour & RTC_HOURS_PM_BIT) ? ((time.hour & ~RTC_HOURS_PM_BIT) % 12) + 12 : time.hour % 12;
|
|
|
+ }
|
|
|
|
|
|
- clock_current_time.seconds = safe_time.seconds;
|
|
|
- clock_current_time.minutes = safe_time.minutes;
|
|
|
- clock_current_time.hours = safe_time.hours;
|
|
|
- clock_current_time.day = safe_time.day;
|
|
|
- clock_current_time.month = safe_time.month;
|
|
|
- clock_current_time.year = safe_time.year;
|
|
|
-
|
|
|
- disable_nmi();
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_SECONDS_REG);
|
|
|
- cpu_write_port_byte(CMOS_DATA_PORT, seconds);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_MINUTES_REG);
|
|
|
- cpu_write_port_byte(CMOS_DATA_PORT, minutes);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_HOURS_REG);
|
|
|
- cpu_write_port_byte(CMOS_DATA_PORT, hours);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_DAY_REG);
|
|
|
- cpu_write_port_byte(CMOS_DATA_PORT, day);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_MONTH_REG);
|
|
|
- cpu_write_port_byte(CMOS_DATA_PORT, month);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_YEAR_REG);
|
|
|
- cpu_write_port_byte(CMOS_DATA_PORT, year);
|
|
|
- enable_nmi();
|
|
|
+ TRACE("Current time: 20%02u-%02u-%02u %02u:%02u:%02u\n",
|
|
|
+ time.year,
|
|
|
+ time.month,
|
|
|
+ time.day,
|
|
|
+ time.hour,
|
|
|
+ time.minute,
|
|
|
+ time.second);
|
|
|
|
|
|
- leave_critical(&critical);
|
|
|
- return ERR_SUCCESS;
|
|
|
-}
|
|
|
+ int i;
|
|
|
+ clock_startup_timestamp = 0;
|
|
|
|
|
|
-void clock_init()
|
|
|
-{
|
|
|
- critical_t critical;
|
|
|
+ for (i = 1980; i < (int)time.year + 2000; i++)
|
|
|
+ {
|
|
|
+ clock_startup_timestamp += (!(i % 400) || (!(i % 4) && (i % 100))) ? 31622400000000LL : 31536000000000LL;
|
|
|
+ }
|
|
|
|
|
|
- enter_critical(&critical);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_STB_REG);
|
|
|
- clock_settings = cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
+ int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
|
|
+ if (!(i % 400) || (!(i % 4) && (i % 100))) month_days[1]++;
|
|
|
|
|
|
- if ((clock_settings & (CLOCK_ALARM_INT | CLOCK_PERIODIC_INT | CLOCK_UPDATE_INT)) != CLOCK_UPDATE_INT)
|
|
|
+ for (i = 1; i < time.month; i++)
|
|
|
{
|
|
|
- clock_settings &= ~(CLOCK_ALARM_INT | CLOCK_PERIODIC_INT);
|
|
|
- clock_settings |= CLOCK_UPDATE_INT;
|
|
|
-
|
|
|
- disable_nmi();
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_STB_REG);
|
|
|
- cpu_write_port_byte(CMOS_DATA_PORT, clock_settings);
|
|
|
- enable_nmi();
|
|
|
+ clock_startup_timestamp += month_days[i - 1] * 86400000000LL;
|
|
|
}
|
|
|
|
|
|
- register_irq_handler(CLOCK_IRQ, &clock_irq, FALSE);
|
|
|
- cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_STC_REG);
|
|
|
- cpu_read_port_byte(CMOS_DATA_PORT);
|
|
|
- leave_critical(&critical);
|
|
|
+ clock_startup_timestamp += (time.day - 1) * 86400000000LL;
|
|
|
+ clock_startup_timestamp += time.hour * 3600000000LL;
|
|
|
+ clock_startup_timestamp += time.minute * 60000000LL;
|
|
|
+ clock_startup_timestamp += time.second * 1000000LL;
|
|
|
+ clock_startup_timestamp -= adjustment;
|
|
|
+ TRACE("Startup timestamp: %lld\n", clock_startup_timestamp);
|
|
|
}
|