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基地址。
获取I/O基地址的标准步骤如下:
- 通过PCI总线枚举找到UHCI控制器的Bus/Device/Function号。
- 向PCI配置空间地址端口(0xCF8)写入目标设备的配置空间偏移0x20(BAR0)。
- 从PCI配置空间数据端口(0xCFC)读取BAR0的值。
- 检查bit 0是否为1,确认是I/O空间。
- 将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空间
}
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位) |
2.3 关键寄存器分类详解
根据功能,UHCI寄存器可分为五大类。下面通过SVG结构图展示分类关系,再逐一说明每个类别的核心寄存器。
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字节(控制端点最大包大小) |
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校验错误或总线超时 |
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:—— 保留
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=正常工作 |
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控制器的初始化编程建立了完整的寄存器视图。下一阶段将基于这些寄存器操作,构建帧列表和传输描述符的调度体系。