##系列文章
高通msm-V4L2-Camera驱动浅析1-初识
高通msm-V4L2-Camera驱动浅析2-框架详解
高通msm-V4L2-Camera驱动浅析3-session

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

  • 方法1:通过【帧IO】访问方式
    使用read和write的方式,通过read读取每一帧数据,数据需要在内核和用户之间拷贝,这种方式访问速度会非常慢。

  • 方法2:通过【流IO】访问方式:

    • 内存映射缓冲区(V4L2_MEMORY_MMAP):在内核空间开辟缓冲区,应用通过mmap()系统调用映射到用户地址空间
    • 用户空间缓冲区(V4L2_MEMORY_USERPTR):在用户空间的应用中开辟缓冲区,用户与内核空间之间交换缓冲区指针。

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

推荐文章

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

V4L2 videobuffer2的介绍,数据流分析

buffer 类型

  • V4L2_BUF_TYPE_VIDEO_CAPTURE = 1,指定buf的类型为capture,用于视频捕获设备(单平面)
  • V4L2_BUF_TYPE_VIDEO_OUTPUT = 2,指定buf的类型output,用于视频输出设备
  • V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,指定buf的类型为overlay,用于overlay设备
  • V4L2_BUF_TYPE_VBI_CAPTURE = 4,用于vbi捕获设备
  • V4L2_BUF_TYPE_VBI_OUTPUT = 5,用于vbi输出设备
  • V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,用于切片vbi捕获设备
  • V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7,用于切片vbi输出设备
  • V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,用于视频输出overlay设备
  • V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,指定buf的类型为capture,用于视频捕获设备(多平面)

缓存区平面

一、相关结构体

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);
//释放buffervoid (*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);
};
  • buf_queue:
    将buffer传递给驱动程序;驱动程序可能在这个缓冲区上启动硬件操作;
    驱动程序应该通过调用vb2_buffer_done()函数来返回缓冲区;
    它总是在调用STREAMON IOCTL之后被调用;
    如果用户在调用STREAMON之前预排队缓冲区,则可能在start_streaming回调函数之前调用

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);
···}
  • USERPTR类型需要的操作:get_userptr, put_userptr。
  • MMAP类型所需的操作:alloc, put, num_users, MMAP。
  • 读写访问类型需要的操作:alloc, put, num_users, vaddr。
  • DMABUF类型所需的操作:attach_dmabuf, detach_dmabuf,map_dmabuf, unmap_dmabuf。

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);
};
  • 调用fill_user_buffer进行buffer数据填充

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;
};
  • 当 type = V4L2_MEMORY_MMAP方式
    m.offset是内核空间图像数据存放的开始地址,
    通过mmap映射返回一个缓冲区指针p,p+byteused是图像数据在进程的虚拟地址空间所占区域
  • 当 type = V4L2_MEMORY_USERPTR
    图像数据开始地址的指针m.userptr。
    userptr是一个用户空间的指针,userptr+byteused便是所占的虚拟地址空间,应用可以直接访问。

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是如何申请的?

  • 用户空间:通过VIDIOC_REQBUFS
    hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c
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)
这里可以知道,高通使用的是用户空间缓冲区

  • 内核空间
    kernel/msm-4.9/drivers/media/platform/msm/camera_v2/camera/camera.c
static int camera_v4l2_reqbufs(struct file *filep, void *fh,struct v4l2_requestbuffers *req)
{···ret = vb2_reqbufs(&sp->vb2_q, req);
···return ret;
}

申请内存的方式如下:

  • a. USERPTR:此种方式由用户态去申请空间(比如用vmalloc,申请的大小可能有限).在VIDIOC_QBUF时会将用户态信息v4l2_buffer转换为内核态下vb2_buffer信息
  • b. DMA的方式,也是由用户态去申请空间
  • c. MMAP方式: 此方式由内核态去申请

2.2 buffer是如何入队的?

  • 用户空间:通过VIDIOC_QBUF
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;
}
  • 1.将buffer添加到队列里面,并且设置buffer状态为:VB2_BUF_STATE_QUEUED
    list_add_tail(&vb->queued_entry, &q->queued_list);
    vb->state = VB2_BUF_STATE_QUEUED;
    入队的意思:把用户缓冲区送给kernel使用

  • 2.如果已经开启流,则将buf交给驱动程序进行处理
    __enqueue_in_driver(vb);
    该函数会调用buf_queue将buf交给驱动程序进行处理

  • 3.把buffer信息填充到用户空间
    .fill_user_buffer = __fill_v4l2_buffer,

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

  • buffe是在ISP填充这个数据的,填充好了之后,会发送一个中断信号。
  • 中断处理程序msm_isp_process_axi_irq设置当前buf的状态是填充好的,最终把buf添加到done buffers队列里

调用流程如下:

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;
···//填充bufdone_buf = stream_info->buf[pingpong_bit];
···if (stream_info->pending_buf_info.is_buf_done_pending != 1) {//处理填充的bufmsm_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);
···
}
  • 将缓冲区添加到done buffers链表中
    list_add_tail(&vb->done_entry, &q->done_list);
  • 设置buf状态:VB2_BUF_STATE_DONE
    vb->state = state;

2.4 buffer是如何出队的

  • 用户空间:VIDIOC_DQBUF
    hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c
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取出一个填充好的bufferret = __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 中删除。

三、总结

  • 应用层和kernel层共同操作一个buffer queue。

  • 出队:应用层通过VIDIOC_DQBUF从buffer队列获取填充好的数据
    (此时如果队列内存在有效数据,那么kernel会返回,否则kernel阻塞,直到有效数据出现)

  • 入队:使用完后,再把应用层再通过VIDIOC_QBUF将buffer返回给kernel,就是入队,供kernel使用。

  • kernel 向应用层提供了两个接口:一个QUEUE_BUF,一个DEQUE_BUF*

  • kernel会记录buffer队列中哪一个包含有效数据,如果应用层DEQUEUE一个buffer,那么kernel会判断这个buffer是否已经填充了camera数据,如果是则返回,不是则阻塞。

  • kernel在收到camera数据传输完毕的中断后,会把队列中的一个buffer置为可用,同时唤醒阻塞在buffer queue上的进程。

  • kernel和应用的关系,就是生产者和消费者关系,kernel负责填充camera数据,app负责消费camera数据

stay hungry stay foolish!

高通msm-V4L2-Camera驱动浅析5-buffer相关推荐

  1. Android高通平台调试Camera驱动全纪录

    项目比较紧,3周内把一个带有外置ISP,MIPI数据通信,800万像素的camera从无驱动到实现客户全部需求. 1日 搭平台,建环境,编译内核,烧写代码. 我是一直在Window下搭个虚拟机登服务器 ...

  2. android camera(3)--- 高通平台8916 camera移植

    物联网即将到来的明天,是各个岗位都需要人才的明天,不是别的不重要,是硬件一样重要,系统开发一样重要,驱动开发一样重要,结构MD一样重要...... 注:此文档以在高通8916平台移植OV5648为例, ...

  3. 高通MSM平台上的AMSS

    AMSS(Advanced Mobile Subscriber Station)的source实际上是QC BREW(Binary Runtime Environment For Wireless)平 ...

  4. 请把Camera hold住 - Android高通平台调试Camera驱动全纪录

    项目比较紧,3周内把一个带有外置ISP,MIPI数据通信,800万像素的camera从无驱动到实现客户全部需求. 1日 搭平台,建环境,编译内核,烧写代码. 我是一直在Window下搭个虚拟机登服务器 ...

  5. 高通字库芯片GT20L16S1Y驱动 0.96寸 OLED 任意显示中文

    连续两个月的加班,给ODM客户生产温控器订单,今天终于顺利发货,对于工程师出身的我,终于可以对着电脑,消停几天,研究技术,分享技术了,闲话少说,直接进入正题: 半年前有个老客户介绍个中央某院的项目,我 ...

  6. 高通 msm平台GPIO相关的device tree设置

    转载自:http://blog.csdn.net/hongzg1982/article/details/47784627 GPIO相关的dvice tree设置和interrupt设置 gpoi号以及 ...

  7. 红米1S高通版本安装win7驱动

    在win7 64位下,红米1S高通版本不能自动安装.原因是高通驱动没有签名,重启系统,按F8,选择最后一个"禁止数字签名验证",就可以安装了. 安装时,手机端"开发者选项 ...

  8. 高通电源管理qpnp-vm-bms驱动-电量计

    1. compatible节点: qpnp-vm-bms.c使用来控制电池曲线的和BMS功能的,其compatible节点是"qcom,qpnp-vm-bms" 2. probe函 ...

  9. 高通 android平台LCD驱动分析

    目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...

  10. android 副屏驱动_高通 android平台LCD驱动分析

    目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...

最新文章

  1. mac下 VisualBox 虚拟机转移到活动硬盘,出现 UUID 错误
  2. docker 容器互访三种方式
  3. ABAP程序设计的一点建议
  4. java网关限流_网关限流使用
  5. 聚焦 AI + 大数据全球视野引领行业创新升级
  6. java web mvc 拆分_JAVA WEB初接触——简单的MVC架构
  7. ubuntu18 安装redis-manager
  8. 程序员求职面试三部曲之二:提高面试的成功率
  9. linux教程 sed命令的用法,Linux基础教程之文件三剑客sed命令用法详解
  10. 四舍五入算法 php,3种PHP实现四舍五入的方法
  11. 笔记本--摄像头驱动--找不到相机--浏览器打开相机失败--未检测到摄像头--剩下的标题我就不想了--太长了
  12. scala 返回值_Scala系列3-Scala函数
  13. 【图像分割】基于matlab GUI二值化+灰白质医学影像分割【含Matlab源码 184期】
  14. 十个C语言项目,从小白到月入10K
  15. css美化滚动条样式,css3美化滚动条样式
  16. 入门小白不到三个月就学会了用maya软件如何制作动画
  17. 新浪-InSAR博客网址Blog of Jerome Cheung
  18. vue-cli在webpack环境下怎样生成开发环境模板(适合初学者)
  19. 又一次算法作业hhhhhh
  20. matlab求第二类曲面积分,第二型曲面积分的参数形式计算

热门文章

  1. html摄像头app,HTML5 Camera(摄像头) 和 Video(视频)控制
  2. 【win10问题】——打开PPT显示错误“应用程序无法正常启动(0xc0000142)。请单击确定关闭应用程序。”
  3. 在知识分享的时代,你焦虑个毛啊
  4. vim配置文件(分享两篇)——vimrc
  5. Ubuntu 18.04 查看显示器信息
  6. 《大学“电路分析基础”课程实验合集.实验三》丨基尔霍夫定律的验证
  7. 最新WordPress主题 阿里百秀XIU v7.7版本
  8. 【C++静态成员函数不能调用非静态成员变量】
  9. Java实现LeetCode第32场双周赛(题号5468,5469,5470,5485)
  10. unity 使用360度全景图