Avoids nehalem keyboard issues, better monitors
[akaros.git] / kern / arch / i686 / console.c
1 /* See COPYRIGHT for copyright information. */
2
3 #include <arch/x86.h>
4 #include <arch/arch.h>
5 #include <arch/console.h>
6 #include <arch/kbdreg.h>
7 #include <atomic.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <stdio.h>
11
12 #include <ros/memlayout.h>
13
14 void cons_intr(int (*proc)(void));
15 void scroll_screen(void);
16
17
18 /***** Serial I/O code *****/
19
20 #define COM1            0x3F8
21
22 #define COM_RX                  0               // In:  Receive buffer (DLAB=0)
23 #define COM_DLL                 0               // Out: Divisor Latch Low (DLAB=1)
24 #define COM_DLM                 1               // Out: Divisor Latch High (DLAB=1)
25 #define COM_IER                 1               // Out: Interrupt Enable Register
26 #define COM_IER_RDI             0x01    //   Enable receiver data interrupt
27 #define COM_IIR                 2               // In:  Interrupt ID Register
28 #define COM_FCR                 2               // Out: FIFO Control Register
29 #define COM_LCR                 3               // Out: Line Control Register
30 #define COM_LCR_DLAB    0x80    //   Divisor latch access bit
31 #define COM_LCR_WLEN8   0x03    //   Wordlength: 8 bits
32 #define COM_MCR                 4               // Out: Modem Control Register
33 #define COM_MCR_RTS             0x02    // RTS complement
34 #define COM_MCR_DTR             0x01    // DTR complement
35 #define COM_MCR_OUT2    0x08    // Out2 complement
36 #define COM_LSR                 5               // In:  Line Status Register
37 #define COM_LSR_DATA    0x01    //   Data available
38 #define COM_LSR_READY   0x20    //   Ready to send
39
40 static bool SREADONLY serial_exists;
41
42 int
43 serial_proc_data(void)
44 {
45         if (!(inb(COM1+COM_LSR) & COM_LSR_DATA))
46                 return -1;
47         return inb(COM1+COM_RX);
48 }
49
50 int serial_read_byte()
51 {
52         return serial_proc_data();
53 }
54
55 void
56 serial_intr(void)
57 {
58         if (serial_exists)
59                 cons_intr(serial_proc_data);
60 }
61
62 void
63 serial_init(void)
64 {
65         // Turn off the FIFO
66         outb(COM1+COM_FCR, 0);
67         
68         // Set speed; requires DLAB latch
69         outb(COM1+COM_LCR, COM_LCR_DLAB);
70         // Setting speed to 115200 (setting the divider to 1)
71         outb(COM1+COM_DLL, 1);
72         outb(COM1+COM_DLM, 0);
73
74         // 8 data bits, 1 stop bit, parity off; turn off DLAB latch
75         outb(COM1+COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB);
76
77         // This should turn on hardware flow control
78         outb(COM1+COM_MCR, COM_MCR_RTS | COM_MCR_DTR);
79         // Enable rcv interrupts
80         outb(COM1+COM_IER, COM_IER_RDI);
81
82         // Clear any preexisting overrun indications and interrupts
83         // Serial port doesn't exist if COM_LSR returns 0xFF
84         {
85                 bool lbool = ((inb(COM1+COM_LSR) != 0xFF));
86                 serial_exists = SINIT(lbool);
87         }
88         (void) inb(COM1+COM_IIR);
89         (void) inb(COM1+COM_RX);
90
91 }
92
93 void serial_send_byte(uint8_t b)
94 {
95         while (!(inb(COM1+COM_LSR) & COM_LSR_READY));
96         outb(COM1, b);
97 }
98
99 static void
100 serial_putc(int c)
101 {
102         switch (c & 0xff) {
103         case '\b':
104                 serial_send_byte('\b');
105                 serial_send_byte((uint8_t)(' '));
106                 serial_send_byte('\b');
107                 break;
108         case '\n':
109         case '\r':
110                 serial_send_byte((uint8_t)('\n'));
111                 serial_send_byte((uint8_t)('\r'));
112                 break;
113         default:
114                 serial_send_byte((uint8_t)(c & 0xff));
115                 break;
116         }
117         return;
118 }
119
120
121
122 /***** Parallel port output code *****/
123 // For information on PC parallel port programming, see the class References
124 // page.
125
126 // Stupid I/O delay routine necessitated by historical PC design flaws
127 static void
128 delay(void)
129 {
130         inb(0x84);
131         inb(0x84);
132         inb(0x84);
133         inb(0x84);
134 }
135
136 static void
137 lpt_putc(int c)
138 {
139         int i;
140
141         for (i = 0; !(inb(0x378+1) & 0x80) && i < 12800; i++)
142                 delay();
143         outb(0x378+0, c);
144         outb(0x378+2, 0x08|0x04|0x01);
145         outb(0x378+2, 0x08);
146 }
147
148
149
150
151 /***** Text-mode CGA/VGA display output with scrolling *****/
152 #define MAX_SCROLL_LENGTH       20
153 #define SCROLLING_CRT_SIZE      (MAX_SCROLL_LENGTH * CRT_SIZE)
154
155 static spinlock_t SRACY lock = SPINLOCK_INITIALIZER;
156
157 static unsigned SREADONLY addr_6845;
158 static uint16_t *SLOCKED(&lock) COUNT(CRT_SIZE) crt_buf;
159 static uint16_t SLOCKED(&lock) crt_pos;
160
161 static uint16_t (SLOCKED(&lock) scrolling_crt_buf)[SCROLLING_CRT_SIZE];
162 static uint16_t SLOCKED(&lock) scrolling_crt_pos;
163 static uint8_t  SLOCKED(&lock) current_crt_buf;
164
165 void
166 cga_init(void)
167 {
168         volatile uint16_t SLOCKED(&lock)*COUNT(CRT_SIZE) cp;
169         uint16_t was;
170         unsigned pos;
171
172         cp = (uint16_t *COUNT(CRT_SIZE)) TC(KERNBASE + CGA_BUF);
173         was = *cp;
174         *cp = (uint16_t) 0xA55A;
175         if (*cp != 0xA55A) {
176                 cp = (uint16_t *COUNT(CRT_SIZE)) TC(KERNBASE + MONO_BUF);
177                 addr_6845 = SINIT(MONO_BASE);
178         } else {
179                 *cp = was;
180                 addr_6845 = SINIT(CGA_BASE);
181         }
182         
183         /* Extract cursor location */
184         outb(addr_6845, 14);
185         pos = inb(addr_6845 + 1) << 8;
186         outb(addr_6845, 15);
187         pos |= inb(addr_6845 + 1);
188
189         crt_buf = (uint16_t SLOCKED(&lock)*COUNT(CRT_SIZE)) cp;
190         crt_pos = pos;
191         scrolling_crt_pos = 0;
192         current_crt_buf = 0;
193
194 }
195
196 static void set_screen(uint8_t screen_num)
197 {
198         uint16_t leftovers = (scrolling_crt_pos % CRT_COLS);
199         leftovers = (leftovers) ? CRT_COLS - leftovers : 0;
200         
201         int offset = scrolling_crt_pos + leftovers - (screen_num + 1)*CRT_SIZE;
202         offset = (offset > 0) ? offset : 0;
203
204         memcpy(crt_buf, scrolling_crt_buf + offset, CRT_SIZE * sizeof(uint16_t));
205 }
206
207 static void scroll_screen_up(void)
208 {
209         if(current_crt_buf <  (scrolling_crt_pos / CRT_SIZE))
210                 current_crt_buf++;
211         set_screen(current_crt_buf);
212 }
213
214 static void scroll_screen_down(void)
215 {
216         if(current_crt_buf > 0) 
217                 current_crt_buf--;
218         set_screen(current_crt_buf);
219 }
220
221 static void reset_screen(void)
222 {
223         current_crt_buf = 0;
224         set_screen(current_crt_buf);
225 }
226
227 void
228 cga_putc(int c)
229 {
230         // if no attribute given, then use black on white
231         if (!(c & ~0xFF))
232                 c |= 0x0700;
233
234         switch (c & 0xff) {
235         case '\b':
236                 if (crt_pos > 0) {
237                         crt_pos--;
238                         scrolling_crt_pos--;
239
240                         crt_buf[crt_pos] = (c & ~0xff) | ' ';
241                         scrolling_crt_buf[scrolling_crt_pos] = crt_buf[crt_pos];
242                 }
243                 break;
244         case '\n':
245                 crt_pos += CRT_COLS;
246                 scrolling_crt_pos += CRT_COLS;
247                 /* fallthru */
248         case '\r':
249                 crt_pos -= (crt_pos % CRT_COLS);
250                 scrolling_crt_pos -= (scrolling_crt_pos % CRT_COLS);
251                 break;
252         case '\t':
253                 cga_putc(' ');
254                 cga_putc(' ');
255                 cga_putc(' ');
256                 cga_putc(' ');
257                 cga_putc(' ');
258                 break;
259         default:
260                 crt_buf[crt_pos++] = c;         /* write the character */
261                 scrolling_crt_buf[scrolling_crt_pos++] = c;
262                 break;
263         }
264
265         // The purpose of this is to allow the screen to appear as if it is scrolling as
266         // more lines are added beyond the size of the monitor.  The top line is dropped
267         // and everything is shifted up by one.
268         if (crt_pos >= CRT_SIZE) {
269                 int i;
270
271                 memcpy(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
272                 for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
273                         crt_buf[i] = 0x0700 | ' ';
274                 crt_pos -= CRT_COLS;
275         }
276         // Do the same for the scrolling crt buffer when it hits its capacity
277         if (scrolling_crt_pos >= SCROLLING_CRT_SIZE) {
278                 int i;
279
280                 memcpy(scrolling_crt_buf, scrolling_crt_buf + CRT_COLS, 
281                        (SCROLLING_CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
282                 for (i = SCROLLING_CRT_SIZE - CRT_COLS; i < SCROLLING_CRT_SIZE; i++)
283                         scrolling_crt_buf[i] = 0x0700 | ' ';
284                 scrolling_crt_pos -= CRT_COLS;
285         }
286
287
288         /* move that little blinky thing */
289         outb(addr_6845, 14);
290         outb(addr_6845 + 1, crt_pos >> 8);
291         outb(addr_6845, 15);
292         outb(addr_6845 + 1, crt_pos);
293 }
294
295
296 /***** Keyboard input code *****/
297
298 #define NO              0
299
300 #define SHIFT   (1<<0)
301 #define CTL             (1<<1)
302 #define ALT             (1<<2)
303
304 #define CAPSLOCK        (1<<3)
305 #define NUMLOCK         (1<<4)
306 #define SCROLLLOCK      (1<<5)
307
308 #define E0ESC           (1<<6)
309
310 static uint8_t (SREADONLY shiftcode)[256] = 
311 {
312         [0x1D] CTL,
313         [0x2A] SHIFT,
314         [0x36] SHIFT,
315         [0x38] ALT,
316         [0x9D] CTL,
317         [0xB8] ALT
318 };
319
320 static uint8_t (SREADONLY togglecode)[256] = 
321 {
322         [0x3A] CAPSLOCK,
323         [0x45] NUMLOCK,
324         [0x46] SCROLLLOCK
325 };
326
327 static uint8_t normalmap[256] =
328 {
329         NO,   0x1B, '1',  '2',  '3',  '4',  '5',  '6',  // 0x00
330         '7',  '8',  '9',  '0',  '-',  '=',  '\b', '\t',
331         'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',  // 0x10
332         'o',  'p',  '[',  ']',  '\n', NO,   'a',  's',
333         'd',  'f',  'g',  'h',  'j',  'k',  'l',  ';',  // 0x20
334         '\'', '`',  NO,   '\\', 'z',  'x',  'c',  'v',
335         'b',  'n',  'm',  ',',  '.',  '/',  NO,   '*',  // 0x30
336         NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
337         NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
338         '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
339         '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
340         [0xC7] KEY_HOME,        [0x9C] '\n' /*KP_Enter*/,
341         [0xB5] '/' /*KP_Div*/,  [0xC8] KEY_UP,
342         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
343         [0xCD] KEY_RT,          [0xCF] KEY_END,
344         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
345         [0xD2] KEY_INS,         [0xD3] KEY_DEL
346 };
347
348 static uint8_t shiftmap[256] = 
349 {
350         NO,   033,  '!',  '@',  '#',  '$',  '%',  '^',  // 0x00
351         '&',  '*',  '(',  ')',  '_',  '+',  '\b', '\t',
352         'Q',  'W',  'E',  'R',  'T',  'Y',  'U',  'I',  // 0x10
353         'O',  'P',  '{',  '}',  '\n', NO,   'A',  'S',
354         'D',  'F',  'G',  'H',  'J',  'K',  'L',  ':',  // 0x20
355         '"',  '~',  NO,   '|',  'Z',  'X',  'C',  'V',
356         'B',  'N',  'M',  '<',  '>',  '?',  NO,   '*',  // 0x30
357         NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
358         NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
359         '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
360         '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
361         [0xC7] KEY_HOME,        [0x9C] '\n' /*KP_Enter*/,
362         [0xB5] '/' /*KP_Div*/,  [0xC8] KEY_UP,
363         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
364         [0xCD] KEY_RT,          [0xCF] KEY_END,
365         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
366         [0xD2] KEY_INS,         [0xD3] KEY_DEL
367 };
368
369 #define C(x) (x - '@')
370
371 static uint8_t ctlmap[256] = 
372 {
373         NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO, 
374         NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO, 
375         C('Q'),  C('W'),  C('E'),  C('R'),  C('T'),  C('Y'),  C('U'),  C('I'),
376         C('O'),  C('P'),  NO,      NO,      '\r',    NO,      C('A'),  C('S'),
377         C('D'),  C('F'),  C('G'),  C('H'),  C('J'),  C('K'),  C('L'),  NO, 
378         NO,      NO,      NO,      C('\\'), C('Z'),  C('X'),  C('C'),  C('V'),
379         C('B'),  C('N'),  C('M'),  NO,      NO,      C('/'),  NO,      NO,
380         [0x97] KEY_HOME,
381         [0xB5] C('/'),          [0xC8] KEY_UP,
382         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
383         [0xCD] KEY_RT,          [0xCF] KEY_END,
384         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
385         [0xD2] KEY_INS,         [0xD3] KEY_DEL
386 };
387
388 static uint8_t * COUNT(256) (SREADONLY charcode)[4] = {
389         normalmap,
390         shiftmap,
391         ctlmap,
392         ctlmap
393 };
394
395 /*
396  * Get data from the keyboard.  If we finish a character, return it.  Else 0.
397  * Return -1 if no data.
398  */
399 static uint32_t SLOCKED(&lock) shift;
400 static bool SLOCKED(&lock) crt_scrolled = FALSE;
401
402 static int
403 kbd_proc_data(void)
404 {
405         int c;
406         uint8_t data;
407
408 #ifdef __CONFIG_KB_CORE0_ONLY__
409         /* Ghetto hack to avoid crashing brho's buggy nehalem. */
410         uint32_t eax, ebx, ecx, edx, family, model, stepping;
411         cpuid(1, &eax, &ebx, &ecx, &edx);
412         family = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8);
413         model = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4);
414         stepping = eax & 0x0000000F;
415         if (family == 6 && model == 26 && stepping == 4)
416                 if (core_id())
417                         return -1;
418 #endif /* __CONFIG_KB_CORE0_ONLY */
419
420         if ((inb(KBSTATP) & KBS_DIB) == 0)
421                 return -1;
422
423         data = inb(KBDATAP);
424
425         if (data == 0xE0) {
426                 // E0 escape character
427                 shift |= E0ESC;
428                 return 0;
429         } else if (data & 0x80) {
430                 // Key released
431                 data = (shift & E0ESC ? data : data & 0x7F);
432                 shift &= ~(shiftcode[data] | E0ESC);
433                 return 0;
434         } else if (shift & E0ESC) {
435                 // Last character was an E0 escape; or with 0x80
436                 data |= 0x80;
437                 shift &= ~E0ESC;
438         }
439
440         shift |= shiftcode[data];
441         shift ^= togglecode[data];
442
443         c = charcode[shift & (CTL | SHIFT)][data];
444
445         //Scrolling screen functionality
446         if((shift & SHIFT) && ((c == KEY_UP) || (c == KEY_PGUP))) {
447                 crt_scrolled = TRUE;
448                 scroll_screen_up();
449                 return 0;
450         }
451         else if((shift & SHIFT) && ((c == KEY_DN) || (c == KEY_PGDN))) {
452                 crt_scrolled = TRUE;
453                 scroll_screen_down();
454                 return 0;
455         }
456         else if((shift & SHIFT) && c == KEY_RT) {
457                 crt_scrolled = FALSE;
458                 reset_screen();
459                 return 0;
460         }
461
462         // On keypress other than SHIFT, reset if we were scrolled
463         if(crt_scrolled && (!(shift & SHIFT))) {
464                 crt_scrolled = FALSE;
465                 reset_screen();
466         }
467
468         //Force character to capital if caps lock on
469         if (shift & CAPSLOCK) {
470                 if ('a' <= c && c <= 'z')
471                         c += 'A' - 'a';
472                 else if ('A' <= c && c <= 'Z')
473                         c += 'a' - 'A';
474         }
475
476         // Process special keys
477         // Ctrl-Alt-Del: reboot
478         if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
479                 cprintf("Rebooting!\n");
480                 outb(0x92, 0x3); // courtesy of Chris Frost
481         }
482
483         return c;
484 }
485
486 void
487 kbd_intr(void)
488 {
489         cons_intr(kbd_proc_data);
490 }
491
492 void
493 kbd_init(void)
494 {
495 }
496
497
498
499 /***** General device-independent console code *****/
500 // Here we manage the console input buffer,
501 // where we stash characters received from the keyboard or serial port
502 // whenever the corresponding interrupt occurs.
503
504 #define CONSBUFSIZE     512
505 struct cons {
506         uint8_t buf[CONSBUFSIZE];
507         uint32_t rpos;
508         uint32_t wpos;
509 };
510
511 static struct cons SLOCKED(&lock) cons;
512
513 // called by device interrupt routines to feed input characters
514 // into the circular console input buffer.
515 void
516 cons_intr(int (*proc)(void))
517 {
518         int c;
519
520         while ((c = (*proc)()) != -1) {
521                 if (c == 0)
522                         continue;
523                 spin_lock_irqsave(&lock);
524                 cons.buf[cons.wpos++] = c;
525                 if (cons.wpos == CONSBUFSIZE)
526                         cons.wpos = 0;
527                 spin_unlock_irqsave(&lock);
528         }
529 }
530
531 // return the next input character from the console, or 0 if none waiting
532 int
533 cons_getc(void)
534 {
535         int c;
536
537         // poll for any pending input characters,
538         // so that this function works even when interrupts are disabled
539         // (e.g., when called from the kernel monitor).
540         #ifndef __CONFIG_SERIAL_IO__
541                 serial_intr();
542         #endif
543         kbd_intr();
544
545         // grab the next character from the input buffer.
546         spin_lock_irqsave(&lock);
547         if (cons.rpos != cons.wpos) {
548                 c = cons.buf[cons.rpos++];
549                 if (cons.rpos == CONSBUFSIZE)
550                         cons.rpos = 0;
551                 spin_unlock_irqsave(&lock);
552                 return c;
553         }
554         spin_unlock_irqsave(&lock);
555         return 0;
556 }
557
558 // output a character to the console
559 void
560 cons_putc(int c)
561 {
562         //static uint32_t lock; zra: moving up for sharC annotations
563         spin_lock_irqsave(&lock);
564         #ifndef __CONFIG_SERIAL_IO__
565                 serial_putc(c);
566         #endif
567         //lpt_putc(c);
568         cga_putc(c);
569         spin_unlock_irqsave(&lock);
570 }
571
572 // initialize the console devices
573 void
574 cons_init(void)
575 {
576         cga_init();
577         kbd_init();
578         serial_init();
579
580         if (!serial_exists)
581                 cprintf("Serial port does not exist!\n");
582 }
583
584
585 // `High'-level console I/O.  Used by readline and cprintf.
586
587 void
588 cputchar(int c)
589 {
590         cons_putc(c);
591 }
592
593 void
594 cputbuf(const char*COUNT(len) buf, int len)
595 {
596         int i;
597         for(i = 0; i < len; i++)
598                 cons_putc(buf[i]);
599 }
600
601 int
602 getchar(void)
603 {
604         int c;
605
606         while ((c = cons_getc()) == 0)
607                 /* do nothing */;
608         return c;
609 }
610
611 int
612 iscons(int fdnum)
613 {
614         // used by readline
615         return 1;
616 }