v4l2子系统学习(三)编写虚拟摄像头驱动

文章目录

  • 1、声明
  • 2、前言
  • 3、虚拟摄像头驱动编写
    • 3.1、编写硬件相关代码
    • 3.2、程序示例

1、声明

本文是在学习韦东山《驱动大全》V4L2子系统时,为梳理知识点和自己回看而记录,全部内容高度复制粘贴。

韦老师的《驱动大全》:商品详情

其对应的讲义资料:https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git

2、前言

前面两章,我们基于v4l2在应用层实现了获取摄像头数据,了解了应用层中获取摄像头数据应该有的流程。接着,借着应用层的每一步操作来了解其驱动是如何实现的,从而了解整个v4l2的驱动框架。

从宏观的角度来看,前期的所有准备,无非就是想知道在编写设备驱动程序时要去分配设置哪个结构体,实现哪些关键函数。对于其它子系统的学习也是如此,再者,如今编写驱动的机会只会越来越少,学习驱动的首要目的还是为了养兵千日,用兵一时。

3、虚拟摄像头驱动编写

3.1、编写硬件相关代码

ioctl相关:

1、分配设置注册video_device结构体。

2、实现相关的ioctl函数(如获取设备能力、枚举格式、枚举分辨率、设置分辨率等)。

buffer相关:

3、分配设置vb2_queue结构体。

4、实现相关的vb2_ops函数(如queue_setup()、buf_queue()、start_streaming()、stop_streaming()等函数)。

数据传输:

5、实现start_streaming()、stop_streaming()

3.2、程序示例

#include <linux/version.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>extern unsigned char red[8230];
extern unsigned char blue[8267];
extern unsigned char green[8265];static int g_copy_cnt = 0;static struct list_head g_queued_bufs;
static struct timer_list g_virtual_timer;
static struct mutex g_vb_queue_lock;  /* Protects vb_queue and capt_file */
static struct mutex g_v4l2_lock;      /* Protects everything else *//* intermediate buffers with raw data from the USB device */
struct virtual_frame_buf {/* common v4l buffer stuff -- must be first */struct vb2_v4l2_buffer vb;struct list_head list;
};/* Private functions */
static struct virtual_frame_buf *virtual_get_next_buf(void)
{//unsigned long flags;struct virtual_frame_buf *buf = NULL;//spin_lock_irqsave(&s->queued_bufs_lock, flags);if (list_empty(&g_queued_bufs))goto leave;buf = list_entry(g_queued_bufs.next, struct virtual_frame_buf, list);list_del(&buf->list);
leave://spin_unlock_irqrestore(&s->queued_bufs_lock, flags);return buf;
}#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
static void virtual_timer_expire(unsigned long data)
#else
static void virtual_timer_expire(struct timer_list *t)
#endif
{/* 从硬件上读到数据(使用red/green/blue数组来模拟) *//* 获得第1个空闲buffer */struct virtual_frame_buf *buf = virtual_get_next_buf();void *ptr;if (buf){/* 写入buffer */ptr = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);if (g_copy_cnt <= 60){memcpy(ptr, red, sizeof(red));vb2_set_plane_payload(&buf->vb.vb2_buf, 0, sizeof(red));}else if(g_copy_cnt <= 120){memcpy(ptr, green, sizeof(green));vb2_set_plane_payload(&buf->vb.vb2_buf, 0, sizeof(green));}else {memcpy(ptr, blue, sizeof(blue));vb2_set_plane_payload(&buf->vb.vb2_buf, 0, sizeof(blue));}/* vb2_buffer_done */vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);}g_copy_cnt++;if (g_copy_cnt > 180)g_copy_cnt = 0;/* 再次设置timer的超时时间 */mod_timer(&g_virtual_timer, jiffies + HZ/30);
}static int virtual_querycap(struct file *file, void *fh,struct v4l2_capability *cap)
{strlcpy(cap->driver, "100ask_virtual_video", sizeof(cap->driver));strlcpy(cap->card, "no-card", sizeof(cap->card));cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE ;cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;return 0;
}static int virtual_enum_fmt_cap(struct file *file, void *priv,struct v4l2_fmtdesc *f)
{if (f->index > 0)return -EINVAL;strlcpy(f->description, "100ask motion jpeg", sizeof(f->description));f->pixelformat = V4L2_PIX_FMT_MJPEG;return 0;
}static int virtual_s_fmt_cap(struct file *file, void *priv,struct v4l2_format *f)
{/* 分辨用户传入的参数是否可用* 如果不可用, 给APP提供最接近的、硬件支持的参数*/if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)return -EINVAL;if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)return -EINVAL;f->fmt.pix.width = 800;f->fmt.pix.height = 600;return 0;
}static int virtual_enum_framesizes(struct file *file, void *fh,struct v4l2_frmsizeenum *fsize)
{if (fsize->index > 0)return -EINVAL;fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;fsize->discrete.width = 800;fsize->discrete.height = 600;return 0;
}static int virtual_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{struct v4l2_pix_format *pix = &f->fmt.pix;pix->width = 800;pix->height = 600;pix->field = V4L2_FIELD_NONE;pix->pixelformat = V4L2_PIX_FMT_MJPEG;pix->bytesperline = 0;return 0;
}static const struct v4l2_file_operations virtual_fops = {.owner                    = THIS_MODULE,.open                     = v4l2_fh_open,.release                  = vb2_fop_release,.read                     = vb2_fop_read,.poll                     = vb2_fop_poll,.mmap                     = vb2_fop_mmap,.unlocked_ioctl           = video_ioctl2,
};static const struct v4l2_ioctl_ops virtual_ioctl_ops = {.vidioc_querycap          = virtual_querycap,.vidioc_enum_fmt_vid_cap  = virtual_enum_fmt_cap,.vidioc_s_fmt_vid_cap     = virtual_s_fmt_cap,.vidioc_enum_framesizes   = virtual_enum_framesizes,.vidioc_g_fmt_vid_cap     = virtual_g_fmt,.vidioc_reqbufs           = vb2_ioctl_reqbufs,.vidioc_create_bufs       = vb2_ioctl_create_bufs,.vidioc_prepare_buf       = vb2_ioctl_prepare_buf,.vidioc_querybuf          = vb2_ioctl_querybuf,.vidioc_qbuf              = vb2_ioctl_qbuf,.vidioc_dqbuf             = vb2_ioctl_dqbuf,.vidioc_streamon          = vb2_ioctl_streamon,.vidioc_streamoff         = vb2_ioctl_streamoff,
};static struct video_device g_vdev = {.name                     = "100ask_virtual_video",.release                  = video_device_release_empty,.fops                     = &virtual_fops,			// v4l2接口层相关的ioctl操作函数.ioctl_ops                = &virtual_ioctl_ops,		// 与硬件相关的ioctl操作函数
};static struct v4l2_device g_v4l2_dev;static struct vb2_queue g_vb_queue;static int virtual_queue_setup(struct vb2_queue *vq,unsigned int *nbuffers,unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
{/* 假装:至少需要8buffer, 每个buffer只有1个plane *//* Need at least 8 buffers */if (vq->num_buffers + *nbuffers < 8)*nbuffers = 8 - vq->num_buffers;*nplanes = 1;sizes[0] = PAGE_ALIGN(800*600*2);return 0;
}static void virtual_buf_queue(struct vb2_buffer *vb)
{/* 把这个buffer告诉硬件相关的驱动程序 */struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);struct virtual_frame_buf *buf =container_of(vbuf, struct virtual_frame_buf, vb);//unsigned long flags;//spin_lock_irqsave(&s->queued_bufs_lock, flags);list_add_tail(&buf->list, &g_queued_bufs);		//buffer放到驱动程序里的链表//spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
}static int virtual_start_streaming(struct vb2_queue *vq, unsigned int count)
{/* 启动硬件传输 *//* 使用timer来模拟硬件中断* 创建timer* 启动timer*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)setup_timer(&g_virtual_timer, virtual_timer_expire, 0);
#elsetimer_setup(&g_virtual_timer, virtual_timer_expire, 0);
#endifg_virtual_timer.expires = jiffies + HZ/30;add_timer(&g_virtual_timer);return 0;
}static void virtual_stop_streaming(struct vb2_queue *vq)
{/* 停止硬件传输 */del_timer(&g_virtual_timer);while (!list_empty(&g_queued_bufs)) {struct virtual_frame_buf *buf;buf = list_entry(g_queued_bufs.next,struct virtual_frame_buf, list);list_del(&buf->list);vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);}
}/* buffer管理要用到的vb2_ops */
static const struct vb2_ops virtul_vb2_ops = {.queue_setup            = virtual_queue_setup,		// APP调用ioctl VIDIOC_REQBUFS或VIDIOC_CREATE_BUFS时,驱动程序在分配内存之前,会调用此函数。.buf_queue              = virtual_buf_queue,		//buffer传送给驱动.start_streaming        = virtual_start_streaming,	// 启动硬件传输(操作硬件).stop_streaming         = virtual_stop_streaming,	// 关闭硬件传输(操作硬件).wait_prepare           = vb2_ops_wait_prepare,.wait_finish            = vb2_ops_wait_finish,
};static void virtual_video_release(struct v4l2_device *v)
{
}static int virtual_video_drv_init(void)
{int ret;/* 分配/设置/注册video_device */printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 1、设置vb2_queue结构体 */g_vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;g_vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;g_vb_queue.drv_priv = NULL;g_vb_queue.buf_struct_size = sizeof(struct virtual_frame_buf);  /* 分配vb时, 分配的空间大小为buf_struct_size */g_vb_queue.ops = &virtul_vb2_ops;			// 设置buffer管理要用到的vb2_opsg_vb_queue.mem_ops = &vb2_vmalloc_memops;	// 设置buffer管理要用到的vb2_mem_opsg_vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;ret = vb2_queue_init(&g_vb_queue);if (ret) {printk("Could not initialize vb2 queue\n");return -1;;}printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);mutex_init(&g_vb_queue_lock);/* 2、设置video_device结构体 */g_vdev.queue = &g_vb_queue;     g_vdev.queue->lock = &g_vb_queue_lock;/* Register the v4l2_device structure(辅助作用) */g_v4l2_dev.release = virtual_video_release;strcpy(g_v4l2_dev.name, "virtual_v4l2");ret = v4l2_device_register(NULL, &g_v4l2_dev);if (ret) {printk("Failed to register v4l2-device (%d)\n", ret);return -1;}printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);g_vdev.v4l2_dev = &g_v4l2_dev;g_vdev.lock = &g_v4l2_lock;mutex_init(&g_v4l2_lock);/* 3、注册video_device结构体 */ ret = video_register_device(&g_vdev, VFL_TYPE_GRABBER, -1);if (ret) {printk("Failed to register as video device (%d)\n", ret);return -1;}printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);INIT_LIST_HEAD(&g_queued_bufs);return 0;
}static void virtual_video_drv_exit(void)
{/* 反注册/释放video_device */v4l2_device_unregister(&g_v4l2_dev);video_unregister_device(&g_vdev);
}module_init(virtual_video_drv_init);
module_exit(virtual_video_drv_exit);MODULE_LICENSE("GPL");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/22670.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后“智驾平权”时代,谁为安全冗余和体验升级“买单”

线控底盘&#xff0c;正在成为新势力争夺下一个技术普及红利的新赛点。 尤其是进入2025年&#xff0c;比亚迪、长安等一线传统自主品牌率先开启高阶智驾的普及战&#xff0c;加上此前已经普及的智能座舱&#xff0c;舱驾智能的「科技平权」进一步加速行业启动「线控底盘」上车窗…

【Node.js】express框架

目录 1初识express框架 2 初步使用 2.1 安装 2.2 创建基本的Web服务器 2.3 监听方法 2.3.1 监听get请求 2.3.2 监听post请求 2.4 响应客户端 2.5 获取url中的参数(get) 2.5.1 获取查询参数 2.5.2 获取动态参数 2.6 托管静态资源 2.6.1 挂载路径前缀 2.6.2 托管多…

树形DP(树形背包+换根DP)

树形DP 没有上司的舞会 家常便饭了&#xff0c;写了好几遍&#xff0c;没啥好说的&#xff0c;正常独立集问题。 int head[B]; int cnt; struct node {int v,nxt; }e[B<<1]; void modify(int u,int v) {e[cnt].nxthead[u];e[cnt].vv;head[u]cnt; } int a[B]; int f[B]…

REACT--组件通信

组件之间如何进行通信&#xff1f; 组件通信 组件的通信主要借助props传递值 分为整体接收、解构接收 整体接收 import PropTypes from prop-types;//子组件 function Welcome(props){return (<div>hello Welcome,{props.count},{props.msg}</div>) }// 对 We…

【排序算法】六大比较类排序算法——插入排序、选择排序、冒泡排序、希尔排序、快速排序、归并排序【详解】

文章目录 六大比较类排序算法&#xff08;插入排序、选择排序、冒泡排序、希尔排序、快速排序、归并排序&#xff09;前言1. 插入排序算法描述代码示例算法分析 2. 选择排序算法描述优化代码示例算法分析 3. 冒泡排序算法描述代码示例算法分析与插入排序对比 4. 希尔排序算法描…

纠错检索增广生成论文

一、摘要 动机&#xff1a;RAG严重依赖于检索文档的相关性&#xff0c;如果检索出错&#xff0c;那么LLM的输出结果也会出现问题 解决方案&#xff1a;提出纠正性检索增强生成&#xff08;CRAG&#xff09;即设计一个轻量级的检索评估器&#xff0c;用来评估针对某个查询检索…

Java NIO与传统IO性能对比分析

Java NIO与传统IO性能对比分析 在Java中&#xff0c;I/O&#xff08;输入输出&#xff09;操作是开发中最常见的任务之一。传统的I/O方式基于阻塞模型&#xff0c;而Java NIO&#xff08;New I/O&#xff09;引入了非阻塞和基于通道&#xff08;Channel&#xff09;和缓冲区&a…

easelog(1)基础C++日志功能实现

EaseLog(1)基础C日志功能实现 Author: Once Day Date: 2025年2月22日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 注&#xff1a;本简易日志组件代码实现参考了Google …

Vue面试2

1.跨域问题以及如何解决跨域 跨域问题&#xff08;Cross-Origin Resource Sharing, CORS&#xff09;是指在浏览器中&#xff0c;当一个资源试图从一个不同的源请求另一个资源时所遇到的限制。这种限制是浏览器为了保护用户安全而实施的一种同源策略&#xff08;Same-origin p…

MongoDB应用设计调优

应用范式设计 什么是范式 数据库范式概念是数据库技术的基本理论&#xff0c;几乎是伴随着数据库软件产品的推出而产生的。在传统关系型数据库领域&#xff0c;应用开发中遵循范式是最基本的要求。但随着互联网行业的发展&#xff0c;NoSQL开始变得非常流行&#xff0c;在许多…

Mac安装配置Tomcat 8

一、官网下载 Index of /disthttps://archive.apache.org/dist/ 1、进入界面如下&#xff1a; 2、我们找到Tomcat文件夹并进入 3、找到Tomcat 8并打开 4、找到对应的版本打开 5、打开bin 6、找到“apache-tomcat-8.5.99.tar.gz”并下载 二、配置Tomcat 1、解压已经下载好的…

【论文精读】VLM-AD:通过视觉-语言模型监督实现端到端自动驾驶

论文地址&#xff1a; VLM-AD: End-to-End Autonomous Driving through Vision-Language Model Supervision 摘要 人类驾驶员依赖常识推理来应对复杂多变的真实世界驾驶场景。现有的端到端&#xff08;E2E&#xff09;自动驾驶&#xff08;AD&#xff09;模型通常被优化以模仿…

百度搜索,能否将DeepSeek变成“内功”?

最近&#xff0c;所有的云平台和主流APP都在努力接入DeepSeek。其中&#xff0c;搜索类APP与搜索引擎更是“战况激烈”。那么问题来了&#xff0c;接入DeepSeek已经变成了标准配置&#xff0c;到底应该如何做出差异化&#xff1f;接入DeepSeek这件事能不能实现11大于2的效果&am…

Flask实现高效日志记录模块

目录 一. 简介&#xff1a; 1. 为什么需要请求日志 二. 日志模块组成 1. 对应日志表创建&#xff08;包含日志记录的关键字段&#xff09; 2. 编写日志记录静态方法 3. 在Flask中捕获请求日志 4. 捕获异常并记录错误日志 5. 编写日志接口数据展示 6. 写入数据展…

25轻化工程研究生复试面试问题汇总 轻化工程专业知识问题很全! 轻化工程复试全流程攻略 轻化工程考研复试真题汇总

轻化工程复试心里没谱&#xff1f;学姐带你玩转面试准备&#xff01; 是不是总觉得老师会问些刁钻问题&#xff1f;别焦虑&#xff01;其实轻化工程复试套路就那些&#xff0c;看完这篇攻略直接掌握复试通关密码&#xff01;文中有重点面试题可直接背~ 目录 一、这些行为赶紧避…

查看已经安装的Python库,高效管理自己的Python开发环境

在日常的Python开发中&#xff0c;掌握如何管理和查看已经安装的库是非常重要的。这不仅能帮助你了解当前项目的依赖关系&#xff0c;还能避免出现版本冲突等问题。在这篇文章中&#xff0c;我们将详细介绍查看已安装Python库的方法&#xff0c;并提供一些实用的工具和技巧。 …

Selenium实战案例1:论文pdf自动下载

在上一篇文章中&#xff0c;我们介绍了Selenium的基础用法和一些常见技巧。今天&#xff0c;我们将通过中国科学&#xff1a;信息科学网站内当前目录论文下载这一实战案例来进一步展示Selenium的web自动化流程。 目录 中国科学&#xff1a;信息科学当期目录论文下载 1.网页内…

Visual Studio Code 2025 安装与高效配置教程

一、软件简介与下载 1. Visual Studio Code 是什么&#xff1f; Visual Studio Code&#xff08;简称VS Code&#xff09;是微软推出的免费开源代码编辑器&#xff0c;支持 智能代码补全、Git集成、插件扩展 等功能&#xff0c;适用于前端开发、Python、Java等多种编程场景。…

工业路由器和工业交换机,打造高效稳定的工业网络?

工业路由器和工业交换机各有千秋&#xff0c;但如何将它们完美结合&#xff0c;构建稳定高效的工业网络&#xff1f;答案就在这里&#xff01; 工业物联网&#xff08;IIoT&#xff09;是高效、稳定的工业网络成为智慧工厂、工业自动化和远程监控等场景的基础支撑。工业路由器…

DeepSeek 助力 Vue 开发:打造丝滑的二维码生成(QR Code)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…