title: 编写vivid
date: 2019/4/23 19:40:00
toc: true
---

编写vivid

新内核对video_buf的封装更好了,很多函数基本上套个名字就好了,这个可以参考

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

下面的分析是韦老师的2.x版本的,基本流程如下

1.注册平台设备和驱动;
2.probe()函数:
  a.分配video_device;
  b.设置video_device,包括:release、fops、ioctl_ops、v4l2_dev;
  c.注册设置video_device;
  d.其它:定义/初始化自旋锁/定时器;
3.填充操作函数v4l2_file_operations:
  a.open():初始buf化队列和设置定时器;
  b.close():删除定时器和释放buf队列;
  c.mmap():调用videobuf_mmap_mapper开辟虚拟内存;
  d.poll():调用videobuf_poll_stream实现poll机制非阻塞访问;
4.填充操作函数v4l2_ioctl_ops:
  前面介绍的11个必须ioctl,几乎都是调用内核提供的API;
5.填充操作函数videobuf_queue_ops:
  对buf进行一些操作;
6.填充数据:
  利用定时器,不断产生数据并唤醒进程,实现获取到图像采集数据;

细致流程如下

请求缓冲区 VIDIOC_QUERYBUF
    v4l2_fops
        v4l2_file_operations vivi_fops .unlocked_ioctl = video_ioctl2
            __video_do_ioctl
                ops->vidioc_querybuf(file, fh, p);      //.vidioc_querybuf      = vidioc_querybuf,
                    vidioc_querybuf
                        vb2_querybuf(&dev->vb_vidq, p)
查询缓冲区 VIDIOC_QUERYBUF  
    ops->vidioc_querybuf(file, fh, p)
        vb2_querybuf(&dev->vb_vidq, p)
            vb = q->bufs[b->index]
            __fill_v4l2_buffer(vb, b)  填充 v4l2_buffer  获得缓冲区的格式大小等信息
            
vivi_mmap
    v4l2_fops.v4l2_mmap  >  vdev->fops->mmap
        vivi_mmap
            vb2_mmap(&dev->vb_vidq, vma)
                ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma); 
                // 搜索
                    #define call_memop(q, op, args...)                  \
                        (((q)->mem_ops->op) ?                       \
                            ((q)->mem_ops->op(args)) : 0)

                        q->ops = &vivi_video_qops;
                        q->mem_ops = &vb2_vmalloc_memops;
                        
                也就是调用       vb2_vmalloc_memops.mmap
                    vb2_vmalloc_mmap
                        remap_vmalloc_range

放入队列 VIDIOC_QBUF
    ops->vidioc_qbuf
        vb2_qbuf(&dev->vb_vidq, p)
                    call_qop(q, wait_prepare, q);
                    down_read(mmap_sem);
                    call_qop(q, wait_finish, q);
        list_add_tail(&vb->queued_entry, &q->queued_list);
        __enqueue_in_driver(vb);  这里执行驱动自身可能需要的入队列后的初始化
        __fill_v4l2_buffer(vb, b);

启动摄像头 VIDIOC_STREAMON       
    vb2_streamon        
        start_streaming
        q->streaming = 1;
        
        
v4l2_poll
    vdev->fops->poll
        vivi_poll
            搜索 wait
            队列  DECLARE_WAITQUEUE(wait, current);
            
            timeout = msecs_to_jiffies(frames_to_ms(1));
            vivi_thread_tick(dev);
                vb2_buffer_done
                    wake_up(&q->done_wq);  //唤醒
//创建了一个线程           
static int vivi_thread(void *data)
{
    vivi_sleep(dev);        //具体30ms唤醒一次
    
    #define frames_to_ms(frames)                    \
    ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)
#define WAKE_NUMERATOR 30

    
}   


VIDIOC_DQBUF  取出缓冲
ops->vidioc_dqbuf
    vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK)
        call_qop(q, buf_finish, vb)
        list_del(&vb->queued_entry);
        vb->state = VB2_BUF_STATE_DEQUEUED;
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄