天天看點

從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");
}
           

繼續閱讀