分享

嵌入式Linux系统中USB设备驱动程序开发的基本原理

 WUCANADA 2013-07-28

嵌入式Linux系统中USB设备驱动程序开发的基本原理

(2012-07-08 15:46:49)
标签:

杂谈

分类: linux_develop

设 备驱动程序是操作系统内核和机器硬件之间的接口,由一组函数和一些私有数据组成,是应用程序和硬件设备之间的桥梁。在应用程序看来,硬件设备只是一个设备 文件,应用程序可以像操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,主要完成以下功能:对设备的初始化和释放;把数据从内核传送到硬 件设备和从硬件设备读取数据;读取应用程序数据传送给设备文件和回送应用程序请求的数据;检测和处理硬件设备出现的错误。

 Linux USB子系统分析

Linux系统中,USB主机驱动程序由3部分组成:USB主机控制器驱动(HCD)、USB核心驱动(USBD)和不同种类的USB设备类驱动,如图1所示。其中HCDUSBD被称为协议软件或者协议栈,这两部分共同处理与协议相关的操作。USB设备类驱动可以包含多个,不同的功能接口对应不同的驱动程序,它们不直接与USB设备硬件打交道,而是通过协议软件的抽象处理来完成与设备的不同功能接口之间的通信。

嵌入式Linux系统中USB设备驱动程序开发的基本原理

Linux USB子系统中,HCD是直接和硬件进行交互的软件模块,是USB协议栈的最底层部分,是USB主机控制器硬件和数据传输的一种抽象。HCD向上仅对USB总线驱动程序服务,HCD提供了一个软件接口,即HCDI,使得各种USB主机控制器的硬件特性都被软件化,并受USB总线驱动程序的调用和管理。HCD向下则直接管理和检测主控制器硬件的各种行为。HCD提供的功能主要有:主机控制器硬件初始化;为USBD层提供相应的接口函数;提供根HUBROOT HUB)设备配置、控制功能;完成4种类型的数据传输等。

USBD部分是整个USB主机驱动的核心,主要实现的功能有:USB总线管理;USB总线设备管理、USB总线带宽管理、USB4种类型数据传输、USB HUB驱动、为USB设备驱动提供相关接口、提供应用程序访问USB系统的文件接口等。其中USB HUB作为一类特殊的USB设备,其驱动程序被包含在USBD层。

在嵌入式Linux系统中,已经包含USB设备类驱动和USB核心驱动USBD,不需要用户重新编写,用户仅仅需要完成HCD模块即可。

 Linux系统中USB子系统的主要数据结构

Linux系统中,USBD通过定义一组宏、数据结构和函数来抽象出所有硬件或是设备具有依赖关系的部分。USBD中主要有四个数据结构,分别是:

1.usb_device保存一个USB设备的信息,包括设备地址,设备描述符,配置描述符等。

2.usb_bus保存一个USB总线系统的信息,包括总线上设备地址信息,根集线器,带宽使用情况等。一个USB总线系统至少有一个主机控制器和一个根集线器,Linux系统支持多USB总线系统。

3.usb_driver保存客户驱动信息,包括驱动名称,以及驱动提供给USB内核使用的函数指针等。

4.URBUniversal Request Block是进行USB通信的数据结构,USBD通过URBUSB设备类驱动和USBDUSBDHCD间进行数据传输。

 Linux系统中USB设备的加载与卸载

当把一个USB设备插入到一个USB HUB的某个端口时,集中器就会检测到设备的接入,从而在下一次受到主机通过中断交互查询时就会向其报告。集中器的端口在没有设备接入时都处于关闭状态,插入设备之后也不会自动打开,必须由主机通过控制交互发出命令予以打开。所以,在得到集中器的报告之后,主机的USB驱动程序就会为新插入的设备调度若干个控制交互,并向集中器发出打开这个端口的命令,这样新插入的设备就会出现在USB总线上了,并为该设备分配唯一的地址。HUB驱动程序调用函数usb_connect(struct usb_device *dev)usb_new_device(struct usb_device *dev)解析设备的各种描述符信息,分配资源,并与相应的设备驱动程序建立联系。

函数usb_new_device主要完成以下工作:

1.调用usb_set_address把新分配的设备地址传送给设备。

2.调用usb_get_descriptor获得设备的设备描述符,得到设备端点的包的最大长度,接下来的控制传输按这个数据包最大长度进行。

3.调用usb_get_configuration得到设备的所有配置描述符、接口描述符和端点描述符信息。

4.调用usb_set_configuration激活当前的配置作为默认工作配置。

5.在目录“proc/bus/usb”中为设备创建节点。

6.USB子系统中,通过函数usb_find_driversusb_find_interface_driver,为设备的每一个接口寻找相应的驱动程序,驱动程序对接口进行配置并为它们分配所需的资源。当每个接口被成功驱动后,此设备就能正常工作了。

设备拔下时,与之相联的集线器首先检测到设备的拔下信号,通过中断传输将信息传送给集线器的驱动,集线器的驱动先验证设备是否被拔下,如果是则调用usb_disconnect(struct usb_device **pdev)进行处理。设备断开后,USB系统找到设备当前活动配置的每个接口的驱动程序,调用它们提供的disconnect接口函数,中断它们与各个接口的数据传输操作,释放它们为每个接口分配的资源。如果此设备是集线器,则递归调用usb_disconnect来处理它的子设备,释放设备地址,通过usbdevfs_remove_device函数释放给设备创建的文件节点,通过usb_free_dev释放USBD给设备分配的资源。

嵌入式Linux系统中USB摄像头驱动程序实现

通常USB设备类驱动程序需要提供两个数据结构接口,一个针对USBD层,一个针对文件系统。USB摄像头驱动程序需要做的第一件事情就是在USB子系统里注册,并提供一些相关信息,包括该驱动程序支持哪些设备,当被支持的设备从总线插入或拔出时,会有哪些动作等,所有这些信息通过usb_driver的形式传送到USBD中,具体实现如下:

static struct usb_driver cam_driver = {

.name: "cam_video",

.probe: cam_probe,

.disconnect: cam_disconnect,

.id_table: cam_ids,

};

其中,cam_video客户端驱动程序的字符串名称,用于避免驱动程序的重复安装和卸载;cam_probe指向USB驱动程序的探测函数指针,提供给USB内核的函数,用于判断驱动程序是否能对设备的某个接口进行驱动;cam_disconnect指向USB驱动程序中的断开函数的指针,当从系统中被移除或者驱动程序正在从USB核心中卸载时,USB核心将调用该函数;cam_ids列表包含了一系列该驱动程序可以支持的所有不同类型的USB设备,如没有设置该列表,则该驱动程序中的探测回调函数不会被调用。

当一个摄像头连接到USB总线上时,USB内核通过调用camDrive.c中的cam_probe函数判断是否支持该设备,如果支持,为该设备创建设备文件节点,以后应用程序就可以通过标准POSIX函数,把该设备当成普通文件来访问。摄像头驱动程序定义的文件系统接口如下:

struct file_operations cam_fops = {

.owner     = THIS_MODULE,

.open      = cam_v 4l2_open,

.release   = cam_v4l2_release,

.ioctl     = cam_v4l2_ioctl,

.llseek    = no_llseek,

.read      = cam_v4l2_read,

.mmap      = cam_v4l2_mmap,

.poll      = cam_v4l2_poll,

};

USB摄像头驱动程序的初始化函数中,通过usb_register进行设备注册;当从系统卸载驱动程序时,需要通过usb_deregister进行卸载。当驱动程序向USB子系统注册后,插入一个新的USB设备后总是要调用cam_probe函数进行设备驱动程序的查找,以确定新的USB设备硬件中的生产厂商ID和产品自定义ID是否与驱动程序相符,从而确定是否使用该驱动程序。

 USB摄像头驱动程序测试

在嵌入式Linux系统中,USB摄像头被注册为一个标准的视频设备/dev/video,通过影像设备API接口Video4Linux来获取视频和音频数据。现有的Video4Linux有两个版本:v4lv4l2。通过v4l2 API接口获取视频图像的主要操作步骤如下:

1.打开视频设备

Linux系统中,摄像头的设备文件为/dev/video0,调用系统函数open打开该设备。

fd = open (dev_name, O_RDWR);

2.获取视频设备所支持的V4L2特性

所有的V4L2设备驱动都需要支持VIDIOC_QUERYCAP_ioctl的系统调用。通过该调用,确定该驱动程序是否与V4L2规范相兼容,同时获取该设备所支持的V4L2特性。在摄像头应用程序的开发过程中,需要判定该设备是否支持视频捕获。

ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);

3.获取视频设备支持的各种特性

接着,利用ioctl(fd,VIDIOC_QUERYCAP,&cap)函数读取struct v4l2_capability中有关摄像头的信息。该函数成功返回后,这些信息从内核空间拷贝到用户程序空间capability各成员分量中。

ioctl(device_fd, VIDIOCGCAP, &vidcap);

4.设置视频捕获的图像格式

memset(&fmt, 0, sizeof(struct v4l2_format));

fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt.fmt.pix.width = vd->width;

fmt.fmt.pix.height = vd->height;

fmt.fmt.pix.pixelformat = vd->formatIn;

ret = ioctl(fd, VIDIOC_S_FMT, &fmt);

5.视频数据帧捕获

ioctl (fd, VIDIOC_DQBUF, &buf);

获取到视频数据之后,放到buf缓冲区中,通过QT桌面应用开发系统,显示到LCD显示屏上,通过触摸屏进行交互控制。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多