題目: 基于Linu*的網絡攝像頭實現(xiàn)
院(系) 信息科學與工程學院
專 業(yè) 電子科學與技術
摘要
本項目通過編寫Linu*下的USB攝像頭驅動,在Linu*下編寫QT程序完成本項目目標。Linu*是開源的可移植的操作系統(tǒng)內核,QT是一個跨平臺的C++圖形用戶界面應用程序框架,它提供給應用程序開發(fā)者建立藝術級的圖形用戶界面所需的所有功能。QT能將在其上開發(fā)的應用程序移植到PC和嵌入式設備上,進而可以實現(xiàn)嵌入式Linu*上的圖像采集。
本文圍繞USB, Linu*下USB驅動程序開發(fā),和V4L2框架編程3個方面進行研究。詳細敘述了USB驅動原理,Linu*下的USB的驅動程序數(shù)據(jù)結構和一般驅動程序的開發(fā)過程,攝像頭驅動的開發(fā),和V4L2框架下的應用程序開發(fā)。
關鍵字: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 的優(yōu)勢 6
1.1.3 USB 數(shù)據(jù)傳輸 6
1.1.4 USB協(xié)議 7
1.2 USB驅動結構 8
1.3 USB 主機控制器驅動結構 9
1.3.1 主機控制器驅動 9
1.4 USB 主機邏輯組織 13
1.4 USB 驅動程序重要數(shù)據(jù)結構 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通用攝像頭驅動開發(fā) 28
2.2.1 主要數(shù)據(jù)結構 28
2.2.2 USBD層數(shù)據(jù)結構 29
2.2.3 文件系統(tǒng)數(shù)據(jù)結構 29
2.3 共有功能模塊 30
2.3.1 初始化和卸載函數(shù) 30
2.3.2 模塊初始化 30
2.3.3 模塊卸載 31
2.3.4 即插即用功能函數(shù) 31
2.3.5 上層軟件接口函數(shù) 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針插頭作為標準插頭,并通過這個標準接頭,采用菊花瓣形式把所有外設連接起來,它采用串行方式傳輸數(shù)據(jù),目前最大數(shù)據(jù)傳輸率為12Mbps,支持多數(shù)據(jù)流和多個設備并行操作,允許外設熱插拔。
USB 攝像頭不但具有體積小、重量輕、功耗小、工作電壓低和抗燒毀等優(yōu)點,而且在分辨率、動態(tài)范圍、靈敏度、實時傳輸和自掃描等方面的優(yōu)越性,也是其它攝像器件無法比擬的。目前,USB 攝像頭已在航空航天、衛(wèi)星偵察、遙感遙測、天文測量、傳真、靜電復印、非接觸工業(yè)測量、光學圖像處理、圖文識別、數(shù)據(jù)存儲等領域得到了廣泛的應用。
1.1 USB基礎知識
目前 USB 接口雖然只發(fā)展了 2 代( USB1.0/1.1,USB2.0 ),但是USB綜合了一個多平臺標準的所有優(yōu)點:降低成本,增加兼容性,可連接大量的外部設備,融合先進的功能和品質。這些優(yōu)點使得 USB 逐步成為 PC 的接口標準,進入了高速發(fā)展的階段。
在早期的計算機系統(tǒng)上常用串口或并口連接外圍設備,每個接口都需要占用計算機的系統(tǒng)資源(如中斷,I/O 地址,DMA 通道等)。無論是串口還是并口都是點對點的連接,一個接口僅支持一個設備。因此每添加一個新的設備,就需要添加一個 ISA/EISA 或 PCI 卡,同時系統(tǒng)需要重新啟動才能驅動新的設備。而采用 USB 接口則可以避免這類的問題,不但節(jié)省系統(tǒng)的資源而且使用起來非常方便。
USB 第一版的規(guī)范定為 1.1,其最大傳輸速率為12Mbps,最多可支持127個 USB外設連接到計算機系統(tǒng),它的物理結構為星型,也就是說,即使主板上只有一個USB 接口,只要能找到合適的 USB Hub ,就
……(新文秘網http://120pk.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)
/* 主機控制器驅動的私有數(shù)據(jù) */
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; /* 私有數(shù)據(jù)的大小 */
/* 中斷處理函數(shù) */
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);
/* 返回目前的幀數(shù) */
int(*get_frame_number)(struct usb_hcd *hcd);
/* 管理 I/O 請求和設備狀態(tài) */
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 控制器和收發(fā)器需要軟件交互,其他的外部收發(fā)器應該是軟件透明的*/
struct otg_transceiver *transceiver;
/* 隊列數(shù)據(jù)的內存管理 */
struct dma_pool *td_cache;
struct dma_pool *ed_cache;
struct td *td_hash[TD_HASH_SIZE];
struct list_head pending;
/* driver 狀態(tài) */
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* 內核中,使用如下函數(shù)來創(chuàng)建 HCD:
struct usb_hcd *us不忘初心reate_hcd (const struct hc _driver *driver,struct device *dev, char *bus_name);
如下函數(shù)被用來增加和移除 HCD:
int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags);
void usb_remove_hcd(struct usb_hcd *hcd);
使用如下函數(shù)可初始化 OHCI 主機控制器:
int ohci_init (struct ohci_hcd *ohci);
如下函數(shù)分別用于開啟、停止及復位 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 設備都提供了不同級別的配置信息,可以包含一個或多個配置,不同的配置使設備表現(xiàn)出不同的功能組合(在探測/連接期間需從其中選定一個),配置由多個接口組成。
在 USB 協(xié)議中,接口由多個端點組成,代表一個基本的功能,是 USB 設備驅動程序控制的對象,一個功能復雜的 USB 設備可以具有多個接口。每個配置中可以有多個接口,而設備接口是端點的匯集(collection)。例如 USB 揚聲器可以包含一個音頻接口以及對旋鈕和按鈕的接口。一個配置中的所有接口可以同時有效,并可被不同的驅動程序連接。每個接口可以有備用接口,以提供不同質量的服務參數(shù)。
端點是 USB 通信的最基本形式,每一個 USB 設備接口在主機看來就是一個端點的集合。主機只能通過端點與設備進行通信,以使用設備的功能。 USB 系統(tǒng)中每一個端點都有惟一的地址,在這是由設備地址和端點號給出的。每個端點都有一定的屬性,其中包括傳輸方式、總線訪問頻率、帶寬、端點號和數(shù)據(jù)包的最大容量等。一個 USB 端點只能在一個方向承載數(shù)據(jù),或者從主機到設備(稱為輸出端點),或者從設備到主機(稱為輸入端點),因此端點可看作一個單向的管道。端點 0 通常為控制端點,用于設備初始化參數(shù)等。只要設備連接到 USB 上并且上電端點 0 就可以被訪問。端點 1、2 等一般用作數(shù)據(jù)端點,存放主機與設備間往來的數(shù)據(jù)。
USB 設備非常復雜,由許多不同的邏輯單元組成,這些單元之間的關系如下:
設備通常有一個或多個配置;
配置通常有一個或多個接口;
接口通常有一個或多個設置;
接口有零或多個端點。
這種層次化配置信息在設備中通過一組標準的描述符來描述,如下所示。
設備描述符:關于設備的通用信息,如供應商 ID、產品 ID 和修訂 ID,支持的設備類、子類和適用的協(xié)議以及默認端點的最大包大小等。在 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 分配的協(xié)議 code
_ _u8 bMa*PacketSize0; //endpoint0 最大包大小
_ _le16 idVendor; //廠商編號
_ _le16 idProduct; //產品編號
_ _le16 bcdDevice; //設備出廠編號
_ _u8 iManufacturer; //描述廠商字符串的索引
_ _u8 iProduct; //描述產品字符串的索引
_ _u8 iSerialNumber; //描述設備序列號字符串的索引
_ _u8 bNumConfigurations; //可能的配置數(shù)量
} _ _attribute_ _ ((packed));
配置描述符:此配置中的接口數(shù)、支持的掛起和恢復能力以及功率要求。USB 配置在內核中使用 usb_host_config 結構體描述,USB 配置描述符定義為結構體 us不忘初心onfig_descriptor,
struct us不忘初心onfig_descriptor
{
_ _u8 bLength; //描述符長度
_ _u8 bDescriptorType; //描述符類型編號
_ _le16 wTotalLength; //配置所返回的所有數(shù)據(jù)的大小
_ _u8 bNumInterfaces; // 配置所支持的接口數(shù)
_ _u8 bConfigurationValue; //Set_Configuration 命令需要的參數(shù)值
_ _u8 iConfiguration; //描述該配置的字符串的索引值
_ _u8 bmAttributes; //
供電模式的選擇
_ _u8 bMa*Power; //設備從總線提取的最大電流
} _ _attribute_ _ ((packed));
接口描述符:接口類、子類和適用的協(xié)議,接口備用配置的數(shù)目和端點數(shù)目。USB 接口在內核中使用 usb_interface 結構體描述, USB 接口描述符定義為結構體usb_interface_descriptor
struct usb_interface_descriptor
{
_ _u8 bLength;
//描述符長度
_ _u8 bDescriptorType; //描述符類型
_ _u8 bInterfaceNumber;
// 接口的編號
_ _u8 bAlternateSetting; //備用的接口描述符編號
_ _u8 bNumEndpoints;
//該接口使用的端點數(shù),不包括端點 0
_ _u8 bInterfaceClass;
//接口類型
_ _u8 bInterfaceSubClass; //接口子類型
_ _u8 bInterfaceProtocol; //接口所遵循的協(xié)議
_ _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; //// 本端點接收或發(fā)送的最大信息包的大小
_ _u8 bInterval; //輪詢數(shù)據(jù)傳送端點的時間間隔
//對于批量傳送的端點以及控制傳送的端點,此域忽略
//對于同步傳送的端點,此域必須為 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 驅動程序重要數(shù)據(jù)結構
為了符合 USB 規(guī)范的數(shù)據(jù)傳輸格式,在Linu*中定義了一組用來實現(xiàn)控制傳輸,中斷傳輸,批量傳輸,等時傳輸?shù)臄?shù)據(jù)結構:[ehci|ohci|uhci]_[itd|sitd|qh|fstn],是三個主機驅動器的核心,潤健要做的事就是把上層設備驅動傳下來的數(shù)據(jù)轉換成一個個的符合這些數(shù)據(jù)結構要求的數(shù)據(jù)并在內存中放好,利用這些數(shù)據(jù)結構中定義的指針把數(shù)據(jù)鏈成鏈表,直接通過硬件傳出去。
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* 的架構中,有一種數(shù)據(jù)結構叫 URB(USB Request Block) 封裝了這些概念,作為 USB 設備驅動,要使用 USB 通路來傳輸數(shù)據(jù),就只要操控 URB 就可以了。
struct urb
{
/* 私有的:只能由 USB 核心和主機控制器訪問的字段 */
struct kref kref; /*URB 引用計數(shù) */
spinlock_t lock; /* URB 鎖 */
void *hcpriv; /* 主機控制器私有數(shù)據(jù) */
int bandwidth; /* INT/ISO 請求的帶寬 */
atomic_t use_count; /* 并發(fā)傳輸計數(shù) */
u8 reject; /* 傳輸將失敗*/
/* 公共的: 可以被驅動使用的字段 */
struct list_head urb_list; /* 鏈表頭*/
struct usb_device *dev; /* 關聯(lián)的 USB 設備 */
unsigned int pipe; /* 管道信息 */
int status; /* URB 的當前狀態(tài) */
unsigned int transfer_flags; /* URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* 發(fā)送數(shù)據(jù)到設備或從設備接收數(shù)據(jù)的緩沖區(qū) */
dma_addr_t transfer_dma; /*用來以 DMA 方式向設備傳輸數(shù)據(jù)的緩沖區(qū) */
int transfer_buffer_length;/*transfer_buffer 或 transfer_dma 指向緩沖區(qū)的大小*/
int actual_length; /* URB 結束后,發(fā)送或接收數(shù)據(jù)的實際長度 */
unsigned char *setup_packet; /* 指向控制 URB 的設置數(shù)據(jù)包的指針*/
dma_addr_t setup_dma; /*控制 URB 的設置數(shù)據(jù)包的 DMA 緩沖區(qū)*/
int start_frame; /*等時傳輸中用于設置或返回初始幀*/
int number_of_packets; /*等時傳輸中等時緩沖區(qū)數(shù)據(jù) */
int interval; /* URB 被輪詢到的時間間隔(對中斷和等時 URB 有效) */
int error_count; /* 等時傳輸錯誤數(shù)量 */
void *conte*t; /* completion 函數(shù)上下文 */
us不忘初心omplete_t complete; /* 當 URB 被完全傳輸或發(fā)生錯誤時,被調用 */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/*單個 URB 一次可定義多個等時傳輸時,描述各個等時傳輸 */
};
1.5.1 URB 處理流程
USB 設備中的每個端點都處理一個 URB 隊列,在隊列被清空之前,一個 URB的典型生命周期如下:
(1)被一個 USB 設備驅動創(chuàng)建。
創(chuàng)建 URB 結構體的函數(shù)為:
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
urb 結構體在驅動中不能靜態(tài)創(chuàng)建,因為這可能破壞 USB 核心給 URB 使用的引用計數(shù)方法。usb_alloc_urb() 的“反函數(shù)”為:
void usb_free_urb(struct urb *urb);
該函數(shù)用于釋放由 usb_alloc_urb() 分配的 urb 結構體。
(2)初始化,被安排給一個特定 USB 設備的特定端點。
對于中斷 urb ,使用 usb_fill_int_urb() 函數(shù)來初始化 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);
這些初始化函數(shù)使用于控制、中斷和批量數(shù)據(jù)傳輸模式,在等時傳輸下不能使用此類函數(shù)。
(3)被 USB 設備驅動提交給 USB 核心
在邏輯層上把 USB 看著一個個的 pipe ,其實主機不是與從設備的硬件直接打交道,而是與從設備中的 USB 固件( USB 從控制器的驅動)打交道。
設備驅動想要 USB 總線和設備通信,先初始化 URB 結構,把想要傳送的數(shù)據(jù)用系統(tǒng)提供 URB 操作工具填入 URB 中,然后用 usb_submit_urb 向 USB 核心提交。
int usb_submit_urb(struct urb *urb, int mem_flags);
在提交 URB 到 USB 核心后,直到完成函數(shù)被調用之前,不要能訪問URB中的任何成員。
usb_submit_urb中全調用usb_hcd_submit_urb,usb_hcd_submit_urb會找到預先制定的控制器驅動,即調用 hcd->driver->urb_equeue() ,如對ehci控制器來說,urb_enqueue 就是 ehci_urb_enqueue() ,數(shù)據(jù)走到 ehci_urb_enqueue ……(未完,全文共68081字,當前僅顯示12245字,請閱讀下面提示信息。
收藏《畢業(yè)論文:基于Linu*的網絡攝像頭實現(xiàn)》)