v4l2应用框架-摄像头v4l2编程(12)_入队VIDIOC_QBUF
参考链接:
(127条消息) 从应用调用vivi驱动分析v4l2 – 缓存放入队列(VIDIOC_QBUF)_dianlong_lee的博客-CSDN博客_vidioc_qbuf
(127条消息) v4l2框架-将缓存放入队列(VIDIOC_QBUF)_深海带鲤鱼的博客-CSDN博客_vidioc_qbuf
(127条消息) 【genius_platform软件平台开发】第六十一讲:Linux系统之V4L2视频驱动-VIDIOC_QBUF入队列_隨意的風的博客-CSDN博客
VIDIOC_QBUF
这里单独说一下:VIDIOC_QBUF和VIDIOC_DQBUF
VIDIOC_QBUF /* 将空闲的内存加入可捕获视频的队列
VIDIOC_DQBUF /* 将已经捕获好视频的内存拉出已捕获视频的队列 *
在uvc_gadget.c里执行:
if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0) {
printf("Unable to requeue buffer: %s (%d).\n", strerror(errno),
errno);
return ret;
}
会调用到内核态。
内核态的执行流程
uvc_gadget.c的VIDIOC_DQBUF和VIDIOC_QBUF对应到内核的驱动接口是:uvc_v4l2_dqbuf和uvc_v4l2_qbuf,
结构体定义在: kernel/msm-4.14/include/media/v4l2-ioctl.h
const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
.vidioc_querycap = uvc_v4l2_querycap,
.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
.vidioc_reqbufs = uvc_v4l2_reqbufs,
.vidioc_querybuf = uvc_v4l2_querybuf,
.vidioc_qbuf = uvc_v4l2_qbuf,
.vidioc_dqbuf = uvc_v4l2_dqbuf,
.vidioc_streamon = uvc_v4l2_streamon,
.vidioc_streamoff = uvc_v4l2_streamoff,
.vidioc_subscribe_event = uvc_v4l2_subscribe_event,
.vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,
.vidioc_default = uvc_v4l2_ioctl_default,
};
- 先看一下uvc_v4l2_qbuf的主要流程,在分析程序。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CkgCRmu9-1661765148902)(vx_images/364890315247323.png =500x)]
VIDIOC_QBUF程序分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fS14lNTW-1661765148903)(vx_images/457761616244106.png =500x)]
uvc_v4l2_qbuf
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
---->uvcg_video_pump
kernel/msm-4.14/drivers/usb/gadget/function/uvc_v4l2.c
static int
uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
>---struct video_device *vdev = video_devdata(file);
>---struct uvc_device *uvc = video_get_drvdata(vdev);
>---struct uvc_video *video = &uvc->video;
>---int ret;
//video->queue是 struct uvc_video_queue queue;
>---ret = uvcg_queue_buffer(&video->queue, b);
>---if (ret < 0)
>--->---return ret;
//uvcg_video_pump的实现在kernel/msm-4.14/drivers/usb/gadget/function/uvc_video.c,以后有时间再看。
>---return uvcg_video_pump(video);
}
uvcg_queue_buffer
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
---->uvcg_video_pump
kernel/msm-4.14/drivers/usb/gadget/function/uvc_queue.c
int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
>---unsigned long flags;
>---int ret;
//queue->queue 是 struct vb2_queue queue;
>---ret = vb2_qbuf(&queue->queue, buf);
>---if (ret < 0)
>--->---return ret;
//自旋锁
>---spin_lock_irqsave(&queue->irqlock, flags);
>---ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
>---queue->flags &= ~UVC_QUEUE_PAUSED;
>---spin_unlock_irqrestore(&queue->irqlock, flags);
>---return ret;
}
vb2_qbuf
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_queue_or_prepare_buf
-------->vb2_core_qbuf
---->uvcg_video_pump
kernel/msm-4.14/drivers/media/v4l2-core/videobuf2-v4l2.c
//q videobuf2 queue
//b buffer structure passed from userspace to VIDIOC_QBUF() handler in driver
int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
int ret;
//判断q->fileio的值,默认为0,这里不重要
if (vb2_fileio_is_active(q)) {
dprintk(1, "file io in progress\n");
return -EBUSY;
}
ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
//ret = 0.调用vb2_core_qbuf
return ret ? ret : vb2_core_qbuf(q, b->index, b);
}
EXPORT_SYMBOL_GPL(vb2_qbuf);
vb2_queue_or_prepare_buf
分析vb2_queue_or_prepare_buf:好像也没干什么,做检查??
// 一些前期的准备检查
static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
const char *opname)
{
if (b->type != q->type) {
dprintk(1, "%s: invalid buffer type\n", opname);
return -EINVAL;
}
索引检查
if (b->index >= q->num_buffers) {
dprintk(1, "%s: buffer index out of range\n", opname);
return -EINVAL;
}
if (q->bufs[b->index] == NULL) {
/* Should never happen */
dprintk(1, "%s: buffer is NULL\n", opname);
return -EINVAL;
}
if (b->memory != q->memory) {
dprintk(1, "%s: invalid memory type\n", opname);
return -EINVAL;
}
// verify that the planes array passed in struct v4l2_buffer from userspace can be safely used
return __verify_planes_array(q->bufs[b->index], b);
}
__verify_planes_array
/**
* __verify_planes_array() - verify that the planes array passed in struct
* v4l2_buffer from userspace can be safely used
*/
static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
if (!V4L2_TYPE_IS_MULTIPLANAR(b->type))
return 0;
/* Is memory for copying plane information present? */
if (b->m.planes == NULL)
{
dprintk(1, "multi-planar buffer passed but planes array not provided\n");
return -EINVAL;
}
if (b->length < vb->num_planes || b->length > VB2_MAX_PLANES)
{
dprintk(1, "incorrect planes array length, expected %d, got %d\n",
vb->num_planes, b->length);
return -EINVAL;
}
return 0;
}
vb2_core_qbuf
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
kernel/msm-4.14/drivers/media/v4l2-core/videobuf2-core.c
//vb2_core_qbuf() - Queue a buffer from userspace
//@q:>->---videobuf2 queue
//@index:>-id number of the buffer
//@pb:>>---buffer structure passed from userspace to vidioc_qbuf handler in driver
//Should be called from vidioc_qbuf ioctl handler of a driver.
//The passed buffer should have been verified.
// 核心处理函数
int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
{
struct vb2_buffer *vb;
int ret;
if (q->error) {
dprintk(1, "fatal error occurred on queue\n");
return -EIO;
}
// 根据索引取出vb
vb = q->bufs[index];
//通过打log发现只有reqbufs的时候状态设置成了VB2_BUF_STATE_DEQUEUED表示在用户空间控制
//所以要分析__buf_prepare这个函数
switch (vb->state) {
case VB2_BUF_STATE_DEQUEUED:
ret = __buf_prepare(vb, pb);
if (ret)
return ret;
break;
case VB2_BUF_STATE_PREPARED: //已经准备好
break;
case VB2_BUF_STATE_PREPARING: //正在准备中
dprintk(1, "buffer still being prepared\n");
return -EINVAL;
default:
dprintk(1, "invalid buffer state %d\n", vb->state);
return -EINVAL;
}
.......
__buf_prepare
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
---------->__buf_prepare
vb->state
分析vb->state
/**
* enum vb2_buffer_state - current video buffer state
* @VB2_BUF_STATE_DEQUEUED: buffer under userspace control
* @VB2_BUF_STATE_PREPARING: buffer is being prepared in videobuf
* @VB2_BUF_STATE_PREPARED: buffer prepared in videobuf and by the driver
* @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver
* @VB2_BUF_STATE_REQUEUEING: re-queue a buffer to the driver
* @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used
* in a hardware operation
* @VB2_BUF_STATE_DONE: buffer returned from driver to videobuf, but
* not yet dequeued to userspace
* @VB2_BUF_STATE_ERROR: same as above, but the operation on the buffer
* has ended with an error, which will be reported
* to the userspace when it is dequeued
*/
enum vb2_buffer_state {
VB2_BUF_STATE_DEQUEUED,
VB2_BUF_STATE_PREPARING,
VB2_BUF_STATE_PREPARED,
VB2_BUF_STATE_QUEUED,
VB2_BUF_STATE_REQUEUEING,
VB2_BUF_STATE_ACTIVE,
VB2_BUF_STATE_DONE,
VB2_BUF_STATE_ERROR,
};
__buf_prepare(vb, pb);
static int __buf_prepare(struct vb2_buffer *vb, const void *pb)
{
struct vb2_queue *q = vb->vb2_queue;
unsigned int plane;
int ret;
if (q->error) {
dprintk(1, "fatal error occurred on queue\n");
return -EIO;
}
//将vb的state改成正在准备
vb->state = VB2_BUF_STATE_PREPARING;
//q->memory = VB2_MEMORY_MMAP 分析__prepare_mmap
switch (q->memory) {
case VB2_MEMORY_MMAP:
ret = __prepare_mmap(vb, pb);
break;
case VB2_MEMORY_USERPTR:
ret = __prepare_userptr(vb, pb);
break;
case VB2_MEMORY_DMABUF:
ret = __prepare_dmabuf(vb, pb);
break;
default:
WARN(1, "Invalid queue type\n");
ret = -EINVAL;
}
.......
//将vb的state改成准备完毕
vb->state = VB2_BUF_STATE_PREPARED;
return 0;
__prepare_mmap
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
---------->__buf_prepare
------------>__prepare_mmap
/**
* __prepare_mmap() - prepare an MMAP buffer
*/
static int __prepare_mmap(struct vb2_buffer *vb, const void *pb)
{
int ret = 0;
//pb用户空间传进来的,所以存在。
if (pb)
/*
* 变换之后 vb->vb2_queue->buf_ops->
* fill_vb2_buffer对应__fill_vb2_buffer
*/
ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
vb, pb, vb->planes);
return ret ? ret : call_vb_qop(vb, buf_prepare, vb);
}
#define call_bufop(q, op, args...) \
({ >->--->---\
int ret = 0; >--->---\
if (q && q->buf_ops && q->buf_ops->op) \
ret = q->buf_ops->op(args); \
ret; >--->--->---\
})
fill_vb2_buffer是一个函数指针,
/**
* struct vb2_buf_ops - driver-specific callbacks
*
* @verify_planes_array: Verify that a given user space structure contains
*>->--->---enough planes for the buffer. This is called
*>->--->---for each dequeued buffer.
* @fill_user_buffer:>--given a vb2_buffer fill in the userspace structure.
*>->--->---For V4L2 this is a struct v4l2_buffer.
* @fill_vb2_buffer:>---given a userspace structure, fill in the vb2_buffer.
*>->--->---If the userspace structure is invalid, then this op
*>->--->---will return an error.
* @copy_timestamp:>copy the timestamp from a userspace structure to
*>->--->---the vb2_buffer struct.
*/
struct vb2_buf_ops {
>---int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
>---void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
>---int (*fill_vb2_buffer)(struct vb2_buffer *vb, const void *pb,
>--->--->--->---struct vb2_plane *planes);
>---void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);
};
指向:
static const struct vb2_buf_ops v4l2_buf_ops = {
.verify_planes_array = __verify_planes_array_core,
.fill_user_buffer = __fill_v4l2_buffer,
.fill_vb2_buffer = __fill_vb2_buffer,
.copy_timestamp = __copy_timestamp,
};
fill_vb2_buffer
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
---------->__buf_prepare
------------>__prepare_mmap
-------------->call_bufop–>fill_vb2_buffer
/**
* __fill_vb2_buffer() - fill a vb2_buffer with information provided in a
* v4l2_buffer by the userspace. It also verifies that struct
* v4l2_buffer has a valid number of planes.
*/
//__fill_vb2_buffer()-用用户空间在v4l2_bufer中提供的信息填充vb2_buffer。
//它还验证结构v4l2_buffer是否具有有效的平面数。
static int __fill_vb2_buffer(struct vb2_buffer *vb,
const void *pb, struct vb2_plane *planes)
{
struct vb2_queue *q = vb->vb2_queue;
const struct v4l2_buffer *b = pb;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
unsigned int plane;
int ret;
ret = __verify_length(vb, b);
if (ret < 0) {
dprintk(1, "plane parameters verification failed: %d\n", ret);
return ret;
}
/*
* 对于capature
* __verify_length 返回0
*/
if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) {
/*
* If the format's field is ALTERNATE, then the buffer's field
* should be either TOP or BOTTOM, not ALTERNATE since that
* makes no sense. The driver has to know whether the
* buffer represents a top or a bottom field in order to
* program any DMA correctly. Using ALTERNATE is wrong, since
* that just says that it is either a top or a bottom field,
* but not which of the two it is.
*/
dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n");
return -EINVAL;
}
vb->timestamp = 0;
vbuf->sequence = 0;
/*
* 也不是多平面视频格式
*/
if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
if (b->memory == VB2_MEMORY_USERPTR) {
for (plane = 0; plane < vb->num_planes; ++plane) {
planes[plane].m.userptr =
b->m.planes[plane].m.userptr;
planes[plane].length =
b->m.planes[plane].length;
planes[plane].data_offset =
b->m.planes[plane].data_offset;
}
}
if (b->memory == VB2_MEMORY_DMABUF) {
for (plane = 0; plane < vb->num_planes; ++plane) {
planes[plane].m.fd =
b->m.planes[plane].m.fd;
planes[plane].length =
b->m.planes[plane].length;
planes[plane].data_offset =
b->m.planes[plane].data_offset;
}
}
/* Fill in driver-provided information for OUTPUT types */
if (V4L2_TYPE_IS_OUTPUT(b->type)) {
/*
* Will have to go up to b->length when API starts
* accepting variable number of planes.
*
* If bytesused == 0 for the output buffer, then fall
* back to the full buffer size. In that case
* userspace clearly never bothered to set it and
* it's a safe assumption that they really meant to
* use the full plane sizes.
*
* Some drivers, e.g. old codec drivers, use bytesused == 0
* as a way to indicate that streaming is finished.
* In that case, the driver should use the
* allow_zero_bytesused flag to keep old userspace
* applications working.
*/
for (plane = 0; plane < vb->num_planes; ++plane) {
struct vb2_plane *pdst = &planes[plane];
struct v4l2_plane *psrc = &b->m.planes[plane];
if (psrc->bytesused == 0)
vb2_warn_zero_bytesused(vb);
if (vb->vb2_queue->allow_zero_bytesused)
pdst->bytesused = psrc->bytesused;
else
pdst->bytesused = psrc->bytesused ?
psrc->bytesused : pdst->length;
pdst->data_offset = psrc->data_offset;
}
}
} else {
/*
* Single-planar buffers do not use planes array,
* so fill in relevant v4l2_buffer struct fields instead.
* In videobuf we use our internal V4l2_planes struct for
* single-planar buffers as well, for simplicity.
*
* If bytesused == 0 for the output buffer, then fall back
* to the full buffer size as that's a sensible default.
*
* Some drivers, e.g. old codec drivers, use bytesused == 0 as
* a way to indicate that streaming is finished. In that case,
* the driver should use the allow_zero_bytesused flag to keep
* old userspace applications working.
*/
if (b->memory == VB2_MEMORY_USERPTR) {
planes[0].m.userptr = b->m.userptr;
planes[0].length = b->length;
}
if (b->memory == VB2_MEMORY_DMABUF) {
planes[0].m.fd = b->m.fd;
planes[0].length = b->length;
}
if (V4L2_TYPE_IS_OUTPUT(b->type)) {
if (b->bytesused == 0)
vb2_warn_zero_bytesused(vb);
if (vb->vb2_queue->allow_zero_bytesused)
planes[0].bytesused = b->bytesused;
else
planes[0].bytesused = b->bytesused ?
b->bytesused : planes[0].length;
} else
//vivi只符合这里,* bytesused设置为0
planes[0].bytesused = 0;
}
/* Zero flags that the vb2 core handles */
vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) {
/*
* Non-COPY timestamps and non-OUTPUT queues will get
* their timestamp and timestamp source flags from the
* queue.
*
* 这里的if条件满足
* 清空V4L2_BUF_FLAG_TSTAMP_SRC_MASK标志
* /
vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
}
if (V4L2_TYPE_IS_OUTPUT(b->type)) {
/*
* For output buffers mask out the timecode flag:
* this will be handled later in vb2_qbuf().
* The 'field' is valid metadata for this output buffer
* and so that needs to be copied here.
*/
vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
vbuf->field = b->field;
} else {
/* Zero any output buffer flags as this is a capture buffer */
vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
}
return 0;
}
call_vb_qop buf_prepare
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
---------->__buf_prepare
------------>__prepare_mmap
-------------->call_bufop–>fill_vb2_buffer
-------------->call_vb_qop–>buf_prepare
回到__prepare_mmap调用buf_prepare
buf_prepare是个函数指针:
struct vb2_ops {
...
>---int (*buf_prepare)(struct vb2_buffer *vb);
...
@buf_prepare:>---called every time the buffer is queued from userspace and from the VIDIOC_PREPARE_BUF() ioctl;
drivers may perform any initialization required before each hardware operation in this callback;
drivers can access/modify the buffer here as it is still synced for the CPU;
drivers that support VIDIOC_CREATE_BUFS() must also validate the buffer size;
if an error is returned, the buffer will not be queued in driver; optional.
@buf_prepare:>---每当buffer从用户空间和VIDIOC_prepara_buf()ioctl排队时调用;
驱动程序可以在此回调中的每个硬件操作之前执行所需的任何初始化;
驱动程序可以在此处访问/修改缓冲区,因为它仍然与CPU同步;
支持VIDIOC_CREATE_BUFS()的驱动程序还必须验证缓冲区大小;
如果返回错误,缓冲区将不会在驱动程序中排队;可选择的
...
};
太他娘的难了 看不明白
回到 __buf_prepare
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
---------->__buf_prepare
......
if (ret) {
dprintk(1, "buffer preparation failed: %d\n", ret);
vb->state = VB2_BUF_STATE_DEQUEUED;
return ret;
}
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
vb->state = VB2_BUF_STATE_PREPARED;
//更改vb state为 VB2_BUF_STATE_PREPARED
//表示buffer是由videobuf和驱动程序准备的缓冲区
return 0;
回到vb2_core_qbuf
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
.......
//Add to the queued buffers list, a buffer will stay on it until dequeued in dqbuf.
//注释写的很明白,buffer将一直在链表上,直到使用dqbuf出队
//queued_count记录了链表上buffer的个数
//现在将状态改为VB2_BUF_STATE_QUEUED 表示 在videobuf中准备了缓冲区,但在驱动程序中未准备
// 将这个 buffer 挂入 q->queued_list
list_add_tail(&vb->queued_entry, &q->queued_list);
q->queued_count++;
q->waiting_for_buffers = false;
vb->state = VB2_BUF_STATE_QUEUED; //buffer queued in videobuf, but not in driver
/*
* copy_timestamp
* 对应 __copy_timestanp
* 只有是输出的时候才会执行,这里分析的是输入,所以忽略
*/
if (pb)
call_void_bufop(q, copy_timestamp, vb, pb);
trace_vb2_qbuf(q, vb);
/*
* start_streaning_called是在start_streaming中设置的
* 这里为什么会加个判断呢?
* 会不会有一种情况,应用1已经打开了video0,在抓图
* 我现在这个是应用2,又将video0打开了进行设置,所以出现了这种情况?
* 这种情况我们也分析一下
*
* 下面分析可以看出
* 如果在这种情况下,新加入的buffer也会被填充数据
* 这里有个疑问,buffer中的数据会被谁拿到呢?
* 猜测应该是当前的应用,因为关联到了offset
* 还有个疑问,当前应该还没有开始抓图,那么当前的应用中填充了数据的buffer
* 这个buffer还会再次填充吗?
*/
/*
* If already streaming, give the buffer to driver for processing.
* If not, the buffer will be given to driver on next streamon.
*/
if (q->start_streaming_called)
__enqueue_in_driver(vb);
.......
list_add_tail
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
---------->list_head
说一下:list_add_tail(&vb->queued_entry, &q->queued_list);
位置:kernel/msm-4.14/include/linux/list.h
struct list_head {
struct list_head *next, *prev;
};
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
>--->--->--- struct list_head *prev,
>--->--->--- struct list_head *next)
{
>---if (!__list_add_valid(new, prev, next))
>--->---return;
>---next->prev = new;
>---new->next = next;
>---new->prev = prev;
>---WRITE_ONCE(prev->next, new);
}
很多地方说:这个函数完成的功能就是添加一个新的结点在head的左边,其实不然,它是从右向左在head->priv和head两个节点之间插入new。
假设刚开始建立链表,只有struct list_head *head,
那么前两句话有用:将next->prev = new; new->next = next;
这就是将new节点添加到head 节点的左边,那么接 下来两句没用: new->prev = prev; prev->next =new;
如果head左边已近有了其他节点,那么调用list_add_tail()函数后,前边两句的功能一样,都是把新的节点添加在head左边,而后两句就是把新节点添加在原来head之前节点(head->priv)右边,这样就串起来了。
那list_add就反过来,把新的节点添加在head和head之后的节点(head->next)之间;
关于list_add和list_add_tail建立栈和FIFO:
list_add和list_add_tail都是在head两边插入新的节点,所以list_add先插入的节点向右移,head->next是最后插入的节点,list_add_tail先插入的节点向左移,head->next是最先插入的节点;
遍历链表都是从head开始向下,所以用list_add建立的链表先访问的是最后插入的节点,类似于栈;list_add_tail建立的链表先访问的是最先插入的节地点,类似于FIFO。
__enqueue_in_driver
说一下:__enqueue_in_driver
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
---------->__enqueue_in_driver
/**
* __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;
/*
* 状态 VB2_BUF_STATE_ACTIVE
* 缓冲区在驱动程序中排队并且可能在硬件操作中使用。
*/
vb->state = VB2_BUF_STATE_ACTIVE;
atomic_inc(&q->owned_by_drv_count);
trace_vb2_buf_queue(q, vb);
//这里是把所有操作该video节点的应用的buffer都传递过去
call_void_vb_qop(vb, buf_queue, vb);
}
buf_queue
太多不知道对应哪个:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EtpHW29z-1661765148904)(vx_images/3031614220867.png =500x)]
看viv分析的这个(原文链接:https://blog.csdn.net/ldl617/article/details/115316965):
static void buffer_queue(struct vb2_buffer *vb)
{
struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
struct vivi_dmaqueue *vidq = &dev->vidq;
unsigned long flags = 0;
dprintk(dev, 1, "%s\n", __func__);
spin_lock_irqsave(&dev->slock, flags);
/*
* vivi驱动中,只是将buf挂载到一个active的链表上
*/
list_add_tail(&buf->list, &vidq->active);
spin_unlock_irqrestore(&dev->slock, flags);
}
static void vivi_thread_tick(struct vivi_dev *dev)
{
struct vivi_dmaqueue *dma_q = &dev->vidq;
struct vivi_buffer *buf;
unsigned long flags = 0;
dprintk(dev, 1, "Thread tick\n");
spin_lock_irqsave(&dev->slock, flags);
if (list_empty(&dma_q->active)) {
dprintk(dev, 1, "No active queue to serve\n");
goto unlock;
}
/*
* vivi有个定时器,会不断的填充模拟的帧数据到buffer
* 可以看到buffer挂载到active链表后,就会被填充数据
*/
buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
list_del(&buf->list);
buf->vb.timestamp = ktime_get_ns();
/* Fill buffer */
vivi_fillbuff(dev, buf);
dprintk(dev, 1, "filled buffer %p\n", buf);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.index);
unlock:
spin_unlock_irqrestore(&dev->slock, flags);
}
回到vb2_core_qbuf
kernel/msm-4.14/drivers/media/v4l2-core/videobuf2-core.c
ioctl(dev->fd, VIDIOC_QBUF, &buf)
–>uvc_v4l2_qbuf
---->uvcg_queue_buffer
------>vb2_qbuf
-------->vb2_core_qbuf
......
/*
* 在之前VIDIOC_QUERYBUF的文章中我们分析过这个
* fill_user_buffer函数
* 对应__fill_user_buffer
* 其中有这样的几行代码
* if (!q->is_output &&
* b->flags & V4L2_BUF_FLAG_DONE &&
* b->flags & V4L2_BUF_FLAG_LAST)
* q->last_buffer_dequeued = true;
* 出现V4L2_BUF_FLAG_DONE这种标志的情况
* 符合上面分析的情况
* 就是其他应用已经打开当前应用操作的节点,并开始抓图了
*/
/* Fill buffer information for the userspace */
if (pb)
call_void_bufop(q, fill_user_buffer, vb, pb);
/*
* 对于这里有些绕
* 首先对于stream_on来说
* 如果能正常抓图 q->streaming = 1 q->start_streaming_called = 1
* 这里q->start_streaming_called = 0
* 这里的代码和上面if(q->start_streaming_called)的情况不会同时出现
* 说明启动的时候,因为某些原因失败了
* 失败的原因有什么?
* 比如下面说的没有足够的缓冲区
* 但是为什么会没有足够的缓冲区呢?应用程序会判断的
* 只有一种情况就是驱动程序不关心申请到的缓冲区小于最小缓冲区要求
* 也就是min_buffers_needed
* 这样返回给应用程序,应用程序判断没有问题,才会往下走,最后执行stream_on
* 为了验证上面的分析
* 追了一下代码ioctl stream_on的时候,这个后面会分析
* vb2_core_streamon中
* 会判断 q->queued_count >= q->min_buffers_needed,
* 如果不符合的话 start_streaming_called = 0
* 但是会将q->streaming = 1
* 于是就出现了下面这种情况
* 这有点像借用buffer
*/
/*
* If streamon has been called, and we haven't yet called
* start_streaming() since not enough buffers were queued, and
* we now have reached the minimum number of queued buffers,
* then we can finally call start_streaming().
*/
// 如果之前是在start_streaming_called 或者streaming状态,则只有当buffer足够了才会执行start streaming
if (q->streaming && !q->start_streaming_called &&
q->queued_count >= q->min_buffers_needed) {
ret = vb2_start_streaming(q);
if (ret)
return ret;
}
dprintk(2, "qbuf of buffer %d succeeded\n", vb->index);
return 0;
}
struct
v4l2_buffer
/**
* struct v4l2_buffer - video buffer info
* @index:>-id number of the buffer
* @type:>--enum v4l2_buf_type; buffer type (type == *_MPLANE for
*>->---multiplanar buffers);
* @bytesused:>-number of bytes occupied by data in the buffer (payload);
*>->---unused (set to 0) for multiplanar buffers
* @flags:>-buffer informational flags
* @field:>-enum v4l2_field; field order of the image in the buffer
* @timestamp:>-frame timestamp
* @timecode:>--frame timecode
* @sequence:>--sequence count of this frame
* @memory:>enum v4l2_memory; the method, in which the actual video data is
*>->---passed
* @offset:>for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP;
*>->---offset from the start of the device memory for this plane,
*>->---(or a "cookie" that should be passed to mmap() as offset)
* @userptr:>---for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR;
*>->---a userspace pointer pointing to this buffer
* @fd:>>---for non-multiplanar buffers with memory == V4L2_MEMORY_DMABUF;
*>->---a userspace file descriptor associated with this buffer
* @planes:>for multiplanar buffers; userspace pointer to the array of plane
*>->---info structs for this buffer
* @length:>size in bytes of the buffer (NOT its payload) for single-plane
*>->---buffers (when type != *_MPLANE); number of elements in the
*>->---planes array for multi-plane buffers
*
* Contains data exchanged by application and driver using one of the Streaming
* I/O methods.
*/
struct v4l2_buffer {
>---__u32>-->--->---index;
>---__u32>-->--->---type;
>---__u32>-->--->---bytesused;
>---__u32>-->--->---flags;
>---__u32>-->--->---field;
>---struct timeval>->---timestamp;
>---struct v4l2_timecode>---timecode;
>---__u32>-->--->---sequence;
>---/* memory location */
>---__u32>-->--->---memory;
>---union {
>--->---__u32 offset;
>--->---unsigned long userptr;
>--->---struct v4l2_plane *planes;
>--->---__s32>-->---fd;
>---} m;
>---__u32>-->--->---length;
>---__u32>-->--->---reserved2;
>---__u32>-->--->---reserved;
};
uvc_device
struct uvc_device {
>---struct video_device vdev;
>---struct v4l2_device v4l2_dev;
>---enum uvc_state state;
>---struct usb_function func;
>---struct uvc_video video;
>---/* Descriptors */
>---struct {
>--->---const struct uvc_descriptor_header * const *fs_control;
>--->---const struct uvc_descriptor_header * const *ss_control;
>--->---const struct uvc_descriptor_header * const *fs_streaming;
>--->---const struct uvc_descriptor_header * const *hs_streaming;
>--->---const struct uvc_descriptor_header * const *ss_streaming;
>---} desc;
>---unsigned int control_intf;
>---struct usb_ep *control_ep;
>---struct usb_request *control_req;
>---void *control_buf;
>---unsigned int streaming_intf;
>---/* Events */
>---unsigned int event_length;
>---unsigned int event_setup_out : 1;
};
uvc_video
struct uvc_video {
>---struct usb_ep *ep;
>---/* Frame parameters */
>---u8 bpp;
>---u32 fcc;
>---unsigned int width;
>---unsigned int height;
>---unsigned int imagesize;
>---struct mutex mutex;>/* protects frame parameters */
>---/* Requests */
>---unsigned int req_size;
>---struct usb_request *req[UVC_NUM_REQUESTS];
>---__u8 *req_buffer[UVC_NUM_REQUESTS];
>---struct list_head req_free;
>---spinlock_t req_lock;
>---void (*encode) (struct usb_request *req, struct uvc_video *video,
>--->--->---struct uvc_buffer *buf);
>---/* Context data used by the completion handler */
>---__u32 payload_size;
>---__u32 max_payload_size;
>---struct uvc_video_queue queue;
>---unsigned int fid;
};
uvc_video_queue
struct uvc_video_queue {
>---struct vb2_queue queue;
>---unsigned int flags;
>---__u32 sequence;
>---unsigned int buf_used;
>---spinlock_t irqlock;>/* Protects flags and irqqueue */
>---struct list_head irqqueue;
};
vb2_queue
/**
* struct vb2_queue - a videobuf queue
*
* @type:>--private buffer type whose content is defined by the vb2-core
*>->---caller. For example, for V4L2, it should match
*>->---the types defined on enum &v4l2_buf_type
* @io_modes:>--supported io methods (see vb2_io_modes enum)
* @dev:>---device to use for the default allocation context if the driver
*>->---doesn't fill in the @alloc_devs array.
* @dma_attrs:>-DMA attributes to use for the DMA.
* @bidirectional: when this flag is set the DMA direction for the buffers of
*>->---this queue will be overridden with DMA_BIDIRECTIONAL direction.
*>->---This is useful in cases where the hardware (firmware) writes to
*>->---a buffer which is mapped as read (DMA_TO_DEVICE), or reads from
*>->---buffer which is mapped for write (DMA_FROM_DEVICE) in order
*>->---to satisfy some internal hardware restrictions or adds a padding
*>->---needed by the processing algorithm. In case the DMA mapping is
*>->---not bidirectional but the hardware (firmware) trying to access
*>->---the buffer (in the opposite direction) this could lead to an
*>->---IOMMU protection faults.
* @fileio_read_once:>-->---report EOF after reading the first buffer
* @fileio_write_immediately:>--queue buffer after each write() call
* @allow_zero_bytesused:>--allow bytesused == 0 to be passed to the driver
* @quirk_poll_must_check_waiting_for_buffers: Return POLLERR at poll when QBUF
* has not been called. This is a vb1 idiom that has been adopted
* also by vb2.
* @lock:>--pointer to a mutex that protects the vb2_queue struct. The
*>->---driver can set this to a mutex to let the v4l2 core serialize
*>->---the queuing ioctls. If the driver wants to handle locking
*>->---itself, then this should be set to NULL. This lock is not used
*>->---by the videobuf2 core API.
* @owner:>-The filehandle that 'owns' the buffers, i.e. the filehandle
*>->---that called reqbufs, create_buffers or started fileio.
*>->---This field is not used by the videobuf2 core API, but it allows
*>->---drivers to easily associate an owner filehandle with the queue.
* @ops:>---driver-specific callbacks
* @mem_ops:>---memory allocator specific callbacks
* @buf_ops:>---callbacks to deliver buffer information
*>->---between user-space and kernel-space
* @drv_priv:>--driver private data
* @buf_struct_size: size of the driver-specific buffer structure;
*>->---"0" indicates the driver doesn't want to use a custom buffer
*>->---structure type. for example, sizeof(struct vb2_v4l2_buffer)
*>->---will be used for v4l2.
* @timestamp_flags: Timestamp flags; V4L2_BUF_FLAG_TIMESTAMP_* and
*>->---V4L2_BUF_FLAG_TSTAMP_SRC_*
* @gfp_flags:>-additional gfp flags used when allocating the buffers.
*>->---Typically this is 0, but it may be e.g. GFP_DMA or __GFP_DMA32
*>->---to force the buffer allocation to a specific memory zone.
* @min_buffers_needed: the minimum number of buffers needed before
*>->---@start_streaming can be called. Used when a DMA engine
*>->---cannot be started unless at least this number of buffers
*>->---have been queued into the driver.
*/
/*
* Private elements (won't appear at the uAPI book):
* @mmap_lock:>-private mutex used when buffers are allocated/freed/mmapped
* @memory:>current memory type used
* @dma_dir:>---DMA mapping direction.
* @bufs:>--videobuf buffer structures
* @num_buffers: number of allocated/used buffers
* @queued_list: list of buffers currently queued from userspace
* @queued_count: number of buffers queued and ready for streaming.
* @owned_by_drv_count: number of buffers owned by the driver
* @done_list:>-list of buffers ready to be dequeued to userspace
* @done_lock:>-lock to protect done_list list
* @done_wq:>---waitqueue for processes waiting for buffers ready to be dequeued
* @alloc_devs:>memory type/allocator-specific per-plane device
* @streaming:>-current streaming state
* @start_streaming_called: @start_streaming was called successfully and we
*>->---started streaming.
* @error:>-a fatal error occurred on the queue
* @waiting_for_buffers: used in poll() to check if vb2 is still waiting for
*>->---buffers. Only set for capture queues if qbuf has not yet been
*>->---called since poll() needs to return POLLERR in that situation.
* @is_multiplanar: set if buffer type is multiplanar
* @is_output:>-set if buffer type is output
* @copy_timestamp: set if vb2-core should set timestamps
* @last_buffer_dequeued: used in poll() and DQBUF to immediately return if the
*>->---last decoded buffer was already dequeued. Set for capture queues
*>->---when a buffer with the V4L2_BUF_FLAG_LAST is dequeued.
* @fileio:>file io emulator internal data, used only if emulator is active
* @threadio:>--thread io internal data, used only if thread is active
*/
struct vb2_queue {
>---unsigned int>--->--->---type;
>---unsigned int>--->--->---io_modes;
>---struct device>-->--->---*dev;
>---unsigned long>-->--->---dma_attrs;
>---unsigned>--->--->---bidirectional:1;
>---unsigned>--->--->---fileio_read_once:1;
>---unsigned>--->--->---fileio_write_immediately:1;
>---unsigned>--->--->---allow_zero_bytesused:1;
>---unsigned>--->--- quirk_poll_must_check_waiting_for_buffers:1;
>---struct mutex>--->--->---*lock;
>---void>--->--->--->---*owner;
>---const struct vb2_ops>--->---*ops;
>---const struct vb2_mem_ops>---*mem_ops;
>---const struct vb2_buf_ops>---*buf_ops;
>---void>--->--->--->---*drv_priv;
>---unsigned int>--->--->---buf_struct_size;
>---u32>>--->--->---timestamp_flags;
>---gfp_t>-->--->--->---gfp_flags;
>---u32>>--->--->---min_buffers_needed;
>---/* private: internal use only */
>---struct mutex>--->--->---mmap_lock;
>---unsigned int>--->--->---memory;
>---enum dma_data_direction>>---dma_dir;
>---struct vb2_buffer>-->---*bufs[VB2_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;
>---struct device>-->--->---*alloc_devs[VB2_MAX_PLANES];
>---unsigned int>--->--->---streaming:1;
>---unsigned int>--->--->---start_streaming_called:1;
>---unsigned int>--->--->---error:1;
>---unsigned int>--->--->---waiting_for_buffers:1;
>---unsigned int>--->--->---is_multiplanar:1;
>---unsigned int>--->--->---is_output:1;
>---unsigned int>--->--->---copy_timestamp:1;
>---unsigned int>--->--->---last_buffer_dequeued:1;
>---struct vb2_fileio_data>->---*fileio;
>---struct vb2_threadio_data>---*threadio;
#ifdef CONFIG_VIDEO_ADV_DEBUG
>---/*
>--- * Counters for how often these queue-related ops are
>--- * called. Used to check for unbalanced ops.
>--- */
>---u32>>--->--->---cnt_queue_setup;
>---u32>>--->--->---cnt_wait_prepare;
>---u32>>--->--->---cnt_wait_finish;
>---u32>>--->--->---cnt_start_streaming;
>---u32>>--->--->---cnt_stop_streaming;
#endif
};
vb2_plane
/**
* struct vb2_plane - plane information
* @mem_priv:>--private data with this plane
* @dbuf:>--dma_buf - shared buffer object
* @dbuf_mapped:>---flag to show whether dbuf is mapped or not
* @bytesused:>-number of bytes occupied by data in the plane (payload)
* @length:>size of this plane (NOT the payload) in bytes
* @min_length:>minimum required size of this plane (NOT the payload) in bytes.
*>->---@length is always greater or equal to @min_length.
* @offset:>when memory in the associated struct vb2_buffer is
*>->---VB2_MEMORY_MMAP, equals the offset from the start of
*>->---the device memory for this plane (or is a "cookie" that
*>->---should be passed to mmap() called on the video node)
* @userptr:>---when memory is VB2_MEMORY_USERPTR, a userspace pointer
*>->---pointing to this plane
* @fd:>>---when memory is VB2_MEMORY_DMABUF, a userspace file
*>->---descriptor associated with this plane
* @m:>->---Union with memtype-specific data (@offset, @userptr or
*>->---@fd).
* @data_offset:>---offset in the plane to the start of data; usually 0,
*>->---unless there is a header in front of the data
* Should contain enough information to be able to cover all the fields
* of struct v4l2_plane at videodev2.h
*/
struct vb2_plane {
>---void>--->--->---*mem_priv;
>---struct dma_buf>->---*dbuf;
>---unsigned int>--->---dbuf_mapped;
>---unsigned int>--->---bytesused;
>---unsigned int>--->---length;
>---unsigned int>--->---min_length;
>---union {
>--->---unsigned int>---offset;
>--->---unsigned long>--userptr;
>--->---int>>---fd;
>---} m;
>---unsigned int>--->---data_offset;
};
vb2_buffer_state (vb->state)
/**
* enum vb2_buffer_state - current video buffer state
* @VB2_BUF_STATE_DEQUEUED: buffer under userspace control
* @VB2_BUF_STATE_PREPARING: buffer is being prepared in videobuf
* @VB2_BUF_STATE_PREPARED: buffer prepared in videobuf and by the driver
* @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver
* @VB2_BUF_STATE_REQUEUEING: re-queue a buffer to the driver
* @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used
* in a hardware operation
* @VB2_BUF_STATE_DONE: buffer returned from driver to videobuf, but
* not yet dequeued to userspace
* @VB2_BUF_STATE_ERROR: same as above, but the operation on the buffer
* has ended with an error, which will be reported
* to the userspace when it is dequeueds
*/
enum vb2_buffer_state {
VB2_BUF_STATE_DEQUEUED,
VB2_BUF_STATE_PREPARING,
VB2_BUF_STATE_PREPARED,
VB2_BUF_STATE_QUEUED,
VB2_BUF_STATE_REQUEUEING,
VB2_BUF_STATE_ACTIVE,
VB2_BUF_STATE_DONE,
VB2_BUF_STATE_ERROR,
};