v4l2驅動文件之 streaming IO

2021-06-20 00:08:11 字數 3528 閱讀 9411

v4l2驅動編寫篇第六b--流輸入輸出 

使用read()和write()方法,每一幀都要通過i/o操作在使用者和核心空間之間拷貝資料。然而, 當使用流輸入輸出的方式時,這種情況就不會發生。替代的方案是使用者與核心空間之間交換緩衝區的指標,這些緩衝區將被對映到應用的位址空間,這也就使零幀複製數成為可能。有兩種流輸入輸出緩衝區:

* 使用者空間緩衝區 (v4l2_memory_userptr) 是在使用者空間的應用中開闢的。很明顯,在這種情況下,是不需要mmap()呼叫的,但驅動在有效地支援使用者空間緩衝區上的工作將會更難一些。

注意:驅動支援流輸入輸出的方式並非必需,即便做了實現,驅動也不必兩種緩衝區型別都做處理。乙個靈活的驅動可以支援更多的應用。在實際應用中,似乎多數應用都是使用記憶體對映緩衝區的。同時使用兩種緩衝區是不可能的。

現在,我們將要探索一下支援流輸入輸出的眾多而邋遢的細節。任何video4linux2驅動的作者都要了解這部分api。然而值得指出的是,有乙個更高層次的api,它能夠幫助驅動作者寫流驅動。當底層裝置可以支援分散/聚集i/o的時候,這一層(稱為 video-buf)可以使事情變得容易。關於 video-buf api我們將在將來的某期討論。

支援流輸入輸出的驅動應該通知應用這一事實,方法是在vidioc_querycap()方法中設定v4l2_cap_streaming標籤。注意:並沒有辦法來描述支援的是哪一種緩衝區,那是後話。

注意:驅動並不一定要開闢與請求的一樣數目的緩衝區。在很多情況下,有乙個最小值緩衝區數的有意義。如果應用請求的比最小值小,可能實際返回的要多於請求的。以筆者的經驗,mplayer要用兩個緩衝區,如果使用者空間速度慢下來的話,這將很容易溢位(因而丟失幀)。通過強制乙個大一點的最小緩衝區數(可調整的模組引數),cafe_ccic驅動可以使流輸入輸出通道更加強壯。count 欄位應設為方法返回前實際開闢的緩衝區數。

應用可以通設定count欄位為0的方式來釋放掉所有已存在的緩衝區。在這種情況下,驅動必須在釋放緩衝前停止所有的dma操作,否則會發生非常嚴重的事情。如果緩衝區已對映到使用者空間,則釋放緩衝區是不可能的。

相反,如果用的是使用者空間緩衝區,則有意義的字段只有緩衝區的type和memory 欄位v4l2_memory_userptr這個值。應用不需要指定它想用的緩衝區的數目。因為記憶體是在使用者空間開闢的,驅動無須操心。如果驅動支援使用者空間緩衝區,它只須注意應用會使用這一特性,返 回0就可以了,否則通常的-einval 返回值會被呼叫到.

vidioc_reqbufs 命令是應用得知驅動支援的流輸入輸出緩衝區型別的唯一方法。

3、將緩衝區對映到使用者空間

如果使用了使用者空間,在應用向傳入佇列放置緩衝區之前,驅動看不到任何緩衝區相關的呼叫。記憶體對映緩衝區需要更多的設定。應用通常會檢視每乙個開闢了的緩衝區,並將其對映到自己位址空間。第一站是vidioc_querybuf命令,它將轉換成驅動中的 vidioc_querybuf() 方法:

int (*vidioc_querybuf)(struct file *file, void *private_data, truct v4l2_buffer *buf);

進入這個方法時,buf 欄位中要設定的字段有type (在緩衝區開闢時,它將被檢查是否與給定的型別相同)和index,它們可以確定乙個特定的緩衝區。驅動要保證index有意義,並添充buf中的其餘字段。通常來說,驅動內部儲存著乙個v4l2_buffer結構體的陣列, 所以vidioc_querybuf()方法的核心只是乙個結構體的賦值。

應用訪問記憶體對映緩衝區的唯一方法就是將其對映到它們的位址空間,所以 vidioc_querybuf() 呼叫後面通常會跟著乙個驅動的mmap()方法 -這個方法,大家要記住,是儲存在相關裝置的video_device結構體中的fops欄位中的。 裝置如何處理mmap() 將依賴於核心中緩衝區是如何設定的。如果緩衝區可以在remap_pfn_range() 或remap_vmalloc_range()之前對映,那就應該在這個時間來做。對於核心空間的緩衝區,頁也可以在頁錯誤時通過常規的使用 nopage()方法的方式單獨被對映,對於需要的人來說,在linux device drivers 可以找到乙個關於handlingmmap()的乙個很好的討論。

4、流輸入輸出

到現為止,我們己經看了很多設定,卻沒有傳輸過一幀的資料,我們離這步己經很近了,但在些之前還有乙個步 驟要做。當應用通過 vidioc_reqbufs獲得了緩衝區後,那個緩衝區都處於使用者空間狀態。如果他們是使用者空間緩衝區,他們甚至還並不真的存在。在應用可以開始流的輸入輸出之前,它必須至少將乙個緩衝區放到驅動傳入佇列,對於輸出裝置,那些緩衝區當然還要先添完有效的幀資料。

要把乙個緩衝區加入佇列,應用首先要發出乙個vidioc_qbuf ioctl()呼叫,這個呼叫v4l2會對映為對驅動的vidioc_qbuf()方法的呼叫。

int (*vidioc_qbuf) (struct file *file, void *private_data, struct v4l2_buffer *buf);

對於記憶體對映緩衝而言,還是那樣,只有buf的type和 index欄位有效. 驅動只能進行一些明顯的檢查(type 和index 有意義,緩衝區還沒有在驅動的佇列裡,緩衝區己對映等。),把緩衝區放在傳入佇列裡 (設定v4l2_buf_flag_queued 標籤),並返回.

在這點上,使用者空間緩衝區可能會更加複雜,因為驅動可能從來沒看過緩衝區的樣子。使用這個方法時,允許應用在每次向佇列傳入緩衝區時,傳遞不同的位址,所以驅動不能提前做任何的設定。如果你的驅動通過核心空間緩衝區將幀送回,它只須記錄一下應用提供的使用者空間位址就可以了。然而如果你正嘗試將通過 dma直接將資料傳送到使用者空間,那將會非常的具有挑戰性。

如果你的裝置傳輸的是小影象(如usb攝像頭),直接從dma到使用者空間的設定就簡單些。在任何情況下,面對支援直接i/o到使用者空間緩衝的改變,驅動作者都應該做到以下兩點:(1)確定的確值得這麼大的麻煩,因為應用更趨向於使用記憶體對映緩衝區。(2)使用video buf層,它可以幫你解決一些痛苦的難 題 。

5、佇列上的緩衝區傳輸

一旦流輸入輸出開始,驅動就要從它的傳入佇列裡抓取緩衝區,讓裝置更快地實現傳送請求,然後把緩衝區移動到傳出佇列。轉輸開始時,緩衝區標籤也要相應調整。像序號和時間戳這樣的字段必需在這個時候添充。最後,應用會在傳出佇列中認領緩衝區,讓它變回為使用者空 間狀態。這是vidioc_dqbuf的工作,它最終變為如下呼叫:

int (*vidioc_dqbuf) (struct file *file, void *private_data, struct v4l2_buffer *buf);

int (*vidioc_streamon) (struct file *file, void *private_data, enum v4l2_buf_type type);

int (*vidioc_streamoff)(struct file *file, void *private_data, enum v4l2_buf_type type);

對vidioc_streamon()的呼叫應該在檢查型別有意義之後讓裝置開始工作。如查需要的話,驅動可以請求傳入佇列中有一定數目的緩衝區後再開始流的轉輸.

當應用關閉時,它應發出乙個對vidioc_streamoff()的呼叫,這個呼叫要停止裝置。驅動還應從傳入傳出佇列中移除所有的緩衝區,使它們都處於使用者空間狀態。當然,驅動必須準備好,應用可能在沒有停流轉輸的情況下關閉裝置。

v4l2驅動文件之 controls

v4l2的控制介面試圖使事情盡可能地簡單,同時還能完全發揮硬體的功能。它開始於定義乙個標準控制名字 的集合,包括 v4l2 cid brightness,v4l2 cid contrast,v4l2 cid saturation,還有許多其他的。對於白平衡 水平,垂直映象等特性,還提供了一些布林型的...

九 v4l2驅動文件之 streaming IO

分類 v4l2 2013 03 30 21 29 681人閱讀收藏 舉報現在,我們將要探索一下支援流輸入輸出的眾多而邋遢的細節。任何video4linux2驅動的作者都要了解這部分api。然而值得指出的是,有乙個更高層次的api,它能夠幫助驅動作者寫流驅動。當底層裝置可以支援分散 聚集i o的時候,...

十 v4l2驅動文件之 controls

分類 v4l2 2013 03 30 21 35 492人閱讀收藏 舉報v4l2的控制介面試圖使事情盡可能地簡單,同時還能完全發揮硬體的功能。它開始於定義乙個標準控制名字 的集合,包括 v4l2 cid brightness,v4l2 cid contrast,v4l2 cid saturation...