blob: 23c87803a82778b112a365485c05c697f2b68fdc [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
Shelley Chen674e9712017-01-13 09:40:39 -080035#define POWER_BUTTON 0x90
Martin Roth3ee59f72013-07-26 16:31:21 -060036
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000037struct layout_maps {
Patrick Georgi7f965832011-04-21 18:57:16 +020038 const char *country;
39 const unsigned short map[4][0x57];
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000040};
41
Stefan Reinauer1beabe12010-03-25 18:52:24 +000042static struct layout_maps *map;
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000043
Stefan Reinauer1beabe12010-03-25 18:52:24 +000044static struct layout_maps keyboard_layouts[] = {
Stefan Reinauer1b4d3942015-06-29 15:47:34 -070045#if IS_ENABLED(CONFIG_LP_PC_KEYBOARD_LAYOUT_US)
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000046{ .country = "us", .map = {
Patrick Georgie6408a32008-09-17 18:12:46 +000047 { /* No modifier */
Uwe Hermann6a441bf2008-03-20 19:54:59 +000048 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
49 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
50 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
51 0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
52 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
53 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
54 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000055 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
56 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 +000057 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
58 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
Uwe Hermann6a441bf2008-03-20 19:54:59 +000059 },
Patrick Georgie6408a32008-09-17 18:12:46 +000060 { /* Shift */
Uwe Hermann6a441bf2008-03-20 19:54:59 +000061 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
62 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
63 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
64 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
65 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
66 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
67 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000068 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
69 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 +000070 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
71 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
72 },
73 { /* ALT */
74 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
75 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
76 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
77 0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
78 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
79 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
80 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000081 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
82 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 +000083 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
84 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
85 },
86 { /* Shift-ALT */
87 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
88 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
89 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
90 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
91 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
92 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
93 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
Stefan Reinauer4614aed2008-09-18 07:48:59 +000094 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
95 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 +000096 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
97 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
Uwe Hermann6a441bf2008-03-20 19:54:59 +000098 }
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +000099}},
100#endif
Stefan Reinauer1b4d3942015-06-29 15:47:34 -0700101#if IS_ENABLED(CONFIG_LP_PC_KEYBOARD_LAYOUT_DE)
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000102{ .country = "de", .map = {
103 { /* No modifier */
104 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
105 0x37, 0x38, 0x39, 0x30, 0x00, 0x27, 0x08, 0x09,
106 0x71, 0x77, 0x65, 0x72, 0x74, 0x7A, 0x75, 0x69,
107 0x6F, 0x70, 0x00, 0x2B, 0x0A, 0x00, 0x61, 0x73,
108 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x00,
109 0x00, 0x5E, 0x00, 0x23, 0x79, 0x78, 0x63, 0x76,
110 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2D, 0x00, 0x2A,
111 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
112 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
113 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
114 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3C
115 },
116 { /* Shift */
117 0x00, 0x1B, 0x21, 0x22, 0xA7, 0x24, 0x25, 0x26,
118 0x2F, 0x28, 0x29, 0x3D, 0x3F, 0x60, 0x08, 0x00,
119 0x51, 0x57, 0x45, 0x52, 0x54, 0x5A, 0x55, 0x49,
120 0x4F, 0x50, 0x00, 0x2A, 0x0A, 0x00, 0x41, 0x53,
121 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x00,
122 0x00, 0x7E, 0x00, 0x27, 0x59, 0x58, 0x43, 0x56,
123 0x42, 0x4E, 0x4D, 0x3B, 0x3A, 0x5F, 0x00, 0x2A,
124 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
125 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
126 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
127 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3E
128 },
129 { /* ALT */
130 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
131 0x7B, 0x5B, 0x5D, 0x7D, 0x5C, 0x3D, 0x08, 0x09,
132 0x40, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
133 0x6F, 0x70, 0x5B, 0x7E, 0x0A, 0x00, 0x61, 0x73,
134 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
135 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
136 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
137 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
138 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
139 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
140 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x7C
141 },
142 { /* Shift-ALT */
143 /* copied from US */
144 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
145 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
146 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
147 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
148 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
149 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
150 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
151 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
152 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
153 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
154 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00
155 }
156}},
157#endif
Jordan Crousef6145c32008-03-19 23:56:58 +0000158};
159
Patrick Georgie6408a32008-09-17 18:12:46 +0000160#define MOD_SHIFT (1 << 0)
161#define MOD_CTRL (1 << 1)
162#define MOD_CAPSLOCK (1 << 2)
163#define MOD_ALT (1 << 3)
Jordan Crousef6145c32008-03-19 23:56:58 +0000164
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000165static void keyboard_cmd(unsigned char cmd, unsigned char val)
166{
Patrick Georgi583abc22011-11-10 15:48:37 +0100167 while (inb(0x64) & 2);
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000168 outb(cmd, 0x60);
Patrick Georgi583abc22011-11-10 15:48:37 +0100169 mdelay(20);
170
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000171 while (inb(0x64) & 2);
172 outb(val, 0x60);
Patrick Georgi583abc22011-11-10 15:48:37 +0100173 mdelay(20);
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000174}
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
Shelley Chen674e9712017-01-13 09:40:39 -0800259 if (ch == 0x5e)
260 ret = POWER_BUTTON;
261
Jordan Crousef6145c32008-03-19 23:56:58 +0000262 return ret;
263}
Jordan Crouse63f181f2008-04-25 23:09:39 +0000264
Patrick Georgifd5b3702014-01-10 20:40:59 +0100265static int keyboard_wait_write(void)
266{
267 int retries = 10000;
268
269 while(retries-- && (inb(0x64) & 0x02))
270 udelay(50);
271
272 return (retries <= 0) ? -1 : 0;
273}
274
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000275/**
276 * Set keyboard layout
Stefan Reinauer14e22772010-04-27 06:56:47 +0000277 * @param country string describing the keyboard layout language.
Patrick Georgi4727c072008-10-16 19:20:51 +0000278 * Valid values are "us", "de".
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000279 */
280
281int keyboard_set_layout(char *country)
282{
283 int i;
284
285 for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
286 if (strncmp(keyboard_layouts[i].country, country,
287 strlen(keyboard_layouts[i].country)))
288 continue;
289
290 /* Found, changing keyboard layout */
291 map = &keyboard_layouts[i];
292 return 0;
293 }
294
295 /* Nothing found, not changed */
296 return -1;
297}
298
Patrick Georgi657a6dc2008-10-21 15:08:18 +0000299static struct console_input_driver cons = {
300 .havekey = keyboard_havechar,
301 .getchar = keyboard_getchar
302};
303
Jordan Crouse63f181f2008-04-25 23:09:39 +0000304void keyboard_init(void)
305{
Stefan Reinauerd84ef1e2008-09-26 18:37:26 +0000306 map = &keyboard_layouts[0];
Jordan Crouse63f181f2008-04-25 23:09:39 +0000307
Jordan Crouseec6363d2008-10-20 17:07:26 +0000308 /* If 0x64 returns 0xff, then we have no keyboard
309 * controller */
Jordan Crouseec6363d2008-10-20 17:07:26 +0000310 if (inb(0x64) == 0xFF)
311 return;
312
Patrick Georgie6408a32008-09-17 18:12:46 +0000313 /* Empty keyboard buffer */
314 while (keyboard_havechar()) keyboard_getchar();
315
Patrick Georgi657a6dc2008-10-21 15:08:18 +0000316 console_add_input_driver(&cons);
Jordan Crouse63f181f2008-04-25 23:09:39 +0000317}
318
Martin Roth3ee59f72013-07-26 16:31:21 -0600319void keyboard_disconnect(void)
320{
321 /* If 0x64 returns 0xff, then we have no keyboard
322 * controller */
323 if (inb(0x64) == 0xFF)
324 return;
325
326 /* Empty keyboard buffer */
327 while (keyboard_havechar())
328 keyboard_getchar();
329
330 /* Send keyboard disconnect command */
331 outb(I8042_CMD_DIS_KB, 0x64);
332 keyboard_wait_write();
333
334 /* Hand off with empty buffer */
335 while (keyboard_havechar())
336 keyboard_getchar();
337}