(二)通過虛擬驅動vivi分析攝像頭驅動

2021-10-12 03:41:59 字數 4101 閱讀 7021

一、通過指令 「strace -o xawtv.log xawtv」 得到以下呼叫資訊:

// 1~7都是在v4l2_open裡呼叫

open

ioctl(4, vidioc_querycap

// 3~7 都是在get_device_capabilities裡呼叫

3. for()

ioctl(4, vidioc_enuminput // 列舉輸入源,vidioc_enuminput/vidioc_g_input/vidioc_s_input不是必需的

4. for()

ioctl(4, vidioc_enumstd // 列舉標準(制式), 不是必需的

5. for()

ioctl(4, vidioc_enum_fmt // 列舉格式

ioctl(4, vidioc_g_parm

for()

ioctl(4, vidioc_queryctrl // 查詢屬性(比如說亮度值最小值、最大值、預設值)

// 8~10都是通過v4l2_read_attr來呼叫的

8. ioctl(4, vidioc_g_std // 獲得當前使用的標準(制式), 不是必需的

9. ioctl(4, vidioc_g_input

10. ioctl(4, vidioc_g_ctrl // 獲得當前屬性, 比如亮度是多少

ioctl(4, vidioc_try_fmt // 試試能否支援某種格式

ioctl(4, vidioc_s_fmt // 設定攝像頭使用某種格式

// 13~16在v4l2_start_streaming

13. ioctl(4, vidioc_reqbufs // 請求系統分配緩衝區

14. for()

ioctl(4, vidioc_querybuf // 查詢所分配的緩衝區

mmap

15. for ()

ioctl(4, vidioc_qbuf // 把緩衝區放入佇列

16. ioctl(4, vidioc_streamon // 啟動攝像頭

// 17裡都是通過v4l2_write_attr來呼叫的

17. for ()

ioctl(4, vidioc_s_ctrl // 設定屬性

ioctl(4, vidioc_s_input // 設定輸入源

ioctl(4, vidioc_s_std // 設定標準(制式), 不是必需的

// v4l2_nextframe > v4l2_waiton

18. v4l2_queue_all //全部放入buffer

v4l2_waiton

for ()

) = 1 (in [4], left )

ioctl(4, vidioc_dqbuf // de-queue, 把緩衝區從佇列中取出

// 處理, 之以已經通過mmap獲得了緩衝區的位址, 就可以直接訪問資料

ioctl(4, vidioc_qbuf // 把緩衝區放入佇列

}xawtv的幾大函式:

v4l2_open

v4l2_read_attr/v4l2_write_attr //讀寫屬性

v4l2_start_streaming //申請buffer

v4l2_nextframe/v4l2_waiton

二、攝像頭驅動程式必需的11個ioctl: /drivers/media/video/vivi.c -> vivi_ioctl_ops(結構體),修改測試得知:

.vidioc_querycap = vidioc_querycap, // 表示它是乙個攝像頭裝置

/* 用於列舉、獲得、測試、設定攝像頭的資料的格式 */

.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,

.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,

.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,

.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,

/* 緩衝區操作: 申請/查詢/放入佇列/取出佇列 */

.vidioc_reqbufs = vidioc_reqbufs,

.vidioc_querybuf = vidioc_querybuf,

.vidioc_qbuf = vidioc_qbuf,

.vidioc_dqbuf = vidioc_dqbuf,

// 啟動/停止

.vidioc_streamon = vidioc_streamon,

.vidioc_streamoff = vidioc_streamoff,

繼續分析資料的獲取過程:

請求分配緩衝區: ioctl(4, vidioc_reqbufs // 請求系統分配緩衝區

videobuf_reqbufs(佇列, v4l2_requestbuffers) // 佇列在open函式用videobuf_queue_vmalloc_init初始化

// 注意:這個ioctl只是分配緩衝區的頭部資訊,真正的快取還沒有分配

把緩衝區放入佇列:

ioctl(4, vidioc_qbuf // 把緩衝區放入佇列

videobuf_qbuf

q->ops->buf_prepare(q, buf, field); // 呼叫驅動程式提供的函式做些預處理

list_add_tail(&buf->stream, &q->stream); // 把緩衝區放入佇列的尾部

q->ops->buf_queue(q, buf); // 呼叫驅動程式提供的"入佇列函式"

啟動攝像頭

ioctl(4, vidioc_streamon

videobuf_streamon

q->streaming = 1;

用select查詢是否有資料:select(5, [4], null, null, ) = 1 (in [4], left )

// 驅動程式裡必定有: 產生資料、喚醒程序

v4l2_poll

vdev->fops->poll

vivi_poll

videobuf_poll_stream

// 從佇列的頭部獲得緩衝區

buf = list_entry(q->stream.next, struct videobuf_buffer, stream);

// 如果沒有資料則休眠                   

poll_wait(file, &buf->done, wait);

誰來產生資料、誰來喚醒它?

核心執行緒vivi_thread每30ms執行一次,它呼叫

vivi_thread_tick

vivi_fillbuff(fh, buf);     // 構造資料

wake_up(&buf->vb.done); // 喚醒程序

// 把它從佇列中刪掉

list_del(&buf->stream);

videobuf_status(q, b, buf, q->type);

應用程式根據vidioc_dqbuf所得到緩衝區狀態,知道是哪乙個緩衝區有資料

就去讀對應的位址(該位址來自前面的mmap)。

===> vivi.c緩衝區操作過程: ①vidioc_reqbufs(分配頭部資訊) -> ②vidioc_querybuf(返回屬性) / mmap(對映位址,分配實際空間) ->

③ vidioc_qbuf(把緩衝區放入佇列) -> ④vidioc_streamon(啟動攝像頭)-> ⑤用select查詢是否有資料:在佇列頭乙個buf上操作 ->

⑥ vidioc_dqbuf(返回佇列頭的buf並從佇列中刪除) -> ⑦ vidioc_dqbuf(重新放回佇列-③)

怎麼寫攝像頭驅動程式:

分配結構體:video_device:video_device_alloc

設定.fops

.ioctl_ops (裡面需要設定11項)

如果要用核心提供的緩衝區操作函式,還需要構造乙個videobuf_queue_ops

註冊: video_register_device

linux攝像頭虛擬驅動vivi徹底分析3

1 依次裝載驅動程式,出現錯誤如下 2 用dmesg命令檢視詳細輸出 某些函式沒有識別 可見我們的vivi.ko還依賴於其他驅動程式,上一節直接使用這些命令沒有問題,是因為在做虛擬攝像頭vivi之前。我們先接上usb攝像頭,ubuntu裡面自動給我們安裝了其他驅動程式。所以在使用vivi的時候,沒有...

MSIL入門(二)通過物件看IL

class program class myclass 例項字段每次建立型別例項的時候都會進行建立,它們屬於這個型別的例項,而靜態欄位由型別的所有例項共享,並且它會在型別載入時建立。某些靜態字段 文字欄位和對映字段 從不分配。引導程式只需要記錄要對映的字段的位置,並在字段定址時定址這些位置。高階別的...

串列埠通訊系列二 通過txt檔案讀取串列埠引數

從txt檔案中串列埠讀取引數 建立txt檔案,並寫入預設引數 讀取txt檔案 根據txt檔案裡的內容設定串列埠引數 filestream 對檔案系統上的檔案進行讀取 寫入 開啟和關閉操作 streamwriter 流寫入 streamreader 流讀取 list集合 using system us...