USB协议

https://www.cnblogs.com/tureno/articles/6286321.html

http://blog.csdn.net/myarrow/article/details/8279156

1. USB协议

1.1 USB主机系统

       在USB主机系统中,通过根集线器与外部USB从机设备相连的处理芯片,称为USB主机控制器。USB主机控制器包含硬件、软件和固件一部分。

1.2 USB设备系统

        USB设备按功能分为两部分:集线器(Hub)和功能部件。从下图可知,主机通过根集线器连接到各种外围设备(集线器和功能部件)。

1.3 主机和设备之间通信模型
    

                                           主机与设备之间的通信模型


       上图展示了USB主机和USB设备之间的数据传输过程。在设备端,USB设备将非USB格式的数据进行打包处理,转换成USB格式的数据包,然后传递到链路层,经过硬件处理、传递到物理层,由物理层通过PHY以数据流的形式传输到主机。 

       USB主机在USB设备和USB主机之间发起的传输过程,稳为事务。每次事务以2到3个数据包的形式进行USB总线传输。每个数据包包含2到3个步骤:
       1) USB主机控制器向USB设备发出命令
       2) USB控制器和USB设备之间传递读写请求,其方向取决于第一部分的命令是读还是写
       3) 握手信号。
            USB主机控制器向USB设备发送事务类型请求,通过分组标识符来进行识别

 1.4 USB分组标识

        主机和设备之间进行操作,通过分组标识(PID)来进行传输。数据包传输格式一般由:PID、数据/控制信息、CRC校验码组成
        常见的PID主要包括令牌、数据、握手等类型组成。PID码以特定的方式组成,如下表所示:

      PID分组码是数据传输流程中的重要元素。无论硬件还是软件,都要对PID分组码进行分析,从而做出正确响应。USB主机和设备严格按照PID分组码信息进行信息交互。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

附加:

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

 

(一):是USB数据最小的单位,由若干位组成(至于是多少位由具体的域决定),域可分为七个类型: 
  1、同步域(SYNC),八位,值固定为0000 0001,用于本地时钟与输入同步
  2、标识域(PID),由四位标识符+四位标识符反码构成表明包的类型和格式,这是一个很重要的部分,这里可以计算出,USB的标识码有16种

      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进制中的除法。 
  
(二):由域构成的包有四种类型,分别是令牌包、数据包、握手包和特殊包,前面三种是重要的包,不同的包的域结构不同,介绍如下   
  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  
  
(三)事务:分别有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)设备正在忙,无法接收主机发出数据包就发送NAK无效包,通知主机再次发送数据; 
3)相应设备端点被禁止,发送错误包STALL包,事务提前结束,总线直接进入空闲状态。 
  
3、SETUT事务: 
令牌包阶段——主机发送一个PID为SETUP的输出包给设备,通知设备要接收数据; 
数据包阶段——比较简单,就是主机会设备送数据,注意,这里只有一个固定为8个字节的DATA0包,这8个字节的内容就是标准的USB设备请求命令(共有11条) 
握手包阶段——设备接收到主机的命令信息后,返回ACK,此后总线进入空闲状态,并准备下一个传输(在SETUP事务后通常是一个IN或OUT事务构成的传输)
  
(四)传输:传输由OUT、IN、SETUP事务其中的事务构成,传输有四种类型,中断传输、批量传输、同步传输、控制传输,其中中断传输和批量转输的结构一样,同步传输有最简单的结构,而控制传输是最重要的也是最复杂的传输。 
1、中断传输:由OUT事务和IN事务构成,用于键盘、鼠标等HID设备的数据传输中 
2、批量传输:由OUT事务和IN事务构成,用于大容量数据传输,没有固定的传输速率,也不占用带宽,当总线忙时,USB会优先进行其他类型的数据传输,而暂时停止批量转输。 
3、同步传输:由OUT事务和IN事务构成,有两个特殊地方,第一,在同步传输的IN和OUT事务中是没有返回包阶段的;第二,在数据包阶段所有的数据包都为DATA0 
4、控制传输:最重要的也是最复杂的传输,控制传输由三个阶段构成(初始设置阶段、可选数据阶段、状态信息步骤),每一个阶段可以看成一个的传输,也就是说控制传输其实是由三个传输构成的,用来于USB设备初次加接到主机之后,主机通过控制传输来交换信息,设备地址和读取设备的描述符,使得主机识别设备,并安装相应的驱动程序,这是每一个USB开发者都要关心的问题。 
1)、初始设置步骤:就是一个由SET事务构成的传输; 
2)、可选数据步骤:就是一个由IN或OUT事务构成的传输,这个步骤是可选的,要看初始设置步骤有没有要求读/写数据(由SET事务的数据包阶段发送的标准请求命令决定); 
3)、状态信息步骤:顾名思义,这个步骤就是要获取状态信息,由IN或OUT事务构成构成的传输,但是要注意这里的IN和OUT事务和之前的INT和OUT事务有两点不同: 
  (1) 传输方向相反,通常IN表示设备往主机送数据,OUT表示主机往设备送数据;在这里,IN表示主机往设备送数据,而OUT表示设备往主机送数据,这是为了和可选数据步骤相结合; 
  (2) 在这个步骤里,数据包阶段的数据包都是0长度的,即SYNC+PID+CRC16 

--------------------------------------------------------------------------------------------------------------------------------------------------

1.5 数据包传输模式

       当USB设备连接到集线器,集线器状态将发生相应的变化,并将状态变化信息传递给USB主机。USB主机通过根集线器向USB设备发送命令,获取USB设备的各种信息,包含USB设备传输类型、ID号、Product、USB速度等信息。
       USB主机和USB设备之间的数据传输共有四种类型:控制传输、批量传输、中断传输和同频传输。与之对应,USB主机和USB设备之间有四种事务:控制事务、批量事务、中断事务和同步事务。


1.5.1 批量(Bulk)传输


    作用:主要用于非实时性传输,数据包较大而延时要求较低。
    特点:数据传输准备即可,采用批量传输模式的USB从机设备,如U盘
    数据传输分三个阶段:
    a) 令牌阶段:主机发送请求USB设备依据请求PID来判断IN或OUT传输
    b) 数据传输阶段:依据令牌阶段的IN或OUT传输,来决定数据传输为DATA0或DATA1来进行数据传输
    c) 握手阶段:接收信息的一方发送ACK信号以表示接收成功;若为NAK,表示发送失败STALL表示不可预知的错误

 1.5.2 控制(Control)传输


       作用:USB传输过程必须支持的传输模式USB主机为了获取设备描述符、ID、Product等信息,向USB设备发送相应的PID命令
       特点:唯一可以进行IN/OUT传输的传输模式。
数据宽度:控制传输方式可以以8、16、32或64字节的数据进行传输,这取决于设备的传输速度。
       USB主机和设备之间必须支持控制传输,通过端点0进行数据传输。控制传输分为令牌、数据传输和握手阶段。

 1.5.3 中断传输事务


作用:按照一定时刻轮询设备是否有中断传输请求
特点:查询频率取决于端点的模式结构,从1到255ms不等
中断传输主要用于实时性要求非常高的从机设备,如键盘操纵杆和Mouse等
传输过程也分为令牌阶段、数据传输和握手阶段 

1.6 USB描述符

   USB协议中共定义了以下四种描述符:
   1) 设备描述符
   2) 配置描述符
   3) 接口描述符
   4) 端点描述符

   其关系如下图所示:

1.6.1 设备描述符

      每个USB设备都有一个唯一的设备描述符,如下表所示:

1.6.2 配置描述符

   每个USB设备都有默认的配置描述符,支持至少一个接口,每个配置描述符如下表:

 

1.6.3 接口描述符

   设备应至少支持一个接口,如:块传输数据接口,部分设备可能支持其它的接口。复合设备可以支持额外接口,以支持音频和视频功能。标准中并没有定义此类接口。接口可能有多个可选设置,主机将会检查每个可选的设置


1.6.4 端点描述符

   每个设备至少支持控制端点0。USB设备应该支持三类端点:控制端点、输入端点和输出端点。

 2. OTG协议

        OTG设备采用Mini-AB插座,相对于传统的USB数据线,Mini-AB接口多了一根数据线ID,ID线是否接入将Mini-AB接口分为Mini-A和Mini-B接口两种类型。在OTG设备之间数据连接的过程中,通过OTG数据线Mini-A和Mini-B接口来确定OTG设备的主从:接入Mini-A接口的设备默认为A设备(主机设备);接入Mini-B接口的设备,默认为B设备(从设备)。

        A设备和B设备无需交换电缆接口,即可通过主机交换协议(HNP)实现A、B设备之间的角色互换。同时,为了节省电源,OTG允许总线空闲时A设备判断电源。此时,若B设备希望使用总线,可以通过会话请求协议(SRP)请求A设备提供电源。

2.1 HNP(主机交换)协议

    当Mini-A接口接入A设备并确定A设备为主机时;若B设备希望成为主机,则A设备向B设备发送SetFeature命令,允许B设备进行主机交换。B设备检测到总线挂起5ms后,即挂起D+并启动HNP,使总线处于SE0状态。此时A设备检测到总线处于SE0状态,即认为B设备发起主机交换,A设备进行响应。待B设备发现D+线为高电平而D-线为低电平(J状态),表示A设备识别了B设备的HNP请求。B设备开始总线复位并具有总线控制权,主机交换协议完成。

2.2 SRP(会话请求)协议

    对于主机,要求能响应会话请求;对于设备,仅要求能够发起SRP协议。OTG设备,不仅要求发起SRP,而且还能响应SRP请求。
    SRP分为数据线脉冲调制和电压脉冲调两种方式,B设备发起SRP必须满足以下两个条件:
    1) B设备检测到A设备低于其有效的电压阈值,同时B设备低于有效的电压阈值。
    2) B设备必须检测到D+和D-数据线至少在2ms的时间内低于有效阈值,即处于SE0状态。

    数据线脉冲调制会话请求:B设备必须等到满足以上两个条件后,将数据线接入上拉电阻一定的时间,以备A设备过滤数据线上的瞬间电压。与此同时,B设备上拉D+以便于在全速模式下进行初始化操作。A设备在检测到D+变为高电平或D-变为低电平时产生SRP指示信号。
    
    Vbus脉冲调制会话请求:B设备同样需等待满足上述两个初始化条件,然后B设备通过对电容充电以提高总线电压,待达到总线上的电压阈值,唤醒A设备。在充电过程中,一定要保证充电的电压峰值在一定的范围以避免烧坏A设备。

3. USB驱动架构
 

      USB驱动架构如下图所示:

 

  3.1 USB主机端驱动

 

    USB核心(USBD)是整个USB驱动的核心部分,从上图可知,一方面USBD对接收到USB主机控制器的数据进行处理,并传递给上层的设备端驱动软件;同时也接收来自上层的非USB格式数据流,进行相应的数据处理后传递给USB主机控制器驱动。

    USB数据传输都以URB(USB Request Block)请求、URB生成、URB递交、URB释放为主线。从上图可知,当加载控制器驱动之后,注册根据集线器,hub和hcd驱动成为一个整体。接着,主机通过控制传输获取设备的控制描述符等信息,接着详述整个控制传输的流程。usb_submit_urb依据是否连接到根集线器来决定调用urb_enqueue或rh_urb_enqueue函数。
    USB从设备通过集线器或根集线器连接到USB主机上。比如:主机通过根集线器与外界进行数据交互,根集线器通过探测数据线状态的变化来通知USB主机是否有USB外围设备接入。

    在主机端控制器驱动加载的过程中,注册了根集线器,然后匹配了相应的hub驱动程序,同时完成了对Hub的轮询函数和状态处理函数的设置。这样,一旦hub集线器的状态发生变化,就会产生相应的中断,主机端控制器就会执行相应的中断处理函数,下图为hub驱动程序的流程图。

       USB Core中的usb_init()函数中完成了对hub线程(khubd,在usb_hub_init函数中真正地创建)的创建,然后完成相应设备的探测。主机端控制器驱动进行探测时,将hub驱动和主机端控制器驱动结合在一起,相互之间完成调用。 相对于大容量存储设备与主机之间通过控制/批量传输,集线器与主机之间通过中断/控制方式完成数据交互。

3.2 USB设备端驱动

 

    从上图可知,设备端驱动包含两部分:
    1) 底层设备控制器驱动
    2) 上层大容量存储类驱动

3.2.1 设备控制器驱动

       USB设备控制器驱动主要实现Gadget API定义的函数和中断服务函数,可按功能划分为:API函数实现模块和中断处理模块。
       API函数主要实现Gadget API定义的函数功能,如结构体usb_ep_ops和usb_gadget_ops中的函数、usb_gadget_register_driver函数。这些函数是供Gadget Driver调用。
       中断处理模块主要处理设备控制器产生的各种中断,包括端点中断、复位、挂起等中断。

上图为设备端控制器基本架构,主要完成了Gadget驱动和控制器驱动绑定、usb_gadget_register_driver注册。

3.3 OTG驱动

OS_FS: 文件系统
USBD: USB核心
HCD: 主机控制器驱动
UDC: 设备端控制器驱动

       OTG设备支持HNP和SRP协议。OTG设备通过USB OTG电缆连接到一起,其中接Mini-A接口的设备为A设备,默认为主机端,Mini-B接口的设备默认为B设备。当A、B设备完成数据交互之后,A、B设备之间的USB OTG电缆进入挂起状态,如下图所示:

        当B设备写入b_bus_req,向A设备发起HNP请求。待A设备响应之后,A设备发送a_set_b_hnp_en,B设备响应之后即进入主机状态,同时发送请求使用A设备set_device,这样A、B设备完成主从交换。

4. USB 传输流程

 4.1 USB初始化过程

       USB驱动作为一个系统,集成了众多的驱动模块,注册过程非常复杂。从USB系统的角度来说,USB主机驱动主要包含:

       1) USB核驱动

       2) 主机控制器驱动

       3) 集线器驱动

       驱动的加载执行流程如下图所示:

                               USB初始化过程
4.1.1 USB Core的初始化

   USB驱动从USB子系统的初始化开始,USB子系统的初始化在文件driver/usb/core/usb.c

[cpp] view plain copy
 
  1. subsys_initcall(usb_init);  
  2. module_exit(usb_exit);  

        subsys_initcall()是一个宏,可以理解为module_init()。由于此部分代码非常重要,开发者把它看作一个子系统,而不仅仅是一个模块。USB Core这个模块代表的不是某一个设备,而是所有USB设备赖以生存的模块。在Linux中,像这样一个类别的设备驱动被归结为一个子系统。subsys_initcall(usb_init)告诉我们,usb_init才是真正的初始化函数,而usb_exit将是整个USB子系统结束时的清理函数。

 

4.1.2 主机控制器的初始化及驱动执行(以EHCI为例)

   module_init(otg_init); 模块注册
   static init __init otg_init(void);
   platform_driver_register(); 平台注册
   static int __init otg_probe(struct platform_device *pdev); 探测处理函数
   reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); 获取寄存器信息
   data = platform_get_resource(pdev,IORESOURCE_MEM, 1); 获取内存信息
   irq = platform_get_irq(pdev,0); 获取中断号
   usb_create_hcd(&otg_hc_driver, &pdev->dev, pdev->dev.bus_id);
   分配和初始化HCD结构体。对设备数据空间进行分配,初始化计数器、总线、定时器、hcd结构体各成员值。
   ret = usb_add_hcd(hcd,irq,SA_INTERRUPT);
   完成HCD结构体的初始化和注册。申请buffer,注册总线、分配设备端内存空间,向中断向量表中申请中断,注册根集线器,对根集线器状态进行轮询。

4.1.3 注册集线器

       register_root_hub(hcd);
      在USB系统驱动加载的过程中,创建了集线器的线程(khubd),并且一直查询相应的线程事务。HCD驱动中,将集线器作为一个设备添加到主机控制器驱动中,然后进行集线器端口的初始化。在USB主机看来,根集线器本身也是USB主机的设备。USB主机驱动加载完成之后,即开始注册根集线器,并且作为一个设备加载到主机驱动之中。
       USB主机和USB设备之间进行数据交互,USB设备本身并没有总线控制权,U盘被动地接收USB主机发送过来的信息并做出响应。USB主机控制器与根集线器构成了主机系统,然后外接其它的USB设备。
       为了更好地探测到根集线器的状态变化,USB主机控制器驱动增加了状态轮询函数,以一定的时间间隔轮询根集线器状态是否发生变化。一旦根集线器状态发生变化,主机控制器就会产生相应的响应。
       USB主机和USB设备之间的数据传输以URB(USB Request Block)的形式进行。

 

 4.2 URB传输过程

    USB初始化过程中,无论是主机控制器驱动还是根集线器驱动,都是通过URB传输获取设备信息。

4.2.1 申请URB

    struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
    为urb分配内存并执行初始化。

4.2.2 初始化URB

    初始化具体的urb包 

[cpp] view plain copy
 
  1. static inline void usb_fill_bulk_urb(struct urb *urb,  
  2.      struct usb_device *dev,  
  3.      unsigned int pipe,  
  4.      void *transfer_buffer,  
  5.      int buffer_length,  
  6.      usb_complete_t complete_fn,  
  7.      void *context)  
  8.   
  9. static inline void usb_fill_control_urb(struct urb *urb,  
  10.     struct usb_device *dev,  
  11.     unsigned int pipe,  
  12.     unsigned char *setup_packet,  
  13.     void *transfer_buffer,  
  14.     int buffer_length,  
  15.     usb_complete_t complete_fn,  
  16.     void *context)  
  17. static inline void usb_fill_int_urb(struct urb *urb,  
  18.     struct usb_device *dev,  
  19.     unsigned int pipe,  
  20.     void *transfer_buffer,  
  21.     int buffer_length,  
  22.     usb_complete_t complete_fn,  
  23.     void *context,  
  24.     int interval)  

       不同的传输模式下,驱动为之申请不同的URB。其中,Linux内核只支持同步传输外的三种传输事件,ISO事务需要手工进行初始化工作。控制传输事务、批量传输事务、中断传输事务API如上所示。
      三种事务传输模式下的URB初始化函数有很多相似之处,主要参数含义如下:
      • urb: 事务传输中的urb
      • dev: 事务传输的目的设备
      • pipe: USB主机与USB设备之间数据传输的通道
      • transfer_buffer: 发送数据所申请的内存缓冲区首地址
      • length: 发送数据缓冲区的长度
      • context: complete函数的上下文
      • complete_fn: 调用完成函数
      • usb_fill_control_urb()的setup_packet: 即将被发送的设备数据包
      • usb_fill_int_urb()的interval: 中断传输中两个URB调度的时间间隔


4.2.3 提交URB

       URB初始化完成之后,USBD开始通过usb_start_wait_urb()提交urb请求(它调用usb_submit_urb来真正的发送URB请求),添加completition函数。
      接下来,从message.c传到主机控制器(hcd.c),开始真正的usb_hcd_submit_urb()。此时,根据是否为根集线器,进入不同的工作队列。
   usb_start_wait_urb->
   usb_submit_urb->
   usb_hcd_submit_urb


   a) root_hub传输

   若为root hub,将调用rh_urb_enqueue(),共有两种传输事务(控制传输和中断传输)

[cpp] view plain copy
 
  1. static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)  
  2. {  
  3.     if (usb_endpoint_xfer_int(&urb->ep->desc)) // 中断传输  
  4.         return rh_queue_status (hcd, urb);  
  5.     if (usb_endpoint_xfer_control(&urb->ep->desc)) // 控制传输  
  6.         return rh_call_control (hcd, urb);  
  7.     return -EINVAL;  
  8. }  

   b) 非root_hub传输
   对于非常root_hub传输,它调用:
   status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);

   c) 批量传输
   root_hub本身没有批量传输流程,按照控制传输流程,控制传输最终要通过switch语句跳转到Bulk-Only传输流程中。




猜你喜欢

转载自blog.csdn.net/lgb_love/article/details/79770122
今日推荐