高通msm-V4L2-Camera驱动浅析5-buffer

2021-11-04  本文已影响0人  c枫_撸码的日子

系列文章

高通msm-V4L2-Camera驱动浅析1-初识
高通msm-V4L2-Camera驱动浅析2-框架详解
高通msm-V4L2-Camera驱动浅析3-session
高通msm-V4L2-Camera驱动浅析4-stream
高通msm-V4L2-Camera驱动浅析5-buffer

上一篇文章讲到传输图像的方式:

因此stream(流)的概念就诞生了!
stream又涉及到图像的vb2_buffer,vb2_buffer又由vb2_queue 队列管理

推荐文章

Linux V4L2子系统-videobuf2框架分析(三)

V4L2 videobuffer2的介绍,数据流分析

buffer 类型

缓冲区平面

一、相关结构体

1. vb2_queue

struct vb2_queue {
    unsigned int            type;// //buffer类型
    unsigned int            io_modes;//访问IO的方式:mmap、userptr 、dma
···
    const struct vb2_ops        *ops;//vb2_queue的操作函数集合
    const struct vb2_mem_ops    *mem_ops;//buffer memory操作集合
    const struct vb2_buf_ops    *buf_ops; //buffer的操作函数集合
···
    /* private: internal use only */
···
    unsigned int            memory;//当前使用的存储类型
    struct vb2_buffer       *bufs[VB2_MAX_FRAME];//图像buf(缓冲区)
    unsigned int            num_buffers;//已分配/使用的buf(缓冲区)数目
···
}

2. vb2_queue的操作函数集合:vb2_ops

struct vb2_ops {
//在分配内存之前,由VIDIOC_REQBUFS和VIDIOC_CREATE_BUFS处理程序调用
    int (*queue_setup)(struct vb2_queue *q,
               unsigned int *num_buffers, unsigned int *num_planes,
               unsigned int sizes[], struct device *alloc_devs[]);
//释放调用vb2函数时获得的所有锁;
    void (*wait_prepare)(struct vb2_queue *q);
//重新获取在前一个回调中释放的所有锁;
    void (*wait_finish)(struct vb2_queue *q);

//在分配缓冲区后调用一次(在MMAP情况下)或在获取新的USERPTR缓冲区后调用一次;
    int (*buf_init)(struct vb2_buffer *vb);
//buffer从用户空间入队时调用
    int (*buf_prepare)(struct vb2_buffer *vb);
//buffer出队返回给用户空间时调用
    void (*buf_finish)(struct vb2_buffer *vb);
//释放buffer
    void (*buf_cleanup)(struct vb2_buffer *vb);

//开始视频流
    int (*start_streaming)(struct vb2_queue *q, unsigned int count);
//停止视频流
    void (*stop_streaming)(struct vb2_queue *q);

//将缓冲区vb传递给驱动程序;
    void (*buf_queue)(struct vb2_buffer *vb);
};

3. buffer memory操作集合:vb2_mem_ops

struct vb2_mem_ops {
//MMAP类型所需的操作:。
    void        *(*alloc)(struct device *dev, unsigned long attrs,
                  unsigned long size,
                  enum dma_data_direction dma_dir,
                  gfp_t gfp_flags);//分配视频内存

    void        (*put)(void *buf_priv);//释放视频内存

    unsigned int    (*num_users)(void *buf_priv);//返回内存缓冲区的当前用户数量

    int     (*mmap)(void *buf_priv, struct vm_area_struct *vma);//内核缓冲区映射到用户地址空间

//USERPTR类型需要的操作::
    void        *(*get_userptr)(struct device *dev, unsigned long vaddr,
                    unsigned long size,
                    enum dma_data_direction dma_dir);//获取用户空间内存;
    void        (*put_userptr)(void *buf_priv);//释放用户空间内存;

//DMABUF类型所需的操作:
    struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);
    void        *(*attach_dmabuf)(struct device *dev,
                      struct dma_buf *dbuf,
                      unsigned long size,
                      enum dma_data_direction dma_dir);
    void        (*detach_dmabuf)(void *buf_priv);
    int     (*map_dmabuf)(void *buf_priv);
    void        (*unmap_dmabuf)(void *buf_priv);

//缓存同步
    void        (*prepare)(void *buf_priv);
    void        (*finish)(void *buf_priv);
···

}

4. vb2_buf_ops:buffer的操作函数集合

struct vb2_buf_ops {
//验证给定的用户空间是否包含足够的缓冲区平面。
    int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
//给定一个vb2_buffer填充用户空间结构。对于V4L2,是v4l2_buffer。
    void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
//给定一个用户空间结构,填充vb2_buffer。
    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);
};

5.用户空间的buf:v4l2_buffer

struct v4l2_buffer {
    __u32           index;//buffer 序号
    __u32           type;//buffer类型
    __u32           bytesused;//缓冲区已使用byte数
    __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;//dma内存相关的文件描述符
    } m;
    __u32           length;
    __u32           reserved2;
    __u32           reserved;
};

6. 内核空间对应的buf:vb2_buffer

struct vb2_buffer {
    struct vb2_queue    *vb2_queue;//这个驱动程序所属的队列
    unsigned int        index;//buffer序号
    unsigned int        type;//buffer类型
    unsigned int        memory;//buffer内存
    unsigned int        num_planes;//buffer中的位面数量
    struct vb2_plane    planes[VB2_MAX_PLANES];//位面
    u64         timestamp;//时间戳

    enum vb2_buffer_state   state;//buffer状态

    struct list_head    queued_entry;//表示buf可以入队的链表
    struct list_head    done_entry;//表示buf可以出队的链表

}

二、v4l2_buffer的使用

2.1 buffer是如何申请的?

int32_t mm_stream_request_buf(mm_stream_t * my_obj)
{
    int32_t rc = 0;
    struct v4l2_requestbuffers bufreq;
    uint8_t buf_num = my_obj->total_buf_cnt;

    memset(&bufreq, 0, sizeof(bufreq));
    bufreq.count = buf_num;
    bufreq.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    bufreq.memory = V4L2_MEMORY_USERPTR;
    rc = ioctl(my_obj->fd, VIDIOC_REQBUFS, &bufreq);

    return rc;
}

bufreq.memory = 用户空间缓冲区(V4L2_MEMORY_USERPTR)
这里可以知道,高通使用的是用户空间缓冲区

static int camera_v4l2_reqbufs(struct file *filep, void *fh,
    struct v4l2_requestbuffers *req)
{
···
    ret = vb2_reqbufs(&sp->vb2_q, req);
···
    return ret;
}

申请内存的方式如下:

2.2 buffer是如何入队的?

int32_t mm_stream_qbuf(mm_stream_t *my_obj, mm_camera_buf_def_t *buf)
{
···
    rc = ioctl(my_obj->fd, VIDIOC_QBUF, &buffer);
···
}
static int camera_v4l2_qbuf(struct file *filep, void *fh,
    struct v4l2_buffer *pb)
{
···
    ret = vb2_qbuf(&sp->vb2_q, pb);
···
}

vb2_qbuf最终会调用到vb2_core_qbuf

int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
{
    struct vb2_buffer *vb;
···

    /*
     * 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 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);

    /*
     * 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(1, "qbuf of buffer %d succeeded\n", vb->index);
    return 0;
}

2.3 buffer数据在哪里被填充的?

调用流程如下:

msm_isp_process_axi_irq ->
msm_isp_process_axi_irq_stream ->
msm_isp_process_done_buf ->
msm_isp_buf_done ->
msm_vb2_buf_done ->
vb2_buffer_done
void msm_isp_process_axi_irq_stream(···)
{
    struct msm_isp_buffer *done_buf = NULL;
···
    //填充buf
    done_buf = stream_info->buf[pingpong_bit];
···
    if (stream_info->pending_buf_info.is_buf_done_pending != 1) {
        //处理填充的buf
        msm_isp_process_done_buf(vfe_dev, stream_info,
                done_buf, time_stamp, frame_id);
    }
}

最终调用到vb2_buffer_done

void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{
    //buffer队列
    struct vb2_queue *q = vb->vb2_queue;
···

    /* 同步 buffers */
    for (plane = 0; plane < vb->num_planes; ++plane)
        call_void_memop(vb, finish, vb->planes[plane].mem_priv);

    spin_lock_irqsave(&q->done_lock, flags);
    if (state == VB2_BUF_STATE_QUEUED ||
        state == VB2_BUF_STATE_REQUEUEING) {
        vb->state = VB2_BUF_STATE_QUEUED;
    } else {
        /* Add the buffer to the done buffers list */
        list_add_tail(&vb->done_entry, &q->done_list);
        vb->state = state;//设置buf状态
    }
    atomic_dec(&q->owned_by_drv_count);
    spin_unlock_irqrestore(&q->done_lock, flags);
···
}

2.4 buffer是如何出队的

int32_t mm_stream_read_msm_frame(mm_stream_t * my_obj,
                                 mm_camera_buf_info_t* buf_info,
                                 uint8_t num_planes)
{
    struct v4l2_buffer vb;

    vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    vb.memory = V4L2_MEMORY_USERPTR;
    vb.m.planes = &planes[0];
    vb.length = num_planes;

    rc = ioctl(my_obj->fd, VIDIOC_DQBUF, &vb);
static int camera_v4l2_dqbuf(struct file *filep, void *fh,
    struct v4l2_buffer *pb)
{
···
    ret = vb2_dqbuf(&sp->vb2_q, pb, filep->f_flags & O_NONBLOCK);
···
}

最终调用

int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
           bool nonblocking)
{
    struct vb2_buffer *vb = NULL;
    int ret;
    //从done_list取出一个填充好的buffer
    ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);

···
    //调用buf_finish方法
    call_void_vb_qop(vb, buf_finish, vb);

    /* Fill buffer information for the userspace */
    if (pb)
        call_void_bufop(q, fill_user_buffer, vb, pb);

    /* Remove from videobuf queue */
    list_del(&vb->queued_entry);
    q->queued_count--;

···

    /* go back to dequeued state */
    __vb2_dqbuf(vb);
····
}

__vb2_get_done_vb 将q->done_list 中的vb2_buffer中提出来,
通过fill_user_buffer 将vb2_buffer中的v4l2_buffer信息返回,并将其从q->done_list 中删除。

三、总结

stay hungry stay foolish!

上一篇 下一篇

猜你喜欢

热点阅读