Many changes:
[Marmot.git] / irq.c
blob9502034ffca82337c40c01f490bc7882eae02f30
1 /*
2 * irq.c --
4 * Generic IRQ handler.
5 */
7 #include <marmot.h>
10 struct tod {
11 uint8 hour;
12 uint8 minute;
13 uint8 second;
14 } __attribute__((packed));
16 struct tod tod;
20 #define KBD_REG_STATUS 0x64
21 #define KBD_STATUS_OUT_DATA (1 << 0)
22 #define KBD_STATUS_IN_DATA (1 << 1)
23 #define KBD_STATUS_SYSFLAG (1 << 2)
24 #define KBD_STATUS_INDATA_CMD (1 << 3)
25 #define KBD_STATUS_ENABLE (1 << 4)
26 #define KBD_STATUS_XMIT_TIMEOUT (1 << 5)
27 #define KBD_STATUS_AUX_DATA (1 << 5)
28 #define KBD_STATUS_RECV_TIMEOUT (1 << 6)
29 #define KBD_STATUS_PARITY_EVEN (1 << 7)
31 #define KBD_CMD_KBDIRQ 0x01
32 #define KBD_CMD_AUXIRQ 0x02
33 #define KBD_CMD_KBDDIS 0x10
34 #define KBD_CMD_AUXDIS 0x20
35 #define KBD_CMD_XLATE 0x40
38 #define KBD_REG_COMMAND 0x64
39 #define KBD_REG_DATA 0x60
41 #define MOUSE_ACK 0xfa
44 #define RTC_PORT_ADDR 0x70
45 #define RTC_PORT_DATA 0x71
47 #define RTC_SEC 0x00
48 #define RTC_ASEC 0x01
49 #define RTC_MIN 0x02
50 #define RTC_AMIN 0x03
51 #define RTC_HOUR 0x04
52 #define RTC_AHOUR 0x05
53 #define RTC_DOW 0x06
54 #define RTC_DOM 0x07
55 #define RTC_MON 0x08
56 #define RTC_YEAR 0x09
58 #define RTC_REG_A 0x0a
59 #define RTC_REG_A_RATE 0x0f
60 #define RTC_REG_A_DIV 0x70
61 #define RTC_REG_A_UIP 0x80
63 #define RTC_REG_B 0x0b
64 #define RTC_REG_B_DST (1 << 0)
65 #define RTC_REG_B_24H (1 << 1)
66 #define RTC_REG_B_BIN (1 << 2)
67 #define RTC_REG_B_SQE (1 << 3)
68 #define RTC_REG_B_UIE (1 << 4)
69 #define RTC_REG_B_AIE (1 << 5)
70 #define RTC_REG_B_PIE (1 << 6)
71 #define RTC_REG_B_DCU (1 << 7)
73 #define RTC_REG_C 0x0c
74 #define RTC_REG_C_UIE (1 << 4)
75 #define RTC_REG_C_AIE (1 << 5)
76 #define RTC_REG_C_PIE (1 << 6)
77 #define RTC_REG_C_IRQF (1 << 7)
79 #define RTC_REG_D
80 #define RTC_REG_D_RAMP (1 << 7)
82 #define BCD_TO_BIN(b) (((((b) >> 4) & 0xf) * 10) + ((b) & 0xf))
85 void
86 MaskPIC(uint8 irq)
88 uint8 mask, port;
90 if (irq > 7) {
91 irq -= 8;
92 port = 0xa1;
93 } else {
94 port = 0x21;
97 mask = inb(port);
98 mask |= (1 << irq);
99 outb(mask, port);
102 void
103 UnmaskPIC(uint8 irq)
105 uint8 mask;
107 if (irq > 7) {
108 mask = inb(0xa1);
109 mask &= ~(1 << (irq - 8));
110 outb(mask, 0xa1);
112 irq = 2;
115 mask = inb(0x21);
116 mask &= ~(1 << irq);
117 outb(mask, 0x21);
120 void
121 AckPIC(uint8 irq)
123 if (irq > 7) {
124 outb(0x60 | (irq - 8), 0xa0);
125 irq = 2;
128 outb(0x60 | irq, 0x20);
134 CbID
135 GetCBId(void)
137 static uint64 curID = 0;
140 * XXX: One day this should be a little more robust than a one-up.
143 return ++curID;
146 static struct MouseCB *mouseCB;
148 CbID
149 InstallMouseCB(MouseCBFun cb)
151 struct MouseCB *new;
153 new = alloc(sizeof(struct MouseCB));
154 new->next = mouseCB;
155 new->id = GetCBId();
156 new->cb = cb;
158 mouseCB = new;
160 return new->id;
163 void
164 RemoveMouseCB(CbID id)
166 struct MouseCB *s, *del = NULL;
168 ASSERT(mouseCB != NULL);
170 /* Shouldn't need a lock here */
171 if (mouseCB->id == id) {
172 s = mouseCB;
173 mouseCB = s->next;
174 del = s;
175 } else {
176 for (s = mouseCB; s->next != NULL; s = s->next) {
177 if (s->next->id == id) {
178 del = s->next;
179 s->next = s->next->next;
180 break;
185 ASSERT(del != NULL);
186 bzero(del, sizeof(struct MouseCB));
187 free(del);
191 void
192 InstallIDTEntry(uint64 vec, void (*stub)(void))
194 uint64 *idt = (uint64 *)IDT;
195 VA stubAddr = (VA)stub;
197 *(idt + 2 * vec) = (((stubAddr & 0xffff0000) << 32) |
198 0x8e0100000000 |
199 ((CS64 & 0xffff) << 16) |
200 (stubAddr & 0xffff));
201 *(idt + 2 * vec + 1) = (stubAddr >> 32) & 0xffffffff;
205 void
206 DisplayClock(void)
208 Point p0, p1;
209 char time[] = { ' ', ' ', ':', ' ', ' ', ':', ' ', ' ', 0 };
211 time[0] = tod.hour / 10 + '0';
212 time[1] = tod.hour % 10 + '0';
213 time[3] = tod.minute / 10 + '0';
214 time[4] = tod.minute % 10 + '0';
215 time[6] = tod.second / 10 + '0';
216 time[7] = tod.second % 10 + '0';
218 p1.x = xResolution - 4;
219 p1.y = yResolution - 4;
221 p0.x = p1.x - 8 * 8;
222 p0.y = p1.y - 16;
224 ColorRectangle(COLOR_PLEASING_GREEN, p0, p1);
225 PrintMessage(COLOR_BLACK, p0, time);
228 void
229 UpdateClock(void)
231 /* ACK the interrupt */
232 outb(RTC_REG_C, RTC_PORT_ADDR);
233 inb(RTC_PORT_DATA);
235 /* update the time of day */
236 if (++tod.second >= 60) {
237 tod.second = 0;
238 if (++tod.minute >= 60) {
239 tod.minute = 0;
240 if (++tod.hour >= 24) {
241 tod.hour = 0;
248 static inline void
249 KbdWaitWrite(void)
251 while (inb(KBD_REG_STATUS) & KBD_STATUS_IN_DATA); // 0x2
254 static inline void
255 KbdWaitRead(void)
257 while (~inb(KBD_REG_STATUS) & KBD_STATUS_OUT_DATA); // 0x1
260 uint8
261 ReadKeyboard(void)
263 KbdWaitRead();
264 return inb(KBD_REG_DATA);
268 void
269 ClearKeyDisplay(void)
271 Point p0, p1;
273 p0.x = 4;
274 p0.y = yResolution - 4 - 16;
276 p1.x = 4 + 8 * 16; // max of 16 16 pix wide chars
277 p1.y = yResolution - 4;
279 ColorRectangle(COLOR_PLEASING_GREEN, p0, p1);
282 char keys[8];
283 uint64 keyp;
285 #define lbHex(b) ((((b) & 0xf) < 0xa) ? \
286 ((b) & 0xf) + '0' : \
287 (((b) & 0xf) - 0xa) + 'a')
288 #define hbHex(b) (lbHex((b) >> 4))
290 void
291 DisplayKeycode(void)
293 Point p;
294 char c[17];
295 uint64 k;
297 bzero(c, 17);
299 for (k = 0; k < keyp; k++) {
300 c[2 * k] = hbHex(keys[k]);
301 c[2 * k + 1] = lbHex(keys[k]);
304 p.x = 4;
305 p.y = yResolution - 4 - 16;
307 PrintMessage(COLOR_BLACK, p, c);
312 * HaveCompleteScancode --
314 * Check that a fully formed mode 2 raw scancode has been received.
317 static inline int
318 HaveCompleteScancode(void)
321 * ..
322 * f0 ..
323 * e0 ..
324 * e0 f0 ..
325 * e1 .. ..
326 * e1 f0 .. f0 ..
329 if (keys[0] == 0xe0) {
330 if (keyp == 1 || (keyp == 2 && keys[1] == 0xf0)) {
331 return 0;
333 } else if (keys[0] == 0xe1) {
334 if (keyp < 3 || (keyp == 3 && keys[1] == 0xf0) || keyp == 4) {
335 return 0;
337 } else if (keys[0] == 0xf0 && keyp == 1) {
338 return 0;
341 return 1;
345 * Keyboard --
347 * Handle the keyboard interrupt.
350 void
351 Keyboard(uint8 data)
353 keys[keyp++] = data;
355 if (HaveCompleteScancode()) {
356 ClearKeyDisplay();
357 DisplayKeycode();
359 bzero(keys, sizeof(keys));
360 keyp = 0;
365 uint8 mouse[8];
366 uint64 mousep;
367 uint64 mousePacketLength;
370 void
371 MousePacketDisplay(uint8 *packet, uint64 N)
373 Point p0, p1;
374 char s[17];
375 uint64 k;
377 bzero(s, sizeof(s));
379 p0.x = 4;
380 p0.y = yResolution - 8 - 32;
381 p1.x = 4 + 8 * 8;
382 p1.y = p0.y + 16;
383 ColorRectangle(COLOR_PLEASING_GREEN, p0, p1);
385 for (k = 0; k < N; k++) {
386 s[2 * k] = hbHex(packet[k]);
387 s[2 * k + 1] = lbHex(packet[k]);
390 PrintMessage(COLOR_BLACK, p0, s);
394 void
395 Mouse(uint8 data)
397 mouse[mousep++] = data;
399 if (mousep == mousePacketLength) {
400 struct MouseCB *s;
402 for (s = mouseCB; s != NULL; s = s->next) {
403 s->cb(mouse, mousep);
406 bzero(mouse, sizeof(mouse));
407 mousep = 0;
412 void
413 i8042(void)
415 uint8 status, data;
417 status = inb(KBD_REG_STATUS);
418 if (!(status & KBD_STATUS_OUT_DATA)) {
419 return;
422 data = inb(KBD_REG_DATA);
424 if (status & KBD_STATUS_AUX_DATA) {
425 Mouse(data);
426 } else {
427 Keyboard(data);
433 * _IRQPrint --
435 * Display an indicator of received interrupts.
438 static void
439 _IRQPrint(uint64 irq)
441 /* Not reentrant, but that isn't important here. */
442 static int counts[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
443 char s[] = { 'I', 'R', 'Q', ' ', '(', ' ', ')', 0 };
444 char c[] = { '-', '\\', '|', '/' };
445 int count = 0;
446 Point p0, p1;
448 if (count < 16) {
449 count = counts[irq]++;
450 } else {
451 count = counts[16]++;
454 s[3] = (irq < 0xa) ? irq + 0x30 : irq - 0xa + 'a';
455 s[5] = c[count % sizeof(c)];
457 p0.x = 10;
459 if (irq <= 0xf) {
460 p0.y = 30 + irq * 18;
461 } else {
462 p0.y = 30 + 16 * 18;
465 p1.x = p0.x + 8 * 8;
466 p1.y = p0.y + 16;
468 ColorRectangle(COLOR_PLEASING_GREEN, p0, p1);
469 PrintMessage(COLOR_BLACK, p0, s);
473 void
474 DoIRQ(uint64 irq)
476 _IRQPrint(irq - 0x20);
478 MaskPIC(irq - 0x20);
479 AckPIC(irq - 0x20);
481 switch (irq) {
482 case PIT_IRQ:
483 TaskSchedule(irq); // does not return
484 break;
485 case KBD_IRQ:
486 case MOUSE_IRQ:
487 i8042();
488 break;
489 case RTC_IRQ:
490 UpdateClock();
491 DisplayClock();
492 break;
495 UnmaskPIC(irq - 0x20);
499 #define PIT_C0 0x40
500 #define PIT_C1 0x41
501 #define PIT_C2 0x42
502 #define PIT_CW 0x43
505 * Actually 1193181.666... but this isn't used for keeping time to the
506 * round off error isn't significant (and it's doubtful the clock is
507 * that accurate anyways).
509 #define PIT_CLK_FREQ 1193182
510 #define PIT_COUNTDOWN (PIT_CLK_FREQ / TICK_Hz)
512 #define PIT_CW_BIN 0x00
513 #define PIT_CW_BCD 0x01
514 #define PIT_CW_MODE0 0x00
515 #define PIT_CW_MODE1 0x02
516 #define PIT_CW_MODE2 0x04
517 #define PIT_CW_MODE3 0x06
518 #define PIT_CW_MODE4 0x08
519 #define PIT_CW_MODE5 0x0a
520 #define PIT_CW_RW_LATCH 0x00
521 #define PIT_CW_RW_LSB 0x10
522 #define PIT_CW_RW_MSB 0x20
523 #define PIT_CW_RW_LSBMSB 0x30
524 #define PIT_CW_CTR0 0x00
525 #define PIT_CW_CTR1 0x40
526 #define PIT_CW_CTR2 0x80
527 #define PIT_CW_READBACK 0xc0
530 void
531 StartPIT(void)
533 uint16 tick;
536 * Set the PIT to TICK_Hz.
539 tick = PIT_COUNTDOWN;
540 outb(PIT_CW_BIN | PIT_CW_MODE2 | PIT_CW_RW_LSBMSB | PIT_CW_CTR0, PIT_CW);
541 outb(tick & 0xff, PIT_C0);
542 outb(tick >> 8, PIT_C0);
544 InstallIDTEntry(PIT_IRQ, _IRQStub_PIT);
545 UnmaskPIC(PIT_IRQ - 0x20);
549 void
550 StartClock(void)
553 * Disable daylight savings, enable 24 hour clock, and enable
554 * update ended interrupt.
557 outb(RTC_REG_B, RTC_PORT_ADDR);
558 outb(RTC_REG_B_24H | RTC_REG_B_UIE, RTC_PORT_DATA);
561 * Initialize the clock.
564 do {
565 outb(RTC_REG_A, RTC_PORT_ADDR);
566 if ((inb(RTC_PORT_DATA) & RTC_REG_A_UIP) == 0) {
567 break;
569 } while (1);
571 outb(RTC_SEC, RTC_PORT_ADDR);
572 tod.second = BCD_TO_BIN(inb(RTC_PORT_DATA));
573 outb(RTC_MIN, RTC_PORT_ADDR);
574 tod.minute = BCD_TO_BIN(inb(RTC_PORT_DATA));
575 outb(RTC_HOUR, RTC_PORT_ADDR);
576 tod.hour = BCD_TO_BIN(inb(RTC_PORT_DATA));
578 DisplayClock();
580 /* enable in PIC */
581 InstallIDTEntry(RTC_IRQ, _IRQStub_RTC);
582 UnmaskPIC(RTC_IRQ - 0x20);
586 void
587 i8042_WriteCmd(uint8 cmd)
589 KbdWaitWrite();
590 outb(cmd, KBD_REG_COMMAND);
593 uint8
594 i8042_ReadData(void)
596 //KbdWaitRead();
597 return inb(KBD_REG_DATA);
600 void
601 i8042_WriteData(uint8 data)
603 KbdWaitWrite();
604 outb(data, KBD_REG_DATA);
607 void
608 KeyboardInit(void)
610 uint8 cmd;
612 /* Get keyboard command register. */
613 i8042_WriteCmd(0x20);
614 cmd = i8042_ReadData();
616 /* Enable keyboard. */
617 cmd &= ~KBD_CMD_KBDDIS;
618 cmd |= KBD_CMD_KBDIRQ;
620 i8042_WriteCmd(0x60);
621 i8042_WriteData(cmd);
623 /* Set keyboard mode 2. */
624 i8042_WriteData(0xf0);
625 i8042_WriteData(0x02);
627 /* Clear keyboard buffer. */
629 bzero(keys, sizeof(keys));
630 keyp = 0;
632 /* Install handler. */
633 InstallIDTEntry(KBD_IRQ, _IRQStub_KBD);
634 UnmaskPIC(KBD_IRQ - 0x20);
638 uint8
639 i8042_WriteAuxCmd(uint8 cmd)
641 KbdWaitWrite();
642 outb(0xd4, KBD_REG_COMMAND);
643 KbdWaitWrite();
644 outb(cmd, KBD_REG_DATA);
646 return inb(KBD_REG_DATA);
649 void
650 MouseInit(void)
652 uint8 cmd, data;
654 /* Get keyboard command register. */
655 i8042_WriteCmd(0x20);
656 cmd = i8042_ReadData();
658 /* Enable PS/2 mouse. */
659 cmd &= ~KBD_CMD_KBDDIS;
660 cmd |= KBD_CMD_KBDIRQ;
662 i8042_WriteCmd(0x60);
663 i8042_WriteData(cmd);
665 i8042_WriteData(0xa8);
667 /* Check for intellimouse extension */
668 i8042_WriteAuxCmd(0xf3);
669 i8042_WriteAuxCmd(200);
670 i8042_WriteAuxCmd(0xf3);
671 i8042_WriteAuxCmd(100);
672 i8042_WriteAuxCmd(0xf3);
673 i8042_WriteAuxCmd(80);
675 i8042_WriteAuxCmd(0xf2);
676 data = inb(KBD_REG_DATA);
678 if (data == 0x03) {
679 mousePacketLength = 4;
680 } else {
681 mousePacketLength = 3;
684 /* Set sample rate */
685 i8042_WriteAuxCmd(0xf3);
686 i8042_WriteAuxCmd(MOUSE_SAMPLE_RATE);
688 /* Set stream mode */
689 i8042_WriteAuxCmd(0xea); // set stream mode
691 /* Set resolution */
692 i8042_WriteAuxCmd(0xe8);
693 i8042_WriteAuxCmd(3);
695 /* set scaling */
696 i8042_WriteAuxCmd(0xe7);
698 /* Enable data reporting */
699 i8042_WriteAuxCmd(0xf4); // enable data reporting
701 bzero(mouse, sizeof(mouse));
702 mousep = 0;
704 mouseCB = NULL;
706 InstallMouseCB(MousePacketDisplay);
708 /* Install mouse interrupt handler. */
709 InstallIDTEntry(MOUSE_IRQ, _IRQStub_MOUSE);
710 UnmaskPIC(MOUSE_IRQ - 0x20);