Linux&UVC驱动
+ -

LINUX&UVC驱动将缓冲区加入队列VIDIOC_QBUF

2024-04-02 0 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;
}

0 篇笔记 写笔记

LINUX&UVC驱动将缓冲区加入队列VIDIOC_QBUF
将缓冲区放入队列,这样就可以使用该队列读取数据了 // 将缓冲区放入队列 for (unsigned int i = 0; i < reqbuf.count; i++) { memset(&buf, 0, sizeof(buf)); bu......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!