72205dce2c17266bd38d18ec8b3c9d1c0d224f30
[monolithium.git] / kernel / src / clock.c
1 /*
2  * clock.c
3  *
4  * Copyright (C) 2013 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #include <clock.h>
21 #include <exception.h>
22 #include <syscalls.h>
23 #include <irq.h>
24 #include <user.h>
25
26 static clock_time_t clock_current_time;
27 static byte_t clock_settings;
28
29 void clock_irq(registers_t *regs, byte_t irq_num)
30 {
31     bool_t pm = FALSE;
32
33     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_SECONDS_REG);
34     byte_t seconds = cpu_read_port_byte(CMOS_DATA_PORT);
35     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_MINUTES_REG);
36     byte_t minutes = cpu_read_port_byte(CMOS_DATA_PORT);
37     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_HOURS_REG);
38     byte_t hours = cpu_read_port_byte(CMOS_DATA_PORT);
39     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_DAY_REG);
40     byte_t day = cpu_read_port_byte(CMOS_DATA_PORT);
41     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_MONTH_REG);
42     byte_t month = cpu_read_port_byte(CMOS_DATA_PORT);
43     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_YEAR_REG);
44     byte_t year = cpu_read_port_byte(CMOS_DATA_PORT);
45
46     if (hours & CLOCK_PM_BIT) pm = TRUE;
47     hours &= ~CLOCK_PM_BIT;
48
49     if (!(clock_settings & CLOCK_BINARY_MODE))
50     {
51         hours = (hours >> 4) * 10 + (hours & 0x0F);
52         minutes = (minutes >> 4) * 10 + (minutes & 0x0F);
53         seconds = (seconds >> 4) * 10 + (seconds & 0x0F);
54         day = (day >> 4) * 10 + (day & 0x0F);
55         month = (month >> 4) * 10 + (month & 0x0F);
56         year = (year >> 4) * 10 + (year & 0x0F);
57     }
58
59     if (!(clock_settings & CLOCK_24HOUR_MODE))
60     {
61         if (pm) hours += 12;
62         else if (hours == 12) hours = 0;
63     }
64
65     clock_current_time.seconds = seconds;
66     clock_current_time.minutes = minutes;
67     clock_current_time.hours = hours;
68     clock_current_time.day = day;
69     clock_current_time.month = month;
70     clock_current_time.year = year;
71
72     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_STC_REG);
73     cpu_read_port_byte(CMOS_DATA_PORT);
74 }
75
76 bool_t clock_check_time(clock_time_t *time)
77 {
78     byte_t max_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
79     if (time->year % 4 == 0 && (time->year % 100 != 0 || time->year % 400 == 0)) max_days[1]++;
80
81     if (time->seconds >= 60) return FALSE;
82     if (time->minutes >= 60) return FALSE;
83     if (time->hours >= 24) return FALSE;
84     if (time->month > 12 || time->month == 0) return FALSE;
85     if (time->day > max_days[time->month - 1]) return FALSE;
86
87     return TRUE;
88 }
89
90 sysret_t syscall_clock_get_time(clock_time_t *time)
91 {
92     if (get_previous_mode() != KERNEL_MODE && !check_usermode(time, sizeof(clock_time_t)))
93     {
94         return ERR_BADPTR;
95     }
96
97     EH_TRY
98     {
99         time->seconds = clock_current_time.seconds;
100         time->minutes = clock_current_time.minutes;
101         time->hours = clock_current_time.hours;
102         time->day = clock_current_time.day;
103         time->month = clock_current_time.month;
104         time->year = clock_current_time.year;
105     }
106     EH_CATCH
107     {
108         EH_ESCAPE(return ERR_BADPTR);
109     }
110     EH_DONE;
111
112     return ERR_SUCCESS;
113 }
114
115 sysret_t syscall_clock_set_time(clock_time_t *time)
116 {
117     clock_time_t safe_time = {0};
118
119     if (get_previous_mode() == USER_MODE)
120     {
121         if (!check_usermode(time, sizeof(clock_time_t))) return ERR_BADPTR;
122
123         EH_TRY safe_time = *time;
124         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
125         EH_DONE;
126
127         if (!check_privileges(PRIVILEGE_SET_TIME)) return ERR_FORBIDDEN;
128     }
129     else
130     {
131         safe_time = *time;
132     }
133
134     if (!clock_check_time(&safe_time)) return ERR_INVALID;
135
136     byte_t seconds = safe_time.seconds;
137     byte_t minutes = safe_time.minutes;
138     byte_t hours = safe_time.hours;
139     byte_t day = safe_time.day;
140     byte_t month = safe_time.month;
141     byte_t year = safe_time.year;
142
143     if (!(clock_settings & CLOCK_BINARY_MODE))
144     {
145         hours = ((hours / 10) << 4) + (hours % 10);
146         minutes = ((minutes / 10) << 4) + (minutes % 10);
147         seconds = ((seconds / 10) << 4) + (seconds % 10);
148         day = ((day / 10) << 4) + (day % 10);
149         month = ((month / 10) << 4) + (month % 10);
150         year = ((year / 10) << 4) + (year % 10);
151     }
152
153     if (!(clock_settings & CLOCK_24HOUR_MODE) && (hours > 12)) hours |= CLOCK_PM_BIT;
154
155     critical_t critical;
156     enter_critical(&critical);
157
158     clock_current_time.seconds = safe_time.seconds;
159     clock_current_time.minutes = safe_time.minutes;
160     clock_current_time.hours = safe_time.hours;
161     clock_current_time.day = safe_time.day;
162     clock_current_time.month = safe_time.month;
163     clock_current_time.year = safe_time.year;
164
165     disable_nmi();
166     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_SECONDS_REG);
167     cpu_write_port_byte(CMOS_DATA_PORT, seconds);
168     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_MINUTES_REG);
169     cpu_write_port_byte(CMOS_DATA_PORT, minutes);
170     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_HOURS_REG);
171     cpu_write_port_byte(CMOS_DATA_PORT, hours);
172     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_DAY_REG);
173     cpu_write_port_byte(CMOS_DATA_PORT, day);
174     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_MONTH_REG);
175     cpu_write_port_byte(CMOS_DATA_PORT, month);
176     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_YEAR_REG);
177     cpu_write_port_byte(CMOS_DATA_PORT, year);
178     enable_nmi();
179
180     leave_critical(&critical);
181     return ERR_SUCCESS;
182 }
183
184 void clock_init()
185 {
186     critical_t critical;
187
188     enter_critical(&critical);
189     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_STB_REG);
190     clock_settings = cpu_read_port_byte(CMOS_DATA_PORT);
191
192     if ((clock_settings & (CLOCK_ALARM_INT | CLOCK_PERIODIC_INT | CLOCK_UPDATE_INT)) != CLOCK_UPDATE_INT)
193     {
194         clock_settings &= ~(CLOCK_ALARM_INT | CLOCK_PERIODIC_INT);
195         clock_settings |= CLOCK_UPDATE_INT;
196
197         disable_nmi();
198         cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_STB_REG);
199         cpu_write_port_byte(CMOS_DATA_PORT, clock_settings);
200         enable_nmi();
201     }
202
203     register_irq_handler(CLOCK_IRQ, &clock_irq, FALSE);
204     cpu_write_port_byte(CMOS_CMD_PORT, CMOS_RTC_STC_REG);
205     cpu_read_port_byte(CMOS_DATA_PORT);
206     leave_critical(&critical);
207 }