mb/google/nissa: Create pujjogatwin variant
[coreboot2.git] / payloads / libpayload / drivers / i8042 / keyboard.c
blob2453392ecf159b8bce851c4c9ae1f5e9fcbc8db7
1 /*
3 * Copyright (C) 2008 Advanced Micro Devices, Inc.
4 * Copyright (C) 2017 Patrick Rudolph <siro@das-labor.org>
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.
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.
30 #include <stdbool.h>
31 #include <stdint.h>
33 #include <keycodes.h>
34 #include <libpayload-config.h>
35 #include <libpayload.h>
37 #include "i8042.h"
39 #ifdef DEBUG
40 #define debug(x...) printf(x)
41 #else
42 #define debug(x...) do {} while (0)
43 #endif
45 #define POWER_BUTTON 0x90
46 #define MEDIA_KEY_PREFIX 0xE0
48 struct layout_maps {
49 const char *country;
50 const unsigned short map[4][0x59];
53 static struct layout_maps *map;
54 static int modifier = 0;
55 int (*media_key_mapping_callback)(char ch);
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),
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),
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),
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),
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),
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),
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),
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),
178 #endif
181 static void keyboard_drain_input(void)
183 while (i8042_data_ready_ps2())
184 (void)i8042_read_data_ps2();
187 static bool keyboard_cmd(unsigned char cmd)
189 const uint64_t timeout_us = cmd == I8042_KBCMD_RESET ? 1*1000*1000 : 200*1000;
190 const uint64_t start_time = timer_us(0);
192 i8042_write_data(cmd);
194 do {
195 if (!i8042_data_ready_ps2()) {
196 udelay(50);
197 continue;
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;
212 } while (timer_us(start_time) < timeout_us);
214 debug("ERROR: Keyboard command timed out.\n");
215 return false;
218 static bool set_scancode_set(const unsigned char set)
220 bool ret;
222 if (set < 1 || set > 3)
223 return false;
225 ret = keyboard_cmd(I8042_KBCMD_SET_SCANCODE);
226 if (!ret) {
227 debug("ERROR: Keyboard set scancode failed!\n");
228 return ret;
231 ret = keyboard_cmd(set);
232 if (!ret) {
233 debug("ERROR: Keyboard scancode set#%u failed!\n", set);
234 return ret;
237 return ret;
240 static bool keyboard_peek_echo_result(void)
242 const uint8_t ch = i8042_peek_data_ps2();
243 return ch == 0xee || ch == 0xfe;
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;
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),
285 __attribute__((unused))
286 static const char *state_name(enum keyboard_state state)
288 if (state >= ARRAY_SIZE(state_names) || !state_names[state])
289 return "<unknown>";
290 return state_names[state];
293 static uint64_t keyboard_time;
294 static uint64_t state_time;
296 static void keyboard_poll(void)
298 enum keyboard_state next_state = keyboard_state;
299 unsigned int i;
301 switch (keyboard_state) {
303 case STATE_INIT:
304 /* Wait until keyboard_init() has been called. */
305 break;
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;
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;
321 break;
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;
330 if (keyboard_peek_echo_result()) {
331 next_state = STATE_DISABLE_SCAN;
332 keyboard_time = timer_us(0);
334 (void)i8042_read_data_ps2();
336 break;
338 case STATE_DISABLE_SCAN:
339 (void)keyboard_cmd(I8042_KBCMD_DEFAULT_DIS);
340 next_state = STATE_DRAIN_INPUT;
341 break;
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;
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;
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;
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;
371 break;
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;
391 break;
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;
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;
407 next_state = STATE_ENABLE_SCAN;
408 break;
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;
417 next_state = STATE_ENABLE_SCAN;
418 break;
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;
427 next_state = STATE_RUNNING;
428 break;
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;
436 } else {
437 state_time = timer_us(0);
439 break;
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;
447 break;
450 if (keyboard_peek_echo_result()) {
451 (void)i8042_read_data_ps2();
452 next_state = STATE_RUNNING;
455 state_time = timer_us(0);
456 break;
458 case STATE_DETENTION:
459 if (timer_us(state_time) > 5*1000*1000)
460 next_state = STATE_HOTPLUG;
461 break;
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;
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);
486 bool keyboard_havechar(void)
488 keyboard_poll();
489 return i8042_data_ready_ps2() &&
490 (keyboard_state == STATE_RUNNING ||
491 (keyboard_state == STATE_RUNNING_ECHO && !keyboard_peek_echo_result()));
494 unsigned char keyboard_get_scancode(void)
496 unsigned char ch;
498 while (!keyboard_havechar()) ;
500 ch = i8042_read_data_ps2();
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);
533 break;
536 return ch;
539 int keyboard_getmodifier(void)
541 return modifier;
544 void initialize_keyboard_media_key_mapping_callback(int (*media_key_mapper)(char))
546 media_key_mapping_callback = media_key_mapper;
549 int keyboard_getchar(void)
551 unsigned char ch;
552 int shift;
553 int ret = 0;
555 while (!keyboard_havechar()) ;
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);
563 if (!(ch & 0x80) && ch < 0x59) {
564 shift =
565 (modifier & KB_MOD_SHIFT) ^ (modifier & KB_MOD_CAPSLOCK) ? 1 : 0;
567 if (modifier & KB_MOD_ALT)
568 shift += 2;
570 ret = map->map[shift][ch];
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;
588 if (ch == 0x5e)
589 ret = POWER_BUTTON;
591 return ret;
595 * Set keyboard layout
596 * @param country string describing the keyboard layout language.
597 * Valid values are "us", "de".
600 int keyboard_set_layout(char *country)
602 int i;
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;
609 /* Found, changing keyboard layout */
610 map = &keyboard_layouts[i];
611 return 0;
614 /* Nothing found, not changed */
615 return -1;
618 static struct console_input_driver cons = {
619 .havekey = (int (*)(void))keyboard_havechar,
620 .getchar = keyboard_getchar,
621 .input_type = CONSOLE_INPUT_TYPE_EC,
624 void keyboard_init(void)
626 if (keyboard_state != STATE_INIT)
627 return;
629 map = &keyboard_layouts[0];
631 /* Initialized keyboard controller. */
632 if (!i8042_probe() || !i8042_has_ps2())
633 return;
635 /* Enable first PS/2 port */
636 i8042_cmd(I8042_CMD_EN_KB);
638 keyboard_state = STATE_SIMPLIFIED_INIT;
639 keyboard_time = state_time = timer_us(0);
641 console_add_input_driver(&cons);
644 void keyboard_disconnect(void)
646 /* If 0x64 returns 0xff, then we have no keyboard
647 * controller */
648 if (inb(0x64) == 0xFF)
649 return;
651 if (!i8042_has_ps2())
652 return;
654 /* Disable scanning */
655 keyboard_cmd(I8042_KBCMD_DEFAULT_DIS);
656 keyboard_drain_input();
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));
663 /* Send keyboard disconnect command */
664 i8042_cmd(I8042_CMD_DIS_KB);
666 /* Hand off with empty buffer */
667 keyboard_drain_input();
669 /* Release keyboard controller driver */
670 i8042_close();
672 keyboard_state = STATE_INIT;