1 /*
2 *
3 * Copyright (C) 2008 Advanced Micro Devices, Inc.
4 * Copyright (C) 2017 Patrick Rudolph <[email protected]>
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
30 #include <stdbool.h>
31 #include <stdint.h>
32
33 #include <keycodes.h>
34 #include <libpayload-config.h>
35 #include <libpayload.h>
36
37 #include "i8042.h"
38
39 #ifdef DEBUG
40 #define debug(x...) printf(x)
41 #else
42 #define debug(x...) do {} while (0)
43 #endif
44
45 #define POWER_BUTTON 0x90
46 #define MEDIA_KEY_PREFIX 0xE0
47
48 struct layout_maps {
49 const char *country;
50 const unsigned short map[4][0x59];
51 };
52
53 static struct layout_maps *map;
54 static int modifier = 0;
55 int (*media_key_mapping_callback)(char ch);
56
57 static struct layout_maps keyboard_layouts[] = {
58 #if CONFIG(LP_PC_KEYBOARD_LAYOUT_US)
59 { .country = "us", .map = {
60 { /* No modifier */
61 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
62 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
63 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
64 0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
65 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
66 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
67 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
68 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,
70 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
71 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00, KEY_F(11),
72 KEY_F(12),
73 },
74 { /* Shift */
75 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
76 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
77 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
78 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
79 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
80 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
81 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
82 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
83 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
84 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
85 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00, KEY_F(11),
86 KEY_F(12),
87 },
88 { /* ALT */
89 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
90 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,
91 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
92 0x6F, 0x70, 0x5B, 0x5D, 0x0A, 0x00, 0x61, 0x73,
93 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
94 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
95 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
96 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,
98 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
99 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00, KEY_F(11),
100 KEY_F(12),
101 },
102 { /* Shift-ALT */
103 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
104 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
105 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
106 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
107 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
108 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
109 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 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, 0x00, KEY_F(11),
114 KEY_F(12),
115 }
116 }},
117 #endif
118 #if CONFIG(LP_PC_KEYBOARD_LAYOUT_DE)
119 { .country = "de", .map = {
120 { /* No modifier */
121 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
122 0x37, 0x38, 0x39, 0x30, 0x00, 0x27, 0x08, 0x09,
123 0x71, 0x77, 0x65, 0x72, 0x74, 0x7A, 0x75, 0x69,
124 0x6F, 0x70, 0x00, 0x2B, 0x0A, 0x00, 0x61, 0x73,
125 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x00,
126 0x00, 0x5E, 0x00, 0x23, 0x79, 0x78, 0x63, 0x76,
127 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2D, 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, 0x3C, KEY_F(11),
132 KEY_F(12),
133 },
134 { /* Shift */
135 0x00, 0x1B, 0x21, 0x22, 0xA7, 0x24, 0x25, 0x26,
136 0x2F, 0x28, 0x29, 0x3D, 0x3F, 0x60, 0x08, 0x00,
137 0x51, 0x57, 0x45, 0x52, 0x54, 0x5A, 0x55, 0x49,
138 0x4F, 0x50, 0x00, 0x2A, 0x0A, 0x00, 0x41, 0x53,
139 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x00,
140 0x00, 0x7E, 0x00, 0x27, 0x59, 0x58, 0x43, 0x56,
141 0x42, 0x4E, 0x4D, 0x3B, 0x3A, 0x5F, 0x00, 0x2A,
142 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
143 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
144 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
145 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x3E, KEY_F(11),
146 KEY_F(12),
147 },
148 { /* ALT */
149 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
150 0x7B, 0x5B, 0x5D, 0x7D, 0x5C, 0x3D, 0x08, 0x09,
151 0x40, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69,
152 0x6F, 0x70, 0x5B, 0x7E, 0x0A, 0x00, 0x61, 0x73,
153 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B,
154 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
155 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A,
156 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
157 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
158 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
159 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x7C, KEY_F(11),
160 KEY_F(12),
161 },
162 { /* Shift-ALT */
163 /* copied from US */
164 0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E,
165 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x00,
166 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
167 0x4F, 0x50, 0x7B, 0x7D, 0x0A, 0x00, 0x41, 0x53,
168 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A,
169 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
170 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A,
171 0x00, 0x20, 0x00, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5),
172 KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), 0x00, 0x00, KEY_HOME,
173 KEY_UP, KEY_NPAGE, 0x00, KEY_LEFT, 0x00, KEY_RIGHT, 0x00, KEY_END,
174 KEY_DOWN, KEY_PPAGE, 0x00, KEY_DC, 0x00, 0x00, 0x00, KEY_F(11),
175 KEY_F(12),
176 }
177 }},
178 #endif
179 };
180
keyboard_drain_input(void)181 static void keyboard_drain_input(void)
182 {
183 while (i8042_data_ready_ps2())
184 (void)i8042_read_data_ps2();
185 }
186
keyboard_cmd(unsigned char cmd)187 static bool keyboard_cmd(unsigned char cmd)
188 {
189 const uint64_t timeout_us = cmd == I8042_KBCMD_RESET ? 1*1000*1000 : 200*1000;
190 const uint64_t start_time = timer_us(0);
191
192 i8042_write_data(cmd);
193
194 do {
195 if (!i8042_data_ready_ps2()) {
196 udelay(50);
197 continue;
198 }
199
200 const uint8_t data = i8042_read_data_ps2();
201 switch (data) {
202 case 0xfa:
203 return true;
204 case 0xfe:
205 return false;
206 default:
207 /* Warn only if we already disabled keyboard input. */
208 if (cmd != I8042_KBCMD_DEFAULT_DIS)
209 debug("WARNING: Keyboard sent spurious 0x%02x.\n", data);
210 break;
211 }
212 } while (timer_us(start_time) < timeout_us);
213
214 debug("ERROR: Keyboard command timed out.\n");
215 return false;
216 }
217
set_scancode_set(const unsigned char set)218 static bool set_scancode_set(const unsigned char set)
219 {
220 bool ret;
221
222 if (set < 1 || set > 3)
223 return false;
224
225 ret = keyboard_cmd(I8042_KBCMD_SET_SCANCODE);
226 if (!ret) {
227 debug("ERROR: Keyboard set scancode failed!\n");
228 return ret;
229 }
230
231 ret = keyboard_cmd(set);
232 if (!ret) {
233 debug("ERROR: Keyboard scancode set#%u failed!\n", set);
234 return ret;
235 }
236
237 return ret;
238 }
239
keyboard_peek_echo_result(void)240 static bool keyboard_peek_echo_result(void)
241 {
242 const uint8_t ch = i8042_peek_data_ps2();
243 return ch == 0xee || ch == 0xfe;
244 }
245
246 static enum keyboard_state {
247 STATE_INIT = 0,
248 STATE_SIMPLIFIED_INIT,
249 STATE_HOTPLUG,
250 STATE_HOTPLUG_ECHO,
251 STATE_DISABLE_SCAN,
252 STATE_DRAIN_INPUT,
253 STATE_DISABLE_TRANSLATION,
254 STATE_START_SELF_TEST,
255 STATE_SELF_TEST,
256 STATE_CONFIGURE,
257 STATE_CONFIGURE_SET1,
258 STATE_ENABLE_TRANSLATION,
259 STATE_ENABLE_SCAN,
260 STATE_RUNNING,
261 STATE_RUNNING_ECHO,
262 STATE_DETENTION,
263 } keyboard_state;
264
265 #define STATE_NAMES_ENTRY(name) [STATE_##name] = #name
266 static const char *const state_names[] = {
267 STATE_NAMES_ENTRY(INIT),
268 STATE_NAMES_ENTRY(SIMPLIFIED_INIT),
269 STATE_NAMES_ENTRY(HOTPLUG),
270 STATE_NAMES_ENTRY(HOTPLUG_ECHO),
271 STATE_NAMES_ENTRY(DISABLE_SCAN),
272 STATE_NAMES_ENTRY(DRAIN_INPUT),
273 STATE_NAMES_ENTRY(DISABLE_TRANSLATION),
274 STATE_NAMES_ENTRY(START_SELF_TEST),
275 STATE_NAMES_ENTRY(SELF_TEST),
276 STATE_NAMES_ENTRY(CONFIGURE),
277 STATE_NAMES_ENTRY(CONFIGURE_SET1),
278 STATE_NAMES_ENTRY(ENABLE_TRANSLATION),
279 STATE_NAMES_ENTRY(ENABLE_SCAN),
280 STATE_NAMES_ENTRY(RUNNING),
281 STATE_NAMES_ENTRY(RUNNING_ECHO),
282 STATE_NAMES_ENTRY(DETENTION),
283 };
284
285 __attribute__((unused))
state_name(enum keyboard_state state)286 static const char *state_name(enum keyboard_state state)
287 {
288 if (state >= ARRAY_SIZE(state_names) || !state_names[state])
289 return "<unknown>";
290 return state_names[state];
291 }
292
293 static uint64_t keyboard_time;
294 static uint64_t state_time;
295
keyboard_poll(void)296 static void keyboard_poll(void)
297 {
298 enum keyboard_state next_state = keyboard_state;
299 unsigned int i;
300
301 switch (keyboard_state) {
302
303 case STATE_INIT:
304 /* Wait until keyboard_init() has been called. */
305 break;
306
307 case STATE_SIMPLIFIED_INIT:
308 /* On the first try, start opportunistically, do
309 the first steps at once and skip the self-test. */
310 (void)keyboard_cmd(I8042_KBCMD_DEFAULT_DIS);
311 keyboard_drain_input();
312 (void)i8042_set_kbd_translation(false);
313 next_state = STATE_CONFIGURE;
314 break;
315
316 case STATE_HOTPLUG:
317 if (timer_us(state_time) > 1*1000*1000) {
318 i8042_write_data(I8042_KBCMD_ECHO);
319 next_state = STATE_HOTPLUG_ECHO;
320 }
321 break;
322
323 case STATE_HOTPLUG_ECHO:
324 if (!i8042_data_ready_ps2()) {
325 if (timer_us(state_time) > 200*1000)
326 next_state = STATE_HOTPLUG;
327 break;
328 }
329
330 if (keyboard_peek_echo_result()) {
331 next_state = STATE_DISABLE_SCAN;
332 keyboard_time = timer_us(0);
333 }
334 (void)i8042_read_data_ps2();
335
336 break;
337
338 case STATE_DISABLE_SCAN:
339 (void)keyboard_cmd(I8042_KBCMD_DEFAULT_DIS);
340 next_state = STATE_DRAIN_INPUT;
341 break;
342
343 case STATE_DRAIN_INPUT:
344 /* Limit number of bytes drained per poll. */
345 for (i = 0; i < 50 && i8042_data_ready_ps2(); ++i)
346 (void)i8042_read_data_ps2();
347 if (i == 0)
348 next_state = STATE_DISABLE_TRANSLATION;
349 break;
350
351 case STATE_DISABLE_TRANSLATION:
352 /* Be opportunistic and assume it's disabled on failure. */
353 (void)i8042_set_kbd_translation(false);
354 next_state = STATE_START_SELF_TEST;
355 break;
356
357 case STATE_START_SELF_TEST:
358 if (!keyboard_cmd(I8042_KBCMD_RESET))
359 debug("ERROR: Keyboard self-test couldn't be started.\n");
360 /* We ignore errors and always move to the self-test state
361 which will simply try again if necessary. */
362 next_state = STATE_SELF_TEST;
363 break;
364
365 case STATE_SELF_TEST:
366 if (!i8042_data_ready_ps2()) {
367 if (timer_us(state_time) > 5*1000*1000) {
368 debug("WARNING: Keyboard self-test timed out.\n");
369 next_state = STATE_DISABLE_SCAN;
370 }
371 break;
372 }
373
374 const uint8_t self_test_result = i8042_read_data_ps2();
375 switch (self_test_result) {
376 case 0xaa:
377 debug("INFO: Keyboard self-test succeeded.\n");
378 next_state = STATE_CONFIGURE;
379 break;
380 case 0xfc:
381 case 0xfd:
382 /* Failure. Try again. */
383 debug("WARNING: Keyboard self-test failed.\n");
384 next_state = STATE_START_SELF_TEST;
385 break;
386 default:
387 debug("WARNING: Keyboard self-test received spurious 0x%02x\n",
388 self_test_result);
389 break;
390 }
391 break;
392
393 case STATE_CONFIGURE:
394 if (set_scancode_set(2))
395 next_state = STATE_ENABLE_TRANSLATION;
396 else
397 next_state = STATE_CONFIGURE_SET1;
398 break;
399
400 case STATE_CONFIGURE_SET1:
401 if (!set_scancode_set(1)) {
402 debug("ERROR: Keyboard failed to set any scancode set.\n");
403 next_state = STATE_DISABLE_SCAN;
404 break;
405 }
406
407 next_state = STATE_ENABLE_SCAN;
408 break;
409
410 case STATE_ENABLE_TRANSLATION:
411 if (i8042_set_kbd_translation(true) != 0) {
412 debug("ERROR: Keyboard controller set translation failed!\n");
413 next_state = STATE_DISABLE_SCAN;
414 break;
415 }
416
417 next_state = STATE_ENABLE_SCAN;
418 break;
419
420 case STATE_ENABLE_SCAN:
421 if (!keyboard_cmd(I8042_KBCMD_EN)) {
422 debug("ERROR: Keyboard enable scanning failed!\n");
423 next_state = STATE_DISABLE_SCAN;
424 break;
425 }
426
427 next_state = STATE_RUNNING;
428 break;
429
430 case STATE_RUNNING:
431 if (!i8042_data_ready_ps2()) {
432 if (timer_us(state_time) > 500*1000) {
433 i8042_write_data(I8042_KBCMD_ECHO);
434 next_state = STATE_RUNNING_ECHO;
435 }
436 } else {
437 state_time = timer_us(0);
438 }
439 break;
440
441 case STATE_RUNNING_ECHO:
442 if (!i8042_data_ready_ps2()) {
443 if (timer_us(state_time) > 200*1000) {
444 debug("INFO: Keyboard echo timed out.\n");
445 next_state = STATE_HOTPLUG;
446 }
447 break;
448 }
449
450 if (keyboard_peek_echo_result()) {
451 (void)i8042_read_data_ps2();
452 next_state = STATE_RUNNING;
453 }
454
455 state_time = timer_us(0);
456 break;
457
458 case STATE_DETENTION:
459 if (timer_us(state_time) > 5*1000*1000)
460 next_state = STATE_HOTPLUG;
461 break;
462
463 }
464
465 switch (next_state) {
466 case STATE_INIT:
467 case STATE_HOTPLUG:
468 case STATE_HOTPLUG_ECHO:
469 case STATE_RUNNING:
470 case STATE_RUNNING_ECHO:
471 case STATE_DETENTION:
472 break;
473 default:
474 if (timer_us(keyboard_time) > 30*1000*1000)
475 next_state = STATE_DETENTION;
476 break;
477 }
478
479 if (keyboard_state != next_state) {
480 debug("INFO: Keyboard advancing state to '%s'.\n", state_name(next_state));
481 keyboard_state = next_state;
482 state_time = timer_us(0);
483 }
484 }
485
keyboard_havechar(void)486 bool keyboard_havechar(void)
487 {
488 keyboard_poll();
489 return i8042_data_ready_ps2() &&
490 (keyboard_state == STATE_RUNNING ||
491 (keyboard_state == STATE_RUNNING_ECHO && !keyboard_peek_echo_result()));
492 }
493
keyboard_get_scancode(void)494 unsigned char keyboard_get_scancode(void)
495 {
496 unsigned char ch;
497
498 while (!keyboard_havechar()) ;
499
500 ch = i8042_read_data_ps2();
501
502 switch (ch) {
503 case 0x36:
504 case 0x2a:
505 modifier |= KB_MOD_SHIFT;
506 break;
507 case 0x80 | 0x36:
508 case 0x80 | 0x2a:
509 modifier &= ~KB_MOD_SHIFT;
510 break;
511 case 0x38:
512 modifier |= KB_MOD_ALT;
513 break;
514 case 0x80 | 0x38:
515 modifier &= ~KB_MOD_ALT;
516 break;
517 case 0x1d:
518 modifier |= KB_MOD_CTRL;
519 break;
520 case 0x80 | 0x1d:
521 modifier &= ~KB_MOD_CTRL;
522 break;
523 case 0x3a:
524 if (modifier & KB_MOD_CAPSLOCK) {
525 modifier &= ~KB_MOD_CAPSLOCK;
526 if (keyboard_cmd(I8042_KBCMD_SET_MODE_IND))
527 keyboard_cmd(I8042_MODE_CAPS_LOCK_OFF);
528 } else {
529 modifier |= KB_MOD_CAPSLOCK;
530 if (keyboard_cmd(I8042_KBCMD_SET_MODE_IND))
531 keyboard_cmd(I8042_MODE_CAPS_LOCK_ON);
532 }
533 break;
534 }
535
536 return ch;
537 }
538
keyboard_getmodifier(void)539 int keyboard_getmodifier(void)
540 {
541 return modifier;
542 }
543
initialize_keyboard_media_key_mapping_callback(int (* media_key_mapper)(char))544 void initialize_keyboard_media_key_mapping_callback(int (*media_key_mapper)(char))
545 {
546 media_key_mapping_callback = media_key_mapper;
547 }
548
keyboard_getchar(void)549 int keyboard_getchar(void)
550 {
551 unsigned char ch;
552 int shift;
553 int ret = 0;
554
555 while (!keyboard_havechar()) ;
556
557 ch = keyboard_get_scancode();
558 if ((media_key_mapping_callback != NULL) && (ch == MEDIA_KEY_PREFIX)) {
559 ch = keyboard_get_scancode();
560 return media_key_mapping_callback(ch);
561 }
562
563 if (!(ch & 0x80) && ch < 0x59) {
564 shift =
565 (modifier & KB_MOD_SHIFT) ^ (modifier & KB_MOD_CAPSLOCK) ? 1 : 0;
566
567 if (modifier & KB_MOD_ALT)
568 shift += 2;
569
570 ret = map->map[shift][ch];
571
572 if (modifier & KB_MOD_CTRL) {
573 switch (ret) {
574 case 'a' ... 'z':
575 ret &= 0x1f;
576 break;
577 case KEY_DC:
578 /* vulcan nerve pinch */
579 if ((modifier & KB_MOD_ALT) && reset_handler)
580 reset_handler();
581 __fallthrough;
582 default:
583 ret = 0;
584 }
585 }
586 }
587
588 if (ch == 0x5e)
589 ret = POWER_BUTTON;
590
591 return ret;
592 }
593
594 /**
595 * Set keyboard layout
596 * @param country string describing the keyboard layout language.
597 * Valid values are "us", "de".
598 */
599
keyboard_set_layout(char * country)600 int keyboard_set_layout(char *country)
601 {
602 int i;
603
604 for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
605 if (strncmp(keyboard_layouts[i].country, country,
606 strlen(keyboard_layouts[i].country)))
607 continue;
608
609 /* Found, changing keyboard layout */
610 map = &keyboard_layouts[i];
611 return 0;
612 }
613
614 /* Nothing found, not changed */
615 return -1;
616 }
617
618 static struct console_input_driver cons = {
619 .havekey = (int (*)(void))keyboard_havechar,
620 .getchar = keyboard_getchar,
621 .input_type = CONSOLE_INPUT_TYPE_EC,
622 };
623
keyboard_init(void)624 void keyboard_init(void)
625 {
626 if (keyboard_state != STATE_INIT)
627 return;
628
629 map = &keyboard_layouts[0];
630
631 /* Initialized keyboard controller. */
632 if (!i8042_probe() || !i8042_has_ps2())
633 return;
634
635 /* Enable first PS/2 port */
636 i8042_cmd(I8042_CMD_EN_KB);
637
638 keyboard_state = STATE_SIMPLIFIED_INIT;
639 keyboard_time = state_time = timer_us(0);
640
641 console_add_input_driver(&cons);
642 }
643
keyboard_disconnect(void)644 void keyboard_disconnect(void)
645 {
646 /* If 0x64 returns 0xff, then we have no keyboard
647 * controller */
648 if (inb(0x64) == 0xFF)
649 return;
650
651 if (!i8042_has_ps2())
652 return;
653
654 /* Disable scanning */
655 keyboard_cmd(I8042_KBCMD_DEFAULT_DIS);
656 keyboard_drain_input();
657
658 /* Nobody but us seems to still use scancode set #1.
659 So try to hand over with more modern settings. */
660 set_scancode_set(2);
661 i8042_set_kbd_translation(CONFIG(LP_PC_KEYBOARD_TRANSLATION));
662
663 /* Send keyboard disconnect command */
664 i8042_cmd(I8042_CMD_DIS_KB);
665
666 /* Hand off with empty buffer */
667 keyboard_drain_input();
668
669 /* Release keyboard controller driver */
670 i8042_close();
671
672 keyboard_state = STATE_INIT;
673 }
674