虚拟视频驱动vivi.c分析

  • Post author:
  • Post category:其他


——————/内核版本2.6.31

分析哪些ioctl是必须的,分析应用程序如何获得摄像头数据

root@lyl:~# modprobe vivi

从xawtv的main开始分析涉及哪些系统调用,但该过程太复杂,采用下法:

[root@localhost xm]# strace -o xawtv.log xawtv //260k

xawtv涉及系统调用:

1. open

2. VIDIOC_QUERYCAP

3. for VIDIOC_ENUMINPUT

4. for VIDIOC_ENUMSTD

5. for VIDIOC_ENUM_FMT

6. VIDIOC_G_PARM

7. for VIDIOC_QUERYCTRL //查询属性(如亮度最大值、最小值)

8. VIDIOC_G_STD

9. VIDIOC_G_INPUT

10. VIDIOC_G_CTRL

11. VIDIOC_TRY_FMT

12. VIDIOC_S_FMT

13. VIDIOC_REQBUFS

14. for VIDIOC_QUERYBUF mmap

15. for VIDIOC_QBUF

16. VIDIOC_STREAMON

17. for VIDIOC_S_CTRL、VIDIOC_S_INPUT 、VIDIOC_S_STD

18. for select、VIDIOC_DQBUF、VIDIOC_QBUF

循环将缓冲区放入队列,select查询,若有数据再取出队列,处理完再放入队列

——————/精简ioctl,保留最小集合

static const struct v4l2_ioctl_ops 	vivi_ioctl_ops = {
	.vidioc_querycap      = vidioc_querycap, //缺少则不知设备类型
	//列举、获得、测试、设置摄像头数据的格式,必不可少
	.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
	//申请、查询、入列、出列:核心
	.vidioc_reqbufs       = vidioc_reqbufs,
	.vidioc_querybuf      = vidioc_querybuf,
	.vidioc_qbuf          = vidioc_qbuf,
	.vidioc_dqbuf         = vidioc_dqbuf,
	.vidioc_streamon      = vidioc_streamon,	
	.vidioc_streamoff     = vidioc_streamoff,
/*
	.vidioc_s_std         = vidioc_s_std,
	.vidioc_enum_input    = vidioc_enum_input,	
	//枚举输入源,去掉xawtv界面video source变为unknown,之前为Camera 0-3
	.vidioc_g_input       = vidioc_g_input,
	.vidioc_s_input       = vidioc_s_input,
	.vidioc_log_status    = v4l2_ctrl_log_status,
	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
*/
};

——————/

VIDIOC_ENUMSTD感觉制式对模拟量而言,对数字摄像头无用处

case VIDIOC_ENUMSTD:	//v4l2_ioctl.c
	v4l2_std_id id = vfd->tvnorms
	if (id == 0)
		break;
case VIDIOC_G_STD:
	if (ops->vidioc_g_std)
		ret = ops->vidioc_g_std(file, fh, id);
	else if (vfd->current_norm) 
	{
		ret = 0;
		*id = vfd->current_norm;
	}

struct 	video_device 	vivi_template = {
…………
	.tvnorms              = V4L2_STD_525_60,
	.current_norm         = V4L2_STD_NTSC_M,
};

将上两项去掉,xawtv界面TV norm变为unknown但不影响,故:

.vidioc_s_std = vidioc_s_std, 也可去掉

——————/

将vidioc_queryctrl、vidioc_g_ctrl、vidioc_s_ctrl去掉后



——————/数据获取过程:

1、VIDIOC_REQBUFS

vidioc_reqbufs
	videobuf_reqbufs(&fh->vb_vidq, p)	
		videobuf_mmap_setup
			videobuf_alloc
				kzalloc(sizeof(struct vivi_buffer),GFP_KERNEL);
					struct videobuf_buffer vb;

	/*	(struct videobuf_queue *q 、struct v4l2_requestbuffers *  req)
	vb_vidq在open函数被videobuf_queue_init初始化
	v4l2_requestbuffers包含所要求分配的缓冲区数目,至少为2,
	但没包含缓冲区大小,因真正缓存还未分配(分配count* sizeof(struct vivi_buffer))	*/

2、VIDIOC_QUERYBUF

videobuf_querybuf
	videobuf_status (struct videobuf_queue *q, struct v4l2_buffer *b,
			    struct videobuf_buffer *vb, enum v4l2_buf_type type)
		b->length    = vb->bsize;
		b->m.offset  = vb->boff;
		b->type     = type;
		……

v4l2_mmap		//(参数含"大小")此处真正分配内存
	vivi_mmap
		videobuf_mmap_mapper
			CALL(q, mmap_mapper, q, vma);
				__videobuf_mmap_mapper
					struct videobuf_vmalloc_memory *mem;
					mem = q->bufs[first]->priv;
					pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
					mem->vmalloc = vmalloc_user(pages); 

3、VIDIOC_QBUF

videobuf_qbuf(struct videobuf_queue *q,struct v4l2_buffer *b)
	struct videobuf_buffer *buf;
	buf = q->bufs[b->index];
	retval = q->ops->buf_prepare(q, buf, field);		//预处理
	list_add_tail(&buf->stream, &q->stream);
	q->ops->buf_queue(q, buf);

4、VIDIOC_STREAMON

videobuf_streamon(struct videobuf_queue *q)
	q->streaming = 1;	//标志位

5、select查询是否有数据

vivi_poll
	videobuf_poll_stream(struct file *file,struct videobuf_queue *q,poll_table *wait)
		struct videobuf_buffer *buf = NULL;
		//从队列头部获得一个缓冲区
		buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
		//如缓冲区没数据则休眠                			
		poll_wait(file, &buf->done, wait);

谁产生数据、唤醒?
内核线程vivi_thread每30MS执行一次,调用vivi_thread_tick
vivi_thread_tick
	vivi_fillbuff(fh, buf);  	// 构造数据
	wake_up(&buf->vb.done);  // 唤醒进程

6、有数据VIDIOC_DQBUF

videobuf_dqbuf
retval = stream_next_buffer(q, &buf, nonblocking);	
list_del(&buf->stream);	//在队列里获得有数据的缓冲区并删掉
	videobuf_status(q, b, buf, q->type);  //将该缓冲区状态返回给APP

7、APP根据VIDIOC_DQBUF,知哪一缓冲区有数据就去读对应地址

怎么写摄像头驱动:

1、分配video_device : video_device_alloc //struct v4l2_device不重要

2、设置

.fops

.ioctl_ops (11项)

如要用内核提供的缓冲区操作函数,还需构造一videobuf_queue_ops

3、注册video_register_device



版权声明:本文为u013388374原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。