blob: 6431336863f6f9a30f55c1d7564c213a2f14b8e0 [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 Reinauer1b4d3942015-06-29 15:47:34 -070044#if IS_ENABLED(CONFIG_LP_PC_KEYBOARD_LAYOUT_US)
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000045{ .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
Stefan Reinauer1b4d3942015-06-29 15:47:34 -0700100#if IS_ENABLED(CONFIG_LP_PC_KEYBOARD_LAYOUT_DE)
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000101{ .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
Patrick Georgifd5b3702014-01-10 20:40:59 +0100261static int keyboard_wait_write(void)
262{
263 int retries = 10000;
264
265 while(retries-- && (inb(0x64) & 0x02))
266 udelay(50);
267
268 return (retries <= 0) ? -1 : 0;
269}
270
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000271/**
272 * Set keyboard layout
Stefan Reinauer14e22772010-04-27 06:56:47 +0000273 * @param country string describing the keyboard layout language.
Patrick Georgi4727c072008-10-16 19:20:51 +0000274 * Valid values are "us", "de".
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000275 */
276
277int keyboard_set_layout(char *country)
278{
279 int i;
280
281 for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
282 if (strncmp(keyboard_layouts[i].country, country,
283 strlen(keyboard_layouts[i].country)))
284 continue;
285
286 /* Found, changing keyboard layout */
287 map = &keyboard_layouts[i];
288 return 0;
289 }
290
291 /* Nothing found, not changed */
292 return -1;
293}
294
Patrick Georgi657a6dc2008-10-21 15:08:18 +0000295static struct console_input_driver cons = {
296 .havekey = keyboard_havechar,
297 .getchar = keyboard_getchar
298};
299
Jordan Crouse63f181f2008-04-25 23:09:39 +0000300void keyboard_init(void)
301{
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000302 map = &keyboard_layouts[0];
Jordan Crouse63f181f2008-04-25 23:09:39 +0000303
Jordan Crouseec6363d2008-10-20 17:07:26 +0000304 /* If 0x64 returns 0xff, then we have no keyboard
305 * controller */
Jordan Crouseec6363d2008-10-20 17:07:26 +0000306 if (inb(0x64) == 0xFF)
307 return;
308
Patrick Georgie6408a32008-09-17 18:12:46 +0000309 /* Empty keyboard buffer */
310 while (keyboard_havechar()) keyboard_getchar();
311
Patrick Georgi657a6dc2008-10-21 15:08:18 +0000312 console_add_input_driver(&cons);
Jordan Crouse63f181f2008-04-25 23:09:39 +0000313}
314
Martin Roth3ee59f72013-07-26 16:31:21 -0600315void keyboard_disconnect(void)
316{
317 /* If 0x64 returns 0xff, then we have no keyboard
318 * controller */
319 if (inb(0x64) == 0xFF)
320 return;
321
322 /* Empty keyboard buffer */
323 while (keyboard_havechar())
324 keyboard_getchar();
325
326 /* Send keyboard disconnect command */
327 outb(I8042_CMD_DIS_KB, 0x64);
328 keyboard_wait_write();
329
330 /* Hand off with empty buffer */
331 while (keyboard_havechar())
332 keyboard_getchar();
333}