Really make it safe to call printf() from any context.
[pintos-anon] / src / devices / kbd.c
1 #include "devices/kbd.h"
2 #include <ctype.h>
3 #include <debug.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "devices/intq.h"
7 #include "threads/interrupt.h"
8 #include "threads/io.h"
9
10 /* Keyboard data register port. */
11 #define DATA_REG 0x60
12
13 /* Shift state bits. */
14 #define LSHIFT  0x01            /* Left Shift. */
15 #define RSHIFT  0x02            /* Right Shift. */
16 #define LALT    0x04            /* Left Alt. */
17 #define RALT    0x08            /* Right Alt. */
18 #define LCTRL   0x10            /* Left Ctrl. */
19 #define RCTRL   0x20            /* Right Ctrl. */
20 #define CAPS    0x40            /* Caps Lock. */
21
22 /* Current shift state. */
23 static unsigned shift_state;
24
25 /* Keyboard buffer. */
26 static struct intq buffer;
27
28 static intr_handler_func keyboard_interrupt;
29
30 /* Initializes the keyboard. */
31 void
32 kbd_init (void) 
33 {
34   intq_init (&buffer, "keyboard");
35   intr_register (0x21, 0, INTR_OFF, keyboard_interrupt, "8042 Keyboard");
36 }
37
38 /* Retrieves a key from the keyboard buffer.
39    If the buffer is empty, waits for a key to be pressed. */
40 uint8_t
41 kbd_getc (void) 
42 {
43   enum intr_level old_level;
44   uint8_t key;
45
46   old_level = intr_disable ();
47   key = intq_getc (&buffer);
48   intr_set_level (old_level);
49   
50   return key;
51 }
52 \f
53 /* Maps a set of contiguous scancodes into characters. */
54 struct keymap
55   {
56     uint8_t first_scancode;     /* First scancode. */
57     const char *chars;          /* chars[0] has scancode first_scancode,
58                                    chars[1] has scancode first_scancode + 1,
59                                    and so on to the end of the string. */
60   };
61   
62 /* Keys that produce the same characters regardless of whether
63    the Shift keys are down.  Case of letters is an exception
64    that we handle elsewhere.  */
65 static const struct keymap invariant_keymap[] = 
66   {
67     {0x01, "\033"},
68     {0x0e, "\b"},
69     {0x0f, "\tQWERTYUIOP"},
70     {0x1c, "\n"},
71     {0x1e, "ASDFGHJKL"},
72     {0x2c, "ZXCVBNM"},
73     {0x37, "*"},
74     {0x39, " "},
75     {0, NULL},
76   };
77
78 /* Characters for keys pressed without Shift, for those keys
79    where it matters. */
80 static const struct keymap unshifted_keymap[] = 
81   {
82     {0x02, "1234567890-="},
83     {0x1a, "[]"},
84     {0x27, ";'`"},
85     {0x2b, "\\"},
86     {0x33, ",./"},
87     {0, NULL},
88   };
89   
90 /* Characters for keys pressed with Shift, for those keys where
91    it matters. */
92 static const struct keymap shifted_keymap[] = 
93   {
94     {0x02, "!@#$%^&*()_+"},
95     {0x1a, "{}"},
96     {0x27, ":\"~"},
97     {0x2b, "|"},
98     {0x33, "<>?"},
99     {0, NULL},
100   };
101
102 static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
103
104 static void
105 keyboard_interrupt (struct intr_frame *args UNUSED) 
106 {
107   /* Status of shift keys. */
108   bool shift = (shift_state & (LSHIFT | RSHIFT)) != 0;
109   bool alt = (shift_state & (LALT | RALT)) != 0;
110   bool ctrl = (shift_state & (LCTRL | RCTRL)) != 0;
111   bool caps = (shift_state & CAPS) != 0;
112
113   /* Keyboard scancode. */
114   unsigned code;
115
116   /* False if key pressed, true if key released. */
117   bool release;
118
119   /* Character that corresponds to `code'. */
120   uint8_t c;
121
122   /* Read scancode, including second byte if prefix code. */
123   code = inb (DATA_REG);
124   if (code == 0xe0)
125     code = (code << 8) | inb (DATA_REG);
126
127   /* Bit 0x80 distinguishes key press from key release
128      (even if there's a prefix). */
129   release = (code & 0x80) != 0;
130   code &= ~0x80u;
131
132   /* Interpret key. */
133   if (code == 0x3a) 
134     {
135       /* Caps Lock. */
136       if (!release)
137         shift_state ^= CAPS; 
138     }
139   else if (map_key (invariant_keymap, code, &c)
140            || (!shift && map_key (unshifted_keymap, code, &c))
141            || (shift && map_key (shifted_keymap, code, &c)))
142     {
143       /* Ordinary character. */
144       if (!release) 
145         {
146           /* Handle Ctrl, Shift.
147              Note that Ctrl overrides Shift. */
148           if (ctrl && c >= 0x40 && c < 0x60) 
149             {
150               /* A is 0x41, Ctrl+A is 0x01, etc. */
151               c -= 0x40; 
152             }
153           else if (shift == caps)
154             c = tolower (c);
155
156           /* Handle Alt by setting the high bit.
157              This 0x80 is unrelated to the one used to
158              distinguish key press from key release. */
159           if (alt)
160             c += 0x80;
161
162           /* Append to keyboard buffer. */
163           if (!intq_full (&buffer))
164             intq_putc (&buffer, c);
165         }
166     }
167   else
168     {
169       /* Table of shift keys.
170          Maps a keycode into a shift_state bit. */
171       static const unsigned shift_keys[][2] = 
172         {
173           {  0x2a, LSHIFT},
174           {  0x36, RSHIFT},
175           {  0x38, LALT},
176           {0xe038, RALT},
177           {  0x1d, LCTRL},
178           {0xe01d, RCTRL},
179           {0, 0},
180         };
181   
182       const unsigned (*key)[2];
183
184       /* Scan the table. */
185       for (key = shift_keys; (*key)[0] != 0; key++) 
186         if ((*key)[0] == code)
187           {
188             if (release)
189               shift_state &= ~(*key)[1];
190             else
191               shift_state |= (*key)[1];
192             break;
193           }
194     }
195 }
196
197 /* Scans the array of keymaps K for SCANCODE.
198    If found, sets *C to the corresponding character and returns
199    true.
200    If not found, returns false and C is ignored. */
201 static bool
202 map_key (const struct keymap k[], unsigned scancode, uint8_t *c) 
203 {
204   for (; k->first_scancode != 0; k++)
205     if (scancode >= k->first_scancode
206         && scancode < k->first_scancode + strlen (k->chars)) 
207       {
208         *c = k->chars[scancode - k->first_scancode];
209         return true; 
210       }
211
212   return false;
213 }