170 lines
5.5 KiB
Plaintext
170 lines
5.5 KiB
Plaintext
import asm
|
|
import intr.idt as idt
|
|
import drivers.serial.uart.serial as serial
|
|
import string
|
|
import viperlib
|
|
import t, c
|
|
|
|
|
|
define KBD_DATA = 0x60
|
|
define KBD_STATUS = 0x64
|
|
define KBD_COMMAND = 0x64
|
|
|
|
define KBD_BUF_SIZE = 1024
|
|
|
|
define KEY_RELEASED = 0x80
|
|
|
|
buffer: [KBD_BUF_SIZE]uint8
|
|
buf_head: static uint32 = 0
|
|
buf_tail: static uint32 = 0
|
|
buf_count: static uint32 = 0
|
|
kbd_irq_cnt: static uint64 = 0
|
|
# 硬件初始化只做一次(init 有多个调用点,见 init() 内注释)
|
|
_kbd_inited: static int = 0
|
|
|
|
shift_l: static uint8 = 0
|
|
shift_r: static uint8 = 0
|
|
ctrl_l: static uint8 = 0
|
|
alt_l: static uint8 = 0
|
|
caps_lock: static uint8 = 0
|
|
|
|
scancode_set1: [128]char = [
|
|
'\0', '\x1b', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
|
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
|
'\0', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '`', '\0',
|
|
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\0', '\0', '\0', ' ',
|
|
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '-', '8', '6', '2', '\0',
|
|
'1', '+', '4', '5', '3', '0', '.', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
|
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
|
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
|
'\0'
|
|
]
|
|
|
|
scancode_set1_shift: [128]char = [
|
|
'\0', '\x1b', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
|
|
'\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
|
|
'\0', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', '\0',
|
|
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', '\0', '\0', '\0', ' ',
|
|
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '-', '8', '6', '2', '\0',
|
|
'1', '+', '4', '5', '3', '0', '.', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
|
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
|
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
|
'\0'
|
|
]
|
|
|
|
def _wait_read():
|
|
timeout: uint32 = 100000
|
|
while timeout > 0:
|
|
if (asm.inb(KBD_STATUS) & 0x01) != 0: return
|
|
timeout -= 1
|
|
|
|
def _wait_write():
|
|
timeout: uint32 = 100000
|
|
while timeout > 0:
|
|
if (asm.inb(KBD_STATUS) & 0x02) == 0: return
|
|
timeout -= 1
|
|
|
|
def irq_handler() -> int:
|
|
global buf_head, buf_tail, buf_count, kbd_irq_cnt
|
|
kbd_irq_cnt += 1
|
|
# 0x60 是键盘/鼠标**共享**的输出缓冲:AUX 位(0x64 bit5)置位表示这是鼠标字节,
|
|
# 不属于键盘 —— 直接读走会让鼠标驱动永远拿不到(本 ISR 只收键盘字节)。
|
|
st: uint8 = asm.inb(KBD_STATUS)
|
|
if (st & 0x20) != 0:
|
|
return 0
|
|
scancode: uint8 = asm.inb(KBD_DATA)
|
|
# 按键验证:把 IRQ1 收到的扫描码打到串口(高位 0x80 = 释放),
|
|
# 用来确认"键盘中断是否到达"这一环。
|
|
_kmsg: [48]char
|
|
viperlib.snprintf(&_kmsg, 48, "[kbd] sc=0x%x\n", int(scancode))
|
|
serial.puts(&_kmsg)
|
|
if buf_count < KBD_BUF_SIZE:
|
|
buffer[buf_tail] = scancode
|
|
buf_tail = buf_tail + 1
|
|
if buf_tail >= KBD_BUF_SIZE:
|
|
buf_tail = 0
|
|
buf_count += 1
|
|
return 0
|
|
|
|
def read_scancode() -> uint8:
|
|
global buf_head, buf_count
|
|
asm.cli()
|
|
if buf_count == 0:
|
|
asm.sti()
|
|
return 0
|
|
code: uint8 = buffer[buf_head]
|
|
buf_head = buf_head + 1
|
|
if buf_head >= KBD_BUF_SIZE:
|
|
buf_head = 0
|
|
buf_count -= 1
|
|
asm.sti()
|
|
return code
|
|
|
|
def scancode_to_ascii(scancode: uint8) -> char:
|
|
code: uint8 = uint8(scancode & ~uint8(KEY_RELEASED))
|
|
if code >= 128: return '\0'
|
|
shifted: bool = (shift_l or shift_r) != 0
|
|
if caps_lock and code >= 0x10 and code <= 0x32:
|
|
shifted = not shifted
|
|
if shifted:
|
|
return scancode_set1_shift[code]
|
|
return scancode_set1[code]
|
|
|
|
def has_data() -> int:
|
|
return buf_count
|
|
|
|
def init():
|
|
global _kbd_inited
|
|
# 幂等:init() 有两个调用点(devfs 注册 /dev/input/keyboard、kbd_svc.start),
|
|
# 重复执行会再次发 0xAD 禁用键盘接口,而那次 CCB 写入往往失败(键盘数据串入
|
|
# CCB 读取,回读为 0)⇒ 0xAE 恢复不了 ⇒ 接口停在禁用态 ⇒ 按键不再产生任何字节
|
|
# (现场:ki 恒为 2/3,desktop 轮询永远看不到键盘字节,键盘完全无法输入)。
|
|
if _kbd_inited != 0:
|
|
return
|
|
_kbd_inited = 1
|
|
drain: uint32
|
|
drain = 0
|
|
while (asm.inb(KBD_STATUS) & 0x01) != 0:
|
|
asm.inb(KBD_DATA)
|
|
drain += 1
|
|
if drain > 1000: break
|
|
|
|
_wait_write()
|
|
asm.outb(KBD_COMMAND, 0xAD)
|
|
|
|
drain = 0
|
|
while (asm.inb(KBD_STATUS) & 0x01) != 0:
|
|
asm.inb(KBD_DATA)
|
|
drain += 1
|
|
if drain > 1000: break
|
|
|
|
_wait_write()
|
|
asm.outb(KBD_COMMAND, 0x20)
|
|
_wait_read()
|
|
ccb: uint8 = asm.inb(KBD_DATA)
|
|
|
|
ccb = ccb | 0x01
|
|
ccb = ccb | 0x40
|
|
ccb = ccb & 0xEF
|
|
|
|
_wait_write()
|
|
asm.outb(KBD_COMMAND, 0x60)
|
|
_wait_write()
|
|
asm.outb(KBD_DATA, ccb)
|
|
|
|
_wait_write()
|
|
asm.outb(KBD_COMMAND, 0xAE)
|
|
|
|
drain = 0
|
|
while (asm.inb(KBD_STATUS) & 0x01) != 0:
|
|
asm.inb(KBD_DATA)
|
|
drain += 1
|
|
if drain > 1000: break
|
|
|
|
_wait_write()
|
|
asm.outb(KBD_DATA, 0xF4)
|
|
_wait_read()
|
|
asm.inb(KBD_DATA)
|
|
|
|
idt.irqInstallHandler(1, irq_handler, "keyboard")
|