summaryrefslogtreecommitdiffstats
path: root/drivers/media/v4l2-core/videobuf2-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/v4l2-core/videobuf2-core.c')
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c129
1 files changed, 85 insertions, 44 deletions
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 7c4489c42365..c359006074a8 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -576,6 +576,7 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer
static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
unsigned int length;
+ unsigned int bytesused;
unsigned int plane;
if (!V4L2_TYPE_IS_OUTPUT(b->type))
@@ -583,21 +584,24 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
for (plane = 0; plane < vb->num_planes; ++plane) {
- length = (b->memory == V4L2_MEMORY_USERPTR)
+ length = (b->memory == V4L2_MEMORY_USERPTR ||
+ b->memory == V4L2_MEMORY_DMABUF)
? b->m.planes[plane].length
: vb->v4l2_planes[plane].length;
+ bytesused = b->m.planes[plane].bytesused
+ ? b->m.planes[plane].bytesused : length;
if (b->m.planes[plane].bytesused > length)
return -EINVAL;
if (b->m.planes[plane].data_offset > 0 &&
- b->m.planes[plane].data_offset >=
- b->m.planes[plane].bytesused)
+ b->m.planes[plane].data_offset >= bytesused)
return -EINVAL;
}
} else {
length = (b->memory == V4L2_MEMORY_USERPTR)
? b->length : vb->v4l2_planes[0].length;
+ bytesused = b->bytesused ? b->bytesused : length;
if (b->bytesused > length)
return -EINVAL;
@@ -1234,35 +1238,6 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
unsigned int plane;
if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
- /* Fill in driver-provided information for OUTPUT types */
- if (V4L2_TYPE_IS_OUTPUT(b->type)) {
- bool bytesused_is_used;
-
- /* Check if bytesused == 0 for all planes */
- for (plane = 0; plane < vb->num_planes; ++plane)
- if (b->m.planes[plane].bytesused)
- break;
- bytesused_is_used = plane < vb->num_planes;
-
- /*
- * Will have to go up to b->length when API starts
- * accepting variable number of planes.
- *
- * If bytesused_is_used is false, then fall back to the
- * full buffer size. In that case userspace clearly
- * never bothered to set it and it's a safe assumption
- * that they really meant to use the full plane sizes.
- */
- for (plane = 0; plane < vb->num_planes; ++plane) {
- struct v4l2_plane *pdst = &v4l2_planes[plane];
- struct v4l2_plane *psrc = &b->m.planes[plane];
-
- pdst->bytesused = bytesused_is_used ?
- psrc->bytesused : psrc->length;
- pdst->data_offset = psrc->data_offset;
- }
- }
-
if (b->memory == V4L2_MEMORY_USERPTR) {
for (plane = 0; plane < vb->num_planes; ++plane) {
v4l2_planes[plane].m.userptr =
@@ -1279,6 +1254,28 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
b->m.planes[plane].length;
}
}
+
+ /* Fill in driver-provided information for OUTPUT types */
+ if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+ /*
+ * Will have to go up to b->length when API starts
+ * accepting variable number of planes.
+ *
+ * If bytesused == 0 for the output buffer, then fall
+ * back to the full buffer size. In that case
+ * userspace clearly never bothered to set it and
+ * it's a safe assumption that they really meant to
+ * use the full plane sizes.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ struct v4l2_plane *pdst = &v4l2_planes[plane];
+ struct v4l2_plane *psrc = &b->m.planes[plane];
+
+ pdst->bytesused = psrc->bytesused ?
+ psrc->bytesused : pdst->length;
+ pdst->data_offset = psrc->data_offset;
+ }
+ }
} else {
/*
* Single-planar buffers do not use planes array,
@@ -1286,15 +1283,9 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
* In videobuf we use our internal V4l2_planes struct for
* single-planar buffers as well, for simplicity.
*
- * If bytesused == 0, then fall back to the full buffer size
- * as that's a sensible default.
+ * If bytesused == 0 for the output buffer, then fall back
+ * to the full buffer size as that's a sensible default.
*/
- if (V4L2_TYPE_IS_OUTPUT(b->type))
- v4l2_planes[0].bytesused =
- b->bytesused ? b->bytesused : b->length;
- else
- v4l2_planes[0].bytesused = 0;
-
if (b->memory == V4L2_MEMORY_USERPTR) {
v4l2_planes[0].m.userptr = b->m.userptr;
v4l2_planes[0].length = b->length;
@@ -1304,6 +1295,13 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
v4l2_planes[0].m.fd = b->m.fd;
v4l2_planes[0].length = b->length;
}
+
+ if (V4L2_TYPE_IS_OUTPUT(b->type))
+ v4l2_planes[0].bytesused = b->bytesused ?
+ b->bytesused : v4l2_planes[0].length;
+ else
+ v4l2_planes[0].bytesused = 0;
+
}
/* Zero flags that the vb2 core handles */
@@ -1606,6 +1604,11 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
return -EINVAL;
}
+ if (q->error) {
+ dprintk(1, "fatal error occurred on queue\n");
+ return -EIO;
+ }
+
vb->state = VB2_BUF_STATE_PREPARING;
vb->v4l2_buf.timestamp.tv_sec = 0;
vb->v4l2_buf.timestamp.tv_usec = 0;
@@ -1750,12 +1753,14 @@ static int vb2_start_streaming(struct vb2_queue *q)
__enqueue_in_driver(vb);
/* Tell the driver to start streaming */
+ q->start_streaming_called = 1;
ret = call_qop(q, start_streaming, q,
atomic_read(&q->owned_by_drv_count));
- q->start_streaming_called = ret == 0;
if (!ret)
return 0;
+ q->start_streaming_called = 0;
+
dprintk(1, "driver refused to start streaming\n");
if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
unsigned i;
@@ -1901,6 +1906,11 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
return -EINVAL;
}
+ if (q->error) {
+ dprintk(1, "Queue in error state, will not wait for buffers\n");
+ return -EIO;
+ }
+
if (!list_empty(&q->done_list)) {
/*
* Found a buffer that we were waiting for.
@@ -1926,7 +1936,8 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
*/
dprintk(3, "will sleep waiting for buffers\n");
ret = wait_event_interruptible(q->done_wq,
- !list_empty(&q->done_list) || !q->streaming);
+ !list_empty(&q->done_list) || !q->streaming ||
+ q->error);
/*
* We need to reevaluate both conditions again after reacquiring
@@ -2123,6 +2134,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
q->streaming = 0;
q->start_streaming_called = 0;
q->queued_count = 0;
+ q->error = 0;
/*
* Remove all buffers from videobuf's list...
@@ -2200,6 +2212,27 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
}
/**
+ * vb2_queue_error() - signal a fatal error on the queue
+ * @q: videobuf2 queue
+ *
+ * Flag that a fatal unrecoverable error has occurred and wake up all processes
+ * waiting on the queue. Polling will now set POLLERR and queuing and dequeuing
+ * buffers will return -EIO.
+ *
+ * The error flag will be cleared when cancelling the queue, either from
+ * vb2_streamoff or vb2_queue_release. Drivers should thus not call this
+ * function before starting the stream, otherwise the error flag will remain set
+ * until the queue is released when closing the device node.
+ */
+void vb2_queue_error(struct vb2_queue *q)
+{
+ q->error = 1;
+
+ wake_up_all(&q->done_wq);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_error);
+
+/**
* vb2_streamon - start streaming
* @q: videobuf2 queue
* @type: type argument passed from userspace to vidioc_streamon handler
@@ -2557,11 +2590,19 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
}
/*
- * There is nothing to wait for if no buffers have already been queued.
+ * There is nothing to wait for if no buffer has been queued and the
+ * queue isn't streaming, or if the error flag is set.
*/
- if (list_empty(&q->queued_list))
+ if ((list_empty(&q->queued_list) && !vb2_is_streaming(q)) || q->error)
return res | POLLERR;
+ /*
+ * For output streams you can write as long as there are fewer buffers
+ * queued than there are buffers available.
+ */
+ if (V4L2_TYPE_IS_OUTPUT(q->type) && q->queued_count < q->num_buffers)
+ return res | POLLOUT | POLLWRNORM;
+
if (list_empty(&q->done_list))
poll_wait(file, &q->done_wq, wait);