/* This file is part of Fennix Kernel. Fennix Kernel 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. Fennix Kernel 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 Fennix Kernel. If not, see . */ #include "keyboard.hpp" #include #include #include #include #include #include #include #if defined(a64) #include "../../arch/amd64/cpu/gdt.hpp" #elif defined(a32) #elif defined(aa64) #endif #include "../../kernel.h" #define ERROR_COLOR "\eFF0000" #define WARN_COLOR "\eFFAA00" #define DEFAULT_COLOR "\e00FF00" extern void ExPrint(const char *Format, ...); extern void ArrowInput(uint8_t key); extern void UserInput(char *Input); const char sc_ascii_low[] = {'?', '?', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '?', '?', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '?', '?', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', '?', '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' '}; const char sc_ascii_high[] = {'?', '?', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '?', '?', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '\"', '~', '?', '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', '?', '?', '?', ' '}; static int LowerCase = true; nsa static inline int GetLetterFromScanCode(uint8_t ScanCode) { if (ScanCode & 0x80) { switch (ScanCode) { case KEY_U_LSHIFT: LowerCase = true; return KEY_INVALID; case KEY_U_RSHIFT: LowerCase = true; return KEY_INVALID; default: return KEY_INVALID; } } else { switch (ScanCode) { case KEY_D_RETURN: return '\n'; case KEY_D_LSHIFT: LowerCase = false; return KEY_INVALID; case KEY_D_RSHIFT: LowerCase = false; return KEY_INVALID; case KEY_D_BACKSPACE: return ScanCode; default: { if (ScanCode > 0x39) break; if (LowerCase) return sc_ascii_low[ScanCode]; else return sc_ascii_high[ScanCode]; } } } return KEY_INVALID; } nsa void CrashKeyboardDriver::PS2Wait(bool Read) { TimeoutCallNumber++; #if defined(a86) int Timeout = 100000; uint8_t Status = 0; while (Timeout--) { Status = inb(0x64); if (Read) { if ((Status & 1) == 1) return; } else { if ((Status & 2) == 0) return; } } ExPrint(WARN_COLOR "PS/2 controller timeout (%s;%d)\n" DEFAULT_COLOR, Read ? "read" : "write", TimeoutCallNumber - 1); #endif // a86 } /* This simple driver relies on the PS/2 controller to handle the keyboard. Maybe is not the most efficient way but most x86 devices out there still has PS/2 support (emulated or not). We even have to make sure IRQ1 is enabled in the PIC but on x64 we use the I/O APIC... "outb(0x21, 0b11111101);" can be used but the EOI is automatically sent to I/O APIC if enabled/supported which is bad. FIXME: On some real devices, the PS/2 keyboard doesn't send interrupts. TODO: Implement a way to handle USB keyboards in the future. */ CrashKeyboardDriver::CrashKeyboardDriver() : Interrupts::Handler(1) /* IRQ1 */ { #define WaitRead PS2Wait(true) #define WaitWrite PS2Wait(false) #define SetMessageLocation \ Display->SetBufferCursor(8, \ Display->GetHeight - \ Display->GetCurrentFont()->GetInfo().Height - 24) CPU::Interrupts(CPU::Disable); /* Dots will be printed at the bottom of the screen as a progress bar. */ Display->SetBufferCursor(8, Display->GetHeight - Display->GetCurrentFont()->GetInfo().Height - 8); #if defined(a86) /* Disable port 1 & 2 */ { /* Disable Port 1 */ WaitWrite; outb(0x64, 0xAD); /* Disable Port 2 */ WaitWrite; outb(0x64, 0xA7); } Display->Print('.'); /* Flush */ { int Timeout = 100000; while ((inb(0x64) & 1) && Timeout-- > 0) inb(0x60); if (Timeout <= 0) { SetMessageLocation; ExPrint(ERROR_COLOR "PS/2 controller timeout (flush;0)\n" DEFAULT_COLOR); CPU::Stop(); } } Display->Print('.'); /* Test controller */ { /* Save config */ WaitWrite; outb(0x64, 0x20); WaitRead; uint8_t cfg = inb(0x60); /* Test PS/2 controller */ WaitWrite; outb(0x64, 0xAA); WaitRead; uint8_t test = inb(0x60); if (test != 0x55) { if (test == 0xFA) { trace("PS/2 controller sent ACK to test request."); WaitRead; test = inb(0x60); } if (test != 0x55) { SetMessageLocation; ExPrint(ERROR_COLOR "PS/2 controller self test failed (%#x)\n" DEFAULT_COLOR, test); CPU::Stop(); } } /* Restore config */ WaitWrite; outb(0x64, 0x60); WaitWrite; outb(0x60, cfg); } Display->Print('.'); /* Disable scanning; Enable port 1; Set default settings */ { /* Disable scanning */ outb(0x60, 0xF5); /* Enable Port 1 */ WaitWrite; outb(0x64, 0xAE); /* Set default settings */ outb(0x60, 0xF6); } Display->Print('.'); /* Test port 1 */ { WaitWrite; outb(0x64, 0xAB); WaitRead; uint8_t test = inb(0x60); if (test != 0x00) { if (test == 0xFA) { trace("PS/2 keyboard sent ACK to test request."); WaitRead; test = inb(0x60); } if (test != 0x00) { SetMessageLocation; ExPrint(ERROR_COLOR "PS/2 keyboard self test failed (%#x)\n" DEFAULT_COLOR, test); CPU::Stop(); } } } Display->Print('.'); /* Configure the controller */ { /* Read Controller Configuration */ WaitWrite; outb(0x64, 0x20); WaitRead; uint8_t cfg = inb(0x60); /* Enable Port 1 & Port 1 translation */ cfg |= 0b01000001; /* Write Controller Configuration */ WaitWrite; outb(0x64, 0x60); WaitWrite; outb(0x60, cfg); } Display->Print('.'); /* Enable port 1; Set scan code; Enable scanning */ { /* Enable Port 1 */ outb(0x64, 0xAE); /* Set scan code set 1 */ WaitWrite; outb(0x60, 0xF0); WaitWrite; outb(0x60, 0x02); /* Check if we have scan code set 1 */ WaitWrite; outb(0x60, 0xF0); WaitWrite; outb(0x60, 0x00); /* Read scan code set */ WaitRead; uint8_t scs = inb(0x60); if (scs == 0xFA || scs == 0xFE) { if (scs == 0xFA) trace("PS/2 keyboard sent ACK to scan code set request."); if (scs == 0xFE) trace("PS/2 keyboard sent RESEND to scan code set request."); WaitRead; scs = inb(0x60); } if (scs != 0x41) { SetMessageLocation; ExPrint(WARN_COLOR "PS/2 keyboard scan code set 1 not supported (%#x)\n" DEFAULT_COLOR, scs); } /* Enable scanning */ outb(0x60, 0xF4); } Display->Print('.'); #endif // defined(a86) CPU::Interrupts(CPU::Enable); } nsa void CrashKeyboardDriver::OnInterruptReceived(CPU::TrapFrame *Frame) { #if defined(a86) UNUSED(Frame); uint8_t scanCode = inb(0x60); if (scanCode == KEY_D_TAB || scanCode == KEY_D_LCTRL || scanCode == KEY_D_LALT || scanCode == KEY_U_LCTRL || scanCode == KEY_U_LALT) return; switch (scanCode) { case KEY_D_UP: case KEY_D_LEFT: case KEY_D_RIGHT: case KEY_D_DOWN: ArrowInput(scanCode); break; default: break; } int key = GetLetterFromScanCode(scanCode); if (key != KEY_INVALID) { if (key == KEY_D_BACKSPACE) { if (BackSpaceLimit > 0) { Display->Print('\b'); backspace(UserInputBuffer); BackSpaceLimit--; } } else if (key == '\n') { UserInput(UserInputBuffer); BackSpaceLimit = 0; UserInputBuffer[0] = '\0'; } else { append(UserInputBuffer, s_cst(char, key)); Display->Print((char)key); BackSpaceLimit++; } Display->UpdateBuffer(); /* Update as we type. */ } #endif // a64 || a32 }