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中的应用在调用一些系统服务的时候的原理,那么接下来就继续来介绍一 ...
随机推荐
- Windows 10 host where Credential Guard or Device Guard is enabled fails when running Workstation (2146361)
To disable Device Guard or Credential Guard on Itanium based computers: Disable the group policy set ...
- new era
新的博客,将会记录我在工作和学习中遇到的问题以及总结...
- Unity IOC/DI使用
一.IOC介绍 IOC(Inversion of Control),中文译为控制反转,又称为“依赖注入”(DI =Dependence Injection) IOC的基本概念是:不创建对象,但是描述创 ...
- [翻译] JNWSpringAnimation
JNWSpringAnimation https://github.com/jwilling/JNWSpringAnimation JNWSpringAnimation is a subclass o ...
- iso、ios、osi的区别
ISO 国际标准化组织(International Organization for Standardization)简称ISO,是一个全球性的非政府组织,是国际标准化领域中一个十分重要的组织.IS ...
- linux邮件客户端mutt日志文件,发不出邮件
linux上面使用很方便的收发邮件客户端(即MUA),还有一个是mail,大家也可以试试. 在网上找了很多,都说需要msmtp配合使用,其实mutt里面也内建了smtp支持的. mutt and SM ...
- Python学习---网页爬虫[下载图片]
爬虫学习--下载图片 1.主要用到了urllib和re库 2.利用urllib.urlopen()函数获得页面源代码 3.利用正则匹配图片类型,当然正则越准确,下载的越多 4.利用urllib.url ...
- 3D旋转相册的实现
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- EXCHANGE 2013 队列
每当咱在Exchange里查看队列的时候,我们会看到队列分成好几个组,每个邮箱数据库都有自己的目标队列,DAG.AD站点也是,AD林也是一个队列,最后最多的就是外部SMTP域队列. 当传输服务处理队列 ...
- 沉淀,再出发:web前端的一些认识
沉淀,再出发:web前端的一些认识 一.前言 作为程序员,我一直认为全栈是一种最基本的能力,没有了这种目标就会发现自己越往后面发展路就越窄,很多自己不了解的东西会阻塞自己去理解整个系统的开发过程和效率 ...