- 去掉不再需要的 import t, c - class → struct(纯数据聚合) - t.CArray[T, N] → [N]T - static 修饰符移到类型前 - @__attribute(packed) → @__attribute__(packed) - UHCI 中断回调参数改用 callable[[], int] - 同步 CPUID 注释中的旧类型名
480 lines
17 KiB
Plaintext
480 lines
17 KiB
Plaintext
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
|