kern

view src/timer.c @ 55:88a6c4e192f9

Fixed most important task switching bugs. Now it seems that I can switch in and out of user space reliably.
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 15 Aug 2011 04:03:39 +0300
parents b1e8c8251884
children 0be4615594df
line source
1 #include <stdio.h>
2 #include <time.h>
3 #include "intr.h"
4 #include "asmops.h"
5 #include "timer.h"
6 #include "proc.h"
7 #include "sched.h"
8 #include "config.h"
10 /* frequency of the oscillator driving the 8254 timer */
11 #define OSC_FREQ_HZ 1193182
13 /* macro to divide and round to the nearest integer */
14 #define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2))
16 /* I/O ports connected to the 8254 */
17 #define PORT_DATA0 0x40
18 #define PORT_DATA1 0x41
19 #define PORT_DATA2 0x42
20 #define PORT_CMD 0x43
22 /* command bits */
23 #define CMD_CHAN0 0
24 #define CMD_CHAN1 (1 << 6)
25 #define CMD_CHAN2 (2 << 6)
26 #define CMD_RDBACK (3 << 6)
28 #define CMD_LATCH 0
29 #define CMD_ACCESS_LOW (1 << 4)
30 #define CMD_ACCESS_HIGH (2 << 4)
31 #define CMD_ACCESS_BOTH (3 << 4)
33 #define CMD_OP_INT_TERM 0
34 #define CMD_OP_ONESHOT (1 << 1)
35 #define CMD_OP_RATE (2 << 1)
36 #define CMD_OP_SQWAVE (3 << 1)
37 #define CMD_OP_SOFT_STROBE (4 << 1)
38 #define CMD_OP_HW_STROBE (5 << 1)
40 #define CMD_MODE_BIN 0
41 #define CMD_MODE_BCD 1
44 #define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000)
46 struct timer_event {
47 int dt; /* remaining ticks delta from the previous event */
48 struct timer_event *next;
49 };
52 static void intr_handler();
55 static struct timer_event *evlist;
58 void init_timer(void)
59 {
60 /* calculate the reload count: round(osc / freq) */
61 int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ);
63 /* set the mode to square wave for channel 0, both low
64 * and high reload count bytes will follow...
65 */
66 outb(CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE, PORT_CMD);
68 /* write the low and high bytes of the reload count to the
69 * port for channel 0
70 */
71 outb(reload_count & 0xff, PORT_DATA0);
72 outb((reload_count >> 8) & 0xff, PORT_DATA0);
74 /* set the timer interrupt handler */
75 interrupt(IRQ_TO_INTR(0), intr_handler);
76 }
78 void sleep(unsigned long msec)
79 {
80 int ticks, tsum, istate;
81 struct timer_event *ev, *node;
83 printf("sleep(%lu)\n", msec);
85 if((ticks = MSEC_TO_TICKS(msec)) <= 0) {
86 return;
87 }
89 if(!(ev = malloc(sizeof *ev))) {
90 printf("sleep: failed to allocate timer_event structure\n");
91 return;
92 }
94 istate = get_intr_state();
95 disable_intr();
97 /* insert at the beginning */
98 if(!evlist || ticks <= evlist->dt) {
99 ev->next = evlist;
100 evlist = ev;
102 ev->dt = ticks;
103 if(ev->next) {
104 ev->next->dt -= ticks;
105 }
106 } else {
108 tsum = evlist->dt;
109 node = evlist;
111 while(node->next && ticks > tsum + node->next->dt) {
112 tsum += node->next->dt;
113 node = node->next;
114 }
116 ev->next = node->next;
117 node->next = ev;
119 /* fix the relative times */
120 ev->dt = ticks - tsum;
121 if(ev->next) {
122 ev->next->dt -= ev->dt;
123 }
124 }
126 set_intr_state(istate);
128 /* wait on the address of this timer event */
129 wait(ev);
130 }
132 /* This will be called by the interrupt dispatcher approximately
133 * every 1/250th of a second, so it must be extremely fast.
134 * For now, just increasing a tick counter will suffice.
135 */
136 static void intr_handler(int inum)
137 {
138 int istate;
139 struct process *cur_proc;
141 nticks++;
143 printf("TICKS: %d\n", nticks);
145 istate = get_intr_state();
146 disable_intr();
148 /* find out if there are any timers that have to go off */
149 if(evlist) {
150 evlist->dt--;
152 while(evlist->dt <= 0) {
153 struct timer_event *ev = evlist;
154 evlist = evlist->next;
156 printf("timer going off!!!\n");
157 /* wake up all processes waiting on this address */
158 wakeup(ev);
159 free(ev);
160 }
161 }
163 if((cur_proc = get_current_proc())) {
164 /* if the timeslice of this process has expire, call the scheduler */
165 if(--cur_proc->ticks_left <= 0) {
166 schedule();
167 }
168 }
170 set_intr_state(istate);
171 }