0663f47bc636f2e1acd88f0ef2aafceb118555bb
[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         outb(cmd, 0x60);
170         /* wait until keyboard controller accepts cmds: */
171         while (inb(0x64) & 2);
172         outb(val, 0x60);
173         while (inb(0x64) & 2);
174 }
175
176 int keyboard_havechar(void)
177 {
178         unsigned char c = inb(0x64);
179         return (c == 0xFF) ? 0 : c & 1;
180 }
181
182 unsigned char keyboard_get_scancode(void)
183 {
184         unsigned char ch = 0;
185
186         if (keyboard_havechar())
187                 ch = inb(0x60);
188
189         return ch;
190 }
191
192 int keyboard_getchar(void)
193 {
194         static int modifier = 0;
195         unsigned char ch;
196         int shift;
197         int ret = 0;
198
199         while (!keyboard_havechar()) ;
200
201         ch = keyboard_get_scancode();
202
203         switch (ch) {
204         case 0x36:
205         case 0x2a:
206                 modifier |= MOD_SHIFT;
207                 break;
208         case 0x80 | 0x36:
209         case 0x80 | 0x2a:
210                 modifier &= ~MOD_SHIFT;
211                 break;
212         case 0x38:
213                 modifier |= MOD_ALT;
214                 break;
215         case 0x80 | 0x38:
216                 modifier &= ~MOD_ALT;
217                 break;
218         case 0x1d:
219                 modifier |= MOD_CTRL;
220                 break;
221         case 0x80 | 0x1d:
222                 modifier &= ~MOD_CTRL;
223                 break;
224         case 0x3a:
225                 if (modifier & MOD_CAPSLOCK) {
226                         modifier &= ~MOD_CAPSLOCK;
227                         keyboard_cmd(0xed, (0 << 2));
228                 } else {
229                         modifier |= MOD_CAPSLOCK;
230                         keyboard_cmd(0xed, (1 << 2));
231                 }
232                 break;
233         }
234
235         if (!(ch & 0x80) && ch < 0x57) {
236                 shift =
237                     (modifier & MOD_SHIFT) ^ (modifier & MOD_CAPSLOCK) ? 1 : 0;
238
239                 if (modifier & MOD_ALT)
240                         shift += 2;
241
242                 ret = map->map[shift][ch];
243
244                 if (modifier & MOD_CTRL) {
245                         switch (ret) {
246                         case 'a' ... 'z':
247                                 ret &= 0x1f;
248                                 break;
249                         case KEY_DC:
250                                 /* vulcan nerve pinch */
251                                 if ((modifier & MOD_ALT) && reset_handler)
252                                         reset_handler();
253                         default:
254                                 ret = 0;
255                         }
256                 }
257         }
258
259         return ret;
260 }
261
262 static int keyboard_wait_read(void)
263 {
264         int retries = 10000;
265
266         while(retries-- && !(inb(0x64) & 0x01))
267                 udelay(50);
268
269         return (retries <= 0) ? -1 : 0;
270 }
271
272 static int keyboard_wait_write(void)
273 {
274         int retries = 10000;
275
276         while(retries-- && (inb(0x64) & 0x02))
277                 udelay(50);
278
279         return (retries <= 0) ? -1 : 0;
280 }
281
282 static unsigned char keyboard_get_mode(void)
283 {
284         outb(I8042_CMD_READ_MODE, 0x64);
285         keyboard_wait_read();
286         return inb(0x60);
287 }
288
289 static void keyboard_set_mode(unsigned char mode)
290 {
291         outb(I8042_CMD_WRITE_MODE, 0x64);
292         keyboard_wait_write();
293         outb(mode, 0x60);
294 }
295
296 /**
297  * Set keyboard layout
298  * @param country string describing the keyboard layout language.
299  * Valid values are "us", "de".
300  */
301
302 int keyboard_set_layout(char *country)
303 {
304         int i;
305
306         for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
307                 if (strncmp(keyboard_layouts[i].country, country,
308                                         strlen(keyboard_layouts[i].country)))
309                         continue;
310
311                 /* Found, changing keyboard layout */
312                 map = &keyboard_layouts[i];
313                 return 0;
314         }
315
316         /* Nothing found, not changed */
317         return -1;
318 }
319
320 static struct console_input_driver cons = {
321         .havekey = keyboard_havechar,
322         .getchar = keyboard_getchar
323 };
324
325 void keyboard_init(void)
326 {
327         u8 mode;
328         map = &keyboard_layouts[0];
329
330         /* If 0x64 returns 0xff, then we have no keyboard
331          * controller */
332
333         if (inb(0x64) == 0xFF)
334                 return;
335
336         /* Empty keyboard buffer */
337         while (keyboard_havechar()) keyboard_getchar();
338
339         /* Read the current mode */
340         mode = keyboard_get_mode();
341
342         /* Turn on scancode translate mode so that we can
343            use the scancode set 1 tables */
344
345         mode |= I8042_MODE_XLATE;
346
347         /* Write the new mode */
348         keyboard_set_mode(mode);
349
350         console_add_input_driver(&cons);
351 }
352