2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/at_keyboard.h>
21 #include <grub/cpu/at_keyboard.h>
22 #include <grub/cpu/io.h>
23 #include <grub/misc.h>
24 #include <grub/term.h>
25 #include <grub/time.h>
26 #include <grub/loader.h>
29 GRUB_MOD_LICENSE ("GPLv3+");
31 static grub_uint8_t grub_keyboard_controller_orig;
32 static grub_uint8_t grub_keyboard_orig_set;
33 struct grub_ps2_state ps2_state;
38 grub_keyboard_controller_init (void);
41 keyboard_controller_wait_until_ready (void)
43 /* 50 us would be enough but our current time resolution is 1ms. */
45 while (! KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)));
51 grub_uint64_t endtime;
54 endtime = grub_get_time_ms () + 20;
56 keyboard_controller_wait_until_ready ();
57 ack = grub_inb (KEYBOARD_REG_DATA);
58 } while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK
59 && grub_get_time_ms () < endtime);
64 at_command (grub_uint8_t data)
67 for (i = 0; i < GRUB_AT_TRIES; i++)
70 keyboard_controller_wait_until_ready ();
71 grub_outb (data, KEYBOARD_REG_STATUS);
73 if (ack == GRUB_AT_NACK)
75 if (ack == GRUB_AT_ACK)
79 return (i != GRUB_AT_TRIES);
83 grub_keyboard_controller_write (grub_uint8_t c)
85 at_command (KEYBOARD_COMMAND_WRITE);
86 keyboard_controller_wait_until_ready ();
87 grub_outb (c, KEYBOARD_REG_DATA);
90 #if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS)
91 #define USE_SCANCODE_SET 1
93 #define USE_SCANCODE_SET 0
99 grub_keyboard_controller_read (void)
101 at_command (KEYBOARD_COMMAND_READ);
102 keyboard_controller_wait_until_ready ();
103 return grub_inb (KEYBOARD_REG_DATA);
109 write_mode (int mode)
112 for (i = 0; i < GRUB_AT_TRIES; i++)
115 keyboard_controller_wait_until_ready ();
116 grub_outb (0xf0, KEYBOARD_REG_DATA);
117 keyboard_controller_wait_until_ready ();
118 grub_outb (mode, KEYBOARD_REG_DATA);
119 keyboard_controller_wait_until_ready ();
121 if (ack == GRUB_AT_NACK)
123 if (ack == GRUB_AT_ACK)
128 return (i != GRUB_AT_TRIES);
142 keyboard_controller_wait_until_ready ();
143 ret = grub_inb (KEYBOARD_REG_DATA);
144 } while (ret == GRUB_AT_ACK);
145 /* QEMU translates the set even in no-translate mode. */
146 if (ret == 0x43 || ret == 1)
148 if (ret == 0x41 || ret == 2)
150 if (ret == 0x3f || ret == 3)
158 /* You must have visited computer museum. Keyboard without scancode set
159 knowledge. Assume XT. */
160 if (!grub_keyboard_orig_set)
162 grub_dprintf ("atkeyb", "No sets support assumed\n");
163 ps2_state.current_set = 1;
167 #if !USE_SCANCODE_SET
168 ps2_state.current_set = 1;
172 grub_keyboard_controller_write (grub_keyboard_controller_orig
173 & ~KEYBOARD_AT_TRANSLATE
174 & ~KEYBOARD_AT_DISABLE);
176 keyboard_controller_wait_until_ready ();
177 grub_outb (KEYBOARD_COMMAND_ENABLE, KEYBOARD_REG_DATA);
180 ps2_state.current_set = query_mode ();
181 grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
182 if (ps2_state.current_set == 2)
186 ps2_state.current_set = query_mode ();
187 grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
188 if (ps2_state.current_set == 1)
190 grub_dprintf ("atkeyb", "no supported scancode set found\n");
195 keyboard_controller_led (grub_uint8_t leds)
197 keyboard_controller_wait_until_ready ();
198 grub_outb (0xed, KEYBOARD_REG_DATA);
199 keyboard_controller_wait_until_ready ();
200 grub_outb (leds & 0x7, KEYBOARD_REG_DATA);
204 grub_at_keyboard_is_alive (void)
206 if (ps2_state.current_set != 0)
209 && KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS))
210 && grub_inb (KEYBOARD_REG_DATA) == 0x55)
212 grub_keyboard_controller_init ();
216 if (KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
218 grub_outb (0xaa, KEYBOARD_REG_STATUS);
224 /* If there is a character pending, return it;
225 otherwise return GRUB_TERM_NO_KEY. */
227 grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
231 grub_uint8_t old_led;
233 if (!grub_at_keyboard_is_alive ())
234 return GRUB_TERM_NO_KEY;
236 if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
238 at_key = grub_inb (KEYBOARD_REG_DATA);
239 old_led = ps2_state.led_status;
241 ret = grub_ps2_process_incoming_byte (&ps2_state, at_key);
242 if (old_led != ps2_state.led_status)
243 keyboard_controller_led (ps2_state.led_status);
248 grub_keyboard_controller_init (void)
250 ps2_state.at_keyboard_status = 0;
251 /* Drain input buffer. */
254 keyboard_controller_wait_until_ready ();
255 if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
257 keyboard_controller_wait_until_ready ();
258 grub_inb (KEYBOARD_REG_DATA);
260 #if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS)
261 grub_keyboard_controller_orig = 0;
262 grub_keyboard_orig_set = 2;
263 #elif defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_COREBOOT)
264 /* *BSD relies on those settings. */
265 grub_keyboard_controller_orig = KEYBOARD_AT_TRANSLATE;
266 grub_keyboard_orig_set = 2;
268 grub_keyboard_controller_orig = grub_keyboard_controller_read ();
269 grub_keyboard_orig_set = query_mode ();
272 keyboard_controller_led (ps2_state.led_status);
276 grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused)))
278 if (ps2_state.current_set == 0)
279 return GRUB_ERR_NONE;
280 if (grub_keyboard_orig_set)
281 write_mode (grub_keyboard_orig_set);
282 grub_keyboard_controller_write (grub_keyboard_controller_orig);
283 return GRUB_ERR_NONE;
287 grub_at_fini_hw (int noreturn __attribute__ ((unused)))
289 return grub_keyboard_controller_fini (NULL);
293 grub_at_restore_hw (void)
295 if (ps2_state.current_set == 0)
296 return GRUB_ERR_NONE;
298 /* Drain input buffer. */
301 keyboard_controller_wait_until_ready ();
302 if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
304 keyboard_controller_wait_until_ready ();
305 grub_inb (KEYBOARD_REG_DATA);
308 keyboard_controller_led (ps2_state.led_status);
310 return GRUB_ERR_NONE;
314 static struct grub_term_input grub_at_keyboard_term =
316 .name = "at_keyboard",
317 .fini = grub_keyboard_controller_fini,
318 .getkey = grub_at_keyboard_getkey
321 GRUB_MOD_INIT(at_keyboard)
323 grub_term_register_input ("at_keyboard", &grub_at_keyboard_term);
324 grub_loader_register_preboot_hook (grub_at_fini_hw, grub_at_restore_hw,
325 GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
328 GRUB_MOD_FINI(at_keyboard)
330 grub_keyboard_controller_fini (NULL);
331 grub_term_unregister_input (&grub_at_keyboard_term);