keyboard.c
1 #include "keyboard.h" 2 #include "kernel.h" 3 #include "idt.h" 4 #include "string.h" 5 6 // Keyboard state 7 static uint8_t key_state = 0; 8 static keyboard_callback_t key_callback = NULL; 9 10 // PS/2 Set 2 prefix tracking 11 static uint8_t break_prefix = 0; // 1 = next byte is a break code (after F0) 12 static uint8_t extended_prefix = 0; // 1 = we saw an E0 (extended key) 13 14 // Keyboard buffer (circular) 15 static char key_buffer[KEYBOARD_BUFFER_SIZE]; 16 static uint32_t key_buffer_start = 0; 17 static uint32_t key_buffer_end = 0; 18 19 // US QWERTY layout - PS/2 Set 2 scancodes (keyboard default) 20 // PS/2 Set 2 scancode table (US QWERTY) 21 static const char scancode_to_ascii[128] = { 22 [0x00] = 0, [0x01] = 0, [0x02] = 0, [0x03] = 0, 23 [0x04] = 0, [0x05] = 0, [0x06] = 0, [0x07] = 0, 24 [0x08] = 0, [0x09] = 0, [0x0A] = 0, [0x0B] = 0, 25 [0x0C] = 0, [0x0D] = '\t', [0x0E] = '`', [0x0F] = 0, 26 [0x10] = 0, [0x11] = 0, [0x12] = 0, [0x13] = 0, 27 [0x14] = 0, [0x15] = 'q', [0x16] = '1', [0x17] = 0, 28 [0x18] = 0, [0x19] = 0, [0x1A] = 'z', [0x1B] = 's', 29 [0x1C] = 'a', [0x1D] = 'w', [0x1E] = '2', [0x1F] = 0, 30 [0x20] = 0, [0x21] = 'c', [0x22] = 'x', [0x23] = 'd', 31 [0x24] = 'e', [0x25] = '4', [0x26] = '3', [0x27] = 0, 32 [0x28] = 0, [0x29] = ' ', [0x2A] = 'v', [0x2B] = 'f', 33 [0x2C] = 't', [0x2D] = 'r', [0x2E] = '5', [0x2F] = 0, 34 [0x30] = 0, [0x31] = 'n', [0x32] = 'b', [0x33] = 'h', 35 [0x34] = 'g', [0x35] = 'y', [0x36] = '6', [0x37] = 0, 36 [0x38] = 0, [0x39] = 0, [0x3A] = 'm', [0x3B] = 'j', 37 [0x3C] = 'u', [0x3D] = '7', [0x3E] = '8', [0x3F] = 0, 38 [0x40] = 0, [0x41] = ',', [0x42] = 'k', [0x43] = 'i', 39 [0x44] = 'o', [0x45] = '0', [0x46] = '9', [0x47] = 0, 40 [0x48] = 0, [0x49] = '.', [0x4A] = '/', [0x4B] = 'l', 41 [0x4C] = ';', [0x4D] = 'p', [0x4E] = '-', [0x4F] = 0, 42 [0x50] = 0, [0x51] = 0, [0x52] = '\'', [0x53] = 0, 43 [0x54] = '[', [0x55] = '=', [0x56] = 0, [0x57] = 0, 44 [0x58] = 0, [0x59] = 0, [0x5A] = '\n', [0x5B] = ']', 45 [0x5C] = 0, [0x5D] = '\\', [0x5E] = 0, [0x5F] = 0, 46 [0x60] = 0, [0x61] = 0, [0x62] = 0, [0x63] = 0, 47 [0x64] = 0, [0x65] = 0, [0x66] = '\b', [0x67] = 0, 48 [0x68] = 0, [0x69] = 0, [0x6A] = 0, [0x6B] = 0, 49 [0x6C] = 0, [0x6D] = 0, [0x6E] = 0, [0x6F] = 0, 50 [0x70] = 0, [0x71] = 0, [0x72] = 0, [0x73] = 0, 51 [0x74] = 0, [0x75] = 0, [0x76] = 0, [0x77] = 0, 52 [0x78] = 0, [0x79] = 0, [0x7A] = 0, [0x7B] = 0, 53 [0x7C] = 0, [0x7D] = 0, [0x7E] = 0, [0x7F] = 0 54 }; 55 56 // PS/2 Set 2 shift table (US QWERTY) 57 static const char scancode_to_ascii_shift[128] = { 58 [0x00] = 0, [0x01] = 0, [0x02] = 0, [0x03] = 0, 59 [0x04] = 0, [0x05] = 0, [0x06] = 0, [0x07] = 0, 60 [0x08] = 0, [0x09] = 0, [0x0A] = 0, [0x0B] = 0, 61 [0x0C] = 0, [0x0D] = '\t', [0x0E] = '~', [0x0F] = 0, 62 [0x10] = 0, [0x11] = 0, [0x12] = 0, [0x13] = 0, 63 [0x14] = 0, [0x15] = 'Q', [0x16] = '!', [0x17] = 0, 64 [0x18] = 0, [0x19] = 0, [0x1A] = 'Z', [0x1B] = 'S', 65 [0x1C] = 'A', [0x1D] = 'W', [0x1E] = '@', [0x1F] = 0, 66 [0x20] = 0, [0x21] = 'C', [0x22] = 'X', [0x23] = 'D', 67 [0x24] = 'E', [0x25] = '$', [0x26] = '#', [0x27] = 0, 68 [0x28] = 0, [0x29] = ' ', [0x2A] = 'V', [0x2B] = 'F', 69 [0x2C] = 'T', [0x2D] = 'R', [0x2E] = '%', [0x2F] = 0, 70 [0x30] = 0, [0x31] = 'N', [0x32] = 'B', [0x33] = 'H', 71 [0x34] = 'G', [0x35] = 'Y', [0x36] = '^', [0x37] = 0, 72 [0x38] = 0, [0x39] = 0, [0x3A] = 'M', [0x3B] = 'J', 73 [0x3C] = 'U', [0x3D] = '&', [0x3E] = '*', [0x3F] = 0, 74 [0x40] = 0, [0x41] = '<', [0x42] = 'K', [0x43] = 'I', 75 [0x44] = 'O', [0x45] = ')', [0x46] = '(', [0x47] = 0, 76 [0x48] = 0, [0x49] = '>', [0x4A] = '?', [0x4B] = 'L', 77 [0x4C] = ':', [0x4D] = 'P', [0x4E] = '_', [0x4F] = 0, 78 [0x50] = 0, [0x51] = 0, [0x52] = '"', [0x53] = 0, 79 [0x54] = '{', [0x55] = '+', [0x56] = 0, [0x57] = 0, 80 [0x58] = 0, [0x59] = 0, [0x5A] = '\n', [0x5B] = '}', 81 [0x5C] = 0, [0x5D] = '|', [0x5E] = 0, [0x5F] = 0, 82 [0x60] = 0, [0x61] = 0, [0x62] = 0, [0x63] = 0, 83 [0x64] = 0, [0x65] = 0, [0x66] = '\b', [0x67] = 0, 84 [0x68] = 0, [0x69] = 0, [0x6A] = 0, [0x6B] = 0, 85 [0x6C] = 0, [0x6D] = 0, [0x6E] = 0, [0x6F] = 0, 86 [0x70] = 0, [0x71] = 0, [0x72] = 0, [0x73] = 0, 87 [0x74] = 0, [0x75] = 0, [0x76] = 0, [0x77] = 0, 88 [0x78] = 0, [0x79] = 0, [0x7A] = 0, [0x7B] = 0, 89 [0x7C] = 0, [0x7D] = 0, [0x7E] = 0, [0x7F] = 0 90 }; 91 92 // Keyboard interrupt handler (IRQ1) 93 void keyboard_handler(void) { 94 uint8_t code = inb(0x60); 95 96 // Handle PS/2 Set 2 prefixes first 97 if (code == 0xE0) { // Extended key prefix 98 extended_prefix = 1; 99 goto eoi; 100 } 101 if (code == 0xF0) { // Break sequence prefix 102 break_prefix = 1; 103 goto eoi; 104 } 105 106 // Real scancode byte 107 uint8_t scancode = code; 108 int pressed = !break_prefix; // make if no F0 prefix 109 110 // Reset prefix flags (they're consumed) 111 break_prefix = 0; 112 extended_prefix = 0; // TODO: use this for extended keys later 113 114 // Update modifier key states (PS/2 Set 2 scancodes) 115 if (scancode == 0x12 || scancode == 0x59) { // Left shift (0x12) or right shift (0x59) 116 if (pressed) { 117 key_state |= KEY_STATE_SHIFT; 118 } else { 119 key_state &= ~KEY_STATE_SHIFT; 120 } 121 } else if (scancode == 0x14) { // Control (0x14) 122 if (pressed) { 123 key_state |= KEY_STATE_CTRL; 124 } else { 125 key_state &= ~KEY_STATE_CTRL; 126 } 127 } else if (scancode == 0x11) { // Alt (0x11) 128 if (pressed) { 129 key_state |= KEY_STATE_ALT; 130 } else { 131 key_state &= ~KEY_STATE_ALT; 132 } 133 } else if (scancode == 0x58 && pressed) { // Caps lock (0x58, only on press) 134 key_state ^= KEY_STATE_CAPS; 135 keyboard_set_leds((key_state & KEY_STATE_CAPS) ? 0x04 : 0x00); 136 } 137 138 // Only generate characters on key press 139 if (!pressed) { 140 // For key releases, just update state and return 141 goto eoi; 142 } 143 144 // Get character from scancode table 145 char ch = 0; 146 if (scancode < 128) { 147 if (key_state & KEY_STATE_SHIFT) { 148 ch = scancode_to_ascii_shift[scancode]; 149 } else { 150 ch = scancode_to_ascii[scancode]; 151 } 152 153 // Handle caps lock for letters 154 if ((key_state & KEY_STATE_CAPS) && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { 155 if (ch >= 'a' && ch <= 'z') { 156 ch -= 32; // Convert to uppercase 157 } else if (ch >= 'A' && ch <= 'Z') { 158 ch += 32; // Convert to lowercase 159 } 160 } 161 162 // Handle Ctrl combinations 163 if ((key_state & KEY_STATE_CTRL) && ch >= 'a' && ch <= 'z') { 164 ch = ch - 'a' + 1; // Ctrl+A = 1, Ctrl+B = 2, etc. 165 } 166 167 if (ch != 0) { 168 // Add to buffer 169 uint32_t next_end = (key_buffer_end + 1) % KEYBOARD_BUFFER_SIZE; 170 if (next_end != key_buffer_start) { 171 key_buffer[key_buffer_end] = ch; 172 key_buffer_end = next_end; 173 } 174 175 // Call callback if set 176 if (key_callback) { 177 key_callback(ch); 178 } 179 } 180 } 181 182 eoi: 183 // Send End of Interrupt 184 outb(0x20, 0x20); 185 } 186 187 // Initialize keyboard 188 void keyboard_init(void) { 189 // Clear buffer and state 190 key_buffer_start = 0; 191 key_buffer_end = 0; 192 key_state = 0; 193 break_prefix = 0; 194 extended_prefix = 0; 195 196 // Ensure we get Set 2 scancodes by clearing XLAT bit 197 outb(0x64, 0x20); // Read command byte 198 while (inb(0x64) & 0x02); // Wait for response 199 uint8_t cfg = inb(0x60); 200 cfg &= ~0x40; // Clear XLAT (bit 6) - no Set 2->Set 1 translation 201 outb(0x64, 0x60); // Write command byte 202 while (inb(0x64) & 0x02); // Wait for ready 203 outb(0x60, cfg); 204 205 // Wait for keyboard controller 206 while (inb(0x64) & 0x02); 207 208 // Enable keyboard (keeping default Set 2 scancodes) 209 outb(0x60, 0xF4); 210 211 // Note: disabled XLAT so keyboard stays in Set 2 212 // which matches the scancode tables 213 214 // Register interrupt handler for IRQ1 215 register_interrupt_handler(33, keyboard_handler); 216 217 terminal_writestring("Keyboard initialized\n"); 218 } 219 220 // Get character from buffer 221 char keyboard_getchar(void) { 222 if (key_buffer_start == key_buffer_end) { 223 return 0; // No character available 224 } 225 226 char ch = key_buffer[key_buffer_start]; 227 key_buffer_start = (key_buffer_start + 1) % KEYBOARD_BUFFER_SIZE; 228 return ch; 229 } 230 231 // Check if character available 232 int keyboard_haschar(void) { 233 return key_buffer_start != key_buffer_end; 234 } 235 236 // Set keyboard LEDs 237 void keyboard_set_leds(uint8_t leds) { 238 while (inb(0x64) & 0x02); 239 outb(0x60, 0xED); 240 while (inb(0x64) & 0x02); 241 outb(0x60, leds); 242 } 243 244 // Set keyboard callback 245 void keyboard_set_callback(keyboard_callback_t callback) { 246 key_callback = callback; 247 }