feat(keyboard): 接入 USB 键盘输入到 /dev/input/keyboard

现场证据:keyboard.init 的 0xF4 被 ACK,但按键时 [kbd] first IRQ1 从不出现;同时 [usb] device addr=1 class=03 proto=01 ep=1 说明真实按键来自 USB 键盘。usb_kbd 已通过 UHCI 中断回调收到报告并生成 kbd_buf 事件,但全仓无人消费(usb_kbd_thread 从未启动),所以 /dev/input/keyboard 永远读不到 USB 按键。① keyboard.vp 新增 inject_byte/inject_csi/inject_csi_tilde 三个公开注入函数,复用 _out_push/_emit_csi,不改 read_scancode/has_data/irq_handler 语义。② usb_kbd.vp 在 _on_int_complete 的按下沿(与上一帧 6 键码比较后新出现的键)按 boot protocol(枚举时已 SET_PROTOCOL(0):modifier+reserved+keycode[6])解码成 ASCII/ANSI,注入同一 out_buf:字母数字含 Shift/CapsLock、Enter\\n、Backspace\\b、方向键 ESC[A/B/C/D、Home/End ESC[H/F、Delete/PgUp/PgDn/Insert ESC[3~/5~/6~/2~,与 PS/2 路径一致;按住不重复、松开不产字节。③ 不新建线程、不启动 kbd_thread,USB 与 PS/2 共用同一队列,中断上下文内写入(IF=0)。④ 加一次性诊断 [usb_kbd] first key。
This commit is contained in:
TermiNexus committed 2026-10-17 15:43:48 +08:00
1 parent d19d96dc70
commit eb953bb1d3
2 files changed
+66 -1

No files matched your search

@@ -99,6 +99,16 @@ def _emit_csi_tilde(digit: uint8):
_out_push(digit)
_out_push(uint8(0x7E))
# USB HID 等外部来源注入已解码字节,与 PS/2 IRQ 共用同一个 out_buf。
def inject_byte(b: uint8):
_out_push(b)
def inject_csi(final: uint8):
_emit_csi(final)
def inject_csi_tilde(digit: uint8):
_emit_csi_tilde(digit)
# CapsLock 只翻转字母大小写(符号仍由 Shift 决定)
def _is_letter_code(code: uint8) -> bool:
if code >= uint8(0x10) and code <= uint8(0x19): return True
+56 -1
View File
@@ -2,6 +2,7 @@ import t, c
import drivers.usb.usb as usb
import drivers.usb.hid.hid as hid
import drivers.usb.uhci as uhci
import drivers.input.keyboard.keyboard as keyboard
import drivers.serial.uart.serial as serial
define KBD_BUF_SIZE = 1024
@@ -22,6 +23,55 @@ kbd_report: hid.hid_kbd_report
kbd_prev_keycodes: [6]uint8
kbd_initialized: int = 0
kbd_slot_id: int = -1
kbd_caps: int = 0
kbd_first_key: int = 0
# HID boot 报告 usage ID (0x04..0x38) -> ASCII;CapsLock / 方向键等在 _decode_press 单独处理。
hid_ascii: [64]char = [
'\0', '\0', '\0', '\0',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'\n', '\x1b', '\b', '\t', ' ',
'-', '=', '[', ']', '\\', '\0', ';', '\'', '`', ',', '.', '/',
'\0', '\0', '\0', '\0', '\0', '\0', '\0'
]
hid_ascii_shift: [64]char = [
'\0', '\0', '\0', '\0',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
'\n', '\x1b', '\b', '\t', ' ',
'_', '+', '{', '}', '|', '\0', ':', '"', '~', '<', '>', '?',
'\0', '\0', '\0', '\0', '\0', '\0', '\0'
]
# 按下沿调用一次:boot 报告 keycode -> ASCII / ANSI,直接进 PS/2 同一输出队列。
def _decode_press(code: uint8, modifier: uint8):
global kbd_caps
if code == uint8(0x39):
kbd_caps = kbd_caps ^ 1
return
if code == uint8(0x52): keyboard.inject_csi(uint8(0x41)) # Up
elif code == uint8(0x51): keyboard.inject_csi(uint8(0x42)) # Down
elif code == uint8(0x4F): keyboard.inject_csi(uint8(0x43)) # Right
elif code == uint8(0x50): keyboard.inject_csi(uint8(0x44)) # Left
elif code == uint8(0x4A): keyboard.inject_csi(uint8(0x48)) # Home
elif code == uint8(0x4D): keyboard.inject_csi(uint8(0x46)) # End
elif code == uint8(0x49): keyboard.inject_csi_tilde(uint8(0x32)) # Insert
elif code == uint8(0x4C): keyboard.inject_csi_tilde(uint8(0x33)) # Delete
elif code == uint8(0x4B): keyboard.inject_csi_tilde(uint8(0x35)) # PageUp
elif code == uint8(0x4E): keyboard.inject_csi_tilde(uint8(0x36)) # PageDown
elif int(code) < 64:
shifted: bool = (modifier & uint8(0x22)) != 0
if kbd_caps != 0 and code >= uint8(0x04) and code <= uint8(0x1D):
shifted = not shifted
ch: char = hid_ascii[int(code)]
if shifted:
ch = hid_ascii_shift[int(code)]
if ch != '\0':
keyboard.inject_byte(uint8(ch))
def init() -> int:
global kbd_dev, kbd_initialized, kbd_slot_id
@@ -47,7 +97,7 @@ def init() -> int:
return 0
def _on_int_complete() -> int:
global kbd_buf_head, kbd_buf_tail, kbd_buf_count, kbd_prev_keycodes
global kbd_buf_head, kbd_buf_tail, kbd_buf_count, kbd_prev_keycodes, kbd_first_key
if kbd_initialized == 0: return 0
j: int
for i in range(6):
@@ -59,6 +109,11 @@ def _on_int_complete() -> int:
found = 1
break
if found == 0:
if kbd_first_key == 0:
kbd_first_key = 1
serial.puts("[usb_kbd] first key\n")
# 只在按下沿解码,避免按住时重复入队。
_decode_press(code, kbd_report.modifier)
if kbd_buf_count < KBD_BUF_SIZE:
kbd_buf[kbd_buf_tail].modifier = kbd_report.modifier
kbd_buf[kbd_buf_tail].keycode = code