/* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. * * VAS_EBOOT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VAS_EBOOT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VAS_EBOOT. If not, see . */ #include #include #include #include #include #include #include #include #include VAS_EBOOT_MOD_LICENSE ("GPLv3+"); static VasEBoot_uint8_t VasEBoot_keyboard_controller_orig; static VasEBoot_uint8_t VasEBoot_keyboard_orig_set; struct VasEBoot_ps2_state ps2_state; static int ping_sent; static void VasEBoot_keyboard_controller_init (void); static void keyboard_controller_wait_until_ready (void) { unsigned int i = 200; /* 50 us would be enough but our current time resolution is 1ms. */ VasEBoot_millisleep (1); while (!KEYBOARD_COMMAND_ISREADY (VasEBoot_inb (KEYBOARD_REG_STATUS))) { VasEBoot_millisleep (1); /* Timeout. */ if (!i--) break; } } static VasEBoot_uint8_t wait_ack (void) { VasEBoot_uint64_t endtime; VasEBoot_uint8_t ack; endtime = VasEBoot_get_time_ms () + 20; do { keyboard_controller_wait_until_ready (); ack = VasEBoot_inb (KEYBOARD_REG_DATA); } while (ack != VAS_EBOOT_AT_ACK && ack != VAS_EBOOT_AT_NACK && VasEBoot_get_time_ms () < endtime); return ack; } static int at_command (VasEBoot_uint8_t data) { unsigned i; for (i = 0; i < VAS_EBOOT_AT_TRIES; i++) { VasEBoot_uint8_t ack; keyboard_controller_wait_until_ready (); VasEBoot_outb (data, KEYBOARD_REG_STATUS); ack = wait_ack (); if (ack == VAS_EBOOT_AT_NACK) continue; if (ack == VAS_EBOOT_AT_ACK) break; return 0; } return (i != VAS_EBOOT_AT_TRIES); } static void VasEBoot_keyboard_controller_write (VasEBoot_uint8_t c) { at_command (KEYBOARD_COMMAND_WRITE); keyboard_controller_wait_until_ready (); VasEBoot_outb (c, KEYBOARD_REG_DATA); } #if defined (VAS_EBOOT_MACHINE_MIPS_LOONGSON) || defined (VAS_EBOOT_MACHINE_QEMU) || defined (VAS_EBOOT_MACHINE_COREBOOT) || defined (VAS_EBOOT_MACHINE_MIPS_QEMU_MIPS) #define USE_SCANCODE_SET 1 #else #define USE_SCANCODE_SET 0 #endif #if !USE_SCANCODE_SET static VasEBoot_uint8_t VasEBoot_keyboard_controller_read (void) { at_command (KEYBOARD_COMMAND_READ); keyboard_controller_wait_until_ready (); return VasEBoot_inb (KEYBOARD_REG_DATA); } #endif static int write_mode (int mode) { unsigned i; for (i = 0; i < VAS_EBOOT_AT_TRIES; i++) { VasEBoot_uint8_t ack; keyboard_controller_wait_until_ready (); VasEBoot_outb (0xf0, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); VasEBoot_outb (mode, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); ack = wait_ack (); if (ack == VAS_EBOOT_AT_NACK) continue; if (ack == VAS_EBOOT_AT_ACK) break; return 0; } return (i != VAS_EBOOT_AT_TRIES); } static int query_mode (void) { VasEBoot_uint8_t ret; int e; e = write_mode (0); if (!e) return 0; do { keyboard_controller_wait_until_ready (); ret = VasEBoot_inb (KEYBOARD_REG_DATA); } while (ret == VAS_EBOOT_AT_ACK); /* QEMU translates the set even in no-translate mode. */ if (ret == 0x43 || ret == 1) return 1; if (ret == 0x41 || ret == 2) return 2; if (ret == 0x3f || ret == 3) return 3; return 0; } static void set_scancodes (void) { /* You must have visited computer museum. Keyboard without scancode set knowledge. Assume XT. */ if (!VasEBoot_keyboard_orig_set) { VasEBoot_dprintf ("atkeyb", "No sets support assumed\n"); ps2_state.current_set = 1; return; } #if !USE_SCANCODE_SET ps2_state.current_set = 1; return; #else VasEBoot_keyboard_controller_write (VasEBoot_keyboard_controller_orig & ~KEYBOARD_AT_TRANSLATE & ~KEYBOARD_AT_DISABLE); keyboard_controller_wait_until_ready (); VasEBoot_outb (KEYBOARD_COMMAND_ENABLE, KEYBOARD_REG_DATA); write_mode (2); ps2_state.current_set = query_mode (); VasEBoot_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); if (ps2_state.current_set == 2) return; write_mode (1); ps2_state.current_set = query_mode (); VasEBoot_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); if (ps2_state.current_set == 1) return; VasEBoot_dprintf ("atkeyb", "no supported scancode set found\n"); #endif } static void keyboard_controller_led (VasEBoot_uint8_t leds) { keyboard_controller_wait_until_ready (); VasEBoot_outb (0xed, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); VasEBoot_outb (leds & 0x7, KEYBOARD_REG_DATA); } int VasEBoot_at_keyboard_is_alive (void) { if (ps2_state.current_set != 0) return 1; if (ping_sent && KEYBOARD_COMMAND_ISREADY (VasEBoot_inb (KEYBOARD_REG_STATUS)) && VasEBoot_inb (KEYBOARD_REG_DATA) == 0x55) { VasEBoot_keyboard_controller_init (); return 1; } if (KEYBOARD_COMMAND_ISREADY (VasEBoot_inb (KEYBOARD_REG_STATUS))) { VasEBoot_outb (0xaa, KEYBOARD_REG_STATUS); ping_sent = 1; } return 0; } /* If there is a character pending, return it; otherwise return VAS_EBOOT_TERM_NO_KEY. */ static int VasEBoot_at_keyboard_getkey (struct VasEBoot_term_input *term __attribute__ ((unused))) { VasEBoot_uint8_t at_key; int ret; VasEBoot_uint8_t old_led; if (!VasEBoot_at_keyboard_is_alive ()) return VAS_EBOOT_TERM_NO_KEY; if (! KEYBOARD_ISREADY (VasEBoot_inb (KEYBOARD_REG_STATUS))) return VAS_EBOOT_TERM_NO_KEY; at_key = VasEBoot_inb (KEYBOARD_REG_DATA); old_led = ps2_state.led_status; ret = VasEBoot_ps2_process_incoming_byte (&ps2_state, at_key); if (old_led != ps2_state.led_status) keyboard_controller_led (ps2_state.led_status); return ret; } static void VasEBoot_keyboard_controller_init (void) { ps2_state.at_keyboard_status = 0; /* Drain input buffer. */ while (1) { keyboard_controller_wait_until_ready (); if (! KEYBOARD_ISREADY (VasEBoot_inb (KEYBOARD_REG_STATUS))) break; keyboard_controller_wait_until_ready (); VasEBoot_inb (KEYBOARD_REG_DATA); } #if defined (VAS_EBOOT_MACHINE_MIPS_LOONGSON) || defined (VAS_EBOOT_MACHINE_MIPS_QEMU_MIPS) VasEBoot_keyboard_controller_orig = 0; VasEBoot_keyboard_orig_set = 2; #elif defined (VAS_EBOOT_MACHINE_QEMU) || defined (VAS_EBOOT_MACHINE_COREBOOT) /* *BSD relies on those settings. */ VasEBoot_keyboard_controller_orig = KEYBOARD_AT_TRANSLATE; VasEBoot_keyboard_orig_set = 2; #else VasEBoot_keyboard_controller_orig = VasEBoot_keyboard_controller_read (); VasEBoot_keyboard_orig_set = query_mode (); #endif set_scancodes (); keyboard_controller_led (ps2_state.led_status); } static VasEBoot_err_t VasEBoot_keyboard_controller_fini (struct VasEBoot_term_input *term __attribute__ ((unused))) { if (ps2_state.current_set == 0) return VAS_EBOOT_ERR_NONE; if (VasEBoot_keyboard_orig_set) write_mode (VasEBoot_keyboard_orig_set); VasEBoot_keyboard_controller_write (VasEBoot_keyboard_controller_orig); return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_at_fini_hw (int noreturn __attribute__ ((unused))) { return VasEBoot_keyboard_controller_fini (NULL); } static VasEBoot_err_t VasEBoot_at_restore_hw (void) { if (ps2_state.current_set == 0) return VAS_EBOOT_ERR_NONE; /* Drain input buffer. */ while (1) { keyboard_controller_wait_until_ready (); if (! KEYBOARD_ISREADY (VasEBoot_inb (KEYBOARD_REG_STATUS))) break; keyboard_controller_wait_until_ready (); VasEBoot_inb (KEYBOARD_REG_DATA); } set_scancodes (); keyboard_controller_led (ps2_state.led_status); return VAS_EBOOT_ERR_NONE; } static struct VasEBoot_term_input VasEBoot_at_keyboard_term = { .name = "at_keyboard", .fini = VasEBoot_keyboard_controller_fini, .getkey = VasEBoot_at_keyboard_getkey }; VAS_EBOOT_MOD_INIT(at_keyboard) { VasEBoot_term_register_input ("at_keyboard", &VasEBoot_at_keyboard_term); VasEBoot_loader_register_preboot_hook (VasEBoot_at_fini_hw, VasEBoot_at_restore_hw, VAS_EBOOT_LOADER_PREBOOT_HOOK_PRIO_CONSOLE); } VAS_EBOOT_MOD_FINI(at_keyboard) { VasEBoot_keyboard_controller_fini (NULL); VasEBoot_term_unregister_input (&VasEBoot_at_keyboard_term); }