xHCI

xHCI

2023-09-14
bios
  • USB Driver(USBD): 总线驱动,用来枚举USB设备,给USB设备安装Protocol。
  • Host Controller Driver(xHCD): xHC控制器驱动。
  • Host Controller (xHC):USB控制器,是一个硬件设备。
  • USB Device: 包括HUB和Function,比如鼠标、键盘。

XHCI介绍 #

  • xHCI包含三个空间

  • 主机配置空间:一般是PCI配置空间。

  • MMIO空间:主要放一些寄存器(Capability Registers、Operational Registers、Runtime Registers和Doorbell Array)。

  • 主机内存:主要放一些数据结构,比如Device Context Base Address Array, Device Contexts, Transfer Ring等。

  • xHCI支持的传输类型:Isochronous(等时传输)、Interrupt(中断传输)、Control(控制传输)、Bulk(批量传输)。

  • Capability Register:这些值作为Host Controller Driver的参数。

  • Runtime和Operational Registers指定主机控制器配置和运行变化状态。系统软件通过该寄存器来控制和监控主机控制器的Operational状态。

  • xHCI Extended Capabilities说明了xHC实现的一些可选特性。

  • Doorbell Array:最多支持256个Doorbell寄存器的数组,每个Doorbell寄存器都向系统软件提供了一种机制,用于通知xHC是否有域槽位或者Endpoint相关的工作要执行。Doorbell寄存器的DB Target字段表示按下门铃的原因。Doorbell寄存器0被主机控制器用域Command Ring管理。

  • Device Slot 表示USB设备的多个XHCI数据结构。每个设备由Device Context BaseAddress Array中的一个元素、Doorbell Array register中的一个寄存器和设备的Device Context组成。Slot ID用于标识特定的Device Slot。

  • Command Ring:软件使用Command Ring将Command传递给xHC。

  • Event Ring:xHC使用Event Ring将Command Completion和Asynchronous event传递给软件。

  • Transfer Ring:软件使用Transfer Ring为Endpoint安排工作。Transfer Ring是一个循环队列(队列中每个元素都是是Transfer Descriptor(TD)),每个TD定义了一个或多个数据Buffer。

XHCI数据结构 #

Device Context Base Address Array #

  • Device Context Base Address Array (DCBAA)是一个指针数组,数组的每个元素都指向了一个Device Context数据结构。数组最多255个元素。
  • DCBAA的数组下标就是SLOT ID。
  • 当检测到插入了一个USB设备后:1. 软件初始化一个Device Context数据结构;2. 从xHC获取一个Slot ID;3. 将此Device Context的指针插入到DCBAA的SLOT ID的位置。

Device Context #

  • Device Context用来记录设备的配置和状态信息。
  • Device Context由32个数据结构组成,第一个数据结构是Slot Context,剩余的数据结构是Endpoint Context。
  • 在枚举USB设备时,软件创建一个Device Context数据结构并初始化为0,在执行了Address Device命令后将该数据结构的所属权传递给xHC。在执行了Disable Slot命令后,xHC会失去该数据结构的所属权。
typedef struct _DEVICE_CONTEXT {
  SLOT_CONTEXT        Slot;
  ENDPOINT_CONTEXT    EP[31];
} DEVICE_CONTEXT;

Slot Context #

  • Slot Context提供了control、state、addressing和电源管理。
typedef struct _SLOT_CONTEXT {
  UINT32    RouteString    : 20;
  UINT32    Speed          : 4;
  UINT32    RsvdZ1         : 1;
  UINT32    MTT            : 1;
  UINT32    Hub            : 1;
  UINT32    ContextEntries : 5;

  UINT32    MaxExitLatency : 16;
  UINT32    RootHubPortNum : 8;
  UINT32    PortNum        : 8;

  UINT32    TTHubSlotId    : 8;
  UINT32    TTPortNum      : 8;
  UINT32    TTT            : 2;
  UINT32    RsvdZ2         : 4;
  UINT32    InterTarget    : 10;

  UINT32    DeviceAddress  : 8;
  UINT32    RsvdZ3         : 19;
  UINT32    SlotState      : 5;

  UINT32    RsvdZ4;
  UINT32    RsvdZ5;
  UINT32    RsvdZ6;
  UINT32    RsvdZ7;
} SLOT_CONTEXT;

Endpoint Context #

  • Endpoint Context数据结构定义了特定的USB Endpoint的配置和状态。Endpoint Context字段包含了Endpoint相关的type、control、state和带宽信息。这些信息由USB设备提供。Endpoint Context还定义了一个TR Dequeue 指定字段,通常提供了一个指向了与此pipe关联的Transfer Ring。

Rings #

  • Ring是一个循环队列,xHC使用三种类型的Ring:
    • Command Ring:(每个XHC一个)软件使用Command Ring将命令发送给xHC。
    • Event Ring:(每个中断一个)xHC使用Event Ring将命令状态、结果传递给软件。
    • Transfer Ring:(每个Endpoint或Stream一个)Transfer Ring被用来在内存和设备Endpoint之间传输数据。

Command接口 #

  • 为了管理xHC和连接到xHC的设备,xHC提供了一个Command Ring接口,一个Command Ring上的项目被称为CD(Command Descriptor)。
  • 所有命令都会在Event Ring上生成一个命令完成事件,该事件用于报告命令完成状态。
  • xHCI 命令集合
命令 描述
No Op 测试TRB Ring机制
Enable Slot 返回设备的Slot ID并将设备Slot状态从Disabled改为Default
Disable Slot 将Device Slot从其他任何状态改为Disabled状态
Address Device 启用Default Control Endpoint,(可选)向USB设备发出SET_ADDRESS命令并将Device Slot设置为Addressed状态
Configure Endpoint 启用或者禁用设备的Enpoint
Evaluate Context 告知xHC软件已经修改了选定的Context参数
Reset Endpoint 复位Endpoint,该命令用于将一个halted endpoint恢复
Stop Endpoint 停止Endpoint
Set TR Dequeue Pointer 更新一个启用的endpoint的Transfer Ring Dequeue
Reset Device 复位Device Slot,此命令用于在复位一个USB设备时同步Device Slot的状态

Endpoint #

  • 一个USB设备支持最高31个Endpoints。

USB设备初始化 #

  • 下面是一个连到ROOT HUB的USB设备初始化的流程:
    1. 当检测到一个USB设备连接后,xHC会将CCS和CSC置为1,并生成一个端口变更事件。
    2. 收到端口状态变更事件后,软件根据Port ID字段来确认是哪个Port生成的事件。
    3. 软件读取PORTSC寄存器。 USB3协议的Port尝试进入Enabled状态,连接的USB设备进入为Default状态。
    4. 软件通过Enable Slot命令来从xHC获取设备的slot,XHC会返回一个SLOT ID。Enable Slot执行成功后,Device Slot会进入Enabled状态。
    5. 获取到设备的slot后,软件初始化此slot关联的数据结构。
      1. 分配Input Context数据结构。
      2. 将Input Context中的Input Control Context的A0和A1标志位置为1。
      3. 初始化Input Slot Context数据结构,主要是设置Root Hub Port Number、Route String和Context Entries。
      4. 为Default Control Endpoint初始化Transfer Ring。
      5. 初始化Input Default Control Endpoint 0 Context,主要是设置EP type = Control、Max Packet Size等信息。
      6. 分配Output Device Context 数据结构,从Device Context Base Address Array中选择一个下标(Device Slot Id)用来指向Output Device Context数据结构。
    6. 软件使用Address Device命令来给设备分配地址,并启用其Default Control Endpoint。此命令会将Device Slot从Enabled状态置为Addressed状态,将USB设备从Default状态置为Address状态。
    7. 对于LS,HS, 和SS设备,其Default Control Endpoint允许的包大小是固定的,分别为8、64、512字节。对于FS设备,系统软件需要做一些操作来决定最大包大小(此处省略)。
    8. Default Control Endpoint配置完成后,系统软件可以获取到完整的Device Descriptor和Configuration Descriptor,以便将其交给适合的Class Drivers。(软件通过Endpoint 0的GET_DESCRIPTOR请求获取USB描述符)
    9. 软件会发出将Contxt Bit 0置为1的Evaluate Context命令,用来告知xHC最大退出延迟的值。此命令同样会修改Output Slot Context Interrupter部分字段的值。
    10. Class Driver会使用Configure EndPoint命令来配置Device Slot,并通过Default Control Endpoint发出USB SET_CONFIGURATION请求来设置USB设备。需要成功设置完这两项操作,才能将USB设备的状态从Address到Configured,并将Device SLot从Addressed变更为Configured
    11. 如果需要,系统软件可能会配置Alternate Interface。

Resetting a Root Hub Port #

  • 复位Root HUb port和连接上的USB设备。如果成功了,就会将PORT的状态设置为Enabled,并且可以获取到设备的Speed(位于PORTSC 的Port Speed)。
  • 无论RESET是否执行成功,Port Reset Change(PRC)标志位都会置为1。如果PRC是从0变为1,则还会生成一个端口变更事件。

Device Slot Assignment #

  • 当执行完RESET PORT后,软件会向XHC发出一个Enable Slot命令(通过Command Ring),

Device Slot Initialization #

  • 一旦USB设备获得了Slot ID,软件会初始化SLOT对应的数据结构,流程如下:
    1. 初始化Input Context Data 数据结构,将所有字段初始化为0。

描述符 #

设备描述符 #

  • 设备描述符用于表示USB设备的一般信息,如制造商ID、产品序列号等。
  • 设备上电时,主机USB系统软件读取设备描述符的前8字节,得到endpoint所支持的最大数据包长度,后续控制传输就使用此值进行工作。
typedef struct {
  UINT8     Length; //描述符字节长度0X12
  UINT8     DescriptorType; //描述符的类型
  UINT16    BcdUSB; //USB设备支持的协议版本号
  UINT8     DeviceClass;    //设备类代码
  UINT8     DeviceSubClass; //子类代码
  UINT8     DeviceProtocol; //协议码
  UINT8     MaxPacketSize0; //断点0的最大包长度
  UINT16    IdVendor;   //厂商ID
  UINT16    IdProduct;  //产品ID
  UINT16    BcdDevice;  //设备发行号
  UINT8     StrManufacturer;    //厂商信息的字符串描述符索引值
  UINT8     StrProduct; //产品信息的字符串描述符索引值
  UINT8     StrSerialNumber;    //设备序列号信息的字符串描述符索引值
  UINT8     NumConfigurations;  //配置描述符数目
} USB_DEVICE_DESCRIPTOR;

/*
案例
------------------------------------------
Device Descriptor	
bLength :	0x0012
bDescriptorType :	0x0001
bcdUSB :	0x0320  - Spec# = 03.20
bDeviceClass :	0x00    - Defined at Interface level
bDeviceSubClass :	0x00
bDeviceProtocol :	0x00
bMaxEP0Size :	0x09    - 9 bytes
idVendor :	0x0B95  - "ASIX Electronics Corp."
idProduct :	0x1790  - "AX88179 Gigabit Ethernet"
bcdDevice :	0x0200  - Device# = 02.00
iManufacturer :	0x01    - "ASIX"
iProduct :	0x02    - "AX88179A"
iSerialNumber :	0x03    - "00F30573"
bNumConfigurations :	0x03
------------------------------------------
*/