本文共 38306 字,大约阅读时间需要 127 分钟。
v4l2驱动框架相对还是挺复杂的,
最好的参考例子有
v4l2-pci-skeleton.c
和
vivi (Virtual Video)
其中vivi在最新的Linux 4.xx版本也变得非常复杂。
所以采用Linux-3.16.74版本作为学习...
1. 该版本没有使用platform_driver框架, 故模块初始化入口函数vivi_init直接进行video设备的注册。
(如果使用platform driver的框架, 会在probe函数中进行注册)
2. vivi_init初始化过程, 最主要的动作就是创建vivi实例。
主要函数为vivi_create_instance
通常的, 里面会分配好vivi设备描述结构体, vivi_dev
创建v4l2_dev设备, 调用v4l2_device_register注册一个v4l2设备, 这个相比video_device来说, 相当于一个父节点, 描述的信息更加common, 注册函数中会对该设备进行一些通用的初始化和资源分配。
下一步是构建video_device并注册该设备。
关于ctrl_handler, 如亮度, 对比度, 透明度等等暂时先不具体研究。
4. 创建vb2_queue, video设备内部使用的队列。
该结构体也很复杂, 直接贴图...
struct vb2_queue { › enum v4l2_buf_type› › type; › unsigned int› › › io_modes; › unsigned int› › › io_flags; › struct mutex› › › *lock; › struct v4l2_fh› › › *owner; › const struct vb2_ops› › *ops; › const struct vb2_mem_ops› *mem_ops; › void› › › › *drv_priv; › unsigned int› › › buf_struct_size; › u32›› › › timestamp_flags; › gfp_t› › › › gfp_flags; › u32›› › › min_buffers_needed; /* private: internal use only */ › enum v4l2_memory› › memory; › struct vb2_buffer› › *bufs[VIDEO_MAX_FRAME]; › unsigned int› › › num_buffers; › struct list_head› › queued_list; › unsigned int› › › queued_count; › atomic_t› › › owned_by_drv_count; › struct list_head› › done_list; › spinlock_t› › › done_lock; › wait_queue_head_t› › done_wq; › void› › › › *alloc_ctx[VIDEO_MAX_PLANES]; › unsigned int› › › plane_sizes[VIDEO_MAX_PLANES]; › unsigned int› › › streaming:1; › unsigned int› › › start_streaming_called:1; › unsigned int› › › waiting_for_buffers:1; m› struct vb2_fileio_data› › *fileio; › struct vb2_threadio_data› *threadio;
可以看到vb2_queue内部有两个队列, queued_list和done_list
即用户req bufs并mmap到用户态后, 调用queue buffer时, 会将这些空buffer放入queued_list,
vivi设备内部线程, 或工作队列, 或定时器等 隔段时间将要显示的yuv数据填充到申请的缓存中。
并放入done_list, 即完成队列中。
如此, 用户态就能通过dequeue buffer取出done_list中的数据, 进行渲染, 存储或传输等操作了。
5. 继续填充video_device数据结构, 比如file_operations, ioctls等等
1457 › vfd = &dev->vdev; 1458 › *vfd = vivi_template; 1459 › vfd->debug = debug; 1460 › vfd->v4l2_dev = &dev->v4l2_dev; 1461 › vfd->queue = q;
6. 设备都填充好后, 调用video_register_device即可
ok, 是不是很简单...
是的, 当你对linux设备框架理解后, 它就会变得越来越简单...
所以我还需不断实践加总结, 加深理解...
后面我们跟下代码, 最关键的VIDIOC_QBUF和VIDIOC_DQBUF,
看下内部的buffer是如何流动的。
1. VIDIOC_QBUF - >
查看vivi_ioctl_ops
1320 static const struct v4l2_ioctl_ops vivi_ioctl_ops = { 1321 › .vidioc_querycap = vidioc_querycap, 1322 › .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 1323 › .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 1324 › .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 1325 › .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 1326 › .vidioc_enum_framesizes = vidioc_enum_framesizes, 1327 › .vidioc_reqbufs = vb2_ioctl_reqbufs, 1328 › .vidioc_create_bufs = vb2_ioctl_create_bufs, 1329 › .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 1330 › .vidioc_querybuf = vb2_ioctl_querybuf, 1331 › .vidioc_qbuf = vb2_ioctl_qbuf, 1332 › .vidioc_dqbuf = vb2_ioctl_dqbuf, 1333 › .vidioc_enum_input = vidioc_enum_input, 1334 › .vidioc_g_input = vidioc_g_input, 1335 › .vidioc_s_input = vidioc_s_input, 1336 › .vidioc_enum_frameintervals = vidioc_enum_frameintervals, 1337 › .vidioc_g_parm = vidioc_g_parm, 1338 › .vidioc_s_parm = vidioc_s_parm, 1339 › .vidioc_streamon = vb2_ioctl_streamon, 1340 › .vidioc_streamoff = vb2_ioctl_streamoff, 1341 › .vidioc_log_status = v4l2_ctrl_log_status, 1342 › .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, m1343 › .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 1344 }; 1345 1346 static const struct video_device vivi_template = { 1347 › .name› › = "vivi", 1348 › .fops = &vivi_fops, 1349 › .ioctl_ops ›= &vivi_ioctl_ops, 1350 › .release› = video_device_release_empty, 1351 };
对应实现为vb2_ioctl_qbuf,
是的v4l2-core核心层帮忙实现的逻辑越来越多...这个queue buffer的操作也有通用的一些流程了...
2. vb2_ioctl_qbuf
-> vb2_qbuf->vb2_internal_qbuf->
1785 static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) 1786 { 1787 › int ret = vb2_queue_or_prepare_buf(q, b, "qbuf"); 1788 › struct vb2_buffer *vb; 1789 1790 › if (ret) 1791 › › return ret; 1792 1793 › vb = q->bufs[b->index]; 1794 1795 › switch (vb->state) { 1796 › case VB2_BUF_STATE_DEQUEUED: 1797 › › ret = __buf_prepare(vb, b); 1798 › › if (ret) 1799 › › › return ret; 1800 › › break; 1801 › case VB2_BUF_STATE_PREPARED: 1802 › › break; 1803 › case VB2_BUF_STATE_PREPARING: 1804 › › dprintk(1, "buffer still being prepared\n"); 1805 › › return -EINVAL; 1806 › default: 1807 › › dprintk(1, "invalid buffer state %d\n", vb->state); 1808 › › return -EINVAL; 1809 › } 1810 1811 › /* 1812 › * Add to the queued buffers list, a buffer will stay on it until 1813 › * dequeued in dqbuf. 1814 › */ 1815 › list_add_tail(&vb->queued_entry, &q->queued_list); 1816 › q->queued_count++; 1817 › q->waiting_for_buffers = false; 1818 › vb->state = VB2_BUF_STATE_QUEUED;
首先vb2_queue_or_prepare_buf(q, b, "buf"), 进去看了下, 貌似没干啥事, 很多是检查数据合法性。先不管了。
下面vb = q->bufs[b->index];
即从vb2_buffer中取得当前要入列的帧。 是的, vb2_queue内部使用vb2_buffer来对buffer进行描述。
vb2_buffer中有两个list_head, 分别是queued_entry和done_entry,
list_head结构很简单, 就两个指针, 分别指向前一个和下一个。(对linux kernel里的list就是一个双向链表)
struct list_head {
› struct list_head *next, *prev; };在reqbufs阶段, 会根据用户需求, 分配好需求的帧缓存, 其中vb2_buffer就是描述帧缓存的信息。
vb2_buffer分配好后, queued_entry和done_entry同时被分配出来。入队出队的就是list_head这个指针。
看到注释那里, 就是将vb->queued_entry放入到vb2的queued_list中。 入列就完成了...
ok, queued_list中的buffer 最终是要被vivi设备拿来处理的...嗯 需要填充后丢给done_list..
那queued_list中的buffer如何取出并处理呢?
3. 关于queued_list中的数据如何取出并处理?
直接搜索queued_list 貌似没搜着... (可能又用了一些啥技巧...比如CALL(a, b, op) 啥的)....
但根据常识, 将queued_list中的buffer取出并进行处理的过程(这里是填充数据), 不应该由v4l2-core核心来实现,
而应该由具体的驱动来实现。 这里是vivi.c
所以去vivi.c中进行查看...
677 static void vivi_thread_tick(struct vivi_dev *dev) 678 { 679 › struct vivi_dmaqueue *dma_q = &dev->vidq; 680 › struct vivi_buffer *buf; 681 › unsigned long flags = 0; 682 683 › dprintk(dev, 1, "Thread tick\n"); 684 685 › spin_lock_irqsave(&dev->slock, flags); 686 › if (list_empty(&dma_q->active)) { 687 › › dprintk(dev, 1, "No active queue to serve\n"); 688 › › spin_unlock_irqrestore(&dev->slock, flags); 689 › › return; 690 › } 691 692 › buf = list_entry(dma_q->active.next, struct vivi_buffer, list); 693 › list_del(&buf->list); 694 › spin_unlock_irqrestore(&dev->slock, flags); 695 696 › v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); 697 698 › /* Fill buffer */ 699 › vivi_fillbuff(dev, buf); 700 › dprintk(dev, 1, "filled buffer %p\n", buf); 701 702 › vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); 703 › dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); 704 }
很容易找到, vivi.c中拉起了一个内核线程。
主要工作是定时从dma_q->active队列中取出buffer进行填充。
查看vivi_dmaqueue数据结构,
192 struct vivi_dmaqueue { 193 › struct list_head active; 194 195 › /* thread for generating video stream*/ 196 › struct task_struct *kthread; 197 › wait_queue_head_t wq; 198 › /* Counters to control fps rate */ 199 › int frame; 200 › int ini_jiffies; 201 };
内部有个active队列。
即从活跃的队列中取出数据进行填充。 ok, 继续...
875 static void buffer_queue(struct vb2_buffer *vb) 876 { 877 › struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 878 › struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); 879 › struct vivi_dmaqueue *vidq = &dev->vidq; 880 › unsigned long flags = 0; 881 882 › dprintk(dev, 1, "%s\n", __func__); 883 884 › spin_lock_irqsave(&dev->slock, flags); 885 › list_add_tail(&buf->list, &vidq->active); 886 › spin_unlock_irqrestore(&dev->slock, flags); 887 }
在buffer_queue函数中找到了, 将vivi_buffer的list_head放入活跃队列的尾部。
看下vivi_buffer的结构体
185 /* buffer for one video frame */ 186 struct vivi_buffer { 187 › /* common v4l buffer stuff -- must be first */ 188 › struct vb2_buffer› vb; 189 › struct list_head› list; 190 };
关于buffer_queue的调用,
929 static const struct vb2_ops vivi_video_qops = { 930 › .queue_setup› › = queue_setup, 931 › .buf_prepare› › = buffer_prepare, 932 › .buf_queue› › = buffer_queue, 933 › .start_streaming› = start_streaming, 934 › .stop_streaming›› = stop_streaming, 935 › .wait_prepare› › = vivi_unlock, 936 › .wait_finish› › = vivi_lock, 937 };
它是vb2_queue队列的一个操作回调函数。
它何时会被调用?
搜索下只能在videobuf-core.c中找到...
但我们是vb2... 所以到
videobuf2-core.c中寻找, 果然使用了技巧...
/** * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing */static void __enqueue_in_driver(struct vb2_buffer *vb){ struct vb2_queue *q = vb->vb2_queue; unsigned int plane; vb->state = VB2_BUF_STATE_ACTIVE; atomic_inc(&q->owned_by_drv_count); /* sync buffers */ for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, prepare, vb->planes[plane].mem_priv); call_void_vb_qop(vb, buf_queue, vb);}
看注释... __enqueue_in_driver这个函数将最终会利用call_void_vb_qop 调用buf_queue函数, 进行驱动层面的数据处理。
如果是编解码器, 需要丢给编解码器进行数据处理。
如果是摄像头, 需要把摄像头采集的数据填充给这一个buffer。
我们这里是vivi驱动, buf_queue函数会将vb2_buffer放入活跃队列中。
内核线程会从活跃队列中取数据并填充。最终放入done_list中。
ok, 我们看看__enqueue_in_driver被谁调用了?
很好找, 就在之前描述的v4l2_internel_qbuf中... 之前没贴全... 我们继续贴代码。
1811 › /* 1812 › * Add to the queued buffers list, a buffer will stay on it until 1813 › * dequeued in dqbuf. 1814 › */ 1815 › list_add_tail(&vb->queued_entry, &q->queued_list); 1816 › q->queued_count++; 1817 › q->waiting_for_buffers = false; 1818 › vb->state = VB2_BUF_STATE_QUEUED; 1819 › if (V4L2_TYPE_IS_OUTPUT(q->type)) { 1820 › › /* 1821 › › * For output buffers copy the timestamp if needed, 1822 › › * and the timecode field and flag if needed. 1823 › › */ 1824 › › if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == 1825 › › V4L2_BUF_FLAG_TIMESTAMP_COPY) 1826 › › › vb->v4l2_buf.timestamp = b->timestamp; 1827 › › vb->v4l2_buf.flags |= b->flags & V4L2_BUF_FLAG_TIMECODE; 1828 › › if (b->flags & V4L2_BUF_FLAG_TIMECODE) 1829 › › › vb->v4l2_buf.timecode = b->timecode; 1830 › } 1831 1832 › /* 1833 › * If already streaming, give the buffer to driver for processing. 1834 › * If not, the buffer will be given to driver on next streamon. 1835 › */ 1836 › if (q->start_streaming_called) 1837 › › __enqueue_in_driver(vb);
所以这个数据放入queued_list的同时, 又调用驱动的buf_queue, 放入了vivi驱动中的active队列。
但这个buffer并没有从queued_list中删除, 那具体数据会在什么时候被删除呢?
根据注释的意思, a buffer will stay on it util dequeued in dqbuf。
是的这个buffer会在dqbuf时才会被删除...
其实也好理解, 对于用户态来说, 它并不认为是对两个队列进行操作, dequeue和enqueue的感觉上是同一个队列。。。
从队列头部取一个, 处理完, 放入队列尾部, 感觉上就是这样的。
所以vivi设备内部的buf虽然已经从queued_list取出并放入活跃队列, 并最终进行数据填充放入done_list。
但该数据不会从queued_list中删除, 只有当dequeue buffer成功后, 才会在queued_list中删除!!!
后面继续看dequeue buffer的逻辑.
4. VIDIOC_DQBUF
很快找到, vivi驱动中会调用vb2_ioctl_dqbuf
5. vb2_ioctl_dqbuf
-> vb2_dqbuf -> vb2_internal_dqbuf ->
static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking){ struct vb2_buffer *vb = NULL; int ret; if (b->type != q->type) { dprintk(1, "invalid buffer type\n"); return -EINVAL; } ret = __vb2_get_done_vb(q, &vb, b, nonblocking); if (ret < 0) return ret; switch (vb->state) { case VB2_BUF_STATE_DONE: dprintk(3, "returning done buffer\n"); break; case VB2_BUF_STATE_ERROR: dprintk(3, "returning done buffer with errors\n"); break; default: dprintk(1, "invalid buffer state\n"); return -EINVAL; } call_void_vb_qop(vb, buf_finish, vb); /* Fill buffer information for the userspace */ __fill_v4l2_buffer(vb, b); /* Remove from videobuf queue */ list_del(&vb->queued_entry); q->queued_count--; /* go back to dequeued state */ __vb2_dqbuf(vb); dprintk(1, "dqbuf of buffer %d, with state %d\n", vb->v4l2_buf.index, vb->state); /* * After calling the VIDIOC_DQBUF V4L2_BUF_FLAG_DONE must be * cleared. */ b->flags &= ~V4L2_BUF_FLAG_DONE; return 0;}
代码不长, 直接全贴了。逻辑很简单, 从done_list取出一个buffer, 并从done_list中删除, 填充到v4l2_buffer中给用户态,
同时删除queued_entry!!!
关键函数是 __vb2_get_done_vb
/** * __vb2_get_done_vb() - get a buffer ready for dequeuing * * Will sleep if required for nonblocking == false. */static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, struct v4l2_buffer *b, int nonblocking){ unsigned long flags; int ret; /* * Wait for at least one buffer to become available on the done_list. */ ret = __vb2_wait_for_done_vb(q, nonblocking); if (ret) return ret; /* * Driver's lock has been held since we last verified that done_list * is not empty, so no need for another list_empty(done_list) check. */ spin_lock_irqsave(&q->done_lock, flags); *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry); /* * Only remove the buffer from done_list if v4l2_buffer can handle all * the planes. */ ret = __verify_planes_array(*vb, b); if (!ret) list_del(&(*vb)->done_entry); spin_unlock_irqrestore(&q->done_lock, flags); return ret;}
可以看到, 从done_list中取出一个数据, 并在done_list中删除该数据。
转载地址:http://jpcrj.baihongyu.com/