95827463ca2b3827fd0ac47c265413857a4175b7
[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 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 timeout = 10000;
267
268         while(timeout-- && !(inb(0x64) & 0x01))
269                 udelay(50);
270
271         return (timeout <= 0) ? -1 : 0;
272 }
273
274 static int keyboard_wait_write(void)
275 {
276         int timeout = 10000;
277
278         while(timeout-- && (inb(0x64) & 0x02))
279                 udelay(50);
280
281         return (timeout <= 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 int keyboard_add_reset_handler(void (*new_handler)(void))
323 {
324         reset_handler = new_handler;
325
326         return 0;
327 }
328
329 void keyboard_init(void)
330 {
331         u8 mode;
332         map = &keyboard_layouts[0];
333
334         /* If 0x64 returns 0xff, then we have no keyboard
335          * controller */
336
337         if (inb(0x64) == 0xFF)
338                 return;
339
340         /* Empty keyboard buffer */
341         while (keyboard_havechar()) keyboard_getchar();
342
343         /* Read the current mode */
344         mode = keyboard_get_mode();
345
346         /* Turn on scancode translate mode so that we can
347            use the scancode set 1 tables */
348
349         mode |= I8042_MODE_XLATE;
350
351         /* Write the new mode */
352         keyboard_set_mode(mode);
353 }
354