diff options
Diffstat (limited to 'kernel/src/arch/i686/drivers/rtc.c')
-rw-r--r-- | kernel/src/arch/i686/drivers/rtc.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/kernel/src/arch/i686/drivers/rtc.c b/kernel/src/arch/i686/drivers/rtc.c new file mode 100644 index 0000000..ba69d15 --- /dev/null +++ b/kernel/src/arch/i686/drivers/rtc.c @@ -0,0 +1,152 @@ +#include "time.h" +#include <stdint.h> +#include <sys.h> +#include <arch/i686/drivers/rtc.h> +#include <arch/i686/asm.h> + +#define CMOS_WRITE_PORT 0x70 +#define CMOS_READ_PORT 0x71 + +#define CMOS_REG_SEC 0x00 +#define CMOS_REG_MIN 0x02 +#define CMOS_REG_HOUR 0x04 +#define CMOS_REG_WDAY 0x06 +#define CMOS_REG_MDAY 0x07 +#define CMOS_REG_MON 0x08 +#define CMOS_REG_YEAR 0x09 +#define CMOS_REG_CEN 0x32 + +// Live buffers to work on data +static struct Time time; +static struct Time localtime; + +// Front buffers so interupts dont request data that is half done +static struct Time cur_time; +static struct Time cur_localtime; + +// Current set Time Zone +static enum Timezone last_timezone = UTC; + +static uint8_t cmos_read(uint8_t reg) { + uint8_t hex, ret; + + outb(CMOS_WRITE_PORT, reg); + hex = inb(CMOS_READ_PORT); + + ret = hex & 0x0F; + ret += (hex & 0xF0) / 16 * 10; + + return ret; +} + +static int mday_offset[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static void update_localtime(void) { + + int change, max; + + // set localtime + localtime = time; + + // if tz is UTC, we dont need to do anythin + if (last_timezone == UTC) { + cur_localtime = localtime; + return; + } + + // check if day rolled over + change = localtime.hour < 0 ? -1 : localtime.hour >= 24 ? 1 : 0; + if (!change) return; + + // roll over day + localtime.hour = (localtime.hour + 24) % 24; + localtime.wday = (localtime.wday + change + 7) % 7; + localtime.mday += change; + localtime.yday += change; + + // check if month rolled over + max = month_days[localtime.mon]; + if (localtime.leap && localtime.mon == 1) max++; + change = localtime.mday < 0 ? -1 : localtime.mday >= max ? 1 : 0; + if (!change) return; + + // roll over month + localtime.mon = (localtime.mon + change + 12) % 12; + + // check if year rolled over + max = localtime.leap ? 366 : 365; + change = localtime.yday < 0 ? -1 : localtime.yday >= max ? 1 : 0; + if (!change) return; + + // roll over year + localtime.yn += change; + + // check if cen rolled over + change = localtime.yn < 0 ? -1 : localtime.yn >= 100 ? 1 : 0; + if (!change) goto year; + + // roll over cen + localtime.cen += change; + + +year: + + localtime.year = localtime.yn + localtime.cen * 100; + localtime.leap = localtime.year % 4 == 0 && localtime.year % 100 != 0; + + if (localtime.leap && localtime.yday == -1) + localtime.yday = 365; + else if (localtime.yday == -1) + localtime.yday = 364; + else + localtime.yday = 0; + + localtime.year -= 1900; + + cur_localtime = localtime; + +} + +void rtc_update(void) { + time.sec = cmos_read(CMOS_REG_SEC); + time.min = cmos_read(CMOS_REG_MIN); + time.hour = cmos_read(CMOS_REG_HOUR); + time.wday = cmos_read(CMOS_REG_WDAY) - 1; + time.mday = cmos_read(CMOS_REG_MDAY); + time.mon = cmos_read(CMOS_REG_MON) - 1; + time.yn = cmos_read(CMOS_REG_YEAR); + time.cen = 20; + + time.year = time.yn + time.cen * 100; + + time.leap = time.year % 4 == 0 && time.year % 100 != 0; + + time.yday = mday_offset[time.mon] + time.mday; + + if (time.leap && time.mon > 2) + time.yday++; + + time.year -= 1900; + + update_localtime(); + + cur_time = time; +} + +struct Time rtc_utctime(void) { + return cur_time; +} + +struct Time rtc_localtime(enum Timezone tz) { + if (tz != last_timezone) { + last_timezone = tz; + update_localtime(); + } + return cur_localtime; +} |