LINUX&UVC驱动将缓冲区加入队列VIDIOC_QBUF
2024-04-02
108
0
将缓冲区放入队列,这样就可以使用该队列读取数据了
// 将缓冲区放入队列
for (unsigned int i = 0; i < reqbuf.count; i++) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
perror("Failed to queue buffer");
close(fd);
return EXIT_FAILURE;
}
}
VIDIOC_QBUF对应的内核回调是vidioc_qbuf
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_qbuf = uvc_ioctl_qbuf,
}
static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
if (!uvc_has_privileges(handle))
return -EBUSY;
return uvc_queue_buffer(&stream->queue, stream->vdev.v4l2_dev->mdev, buf);
}
vb2_qbuf
int uvc_queue_buffer(struct uvc_video_queue *queue,
struct media_device *mdev, struct v4l2_buffer *buf)
{
int ret;
mutex_lock(&queue->mutex);
ret = vb2_qbuf(&queue->queue, mdev, buf);
mutex_unlock(&queue->mutex);
return ret;
}
vb2_qbuf:
int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
struct v4l2_buffer *b)
{
struct media_request *req = NULL;
int ret;
if (vb2_fileio_is_active(q)) {
dprintk(1, "file io in progress\n");
return -EBUSY;
}
ret = vb2_queue_or_prepare_buf(q, mdev, b, false, &req);
if (ret)
return ret;
ret = vb2_core_qbuf(q, b->index, b, req);
if (req)
media_request_put(req);
return ret;
}
EXPORT_SYMBOL_GPL(vb2_qbuf);
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;
}
// memory类型检查
if (b->memory != q->memory) {
dprintk(1, "%s: invalid memory type\n", opname);
return -EINVAL;
}
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函数
// 核心处理函数
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];
switch (vb->state)
{
/*
前面的所有分析中只有reqbufs的时候
* 状态设置成了VB2_BUF_STATE_DEQUEUED
* 表示在用户空间控制
* 所以要分析__buf_prepare这个函数
* */
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;
}
// 添加到队列缓冲区buffer list中,数据帧将一直保留在该list队列中,直到在dqbuf中退出队列
// 将这个 buffer 挂入 q->queued_list
/*
* Add to the queued buffers list, a buffer will stay on it until
* dequeued in dqbuf.
*/
list_add_tail(&vb->queued_entry, &q->queued_list);
q->queued_count++;
q->waiting_for_buffers = false;
vb->state = VB2_BUF_STATE_QUEUED;
if (pb)
call_void_bufop(q, copy_timestamp, vb, pb);
trace_vb2_qbuf(q, vb);
/*
* 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);
/* Fill buffer information for the userspace */
if (pb)
call_void_bufop(q, fill_user_buffer, vb, pb);
// 如果之前是在start_streaming_called 或者streaming状态,则只有当buffer足够了才会执行start streaming
/*
* 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().
*/
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;
}
EXPORT_SYMBOL_GPL(vb2_core_qbuf);
buf_prepare函数
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;
}
// 设置state=VB2_BUF_STATE_PREPARING
vb->state = VB2_BUF_STATE_PREPARING;
switch (q->memory) {
/*
* 驱动会执行MMAP
*/
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;
}
...
prepare_mmap函数
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
* call_bufop 对应__fill_vb2_buffer
*/
ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
vb, pb, vb->planes);
/*
* vb->vb2_queue->ops->buf_prepare
* 就是buf_prepare
*/
return ret ? ret : call_vb_qop(vb, buf_prepare, vb);
}
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.
*/
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;
// 对于capature length=0
ret = __verify_length(vb, b);
if (ret < 0) {
dprintk(1, "plane parameters verification failed: %d\n", ret);
return ret;
}
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;
}
}
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;
}
}
/* 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;
}
// dma方式
if (b->memory == VB2_MEMORY_DMABUF) {
planes[0].m.fd = b->m.fd;
planes[0].length = b->length;
}
/*
*enum v4l2_buf_type {
V4L2_BUF_TYPE_VIDEO_CAPTURE = 1,
V4L2_BUF_TYPE_VIDEO_OUTPUT = 2,
V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,
V4L2_BUF_TYPE_VBI_CAPTURE = 4,
V4L2_BUF_TYPE_VBI_OUTPUT = 5,
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,
V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7,
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,
V4L2_BUF_TYPE_SDR_CAPTURE = 11,
V4L2_BUF_TYPE_SDR_OUTPUT = 12,
V4L2_BUF_TYPE_META_CAPTURE = 13,
V4L2_BUF_TYPE_PRIVATE = 0x80,
};
#define V4L2_TYPE_IS_MULTIPLANAR(type) \
((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE \
|| (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
#define V4L2_TYPE_IS_OUTPUT(type) \
((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT \
|| (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE \
|| (type) == V4L2_BUF_TYPE_VIDEO_OVERLAY \
|| (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY \
|| (type) == V4L2_BUF_TYPE_VBI_OUTPUT \
|| (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT \
|| (type) == V4L2_BUF_TYPE_SDR_OUTPUT)
*/
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
{
// 所以V4L2_BUF_TYPE_VIDEO_CAPTURE会走到这里
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.
*/
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;
}
buffer_prepare函数
static int buffer_prepare(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);
unsigned long size;
BUG_ON(NULL == dev->fmt);
// 检查wh等数值
/*
* Theses properties only change when queue is idle, see s_fmt.
* The below checks should not be performed here, on each
* buffer_prepare (i.e. on each qbuf). Most of the code in this function
* should thus be moved to buffer_init and s_fmt.
*/
if (dev->width < 48 || dev->width > MAX_WIDTH ||
dev->height < 32 || dev->height > MAX_HEIGHT)
return -EINVAL;
size = dev->width * dev->height * 2;
/*
*vb->planes[i].length == size
* 这是之前reqbufs时候设置的
*/
if (vb2_plane_size(vb, 0) < size) {
dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
__func__, vb2_plane_size(vb, 0), size);
return -EINVAL;
}
/*
* vb2_set_plane_payload-> vb->planes[0].bytesused = size;
* 终于找到bytesused设置的地方了
* 这里只设置planes[0],而不考虑planes[1],是单平面的视频格式
*/
vb2_set_plane_payload(&buf->vb, 0, size);
buf->fmt = dev->fmt;
precalculate_bars(dev);
precalculate_line(dev);
return 0;
}
vb2_set_plane_payload函数
/**
* vb2_set_plane_payload() - set bytesused for the plane plane_no
* @vb: buffer for which plane payload should be set
* @plane_no: plane number for which payload should be set
* @size: payload in bytes
*/
static inline void vb2_set_plane_payload(struct vb2_buffer *vb,
unsigned int plane_no, unsigned long size)
{
if (plane_no < vb->num_planes)
vb->planes[plane_no].bytesused = size;
}