版权声明:本文为博主原创文章,如有需要, 请注明转载地址:http://blog.csdn.net/tech_pro。若是侵权用于商业用途,请联系博主,否则将追究责任。 https://blog.csdn.net/TECH_PRO/article/details/79572670
一、vivi摄像头驱动基本框架
- 分配一个video_device结构体变量
- 设置这个结构体变量
- 注册这个结构体变量
二、vivi摄像头驱动数据的获取过程
- 请求分配缓冲区
- 查询缓冲区,并为缓冲区分配空间
- 将缓冲区放入队列
- 启动摄像头
- 通过poll机制来对查询是否有数据,如果有数据可以通过定时器或者内核线程来唤醒它
- 将数据从缓冲区中取出,并将这个缓冲区从队列中删除
- 判断摄像头是否继续工作,如果继续工作则重复执行3、5、6步,否则结束
三、vivi摄像头驱动的简单实现步骤
1、自定义一个结构体yl_vivi_dev来表示vivi驱动的核心结构体,具体实现如下:
/* 定义一个结构体用来表示虚拟摄像头设备 */
struct yl_vivi_dev {
struct video_device *video_device; /* vivi驱动遵循V4L2框架的核心结构体 */
struct v4l2_format yl_vivi_format; /* 摄像头数据格式 */
struct videobuf_queue yl_videobuf_queue; /* 缓冲区队列 */
spinlock_t yl_videobuf_queue_slock; /* 用去缓冲区队列构建时使用的自旋锁 */
struct list_head yl_videobuf_list; /* 表示一个本地的队列,用来对缓冲区进行操作的 */
struct timer_list yl_vivi_timer; /* 通过定时器来填充摄像头数据 */
};
static struct yl_vivi_dev *yl_vivi_dev;
2、vivi驱动模块的输入、输出函数接口定义
/* 模块入口函数 */
static int yl_vivi_init(void)
{
int ret;
/* 给yl_vivi_dev的结构体变量分配内存 */
yl_vivi_dev = kzalloc(sizeof(struct yl_vivi_dev), GFP_KERNEL);
if(!yl_vivi_dev)
{
return -ENOMEM;
}
/* 1 分配一个video_device的结构体变量 */
yl_vivi_dev->video_device = video_device_alloc();
if (NULL == yl_vivi_dev->video_device)
{
kfree(yl_vivi_dev);
return -ENOMEM;
}
/* 2 设置这个video_device结构体变量 */
/* 2.1 给video_device结构体变量提供release函数,可以不用具体实现这个函数 */
yl_vivi_dev->video_device->release = yl_vivi_release;
/* 2.2 fops */
yl_vivi_dev->video_device->fops = &yl_vivi_fops;
/* 2.3 ioctl_ops */
yl_vivi_dev->video_device->ioctl_ops = &yl_vivi_ioctl_ops;
/* 3 注册这个video_device结构体变量 */
ret = video_register_device(yl_vivi_dev->video_device, VFL_TYPE_GRABBER, -1);
return ret;
}
/* 模块出口函数 */
static void yl_vivi_exit(void)
{
video_unregister_device(yl_vivi_dev->video_device);
video_device_release(yl_vivi_dev->video_device);
kfree(yl_vivi_dev);
}
主要完成video_device结构体的分配、设置和注册工作,核心是:
/* 2.2 fops */
yl_vivi_dev->video_device->fops = &yl_vivi_fops;
/* 2.3 ioctl_ops */
yl_vivi_dev->video_device->ioctl_ops = &yl_vivi_ioctl_ops;
yl_vivi_fops和yl_vivi_ioctl_ops的具体定义如下所示,这两个结构体是应用程序调用驱动程序时的主要接口:
/* video_device结构体变量的file操作函数 */
static const struct v4l2_file_operations yl_vivi_fops = {
.owner = THIS_MODULE,
.open = yl_vivi_open,
.release = yl_vivi_close,
.mmap = yl_vivi_mmap,
.poll = yl_vivi_poll,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
};
/* video_device结构体变量的ioctl操作函数 */
static const struct v4l2_ioctl_ops yl_vivi_ioctl_ops = {
/* 查询摄像头设备的基本性能 */
.vidioc_querycap = yl_vivi_vidioc_querycap,
/* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap = yl_vivi_vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = yl_vivi_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = yl_vivi_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = yl_vivi_vidioc_s_fmt_vid_cap,
/* 用于申请、查询、入队列、出队列的摄像头数据的缓冲区的操作 */
.vidioc_reqbufs = yl_vivi_vidioc_reqbufs,
.vidioc_querybuf = yl_vivi_vidioc_querybuf,
.vidioc_qbuf = yl_vivi_vidioc_qbuf,
.vidioc_dqbuf = yl_vivi_vidioc_dqbuf,
/* 用于启动和关闭摄像头设备 */
.vidioc_streamon = yl_vivi_vidioc_streamon,
.vidioc_streamoff = yl_vivi_vidioc_streamoff,
};
下面主要工作就是设置这两个结构体变量的成员函数。
3、查询摄像头的基本性能
/* 查询摄像头设备的基本性能 */
static int yl_vivi_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strcpy(cap->driver, "yl_vivi");
strcpy(cap->card, "yl_vivi");
cap->version = 0x0001;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
4、列举、获得、测试、设置摄像头的数据的格式
/* 列举摄像头支持的数据格式 */
static int yl_vivi_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index >= 1)
return -EINVAL;
strcpy(f->description, "4:2:2, packed, YUYV");
f->pixelformat = V4L2_PIX_FMT_YUYV;
return 0;
}
/* 获得摄像头所支持的数据格式 */
static int yl_vivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
memcpy(f, &yl_vivi_dev->yl_vivi_format, sizeof(struct v4l2_format));
return (0);
}
/* 测试摄像头所支持的数据格式 */
static int yl_vivi_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 = 1366;
maxh = 768;
/* 调整format的width, height,
* 计算bytesperline, sizeimage
*/
v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
&f->fmt.pix.height, 32, maxh, 0, 0);
f->fmt.pix.bytesperline =
(f->fmt.pix.width * 16) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
return 0;
}
/* 设置摄像头的数据格式 */
static int yl_vivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
int ret = yl_vivi_vidioc_try_fmt_vid_cap(file, NULL, f);
if (ret < 0)
return ret;
memcpy(&yl_vivi_dev->yl_vivi_format, f, sizeof(struct v4l2_format));
return ret;
}
5、申请、查询、入队列、出队列的摄像头数据的缓冲区的操作
定义缓冲区之前,需要先定义缓冲区队列,代码在yl_vivi_open函数中定义,具体如下:
/* 缓冲区队列的初始化 */
videobuf_queue_vmalloc_init(&yl_vivi_dev->yl_videobuf_queue, &yl_vivi_video_qops,
NULL, &yl_vivi_dev->yl_videobuf_queue_slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer), NULL);
yl_vivi_video_qops这个结构体的具体定义和成员函数的实现如下:
static struct videobuf_queue_ops yl_vivi_video_qops = {
.buf_setup = yl_vivi_buffer_setup, /* 计算大小以免浪费 */
.buf_prepare = yl_vivi_buffer_prepare,
.buf_queue = yl_vivi_buffer_queue,
.buf_release = yl_vivi_buffer_release,
};
/* 调整videobuf的尺寸和数量 */
static int yl_vivi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
{
*size = yl_vivi_dev->yl_vivi_format.fmt.pix.sizeimage;
if (0 == *count)
*count = 32;
return 0;
}
/* 设置缓冲区,做一些准备工作 */
static int yl_vivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
enum v4l2_field field)
{
/* 设置videobuf */
vb->size = yl_vivi_dev->yl_vivi_format.fmt.pix.sizeimage;
vb->bytesperline = yl_vivi_dev->yl_vivi_format.fmt.pix.bytesperline;
vb->width = yl_vivi_dev->yl_vivi_format.fmt.pix.width;
vb->height = yl_vivi_dev->yl_vivi_format.fmt.pix.height;
vb->field = field;
/* 设置缓冲区处于就绪状态 */
vb->state = VIDEOBUF_PREPARED;
return 0;
}
/* 将缓冲区放入队列 */
static void yl_vivi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
vb->state = VIDEOBUF_QUEUED;
/* 把缓冲区放入本地队列尾部 */
list_add_tail(&vb->queue, &yl_vivi_dev->yl_videobuf_list);
}
/* 释放缓冲区 */
static void yl_vivi_buffer_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
videobuf_vmalloc_free(vb);
vb->state = VIDEOBUF_NEEDS_INIT;
}
和缓冲区相关的操作接口定义如下:
static int yl_vivi_vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
return (videobuf_reqbufs(&yl_vivi_dev->yl_videobuf_queue, p));
}
static int yl_vivi_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_querybuf(&yl_vivi_dev->yl_videobuf_queue, p));
}
static int yl_vivi_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_qbuf(&yl_vivi_dev->yl_videobuf_queue, p));
}
static int yl_vivi_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_dqbuf(&yl_vivi_dev->yl_videobuf_queue, p, file->f_flags & O_NONBLOCK));
}
6、启动和关闭摄像头
static int yl_vivi_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
return videobuf_streamon(&yl_vivi_dev->yl_videobuf_queue);
}
static int yl_vivi_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
videobuf_streamoff(&yl_vivi_dev->yl_videobuf_queue);
return 0;
}
7、vivi驱动的open、close、mmap和poll函数的实现
open函数主要负责缓冲区队列和定时器的设置工作,通过定时器来实现周期性的vivi摄像头数据填充:
static int yl_vivi_open(struct file *file)
{
/* 初始化缓冲区队列的自旋锁 */
spin_lock_init(&yl_vivi_dev->yl_videobuf_queue_slock);
/* 初始化缓冲区队列的list */
INIT_LIST_HEAD(&yl_vivi_dev->yl_videobuf_list);
/* 缓冲区队列的初始化 */
videobuf_queue_vmalloc_init(&yl_vivi_dev->yl_videobuf_queue, &yl_vivi_video_qops,
NULL, &yl_vivi_dev->yl_videobuf_queue_slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer), NULL);
/* 初始化并添加一个定时器用来给虚拟摄像头提供数据 */
init_timer(&yl_vivi_dev->yl_vivi_timer);
yl_vivi_dev->yl_vivi_timer.function = yl_vivi_timer_function;
yl_vivi_dev->yl_vivi_timer.expires = jiffies + 1;
add_timer(&yl_vivi_dev->yl_vivi_timer);
return 0;
}
static int yl_vivi_close(struct file *file)
{
del_timer(&yl_vivi_dev->yl_vivi_timer);
videobuf_stop(&yl_vivi_dev->yl_videobuf_queue);
videobuf_mmap_free(&yl_vivi_dev->yl_videobuf_queue);
return 0;
}
mmap函数主要是来分配和映射缓冲区的:
static int yl_vivi_mmap(struct file *file, struct vm_area_struct *vma)
{
return videobuf_mmap_mapper(&yl_vivi_dev->yl_videobuf_queue, vma);
}
poll函数实现休眠功能,让vivi在无数据获取时进入休眠状态:
static unsigned int yl_vivi_poll(struct file *file, struct poll_table_struct *wait)
{
return videobuf_poll_stream(file, &yl_vivi_dev->yl_videobuf_queue, wait);
}
8、定时器函数的具体实现,通过该函数将缓冲区从队列中取出并填充缓冲区,唤醒poll引起的休眠
/* 通过定时器来不停的向缓冲区不停的填充数据 */
static void yl_vivi_timer_function(unsigned long data)
{
struct videobuf_buffer *vb;
void *vbuf;
static unsigned char color = 0x00;
/* 判断用于连接缓冲区的本地队列是否为空 */
if (list_empty(&yl_vivi_dev->yl_videobuf_list))
{
goto out;
}
/* 从这个本地队列中取出第一个缓冲区 */
vb = list_entry(yl_vivi_dev->yl_videobuf_list.next,
struct videobuf_buffer, queue);
if (!waitqueue_active(&vb->done)) goto out;
/* 填充数据 */
vbuf = videobuf_to_vmalloc(vb);
memset(vbuf, color, vb->size);
vb->field_count++;
vb->state = VIDEOBUF_DONE;
/* 把videobuf从本地队列中删除 */
list_del(&vb->queue);
/* 唤醒 */
wake_up(&vb->done);
out:
/* 修改时间,让缓冲区可以不停的获得数据 */
mod_timer(&yl_vivi_dev->yl_vivi_timer, jiffies + HZ/25);
if(color > 0xff) color = 0x00; else color += 1;
}
附录:完整的vivi虚拟摄像头驱动的实现代码(本代码是在linux2.6.31.14内核下进行编辑和编译的)
#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>
/* 定义一个结构体用来表示虚拟摄像头设备 */
struct yl_vivi_dev {
struct video_device *video_device;
struct v4l2_format yl_vivi_format;
struct videobuf_queue yl_videobuf_queue;
spinlock_t yl_videobuf_queue_slock;
struct list_head yl_videobuf_list; /* 表示一个本地的队列,用来对缓冲区进行操作的 */
struct timer_list yl_vivi_timer;
};
static struct yl_vivi_dev *yl_vivi_dev;
/* 调整videobuf的尺寸和数量 */
static int yl_vivi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
{
*size = yl_vivi_dev->yl_vivi_format.fmt.pix.sizeimage;
if (0 == *count)
*count = 32;
return 0;
}
/* 设置缓冲区,做一些准备工作 */
static int yl_vivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
enum v4l2_field field)
{
/* 设置videobuf */
vb->size = yl_vivi_dev->yl_vivi_format.fmt.pix.sizeimage;
vb->bytesperline = yl_vivi_dev->yl_vivi_format.fmt.pix.bytesperline;
vb->width = yl_vivi_dev->yl_vivi_format.fmt.pix.width;
vb->height = yl_vivi_dev->yl_vivi_format.fmt.pix.height;
vb->field = field;
/* 设置缓冲区处于就绪状态 */
vb->state = VIDEOBUF_PREPARED;
return 0;
}
/* 将缓冲区放入队列 */
static void yl_vivi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
vb->state = VIDEOBUF_QUEUED;
/* 把缓冲区放入本地队列尾部 */
list_add_tail(&vb->queue, &yl_vivi_dev->yl_videobuf_list);
}
/* 释放缓冲区 */
static void yl_vivi_buffer_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
videobuf_vmalloc_free(vb);
vb->state = VIDEOBUF_NEEDS_INIT;
}
static struct videobuf_queue_ops yl_vivi_video_qops = {
.buf_setup = yl_vivi_buffer_setup, /* 计算大小以免浪费 */
.buf_prepare = yl_vivi_buffer_prepare,
.buf_queue = yl_vivi_buffer_queue,
.buf_release = yl_vivi_buffer_release,
};
/* 查询摄像头设备的基本性能 */
static int yl_vivi_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strcpy(cap->driver, "yl_vivi");
strcpy(cap->card, "yl_vivi");
cap->version = 0x0001;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
/* 列举摄像头支持的数据格式 */
static int yl_vivi_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index >= 1)
return -EINVAL;
strcpy(f->description, "4:2:2, packed, YUYV");
f->pixelformat = V4L2_PIX_FMT_YUYV;
return 0;
}
/* 获得摄像头所支持的数据格式 */
static int yl_vivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
memcpy(f, &yl_vivi_dev->yl_vivi_format, sizeof(struct v4l2_format));
return (0);
}
/* 测试摄像头所支持的数据格式 */
static int yl_vivi_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 = 1366;
maxh = 768;
/* 调整format的width, height,
* 计算bytesperline, sizeimage
*/
v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
&f->fmt.pix.height, 32, maxh, 0, 0);
f->fmt.pix.bytesperline =
(f->fmt.pix.width * 16) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
return 0;
}
/* 设置摄像头的数据格式 */
static int yl_vivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
int ret = yl_vivi_vidioc_try_fmt_vid_cap(file, NULL, f);
if (ret < 0)
return ret;
memcpy(&yl_vivi_dev->yl_vivi_format, f, sizeof(struct v4l2_format));
return ret;
}
static int yl_vivi_vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
return (videobuf_reqbufs(&yl_vivi_dev->yl_videobuf_queue, p));
}
static int yl_vivi_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_querybuf(&yl_vivi_dev->yl_videobuf_queue, p));
}
static int yl_vivi_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_qbuf(&yl_vivi_dev->yl_videobuf_queue, p));
}
static int yl_vivi_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return (videobuf_dqbuf(&yl_vivi_dev->yl_videobuf_queue, p, file->f_flags & O_NONBLOCK));
}
static int yl_vivi_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
return videobuf_streamon(&yl_vivi_dev->yl_videobuf_queue);
}
static int yl_vivi_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
videobuf_streamoff(&yl_vivi_dev->yl_videobuf_queue);
return 0;
}
/* video_device结构体变量的ioctl操作函数 */
static const struct v4l2_ioctl_ops yl_vivi_ioctl_ops = {
/* 查询摄像头设备的基本性能 */
.vidioc_querycap = yl_vivi_vidioc_querycap,
/* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap = yl_vivi_vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = yl_vivi_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = yl_vivi_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = yl_vivi_vidioc_s_fmt_vid_cap,
/* 用于申请、查询、入队列、出队列的摄像头数据的缓冲区的操作 */
.vidioc_reqbufs = yl_vivi_vidioc_reqbufs,
.vidioc_querybuf = yl_vivi_vidioc_querybuf,
.vidioc_qbuf = yl_vivi_vidioc_qbuf,
.vidioc_dqbuf = yl_vivi_vidioc_dqbuf,
/* 用于启动和关闭摄像头设备 */
.vidioc_streamon = yl_vivi_vidioc_streamon,
.vidioc_streamoff = yl_vivi_vidioc_streamoff,
};
/* 通过定时器来不停的向缓冲区不停的填充数据 */
static void yl_vivi_timer_function(unsigned long data)
{
struct videobuf_buffer *vb;
void *vbuf;
static unsigned char color = 0x00;
/* 判断用于连接缓冲区的本地队列是否为空 */
if (list_empty(&yl_vivi_dev->yl_videobuf_list))
{
goto out;
}
/* 从这个本地队列中取出第一个缓冲区 */
vb = list_entry(yl_vivi_dev->yl_videobuf_list.next,
struct videobuf_buffer, queue);
if (!waitqueue_active(&vb->done)) goto out;
/* 填充数据 */
vbuf = videobuf_to_vmalloc(vb);
memset(vbuf, color, vb->size);
vb->field_count++;
vb->state = VIDEOBUF_DONE;
/* 把videobuf从本地队列中删除 */
list_del(&vb->queue);
/* 唤醒 */
wake_up(&vb->done);
out:
/* 修改时间,让缓冲区可以不停的获得数据 */
mod_timer(&yl_vivi_dev->yl_vivi_timer, jiffies + HZ/25);
if(color > 0xff) color = 0x00; else color += 1;
}
static int yl_vivi_open(struct file *file)
{
/* 初始化缓冲区队列的自旋锁 */
spin_lock_init(&yl_vivi_dev->yl_videobuf_queue_slock);
/* 初始化缓冲区队列的list */
INIT_LIST_HEAD(&yl_vivi_dev->yl_videobuf_list);
/* 缓冲区队列的初始化 */
videobuf_queue_vmalloc_init(&yl_vivi_dev->yl_videobuf_queue, &yl_vivi_video_qops,
NULL, &yl_vivi_dev->yl_videobuf_queue_slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
sizeof(struct videobuf_buffer), NULL);
/* 初始化并添加一个定时器用来给虚拟摄像头提供数据 */
init_timer(&yl_vivi_dev->yl_vivi_timer);
yl_vivi_dev->yl_vivi_timer.function = yl_vivi_timer_function;
yl_vivi_dev->yl_vivi_timer.expires = jiffies + 1;
add_timer(&yl_vivi_dev->yl_vivi_timer);
return 0;
}
static int yl_vivi_close(struct file *file)
{
del_timer(&yl_vivi_dev->yl_vivi_timer);
videobuf_stop(&yl_vivi_dev->yl_videobuf_queue);
videobuf_mmap_free(&yl_vivi_dev->yl_videobuf_queue);
return 0;
}
static int yl_vivi_mmap(struct file *file, struct vm_area_struct *vma)
{
return videobuf_mmap_mapper(&yl_vivi_dev->yl_videobuf_queue, vma);
}
static unsigned int yl_vivi_poll(struct file *file, struct poll_table_struct *wait)
{
return videobuf_poll_stream(file, &yl_vivi_dev->yl_videobuf_queue, wait);
}
/* video_device结构体变量的file操作函数 */
static const struct v4l2_file_operations yl_vivi_fops = {
.owner = THIS_MODULE,
.open = yl_vivi_open,
.release = yl_vivi_close,
.mmap = yl_vivi_mmap,
.poll = yl_vivi_poll,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
};
/* 提供给video_device结构体变量的release函数 */
static void yl_vivi_release(struct video_device *vdev)
{
}
/* 模块入口函数 */
static int yl_vivi_init(void)
{
int ret;
/* 给yl_vivi_dev的结构体变量分配内存 */
yl_vivi_dev = kzalloc(sizeof(struct yl_vivi_dev), GFP_KERNEL);
if(!yl_vivi_dev)
{
return -ENOMEM;
}
/* 1 分配一个video_device的结构体变量 */
yl_vivi_dev->video_device = video_device_alloc();
if (NULL == yl_vivi_dev->video_device)
{
kfree(yl_vivi_dev);
return -ENOMEM;
}
/* 2 设置这个video_device结构体变量 */
/* 2.1 给video_device结构体变量提供release函数,可以不用具体实现这个函数 */
yl_vivi_dev->video_device->release = yl_vivi_release;
/* 2.2 fops */
yl_vivi_dev->video_device->fops = &yl_vivi_fops;
/* 2.3 ioctl_ops */
yl_vivi_dev->video_device->ioctl_ops = &yl_vivi_ioctl_ops;
/* 3 注册这个video_device结构体变量 */
ret = video_register_device(yl_vivi_dev->video_device, VFL_TYPE_GRABBER, -1);
return ret;
}
/* 模块出口函数 */
static void yl_vivi_exit(void)
{
video_unregister_device(yl_vivi_dev->video_device);
video_device_release(yl_vivi_dev->video_device);
kfree(yl_vivi_dev);
}
module_init(yl_vivi_init);
module_exit(yl_vivi_exit);
MODULE_LICENSE("GPL");