USB学习之描述符篇,协议包,枚举过程,linux系列

来源:互联网 发布:lua java 编辑:程序博客网 时间:2024/06/13 04:10

linux下usb驱动源码分析


篇二:

USB学习之描述符篇


开发无线键鼠有2年多了,一直对HID这东西一知半解。赶在新项目需要重新编写USB描述符部分,开始搜集资料,深入学习。

 

    枚举可以理解为主机按不定的顺序向USB设备讨要设备信息,好给它分配资源,若枚举不成功,就放弃分配资源,免得浪费资源。一般都是使用中断传输方式通信。

    常用的描述符有以下几种:01H、设备描述符  02H、配置描述符  03H、字符串描述符  04H、接口描述符  05H、端点描述符

21H:HID描述符 22H:HID报告

    一个设备只能有一个设备描述符,而一个设备描述符可以包含多个配置描述符(bNumConfigurations ),一个配置描述符又可以包含多个接口描述符,一个接口使用了几个端点,就有几个端点描述符。

以下为HID描述:(一个USB设备同时包含键盘和鼠标,使用2个接口)

一、设备描述符:Device descriptor

hid_device_descriptor =
{

   0x12 ,                       //bLength   该段描述符总长18个,不可变
   0x01,                        //bDescriptorType:常用的如下0x01:设备  0x02配置 0x03字符 0x04接口 0x05端点 0x21HID
   0x0200,                    //bcdUSB  USB版本号: 1.1--0x0110   2.0--0x0200  3.0--0x0300
   0x00,                        //bDeviceClass  HID 不使用接口联合描述字与下面一起设置为00H
   0x00,                        //bDeviceSubClass
   0x00,                        //bDeviceProtocol
   8,                              // bMaxPacketSize0 端点0最大包的大小  USB2.0:低速--8  全速:8、16、32、64  高速:64
   0x1223,                    // idVendor     VID
   0x3F07,                    // idProduct     PID
   0x1110,                     // bcdDevice   厂商指定的设备版本号
   0x01,                         // iManufacturer  指向描述制造商字符串的索引
   0x02,                         // iProduct           指向描述产品的字符串索引
   0x00,                         // iSerialNumber  指向设备序列号的字符串索引
   0x01                          // bNumConfigurations  定义配置描述符的数量
};

二、配置描述符

hid_configuration_descriptor =

{
   0x09,                         // bLength  长度9个,不可变
   0x02,                         // bDescriptorType 配置描述符
   0x3b00,                     // wTotallength= 9+(9+9+7)+(9+9+7)  配置描述符+(接口描述符+HID描述符+端点描述符)*接口数
   0x02,                         // bNumInterfaces   接口数量=2(键盘+鼠标)
   0x01,                         // bConfigurationValue  Set_Configuration命令需要的参数值
   0x00,                         //iConfiguration 配置字符串索引
   0xa0,                         // bmAttributes bit7=1 bit6:1--自供电 0--总线供电 bit5:1--远程唤起 0--不支持 bit[4:0]=0

   0x32                          // MaxPower (in 2mA units) 50*2mA=100mA
};

三、接口配置符

keyboard_interface_descriptor =
{  
   0x09,                               // bLength                长度9个,不可变
   0x04,                               // bDescriptorType   接口描述符
   0x00,                               // bInterfaceNumber  接口0  (接口从0开始,键盘定义0,鼠标定义1)
   0x00,                               // bAlternateSetting   接口索引值
   0x01,                               // bNumEndpoints     端点个数1(端点0不可用,比如EP1)
   0x03,                               // bInterfaceClass     (3 = HID)
   0x01,                               // bInterfaceSubClass  接口子类型:01为Boot Device,键鼠在BIOS下就启动
   0x01,                               // bInterfaceProcotol    接口协议:00--None  01--Keyboard  02--Mouse
   0x00                                // iInterface                  描述该接口的字符串索引
};

mouse_interface_descriptor =
{  
   0x09,                               // bLength   长度9个,不可变
   0x04,                               // bDescriptorType   接口描述符
   0x01,                               // bInterfaceNumber  接口1   不同接口
   0x00,                               // bAlternateSetting   接口索引值
   0x01,                               // bNumEndpoints     端点个数1(端点0不可用,比如EP2)
   0x03,                               // bInterfaceClass     (3 = HID)
   0x01,                               // bInterfaceSubClass  
接口子类型:01为Boot Device,键鼠在BIOS下就启动
   0x02,                               // bInterfaceProcotol    
接口协议:00--None  01--Keyboard  02--Mouse
   0x00                                // iInterface                  描述该接口的字符串索引
};

四、HID描述符

 keyboard_hid_descriptor =
{   
   0x09,                               // bLength                 长度9个,不可变
   0x21,                               // bDescriptorType    HID描述符
   0x0110,                           // bcdHID                   HID专属版本号
   0x00,                               // bCountryCode       国家代码
   0x01,                               // bNumDescriptors   附属类描述字的数目1个
   0x22,                               // bDescriptorType    描述字类型:报告   
   HID_KEYBOARD_REPORT_DESCRIPTOR_SIZE  // 键盘HID报告描述字总字节数,比如:0x75,0x00,低字节在前      
};

 mouse_hid_descriptor =
{   
   0x09,                               // bLength                 长度9个,不可变
   0x21,                               // bDescriptorType    HID描述符
   0x0110,                           // bcdHID                   HID专属版本号
   0x00,                               // bCountryCode       国家代码
   0x01,                               // bNumDescriptors   附属类描述字的数目1个
   0x22,                               // bDescriptorType    描述字类型:报告   
   HID_MOUSE_REPORT_DESCRIPTOR_SIZE  // 鼠标HID报告描述字总字节数,比如0x34, 0x00,低字节在前       
};

五、端点描述符

hid_keyboard_endpoint1_in_descriptor  =
{   
   0x07,                               // bLength                   长度7个,不可变
   0x05,                               // bDescriptorType      端点描述符
   0x81,                               // bEndpointAddress   bit[7]:1--IN  0--OUT  地址为EP1,输入
   0x03,                               // bmAttributes            传输类型(中断--03H)
   0x08,                               // MaxPacketSize_LSB   端点1最大信息包尺寸
   0x00,                               // MaxPacketSize_MSB               
   0x08,                               //bInterval                   轮询间隔 一帧为8个中断间隔

};

hid_mouse_endpoint2_in_descriptor  =
{   
   0x07,                               // bLength                   长度7个,不可变
   0x05,                               // bDescriptorType      端点描述符
   0x82,                               // bEndpointAddress   bit[7]:1--IN  0--OUT  地址为EP2,输入
   0x03,                               // bmAttributes            传输类型(中断--03H)
   0x08,                               // MaxPacketSize_LSB   端点1最大信息包尺寸
   0x00,                               // MaxPacketSize_MSB               
   0x08,                               //bInterval                   轮询间隔 一帧为8个中断间隔

};

 

主机通过标准请求命令来获得以上HID描述符和HID报告:

     标准USB设备请求命令共有11个,大小都是8个字节,具有相同的结构,由5 个字段构成(字段是标准请求命令的数据部分),结构如下(括号中的数字表示字节数,首字母bm,b,w分别表示位图、字节,双字节):
bmRequestType(1)+bRequest(1) +wvalue(2)+wIndex(2) +wLength(2)
一、bmRequestType:

        bit[7]: 说明请求的传输方向  1--主机到设备(OUT)   0--设备到主机(IN)

        bit[6:5]:00--标准请求命令    01--专门类请求   10--用户定义的请求    11--保留

        bit[4:0]:00000--接收者为设备   00001--接收者为接口   00010--接收者为端点  00011--接收者为其他元件 其他设置保留

二、bRequest:

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

       wValue:0000H    wIndex:0000H(设备)、接口号或端点号  wLength:0002H
       A:[To Device]获取设备的状态:
          位0:自供电(0表示总线供电;1表示自供电).
          位1:远程唤醒(0表示不支持远程唤醒;1表示远程唤醒).
          位2~15:保留.
          一般选择总线供电,不支持远程唤醒,所以返回数据就是0x0000.
       B:[To Interface]获取接口的状态:
          接口状态的16位字节全部保留,所以返回数据就是0x0000.
       C:[To Endpoint]获取端点的状态:
          位0:Halt(0表示端点允许;1表示端点禁止).
          位1~15:保留(复位为0).

      
    2、Clear Feature (01H) 清除特性 

      wValue:所要禁用的特征   wIndex:0000H(设备)、接口号或端点号  wLength:0000H
       A:[To Device]清除设备的远程唤醒功能,并返回一个空包.
       B:[To Endpoint]解禁端点.
   
    3、Set Feature (03H) 设置特性 

     wValue:所要使能的特征   wIndex:0000H(设备)、接口号或端点号  wLength:0000H

       A:[To Device]设置设备的远程唤醒功能,并返回一个空包.
       B:[To Endpoint]禁止端点.


    4、Set Address (05H) 设置地址 

      wValue:新的设备地址,范围0001H到007FH   wIndex:0000H  wLength:0000H
       A:设置设备地址.


    5、Get Descriptor (06H) 获取描述符 

     wValue:高字节--描述符类型  低字节--描述符索引   wIndex:0000H或ID  wLength:需返回的字节数
       A:[To Device]获取设备描述符:
          描述当前USB协议的版本号.设备端点0的FIFO大小.USB设备的ID号等.
       B:[To Configuration]获取配置描述符:
          描述USB设备接口个数及是否有自供电能力等.
       C:[To Interface]获取接口描述符:
          描述端点0以外的物理端点个数等信息.
       D:[To Endpoint]获取端点描述符:
          描述端点0各端点的传输类型和最大信息包大小和端点的传输方向(IN/OUT).


    6、Set Descriptor (07H) 设置描述符(可选,无法更新) 

    wValue:高字节--描述符类型  低字节--描述符索引   wIndex:0000H或ID  wLength:需传输给设备的字节数


    7、Get Configuration (08H) 获取配置信息 

    wValue:0000H   wIndex:0000H   wLength:0001H


    8、Set Configuration (09H) 设置配置 

    wValue:低字节规定了一个配置,若此值与设备支持的配置匹配,设备将实现所请求配置   wIndex:0000H  wLength:0000H
       A:[To Configuration]设置配置描述符.
       B:[To Interface]设置接口描述符.
       C:[To Endpoint]设置端点描述符.


    9、Get Interface (0AH) 获取接口信息    
    wValue:0000H   wIndex:接口号(bInterfaceNumber)  wLength:0001H


    10、Set Interface (0BH) 设置接口 

    wValue:要选择的替代设置(bAlternateSetting)   wIndex:接口号(bInterfaceNumber)  wLength:0000H


    11、SYNCH_FRAME(0CH)

    wValue:0000H   wIndex:0000H  wLength:0006H
        用于设备设置和报告一个端点的同步帧.

一个描述设备描述符和描述配置描述符过程如下图:

USB学习之描述符篇--枚举 - lastnight1034 - lastnight1034的博客
可以看到80  06  00  01  00  00  12  00主机发给设备的请求:
bmRequestType=80H说明这是主机发给设备的标准请求;
bRequest=06H说明这句的作用是Get Descriptor
wValue=0100H(注意这是小端模式,高字节在图片里显示在后)说明需要设备上传设备描述符(01)
wLength=0012H(注意这是小端模式,高字节在图片里显示在后)说明设备必须上传12H个字节长度的数据
于是设备上传了0012H长的设备描述符:12  01  00  02  00  00  00  08  23  12  07  3f  10  11  01  02  00  01
第四行80  06  00  02  00  00  09  00主机发给设备请求:
按上面的解释,说明这是主机要求设备上传配置描述符(02H),因为主机无法得知配置描述符里的wTotallength多大,所以先发个标准长度0009H来试探。
于是设备上传了09H长的配置描述符:09  02  3b  00  02  01  00  a0  32
主机得知配置总的含有003bH个字节,于是再次发给设备上传配置描述符的请求:80  06 00 02 00 00 3b  00,要求的总字节长度为003bH
之后设备上传了003bH的数据:9个配置描述符+9个接口0描述符+9个HID描述符+7个端点1描述符+9个接口1描述符+9个HID描述符+7个端点1描述符
后主机进行设置配置:00  09  01  00  00  00  00  00 设置了配置描述符,使能端点1和端点2

 

USB学习之描述符篇--枚举 - lastnight1034 - lastnight1034的博客
因为有2个接口,所以分2次分别设置:
EP1:读取设备描述符,试探性配置描述符,返回键盘的配置描述符长度0022H,再次以0022H长度读取配置描述符。设置配置描述符,挂起等配置完成。
配置完成后读取HID报告:81  06  00  22  00  00  b5  00
81代表主机发给设备的接口   06代表Get Descriptor   22H为HID报告  wIndex:00为接口0  长度为75H+40H=b5H(?)
设备上传接口0的0075H长度字节HID报告。
开始设置接口1,仍然继续读取设备描述符试探性配置描述符,返回键盘的配置描述符长度0022H,再次以0022H长度读取配置描述符。设置配置描述符,挂起等配置完成。
配置完成后读取HID报告:不同的是wIndex:0001H 配置接口1,长度为34H+40H=74H(?)
设备上传接口1的0034H长度字节HID报告。结束后SET REPORT,结束,等待设备上传端点键盘鼠标数据。
 
剩下的是字符串这个不是必须的还没讲到。

篇三:

USB协通讯议--深入理解


0. 基本概念

       一个【传输】(控制、批量、中断、等时):由多个【事务】组成;

       一个【事务】(IN、OUT、SETUP):由一多个【Packet】组成。

       USB数据在【主机软件】与【USB设备特定的端点】间被传输。【主机软件】与【USB设备特定的端点】间的关联叫做【pipes】。一个USB设备可以有多个管道(pipes)。

1. 包(Packet)

        包(Packet)是USB系统中信息传输的基本单元,所有数据都是经过打包后在总线上传输的。数据在 USB总线上的传输以包为单位,包只能在帧内传输。高速USB 总线的帧周期为125us,全速以及低速 USB 总线的帧周期为 1ms帧的起始由一个特定的包(SOF 包)表示,帧尾为 EOF。EOF不是一个包,而是一种电平状态,EOF期间不允许有数据传输。
       注意:虽然高速USB总线和全速/低速USB总线的帧周期不一样,但是SOF包中帧编号的增加速度是一样的,因为在高速USB系统中,SOF包中帧编号实际上取得是计数器的高11位,最低三位作为微帧编号没有使用,因此其帧编号的增加周期也为 1mS。

      USB总线上的情形是怎样的?

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

 

        USB包由五部分组成,即同步字段(SYNC)、包标识符字段(PID)、数据字段、循环冗余校验字段(CRC)和包结尾字段(EOP),包的基本格式如下图:

 

 

1.1 PID类型(即包类型)

 1.2 Token Packets

     此格式适用于IN、OUT、SETUP、PING。

PID            数据传输方向

IN              Device->Host

OUT         Host->Device

SETUP    Host->Device

PING        Device->Host

1.3 Start-of-Frame(SOF) Packets

      SOF包由Host发送给Device。
    1) 对于full-speed总线,每隔1.00 ms ±0.0005 ms发送一次;
    2) 对于high-speed总线,每隔125 μs ±0.0625 μs发送一次;
    SOF包构成如下图所示:

 

1.4 Data Packets 

     有四种类类型的数据包:DATA0, DATA1, DATA2,and MDATA,且由PID来区分。DATA0DATA1被定义为支持数据切换同步(data toggle synchronization)。

1.5 Handshake Packets

• ACK: 对于IN事务,它将由host发出;对于OUT、SETUP和PING事务,它将由device发出。

• NAK: 在数据阶段,对于IN事务,它将由device发出;在握手阶段,对于OUT和PING事务,它也将由device发出;host从不发送NAK包

2. 事务(Transaction)

       在USB上数据信息的一次接收或发送的处理过程称为事务处理(Transaction)即:The delivery of service to an endpoint。一个事务由一系统packet组成,具体由哪些packet组成,它取决于具体的事务。可能由如下包组成:

       • 一个token packet

       • 可选的data pcket

        可选的handshake packet

        可选的special packet

2.1 输入(IN)事务处理

     输入事务处理:表示USB主机从总线上的某个USB设备接收一个数据包的过程。

        •【正常】的输入事务处理


    

      •【设备忙】时的输入事务处理    


     •【设备出错】时的输入事务处理

  

2.2. 输出(OUT)事务处理

        输出事务处理:表示USB主机把一个数据包输出到总线上的某个USB设备接收的过程。

    •【正常】的输出事务处理
   

 

    •【设备忙时】的输出事务处理

 

    •【设备出错】的输出事务处理

 

2.3 设置(SETUP)事务处理  

   •【正常】的设置事务处理

 

 
   •【设备忙时】的设置事务处理

 


   •【设备出错】的设置事务处理

 

3. USB传输类型

       在USB的传输中,定义了4种传输类型:

        控制传输 (Control Transfer)

        中断传输 (Interrupt Transfer)

       批量传输 (Bulk Transfer)

        同步传输 (Isochronous)

3.1 控制传输 (Control Transfer)

      控制传输由2~3个阶段组成:

      1) 建立阶段(Setup)

      2) 数据阶段(无数据控制没有此阶段)(DATA)

      3) 状态阶段(Status)

     每个阶段都由一次或多次(数据阶段)事务传输组成(Transaction)。

      控制数据由USB系统软件用于配置设备(在枚举时),其它的驱动软件可以选择使用control transfer实现具体的功能,数据传输是不可丢失的。

3.1.1 建立阶段

     主机从USB设备获取配置信息,并设置设备的配置值。建立阶段的数据交换包含了SETUP令牌封包、紧随其后的DATA0数据封包以及ACK握手封包。它的作用是执行一个设置(概念含糊)的数据交换,并定义此控制传输的内容(即:在Data Stage中IN或OUT的data包个数,及发送方向,在Setup Stage已经被设定)

 

3.1.2 数据阶段

     根据数据阶段的数据传输的方向,控制传输又可分为3种类型:

     1) 控制读取(读取USB描述符)

     2) 控制写入(配置USB设备)

     3) 无数据控制

 

     数据传输阶段:用来传输主机与设备之间的数据。

     控制读取

     是将数据从设备读到主机上,读取的数据USB设备描述符。该过程如下图的【Control Read】所示。对每一个数据信息包而言,首先,主机会发送一个IN令牌信息包,表示要读数据进来。然后,设备将数据通过DATA1/DATA0数据信息包回传给主机。最后,主机将以下列的方式加以响应:当数据已经正确接收时,主机送出ACK令牌信息包;当主机正在忙碌时,发出NAK握手信息包;当发生了错误时,主机发出STALL握手信息包。

     控制写入

       是将数据从主机传到设备上,所传的数据即为对USB设备的配置信息,该过程如下的图Control Wirte所示。对每一个数据信息包而言,主机将会送出一个OUT令牌信息包,表示数据要送出去。紧接着,主机将数据通过DATA1/DATA0数据信息包传递至设备。最后,设备将以下列方式加以响应:当数据已经正确接收时,设备送出ACK令牌信息包;当设备正在忙碌时,设备发出NAK握手信息包;当发生了错误时,设备发出STALL握手信息包。

 

3.1.3 状态阶段

       状态阶段:用来表示整个传输的过程已完全结束。
       状态阶段传输的方向必须与数据阶段的方向相反,即原来是IN令牌封包,这个阶段应为OUT令牌封包;反之,原来是OUT令牌封包,这个阶段应为IN令牌封包。

       对于【控制读取】而言,主机会送出OUT令牌封包,其后再跟着0长度的DATA1封包。而此时,设备也会做出相对应的动作,送ACK握手封包、NAK握手封包或STALL握手封包。

      相对地对于【控制写入】传输,主机会送出IN令牌封包,然后设备送出表示完成状态阶段的0长度的DATA1封包,主机再做出相对应的动作:送ACK握手封包、NAK握手封包或STALL握手封包。
 

 3.2 批量传输 (Bulk Transfer)

      用于传输大量数据,要求传输不能出错,但对时间没有要求,适用于打印机、存储设备等。

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

      它通过在硬件级执行“错误检测”和“重传”来确保host与device之间“准确无误”地传输数据,即可靠传输。它由三种包组成(即IN事务或OUT事务):

        1) token

        2) data

        3) handshake

1) For IN Token (即:IN Transaction)

     ACK: 表示host正确无误地接收到数据

     NAK: 指示设备暂时不能返回或接收数据  (如:设备忙)

     STALL:指示设备永远停止,需要host软件的干预 (如:设备出错) 

2) For OUT Token (即:OUT Transaction)

     如果接收到的数据包有误,如:CRC错误,Device不发送任何handshake包

     ACK: Device已经正确无误地接收到数据包,且通知Host可以按顺序发送下一个数据包

        • NAK: Device 已经正确无误地接收到数据包,且通知Host重传数据,由于Device临时状况(如buffer满)

        • STALL: 指示Device endpoint已经停止,且通知Host不再重传

3) Bulk读写序列

      即由一系统IN事务或OUT事务组成。

3.3 中断传输(Interrupt Transfer)

    中断传输由IN或OUT事务组成。 

    中断传输在流程上除不支持PING 之外,其他的跟批量传输是一样的。他们之间的区别也仅在于事务传输发生的端点不一样、支持的最大包长度不一样、优先级不一样等这样一些对用户来说透明的东西。
     主机在排定中断传输任务时,会根据对应中断端点描述符中指定的查询间隔发起中断传输。中断传输有较高的优先级,仅次于同步传输。
     同样中断传输也采用PID翻转的机制来保证收发端数据同步。下图为中断传输的流程图。

     中断传输方式总是用于对设备的查询,以确定是否有数据需要传输。因此中断传输的方向总是从USB设备到主机。

   

     DATA0或DATA1中的包含的是中断信息,而不是中断数据。

3.4 同步传输(Isochronous Transfer)

1) 它由两种包组成:

        1) token

        2) data

       同步传输不支持“handshake”和“重传能力”,所以它是不可靠传输。

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

      同步传输适用于必须以固定速率抵达或在指定时刻抵达,可以容忍偶尔错误的数据上。实时传输一般用于麦
克风、喇叭、UVC Camera等设备。实时传输只需令牌与数据两个信息包阶段,没有握手包,故数据传错时不会重传。

 

Isochronous data is continuous and real-time in creation, delivery, and consumption. Timing-relatedinformation is implied by the steady rate at which isochronous data is received and transferred. Isochronousdata must be delivered at the rate received to maintain its timing. In addition to delivery rate, isochronousdata may also be sensitive to delivery delays. For isochronous pipes, the bandwidth required is typicallybased upon the sampling characteristics of the associated function. The latency required is related to thebuffering available at each endpoint.

A typical example of isochronous data is voice. If the delivery rate of these data streams is not maintained,drop-outs in the data stream will occur due to buffer or frame underruns or overruns. Even if data isdelivered at the appropriate rate by USB hardware, delivery delays introduced by software may degrade applications requiring real-time turn-around, such as telephony-based audio conferencing.

The timely delivery of isochronous data is ensured at the expense of potential transient losses in the data stream. In other words, any error in electrical transmission is not corrected by hardware mechanisms such as retries. In practice, the core bit error rate of the USB is expected to be small enough not to be an issue.  USB isochronous data streams are allocated a dedicated portion of USB bandwidth to ensure that data can be delivered at the desired rate. The USB is also designed for minimal delay of isochronous data transfers.


 

 篇四:

USB枚举过程

 

 

 

 

 

1. 枚举是什么?

       枚举就是从设备读取一些信息,知道设备是什么样的设备,如何进行通信,这样主机就可以根据这些信息来加载合适的驱动程序。调试USB设备,很重要的一点就是USB的枚举过程,只要枚举成功了,那么就已经成功大半了。
       USB架构中, hub负责检测设备的连接和断开,利用其中断IN端点(Interrupt IN Endpoint)来向主机(Host)报告。在系统启动时,主机轮询它的根hub(Root Hub)的状态看是否有设备(包括子hub和子hub上的设备)连接。USB总线拓扑结构见下图(最顶端为主机的Root Hub):
                                              USB总线拓扑结构 

        一旦获悉有新设备连接上来,主机就会发送一系列的请求(Resqusts)给设备所挂载到的hub,再由hub建立起一条连接主机(Host)和设备(Device)之间的通信通道。然后主机以控制传输(Control Transfer)的方式,通过端点0(Endpoint 0)对设备发送各种请求,设备收到主机发来的请求后回复相应的信息,进行枚举(Enumerate)操作。所有的USB设备必须支持标准请求(StandardRequests),控制传输方式(Control Transfer)和端点0(Endpoint 0)。

       在讲解枚举之前,先大概说说USB的一种传输模式——控制传输。这种传输在USB中是非常重要的,它要保证数据的正确性,在设备的枚举过程中都是使用控制传输的。控制传输分为三个阶段:①建立阶段。②数据阶段。③确认阶段。

        建立(setup)阶段:都是由USB主机发起,它是一个setup数据包,里面包含一些数据请求的命令以及一些数据。如果建立阶段是输入请求,那么数据阶段就要输入数据;如果建立阶段是输出请求,那么数据阶段就要输出数据。如果在数据阶段,即便不需要传送数据,也要发一个0长度的数据包。数据阶段过后就是确认阶段。确认阶段刚好跟数据阶段相反,如果是输入请求,则它是一个输出数据包;如果是输出请求,则它是一个输入数据包。确认阶段用来确认数据的正确传输。

1.1 通信传输流

1.2 设备状态图

 1.3 状态详解

       1) 连接(Attached)
       设备可以连接到USB或者从USB上拔出.USB设备从总线上拨出后的状态在规范没定义,只说明一旦USB连到总线要求的操作以及属性.


       2) 上电(Powered)
       USB设备的电源可来自外部电源,也可从USB接口的集线器而来。电源来自外部电源的USB设备被称作自给电源式的(self-powered)。尽管自给电源式的USB设备可能在连接上USB接口以前可能已经带电,但它们直到连线上USB接口后才能被看作是加电状态(Powered state)。而这时候VBUS已经对设备产生作用了.
一个设备可能有既支持自给电源的,同时也支持总线电源式的配置。有一些支持其中的一种,而另一些设备配置可能只有在自给电源下才能被使用。设备对电源支持的能力是通过配置描述表(configuration descriptor)来反映的。当前的电源供给形式被作为设备状态的一部分被反映出来。设备可在任何时候改变它们的供电来源,比如说:从自给式向总线式改变,如果一个配置同时支持两种模式,那此状态的最大电源需求就是指设备在两种模式下从VBUS上获取电能的最大值。设备必须以此最大电源作为参照,而究竟处于何状态是不考虑的。如果有一配置仅支持一种电源模式,那么电源模式的改变会使得设备失去当前配置与地址,返回加电状态。如果一个设备是自给电源式,并且当前配置需要大于100mA电流,那么如果此设备转到了总线电源式,它必须返回地址状态(Address state)。自给电源式集线器使用VBUS来为集线控制器(Hub controller)提供电源,因而可以仍然保持配置状态(Configured state),尽管自给电源停止提供电源。


      3)默认状态(Default)

      设备上电后,它不响应任何总线处理,直到总线接收到复位信号为止.接收到复位信号后,用默认的地址可以对设备寻址.
       当用复位过程完成后,USB设备在正确的速度下操作(即低速/全速/高速).低速和全速的数据选择由设备的终端电阻决定.能进行高速操作的设备决定它是否在复位的过程的一部分执行高速操作.
        能进行高速操作的设备在全速的电气环境中操作时,必须能以全速成功复位.设备成功复位后,设备必须成功响应设备和配置描述符请求,并且返回适当的信息.当在全速下工作时,设备可能或者不能支持预定义的功能.


       4) 地址(Address)
       所有的USB设备在加电复位以后都使用缺省地址。每一设备在连接或复位后由主机分配一个唯一的地址。当USB设备处于挂起状态时,它保持这个地址不变。
        USB设备只对缺省通道(Pipe)请求发生响应,而不管设备是否已经被分配地址或在使用缺省地址。


        5) 配置状态( Configured )
        在USB设备正常工作以前,设备必须被正确配置。从设备的角度来看,配置包括一个将非零值写入设备配置寄存器的操作。配置一个设备或改变一个可变的设备设置会使得与这个相关接口的终端结点的所有的状态与配置值被设成缺省值。这包括将正在使用(date toggle)的结点(end point)的 (Date toggle)被设置成DATA0。


        6) 挂起状态
        为节省电源,USB设备在探测不到总线传输时自动进入中止状态。当中止时,USB设备保持本身的内部状态,包括它的地址及配置。
        所有的设备在一段特定的时间内探测不到总线活动时必须进入中止态。不管设备是被分配了非缺省的地址或者是被配置了,已经连接的设备必须在任何加电的时刻随时准备中止。总线活动的中止可能是因为主机本身进入了中止状态。另外,USB设备必须在所连接的集线器端口失效时进入中止态。这就是所指的选择性中止(Selective suspend)。
       USB设备在总线活动来到时结束中止态。USB设备也可以远程唤醒的电流信号来请求主机退出中止态或选择性中止态。具体设备具有的远程唤醒的能力是可选的,也就是说,如果一个设备有远程唤醒的能力,此设备必须能让主机控制此能力的有效与否。当设备复位时,远程唤醒能力必须被禁止。

 

2. 枚举步骤

       USB协议定义了设备的6种状态,仅在枚举过程中,设备就经历了4个状态的迁移:上电状态(Powered),默认状态(Default),地址状态(Address)和配置状态(Configured)(其他两种是连接状态和挂起状态(Suspend))。

2.1 用户把USB设备插入USB端口或给系统启动时设备上电

      这里指的USB端口指的是主机下的根hub或主机下行端口上的hub端口。Hub给端口供电,连接着的设备处于上电状态。此时,USB设备处于加电状态,它所连接的端口是无效的。

2.2 Hub监测它各个端口数据线上(D+/D-)的电压

      在hub端,数据线D+和D-都有一个阻值在14.25k到24.8k的下拉电阻Rpd,而在设备端,D+(全速,高速)和D-(低速)上有一个1.5k的上拉电阻Rpu。当设备插入到hub端口时,有上拉电阻的一根数据线被拉高到幅值的90%的电压(大致是3V)。hub检测到它的一根数据线是高电平,就认为是有设备插入,并能根据是D+还是D-被拉高来判断到底是什么设备(全速/低速)插入端口(全速、高速设备的区分在我将来的文章中描述)。如下图。

                                 USB全速/高速设备上电连接

       检测到设备后,hub继续给设备供电,但并不急于与设备进行USB传输。

USB接口定义如下图所示:

2.3 Host了解连接的设备

      每个hub利用它自己的中断端点向主机报告它的各个端口的状态(对于这个过程,设备是看不到的,也不必关心),报告的内容只是hub端口的设备连接/断开的事件。如果有连接/断开事件发生,那么host会发送一个 Get_Port_Status请求(request)给hub以了解此次状态改变的确切含义。Get_Port_Status等请求属于所有hub都要求支持的hub类标准请求(standard hub-class requests)。

2.4 Hub检测所插入的设备是高速还是低速设备

    hub通过检测USB总线空闲(Idle)时差分线的高低电压来判断所连接设备的速度类型,当host发来Get_Port_Status请求时,hub就可以将此设备的速度类型信息回复给host。USB 2.0规范要求速度检测要先于复位(Reset)操作。

2.5 hub复位设备

      主机一旦得知新设备已连上以后,它至少等待100ms以使得插入操作的完成以及设备电源稳定工作。然后主机控制器就向hub发出一个 Set_Port_Feature请求让hub复位其管理的端口(刚才设备插上的端口)。hub通过驱动数据线到复位状态(D+和D-全为低电平 ),并持续至少10ms当然,hub不会把这样的复位信号发送给其他已有设备连接的端口,所以其他连在该hub上的设备自然看不到复位信号,不受影响

2.6 Host检测所连接的全速设备是否是支持高速模式

       因为根据USB 2.0协议,高速(High Speed)设备在初始时是默认全速(Full Speed )状态运行,所以对于一个支持USB 2.0的高速hub,当它发现它的端口连接的是一个全速设备时,会进行高速检测,看看目前这个设备是否还支持高速传输,如果是,那就切到高速信号模式,否则就一直在全速状态下工作。
       同样的,从设备的角度来看,如果是一个高速设备,在刚连接bub或上电时只能用全速信号模式运行(根据USB 2.0协议,高速设备必须向下兼容USB 1.1的全速模式)。随后hub会进行高速检测,之后这个设备才会切换到高速模式下工作。假如所连接的hub不支持USB 2.0,即不是高速hub,不能进行高速检测,设备将一直以全速工作。

2.7 Hub建立设备和主机之间的信息通道

       主机不停地向hub发送Get_Port_Status请求,以查询设备是否复位成功。Hub返回的报告信息中有专门的一位用来标志设备的复位状态。
       当hub撤销了复位信号,设备就处于默认/空闲状态(Default state),准备接收主机发来的请求。设备和主机之间的通信通过控制传输,默认地址0,端点号0进行。此时,设备能从总线上得到的最大电流是100mA。(
所有的USB设备在总线复位后其地址都为0,这样主机就可以跟那些刚刚插入的设备通过地址0通信。)

2.8 主机发送Get_Descriptor请求获取默认管道的最大包长度

       默认管道(Default Pipe)在设备一端来看就是端点0。主机此时发送的请求是默认地址0,端点0,虽然所有未分配地址的设备都是通过地址0来获取主机发来的请求,但由于枚举过程不是多个设备并行处理,而是一次枚举一个设备的方式进行,所以不会发生多个设备同时响应主机发来的请求。
      设备描述符的第8字节代表设备端点0的最大包大小。虽然说设备所返回的设备描述符(Device Descriptor)长度只有18字节,但系统也不在乎,此时,描述符的长度信息对它来说是最重要的,其他的瞄一眼就过了。当完成第一次的控制传输后,也就是完成控制传输的状态阶段,系统会要求hub对设备进行再一次的复位操作(USB规范里面可没这要求)。再次复位的目的是使设备进入一个确定的状态。

2.9 主机给设备分配一个地址

       主机控制器通过Set_Address请求向设备分配一个唯一的地址。在完成这次传输之后,设备进入地址状态(Address state),之后就启用新地址继续与主机通信。这个地址对于设备来说是终生制的,设备在,地址在;设备消失(被拔出,复位,系统重启),地址被收回。同一个设备当再次被枚举后得到的地址不一定是上次那个了。

2.10 主机获取设备的信息

       主机发送 Get_Descriptor请求到新地址读取设备描述符,这次主机发送Get_Descriptor请求可算是诚心,它会认真解析设备描述符的内容。设备描述符内信息包括端点0的最大包长度,设备所支持的配置(Configuration)个数,设备类型,VID(Vendor ID,由USB-IF分配), PID(Product ID,由厂商自己定制)等信息。Get_Descriptor请求(Device type)和设备描述符(已抹去VID,PID等信息)见下图:

  标准Get_Descriptor请求

                        设备描述符(Device Descriptor)
      之后主机发送Get_Descriptor请求,读取配置描述符(Configuration Descriptor),字符串等,逐一了解设备更详细的信息。事实上,对于配置描述符的标准请求中,有时wLength一项会大于实际配置描述符的长度(9字节),比如255。这样的效果便是:主机发送了一个Get_Descriptor_Configuration 的请求,设备会把接口描述符,端点描述符等后续描述符一并回给主机,主机则根据描述符头部的标志判断送上来的具体是何种描述符。

      接下来,主机就会获取配置描述符。配置描述符总共为9字节。主机在获取到配置描述符后,根据里面的配置集合总长度,再获取配置集合。配置集合包括配置描述符,接口描述符,端点描符等等。
     如果有字符串描述符的话,还要获取字符串描述符。另外HID设备还有HID描述符等。

2.11  主机给设备挂载驱动(复合设备除外)

      主机通过解析描述符后对设备有了足够的了解,会选择一个最合适的驱动给设备。  然后tell the world(announce_device)说明设备已经找到了,最后调用设备模型提供的接口device_add将设备添加到 usb 总线的设备列表里,然后 usb总线会遍历驱动列表里的每个驱动,调用自己的 match(usb_device_match) 函数看它们和你的设备或接口是否匹配,匹配的话调用device_bind_driver函数,现在就将控制权交到设备驱动了。   

     对于复合设备,通常应该是不同的接口(Interface)配置给不同的驱动,因此,需要等到当设备被配置并把接口使能后才可以把驱动挂载上去。
     设备-配置-接口-端点关系见下图:

                 USB 设备-配置-接口-端点关系
    实际情况没有上述关系复杂。一般来说,一个设备就一个配置,一个接口,如果设备是多功能符合设备,则有多个接口。端点一般都有好几个,比如Mass Storage设备一般就有两个端点(控制端点0除外)。

2.12 设备驱动选择一个配置

     驱动(注意,这里是驱动,之后的事情都是有驱动来接管负责与设备的通信)根据前面设备回复的信息,发送Set_Configuration请求来正式确定选择设备的哪个配置(Configuration)作为工作配置(对于大多数设备来说,一般只有一个配置被定义)。至此,设备处于配置状态(Configured),当然,设备也应该使能它的各个接口(Interface)。
    对于复合设备,主机会在这个时候根据设备接口信息,给它们挂载驱动。

 3. 控制传输

     控制传输是最重要和最复杂的一种传输类型,其中使用了一个8字节大小的DATA0数据包,这8个字节的数据包是主机用来发送控制阶段中的请求命令,而这些请求命令是主机配置USB设备的关键。

1) bmRequestType(向谁请求)

    D7:传输方向
            0=主机至设备;1=设备至主机
    D6..5:命令类型
           D6D5=00:标准请求命令; D6D5=01:类请求命令;
           D6D5=10:用户定义命令; D6D5=11:保留。

    D4..0:接受者类型
          0=设备;1=接口 ;
          2=端点;3=其它
          4..31 保留
       这个域表明此请求的特性。特别地,这个域表明了第二阶段控制传输方向。如果wLength域被设作0的话,表明没有数据传送阶段,那Direction位就会被忽略.
       USB说明定义了一系列所有设备必须支持的标准请求。这些请求见下面的表<Standard Device Requests>。另外,一个设备类可定义更多的请求。设备厂商也可定义设备支持的请求.
       请求可被导引到设备,设备接口,或某一个设备端结点(endpoint)上。这个请求域也指定了接收者。当指定的是接口或端结点(endpoint)时,wIndex域指出那个接口或端节点

 
2) bmRequest(什么请求)
       这个域标识特别的请求。bmRequestType域的Type字段可修改此域的含义。本文仅定义Type 字段为0即标准设备请求时bRequest域值的含义。
 
3) wValue域
    此域用来传送当前请求的参数,随请求不同而变。
4) wIndex域
    当bmRequestType的Recipient字段为接口或端点时,wIndex域用来表明是哪一个接口或端结。
5) wLength域
    这个域表明第二阶段的数据传输长度。传输方向由bmRequstType域的Direction位指出。wLength域为0则表明无数据传输。在输入请求下,设备返回的数据长度不应多于wLength,但可以少于。在输出请求下,wLength指出主机发出的确切数据量。如果主机发送多于wLength的数据,设备做出的响应是无定义的。
 

3.1 标准设备请求

 

3.2 标准请求码(bRequest的值)

 

3.3 描述符类型

3.4 GET_DESCRIPTOR(读取描述符)

     这个请求返回存在的描述符.
        wValue域:
        高一字节:标识描述表类型(Descriptor Types)
        低一字节:表示描述符号的索引,将几个类型相同的描述符应用到设备中时,描述符索引用于选择一个特定的描述符(只能是配置和字符串描述符)。
        例如:设备可以用几个配置描述符。对于其他可以通过GetDescriptor()请求取得的标准描述符来说,描述符的索引须为0。描述符索引值的范围从0开始到设备使用该类型描述符的数量减1。
       wIndex域:
       标识字串描述表的语言(Language ID),如果是其它语言的话就设为0。
       wLength
      表示要返回多少字节。如果描述表长度大于wLength域值,那么只有描述表的初始部分被返回。如果描述表比wLength域值小,则发送一个短包来标志传输的结束。一个短包被定义成一个长度短于最大负载长度或一个空(NULL)包。
       这个标准请求包括3种描述符:设备(也就是设备的限定符)、配置(也就是其他速度配置)、及字符串。能进行高速操作的设备支持设备限定描述符返回有关设备不支持的速度信息(包括默认端点的wMaxPacketSize和其他速度配置的数量)。
       其他速度配置用与配置描述符相同的结构返回信息,但如果设备在其他速度下操作则返回配置信息。请求配置描述符将返回配置描述符,所有接口描述符和在这个请求中所有接口的端点描述符。
       第一个接口描述符的设备请求会一次返回配置描述表,所有的接口描述表和所有接口的端节点的描述表。第一个接口描述符紧跟着配置描述符号,第一个接口的端节点的描述符号随后。如果有其它的接口与端节点,它们的描述符欲跟在第一个接口与端节点描述符之后。与类有关的描述符,和/或厂商定义的描述符跟在标准描述符之后.
        所有的设备必须提供一个设备描述符并且至少一个配置描述符,如果一个设备不支持一个请求的描述符,则返回请求错误。
    •缺省状态:此请求合法。
    •地址状态:此请求合法。
    •配置状态:此请求合法。


 3.5 GET_INTERFACE(取得接口)

      这个请求向指定接口返回选中的备用设备。
      一些USB设备有接口设置互斥的配置。这个请求允许主机确定当前选定的备用设置。如果wValue或者wLength的值与上面指定的不一致,那么设备的行为没有定义;如果指定的接口不存在,那么设备将用请求错误响应。
     •默认状态:当设备处于默认状态时接收到这个请求,设备的行为没  有定义
     •地址状态:设备给出请求错误
     •配置状态:当设备处于配置状态时,这是一个有效的请求