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


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

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

主机与设备之间的通信模型
1.4 USB分组标识

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

1.5.2 控制(Control)传输

1.5.3 中断传输事务

1.6 USB描述符
其关系如下图所示:

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

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

1.6.3 接口描述符
设备应至少支持一个接口,如:块传输数据接口,部分设备可能支持其它的接口。复合设备可以支持额外接口,以支持音频和视频功能。标准中并没有定义此类接口。接口可能有多个可选设置,主机将会检查每个可选的设置。
每个设备至少支持控制端点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(会话请求)协议
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设备端驱动


3.2.1 设备控制器驱动

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

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驱动从USB子系统的初始化开始,USB子系统的初始化在文件driver/usb/core/usb.c
subsys_initcall(usb_init); 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为例)
4.1.3 注册集线器
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包
static inline void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context) static inline void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context) static inline void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context, int interval)
若为root hub,将调用rh_urb_enqueue(),共有两种传输事务(控制传输和中断传输)
static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) { if (usb_endpoint_xfer_int(&urb->ep->desc)) // 中断传输 return rh_queue_status (hcd, urb); if (usb_endpoint_xfer_control(&urb->ep->desc)) // 控制传输 return rh_call_control (hcd, urb); return -EINVAL; }
b) 非root_hub传输
对于非常root_hub传输,它调用:
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
c) 批量传输
root_hub本身没有批量传输流程,按照控制传输流程,控制传输最终要通过switch语句跳转到Bulk-Only传输流程中。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/214322.html原文链接:https://javaforall.net
