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