本篇仿照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进行一些准备工作
 * 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 + 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进行一些准备工作
 * 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 + 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

Linux摄像头驱动学习之:(三)从零写虚拟驱动(仿照vivi.c)的更多相关文章

  1. Linux内核驱动学习(三)字符型设备驱动之初体验

    Linux字符型设备驱动之初体验 文章目录 Linux字符型设备驱动之初体验 前言 框架 字符型设备 程序实现 cdev kobj owner file_operations dev_t 设备注册过程 ...

  2. IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试

    IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...

  3. Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析

    SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈.可以实现用户主平台数据通过SDIO口到无线网络之间的转 ...

  4. 嵌入式Linux驱动学习之路(二十五)虚拟网卡驱动程序

    一.协议栈层次对比 设备无关层到驱动层的体系结构 1).网络协议接口层向网络层协议提供提供统一的数据包收发接口,不论上层协议为ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过 ...

  5. 27、从零写UVC驱动之分析数据传输(设置ubuntu通过串口打印,指定打印到文件,ubuntu切换root用户)

    A. 设置ubuntu让它从串口0输出printk信息a. 设置vmware添加serial port, 使用文件作为串口(在vmware中设置,文件是保存在windows中)b. 启动ubuntu, ...

  6. Linux网络课程学习第三天

    第三天在线视频学习. 学习内容:继续详细介绍了第二章节 Linux常用命令的使用方法. 学习感受:万事开头难,作为Linux零基础的我相信在这本书学完之后会有所收获. 学习心得:记住刘老师的一句口头禅 ...

  7. 嵌入式Linux驱动学习之路(二十)USB设备驱动

    USB在接入系统的时候,以0的设备ID和主机通信,然后由主机为其分配新的ID. 在主机端,D+和D-都是下拉接地的.而设备端的D-接上拉时,表明此设备为高速设备:12M/s. D+接上拉时则是全速设备 ...

  8. Linux USB驱动学习总结(二)---- USB设备驱动

    USB 设备驱动: 一.USB 描述符:(存在于USB 的E2PROM里面) 1.  设备描述符:struct usb_device_descriptor 2.  配置描述符:struct usb_c ...

  9. 鸟哥的linux私房菜学习-(三)X Window与文本模式的切换

    通常我们也称文本模式为终端机接口, terminal 或 console喔!Linux默认的情况下会提供六个Terminal来让使用者登陆, 切换的方式为使用:[Ctrl] + [Alt] + [F1 ...

随机推荐

  1. web移动前端的click点透问题

    在移动端开发中,有时会出现click点透的问题. 一.什么是click点透 以下情况,在B元素上有半透明红色遮盖层A,黄色B元素内有可点击链接C. tips:以下举例仅针对webkit内核浏览器,所有 ...

  2. solr 4.6配置正解

    最近在学习solr,可是在网上找了很多个配置的资料,要不就是solr版本不对,反正各种问题.最后终于出来了,在这里给大家分享一下 1.准备工作 我们要先去下载一个tomcat,我下载的版本是tomca ...

  3. Balanced Lineup(树状数组 POJ3264)

    Balanced Lineup Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 40493 Accepted: 19035 Cas ...

  4. iOS开发之生成二维码

    一.二维码的生成 从iOS7开始集成了二维码的生成和读取功能 此前被广泛使用的zbarsdk目前不支持64位处理器   1.二维码的内容(传统的条形码只能放数字) 纯文本 名片 URL   2.生成二 ...

  5. CSS布局基础之二认识Viewport

    什么是viewport viewport,等同于浏览器窗口. 功能:约束你网站中最顶级包含块(containing block)元素html标签. 什么是包含块(containing block)?下 ...

  6. XAF学习资源整合大全

    近期有很多XAF初学者与我联系,我多数时间在重复很多入门问题,所以决定整理一篇XAF资源列表,方便大家查找资料,也请知晓其他资源的人留言或与我联系,我将新资源追加到本篇文章中,方便更多人. 一.本博客 ...

  7. jquery总结01-基本概念和选择器

    dom元素和jquery元素的区别 dom元素支持dom元素自带的属性和方法,jquery元素支持jquery元素自带的属性和方法 dom                  var div = doc ...

  8. viewport和media query

    viewport: 你可以定义viewport的宽度.如果你不使用width=device-width,在移动端上你的页面延伸会超过视窗布局的宽度(width=980px),如果你使用了width=d ...

  9. 和Java相关的一些好文章(不定期更新)

    1.Java 集合类详解 (包括arraylist,linkedlist,vector,stack,hashmap,hashtable,treemap,collection等). 2.Java 理论与 ...

  10. python decorator的理解

    一.decorator的作用 装饰器本质上是一个Python函数,可以让其他函数在不做任何代码变动的前提下增加额外功能. 装饰器的返回值也是一个函数对象.python里函数也是对象. 它经常用于有切面 ...