@@ -765,6 +765,10 @@ public:
{
return v4l_queue_reqbufs(fd->g_v4l_fd(), this, count, flags);
}
+ int remove_bufs(cv4l_fd *fd, unsigned index = 0, unsigned count = 0)
+ {
+ return v4l_queue_remove_bufs(fd->g_v4l_fd(), this, index, count);
+ }
bool has_create_bufs(cv4l_fd *fd) const
{
return v4l_queue_has_create_bufs(fd->g_v4l_fd(), this);
@@ -1547,12 +1547,29 @@ static inline void *v4l_queue_g_dataptr(const struct v4l_queue *q, unsigned inde
return v4l_queue_g_mmapping(q, index, plane);
}
-static inline int v4l_queue_querybufs(struct v4l_fd *f, struct v4l_queue *q, unsigned from)
+static inline int v4l_queue_remove_bufs(struct v4l_fd *f, struct v4l_queue *q, unsigned index, unsigned count)
{
- unsigned b, p;
+ struct v4l2_remove_buffers removebufs;
int ret;
- for (b = from; b < v4l_queue_g_buffers(q); b++) {
+ memset(&removebufs, 0, sizeof(removebufs));
+ removebufs.type = q->type;
+ removebufs.index = index;
+ removebufs.count = count;
+
+ ret = v4l_ioctl(f, VIDIOC_REMOVE_BUFS, &removebufs);
+ if (!ret)
+ q->buffers -= removebufs.count;
+
+ return ret;
+}
+
+static inline int v4l_queue_querybufs(struct v4l_fd *f, struct v4l_queue *q, unsigned from, unsigned count)
+{
+ unsigned b, p, max = from + count;
+ int ret;
+
+ for (b = from; b < max; b++) {
struct v4l_buffer buf;
v4l_buffer_init(&buf, v4l_queue_g_type(q), v4l_queue_g_memory(q), b);
@@ -1605,7 +1622,7 @@ static inline int v4l_queue_reqbufs(struct v4l_fd *f,
}
}
}
- return v4l_queue_querybufs(f, q, 0);
+ return v4l_queue_querybufs(f, q, 0, reqbufs.count);
}
static inline bool v4l_queue_has_create_bufs(struct v4l_fd *f, const struct v4l_queue *q)
@@ -1649,7 +1666,7 @@ static inline int v4l_queue_create_bufs(struct v4l_fd *f,
}
}
q->buffers += createbufs.count;
- return v4l_queue_querybufs(f, q, q->buffers - createbufs.count);
+ return v4l_queue_querybufs(f, q, createbufs.index, createbufs.count);
}
static inline int v4l_queue_mmap_bufs(struct v4l_fd *f,
@@ -1466,6 +1466,7 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
printf("Buffer ioctls%s:\n", suffix);
printf("\ttest VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: %s\n", ok(testReqBufs(&node)));
printf("\ttest CREATE_BUFS maximum buffers: %s\n", ok(testCreateBufsMax(&node)));
+ printf("\ttest VIDIOC_REMOVE_BUFS: %s\n", ok(testRemoveBufs(&node)));
// Reopen after each streaming test to reset the streaming state
// in case of any errors in the preceeding test.
node.reopen();
@@ -385,6 +385,7 @@ int testReadWrite(struct node *node);
int testExpBuf(struct node *node);
int testBlockingWait(struct node *node);
int testCreateBufsMax(struct node *node);
+int testRemoveBufs(struct node *node);
// 32-bit architecture, 32/64-bit time_t tests
int testTime32_64(struct node *node);
@@ -529,6 +529,124 @@ static int testCanSetSameTimings(struct node *node)
return 0;
}
+int testRemoveBufs(struct node *node)
+{
+ int ret;
+ unsigned i;
+
+ node->reopen();
+
+ for (i = 1; i <= V4L2_BUF_TYPE_LAST; i++) {
+ struct v4l2_remove_buffers removebufs = { };
+ unsigned buffers;
+
+ if (!(node->valid_buftypes & (1 << i)))
+ continue;
+
+ cv4l_queue q(i, V4L2_MEMORY_MMAP);
+
+ if (testSetupVbi(node, i))
+ continue;
+ ret = q.remove_bufs(node, 0, 0);
+ if (ret == ENOTTY)
+ continue;
+
+ q.init(i, V4L2_MEMORY_MMAP);
+ ret = q.create_bufs(node, 0);
+
+ memset(&removebufs, 0xff, sizeof(removebufs));
+ removebufs.index = 0;
+ removebufs.count = 0;
+ removebufs.type = q.g_type();
+ fail_on_test(doioctl(node, VIDIOC_REMOVE_BUFS, &removebufs));
+ fail_on_test(check_0(removebufs.reserved, sizeof(removebufs.reserved)));
+
+ buffer buf(i);
+
+ /* Create only 1 buffer */
+ fail_on_test(q.create_bufs(node, 1));
+ buffers = q.g_buffers();
+ fail_on_test(buffers != 1);
+ /* Removing buffer index 1 must fail */
+ fail_on_test(q.remove_bufs(node, 1, buffers) != EINVAL);
+ /* Removing buffer index 0 is valid */
+ fail_on_test(q.remove_bufs(node, 0, buffers));
+ /* Removing buffer index 0 again must fail */
+ fail_on_test(q.remove_bufs(node, 0, 1) != EINVAL);
+ /* Create 3 buffers indexes 0 to 2 */
+ fail_on_test(q.create_bufs(node, 3));
+ /* Remove them one by one */
+ fail_on_test(q.remove_bufs(node, 2, 1));
+ fail_on_test(q.remove_bufs(node, 0, 1));
+ fail_on_test(q.remove_bufs(node, 1, 1));
+ /* Removing buffer index 0 again must fail */
+ fail_on_test(q.remove_bufs(node, 0, 1) != EINVAL);
+
+ /* Create 4 buffers indexes 0 to 3 */
+ fail_on_test(q.create_bufs(node, 4));
+ /* Remove buffers index 1 and 2 */
+ fail_on_test(q.remove_bufs(node, 1, 2));
+ /* Add 3 more buffers should be indexes 4 to 6 */
+ fail_on_test(q.create_bufs(node, 3));
+ /* Query buffers:
+ * 1 and 2 have been removed they must fail
+ * 0 and 3 to 6 must exist*/
+ fail_on_test(buf.querybuf(node, 0));
+ fail_on_test(buf.querybuf(node, 1) != EINVAL);
+ fail_on_test(buf.querybuf(node, 2) != EINVAL);
+ fail_on_test(buf.querybuf(node, 3));
+ fail_on_test(buf.querybuf(node, 4));
+ fail_on_test(buf.querybuf(node, 5));
+ fail_on_test(buf.querybuf(node, 6));
+
+ /* Remove existing buffer index 6 with bad type must fail */
+ memset(&removebufs, 0xff, sizeof(removebufs));
+ removebufs.index = 6;
+ removebufs.count = 1;
+ removebufs.type = 0;
+ fail_on_test(doioctl(node, VIDIOC_REMOVE_BUFS, &removebufs) != EINVAL);
+
+ /* Remove existing buffer index 6 with bad type and count == 0
+ * must fail */
+ memset(&removebufs, 0xff, sizeof(removebufs));
+ removebufs.index = 6;
+ removebufs.count = 0;
+ removebufs.type = 0;
+ fail_on_test(doioctl(node, VIDIOC_REMOVE_BUFS, &removebufs) != EINVAL);
+
+ /* Remove with count == 0 must always return 0 */
+ fail_on_test(q.remove_bufs(node, 0, 0));
+ fail_on_test(q.remove_bufs(node, 1, 0));
+ fail_on_test(q.remove_bufs(node, 6, 0));
+ fail_on_test(q.remove_bufs(node, 7, 0));
+ fail_on_test(q.remove_bufs(node, 0xffffffff, 0));
+
+ /* Remove crossing max allowed buffers boundary must fail */
+ fail_on_test(q.remove_bufs(node, q.g_max_num_buffers() - 2, 7) != EINVAL);
+
+ /* Remove overflow must fail */
+ fail_on_test(q.remove_bufs(node, 3, 0xfffffff) != EINVAL);
+
+ /* Remove 2 buffers on index 2 when index 2 is free must fail */
+ fail_on_test(q.remove_bufs(node, 2, 2) != EINVAL);
+
+ /* Remove 2 buffers on index 0 when index 1 is free must fail */
+ fail_on_test(q.remove_bufs(node, 0, 2) != EINVAL);
+
+ /* Remove 2 buffers on index 1 when index 1 and 2 are free must fail */
+ fail_on_test(q.remove_bufs(node, 1, 2) != EINVAL);
+
+ /* Create 2 buffers, that must fill the hole */
+ fail_on_test(q.create_bufs(node, 2));
+ /* Remove all buffers */
+ fail_on_test(q.remove_bufs(node, 0, 7));
+
+ fail_on_test(q.reqbufs(node, 0));
+ }
+
+ return 0;
+}
+
int testReqBufs(struct node *node)
{
struct v4l2_create_buffers crbufs = { };
Add new test for REMOVE_BUFS ioctl. It create buffers and check if they could be removed from queue. It also check that removing non existing buffer or a queued buffer failed. Since using REMOVE_BUFS can create "holes" v4l_queue_querybufs() function needs to be modify to do a range check between [from..from+count-1]. Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com> --- version 9: - Add tests to check return values when count = 0. - Add tests to check return values when attempt to remove again buffers. - Call fail_on_test(q.reqbufs(node, 0)) at the end of the test. - Do not print WARN if REMOVE_BUFS ioctl isn't supported - Stop testing if CREATE_BUFS is supported. utils/common/cv4l-helpers.h | 4 + utils/common/v4l-helpers.h | 27 ++++- utils/v4l2-compliance/v4l2-compliance.cpp | 1 + utils/v4l2-compliance/v4l2-compliance.h | 1 + utils/v4l2-compliance/v4l2-test-buffers.cpp | 118 ++++++++++++++++++++ 5 files changed, 146 insertions(+), 5 deletions(-)