blob: 944b9a66a22c21dbd82a01743d65e58be0ecf2c2 [file] [log] [blame]
Jordan Crousef6145c32008-03-19 23:56:58 +00001/*
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
Jordan Crousef6145c32008-03-19 23:56:58 +000030#include <libpayload.h>
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000031#include <config.h>
Patrick Georgie6408a32008-09-17 18:12:46 +000032#include <curses.h>
Jordan Crousef6145c32008-03-19 23:56:58 +000033
Jordan Crouse63f181f2008-04-25 23:09:39 +000034#define I8042_CMD_READ_MODE 0x20
35#define I8042_CMD_WRITE_MODE 0x60
36
37#define I8042_MODE_XLATE 0x40
38
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000039static void (*reset_handler)(void) = NULL;
40
41struct layout_maps {
42 char *country;
43 unsigned short map[4][0x57];
44};
45
46struct layout_maps *map;
47
48struct layout_maps keyboard_layouts[] = {
49#ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
50{ .country = "us", .map = {
Patrick Georgie6408a32008-09-17 18:12:46 +000051 { /* No modifier */
Uwe Hermann6a441bf2008-03-20 19:54:59 +000052 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,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000059 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,
Patrick Georgie6408a32008-09-17 18:12:46 +000061 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
62 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
Uwe Hermann6a441bf2008-03-20 19:54:59 +000063 },
Patrick Georgie6408a32008-09-17 18:12:46 +000064 { /* Shift */
Uwe Hermann6a441bf2008-03-20 19:54:59 +000065 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,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000072 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,
Patrick Georgie6408a32008-09-17 18:12:46 +000074 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,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000085 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,
Patrick Georgie6408a32008-09-17 18:12:46 +000087 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,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000098 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,
Patrick Georgie6408a32008-09-17 18:12:46 +0000100 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
101 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000102 }
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000103}},
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
Jordan Crousef6145c32008-03-19 23:56:58 +0000162};
163
Patrick Georgie6408a32008-09-17 18:12:46 +0000164#define MOD_SHIFT (1 << 0)
165#define MOD_CTRL (1 << 1)
166#define MOD_CAPSLOCK (1 << 2)
167#define MOD_ALT (1 << 3)
Jordan Crousef6145c32008-03-19 23:56:58 +0000168
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000169static 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
Jordan Crousef6145c32008-03-19 23:56:58 +0000179int keyboard_havechar(void)
180{
181 unsigned char c = inb(0x64);
182 return c & 1;
183}
184
185unsigned 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
195int keyboard_getchar(void)
196{
Stefan Reinauer56471f12008-09-02 09:35:43 +0000197 static int modifier = 0;
Jordan Crousef6145c32008-03-19 23:56:58 +0000198 unsigned char ch;
199 int shift;
Jordan Crousef6145c32008-03-19 23:56:58 +0000200 int ret = 0;
201
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000202 while (!keyboard_havechar()) ;
Jordan Crousef6145c32008-03-19 23:56:58 +0000203
204 ch = keyboard_get_scancode();
205
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000206 switch (ch) {
Jordan Crousef6145c32008-03-19 23:56:58 +0000207 case 0x36:
208 case 0x2a:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000209 modifier |= MOD_SHIFT;
Jordan Crousef6145c32008-03-19 23:56:58 +0000210 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000211 case 0x80 | 0x36:
212 case 0x80 | 0x2a:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000213 modifier &= ~MOD_SHIFT;
Stefan Reinauer96b734c2008-08-12 14:19:40 +0000214 break;
Patrick Georgie6408a32008-09-17 18:12:46 +0000215 case 0x38:
216 modifier |= MOD_ALT;
217 break;
218 case 0x80 | 0x38:
219 modifier &= ~MOD_ALT;
220 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000221 case 0x1d:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000222 modifier |= MOD_CTRL;
Jordan Crousef6145c32008-03-19 23:56:58 +0000223 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000224 case 0x80 | 0x1d:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000225 modifier &= ~MOD_CTRL;
Jordan Crousef6145c32008-03-19 23:56:58 +0000226 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000227 case 0x3a:
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000228 if (modifier & MOD_CAPSLOCK) {
Jordan Crousef6145c32008-03-19 23:56:58 +0000229 modifier &= ~MOD_CAPSLOCK;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000230 keyboard_cmd(0xed, (0 << 2));
231 } else {
Jordan Crousef6145c32008-03-19 23:56:58 +0000232 modifier |= MOD_CAPSLOCK;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000233 keyboard_cmd(0xed, (1 << 2));
234 }
Jordan Crousef6145c32008-03-19 23:56:58 +0000235 break;
236 }
237
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000238 if (!(ch & 0x80) && ch < 0x57) {
239 shift =
240 (modifier & MOD_SHIFT) ^ (modifier & MOD_CAPSLOCK) ? 1 : 0;
Patrick Georgie6408a32008-09-17 18:12:46 +0000241
242 if (modifier & MOD_ALT)
243 shift += 2;
244
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000245 ret = map->map[shift][ch];
Jordan Crousef6145c32008-03-19 23:56:58 +0000246
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000247 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 }
Jordan Crousef6145c32008-03-19 23:56:58 +0000260 }
261
262 return ret;
263}
Jordan Crouse63f181f2008-04-25 23:09:39 +0000264
265static 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
275static 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
285static unsigned char keyboard_get_mode(void)
286{
287 outb(I8042_CMD_READ_MODE, 0x64);
288 keyboard_wait_read();
289 return inb(0x60);
290}
291
292static 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
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000299/**
300 * Set keyboard layout
301 * @param country string describing the keyboard layout language.
302 * Valid values are "en", "de".
303 */
304
305int 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
323int keyboard_add_reset_handler(void (*new_handler)(void))
324{
325 reset_handler = new_handler;
326
327 return 0;
328}
329
Jordan Crouse63f181f2008-04-25 23:09:39 +0000330void keyboard_init(void)
331{
332 u8 mode;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000333 map = &keyboard_layouts[0];
Jordan Crouse63f181f2008-04-25 23:09:39 +0000334
Patrick Georgie6408a32008-09-17 18:12:46 +0000335 /* Empty keyboard buffer */
336 while (keyboard_havechar()) keyboard_getchar();
337
Jordan Crouse63f181f2008-04-25 23:09:39 +0000338 /* 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