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 while (! KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)));
49 grub_uint64_t endtime;
52 endtime = grub_get_time_ms () + 20;
54 ack = grub_inb (KEYBOARD_REG_DATA);
55 while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK
56 && grub_get_time_ms () < endtime);
61 at_command (grub_uint8_t data)
64 for (i = 0; i < GRUB_AT_TRIES; i++)
67 keyboard_controller_wait_until_ready ();
68 grub_outb (data, KEYBOARD_REG_STATUS);
70 if (ack == GRUB_AT_NACK)
72 if (ack == GRUB_AT_ACK)
76 return (i != GRUB_AT_TRIES);
80 grub_keyboard_controller_write (grub_uint8_t c)
82 at_command (KEYBOARD_COMMAND_WRITE);
83 keyboard_controller_wait_until_ready ();
84 grub_outb (c, KEYBOARD_REG_DATA);
87 #if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS)
88 #define USE_SCANCODE_SET 1
90 #define USE_SCANCODE_SET 0
96 grub_keyboard_controller_read (void)
98 at_command (KEYBOARD_COMMAND_READ);
99 keyboard_controller_wait_until_ready ();
100 return grub_inb (KEYBOARD_REG_DATA);
106 write_mode (int mode)
109 for (i = 0; i < GRUB_AT_TRIES; i++)
112 keyboard_controller_wait_until_ready ();
113 grub_outb (0xf0, KEYBOARD_REG_DATA);
114 keyboard_controller_wait_until_ready ();
115 grub_outb (mode, KEYBOARD_REG_DATA);
116 keyboard_controller_wait_until_ready ();
118 if (ack == GRUB_AT_NACK)
120 if (ack == GRUB_AT_ACK)
125 return (i != GRUB_AT_TRIES);
138 keyboard_controller_wait_until_ready ();
141 ret = grub_inb (KEYBOARD_REG_DATA);
142 while (ret == GRUB_AT_ACK);
144 /* QEMU translates the set even in no-translate mode. */
145 if (ret == 0x43 || ret == 1)
147 if (ret == 0x41 || ret == 2)
149 if (ret == 0x3f || ret == 3)
157 /* You must have visited computer museum. Keyboard without scancode set
158 knowledge. Assume XT. */
159 if (!grub_keyboard_orig_set)
161 grub_dprintf ("atkeyb", "No sets support assumed\n");
162 ps2_state.current_set = 1;
166 #if !USE_SCANCODE_SET
167 ps2_state.current_set = 1;
171 grub_keyboard_controller_write (grub_keyboard_controller_orig
172 & ~KEYBOARD_AT_TRANSLATE);
175 ps2_state.current_set = query_mode ();
176 grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
177 if (ps2_state.current_set == 2)
181 ps2_state.current_set = query_mode ();
182 grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
183 if (ps2_state.current_set == 1)
185 grub_dprintf ("atkeyb", "no supported scancode set found\n");
190 keyboard_controller_led (grub_uint8_t leds)
192 keyboard_controller_wait_until_ready ();
193 grub_outb (0xed, KEYBOARD_REG_DATA);
194 keyboard_controller_wait_until_ready ();
195 grub_outb (leds & 0x7, KEYBOARD_REG_DATA);
199 grub_at_keyboard_is_alive (void)
201 if (ps2_state.current_set != 0)
204 && KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS))
205 && grub_inb (KEYBOARD_REG_DATA) == 0x55)
207 grub_keyboard_controller_init ();
211 if (KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
213 grub_outb (0xaa, KEYBOARD_REG_STATUS);
219 /* If there is a character pending, return it;
220 otherwise return GRUB_TERM_NO_KEY. */
222 grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
226 grub_uint8_t old_led;
228 if (!grub_at_keyboard_is_alive ())
229 return GRUB_TERM_NO_KEY;
231 if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
233 at_key = grub_inb (KEYBOARD_REG_DATA);
234 old_led = ps2_state.led_status;
236 ret = grub_ps2_process_incoming_byte (&ps2_state, at_key);
237 if (old_led != ps2_state.led_status)
238 keyboard_controller_led (ps2_state.led_status);
243 grub_keyboard_controller_init (void)
245 ps2_state.at_keyboard_status = 0;
246 /* Drain input buffer. */
249 keyboard_controller_wait_until_ready ();
250 if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
252 keyboard_controller_wait_until_ready ();
253 grub_inb (KEYBOARD_REG_DATA);
255 #if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS)
256 grub_keyboard_controller_orig = 0;
257 grub_keyboard_orig_set = 2;
258 #elif defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_COREBOOT)
259 /* *BSD relies on those settings. */
260 grub_keyboard_controller_orig = KEYBOARD_AT_TRANSLATE;
261 grub_keyboard_orig_set = 2;
263 grub_keyboard_controller_orig = grub_keyboard_controller_read ();
264 grub_keyboard_orig_set = query_mode ();
267 keyboard_controller_led (ps2_state.led_status);
271 grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused)))
273 if (ps2_state.current_set == 0)
274 return GRUB_ERR_NONE;
275 if (grub_keyboard_orig_set)
276 write_mode (grub_keyboard_orig_set);
277 grub_keyboard_controller_write (grub_keyboard_controller_orig);
278 return GRUB_ERR_NONE;
282 grub_at_fini_hw (int noreturn __attribute__ ((unused)))
284 return grub_keyboard_controller_fini (NULL);
288 grub_at_restore_hw (void)
290 if (ps2_state.current_set == 0)
291 return GRUB_ERR_NONE;
293 /* Drain input buffer. */
296 keyboard_controller_wait_until_ready ();
297 if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
299 keyboard_controller_wait_until_ready ();
300 grub_inb (KEYBOARD_REG_DATA);
303 keyboard_controller_led (ps2_state.led_status);
305 return GRUB_ERR_NONE;
309 static struct grub_term_input grub_at_keyboard_term =
311 .name = "at_keyboard",
312 .fini = grub_keyboard_controller_fini,
313 .getkey = grub_at_keyboard_getkey
316 GRUB_MOD_INIT(at_keyboard)
318 grub_term_register_input ("at_keyboard", &grub_at_keyboard_term);
319 grub_loader_register_preboot_hook (grub_at_fini_hw, grub_at_restore_hw,
320 GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
323 GRUB_MOD_FINI(at_keyboard)
325 grub_keyboard_controller_fini (NULL);
326 grub_term_unregister_input (&grub_at_keyboard_term);