@@ -111,6 +111,8 @@ static std::string subdevcap2s(unsigned cap)
if (cap & V4L2_SUBDEV_CAP_RO_SUBDEV)
s += "\t\tRead-Only Sub-Device\n";
+ if (cap & V4L2_SUBDEV_CAP_MPLEXED)
+ s += "\t\tSub-Device supports Multiplexed Streams\n";
return s;
}
@@ -1210,6 +1210,10 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
if (node.is_subdev()) {
bool has_source = false;
bool has_sink = false;
+ struct v4l2_subdev_routing sd_routing[2] = {};
+ struct v4l2_subdev_route sd_routes[2][256] = {};
+ bool has_routes = !!(subdevcap.capabilities & V4L2_SUBDEV_CAP_MPLEXED);
+ int ret;
node.frame_interval_pad = -1;
node.enum_frame_interval_pad = -1;
@@ -1221,6 +1225,22 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
}
node.is_passthrough_subdev = has_source && has_sink;
+ if (has_routes) {
+ for (unsigned which = V4L2_SUBDEV_FORMAT_TRY;
+ which <= V4L2_SUBDEV_FORMAT_ACTIVE; which++) {
+
+ sd_routing[which].which = which;
+ sd_routing[which].routes = (__u64)sd_routes[which];
+ sd_routing[which].num_routes = 256;
+
+ ret = doioctl(&node, VIDIOC_SUBDEV_G_ROUTING, &sd_routing[which]);
+ if (ret) {
+ fail("VIDIOC_SUBDEV_G_ROUTING: failed to get routing\n");
+ sd_routing[which].num_routes = 0;
+ }
+ }
+ }
+
for (unsigned pad = 0; pad < node.entity.pads; pad++) {
printf("Sub-Device ioctls (%s Pad %u):\n",
(node.pads[pad].flags & MEDIA_PAD_FL_SINK) ?
@@ -1230,32 +1250,86 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
node.has_subdev_enum_fival = 0;
for (unsigned which = V4L2_SUBDEV_FORMAT_TRY;
which <= V4L2_SUBDEV_FORMAT_ACTIVE; which++) {
- printf("\ttest %s VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: %s\n",
- which ? "Active" : "Try",
- ok(testSubDevEnum(&node, which, pad)));
- printf("\ttest %s VIDIOC_SUBDEV_G/S_FMT: %s\n",
- which ? "Active" : "Try",
- ok(testSubDevFormat(&node, which, pad)));
- printf("\ttest %s VIDIOC_SUBDEV_G/S_SELECTION/CROP: %s\n",
- which ? "Active" : "Try",
- ok(testSubDevSelection(&node, which, pad)));
- if (which)
- printf("\ttest VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: %s\n",
- ok(testSubDevFrameInterval(&node, pad)));
+ struct v4l2_subdev_routing dummy_routing;
+ struct v4l2_subdev_route dummy_routes[1];
+
+ const struct v4l2_subdev_routing *routing;
+ const struct v4l2_subdev_route *routes;
+
+ if (has_routes) {
+ routing = &sd_routing[which];
+ routes = sd_routes[which];
+ } else {
+ dummy_routing.num_routes = 1;
+ dummy_routing.routes = (__u64)&dummy_routes;
+ dummy_routes[0].source_pad = pad;
+ dummy_routes[0].source_stream = 0;
+ dummy_routes[0].sink_pad = pad;
+ dummy_routes[0].sink_stream = 0;
+ dummy_routes[0].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
+
+ routing = &dummy_routing;
+ routes = dummy_routes;
+ }
+
+ for (unsigned i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *r = &routes[i];
+ unsigned stream;
+
+ if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ if ((node.pads[pad].flags & MEDIA_PAD_FL_SINK) &&
+ (r->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE))
+ continue;
+
+ if ((node.pads[pad].flags & MEDIA_PAD_FL_SINK) &&
+ (r->sink_pad == pad))
+ stream = r->sink_stream;
+ else if ((node.pads[pad].flags & MEDIA_PAD_FL_SOURCE) &&
+ (r->source_pad == pad))
+ stream = r->source_stream;
+ else
+ continue;
+
+ printf("\t%s Stream %u\n",which ? "Active" : "Try",
+ stream);
+
+ printf("\ttest %s VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: %s\n",
+ which ? "Active" : "Try",
+ ok(testSubDevEnum(&node, which, pad, stream)));
+ printf("\ttest %s VIDIOC_SUBDEV_G/S_FMT: %s\n",
+ which ? "Active" : "Try",
+ ok(testSubDevFormat(&node, which, pad, stream)));
+ printf("\ttest %s VIDIOC_SUBDEV_G/S_SELECTION/CROP: %s\n",
+ which ? "Active" : "Try",
+ ok(testSubDevSelection(&node, which, pad, stream)));
+ if (which)
+ printf("\ttest VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: %s\n",
+ ok(testSubDevFrameInterval(&node, pad, stream)));
+ }
+ }
+
+ /*
+ * These tests do not make sense for subdevs with multiplexed streams,
+ * as the try & active cases may have different routing and thus different
+ * behavior.
+ */
+ if (!has_routes) {
+ if (node.has_subdev_enum_code && node.has_subdev_enum_code < 3)
+ fail("VIDIOC_SUBDEV_ENUM_MBUS_CODE: try/active mismatch\n");
+ if (node.has_subdev_enum_fsize && node.has_subdev_enum_fsize < 3)
+ fail("VIDIOC_SUBDEV_ENUM_FRAME_SIZE: try/active mismatch\n");
+ if (node.has_subdev_enum_fival && node.has_subdev_enum_fival < 3)
+ fail("VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: try/active mismatch\n");
+ if (node.has_subdev_fmt && node.has_subdev_fmt < 3)
+ fail("VIDIOC_SUBDEV_G/S_FMT: try/active mismatch\n");
+ if (node.has_subdev_selection && node.has_subdev_selection < 3)
+ fail("VIDIOC_SUBDEV_G/S_SELECTION: try/active mismatch\n");
+ if (node.has_subdev_selection &&
+ node.has_subdev_selection != node.has_subdev_fmt)
+ fail("VIDIOC_SUBDEV_G/S_SELECTION: fmt/selection mismatch\n");
}
- if (node.has_subdev_enum_code && node.has_subdev_enum_code < 3)
- fail("VIDIOC_SUBDEV_ENUM_MBUS_CODE: try/active mismatch\n");
- if (node.has_subdev_enum_fsize && node.has_subdev_enum_fsize < 3)
- fail("VIDIOC_SUBDEV_ENUM_FRAME_SIZE: try/active mismatch\n");
- if (node.has_subdev_enum_fival && node.has_subdev_enum_fival < 3)
- fail("VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: try/active mismatch\n");
- if (node.has_subdev_fmt && node.has_subdev_fmt < 3)
- fail("VIDIOC_SUBDEV_G/S_FMT: try/active mismatch\n");
- if (node.has_subdev_selection && node.has_subdev_selection < 3)
- fail("VIDIOC_SUBDEV_G/S_SELECTION: try/active mismatch\n");
- if (node.has_subdev_selection &&
- node.has_subdev_selection != node.has_subdev_fmt)
- fail("VIDIOC_SUBDEV_G/S_SELECTION: fmt/selection mismatch\n");
printf("\n");
}
}
@@ -364,10 +364,10 @@ int testDecoder(struct node *node);
// SubDev ioctl tests
int testSubDevCap(struct node *node);
-int testSubDevEnum(struct node *node, unsigned which, unsigned pad);
-int testSubDevFormat(struct node *node, unsigned which, unsigned pad);
-int testSubDevSelection(struct node *node, unsigned which, unsigned pad);
-int testSubDevFrameInterval(struct node *node, unsigned pad);
+int testSubDevEnum(struct node *node, unsigned which, unsigned pad, unsigned stream);
+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);
// Buffer ioctl tests
int testReqBufs(struct node *node);
@@ -25,7 +25,7 @@
#include "v4l2-compliance.h"
-#define VALID_SUBDEV_CAPS (V4L2_SUBDEV_CAP_RO_SUBDEV)
+#define VALID_SUBDEV_CAPS (V4L2_SUBDEV_CAP_RO_SUBDEV | V4L2_SUBDEV_CAP_MPLEXED)
int testSubDevCap(struct node *node)
{
@@ -53,6 +53,7 @@ static int testSubDevEnumFrameInterval(struct node *node, unsigned which,
memset(&fie, 0, sizeof(fie));
fie.which = which;
fie.pad = pad;
+ fie.stream = 0;
fie.code = code;
fie.width = width;
fie.height = height;
@@ -82,6 +83,7 @@ static int testSubDevEnumFrameInterval(struct node *node, unsigned which,
memset(&fie, 0xff, sizeof(fie));
fie.which = which;
fie.pad = pad;
+ fie.stream = 0;
fie.code = code;
fie.width = width;
fie.height = height;
@@ -127,6 +129,7 @@ static int testSubDevEnumFrameSize(struct node *node, unsigned which,
memset(&fse, 0, sizeof(fse));
fse.which = which;
fse.pad = pad;
+ fse.stream = 0;
fse.code = code;
ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse);
node->has_subdev_enum_fsize |= (ret != ENOTTY) << which;
@@ -136,6 +139,7 @@ static int testSubDevEnumFrameSize(struct node *node, unsigned which,
memset(&fie, 0, sizeof(fie));
fie.which = which;
fie.pad = pad;
+ fie.stream = 0;
fie.code = code;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != ENOTTY);
return ret;
@@ -151,6 +155,7 @@ static int testSubDevEnumFrameSize(struct node *node, unsigned which,
memset(&fse, 0xff, sizeof(fse));
fse.which = which;
fse.pad = pad;
+ fse.stream = 0;
fse.code = code;
fse.index = 0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse));
@@ -194,7 +199,7 @@ static int testSubDevEnumFrameSize(struct node *node, unsigned which,
return 0;
}
-int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
+int testSubDevEnum(struct node *node, unsigned which, unsigned pad, unsigned stream)
{
struct v4l2_subdev_mbus_code_enum mbus_core_enum;
unsigned num_codes;
@@ -203,6 +208,7 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
memset(&mbus_core_enum, 0, sizeof(mbus_core_enum));
mbus_core_enum.which = which;
mbus_core_enum.pad = pad;
+ mbus_core_enum.stream = stream;
ret = doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum);
node->has_subdev_enum_code |= (ret != ENOTTY) << which;
if (ret == ENOTTY) {
@@ -213,8 +219,10 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
memset(&fie, 0, sizeof(fie));
fse.which = which;
fse.pad = pad;
+ fse.stream = stream;
fie.which = which;
fie.pad = pad;
+ fie.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != ENOTTY);
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != ENOTTY);
return ret;
@@ -225,16 +233,19 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
mbus_core_enum.index = ~0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL);
mbus_core_enum.pad = node->entity.pads;
+ mbus_core_enum.stream = stream;
mbus_core_enum.index = 0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL);
memset(&mbus_core_enum, 0xff, sizeof(mbus_core_enum));
mbus_core_enum.which = which;
mbus_core_enum.pad = pad;
+ mbus_core_enum.stream = stream;
mbus_core_enum.index = 0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum));
fail_on_test(check_0(mbus_core_enum.reserved, sizeof(mbus_core_enum.reserved)));
fail_on_test(mbus_core_enum.code == ~0U);
fail_on_test(mbus_core_enum.pad != pad);
+ fail_on_test(mbus_core_enum.stream != stream);
fail_on_test(mbus_core_enum.index);
fail_on_test(mbus_core_enum.which != which);
do {
@@ -251,6 +262,7 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
fail_on_test(!mbus_core_enum.code);
fail_on_test(mbus_core_enum.which != which);
fail_on_test(mbus_core_enum.pad != pad);
+ fail_on_test(mbus_core_enum.stream != stream);
fail_on_test(mbus_core_enum.index != i);
ret = testSubDevEnumFrameSize(node, which, pad, mbus_core_enum.code);
@@ -259,7 +271,7 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
return 0;
}
-int testSubDevFrameInterval(struct node *node, unsigned pad)
+int testSubDevFrameInterval(struct node *node, unsigned pad, unsigned stream)
{
struct v4l2_subdev_frame_interval fival;
struct v4l2_fract ival;
@@ -267,6 +279,7 @@ int testSubDevFrameInterval(struct node *node, unsigned pad)
memset(&fival, 0xff, sizeof(fival));
fival.pad = pad;
+ fival.stream = stream;
ret = doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival);
if (ret == ENOTTY) {
fail_on_test(node->enum_frame_interval_pad >= 0);
@@ -278,6 +291,7 @@ int testSubDevFrameInterval(struct node *node, unsigned pad)
node->frame_interval_pad = pad;
fail_on_test(check_0(fival.reserved, sizeof(fival.reserved)));
fail_on_test(fival.pad != pad);
+ fail_on_test(fival.stream != stream);
fail_on_test(!fival.interval.numerator);
fail_on_test(!fival.interval.denominator);
fail_on_test(fival.interval.numerator == ~0U || fival.interval.denominator == ~0U);
@@ -289,20 +303,25 @@ int testSubDevFrameInterval(struct node *node, unsigned pad)
}
fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival));
fail_on_test(fival.pad != pad);
+ fail_on_test(fival.stream != stream);
fail_on_test(ival.numerator != fival.interval.numerator);
fail_on_test(ival.denominator != fival.interval.denominator);
fail_on_test(check_0(fival.reserved, sizeof(fival.reserved)));
memset(&fival, 0, sizeof(fival));
fival.pad = pad;
+ fival.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival));
fail_on_test(fival.pad != pad);
+ fail_on_test(fival.stream != stream);
fail_on_test(ival.numerator != fival.interval.numerator);
fail_on_test(ival.denominator != fival.interval.denominator);
fival.pad = node->entity.pads;
+ fival.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) != EINVAL);
fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != EINVAL);
fival.pad = pad;
+ fival.stream = stream;
fival.interval = ival;
fival.interval.numerator = 0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival));
@@ -339,7 +358,7 @@ static int checkMBusFrameFmt(struct node *node, struct v4l2_mbus_framefmt &fmt)
return 0;
}
-int testSubDevFormat(struct node *node, unsigned which, unsigned pad)
+int testSubDevFormat(struct node *node, unsigned which, unsigned pad, unsigned stream)
{
struct v4l2_subdev_format fmt;
struct v4l2_subdev_format s_fmt;
@@ -348,6 +367,7 @@ int testSubDevFormat(struct node *node, unsigned which, unsigned pad)
memset(&fmt, 0, sizeof(fmt));
fmt.which = which;
fmt.pad = pad;
+ fmt.stream = stream;
ret = doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt);
node->has_subdev_fmt |= (ret != ENOTTY) << which;
if (ret == ENOTTY) {
@@ -358,14 +378,17 @@ int testSubDevFormat(struct node *node, unsigned which, unsigned pad)
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt) != EINVAL);
fmt.which = 0;
fmt.pad = node->entity.pads;
+ fmt.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt) != EINVAL);
memset(&fmt, 0xff, sizeof(fmt));
fmt.which = which;
fmt.pad = pad;
+ fmt.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt));
fail_on_test(check_0(fmt.reserved, sizeof(fmt.reserved)));
fail_on_test(fmt.which != which);
fail_on_test(fmt.pad != pad);
+ fail_on_test(fmt.stream != stream);
fail_on_test(checkMBusFrameFmt(node, fmt.format));
s_fmt = fmt;
memset(s_fmt.reserved, 0xff, sizeof(s_fmt.reserved));
@@ -378,6 +401,7 @@ int testSubDevFormat(struct node *node, unsigned which, unsigned pad)
fail_on_test(ret && ret != ENOTTY);
fail_on_test(s_fmt.which != which);
fail_on_test(s_fmt.pad != pad);
+ fail_on_test(s_fmt.stream != stream);
if (ret) {
warn("VIDIOC_SUBDEV_G_FMT is supported but not VIDIOC_SUBDEV_S_FMT\n");
return 0;
@@ -422,7 +446,7 @@ static target_info targets[] = {
{ ~0U },
};
-int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
+int testSubDevSelection(struct node *node, unsigned which, unsigned pad, unsigned stream)
{
struct v4l2_subdev_selection sel;
struct v4l2_subdev_selection s_sel;
@@ -434,10 +458,12 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
targets[V4L2_SEL_TGT_NATIVE_SIZE].readonly = is_sink;
memset(&crop, 0, sizeof(crop));
crop.pad = pad;
+ crop.stream = stream;
crop.which = which;
memset(&sel, 0, sizeof(sel));
sel.which = which;
sel.pad = pad;
+ sel.stream = stream;
sel.target = V4L2_SEL_TGT_CROP;
ret = doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel);
node->has_subdev_selection |= (ret != ENOTTY) << which;
@@ -450,6 +476,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(check_0(crop.reserved, sizeof(crop.reserved)));
fail_on_test(crop.which != which);
fail_on_test(crop.pad != pad);
+ fail_on_test(crop.stream != stream);
fail_on_test(memcmp(&crop.rect, &sel.r, sizeof(sel.r)));
for (unsigned tgt = 0; targets[tgt].target != ~0U; tgt++) {
@@ -457,6 +484,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
memset(&sel, 0xff, sizeof(sel));
sel.which = which;
sel.pad = pad;
+ sel.stream = stream;
sel.target = tgt;
ret = doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel);
targets[tgt].found = !ret;
@@ -468,6 +496,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(check_0(sel.reserved, sizeof(sel.reserved)));
fail_on_test(sel.which != which);
fail_on_test(sel.pad != pad);
+ fail_on_test(sel.stream != stream);
fail_on_test(sel.target != tgt);
fail_on_test(!sel.r.width);
fail_on_test(sel.r.width == ~0U);
@@ -479,9 +508,11 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel) != EINVAL);
sel.which = 0;
sel.pad = node->entity.pads;
+ sel.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel) != EINVAL);
sel.which = which;
sel.pad = pad;
+ sel.stream = stream;
s_sel = sel;
memset(s_sel.reserved, 0xff, sizeof(s_sel.reserved));
ret = doioctl(node, VIDIOC_SUBDEV_S_SELECTION, &s_sel);
@@ -495,6 +526,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(check_0(crop.reserved, sizeof(crop.reserved)));
fail_on_test(crop.which != which);
fail_on_test(crop.pad != pad);
+ fail_on_test(crop.stream != stream);
fail_on_test(memcmp(&crop.rect, &sel.r, sizeof(sel.r)));
}
}
@@ -503,6 +535,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(!ret && targets[tgt].readonly);
fail_on_test(s_sel.which != which);
fail_on_test(s_sel.pad != pad);
+ fail_on_test(s_sel.stream != stream);
if (ret && !targets[tgt].readonly && tgt != V4L2_SEL_TGT_NATIVE_SIZE)
warn("VIDIOC_SUBDEV_G_SELECTION is supported for target %u but not VIDIOC_SUBDEV_S_SELECTION\n", tgt);
if (ret)
Add basic support for routing and streams. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> --- utils/common/v4l2-info.cpp | 2 + utils/v4l2-compliance/v4l2-compliance.cpp | 124 ++++++++++++++++---- utils/v4l2-compliance/v4l2-compliance.h | 8 +- utils/v4l2-compliance/v4l2-test-subdevs.cpp | 43 ++++++- 4 files changed, 143 insertions(+), 34 deletions(-)