IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 【genius_platform软件平台开发】第六十讲:Linux系统之V4L2视频驱动-mmap映射 -> 正文阅读

[系统运维]【genius_platform软件平台开发】第六十讲:Linux系统之V4L2视频驱动-mmap映射

1. 概述

详细讲解请参考这篇博客:【genius_platform软件平台开发】第五十一讲:Linux系统内存映射mmap原理
主要就是内核内存区域的迎神,方便用户空间操控该内存,获取数据。

2. 应用层

// 为这个结构体变量分配内存,这个结构体主要的目的保存的是每一个缓冲帧的地址和大小
    // 将多个(m_nBuffersCount)已申请到的缓冲帧映射到应用程序空间,用m_pBuffers指针记录
    // 获取每个缓存的信息,并mmap到用户空间
    m_pBuffers = (StuV4l2FrameBuffer *)calloc(info.count, sizeof(StuV4l2FrameBuffer));
    CHECKI(m_pBuffers);
    LOGMSG("CV4l2CaptureIr::initMmap calloc m_pBuffers=[%p] size=[%u]", m_pBuffers, info.count * sizeof(StuV4l2FrameBuffer));
    // 映射所有的缓存
    for (int nIdx = 0; nIdx < m_nBuffersCount; ++nIdx)
    {
        /*
        * struct v4l2_buffer
        * {
        *    __u32 index;                    // buffer 序号
        *    enum v4l2_buf_type type;        // buffer 类型
        *    __u32 bytesused;                // buffer 中已使用的字节数
        *    __u32 flags;                    // 区分是MMAP 还是USERPTR
        *    enum v4l2_field field;
        *    struct timeval timestamp;       // 获取第一个字节时的系统时间
        *    struct v4l2_timecode timecode;
        *     __u32 sequence;                // 队列中的序号
        *     enum v4l2_memory memory;        // IO 方式,被应用程序设置
        *     union m
        *     {
        *        __u32 offset;               // 缓冲帧地址,只对MMAP 有效
        *        unsigned long userptr;
        *     };
        *     __u32 length;                  // 缓冲帧长度
        *     __u32 input;
        *     __u32 reserved;
        *  };
        * 原文链接:https://blog.csdn.net/u010661782/article/details/49020695
        */
        struct v4l2_buffer info;
        MEMSET(info);
        info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        info.memory = V4L2_MEMORY_MMAP;
        info.index = nIdx;

        // 将申请的内核缓冲区放入视频采集输入队列中排队
        if (-1 == this->ioCtrl(m_nFd, VIDIOC_QBUF, &info))
        {
            LOGERROR("CV4l2CaptureIr::initMmap is error... VIDIOC_QBUF");
            return ReturnCode_Error;
        }

        // 查询帧缓冲区在内核空间中的长度和偏移量
        // 查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小
        // 获取到对应index的缓存信息,此处主要利用length信息及offset信息来完成后面的mmap操作
        // 查询申请到的缓冲区的信息
        if (-1 == this->ioCtrl(m_nFd, VIDIOC_QUERYBUF, &info))
        {
            LOGERROR("CV4l2CaptureIr::initMmap is error... DALI_IR_SENSOR_DEV_NAME=[%s] VIDIOC_QUERYBUF", DALI_IR_SENSOR_DEV_NAME);
            return ReturnCode_Error;
        }

        // 转换成相对地址
        m_pBuffers[nIdx].unLength = info.length;
        /*
         * 将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取 / 处理视频数据;
         * void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
         *     addr 映射起始地址,一般为NULL ,让内核自动选择
         *     length 被映射内存块的长度
         *     prot 标志映射后能否被读写,其值为PROT_EXEC,PROT_READ,PROT_WRITE, PROT_NONE
         *     flags 确定此内存映射能否被其他进程共享,MAP_SHARED,MAP_PRIVATE
         *     fd,offset, 确定被映射的内存地址
         *     返回成功映射后的地址,不成功返回MAP_FAILED ((void*)-1);
         *
         *     int munmap(void *addr, size_t length);// 断开映射
         *     addr 为映射后的地址,length 为映射后的内存长度
         *
         * 2019-04-13 00:29:24.595 Message: CV4l2CaptureIr::initMmap nIdx=[0] pBuffer=[0x7fbda41000] unLength=[77440] m_nFd=[3] offset=[0]
         * 2019-04-13 00:29:24.595 Message: CV4l2CaptureIr::initMmap nIdx=[1] pBuffer=[0x7fbda2e000] unLength=[77440] m_nFd=[3] offset=[77824]
         * 2019-04-13 00:29:24.595 Message: CV4l2CaptureIr::initMmap nIdx=[2] pBuffer=[0x7fbda1b000] unLength=[77440] m_nFd=[3] offset=[155648]
         * 2019-04-13 00:29:24.595 Message: CV4l2CaptureIr::initMmap nIdx=[3] pBuffer=[0x7fbda08000] unLength=[77440] m_nFd=[3] offset=[233472]
         * 分析一下这个结果,帧数据大小为160*242*2 = 77440,而且是平面视频格式,offset是length经过也对齐后的,77440页对齐后大小为77824,
         * mmap需要页对齐,所以这里将length进行一个页对齐的操作,通过offset找到对应的buffer及plane的值;
         *
         * 原文链接:https ://blog.csdn.net/u010661782/article/details/49020695
         */
         // 应用程序通过内存映射,将帧缓冲区的地址映射到用户空间,返回映射后的地址
        m_pBuffers[nIdx].pBuffer = mmap(NULL /*addr anywhere */, info.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, m_nFd, info.m.offset);
        // MAP_FAILED表示mmap没有成功映射,其返回的值
        if (MAP_FAILED == m_pBuffers[nIdx].pBuffer)
        {
            LOGERROR("CV4l2CaptureIr::initMmap DALI_IR_SENSOR_DEV_NAME=[%s] VIDIOC_QUERYBUF", DALI_IR_SENSOR_DEV_NAME);
            return ReturnCode_Error;
        }

        LOGMSG("CV4l2CaptureIr::initMmap nIdx=[%d] pBuffer=[%p] unLength=[%d] m_nFd=[%d] offset=[%u]", nIdx, m_pBuffers[nIdx].pBuffer, m_pBuffers[nIdx].unLength, m_nFd, info.m.offset);
    }

3. 驱动层

3.1 vb2_mmap函数


int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
{
	unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
	struct vb2_buffer *vb;
	unsigned int buffer = 0, plane = 0;
	int ret;
	unsigned long length;
	
	// 检查memory类型
	if (q->memory != VB2_MEMORY_MMAP) {
		dprintk(1, "queue is not currently set up for mmap\n");
		return -EINVAL;
	}

	/*
	 * Check memory area access mode.
	 */
	if (!(vma->vm_flags & VM_SHARED)) {
		dprintk(1, "invalid vma flags, VM_SHARED needed\n");
		return -EINVAL;
	}
	if (q->is_output) {
		if (!(vma->vm_flags & VM_WRITE)) {
			dprintk(1, "invalid vma flags, VM_WRITE needed\n");
			return -EINVAL;
		}
	} else {
		if (!(vma->vm_flags & VM_READ)) {
			dprintk(1, "invalid vma flags, VM_READ needed\n");
			return -EINVAL;
		}
	}

	mutex_lock(&q->mmap_lock);

	if (vb2_fileio_is_active(q)) {
		dprintk(1, "mmap: file io in progress\n");
		ret = -EBUSY;
		goto unlock;
	}

	//找到与用户空间传递的偏移量对应的平面
	/*
	 * Find the plane corresponding to the offset passed by userspace.
	 */
	ret = __find_plane_by_offset(q, off, &buffer, &plane);
	if (ret)
		goto unlock;

	vb = q->bufs[buffer];

	//MMAP需要页面对齐的缓冲区,我们需要对缓冲区长度使用在_vb2_buf_mem_alloc()处对齐
	/*
	 * MMAP requires page_aligned buffers.
	 * The buffer length was page_aligned at __vb2_buf_mem_alloc(),
	 * so, we need to do the same here.
	 */
	length = PAGE_ALIGN(vb->planes[plane].length);
	if (length < (vma->vm_end - vma->vm_start)) {
		dprintk(1,
			"MMAP invalid, as it would overflow buffer length\n");
		ret = -EINVAL;
		goto unlock;
	}

	ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);

unlock:
	mutex_unlock(&q->mmap_lock);
	if (ret)
		return ret;

	dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane);
	return 0;
}
EXPORT_SYMBOL_GPL(vb2_mmap);

#define log_memop(vb, op)						\
	dprintk(2, "call_memop(%p, %d, %s)%s\n",			\
		(vb)->vb2_queue, (vb)->index, #op,			\
		(vb)->vb2_queue->mem_ops->op ? "" : " (nop)")

#define call_memop(vb, op, args...)					\
({									\
	struct vb2_queue *_q = (vb)->vb2_queue;				\
	int err;							\
									\
	log_memop(vb, op);						\
	err = _q->mem_ops->op ? _q->mem_ops->op(args) : 0;		\
	if (!err)							\
		(vb)->cnt_mem_ ## op++;					\
	err;								\
})
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 12:14:41  更:2022-04-28 12:16:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 20:01:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码