- _wait_for_complete 原先只查 status TD 的 bit21|bit22,DATA 阶段的 CRC/超时(bit18)、bitstuff(bit17)、babble(bit20) 会被当成成功 ⇒ GET_DESCRIPTOR 拿到零字节缓冲区仍返回 0,最终表现为 [usb] device class=00 proto=00 ep=0、usb_kbd 找不到键盘(现场)。改为对齐 Linux usb-uhci.h:新增 TD_CTRL_ERR_MASK/NAK_MASK 与 _td_error(),control_transfer 收尾逐个校验 setup/data/status 三个 TD,失败时打印三个状态字。 - usb.vp 增加两条探针:[usb] devdesc blen/btype/maxpkt0 与 [usb] cfgdesc total/b0/t0/iface/proto/ep,用于区分数据未落盘与解析未命中。
347 lines
12 KiB
Plaintext
347 lines
12 KiB
Plaintext
import mm.mm as mm
|
||
import string
|
||
import drivers.serial.uart.serial as serial
|
||
import drivers.usb.uhci as uhci
|
||
import drivers.usb.pci as pci
|
||
import platform.pch.timer as timer
|
||
import viperlib
|
||
|
||
|
||
define USB_DESC_DEVICE = 1
|
||
define USB_DESC_CONFIG = 2
|
||
define USB_DESC_STRING = 3
|
||
define USB_DESC_INTERFACE = 4
|
||
define USB_DESC_ENDPOINT = 5
|
||
|
||
define USB_REQ_GET_STATUS = 0
|
||
define USB_REQ_CLEAR_FEATURE = 1
|
||
define USB_REQ_SET_FEATURE = 3
|
||
define USB_REQ_SET_ADDRESS = 5
|
||
define USB_REQ_GET_DESCRIPTOR = 6
|
||
define USB_REQ_SET_DESCRIPTOR = 7
|
||
define USB_REQ_GET_CONFIGURATION = 8
|
||
define USB_REQ_SET_CONFIGURATION = 9
|
||
|
||
define USB_REQ_SET_INTERFACE = 11
|
||
|
||
define HID_CLASS = 0x03
|
||
define HID_SUBCLASS_BOOT = 0x01
|
||
define HID_PROTO_KEYBOARD = 0x01
|
||
define HID_PROTO_MOUSE = 0x02
|
||
|
||
define HID_REQ_SET_PROTOCOL = 0x0B
|
||
define HID_REQ_SET_IDLE = 0x0A
|
||
define HID_REQ_GET_REPORT = 0x01
|
||
|
||
define USB_MAX_DEVICES = 8
|
||
|
||
class usb_dev_desc:
|
||
bLength: uint8
|
||
bDescriptorType: uint8
|
||
bcdUSB: uint16
|
||
bDeviceClass: uint8
|
||
bDeviceSubClass: uint8
|
||
bDeviceProtocol: uint8
|
||
bMaxPacketSize0: uint8
|
||
idVendor: uint16
|
||
idProduct: uint16
|
||
bcdDevice: uint16
|
||
iManufacturer: uint8
|
||
iProduct: uint8
|
||
iSerialNumber: uint8
|
||
bNumConfigurations: uint8
|
||
|
||
class usb_config_desc:
|
||
bLength: uint8
|
||
bDescriptorType: uint8
|
||
wTotalLength: uint16
|
||
bNumInterfaces: uint8
|
||
bConfigurationValue: uint8
|
||
iConfiguration: uint8
|
||
bmAttributes: uint8
|
||
bMaxPower: uint8
|
||
|
||
class usb_iface_desc:
|
||
bLength: uint8
|
||
bDescriptorType: uint8
|
||
bInterfaceNumber: uint8
|
||
bAlternateSetting: uint8
|
||
bNumEndpoints: uint8
|
||
bInterfaceClass: uint8
|
||
bInterfaceSubClass: uint8
|
||
bInterfaceProtocol: uint8
|
||
iInterface: uint8
|
||
|
||
class usb_ep_desc:
|
||
bLength: uint8
|
||
bDescriptorType: uint8
|
||
bEndpointAddress: uint8
|
||
bmAttributes: uint8
|
||
wMaxPacketSize: uint16
|
||
bInterval: uint8
|
||
|
||
class usb_setup_pkt:
|
||
bmRequestType: uint8
|
||
bRequest: uint8
|
||
wValue: uint16
|
||
wIndex: uint16
|
||
wLength: uint16
|
||
|
||
class usb_device:
|
||
addr: uint8
|
||
ls: int
|
||
max_pkt0: uint8
|
||
iface_class: uint8
|
||
iface_subclass: uint8
|
||
iface_protocol: uint8
|
||
iface_num: uint8
|
||
ep_in: uint8
|
||
ep_in_max: uint16
|
||
ep_in_interval: uint8
|
||
ep_in_toggle: uint8
|
||
configured: int
|
||
|
||
_devices: [USB_MAX_DEVICES]usb_device
|
||
_device_count: int = 0
|
||
|
||
def _setup_packet(bmRequestType: uint8, bRequest: uint8, wValue: uint16, wIndex: uint16, wLength: uint16, buf: usb_setup_pkt*):
|
||
buf.bmRequestType = bmRequestType
|
||
buf.bRequest = bRequest
|
||
buf.wValue = wValue
|
||
buf.wIndex = wIndex
|
||
buf.wLength = wLength
|
||
|
||
def _send_control(dev_addr: uint8, ls: int, max_pkt: uint8, setup: usb_setup_pkt*, data_buf: rawptr, data_len: uint16, data_in: int) -> int:
|
||
return uhci.control_transfer(dev_addr, 0, ls, max_pkt, setup, data_buf, data_len, data_in)
|
||
|
||
def get_descriptor(dev_addr: uint8, ls: int, max_pkt: uint8, desc_type: uint8, desc_idx: uint8, buf: rawptr, length: uint16) -> int:
|
||
setup: usb_setup_pkt
|
||
_setup_packet(0x80, USB_REQ_GET_DESCRIPTOR, (uint16(desc_type) << 8) | desc_idx, 0, length, &setup)
|
||
return _send_control(dev_addr, ls, max_pkt, &setup, buf, length, 1)
|
||
|
||
def set_address(dev_addr: uint8, ls: int, max_pkt: uint8, new_addr: uint8) -> int:
|
||
setup: usb_setup_pkt
|
||
_setup_packet(0x00, USB_REQ_SET_ADDRESS, new_addr, 0, 0, &setup)
|
||
return _send_control(dev_addr, ls, max_pkt, &setup, None, 0, 0)
|
||
|
||
def set_configuration(dev_addr: uint8, ls: int, max_pkt: uint8, config_val: uint8) -> int:
|
||
setup: usb_setup_pkt
|
||
_setup_packet(0x00, USB_REQ_SET_CONFIGURATION, config_val, 0, 0, &setup)
|
||
return _send_control(dev_addr, ls, max_pkt, &setup, None, 0, 0)
|
||
|
||
def hid_set_protocol(dev_addr: uint8, ls: int, max_pkt: uint8, iface: uint8, protocol: uint8) -> int:
|
||
setup: usb_setup_pkt
|
||
_setup_packet(0x21, HID_REQ_SET_PROTOCOL, protocol, uint16(iface), 0, &setup)
|
||
return _send_control(dev_addr, ls, max_pkt, &setup, None, 0, 0)
|
||
|
||
def hid_set_idle(dev_addr: uint8, ls: int, max_pkt: uint8, iface: uint8, duration: uint8) -> int:
|
||
setup: usb_setup_pkt
|
||
_setup_packet(0x21, HID_REQ_SET_IDLE, uint16(duration) << 8, uint16(iface), 0, &setup)
|
||
return _send_control(dev_addr, ls, max_pkt, &setup, None, 0, 0)
|
||
|
||
def _parse_config(dev: usb_device*, config_buf: rawptr, total_len: uint16):
|
||
offset: int = 0
|
||
found_hid: int = 0
|
||
while offset < int(total_len):
|
||
p: uint8* = config_buf + uint64(offset)
|
||
desc_len: uint8 = *p
|
||
p2: uint8* = config_buf + uint64(offset) + 1
|
||
desc_type: uint8 = *p2
|
||
if desc_len == 0: break
|
||
# 边界:描述符整体须落在 config_buf(total_len 字节)内,且不得短于
|
||
# 所访问的结构体(interface=9 / endpoint=7),否则越界读。
|
||
if offset + int(desc_len) > int(total_len): break
|
||
if desc_type == USB_DESC_INTERFACE and int(desc_len) >= 9:
|
||
iface: usb_iface_desc* = usb_iface_desc*(config_buf + uint64(offset))
|
||
if iface.bInterfaceClass == HID_CLASS:
|
||
found_hid = 1
|
||
dev.iface_class = HID_CLASS
|
||
dev.iface_subclass = iface.bInterfaceSubClass
|
||
dev.iface_protocol = iface.bInterfaceProtocol
|
||
dev.iface_num = iface.bInterfaceNumber
|
||
if desc_type == USB_DESC_ENDPOINT and found_hid and int(desc_len) >= 7:
|
||
ep: usb_ep_desc* = usb_ep_desc*(config_buf + uint64(offset))
|
||
if (ep.bEndpointAddress & 0x80) != 0:
|
||
dev.ep_in = ep.bEndpointAddress & 0x0F
|
||
dev.ep_in_max = ep.wMaxPacketSize
|
||
dev.ep_in_interval = ep.bInterval
|
||
found_hid = 0
|
||
offset = offset + int(desc_len)
|
||
|
||
def enumerate_port(port: int) -> int:
|
||
global _device_count
|
||
if _device_count >= USB_MAX_DEVICES: return -1
|
||
|
||
ls: int = uhci.port_is_low_speed(port)
|
||
|
||
reset_ok: int = -1
|
||
for reset_retry in range(3):
|
||
if uhci.port_reset(port) == 0:
|
||
reset_ok = 0
|
||
break
|
||
timer.timer_msleep(50)
|
||
if reset_ok != 0:
|
||
return -2
|
||
|
||
timer.timer_msleep(10)
|
||
|
||
dev_buf: rawptr = mm.malloc(usb_dev_desc.__sizeof__())
|
||
if dev_buf is None: return -3
|
||
string.memset(dev_buf, 0, usb_dev_desc.__sizeof__())
|
||
|
||
result: int = get_descriptor(0, ls, 8, USB_DESC_DEVICE, 0, dev_buf, 8)
|
||
retry: int
|
||
for retry in range(3):
|
||
if result == 0: break
|
||
timer.timer_msleep(50)
|
||
uhci.port_reset(port)
|
||
timer.timer_msleep(10)
|
||
result = get_descriptor(0, ls, 8, USB_DESC_DEVICE, 0, dev_buf, 8)
|
||
if result != 0:
|
||
mm.free(dev_buf)
|
||
return -4
|
||
|
||
desc: usb_dev_desc* = dev_buf
|
||
max_pkt: uint8 = desc.bMaxPacketSize0
|
||
if max_pkt < 8: max_pkt = 8
|
||
# 探针:设备描述符只要读回 18/8 字节,bLength 必为 18、bDescriptorType 必为 1。
|
||
# 全 0 ⇒ 控制传输的 DATA 阶段没落盘(而非解析问题)。
|
||
_db0: [96]char
|
||
viperlib.snprintf(&_db0, 96, "[usb] devdesc blen=%d btype=%d maxpkt0=%d\n",
|
||
int(desc.bLength), int(desc.bDescriptorType), int(desc.bMaxPacketSize0))
|
||
serial.puts(&_db0)
|
||
|
||
new_addr: uint8 = uint8(_device_count + 1)
|
||
result = set_address(0, ls, max_pkt, new_addr)
|
||
for retry in range(3):
|
||
if result == 0: break
|
||
timer.timer_msleep(20)
|
||
result = set_address(0, ls, max_pkt, new_addr)
|
||
if result != 0:
|
||
mm.free(dev_buf)
|
||
return -5
|
||
|
||
timer.timer_msleep(10)
|
||
|
||
dev: usb_device* = &_devices[_device_count]
|
||
dev.addr = new_addr
|
||
dev.ls = ls
|
||
dev.max_pkt0 = max_pkt
|
||
dev.configured = 0
|
||
dev.iface_class = 0
|
||
dev.iface_subclass = 0
|
||
dev.iface_protocol = 0
|
||
dev.ep_in = 0
|
||
dev.ep_in_max = 0
|
||
dev.ep_in_interval = 0
|
||
dev.ep_in_toggle = 0
|
||
|
||
config_buf: rawptr = mm.malloc(256)
|
||
if config_buf is None:
|
||
mm.free(dev_buf)
|
||
return -6
|
||
string.memset(config_buf, 0, 256)
|
||
|
||
result = get_descriptor(new_addr, ls, max_pkt, USB_DESC_CONFIG, 0, config_buf, 9)
|
||
for retry in range(3):
|
||
if result == 0: break
|
||
timer.timer_msleep(50)
|
||
result = get_descriptor(new_addr, ls, max_pkt, USB_DESC_CONFIG, 0, config_buf, 9)
|
||
if result != 0:
|
||
mm.free(config_buf)
|
||
mm.free(dev_buf)
|
||
return -7
|
||
|
||
cfg: usb_config_desc* = config_buf
|
||
total_len: uint16 = cfg.wTotalLength
|
||
if total_len > 256: total_len = 256
|
||
|
||
result = get_descriptor(new_addr, ls, max_pkt, USB_DESC_CONFIG, 0, config_buf, total_len)
|
||
for retry in range(3):
|
||
if result == 0: break
|
||
timer.timer_msleep(50)
|
||
result = get_descriptor(new_addr, ls, max_pkt, USB_DESC_CONFIG, 0, config_buf, total_len)
|
||
if result != 0:
|
||
mm.free(config_buf)
|
||
mm.free(dev_buf)
|
||
return -8
|
||
|
||
_parse_config(dev, config_buf, total_len)
|
||
|
||
# 探针:配置描述符读回后,wTotalLength 应为 0x22/0x23、首字节应为 09 02(bLength=9, CONFIG)。
|
||
# total=0 或首字节为 0 ⇒ DATA 阶段没传回来;有内容但 iface 仍为 0 ⇒ 解析/布局问题。
|
||
_cb: uint8* = config_buf
|
||
_db1: [128]char
|
||
viperlib.snprintf(&_db1, 128, "[usb] cfgdesc total=%d b0=%02X t0=%02X iface=%02X proto=%02X ep=%d\n",
|
||
int(total_len), int(_cb[0]), int(_cb[1]),
|
||
int(dev.iface_class), int(dev.iface_protocol), int(dev.ep_in))
|
||
serial.puts(&_db1)
|
||
|
||
result = set_configuration(new_addr, ls, max_pkt, cfg.bConfigurationValue)
|
||
if result != 0:
|
||
mm.free(config_buf)
|
||
mm.free(dev_buf)
|
||
return -9
|
||
|
||
dev.configured = 1
|
||
|
||
if dev.iface_class == HID_CLASS:
|
||
hid_set_protocol(new_addr, ls, max_pkt, dev.iface_num, 0)
|
||
hid_set_idle(new_addr, ls, max_pkt, dev.iface_num, 0)
|
||
|
||
_device_count = _device_count + 1
|
||
mm.free(config_buf)
|
||
mm.free(dev_buf)
|
||
return int(new_addr)
|
||
|
||
def init() -> int:
|
||
global _device_count
|
||
_device_count = 0
|
||
|
||
io_base: uint16 = pci.find_uhci()
|
||
if io_base == 0:
|
||
serial.puts("[usb] UHCI controller not found\n")
|
||
return -1
|
||
|
||
buf: [64]char
|
||
viperlib.snprintf(&buf, 64, "[usb] UHCI at I/O 0x%04X\n", uint32(io_base))
|
||
serial.puts(buf)
|
||
|
||
if uhci.init(io_base) != 0:
|
||
serial.puts("[usb] UHCI init failed\n")
|
||
return -2
|
||
|
||
serial.puts("[usb] UHCI initialized\n")
|
||
|
||
timer.timer_msleep(50)
|
||
|
||
for port in range(2):
|
||
if uhci.port_is_connected(port):
|
||
viperlib.snprintf(&buf, 64, "[usb] port %d connected\n", uint32(port))
|
||
serial.puts(buf)
|
||
dev_id: int = enumerate_port(port)
|
||
if dev_id > 0:
|
||
dev: usb_device* = &_devices[dev_id - 1]
|
||
viperlib.snprintf(&buf, 64, "[usb] device addr=%d class=%02X proto=%02X ep=%d\n",
|
||
uint32(dev.addr), uint32(dev.iface_class),
|
||
uint32(dev.iface_protocol), uint32(dev.ep_in))
|
||
serial.puts(buf)
|
||
else:
|
||
viperlib.snprintf(&buf, 64, "[usb] enumerate port %d failed: %d\n", uint32(port), int32(dev_id))
|
||
serial.puts(buf)
|
||
|
||
return _device_count
|
||
|
||
def find_hid_device(protocol: uint8) -> usb_device*:
|
||
i: int
|
||
for i in range(_device_count):
|
||
dev: usb_device* = &_devices[i]
|
||
if dev.iface_class == HID_CLASS:
|
||
if dev.iface_protocol == protocol:
|
||
if dev.configured != 0:
|
||
return dev
|
||
return None
|
||
|
||
def read_interrupt(dev: usb_device*, buf: rawptr) -> int:
|
||
return 0
|