* add keyboard layout support to libpayload
[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.h>
31 #include <config.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 static void (*reset_handler)(void) = NULL;
40
41 struct layout_maps {
42         char *country;
43         unsigned short map[4][0x57];
44 };
45
46 struct layout_maps *map;
47
48 struct layout_maps keyboard_layouts[] = {
49 #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
50 { .country = "us", .map = {
51         { /* No modifier */
52          0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
53          0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
54          0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
55          0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
56          0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
57          0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
58          0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
59          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
60          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
61          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
62          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
63          },
64         { /* Shift */
65          0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
66          0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
67          0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
68          0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
69          0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
70          0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
71          0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
72          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
73          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
74          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
75          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
76          },
77         { /* ALT */
78          0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
79          0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
80          0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
81          0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
82          0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
83          0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
84          0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
85          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
86          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
87          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
88          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
89          },
90         { /* Shift-ALT */
91          0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
92          0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
93          0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
94          0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
95          0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
96          0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
97          0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
98          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
99          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
100          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
101          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
102          }
103 }},
104 #endif
105 #ifdef CONFIG_PC_KEYBOARD_LAYOUT_DE
106 { .country = "de", .map = {
107         { /* No modifier */
108          0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
109          0x37, 0x38, 0x39, 0x30, 0x00, 0x27, 0x08, 0x09,
110          0x71, 0x77, 0x65, 0x72, 0x74, 0x7A, 0x75, 0x69,
111          0x6F, 0x70, 0x00, 0x2B, 0x0A, 0x00, 0x61, 0x73,
112          0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x00,
113          0x00, 0x5E, 0x00, 0x23, 0x79, 0x78, 0x63, 0x76,
114          0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2D, 0x00, 0x2A,
115          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
116          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
117          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
118          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3C
119          },
120         { /* Shift */
121          0x00, 0x1B, 0x21, 0x22, 0xA7, 0x24, 0x25, 0x26,
122          0x2F, 0x28, 0x29, 0x3D, 0x3F, 0x60, 0x08, 0x00,
123          0x51, 0x57, 0x45, 0x52, 0x54, 0x5A, 0x55, 0x49,
124          0x4F, 0x50, 0x00, 0x2A, 0x0A, 0x00, 0x41, 0x53,
125          0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x00,
126          0x00, 0x7E, 0x00, 0x27, 0x59, 0x58, 0x43, 0x56,
127          0x42, 0x4E, 0x4D, 0x3B, 0x3A, 0x5F, 0x00, 0x2A,
128          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
129          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
130          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
131          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3E
132          },
133         { /* ALT */
134          0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
135          0x7B, 0x5B, 0x5D, 0x7D, 0x5C, 0x3D, 0x08, 0x09,
136          0x40, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
137          0x6F, 0x70, 0x5B, 0x7E, 0x0A, 0x00, 0x61, 0x73,
138          0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
139          0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
140          0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
141          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
142          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
143          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
144          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x7C
145          },
146         { /* Shift-ALT */
147          /* copied from US */
148          0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
149          0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
150          0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
151          0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
152          0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
153          0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
154          0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
155          0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
156          KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
157          KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
158          KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
159          }
160 }},
161 #endif
162 };
163
164 #define MOD_SHIFT    (1 << 0)
165 #define MOD_CTRL     (1 << 1)
166 #define MOD_CAPSLOCK (1 << 2)
167 #define MOD_ALT      (1 << 3)
168
169 static void keyboard_cmd(unsigned char cmd, unsigned char val)
170 {
171         outb(cmd, 0x60);
172         /* wait until keyboard controller accepts cmds: */
173         while (inb(0x64) & 2);
174         outb(val, 0x60);
175         while (inb(0x64) & 2);
176 }
177
178
179 int keyboard_havechar(void)
180 {
181         unsigned char c = inb(0x64);
182         return c & 1;
183 }
184
185 unsigned char keyboard_get_scancode(void)
186 {
187         unsigned char ch = 0;
188
189         if (keyboard_havechar())
190                 ch = inb(0x60);
191
192         return ch;
193 }
194
195 int keyboard_getchar(void)
196 {
197         static int modifier = 0;
198         unsigned char ch;
199         int shift;
200         int ret = 0;
201
202         while (!keyboard_havechar()) ;
203
204         ch = keyboard_get_scancode();
205
206         switch (ch) {
207         case 0x36:
208         case 0x2a:
209                 modifier |= MOD_SHIFT;
210                 break;
211         case 0x80 | 0x36:
212         case 0x80 | 0x2a:
213                 modifier &= ~MOD_SHIFT;
214                 break;
215         case 0x38:
216                 modifier |= MOD_ALT;
217                 break;
218         case 0x80 | 0x38:
219                 modifier &= ~MOD_ALT;
220                 break;
221         case 0x1d:
222                 modifier |= MOD_CTRL;
223                 break;
224         case 0x80 | 0x1d:
225                 modifier &= ~MOD_CTRL;
226                 break;
227         case 0x3a:
228                 if (modifier & MOD_CAPSLOCK) {
229                         modifier &= ~MOD_CAPSLOCK;
230                         keyboard_cmd(0xed, (0 << 2));
231                 } else {
232                         modifier |= MOD_CAPSLOCK;
233                         keyboard_cmd(0xed, (1 << 2));
234                 }
235                 break;
236         }
237
238         if (!(ch & 0x80) && ch < 0x57) {
239                 shift =
240                     (modifier & MOD_SHIFT) ^ (modifier & MOD_CAPSLOCK) ? 1 : 0;
241
242                 if (modifier & MOD_ALT)
243                         shift += 2;
244
245                 ret = map->map[shift][ch];
246
247                 if (modifier & MOD_CTRL) {
248                         switch (ret) {
249                         case 'a' ... 'z':
250                                 ret &= 0x1f;
251                                 break;
252                         case KEY_DC:
253                                 /* vulcan nerve pinch */
254                                 if ((modifier & MOD_ALT) && reset_handler)
255                                         reset_handler();
256                         default:
257                                 ret = 0;
258                         }
259                 }
260         }
261
262         return ret;
263 }
264
265 static int keyboard_wait_read(void)
266 {
267         int timeout = 10000;
268
269         while(timeout-- && !(inb(0x64) & 0x01))
270                 udelay(50);
271
272         return (timeout <= 0) ? -1 : 0;
273 }
274
275 static int keyboard_wait_write(void)
276 {
277         int timeout = 10000;
278
279         while(timeout-- && (inb(0x64) & 0x02))
280                 udelay(50);
281
282         return (timeout <= 0) ? -1 : 0;
283 }
284
285 static unsigned char keyboard_get_mode(void)
286 {
287         outb(I8042_CMD_READ_MODE, 0x64);
288         keyboard_wait_read();
289         return inb(0x60);
290 }
291
292 static void keyboard_set_mode(unsigned char mode)
293 {
294         outb(I8042_CMD_WRITE_MODE, 0x64);
295         keyboard_wait_write();
296         outb(mode, 0x60);
297 }
298
299 /**
300  * Set keyboard layout
301  * @param country string describing the keyboard layout language. 
302  * Valid values are "en", "de".
303  */
304
305 int keyboard_set_layout(char *country)
306 {
307         int i;
308
309         for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
310                 if (strncmp(keyboard_layouts[i].country, country,
311                                         strlen(keyboard_layouts[i].country)))
312                         continue;
313
314                 /* Found, changing keyboard layout */
315                 map = &keyboard_layouts[i];
316                 return 0;
317         }
318
319         /* Nothing found, not changed */
320         return -1;
321 }
322
323 int keyboard_add_reset_handler(void (*new_handler)(void))
324 {
325         reset_handler = new_handler;
326
327         return 0;
328 }
329
330 void keyboard_init(void)
331 {
332         u8 mode;
333         map = &keyboard_layouts[0];
334
335         /* Empty keyboard buffer */
336         while (keyboard_havechar()) keyboard_getchar();
337
338         /* Read the current mode */
339         mode = keyboard_get_mode();
340
341         /* Turn on scancode translate mode so that we can
342            use the scancode set 1 tables */
343
344         mode |= I8042_MODE_XLATE;
345
346         /* Write the new mode */
347         keyboard_set_mode(mode);
348 }
349