原文地址:http://blog.chinaunix.net/uid-25445243-id-4040449.html

一、USB硬件介绍
1.1、概述

        一条USB传输线分别由地线、电源线、D+和D-四条线构成,D+和D-是差分输入线,它使用的是3.3V的电压(与CMOS的5V电平不同),而电源线和地线可向设备提供5V电压,最大电流为500mA(可以在编程中设置)。
        USB设备可以直接和HOST通信,或者通过Hub和Host通信。
一个USB系统中仅有一个USB 主机,设备包括USB功能设备和USB HUB,最多支持127个设备。物理连接指的是USB传输线。在USB 2.0系统中要求使用屏蔽双绞线。
1.2、USB HUB
        USB HUB提供了一种低成本、低复杂度的USB接口扩展方法。HUB的上行PORT面向HOST,下行PORT面向设备(HUB或功能设备)。在下行PORT上,HUB提供了设备连接检测和设备移除检测的能力,并给各下行PORT供电。HUB可以单独使能各下行PORT不同PORT可以工作在不同的速度等级(高速/全速/低速)。
        如果USB设备通过集线器和Host通信,主机集线器监视着每个端口的信号电压,当有新设备接入时便可觉察。集线器端口的两根信号线的每一根都有15kΩ的下拉电阻,而每一个设备在D+都有一个 1.5kΩ的上拉电阻。当用USB线将PC和设备接通后,设备的上拉电阻使信号线的电位升高,因此被主机集线器检测到。

二、USB数据格式

        USB的重要关键字:

        端点(Endpoint):位于USB设备或主机上的一个数据缓冲区,是USB设备中可以进行数据收发的最小单元支持单向或者双向的数据传,用来存放和发送USB的各种数据,每一个端点都有惟一的确定地址,有不同的传输特性(如输入端点、输出端点、配置端点、批量传输端点)。设备支持端点的数量是有限制的,除默认端点外低速设备最多支持2组端点(2 个输入,2 个输出),高速和全速设备最多支持15组端点。

        帧:时间概念,在USB中,一帧就是1MS,它是一个独立的单元,包含了一系列总线动作,USB将1帧分为好几份,每一份是一个USB的传输动作。

        上行、下行:设备到主机为上行,主机到设备为下行。

2.1、USB数据传输方法

        数据在USB线里传送是由低位到高位发送的。

        USB采用不归零取反来传输数据,当传输线上的差分数据输入0时就取反,输入1时就保持原值,为了确保信号发送的准确性,当在USB总线上发送一个包时,传输设备就要进行位插入操作(即在数据流中每连续6个1后就插入一个0),从而强迫NRZI码发生变化。这些是由专门硬件处理的。

2.2、数据格式

        USB数据是由二进制数字串构成的,首先数字串构成域(有七种),域再构成包,包再构成事务(IN、OUT、SETUP),事务最后构成传输(中断传输、并行传输、批量传输和控制传输)。下面介绍一下域、包和事务。

2.2.1、域
        是USB数据最小的单位,由若干位组成(多少位由具体的域决定),域可分为七个类型:

        1、同步域(SYNC)
        所有的USB包都由SYNC开始,高速包的SYNC宽度为32bit,全速/低速包的SYNC段度为8bit。实际接收到的SYNC长度由于USB HUB的关系,可能会小于该值。八位的值固定为0000 0001,用于本地时钟与输入同步。

        2、标识域(PID)
        由四位标识符 + 四位标识符反码构成,表明包的类型和格式,这是一个很重要的部分,这里可以计算出,USB的标识码有16种。
USB2.0使用了十六
种标识码,标识码的作用是用来说明包的属性的,标识码是和包联系在一起的,标识码分别有以下十六种:

        令牌包 :

        0x01:输出(OUT)启动一个方向为主机到设备的传输,并包含了设备地址和标号。

        0x09:输入(IN) 启动一个方向为设备到主机的传输,并包含了设备地址和标号。

        0x05:帧起始(SOF)表示一个帧的开始,并且包含了相应的帧号。

        0x0d:设置(SETUP)启动一个控制传输,用于主机对设备的初始化

        数据包 :

        0x03:偶数据包(DATA0)。

        0x0b:奇数据包(DATA1)。

        握手包:

        0x02:确认接收到无误的数据包(ACK)。

        0x0a:无效,接收(发送)端正在忙而无法接收(发送)信息。

        0x0e:错误,端点被禁止或不支持控制管道请求。

        特殊包0x0C前导,用于启动下行端口的低速设备的数据传输。

        3、地址域(ADDR):七位地址,代表了设备在主机上的地址,地址000 0000被命名为零地址,是任何设备第一次连接到主机时,在被主机配置、枚举前的默认地址,由此可以知道为什么一个USB主机只能接127个设备的原因。

        4、端点域(ENDP),四位,由此可知一个USB设备端点数量最大为16个。

        5、帧号域(FRAM),11位,每一个帧都有一个特定的帧号,帧号域最大容量0x800,对于同步传输有重要意义。

        6、数据域(DATA):长度为0~1023字节,在不同的传输类型中,数据域的长度各不相同,但必须为整数个字节的长度。

        7、校验域(CRC):对令牌包和数据包中非PID域进行校验的一种方法,CRC校验在通讯中应用很泛,是一种很好的校验方法,CRC码的除法是模2运算,不同于10进制中的除法。

2.2.2、包
        USB数据包的格式如下:

        由域构成的包有四种类型,分别是令牌包、数据包、握手包和特殊包,前面三种是重要的包,不同包的域结构不同,介绍如下:        

        1、令牌包

        分为输入包、输出包、设置包和帧起始包(注意这里的输入包是用于设置输入命令的,输出包是用来设置输出命令的,而不是放数据的)其中输入包、输出包和设置包的格式都是一样的:

                                               SYNC + PID + ADDR + ENDP + CRC5(五位的校验码)

        帧起始包的格式:

                                               SYNC + PID + 11位FRAM + CRC5(五位的校验码)

        2、数据包

        分为DATA0包和DATA1包,当USB发送数据的时候,如果一次发送的数据长度大于相应端点的容量时,就需要把数据包分为好几个包,分批发送,DATA0包和DATA1包交替发送,即如果第一个数据包是DATA0,那第二个数据包就是DATA1。但也有例外情况,在同步传输中(四类传输类型中之一),所有的数据包都是为DATA0,格式如下:

                                                        SYNC + PID + 0~1023字节 + CRC16

        3、握手包
        结构最为简单的包,格式如下:

                                                                    SYNC + PID
        握手包包括 ACK、NAK、STALL以及NYET 四种,其中 ACK 表示肯定的应答,成功的数据传输。NAK 表示否定的应答,失败的数据传输,要求重新传输。STALL表示功能错误或端点被设置了STALL属性。NYET表示尚未准备好,要求等待。

2.2.3、事务

        分别有IN、OUT和SETUP三大事务,每一种事务都由令牌包、数据包、握手包三个阶段构成,这里用阶段的意思是因为这些包的发送是有一定的时间先后顺序的,事务的三个阶段如下:

        1、令牌包阶段:启动一个输入、输出或设置的事务。

        2、数据包阶段:按输入、输出发送相应的数据。

        3、握手包阶段:返回数据接收情况,在同步传输的IN和OUT事务中没有这个阶段,这是比较特殊的。

        事务的三种类型如下(以下按三个阶段来说明一个事务):

        1、 IN事务:

        令牌包阶段——主机发送一个PID为IN的输入包给设备,通知设备要往主机发送数据;

        数据包阶段——设备根据情况会作出三种反应(要注意:数据包阶段也不总是传送数据的,根据传输情况还会提前进入握手包阶段)。

        1) 设备端点正常:设备往主机里面发出数据包(DATA0与DATA1交替);

        2) 设备正在忙:无法往主机发出数据包就发送NAK无效包,IN事务提前结束,到了下一个IN事务才继续;

        3) 相应设备端点被禁止:发送错误包STALL包,事务也就提前结束了,总线进入空闲状态。

        握手包阶段——主机正确接收到数据之后就会向设备发送ACK包。

        2、 OUT事务:

        令牌包阶段——主机发送一个PID为OUT的输出包给设备,通知设备要接收数据;

        数据包阶段——比较简单,就是主机会往设备送数据,DATA0与DATA1交替

        握手包阶段——设备根据情况会作出三种反应

        1) 设备端点接收正确,设备给主机返回ACK,通知主机可以发送新的数据,如果数据包发生了CRC校验错误,将不返回任何握手信息;

        2) 设备正在忙,无法给主机返回ACK,就发送NAK无效包,通知主机再次发送数据;

        3) 相应设备端点被禁止,发送错误包STALL包,事务提前结束,总线直接进入空闲状态。

        3、SETUT事务:

        令牌包阶段——主机发送一个PID为SETUP的输出包给设备,通知设备要接收数据;

        数据包阶段——比较简单,就是主机往设备送数据,注意,这里只有一个固定为8个字节的DATA0包,这8个字节的内容就是标准的USB设备请求命令,详见2.4。

        握手包阶段——设备接收到主机的命令信息后,返回ACK,此后总线进入空闲状态,并准备下一个传输(在SETUP事务后通常是一个IN或OUT事务构成的传输)。

2.3、标准的USB设备请求命令

        标准的USB设备请求命令是用在控制传输中的“初始设置步骤”里的数据包阶段(即DATA0,由八个字节构成)命令共有11个,大小都是8个字节,具有相同的结构,由5个字段构成(字段是标准请求命令的数据部分),结构如下(括号中的数字表示字节数,首字母bm,b,w分别表示位图、字节,双字节):

                    bmRequestType(1) + bRequest(1) + wvalue(2) + wIndex(2) + wLength(2)

        各字段的意义如下:

        1、bmRequestType:D7D6D5D4D3D2D1D0

        D7=0主机到设备

            =1设备到主机;

        D6D5=00标准请求命令

            =01 类请求命令

            =10用户定义的命令    

            =11保留值

        D4D3D2D1D0=00000 接收者为设备

            =00001 接收者为设备

            =00010 接收者为端点

            =00011 接收者为其他接收者

            =其他  其他值保留

        2、bRequest:请求命令代码,在标准的USB命令中,每一个命令都定义了编号,编号的值就为字段的值,编号与命令名称如下(要注意这里的命令代码要与其他字段结合使用,可以说命令代码是标准请求命令代码的核心,正是因为这些命令代码而决定了11个USB标准请求命令):

        0  GET_STATUS:用来返回特定接收者的状态;

        1  CLEAR_FEATURE:用来清除或禁止接收者的某些特性;

        2) 3  SET_FEATURE:用来启用或激活命令接收者的某些特性;

        5  SET_ADDRESS:用来给设备分配地址;

        6  GET_DEscriptOR:用于主机获取设备的特定描述符;

        7  SET_DEscriptOR:修改设备中有关的描述符,或者增加新的描述符;

        8  GET_CONFIGURATION:用于主机获取设备当前设备的配置值(注同上面的不同);

        9  SET_CONFIGURATION:用于主机指示设备采用的要求的配置;

        10  GET_INTERFACE:用于获取当前某个接口描述符编号;

        11  SET_INTERFACE:用于主机要求设备用某个描述符来描述接口;

        12 SYNCH_FRAME:用于设备设置和报告一个端点的同步帧;

       控制传输是USB的重心,而这11个命令是控制传输的重心,所以这11个命令是重中之重。
2.4、
描述符(Descriptor)

        Descriptor是一个完整的数据结构,可以通过C语言等编程实现,并存储在USB设备中,用于描述一个USB设备的所有属性,USB主机是通过一系列命令来要求设备发送这些信息的。它的作用就是给主机传递信息,从而让主机知道设备具有什么功能、属于哪一类设备、要占用多少带宽、使用哪类传输方式及数据量的大小,只有主机确定了这些信息之后,设备才能真正开始工作,所以描述符也是十分重要的部分,标准的描述符有5种,USB为这些描述符定义了编号:

        1——设备描述符

        2——配置描述符

        3——字符描述符

        4——接口描述符

        5——端点描述符

        上面的描述符之间有一定的关系,一个设备只有一个设备描述符,而一个设备描述符可以包含多个配置描述符,而一个配置描述符可以包含多个接口描述符,一个接口使用了几个端点,就有几个端点描述符。这些描述符是用一定的字段构成的,分别如下说明:

        1、设备描述符

struct _DEVICE_DEscriptOR_STRUCT

{

    BYTE bLength;          //设备描述符的字节数大小,为0x12

    BYTE bDescriptorType;  //描述符类型编号,为0x01

    WORD bcdUSB;           //USB版本号

    BYTE bDeviceClass;  //USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型

                        //0x00不是在设备描述符中定义的,如HID

    BYTE bDeviceSubClass;   //usb分配的子类代码,同上,值由USB规定和分配的

    BYTE bDeviceProtocl;    //USB分配的设备协议代码,同上

    BYTE bMaxPacketSize0;   //端点0的最大包的大小

    WORD idVendor;          //厂商编号

    WORD idProduct;         //产品编号

    WORD bcdDevice;         //设备出厂编号

    BYTE iManufacturer;     //描述厂商字符串的索引

    BYTE iProduct;          //描述产品字符串的索引

    BYTE iSerialNumber;     //描述设备序列号字符串的索引

    BYTE bNumConfiguration; //可能的配置数量

}

        2、配置描述符

struct _CONFIGURATION_DEscriptOR_STRUCT

{

    BYTE bLength;          //设备描述符的字节数大小,为0x12

    BYTE bDescriptorType;  //描述符类型编号,为0x01

    WORD wTotalLength;     //配置所返回的所有数量的大小

    BYTE bNumInterface;    //此配置所支持的接口数量

    BYTE bConfigurationVale;   //Set_Configuration命令需要的参数值

    BYTE iConfiguration;       //描述该配置的字符串的索引值

    BYTE bmAttribute;          //供电模式的选择

    BYTE MaxPower;             //设备从总线提取的最大电流

}

        3、字符描述符

struct _STRING_DEscriptOR_STRUCT

{

    BYTE bLength;          //设备描述符的字节数大小,为0x12

    BYTE bDescriptorType;  //描述符类型编号,为0x01

    BYTE SomeDescriptor[36];          //UNICODE编码的字符串

}

        4、接口描述符

struct _INTERFACE_DEscriptOR_STRUCT

{

    BYTE bLength;          //设备描述符的字节数大小,为0x12

    BYTE bDescriptorType;  //描述符类型编号,为0x01

    BYTE bInterfaceNunber; //接口的编号

    BYTE bAlternateSetting;//备用的接口描述符编号

    BYTE bNumEndpoints;    //该接口使用端点数,不包括端点0

    BYTE bInterfaceClass;  //接口类型

    BYTE bInterfaceSubClass;//接口子类型

    BYTE bInterfaceProtocol;//接口所遵循的协议

    BYTE iInterface;        //描述该接口的字符串索引值

}

        5、端点描述符

struct _ENDPOIN_DEscriptOR_STRUCT

{

    BYTE bLength;          //设备描述符的字节数大小,为0x12

    BYTE bDescriptorType;  //描述符类型编号,为0x01

    BYTE bEndpointAddress; //端点地址及输入输出属性

    BYTE bmAttribute;      //端点的传输类型属性

    WORD wMaxPacketSize;   //端点收、发最大包的大小

    BYTE bInterval;        //主机查询端点的时间间隔

}

三、传输
        USB系统中的数据传输,宏观看是在HOST和USB功能设备之间进行。微观看是在应用软件的Buffer和USB功能设备的端点之间进行。一般来说端点都有Buffer,可以认为USB通讯就是应用软件Buffer和设备端点Buffer之间的数据交换,交换的通道称为管道。应用软件通过和设备之间的数据交换来完成设备控制和数据传输。通常需要多个管道来完成数据交换,因为同一管道只支持一种类型的数据传输。用在一起来对设备进行控制的若干管道称为设备的接口,这就是端点、管道和接口的关系。
        USB 采用“令牌包”-“数据包”-“握手包”的传输机制,在令牌包中指定数据包去向或者来源的设备地址和端点(Endpoint),从而保证了只有一个设备对被广播的数据包/令牌包作出响应。握手包表示了传输的成功与否。
        USB 采用轮询的广播机制传输数据,所有的传输都由主机发起,任何时刻整个 USB 体系内仅允许一个数据包的传输,即不同物理传输线上看到的数据包都是同一被广播的数据包。

        传输由OUT、IN和SETUP事务构成,传输有四种类型,中断传输、批量传输、同步传输、控制传输,其中中断传输和批量传输的结构一样,同步传输有最简单的结构,而控制传输是最重要的也是最复杂的传输。

3.1、 控制传输
        控制传输是一种可靠的双向传输,是最重要也是最复杂的,一次控制传输可分为构成(初始设置阶段、可选数据阶段、状信息步骤),每一个阶段可以看成一个传输,也就是说控制传输其实是由三个传输构成的,在USB设备初次接到主机后,主机通过控制传输来交换信息、设备地址和读取设备的描述符,使得主机识别设备,并安装相应的驱动程序,这是每一个USB开发者都要关心的问题。
        第一阶段:从HOST到Device的SETUP事务传输,这个阶段指定了此次控制传输的请求类型。
        第二阶段:数据阶段,也有些请求没有数据阶段。
        第三阶段:状态阶段,通过一次IN/OUT传输表明请求是否成功完成。
        控制传输通过控制管道在应用软件和Device的控制端点之间进行,控制传输过程中传输的数据是有格式定义的,USB 设备或主机可根据格式定义解析获得的数据含义。其他三种传输类型都没有格式定义。控制传输对于最大包长度有固定的要求。对于高速设备该值为64Byte,对于低速设备该值为8,全速设备可以是8或16或32或64。
        最大包长度表征了一个端点单次接收/发送数据的能力,实际上反应的是该端点对应Buffer的大小。Buffer越大,单次可接收/发送的数据包越大,反之亦反。当通过一个端点进行数据传输时,若数据的大小超过该端点的最大包长度时,需要将数据分成若干个数据包传输。并保证除最后一个包外,所有的包长度均等于该最大包长度。也就是说如果一个端点收到/发送了一个长度小于最大包长度的包,即意味着数据传输结束
        控制传输在访问总线时也受到一些限制,如高速端点的控制传输不能占用超过 20%的微帧,全速和低速的则不能超过 10%。在一帧内如果有多余的未用时间,并且没有同步和中断传输,可以用来进行控制传输。


        控制传输(Transaction) 一次控制传输分为三(或两个)个阶段:建立(Setup)、数据(DATA)(可能没有)以及状态(Status)。每个阶段都由一次或多次(数据阶段)事务传输组成(Transaction)。
        上图为建立阶段的事务传输流程图。可以看出:
        与批量传输相比,在流程上并没有多大区别,区别只在于该事务传输发生的端点不一样、支持的最大包长度不一样、优先级不一样等这样一些对用户来说透明的东西。
        建立阶段过后,可能会有数据阶段,这个阶段将会通过一次或多次控制传输事务,完成数据的传输。同样也会采用PID翻转的机制。
        建立阶段,Device 只能返回ACK包,或者不返回任何包。
        最后是状态阶段,通过一次方向与前一次相反的控制事务传输来表明传输的成功与否。如果成功会返回一个长度为0的数据包,否则返回 NAK或STALL。下图为整个控制传输的示意图:

3.2、中断传输

        中断传输是一种轮询的传输方式,是一种单向的传输。HOST通过固定的间隔对中断端点进行查询,若有数据传输或可以接收数据则返回数据或发送数据。否则返回NAK,表示尚未准备好。中断传输的延迟有保证,但并非实时传输,它是一种延迟有限的可靠传输,支持错误重传。对于高速/全速/低速端点,最大包长度分别可以达到1024/64/8 Bytes。 高速中断传输不得占用超过 80%的微帧时间,全速和低速不得超过90%。 中断端点的轮询间隔由在端点描述符中定义,全速端点的轮询间隔可以是1~255mS。低速端点为10~255mS,高速端点为(2interval-1)*125uS,其中 interval 取 1到 16 之间的值。
        除高速高带宽中断端点外,一个微帧内仅允许一次中断事务传输。高速高带宽端点最多可以在一个微帧内进行三次中断事务传输,传输高达 3072 字节的数据。
        所谓单向传输,并不是说该传输只支持一个方向的传输。而是指在某个端点上该传输仅支持一个方向,或输出、或输入。如果需要在两个方向上进行某种单向传输,需要占用两个端点,分别配置成不同的方向。可以拥有相同的端点编号。
        中断传输由OUT事务和IN事务构成,用于键盘、鼠标等HID设备的数据传输。
        中断传输在流程上除不支持PING之外,其他的跟批量传输是一样的。他们之间的区别也仅在于事务传输发生的端点不一样、支持的最大包长度不一样、优先级不一样等这样一些对用户来说透明的东西。
        主机在排定中断传输任务时,会根据对应中断端点描述符中指定的查询间隔发起中断传输。中断传输有较高的优先级,仅次于同步传输。同样中断传输也采用 PID 翻转的机制来保证收发端数据同步。
        下图为中断传输的流程图。

3.3、批量传输
        批量传输由OUT事务和IN事务构成,是一种可靠的单向传输,但延迟没有保证,它尽量利用可以利用的带宽来完成传输,适合数据量比较大的传输。低速USB设备不支持批量传输,高速批量端点的最大包长度为512,全速批量端点的最大包长度可以为8、16、32、64。
        批量传输在访问USB总线时,相对其他传输类型具有最低的优先级,USB HOST总是优先安排其他类型的传输,当总线带宽有富余时才安排批量传输。高速的批量端点必须支持PING操作,向主机报告端点的状态。NYET表示否定应答,没有准备好接收下一个数据包,ACK 表示肯定应答,已经准备好接收下一个数据包。

        图中一个方框表示一个 Packet,灰色的包表示主机发出的包,白色的包表示Device发出的包。批量传输是可靠的传输,需要握手包来表明传输的结果。若数据量比较大,将采用多次批量事务传输来完成全部数据的传输,传输过程中数据包的PID按照 DATA0-DATA1-DATA0-...的方式翻转,以保证发送端和接收端的同步。若成功则将错误次数计数器清0,否则累加该计数器。USB允许连续3次以下的传输错误,会重试该传输。超过三次后,HOST 认为该端点功能错误(STALL),放弃该端点的传输任务。
        一次批量传输(Transfer)由 1 次到多次批量事务传输(Transaction)组成。
        翻转同步:发送端按照 DATA0-DATA1-DATA0-...的顺序发送数据包,只有成功的事务传输才会导致PID翻转,也就是说发送段只有在接收到ACK后才会翻转PID,发送下一个数据包,否则会重试本次事务传输。同样,若在接收端发现接收到的数据包不是按照此顺序翻转的,比如连续收到两个DATA0,那么接收端认为第二个DATA0是前一个DATA0的重传。

3.4、同步传输

        同步传输是一种实时的、不可靠的传输,不支持错误重发机制。只有高速和全速端点支持同步传输,高速同步端点的最大包长度为1024,低速的为1023。由OUT事务和IN事务构成。有两个特殊地方,第一,在同步传输的IN和OUT事务中是没有返回包阶段的;第二,在数据包阶段所有的数据包都为DATA0
        除高速高带宽同步端点外,一个微帧内仅允许一次同步事务传输,高速高带宽端点最多可以在一个微帧内进行三次同步事务传输,传输高达 3072 字节的数据。全速同步传输不得占用超过 80%的帧时间,高速同步传输不得占用超过90%的微帧时间。同步端点的访问也和中断端点一样,有固定的时间间隔限制。

        同步传输是不可靠的传输,所以它没有握手包,也不支持PID翻转。主机在排定事务传输时,同步传输有最高的优先级。

3.5、分离传输(Split Transaction)
        分离传输是在主机控制器和USB HUB之间的传输,它仅在主机控制器和HUB之间执行,通过分离传输,可以允许全速/低速设备连接到高速主机。分离传输对于USB设备来说是透明的、不可见的。
        分离传输,顾名思义就是把一次完整的事务传输分成两个事务传输来完成。其出发点是高速传输和全速/低速传输的速度不相等,如果使用一次完整的事务来传输,势必会造成比较长的等待时间,从而降低了高速USB总线的利用率。通过将一次传输分成两次,将令牌(和数据)的传输与响应数据(和握手)的传输分开,这样就可以在中间插入其他高速传输,从而提高总线的利用率。
3.6、总结

        包是USB总线数据传输的最小单位,不能被打断或干扰,否则会引发错误。若干个数据包组成一次事务传输,一次事务传输也不能打断,属于一次事务传输的几个包必须连续,不能跨帧完成。一次传输由一次到多次事务传输构成,可以跨帧完成。
四、枚举

        USB设备连接到HOST时,HOST必须通过默认的控制管道对其进行枚举,完成获得其设备描述、进行地址分配、获得其配置描述、进行配置等操作后方可正常使用。USB设备的即插即用特性即依赖于此。

4.1、枚举时USB的状态

        当USB设备插上主机时,主机就通过一系列的动作来对设备进行枚举配置(配置是属于枚举的一个态,态表示暂时的状态),这些态如下:

        1、接入态(Attached):设备接入主机后,主机通过检测信号线上的电平变化来发现设备的接入;

        2、供电态(Powered):就是给设备供电,分为设备接入时的默认供电值,配置阶段后的供电值(按数据中要求的最大值,可通过编程设置);

        3、缺省态(Default):USB在被配置之前,通过缺省地址0与主机进行通信;

        4、地址态(Address):经过了配置,USB设备被复位后,就可以按主机分配给它的唯一地址来与主机通信,这种状态就是地址态;

        5、配置态(Configured):通过各种标准的USB请求命令来获取设备的各种信息,并对设备的某些信息进行改变或设置。

        6、挂起态(Suspended):总线供电设备在3ms内没有总线操作,即USB总线处于空闲状态的话,该设备就要自动进入挂起状态,在进入挂起状态后,总的电流功耗不超过280uA。
        状态转移图如下所示:


       
4.2、枚举过程

        1、用户将一个USB设备插入USB端口,主机为端口供电,设备此时处于上电状态。主机检测设备。集线器使用中断通道将事件报告给主机。

        2、主机发送Get_Port_Status(读端口状态)请求,以获取更多的设备信息。 返回的消息告诉主机该设备是什么时候连接的。 集线器检测设备是低速运行还是高速运行,并将此信息送给主机,这是对Get_Port_Status请 求的响应。

        3、主机发送Set_Port_Feature(写端口状态)请求给集线器,要求它复位端口,请求集线器来重新设置端口。集线器使设备的USB数据线处于重启(RESET)状态至少10ms。

        4、主机使用Chirp K信号来了解全速设备是否支持高速运行。

        5、主机发送另一个Get_Port_Status请求,确定设备是否已经从复位状态退出。返回的数据有一位表示设备仍然处于重启状态。当集线器释放了重启状态,设备此时处于缺省状态,且已准备好在零端点通过缺省通道响应主机控制传输。缺省地址为00h,设备能从总线获取高达100mA的电流。
        6、集线器检测设备速度 

        集线器通过测定哪根信号线(D+或D-)在空闲时有更高的电压来检测设备是低速设备还是全速设备。全速和高速设备D+有上拉电阻,低速设备D-有上拉电阻。

        7、获取最大数据包长度
        主机向addre
ss 0发送Get_Device_Deor(读设备描述符)报文,以取得缺省控制管道所支持的最大数据包长度并在有限的时间内等待USB设备的响应该长度包含在设备描述符的bMaxPacketSize0字段中,其地址偏移量为7,所以这时主机只需读取该描述符的前8个字节。注意,主机一次只能枚举一个USB设备,所以同一时刻只能有一个USB设备使用缺省地址0。
        例:主机向设备发送一个八字节请求:80 06 00 01 00 00 40 00,设备接收到请求后产生一
个中断,我们可以通过读中断寄存器知道中断源,并且可以加读最后状态寄存器来确定第一个接到的包是否为一个Setup包。当控制器处理程序判断出它是一个Get_descriptor请求时,把设备描述符的前16个字节发送到端点0缓冲区中。剩下的2个字节描述符第一次请求时不再发送。

        8、主机分配一个新的地址给设备

        主机通过发送一个Set_Address请求来分配一个唯一的地址给设备。设备读取这个请求,返回一个确认,并保存新的地址。从此开始所有通信都使用这个新地址。

        例:当主机收到正确的前16字节描述符后,会给设备分配一个地址,我的PC分配的地址为:0x03(这个要看你的机子当的USB接口设备数目而定) Set_Address 请求所发送的数据为:00 05 03 00 00 00 00 00,其中的03就表示主机为设备配的地址为0x03,在以后的通信里设备就只对0x03地址作出应答。当设备产生一个接收中断后,根据所分配的地址设置设备的地址寄存器相应位。

        9、主机向新地址重新发送Get_Device_Deor命令,此次读取其设备描述符的全部字段,以了解该设备的总体信息,如VID,PID。
        例:主机发送设备描述符标准请求Get_descriptor:80 06 00 01 00 00 12 00,此次将要求把18个字节全部发送完。所以主
机要分两次来读取。第一次读取16个字节,第二次读取两个字节,最后主机发送0表示发送完毕的应答。

         10、主机向设备循环发送Get_Device_Configuration命令,要求USB设备回答,以读取全部配置信息。

        11、主机发送Get_Device_String命令,获得字符集描述(unicode),比如产商、产品描述、型号等等。此时主机将会弹出窗口,展示发现新设备的信息,产商、产品描述、型号等。

        根据Device_Deor和Device_Configuration应答,PC判断是否能够提供USB的Driver,一般win2k能提供几大类的设备,如游戏操作杆、存储、打印机、扫描仪等,操作就在后台运行。但是Win98却不可以,所以在此时将会弹出对话框,索要USB的Driver。

        12、主机分配并加载设备驱动程序,这时就可能作应用中的数据传输了。

        13、主机发送Set_Configuration(x)(写配置)命令请求为该设备选择一个合适的配置(x代表非0的配置值)。如果配置成功,USB设备进入“配置”状态,并可以和客户软件进行数据传输。此时,常规的USB完成了其必须进行的配置和连接工作,至此设备应当可以开始使用。不过,USB协议还提供了一些用户可选的协议,设备如果不应答,也不会出错,但是会影响到系统的功能。

        14、主机为复合设备接口分配驱动程序。如果集线器检测到有过流现象,或者主机要求集线器关闭电源,则USB总线切断设备供电电源。在这种情况下,设备与主机无法通信,但设备处于连接状态。   

        使用USB View 采集到的数据:

Device Descriptor:
bcdUSB:             0x0100
bDeviceClass:         0xDC
bDeviceSubClass:      0x00
bDeviceProtocol:      0x00
bMaxPacketSize0:      0x10 (16)
idVendor:           0x0471
idProduct:          0x0666
bcdDevice:          0x0100
iManufacturer:        0x00
iProduct:             0x00
iSerialNumber:        0x00
bNumConfigurations:   0x01

ConnectionStatus: DeviceConnected
Current Config Value: 0x01
Device Bus Speed:     Full
Device Address:       0x02
Open Pipes:              4

Endpoint Descriptor:
bEndpointAddress:     0x81
Transfer Type:   Interrupt
wMaxPacketSize:     0x0010 (16)
bInterval:            0x0A

Endpoint Descriptor:
bEndpointAddress:     0x01
Transfer Type:   Interrupt
wMaxPacketSize:     0x0010 (16)
bInterval:            0x0A

Endpoint Descriptor:
bEndpointAddress:     0x82
Transfer Type:        Bulk
wMaxPacketSize:     0x0040 (64)
bInterval:            0x0A

Endpoint Descriptor:
bEndpointAddress:     0x02
Transfer Type:        Bulk
wMaxPacketSize:     0x0040 (64)
bInterval:            0x0A

4.3、USB枚举实例

        对2440的USB HOST进行初始化完毕(主要包括对符合OHCI规范的寄存器的初始化—总线复位、中断使能、清除中断标志、电源管理、内存指针寄存器的初始化,各种数据结构的初始化等),等待USB设备的插入,当2440检测到有设备插入,就要对设备进行枚举了。起始枚举就相当于主机和设备建立连接的过程(接头),Host向Device询问一些东西,Device将自身的设备类型,如何进行通信报告给Host,这样Host就知道怎么对Device进行操作了。

        枚举的过程实际上用到而且只用到了总线的“控制传输(Control Transfer)”。这种传输方式通常用于配置/命令/状态等情形,其中的设置操作setup和状态操作status过程的数据包具有USB 协议定义的数据结构,因此,控制传输只能通过消息管道进行。

        一个完整的控制传输包括三个过程:

        1、建立连接。    
        2、数据过程(可选) 。        
        3、状态过程。

        建立连接的过程都是由Host发起,它开始于一个Setup令牌包,后面紧跟一个DATA0包。如果是控制输入传输,数据过程则为输入数据,若是控制输出传输,则数据过程是输出数据。

        数据过程的可选型是指设置过程需要指定数据长度,如果指定为0,则没有数据过程。状态过程跟在数据过程之后,状态过程恰好和数据过程的数据传输方向相反,因为此阶段主要是用来确认之前两阶段的所有数据都已经正确传输了。

       好了,下面就结合我的这个实例来看看枚举的详细过程:

        1、控制2440向U盘发送第一个Setup包,内容是80 06 00 01 00 00 08 00,其中最后的00 08表示得到DEVICE_DCESCRIPTOR的前8个字节,因为这个包的主要目的是要获得USB Device中端点0的最大包的大小(第8个字节),所以只需要8个字节就可以了。USB Device返回的设备标识符为12 01 10 01 00 00 00 40,下面我们需要把0x40记录下来,将其放到Endpoint Descriptor数据结构的DWORD0的MPS(bit16~bit32)块中去。
        2、接下来2440发送第二个Setup包,内容是00 05 01 00 00 00 00 00,这一次的作用是为USB设备分配地址。如果USB Device接收并接受了此地址设置包,会返回一个长度为0的数据包。主机接收到长度为0的状态包之后就会返回一个ACK给Device,Device再接收到这个ACK之后,就可以启用新地址了。这样Device就得到了一个唯一的设备地址,作为主机通信的唯一表示。

        3、发送第三个Setup包,内容是80 06 00 02 00 00 09 00,这次是为了获取配置描述符集合的大小,此位位于读回数据的第三个字节。U盘返回的数据为09 02 20 00 01 01 00 80 32,即描述符集合总大小为0x20。

        4、发送第四个Setup包,内容是80 06 00 02 00 00 09 00,和上次不同的仅仅是,这次要读回来的数据是整个配置描述符区域。U盘返回来的数据是09 02 20 00 01 01 00 80 32 09 04 00 00 02 08 06 50 00 07 05 82 02 40 00 00 07 05 02 02 40 00 00。这时候我们就可以知道该设备是什么类型的设备,支持什么样的操作了。

上述这两个过程也有的程序就是直接读取0xff个字符大小,当然同样可以达到读回设备描述符集合的目的。

至此,我们已经得到了所需要的设备信息,之后就可以对设备进行配置了。

        5、向设备发送第五个Setup包,数据为00 09 01 00 00 00 00 00,USB Device返回一个长度为0的数据包,表明数据正确接收。至此,USB枚举过程就完成了。初始设置步骤:就是一个由SET事务构成的传输

        可选数据步骤:就是一个由IN或OUT事务构成的传输,这个步骤是可选的,要看初始设置步骤有没有要求读/写数据(由SET事务的数据包阶段发送的标准请求命令决定)。

        状态信息步骤:这个步骤就是要获取状态信息,由IN或OUT事务构成的传输,但是要注意这里的IN和OUT事务和之前的INT和OUT事务有两点不同:

        1) 传输方向相反,通常IN表示设备往主机送数据,OUT表示主机往设备送数据;在这里,IN表示主机往设备送数据,而OUT表示设备往主机送数据,这是为了和可选数据步骤相结合;

        2) 在这个步骤里,数据包阶段的数据包都是0长度的,即SYNC+PID+CRC16。


GitHub 加速计划 / li / linux-dash
6
1
下载
A beautiful web dashboard for Linux
最近提交(Master分支:3 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐