从应用调用vivi驱动分析v4l2 -- 运行设备(VIDIOC_STREAMON)

Linux v4l2架构学习总链接

vivi代码

v4l2测试代码

step 6 : 运行设备

enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
{
    printf("ERR(%s):VIDIOC_STREAMON failed\n", __func__);
    return -1;
}

调用vidioc_streamon

vidioc_streamon

      -> vb2_streamon

            -> vb2_core_streamon


static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_streamon(&dev->vb_vidq, i);
}

int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
{
	return vb2_core_streamon(q, type);
}

int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
{
	int ret;

	if (type != q->type) {
		dprintk(1, "invalid stream type\n");
		return -EINVAL;
	}


        /* 
         * 如果已经启动了,则这里不再继续执行 
         * start_streaming_called 不用考虑???
         * 在qbuf的时候有2种情况
         * 1. q->start_streaming_called = 1
         * 2. q->start_streaming_called = 0, q->streaming = 1
         * 情况1,说明其他应用已经正常抓图了,也就是底层驱动start_streaming执行没问题
         * 情况2,说明底层驱动start_streaming执行失败了,这时候会继续执行stream_on
         *       只有stream_on没有问题,qbuf才会返回正常值给应用
         *       所以需要应用把关,如果返回值错了,就不要向下走了
         * 所以这里也可以知道 如果q->streaming = 1
         * 那么q->start_streaming_called = 1
         * 这里就可以直接返回了
         */


	if (q->streaming) {
		dprintk(3, "already streaming\n");
		return 0;
	}

	if (!q->num_buffers) {
		dprintk(1, "no buffers have been allocated\n");
		return -EINVAL;
	}

	if (q->num_buffers < q->min_buffers_needed) {
		dprintk(1, "need at least %u allocated buffers\n",
				q->min_buffers_needed);
		return -EINVAL;
	}

        

	/*
	 * Tell driver to start streaming provided sufficient buffers
	 * are available.
	 */


        /*
         * 这个if语句上一篇文章提了一句
         * 就是缓冲区不够,无法启动
         * 导致 q->streaming = 1 
         * start_streaming_called = 0
         * 
         */

	if (q->queued_count >= q->min_buffers_needed) {

            
                /*
                 * 这里牵扯到使用media
                 * 因为vivi没有使用,所以这里暂时跳过
                 * 后面再分析
                 */


		ret = v4l_vb2q_enable_media_source(q);
		if (ret)
			return ret;
		ret = vb2_start_streaming(q);
		if (ret)
			return ret;
	}


        /*
         * 执行了stream_on的标志
         */

	q->streaming = 1;
	return 0;
}

vidioc_streamon

      -> vb2_streamon

            -> vb2_core_streamon

                   -> vb2_start_streaming


static int vb2_start_streaming(struct vb2_queue *q)
{
	struct vb2_buffer *vb;
	int ret;

	/*
	 * If any buffers were queued before streamon,
	 * we can now pass them to driver for processing.
	 */


        /*
         * 之前分析过__enqueue_in_driver
         * 当时的情况是在本次应用QBUF的时候,发现操作的video节点被其他的应用打开
         * 且正在抓图
         * 这种情况下__enqueue_in_driver会被调用
         * 现在看来在stream_on的时候,还会被再次调用
         * 主意这里不仅仅是当前应用的buffer,还有其他操作该video节点应用的buffer
         * 底层驱动是不是要判断一下,防止被多次添加
         * 另外q->owned_by_drv_count的值一直在增加
         * 对于owned_by_drv_count最后是怎么判断?
         *       下面失败的时候,会dec,所以不用担心
         * 如果是ioctl(STREAM_ON)能走到这里,说明只有当前这一个应用在操作节点
         */


	list_for_each_entry(vb, &q->queued_list, queued_entry)
		__enqueue_in_driver(vb);

	/* Tell the driver to start streaming */



        /*
         * start_streaming_called
         * 记录的是底层驱动start_streaming被调用
         */


	q->start_streaming_called = 1;
	ret = call_qop(q, start_streaming, q,
		       atomic_read(&q->owned_by_drv_count));
	if (!ret)
		return 0;


        /*
         * 调用失败置0
         */


	q->start_streaming_called = 0;

	dprintk(1, "driver refused to start streaming\n");
	/*
	 * If you see this warning, then the driver isn't cleaning up properly
	 * after a failed start_streaming(). See the start_streaming()
	 * documentation in videobuf2-core.h for more information how buffers
	 * should be returned to vb2 in start_streaming().
	 */
	if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
		unsigned i;

		/*
		 * Forcefully reclaim buffers if the driver did not
		 * correctly return them to vb2.
		 */
		for (i = 0; i < q->num_buffers; ++i) {
			vb = q->bufs[i];
			if (vb->state == VB2_BUF_STATE_ACTIVE)
                                

                                /*
                                 * 这里面会把owned_by_drv_count进行减操作
                                 */
				vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
		}
		/* Must be zero now */
		WARN_ON(atomic_read(&q->owned_by_drv_count));
	}
	/*
	 * If done_list is not empty, then start_streaming() didn't call
	 * vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED) but STATE_ERROR or
	 * STATE_DONE.
	 */
	WARN_ON(!list_empty(&q->done_list));
	return ret;
}

vidioc_streamon

      -> vb2_streamon

            -> vb2_core_streamon

                   -> vb2_start_streaming

                        -> call_qop(start_streaming    ---  start_streaming


static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
	struct vivi_dev *dev = vb2_get_drv_priv(vq);
	dprintk(dev, 1, "%s\n", __func__);
	return vivi_start_generating(dev);
}

static int vivi_start_generating(struct vivi_dev *dev)
{
	struct vivi_dmaqueue *dma_q = &dev->vidq;

	dprintk(dev, 1, "%s\n", __func__);

	/* Resets frame counters */
	dev->ms = 0;
	dev->mv_count = 0;
	dev->jiffies = jiffies;

	dma_q->frame = 0;
	dma_q->ini_jiffies = jiffies;
	dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);

	if (IS_ERR(dma_q->kthread)) {
		v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
		return PTR_ERR(dma_q->kthread);
	}
	/* Wakes thread */
	wake_up_interruptible(&dma_q->wq);

	dprintk(dev, 1, "returning from %s\n", __func__);
	return 0;
}

static void vivi_thread_tick(struct vivi_dev *dev)
{
	struct vivi_dmaqueue *dma_q = &dev->vidq;
	struct vivi_buffer *buf;
	unsigned long flags = 0;

	dprintk(dev, 1, "Thread tick\n");

	spin_lock_irqsave(&dev->slock, flags);
	if (list_empty(&dma_q->active)) {
		dprintk(dev, 1, "No active queue to serve\n");
		goto unlock;
	}


        /*
         * 取出active上的每一个buffer
         */


	buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
	list_del(&buf->list);

	buf->vb.timestamp = ktime_get_ns();

	/* Fill buffer */



        /*
         * 这里相当于填充帧数据
         */


	vivi_fillbuff(dev, buf);
	dprintk(dev, 1, "filled buffer %p\n", buf);

        
        /*
         * 这里最重要
         */

	vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
	dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.index);
unlock:
	spin_unlock_irqrestore(&dev->slock, flags);
}

vidioc_streamon

      -> vb2_streamon

            -> vb2_core_streamon

                   -> vb2_start_streaming

                        -> call_qop(start_streaming    ---  start_streaming

                              -> vb2_buffer_done

void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{
	struct vb2_queue *q = vb->vb2_queue;
	unsigned long flags;
	unsigned int plane;

	if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE))
		return;

	if (WARN_ON(state != VB2_BUF_STATE_DONE &&
		    state != VB2_BUF_STATE_ERROR &&
		    state != VB2_BUF_STATE_QUEUED &&
		    state != VB2_BUF_STATE_REQUEUEING))
		state = VB2_BUF_STATE_ERROR;

#ifdef CONFIG_VIDEO_ADV_DEBUG
	/*
	 * Although this is not a callback, it still does have to balance
	 * with the buf_queue op. So update this counter manually.
	 */
	vb->cnt_buf_done++;
#endif
	dprintk(4, "done processing on buffer %d, state: %d\n",
			vb->index, state);


        /*
         * 对于mmap来说
         * need_cache_sync_on_finish这个是1
         * 同样的need_cache_sync_on_prepare也是1,这个之前分析过
         * 也就是说填充完buffer后,需要调用驱动的finish进行同步
         * 这里对应vivi的buffer_finish,这里只是打印信息,所以忽略
         */


	if ((state != VB2_BUF_STATE_QUEUED &&
	     state != VB2_BUF_STATE_REQUEUEING) &&
	    vb->need_cache_sync_on_finish) {
		for (plane = 0; plane < vb->num_planes; ++plane)
			call_void_memop(vb, finish, vb->planes[plane].mem_priv);
	}

	spin_lock_irqsave(&q->done_lock, flags);
	if (state == VB2_BUF_STATE_QUEUED ||
	    state == VB2_BUF_STATE_REQUEUEING) {
		vb->state = VB2_BUF_STATE_QUEUED;
	} else {

                /*
                 * 把buffer挂载到done_list链表上
                 */

		/* Add the buffer to the done buffers list */
		list_add_tail(&vb->done_entry, &q->done_list);
		vb->state = state;
	}
	atomic_dec(&q->owned_by_drv_count);
	spin_unlock_irqrestore(&q->done_lock, flags);

	trace_vb2_buf_done(q, vb);

	switch (state) {
	case VB2_BUF_STATE_QUEUED:
		return;
	case VB2_BUF_STATE_REQUEUEING:
		if (q->start_streaming_called)
			__enqueue_in_driver(vb);
		return;
	default:
		/* Inform any processes that may be waiting for buffers */


                /*
                 * 唤醒等待队列中的进程
                 * 一定要记住这句代码,后面会用到
                 */


		wake_up(&q->done_wq);
		break;
	}
}

 

 

你可能感兴趣的:(#)