/ kernel / drivers / keyboard.c
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  }