第2节 USB主机概述
本部分论述了与EHCI兼容的主机中使用的数据结构。EHCI规范中一些数据结构类型是专用来处理同步传输的。
一旦同步传输的数据结构被去除,只剩下四种EHCI数据结构:周期表(调度)、异步表(调度)、队列头部(QHs)和队列元素传输描述符(qTDs)。
USB双模式控制器中的主机控制器使用两种不同的系统调度USB传输:
周期表(调度)处理中断和同步传输
异步表(调度)处理控制和块传输
异步和周期表(调度)都用QH及qTD数据结构配置传输。通常有一个为主机访问的端点定义的QH。该QH决定传输用的USB地址和端点号以及端点的其他信息。QH包含一个指向当前qTD的指针、一个存放当前qTD的内容的qTD 覆盖区域(overlay area)以及一个指向下一qTD的指针。qTD定义实际的传输(字节数、读写数据的位置及状态)。
2.1 队列首部(QH)
QH的主要目的是用来定义已分配地址的具体端点的特性。这意味着在大多数情况下一个已分配地址的设备端点对应对应一个QH。QHs是32字节对齐的。
图1是一个简化的QH数据结构图,其中和SPLIT事务、同步传输以及大量传输(传输需求超过一个缓冲区)相关的字段被移除了或者设为了静态值。这是一种EHCI规范中定义的QH数据结构的简化形式。

图1 简化的队列头部(QH)设计
1.偏移量0x04-0x0B包含静态端点状态。
2.主机控制器可读/写;其他为只读。
2.1.1 队列首部平行链接指针(偏移 :0x00)
QH的第一个长字包含了一个链接指针指向下一个待处理的数据对象,该对象在本队列中的所有要求的处理都完成之后处理。控制位定义如表2-1所示。该指针可能指向一个队列首部或者一个同步传输描述符。在本文中,他总是指向下一个QH。
2.1.2 端点特性(偏移:0x04)
QH的第二个长字详细说明了端点的静态信息。这些信息在端点的生命周期中不改变。这些字段由USB主机的软件协议栈在QH建立时设置,并且决不被主机控制器硬件所修改。

2.1.3 端点性能(偏移:0x08)
QH的第三个长字指明了和SPLIT事务相关的一些参数,因此该长字的大部分总是设为相同的值。
2.1.4(指向)当前qTD指针(偏移:0x0C)
该长字是当前被处理的qTD的地址。主机控制器在读入qTD时写该字段。当创建一个新QH时,软件不需要初始化该长字。
2.1.5 qTD覆盖区域(overlay area)(偏移:0x10-0x2C)
这个区域的八个长字是当前正在被处理或上次处理的有用的 qTD 的拷贝。当一次传输在进行时,控制器将增加的状态信息写入到 qTD 覆盖区域。传输完成后,结果被写回到原有的qTD区域(当前qTD指针所指向的地址)。
这些值在主机拷贝进当前 qTD 时进行初始化。因此,不需要软件初始化这些字段。唯一例外的是指向下一个qTD的指针字段,该字段需要在创建一新QH时软件进行初始设置,该字段应被设为该端点要处理的第一个 qTD 的地址(利用T标志位清零表明以一有效的指针),控制器用该地址去访问该端点qTD链表的起始。
2.2 队列元素传输描述符(qTD)
qTD 定义了控制、批量、中断传输的实际数据活动。qTD 被当作一个单链表处理。QH中的下一qTD指针(next qTD pointer)要被初始化为链表中的第一个qTD的地址。第一个qTD处理完成后,控制器使用第一个qTD中的next qTD pointer去查找第二个qTD,就此重复直到一个qTD的next qTD pointer字段无效为止。QTD一定要个32字节的边界对齐 。
图2是EHCI规范中定义的qTD的简化版本,可传输的数据最大为4KB。

图1 简化的队列头部(QH)设计
主机控制器可读/写;其他为只读。
2.2.1 (指向)下一个qTD指针(偏移:0x00)
qTD的第一个长字是指向另一个qTD的指针,该指针用来创建一个qTD的单链表。
2.2.2 qTD令牌(偏移:0x08)
队列元素传输描述符的第三个长字包括主机控制执行一个 USB 事务所需要的大部分信息(剩下的端点和地址信息在QH中指明)。
2.2.3 qTD缓冲页指针(偏移:0x0C)
qTD缓冲页指针用来指明传输数据缓冲区的内存地址。
2.3 周期表(调度)
图3周期表(调度)的结构图。所以中断传输使用这种表(调度)。

图3 周期调度组织
USB模块的PERIODICLISTBASE寄存器和FRINDEX(13-3位)组合成一个指针指向一指针数组,该数组命名为周期帧列表(periodic frame list)。指向周期帧列表的指针随帧(1ms)递增。
周期帧列表是 4KB 的页对齐的帧列表链接指针数组,帧列表的长度可以通过USBCMD[FS]字段设定。EHCI规范支持周期帧列表包含1024、512或256个元素。USB 模块周期帧列表可以有 128、64、32、16 或 8 元素。在内存较小嵌入式应用中,使用非 EHCI 兼容的帧列表大小有助于减少 USB 软件所需要的内存空间。
2.3.1 帧列表链接指针
帧列表链接指针指向主机控制器在周期调度中当前帧的的第一个处理项。该链接指针在周期帧列表中与长字边界对齐。图4是帧列表链接指针的格式。

图4 帧列表链指针的格式
帧列表链接指针访问的内存对象32字节对齐的,(帧列表指针中的最低有效位The least significant bits in a frame list pointer key the host controller as to the type of object the pointer is referencing)。对中断传输来说帧列表链接指针总是指向一个QH(TYPE = 0b01)。
最低有效位为终止位,该位用来表明指针的内容是否有效。当该位设置时,主机控制器忽略帧列表中的项;清零时,帧列表链接指针可用来访问引用的对象,本文中为QH。
2.3.2 周期表(调度)遍历(过程)
周期表(调度)可通过周期调度使能位(USBCMD[PSE])来使能或禁止。PSE 位的改变不一定即时生效。USBSTS[PS]位反应了周期表(调度)的当前状态。如果PS位清零,主机控制器就不尝试遍历周期表。同样,如果PS位设置,在微帧(每125us)的起始USB控制器通过查找周期表调度USB通信量。周期通信量保证了总线带宽,这样看来周期表(调度)的优先级比异步表(调度)的高。控制器使用指向周期帧列表的指针访问当前帧列表链接指针。如果T标志位清零(表示为有效指针)控制器帧列表链接指针指向的QH。
主机控制器在每个微帧的开始调度周期通信量,但指向周期帧列表的指针只会为一个整帧而增加。周期帧列表中的每一项会被访问 8 次每帧。QH[uFrame S-mask]字段值确定每个微帧如果通信量用来调度一已知的QH。
如果找到的QH对当前微帧是有效的,那么主机控制器就处理列表中该QH的所有qTD。当第一个QH的qTDs完成后,控制器检测第一个QH是否指向另一个QH。如果有,将移到第二个QH并处理它的qTDs。如此重复直到最后一个QH(该QH不指向另一个QH)。此时,控制器切换到异步表(调度)。微帧中剩下的所有时间用来处理异步表(调度)传输。
如果周期表(调度)禁止或者当前列表链接指针中的T标志位被设置,那么整个帧可以使用异步表(调度)通信量。
2.3.3 在周期表(调度)中增加中断队列首部
当一个中断设备端点被激活后,主机软件应该为那个端点创建一个QH并连接到周期表中。QHs被链接到周期表中因此它们将被以适当的速率轮询。每一个周期帧列表项使用1ms。因此,每个周期帧列表中不指向已知QH的项会有1ms的延时。
例如:为了为 FS/LS 中断获得 8ms 的轮询速率,周期帧列表中每逢第八项指向QH(QH[uFrame S-mask]位应被置位)。
图 5 是怎样为一单中断设置周期帧列表的例子,该中断每 4ms 轮询一次。周期帧列表中每逢4项是指向相同的QH这样得到想要的轮询速率。

图5 周期帧列表实例 - 每4ms轮询一次的中断
如果一个高速(HS)中断需要小于 1ms 的轮询速率,QH[uFrame S-mask]的值产生想要 的轮询速率。QH 链接到周期帧列表中的每一个项,QH[uFrame S-mask]字段中的设置位之间的间隔决定中断产生的频率。
例如:QH[uFrame S-mask]设置为0b01010101(间隔为1),那么每隔一个微帧产生中断(每 250us)。值 0b00010001(间隔为 4)产生一个中断每 4 个微帧(每500us)。
2.4 异步表(调度)
图6是异步表(调度)的结构图。该表(调度)在所有控制和批量传输中使用。因为控制和批量传输不保证 USB 总线带宽,控制器只有在以下情况使用该列表:
到达周期列表的结尾
周期列表禁止
周期列表空

图6 同步调度组织
异步列表是一个简单的队列首部循环表,ASYNCLISTADDR寄存器指向该表。软件应初始化ASYNCLISTADDR使它指向第一个QH。控制器处理列表时设置ASYNCLISTADDR指向下一个QH。在这种方式下,控制器在处理完周期队列之后,它并没有返回到异步列表的开始处,而是回到上次离开的点进行处理。这就为异步队列中的QH实现了彻底的循环服务。
2.4.1 异步表(调度)遍历(过程)
异步表(调度)遍历可以通过异步表(调度)使能位(USBCMD[ASE])来使能或禁止。。ASE位的改变不一定即时生效。USBSTS[AS]位反应了异步表(调度)的当前状态。如果AS位清零,主机控制器就不尝试遍历异步表。同样,如果AS位设置,主机控制器使用ASYNCLISTADDR寄存器遍历异步表。
主机控制器使用 ASYNCLISTADDR 寄存器的值开始遍历异步表,读第一个引用的QH并执行事务并以适合的方法遍历链表。主控制器处理完异步表,就保持最后访问的QH的平行指针在ASYNCLISTADDR寄存器。下次再访问异步表时这就是第一个服务的QH。这就实现了处理异步表的循环共享。
主机控制器在以下事件发生时结束处理异步表:
到达微帧的末尾
主机控制器检测到列表空
异步表(调度)禁止(USBCMD[ASE]清空)
2.4.2 在异步表(调度)中增加控制和批量队列首部(QH)
每当一个新的设备端点激活就有一个QH加入到列表中。该端点的所有通信量在一个qTD中设置并链接到合适的QH上。主机控制器通过环中的QHs循环检测激活的qTDs。
因为异步表(调度)在一个环中处理QH,所以异步表激活时环不能断开。这表示向异步表中添加或从中移除QH时要小心进行。
一旦同步传输的数据结构被去除,只剩下四种EHCI数据结构:周期表(调度)、异步表(调度)、队列头部(QHs)和队列元素传输描述符(qTDs)。
USB双模式控制器中的主机控制器使用两种不同的系统调度USB传输:
周期表(调度)处理中断和同步传输
异步表(调度)处理控制和块传输
异步和周期表(调度)都用QH及qTD数据结构配置传输。通常有一个为主机访问的端点定义的QH。该QH决定传输用的USB地址和端点号以及端点的其他信息。QH包含一个指向当前qTD的指针、一个存放当前qTD的内容的qTD 覆盖区域(overlay area)以及一个指向下一qTD的指针。qTD定义实际的传输(字节数、读写数据的位置及状态)。
2.1 队列首部(QH)
QH的主要目的是用来定义已分配地址的具体端点的特性。这意味着在大多数情况下一个已分配地址的设备端点对应对应一个QH。QHs是32字节对齐的。
图1是一个简化的QH数据结构图,其中和SPLIT事务、同步传输以及大量传输(传输需求超过一个缓冲区)相关的字段被移除了或者设为了静态值。这是一种EHCI规范中定义的QH数据结构的简化形式。

图1 简化的队列头部(QH)设计
1.偏移量0x04-0x0B包含静态端点状态。
2.主机控制器可读/写;其他为只读。
2.1.1 队列首部平行链接指针(偏移 :0x00)
QH的第一个长字包含了一个链接指针指向下一个待处理的数据对象,该对象在本队列中的所有要求的处理都完成之后处理。控制位定义如表2-1所示。该指针可能指向一个队列首部或者一个同步传输描述符。在本文中,他总是指向下一个QH。
表2-1 队列首部平行链接指针


2.1.2 端点特性(偏移:0x04)
QH的第二个长字详细说明了端点的静态信息。这些信息在端点的生命周期中不改变。这些字段由USB主机的软件协议栈在QH建立时设置,并且决不被主机控制器硬件所修改。
表2-2 端点特性

2.1.3 端点性能(偏移:0x08)
QH的第三个长字指明了和SPLIT事务相关的一些参数,因此该长字的大部分总是设为相同的值。
表2-3 端点性能


2.1.4(指向)当前qTD指针(偏移:0x0C)
该长字是当前被处理的qTD的地址。主机控制器在读入qTD时写该字段。当创建一个新QH时,软件不需要初始化该长字。
2.1.5 qTD覆盖区域(overlay area)(偏移:0x10-0x2C)
这个区域的八个长字是当前正在被处理或上次处理的有用的 qTD 的拷贝。当一次传输在进行时,控制器将增加的状态信息写入到 qTD 覆盖区域。传输完成后,结果被写回到原有的qTD区域(当前qTD指针所指向的地址)。
这些值在主机拷贝进当前 qTD 时进行初始化。因此,不需要软件初始化这些字段。唯一例外的是指向下一个qTD的指针字段,该字段需要在创建一新QH时软件进行初始设置,该字段应被设为该端点要处理的第一个 qTD 的地址(利用T标志位清零表明以一有效的指针),控制器用该地址去访问该端点qTD链表的起始。
2.2 队列元素传输描述符(qTD)
qTD 定义了控制、批量、中断传输的实际数据活动。qTD 被当作一个单链表处理。QH中的下一qTD指针(next qTD pointer)要被初始化为链表中的第一个qTD的地址。第一个qTD处理完成后,控制器使用第一个qTD中的next qTD pointer去查找第二个qTD,就此重复直到一个qTD的next qTD pointer字段无效为止。QTD一定要个32字节的边界对齐 。
图2是EHCI规范中定义的qTD的简化版本,可传输的数据最大为4KB。

图1 简化的队列头部(QH)设计
2.2.1 (指向)下一个qTD指针(偏移:0x00)
qTD的第一个长字是指向另一个qTD的指针,该指针用来创建一个qTD的单链表。
表2-4 qTD下个元素传输指针(长字0)


2.2.2 qTD令牌(偏移:0x08)
队列元素传输描述符的第三个长字包括主机控制执行一个 USB 事务所需要的大部分信息(剩下的端点和地址信息在QH中指明)。
表2-5 qTD令牌(长字2)


2.2.3 qTD缓冲页指针(偏移:0x0C)
qTD缓冲页指针用来指明传输数据缓冲区的内存地址。
表2-6 qTD缓冲区指针


2.3 周期表(调度)
图3周期表(调度)的结构图。所以中断传输使用这种表(调度)。

图3 周期调度组织
USB模块的PERIODICLISTBASE寄存器和FRINDEX(13-3位)组合成一个指针指向一指针数组,该数组命名为周期帧列表(periodic frame list)。指向周期帧列表的指针随帧(1ms)递增。
周期帧列表是 4KB 的页对齐的帧列表链接指针数组,帧列表的长度可以通过USBCMD[FS]字段设定。EHCI规范支持周期帧列表包含1024、512或256个元素。USB 模块周期帧列表可以有 128、64、32、16 或 8 元素。在内存较小嵌入式应用中,使用非 EHCI 兼容的帧列表大小有助于减少 USB 软件所需要的内存空间。
2.3.1 帧列表链接指针
帧列表链接指针指向主机控制器在周期调度中当前帧的的第一个处理项。该链接指针在周期帧列表中与长字边界对齐。图4是帧列表链接指针的格式。
图4 帧列表链指针的格式
帧列表链接指针访问的内存对象32字节对齐的,(帧列表指针中的最低有效位The least significant bits in a frame list pointer key the host controller as to the type of object the pointer is referencing)。对中断传输来说帧列表链接指针总是指向一个QH(TYPE = 0b01)。
最低有效位为终止位,该位用来表明指针的内容是否有效。当该位设置时,主机控制器忽略帧列表中的项;清零时,帧列表链接指针可用来访问引用的对象,本文中为QH。
2.3.2 周期表(调度)遍历(过程)
周期表(调度)可通过周期调度使能位(USBCMD[PSE])来使能或禁止。PSE 位的改变不一定即时生效。USBSTS[PS]位反应了周期表(调度)的当前状态。如果PS位清零,主机控制器就不尝试遍历周期表。同样,如果PS位设置,在微帧(每125us)的起始USB控制器通过查找周期表调度USB通信量。周期通信量保证了总线带宽,这样看来周期表(调度)的优先级比异步表(调度)的高。控制器使用指向周期帧列表的指针访问当前帧列表链接指针。如果T标志位清零(表示为有效指针)控制器帧列表链接指针指向的QH。
主机控制器在每个微帧的开始调度周期通信量,但指向周期帧列表的指针只会为一个整帧而增加。周期帧列表中的每一项会被访问 8 次每帧。QH[uFrame S-mask]字段值确定每个微帧如果通信量用来调度一已知的QH。
如果找到的QH对当前微帧是有效的,那么主机控制器就处理列表中该QH的所有qTD。当第一个QH的qTDs完成后,控制器检测第一个QH是否指向另一个QH。如果有,将移到第二个QH并处理它的qTDs。如此重复直到最后一个QH(该QH不指向另一个QH)。此时,控制器切换到异步表(调度)。微帧中剩下的所有时间用来处理异步表(调度)传输。
如果周期表(调度)禁止或者当前列表链接指针中的T标志位被设置,那么整个帧可以使用异步表(调度)通信量。
2.3.3 在周期表(调度)中增加中断队列首部
当一个中断设备端点被激活后,主机软件应该为那个端点创建一个QH并连接到周期表中。QHs被链接到周期表中因此它们将被以适当的速率轮询。每一个周期帧列表项使用1ms。因此,每个周期帧列表中不指向已知QH的项会有1ms的延时。
例如:为了为 FS/LS 中断获得 8ms 的轮询速率,周期帧列表中每逢第八项指向QH(QH[uFrame S-mask]位应被置位)。
图 5 是怎样为一单中断设置周期帧列表的例子,该中断每 4ms 轮询一次。周期帧列表中每逢4项是指向相同的QH这样得到想要的轮询速率。

图5 周期帧列表实例 - 每4ms轮询一次的中断
如果一个高速(HS)中断需要小于 1ms 的轮询速率,QH[uFrame S-mask]的值产生想要 的轮询速率。QH 链接到周期帧列表中的每一个项,QH[uFrame S-mask]字段中的设置位之间的间隔决定中断产生的频率。
例如:QH[uFrame S-mask]设置为0b01010101(间隔为1),那么每隔一个微帧产生中断(每 250us)。值 0b00010001(间隔为 4)产生一个中断每 4 个微帧(每500us)。
2.4 异步表(调度)
图6是异步表(调度)的结构图。该表(调度)在所有控制和批量传输中使用。因为控制和批量传输不保证 USB 总线带宽,控制器只有在以下情况使用该列表:
到达周期列表的结尾
周期列表禁止
周期列表空

图6 同步调度组织
异步列表是一个简单的队列首部循环表,ASYNCLISTADDR寄存器指向该表。软件应初始化ASYNCLISTADDR使它指向第一个QH。控制器处理列表时设置ASYNCLISTADDR指向下一个QH。在这种方式下,控制器在处理完周期队列之后,它并没有返回到异步列表的开始处,而是回到上次离开的点进行处理。这就为异步队列中的QH实现了彻底的循环服务。
2.4.1 异步表(调度)遍历(过程)
异步表(调度)遍历可以通过异步表(调度)使能位(USBCMD[ASE])来使能或禁止。。ASE位的改变不一定即时生效。USBSTS[AS]位反应了异步表(调度)的当前状态。如果AS位清零,主机控制器就不尝试遍历异步表。同样,如果AS位设置,主机控制器使用ASYNCLISTADDR寄存器遍历异步表。
主机控制器使用 ASYNCLISTADDR 寄存器的值开始遍历异步表,读第一个引用的QH并执行事务并以适合的方法遍历链表。主控制器处理完异步表,就保持最后访问的QH的平行指针在ASYNCLISTADDR寄存器。下次再访问异步表时这就是第一个服务的QH。这就实现了处理异步表的循环共享。
主机控制器在以下事件发生时结束处理异步表:
到达微帧的末尾
主机控制器检测到列表空
异步表(调度)禁止(USBCMD[ASE]清空)
2.4.2 在异步表(调度)中增加控制和批量队列首部(QH)
每当一个新的设备端点激活就有一个QH加入到列表中。该端点的所有通信量在一个qTD中设置并链接到合适的QH上。主机控制器通过环中的QHs循环检测激活的qTDs。
因为异步表(调度)在一个环中处理QH,所以异步表激活时环不能断开。这表示向异步表中添加或从中移除QH时要小心进行。


