Reboot when Ctrl+Alt+Del is pressed.
[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/input.h"
7 #include "threads/init.h"
8 #include "threads/interrupt.h"
9 #include "threads/io.h"
10
11 /* Keyboard data register port. */
12 #define DATA_REG 0x60
13
14 /* Current state of shift keys.
15    True if depressed, false otherwise. */
16 static bool left_shift, right_shift;    /* Left and right Shift keys. */
17 static bool left_alt, right_alt;        /* Left and right Alt keys. */
18 static bool left_ctrl, right_ctrl;      /* Left and right Ctl keys. */
19
20 /* Status of Caps Lock.
21    True when on, false when off. */
22 static bool caps_lock;
23
24 /* Number of keys pressed. */
25 static int64_t key_cnt;
26
27 static intr_handler_func keyboard_interrupt;
28
29 /* Initializes the keyboard. */
30 void
31 kbd_init (void) 
32 {
33   intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
34 }
35
36 /* Prints keyboard statistics. */
37 void
38 kbd_print_stats (void) 
39 {
40   printf ("Keyboard: %lld keys pressed\n", key_cnt);
41 }
42 \f
43 /* Maps a set of contiguous scancodes into characters. */
44 struct keymap
45   {
46     uint8_t first_scancode;     /* First scancode. */
47     const char *chars;          /* chars[0] has scancode first_scancode,
48                                    chars[1] has scancode first_scancode + 1,
49                                    and so on to the end of the string. */
50   };
51   
52 /* Keys that produce the same characters regardless of whether
53    the Shift keys are down.  Case of letters is an exception
54    that we handle elsewhere.  */
55 static const struct keymap invariant_keymap[] = 
56   {
57     {0x01, "\033"},             /* Escape. */
58     {0x0e, "\b"},
59     {0x0f, "\tQWERTYUIOP"},
60     {0x1c, "\r"},
61     {0x1e, "ASDFGHJKL"},
62     {0x2c, "ZXCVBNM"},
63     {0x37, "*"},
64     {0x39, " "},
65     {0x53, "\177"},             /* Delete. */
66     {0, NULL},
67   };
68
69 /* Characters for keys pressed without Shift, for those keys
70    where it matters. */
71 static const struct keymap unshifted_keymap[] = 
72   {
73     {0x02, "1234567890-="},
74     {0x1a, "[]"},
75     {0x27, ";'`"},
76     {0x2b, "\\"},
77     {0x33, ",./"},
78     {0, NULL},
79   };
80   
81 /* Characters for keys pressed with Shift, for those keys where
82    it matters. */
83 static const struct keymap shifted_keymap[] = 
84   {
85     {0x02, "!@#$%^&*()_+"},
86     {0x1a, "{}"},
87     {0x27, ":\"~"},
88     {0x2b, "|"},
89     {0x33, "<>?"},
90     {0, NULL},
91   };
92
93 static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
94
95 static void
96 keyboard_interrupt (struct intr_frame *args UNUSED) 
97 {
98   /* Status of shift keys. */
99   bool shift = left_shift || right_shift;
100   bool alt = left_alt || right_alt;
101   bool ctrl = left_ctrl || right_ctrl;
102
103   /* Keyboard scancode. */
104   unsigned code;
105
106   /* False if key pressed, true if key released. */
107   bool release;
108
109   /* Character that corresponds to `code'. */
110   uint8_t c;
111
112   /* Read scancode, including second byte if prefix code. */
113   code = inb (DATA_REG);
114   if (code == 0xe0)
115     code = (code << 8) | inb (DATA_REG);
116
117   /* Bit 0x80 distinguishes key press from key release
118      (even if there's a prefix). */
119   release = (code & 0x80) != 0;
120   code &= ~0x80u;
121
122   /* Interpret key. */
123   if (code == 0x3a) 
124     {
125       /* Caps Lock. */
126       if (!release)
127         caps_lock = !caps_lock;
128     }
129   else if (map_key (invariant_keymap, code, &c)
130            || (!shift && map_key (unshifted_keymap, code, &c))
131            || (shift && map_key (shifted_keymap, code, &c)))
132     {
133       /* Ordinary character. */
134       if (!release) 
135         {
136           /* Reboot if Ctrl+Alt+Del pressed. */
137           if (c == 0177 && ctrl && alt)
138             reboot ();
139
140           /* Handle Ctrl, Shift.
141              Note that Ctrl overrides Shift. */
142           if (ctrl && c >= 0x40 && c < 0x60) 
143             {
144               /* A is 0x41, Ctrl+A is 0x01, etc. */
145               c -= 0x40; 
146             }
147           else if (shift == caps_lock)
148             c = tolower (c);
149
150           /* Handle Alt by setting the high bit.
151              This 0x80 is unrelated to the one used to
152              distinguish key press from key release. */
153           if (alt)
154             c += 0x80;
155
156           /* Append to keyboard buffer. */
157           if (!input_full ())
158             {
159               key_cnt++;
160               input_putc (c);
161             }
162         }
163     }
164   else
165     {
166       /* Maps a keycode into a shift state variable. */
167       struct shift_key 
168         {
169           unsigned scancode;
170           bool *state_var;
171         };
172
173       /* Table of shift keys. */
174       static const struct shift_key shift_keys[] = 
175         {
176           {  0x2a, &left_shift},
177           {  0x36, &right_shift},
178           {  0x38, &left_alt},
179           {0xe038, &right_alt},
180           {  0x1d, &left_ctrl},
181           {0xe01d, &right_ctrl},
182           {0,      NULL},
183         };
184   
185       const struct shift_key *key;
186
187       /* Scan the table. */
188       for (key = shift_keys; key->scancode != 0; key++) 
189         if (key->scancode == code)
190           {
191             *key->state_var = !release;
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 }