123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- /*
- * clock.c
- *
- * 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
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <clock.h>
- #include <exception.h>
- #include <syscalls.h>
- #include <irq.h>
- #include <user.h>
- #include <timer.h>
- #include <log.h>
- #define RTC_IRQ 8
- #define RTC_STA_UPDATING (1 << 7)
- #define RTC_HOURS_PM_BIT (1 << 7)
- #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)
- #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))
- typedef struct
- {
- byte_t second, minute, hour, day, month, year;
- } rtc_time_t;
- static clock_time_t clock_startup_timestamp = 0;
- static clock_time_t clock_adjustment = 0;
- static byte_t rtc_settings;
- 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;
- int64_t microseconds = (int64_t)(timer_get_nanoseconds() / 1000ULL);
- EH_TRY
- {
- *time = clock_startup_timestamp + clock_adjustment + microseconds;
- }
- EH_CATCH
- {
- EH_ESCAPE(return ERR_BADPTR);
- }
- EH_DONE;
- return ERR_SUCCESS;
- }
- sysret_t syscall_clock_set_time(clock_time_t *time)
- {
- clock_time_t safe_time;
- if (get_previous_mode() == USER_MODE)
- {
- if (!check_usermode(time, sizeof(clock_time_t))) return ERR_BADPTR;
- EH_TRY safe_time = *time;
- EH_CATCH EH_ESCAPE(return ERR_BADPTR);
- EH_DONE;
- if (!check_privileges(PRIVILEGE_SET_TIME)) return ERR_FORBIDDEN;
- }
- else
- {
- safe_time = *time;
- }
- 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);
- rtc_time_t time;
- clock_time_t adjustment;
- for (;;)
- {
- 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 (!(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);
- }
- 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;
- }
- TRACE("Current time: 20%02u-%02u-%02u %02u:%02u:%02u\n",
- time.year,
- time.month,
- time.day,
- time.hour,
- time.minute,
- time.second);
- int i;
- clock_startup_timestamp = 0;
- for (i = 1980; i < (int)time.year + 2000; i++)
- {
- clock_startup_timestamp += (!(i % 400) || (!(i % 4) && (i % 100))) ? 31622400000000LL : 31536000000000LL;
- }
- 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 < time.month; i++)
- {
- clock_startup_timestamp += month_days[i - 1] * 86400000000LL;
- }
- 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);
- }
|