Browse Source

Rewrite the system clock to use timestamps instead of calendar time

coderain 4 months ago
parent
commit
a16949de96
4 changed files with 173 additions and 167 deletions
  1. 58 14
      drivers/fatfs/src/main.c
  2. 0 23
      kernel/include/clock.h
  3. 114 125
      kernel/src/clock.c
  4. 1 5
      sdk/clock.h

+ 58 - 14
drivers/fatfs/src/main.c

@@ -2,7 +2,7 @@
  * FAT12/16/32 Filesystem Driver
  * main.c
  *
- * Copyright (C) 2016 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
@@ -123,20 +123,64 @@ static void fatfs_set_entry_name(fatfs_dirent_t *dirent, const char *name)
     }
 }
 
-static inline void fatfs_pack_file_time(clock_time_t *os_time, word_t *date, word_t *time)
+static inline void fatfs_pack_file_time(clock_time_t os_time, word_t *date, word_t *time)
 {
-    if (date) *date = (os_time->year << 9) | (os_time->month << 5) | os_time->day;
-    if (time) *time = (os_time->hours << 11) | (os_time->minutes << 5) | (os_time->seconds >> 1);
+    if (os_time < 0) os_time = 0;
+
+    if (date)
+    {
+        clock_time_t total_days = os_time / 86400000000LL;
+
+        int i;
+        for (i = 1980;; i++)
+        {
+            int year_days = (!(i % 400) || (!(i % 4) && (i % 100))) ? 366 : 365;
+            if (total_days < year_days) break;
+            total_days -= year_days;
+        }
+
+        *date = (i - 1980) << 9;
+
+        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]++;
+        for (i = 1; i <= 12; i++)
+        {
+            if (total_days < month_days[i - 1]) break;
+            total_days -= month_days[i - 1];
+        }
+
+        *date |= (i << 5) | total_days;
+    }
+
+    if (time)
+    {
+        clock_time_t total_seconds = (os_time / 1000000) % 86400;
+        *time = ((total_seconds / 3600) << 11) | (((total_seconds / 60) % 60) << 5) | ((total_seconds % 60) >> 1);
+    }
 }
 
 static inline void fatfs_unpack_file_time(word_t date, word_t time, clock_time_t *os_time)
 {
-    os_time->day = date & 0x1F;
-    os_time->month = (date >> 5) & 0x0F;
-    os_time->year = (date >> 9) & 0x7F;
-    os_time->seconds = (time & 0x1F) << 1;
-    os_time->minutes = (time >> 5) & 0x3F;
-    os_time->hours = (time >> 11) & 0x1F;
+    clock_time_t year = (date >> 9) & 0x7F;
+    clock_time_t month = (date >> 5) & 0x0F;
+    clock_time_t day = date & 0x1F;
+    clock_time_t hour = (time >> 11) & 0x1F;
+    clock_time_t minute = (time >> 5) & 0x3F;
+    clock_time_t second = (time & 0x1F) << 1;
+    clock_time_t total_days = 0;
+
+    int i;
+    for (i = 1980; i < 1980 + year; i++)
+    {
+        total_days += (!(i % 400) || (!(i % 4) && (i % 100))) ? 366 : 365;
+    }
+
+    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]++;
+    for (i = 1; i < month; i++) total_days += month_days[i - 1];
+    total_days += day - 1;
+
+    *os_time = (60 * (60 * (24 * total_days + hour) + minute) + second) * 1000000;
 }
 
 static dword_t fatfs_get_next_cluster(fatfs_volume_t *volume, dword_t cluster)
@@ -486,7 +530,7 @@ static dword_t fatfs_resize_file(fatfs_file_t *file, qword_t new_size)
     dirent.size = file->header.size;
     dirent.first_cluster_low = file->first_cluster & 0xFFFF;
     dirent.first_cluster_high = file->first_cluster >> 16;
-    fatfs_pack_file_time(&current_time, &dirent.modification_date, &dirent.modification_time);
+    fatfs_pack_file_time(current_time, &dirent.modification_date, &dirent.modification_time);
 
     return device_write(volume->header.device,
                         &dirent,
@@ -782,9 +826,9 @@ static dword_t fatfs_load_file(file_t **_file)
 
         fatfs_set_entry_name(&dirent, file_name);
         dirent.duration = 1;
-        fatfs_pack_file_time(&current_time, &dirent.creation_date, &dirent.creation_time);
-        fatfs_pack_file_time(&current_time, &dirent.modification_date, &dirent.modification_time);
-        fatfs_pack_file_time(&current_time, &dirent.last_accessed_date, NULL);
+        fatfs_pack_file_time(current_time, &dirent.creation_date, &dirent.creation_time);
+        fatfs_pack_file_time(current_time, &dirent.modification_date, &dirent.modification_time);
+        fatfs_pack_file_time(current_time, &dirent.last_accessed_date, NULL);
         dirent.first_cluster_high = 0;
         dirent.first_cluster_low = 0;
         dirent.size = 0;

+ 0 - 23
kernel/include/clock.h

@@ -22,32 +22,9 @@
 
 #include <sdk/clock.h>
 
-#define CLOCK_IRQ 8
-#define CLOCK_PM_BIT (1 << 7)
-
-#define CLOCK_DAYLIGHT_SAVINGS (1 << 0)
-#define CLOCK_24HOUR_MODE (1 << 1)
-#define CLOCK_BINARY_MODE (1 << 2)
-#define CLOCK_SQUARE_WAVE (1 << 3)
-#define CLOCK_UPDATE_INT (1 << 4)
-#define CLOCK_ALARM_INT (1 << 5)
-#define CLOCK_PERIODIC_INT (1 << 6)
-#define CLOCK_DISABLE (1 << 7)
-
 #define CMOS_CMD_PORT 0x70
 #define CMOS_DATA_PORT 0x71
 
-#define CMOS_RTC_SECONDS_REG 0x00
-#define CMOS_RTC_MINUTES_REG 0x02
-#define CMOS_RTC_HOURS_REG 0x04
-#define CMOS_RTC_DAY_REG 0x07
-#define CMOS_RTC_MONTH_REG 0x08
-#define CMOS_RTC_YEAR_REG 0x09
-#define CMOS_RTC_STA_REG 0x0A
-#define CMOS_RTC_STB_REG 0x0B
-#define CMOS_RTC_STC_REG 0x0C
-
 void clock_init();
-bool_t clock_check_time(clock_time_t *time);
 
 #endif

+ 114 - 125
kernel/src/clock.c

@@ -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);
 }

+ 1 - 5
sdk/clock.h

@@ -22,11 +22,7 @@
 
 #include "defs.h"
 
-typedef struct
-{
-    word_t year;
-    byte_t day, month, hours, minutes, seconds;
-} clock_time_t;
+typedef int64_t clock_time_t;
 
 sysret_t syscall_clock_get_time(clock_time_t *time);
 sysret_t syscall_clock_set_time(clock_time_t *time);