libpayload: Fix handling of CAPS LOCK key on PS/2 keyboards
[coreboot.git] / payloads / libpayload / drivers / keyboard.c
1 /*
2  * This file is part of the libpayload project.
3  *
4  * Copyright (C) 2008 Advanced Micro Devices, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <libpayload-config.h>
31 #include <libpayload.h>
32 #include <curses.h>
33
34 #define I8042_CMD_READ_MODE  0x20
35 #define I8042_CMD_WRITE_MODE 0x60
36
37 #define I8042_MODE_XLATE     0x40
38
39 struct layout_maps {
40         const char *country;
41         const unsigned short map[4][0x57];
42 };
43
44 static struct layout_maps *map;
45
46 static struct layout_maps keyboard_layouts[] = {
47 #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
48 { .country = "us", .map = {
49         { /* No modifier */
50          0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
51          0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
52          0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
53          0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
54          0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
55          0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
56          0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
57          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
58          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
59          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
60          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
61          },
62         { /* Shift */
63          0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
64          0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
65          0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
66          0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
67          0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
68          0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
69          0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
70          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
71          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
72          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
73          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
74          },
75         { /* ALT */
76          0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
77          0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
78          0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
79          0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
80          0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
81          0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
82          0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
83          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
84          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
85          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
86          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
87          },
88         { /* Shift-ALT */
89          0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
90          0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
91          0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
92          0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
93          0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
94          0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
95          0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
96          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
97          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
98          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
99          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
100          }
101 }},
102 #endif
103 #ifdef CONFIG_PC_KEYBOARD_LAYOUT_DE
104 { .country = "de", .map = {
105         { /* No modifier */
106          0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
107          0x37, 0x38, 0x39, 0x30, 0x00, 0x27, 0x08, 0x09,
108          0x71, 0x77, 0x65, 0x72, 0x74, 0x7A, 0x75, 0x69,
109          0x6F, 0x70, 0x00, 0x2B, 0x0A, 0x00, 0x61, 0x73,
110          0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x00,
111          0x00, 0x5E, 0x00, 0x23, 0x79, 0x78, 0x63, 0x76,
112          0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2D, 0x00, 0x2A,
113          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
114          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
115          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
116          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3C
117          },
118         { /* Shift */
119          0x00, 0x1B, 0x21, 0x22, 0xA7, 0x24, 0x25, 0x26,
120          0x2F, 0x28, 0x29, 0x3D, 0x3F, 0x60, 0x08, 0x00,
121          0x51, 0x57, 0x45, 0x52, 0x54, 0x5A, 0x55, 0x49,
122          0x4F, 0x50, 0x00, 0x2A, 0x0A, 0x00, 0x41, 0x53,
123          0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x00,
124          0x00, 0x7E, 0x00, 0x27, 0x59, 0x58, 0x43, 0x56,
125          0x42, 0x4E, 0x4D, 0x3B, 0x3A, 0x5F, 0x00, 0x2A,
126          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
127          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
128          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
129          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3E
130          },
131         { /* ALT */
132          0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
133          0x7B, 0x5B, 0x5D, 0x7D, 0x5C, 0x3D, 0x08, 0x09,
134          0x40, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
135          0x6F, 0x70, 0x5B, 0x7E, 0x0A, 0x00, 0x61, 0x73,
136          0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
137          0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
138          0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
139          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
140          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
141          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
142          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x7C
143          },
144         { /* Shift-ALT */
145          /* copied from US */
146          0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
147          0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
148          0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
149          0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
150          0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
151          0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
152          0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
153          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
154          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
155          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
156          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
157          }
158 }},
159 #endif
160 };
161
162 #define MOD_SHIFT    (1 << 0)
163 #define MOD_CTRL     (1 << 1)
164 #define MOD_CAPSLOCK (1 << 2)
165 #define MOD_ALT      (1 << 3)
166
167 static void keyboard_cmd(unsigned char cmd, unsigned char val)
168 {
169         while (inb(0x64) & 2);
170         outb(cmd, 0x60);
171         mdelay(20);
172
173         while (inb(0x64) & 2);
174         outb(val, 0x60);
175         mdelay(20);
176 }
177
178 int keyboard_havechar(void)
179 {
180         unsigned char c = inb(0x64);
181         return (c == 0xFF) ? 0 : c & 1;
182 }
183
184 unsigned char keyboard_get_scancode(void)
185 {
186         unsigned char ch = 0;
187
188         if (keyboard_havechar())
189                 ch = inb(0x60);
190
191         return ch;
192 }
193
194 int keyboard_getchar(void)
195 {
196         static int modifier = 0;
197         unsigned char ch;
198         int shift;
199         int ret = 0;
200
201         while (!keyboard_havechar()) ;
202
203         ch = keyboard_get_scancode();
204
205         switch (ch) {
206         case 0x36:
207         case 0x2a:
208                 modifier |= MOD_SHIFT;
209                 break;
210         case 0x80 | 0x36:
211         case 0x80 | 0x2a:
212                 modifier &= ~MOD_SHIFT;
213                 break;
214         case 0x38:
215                 modifier |= MOD_ALT;
216                 break;
217         case 0x80 | 0x38:
218                 modifier &= ~MOD_ALT;
219                 break;
220         case 0x1d:
221                 modifier |= MOD_CTRL;
222                 break;
223         case 0x80 | 0x1d:
224                 modifier &= ~MOD_CTRL;
225                 break;
226         case 0x3a:
227                 if (modifier & MOD_CAPSLOCK) {
228                         modifier &= ~MOD_CAPSLOCK;
229                         keyboard_cmd(0xed, (0 << 2));
230                 } else {
231                         modifier |= MOD_CAPSLOCK;
232                         keyboard_cmd(0xed, (1 << 2));
233                 }
234                 break;
235         }
236
237         if (!(ch & 0x80) && ch < 0x57) {
238                 shift =
239                     (modifier & MOD_SHIFT) ^ (modifier & MOD_CAPSLOCK) ? 1 : 0;
240
241                 if (modifier & MOD_ALT)
242                         shift += 2;
243
244                 ret = map->map[shift][ch];
245
246                 if (modifier & MOD_CTRL) {
247                         switch (ret) {
248                         case 'a' ... 'z':
249                                 ret &= 0x1f;
250                                 break;
251                         case KEY_DC:
252                                 /* vulcan nerve pinch */
253                                 if ((modifier & MOD_ALT) && reset_handler)
254                                         reset_handler();
255                         default:
256                                 ret = 0;
257                         }
258                 }
259         }
260
261         return ret;
262 }
263
264 static int keyboard_wait_read(void)
265 {
266         int retries = 10000;
267
268         while(retries-- && !(inb(0x64) & 0x01))
269                 udelay(50);
270
271         return (retries <= 0) ? -1 : 0;
272 }
273
274 static int keyboard_wait_write(void)
275 {
276         int retries = 10000;
277
278         while(retries-- && (inb(0x64) & 0x02))
279                 udelay(50);
280
281         return (retries <= 0) ? -1 : 0;
282 }
283
284 static unsigned char keyboard_get_mode(void)
285 {
286         outb(I8042_CMD_READ_MODE, 0x64);
287         keyboard_wait_read();
288         return inb(0x60);
289 }
290
291 static void keyboard_set_mode(unsigned char mode)
292 {
293         outb(I8042_CMD_WRITE_MODE, 0x64);
294         keyboard_wait_write();
295         outb(mode, 0x60);
296 }
297
298 /**
299  * Set keyboard layout
300  * @param country string describing the keyboard layout language.
301  * Valid values are "us", "de".
302  */
303
304 int keyboard_set_layout(char *country)
305 {
306         int i;
307
308         for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
309                 if (strncmp(keyboard_layouts[i].country, country,
310                                         strlen(keyboard_layouts[i].country)))
311                         continue;
312
313                 /* Found, changing keyboard layout */
314                 map = &keyboard_layouts[i];
315                 return 0;
316         }
317
318         /* Nothing found, not changed */
319         return -1;
320 }
321
322 static struct console_input_driver cons = {
323         .havekey = keyboard_havechar,
324         .getchar = keyboard_getchar
325 };
326
327 void keyboard_init(void)
328 {
329         u8 mode;
330         map = &keyboard_layouts[0];
331
332         /* If 0x64 returns 0xff, then we have no keyboard
333          * controller */
334
335         if (inb(0x64) == 0xFF)
336                 return;
337
338         /* Empty keyboard buffer */
339         while (keyboard_havechar()) keyboard_getchar();
340
341         /* Read the current mode */
342         mode = keyboard_get_mode();
343
344         /* Turn on scancode translate mode so that we can
345            use the scancode set 1 tables */
346
347         mode |= I8042_MODE_XLATE;
348
349         /* Write the new mode */
350         keyboard_set_mode(mode);
351
352         console_add_input_driver(&cons);
353 }
354