Major reorganization in prep for appserver merge
[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
409         if ((inb(KBSTATP) & KBS_DIB) == 0)
410                 return -1;
411
412         data = inb(KBDATAP);
413
414         if (data == 0xE0) {
415                 // E0 escape character
416                 shift |= E0ESC;
417                 return 0;
418         } else if (data & 0x80) {
419                 // Key released
420                 data = (shift & E0ESC ? data : data & 0x7F);
421                 shift &= ~(shiftcode[data] | E0ESC);
422                 return 0;
423         } else if (shift & E0ESC) {
424                 // Last character was an E0 escape; or with 0x80
425                 data |= 0x80;
426                 shift &= ~E0ESC;
427         }
428
429         shift |= shiftcode[data];
430         shift ^= togglecode[data];
431
432         c = charcode[shift & (CTL | SHIFT)][data];
433
434         //Scrolling screen functionality
435         if((shift & SHIFT) && ((c == KEY_UP) || (c == KEY_PGUP))) {
436                 crt_scrolled = TRUE;
437                 scroll_screen_up();
438                 return 0;
439         }
440         else if((shift & SHIFT) && ((c == KEY_DN) || (c == KEY_PGDN))) {
441                 crt_scrolled = TRUE;
442                 scroll_screen_down();
443                 return 0;
444         }
445         else if((shift & SHIFT) && c == KEY_RT) {
446                 crt_scrolled = FALSE;
447                 reset_screen();
448                 return 0;
449         }
450
451         // On keypress other than SHIFT, reset if we were scrolled
452         if(crt_scrolled && (!(shift & SHIFT))) {
453                 crt_scrolled = FALSE;
454                 reset_screen();
455         }
456
457         //Force character to capital if caps lock on
458         if (shift & CAPSLOCK) {
459                 if ('a' <= c && c <= 'z')
460                         c += 'A' - 'a';
461                 else if ('A' <= c && c <= 'Z')
462                         c += 'a' - 'A';
463         }
464
465         // Process special keys
466         // Ctrl-Alt-Del: reboot
467         if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
468                 cprintf("Rebooting!\n");
469                 outb(0x92, 0x3); // courtesy of Chris Frost
470         }
471
472         return c;
473 }
474
475 void
476 kbd_intr(void)
477 {
478         cons_intr(kbd_proc_data);
479 }
480
481 void
482 kbd_init(void)
483 {
484 }
485
486
487
488 /***** General device-independent console code *****/
489 // Here we manage the console input buffer,
490 // where we stash characters received from the keyboard or serial port
491 // whenever the corresponding interrupt occurs.
492
493 #define CONSBUFSIZE     512
494 struct cons {
495         uint8_t buf[CONSBUFSIZE];
496         uint32_t rpos;
497         uint32_t wpos;
498 };
499
500 static struct cons SLOCKED(&lock) cons;
501
502 // called by device interrupt routines to feed input characters
503 // into the circular console input buffer.
504 void
505 cons_intr(int (*proc)(void))
506 {
507         int c;
508
509         while ((c = (*proc)()) != -1) {
510                 if (c == 0)
511                         continue;
512                 spin_lock_irqsave(&lock);
513                 cons.buf[cons.wpos++] = c;
514                 if (cons.wpos == CONSBUFSIZE)
515                         cons.wpos = 0;
516                 spin_unlock_irqsave(&lock);
517         }
518 }
519
520 // return the next input character from the console, or 0 if none waiting
521 int
522 cons_getc(void)
523 {
524         int c;
525
526         // poll for any pending input characters,
527         // so that this function works even when interrupts are disabled
528         // (e.g., when called from the kernel monitor).
529         #ifndef SERIAL_IO
530                 serial_intr();
531         #endif
532         kbd_intr();
533
534         // grab the next character from the input buffer.
535         spin_lock_irqsave(&lock);
536         if (cons.rpos != cons.wpos) {
537                 c = cons.buf[cons.rpos++];
538                 if (cons.rpos == CONSBUFSIZE)
539                         cons.rpos = 0;
540                 spin_unlock_irqsave(&lock);
541                 return c;
542         }
543         spin_unlock_irqsave(&lock);
544         return 0;
545 }
546
547 // output a character to the console
548 void
549 cons_putc(int c)
550 {
551         //static uint32_t lock; zra: moving up for sharC annotations
552         spin_lock_irqsave(&lock);
553         #ifndef SERIAL_IO
554                 serial_putc(c);
555         #endif
556         //lpt_putc(c);
557         cga_putc(c);
558         spin_unlock_irqsave(&lock);
559 }
560
561 // initialize the console devices
562 void
563 cons_init(void)
564 {
565         cga_init();
566         kbd_init();
567         serial_init();
568
569         if (!serial_exists)
570                 cprintf("Serial port does not exist!\n");
571 }
572
573
574 // `High'-level console I/O.  Used by readline and cprintf.
575
576 void
577 cputchar(int c)
578 {
579         cons_putc(c);
580 }
581
582 void
583 cputbuf(const char*COUNT(len) buf, int len)
584 {
585         int i;
586         for(i = 0; i < len; i++)
587                 cons_putc(buf[i]);
588 }
589
590 int
591 getchar(void)
592 {
593         int c;
594
595         while ((c = cons_getc()) == 0)
596                 /* do nothing */;
597         return c;
598 }
599
600 int
601 iscons(int fdnum)
602 {
603         // used by readline
604         return 1;
605 }