diff mbox series

[v7,5/8] v4l2-ctl/compliance: Add simple routing test

Message ID 20230808-streams-support-v7-5-bd0b42a5826d@ideasonboard.com
State New
Headers show
Series v4l-utils: Support multiplexed streams | expand

Commit Message

Tomi Valkeinen Aug. 8, 2023, 6:19 a.m. UTC
Add a simple test for VIDIOC_SUBDEV_G_ROUTING/VIDIOC_SUBDEV_S_ROUTING.

We can't (at least at the moment) really know here what kind of routings
the driver would accept, but we can test a VIDIOC_SUBDEV_G_ROUTING call,
followed by a VIDIOC_SUBDEV_S_ROUTING call with the routing we
received.

Additionally, we can check that the returned pads and flags look fine,
and also that setting obviously invalid routing will fail.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
 utils/v4l2-compliance/v4l2-compliance.cpp   | 12 +++++
 utils/v4l2-compliance/v4l2-compliance.h     |  1 +
 utils/v4l2-compliance/v4l2-test-subdevs.cpp | 78 +++++++++++++++++++++++++++++
 3 files changed, 91 insertions(+)
diff mbox series

Patch

diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp
index e8359b2f..4b232314 100644
--- a/utils/v4l2-compliance/v4l2-compliance.cpp
+++ b/utils/v4l2-compliance/v4l2-compliance.cpp
@@ -1249,6 +1249,18 @@  void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
 		node.is_passthrough_subdev = has_source && has_sink;
 
 		if (has_routes) {
+			printf("Sub-Device routing ioctls:\n");
+
+			for (unsigned which = V4L2_SUBDEV_FORMAT_TRY;
+				which <= V4L2_SUBDEV_FORMAT_ACTIVE; which++) {
+
+				printf("\ttest %s VIDIOC_SUBDEV_G_ROUTING/VIDIOC_SUBDEV_S_ROUTING: %s\n",
+				       which ? "Active" : "Try",
+				       ok(testSubDevRouting(&node, which)));
+			}
+
+			printf("\n");
+
 			for (unsigned which = V4L2_SUBDEV_FORMAT_TRY;
 				which <= V4L2_SUBDEV_FORMAT_ACTIVE; which++) {
 
diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h
index 0cd43980..35b2274b 100644
--- a/utils/v4l2-compliance/v4l2-compliance.h
+++ b/utils/v4l2-compliance/v4l2-compliance.h
@@ -375,6 +375,7 @@  int testSubDevEnum(struct node *node, unsigned which, unsigned pad, unsigned str
 int testSubDevFormat(struct node *node, unsigned which, unsigned pad, unsigned stream);
 int testSubDevSelection(struct node *node, unsigned which, unsigned pad, unsigned stream);
 int testSubDevFrameInterval(struct node *node, unsigned pad, unsigned stream);
+int testSubDevRouting(struct node *node, unsigned which);
 
 // Buffer ioctl tests
 int testReqBufs(struct node *node);
diff --git a/utils/v4l2-compliance/v4l2-test-subdevs.cpp b/utils/v4l2-compliance/v4l2-test-subdevs.cpp
index 5545b0dc..38339ee3 100644
--- a/utils/v4l2-compliance/v4l2-test-subdevs.cpp
+++ b/utils/v4l2-compliance/v4l2-test-subdevs.cpp
@@ -551,3 +551,81 @@  int testSubDevSelection(struct node *node, unsigned which, unsigned pad, unsigne
 
 	return have_sel ? 0 : ENOTTY;
 }
+
+int testSubDevRouting(struct node *node, unsigned which)
+{
+	const uint32_t all_route_flags_mask = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
+	struct v4l2_subdev_routing routing = {};
+	struct v4l2_subdev_route routes[NUM_ROUTES_MAX] = {};
+	unsigned int i;
+	int ret;
+
+	routing.which = which;
+	routing.routes = (__u64)&routes;
+	routing.num_routes = 0;
+	memset(routing.reserved, 0xff, sizeof(routing.reserved));
+
+	/*
+	 * First test that G_ROUTING either returns success, or ENOSPC and
+	 * updates num_routes.
+	 */
+
+	ret = doioctl(node, VIDIOC_SUBDEV_G_ROUTING, &routing);
+	fail_on_test(ret && ret != ENOSPC);
+	fail_on_test(ret == ENOSPC && routing.num_routes == 0);
+	fail_on_test(check_0(routing.reserved, sizeof(routing.reserved)));
+
+	if (!routing.num_routes)
+		return 0;
+
+	/*
+	 * Then get the actual routes, and verify that the num_routes gets
+	 * updated to the correct number.
+	 */
+
+	uint32_t num_routes = routing.num_routes;
+	routing.num_routes = num_routes + 1;
+	fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_ROUTING, &routing));
+	fail_on_test(routing.num_routes != num_routes);
+
+	/* Check the validity of route pads and flags */
+
+	if (node->pads) {
+		for (i = 0; i < routing.num_routes; ++i) {
+			const struct v4l2_subdev_route *route = &routes[i];
+			const struct media_pad_desc *sink;
+			const struct media_pad_desc *source;
+
+			fail_on_test(route->sink_pad >= node->entity.pads);
+			fail_on_test(route->source_pad >= node->entity.pads);
+
+			sink = &node->pads[route->sink_pad];
+			source = &node->pads[route->source_pad];
+
+			fail_on_test(!(sink->flags & MEDIA_PAD_FL_SINK));
+			fail_on_test(!(source->flags & MEDIA_PAD_FL_SOURCE));
+			fail_on_test(route->flags & ~all_route_flags_mask);
+		}
+	}
+
+	/* Set the same routes back, which should always succeed. */
+
+	memset(routing.reserved, 0xff, sizeof(routing.reserved));
+	fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_ROUTING, &routing));
+	fail_on_test(check_0(routing.reserved, sizeof(routing.reserved)));
+
+	/* Test setting invalid pads */
+
+	if (node->pads) {
+		for (i = 0; i < routing.num_routes; ++i) {
+			struct v4l2_subdev_route *route = &routes[i];
+
+			route->sink_pad = node->entity.pads + 1;
+		}
+
+		memset(routing.reserved, 0xff, sizeof(routing.reserved));
+		fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_ROUTING, &routing) != EINVAL);
+	}
+
+	return 0;
+}