——————/内核版本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,保留最小集合
分析哪些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 版权协议,转载请附上原文出处链接和本声明。