4 * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <exception.h>
30 #define RTC_STA_UPDATING (1 << 7)
32 #define RTC_HOURS_PM_BIT (1 << 7)
34 #define RTC_STB_DST (1 << 0)
35 #define RTC_STB_24HOUR (1 << 1)
36 #define RTC_STB_BINARY (1 << 2)
37 #define RTC_STB_SQUARE_WAVE (1 << 3)
38 #define RTC_STB_UPDATE_INT (1 << 4)
39 #define RTC_STB_ALARM_INT (1 << 5)
40 #define RTC_STB_PERIODIC_INT (1 << 6)
41 #define RTC_STB_DISABLE (1 << 7)
43 #define RTC_SECONDS_REG 0x00
44 #define RTC_MINUTES_REG 0x02
45 #define RTC_HOURS_REG 0x04
46 #define RTC_DAY_REG 0x07
47 #define RTC_MONTH_REG 0x08
48 #define RTC_YEAR_REG 0x09
49 #define RTC_STA_REG 0x0A
50 #define RTC_STB_REG 0x0B
51 #define RTC_STC_REG 0x0C
53 #define BCD_TO_BINARY(x) (((x) >> 4) * 10 + ((x) & 0x0F))
57 byte_t second, minute, hour, day, month, year;
60 static clock_time_t clock_startup_timestamp = 0;
61 static clock_time_t clock_adjustment = 0;
62 static byte_t rtc_settings;
64 static void read_rtc(rtc_time_t *time)
67 enter_critical(&critical);
69 cpu_write_port_byte(CMOS_CMD_PORT, RTC_STA_REG);
70 for (;;) if (!(cpu_read_port_byte(CMOS_DATA_PORT) & RTC_STA_UPDATING)) break;
72 cpu_write_port_byte(CMOS_CMD_PORT, RTC_SECONDS_REG);
73 time->second = cpu_read_port_byte(CMOS_DATA_PORT);
74 cpu_write_port_byte(CMOS_CMD_PORT, RTC_MINUTES_REG);
75 time->minute = cpu_read_port_byte(CMOS_DATA_PORT);
76 cpu_write_port_byte(CMOS_CMD_PORT, RTC_HOURS_REG);
77 time->hour = cpu_read_port_byte(CMOS_DATA_PORT);
78 cpu_write_port_byte(CMOS_CMD_PORT, RTC_DAY_REG);
79 time->day = cpu_read_port_byte(CMOS_DATA_PORT);
80 cpu_write_port_byte(CMOS_CMD_PORT, RTC_MONTH_REG);
81 time->month = cpu_read_port_byte(CMOS_DATA_PORT);
82 cpu_write_port_byte(CMOS_CMD_PORT, RTC_YEAR_REG);
83 time->year = cpu_read_port_byte(CMOS_DATA_PORT);
85 leave_critical(&critical);
88 sysret_t syscall_clock_get_time(clock_time_t *time)
90 if (get_previous_mode() != KERNEL_MODE && !check_usermode(time, sizeof(clock_time_t))) return ERR_BADPTR;
91 int64_t microseconds = (int64_t)(timer_get_nanoseconds() / 1000ULL);
95 *time = clock_startup_timestamp + clock_adjustment + microseconds;
99 EH_ESCAPE(return ERR_BADPTR);
106 sysret_t syscall_clock_set_time(clock_time_t *time)
108 clock_time_t safe_time;
110 if (get_previous_mode() == USER_MODE)
112 if (!check_usermode(time, sizeof(clock_time_t))) return ERR_BADPTR;
114 EH_TRY safe_time = *time;
115 EH_CATCH EH_ESCAPE(return ERR_BADPTR);
118 if (!check_privileges(PRIVILEGE_SET_TIME)) return ERR_FORBIDDEN;
125 int64_t microseconds = (int64_t)(timer_get_nanoseconds() / 1000ULL);
126 clock_time_t new_timestamp = safe_time;
127 clock_time_t old_timestamp = clock_startup_timestamp + clock_adjustment + microseconds;
128 clock_adjustment = new_timestamp - old_timestamp;
130 // TODO: Update the RTC
136 cpu_write_port_byte(CMOS_CMD_PORT, RTC_STB_REG);
137 rtc_settings = cpu_read_port_byte(CMOS_DATA_PORT);
140 clock_time_t adjustment;
146 adjustment = (clock_time_t)(timer_get_nanoseconds() / 1000ULL);
148 if (memcmp(&time, &check, sizeof(rtc_time_t))) break;
151 if (!(rtc_settings & RTC_STB_BINARY))
153 time.second = BCD_TO_BINARY(time.second);
154 time.minute = BCD_TO_BINARY(time.minute);
155 time.hour = BCD_TO_BINARY(time.hour & ~RTC_HOURS_PM_BIT) | (time.hour & RTC_HOURS_PM_BIT);
156 time.day = BCD_TO_BINARY(time.day);
157 time.month = BCD_TO_BINARY(time.month);
158 time.year = BCD_TO_BINARY(time.year);
161 if (!(rtc_settings & RTC_STB_24HOUR))
163 time.hour = (time.hour & RTC_HOURS_PM_BIT) ? ((time.hour & ~RTC_HOURS_PM_BIT) % 12) + 12 : time.hour % 12;
166 TRACE("Current time: 20%02u-%02u-%02u %02u:%02u:%02u\n",
175 clock_startup_timestamp = 0;
177 for (i = 1980; i < (int)time.year + 2000; i++)
179 clock_startup_timestamp += (!(i % 400) || (!(i % 4) && (i % 100))) ? 31622400000000LL : 31536000000000LL;
182 int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
183 if (!(i % 400) || (!(i % 4) && (i % 100))) month_days[1]++;
185 for (i = 1; i < time.month; i++)
187 clock_startup_timestamp += month_days[i - 1] * 86400000000LL;
190 clock_startup_timestamp += (time.day - 1) * 86400000000LL;
191 clock_startup_timestamp += time.hour * 3600000000LL;
192 clock_startup_timestamp += time.minute * 60000000LL;
193 clock_startup_timestamp += time.second * 1000000LL;
194 clock_startup_timestamp -= adjustment;
195 TRACE("Startup timestamp: %lld\n", clock_startup_timestamp);