高通msm-V4L2-Camera驱动浅析5-buffer
##系列文章
高通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相关推荐
- Android高通平台调试Camera驱动全纪录
项目比较紧,3周内把一个带有外置ISP,MIPI数据通信,800万像素的camera从无驱动到实现客户全部需求. 1日 搭平台,建环境,编译内核,烧写代码. 我是一直在Window下搭个虚拟机登服务器 ...
- android camera(3)--- 高通平台8916 camera移植
物联网即将到来的明天,是各个岗位都需要人才的明天,不是别的不重要,是硬件一样重要,系统开发一样重要,驱动开发一样重要,结构MD一样重要...... 注:此文档以在高通8916平台移植OV5648为例, ...
- 高通MSM平台上的AMSS
AMSS(Advanced Mobile Subscriber Station)的source实际上是QC BREW(Binary Runtime Environment For Wireless)平 ...
- 请把Camera hold住 - Android高通平台调试Camera驱动全纪录
项目比较紧,3周内把一个带有外置ISP,MIPI数据通信,800万像素的camera从无驱动到实现客户全部需求. 1日 搭平台,建环境,编译内核,烧写代码. 我是一直在Window下搭个虚拟机登服务器 ...
- 高通字库芯片GT20L16S1Y驱动 0.96寸 OLED 任意显示中文
连续两个月的加班,给ODM客户生产温控器订单,今天终于顺利发货,对于工程师出身的我,终于可以对着电脑,消停几天,研究技术,分享技术了,闲话少说,直接进入正题: 半年前有个老客户介绍个中央某院的项目,我 ...
- 高通 msm平台GPIO相关的device tree设置
转载自:http://blog.csdn.net/hongzg1982/article/details/47784627 GPIO相关的dvice tree设置和interrupt设置 gpoi号以及 ...
- 红米1S高通版本安装win7驱动
在win7 64位下,红米1S高通版本不能自动安装.原因是高通驱动没有签名,重启系统,按F8,选择最后一个"禁止数字签名验证",就可以安装了. 安装时,手机端"开发者选项 ...
- 高通电源管理qpnp-vm-bms驱动-电量计
1. compatible节点: qpnp-vm-bms.c使用来控制电池曲线的和BMS功能的,其compatible节点是"qcom,qpnp-vm-bms" 2. probe函 ...
- 高通 android平台LCD驱动分析
目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...
- android 副屏驱动_高通 android平台LCD驱动分析
目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...
最新文章
- mac下 VisualBox 虚拟机转移到活动硬盘,出现 UUID 错误
- docker 容器互访三种方式
- ABAP程序设计的一点建议
- java网关限流_网关限流使用
- 聚焦 AI + 大数据全球视野引领行业创新升级
- java web mvc 拆分_JAVA WEB初接触——简单的MVC架构
- ubuntu18 安装redis-manager
- 程序员求职面试三部曲之二:提高面试的成功率
- linux教程 sed命令的用法,Linux基础教程之文件三剑客sed命令用法详解
- 四舍五入算法 php,3种PHP实现四舍五入的方法
- 笔记本--摄像头驱动--找不到相机--浏览器打开相机失败--未检测到摄像头--剩下的标题我就不想了--太长了
- scala 返回值_Scala系列3-Scala函数
- 【图像分割】基于matlab GUI二值化+灰白质医学影像分割【含Matlab源码 184期】
- 十个C语言项目,从小白到月入10K
- css美化滚动条样式,css3美化滚动条样式
- 入门小白不到三个月就学会了用maya软件如何制作动画
- 新浪-InSAR博客网址Blog of Jerome Cheung
- vue-cli在webpack环境下怎样生成开发环境模板(适合初学者)
- 又一次算法作业hhhhhh
- matlab求第二类曲面积分,第二型曲面积分的参数形式计算
热门文章
- html摄像头app,HTML5 Camera(摄像头) 和 Video(视频)控制
- 【win10问题】——打开PPT显示错误“应用程序无法正常启动(0xc0000142)。请单击确定关闭应用程序。”
- 在知识分享的时代,你焦虑个毛啊
- vim配置文件(分享两篇)——vimrc
- Ubuntu 18.04 查看显示器信息
- 《大学“电路分析基础”课程实验合集.实验三》丨基尔霍夫定律的验证
- 最新WordPress主题 阿里百秀XIU v7.7版本
- 【C++静态成员函数不能调用非静态成员变量】
- Java实现LeetCode第32场双周赛(题号5468,5469,5470,5485)
- unity 使用360度全景图