Linux USB基础之虚拟串口枚举流程(三)

Linux USB基础之虚拟串口枚举流程(三)

1 USB检测与枚举过程

1.1 USB插拔检测

USB集线器的每个下游端口的D+和D-上,分别接了一个15K的下拉电阻到低。当集线器的端口悬空(即没有设备插入)时,输入端就被此两个下拉电阻拉到低电平。在USB设备端,在D+或D-上接入1.5K的上拉电阻到3.3V的电源。1.5K的上拉电阻接在D+或D-上,由设备的速度决定。对于全速设备和高速设备,上拉电阻接在D+上,而低速设备的上拉电阻则是接在D-上。可简单记为:速度快的,上拉电阻接正;速度慢的,上拉电阻接负的。

当设备插入到集线器时,接了上拉电阻的数据线的电压由1.5K的上拉电阻和15K的下拉电阻分压决定,结果大概为3V。在集线器的接收端来说,是一个高电平信号。集线器检测到此状态后,就报告给USB主控制器,此时就检测到设备的插入。USB高速设备先是识别成全速设备,然后在通过集线器和设备两者的确认,在切换到高速模式下。在高速模式下,是电流传输模式,此时需要将D+上的上拉电阻断开。

1.2 USB枚举过程

1) USB主机检测到USB设备插入后,会先对设备进行复位。
USB设备在总线复位后其地址为0,此时主机可通过地址0和该设备进行通信。USB主机往地址0的设备的端点0发送获取设备描述符的标准请求(这是控制传输的建立过程)。设备收到该请求后,会按主机请求的参数,在数据过程将设备描述符返回给主机。主机在成功获取到该数据包的设备描述符并且确认没有错误后,就会返回一个0长度的确认数据包(状态过程)给设备,从而进入到接下来的设置地址阶段。注意:第一次主机只会读取一个数据包的设备描述符,标准的设备描述符有18byte,有些USB设备的端点0大小不足18byte(但至少具有8byte),这种情况下,USB主机也只发送一次数据输入请求,多余的数据将不会再次请求。因此,如果当设备断电0大小不足18字节时,就需要注意该问题,也就是说在第一次获取设备描述符时,只需要返回一次数据即可,不需要再等主句继续获取剩余数据(如果还有),因为主机不会这么干的。房主急成功获取到设备描述符的前8byte之后,他就知道端点0的最大包长度了,因为端点0最大包长度刚好在设备描述符的第八字节处。

2) USB主机对设备又一次复位,开始设置地址阶段。
USB主机往地址为0的设备的端点0发出一个设置地址的请求(控制传输的建立过程),新的设备地址包含在建立过程的数据包中。具体的地址有USB主机负责管理,主机会分配一个唯一的地址给刚接入的设备。USB设备在收到这个建立过程之后,就直接进入到状态过程,因为此控制传输没有数据过程。设备等待主机请求状态返回(一个输入令牌包),收到输入令牌包后,设备就返回0长度的状态数据包。如果主机确认该状态包已经正确收到,就会发送应答包ACK给设备,设备在收到这个ACK之后,就要启用新的设备地址了。这样设备就分配到了一个唯一的设备地址,以后主机就通过他来访问该设备。

3) USB主机重新发送获取设备描述符的命令,读取完整设备描述符。
主机向新地址重新发送Get_Device_Descriptor命令,此次读取其设备描述符的全部字段,以了解该设备的总体信息,如VID,PID。

4) USB主机获取配置描述符。
配置描述符总共9byte。主机在获取到配置描述符之后,根据配置描述符中描述的配置集合总长度,获取配置集合。获取配置描述符和获取配置描述符集合的请求是差不多的,只是指定的长度不一样。

5) USB主机获取设备字符串,获得描述字符集。
描述字符集包括了产商、产品描述、型号等信息。

6) USB主机展示新设备信息。
此时主机将会弹出窗口,展示发现新设备的信息、产商、产品描述、型号等。

7) PC判断能够提供该类USB的驱动。
根据设备描述符和配置描述符,PC判断是否能够提供USB的Driver,一般能提供几大类的设备,如游戏操作杆、存储、打印机、扫描仪等。

8) USB主机发送配置命令,请求为设备选择一个配置
加载USB设备驱动以后,主机发送Set_Configuration(x)命令请求为该设备选择一个合适的配置。如果配置成功,USB设备进入“配置”状态,并可以和客户软件进行数据传输。

1.3 虚拟串口枚举流程

1.3.1 第一次获取设备描述符

struct usb_ctrlrequest {
__u8 bRequestType		= 0x80;// 这是一个主机发给设备(bit0~bit4)的一个标准(bit5~bit6)的请求命令,请求的结果是要求设备给Host返回(bit7 == 1)
__u8 bRequest			= 0x06;// 是一个GET_DESCRIPTOR,即获取描述符的请求
__le16 wValue		= 0x0100;// 高字节表示描述符类型,01表示设备,02表示配置;低字节表示索引。
__le16 wIndex			= 0x0000;
__le16 wLength		= 0x0040;
} __attribute__ ((packed));
struct usb_device_descriptor {
    __u8  bLength 			=  0x12; ///长度
    __u8  bDescriptorType		=  0x01; ///描述符类型

    __le16 bcdUSB			= 0x0200; //该位表示版本号,使用BCD码表示,即USB2.0版本
    __u8  bDeviceClass		= 0x02;///设备类信息	
    __u8  bDeviceSubClass	= 0x00;///设备子类型
    __u8  bDeviceProtocol		= 0x00;///协议	码,0表示没指定任何协议
    __u8  bMaxPacketSize0	= 0x40;///端点0最大传输大小	
    __le16 idVendor			= 0x0525;///厂商 ID
    __le16 idProduct			= 0xa4a7;///设备 ID
    __le16 bcdDevice			= 0x0414;/// 设备版本号
    __u8  iManufacturer		= 0x01; ///描述厂商信息的字符串描述符的索引值
    __u8  iProduct			= 0x02;///描述产品信息的字串描述符的索引值
    __u8  iSerialNumber		= 0x00;///描述设备序列号信息的字串描述符的索引值-03
    __u8  bNumConfigurations	= 0x01;///设备当前速度模式下支持的配置数量。有的设备可以在多个速度模式下操作,这里包括的只是当前速度模式下的配置数目,不是总的配置数目 
} __attribute__ ((packed));

1.3.2 第二次设备复位

host通过驱动数据线到复位状态(D+和D-全为低电平 ),并持续至少10ms。

1.3.3 分配设备地址

struct usb_ctrlrequest {
__u8 bRequestType		= 0x00;// 这是一个主机发给设备(bit0~bit4)的一个标准(bit5~bit6)的请求命令,请求的结果是要求设备给Host返回(bit7 == 1)
__u8 bRequest			= 0x05;// 是一个SET_ADDRESS,即配置设备地址描述符
__le16 wValue		= 0x0025;// 表示设备地址为0x25。
__le16 wIndex			= 0x0000;
__le16 wLength		= 0x0000;
} __attribute__ ((packed));

1.3.4 第二次获取设备描述符

这次要注意,这里以及使用新分配的设备地址3了,而不是默认地址0。同时后面也都会使用新分配的地址来通信。

1.3.5 第一次获取设备配置

struct usb_ctrlrequest {
__u8 bRequestType		= 0x80;// 这是一个主机发给设备(bit0~bit4)的一个标准(bit5~bit6)的请求命令,请求的结果是要求设备给Host返回(bit7 == 1)
__u8 bRequest			= 0x06;// 是一个GET_DESCRIPTOR,即获取描述符的请求
__le16 wValue		= 0x0002;// 高字节表示描述符类型,01表示设备,02表示配置。
__le16 wIndex			= 0x0000;
__le16 wLength		= 0x00FF;
} __attribute__ ((packed));
1.3.5.1 获取配置描述符
struct usb_config_descriptor {
	__u8  bLength      		= 0x09;    	//此描述符长度为9
	__u8  bDescriptorType    	= 0x02;    	// 2代表的配置描述符 
 
	__le16 wTotalLength      	= 0x4b;    	//此配置信息的总长为34字节(包括配置,接口,端点和设备类及厂商定义的描述符)
	__u8  bNumInterfaces     = 0x02;    	//表示有一个接口描述符
	__u8  bConfigurationValue	= 0x02;    	//在SetConfiguration()请求中用2作参数来选定此配置-0x01
	__u8  iConfiguration     	= 0x04;    	//描述此配置的字串描述表索引-0x00
	__u8  bmAttributes       = 0xC0;    	//D7: 保留(设为一)D6: 自给电源 D5: 远程唤醒 D4..0:保留(设为一)   表示这是一个由总线供电,并支持远程唤醒功能(可以睡眠节约电)
	__u8  bMaxPower          = 0x64; 	//在此配置下的总线电源耗费量。以 2mA 为一个单位 即2 * 50 = 100ms
} __attribute__ ((packed));
1.3.5.2 获取接口关联描述符
struct usb_interface_assoc_descriptor {
	__u8  bLength			= 0x08;
	__u8  bDescriptorType		= 0x0b;	//IAD描述符类型

	__u8  bFirstInterface		= 0x00;	//起始接口
	__u8  bInterfaceCount		= 0x02;	//接口数量
	__u8  bFunctionClass		= 0x02;	//类型代码
	__u8  bFunctionSubClass	= 0x02;	//子类型代码
	__u8  bFunctionProtocol	= 0x01;	//协议代码
	__u8  iFunction			= 0x07;	//描述字符串索引
} __attribute__ ((packed));
1.3.5.3 获取设备接口描述符
struct usb_interface_descriptor {
	__u8  bLength        	= 0x09;    //该描述符的字节数
	__u8  bDescriptorType  	= 0x04;    //描述符类型,4代表接口描述符
 
	__u8  bInterfaceNumber	= 0x00;     //接口号,当前配置支持的接口数组索引(从零开始)
	__u8  bAlternateSetting 	= 0x00;     //可选设置的索引值,这里无
	__u8  bNumEndpoints  	= 0x01;    //端点描述符数量,1个    
	__u8  bInterfaceClass    	= 0x02;    //接口所属的类值,由USB说明保留, 2代表CDC contrl
	__u8  bInterfaceSubClass 	= 0x02;    //子类码 这些值的定义视bInterfaceClass域而定
	__u8  bInterfaceProtocol   = 0x01;    //协议码:bInterfaceClass 和bInterfaceSubClass 域的值而定
	__u8  iInterface        	= 0x05;    //描述此接口的字串描述表的索引值
} __attribute__ ((packed));
1.3.5.4 Header Functional Descriptor
/* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
struct usb_cdc_header_desc {
	__u8	bLength				= 0x05;
	__u8	bDescriptorType		= 0x24;	/* bDescriptorType: CS_INTERFACE */
	__u8	bDescriptorSubType	= 0x00; 	/* bDescriptorSubtype: Header Func Desc */

	__le16	bcdCDC				= 0x0110; /* bcdCDC: spec release number */
} __attribute__ ((packed));
1.3.5.5 Abstract Control Management Descriptor
/* "Abstract Control Management Descriptor" from CDC spec  5.2.3.3 */
struct usb_cdc_acm_descriptor {
	__u8	bLength				= 0x04;
	__u8	bDescriptorType		= 0x24;
	__u8	bDescriptorSubType	= 0x02; /* bDescriptorSubtype: Abstract Control Management desc */

	__u8	bmCapabilities		0x02;
} __attribute__ ((packed));
1.3.5.6 Union Functional Descriptor
/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */
struct usb_cdc_union_desc {
	__u8	bLength				= 0x05;
	__u8	bDescriptorType		= 0x24;
	__u8	bDescriptorSubType	= 0x06; /* bDescriptorSubtype: Union func desc */

	__u8	bMasterInterface0		= 0x00;
	__u8	bSlaveInterface0		= 0x01;
	/* ... and there could be other slave interfaces */
} __attribute__ ((packed));
1.3.5.7 Call Management Descriptor
/* "Call Management Descriptor" from CDC spec  5.2.3.2 */
struct usb_cdc_call_mgmt_descriptor {
	__u8	bLength				= 0x05;
	__u8	bDescriptorType		= 0x24; /* bDescriptorType: CS_INTERFACE */
	__u8	bDescriptorSubType	= 0x01; /* bDescriptorSubtype: Call Management Func Desc */

	__u8	bmCapabilities		= 0x00; /* bmCapabilities: D0+D1 */
	__u8	bDataInterface			= 0x01; /* bDataInterface: 1 *
} __attribute__ ((packed));
1.3.5.8 获取notify端点描述符
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {   ///USB 端点描述符(每个USB设备最多有16个端点)
     __u8  bLength			= 0x07; ///描述符的字节长度
     __u8  bDescriptorType	= 0x05;///描述符类型,对于端点就是USB_DT_ENDPOINT
     __u8  bEndpointAddress	= 0x83;///bit0~3表示端点地址,bit8 表示方向,输入还是输出
     __u8  bmAttributes		= 0x03;///属性(bit0、bit1构成传输类型,00--控制,01--等时,10--批量,11--中断)
    __le16 wMaxPacketSize	= 0x000a;///端点一次可以处理的最大字节数
    __u8  bInterval			= 0x20;///希望主机轮询自己的时间间隔
 
     /* NOTE:  these two are _only_ in audio endpoints. */
     /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
     __u8  bRefresh;
     __u8  bSynchAddress;
} __attribute__ ((packed));
1.3.5.9 获取数据类接口描述符
struct usb_interface_descriptor {
	__u8  bLength        	= 0x09;    //该描述符的字节数
	__u8  bDescriptorType  	= 0x04;    //描述符类型,4代表接口描述符
 
	__u8  bInterfaceNumber	= 0x01;     //接口号,当前配置支持的接口数组索引(从零开始)
	__u8  bAlternateSetting 	= 0x00;     //可选设置的索引值,这里无
	__u8  bNumEndpoints  	= 0x02;    //端点描述符数量,2个    
	__u8  bInterfaceClass    	= 0x0A;    //接口所属的类值,由USB说明保留, 2代表CDC data
	__u8  bInterfaceSubClass 	= 0x00;    //子类码 这些值的定义视bInterfaceClass域而定
	__u8  bInterfaceProtocol   = 0x00;    //协议码:bInterfaceClass 和bInterfaceSubClass 域的值而定
	__u8  iInterface        	= 0x06;    //描述此接口的字串描述表的索引值
} __attribute__ ((packed));
1.3.5.10 获取OUT端点描述符
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {   ///USB 端点描述符(每个USB设备最多有16个端点)
__u8  bLength			= 0x07; ///描述符的字节长度
    __u8  bDescriptorType	= 0x05;///描述符类型,对于端点就是USB_DT_ENDPOINT
    __u8  bEndpointAddress	= 0x02;///bit0~3表示端点地址,bit8 表示方向,输入还是输出
    __u8  bmAttributes		= 0x02;///属性(bit0、bit1构成传输类型,00--控制,01--等时,10--批量,11--中断)
__le16 wMaxPacketSize	= 0x40;///端点一次可以处理的最大字节数
    __u8  bInterval			= 0x00;///希望主机轮询自己的时间间隔
 
/* NOTE:  these two are _only_ in audio endpoints. */
    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    __u8  bRefresh;
    __u8  bSynchAddress;
} __attribute__ ((packed));
1.3.5.11 获取IN端点描述符
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {   ///USB 端点描述符(每个USB设备最多有16个端点)
__u8  bLength			= 0x07; ///描述符的字节长度
    __u8  bDescriptorType	= 0x05;///描述符类型,对于端点就是USB_DT_ENDPOINT
    __u8  bEndpointAddress	= 0x81;///bit0~3表示端点地址,bit8 表示方向,输入还是输出
__u8  bmAttributes		= 0x02;///属性(bit0、bit1构成传输类型,00--控制,01--等时,10--批量,11--中断)
   __le16 wMaxPacketSize	= 0x40;///端点一次可以处理的最大字节数
    __u8  bInterval			= 0x00;///希望主机轮询自己的时间间隔
 
    /* NOTE:  these two are _only_ in audio endpoints. */
    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    __u8  bRefresh;
    __u8  bSynchAddress;
} __attribute__ ((packed));

1.3.6 获取设备字符串描述符

1.3.6.1 获取字符串描述符语言ID
struct usb_ctrlrequest {
__u8 bRequestType		= 0x80;// 这是一个主机发给设备(bit0~bit4)的一个标准(bit5~bit6)的请求命令,请求的结果是要求设备给Host返回(bit7 == 1)
__u8 bRequest			= 0x06;// 是一个GET_DESCRIPTOR,即获取描述符的请求
__le16 wValue		= 0x0300;// 高字节表示描述符类型,01表示设备,02表示配置,3表示字符串;低字节表示索引。
__le16 wIndex			= 0x0000;
__le16 wLength		= 0x00FF;
} __attribute__ ((packed));
/* USB_DT_STRING: String descriptor */
struct usb_string_descriptor {
	__u8  bLength		= 0x04;
	__u8  bDescriptorType	= 0x03;

	__le16 wData[1]		= 0x0409;/* UTF-16LE encoded 代表0x0409的语言编码 */
} __attribute__ ((packed));
1.3.6.2 获取字符串描述符(产品信息)
struct usb_ctrlrequest {
__u8 bRequestType		= 0x80;// 这是一个主机发给设备(bit0~bit4)的一个标准(bit5~bit6)的请求命令,请求的结果是要求设备给Host返回(bit7 == 1)
__u8 bRequest			= 0x06;// 是一个GET_DESCRIPTOR,即获取描述符的请求
__le16 wValue		= 0x0301;// 高字节表示描述符类型,01表示设备,02表示配置,3表示字符串;低字节表示索引。
__le16 wIndex			= 0x0000;
__le16 wLength		= 0x00FF;
} __attribute__ ((packed));
/* USB_DT_STRING: String descriptor */
struct usb_string_descriptor {
	__u8  bLength		= 0x04;
	__u8  bDescriptorType	= 0x03;

	__le16 wData[1]		= “Gadget Serial v2.4”;/
} __attribute__ ((packed));
1.3.6.3 获取字符串描述符(商家信息)

无。

1.3.7 第三次获取设备描述符

同上。

1.3.8 第二次获取设备配置

同上。

1.3.9 选中设备配置

struct usb_ctrlrequest {
__u8 bRequestType		= 0x00;// 这是一个主机发给设备(bit0~bit4)的一个标准(bit5~bit6)的请求命令,请求的结果是要求设备给Host返回(bit7 == 1)
__u8 bRequest			= 0x09;// 是一个SET_CONFIGURATION,即配置设备。
__le16 wValue		= 0x0001;// 表示选中的配置Index
__le16 wIndex			= 0x0000;
__le16 wLength		= 0x0000;
} __attribute__ ((packed));

1.3.10 获取Line_Coding

struct usb_ctrlrequest {
__u8 bRequestType		= 0xA1;// 这是一个主机发给设备(bit0~bit4)的一个标准(bit5~bit6)的请求命令,请求的结果是要求设备给Host返回(bit7 == 1)
__u8 bRequest			= 0x21;
__le16 wValue		= 0x0000;
__le16 wIndex			= 0x0000;
__le16 wLength		= 0x0007;
} __attribute__ ((packed));
/* Line Coding Structure from CDC spec 6.2.13 */
struct usb_cdc_line_coding {
	__le32	dwDTERate		= 0x00;
	__u8	bCharFormat		= 0x00;
#define USB_CDC_1_STOP_BITS			0
#define USB_CDC_1_5_STOP_BITS			1
#define USB_CDC_2_STOP_BITS			2

	__u8	bParityType		= 0x00;
#define USB_CDC_NO_PARITY				0
#define USB_CDC_ODD_PARITY			1
#define USB_CDC_EVEN_PARITY			2
#define USB_CDC_MARK_PARITY			3
#define USB_CDC_SPACE_PARITY			4

	__u8	bDataBits			= 0x00;
} __attribute__ ((packed));

1.3.11 配置Line_Coding

struct usb_ctrlrequest {
__u8 bRequestType		= 0x21;// 这是一个主机发给设备(bit0~bit4)的一个标准(bit5~bit6)的请求命令,请求的结果是要求设备给Host返回(bit7 == 1)
__u8 bRequest			= 0x22;
__le16 wValue		= 0x0000;
__le16 wIndex			= 0x0000;
__le16 wLength		= 0x0000;
} __attribute__ ((packed));
发布了47 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u013836909/article/details/104886626
今日推荐