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