第一章 USB协议解析
目录
一、USB的全称: Universal Serial Bus(通用串行总线)
前言
本文根据usb2.0协议规范,讲解usb基础概念
一、USB的全称: Universal Serial Bus(通用串行总线)
二、USB的特点:可扩展,即插即用
现在的PC机基本都保留了鼠标,键盘,耳机,麦克风的专用接口,但是随着电子产品的发展,PC机的外设接口就不够用了,像打印机,移动硬盘,手机等也需要和PC机交换数据怎么办?为了减少PC机的专用 接口,就需要开发一种通用的数据交换接口,USB就是为了解决这个问题而设计出来的。
三、USB协议版本
- usb1.0 – low speed(低速),最大传输速率 1.5Mbps
-
usb 1.1 – full speed(全速),最大传输速率 12Mbps
-
usb 2.0 – high speed(高速),最大传输速率 480Mbps
-
usb 3.0 – super speed(超高速),最大传输速率 5Gbps
四、USB的总线拓扑图
来源:USB 2.0 Specification
USB系统由 Host, Hub, Func(usb device) 3部分组成。
4.1 Host:usb主机控制器
负责usb设备的供电,枚举,配置,数据的发送和接收,usb设备和总线状态的管理等;在嵌入式系统中,host一般都是集成在Soc中;
4.2 Hub: 集线器
提供USB的接入点,即port口,port口接入和拔掉usb设备的状态管理,负责port口的电源管理,向下兼容低速usb设备(如usb1.0的鼠标可以插到usb2.0的接口上使用);一般usb主机控制器也会集成一个hub,叫做root hub(根集线器)
usb物理上的可扩展和即插即用的功能基本就是Hub提供的;
典型的Hub示意图:
来源:USB 2.0 Specification
4.3 Func(usb device):usb设备
通过usb接口给主机提供额外功能,如外部存储功能(U盘,移动硬盘),usb鼠标,usb键盘,打印机等。Hub也属于usb设备的一种;
因为在数据传输过程中Hub和usb线都会造成延迟,所以USB总线拓扑图中最多允许有7层的深度(包括Host所在的第一层);
五、usb总线枚举
usb总线枚举很重要的作用就是要知道接入Hub port口的是什么样的usb设备;
比如:只有知道usb设备的最大工作电流,host才能决定是否让usb设备接入;只有知道usb的接口类型,host才能安排合适的软件驱动进行工作等;
usb协议规定使用4种描述符来描述一个usb设备:设备描述符,配置描述符,接口描述符,端点描述符;
4种描述符的层次关系示例如下:
5.1 设备描述符
描述usb设备信息,包括厂商ID,产品ID,usb版本号,支持的设备类,子类,设备遵循的协议,默认端点0的最大包长度等;
一个usb设备只有1个设备描述符;
5.2 配置描述符
描述这个配置包含的接口数量,供电模式,最大工作电流等;
usb设备可以包含1个或多个配置描述符,但工作时只能选1个配置描述符生效;
5.3 接口描述符
描述接口编号,端点数量,接口类,子类和遵循的协议等;一个接口代表一个基本功能,如鼠标功能,键盘功能;
一个usb设备可以有多个接口同时工作;
5.4 端点描述符
描述端点地址,数据传输类型,数据传输方向,数据接收或发送的最大包长度,轮询传输时的轮询频率等;
一个接口可以有多个端点,端点方向分为:输入端点和输出端点,除端点0外,一个端点只能承载一个方向的数据,端点0是默认控制端点,双向数据传输,只要usb设备上电并reset后就可以使用,主要用于设备初始化参数等;
端点的数据传输类型包括:控制,同步,批量,中断;
以上4种描述符的详细定义可参考 USB 2.0 Specification 文档;
5.5 usb总线枚举流程
- usb设备插到hub的port口上,hub将会给usb设备供电,并通知host有设备接入;
- host至少需要等待100ms,让usb设备完全插入hub的port口并供电稳定,等待完成后,host给hub下发 port enable和 port reset的命令;
- hub执行完 port enable和port reset命令后,usb设备的寄存器和状态都被复位,并让默认控制端点0处于工作状态,此时usb设备的工作电流不能超过100mA
- host 通过默认控制端点0给usb设备分配和设置一个usb host系统中唯一无歧义的地址
- host 从usb设备中读取所有配置信息
- host根据每个配置描述的工作电流,优先考虑usb协议标准设备等从中选一个配置,并把这个配置设置到usb设备
- host给选中的配置中的所有接口找到合适的驱动
问题:为什么要有默认端点0?
分析:一般来说,端点属于接口,一个接口就表示一个基本功能,但是usb设备刚接到Hub的port口时,host还不知道usb设备支持哪些接口,是usb鼠标还是usb键盘还是其它的usb类型设备,所以不能使用接口中的端点进行数据通信;要确定usb设备支持哪些接口,就需要端口通信来获取各种描述符;所以不管usb设备支持哪些接口,所有类型的usb设备必须要实现端点0的功能;
六、usb数据传输
6.1 管道(Pipe)
usb总线枚举结束后,host给usb设备支持的所有接口功能找到了合适的驱动后,驱动就接管了usb设备的数据传输操作;
数据传输首先要知道的是源地址,目的地址;
源地址就是主机的接收或者发送的缓存物理地址;
目的地址比较复杂,因为usb总线上可以接入多个usb设备,比如主机上可以同时使用usb鼠标,usb键盘,U盘等,所以host必须要给不同的usb设备分配唯一无歧义的设备地址,比如usb鼠标的设备地址是1,usb键盘的设备地址是2,U盘的设备地址是3,这个分配设备地址的操作是在usb总线枚举过程中确定的;
因为每个接口功能还有多个端点,所以数据传输时还需要指定端点,根据端点描述符的定义,端点有地址,方向,传输类型的属性;
所以目的地址就包含:usb设备地址,端点地址,端点方向,端点传输类型;
usb协议中用管道(pipe)来指代目的地址;Linux内核驱动中用一个整形值来表示管道,bit7用来表示端点方向,bit8~bit14表示usb设备地址,bit15~bit18表示端点号,bit30~bit31表示管道类型;
所以,数据传输必须要通过端点,只要提供了管道就能找到对应的端点;
来源:USB 2.0 Specification
6.2 端点(Endpoints)
端点是数据传输的基本单元,端点有地址,方向,类型属性
端点的方向是以host的视角来确定,类型有:IN,OUT
- IN:host接收usb设备的数据
- OUT:host发送数据给usb设备
端点的数据传输类型包括:控制,同步,批量,中断
6.2.1 控制传输(Control Transfers)
突发、非周期、主机软件发起的请求/响应数据传输,通常用于命令/状态操作;
比如usb总线枚举过程中获取设备描述符,配置描述符,设置usb设备地址等;
6.2.2 同步传输(Isochronous Transfers)
周期连续的数据传输,一般是时间敏感的数据流,传输过程不保证数据一定传输正确,但保证在周期内把数据传输完毕;
比如传输的视频流或音频流;
6.2.3 批量传输(Bulk Transfers)
非周期、大数据包突发数据传输,通常用于可以使用任何可用带宽的数据,也可以延迟到可用带宽。
比如U盘文件传输;
6.2.4 中断传输(Interrupt Transfers)
低频、有限延迟的数据传输;
比如usb鼠标和usb键盘的数据传输;
以上4种传输类型的详细定义请参考 USB 2.0 Specification 文档
6.3 USB协议层
usb协议规定数据流组织方式:bit → packet → transaction → (micro)frame
6.3.1 bit
usb总线上发送和接受的数据都转化成二进制表示(0和1),再把0和1转换成高低电平;
6.3.2 packet
多个bit组成一个packet;
packet被划分成多个Field,每个Field由若干个bit组成:SYNC Field, Packet Identifier Field(PID),Address Fields, Frame Number Field,Data Field,CRC Field;
其中Address Fields包括:usb设备地址 和 端点地址;
PID:表示packet的类型,常见有3种:token,data,handshake
- token packet
由8bit PID + 7bit usb设备地址 + 4bit 端点地址 + 5bit CRC5 组成;
PID表示packet的类型:IN,OUT,SETUP
token packet主要起到确定传输目的地址和方向的作用
- data packet
由8bit PID + 0~8192bit data + 16bit CRC16 组成;
PID表示packet的类型:DATA0,DATA1, DATA2, MDATA
PID主要用于保证数据传输过程中在多个事务中的数据序列同步
- handshake packet
由 8bit PID 组成;
handshake packet主要用于回复数据传输结果的状态:数据接收成功,命令被接收或被拒绝,处于流控状态,被挂起 等;
详细定义请参考 USB 2.0 Specification 文档
6.3.3 transaction(事务)
由一个或多个packet组成;
一般是 1个 token packet + 1个 data packet + 1 个handshake packet 组成一个事务;根据不同类型的端点,组成transaction的packet个数和类型会有所不同;
抽象理解transaction要干的活:发一个带目的地址和方向(接收或发送)的packet,通知usb总线上对应的usb设备准备接收或发送数据,接收或者发送一个data packet,回复数据接收或发送结果;
6.3.4 (micro)frame
由若干个 transaction 组成;
为了保证数据传输的速度和准确性,对于低速或全速(low/full-speed)usb设备,host每隔 1ms 发送一个 Start-of-Frame(SOF) packet,对内部端点时钟和host时钟进行同步;对于高速(high-speed)usb设备,host 每隔 125us 发送一个 Start-of-Frame(SOF) packet;
1ms被定义为frame,125us被定义为micro-frame;
在frame或micro-frame时间内会传输若干个transaction;
来源:USB 2.0 Specification
问题:为什么要区分不同的数据传输类型?
考虑这样的使用场景:一个usb总线上接入了100个U盘和1个usb键盘,100个U盘都在传送大批量的文件,毫无疑问usb总线上大部分时间都在传输U盘文件数据,极小部分时间传输usb键盘数据,那些还未来得及传输的U盘文件数据就会放入长长的buffer队列等待usb总线传输,如果usb键盘数据也放入长长的buffer队列等待传输,就会导致在usb键盘上敲字母的时候,电脑反应很慢,这是不能接受的;
区分不同的数据传输类型,就可以对不同的传输类型使用不同的传输策略,提高usb总线利用率;
比如一座桥上,划分出人行道和机动车道,走路速度慢,只能用人行道通过;车辆速度快,只能从机动车道通过;人行车道远比机动车道窄,这样就可以大大提高这座桥的利用率;
当软件驱动发起一次数据传输,即 IRP(I/O Request Packet),host会把IRP切割成多个 transaction来完成,比如U盘驱动发起一次写10MB数据到U盘的请求,这个请求(IRP)会被切割成多个transaction来完成;
来源:USB 2.0 Specification
我们知道,usb协议规定总线是按(micro)frame来传输数据,每个(micro)frame时间内可以传输若干个transaction,每个transaction可以属于不同端点类型;
来源:USB 2.0 Specification
为了保证对 Control Transfers,Isochronous Transfers,Bulk Transfers,Interrupt Transfers 4种传输类型得到比较公平的数据传输机会,usb协议规定在(micro)frame时间内,传输每种类型的transaction时间占(micro)frame的比例不能超过特定的值;
比如高速(high-speed)usb设备:
control transfers 最多占用20%的micro-frame时间;
isochronous transfers + interrupt transfers(这2种类型都是周期传输) 最多占用 80% 的micro-frame时间;
bulk transfers 只有在micro-frame还有剩余空闲时间时才能进行数据传输;
micro-frame就像是10节车厢的火车,每125us(usb协议规定的micro-frame时间长度)发一趟车,
第1~2节车厢:
- 优先装载 control transfers的transaction;
- 如果没装满,优先考虑装载isochronous transfers或 interrupt transfers 的transaction;
- 如果还没装满,装载 bulk transfers的transaction
第3~10节车厢:
- 优先装载isochronous transfers或 interrupt transfers 的transaction;
- 如果没装满,装载 bulk transfers的transaction;
如果10节车厢没有装满,就装载 bulk transfers的transaction;
Linux内核线程调度单位是时间片,usb总线的调度单位是(micro)frame;
usb总线的调度算法值得参考;
参考资料
- USB 2.0 Specification
- Linux那些事之我是USB