- Implemented execp*.
[planlOS.git] / system / kernel / ke / timer.c
bloba398daae9e0f881c1b3b4e871be6c8046060d3af
1 /*
2 Copyright (C) 2008 Mathias Gottschlag
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in the
6 Software without restriction, including without limitation the rights to use,
7 copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
8 Software, and to permit persons to whom the Software is furnished to do so,
9 subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #include "ke/timer.h"
23 #include "ke/ports.h"
24 #include "ke/apic.h"
25 #include "ke/interrupts.h"
26 #include "ke/debug.h"
27 #include "ke/errors.h"
28 #include <stdlib.h>
29 #include <string.h>
31 // TODO: Make this work without APIC
33 static volatile uint32_t interrupt_count = 0;
34 static uint32_t ticks_per_usec = 0;
35 static volatile uint32_t acpi_start = 0;
37 static uint32_t us_per_tick = 0;
39 static void keTimerCalibrationHandler(KeCPU *cpu, uint8_t intno)
41 if (interrupt_count == 0)
43 // Initialize APIC timer
44 keAPICWrite(0x3e0, 0xb);
45 keAPICWrite(0x320, 0x20020);
46 keAPICWrite(0x380, 0xFFFFFFFF);
48 else if (interrupt_count == 1)
50 acpi_start = keAPICRead(0x390);
52 else if (interrupt_count == 2)
54 // Get APIC timer speed
55 ticks_per_usec = (acpi_start - keAPICRead(0x390)) / 10000;
57 interrupt_count++;
60 static void keTimerHandler(KeCPU *cpu, uint8_t intno)
62 // Next timer
63 KeTimer *oldtimer = cpu->next_timer;
64 cpu->next_timer = cpu->next_timer->next;
65 oldtimer->active = 0;
66 oldtimer->prev = 0;
67 oldtimer->next = 0;
68 oldtimer->queued = 0;
69 // Increase current time and restart periodic timer
70 if (oldtimer->periodic)
72 cpu->time += oldtimer->time;
73 keSetTimer(oldtimer, oldtimer->time, 1);
75 else
77 cpu->time = oldtimer->time;
79 if (cpu->next_timer != oldtimer)
81 // Start next timer if it wasn't already started
82 if (cpu->time > cpu->next_timer->targettime)
84 cpu->next_timer->targettime = cpu->time + 30;
86 keAPICWrite(0x380, ticks_per_usec * (cpu->next_timer->targettime - cpu->time));
87 cpu->next_timer->active = 1;
89 if (oldtimer->callback) oldtimer->callback(oldtimer);
91 if (oldtimer == cpu->sched_timer)
93 char *cpusign = (char*)0xC00B8000 + keAPICGetCPU() * 2;
94 cpusign[0] = '*';
95 cpusign[1]++;
97 // Schedule to another thread here
98 KeThread *newthread = keGetSchedulableThread(1);
99 if (newthread)
101 cpu->currentthread = newthread;
103 else
105 keLockSpinlock(&cpu->idlethread->active);
106 cpu->currentthread = cpu->idlethread;
108 // EOI
109 keAPICWrite(0xb0, 0);
112 void keInitTimer(uint32_t frequency)
114 // Calibrate APIC timer
115 keRegisterIRQ(0, keTimerCalibrationHandler);
116 // Set PIT to 10ms-interval
117 uint16_t divisor = (uint16_t)(1193180 / 100);
118 outb(0x43, 0x34);
119 outb(0x40, divisor & 0xFF);
120 outb(0x40, divisor >> 8);
121 asm ("sti");
122 while (interrupt_count < 3)
124 char *cpusign = (char*)0xC00B8000 + keAPICGetCPU() * 2;
125 cpusign[0] = 'k';
126 cpusign[1]++;
127 asm("hlt");
129 // Stop APIC timer
130 keAPICWrite(0x320, 0x10000);
131 asm("cli");
132 keRegisterIRQ(0, 0);
133 keRegisterIRQ(0x10, keTimerHandler);
134 us_per_tick = 1000000 / frequency;
137 void keInstallTimer(void)
139 keAPICWrite(0x3e0, 0xb);
140 keAPICWrite(0x320, 0x20030);
141 KeCPU *cpu = keGetCurrentCPU();
142 cpu->sched_timer = keCreateTimer();
143 cpu->starttime = keGetUnixTime();
144 keSetTimer(cpu->sched_timer, us_per_tick, 1);
145 // keRegisterIRQ(0, keTimerHandler);
148 KeTimer *keCreateTimer(void)
150 KeTimer *timer = malloc(sizeof(KeTimer));
151 memset(timer, 0, sizeof(KeTimer));
152 return timer;
154 int keDestroyTimer(KeTimer *timer)
156 free(timer);
157 return 0;
159 int keSetTimer(KeTimer *timer, uint64_t time, int periodic)
161 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
162 KeCPU *cpu = keGetCurrentCPU();
163 if (timer->queued) keCancelTimer(timer);
164 // Store data in the struct
165 timer->time = time;
166 uint32_t currenttime = keGetTime();
167 if (periodic)
169 timer->targettime = currenttime + time;
171 else
173 timer->targettime = time;
175 timer->periodic = periodic;
176 // Add timer to timer queue
177 if (!cpu->next_timer)
179 keAPICWrite(0x380, ticks_per_usec * (timer->targettime - currenttime));
180 timer->active = 1;
181 cpu->next_timer = timer;
183 else
185 if (cpu->next_timer->targettime > timer->targettime)
187 // First timer to expire
188 if (currenttime >= timer->targettime)
190 timer->targettime = currenttime + 1;
192 keAPICWrite(0x380, ticks_per_usec * (timer->targettime - currenttime));
193 timer->next = cpu->next_timer;
194 cpu->next_timer = timer;
195 timer->active = 1;
197 else
199 // Add later in the queue
200 KeTimer *prev_timer = cpu->next_timer;
201 while (prev_timer->next && (prev_timer->next->targettime < timer->targettime))
203 prev_timer = prev_timer->next;
205 timer->next = prev_timer->next;
206 prev_timer->next = timer;
209 timer->queued = 1;
210 keSetExecutionLevel(oldlevel);
211 return 0;
213 int keCancelTimer(KeTimer *timer)
215 // TODO
216 return KE_ERROR_UNKNOWN;
218 int keAdjustTimer(KeTimer *timer, uint64_t time)
220 // TODO: Currently only works for timers on the current CPU!
221 if (!timer->queued) return KE_ERROR_UNKNOWN;
222 if (time == 0) time = 1;
223 // Set target time
224 uint32_t currenttime = keGetTime();
225 timer->targettime = currenttime + time;
226 // TODO: Move back in the queue if needed
227 if (timer->active) keAPICWrite(0x380, ticks_per_usec * (timer->targettime - currenttime));
228 return 0;
231 uint64_t keGetTime(void)
233 KeCPU *cpu = keGetCurrentCPU();
234 if (!cpu->next_timer || !cpu->next_timer->active) return cpu->time;
235 uint32_t tickcount = keAPICRead(0x390);
236 return cpu->next_timer->targettime - tickcount / ticks_per_usec;
238 uint32_t keGetTimePerTick(void)
240 return us_per_tick;
243 static uint8_t keReadCMOS(uint8_t index)
245 uint8_t indexbyte = inb(0x70);
246 indexbyte = (indexbyte & 0x80) | (index & 0x7F);
247 outb(0x70, indexbyte);
249 return inb(0x71);
252 static unsigned int days[12] = {
254 31 + 28,
255 31 + 28 + 31,
256 31 + 28 + 31 + 30,
257 31 + 28 + 31 + 30 + 31,
258 31 + 28 + 31 + 30 + 31 + 30,
259 31 + 28 + 31 + 30 + 31 + 30 + 31,
260 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
261 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
262 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
263 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
264 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31
267 static int keIsLeapYear(unsigned int year)
269 if (year % 400 == 0) return 1;
270 if (year % 100 == 0) return 0;
271 if (year % 4 == 0) return 1;
272 return 0;
275 uint32_t keGetUnixTime(void)
277 // Read in data
278 uint32_t seconds = keReadCMOS(0x0);
279 uint32_t minutes = keReadCMOS(0x2);
280 uint32_t hours = keReadCMOS(0x4);
281 uint32_t day = keReadCMOS(0x7);
282 uint32_t month = keReadCMOS(0x8);
283 uint32_t year = keReadCMOS(0x9);
284 uint32_t century = keReadCMOS(0x32);
285 // BCD
286 seconds = (seconds & 0xF) + (seconds >> 4) * 10;
287 minutes = (minutes & 0xF) + (minutes >> 4) * 10;
288 hours = (hours & 0xF) + (hours >> 4) * 10;
289 day = (day & 0xF) + (day >> 4) * 10;
290 month = (month & 0xF) + (month >> 4) * 10;
291 year = (year & 0xF) + (year >> 4) * 10;
292 century = (century & 0xF) + (century >> 4) * 10;
293 year += century * 100;
294 day--;
295 // Add data
296 if (month > 1)
297 day += days[month - 2];
298 if ((month > 2) && keIsLeapYear(year))
299 day++;
300 year -= 1900;
301 int time = seconds + minutes * 60 + hours * 3600 + day * 86400
302 + (year - 70) * 31536000 + ((year - 69) / 4) * 86400
303 - ((year - 1) / 100) * 86400 + ((year + 299) / 400) * 86400;
304 return time;