USB协议有很多博主讲的很详细了,我这里就是按照我个人的理解总结下,本文章倾向于总结结构,关于结构中的每个字节的具体含义推荐大家还是去找手册,我这里只是一笔带过。
本文章借鉴了其他博主的一些内容,侵删!!!!
1 USB协议术语
首先来看下USB通信的简单框图
如上图所示,左边是主机(上位机),右边是USB设备,对于USB协议来说,整个通信链路上,只能有一个主机,剩下的都是从机,一个主机最多挂载127的设备,为什么是127个设备呢,这个是和协议中的一个指令参数有关,后续我们再说。
1.1配置(Configuration)
一个USB设备可以有多种配置,不同的配置对应于完全不同的功能,比如一会是作为U盘用,一会是作为网卡用,这就是两个不同的配置。
主机可以控制设备选择哪个具体的配置,但是设备当前只能选择一个配置。一般1个USB设备只包含一个配置。
1.2 接口(Interface)
配置下一级是接口
一个配置可以有多个接口,每个接口都是这个配置下的一个小的功能,比如UVC类USB,分为 控制通信 interface功能和数据interface功能,比如,一个打印机可以有一个接口用于打印,另一个接口用于管理。
1.3 End point (ep)端点
接口下一级是端点
一个接口完成一种功能,每种接口(功能)配有1个或者多个端点,多个端点共同完成某个接口的功能。
端点和主机之间的通信 线路 被称之为 pipes(管道)。
主机和USB设备通信,实际上是要指定和USB设备中的哪个端点通信,因为一个USB设备可以支持多个不同的端点,每个端点都可以传输不同的数据,这些端点共享USB的带宽,(因为物理链路上就只有那么几根,速率也是固定的)
每个USB设备最多支持16个端点同时每个USB设备都必须支持端点0,因此用户最多只能多加15个端点。注意除了控制端点0是双向的以外,其余的每个端点的方向是单向的,要么是IN,要么是OUT,不能同时IN OUT。
为什么是16个端点和127个USB设备呢,这个是由 USB通信协议规定的,USB通信协议的最基本单位是包(packet),一个包的基本格式如下图,下图中的ADDR中的7bit是USB设备地址,就是指是和哪个USB设备通信的,4bit ENDP是端点号,端点号是指和USB设备中的哪个端点通信的。
端点的分类
1 Control Endpoint: 端点0,最基本的控制通信指令传输。
2. Bulk Endpoint: 用于大批量数据传输的端点
3. Interrupt Endpoint: 用于低延迟的中断传输端点
4. Isochronous Endpoint: 同步端点,用于对延迟要求比较低的场景,比如同步视屏,这些场景可以丢数据,但是不能有延迟
上述术语结构可以如下图所示:
1.4 枚举和重枚举
枚举是指当USB设备插入后,HOST会探测USB管脚上的电信号状态,当电信号状态发生改变,则Host 会通过USB协议读取 USB设备的 设备描述符,然后发送SET_CONFIGURATION request。
重枚举就是进行两次枚举,这可能发生在设备重新启动或发生了某些变化导致需要重新枚举的情况下。在重枚举过程中,主机会重新执行枚举过程中的步骤,以确保设备的正确配置。
比如对于cyusb3014来说:
当第一次插入USB时,FX3自动枚举并通过USB电缆下载固件和USB描述符表。然后触发软断开连接,再这之后,FX3再次枚举,这一次作为由下载的信息定义的设备。这种两步枚举,被称为重枚举
1.5 USB 速率 (Super-Hi-Full- low Speed )
1) USB 3.0 (SuperSpeed) interface at 5 Gbps
2) USB 2.0 (Hi-Speed) interface at 480 Mbps
3) USB 1.1 (Full Speed) interface at 12 Mbps
4 )USB 1.0 (Low Speed) interface at 1 Mbps
1.6 USB描述符
USB描述符是USB设备的特色,USB各种各样灵活的功能就是通过USB描述符实现的。USB描述符是对设备的配置、接口、端点、字符串的描述,主机会在枚举此设备的时候根据设备实现的描述符去确定设备到底是一个什么样的设备、设备需要的总线资源、和设备的通讯方式等等。
2 USB传输协议
USB所有的数据传输都是通过包packet 来进行的,不同的包packet类型组成事务(Transction),不同的事务类型组成传输(Transfer )。
2.1 包packet
一个包的基本格式:注意不同类型的包包含的内容不一样,后面具体说明
不过所有包的基本格式都是:
包的类型通过PID字节分为 :令牌包,数据包,握手包,帧首包(SOF)
1 令牌包 :令牌包包含 OUT/ IN/ SETUP ,这个包的主要作用是标识本次传输事务是什么类型的,发到哪里的,因此令牌包没有数据. 具体的数据或者指令在后面的数据包中。
2 数据包 主要发送数据,没有ADDR+ENDP ,PID用于识别不同类的 数据包,这个包的主要作用是利用DATA 关键字传输数据或者指令。
3 握手包 握手包只有PID部分,没有其他部分,主要是用来握手用的。
4帧首包
如下图所示:PID是 0XA5
SOF包每隔一端时间就从HOST 发送一个,发送到总线上的所有设备,不会引起任何功能生成应答包。
SOF包由两个作用。第一个作用是当一个功能检测到SOF包后,就知道USB主机控制器开始启动一个帧(微帧)了。第二个作用时标功能, 每一个设备功能可以接收SOF包,通过Frame域来判断当前的时间走到哪里了。
2.2 事务
事务类型分为:SETUP 事务,IN事务,OUT 事务
每一个事务类型: 比如SETUP 事务,IN事务,OUT 事务 的 结构都是
每个事务类型中的:
令牌包用于标识本次传输是发到哪里的,方向是什么
数据包用于传输具体的指令或者数据
握手包用于握手
比如SETUP事务的结构:
IN事务的结构
OUT事务结构
2.3 传输
传输类型分为:控制传输,批量传输,中断传输,同步传输
每一个传输类型都是由事务组成的
控制传输:
如上图,控制传输中的建立阶段是Setup事务,其他传输都没有,Setup事务主要是通过Setup事务中的 数据包中的DATA字节传输要请求什么命令,然后在数据阶段 从设备 通过 IN 事务中的 数据包返回 SETUP事务请求的数据,最后 HOST再发送一个 OUT 事务(状态阶段)
同步传输:分为读和写,方向不一样
同步传输只有令牌包(通知后面的数据包是给谁的),然后若干个 数据包,没有握手包。
批量传输:批量传输分为读和写,方向不一样
首先通过 IN(OUT)事务中的令牌包通知后面的数据包是给谁的,方向是什么,然后通过IN(OUT)事务中的数据包发送数据,这里可能会发送多个数据包,最后发送握手包
中断传输:分为读和写,方向不一样
首先通过 IN(OUT)事务中的令牌包通知后面的数据包是给谁的,方向是什么,然后通过IN(OUT)事务中的数据包发送数据,这里可能会发送多个数据包,最后发送握手包
他们之间的关系如下图
本图来自博客: 一个早期的程序员,侵删!!!!!!
关于更加详细的结构解释参考我自己画的图
2.4 建立连接(枚举)
当主机检测到D+与D-之间有电压差,就认为有新的设置接入,然后主机就会发送一系列的请求(Resqusts)给设备。
主机以控制传输(Control Transfer)的方式,通过端点0(Endpoint 0)对设备发送各种请求(Resqusts),设备收到主机发来的请求后回复相应的信息,进行枚举(Enumerate)操作。所有USB设备必须支持标准请求(StandardRequests),控制传输方式(Control Transfer)和端点0(Endpoint 0)。
这里建立连接的时候发送的数据,是通过上面说的 控制传输 方式来通信的:
上面说的请求Resqusts字段 是在 控制传输中 的SETUP事务中 的DATA0 包中的 数据阶段的,如下图
每个请求request的 格式都是8个字节
这些 请求的格式如下图:
bmRequestype : 请求的分类,比如标准请求Standard ,用户请求 Vendor....我们主要关注标准请求,这个是 建立连接阶段必须要发送的。
bRequest: 每个请求类下面的 不同指令,比如标准请求的分类是:Clear_Feature、Get_Configuration、Get_Descriptor、Get_Interface、Get_Status、Set_Address、Set_Configuration、Set_Descriptor、Set_Interface、Set_Feature、Synch_Frame。...
连接过程
设备插入
1)用户插入USB(设备处于上电状态,端口不工作),Hub检测各个端口(能判断是什么设备)并用中断端点向host报告各个端口状态。
2)host发送request(Get_Port_Status)请求连接设备,hub回复设备速度类型信息。
3)host等待100ms,让电源稳定,发送复位请求(Set_Port_Feature)到设备(单对单),host不断查询设备复位状态(Get_Port_Status请求),hub持续10ms复位,当撤销后设备就处于默认/空闲状态(Default state),准备接收主机发来的请求。
4)主机发送地址为0的Get Descriptor请求(当设备第一次联机的时候必须回复地址为0的请求) (再次复位,再次复位的目的是使设备进入一个确定的状态,这个不是规定的状态,是windows的行为)。然后设备回复这个指令
5)Host通过Set_Address请求向设备分配一个唯一的地址。在完成这次传输之后,设备进入地址状态(Address state),之后就启用新地址继续与主机通信。这个地址对于设备来说是终生制的,设备在,地址在;设备消失(被拔出,复位,系统重启),地址被收回。同一个设备当再次被枚举后得到的地址不一定是上次那个了。
枚举过程
6) 然后正式进入描述符的解析,主机发送 Get_Descriptor请求到新地址读取设备描述符,这次主机发送Get_Descriptor请求可算是诚心,它会认真解析设备描述符的内容。设备接到包后就开始解析包(其实就是你在固件程序里判断处理) ,然后按固定格式返回自己设备的设备描述符,这一步主要是主机知道你的USB设备的基础属性。设备描述符内信息包括端点0的最大包长度,设备所支持的配置(Configuration)个数,设备类型,VID(Vendor ID,由USB-IF分配), PID(Product ID,由厂商自己定制)等信息。之后主机发送Get_Descriptor请求,读取配置描述符(Configuration Descriptor),字符串等,逐一了解设备更详细的信息。
7)主机通过解析描述符后对设备有了足够的了解,选择一个最合适的驱动给设备。 然后tell the world(announce_device)说明设备已经找到了,最后调用设备模型提供的接口device_add将设备添加到 usb 总线的设备列表里,然后 usb总线会遍历驱动列表里的每个驱动,调用自己的 matc(usb_device_match) 函数看它们和你的设备或接口是否匹配,匹配的话调用device_bind_driver函数,现在就将控制权交到设备驱动了。(读完设备描述符,控制权交给驱动,设备进入配置状态)驱动(注意,这里是驱动,之后的事情都是有驱动来接管负责与设备的通信)。
8)根据前面设备回复的信息,发送Set_Configuration请求来正式确定选择设备的哪个配置(Configuration)作为工作配置(对于大多数设备来说,一般只有一个配置被定义)。至此,设备处于配置状态(Configured),当然,设备也应该使能它的各个接口(Interface)
至此一个基本的USB协议框架就完成了