myvivi.c

insmod:error inserting ‘myvivi.ko’:-1 Invalid parameters	原因:
video_register_device
	video_register_device_index
		/* the release callback MUST be present */
		WARN_ON(!vdev->release);
		if (!vdev->release)
			return -EINVAL;
故video_device的release成员必须赋值
——————/
#sudo insmod myvivi.ko
Killed
#dmesg
BUG:unable to handle kernel NULL pointer dereference at 00000014 原因:
video_register_device
	video_register_device_index
		if (vdev->fops->unlocked_ioctl)
			…………
		//此处还未给fops赋值,故空指针,添加myvivi_device->fops = &myvivi_fops后注册成功
——————/
buf_release在源码videobuf-core.c中:
videobuf_queue_cancel (struct videobuf_queue *q)
	for (i = 0; i < VIDEO_MAX_FRAME; i++)	//32
		q->ops->buf_release(q, q->bufs[i]);	
		// struct videobuf_buffer *bufs[VIDEO_MAX_FRAME];
——————/
struct videobuf_vmalloc_memory
{
	u32                 magic;
	void                *vmalloc;
	struct vm_area_struct *vma;
};

videobuf_vmalloc_free (struct videobuf_buffer *buf)
	struct videobuf_vmalloc_memory *mem = buf->priv;
	vfree(mem->vmalloc);
	mem->vmalloc = NULL;
——————/
myvivi_poll
	videobuf_poll_stream(struct file *file,struct videobuf_queue *q, poll_table *wait)
		if (!list_empty(&q->stream))
			buf = list_entry(q->stream.next,struct videobuf_buffer, stream);	
			//从链表头部获得一videobuf_buffer
			poll_wait(file, &buf->done, wait);
			//没数据在buf->done上休眠
myvivi提供poll函数,但未构造数据从buf->done唤醒进程,此时测试效果select timeout
——————/
for
{
	videobuf_qbuf
		list_add_tail(&buf->stream, &q->stream);
		q->ops->buf_queue(q, buf);
			list_add_tail(&vb->queue, &myvivi_vb_local_queue);
}
效果:
struct videobuf_buffer *vb = list_entry(myvivi_vb_local_queue.next,struct videobuf_buffer, queue);
myvivi_fillbuff(vb);
list_del(&vb->queue);
wake_up(&vb->done);
从local_queue队列头部获得buf 1,填充数据后从队列中删除,并唤醒阻塞在done上的进程,效果:

阻塞进程唤醒后,APP执行VIDIOC_DQBUF命令,从队列头部获得buf 1
videobuf_dqbuf
	stream_next_buffer
		struct videobuf_buffer *buf=list_entry(q->stream.next, struct videobuf_buffer, stream);
	list_del(&buf->stream);
效果:

APP处理完buf 1,又调用命令VIDIOC_QBUF,效果:

周而复始
——————/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;
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;
static struct v4l2_device v4l2_dev;

/* 参考v4l2-framework.txt、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. */

/* APP调用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;	//用于初始化videobuf_buffer的bsize成员
	if (0 == *count)
		*count = 32;
	return 0;
}

/*APP调用VIDIOC_QBUF导致此函数被调用,它会填充video_buffer结构体*/
static int myvivi_buffer_prepare(struct videobuf_queue *vq, 
	struct videobuf_buffer *vb,enum v4l2_field field)
{
    /* 设置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;
    /* 设置状态 */
	vb->state = VIDEOBUF_PREPARED;
	return 0;
}

/* APP调用VIDIOC_QBUF时,先调用buf_prepare,然后把buf放入videobuf_queue队列,再调用buf_queue
videobuf_qbuf(struct videobuf_queue *q,struct v4l2_buffer *b)
	q->ops->buf_prepare(q, buf, field);
	list_add_tail(&buf->stream, &q->stream);
	q->ops->buf_queue(q, buf);	*/
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,
};

static int myvivi_open(struct file *file)
{
    /* 初始化队列 */
	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,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 = 1;
	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 0;
}

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,
    .unlocked_ioctl      = video_ioctl2,
    .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;
    
    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;

    vbuf = videobuf_to_vmalloc(vb); //虚拟地址
    memset(vbuf, 0, vb->size);
    vb->field_count++;
    do_gettimeofday(&ts);
    vb->ts = ts;
    vb->state = VIDEOBUF_DONE;

    list_del(&vb->queue);
    wake_up(&vb->done);
out:
    mod_timer(&myvivi_timer, jiffies + HZ/30);    /*30fps*/
}

static int myvivi_init(void)
{
    int error;
    myvivi_device = video_device_alloc();
    myvivi_device->release = myvivi_release;
    myvivi_device->fops    = &myvivi_fops;
    myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
    spin_lock_init(&myvivi_queue_slock);
    error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1);	
	//	-1含义/dev/video[first free]

	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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值