blob: 7bdcb6013f1e2efac11578165730dbf13a0658c8 [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 Crousedb8c0ab2008-11-24 17:54:46 +000030#include <libpayload-config.h>
Jordan Crousef6145c32008-03-19 23:56:58 +000031#include <libpayload.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 +000039struct layout_maps {
40 char *country;
41 unsigned short map[4][0x57];
42};
43
Stefan Reinauer1beabe12010-03-25 18:52:24 +000044static struct layout_maps *map;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000045
Stefan Reinauer1beabe12010-03-25 18:52:24 +000046static struct layout_maps keyboard_layouts[] = {
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000047#ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
48{ .country = "us", .map = {
Patrick Georgie6408a32008-09-17 18:12:46 +000049 { /* No modifier */
Uwe Hermann6a441bf2008-03-20 19:54:59 +000050 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,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000057 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,
Patrick Georgie6408a32008-09-17 18:12:46 +000059 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
60 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
Uwe Hermann6a441bf2008-03-20 19:54:59 +000061 },
Patrick Georgie6408a32008-09-17 18:12:46 +000062 { /* Shift */
Uwe Hermann6a441bf2008-03-20 19:54:59 +000063 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,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000070 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,
Patrick Georgie6408a32008-09-17 18:12:46 +000072 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,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000083 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,
Patrick Georgie6408a32008-09-17 18:12:46 +000085 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,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000096 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,
Patrick Georgie6408a32008-09-17 18:12:46 +000098 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
99 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000100 }
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000101}},
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
Jordan Crousef6145c32008-03-19 23:56:58 +0000160};
161
Patrick Georgie6408a32008-09-17 18:12:46 +0000162#define MOD_SHIFT (1 << 0)
163#define MOD_CTRL (1 << 1)
164#define MOD_CAPSLOCK (1 << 2)
165#define MOD_ALT (1 << 3)
Jordan Crousef6145c32008-03-19 23:56:58 +0000166
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000167static 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
Jordan Crousef6145c32008-03-19 23:56:58 +0000176int keyboard_havechar(void)
177{
178 unsigned char c = inb(0x64);
Jordan Crouseec6363d2008-10-20 17:07:26 +0000179 return (c == 0xFF) ? 0 : c & 1;
Jordan Crousef6145c32008-03-19 23:56:58 +0000180}
181
182unsigned 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
192int keyboard_getchar(void)
193{
Stefan Reinauer56471f12008-09-02 09:35:43 +0000194 static int modifier = 0;
Jordan Crousef6145c32008-03-19 23:56:58 +0000195 unsigned char ch;
196 int shift;
Jordan Crousef6145c32008-03-19 23:56:58 +0000197 int ret = 0;
198
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000199 while (!keyboard_havechar()) ;
Jordan Crousef6145c32008-03-19 23:56:58 +0000200
201 ch = keyboard_get_scancode();
202
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000203 switch (ch) {
Jordan Crousef6145c32008-03-19 23:56:58 +0000204 case 0x36:
205 case 0x2a:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000206 modifier |= MOD_SHIFT;
Jordan Crousef6145c32008-03-19 23:56:58 +0000207 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000208 case 0x80 | 0x36:
209 case 0x80 | 0x2a:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000210 modifier &= ~MOD_SHIFT;
Stefan Reinauer96b734c2008-08-12 14:19:40 +0000211 break;
Patrick Georgie6408a32008-09-17 18:12:46 +0000212 case 0x38:
213 modifier |= MOD_ALT;
214 break;
215 case 0x80 | 0x38:
216 modifier &= ~MOD_ALT;
217 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000218 case 0x1d:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000219 modifier |= MOD_CTRL;
Jordan Crousef6145c32008-03-19 23:56:58 +0000220 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000221 case 0x80 | 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 0x3a:
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000225 if (modifier & MOD_CAPSLOCK) {
Jordan Crousef6145c32008-03-19 23:56:58 +0000226 modifier &= ~MOD_CAPSLOCK;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000227 keyboard_cmd(0xed, (0 << 2));
228 } else {
Jordan Crousef6145c32008-03-19 23:56:58 +0000229 modifier |= MOD_CAPSLOCK;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000230 keyboard_cmd(0xed, (1 << 2));
231 }
Jordan Crousef6145c32008-03-19 23:56:58 +0000232 break;
233 }
234
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000235 if (!(ch & 0x80) && ch < 0x57) {
236 shift =
237 (modifier & MOD_SHIFT) ^ (modifier & MOD_CAPSLOCK) ? 1 : 0;
Patrick Georgie6408a32008-09-17 18:12:46 +0000238
239 if (modifier & MOD_ALT)
240 shift += 2;
241
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000242 ret = map->map[shift][ch];
Jordan Crousef6145c32008-03-19 23:56:58 +0000243
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000244 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 }
Jordan Crousef6145c32008-03-19 23:56:58 +0000257 }
258
259 return ret;
260}
Jordan Crouse63f181f2008-04-25 23:09:39 +0000261
262static int keyboard_wait_read(void)
263{
264 int timeout = 10000;
265
266 while(timeout-- && !(inb(0x64) & 0x01))
267 udelay(50);
268
269 return (timeout <= 0) ? -1 : 0;
270}
271
272static int keyboard_wait_write(void)
273{
274 int timeout = 10000;
275
276 while(timeout-- && (inb(0x64) & 0x02))
277 udelay(50);
278
279 return (timeout <= 0) ? -1 : 0;
280}
281
282static unsigned char keyboard_get_mode(void)
283{
284 outb(I8042_CMD_READ_MODE, 0x64);
285 keyboard_wait_read();
286 return inb(0x60);
287}
288
289static 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
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000296/**
297 * Set keyboard layout
298 * @param country string describing the keyboard layout language.
Patrick Georgi4727c072008-10-16 19:20:51 +0000299 * Valid values are "us", "de".
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000300 */
301
302int 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
Patrick Georgi657a6dc2008-10-21 15:08:18 +0000320static struct console_input_driver cons = {
321 .havekey = keyboard_havechar,
322 .getchar = keyboard_getchar
323};
324
Jordan Crouse63f181f2008-04-25 23:09:39 +0000325void keyboard_init(void)
326{
327 u8 mode;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000328 map = &keyboard_layouts[0];
Jordan Crouse63f181f2008-04-25 23:09:39 +0000329
Jordan Crouseec6363d2008-10-20 17:07:26 +0000330 /* If 0x64 returns 0xff, then we have no keyboard
331 * controller */
332
333 if (inb(0x64) == 0xFF)
334 return;
335
Patrick Georgie6408a32008-09-17 18:12:46 +0000336 /* Empty keyboard buffer */
337 while (keyboard_havechar()) keyboard_getchar();
338
Jordan Crouse63f181f2008-04-25 23:09:39 +0000339 /* 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);
Patrick Georgi657a6dc2008-10-21 15:08:18 +0000349
350 console_add_input_driver(&cons);
Jordan Crouse63f181f2008-04-25 23:09:39 +0000351}
352