在Ubuntu系統(tǒng)中接上usb攝像頭設(shè)備時(shí),系統(tǒng)會(huì)自動(dòng)安裝對(duì)應(yīng)的usb設(shè)備驅(qū)動(dòng)程序。 (通過(guò)追蹤應(yīng)用程序xawtv調(diào)用驅(qū)動(dòng)vivi的過(guò)程,使之生成對(duì)應(yīng)的TXT文件,在文件中搜索 /dev/video*字段,得到一系列函數(shù),再打開(kāi)xawtv源碼,得到vivi驅(qū)動(dòng)必須的系統(tǒng)調(diào)用,進(jìn)而分析驅(qū)動(dòng)框架) 一、xawtv所涉及的vivi驅(qū)動(dòng)的系統(tǒng)調(diào)用 使用方法 :執(zhí)行 strace -o xawtv.txt xawtv ,生成了調(diào)用過(guò)程xawtv.txt 搜索 /dev/video0,得到如下: open("/dev/video0", O_RDWR|O_LARGEFILE) = 4 ![]() open("/dev/video0", O_RDWR|O_LARGEFILE) = 4 ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbff6c704) = 0 ioctl(4, VIDIOC_G_FMT or VT_SENDSIG, 0xbff6c638) = 0 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0 ioctl(4, 0xc02c564a, 0xbff6c518) = -1 EINVAL (Invalid argument) ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0 ioctl(4, 0xc02c564a, 0xbff6c518) = -1 EINVAL (Invalid argument) ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0 ioctl(4, 0xc02c564a, 0xbff6c518) = -1 EINVAL (Invalid argument) ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0 ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = -1 EINVAL (Invalid argument) ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbff6c544) = 0 ioctl(4, VIDIOC_G_INPUT, 0xbff6c3ec) = 0 ioctl(4, VIDIOC_ENUMINPUT, 0xbff6c3ec) = 0View Code xawtv涉及的vivi驅(qū)動(dòng)的系統(tǒng)調(diào)用: // 1~7都是在v4l2_open里調(diào)用 1. open 2. ioctl(4, VIDIOC_QUERYCAP // 3~7 都是在get_device_capabilities里調(diào)用 3. for() ioctl(4, VIDIOC_ENUMINPUT // 列舉輸入源,VIDIOC_ENUMINPUT/VIDIOC_G_INPUT/VIDIOC_S_INPUT不是必需的 4. for() ioctl(4, VIDIOC_ENUMSTD // 列舉標(biāo)準(zhǔn)(制式), 不是必需的 5. for() ioctl(4, VIDIOC_ENUM_FMT // 列舉格式 6. ioctl(4, VIDIOC_G_PARM 7. for() ioctl(4, VIDIOC_QUERYCTRL // 查詢屬性(比如說(shuō)亮度值最小值、最大值、默認(rèn)值) // 8~10都是通過(guò)v4l2_read_attr來(lái)調(diào)用的 8. ioctl(4, VIDIOC_G_STD // 獲得當(dāng)前使用的標(biāo)準(zhǔn)(制式), 不是必需的 9. ioctl(4, VIDIOC_G_INPUT 10. ioctl(4, VIDIOC_G_CTRL // 獲得當(dāng)前屬性, 比如亮度是多少 11. ioctl(4, VIDIOC_TRY_FMT // 試試能否支持某種格式 12. ioctl(4, VIDIOC_S_FMT // 設(shè)置攝像頭使用某種格式 // 13~16在v4l2_start_streaming 13. ioctl(4, VIDIOC_REQBUFS // 請(qǐng)求系統(tǒng)分配緩沖區(qū) 14. for() ioctl(4, VIDIOC_QUERYBUF // 查詢所分配的緩沖區(qū) mmap 15. for () ioctl(4, VIDIOC_QBUF // 把緩沖區(qū)放入隊(duì)列 16. ioctl(4, VIDIOC_STREAMON // 啟動(dòng)攝像頭 // 17里都是通過(guò)v4l2_write_attr來(lái)調(diào)用的 17. for () ioctl(4, VIDIOC_S_CTRL // 設(shè)置屬性 ioctl(4, VIDIOC_S_INPUT // 設(shè)置輸入源 ioctl(4, VIDIOC_S_STD // 設(shè)置標(biāo)準(zhǔn)(制式), 不是必需的 // v4l2_nextframe > v4l2_waiton 18. v4l2_queue_all v4l2_waiton for () { select(5, [4], NULL, NULL, {5, 0}) = 1 (in [4], left {4, 985979}) ioctl(4, VIDIOC_DQBUF // de-queue, 把緩沖區(qū)從隊(duì)列中取出 // 處理, 之以已經(jīng)通過(guò)mmap獲得了緩沖區(qū)的地址, 就可以直接訪問(wèn)數(shù)據(jù) ioctl(4, VIDIOC_QBUF // 把緩沖區(qū)放入隊(duì)列 } ? 由上可知xawtv的幾大函數(shù): 1. v4l2_open 2. v4l2_read_attr/v4l2_write_attr 3. v4l2_start_streaming 4. v4l2_nextframe/v4l2_waiton 攝像頭驅(qū)動(dòng)程序必需的11個(gè)ioctl: ? // 表示它是一個(gè)攝像頭設(shè)備 .vidioc_querycap = vidioc_querycap, /* 用于列舉、獲得、測(cè)試、設(shè)置攝像頭的數(shù)據(jù)的格式 */ .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, /* 緩沖區(qū)操作: 申請(qǐng)/查詢/放入隊(duì)列/取出隊(duì)列 */ .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, // 啟動(dòng)/停止 .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, ? 分析數(shù)據(jù)的獲取過(guò)程: 1. 請(qǐng)求分配緩沖區(qū): ioctl(4, VIDIOC_REQBUFS // 請(qǐng)求系統(tǒng)分配緩沖區(qū) videobuf_reqbufs(隊(duì)列, v4l2_requestbuffers) // 隊(duì)列在open函數(shù)用videobuf_queue_vmalloc_init初始化 // 注意:這個(gè)IOCTL只是分配緩沖區(qū)的頭部信息,真正的緩存還沒(méi)有分配呢 //在驅(qū)動(dòng)程序有一條原則,這些資源只有在我們用到的時(shí)候才進(jìn)行分配 2. 查詢映射緩沖區(qū): ioctl(4, VIDIOC_QUERYBUF // 查詢所分配的緩沖區(qū) videobuf_querybuf // 獲得緩沖區(qū)的數(shù)據(jù)格式、每一行長(zhǎng)度、高度、緩沖區(qū)使用狀態(tài)、在內(nèi)核空間的偏移地址、緩沖區(qū)長(zhǎng)度等 mmap(參數(shù)里有"大小") // 在這里才分配緩存 v4l2_mmap vivi_mmap videobuf_mmap_mapper videobuf-vmalloc.c里的__videobuf_mmap_mapper mem->vmalloc = vmalloc_user(pages); // 在這里才給緩沖區(qū)分配空間 3. 把緩沖區(qū)放入隊(duì)列: ioctl(4, VIDIOC_QBUF // 把緩沖區(qū)放入隊(duì)列 videobuf_qbuf q->ops->buf_prepare(q, buf, field); // 調(diào)用驅(qū)動(dòng)程序提供的函數(shù)做些預(yù)處理 list_add_tail(&buf->stream, &q->stream); // 把緩沖區(qū)放入隊(duì)列的尾部 q->ops->buf_queue(q, buf); // 調(diào)用驅(qū)動(dòng)程序提供的"入隊(duì)列函數(shù)" 4. 啟動(dòng)攝像頭 ioctl(4, VIDIOC_STREAMON videobuf_streamon q->streaming = 1; 5. 用select查詢是否有數(shù)據(jù) // 驅(qū)動(dòng)程序里必定有: 產(chǎn)生數(shù)據(jù)、喚醒進(jìn)程 v4l2_poll vdev->fops->poll vivi_poll videobuf_poll_stream // 從隊(duì)列的頭部獲得緩沖區(qū) buf = list_entry(q->stream.next, struct videobuf_buffer, stream); // 如果沒(méi)有數(shù)據(jù)則休眠,在buf->done這里進(jìn)行休眠 poll_wait(file, &buf->done, wait); 誰(shuí)來(lái)產(chǎn)生數(shù)據(jù)、誰(shuí)來(lái)喚醒它? 內(nèi)核線程vivi_thread每30MS執(zhí)行一次,它調(diào)用 vivi_thread_tick vivi_fillbuff(fh, buf); // 構(gòu)造數(shù)據(jù) wake_up(&buf->vb.done); // 喚醒進(jìn)程 6. 有數(shù)據(jù)后從隊(duì)列里取出緩沖區(qū) // 有那么多緩沖區(qū),APP如何知道哪一個(gè)緩沖區(qū)有數(shù)據(jù)?調(diào)用VIDIOC_DQBUF ioctl(4, VIDIOC_DQBUF vidioc_dqbuf // 在隊(duì)列里獲得有數(shù)據(jù)的緩沖區(qū) retval = stream_next_buffer(q, &buf, nonblocking); // 把它從隊(duì)列中刪掉 list_del(&buf->stream); // 把這個(gè)緩沖區(qū)的狀態(tài)返回給APP videobuf_status(q, b, buf, q->type); 7. 應(yīng)用程序根據(jù)VIDIOC_DQBUF所得到緩沖區(qū)狀態(tài),知道是哪一個(gè)緩沖區(qū)有數(shù)據(jù) 就去讀對(duì)應(yīng)的地址(該地址來(lái)自前面的mmap) ? 總結(jié)數(shù)據(jù)獲取過(guò)程:(圖片來(lái)自:https://blog.csdn.net/ljmiaw/article/details/72801456)
? ? ? 怎么寫(xiě)攝像頭驅(qū)動(dòng)程序: 1. 分配video_device:video_device_alloc 2. 設(shè)置 .fops .ioctl_ops (里面需要設(shè)置11項(xiàng)) 如果要用內(nèi)核提供的緩沖區(qū)操作函數(shù),還需要構(gòu)造一個(gè)videobuf_queue_ops 3. 注冊(cè): video_register_device ? 來(lái)源:http://www./content-4-151051.html |
|
來(lái)自: 印度阿三17 > 《開(kāi)發(fā)》