USB协议架构及驱动架构

来源:互联网 发布:儿童手表下载软件 编辑:程序博客网 时间:2024/06/05 02:47
 5224人阅读 评论(0) 收藏 举报

目录(?)[-]

  1. USB协议
    1. USB主机系统
    2. USB设备系统
    3. 主机和设备之间通信模型    
    4. USB分组标识
    5. 数据包传输模式
      1. 批量Bulk传输
      2. 控制Control传输
      3. 中断传输事务
    6. USB描述符
      1. 设备描述符
      2. 配置描述符
      3. 接口描述符
      4. 端点描述符
    7. OTG协议
      1. HNP主机交换协议
      2. SRP会话请求协议
    8. USB驱动架构  
      1. USB主机端驱动
      2. USB设备端驱动
        1. 设备控制器驱动
      3. OTG驱动
    9. USB 传输流程
      1. USB初始化过程
        1. USB初始化过程 411 USB Core的初始化
        2. 主机控制器的初始化及驱动执行以EHCI为例
        3. 注册集线器
      2. URB传输过程
        1. 申请URB
        2. 初始化URB
        3. 提交URB

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分组码信息进行信息交互。

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 plaincopy
  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 plaincopy
  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 plaincopy
  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传输流程中。

0 0
原创粉丝点击