天天看点

从vivi学习V4L2架构(十一):stream off

一、Streamoff应用层代码

enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0)
     printf("VIDIOC_STREAMOFF fail\n");
           

二、Streamoff底层调用流程

ioctl进过一些调用进入__video_do_ioctl函数

static long __video_do_ioctl(struct file *file,
		unsigned int cmd, void *arg)
{
	struct video_device *vfd = video_devdata(file);
	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
	void *fh = file->private_data;
	struct v4l2_fh *vfh = NULL;
	struct v4l2_format f_copy;
	int use_fh_prio = 0;
	long ret = -EINVAL;
... ...

	case VIDIOC_STREAMOFF:
	{
		enum v4l2_buf_type i = *(int *)arg;

		if (!ops->vidioc_streamoff)
			break;
		dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
        //直接调用video_device.ioctl_ops的vidioc_streamoff
		ret = ops->vidioc_streamoff(file, fh, i);
		break;
	}
... ...
}
           

接着看video_device.ioctl_ops的vidioc_streamoff

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

int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
{
	if (q->fileio) {
		dprintk(1, "streamoff: file io in progress\n");
		return -EBUSY;
	}

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

	if (!q->streaming) {
		dprintk(1, "streamoff: not streaming\n");
		return -EINVAL;
	}

	/*
	 * Cancel will pause streaming and remove all buffers from the driver
	 * and videobuf, effectively returning control over them to userspace.
	 */
    //__vb2_queue_cancel主要工作如上面英文注释,暂停stream并从驱动程序和videobuf中
    //删除所有缓冲区,从而有效地将对它们的控制权返回给用户空间。
	__vb2_queue_cancel(q);

	dprintk(3, "Streamoff successful\n");
	return 0;
}
EXPORT_SYMBOL_GPL(vb2_streamoff);
           

进去看下__vb2_queue_cancel

static void __vb2_queue_cancel(struct vb2_queue *q)
{
	unsigned int i;

	/*
	 * Tell driver to stop all transactions and release all queued
	 * buffers.
	 */
    //先去stop_streaming
	if (q->streaming)
		call_qop(q, stop_streaming, q);
    //将streaming赋值为0
	q->streaming = 0;

	/*
	 * Remove all buffers from videobuf's list...
	 */
    //将vb2_queue的queued_list链表上所有链表节点删除掉
	INIT_LIST_HEAD(&q->queued_list);

	/*
	 * ...and done list; userspace will not receive any buffers it
	 * has not already dequeued before initiating cancel.
	 */

    //将vb2_queue的done_list链表上所有链表节点删除掉
	INIT_LIST_HEAD(&q->done_list);
	atomic_set(&q->queued_count, 0);

    //这里又把done_wq上所有的等待work全部唤醒
	wake_up_all(&q->done_wq);

	/*
	 * Reinitialize all buffers for next use.
	 */
    //重现将vb2_buffer的状态设置为VB2_BUF_STATE_DEQUEUED
	for (i = 0; i < q->num_buffers; ++i)
		q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
}
           

stop_streaming

static int stop_streaming(struct vb2_queue *vq)
{
	struct vivi_dev *dev = vb2_get_drv_priv(vq);
	dprintk(dev, 1, "%s\n", __func__);
	vivi_stop_generating(dev);
	return 0;
}

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

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

	/* shutdown control thread */
    //这里停掉dma_q->kthread,也就是之前创建的vivi_thread
	if (dma_q->kthread) {
		kthread_stop(dma_q->kthread);
		dma_q->kthread = NULL;
	}

	/*
	 * Typical driver might need to wait here until dma engine stops.
	 * In this case we can abort imiedetly, so it's just a noop.
	 */

	/* Release all active buffers */
    //如果dma_q->active链表不为空,则循环从active链表删除掉vivi_buffer
	while (!list_empty(&dma_q->active)) {
		struct vivi_buffer *buf;
		buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
		list_del(&buf->list);
        //将vb->state设置为VB2_BUF_STATE_ERROR并将vb2_buffer->done-entry加入到
        //vb2_queue->done_list链表中
		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
		dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
	}
}
           

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;

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

	if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
		return;

	dprintk(4, "Done processing on buffer %d, state: %d\n",
			vb->v4l2_buf.index, vb->state);

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

	/* Inform any processes that may be waiting for buffers */
    //唤醒在done_wq上等待数据的,也就是之前的DQBUF流程
	wake_up(&q->done_wq);
}
           

总结:streamoff 底层需要做的事情就是将streamon创建的vivi_thread给停下来,再从vidq->active链表中将还未填数据的vivi_buffer删除掉,并将vb2_buffer加入到vb2_queue的done_list链表中,唤醒done_wq去处理。 唤醒done_wq其实就是将vb2_buffer从done_list里面取出并删除,并把vb2_buffer的信息返回给上层应用,还有将vb2_buffer从vb2_queue->queued_list链表中删除。最后将b2_queue的queued_list、done_list两个链表重新初始化, vb2_buffer的state重新初始化为VB2_BUF_STATE_DEQUEUED。

应用层最后还需调用munmap, 取消内存映射涉及mm模块的代码不清楚,而驱动层没有对应的驱动代码就贴一下应有层代码。

for (i = 0; i < req.count; ++i)
{
    if (-1 == munmap (buffers[i].start, buffers[i].length))
        printf ("munmap error");
}
           

继续阅读