目录
1. 物理特性
1.1 引脚
1.2 USB 信号(差分信号)
1.3 USB 信号编码(NRZI)
1.4 USB 字节序
1.5 USB 设备检测
2. 通信协议
2.1 包组成(Packets Content)
2.1.1 PID 域
2.1.2 Address 地址域
2.1.3 Frame Number 帧号域
2.1.4 Data 数据域
2.1.5 CRC域
2.2 包类型(Packets Type)
2.2.1 令牌包(Token)
2.2.2 数据包(Data)
2.2.3 握手包(Handshake)
2.3 事务(Transaction)
2.3.1 SetUp 事务
2.3.2 IN 事务
2.3.3 OUT 事务
2.4 传输(Transfer)
2.4.1 控制传输 (Control Transfer)
2.4.2 批量传输 (Bulk Transfer)
2.4.3 中断传输 (Interrupt Transfer)
2.4.4 同步传输 (Isochronous)
3. USB 请求(用于控制传输)
4. USB 描述符
4.1 设备描述符
4.2 配置描述符
4.3 接口描述符
4.4 端点描述符
4.5 字符串描述符
5 USB 枚举
5.1 获取设备描述符 GET_DESCRIPTOR
5.2 设置地址SET_ADDRESS
5.3 请求设备描述 GET_DESCRIPTOR
5.4 请求配置描述 GET_DESCRIPTOR
5.5 读取完整设备描述及配置描述
5.6 配置设备 SET_CONFIGURATION
一条USB传输线分别由地线、电源线、D+和D-四条线构成,D+和D-是差分输入线,它使用的是3.3V的电压(与CMOS的5V电平不同),而电源线和地线可向设备提供5V电压,最大电流为500mA(可以在编程中设置)。
引脚标号 | 信号名称 | 缆线颜色 |
1 | Vcc | 红 |
2 | Data- (D-) | 白 |
3 | Data+ (D+) | 绿 |
4 | GND | 黑 |
USB设备可以直接和HOST通信,或者通过Hub和Host通信。一个USB系统中仅有一个USB 主机,设备包括USB功能设备和USB HUB,最多支持127个设备。物理连接指的是USB传输线。在USB 2.0系统中要求使用屏蔽双绞线。
使用差分信号进行数据传输,有效的降低外接带来的干扰。
USB 数据传输的传输使用反向不归零编码进行传送(NRZI)
反向不归零编码方式可以保证数据的完整性,而且不要求传输过程中由独立的时钟信号。
LSB 先发,MSB 后
USB1.0和USB1.1支持1.5Mb/s的低速模式和12Mb/bs的全速模式。在USB2.0以上支持480Mb/s的高速模式。
当 USB 设备接入系统时刻,系统通过检测 USB 上的 D+或者D- 线上的上拉电阻的方式来识别低速和全速设备。如下图所示,当主机端没有设备接入的时候,其D+和D-的下拉电阻使得其电压几乎为 0V;当全速/低速设备接入后,在D+或者D-端的上拉电阻会使得D+/D-出现高电平,而另外一根是低电平。主机端便知道有设备插入。
对于高速设备,和全速设备一样,在D+上存在上拉电阻,对于高速设备的的识别,主机先把高速设备检测为全速设备,然后再通过“Chirp 序列”的总线握手机制来识别高速和全速设备。
USB 协议中:
域组成了包,不同的域,组成了不同类型的包(Token、Data、HandShake)。
多个包一起组成了一个事务(SetUp事务,IN 事务,OUT 事务)
多个事务,进而组成了不同类型的传输(控制传输,中断传输,批量传输,同步传输)
包(Packet)是USB系统中信息传输的基本单元,所有数据都是经过打包后在总线上传输的。数据在 USB总线上的传输以包为单位,包只能在帧内传输。高速USB总线的帧周期为125us,全速以及低速 USB 总线的帧周期为 1ms。帧的起始由一个特定的包(SOF 包)表示,帧尾为 EOF。EOF不是一个包,而是一种电平状态,EOF期间不允许有数据传输。
包是USB总线上数据传输的最小单位,不能被打断或干扰,否则会引发错误。若干个数据包组成一次事务传输,一次事务传输也不能打断,属于一次事务传输的几个包必须连续,不能跨帧完成。一次传输由一次到多次事务传输构成,可以跨帧完成。
USB包由五部分组成,即同步字段(SYNC)、包标识符字段(PID)、数据字段、循环冗余校验字段(CRC)和包结尾字段(EOP),包的基本格式如下图:
不同的 PID 标识了不同类型的 USB 包。由四位标识符 + 四位标识符反码构成。
这里只用(PID0~4),PID4~7是PID0~4的取反,用来校验 PID
根据 USB2.0 的 Spec 描述:
可以看出,由 PID 主要将 USB 的包分为了 4 类:
1. 令牌包(Token) :
0x01:输出(OUT)启动一个方向为主机到设备的传输,并包含了设备地址和标号。
0x09:输入(IN) 启动一个方向为设备到主机的传输,并包含了设备地址和标号。
0x05:帧起始(SOF)表示一个帧的开始,并且包含了相应的帧号。
0x0d:设置(SETUP)启动一个控制传输,用于主机对设备的初始化。
2. 数据包(Data) :
0x03:偶数据包(DATA0)。
0x0b:奇数据包(DATA1)。
0x07:高速设备的 PID 的同步包
0x0f :高速设备分离包,高带宽的同步事务
3. 握手包(HandShake):
0x02:确认接收到无误的数据包(ACK)。
0x0a:无效(NAK),接收(发送)端正在忙而无法接收(发送)信息。
0x0e:错误(STALL),端点被禁止或不支持控制管道请求。
0x06:无响应(NYET)。
4. 特殊类:
前导包,错误包,分裂事务和 PING 测试
PID 数据传输方向
IN Device->Host
OUT Host->Device
SETUP Host->Device
PING Device->Host
地址域有两部分组成:
【 7bits 的设备地址 ADDR + 4 bits 的端点地址 ENDP】
可以知道,USB 系统最大支持链接 127 个设备,每个设备最多 32 个端点。
这个 ENDP 只用在 IN/OUT/SETUP令牌包中。
当USB令牌包的 PID 为 SOF时候,其数据字段必须为11位的帧序列号。
帧号占11位,主机每发出一个帧,帧号都会自加1,当帧号达到0x7FF时,将归零重新开始计数。对于同步传输有重要意义。
仅存在于 DATA 信息包,根据不同的传输类型,拥有不同大小的字节(0--1023字节)
用于进行数据的 CRC 校验
根据域的 PID 描述,令牌包有4种:
令牌包有四种:
输入包(IN)、输出包(OUT)和设置包(SETUP)的格式都是一样的:
SYNC + PID + ADDR(7 bits) + ENDP(4bits) + CRC5(五位的校验码)
帧起始包(SOF)的格式:
SYNC + PID + 11位FRAM + CRC5(五位的校验码)
SOF包由Host发送给Device
对于full-speed总线,每隔1.00 ms ±0.0005 ms发送一次;
对于high-speed总线,每隔125 μs ±0.0625 μs发送一次;
分为DATA0包和DATA1包,当USB发送数据的时候,如果一次发送的数据长度大于相应端点的容量时,就需要把数据包分为好几个包,分批发送,DATA0包和DATA1包交替发送,即如果第一个数据包是DATA0,那第二个数据包就是DATA1。但也有例外情况,在同步传输中(四类传输类型中之一),所有的数据包都是为DATA0,格式如下:
SYNC + PID + 0~1023字节 + CRC16
结构最为简单的包,格式如下:
SYNC + PID
在USB上数据信息的一次接收或发送的处理过程称为事务处理(Transaction)。一个事务由一系统packet组成,具体由哪些packet组成,它取决于具体的事务。
USB 事务分为3类:
事务 | 描述 |
---|---|
SetUp 事务 | 主机用来向设备发送控制命令 |
Data IN 事务 | 主机用来从设备读取数据 |
Data OUT 事务 | 主机用来向设备发送数据 |
•【正常】的设置事务处理
•【设备忙时】的设置事务处理
•【设备出错】的设置事务处理
输入事务处理:表示USB主机从总线上的某个USB设备接收一个数据包的过程。
•【正常】的输入事务处理
•【设备忙】时的输入事务处理
•【设备出错】时的输入事务处理
输出事务处理:表示USB主机把一个数据包输出到总线上的某个USB设备的过程。
•【正常】的输出事务处理
•【设备忙时】的输出事务处理
•【设备出错】的输出事务处理
在USB的传输中,定义了4种传输类型:
• 控制传输 (Control Transfer)
• 中断传输 (Interrupt Transfer)
• 批量传输 (Bulk Transfer)
• 同步传输 (Isochronous)
控制传输由2~3个阶段组成:
1) 建立阶段(Setup)
2) 数据阶段(无数据控制没有此阶段)(DATA)
3) 状态阶段(Status)
每个阶段都由一次或多次(数据阶段)事务传输组成(Transaction)。
控制数据由USB系统软件用于配置设备(在枚举时),其它的驱动软件可以选择使用control transfer实现具体的功能,数据传输是不可丢失的。
1) 建立阶段(Setup)
主机从USB设备获取配置信息,并设置设备的配置值。建立阶段的数据交换包含了SETUP令牌封包、紧随其后的DATA0数据封包以及ACK握手封包。它的作用是执行一个设置(概念含糊)的数据交换,并定义此控制传输的内容(即:在Data Stage中IN或OUT的data包个数,及发送方向,在Setup Stage已经被设定)。
2) 数据阶段(DATA)
数据传输阶段:用来传输主机与设备之间的数据。根据数据阶段的数据传输的方向,控制传输又可分为3种类型:
A) 控制读取(读取USB描述符)
B) 控制写入(配置USB设备)
C) 无数据控制
A) 控制读取
是将数据从设备读到主机上,读取的数据USB设备描述符。该过程如下图的【Control Read】所示。对每一个数据信息包而言,首先,主机会发送一个IN令牌信息包,表示要读数据进来。然后,设备将数据通过DATA1/DATA0数据信息包回传给主机。最后,主机将以下列的方式加以响应:当数据已经正确接收时,主机送出ACK令牌信息包;当主机正在忙碌时,发出NAK握手信息包;当发生了错误时,主机发出STALL握手信息包。
B) 控制写入
是将数据从主机传到设备上,所传的数据即为对USB设备的配置信息,该过程如下的图【Control Wirte】所示。对每一个数据信息包而言,主机将会送出一个OUT令牌信息包,表示数据要送出去。紧接着,主机将数据通过DATA1/DATA0数据信息包传递至设备。最后,设备将以下列方式加以响应:当数据已经正确接收时,设备送出ACK令牌信息包;当设备正在忙碌时,设备发出NAK握手信息包;当发生了错误时,设备发出STALL握手信息包。
3) 状态阶段(Status)
状态阶段:用来表示整个传输的过程已完全结束。
状态阶段传输的方向必须与数据阶段的方向相反,即原来是IN令牌封包,这个阶段应为OUT令牌封包;反之,原来是OUT令牌封包,这个阶段应为IN令牌封包。
对于【控制读取】而言,主机会送出OUT令牌封包,其后再跟着0长度的DATA1封包。而此时,设备也会做出相对应的动作,送ACK握手封包、NAK握手封包或STALL握手封包。
相对地对于【控制写入】传输,主机会送出IN令牌封包,然后设备送出表示完成状态阶段的0长度的DATA1封包,主机再做出相对应的动作:送ACK握手封包、NAK握手封包或STALL握手封包。
用于传输大量数据,要求传输不能出错,但对时间没有要求,适用于打印机、存储设备等。
批量传输是可靠的传输,需要握手包来表明传输的结果。若数据量比较大,将采用多次批量事务传输来完成全部数据的传输,传输过程中数据包的PID 按照 DATA0-DATA1-DATA0-…的方式翻转,以保证发送端和接收端的同步。
一次批量传输(Transfer)由 1 次到多次批量事务传输(Transaction)组成。
USB 允许连续 3次以下的传输错误,会重试该传输,若成功则将错误次数计数器清零,否则累加该计数器。超过三次后,HOST 认为该端点功能错误(STALL),放弃该端点的传输任务。(重传机制)
翻转同步:发送端按照 DATA0-DATA1-DATA0-…的顺序发送数据包,只有成功的事务传输才会导致 PID 翻转,也就是说发送端只有在接收到 ACK 后才会翻转 PID,发送下一个数据包,否则会重试本次事务传输。同样,若在接收端发现接收到到的数据包不是按照此顺序翻转的,比如连续收到两个 DATA0,那么接收端认为第二个 DATA0 是前一个 DATA0 的重传。(重传机制)
它通过在硬件级执行“错误检测”和“重传”来确保host与device之间“准确无误”地传输数据,即可靠传输。它由三种包组成(即IN事务或OUT事务):
1) token
2) data
3) handshake
For IN Token (即:IN Transaction)
• ACK: 表示host正确无误地接收到数据
• NAK: 指示设备暂时不能返回或接收数据 (如:设备忙)
• STALL:指示设备永远停止,需要host软件的干预 (如:设备出错)
For OUT Token (即:OUT Transaction)
如果接收到的数据包有误,如:CRC错误,Device不发送任何handshake包
• ACK: Device已经正确无误地接收到数据包,且通知Host可以按顺序发送下一个数据包
• NAK: Device 已经正确无误地接收到数据包,且通知Host重传数据,由于Device临时状况(如buffer满)
• STALL: 指示Device endpoint已经停止,且通知Host不再重传
Bulk读写序列:
即由一系统IN事务或OUT事务组成。
中断传输由IN或OUT事务组成。
中断传输在流程上除不支持PING 之外,其他的跟批量传输是一样的。他们之间的区别也仅在于事务传输发生的端点不一样、支持的最大包长度不一样、优先级不一样等这样一些对用户来说透明的东西。主机在排定中断传输任务时,会根据对应中断端点描述符中指定的查询间隔发起中断传输。中断传输有较高的优先级,仅次于同步传输。
同样中断传输也采用PID翻转的机制来保证收发端数据同步。下图为中断传输的流程图。
中断传输方式总是用于对设备的查询,以确定是否有数据需要传输。因此中断传输的方向总是从USB设备到主机。
DATA0或DATA1中的包含的是中断信息,而不是中断数据。
它由两种包组成:
1) token
2) data
同步传输不支持“handshake”和“重传能力”,所以它是不可靠传输。
同步传输是不可靠的传输,所以它没有握手包,也不支持PID翻转。主机在排定事务传输时,同步传输有最高的优先级。同步传输适用于必须以固定速率抵达或在指定时刻抵达,可以容忍偶尔错误的数据上。实时传输一般用于麦克风、喇叭、UVC Camera等设备。实时传输只需令牌与数据两个信息包阶段,没有握手包,故数据传错时不会重传。
标准的USB设备请求命令是用在控制传输中的“初始设置步骤”里的数据包阶段(即DATA0,由8个字节构成)。
也就是说,是控制传输的建立阶段(SetUP)的 DATA0 的 8 个字节。
命令共有11个,大小都是8个字节,具有相同的结构,由5个字段构成(字段是标准请求命令的数据部分),结构如下(括号中的数字表示字节数,首字母bm,b,w分别表示位图、字节,双字节):
bmRequestType(1) + bRequest(1) + wvalue(2) + wIndex(2) + wLength(2)
表1、USB命令的结构 |
||||
偏移量 |
域 |
长度(字节) |
值 |
描述 |
0 |
bmRequestType |
1 |
位图 |
请求特征:
|
1 |
bRequest |
1 |
值 |
命令类型编码值(见表2) |
2 |
wValue |
2 |
值 |
根据不同的命令,含义也不同 |
4 |
wIndex |
2 |
索引或偏移 |
根据不同的命令,含义也不同,主要用于传送索引或偏 移 |
6 |
wLength |
2 |
|
如有数据传送阶段,此为数据字节数。 |
其中 bRequest 为命令编码值,含意见表2:
表2、USB标准命令的编码值 |
|
bRequest |
Value |
GET_STATUS |
0 |
CLEAR_FEATURE |
1 |
为将来保留 |
2 |
SET_FEATURE |
3 |
为将来保留 |
4 |
SET_ADDRESS |
5 |
GET_DESCRIPTOR |
6 |
SET_DESCRIPTOR |
7 |
GET_CONFIGURATION |
8 |
SET_CONFIGURATION |
9 |
GET_INTERFACE |
10 |
SET_INTERFACE |
11 |
SYNCH_FRAME |
12 |
对应的11种的命令,其他的域的含义对照表为:
表3、USB的11种标准命令 |
||||||
命令 |
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
Clear_Feature |
00000000B |
CLEAR_FEATURE |
特性选择符 |
零 |
零 |
无 |
Get_Configuration |
10000000B |
GET_CONFIGURATION |
零 |
零 |
一 |
配置值 |
Get_Descriptor |
10000000B |
GET_DESCRIPTOR |
描述表种类(高字节,见表5)和索引(低字节) |
零或语言标志 |
描述表长 |
描述表 |
Get_Interface |
10000001B |
GET_INTERFACE |
零 |
接口号 |
一 |
可选设置 |
Get_Status |
10000000B |
GET_STATUS |
零 |
零(返回设备状态) |
二 |
设备, |
Set_Address |
00000000B |
SET_ADDRESS |
设备地址 |
零 |
零 |
无 |
Set_Configuration |
00000000B |
SET_CONFIGURATION |
配置值(高字节为0,低字节表示要设置的配置值) |
零 |
零 |
无 |
Set_Descriptor |
00000000B |
SET_DESCRIPTOR |
描述表种类(高字节,见表5)和索引(低字节) |
零或语言标志 |
描述表长 |
描述表 |
Set_Feature |
00000000B |
SET_FEATURE |
特性选择符(1表示设备,0表示端点) |
零 |
零 |
无 |
Set_Interface |
00000001B |
SET_INTERFACE |
可选设置 |
接口号 |
零 |
无 |
Synch_Frame |
100000010B |
SYNCH_FRAME |
零 |
端点号 |
二 |
帧号 |
USB协议为USB设备定义了一套描述设备功能和属性的有固定结构的描述符,包括标准的描述符即设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符,还有百标准描述符,如类描述符。USB设备通过这些描述符向USB主机汇报设备的各种各样属性,主机通过对这些描述符的访问对设备进行类型识别、配置并为其提供相应的客户端驱动程序。
USB设备通过描述符反映自己的设备特性。USB描述符是由特定格式排列的一组数据结构组成。
在USB设备枚举过程中,主机端的协义软件需要解析从USB设备读取的所有描述符信息。在USB主向设备发送读取描述符的请求后,USB设备将所有的描述符以连续的数据流方式传输给USB主机。主机从第一个读到的字符开始,根据双方规定好的数据格式,顺序地解析读到的数据流。
在USB1.X中,规定了5种标准描述符:
每个USB设备只有一个设备描述符,而一个设备中可包含一个或多个配置描述符,即USB设备可以有多种配置。设备的每一个配置中又可以包含一个或多个接口描述符,即USB设备可以支持多种功能(接口),接口的特性通过描述符提供。
在USB主机访问USB设备的描述符时,USB设备依照设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符顺序将所有描述符传给主机。一设备至少要包含设备描述符、配置描述符和接口描述符,如果USB设备没有端点描述符,则它仅仅用默认管道与主机进行数据传输。
设备描述符给出了USB设备的一般信息,包括对设备及在设备配置中起全程作用的信息,包括制造商标识号ID、产品序列号、所属设备类号、默认端点的最大包长度和配置描述符的个数等。一个USB设备必须有且仅有一个设备描述符。设备描述符是设备连接到总线上时USB主机所读取的第一个描述符,它包含了14个字段,结构如下:
表4、USB设备描述符的结构 |
||||
偏移量 |
域 |
大小 |
值 |
描述 |
0 |
bLength |
1 |
数字 |
此描述表的字节数 |
1 |
bDecriptorType |
1 |
常量 |
描述符的类型(此处应为0x01,即设备描述符) |
2 |
bcdUSB |
2 |
BCD码 |
此设备与描述表兼容的USB设备说明版本号(BCD 码) |
4 |
bDeviceClass |
1 |
类 |
设备类码: |
5 |
bDeviceSubClass |
1 |
子类 |
子类挖码 |
6 |
bDevicePortocol |
1 |
协议 |
协议码 |
7 |
bMaxPacketSize0 |
1 |
数字 |
端点0的最大包大小(仅8,16,32,64 |
8 |
idVendor |
2 |
ID |
厂商标志(由USB-IF组织赋值) |
10 |
idProduct |
2 |
ID |
产品标志(由厂商赋值) |
12 |
bcdDevice |
2 |
BCD 码 |
设备发行号(BCD 码) |
14 |
iManufacturer |
1 |
索引 |
描述厂商信息的字符串描述符的索引值。 |
15 |
iProduct |
1 |
索引 |
描述产品信息的字串描述符的索引值。 |
16 |
iSerialNumber |
1 |
索引 |
描述设备序列号信息的字串描述符的索引值。 |
17 |
bNumConfigurations |
1 |
数字 |
可能的配置描述符数目 |
其中 bDescriptorType 为描述符的类型,其含义可查下表(此表也适用于标准命令Get_Descriptor中wValue域高字节的取值含义):
表5、USB描述符的类型值 |
||
类型 |
描述符 |
描述符值 |
标准描述符 |
设备描述符(Device Descriptor) |
0x01 |
配置描述符(Configuration Descriptor) |
0x02 |
|
字符串描述符(String Descriptor) |
0x03 |
|
接口描述符(Interface Descriptor) |
0x04 |
|
端点描述符(EndPont Descriptor) |
0x05 |
|
类描述符 |
集线器类描述符(Hub Descriptor) |
0x29 |
人机接口类描述符(HID) |
0x21 |
|
厂商定义的描述符 |
|
0xFF |
设备类代码bDeviceClass可查下表:
表6、设备的类别(bDeviceClass) |
||
值(十进制) |
值(十六进制) |
说明 |
0 |
0x00 |
接口描述符中提供类的值 |
2 |
0x02 |
通信类 |
9 |
0x09 |
集线器类 |
220 |
0xDC |
用于诊断用途的设备类 |
224 |
0xE0 |
无线通信设备类 |
255 |
0xFF |
厂商定义的设备类 |
下表列出了一个USB鼠标的设备描述符的例子,供大家分析一下:
表7、一种鼠标的设备描述符示例 |
|
字段 |
描述符值(十六制) |
bLength |
0x12 |
bDecriptorType |
0x01 |
bcdUSB |
x0110 |
bDeviceClass |
0x00 |
bDeviceSubClass |
0x00 |
bDevicePortocol |
0x00 |
bMaxPacketSize0 |
0x08 |
idVendor |
0x045E(Microsoft Corporation) |
idProduct |
0x0047 |
bcdDevice |
0x300 |
iManufacturer |
0x01 |
iProduct |
0x03 |
iSerialNumber |
0x00 |
bNumConfigurations |
0x01 |
配置描述符中包括了描述符的长度(属于此描述符的所有接口描述符和端点描述符的长度的和)、供电方式(自供电/总线供电)、最大耗电量等。主果主机发出USB标准命令Get_Descriptor要求得到设备的某个配置描述符,那么除了此配置描述符以外,此配置包含的所有接口描述符与端点描述符都将提供给USB主机。
表8、USB配置描述符的结构 |
||||
偏移量 |
域 |
大小 |
值 |
描述 |
0 |
bLength |
1 |
数字 |
此描述表的字节数长度。 |
1 |
bDescriptorType |
1 |
常量 |
配置描述表类型(此处为0x02) |
2 |
wTotalLength |
2 |
数字 |
此配置信息的总长(包括配置,接口,端点和设备类及厂商定义的描述符) |
4 |
bNumInterfaces |
1 |
数字 |
此配置所支持的接口个数 |
5 |
bCongfigurationValue |
1 |
数字 |
在SetConfiguration()请求中用作参数来选定此配置。 |
6 |
iConfiguration |
1 |
索引 |
描述此配置的字串描述表索引 |
7 |
bmAttributes |
1 |
位图 |
配置特性: |
8 |
MaxPower |
1 |
mA |
在此配置下的总线电源耗费量。以 2mA 为一个单位。 |
下面是一种硬盘的配置描述符示例:
表9、一种硬盘的配置描述符示例 |
|
字段 |
描述符值(十六进制) |
bLength |
0x09 |
bDescriptorType |
0x02 |
wTotalLength |
0x01F |
bNumInterfaces |
0x01 |
bCongfigurationValue |
0x01 |
iConfiguration |
0x00 |
bmAttributes |
0x0C |
MaxPower |
0x32 |
配置描述符中包含了一个或多个接口描述符,这里的“接口”并不是指物理存在的接口,在这里把它称之为“功能”更易理解些,例如一个设备既有录音的功能又有扬声器的功能,则这个设备至少就有两个“接口”。
如果一个配置描述符不止支持一个接口描述符,并且每个接口描述符都有一个或多个端点描述符,那么在响应USB主机的配置描述符命令时,USB设备的端点描述符总是紧跟着相关的接口描述符后面,作为配置描述符的一部分被返回。接口描述符不可直接用 Set_Descriptor 和 Get_Descriptor 来存取。
如果一个接口仅使用端点0,则接口描述符以后就不再返回端点描述符,并且此接口表现的是一个控制接口的特性,它使用与端点0相关联的默认管道进行数据传输。在这种情况下 bNumberEndpoints 域应被设置成0。接口描述符在说明端点个数并不把端点0计算在内。
表10、USB接口描述符的结构 |
||||
偏移量 |
域 |
大小 |
值 |
说明 |
0 |
bLength |
1 |
数字 |
此表的字节数 |
1 |
bDescriptorType |
1 |
常量 |
接口描述表类(此处应为0x04) |
2 |
bInterfaceNumber |
1 |
数字 |
接口号,当前配置支持的接口数组索引(从零开始)。 |
3 |
bAlternateSetting |
1 |
数字 |
可选设置的索引值。 |
4 |
bNumEndpoints |
1 |
数字 |
此接口用的端点数量,如果是零则说明此接口只用缺省控制管道。 |
5 |
bInterfaceClass |
1 |
类 |
接口所属的类值: |
6 |
bInterfaceSubClass |
1 |
子类 |
子类码 |
7 |
bInterfaceProtocol |
1 |
协议 |
协议码:bInterfaceClass 和bInterfaceSubClass 域的值而定.如果一个接口支持设备类相关的请求此域的值指出了设备类说明中所定义的协议. |
8 |
iInterface |
1 |
索引 |
描述此接口的字串描述表的索引值。 |
对于 bInterfaceClass 字段,表示接口所属的类别,USB协议根据功能将不同的接口划分成不的类,其具体含义如下表所示:
表11、USB协议定义的接口类别(bInterfaceClass) |
|
值(十六进制) |
类别 |
0x01 |
音频类 |
0x02 |
CDC控制类 |
0x03 |
人机接口类(HID) |
0x05 |
物理类 |
0x06 |
图像类 |
0x07 |
打印机类 |
0x08 |
大数据存储类 |
0x09 |
集线器类 |
0x0A |
CDC数据类 |
0x0B |
智能卡类 |
0x0D |
安全类 |
0xDC |
诊断设备类 |
0xE0 |
无线控制器类 |
0xFE |
特定应用类(包括红外的桥接器等) |
0xFF |
厂商定义的设备 |
端点是设备与主机之间进行数据传输的逻辑接口,除配置使用的端点0(控制端点,一般一个设备只有一个控制端点)为双向端口外,其它均为单向。端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。
除了描述符中描述的端点外,每个设备必须要有一个默认的控制型端点,地址为0,它的数据传输为双向,而且没有专门的描述符,只是在设备描述符中定义了它的最大包长度。主机通过此端点向设备发送命令,获得设备的各种描述符的信息,并通过它来配置设备。
表12、USB端点描述符的结构 |
||||
偏移量 |
域 |
大小 |
值 |
说明 |
0 |
bLength |
1 |
数字 |
此描述表的字节数长度 |
1 |
bDescriptorType |
1 |
常量 |
端点描述表类(此处应为0x05) |
2 |
bEndpointAddress |
1 |
端点 |
此描述表所描述的端点的地址、方向: |
3 |
bmAttributes |
1 |
位图 |
此域的值描述的是在bConfigurationValue域所指的配置下端点的特性。 |
4 |
wMaxPacketSize |
2 |
数字 |
当前配置下此端点能够接收或发送的最大数据包的大小。 |
6 |
bInterval |
1 |
数字 |
周期数据传输端点的时间间隙。 |
下表是一种鼠标的端点描述符的示例,该端点是一个中断端点:
表13、一种鼠标的端点描述符示例 |
|
域 |
值(十六进制) |
bLength |
0x07 |
bDescriptorType |
0x05 |
bEndpointAddress |
0x81 |
bmAttributes |
0x03 |
wMaxPacketSize |
0x04 |
bInterval |
0x0A |
字符串描述符是一种可选的USB标准描述符,描述了如制商、设备名称或序列号等信息。如果一个设备无字符串描述符,则其它描述符中与字符串有关的索引值都必须为0。字符串使用的是Unicode编码。
主机请示得到某个字符串描述符时一般分成两步:首先主机向设备发出USB标准命令Get_Descriptor,其中所使用的字符串的索引值为0,设备返回一个字符串描述符,此描述符的结构如下:
表14、USB字符串描述符(响应主机请求时返回的表示语言ID的字符串描述符) |
||||
偏移量 |
域 |
大小 |
值 |
描述 |
0 |
bLength |
1 |
N+2 |
此描述表的字节数 |
1 |
bDescriptorType |
1 |
常量 |
字串描述表类型(此处应为0x03) |
2 |
wLANGID[0] |
2 |
数字 |
语言标识(LANGID) |
|
… |
… |
… |
… |
N |
wLANGID[x] |
2 |
数字 |
语言标识(LANGID) |
该字符串描述符双字节的语言ID的数组,wLANGID[0]~wLANGID[x]指明了设备支持的语言,具体含义可查看USB_LANGIDs.pdf(http://www.baiheee.com/Documents/090518/090518112619/USB_LANGIDs.pdf)。
主机根据自己需要的语言,再次向设备发出USB标准命令Get_Descriptor,指明所要求得到的字符串的索引值和语言。这次设备所返回的是Unicode编号的字符串描述符,其结构如下:
表15、Unicode字符串描述符(响应主机请求时真正表示字符串编码的字符串描述符) |
||||
偏移量 |
域 |
大小 |
值 |
描述 |
0 |
bLength |
1 |
数字 |
此描述表的字节数(bString域的数值N+2) |
1 |
bDescriptorType |
1 |
常量 |
字串描述表类型(此处应为0x03) |
2 |
bString |
N |
数字 |
UNICODE 编码的字串 |
bString 域为设备实际返回的以UNICODE编码的字符串流,我们在编写设备端硬件驱动的时候需要将字符串转换为UNICODE编码,您可以通过一些UNICODE转换工具进行转换。
当USB设备插上主机时,主机就通过一系列的动作来对设备进行枚举配置(配置是属于枚举的一个态,态表示暂时的状态),这些态如下:
1、接入态(Attached):设备接入主机后,主机通过检测信号线上的电平变化来发现设备的接入;
2、供电态(Powered):就是给设备供电,分为设备接入时的默认供电值,配置阶段后的供电值(按数据中要求的最大值,可通过编程设置);
3、缺省态(Default):USB在被配置之前,通过缺省地址0与主机进行通信;
4、地址态(Address):经过了配置,USB设备被复位后,就可以按主机分配给它的唯一地址来与主机通信,这种状态就是地址态;
5、配置态(Configured):通过各种标准的USB请求命令来获取设备的各种信息,并对设备的某些信息进行改变或设置。
6、挂起态(Suspended):总线供电设备在3ms内没有总线操作,即USB总线处于空闲状态的话,该设备就要自动进入挂起状态,在进入挂起状态后,总的电流功耗不超过280uA。
此刻插上的 USB 设备地址还没有被配置,默认地址是0,使用 0 端点进行交互。
总线复位及向默认地址0发送GET_DESCRIPTOR指令包,请求设备描述。第一获取描述符要先复位设备,然后等待至少100ms(100ms可以满足大多数设备),这里实际只等待了43ms。如图一所示:
1)Index[4 - 5]:表示USB插入总线复位
2)Index[7 - 8]:表示主机向默认地址发送GET_DESCRIPTOR指令包,详细信息也抓出来了,如图所示:
3)Index[15 - 17]:表示设备向主机发送设备描述数据Index[16]
4)Index[18 - 19]:表示主机完成GET_DESCRIPTOR指令后,给设备发送一个空应答;
可以看到:数据是由二进制数字串构成的,首先数字串构成域(有七种:同步域(SYNC)、标识域(PID)、地址域(ADDR)、端点域(ENDP)、数据域(DATA)、帧号域(FRAM)、校验域(CRC)),域再构成包(比如握手包:格式如下 SYNC+PID,ACK属于PID的一种),包再构成事务(IN、OUT、SETUP,每一种事务都由令牌包、数据包、握手包三个阶段构成),事务最后构成传输(中断传输、并行传输、批量传输和控制传输)。
可以看到, packets 96,是 DATA0 ,红色部分是带的数据,也就是前面说的 USB 请求部分:
bmRequestType(1) + bRequest(1) + wvalue(2) + wIndex(2) + wLength(2)
80 06 00 01 00 00 40 00
bmRequestType=0x80,bRequest=0x06, wvalue=0x0100, wIndex=0x0000, wLength=0x0040;
根据usb2.0协议9.4.3节描述,获取描述符时bmRequestType=0x80,bRequest=0x06,这个是协议规定固定的。
wvalue=0x0100,高字节表示描述符类型,01表示设备,02表示配置;低字节表示索引。比如设备有多个配置,那需要读取不同配置的时候就通过低字节。或者一个配置下有多个接口,通过索引选择不同的接口。
index=0x0000,这个参数如果为0,则不关心;如果为非零,则表示Langurage ID,每一位都有对应的意义。
length=0x0040,表示请求的数据包长度为64.
所以本次设置事务的目的明确了,获取设备描述符,长度为64字节。
输入事务,是usb设备对请求进行回应,传输了16个字节的数据,为什么是传输了16个字节而不是64字节,看看偏移地址7,bMaxPacketSize0=0x10,即该设备的最大包传输大小为16字节,如果超过16字节,需要多次传输。实际设备描述符大小为18,可以看第三步的图,传输完16字节之后,主机再次发送令牌环,设备把剩下的2字节传输完成。这里我们只要获得bMaxPacketSize0 值就可以了,所以接下来直接对其进行了复位操作,否则设备还在等待传输剩余的2个字节呢。(此地,因为并不知道端点 0 的数据能力)
struct usb_device_descriptor {
u8 bLength;
u8 bDescriptorType;
u16 bcdUSB;
u8 bDeviceClass;
u8 bDeviceSubClass;
u8 bDeviceProtocol;
u8 bMaxPacketSize0;
u16 idVendor;
u16 idProduct;
u16 bcdDevice;
u8 iManufacturer;
u8 iProduct;
u8 iSerialNumber;
u8 bNumConfigurations;
} __attribute__ ((packed));
输出事务:因为获取描述符之前把设备包大小的初始值设为了64,所以输入事务之后,就认为传输完成,主机发送了一个输出事务,响应设备,已经收到数据。
再次复位总线及向设备发送SET_ADDRESS指令包,设置设备地址。如图二所示:
1)Index[22 - 23]:表示再次总线复位,该复位自动完成,不是手工插拔USB完成
2)Index[25 - 27]:表示主机向默认地址发送SET_ADDRESS指令包,详细信息如图所示:
设置地址为1
3)Index[29 - 31]:表示设备完成SET_ADDRESS指令后,给主机发送一个空应答;
设置地址为0x0002,由于我是从网上找的图,所以两幅图地址设置的不一样,这里注意一下
向第二步设定的设备地址发送GET_DESCRIPTOR指令包,请求设备描述。
1)Index[33 - 35]:表示主机向地址01发送GET_DESCRIPTOR指令包,详细信息如图
2)Index[41 - 43]:表示设备向主机发送设备描述数据Index[42]
3)Index[45 - 47]:表示设备向主机发送设备描述数据Index[46]
4)Index[48 - 50]:表示主机完成GET_DESCRIPTOR指令后,给设备发送一个空应答
向第二步设定的设备地址发送GET_DESCRIPTOR指令包,请求配置描述
1)Index[52 - 54]:表示主机向地址01发送GET_DESCRIPTOR指令包,详细信息如图所示
2)Index[57 - 59]:表示设备向主机发送配置描述数据Index[58]
3)Index[60 - 62]:表示主机完成GET_DESCRIPTOR指令后,给设备发送一个空应答;
获得数据:
09 02 2E 00 01 01 00 60 01
解析输入事件获取的配置信息:
struct usb_configuration_descriptor {
u8 bLength; /*09,描述符长度为9*/
u8 bDescriptorType;/* 0x2, 表示配置描述符*/
u16 wTotalLength; /*0x002E,表示当前配置下的各种描述信息总长度为46*/
u8 bNumInterfaces; /*0x01,当前配置下共一个接口*/
u8 bConfigurationValue; /*0x01,当前配置索引,当设置某一配置时,给SetConfiguration(x)传递的参数*/
u8 iConfiguration;/*0x00,字符串描述索引*/
u8 bmAttributes;/*0x60,属性信息*/
u8 bMaxPower;/*0x01当前配置最大消耗电流*/
} __attribute__ ((packed));
再次读取配置描述符,读取长度 wTotalLength 46字节。
09 02 2E 00 01 01 00 60 01 和第四步获得的一样
09 04 00 00 04 00 00 00 00 描述符长度也是09,04表示是一个接口描述符
07 05 81 03 08 00 c8 描述符长度为07,05表示端点描述符
07 05 01 03 08 00 c8
07 05 82 02 40 00 00
07 05 02 02 40 00 00
接口描述符详解:
struct usb_interface_descriptor {
u8 bLength; /*0x09,描述符长度*/
u8 bDescriptorType;/* 0x04 ,表示接口描述符*/
u8 bInterfaceNumber; /*0,接口号为0*/
u8 bAlternateSetting; /*0,接口的可选设置*/
u8 bNumEndpoints; /*04,当前接口共4个端点*/
u8 bInterfaceClass; /*0*/
u8 bInterfaceSubClass;/*0*/
u8 bInterfaceProtocol; /*0*/
u8 iInterface; /*0*/
} ;
端点描述符详解:
struct usb_endpoint_descriptor {
u8 bLength; /*07,描述符长度*/
u8 bDescriptorType;/* 0x5 ,表端点描述符*/
u8 bEndpointAddress;/*0x81,bit[7] =1,表示输入端点,0表示输出端点;bit[6:4],保留;bit[3:0]端点号,为1*/
u8 bmAttributes;/*0x03,bit[1:0]=11,表传输类型为中断传输*/
u16 wMaxPacketSize;/*0x0008,当前端点最大发送和接收包大小*/
u8 bInterval;/*0xc8,查询时发送数据的间隔时间*/
} ;
向第二步设定的设备地址发送SET_CONFIGURATION指令包,设置配置描述。
1)Index[139 - 141]:表示主机向地址01发送SET_CONFIGURATION指令包,详细信息如下:
2)Index[143 - 145]:表示设备完成 SET_CONFIGURATION 指令后,给主机发送一个空应答
至此,枚举过程结束,设备可通过驱动与主机通信了。
/* device request (setup) */
struct devrequest {
unsigned char requesttype; /*0x00,表请求设置*/
unsigned char request; /*0x09,表示设置配置*/
unsigned shortvalue; /*0x0001,使用配置,必须匹配从配置描述符都过来的bConfigurationValue*/
unsigned shortindex; /*0x0000,协议规定设为0*/
unsigned shortlength; /*0x0000,协议规定设为0*/
} __attribute__ ((packed));
USB枚举过程:
0 插入设备
1 获取USB设备的设备描述符 : 使用默认地址0,使用稍微长一些的数据长度(比如:0x40)
2 成功后复位设备
3 对设备进行地址设置 : 使设备从地址状态进入寻址状态
4 再次获取设备描述符 : 使用新的地址
5 获取设备配置符
6 根据设备配置符中的配置符总长度,获取其他配置符(接口,端点等)
7 设置配置 : 使设备从地址状态进入配置状态