设备驱动程序是操作系统内核和机器硬件之间的接口。它由一组函数和一些私有数据组成。它是应用程序和硬件设备之间的桥梁。从应用程序的角度来看,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样操作硬件设备。
设备驱动程序是内核的一部分,主要完成以下功能:初始化和释放设备;从内核向硬件设备传输数据以及从硬件设备读取数据;读取应用程序数据并将其传输到设备文件并发送回应用程序请求。数据;检测并处理硬件设备中的错误。
1.Linux USB子系统分析
在Linux系统中,USB主机驱动程序由3部分组成:USB主机控制器驱动程序(HCD)、USB核心驱动程序(USBD)和不同类型的USB设备类驱动程序,如下所示。 HCD和USBD被称为协议软件或协议栈,这两部分共同处理协议相关的操作。
USB设备类驱动程序可以包含多个、不同的功能接口,对应于不同的驱动程序。它们不直接与USB设备硬件打交道,而是通过协议软件的抽象处理完成与设备不同功能接口的通信。
在Linux USB子系统中,HCD是一个直接与硬件交互的软件模块。它是USB协议栈的最底层部分,是USB主控制器硬件和数据传输的抽象。
HCD 只为 USB 总线驱动程序提供服务。 HCD提供了软件接口,即HCDI,使得各种USB主控制器的硬件功能都是基于软件的,由USB总线驱动程序调用和管理。 HCD向下直接管理和检测主控硬件的各种行为。 HCD提供的功能主要包括:上位控制器硬件初始化;为USBD层提供相应的接口函数;提供根HUB(ROOT HUB)设备配置和控制功能;完成四种数据传输等
USBD部分是整个USB主机驱动的核心。主要功能包括:USB总线管理; USB总线设备管理、USB总线带宽管理、USB数据传输的四种类型、USB HUB驱动,并为USB设备驱动提供相关接口。为应用程序提供对USB系统文件接口等的访问。其中,USB HUB是一种特殊类型的USB设备,其驱动程序包含在USBD层中。
在嵌入式Linux系统中,已经包含了HCD模块和USB核心驱动USBD,不需要用户重写。用户只需完成USB设备类驱动即可。
2、Linux系统中USB子系统主要数据结构
在 Linux 系统中,USBD 通过定义一组宏、数据结构和函数来抽象掉所有硬件或设备依赖性。
USBD中有四种主要的数据结构,分别是:
1.保存USB设备的信息,包括设备地址、设备描述符、配置描述符等。
2、保存USB总线系统的信息,包括总线上的设备地址信息、根集线器、带宽使用情况等。一个USB总线系统至少有一个主机控制器和一个根集线器,Linux系统支持多个USB总线系统。
3. 保存客户驱动程序信息,包括驱动程序名称以及驱动程序提供给USB内核的函数指针。
4.URB(Block)是USB通信的一种数据结构。 USBD 使用 URB 在 USB 设备类驱动程序和 USBD、USBD 和 HCD 之间传输数据。
3、Linux系统中USB设备的加载和卸载
当USB设备插入USB HUB的端口时,集中器会检测该设备的访问,并在下次主机通过中断交互查询时向其报告。集中器的端口在无设备连接时关闭,插入设备后不会自动打开。主机必须通过控制交互发出命令才能打开它。因此,主机的USB驱动程序在得到集中器的报告后,会为新插入的设备安排几次控制交互,并向集中器发出打开端口的命令,这样新插入的设备就会出现在主机的USB总线上。设备并为该设备分配唯一的地址。
HUB驱动调用函数(*dev)和(*dev)来解析设备的各种描述符信息,分配资源,并与相应的设备驱动建立联系。
该函数主要完成以下任务:
1. 调用将新分配的设备地址传送给设备。
2、调用获取设备的设备描述符,获取设备端点数据包的最大长度。后续的控制传输根据数据包的最大长度进行。
3、调用n获取设备的所有配置描述符、接口描述符和端点描述符信息。
4. 调用n 将当前配置激活为默认工作配置。
5. 在目录“proc/bus/usb”中为设备创建节点。
6、在USB子系统中,通过函数和河流,为设备的各个接口找到对应的驱动程序。驱动程序配置接口并向它们分配所需的资源。当各个接口驱动成功后,设备即可正常工作。
当设备被拔出时,与其关联的集线器首先检测到设备的拔出信号,并通过中断传输将信息发送给集线器驱动程序。集线器驱动首先验证设备是否已拔出,如果是,则调用( **pdev )进行处理。设备断开连接后,USB系统找到设备当前配置的各个接口的驱动程序,调用它们提供的接口函数,中断它们与各个接口的数据传输操作,并释放它们为每个接口分配的资源。如果此设备是集线器,则递归调用处理其子设备,释放设备地址,通过ce函数释放为设备创建的文件节点,并释放USBD分配给设备的资源。
4、USB驱动编写步骤
1.所有usb驱动必须创建主结构体
-> *所有者
(如果正确计算了对驱动程序的引用,应该是这样)
->常量字符*名称
(驱动名称,运行时可在/sys/bus/usb//查看)
->常量*
(包括该驱动可以支持的所有不同类型的驱动设备,检测回调函数不会被调用)
->int (*probe)(*intf,const *id)
(USB驱动检测函数确认后应正确初始化,然后返回0,如果发生错误则返回负值)
->void(*)(*intf)
(当USB核心从系统中删除或从USB核心卸载驱动程序时,USB核心将调用此函数)
代码示例:
={
.所有者 = ,
.name="",
。 = ,
.探针 = ,
。 = ,
};
2.()注册将注册到USB核心。传统上,这项工作是在USB驱动模块初始化代码中完成的。
(空白)
...
(&);
...
3. USB 核心使用此表来确定哪个设备应使用哪个驱动程序。热插拔脚本使用它来确定当特定设备插入系统时应自动加载哪个驱动程序。
->__u16(确定设备和结构中以下哪些字段匹配)
->__u16(设备的 USB 制造商 ID)
->__u16(设备的USB产品ID)
4、USB骨架程序要点如下:
a -- USB驱动注册和注销
USB驱动程序在注册时会发送一条命令,通常是在驱动程序的初始化函数中。
当您想要从系统中卸载驱动程序时,您需要注销USB子系统。即需要进行函数处理。
b -- 当usb设备插入时,为了让linux-(Linux下支持PCI、USB等设备的热插拔)系统自动加载驱动程序,需要创建一个
代码如下(该模块仅支持特定设备):
[] = {
{ (, ) },
{ }/* 入口 */};
(USB, );
该宏使用制造商 ID 和产品 ID 为我们提供设备的唯一标识符。当系统将具有匹配ID的USB设备插入USB总线时,驱动程序将被注册到USB核心中。驱动程序中的探测函数也会被调用。结构体指针、接口号和接口ID都传递给函数。
c -- void * (*dev, int ifnum, const *id)
驱动程序需要确认插入的设备是否可以被接受。如果不被接受,或者在初始化过程中发生任何错误,探测函数将返回 NULL 值。否则,返回包含设备驱动程序状态的指针。通过这个指针,可以访问所有结构体中的回调函数。
d -- 在骨架驱动中,最后一点是我们需要注册devfs。
我们创建一个缓冲区来保存发送到 USB 设备和从 USB 设备接收的数据,USB urb 被初始化,然后我们向 devfs 子系统注册该设备,允许 devfs 用户访问我们的设备。注册流程如下:
/* 这个和它的 devfs 节点 */
(名称,"skel%d", skel->;次要);
skel->devfs = (, 名称,, , + skel->次要,
| | | | | , &,无效的);
如果功能失败,不用担心,devfs 子系统会将这种情况报告给用户。
当然最后,如果设备从USB总线上拔掉,设备指针就会调用该函数。驱动程序需要清除已分配的所有私有数据、关闭 urbs 并从 devfs 中注销自身。
/* 我们的 devfs 节点 */(skel->;devfs);
5、其他
a --(描述 USB 端点)
→(包括)tor(包含真实端点信息、数据格式、真实驱动程序关心的字段)
端点描述符:
= 81(in)(第8位为1,为输入设备)(USB端点地址,包括端点方向)
= 03()(端点类型,中断传输)
= 0008(一次传输8个字节)(端点一次可以处理的最大字节长度)
= 08(8ms)(如果端点是中断,则该值为轮询间隔)
b -- USB 端点捆绑为接口。 USB接口只处理一种USB逻辑连接,如鼠标、键盘等。
一个USB设备可以有多个接口,USB扬声器:用于按键的USB键盘,以及USB音频流,这需要两个不同的驱动程序。
USB驱动程序通常将函数转换为
c——描述usb接口
→ *(界面结构数组,包含可能用于该界面的所有可选设置)
→
→(可选设置数量)
→ *(接口当前活动设置)
→(USB核心分配给接口的次设备号,调用成功时有效)
d --USB设备非常复杂,由许多不同的逻辑单元组成。简单关系如下:
设备通常有不止一种配置
配置通常具有多个接口
界面通常有不止一种设置
接口通常有多个端点
设备描述-》配置描述-》接口描述-》端点描述
e -- USB sysfs 设备命名方案
根集线器 - 集线器端口号:配置。界面
对于usb hub树中的更高级别的词树命名方案
根集线器 - 集线器端口号 - 集线器端口号:配置。界面
f - Linux 内核代码通过 urb(USB 请求块)与所有 USB 设备进行通信。
使用urb来描述(在/linux/usb.h中定义)
->URB使用异步USB设备特定的USB端点来发送/接收数据,使用类似于网络代码的东西
-> urbs 是动态创建的,可以随时由驱动程序或 USB 核心取消。它们有一个内部引用计数,可以被多次调用,这样当最后一个用户释放它们时它们可以自动销毁。
-> urb 使流处理或其他复杂的重叠通信成为可能,从而实现高数据传输速度。
->() 创建 urb 包() 释放 urb 包
->() 正确初始化将发送到 USB 设备的中断端点 urb
() .. .. .. ... 批量传输端点 urb
() .. .. .. ... 控制端点 urb
等时urbs提交到核心时必须手动初始化(可惜没有功能)
->()urb被usb驱动正确创建并初始化后,就可以提交给usb core并发送给usb设备了。如果调用成功,函数返回0,urb控制权转移到usb核心。
->()或()取消已经提交到核心的urb
5、USB驱动开发简单示例
1.USB摄像头驱动在嵌入式Linux系统中的实现
通常USB设备类驱动需要提供两种数据结构接口,一种用于USBD层,一种用于文件系统。 USB 摄像头驱动程序需要做的第一件事是在 USB 子系统中注册并提供一些相关信息,包括驱动程序支持哪些设备以及当支持的设备插入总线或从总线上拔出时会发生什么操作。等等,所有这些信息都以以下形式传输到USBD:
= {
。姓名:””,
。探测: ,
.: ,
.: ,
};
在
是客户端驱动的字符串名称,用于避免驱动的重复安装和卸载;
指向USB驱动程序的检测函数指针,该指针是提供给USB内核的函数,用于判断驱动程序是否可以驱动设备的某个接口;
指向USB驱动程序中断开连接函数的指针,当USB核心从系统中删除或从USB核心卸载驱动程序时,USB核心将调用该函数;
该列表包含驱动程序可以支持的所有不同类型的 USB 设备的列表。如果未设置该列表,则不会调用驱动程序中的检测回调函数。
当相机连接到USB总线时,USB内核通过调用.c中的函数来判断该设备是否支持。如果支持,则会为该设备创建一个设备文件节点。未来应用程序可以使用标准的POSIX功能将设备当作普通文件来访问。相机驱动定义的文件系统接口如下:
= {
.所有者 = ,
.打开=,
。 = ,
.ioctl = ,
。 = ,
.读=,
.mmap = ,
.民意调查 = ,
};
在USB摄像头驱动的初始化函数中,通过以下方式注册设备:当从系统中卸载驱动程序时,需要通过卸载。驱动程序注册到USB子系统后,插入新的USB设备后,总是必须调用一个函数来搜索设备驱动程序,以确定新的USB设备硬件中的制造商ID和产品自定义ID是否与驱动程序一致。匹配来判断是否使用驱动程序。
2.USB摄像头驱动测试
在嵌入式Linux系统中,USB摄像头注册为标准视频设备/dev/video,通过成像设备API接口获取视频和音频数据。
现有两个版本:v4l 和 v4l2。通过v4l2 API接口获取视频图像的主要步骤如下:
a -- 打开视频设备
在Linux系统中,摄像头的设备文件为/dev/,调用系统函数open来打开设备。
fd = 打开 (, );
b -- 获取视频设备支持的V4L2特性
所有V4L2设备驱动程序都需要支持l系统调用。通过这个调用,判断驱动程序是否兼容V4L2规范,并获取设备支持的V4L2特性。在开发相机应用程序时,您需要确定设备是否支持视频捕获。
ret = ioctl(fd, , &cap);
c -- 获取视频设备支持的各种功能
接下来,使用ioctl(fd,,&cap)函数读取相机的信息。函数成功返回后,这些信息就会从内核空间复制到用户程序空间的各个成员组件中。
ioctl(,,&);
d -- 设置视频捕获的图像格式
(&fmt, 0, ( ));
fmt.类型 = ;
fmt.fmt.pix.width = vd->宽度;
fmt.fmt.pix。 = vd->;
fmt.fmt.pix。 = vd->;
ret = ioctl(fd, , &fmt);
e——视频数据帧捕获
ioctl (fd, , &buf);
视频数据获取后放入buf缓冲区中,通过QT桌面应用开发系统显示在LCD显示屏上,并通过触摸屏进行交互控制。
扫一扫在手机端查看
-
Tags : linux usb驱动详解
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。