From: Tulio A M Mendes Date: Thu, 12 Feb 2026 03:20:48 +0000 (-0300) Subject: feat: RTC driver (CMOS real-time clock) + clock_gettime(CLOCK_REALTIME) uses wall... X-Git-Url: https://projects.tadryanom.me/sitemap.xml?a=commitdiff_plain;h=54b5da1aa8644a46b56054b1fbe8d6648d53ffda;p=AdrOS.git feat: RTC driver (CMOS real-time clock) + clock_gettime(CLOCK_REALTIME) uses wall-clock time --- diff --git a/include/rtc.h b/include/rtc.h new file mode 100644 index 0000000..8b35bc0 --- /dev/null +++ b/include/rtc.h @@ -0,0 +1,19 @@ +#ifndef RTC_H +#define RTC_H + +#include + +struct rtc_time { + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t day; + uint8_t month; + uint16_t year; +}; + +void rtc_init(void); +void rtc_read(struct rtc_time* t); +uint32_t rtc_unix_timestamp(void); + +#endif diff --git a/src/drivers/rtc.c b/src/drivers/rtc.c new file mode 100644 index 0000000..4a71ab5 --- /dev/null +++ b/src/drivers/rtc.c @@ -0,0 +1,95 @@ +#include "rtc.h" + +#define CMOS_ADDR 0x70 +#define CMOS_DATA 0x71 + +#define RTC_REG_SEC 0x00 +#define RTC_REG_MIN 0x02 +#define RTC_REG_HOUR 0x04 +#define RTC_REG_DAY 0x07 +#define RTC_REG_MON 0x08 +#define RTC_REG_YEAR 0x09 +#define RTC_REG_STATB 0x0B + +static inline void port_outb(uint16_t port, uint8_t val) { + __asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint8_t port_inb(uint16_t port) { + uint8_t ret; + __asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +static uint8_t cmos_read(uint8_t reg) { + port_outb(CMOS_ADDR, reg); + return port_inb(CMOS_DATA); +} + +static int rtc_updating(void) { + port_outb(CMOS_ADDR, 0x0A); + return port_inb(CMOS_DATA) & 0x80; +} + +static uint8_t bcd_to_bin(uint8_t v) { + return (uint8_t)((v & 0x0F) + ((v >> 4) * 10)); +} + +void rtc_init(void) { + /* Nothing to do — CMOS is always available on x86 */ +} + +void rtc_read(struct rtc_time* t) { + while (rtc_updating()) {} + + t->second = cmos_read(RTC_REG_SEC); + t->minute = cmos_read(RTC_REG_MIN); + t->hour = cmos_read(RTC_REG_HOUR); + t->day = cmos_read(RTC_REG_DAY); + t->month = cmos_read(RTC_REG_MON); + t->year = cmos_read(RTC_REG_YEAR); + + uint8_t statb = cmos_read(RTC_REG_STATB); + int bcd = !(statb & 0x04); + + if (bcd) { + t->second = bcd_to_bin(t->second); + t->minute = bcd_to_bin(t->minute); + t->hour = bcd_to_bin((uint8_t)(t->hour & 0x7F)); + t->day = bcd_to_bin(t->day); + t->month = bcd_to_bin(t->month); + t->year = bcd_to_bin((uint8_t)t->year); + } + + t->year += 2000; + + if (!(statb & 0x02) && (cmos_read(RTC_REG_HOUR) & 0x80)) { + t->hour = (uint8_t)((t->hour + 12) % 24); + } +} + +static int is_leap(uint16_t y) { + return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); +} + +uint32_t rtc_unix_timestamp(void) { + struct rtc_time t; + rtc_read(&t); + + static const uint16_t mdays[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + + uint32_t y = t.year; + uint32_t days = 0; + + for (uint32_t i = 1970; i < y; i++) { + days += is_leap((uint16_t)i) ? 366 : 365; + } + + days += mdays[t.month - 1]; + if (t.month > 2 && is_leap((uint16_t)y)) days++; + days += t.day - 1; + + return days * 86400 + t.hour * 3600 + t.minute * 60 + t.second; +} diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index b133e82..0bb88fe 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -1488,13 +1488,18 @@ static int syscall_clock_gettime_impl(uint32_t clk_id, struct timespec* user_tp) if (clk_id != CLOCK_REALTIME && clk_id != CLOCK_MONOTONIC) return -EINVAL; - uint32_t ticks = get_tick_count(); - const uint32_t TICK_MS = 20; - uint32_t total_ms = ticks * TICK_MS; - struct timespec tp; - tp.tv_sec = total_ms / 1000U; - tp.tv_nsec = (total_ms % 1000U) * 1000000U; + if (clk_id == CLOCK_REALTIME) { + extern uint32_t rtc_unix_timestamp(void); + tp.tv_sec = rtc_unix_timestamp(); + tp.tv_nsec = 0; + } else { + uint32_t ticks = get_tick_count(); + const uint32_t TICK_MS = 20; + uint32_t total_ms = ticks * TICK_MS; + tp.tv_sec = total_ms / 1000U; + tp.tv_nsec = (total_ms % 1000U) * 1000000U; + } if (copy_to_user(user_tp, &tp, sizeof(tp)) < 0) return -EFAULT; return 0;