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