虚拟摄像头驱动vivi的简单实现

版权声明:本文为博主原创文章,如有需要, 请注明转载地址:http://blog.csdn.net/tech_pro。若是侵权用于商业用途,请联系博主,否则将追究责任。 https://blog.csdn.net/TECH_PRO/article/details/79572670

一、vivi摄像头驱动基本框架

  1.     分配一个video_device结构体变量
  2.     设置这个结构体变量
  3.     注册这个结构体变量

二、vivi摄像头驱动数据的获取过程

  1.     请求分配缓冲区
  2.     查询缓冲区,并为缓冲区分配空间
  3.     将缓冲区放入队列
  4.     启动摄像头
  5.     通过poll机制来对查询是否有数据,如果有数据可以通过定时器或者内核线程来唤醒它
  6.     将数据从缓冲区中取出,并将这个缓冲区从队列中删除
  7.     判断摄像头是否继续工作,如果继续工作则重复执行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这个结构体的具体定义和成员函数的实现如下:

扫描二维码关注公众号,回复: 3849979 查看本文章
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");

猜你喜欢

转载自blog.csdn.net/TECH_PRO/article/details/79572670