2.4 自己编写一个vivi驱动程序
学习目标:从零编写一个vivi驱动程序,并测试;
一. vivi驱动应用程序调用过程
上节对xawtv对vivi程序调用欧城进行了详细分析,可总结为以下流程:

二、仿照vivi.c编写myvivi.c驱动程序
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/highmem.h>
#include <linux/freezer.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h> static struct v4l2_format myvivi_format; /* 队列操作1: 定义 */
static struct videobuf_queue myvivi_vb_vidqueue;
static spinlock_t myvivi_queue_slock; static struct list_head myvivi_vb_local_queue; //定义本地队列,用于把videobuf放入该队列的尾部 static struct timer_list myvivi_timer; //定义定时器 /* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
/* APP调用ioctl VIDIOC_REQBUFS时会导致此函数被调用,
* 它重新调整count和size
*/
static int myvivi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
{ *size = myvivi_format.fmt.pix.sizeimage; if ( == *count)
*count = ; return ;
} /* APP调用ioctlVIDIOC_QBUF时导致此函数被调用,
* 它会填充video_buffer结构体并调用videobuf_iolock来分配内存
*
*/
static int myvivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
enum v4l2_field field)
{
/* 0. 设置videobuf */
vb->size = myvivi_format.fmt.pix.sizeimage;
vb->bytesperline = myvivi_format.fmt.pix.bytesperline;
vb->width = myvivi_format.fmt.pix.width;
vb->height = myvivi_format.fmt.pix.height;
vb->field = field; /* 1. 做些准备工作 */ #if 0
/* 2. 调用videobuf_iolock为类型为V4L2_MEMORY_USERPTR的videobuf分配内存 */
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
rc = videobuf_iolock(vq, &buf->vb, NULL);
if (rc < )
goto fail;
}
#endif
/* 3. 设置状态 */
vb->state = VIDEOBUF_PREPARED; return ;
} /* APP调用ioctl VIDIOC_QBUF时:
* 1. 先调用buf_prepare进行一些准备工作
* 2. 把buf放入stream队列
* 3. 调用buf_queue(起通知、记录作用)
*/
static void myvivi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
vb->state = VIDEOBUF_QUEUED; /* 把videobuf放入本地一个队列尾部
* 定时器处理函数就可以从本地队列取出videobuf
*/
list_add_tail(&vb->queue, &myvivi_vb_local_queue);
} /* APP不再使用队列时, 用它来释放内存 */
static void myvivi_buffer_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
videobuf_vmalloc_free(vb);
vb->state = VIDEOBUF_NEEDS_INIT;
} static struct videobuf_queue_ops myvivi_video_qops = {
.buf_setup = myvivi_buffer_setup, /* 计算大小以免浪费 */
.buf_prepare = myvivi_buffer_prepare,
.buf_queue = myvivi_buffer_queue,
.buf_release = myvivi_buffer_release,
}; /* ------------------------------------------------------------------
File operations for the device
------------------------------------------------------------------*/ static int myvivi_open(struct file *file)
{
/* 队列操作2: 初始化 */
videobuf_queue_vmalloc_init(&myvivi_vb_vidqueue, &myvivi_video_qops,
NULL, &myvivi_queue_slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer), NULL); /* 倒数第2个参数是buffer的头部大小 */ myvivi_timer.expires = jiffies + ;
add_timer(&myvivi_timer); return ;
} static int myvivi_close(struct file *file)
{
del_timer(&myvivi_timer);
videobuf_stop(&myvivi_vb_vidqueue);
videobuf_mmap_free(&myvivi_vb_vidqueue); return ;
} static int myvivi_mmap(struct file *file, struct vm_area_struct *vma)
{
return videobuf_mmap_mapper(&myvivi_vb_vidqueue, vma);
} static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait)
{
return videobuf_poll_stream(file, &myvivi_vb_vidqueue, wait);
} static int myvivi_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strcpy(cap->driver, "myvivi");
strcpy(cap->card, "myvivi");
cap->version = 0x0001;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return ;
} /* 列举支持哪种格式 */
static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index >= )
return -EINVAL; strcpy(f->description, "4:2:2, packed, YUYV");
f->pixelformat = V4L2_PIX_FMT_YUYV;
return ;
} /* 返回当前所使用的格式 */
static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
memcpy(f, &myvivi_format, sizeof(myvivi_format));
return ();
} /* 测试驱动程序是否支持某种格式 */
static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
unsigned int maxw, maxh;
enum v4l2_field field; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
return -EINVAL; field = f->fmt.pix.field; if (field == V4L2_FIELD_ANY) {
field = V4L2_FIELD_INTERLACED;
} else if (V4L2_FIELD_INTERLACED != field) {
return -EINVAL;
} maxw = ;
maxh = ; /* 调整format的width, height,
* 计算bytesperline, sizeimage
*/
v4l_bound_align_image(&f->fmt.pix.width, , maxw, ,
&f->fmt.pix.height, , maxh, , );
f->fmt.pix.bytesperline =
(f->fmt.pix.width * ) >> ;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline; return ;
} static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f);
if (ret < )
return ret; memcpy(&myvivi_format, f, sizeof(myvivi_format)); return ret;
} static int myvivi_vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
return (videobuf_reqbufs(&myvivi_vb_vidqueue, p));
} static int myvivi_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_querybuf(&myvivi_vb_vidqueue, p));
} static int myvivi_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_qbuf(&myvivi_vb_vidqueue, p));
} static int myvivi_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_dqbuf(&myvivi_vb_vidqueue, p,
file->f_flags & O_NONBLOCK));
} static int myvivi_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
return videobuf_streamon(&myvivi_vb_vidqueue);
} static int myvivi_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
videobuf_streamoff(&myvivi_vb_vidqueue);
return ;
} static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {
// 表示它是一个摄像头设备
.vidioc_querycap = myvivi_vidioc_querycap, /* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap = myvivi_vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = myvivi_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = myvivi_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = myvivi_vidioc_s_fmt_vid_cap, /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
.vidioc_reqbufs = myvivi_vidioc_reqbufs,
.vidioc_querybuf = myvivi_vidioc_querybuf,
.vidioc_qbuf = myvivi_vidioc_qbuf,
.vidioc_dqbuf = myvivi_vidioc_dqbuf, // 启动/停止
.vidioc_streamon = myvivi_vidioc_streamon,
.vidioc_streamoff = myvivi_vidioc_streamoff,
}; static const struct v4l2_file_operations myvivi_fops = {
.owner = THIS_MODULE,
.open = myvivi_open,
.release = myvivi_close,
.mmap = myvivi_mmap,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
.poll = myvivi_poll,
}; static struct video_device *myvivi_device; static void myvivi_release(struct video_device *vdev)
{
} static void myvivi_timer_function(unsigned long data)
{
struct videobuf_buffer *vb;
void *vbuf;
struct timeval ts; /* 1. 构造数据: 从队列头部取出第1个videobuf, 填充数据
*/ /* 1.1 从本地队列取出第1个videobuf */
if (list_empty(&myvivi_vb_local_queue)) { //已在myvivi_buffer_queue函数中放入的
goto out;
} vb = list_entry(myvivi_vb_local_queue.next,
struct videobuf_buffer, queue); /* Nobody is waiting on this buffer, return */
if (!waitqueue_active(&vb->done))
goto out; /* 1.2 填充数据 */
vbuf = videobuf_to_vmalloc(vb);
memset(vbuf, , vb->size); // 写入0;
vb->field_count++;
do_gettimeofday(&ts);
vb->ts = ts;
vb->state = VIDEOBUF_DONE; /* 1.3 把videobuf从本地队列中删除 *****注意:这不是myvivi_vb_local_queue队列*/
list_del(&vb->queue); /* 2. 唤醒进程: 唤醒videobuf->done上的进程 */
wake_up(&vb->done); out:
/* 3. 修改timer的超时时间 : 30fps, 1秒里有30帧数据
* 每1/30 秒产生一帧数据
*/
mod_timer(&myvivi_timer, jiffies + HZ/);
} static int myvivi_init(void)
{
int error; /* 1. 分配一个video_device结构体 */
myvivi_device = video_device_alloc(); /* 2. 设置 */ /* 2.1 */
myvivi_device->release = myvivi_release; /* 2.2 */
myvivi_device->fops = &myvivi_fops; /* 2.3 */
myvivi_device->ioctl_ops = &myvivi_ioctl_ops; /* 2.4 队列操作
* a. 定义/初始化一个队列(会用到一个spinlock)
*/
spin_lock_init(&myvivi_queue_slock); /* 3. 注册 */
error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -); /* 用定时器产生数据并唤醒进程 */
init_timer(&myvivi_timer);
myvivi_timer.function = myvivi_timer_function; INIT_LIST_HEAD(&myvivi_vb_local_queue); return error;
} static void myvivi_exit(void)
{
video_unregister_device(myvivi_device);
video_device_release(myvivi_device);
} module_init(myvivi_init);
module_exit(myvivi_exit);
MODULE_LICENSE("GPL");
(参考:https://home.cnblogs.com/u/linhaostudy和百问网)
2.4 自己编写一个vivi驱动程序的更多相关文章
- 嵌入式Linux设备驱动程序:编写内核设备驱动程序
嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...
- 10.4 android输入系统_框架、编写一个万能模拟输入驱动程序、reader/dispatcher线程启动过程源码分析
1. 输入系统框架 android输入系统官方文档 // 需FQhttp://source.android.com/devices/input/index.html <深入理解Android 卷 ...
- 第六章 第一个Linux驱动程序:统计单词个数
现在进入了实战阶段,使用统计单词个数的实例让我们了解开发和测试Linux驱动程序的完整过程.第一个Linux驱动程序是统计单词个数. 这个Linux驱动程序没有访问硬件,而是利用设备文件作为介质与应用 ...
- 第6章 第一个Linux驱动程序:统计单词个数
编写一个Linux的一般步骤: 第1步:建立Linux驱动骨架(装载和卸载Linux驱动) 第2步:注册和注销设备文件 第3步:指定和驱动相关的信息 第4步:指定回调函数 第5步:编写业务逻辑 第6步 ...
- 如何编写Linux设备驱动程序
一.Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看 ...
- 从头开始编写一个Orchard网上商店模块(6) - 创建购物车服务和控制器
原文地址: http://skywalkersoftwaredevelopment.net/blog/writing-an-orchard-webshop-module-from-scratch-pa ...
- MFC+WinPcap编写一个嗅探器之一(准备)
知识准备: MFC:http://www.jizhuomi.com/software/257.html WinPcap:http://www.ferrisxu.com/WinPcap/html/ind ...
- 8.1 编写USB鼠标驱动程序,并测试
学习目标:编写USB鼠标驱动程序,并测试(将USB鼠标的左键当作L按键,将USB鼠标的右键当作S按键,中键当作回车按键). 一.怎么写USB设备驱动程序?步骤如下: 1. 首先先定义全局变量usb_d ...
- Android系统篇之—-编写简单的驱动程序并且将其编译到内核源码中【转】
本文转载自:大神 通过之前的一篇文章,我们了解了 Android中的Binder机制和远程服务调用 在这篇文章中主要介绍了Android中的应用在调用一些系统服务的时候的原理,那么接下来就继续来介绍一 ...
随机推荐
- 使用 Azure PowerShell 将 IaaS 资源从经典部署模型迁移到 Azure Resource Manager
以下步骤演示了如何使用 Azure PowerShell 命令将基础结构即服务 (IaaS) 资源从经典部署模型迁移到 Azure Resource Manager 部署模型. 也可根据需要通过 Az ...
- 【 PostgreSQL】工作中常用SQL语句干货
接触gp数据库近一年的时间,语法上和其他数据库还是有些许不同,工作中常用的操作语句分享给大家! -- 建表语句 create table ods.ods_b_bill_m ( acct_month t ...
- 初始HTML
了解HTML 1.1 HTML的作用 HTML就是用来制作网页 1.2 什么是HTML HTML是英文HyperText Markup Language的首字母缩写,即超文本标 ...
- FTP作业
实现一个FTP网盘的功能 pa's's
- ASP.NET Core Startup类 Configure()方法 | ASP.NET Core 中间件详细说明
ASP.NET Core 程序启动过程如下 目录 Startup 类 Configure() 方法 中间件 使用中间件 Configure 方法 的参数 IApplicationBuilder Ext ...
- 移动App中常见的Web漏洞
智能手机的存在让网民的生活从PC端开始往移动端转向,现在网民的日常生活需求基本上一部手机就能解决.外卖,办公,社交,银行转账等等都能通过移动端App实现.那么随之也带来了很多信息安全问题,大量的用户信 ...
- 5、Node.js 回调函数
内容:回调函数:阻塞/同步.非阻塞.和异步区别:阻塞和非阻塞代码实例 Node.js 回调函数Node.js 异步编程的直接体现就是回调.异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了. ...
- 启动bash shell的三种方式下,检查的启动文件
启动bash shell的三种方式 1.登录时当做默认登录shell 2.作为非登录shell的交互式shell 3.作为运行脚本的非交互shell 一.登录shell 登录Linux系统时,bash ...
- UVa 12230 - Crossing Rivers(数学期望)
链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
- .net根据经纬度获取地址(百度api)
private string GetAddress(string lng, string lat) { try { string url = @"http://api.map.baidu.c ...