(三)从零写虚拟驱动(仿照vivi.c)

本文档详细介绍了如何从零开始编写一个虚拟视频驱动,模仿vivi.c,涵盖了video_device结构体的分配与初始化、videobuf_queue的配置、以及V4L2框架相关操作函数的实现,包括缓冲区的分配、准备、队列管理和释放等。此外,还涉及定时器的使用来模拟视频数据的生成和通知机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(三)从零写虚拟驱动(仿照vivi.c)
转载地址:https://www.cnblogs.com/blogs-of-lxl/p/4983737.html
本篇仿照vivi.c 写虚拟视频驱动,代码(myvivi.c+fillbuf.c+Makefile)如下:
//myvivi.c=============
/* 仿照vivi.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;

static struct timer_list myvivi_timer;

#include “fillbuf.c”

/* 参考documentations/video4linux/v4l2-framework.txt:
drivers\media\video\videobuf-core.c
ops->buf_setup - calculates the size of the video buffers and avoid they
to waste more than some maximum limit of RAM;
ops->buf_prepare - fills the video buffer structs and calls
videobuf_iolock() to alloc and prepare mmaped memory;
ops->buf_queue - advices the driver that another buffer were
requested (by read() or by QBUF);
ops->buf_release - frees any buffer that were allocated.
*/

/* ------------------------------------------------------------------
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 (0 == *count)
*count = 32;

return 0;
}

/* 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. 做些准备工作 */
myvivi_precalculate_bars(0);

#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 < 0)
goto fail;
}
#endif
/
3. 设置状态 */
vb->state = VIDEOBUF_PREPARED;

return 0;
}

/* APP调用ioctl VIDIOC_QBUF时:

    1. 先调用buf_prepare进行一些准备工作
    1. 把buf放入stream队列
    1. 调用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 + 1;
add_timer(&myvivi_timer);

return 0;
}

static int myvivi_close(struct file *file)
{
del_timer(&myvivi_timer);
videobuf_stop(&myvivi_vb_vidqueue);
videobuf_mmap_free(&myvivi_vb_vidqueue);

return 0;
}

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 0;
}

/* 列举支持哪种格式 */
static int myvivi_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 myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
memcpy(f, &myvivi_format, sizeof(myvivi_format));
return (0);
}

/* 测试驱动程序是否支持某种格式 */
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  = 1024;
	 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 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 < 0)
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 0;
}

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)) {
    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, 0xff, vb->size);
myvivi_fillbuff(vb);

vb->field_count++;
do_gettimeofday(&ts);
vb->ts = ts;
vb->state = VIDEOBUF_DONE;

/* 1.3 把videobuf从本地队列中删除 */
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/30);
}

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, -1);

/* 用定时器产生数据并唤醒进程 */

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”);

//fillbuf.c=============
/* 仿照vivi.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;

static struct timer_list myvivi_timer;

#include “fillbuf.c”

/* 参考documentations/video4linux/v4l2-framework.txt:

  • drivers\media\video\videobuf-core.c
    

ops->buf_setup - calculates the size of the video buffers and avoid they
to waste more than some maximum limit of RAM;
ops->buf_prepare - fills the video buffer structs and calls
videobuf_iolock() to alloc and prepare mmaped memory;
ops->buf_queue - advices the driver that another buffer were
requested (by read() or by QBUF);
ops->buf_release - frees any buffer that were allocated.

*/

/* ------------------------------------------------------------------
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 (0 == *count)
*count = 32;

return 0;
}

/* 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. 做些准备工作 */
myvivi_precalculate_bars(0);

#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 < 0)
goto fail;
}
#endif
/
3. 设置状态 */
vb->state = VIDEOBUF_PREPARED;

return 0;
}

/* APP调用ioctl VIDIOC_QBUF时:

    1. 先调用buf_prepare进行一些准备工作
    1. 把buf放入stream队列
    1. 调用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 + 1;
add_timer(&myvivi_timer);

return 0;
}

static int myvivi_close(struct file *file)
{
del_timer(&myvivi_timer);
videobuf_stop(&myvivi_vb_vidqueue);
videobuf_mmap_free(&myvivi_vb_vidqueue);

return 0;
}

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 0;
}

/* 列举支持哪种格式 */
static int myvivi_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 myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
memcpy(f, &myvivi_format, sizeof(myvivi_format));
return (0);
}

/* 测试驱动程序是否支持某种格式 */
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 = 1024;
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 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 < 0)
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 0;
}

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)) {
    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, 0xff, vb->size);
myvivi_fillbuff(vb);

vb->field_count++;
do_gettimeofday(&ts);
vb->ts = ts;
vb->state = VIDEOBUF_DONE;

/* 1.3 把videobuf从本地队列中删除 */
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/30);
}

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, -1);

/* 用定时器产生数据并唤醒进程 */

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”);

Makefile=============

KERN_DIR = /usr/src/linux-headers-2.6.31-14-generic

all:
make -C $(KERN_DIR) M=pwd modules

clean:
make -C $(KERN_DIR) M=pwd modules clean
rm -rf modules.order

obj-m += myvivi.o

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值