1.USB主机
在Linux驱动中,USB驱动处于最底层的是USB主机控制器硬件,在其之上运行的是USB主机控制器驱动,主机控制器之上为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。
因此,在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制插入其中的USB设备,后者控制USB设备如何与主机通信。Linux内核USB核心负责USB驱动管理和协议处理的主要工作。主机控制器驱动和设备驱动之间的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;通过全局变量维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。
2.USB设备
Linux内核中USB设备侧驱动程序分为3个层次:UDC驱动程序、Gadget API和Gadget驱动程序。UDC驱动程序直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。当前Gadget API是UDC驱动程序回调函数的简单包装。Gadget驱动程序具体控制USB设备功能的实现,使设备表现出网络连接、打印机或USB Mass Storage等特性,它使用Gadget API控制UDC实现上述功能。Gadget API把下层的UDC驱动程序和上层的Gadget驱动程序隔离开,使得在Linux系统中编写USB设备侧驱动程序时能够把功能的实现和底层通信分离。
3.层次
在USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。USB设备程序绑定到接口上。
对于这四个层次的简单描述如下:
(1)设备通常具有一个或多个的配置
(2)配置经常具有一个或多个的接口
(3)接口没有或具有一个以上的端点
4.端点
USB通信最基本的形式是通过端点(USB端点分中断(Interrupt)、批量(Bulk)、等时(ISO)、控制(Control)四种,每种用途不同),USB端点只能往一个方向传送数据,从主机到设备或者从设备到主机,端点可以看作是单向的管道(pipe)。驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否已经安装了硬件。USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序,热插拨脚本使用它来确定当一个特定的设备插入到系统时该自动执行哪一个驱动程序的Probe。
5. 数据结构
(1)USB设备:对应数据结构struct usb_device
(2)配置:struct usb_host_config (任一时刻,只能有一个配置生效)
(3)USB接口:struct usb_interface (USB 核心将其传递给USB设备驱动,并由USB设备驱动负责后续的控制。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要 一个以上的驱动程序。)
(4)端点: struct usb_host_endpoint ,它所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。
6. USB端点分类
USB 通讯的最基本形式是通过一个称为端点的东西。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。
USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:
(1)控制CONTROL
控制端点被用来控制对USB设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB 设备都有一个控制端点称为端点 0, 被 USB 核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.
(2)中断INTERRUPT
每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB 键盘和鼠标的主要的数据传送方法。它还用以传送数据到USB设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.
(3)批量BULK
批量端点用以传送大量数据。这些端点通常比中断端点大得多. 它们普遍用于不能有任何数据丢失的情况。USB 协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass Storage和USB网络设备上。
(4)等时ISOCHRONOUS
等时端点也批量传送大量数据, 但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。
控制和批量端点用于异步数据传送,而中断和等时端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。
7. endpoint
C/C++ Code复制内容到剪贴板structusb_host_endpoint{structusb_endpoint_descriptordesc;//端点描述符structlist_headurb_list;//此端点的URB对列,由USB核心维护void*hcpriv;structep_device*ep_dev;/*Forsysfsinfo*/unsignedchar*extra;/*Extradescriptors*/intextralen;intenabled;};当调用USB设备驱动调用usb_submit_urb提交urb请求时,将调用int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)把此urb增加到urb_list的尾巴上。(hcd: Host Controller Driver,对应数据结构struct usb_hcd )
8. urb
所有USB通讯均为请求--响应模式,USB设备不会主动向Host发送数据。写数据:USB设备驱动发送urb请求给USB设备,USB设备不需要回数据。读数据:USB设备驱动发送urb请求给USB设备,USB设备需要回数据。
USB 设备驱动通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。
urb 以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个 USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。
一个 urb 的典型生命循环如下:
(1)被创建;
(2)被分配给一个特定 USB 设备的特定端点;
(3)被提交给 USB 核心;
(4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动;
(5)被 USB 主机控制器驱动处理, 并传送到设备;
(6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。
urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。
8.1 提交 urb
一旦 urb 被正确地创建并初始化, 它就可以提交给 USB 核心以发送出到 USB 设备. 这通过调用函数sb_submit_urb 实现.
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
参数:
struct urb *urb :指向被提交的 urb 的指针
gfp_t mem_flags :使用传递给 kmalloc 调用同样的参数, 用来告诉 USB 核心如何及时分配内存缓冲
因为函数 usb_submit_urb 可被在任何时候被调用(包括从一个中断上下文), mem_flags 变量必须正确设置. 根据 usb_submit_urb 被调用的时间,只有 3 个有效值可用:
GFP_ATOMIC
只要满足以下条件,就应当使用此值:
1) 调用者处于一个 urb 结束处理例程,中断处理例程,底半部,tasklet或者一个定时器回调函数.
2) 调用者持有自旋锁或者读写锁. 注意如果正持有一个信号量, 这个值不必要.
3) current-state 不是 TASK_RUNNING. 除非驱动已自己改变 current 状态,否则状态应该一直是TASK_RUNNING .
GFP_NOIO
驱动处于块 I/O 处理过程中. 它还应当用在所有的存储类型的错误处理过程中.
GFP_KERNEL
所有不属于之前提到的其他情况
在 urb 被成功提交给 USB 核心之后, 直到结束处理例程函数被调用前,都不能访问 urb 结构的任何成员
8.2 urb结束处理例程
如果 usb_submit_urb 被成功调用, 并把对 urb 的控制权传递给 USB 核心, 函数返回 0; 否则返回一个负的错误代码. 如果函数调用成功, 当 urb 被结束的时候结束处理例程会被调用一次.当这个函数被调用时, USB 核心就完成了这个urb, 并将它的控制权返回给设备驱动.
只有3 种结束urb并调用结束处理例程的情况:
(1)urb 被成功发送给设备, 且设备返回正确的确认.如果这样, urb 中的status变量被设置为 0.
(2)发生错误, 错误值记录在 urb 结构中的 status 变量.
(3)urb 从 USB 核心unlink. 这发生在要么当驱动通过调用 usb_unlink_urb 或者 usb_kill_urb告知 USB 核心取消一个已提交的 urb,或者在一个 urb 已经被提交给它时设备从系统中去除.
9. 探测和断开
在 struct usb_driver 结构中, 有 2 个 USB 核心在适当的时候调用的函数:
(1)当设备插入时, 如果 USB 核心认为这个驱动可以处理(USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序),则调用探测(probe)函数,探测函数检查传递给它的设备信息, 并判断驱动是否真正合适这个设备.
(2)由于某些原因,设备被移除或驱动不再控制设备时,调用断开(disconnect)函数,做适当清理.
探测和断开回调函数都在USB集线器内核线程上下文中被调用, 因此它们休眠是合法的. 为了缩短 USB 探测时间,大部分工作尽可能在设备打开时完成.这是因为 USB 核心是在一个线程中处理 USB 设备的添加和移除, 因此任何慢设备驱动都可能使 USB 设备探测时间变长。
9.1探测函数分析
在探测回调函数中, USB设备驱动应当初始化它可能用来管理 USB 设备的所有本地结构并保存所有需要的设备信息到本地结构, 因为在此时做这些通常更容易.为了和设备通讯,USB 驱动通常要探测设备的端点地址和缓冲大小.
PS:Linux USB驱动相关细节知识补充
1. 在usb_fill_bulk_urb,usb_fill_int_urb,usb_fill_control_urb都需要指定回调函数,当此URB请求完成时,usb core回调用此函数。
注意:urb 回调函数是在中断上下文运行, 因此它不应做任何内存分配, 持有任何信号量, 或任何可导致进程休眠的事情. 如果从回调中提交 urb 并需要分配新内存块, 需使用 GFP_ATOMIC 标志来告知 USB 核心不要休眠.
2. urb封装函数:
(1)int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,void*data, int len, int*actual_length,int timeout)
功能:创建批量 urb 并发送到指定的设备, 接着在返回之前等待完成.
参数:
struct usb_device *usb_dev :目标 USB 设备指针
unsigned int pipe :目标 USB 设备的特定端点. 必须使用特定的宏创建.
void *data :如果是 OUT 端点, 指向要发送到设备的数据的指针. 如果是 IN 端点, 这是从设备读取的数据的缓冲区指针.
int len : data 参数指向的缓冲的长度
int *actual_length :指向函数放置真实字节数的指针,根据端点方向,这些字节要么是被发送到设备的,要么是从设备中读取的.
int timeout :时钟嘀哒数, 应等待的时间. 如果为 0, 函数永远等待操作完成.
返回值:成功返回0,actual_length 参数包含被传送或从设备中读取的字节数.否则返回负的错误值.
(2)int usb_control_msg(struct usb_device*dev, unsigned int pipe, __u8 request,__u8 requesttype, __u16 value, __u16 index,void *data, __u16 size,int timeout)
功能:创建控制 urb 并发送到指定的设备, 接着在返回之前等待完成.
参数:
struct usb_device *usb_dev :目标 USB 设备指针
unsigned int pipe :目标 USB 设备的特定端点. 必须使用特定的宏创建.
__u8 request :控制消息的 USB 请求值.
__u8 requesttype :控制消息的 USB 请求类型.
__u16 value :控制消息的 USB 消息值.
__u16 index :控制消息的 USB 消息索引值.
void *data :如果是 OUT 端点, 指向要发送到设备的数据的指针. 如果是 IN 端点, 这是从设备读取的数据的缓冲区指针.
__u16 size : data 参数指向的缓冲的长度
int timeout :时钟嘀哒数, 应等待的时间. 如果为 0, 函数永远等待操作完成.
返回值:成功返回被传送到或从设备读取的字节数.否则返回负的错误值.
(3)int usb_interrupt_msg(struct usb_device*usb_dev, unsigned int pipe,void *data,int len, int *actual_length,int timeout)
功能:创建中断 urb 并发送到指定的设备, 接着在返回之前等待完成.其实就是usb_bulk_msg的包装,所有参数和usb_bulk_msg一样使用
Linux系统中USB驱动程序的工作流程详解
(3)USB接口:struct usb_interface (USB 核心将其传递给USB设备驱动,并由USB设备驱动负责后续的控制。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要 一个以上的驱动程序。) (4)端点: struct usb_host_endpoint ,它所包含的真实端点信息在另一个结构中:struct usb_endpo...
Linux下USB驱动详解linux中usb驱动
usb_submit_urb():把urb报文传到USB设备驱动模块中去,实现数据传输。usb_read():从USB设备读取数据。usb_write():向USB设备写入数据。usb_bulk_msg():应用程序发送和接收USB数据报文。数据传输过程中,USB还采用了如下协议:1. USB 设备认证协议(USB Device Authentication Protocol)2. USB 设备...
Linux USB 驱动开发实例(一)——USB摄像头驱动实现源码分析
一、初始化设备模块 模块初始化和卸载通过调用`module_init`和`module_exit`函数实现,关键数据结构为USB驱动结构,支持即插即用功能,通过`spca5xx_probe`和`spca5xx_disconnect`函数。二、上层软件接口模块 基于V4L协议规范,通过`file_operations`数据结构实现设备关键系统调用,功能包括:Open打开初始化...
Linux系统USB设备自动识别技术linuxusb识别
1)首先,用户需要将USB设备插入到Linux操作系统对应的USB端口上;2)接着,系统会向该USB端口发送一个USB设备检测信息,并查询该USB设备的设备类型、型号、管理等信息,以确定这是一个可识别的USB设备;3)然后,系统会安装相应的USB设备驱动程序;4)最后,系统会将USB设备识别作为该操作系统可用的设备...
调试LinuxUSB串口调试实战指南linuxusb串口
在Linux系统下,比较常见的USB调试方法有两种:一种是经典的串行调试,另一种是物理串行调试。本文将着重介绍如何使用Linux的USB串行口调试程序,步骤如下:(1)检查并安装USB串行线驱动 本篇文章将介绍使用USB串口来进行Linux USB调试,首先需要先确认安装USB串口驱动,具体安装方法如下所示:sudo apt-get...
linux系统中怎么驱动U盘?
1、插入U盘。2、输入 fdisk -l \/dev\/sda 查看输出结果例如:3、看了上面的输出就知道U盘所在的设备了,\/dev\/sdb1,接着便是挂载了,假设将U盘挂载到\/mnt\/usb目录中,就是 mount -t msdos \/dev\/sdb1 \/mnt\/usb 如果是fat16就使用下面的命令 mount -t msdos \/dev\/sdb1 \/mnt\/usb 如果是...
linux系统下安装usb网卡驱动图文?
usb_modeswitch -KW -v 148f -p 2878 模式切换后加载显示 lsusb| grep Ralink Bus 001 Device 003: ID 148f:7601 Ralink Technology, Corp.可以看到型号为 7601 ,据此我们选择 mt7601u 驱动进行‘安装。安装驱动 mt7601u , 如果已经自带跳过此步骤 方法1:有可能系统内核已经集成好了你需要的...
Linux CDC ACM驱动介绍
以USB转串口芯片CH342为例,介绍USB串口设备在Linux系统中识别过程:1. 通过lsusb命令查看系统下所有USB设备。2. 通过lsusb -v命令查看更多设备信息。3. 通过dmesg内核消息查看设备连接信息及驱动匹配情况。4. 可以修改内核消息等级查看更多驱动log日志。Linux支持不同等级的日志输出,可在驱动代码中添加调试...
怎样写linux下的USB设备驱动程序
注册USB驱动程序:所 有的USB驱动程序都必须创建的结构体是struct usb_driver。这个结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述USB驱动程序。创建一个有效的 struct usb_driver结构体,只须要初始化五个字段就可以了,在框架程序中是这样的:static struct usb_driver skel_driver = {...
linuxusb串口驱动linuxusb串口
先获取一下\/dev的列表,然后得到插播事件后,在获取一下当前的\/dev列表,然后做一下比较linux系统会自动识别USB接口为串口磁盘sda(通常为sda1,可通过fdisk-l命令查询),挂载就可用。例:新建挂载目录mkdir\/mnt\/usb,挂载mount\/dev\/sda1\/mnt\/usb,用完卸载挂起点unmount\/dev\/sda1\/mnt\/usb。如何查看...