Files
TermiNexus 3583f78efc refactor(kernel): 适配 ViperC 新语法
- 去掉不再需要的 import t, c
- class → struct(纯数据聚合)
- t.CArray[T, N] → [N]T
- static 修饰符移到类型前
- @__attribute(packed) → @__attribute__(packed)
- UHCI 中断回调参数改用 callable[[], int]
- 同步 CPUID 注释中的旧类型名
2026-10-26 22:39:56 +08:00

480 lines
17 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asm
import mm.mm as mm
import string
import drivers.serial.uart.serial as serial
import platform.pch.timer as timer
import intr.idt as idt
import platform.pch.pic as pic
import drivers.usb.pci as pci
import viperlib
define UHCI_USBCMD = 0x00
define UHCI_USBSTS = 0x02
define UHCI_USBINTR = 0x04
define UHCI_FRNUM = 0x06
define UHCI_FLBASEADD = 0x08
define UHCI_SOFMOD = 0x0C
define UHCI_PORTSC1 = 0x10
define UHCI_PORTSC2 = 0x12
define UHCI_CMD_RUN = 0x0001
define UHCI_CMD_HCRESET = 0x0002
define UHCI_CMD_GRESET = 0x0004
define UHCI_CMD_MAXPKT = 0x0080
define UHCI_CMD_CF = 0x0040
define UHCI_STS_HCHALTED = 0x0020
define UHCI_STS_USBINT = 0x0001
define UHCI_STS_USBERRINT = 0x0002
define UHCI_STS_RESUME = 0x0004
define UHCI_INTR_TIMEOUT = 0x0001
define UHCI_INTR_RESUME = 0x0002
define UHCI_INTR_IOC = 0x0004
define UHCI_INTR_SHORT = 0x0008
define UHCI_PORT_CONNECT = 0x0001
define UHCI_PORT_CONNECT_CHANGE = 0x0002
define UHCI_PORT_ENABLE = 0x0004
define UHCI_PORT_ENABLE_CHANGE = 0x0008
define UHCI_PORT_RESET = 0x0200
define UHCI_PORT_LOW_SPEED = 0x0100
define TD_CTRL_ACTIVE = 0x00800000
define TD_CTRL_IOC = 0x01000000
define TD_CTRL_LS = 0x04000000
define TD_CTRL_SPD = 0x20000000
define TD_CTRL_CERR_SHIFT = 27
define TD_CTRL_CERR_MASK = 0x18000000
# TD 状态字里的错误位(对照 Linux usb-uhci.h 的 TD_CTRL_ANY_ERROR / uhci_status_bits):
# bit17 Bitstuff / bit18 CRC-Timeout / bit20 Babble / bit21 DataBufErr / bit22 Stalled
# 原先只查 bit21|bit22,DATA 阶段若有 CRC/超时错误会被当成"成功但零字节",
# 于是 config 描述符读回来全 0 → iface_class=0 → usb_kbd 永远找不到键盘(现场)。
define TD_CTRL_ERR_MASK = 0x00760000
# bit19 NAK:C_ERR(3) 用尽后仍被 NAK ⇒ 设备一直不给数据
define TD_CTRL_NAK_MASK = 0x00080000
# Actual Length(bit10:0,编码为 n-1):IN 传输"短读"时错误位可能全 0(QEMU/硬件不把短包
# 当错误),只查错误位会漏 ⇒ 必须比对实际字节数(现场:USB 鼠标的 25 字节配置描述符只落下
# 前 9 字节,iface 解析为 0 ⇒ usb_mse 找不到鼠标)。
define TD_CTRL_ACTLEN_MASK = 0x000007FF
define TD_TOKEN_SETUP = 0x2D
define TD_TOKEN_IN = 0x69
define TD_TOKEN_OUT = 0xE1
define TD_TOKEN_DATA0 = 0
define TD_TOKEN_DATA1 = 1
define TD_LINK_TERMINATE = 0x00000001
define TD_LINK_QH = 0x00000002
define TD_LINK_DEPTH_FIRST = 0x00000004
define UHCI_NUM_FRAMES = 1024
define UHCI_INT_SLOTS = 4
class uhci_td:
link: uint32
ctrl_status: uint32
token: uint32
buffer: uint32
class uhci_qh:
link: uint32
element: uint32
class int_slot:
td: uhci_td*
callback: int*
active: int
qh: uhci_qh*
_io_base: uint16 = 0
_frame_list: uint32* = None
_ctrl_qh: uhci_qh* = None
_bulk_qh: uhci_qh* = None
_irq: int = -1
_int_slots: [UHCI_INT_SLOTS]int_slot
_int_slot_count: int = 0
def _reg_read16(offset: uint16) -> uint16:
return asm.inw(_io_base + offset)
def _reg_write16(offset: uint16, value: uint16):
asm.outw(_io_base + offset, value)
def _reg_read32(offset: uint16) -> uint32:
return asm.inl(_io_base + offset)
def _reg_write32(offset: uint16, value: uint32):
asm.outl(_io_base + offset, value)
def _uhci_irq_handler() -> int:
sts: uint16 = _reg_read16(UHCI_USBSTS)
_reg_write16(UHCI_USBSTS, sts)
if (sts & (UHCI_STS_USBINT | UHCI_STS_USBERRINT)) == 0:
return 0
for si in range(_int_slot_count):
slot: int_slot* = &_int_slots[si]
if slot.active and slot.td is not None and slot.callback is not None:
status: uint32 = slot.td.ctrl_status
if (status & TD_CTRL_ACTIVE) == 0:
slot.active = 0
__asm__(f"""mov rbp, rsp
and rsp, -16
call {asminp(slot.callback, ASM_DESCR.REG_ANY)}
mov rsp, rbp""",
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
ASM_DESCR.CLOBBER_RBX,
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX,
ASM_DESCR.CLOBBER_RSI, ASM_DESCR.CLOBBER_RDI,
ASM_DESCR.CLOBBER_R8, ASM_DESCR.CLOBBER_R9,
ASM_DESCR.CLOBBER_R10, ASM_DESCR.CLOBBER_R11,
ASM_DESCR.CLOBBER_RBP])
return 0
def register_int_callback(cb: callable[[], int]) -> int:
global _int_slot_count
if _int_slot_count >= UHCI_INT_SLOTS:
return -1
si: int = _int_slot_count
qh: uhci_qh* = mm.malloc_direct(uhci_qh.__sizeof__())
if qh is None:
return -2
string.memset(qh, 0, uhci_qh.__sizeof__())
_qh_set_link(qh, 0, 0, 1)
_qh_set_element(qh, 0, 1)
td: uhci_td* = mm.malloc_direct(uhci_td.__sizeof__())
if td is None:
return -3
string.memset(td, 0, uhci_td.__sizeof__())
slot: int_slot* = &_int_slots[si]
slot.td = td
slot.callback = cb
slot.active = 0
slot.qh = qh
_int_slot_count = si + 1
_rebuild_int_chain()
return si
def schedule_int_transfer(slot_id: int, dev_addr: uint8, endpoint: uint8, ls: int, max_pkt: uint8, toggle: uint8, buf: rawptr):
if slot_id < 0 or slot_id >= _int_slot_count:
return
slot: int_slot* = &_int_slots[slot_id]
td: uhci_td* = slot.td
qh: uhci_qh* = slot.qh
if td is None or qh is None:
return
string.memset(td, 0, uhci_td.__sizeof__())
_td_set_link(td, 0, 0, 1)
_td_set_ctrl(td, ls, max_pkt)
_td_set_token(td, TD_TOKEN_IN, dev_addr, endpoint, toggle, max_pkt)
td.ctrl_status = td.ctrl_status | TD_CTRL_IOC
td.buffer = uint32(uint64(buf))
_qh_set_element(qh, uint32(uint64(td)), 0)
slot.active = 1
def _rebuild_int_chain():
if _int_slot_count == 0:
return
first_qh: uhci_qh* = None
prev_qh: uhci_qh* = None
for si in range(_int_slot_count):
cur_qh: uhci_qh* = _int_slots[si].qh
if cur_qh is not None:
if first_qh is None:
first_qh = cur_qh
if prev_qh is not None:
_qh_set_link(prev_qh, uint32(uint64(cur_qh)) | TD_LINK_QH, 1, 0)
prev_qh = cur_qh
if prev_qh is not None:
_qh_set_link(prev_qh, uint32(uint64(_ctrl_qh)) | TD_LINK_QH, 1, 0)
if first_qh is not None:
qh_addr: uint32 = uint32(uint64(first_qh)) | TD_LINK_QH
i: int
for i in range(UHCI_NUM_FRAMES):
# 注意:`uint32* + n` 的指针算术**已按元素(4 字节)缩放**,不要再乘 4 ——
# 之前写成 `_frame_list + uint64(i) * 4`,实际步长 16 字节,1024 次循环写到 16KB,
# 而缓冲只有 UHCI_NUM_FRAMES*4 = 4KB ⇒ 越界 12KB 直接砸坏 buddy 空闲块头
# (现场:[0E][PF] cr2=0xba4c000 err=0x3 rip=0x10e598 rax=0x3000,rax 正是 12288)。
entry_ptr: uint32* = _frame_list + uint64(i)
*entry_ptr = qh_addr
def _td_set_link(td: uhci_td*, addr: uint32, is_qh: int, terminate: int):
val: uint32 = (addr & 0xFFFFFFF0)
if is_qh: val = val | TD_LINK_QH
if terminate: val = val | TD_LINK_TERMINATE
td.link = val
def _td_set_token(td: uhci_td*, pid: uint8, dev_addr: uint8, endpoint: uint8, toggle: uint8, max_len: uint16):
len_bits: uint32
if max_len == 0:
len_bits = 0x7FF
else:
len_bits = uint32(max_len) - 1
td.token = (uint32(pid) << 0) | (uint32(dev_addr) << 8) | (uint32(endpoint & 0x0F) << 15) | (uint32(toggle & 1) << 19) | (len_bits << 21)
def _td_set_ctrl(td: uhci_td*, ls: int, maxlen: uint16):
td.ctrl_status = TD_CTRL_ACTIVE | (3 << TD_CTRL_CERR_SHIFT)
if ls: td.ctrl_status = td.ctrl_status | TD_CTRL_LS
def _qh_set_link(qh: uhci_qh*, addr: uint32, is_qh: int, terminate: int):
val: uint32 = (addr & 0xFFFFFFF0)
if is_qh: val = val | TD_LINK_QH
if terminate: val = val | TD_LINK_TERMINATE
qh.link = val
def _qh_set_element(qh: uhci_qh*, addr: uint32, terminate: int):
val: uint32 = (addr & 0xFFFFFFF0)
if terminate: val = val | TD_LINK_TERMINATE
qh.element = val
def _wait_for_complete(td: uhci_td*, timeout_ms: int) -> int:
start: uint64 = timer.timer_get_ticks()
deadline: uint64 = start + uint64(timeout_ms)
poll_cnt: int = 0
while timer.timer_get_ticks() < deadline:
status: uint32 = td.ctrl_status
if (status & TD_CTRL_ACTIVE) == 0:
if (status & TD_CTRL_ERR_MASK) != 0:
return -1
if (status & TD_CTRL_NAK_MASK) != 0:
return -3
return 0
poll_cnt += 1
if poll_cnt >= 100:
poll_cnt = 0
asm.sti()
asm.hlt()
return -2
# 单个 TD 的完成判定(fail-fast):0=正常 1=仍在 ACTIVE 2=错误位 3=NAK
def _td_error(td: uhci_td*) -> int:
st: uint32 = td.ctrl_status
if (st & TD_CTRL_ACTIVE) != 0: return 1
if (st & TD_CTRL_ERR_MASK) != 0: return 2
if (st & TD_CTRL_NAK_MASK) != 0: return 3
return 0
def reset(io_base: uint16) -> int:
global _io_base
_io_base = io_base
_reg_write16(UHCI_USBCMD, UHCI_CMD_HCRESET)
for i in range(1000):
if (_reg_read16(UHCI_USBCMD) & UHCI_CMD_HCRESET) == 0:
break
_reg_write16(UHCI_USBSTS, 0xFFFF)
_reg_write16(UHCI_USBINTR, 0x0000)
return 0
def init(io_base: uint16) -> int:
global _irq, _io_base, _frame_list, _ctrl_qh, _bulk_qh, _int_slot_count
_io_base = io_base
_int_slot_count = 0
serial.puts("[uhci] resetting...\n")
if reset(io_base) != 0:
serial.puts("[uhci] reset failed\n")
return -1
serial.puts("[uhci] allocating frame list...\n")
_frame_list = mm.malloc(UHCI_NUM_FRAMES * 4)
if _frame_list is None:
serial.puts("[uhci] frame list alloc failed\n")
return -2
string.memset(_frame_list, 0, UHCI_NUM_FRAMES * 4)
_ctrl_qh = mm.malloc_direct(uhci_qh.__sizeof__())
if _ctrl_qh is None:
serial.puts("[uhci] ctrl_qh alloc failed\n")
return -3
string.memset(_ctrl_qh, 0, uhci_qh.__sizeof__())
_qh_set_link(_ctrl_qh, 0, 0, 1)
_qh_set_element(_ctrl_qh, 0, 1)
_bulk_qh = mm.malloc_direct(uhci_qh.__sizeof__())
if _bulk_qh is None:
serial.puts("[uhci] bulk_qh alloc failed\n")
return -4
string.memset(_bulk_qh, 0, uhci_qh.__sizeof__())
_qh_set_link(_bulk_qh, 0, 0, 1)
_qh_set_element(_bulk_qh, 0, 1)
serial.puts("[uhci] setting up frame list...\n")
qh_addr: uint32 = uint32(_ctrl_qh) | TD_LINK_QH
for i in range(UHCI_NUM_FRAMES):
# 同上:不要乘 4(`uint32* + n` 已按 4 字节缩放)。两处都曾以 16 字节步长越界 12KB。
entry_ptr: uint32* = _frame_list + uint64(i)
*entry_ptr = qh_addr
_reg_write32(UHCI_FLBASEADD, uint32(uint64(_frame_list)))
_reg_write16(UHCI_FRNUM, 0)
_reg_write16(UHCI_SOFMOD, 64)
_reg_write16(UHCI_USBCMD, UHCI_CMD_CF | UHCI_CMD_MAXPKT | UHCI_CMD_RUN)
timer.timer_msleep(10)
_reg_write16(UHCI_USBSTS, 0xFFFF)
_reg_write16(UHCI_USBINTR, UHCI_INTR_IOC | UHCI_INTR_SHORT)
_irq = int(pci._uhci_dev.irq)
if _irq > 0 and _irq < 16:
idt.irqInstallHandler(_irq, _uhci_irq_handler, "uhci")
pic.clearMask(uint8(_irq))
serial.puts("[uhci] started\n")
return 0
def port_reset(port: int) -> int:
sc: uint16
if port == 0:
sc = _reg_read16(UHCI_PORTSC1)
else:
sc = _reg_read16(UHCI_PORTSC2)
if (sc & UHCI_PORT_CONNECT) == 0:
return -1
if port == 0:
_reg_write16(UHCI_PORTSC1, sc | UHCI_PORT_CONNECT_CHANGE | UHCI_PORT_ENABLE_CHANGE)
else:
_reg_write16(UHCI_PORTSC2, sc | UHCI_PORT_CONNECT_CHANGE | UHCI_PORT_ENABLE_CHANGE)
if port == 0:
_reg_write16(UHCI_PORTSC1, UHCI_PORT_RESET)
else:
_reg_write16(UHCI_PORTSC2, UHCI_PORT_RESET)
timer.timer_msleep(50)
if port == 0:
_reg_write16(UHCI_PORTSC1, 0x0000)
else:
_reg_write16(UHCI_PORTSC2, 0x0000)
timer.timer_msleep(10)
if port == 0:
sc = _reg_read16(UHCI_PORTSC1)
_reg_write16(UHCI_PORTSC1, sc | UHCI_PORT_ENABLE | UHCI_PORT_CONNECT_CHANGE | UHCI_PORT_ENABLE_CHANGE)
else:
sc = _reg_read16(UHCI_PORTSC2)
_reg_write16(UHCI_PORTSC2, sc | UHCI_PORT_ENABLE | UHCI_PORT_CONNECT_CHANGE | UHCI_PORT_ENABLE_CHANGE)
timer.timer_msleep(10)
if port == 0:
sc = _reg_read16(UHCI_PORTSC1)
else:
sc = _reg_read16(UHCI_PORTSC2)
if (sc & UHCI_PORT_ENABLE) == 0:
return -2
return 0
def port_is_connected(port: int) -> int:
sc: uint16
if port == 0:
sc = _reg_read16(UHCI_PORTSC1)
else:
sc = _reg_read16(UHCI_PORTSC2)
if sc & UHCI_PORT_CONNECT: return 1
return 0
def port_is_low_speed(port: int) -> int:
sc: uint16
if port == 0:
sc = _reg_read16(UHCI_PORTSC1)
else:
sc = _reg_read16(UHCI_PORTSC2)
if sc & UHCI_PORT_LOW_SPEED: return 1
return 0
def control_transfer(dev_addr: uint8, endpoint: uint8, ls: int, max_pkt: uint8, setup_buf: rawptr, data_buf: rawptr, data_len: uint16, data_in: int) -> int:
td_count: int = 2
if data_len > 0:
td_count = td_count + 1
total: uint64 = uint64(td_count) * uhci_td.__sizeof__()
tds: uhci_td* = mm.malloc_direct(total)
if tds is None: return -1
string.memset(tds, 0, total)
setup_td: uhci_td* = tds
_td_set_link(setup_td, uint32(uint64(tds) + uhci_td.__sizeof__()), 0, 0)
_td_set_ctrl(setup_td, ls, 8)
_td_set_token(setup_td, TD_TOKEN_SETUP, dev_addr, endpoint, TD_TOKEN_DATA0, 8)
setup_td.buffer = uint32(uint64(setup_buf))
# data/status 两个 TD 事先声明:收尾时要逐个校验完成状态(见下方 fail-fast 段)
data_td: uhci_td* = None
status_td: uhci_td* = None
if data_len > 0:
data_td = uhci_td*(uint64(tds) + uhci_td.__sizeof__())
_td_set_link(data_td, uint32(uint64(data_td) + uhci_td.__sizeof__()), 0, 0)
_td_set_ctrl(data_td, ls, max_pkt)
if data_in:
_td_set_token(data_td, TD_TOKEN_IN, dev_addr, endpoint, TD_TOKEN_DATA1, data_len)
else:
_td_set_token(data_td, TD_TOKEN_OUT, dev_addr, endpoint, TD_TOKEN_DATA1, data_len)
data_td.buffer = uint32(uint64(data_buf))
status_td = uhci_td*(uint64(data_td) + uhci_td.__sizeof__())
_td_set_link(status_td, 0, 0, 1)
_td_set_ctrl(status_td, ls, max_pkt)
if data_in:
_td_set_token(status_td, TD_TOKEN_OUT, dev_addr, endpoint, TD_TOKEN_DATA1, 0)
else:
_td_set_token(status_td, TD_TOKEN_IN, dev_addr, endpoint, TD_TOKEN_DATA1, 0)
status_td.buffer = 0
else:
status_td = uhci_td*(uint64(tds) + uhci_td.__sizeof__())
_td_set_link(status_td, 0, 0, 1)
_td_set_ctrl(status_td, ls, max_pkt)
_td_set_token(status_td, TD_TOKEN_IN, dev_addr, endpoint, TD_TOKEN_DATA1, 0)
status_td.buffer = 0
_qh_set_element(_ctrl_qh, uint32(uint64(setup_td)), 0)
result: int = _wait_for_complete(status_td, 500)
_qh_set_element(_ctrl_qh, 0, 1)
# fail-fast:不只等 status TD —— DATA 阶段(以及 setup/status)的完成状态逐个校验。
# 只等 status TD 会漏掉"DATA 阶段 CRC/超时但 status 正常"的情形 ⇒ 返回 0 却零字节,
# 调用方(GET_DESCRIPTOR)拿到空缓冲区继续走,最终表现为 iface_class=0(现场)。
fail: int = 0
which: int = 0
st_setup: uint32 = setup_td.ctrl_status
st_data: uint32 = 0
st_status: uint32 = status_td.ctrl_status
e_setup: int = _td_error(setup_td)
if e_setup != 0:
fail = 0 - 10 - e_setup
which = 0
if data_td is not None:
st_data = data_td.ctrl_status
e_data: int = _td_error(data_td)
if e_data != 0:
fail = 0 - 20 - e_data
which = 1
elif data_in != 0 and ((st_data & TD_CTRL_ACTLEN_MASK) + 1) < uint32(data_len):
# 短读:实际字节数少于请求(ACTLEN 编码为 n-1)。错误位可能全 0,不能当成功。
fail = 0 - 20 - 4
which = 1
e_status: int = _td_error(status_td)
if e_status != 0:
fail = 0 - 30 - e_status
which = 2
if fail != 0:
_db: [128]char
viperlib.snprintf(&_db, 128, "[uhci] ctrl fail dev=%d ep=%d which=%d setup=0x%x data=0x%x status=0x%x\n",
uint32(dev_addr), uint32(endpoint), uint32(which),
uint32(st_setup), uint32(st_data), uint32(st_status))
serial.puts(&_db)
mm.free(tds)
if fail != 0:
return fail
return result