一 USB2.0协议学习笔记---基本概念
二 USB2.0协议学习笔记---各种描述符
三 USB2.0协议学习笔记---USB数据包结构
四 USB2.0协议学习笔记---USB工作过程(类的方法)
参考:《圈圈教你玩USB(第二版)》
转载自:cnblogs Little_Village, 看了作者分享的学习笔记, 获益匪浅! 感谢!
一 USB2.0协议学习笔记---基本概念
概念
USB是一种串行通信总线(Universal Serial Bus),经历的版本有USB1.0,USB1.1、USB2.0等。USB是一种主从模式的结构,因此它无法在设备与设备。主机与主机之间互连进行数据交换。直到USB OTG的出现,它通过在不同场合下将身份切换,从而做到主从分时共存一个设备中。
版本 | 低速(LS) | 全速(FS) | 高速(HS) |
USB 1.0 | 1.5Mb/s | 12Mb/s | 不支持 |
USB 1.1 | 1.5Mb/s | 12Mb/s | 不支持 |
USB 2.0 | 1.5Mb/s | 12Mb/s | 450Mb/s |
USB的优点想必用过的人都有所体会。比如即插即用,接口标准统一,扩展性好。。。 USB是一种主从结构的工作模型,主机叫HOST,从机设备叫DEVICE,一个USB主控制器就是可以一个HOST,他可以同时连接127个设备。USB的另一个优点就是极少的链接电缆线,现在常见的USB设备大多数都是仅需要4个线就可以进行快速的数据交换传输,标准USB有4根线缆 :5V Vbus、差分数据线正D+、查分数据线负D-、最后是地线。USB OTG中又增加了一条身份识别的ID线。且在USB低速和全速传输过程中采用的是电压传输模式,到了后来的2.0的高速模式采用了电流驱动传输模式。
USB的设备接入检测机制
主机端在两条差分线上各接了一个15k的下拉电阻,因此在没有设备接入是D+ 和D-就都是低电平。而在设备端会通过将D+或者D-上接一个1.5k的上拉电阻,具体接在那条线上根据设备的速度决定,低速设备接在D-线上,高速和全速设备接在D+上。因此当设备通过USB接口接入主机端时就会有一根差分数据线会在两个电阻的作用下将一条差分数据线电平钳位到3V左右,主机端将检测出这一状态就认为有设备接入了。
USB 描述符与USB的关系
USB只是一种串行通信总线,提供了数据通路在设备和主机之间,主机要区分不同的设备都有什么样的功能同时获取设备的相关信息,这些信息就在描述符中进行定义,为了方便并标准化USB的描述符,USB标准定义了一系列的描述符,有设备描述符(Device Descriptor)、配置描述符(Configuration Descriptor),接口描述符(Interface Descriptor)、端点描述符(End-point Descriptor)、字符串描述符(String Descriptor)、特殊类描述符(class Descriptor)等。这些描述符就是USB标准中指定的一些用来定义USB设备的特性功能的一些profile。
设备描述符
每个USB设备只有一个设描述符下面是USB描述符的结构,它长度固定为18个字节。设备描述符主要记录的信息有:USB协议版本号、设备类型、端点0的最大包长、厂商ID(VID)、产品ID(PID),设备版本号,厂商字符串索引,产品字符串索引,设备序列号和可能的配置数等。
配置描述符
配置描述符主要记录的信息有、配置所包含的接口数,配置的编号,供电方式。是否支持远程唤醒,电流需求量等。
接口描述符
接口描述符主要记录的信息有接口的编号,接口端点数、接口所使用的类、子类、协议、等。
端点描述符
端点描述符主要记录的信息有、端点号及方向、端点的传输类型,最大包长度、查询时间,间隔等
字符串描述符
字符串描述,服务主要是提供一些方便人们阅读的信息,但不是必须。
USB主机在检测到USB设备接入之后就会开始枚举,这个过程后面在仔细研究。现在我理解的意思就是开始和USB设备协商,相互了解,了解的途径就是向USB设备要各种“证件”信息(描述符),从这些证件信息就能知道接入的USB设备是谁,他能干什么活,他的名字和身份证号等信息,等USB主控制器查完所有证件后他就会接受一个USB设备到他的门下,然后给USB设备分配一个7比特的学号,以后就叫学号点名“做题”了。这就是USB描述符的作用,USB设备就是靠这些描述符定义功能等特性的,后面在逐个详细介绍描述符的组成和意义。
二 USB2.0协议学习笔记---各种描述符
USB设备描述符
字段名 | 长 度(字节) | 地址偏移 | 含 义 |
bLenth | 1 | 0 | 描述符长度 |
bDescriptorType | 1 | 1 | 描述符类型 (这里为 1) |
bcdUSB | 2 | 2 | USB规范版本号(BCD码) |
bDeviceClass | 1 | 4 | 类代码 |
bDeviceSubClass | 1 | 5 | 子类代码 |
bDeviceProtocol | 1 | 6 | 协议代码 |
bMaxPackSize0 | 1 | 7 | 端点0最大支持数据包长度 |
idVendor | 2 | 9 | 供应商ID |
idProduct | 2 | 11 | 产品ID |
bcdDevice | 2 | 13 | 设备版本号(BCD码) |
iManufacturer | 1 | 14 | 供应商字符串描述索引 |
iProduct | 1 | 15 | 产品字符描述字符串索引 |
iSerialNumber | 1 | 16 | 产品序列好描述符索引 |
bNumConfigurations | 1 | 17 | 所支持的配置数 |
bLenth :该描述符的长度,单位字节一般USB设备描述符都是固定的18字节即0x12;
bDescriptorType :描述符种类代码。USB设备描述符为0x01;
bcdUSB :USB协议版本,采用BCD编码如2.0就是0x0200,但是USB是小端结构,因此实际就是 【0x00 ,0x20】
bDeviceClass: 设备类代码,大多数是0而在接口描述符中的bInterfaceClass中指定该接口实现的功能,注意的是当bDeviceClass为0时,bDeviceSubClass也必须为0;
bDeviceSubClass :配合bDeviceClass代码决定,USB标准定义。
bDeviceProtocol:设备所使用类的协议,如果没有定义类就为0 ,字段为0xFF表示使用自定义协议。
bMaxPackSize0 :端点0的最大包长,最小是8,因为枚举过程,第一次获取设备描述符只会读取一次,因此需要在这个包中包含USB设备描述符的长度。
idVendor:厂商ID VID,需要交保护费申请。
idProduct :产品ID PID由厂商自己定义。通常主机会根据VID和PID来加载本地或源的驱动程序。
bcdDevice :设备版本号,也采用BCD码,同理USB协议版本号。
iSerialNumber :设备序列号字符串索引值。
bNumConfigurations :设备有多少种配置,大部分USB设备就只有一个配置。
USB配置描述符
需要注意的是在有些时候,配置描述符中会包含接口描述符,特殊类描述符,端点描述符等信息,并在主机枚举请求设备配置描述符时一起返回给主机,不能单独返回给主机。
字段名 | 长 度 (字 节) | 地址偏移量 | 含义 |
bLenth | 1 | 0 | 配置描述符长度 |
bDescriptorType | 1 | 1 | 配置描述符类型 |
wTotalLength | 2 | 2 | 配置信息总长度 |
bNumInterfaces | 1 | 4 | 配置接口数 |
bConfigurationValue | 1 | 5 | 配置值 |
iConfiguration | 1 | 6 | 字符串描述符索引值 |
bmAttributes | 1 | 7 | 配置特性 |
bMaxPower | 1 | 8 | 最大电流(2mA为单位) |
bLenth :该描述符长度
bDescriptorType:描述符类型,配置描述符为0x02;
wTotalLength:表示整个配置描述符的总长度,包括配置描述符,接口描述符,类特殊描述符和端点描述符。
bNumInterfaces:配置支持的接口数。
bConfigurationValue:每个配置都有一个标识值。
iConfiguration:配置描述符索引。
bmAttributes:描述特性 D7保留,D6辨识供电方式,为1表示自供电的,否则是总线供电,D5标识是否支持远程唤醒(1),D4-D0保留。
bMaxPower:总线供电时的最大电流,如值为100则最大电流为200mA。
USB接口描述符
字 段 名 | 长 度 (字节) | 地址偏移 | 含 义 |
bLenth | 1 | 0 | 描述符的长度 |
bDescriptorType | 1 | 1 | 描述符的类型 |
bInterfaceNumber | 1 | 2 | 接口号 |
bAlterateSetting | 1 | 3 | 可替换设置值 |
bNumEndpoint | 1 | 4 | 端点0以外的端点数 |
bInterfaceClass | 1 | 5 | 类代码 |
bInterfaceSubClass | 1 | 6 | 子类代码 |
bInterfaceProtocol | 1 | 7 | 协议代码 |
iInterface | 1 | 8 | 字符串描述符索引值 |
bLenth: 描述符的长度。
bDescriptorType: 描述符的类型(接口描述符为4)
bInterfaceNumber: 表示该接口的编号,用在配置有多个接口,每个接口有唯一放入编号,从0 开始编号。
bAlterateSetting:接口的备用编号,规则同上。一般不用
bNumEndpoint:该接口使用的端点数,不包括端点0。即如果为0 就只有控制端点。
bInterfaceClass:接口使用的类
bInterfaceSubClass:接口使用的子类
bInterfaceProtocol:接口使用的协议。三个一起定义了设备的功能,鼠标键盘就只需要改协议部分就可以。其他两个都是HID类。
iInterface:接口字符串描述符的索引值。
USB端点描述符
域 | 大小(字节) | 偏移 |
bLenth | 1 | 0 |
bDescriptorType | 1 | 1 |
bEndpointAddress | 1 | 2 |
bmAttributes | 1 | 3 |
wMaxPackSize | 2 | 4 |
bInterval | 1 | 6 |
bLenth: 该描述符的长度(字节)
bDescriptorType: 该描述符的类型(0x05)
bEndpointAddress:端点的地址,D7表示端点的传输方向,为1则为输入,为0则为输出,D3-D0为端点号,其他位没有用。
bmAttributes:一个字节的属性描述字节,D1~D0表示端点传输类型,0 为控制传输,1为等时传输,2为批量传输,3为中断传输。如果为等时传输,D3-D2表示等时传输的类型,0表示无同步,1为异步,2为适配,3为同步;D5-D4辨识用途,0为数据端点,1为反馈端点,2为暗反馈端点,3保留;D7-D6保留。但是如果不是同步传输则只用到D1-D0其他位全部保留。
wMaxPackSize:该端点支持的最大数据长度,对于低速和全速设备而言,D10-D0表示数据包最大长,其他位未用,对于高速设备D12-D11表示每个帧的附件传送次数,具体参考USB标准协议。
bInterval:表示该端点的查询时间,对于中断传输表示查询的帧间隔数;对于其他传输方式参考USB标准协议。
类特殊描述符
有些设备还需要有类特殊描述符,这里拿HID设备举例,HID设备的特殊描述符如下
域 | 大小 | 偏移 | 说明 |
bLenth | 1 | 0 | 描述符长 |
bDescriptorType | 1 | 1 | 描述符类型 |
bcdHID | 2 | 2 | HID 协议版本 |
bCountryCode | 1 | 4 | 国家代码 |
bNumDescriptors | 1 | 5 | 下级描述符数量 |
bDescriptorType | 1 | 6 | 下级描述符类型 |
bDescriptorLength | 2 | 7 | 下级描述符长度 |
bDescriptorType | 1 | 9 | 下级描述符的类型(可选) |
wDescriptorLength | 2 | 10 | 下级描述符的长度(可选) |
... | ... | ... | ... |
类比前面描述符的作用这里只需要记一下一下几个字段的意义:
bcdHID:为 HID设备的版本,用BCD码表示,两个字节,如下表示版本为1.10
bCountryCode: 国家代码(这个用在特殊情况下 比如键盘,美式键盘等)。
bNumDescriptors:下级描述符的数量,在HID设备中至少有一个是报告描述符。
bDescriptorType:HID报告描述符为0x22.
bDescriptorLength:对应描述符的大小。
因为下级描述符至少有一个因此,HID类描述至少是10个字节,底下就是一个HID描述符的例子。
0x09, /* bLength: HID Descriptor size */HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID */0x10, /* bcdHID: HID Class Spec release number */0x01,0x00, /* bCountryCode: Hardware target country */0x01, /* bNumDescriptors: Number of HID class descriptors to follow */0x22, /* bDescriptorType */CUSTOMHID_SIZ_REPORT_DESC,/* wItemLength: Total length of Report descriptor */0x00,
看了这么多,现在我对USB协议的理解是这样的:USB设备可以理解为一个类,描述符就是这个类的属性,而枚举过程就是host主动请求调用,获取属性的方法,从而知道这个类的属性,其余在主机和设备数据交换过程的处理细节就是设备类的方法,我们实现一个USB设备就是在定义设备属性和定义方法接口的过程,只是定义属性需要了解到以上USB标准中的这些规则(profile),同时枚举过程的一些操作就是必须要实现的接口,其余的部分就要看我们的设备,设计出来支持的操作有哪些,选择的实现了,好了暂时就这些,后面还有一部分概念需要学习,待续。。。
DEVICE ID 1d50:6018 on Bus 020 Address 003 =================# USB设备描述符bLength : 0x12 (18 bytes)# 该描述符的长度,单位字节一般USB设备描述符都是固定的18字节即0x12;bDescriptorType : 0x1 Device# 描述符种类代码。USB设备描述符为0x01;bcdUSB : 0x200 USB 2.0# USB协议版本,采用BCD编码如2.0就是0x0200,但是USB是小端结构,因此实际就是 【0x00 ,0x20】bDeviceClass : 0xef Miscellaneous# 设备类代码,大多数是0而在接口描述符中的bInterfaceClass中指定该接口实现的功能,# 注意的是当bDeviceClass为0时,bDeviceSubClass也必须为0;bDeviceSubClass : 0x2# 配合bDeviceClass代码决定,USB标准定义。bDeviceProtocol : 0x1# 设备所使用类的协议,如果没有定义类就为0 ,字段为0xFF表示使用自定义协议。bMaxPacketSize0 : 0x40 (64 bytes)# 端点0的最大包长,最小是8,因为枚举过程,第一次获取设备描述符只会读取一次,# 因此需要在这个包中包含USB设备描述符的长度。idVendor : 0x1d50# 厂商ID VID,需要交保护费申请。idProduct : 0x6018# 产品ID PID由厂商自己定义。通常主机会根据VID和PID来加载本地或源的驱动程序。bcdDevice : 0x100 Device 1.0# 设备版本号,也采用BCD码,同理USB协议版本号。iManufacturer : 0x1 Black Sphere Technologies# 设备制造商序列号字符串索引值。iProduct : 0x2 Black Magic Probe# 产品描述符序列号字符串索引值iSerialNumber : 0x3 C1CAAE00# 设备序列号字符串索引值。bNumConfigurations : 0x1# 设备有多少种配置,大部分USB设备就只有一个配置。CONFIGURATION 1: 100 mA ==================================# USB配置描述符bLength : 0x9 (9 bytes)# 该描述符长度bDescriptorType : 0x2 Configuration# 描述符类型,配置描述符为0x02;wTotalLength : 0xbf (191 bytes)# 表示整个配置描述符的总长度,包括配置描述符,接口描述符,类特殊描述符和端点描述符。bNumInterfaces : 0x6# 配置支持的接口数。bConfigurationValue : 0x1# 每个配置都有一个标识值。iConfiguration : 0x0# 配置描述符索引。bmAttributes : 0x80 Bus Powered# 描述特性 D7保留,D6辨识供电方式,为1表示自供电的,否则是总线供电,# D5标识是否支持远程唤醒(1),D4-D0保留。bMaxPower : 0x32 (100 mA)# 总线供电时的最大电流,如值为100则最大电流为200mA。 0x32==50INTERFACE 0: CDC Communication =========================# USB接口描述符bLength : 0x9 (9 bytes) # 描述符的长度。bDescriptorType : 0x4 Interface # 描述符的类型(接口描述符为4)bInterfaceNumber : 0x0 # 表示该接口的编号,用在配置有多个接口,每个接口有唯一放入编号,从0 开始编号。bAlternateSetting : 0x0# 接口的备用编号,规则同上。一般不用bNumEndpoints : 0x1# 该接口使用的端点数,不包括端点0。即如果为0 就只有控制端点。bInterfaceClass : 0x2 CDC Communication# 接口使用的类bInterfaceSubClass : 0x2# 接口使用的子类bInterfaceProtocol : 0x1# 接口使用的协议。# 三个一起定义了设备的功能,鼠标键盘就只需要改协议部分就可以。其他两个都是HID类。iInterface : 0x4 Black Magic GDB Server# 接口字符串描述符的索引值。ENDPOINT 0x82: Interrupt IN ==========================### USB端点描述符bLength : 0x7 (7 bytes)# 该描述符的长度(字节)bDescriptorType : 0x5 Endpoint# 该描述符的类型(0x05)bEndpointAddress : 0x82 IN# 0x82 == b'1000 0010' ==> d7=1, d3-d0=0010# 端点的地址,D7表示端点的传输方向,为1则为输入,为0则为输出,D3-D0为端点号,其他位没有用。bmAttributes : 0x3 Interrupt# 一个字节的属性描述字节,#D1~D0表示端点传输类型,0 为控制传输,1为等时传输,2为批量传输,3为中断传输。#如果为等时传输,#D3-D2表示等时传输的类型,0表示无同步,1为异步,2为适配,3为同步;#D5-D4辨识用途,0为数据端点,1为反馈端点,2为暗反馈端点,3保留;#D7-D6保留。#但是如果不是同步传输则只用到D1-D0其他位全部保留。wMaxPacketSize : 0x10 (16 bytes)# 该端点支持的最大数据长度,# 对于低速和全速设备而言,D10-D0表示数据包最大长,其他位未用,# 对于高速设备D12-D11表示每个帧的附件传送次数,具体参考USB标准协议。bInterval : 0xff# 表示该端点的查询时间,对于中断传输表示查询的帧间隔数;对于其他传输方式参考USB标准协议。INTERFACE 1: CDC Data ==================================bLength : 0x9 (9 bytes)bDescriptorType : 0x4 InterfacebInterfaceNumber : 0x1bAlternateSetting : 0x0bNumEndpoints : 0x2bInterfaceClass : 0xa CDC DatabInterfaceSubClass : 0x0bInterfaceProtocol : 0x0iInterface : 0x0ENDPOINT 0x1: Bulk OUT ===============================bLength : 0x7 (7 bytes)bDescriptorType : 0x5 EndpointbEndpointAddress : 0x1 OUTbmAttributes : 0x2 BulkwMaxPacketSize : 0x40 (64 bytes)bInterval : 0x1ENDPOINT 0x81: Bulk IN ===============================bLength : 0x7 (7 bytes)bDescriptorType : 0x5 EndpointbEndpointAddress : 0x81 INbmAttributes : 0x2 BulkwMaxPacketSize : 0x40 (64 bytes)bInterval : 0x1INTERFACE 2: CDC Communication =========================bLength : 0x9 (9 bytes)bDescriptorType : 0x4 InterfacebInterfaceNumber : 0x2bAlternateSetting : 0x0bNumEndpoints : 0x1bInterfaceClass : 0x2 CDC CommunicationbInterfaceSubClass : 0x2bInterfaceProtocol : 0x1iInterface : 0x5 Black Magic UART PortENDPOINT 0x84: Interrupt IN ==========================bLength : 0x7 (7 bytes)bDescriptorType : 0x5 EndpointbEndpointAddress : 0x84 INbmAttributes : 0x3 InterruptwMaxPacketSize : 0x10 (16 bytes)bInterval : 0xffINTERFACE 3: CDC Data ==================================bLength : 0x9 (9 bytes)bDescriptorType : 0x4 InterfacebInterfaceNumber : 0x3bAlternateSetting : 0x0bNumEndpoints : 0x2bInterfaceClass : 0xa CDC DatabInterfaceSubClass : 0x0bInterfaceProtocol : 0x0iInterface : 0x0ENDPOINT 0x3: Bulk OUT ===============================bLength : 0x7 (7 bytes)bDescriptorType : 0x5 EndpointbEndpointAddress : 0x3 OUTbmAttributes : 0x2 BulkwMaxPacketSize : 0x40 (64 bytes)bInterval : 0x1ENDPOINT 0x83: Bulk IN ===============================bLength : 0x7 (7 bytes)bDescriptorType : 0x5 EndpointbEndpointAddress : 0x83 INbmAttributes : 0x2 BulkwMaxPacketSize : 0x40 (64 bytes)bInterval : 0x1INTERFACE 4: Application Specific ======================bLength : 0x9 (9 bytes)bDescriptorType : 0x4 InterfacebInterfaceNumber : 0x4bAlternateSetting : 0x0bNumEndpoints : 0x0bInterfaceClass : 0xfe Application SpecificbInterfaceSubClass : 0x1bInterfaceProtocol : 0x1iInterface : 0x6 Black Magic Firmware UpgradeINTERFACE 5: Vendor Specific ===========================bLength : 0x9 (9 bytes)bDescriptorType : 0x4 InterfacebInterfaceNumber : 0x5bAlternateSetting : 0x0bNumEndpoints : 0x1bInterfaceClass : 0xff Vendor SpecificbInterfaceSubClass : 0xffbInterfaceProtocol : 0xffiInterface : 0x7 Black Magic Trace CaptureENDPOINT 0x85: Bulk IN ===============================bLength : 0x7 (7 bytes)bDescriptorType : 0x5 EndpointbEndpointAddress : 0x85 INbmAttributes : 0x2 BulkwMaxPacketSize : 0x40 (64 bytes)bInterval : 0x0
三 USB2.0协议学习笔记---USB数据包结构
USB包类型和传输过程
USB是一种串行总线,因此数据都是一位一位传输的,如同串口那样,但是USB在真实物理电路上却不是TTL电平,而是一种差分信号采用NRZI编码,就是用变化表示0,不变表示1,同时在USB中数据时低字节先发送的即LSB。USB中的数据交互最小单位是包一个包由许多域组成,但是统一的是每个包都有同步域开始,然后紧接着PID,这里的PID不是指PID,而是指类似包命令码这么一个意思。PID只用到了低四位,高四位是低四位的取反用于校验PID字段,常见的PID由USB标准中查到如下,其中*号标识的是USB1.1中没有的:
有表中可以看到USB包分为16种四类,现在先以令牌包的格式介绍包格式。记住一个概念,USB中主机占主导地位,因此输入输出都是在主机角度来说的。
令牌包:
输出(OUT),输入(IN),建立令牌包(SETUP)包结构:
同步域 | 8位包标识PID | 7位地址 | 4位端点号 | 5位CRC5校验 | EOP |
SOF同步包,这个包是主机和所有从设备之间同步的特殊包,因此它是广播的,结构也就比较特殊。
同步域 | 8位标识PID | 11位帧号 | 5位CRC5校验 | EOP |
数据包:
数据包都具有同样的结构,一个同步域后面直接跟PID加上数据,最后是CRC校验,同样所有的CRC校验都是从PID字节之后的部分,最后是包结束符EOP。
同步域 | 8位包标识PID | 字节0 | 字节1 | ...... | 16位CRC16校验 | EOP |
主机和设备之间会维护一个数据包类型切换机制用于错误恢复。假如主机发送数据给设备,但是设备的ACK包在传输中被破坏了,所以主机就不知道对方是否收到了数据,主机就会保持自己的数据包类型不变,但是如果下一次设备使用数据包类型和自己不同给自己发数据了,主机就知道上一次丢失ACK那次的传输实际上是成功的。反之就是失败了。
握手包:
ACK :用来确认数据接收正确,这一部分常常有USB物理层自动完成;
NCK:表示没有数据返回,常常是主机查询设备有无数据返回时由设备返回,主机不允许使用NCK应答(猜猜为什么);
STALL:设备无法执行一个请求,或者是端点已经挂起了时,设备会返回STALL;这种情况下需要主机干预才能解除STALL状态。
NYET:只在USB2.0高速事物中使用,表示本次数据接收成功,但是没有足够的空间来接收下一数据,下一次数据传输前主机需要先发送PING探查设备状态,确认可以接收数据。
特殊包:
特殊包是在一些特殊场合使用的包,主要有三个:ERR,SPLIT、PING。
PING:用在高速设备中,主机询问设备是否可以接收数据,设备如果有足够的空间接收数据则会用ACK握手包回应主机。
PRE:只用在高速模式中,作为前导包通知集线器打开,低速端口,因为集线器在平常情况下,防止高速信号导致低速设备误动作,集线器是没有将高速信号传递给速速设备的。
ERR:用在高速事物中的SPLIT令牌包分裂错误指示。
USB定义了这么多的数据包,但是还是不能直接用这些包就开始数据传输了,这里是USB的一个概念,USB的数据交换都是主机首先发起的,所以的引入了USB事物的概念。USB事物通常都是有两个或者三个数据包组成的。令牌包有主机发送启动一个事物,然后主机发送数据包,最后由设备ACK应答。由此USB定义了四种传输类型,每种类型里包含多种USB事务。
批量传输类型
批量输出事务:首先主机发送OUT包告诉设备,我将要输出数据到你,同时在OUT包中指示了设备地址和端口号,然后接着在总线上发送数据包,设备的对应端点会接收下数据包,如果校验完CRC没有问题,则设备会用握手包ACK回应主机,如果数据出错设备什么都不会发送,主机就会自动超时。这里出错主要有两种情况,一种是令牌包出错,另一种情况时数据包出错。
批量输入事务:主机发送IN输入数据包到设备,如果设备有数据要发送就会将数据放到总线上,主机接收数据检验无错误就回应ACK给设备;如果设备无数据或挂起就会用NCK或者STALL回应主机。
PING:是在高速模式里才有的,没有数据过程,主机发送,从机可以接收数据就用ACK回应,否则回应对应的拒绝代码或不回应(令牌包错)。
中断传输类型
中断传输类型,除了数据输入的查询策略,和没有PING事物以外其他的全部同批量事物。常用在数据量不大,但是对时间要求严格的设备中。如人机接口设备的键盘鼠标。
等时传输类型
适用于数据量大,对实时性要求高,但是对数据的正确性要求不是也别严格的场合。因此常用于音频等类似设备中。事物流程图如下:
控制传输类型
控制传输类型是所有里面最复杂额,因为他为了保证数据的传输正确性,同时还是一个综合的过程。控制传输主要有三个过程组成建立过程,数据过程,状态过程。
建立过程:使用建立事物,主机发送SETUP令牌包,然后发送主机数据(DATA0)到设备,设备接收到数据并且回应ACK(必须的)后开始处理。(实际上是一个批量输出的过程)
数据过程(可选):如果上面的建立过程有数据要设备返回设备此时就会将数据在这个过程返回给主机。这个过程可能包含多次数据输入传输,有主机根据控制协议决定。数据过程实际是批量数据输入/出的过程。
状态过程:
实际还是一个批量事物,它的传输方向同数据过程相反,是一个数据为0的批量事物。也可以理解,控制事物实际是批量过程的一个更高层次的应用。因为控制传输过程中的每一次数据传输都是一个批量传输的过程。
控制过程例子:
建立过程
主机 --->设备 OUT + DATA
设备 --->主机 ACK
数据过程(可能有多次,但方向一定是同样的)
主句 --->设备 IN
设备 --->主机 DATA
主机 --->设备 ACK
状态过程
主机 --->设备 OUT(因为上面是IN)
设备 --->主机 DATA
主机 --->设备 ACK
以上内容就是USB协议的全部概念的东西和通信过程,目前的理解,
四 USB2.0协议学习笔记---USB工作过程(类的方法)
前面学习了那么多的概念,这里需要记住一点分层概念即设备 ---> 配置 ---> 接口 ---> 端点,这种分层的概念结构 。 也可以理解为端点构成接口,接口组成配置,配置组成设备。
USB设备的枚举过程
前面说过了,USB只是一种串行通信总线。也就是说USB是一种物理通信通道。但是实际中我们使用了各种各样的USB设备,如U盘,鼠标、键盘等。这些设备都是使用USB总线和主机(PC)进行通信的,但是主机却可以区分不同的设备,这里靠的就是,前面定义的描述符。当我们将USB设备接入主机后主机很快就能知道设备的属性并为其安装对应的驱动,其实在这之前,设备已经和主机进行了很多次数据交换了,这个过程就是枚举。开始了解枚举之前还有明白一个规则,每个USB设备都至少有一个端点即0,这个端点就是USB的控制端点,用来USB设备自己和主机主机间“协商”交换数据用。如端点地址 0x00表示控制输出端点地址,0x80表示控制输入地址。接下来看枚举过程:
- 首先当主机检测到设备插入后就会将USB设备复位,复位后的USB设备默认地址就是0,这样主机就可以用地址0作为设备地址进行通信了。
- 然后主机会向端点0发送获取设备描述符的建立过程,然后设备将在数据过程将设备描述符返回给主机,这里注意如果端点0的最大包长小于设备描述符,设备本应该分多次数据过程,但是主机只会读取第一次的数据,因此设备实际这个过程只需要一次数据过程即使一次无法将设备描述符全部传输。同时USB标准还规定端点0的最小包长至少是8,因为设备描述符的第八个字节就包含了端点0的最大包长,主机会根据这里的值,获取设备的端点0的最大包长。
- 然后主机会再一次对设备复位,然后发送给设备地址0一个设置地址的命令,地址包含在设置设备的建立过程中的数据包中。注意这个地址是由主机管理的(1~127)。设备收到这个命令后直接到状态过程,并在主机请求状态时返回一个长度为0 的状态给主机,如果主机回应了ACK给设备,设备在此之后就将启用这个地址。
- 然后主机使用新地址再次获取设备描述符,这次主机会多次读取设备描述符数据,直到读取完全部的设备描述符。
- 主机再获取配置描述符(设备返回配置描述符时需要将 配置描述符、接口描述符,类特殊描述符、端点描述符一起返回回去,而不能单独返回)。
- 然后是获取报告描述符(如果是HID设备会有这一步)。
- 这一部分不是必然的,设备会根据前面描述符提供的字符串索引获取字符串描述符,从而方便人们阅读设备的相关信息。
以上的这些过程都是使用的USB控制传输。
以上过程忽略了一个概念,就是USB标准请求,即获取描述符是怎么告诉设备的,也有一套标准通信规则如下表:
bRequest的命令码表
具体的请求命令帧组成,可以查阅USB标准文档。这里拿一个不完整的设备枚举过程展示一下设备的枚举过程,数据使用BusHound 软件抓包得到的。
//获取设备描述符
CTL 80 06 00 01 00 00 12 00
//设备返回设备描述符
IN 12 01 00 02 00 00 00 40
93 39 16 14 00 02 01 02 03 01
//先获取设备配置描述符的前9个字节 == 获取知配置描述符的总长度
CTL 80 06 00 02 00 00 09 00
//设备返回配置描述符的前9个字节 == 得知配置描述符的总长度为0x29(41个字节)
IN 09 02 29 00 01 01 00 c0 64
//获取全部配置描述符
CTL 80 06 00 02 00 00 29 00
//设备返回全部配置描述符其中还包括接口配置描述符和断点配置描述符(因为是HID设备还有一个HID设备配置描述符)
IN 09 02 29 00 01 01 00 c0 64 09 04 00 00 02 03 00 00 00 09 21
10 01 00 01 22 21 00 07 05 81 03 40
00 01 07 05 01 03 40 00 01
//设置配置值,配置描述符中有一个位置就是配置这个值的
CTL 00 09 01 00 00 00 00 00
//获取接口
CTL 21 0a 00 00 00 00 00 00
//获取HID设备配置描述符
CTL 81 06 00 22 00 00 61 00
//设备返回全部HID设备报告配置描述符
IN 05 8c 09 01 a1 01 09 03 15 00 26 00 ff 75 08 95 40 81
02 09 04 15 00 26 00 ff 75 08 95 40 91 02
c0
上面大概说了一下设备的枚举过程,如果以上过程完成了即---在电脑中可以看到一个USB设备了,接下来就是完成对应的用户数据传送过程的应用逻辑代码的实现了,这个根据不同的设备包括自定义设备,这一部分除非是一些标准设备,否则你可以做的很有个性化,只要收发双方约定一个统一的标准规则就可以。但是需要记住的是如果你要使用USB进行数据的收发,配置描述符中应该包含一个非0端点的配置,因为端点0是仅用于控制传输的。具体的实现可以详细参考《圈圈教你玩USB(第二版)》一书。