由于工作需要,这几天学习了一下USB协议,虽然现在已经更新到了3.0版本了,我还是循序渐进先从最基础的1.1开始学吧。最主要还是看规范,从网上找了USB1.1英文原版的《Universal Serial Bus Specification》,无奈英语水平有限,只好找了中文版,中英文对照着看。我把一些关键的内容整理了一下,方便日后复习,同时也给大家做个参考。
官方的规范一共有十一章,如果全部看下来可能真的要成SB了。如果是搞软件的只要看下第五、八、九、十章就够用了,最关键的还是第八章----协议层。
先从第五章开始
第五章讲的是数据流模型,主要介绍了一些USB系统的软硬件的核心概念,比如设备端点、四种传输类型等都是非常重要的内容。
设备端点
一个端点是一个可唯一识别的USB设备的Portion,它是主机与设备间通信流的一个结束点。一系列相互独立的端点在一起构成了USB逻辑设备。每个逻辑设备有一个唯一的地址,这个地址是在设备连上主机时,由主机分配的,而设备中的每个端点在设备内部有唯一的端点号。这个端点号是在设备设计时被给定的。每个端点都是一个简单的连接点,或者支持数据流进设备,或者支持其流出设备,两者不可得兼。
一个端点的特性决定了它与客户软件进行的传送的类型。一个端点有以下特性:
·端点的总线访问频率要求
·端点的总线延迟要求
·端点的带宽要求
·端点的端点号
·对错误处理的要求
·端点能接收或发送的包的最大长度
·端点的传送类型
·端点与主机的数据传送方向
每一种设备都有一个缺省的端点0作为控制传输的通道, 除缺省控制通道的缺省端点外,其它端点只有在设备被设置后才可使用。
传输类型
·控制传输:可靠的、非周期性的、由主机软件发起的请求或者回应的传送,通常用于命令事务和状态事务。对于全速设备,数据包长度可以为8,16,32,64个字节,低速设备长度只能为8个字节。
·同步传输:在主机与设备之间的周期性的、连续的通信,一般用于传送与时间相关的信息。这种类型保留了将时间概念包含于数据中的能力。但这并不意味着,传送这样数据的时间总是很重要的,即传送并不一定很紧急。
·中断传输:小规模数据的、低速的、固定延迟的传送。
·批量传输:非周期性的,大包的可靠的传送。典型地用于传送那些可以利用任何带宽的数据,而且这些数据当没有可用带宽时,可以容忍等待。
这四种传输类型各有千秋,将在第八章中详细描述。
关键的第八章
第八章从底向上的介绍了USB1.1的协议层,从数据包的格式到数据包的传输过程都做了详细的说明。
数据包格式
数据位被发送到总线的时候,首先是最低有效位(LSb),所有的包都从同步(SYNC)字段开始的。同步字段里的最后的2位是同步字段结束的记号,并且标志了包标识符(PID,Packet Identifer)的开始。
如图8-1所示,包标识符由4位的包类型字段和其后的4位的校验字段构成,主机和所有功能部件都必须对得到全部PID字段实行完整的译码。
表8-1列出了包标识符类型,编码及其描述:
协议规定了4种不同类型的数据包格式
标记包:标记由PID,ADDR和ENDP构成,其中PID指定了包是输入,输出还是建立类型。对于输出和建立事务,地址和端口字段唯一地确定了接下来将收到数据包的端口。对于输入事务的,这些字段唯一地确定了哪个端口应该传送数据包。只有主机能发出标记包。
帧开始包:主机以每1.00 ms ±0.0005 ms一次的额定速率发出帧开始(SOF)包。如图8-6中所示,SOF包是由指示包类型的PID和其后的11位的帧号字段构成。对帧时间敏感而不需要追踪帧数(例如集线器)的功能部件,仅需对SOF的PID译码 ;可忽略帧数和其CRC。如果功能部件需要追踪帧数,它必须对PID和时间戳都进行译码。对总线时间调配信息的没有特别需要的全速设备可以忽略SOF包。
数据包:如图8-7所示,数据包由PID,包括至少0个字节数据的数据区和CRC构成。有2种类型的数据包,根据不同的PID:DATA0和DATA1来识别。2种数据包PID是为了支持数据切换同步(Data Toggle Synchronization)而定义的。
握手包:如图8-8所示,握手包仅由PID构成。握手包用来报告数据事务的状态,能还在表示数据成功接收,命令的接收或拒绝,流控制(Flow Control)和停止(Halt)条件。只有支持流控制的事务类型才能返回握手信号。
三种握手:ACK(表示数据包被正确接收),NAK(表示设备不能接收数据或者设备不能发送),STALL(表示设备不能传输,或者接收数据,或者不支持一个控制管道请求)
有了以上的基础知识做铺垫,下面重点学习一下四种传输方式,按重要程度排列。
控制传输
控制传送最少有2个事务阶段:建立和状态,数据阶段可选。建立阶段由主机发送SETUP包,收到SETUP包的设备必须返回ACK。
所有的数据阶段里的事务都必须有相同的方向(即全部输入或者全部输出)。在数据时相中要发送的数据的数量和其方向在建立阶段里被指定。如果数据的数量超过了先前确定的数据包大小,数据在支持最大的包大小的多个事务中被发送(输入或者输出)。任何剩下的数据都作为剩余在最后的事务中被发送。
控制传送的状态阶段是序列中的最后一个操作。状态阶段是以相对前面的阶段的数据流方向的变化来刻划的,并且总是使用DATA1 PID。例如,如果数据阶段由输出事务构成的,状态是单一的输入事务。如果控制传送序列没有数据阶段,那么它由建立阶段和其后的由输入事务构成的状态阶段构成。下图说明了事务顺序,数据时序位的值和控制读写序列的数据PID类型。时序位显示在括号中。
状态响应 | 控制写传送(在数据时相发送) | 控制读传送(在握手时相发送) |
功能部件完成 | 零长度的数据包 | ACK握手 |
功能部件有一个错 | STALL 握手 | STALL握手 |
功能部件忙 | NAK 握手 | NAK握手 |
状态阶段响应表
当全部的数据结构被返回到主机的时候,功能部件应该向管道返回一个短于包最大长度(MaxPacketSize)的包以表明数据阶段的结束。如果数据结构是数据是管道包最大长度(wMaxPacketSize)整数倍,功能部件将返回零长度的包以表明数据阶段的结束。
批量传输
批处理事务类型的特点是以错误检测和重试的方式保证主机和功能部件之间的数据的无错发送的能力。如下图所示,批处理事务是由标记,数据和握手包构成的三时相的事务。在某些流控制和挂起条件下,数据时相被握手信号替换,从而产生了没有数据传输的两时相的事务。
当主机准备好了接收批处理数据的时候,它发出输入标记。功能部件端口通过返回数据包,或者如果不能返回数据,则返回NAK或STALL握手作为应答。NAK表示功能部件暂时不能返回数据,而STALL表示端口永久地被停止,需要USB系统软件干涉。如果主机收到合法的的数据包,则它用ACK握手来应答。如果收到数据时主机检测到错误,它不返回握手包给功能部件。当主机准备好了传送成批数据的时候,它首先发出一个后跟数据包的输出标记包。如果数据由功能部件无错地接收到,那么它将返回三个握手中的一个 :
ACK表示数据包无错地接收到,通知主机可以发送下一个包;
NAK表示数据被无错地收到,但主机应该重新发送数据因为数据功能部件处于妨碍它接受数据的暂时的条件(例如缓冲满)中;
如果端口被停止,则返回STALL以告诉主机不要重试传输,因为功能部件上有错误条件。
如果接收到的数据有CRC或者位填充错,那么不返回任何握手。
下图说明了时序位和数据PID在成批读和写中的用法。数据包同步经数据时序切换位(Sequence Toggle Bit)和DATA0/DATA1 PID的使用而达到。当端口经历配置事件(Configuration event)(配置事件在节9.1.1.5和9.4.5中有解释)的时候,批处理端口的切换时序被初始化为DATA0。端口上的数据切换不是作为短包传送或IRP撤消的直接结果而被初始化的。
主机总是通过配置事件初始化总线传送的第一个事务为DATA0 PID。第二的事务使用DATA1 PID,并且,剩余的后继数据传送轮流切换。数据包发送器根据ACK的接收情况来切换而接收器根据数据包的接收(receipt)和验收(acceptance)的情况切换。
中断传输
中断事务可由输入或输出构成。一收到输入标记,功能部件便可返回数据,NAK或STALL。如果端口没有新的中断信息(即没有等待事务的中断)可供返回,功能部件在数据时相里返回NAK握手。如果中断端口的停止特征被设置了,功能部件将返回STALL握手。如果中断是等待事务的,功能部件象数据包那样返回中断信息。作为对数据包接收的反应,主机如果数据无错地被接受则发出ACK握手,或者如果数据包损坏则不返回握手。当端口为实际的中断数据使用中断传送机制的时候,必须遵循数据切换协议。这使功能部件得知主机收到了数据,并且事件条件被清除。
同步传输
如下图所示,同步(ISO)事务有标记和数据时相,而没有握手时相。主机发出输入或输出标记,后跟着端口(输入)或主机(输出)传送数据的数据时相。ISO事务不支持握手时相或重试能力。设备或主机控制器应该只发送DATA0,ISO事务不支持切换时序。
继续看第九章
本章主要描述了标准的请求命令以及USB设备的各种描述符。
标准设备请求
所有的USB设备在设备的缺省控制通道对主机的请求发出响应。这些请求是通过使用控制传输来达到的,请求及请求的参数通过Setup包发向设备,由主机负责设置Setup包内的每个域的值。每个Setup包有8个字节。
偏移量 | 域 | 大小 | 值 | 描述 |
0 | bmRequestType | 1 | 位图 | 请求特征: D7: 传输方向 0=主机至设备 1=设备至主机 D6..5: 种类 0=标准 1=类 2=厂商 3=保留 D4..0: 接受者 0=设备 1=接口 2=端点 3=其他 4..31=保留 |
1 | bRequest | 1 |
值 | 具体请求(参见表8-3) |
2 | wValue | 2 |
值 | 字长域,根据不同的请求含义改变. |
4 | wIndex | 2 |
索引或偏移 | 字长域,根据不同的请求含义改变.典型用于传送索引或偏移. |
6 | wLength | 2 |
| 如有数据传送阶段,此为数据字节数. |
USB协议定义了11中标准设备请求命令,所有设备都应该支持这些请求。
bmRequestType | bRequest | wValue | Windex | wLength | Data |
00000000B 00000001B 00000010B | CLEAR_FEATURE 01 | 特性选择符 | 零 接口号 端点号 | 零 | 无 |
10000000B | GET_CONFIGURATION 08 | 零 | 零 | 一 | 配置值 |
10000000B | GET_DESCRIPTOR 06 | 描述表种类和索引 | 零或语言标志 | 描述表长 | 描述表 |
10000001B | GET_INTERFACE 0A | 零 | 接口号 | 一 | 可选设置 |
10000000B 10000001B 10000010B | GET_STATUS 00 | 零
| 零 接口号 端点号 | 二 | 设备, 接口 ,或 端点状态 |
00000000B | SET_ADDRESS 05 | 设备地址 | 零 | 零 | 无 |
00000000B | SET_CONFIGURATION 09 | 配置值 | 零 | 零 | 无 |
00000000B | SET_DESCRIPTOR 07 | 描述表种类和索引 | 零或语言标志 | 描述表长 | 描述表 |
00000000B 00000001B 00000010B | SET_FEATURE 03 | 特性选择符 | 零 接口号 端点号 | 零 | 无 |
00000001B | SET_INTERFACE 0B | 可选设置 | 接口号 | 零 | 无 |
100000010B | SYNCH_FRAME 0C | 零 | 端点号 | 二 | 幀号 |
描述表种类 | 值 |
DEVICE | 1 |
CONFIGURATION | 2 |
STRING | 3 |
INTERFACE | 4 |
ENDPOINT | 5 |
这里选几个重要的请求说明一下
GET_DESCRIPTOR:获取描述符
这个标准请求包括三种描述符:设备、配置、及字串,所有的设备必须提供一个设备描述表并且至少一个配置描述表,如果一个设备不支持一个请求的描述表,则返回请求错误。
SET_ADDRESS:配置地址
本请求为设备的将来存取设置地址,wValue指出所要设置成的地址值
描述符
设备描述符:设备描述表给出了USB设备的一般信息。这包括对设备及所有设备配置起全程作用的信息。一个USB设备只能有一个设备描述表。
偏移量 | 域 | 大小 | 值 | 描述 |
0 | bLength | 1 | 数字 | 此描述表的字节数 |
1 | bDecriptorType | 1 | 常量 | 描述表种类为设备 |
2 | bcdUSB | 2 | BCD码 | 此设备与描述表兼容的USB设备说明版本号(BCD 码) |
4 | bDeviceClass | 1 | 类 | 设备类码 如果此域的值为0则一个设置下每个接口指出它自己的类,并个接口各自独立工作。 如果此域的值处于1~FEH之间,则设备在不同的接口上支持不同的类。并这些接口可能不能独立工作。此值指出了,这些接口集体的类定义。 如果此域设为FFH,则此设备的类由厂商定义。 |
5 | bDeviceSubClass | 1 | 子类 | 子类码 这些码值的具体含义根据bDeviceClass 域来看。 如bDeviceClass 域为零,此域也须为零 如bDeviceClass 域为FFH,此域的所有值保留。 |
6 | bDevicePortocol | 1 | 协议 | 协议码 这些码的值视bDeviceClass 和 bDeviceSubClass 的值而定。 如果设备支持设备基础上的类相关的协议,此码标志了设备类说明上的值。 如果此域的值为零,则此设备不在设备基础上支持设备类相关的协议。然而,它可能在接口基础上支持设备类相关的协议。 如果此域的值为FFH,此设备使用厂商定义的协议。 |
7 | bMaxPacketSize0 | 1 | 数字 | 端点0的最大包大小(仅8,16,32,64 为合法值) |
8 | idVendor | 2 | ID | 厂商标志(由USB标准付值) |
10 | idProduct | 2 | ID | 产品标志(由厂商付值) |
12 | bcdDevice | 2 | BCD 码 | 设备发行号(BCD 码) |
14 | iManufacturer | 1 | 索引 | 描述厂商信息的字串的索引。 |
15 | iProduct | 1 | 索引 | 描述产品信息的字串的索引。 |
16 | iSerialNumber | 1 | 索引 | 描述设备序列号信息的字串的索引。 |
17 | bNumConfigurations | 1 | 数字 | 可能的设置数 |
配置描述符:配置描述表给出了一设备配置的信息,此描述表给出了此配置下的接口数,每个接口可能独立操作。
偏移量 | 域 | 大小 | 值 | 描述 |
0 | bLength | 1 | 数字 | 此描述表的字节数。 |
1 | bDescriptorType | 1 | 常量 | 配置描述表类型 |
2 | wTotalLength | 2 | 数字 | 此配置信息的总长(包括配置,接口,端点和设备类及厂商定义的描述表) |
4 | bNumInterfaces | 1 | 数字 | 此配置所支持的接口个数 |
5 | bCongfigurationValue | 1 | 数字 | 在SetConfiguration()请求中用作参数来选定此配置。 |
6 | iConfiguration | 1 | 索引 | 描述此配置的字串描述表索引 |
7 | bmAttributes | 1 | 位图 | 配置特性: D7: 保留(设为一) D6: 自给电源 D5: 远程唤醒 D4..0:保留(设为一) 一个既用总线电源又有自给电源的设备会在MaxPower域指出需要从总线取的电量。并设置D6为一。运行时期的实际电源模式可由GetStatus(DEVICE) 请求得到。 |
8 | MaxPower | 1 | mA | 在此配置下的总线电源耗费量。以 2mA 为一个单位。 |
接口描述符:此描述表在一个配置内给出一个接口的信息。如果一个配置支持不止一个接口,端节点的描述表会跟在接口描述表后被返回,接口描述表总是作为配置描述表的一部分被返回。
偏移量 | 域 | 大小 | 值 | 说明 |
0 | bLength | 1 | 数字 | 此表的字节数 |
1 | bDescriptorType | 1 | 常量 | 接口描述表类 |
2 | bInterfaceNumber | 1 | 数字 | 接口号,当前配置支持的接口数组索引(从零开始) |
3 | bAlternateSetting | 1 | 数字 | 可选设置的索引值。 |
4 | bNumEndpoints | 1 | 数字 | 此接口用的端点数量,如果是零则说明此接口只用缺省控制管道。 |
5 | bInterfaceClass | 1 | 类 | 类值 零值为将来的标准保留。 如果此域的值设为FFH,则此接口类由厂商说明。 所有其它的值由USB 说明保留。 |
6 | bInterfaceSubClass | 1 | 子类 | 子类码 这些值的定义视bInterfaceClass域而定。 如果bInterfaceClass域的值为零则此域的值必须为零。 bInterfaceClass域不为FFH则所有值由USB 所保留。 |
7 | bInterfaceProtocol | 1 | 协议 | 协议码:bInterfaceClass 和bInterfaceSubClass 域的值而定.如果一个接口支持设备类相关的请求此域的值指出了设备类说明中所定义的协议.
|
8 | iInterface | 1 | 索引 | 描述此接口的字串描述表的索引值。 |
端点描述符:每个接口使用的结点都有自己的描述表,此描述表被主机用来决定每个节点的带宽需求。每个结点的描述表总是作为配置描述的一部分返回的,结点0无描述表。
偏移量 | 域 | 大小 | 值 | 说明 |
0 | bLength | 1 | 数字 | 此描述表的字节数 |
1 | bDescriptorType | 1 | 常量 | 端点描述表类 |
2 | bEndpointAddress | 1 | 端点 | 此描述表所描述的端点的地址。此地址的编码如下: Bit 3..0 : 端点号. Bit 6..4 : 保留,为零 Bit 7: 方向,如果控制端点则略。 0:出端点 1:入端点 |
3 | bmAttributes | 1 | 位图 | 此域的值描述的是在bConfigurationValue域所指的配置下端点的特性。 Bit 1..0 :传送类型 00=控制传送 01=同步传送 10=批传送 11=中断传送 所有其它的位都保留。
|
4 | wMaxPacketSize | 2 | 数字 | 当前配置下此端点能够接收或发送的最大数据包的大小。 对与同步传送此值用于为每幀的数据净负荷预留时间。而通道可能在实际运行时不需要预留的带宽。实际带宽可由设备通过一种非USB定义的机制汇报给主机. 对于中断传送,批传送,控制传送.端点可能发送较小的数据包。并且在结束传送后既有可能间隙时间来重启,也有可能不需要这段时间。具体请参照第五章。
|
6 | bInterval | 1 | 数字 | 轮寻数据传送端点的时间间隙。 此域的值对于批传送的端点及控制传送的端点忽略。对于同步传送的端点此域必需为1。对于中断传送的端点此域值的范围为1到255。 |
字串描述符:字串描述表是可有可无的。如前所述,如果一个设备无字串描述表,所有其它描述表中有关字串描述表的索引都必须为0。
偏移量 | 域 | 大小 | 值 | 描述 |
0 | bLength | 1 | N+2 | 此描述表的字节数 |
1 | bDescriptorType | 1 | 常量 | 字串描述表类型 |
2 | wLANGID[0] | 2 | 数字 | 语言标识(LANGID) 码0 |
… |
… |
… |
… |
… |
N | wLANGID[x] | 2 | 数字 | 语言标识(LANGID) 码X |
第十章个人感觉没那么重要,在这里就不细说了,大家自己简单浏览一下就OK!
最后举一个最简单的例子吧,当一个USB设备插入到主机以后,主机将依次完成以下操作:
1.对设备第一次复位
2.主机发送SETUP包获取设备描述符
3.对设备进行第二次复位
3.主机为设备设置地址
4,.主机再次获取设备描述符
5.主机获取配置描述符
6.主机获取字符描述符
7.结束