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