blob: 0175bc74d9571816cfdf64b9b4e7e14f9e8dc927 [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
Gabe Blacka2d786f2013-03-12 15:44:56 -070030#include <keycodes.h>
Jordan Crousedb8c0ab2008-11-24 17:54:46 +000031#include <libpayload-config.h>
Jordan Crousef6145c32008-03-19 23:56:58 +000032#include <libpayload.h>
33
Martin Roth3ee59f72013-07-26 16:31:21 -060034#define I8042_CMD_DIS_KB 0xad
35
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000036struct layout_maps {
Patrick Georgi7f965832011-04-21 18:57:16 +020037 const char *country;
38 const unsigned short map[4][0x57];
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000039};
40
Stefan Reinauer1beabe12010-03-25 18:52:24 +000041static struct layout_maps *map;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000042
Stefan Reinauer1beabe12010-03-25 18:52:24 +000043static struct layout_maps keyboard_layouts[] = {
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000044#ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
45{ .country = "us", .map = {
Patrick Georgie6408a32008-09-17 18:12:46 +000046 { /* No modifier */
Uwe Hermann6a441bf2008-03-20 19:54:59 +000047 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
48 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
49 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
50 0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
51 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
52 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
53 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000054 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
55 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 +000056 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
57 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
Uwe Hermann6a441bf2008-03-20 19:54:59 +000058 },
Patrick Georgie6408a32008-09-17 18:12:46 +000059 { /* Shift */
Uwe Hermann6a441bf2008-03-20 19:54:59 +000060 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
61 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
62 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
63 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
64 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
65 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
66 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000067 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
68 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 +000069 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
70 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
71 },
72 { /* ALT */
73 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
74 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
75 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
76 0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
77 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
78 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
79 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000080 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
81 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 +000082 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
83 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
84 },
85 { /* Shift-ALT */
86 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
87 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
88 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
89 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
90 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
91 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
92 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000093 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
94 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 +000095 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
96 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
Uwe Hermann6a441bf2008-03-20 19:54:59 +000097 }
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000098}},
99#endif
100#ifdef CONFIG_PC_KEYBOARD_LAYOUT_DE
101{ .country = "de", .map = {
102 { /* No modifier */
103 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
104 0x37, 0x38, 0x39, 0x30, 0x00, 0x27, 0x08, 0x09,
105 0x71, 0x77, 0x65, 0x72, 0x74, 0x7A, 0x75, 0x69,
106 0x6F, 0x70, 0x00, 0x2B, 0x0A, 0x00, 0x61, 0x73,
107 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x00,
108 0x00, 0x5E, 0x00, 0x23, 0x79, 0x78, 0x63, 0x76,
109 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2D, 0x00, 0x2A,
110 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
111 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
112 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
113 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3C
114 },
115 { /* Shift */
116 0x00, 0x1B, 0x21, 0x22, 0xA7, 0x24, 0x25, 0x26,
117 0x2F, 0x28, 0x29, 0x3D, 0x3F, 0x60, 0x08, 0x00,
118 0x51, 0x57, 0x45, 0x52, 0x54, 0x5A, 0x55, 0x49,
119 0x4F, 0x50, 0x00, 0x2A, 0x0A, 0x00, 0x41, 0x53,
120 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x00,
121 0x00, 0x7E, 0x00, 0x27, 0x59, 0x58, 0x43, 0x56,
122 0x42, 0x4E, 0x4D, 0x3B, 0x3A, 0x5F, 0x00, 0x2A,
123 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
124 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
125 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
126 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3E
127 },
128 { /* ALT */
129 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
130 0x7B, 0x5B, 0x5D, 0x7D, 0x5C, 0x3D, 0x08, 0x09,
131 0x40, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
132 0x6F, 0x70, 0x5B, 0x7E, 0x0A, 0x00, 0x61, 0x73,
133 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
134 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
135 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
136 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
137 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
138 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
139 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x7C
140 },
141 { /* Shift-ALT */
142 /* copied from US */
143 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
144 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
145 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
146 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
147 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
148 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
149 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
150 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
151 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
152 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
153 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
154 }
155}},
156#endif
Jordan Crousef6145c32008-03-19 23:56:58 +0000157};
158
Patrick Georgie6408a32008-09-17 18:12:46 +0000159#define MOD_SHIFT (1 << 0)
160#define MOD_CTRL (1 << 1)
161#define MOD_CAPSLOCK (1 << 2)
162#define MOD_ALT (1 << 3)
Jordan Crousef6145c32008-03-19 23:56:58 +0000163
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000164static void keyboard_cmd(unsigned char cmd, unsigned char val)
165{
Patrick Georgi583abc22011-11-10 15:48:37 +0100166 while (inb(0x64) & 2);
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000167 outb(cmd, 0x60);
Patrick Georgi583abc22011-11-10 15:48:37 +0100168 mdelay(20);
169
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000170 while (inb(0x64) & 2);
171 outb(val, 0x60);
Patrick Georgi583abc22011-11-10 15:48:37 +0100172 mdelay(20);
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000173}
174
Jordan Crousef6145c32008-03-19 23:56:58 +0000175int keyboard_havechar(void)
176{
177 unsigned char c = inb(0x64);
Jordan Crouseec6363d2008-10-20 17:07:26 +0000178 return (c == 0xFF) ? 0 : c & 1;
Jordan Crousef6145c32008-03-19 23:56:58 +0000179}
180
181unsigned char keyboard_get_scancode(void)
182{
183 unsigned char ch = 0;
184
185 if (keyboard_havechar())
186 ch = inb(0x60);
187
188 return ch;
189}
190
191int keyboard_getchar(void)
192{
Stefan Reinauer56471f12008-09-02 09:35:43 +0000193 static int modifier = 0;
Jordan Crousef6145c32008-03-19 23:56:58 +0000194 unsigned char ch;
195 int shift;
Jordan Crousef6145c32008-03-19 23:56:58 +0000196 int ret = 0;
197
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000198 while (!keyboard_havechar()) ;
Jordan Crousef6145c32008-03-19 23:56:58 +0000199
200 ch = keyboard_get_scancode();
201
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000202 switch (ch) {
Jordan Crousef6145c32008-03-19 23:56:58 +0000203 case 0x36:
204 case 0x2a:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000205 modifier |= MOD_SHIFT;
Jordan Crousef6145c32008-03-19 23:56:58 +0000206 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000207 case 0x80 | 0x36:
208 case 0x80 | 0x2a:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000209 modifier &= ~MOD_SHIFT;
Stefan Reinauer96b734c2008-08-12 14:19:40 +0000210 break;
Patrick Georgie6408a32008-09-17 18:12:46 +0000211 case 0x38:
212 modifier |= MOD_ALT;
213 break;
214 case 0x80 | 0x38:
215 modifier &= ~MOD_ALT;
216 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000217 case 0x1d:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000218 modifier |= MOD_CTRL;
Jordan Crousef6145c32008-03-19 23:56:58 +0000219 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000220 case 0x80 | 0x1d:
Stefan Reinauer56471f12008-09-02 09:35:43 +0000221 modifier &= ~MOD_CTRL;
Jordan Crousef6145c32008-03-19 23:56:58 +0000222 break;
Jordan Crousef6145c32008-03-19 23:56:58 +0000223 case 0x3a:
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000224 if (modifier & MOD_CAPSLOCK) {
Jordan Crousef6145c32008-03-19 23:56:58 +0000225 modifier &= ~MOD_CAPSLOCK;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000226 keyboard_cmd(0xed, (0 << 2));
227 } else {
Jordan Crousef6145c32008-03-19 23:56:58 +0000228 modifier |= MOD_CAPSLOCK;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000229 keyboard_cmd(0xed, (1 << 2));
230 }
Jordan Crousef6145c32008-03-19 23:56:58 +0000231 break;
232 }
233
Uwe Hermann6a441bf2008-03-20 19:54:59 +0000234 if (!(ch & 0x80) && ch < 0x57) {
235 shift =
236 (modifier & MOD_SHIFT) ^ (modifier & MOD_CAPSLOCK) ? 1 : 0;
Patrick Georgie6408a32008-09-17 18:12:46 +0000237
238 if (modifier & MOD_ALT)
239 shift += 2;
240
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000241 ret = map->map[shift][ch];
Jordan Crousef6145c32008-03-19 23:56:58 +0000242
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000243 if (modifier & MOD_CTRL) {
244 switch (ret) {
245 case 'a' ... 'z':
246 ret &= 0x1f;
247 break;
248 case KEY_DC:
249 /* vulcan nerve pinch */
250 if ((modifier & MOD_ALT) && reset_handler)
251 reset_handler();
252 default:
253 ret = 0;
254 }
255 }
Jordan Crousef6145c32008-03-19 23:56:58 +0000256 }
257
258 return ret;
259}
Jordan Crouse63f181f2008-04-25 23:09:39 +0000260
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000261/**
262 * Set keyboard layout
Stefan Reinauer14e22772010-04-27 06:56:47 +0000263 * @param country string describing the keyboard layout language.
Patrick Georgi4727c072008-10-16 19:20:51 +0000264 * Valid values are "us", "de".
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000265 */
266
267int keyboard_set_layout(char *country)
268{
269 int i;
270
271 for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
272 if (strncmp(keyboard_layouts[i].country, country,
273 strlen(keyboard_layouts[i].country)))
274 continue;
275
276 /* Found, changing keyboard layout */
277 map = &keyboard_layouts[i];
278 return 0;
279 }
280
281 /* Nothing found, not changed */
282 return -1;
283}
284
Patrick Georgi657a6dc2008-10-21 15:08:18 +0000285static struct console_input_driver cons = {
286 .havekey = keyboard_havechar,
287 .getchar = keyboard_getchar
288};
289
Jordan Crouse63f181f2008-04-25 23:09:39 +0000290void keyboard_init(void)
291{
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000292 map = &keyboard_layouts[0];
Jordan Crouse63f181f2008-04-25 23:09:39 +0000293
Jordan Crouseec6363d2008-10-20 17:07:26 +0000294 /* If 0x64 returns 0xff, then we have no keyboard
295 * controller */
Jordan Crouseec6363d2008-10-20 17:07:26 +0000296 if (inb(0x64) == 0xFF)
297 return;
298
Patrick Georgie6408a32008-09-17 18:12:46 +0000299 /* Empty keyboard buffer */
300 while (keyboard_havechar()) keyboard_getchar();
301
Patrick Georgi657a6dc2008-10-21 15:08:18 +0000302 console_add_input_driver(&cons);
Jordan Crouse63f181f2008-04-25 23:09:39 +0000303}
304
Martin Roth3ee59f72013-07-26 16:31:21 -0600305void keyboard_disconnect(void)
306{
307 /* If 0x64 returns 0xff, then we have no keyboard
308 * controller */
309 if (inb(0x64) == 0xFF)
310 return;
311
312 /* Empty keyboard buffer */
313 while (keyboard_havechar())
314 keyboard_getchar();
315
316 /* Send keyboard disconnect command */
317 outb(I8042_CMD_DIS_KB, 0x64);
318 keyboard_wait_write();
319
320 /* Hand off with empty buffer */
321 while (keyboard_havechar())
322 keyboard_getchar();
323}