2. UHCI寄存器空间布局:I/O基地址获取与关键寄存器分类

UHCI(Universal Host Controller Interface)的寄存器空间全部映射到系统的I/O地址空间,而非内存映射空间。这意味着驱动程序必须通过PCI配置空间获取I/O基地址,再通过偏移地址访问各个功能寄存器。本章将详细拆解这一布局过程,并对关键寄存器按功能分类,为后续的初始化编程打下坚实基础。

2.1 I/O基地址获取:PCI配置空间解析

每个PCI设备(包括UHCI控制器)在PCI配置空间中包含6个Base Address Register(BAR),用于声明设备所需的地址空间类型和大小。UHCI控制器固定使用BAR0(偏移0x20)来存放I/O基地址。

关键点:UHCI只使用I/O空间,因此BAR0的最低bit(bit 0)必须为1(表示I/O空间),驱动程序读取BAR0后需屏蔽低2位(I/O对齐要求)得到真正的基地址。

获取I/O基地址的标准步骤如下:

  1. 通过PCI总线枚举找到UHCI控制器的Bus/Device/Function号。
  2. 向PCI配置空间地址端口(0xCF8)写入目标设备的配置空间偏移0x20(BAR0)。
  3. 从PCI配置空间数据端口(0xCFC)读取BAR0的值。
  4. 检查bit 0是否为1,确认是I/O空间。
  5. 将BAR0值与0xFFFC进行按位与,得到32位I/O基地址(高16位通常为0)。
// 伪代码:读取UHCI I/O基地址
uint32_t pci_read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);

uint8_t bus = 0;   // 示例值,实际需枚举
uint8_t dev = 29;  // 示例值
uint8_t func = 0;
uint32_t bar0 = pci_read_config_dword(bus, dev, func, 0x20);

if (bar0 & 0x01) {  // 检查I/O空间标志
    uint16_t io_base = (uint16_t)(bar0 & 0xFFFC);
    // io_base 即为UHCI控制器的I/O基地址
} else {
    // 错误:该BAR不是I/O空间
}
实战提示:在x86平台上,I/O地址空间只有16位宽(0x0000-0xFFFF),因此BAR0的高16位通常为0。但标准实现中仍建议读取完整的32位并屏蔽低2位,以保证跨平台兼容性。

2.2 寄存器偏移地址映射

UHCI控制器内部寄存器以I/O基地址为起点,通过固定的偏移量访问。每个寄存器宽度为8位、16位或32位,驱动程序必须使用正确的访问宽度,否则会导致未定义行为。

下表列出了UHCI所有寄存器的偏移地址、宽度和访问类型:

偏移地址 寄存器名称 宽度 访问类型 功能简述
0x00 USBCMD 16位 R/W 主机控制器命令寄存器
0x02 USBSTS 16位 R/WC 主机控制器状态寄存器
0x04 USBINTR 16位 R/W 中断使能寄存器
0x06 FRNUM 16位 R/W 帧号寄存器
0x08 FLBASEADD 32位 R/W 帧列表基地址寄存器
0x0C SOFMOD 8位 R/W 帧起始调节寄存器
0x10-0x1F PORTSC1-PORTSC2 16位 R/W 端口状态与控制寄存器(每端口16位)
注意:FLBASEADD(帧列表基地址)是UHCI中唯一的32位寄存器,必须使用32位I/O指令(如inl/outl)访问。使用16位或8位访问会导致高16位数据丢失,造成帧列表寻址错误。

2.3 关键寄存器分类详解

根据功能,UHCI寄存器可分为五大类。下面通过SVG结构图展示分类关系,再逐一说明每个类别的核心寄存器。

UHCI寄存器分类架构图 UHCI寄存器功能分类架构 UHCI寄存器空间 命令类 USBCMD 状态类 USBSTS 中断类 USBINTR 帧列表类 FRNUM/FLBASEADD 端口类 PORTSC 寄存器分类说明 命令类控制主机状态;状态类反映运行/错误;中断类管理中断触发;帧列表类维护USB帧调度;端口类管理物理端口连接 RS/HCRD/GRESET HCHalted/HCR/HSE IOC/RS/CRC/Timeout 帧号/基地址/调节 CCS/CSC/PED

2.4 命令类寄存器(USBCMD)

USBCMD(USB Command Register)位于偏移0x00,是UHCI初始化的核心控制寄存器。驱动程序通过写入该寄存器启动/停止主机控制器、发起全局复位、设置帧列表大小等。

USBCMD关键位域说明:

位域 名称 描述
bit 0 RS(Run/Stop) 1=运行,0=停止。初始化最后一步置1启动调度
bit 1 HCRESET(Host Controller Reset) 写1触发软件复位,复位后硬件自动清0
bit 2 GRESET(Global Reset) 写1触发全局复位(复位所有端口),持续至少50ms
bit 3 EGSM(Enter Global Suspend Mode) 进入全局挂起模式(通常用于电源管理)
bit 4 FGR(Force Global Resume) 强制全局恢复(从挂起唤醒)
bit 5 SWDBG(Software Debug) 软件调试模式使能(可选实现)
bit 6 CF(Configure Flag) 配置标志,驱动程序完成初始化后置1
bit 7 MAXP(Max Packet) 0=64字节,1=32字节(控制端点最大包大小)
初始化顺序:先写HCRESET复位控制器,等待USBSTS中的HCHalted位变为1;然后配置FLBASEADD和FRNUM;最后置RS=1启动运行。CF位应在所有配置完成后置1。

2.5 状态类寄存器(USBSTS)

USBSTS(USB Status Register)位于偏移0x02,反映主机控制器的当前状态和错误条件。驱动程序通过轮询或中断读取该寄存器判断运行状态。

USBSTS关键位域:

位域 名称 描述
bit 0 UI(USB Interrupt) USB中断挂起,由完成描述符或错误触发
bit 1 RI(Resume Interrupt) 恢复中断挂起,检测到远程唤醒信号
bit 2 HCHalted 主机控制器已停止(复位后或RS=0时置1)
bit 3 HCR(Host Controller Reset) 主机控制器复位进行中(写HCRESET后硬件置1,完成后清0)
bit 4 HSE(Host System Error) 主机系统错误(PCI总线错误等严重故障)
bit 5 FIFOERR FIFO错误(内部缓冲区溢出/下溢)
bit 6 CRC/TimeOut CRC校验错误或总线超时
重要:USBSTS中的错误位(HSE、FIFOERR、CRC/TimeOut)是写1清0(W1C)机制。驱动程序在读取后必须写1清除对应位,否则中断会持续触发。初始化阶段应清除所有遗留状态位。

2.6 中断类寄存器(USBINTR)

USBINTR(Interrupt Enable Register)位于偏移0x04,控制哪些状态事件可以产生中断。每个使能位对应USBSTS中的一个状态位。

USBINTR位域映射:

  • bit 0:IOC(Interrupt on Complete)—— 完成描述符中断使能
  • bit 1:RIE(Resume Interrupt Enable)—— 恢复中断使能
  • bit 2:SPIE(Short Packet Interrupt Enable)—— 短包中断使能(可选)
  • bit 3:—— 保留
  • bit 4:HSEE(Host System Error Enable)—— 系统错误中断使能
  • bit 5-15:—— 保留
性能调优建议:在批量传输场景中,建议使能IOC中断并配合帧列表中断(Frame List Interrupt)实现周期性调度。避免使能不必要的错误中断(如CRC/TimeOut),以减少中断处理开销。

2.7 帧列表类寄存器(FRNUM / FLBASEADD / SOFMOD)

帧列表是UHCI调度体系的核心数据结构。三个寄存器共同维护帧列表的运行:

寄存器 偏移 功能
FRNUM 0x06 当前帧号(0-1023),硬件每1ms递增1,写0复位帧号
FLBASEADD 0x08 帧列表的物理基地址(32位,必须4KB对齐)
SOFMOD 0x0C SOF(Start of Frame)调节值,调整帧起始时间精度

帧列表基地址要求:

  • 必须指向4KB对齐的物理内存(低12位为0)。
  • 帧列表大小为1024个条目(4KB),每个条目为4字节的帧列表指针。
  • 驱动程序在初始化时分配4KB连续物理内存,将地址写入FLBASEADD。
// 帧列表基地址设置示例
uint32_t frame_list_phys = 0x100000;  // 假设物理地址1MB,4KB对齐
outl(frame_list_phys, io_base + 0x08); // 写入FLBASEADD

// 帧号复位
outw(0, io_base + 0x06);  // FRNUM写0,从帧0开始调度

2.8 端口类寄存器(PORTSC)

每个USB端口对应一个PORTSC(Port Status and Control Register),偏移从0x10开始,每个端口占用16位。UHCI最多支持2个端口(PORTSC1=0x10,PORTSC2=0x12)。

PORTSC关键位域:

位域 名称 描述
bit 0 CCS(Current Connect Status) 只读,1=设备已连接,0=未连接
bit 1 CSC(Connect Status Change) 连接状态变化标志,写1清0
bit 2 PED(Port Enable/Disable) 1=端口使能,0=端口禁用
bit 3 PEDC(Port Enable/Disable Change) 端口使能状态变化标志
bit 4 LSDA(Low Speed Device Attached) 1=低速设备,0=全速设备
bit 5 —— 保留
bit 6 PR(Port Reset) 写1触发端口复位,持续至少50ms后硬件清0
bit 7 SUSP(Suspend) 1=端口挂起,0=正常工作
端口初始化流程:检测CSC变化后,先读CCS确认设备连接;然后置PR=1启动复位,等待50ms后PR自动清0;最后检查PED是否自动置1(硬件使能),若未使能则手动置PED=1。

2.9 寄存器访问实战示例

以下代码演示了完整的I/O基地址获取和关键寄存器初始化序列:

// UHCI寄存器初始化示例(x86 I/O端口访问)
#include <stdint.h>

#define USBCMD      0x00
#define USBSTS      0x02
#define USBINTR     0x04
#define FRNUM       0x06
#define FLBASEADD   0x08
#define SOFMOD      0x0C
#define PORTSC1     0x10

// 假设已通过PCI枚举获取io_base
void uhci_init(uint16_t io_base, uint32_t frame_list_phys) {
    // 1. 复位主机控制器
    outw(0x0002, io_base + USBCMD);  // 写HCRESET
    while (inw(io_base + USBSTS) & 0x0008); // 等待HCR位清0
    
    // 2. 确认停止状态
    while (!(inw(io_base + USBSTS) & 0x0020)); // 等待HCHalted置1
    
    // 3. 配置帧列表
    outl(frame_list_phys, io_base + FLBASEADD);
    outw(0, io_base + FRNUM);  // 帧号从0开始
    
    // 4. 设置SOF调节(默认值0x40)
    outb(0x40, io_base + SOFMOD);
    
    // 5. 清除所有遗留状态
    outw(0x007F, io_base + USBSTS);  // 写1清0所有状态位
    
    // 6. 使能必要中断(仅IOC)
    outw(0x0001, io_base + USBINTR);
    
    // 7. 启动主机控制器
    outw(0x0001, io_base + USBCMD);  // RS=1
    
    // 8. 等待运行状态
    while (inw(io_base + USBSTS) & 0x0020); // 等待HCHalted清0
    
    // 9. 置配置标志
    uint16_t cmd = inw(io_base + USBCMD);
    cmd |= 0x0040;  // CF=1
    outw(cmd, io_base + USBCMD);
}
性能调优要点:
  • FLBASEADD写入后不要频繁修改,帧列表内容通过内存更新而非寄存器操作。
  • SOFMOD值影响USB帧起始精度,默认0x40适用于大多数系统;若发现SOF抖动过大,可微调该值。
  • 中断使能应最小化:仅使能必要的IOC中断,避免HSE和CRC中断在正常传输中频繁触发。
  • 端口复位(PR)必须保证50ms以上延迟,建议使用精确定时器而非忙等待。

通过以上对I/O基地址获取、偏移映射和五大类寄存器的深入分析,我们已经为UHCI控制器的初始化编程建立了完整的寄存器视图。下一阶段将基于这些寄存器操作,构建帧列表和传输描述符的调度体系。