/* StarForth — Steady-State Virtual Machine Runtime Copyright (c) 2023–2026 Robert A. James. All rights reserved. Licensed under the Starship License, Version 1.0 (SL-1.0). */ /** * usb_kbd.c - a USB boot-protocol keyboard's reports, as key events * (2026-10-14, boards/BRINGUP.md A3). See usb_kbd.h. * * A boot report says which keys are held now, not what changed: what was * held in the report before and is not now has been released, and the * other way round has been pressed. The eight modifier bits are keys too * (left/right Ctrl, Shift, Alt, GUI). A report whose keys are all 0x01 is * the keyboard's "too many keys" (ErrorRollOver) and changes nothing. * * Usage IDs (HID Usage Tables 1.12, page 7) become Linux keycodes * (input-event-codes.h) -- the same numbers the PS/2 and virtio keyboards * already give sk_key_event_poll(). Only the keys a console uses are * mapped; others are dropped. The keyboard does not repeat a held key: * held is held, and nothing here repeats it either. */ #include "starkernel/usb_kbd.h" /* HID usage ID -> Linux keycode; 0 = not mapped. A full-size keyboard: * the main block, F1-F12, the navigation block, arrows and the keypad * (Captain Bob, 2026-10-14: "include caps lock, numlock, keypad and * arrow"). What a key means -- Caps and Num Lock as toggles, the keypad * as digits or navigation -- is the console's (repl.c), for every * keyboard alike. */ static const uint8_t usage_to_key[0x66] = { [0x04] = 30, [0x05] = 48, [0x06] = 46, [0x07] = 32, [0x08] = 18, /* a b c d e */ [0x09] = 33, [0x0A] = 34, [0x0B] = 35, [0x0C] = 23, [0x0D] = 36, /* f g h i j */ [0x0E] = 37, [0x0F] = 38, [0x10] = 50, [0x11] = 49, [0x12] = 24, /* k l m n o */ [0x13] = 25, [0x14] = 16, [0x15] = 19, [0x16] = 31, [0x17] = 20, /* p q r s t */ [0x18] = 22, [0x19] = 47, [0x1A] = 17, [0x1B] = 45, [0x1C] = 21, /* u v w x y */ [0x1D] = 44, /* z */ [0x1E] = 2, [0x1F] = 3, [0x20] = 4, [0x21] = 5, [0x22] = 6, /* 1 2 3 4 5 */ [0x23] = 7, [0x24] = 8, [0x25] = 9, [0x26] = 10, [0x27] = 11, /* 6 7 8 9 0 */ [0x28] = 28, [0x29] = 1, [0x2A] = 14, [0x2B] = 15, [0x2C] = 57, /* enter esc bksp tab space */ [0x2D] = 12, [0x2E] = 13, [0x2F] = 26, [0x30] = 27, [0x31] = 43, /* - = [ ] \ */ [0x32] = 43, [0x33] = 39, [0x34] = 40, [0x35] = 41, [0x36] = 51, /* non-US # ; ' ` , */ [0x37] = 52, [0x38] = 53, [0x39] = 58, /* . / capslock */ [0x3A] = 59, [0x3B] = 60, [0x3C] = 61, [0x3D] = 62, [0x3E] = 63, /* F1-F5 */ [0x3F] = 64, [0x40] = 65, [0x41] = 66, [0x42] = 67, [0x43] = 68, /* F6-F10 */ [0x44] = 87, [0x45] = 88, /* F11 F12 */ [0x46] = 99, [0x47] = 70, [0x48] = 119, /* printscreen scrolllock pause */ [0x49] = 110, [0x4A] = 102, [0x4B] = 104, /* insert home pageup */ [0x4C] = 111, [0x4D] = 107, [0x4E] = 109, /* delete end pagedown */ [0x4F] = 106, [0x50] = 105, [0x51] = 108, [0x52] = 103, /* right left down up */ [0x53] = 69, /* numlock */ [0x54] = 98, [0x55] = 55, [0x56] = 74, [0x57] = 78, [0x58] = 96, /* kp/ kp* kp- kp+ kpenter */ [0x59] = 79, [0x5A] = 80, [0x5B] = 81, [0x5C] = 75, [0x5D] = 76, /* kp1 kp2 kp3 kp4 kp5 */ [0x5E] = 77, [0x5F] = 71, [0x60] = 72, [0x61] = 73, [0x62] = 82, /* kp6 kp7 kp8 kp9 kp0 */ [0x63] = 83, [0x64] = 86, [0x65] = 127, /* kp. non-US\ compose(menu) */ }; /* Modifier bit n -> Linux keycode: LCtrl LShift LAlt LGUI RCtrl RShift RAlt RGUI. */ static const uint8_t modifier_key[8] = { 29, 42, 56, 125, 97, 54, 100, 126 }; #define KBD_EVENTS 64u static struct { uint16_t keycode; uint8_t pressed; } g_events[KBD_EVENTS]; static uint32_t g_head, g_count; /* Num Lock on to start, matching the console (repl.c). */ static uint8_t g_leds_want = USB_KBD_LED_NUM; static void push(uint16_t keycode, int pressed) { if (keycode == 0 || g_count == KBD_EVENTS) return; /* a full queue drops: nothing reads it */ g_events[(g_head + g_count) % KBD_EVENTS].keycode = keycode; g_events[(g_head + g_count) % KBD_EVENTS].pressed = (uint8_t)(pressed != 0); g_count++; } static uint16_t key_for(uint8_t usage) { return usage < sizeof usage_to_key ? usage_to_key[usage] : 0; } static int held(const uint8_t *keys, uint32_t n, uint8_t usage) { uint32_t i; for (i = 0; i < n; i++) if (keys[i] == usage) return 1; return 0; } void usb_kbd_report(uint8_t prev[8], const uint8_t *report, uint32_t len) { uint8_t now[8] = {0}; uint32_t i, n; if (!report || len < 3) return; if (len > 8) len = 8; for (i = 0; i < len; i++) now[i] = report[i]; if (now[2] == 0x01) return; /* ErrorRollOver: nothing known */ for (i = 0; i < 8; i++) { uint8_t bit = (uint8_t)(1u << i); if ((now[0] & bit) != (prev[0] & bit)) push(modifier_key[i], (now[0] & bit) != 0); } n = 6; for (i = 2; i < 8; i++) /* released first, then pressed */ if (prev[i] > 0x01 && !held(now + 2, n, prev[i])) push(key_for(prev[i]), 0); for (i = 2; i < 8; i++) if (now[i] > 0x01 && !held(prev + 2, n, now[i])) push(key_for(now[i]), 1); for (i = 0; i < 8; i++) prev[i] = now[i]; } void usb_kbd_forget(uint8_t prev[8]) { static const uint8_t none[8] = {0}; usb_kbd_report(prev, none, 8); /* everything held, released */ } void usb_kbd_set_leds(uint8_t leds) { g_leds_want = leds; } uint8_t usb_kbd_leds_wanted(void) { return g_leds_want; } int usb_kbd_pop_event(uint16_t *keycode, int *pressed) { if (g_count == 0) return 0; *keycode = g_events[g_head].keycode; *pressed = g_events[g_head].pressed; g_head = (g_head + 1u) % KBD_EVENTS; g_count--; return 1; }