Added more SharC annotations
[akaros.git] / kern / arch / i386 / 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 volatile uint32_t SRACY lock = 0;
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                 cons.buf[cons.wpos++] = c;
513                 if (cons.wpos == CONSBUFSIZE)
514                         cons.wpos = 0;
515         }
516 }
517
518 // return the next input character from the console, or 0 if none waiting
519 int
520 cons_getc(void)
521 {
522         int c;
523
524         spin_lock_irqsave(&lock);
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         if (cons.rpos != cons.wpos) {
536                 c = cons.buf[cons.rpos++];
537                 if (cons.rpos == CONSBUFSIZE)
538                         cons.rpos = 0;
539                 spin_unlock_irqsave(&lock);
540                 return c;
541         }
542         spin_unlock_irqsave(&lock);
543         return 0;
544 }
545
546 // output a character to the console
547 void
548 cons_putc(int c)
549 {
550         //static uint32_t lock; zra: moving up for sharC annotations
551         spin_lock_irqsave(&lock);
552         #ifndef SERIAL_IO
553                 serial_putc(c);
554         #endif
555         //lpt_putc(c);
556         cga_putc(c);
557         spin_unlock_irqsave(&lock);
558 }
559
560 // initialize the console devices
561 void
562 cons_init(void)
563 {
564         cga_init();
565         kbd_init();
566         serial_init();
567
568         if (!serial_exists)
569                 cprintf("Serial port does not exist!\n");
570 }
571
572
573 // `High'-level console I/O.  Used by readline and cprintf.
574
575 void
576 cputchar(int c)
577 {
578         cons_putc(c);
579 }
580
581 void
582 cputbuf(const char*COUNT(len) buf, int len)
583 {
584         int i;
585         for(i = 0; i < len; i++)
586                 cons_putc(buf[i]);
587 }
588
589 int
590 getchar(void)
591 {
592         int c;
593
594         while ((c = cons_getc()) == 0)
595                 /* do nothing */;
596         return c;
597 }
598
599 int
600 iscons(int fdnum)
601 {
602         // used by readline
603         return 1;
604 }