题目: 基于Linu*的网络摄像头实现
院(系) 信息科学与工程学院
专 业 电子科学与技术
摘要
本项目通过编写Linu*下的USB摄像头驱动,在Linu*下编写QT程序完成本项目目标。Linu*是开源的可移植的操作系统内核,QT是一个跨平台的C++图形用户界面应用程序框架,它提供给应用程序开发者建立艺术级的图形用户界面所需的所有功能。QT能将在其上开发的应用程序移植到PC和嵌入式设备上,进而可以实现嵌入式Linu*上的图像采集。
本文围绕USB, Linu*下USB驱动程序开发,和V4L2框架编程3个方面进行研究。详细叙述了USB驱动原理,Linu*下的USB的驱动程序数据结构和一般驱动程序的开发过程,摄像头驱动的开发,和V4L2框架下的应用程序开发。
关键字:Linu*,USB,USB驱动,V4L2,摄像头驱动,图像采集
ABSTRACT
In this item,we chieve the design goal of graduation through writing the USB camera driver of Linu* and the QT software on Linu*.Linu* is an portable open source operation system kernel.QT is a cross platform framwork of C++ GUI application program,providing all API to build artistic GUI for programmer.The program developed on QT can run on PC and embedded device,so we can achieve the goal of video capture on embedded device using this method.
This page focus on USB,USB device driver on Linu* and program on V4L2 framwork three aspects.It discuss on the principle of USB device driver,the data structs of USB device driver on Linu*,the develop process of general Linu* device driver,the camera device driver and application program on V4L2 framwork.
Keywords:Linu*,USB,USB device driver,V4L2,camera device driver,
video capture
目录
摘要 2
ABSTRACT 3
第一章 USB驱动原理 5
1.1 USB基础知识 5
1.1.2 USB 的优势 6
1.1.3 USB 数据传输 6
1.1.4 USB协议 7
1.2 USB驱动结构 8
1.3 USB 主机控制器驱动结构 9
1.3.1 主机控制器驱动 9
1.4 USB 主机逻辑组织 13
1.4 USB 驱动程序重要数据结构 16
1.5 URB 18
1.5.1 URB 处理流程 19
1.5.2 简单的批量和控制 URB 21
1.6 USB 设备的识别过程 24
1.7 USB 设备类驱动 25
第二章USB摄像头驱动 27
2.1 摄像头工作原理 27
2.2通用摄像头驱动开发 28
2.2.1 主要数据结构 28
2.2.2 USBD层数据结构 29
2.2.3 文件系统数据结构 29
2.3 共有功能模块 30
2.3.1 初始化和卸载函数 30
2.3.2 模块初始化 30
2.3.3 模块卸载 31
2.3.4 即插即用功能函数 31
2.3.5 上层软件接口函数 34
第三章 图像采集应用程序 39
3.1 Video4Linu*2简介 39
3.2 程序关键步骤介绍 40
3.3 UDP传输图像 43
参考文献 44
结论 45
英文翻译 46
附录 60
第一章 USB驱动原理
USB 是英文”Universal Serial Bus”的缩写,意为”通用串行总线”。是由 compaq(康柏)、DEC、Intel、IBM、NEC、微软以 及Northern Telecom (北方电讯)等公司于1994年11月共同提出的,主要目的就是为了解决接口标准太多的弊端。USB使用一个4针插头作为标准插头,并通过这个标准接头,采用菊花瓣形式把所有外设连接起来,它采用串行方式传输数据,目前最大数据传输率为12Mbps,支持多数据流和多个设备并行操作,允许外设热插拔。
USB 摄像头不但具有体积小、重量轻、功耗小、工作电压低和抗烧毁等优点,而且在分辨率、动态范围、灵敏度、实时传输和自扫描等方面的优越性,也是其它摄像器件无法比拟的。目前,USB 摄像头已在航空航天、卫星侦察、遥感遥测、天文测量、传真、静电复印、非接触工业测量、光学图像处理、图文识别、数据存储等领域得到了广泛的应用。
1.1 USB基础知识
目前 USB 接口虽然只发展了 2 代( USB1.0/1.1,USB2.0 ),但是USB综合了一个多平台标准的所有优点:降低成本,增加兼容性,可连接大量的外部设备,融合先进的功能和品质。这些优点使得 USB 逐步成为 PC 的接口标准,进入了高速发展的阶段。
在早期的计算机系统上常用串口或并口连接外围设备,每个接口都需要占用计算机的系统资源(如中断,I/O 地址,DMA 通道等)。无论是串口还是并口都是点对点的连接,一个接口仅支持一个设备。因此每添加一个新的设备,就需要添加一个 ISA/EISA 或 PCI 卡,同时系统需要重新启动才能驱动新的设备。而采用 USB 接口则可以避免这类的问题,不但节省系统的资源而且使用起来非常方便。
USB 第一版的规范定为 1.1,其最大传输速率为12Mbps,最多可支持127个 USB外设连接到计算机系统,它的物理结构为星型,也就是说,即使主板上只有一个USB 接口,只要能找到合适的 USB Hub ,就
……(新文秘网https://www.wm114.cn省略4360字,正式会员可完整阅读)……
efine HC_STATE_RUNNING (_ _ACTIVE)
#define HC_STATE_QUIESCING (_ _SUSPEND|_ _TRANSIENT|_ _ACTIVE)
#define HC_STATE_RESUMING (_ _SUSPEND|_ _TRANSIENT)
#define HC_STATE_SUSPENDED (_ _SUSPEND)
#define HC_IS_RUNNING(state) ((state) & _ _ACTIVE)
#define HC_IS_SUSPENDED(state) ((state) & _ _SUSPEND)
/* 主机控制器驱动的私有数据 */
unsigned long hcd_priv[0]_ _attribute_ _((aligned(sizeof(unsigned long))));
};
hc_driver 结构体
struct hc_driver
{
const char *description; /* "ehci-hcd" 等 */
const char *product_desc; /* 产品/厂商字符串 */
size_t hcd_priv_size; /* 私有数据的大小 */
/* 中断处理函数 */
irqreturn_t(*irq)(struct usb_hcd *hcd, struct pt_regs *regs);
int flags;
#define HCD_MEMORY 0*0001 /* HC 寄存器使用的内存和 I/O */
#define HCD_USB11 0*0010 /* USB 1.1 */
#define HCD_USB2 0*0020 /* USB 2.0 */
/* 被调用以初始化 HCD 和根 Hub */
int(*reset)(struct usb_hcd *hcd);
int(*start)(struct usb_hcd *hcd);
/* 挂起 Hub 后,进入 D3(etc)前被调用 */
int(*suspend)(struct usb_hcd *hcd, pm_message_t message);
/* 在进入 D0(etc)后,恢复 Hub 前调用 */
int(*resume)(struct usb_hcd *hcd);
/* 使 HCD 停止写内存和进行 I/O 操作*/
void(*stop)(struct usb_hcd *hcd);
/* 返回目前的帧数 */
int(*get_frame_number)(struct usb_hcd *hcd);
/* 管理 I/O 请求和设备状态 */
int(*urb_enqueue)(struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct urb *urb, gfp_t mem_flags);
int(*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb);
/* 释放 endpoint 资源 */
void(*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
/* 根 Hub 支持 */
int(*hub_status_data)(struct usb_hcd *hcd, char *buf);
int(*hu不忘初心ontrol)(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wInde*,
char *buf, u16 wLength);
int(*bus_suspend)(struct usb_hcd*);
int(*bus_resume)(struct usb_hcd*);
int(*start_port_reset)(struct usb_hcd *, unsigned port_num);
void(*hub_irq_enable)(struct usb_hcd*);
};
ohci_hcd 结构体
struct ohci_hcd
{
spinlock_t lock;
/* 与主机控制器通信的 I/O 内存( DMA 一致) */
struct ohci_regs _ _iomem *regs;
/* 与主机控制器通信的主存( DMA 一致) */
struct ohci_hcca *hcca;
dma_addr_t hcca_dma;
struct ed *ed_rm_list; /* 将被移除 */
struct ed *ed_bulktail; /* 批量队列尾 */
struct ed *ed_controltail; /* 控制队列尾 */
struct ed *periodic[NUM_INTS]; /* int_table“影子” */
/* OTG 控制器和收发器需要软件交互,其他的外部收发器应该是软件透明的*/
struct otg_transceiver *transceiver;
/* 队列数据的内存管理 */
struct dma_pool *td_cache;
struct dma_pool *ed_cache;
struct td *td_hash[TD_HASH_SIZE];
struct list_head pending;
/* driver 状态 */
int num_ports;
int load[NUM_INTS];
u32 hc_control; /* 主机控制器控制寄存器的复制 */
unsigned long ne*t_statechange; /* 挂起/恢复 */
u32 fminterval; /* 被保存的寄存器 */
struct notifier_block reboot_notifier;
unsigned long flags;
};
在 Linu* 内核中,使用如下函数来创建 HCD:
struct usb_hcd *us不忘初心reate_hcd (const struct hc _driver *driver,struct device *dev, char *bus_name);
如下函数被用来增加和移除 HCD:
int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags);
void usb_remove_hcd(struct usb_hcd *hcd);
使用如下函数可初始化 OHCI 主机控制器:
int ohci_init (struct ohci_hcd *ohci);
如下函数分别用于开启、停止及复位 OHCI 控制器:
int ohci_run (struct ohci_hcd *ohci);
void ohci_stop (struct usb_hcd *hcd);
void ohci_usb_reset (struct ohci_hcd *ohci);
1.4 USB 主机逻辑组织
在USB三层设备驱动中的最顶层驱动,即USB设备的逻辑组织,包含了设备、配置、接口3个层次。
每个 USB 设备都提供了不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一个),配置由多个接口组成。
在 USB 协议中,接口由多个端点组成,代表一个基本的功能,是 USB 设备驱动程序控制的对象,一个功能复杂的 USB 设备可以具有多个接口。每个配置中可以有多个接口,而设备接口是端点的汇集(collection)。例如 USB 扬声器可以包含一个音频接口以及对旋钮和按钮的接口。一个配置中的所有接口可以同时有效,并可被不同的驱动程序连接。每个接口可以有备用接口,以提供不同质量的服务参数。
端点是 USB 通信的最基本形式,每一个 USB 设备接口在主机看来就是一个端点的集合。主机只能通过端点与设备进行通信,以使用设备的功能。 USB 系统中每一个端点都有惟一的地址,在这是由设备地址和端点号给出的。每个端点都有一定的属性,其中包括传输方式、总线访问频率、带宽、端点号和数据包的最大容量等。一个 USB 端点只能在一个方向承载数据,或者从主机到设备(称为输出端点),或者从设备到主机(称为输入端点),因此端点可看作一个单向的管道。端点 0 通常为控制端点,用于设备初始化参数等。只要设备连接到 USB 上并且上电端点 0 就可以被访问。端点 1、2 等一般用作数据端点,存放主机与设备间往来的数据。
USB 设备非常复杂,由许多不同的逻辑单元组成,这些单元之间的关系如下:
设备通常有一个或多个配置;
配置通常有一个或多个接口;
接口通常有一个或多个设置;
接口有零或多个端点。
这种层次化配置信息在设备中通过一组标准的描述符来描述,如下所示。
设备描述符:关于设备的通用信息,如供应商 ID、产品 ID 和修订 ID,支持的设备类、子类和适用的协议以及默认端点的最大包大小等。在 Linu* 内核中, USB 设备用 usb_device 结构体来描述, USB 设备描述符定义为 usb_device_descriptor 结构体
struct usb_device_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型编号
_ _le16 bcdUSB; //USB 版本号
_ _u8 bDeviceClass; //USB 分配的设备类 code
_ _u8 bDeviceSubClass;// USB 分配的子类 code
_ _u8 bDeviceProtocol; //USB 分配的协议 code
_ _u8 bMa*PacketSize0; //endpoint0 最大包大小
_ _le16 idVendor; //厂商编号
_ _le16 idProduct; //产品编号
_ _le16 bcdDevice; //设备出厂编号
_ _u8 iManufacturer; //描述厂商字符串的索引
_ _u8 iProduct; //描述产品字符串的索引
_ _u8 iSerialNumber; //描述设备序列号字符串的索引
_ _u8 bNumConfigurations; //可能的配置数量
} _ _attribute_ _ ((packed));
配置描述符:此配置中的接口数、支持的挂起和恢复能力以及功率要求。USB 配置在内核中使用 usb_host_config 结构体描述,USB 配置描述符定义为结构体 us不忘初心onfig_descriptor,
struct us不忘初心onfig_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型编号
_ _le16 wTotalLength; //配置所返回的所有数据的大小
_ _u8 bNumInterfaces; // 配置所支持的接口数
_ _u8 bConfigurationValue; //Set_Configuration 命令需要的参数值
_ _u8 iConfiguration; //描述该配置的字符串的索引值
_ _u8 bmAttributes; //
供电模式的选择
_ _u8 bMa*Power; //设备从总线提取的最大电流
} _ _attribute_ _ ((packed));
接口描述符:接口类、子类和适用的协议,接口备用配置的数目和端点数目。USB 接口在内核中使用 usb_interface 结构体描述, USB 接口描述符定义为结构体usb_interface_descriptor
struct usb_interface_descriptor
{
_ _u8 bLength;
//描述符长度
_ _u8 bDescriptorType; //描述符类型
_ _u8 bInterfaceNumber;
// 接口的编号
_ _u8 bAlternateSetting; //备用的接口描述符编号
_ _u8 bNumEndpoints;
//该接口使用的端点数,不包括端点 0
_ _u8 bInterfaceClass;
//接口类型
_ _u8 bInterfaceSubClass; //接口子类型
_ _u8 bInterfaceProtocol; //接口所遵循的协议
_ _u8 iInterface; //描述该接口的字符串索引值
} _ _attribute_ _ ((packed));
端点描述符:端点地址、方向和类型,支持的最大包大小,如果是中断类型的端点则还包括轮询频率。在 Linu* 内核中,USB 端点使用 usb_host_endpoint 结构体来描述, USB 端点描述符定义为 usb_endpoint_descriptor 结构体
struct usb_endpoint_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型
_ _u8 bEndpointAddress; //端点地址:0~3 位是端点号,第 7 位是方向(0-OUT,1-IN)
_ _u8 bmAttributes; //端点属性:bit[0:1] 的值为00 表示控制,为01 表示同步,为02 表示批量,为03 表示中断
_ _le16 wMa*PacketSize; //// 本端点接收或发送的最大信息包的大小
_ _u8 bInterval; //轮询数据传送端点的时间间隔
//对于批量传送的端点以及控制传送的端点,此域忽略
//对于同步传送的端点,此域必须为 1
//对于中断传送的端点,此域值的范围为 1~255
_ _u8 bRefresh;
_ _u8 bSynchAddress;
} _ _attribute_ _ ((packed));
字符串描述符:在其他描述符中会为某些字段提供字符串索引,它们可被用来检索描述性字符串,可以以多种语言形式提供。字符串描述符是可选的,有的设备有,有的设备没有,字符串描述符对应于 usb_string_descriptor 结构体
struct usb_string_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型
_ _le16 wData[1];
/* 以 UTF-16LE 编码 */
} _ _attribute_ _ ((packed));
1.4 USB 驱动程序重要数据结构
为了符合 USB 规范的数据传输格式,在Linu*中定义了一组用来实现控制传输,中断传输,批量传输,等时传输的数据结构:[ehci|ohci|uhci]_[itd|sitd|qh|fstn],是三个主机驱动器的核心,润健要做的事就是把上层设备驱动传下来的数据转换成一个个的符合这些数据结构要求的数据并在内存中放好,利用这些数据结构中定义的指针把数据链成链表,直接通过硬件传出去。
Split Transaction Isochronous Descriptor(stid)
Isochronous (High-Speed)Transfer Descriptor(itd)
Queue Element Transfer Descriptor(qtd)
Queue Head(qh)
在主机控制器驱动上面是 USB 核心,这一块是USB中最复杂的一块,所幸的是与硬件无关,我们只需要知道它提供了什么接口以及如何使用这些接口,不需要知道 USB 核心是如何工作的,只要使用这些 USB 核心层的 API ,把 USB 当作通路来使用。
1.5 URB
只有站在 USB 核心层上,才能清晰看到上面提到的端点、接口、管道和四种传输类型(控制传输、中断传输、批量传输、等时传输)等这些逻辑结构。
在 Linu* 的架构中,有一种数据结构叫 URB(USB Request Block) 封装了这些概念,作为 USB 设备驱动,要使用 USB 通路来传输数据,就只要操控 URB 就可以了。
struct urb
{
/* 私有的:只能由 USB 核心和主机控制器访问的字段 */
struct kref kref; /*URB 引用计数 */
spinlock_t lock; /* URB 锁 */
void *hcpriv; /* 主机控制器私有数据 */
int bandwidth; /* INT/ISO 请求的带宽 */
atomic_t use_count; /* 并发传输计数 */
u8 reject; /* 传输将失败*/
/* 公共的: 可以被驱动使用的字段 */
struct list_head urb_list; /* 链表头*/
struct usb_device *dev; /* 关联的 USB 设备 */
unsigned int pipe; /* 管道信息 */
int status; /* URB 的当前状态 */
unsigned int transfer_flags; /* URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区 */
dma_addr_t transfer_dma; /*用来以 DMA 方式向设备传输数据的缓冲区 */
int transfer_buffer_length;/*transfer_buffer 或 transfer_dma 指向缓冲区的大小*/
int actual_length; /* URB 结束后,发送或接收数据的实际长度 */
unsigned char *setup_packet; /* 指向控制 URB 的设置数据包的指针*/
dma_addr_t setup_dma; /*控制 URB 的设置数据包的 DMA 缓冲区*/
int start_frame; /*等时传输中用于设置或返回初始帧*/
int number_of_packets; /*等时传输中等时缓冲区数据 */
int interval; /* URB 被轮询到的时间间隔(对中断和等时 URB 有效) */
int error_count; /* 等时传输错误数量 */
void *conte*t; /* completion 函数上下文 */
us不忘初心omplete_t complete; /* 当 URB 被完全传输或发生错误时,被调用 */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/*单个 URB 一次可定义多个等时传输时,描述各个等时传输 */
};
1.5.1 URB 处理流程
USB 设备中的每个端点都处理一个 URB 队列,在队列被清空之前,一个 URB的典型生命周期如下:
(1)被一个 USB 设备驱动创建。
创建 URB 结构体的函数为:
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
urb 结构体在驱动中不能静态创建,因为这可能破坏 USB 核心给 URB 使用的引用计数方法。usb_alloc_urb() 的“反函数”为:
void usb_free_urb(struct urb *urb);
该函数用于释放由 usb_alloc_urb() 分配的 urb 结构体。
(2)初始化,被安排给一个特定 USB 设备的特定端点。
对于中断 urb ,使用 usb_fill_int_urb() 函数来初始化 urb,如下所示:
void usb_fill_[control|int|bulk]_urb(
struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
unsigned char *setup_packet,
void *transfer_buffer,
int buffer_length,
us不忘初心omplete_t completion_fn,
void *conte*t,
void *conte*t, int interval);
这些初始化函数使用于控制、中断和批量数据传输模式,在等时传输下不能使用此类函数。
(3)被 USB 设备驱动提交给 USB 核心
在逻辑层上把 USB 看着一个个的 pipe ,其实主机不是与从设备的硬件直接打交道,而是与从设备中的 USB 固件( USB 从控制器的驱动)打交道。
设备驱动想要 USB 总线和设备通信,先初始化 URB 结构,把想要传送的数据用系统提供 URB 操作工具填入 URB 中,然后用 usb_submit_urb 向 USB 核心提交。
int usb_submit_urb(struct urb *urb, int mem_flags);
在提交 URB 到 USB 核心后,直到完成函数被调用之前,不要能访问URB中的任何成员。
usb_submit_urb中全调用usb_hcd_submit_urb,usb_hcd_submit_urb会找到预先制定的控制器驱动,即调用 hcd->driver->urb_equeue() ,如对ehci控制器来说,urb_enqueue 就是 ehci_urb_enqueue() ,数据走到 ehci_urb_enqueue ……(未完,全文共68081字,当前仅显示12245字,请阅读下面提示信息。
收藏《毕业论文:基于Linu*的网络摄像头实现》)