Hi Tomi, Thank you for the patch. On Fri, Apr 21, 2023 at 03:44:21PM +0300, Tomi Valkeinen wrote: > Add support to get and set subdev routes and to get and set > configurations per stream. > > Based on work from Jacopo Mondi <jacopo@xxxxxxxxxx> and > Niklas Söderlund <niklas.soderlund+renesas@xxxxxxxxxxxx>. > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@xxxxxxxxxxxxxxxx> > --- > utils/v4l2-ctl/v4l2-ctl-subdev.cpp | 310 ++++++++++++++++++++++++++--- > utils/v4l2-ctl/v4l2-ctl.cpp | 2 + > utils/v4l2-ctl/v4l2-ctl.h | 2 + > 3 files changed, 281 insertions(+), 33 deletions(-) > > diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp > index 33cc1342..7ab64646 100644 > --- a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp > +++ b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp > @@ -1,5 +1,13 @@ > #include "v4l2-ctl.h" > > +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) It would be nice to put the 14 implementations of this macro to a common header. Out of scope for this patch. > + > +/* > + * The max value comes from a check in the kernel source code > + * drivers/media/v4l2-core/v4l2-ioctl.c check_array_args() > + */ > +#define NUM_ROUTES_MAX 256 > + > struct mbus_name { > const char *name; > __u32 code; > @@ -19,45 +27,57 @@ static const struct mbus_name mbus_names[] = { > #define SelectionFlags (1L<<4) > > static __u32 list_mbus_codes_pad; > +static __u32 list_mbus_codes_stream = 0; > static __u32 get_fmt_pad; > +static __u32 get_fmt_stream = 0; > static __u32 get_sel_pad; > +static __u32 get_sel_stream = 0; > static __u32 get_fps_pad; > +static __u32 get_fps_stream = 0; > static int get_sel_target = -1; > static unsigned int set_selection; > static struct v4l2_subdev_selection vsel; > static unsigned int set_fmt; > static __u32 set_fmt_pad; > +static __u32 set_fmt_stream = 0; > static struct v4l2_mbus_framefmt ffmt; > static struct v4l2_subdev_frame_size_enum frmsize; > static struct v4l2_subdev_frame_interval_enum frmival; > static __u32 set_fps_pad; > +static __u32 set_fps_stream = 0; > static double set_fps; > +static struct v4l2_subdev_routing routing; > +static struct v4l2_subdev_route routes[NUM_ROUTES_MAX]; > > void subdev_usage() > { > printf("\nSub-Device options:\n" > - " --list-subdev-mbus-codes <pad>\n" > + " --list-subdev-mbus-codes pad=<pad>,stream=<stream>\n" > " display supported mediabus codes for this pad (0 is default)\n" > " [VIDIOC_SUBDEV_ENUM_MBUS_CODE]\n" > - " --list-subdev-framesizes pad=<pad>,code=<code>\n" > + " --list-subdev-framesizes pad=<pad>,stream=<stream>,code=<code>\n" > " list supported framesizes for this pad and code\n" > " [VIDIOC_SUBDEV_ENUM_FRAME_SIZE]\n" > " <code> is the value of the mediabus code\n" > - " --list-subdev-frameintervals pad=<pad>,width=<w>,height=<h>,code=<code>\n" > + " --list-subdev-frameintervals pad=<pad>,stream=<stream>,width=<w>,height=<h>,code=<code>\n" > " list supported frame intervals for this pad and code and\n" > " the given width and height [VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL]\n" > " <code> is the value of the mediabus code\n" > - " --get-subdev-fmt [<pad>]\n" > - " query the frame format for the given pad [VIDIOC_SUBDEV_G_FMT]\n" > - " --get-subdev-selection pad=<pad>,target=<target>\n" > + " --get-subdev-fmt pad=<pad>,stream=<stream>\n" > + " query the frame format for the given pad and optional stream [VIDIOC_SUBDEV_G_FMT]\n" > + " <pad> the pad to get the format from\n" > + " <stream> the stream to get the format from (0 if not specified)\n" > + " --get-subdev-selection pad=<pad>,stream=<stream>,target=<target>\n" > " query the frame selection rectangle [VIDIOC_SUBDEV_G_SELECTION]\n" > " See --set-subdev-selection command for the valid <target> values.\n" > - " --get-subdev-fps [<pad>]\n" > + " --get-subdev-fps pad=<pad>,stream=<stream>\n" > " query the frame rate [VIDIOC_SUBDEV_G_FRAME_INTERVAL]\n" > " --set-subdev-fmt (for testing only, otherwise use media-ctl)\n" > - " --try-subdev-fmt pad=<pad>,width=<w>,height=<h>,code=<code>,field=<f>,colorspace=<c>,\n" > + " --try-subdev-fmt pad=<pad>,stream=<stream>,width=<w>,height=<h>,code=<code>,field=<f>,colorspace=<c>,\n" > " xfer=<xf>,ycbcr=<y>,hsv=<hsv>,quantization=<q>\n" > - " set the frame format [VIDIOC_SUBDEV_S_FMT]\n" > + " set the frame format for the given pad and optional stream [VIDIOC_SUBDEV_S_FMT]\n" > + " <pad> the pad to get the format from\n" > + " <stream> the stream to get the format from (0 if not specified)\n" > " <code> is the value of the mediabus code\n" > " <f> can be one of the following field layouts:\n" > " any, none, top, bottom, interlaced, seq_tb, seq_bt,\n" > @@ -74,31 +94,74 @@ void subdev_usage() > " <q> can be one of the following quantization methods:\n" > " default, full-range, lim-range\n" > " --set-subdev-selection (for testing only, otherwise use media-ctl)\n" > - " --try-subdev-selection pad=<pad>,target=<target>,flags=<flags>,\n" > + " --try-subdev-selection pad=<pad>,stream=<stream>,target=<target>,flags=<flags>,\n" > " top=<x>,left=<y>,width=<w>,height=<h>\n" > " set the video capture selection rectangle [VIDIOC_SUBDEV_S_SELECTION]\n" > " target=crop|crop_bounds|crop_default|compose|compose_bounds|\n" > " compose_default|compose_padded|native_size\n" > " flags=le|ge|keep-config\n" > - " --set-subdev-fps pad=<pad>,fps=<fps> (for testing only, otherwise use media-ctl)\n" > + " --set-subdev-fps pad=<pad>,stream=<stream>,fps=<fps> (for testing only, otherwise use media-ctl)\n" > " set the frame rate [VIDIOC_SUBDEV_S_FRAME_INTERVAL]\n" > + " --get-routing Print the route topology\n" > + " --set-routing <routes>\n" > + " Comma-separated list of route descriptors to setup\n" > + "\n" > + "Routes are defined as\n" > + " routes = route { ',' route } ;\n" > + " route = sink '->' source '[' flags ']' ;\n" > + " sink = sink-pad '/' sink-stream ;\n" > + " source = source-pad '/' source-stream ;\n" > + "\n" > + "where\n" > + " sink-pad = Pad numeric identifier for sink\n" > + " sink-stream = Stream numeric identifier for sink\n" > + " source-pad = Pad numeric identifier for source\n" > + " source-stream = Stream numeric identifier for source\n" > + " flags = Route flags (0: inactive, 1: active)\n" > ); > } > > void subdev_cmd(int ch, char *optarg) > { > char *value, *subs; > + char *endp; > > switch (ch) { > case OptListSubDevMBusCodes: > - if (optarg) > - list_mbus_codes_pad = strtoul(optarg, nullptr, 0); > + if (optarg) { > + /* Legacy pad-only parsing */ > + list_mbus_codes_pad = strtoul(optarg, &endp, 0); > + if (*endp == 0) > + break; > + } > + > + subs = optarg; > + while (subs && *subs != '\0') { > + static constexpr const char *subopts[] = { > + "pad", > + "stream", > + nullptr > + }; > + > + switch (parse_subopt(&subs, subopts, &value)) { > + case 0: > + list_mbus_codes_pad = strtoul(value, nullptr, 0); > + break; > + case 1: > + list_mbus_codes_stream = strtoul(value, nullptr, 0); > + break; > + default: > + subdev_usage(); > + std::exit(EXIT_FAILURE); > + } > + } > break; > case OptListSubDevFrameSizes: > subs = optarg; > while (*subs != '\0') { > static constexpr const char *subopts[] = { > "pad", > + "stream", > "code", > nullptr > }; > @@ -108,6 +171,9 @@ void subdev_cmd(int ch, char *optarg) > frmsize.pad = strtoul(value, nullptr, 0); > break; > case 1: > + frmsize.stream = strtoul(value, nullptr, 0); > + break; > + case 2: > frmsize.code = strtoul(value, nullptr, 0); > break; > default: > @@ -121,6 +187,7 @@ void subdev_cmd(int ch, char *optarg) > while (*subs != '\0') { > static constexpr const char *subopts[] = { > "pad", > + "stream", > "code", > "width", > "height", > @@ -132,12 +199,15 @@ void subdev_cmd(int ch, char *optarg) > frmival.pad = strtoul(value, nullptr, 0); > break; > case 1: > - frmival.code = strtoul(value, nullptr, 0); > + frmival.stream = strtoul(value, nullptr, 0); > break; > case 2: > - frmival.width = strtoul(value, nullptr, 0); > + frmival.code = strtoul(value, nullptr, 0); > break; > case 3: > + frmival.width = strtoul(value, nullptr, 0); > + break; > + case 4: > frmival.height = strtoul(value, nullptr, 0); > break; > default: > @@ -147,14 +217,40 @@ void subdev_cmd(int ch, char *optarg) > } > break; > case OptGetSubDevFormat: > - if (optarg) > - get_fmt_pad = strtoul(optarg, nullptr, 0); > + if (optarg) { > + /* Legacy pad-only parsing */ > + get_fmt_pad = strtoul(optarg, &endp, 0); > + if (*endp == 0) > + break; > + } > + > + subs = optarg; > + while (subs && *subs != '\0') { > + static constexpr const char *subopts[] = { > + "pad", > + "stream", > + nullptr > + }; > + > + switch (parse_subopt(&subs, subopts, &value)) { > + case 0: > + get_fmt_pad = strtoul(value, nullptr, 0); > + break; > + case 1: > + get_fmt_stream = strtoul(value, nullptr, 0); > + break; > + default: > + subdev_usage(); > + std::exit(EXIT_FAILURE); > + } > + } > break; > case OptGetSubDevSelection: > subs = optarg; > while (*subs != '\0') { > static constexpr const char *subopts[] = { > "pad", > + "stream", > "target", > nullptr > }; > @@ -165,6 +261,9 @@ void subdev_cmd(int ch, char *optarg) > get_sel_pad = strtoul(value, nullptr, 0); > break; > case 1: > + get_sel_stream = strtoul(value, nullptr, 0); > + break; > + case 2: > if (parse_selection_target(value, target)) { > fprintf(stderr, "Unknown selection target\n"); > subdev_usage(); > @@ -179,8 +278,33 @@ void subdev_cmd(int ch, char *optarg) > } > break; > case OptGetSubDevFPS: > - if (optarg) > - get_fps_pad = strtoul(optarg, nullptr, 0); > + if (optarg) { > + /* Legacy pad-only parsing */ > + get_fps_pad = strtoul(optarg, &endp, 0); > + if (*endp == 0) > + break; > + } > + > + subs = optarg; > + while (subs && *subs != '\0') { > + static constexpr const char *subopts[] = { > + "pad", > + "stream", > + nullptr > + }; > + > + switch (parse_subopt(&subs, subopts, &value)) { > + case 0: > + get_fps_pad = strtoul(value, nullptr, 0); > + break; > + case 1: > + get_fps_stream = strtoul(value, nullptr, 0); > + break; > + default: > + subdev_usage(); > + std::exit(EXIT_FAILURE); > + } > + } > break; > case OptSetSubDevFormat: > case OptTrySubDevFormat: > @@ -198,6 +322,7 @@ void subdev_cmd(int ch, char *optarg) > "quantization", > "xfer", > "pad", > + "stream", > nullptr > }; > > @@ -244,6 +369,9 @@ void subdev_cmd(int ch, char *optarg) > case 9: > set_fmt_pad = strtoul(value, nullptr, 0); > break; > + case 10: > + set_fmt_stream = strtoul(value, nullptr, 0); > + break; > default: > fprintf(stderr, "Unknown option\n"); > subdev_usage(); > @@ -264,6 +392,7 @@ void subdev_cmd(int ch, char *optarg) > "width", > "height", > "pad", > + "stream", > nullptr > }; > > @@ -298,6 +427,9 @@ void subdev_cmd(int ch, char *optarg) > case 6: > vsel.pad = strtoul(value, nullptr, 0); > break; > + case 7: > + vsel.stream = strtoul(value, nullptr, 0); > + break; > default: > fprintf(stderr, "Unknown option\n"); > subdev_usage(); > @@ -311,6 +443,7 @@ void subdev_cmd(int ch, char *optarg) > while (*subs != '\0') { > static constexpr const char *subopts[] = { > "pad", > + "stream", > "fps", > nullptr > }; > @@ -320,6 +453,9 @@ void subdev_cmd(int ch, char *optarg) > set_fps_pad = strtoul(value, nullptr, 0); > break; > case 1: > + set_fps_stream = strtoul(value, nullptr, 0); > + break; > + case 2: > set_fps = strtod(value, nullptr); > break; > default: > @@ -329,6 +465,47 @@ void subdev_cmd(int ch, char *optarg) > } > } > break; > + case OptSetRouting: { > + struct v4l2_subdev_route *r; > + char *end, *ref, *tok; > + unsigned int flags; > + > + memset(&routing, 0, sizeof(routing)); > + memset(routes, 0, sizeof(routes[0]) * NUM_ROUTES_MAX); > + routing.which = V4L2_SUBDEV_FORMAT_ACTIVE; > + routing.num_routes = 0; > + routing.routes = (__u64)routes; > + > + if (!optarg) > + break; > + > + r = (v4l2_subdev_route *)routing.routes; > + ref = end = strdup(optarg); > + while ((tok = strsep(&end, ",")) != NULL) { > + if (sscanf(tok, "%u/%u -> %u/%u [%u]", > + &r->sink_pad, &r->sink_stream, > + &r->source_pad, &r->source_stream, > + &flags) != 5) { Requiring a space around '->' isn't nice, especially as it's not present in the help text. MC link parsing makes spaces optional, please do the same here. > + free(ref); > + fprintf(stderr, "Invalid route information specified\n"); > + subdev_usage(); > + std::exit(EXIT_FAILURE); > + } > + > + if (flags & ~(V4L2_SUBDEV_ROUTE_FL_ACTIVE)) { > + fprintf(stderr, "Invalid route flags specified: %#x\n", flags); > + subdev_usage(); > + std::exit(EXIT_FAILURE); > + } > + > + r->flags = flags; > + > + r++; > + routing.num_routes++; > + } > + free(ref); > + break; > + } > default: > break; > } > @@ -394,6 +571,7 @@ void subdev_set(cv4l_fd &_fd) > > memset(&fmt, 0, sizeof(fmt)); > fmt.pad = set_fmt_pad; > + fmt.stream = set_fmt_stream; > fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; > > if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0) { > @@ -430,7 +608,7 @@ void subdev_set(cv4l_fd &_fd) > else > fmt.which = V4L2_SUBDEV_FORMAT_TRY; > > - printf("ioctl: VIDIOC_SUBDEV_S_FMT (pad=%u)\n", fmt.pad); > + printf("ioctl: VIDIOC_SUBDEV_S_FMT (pad=%u,stream=%u)\n", fmt.pad, fmt.stream); > ret = doioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt); > if (ret == 0 && (verbose || !options[OptSetSubDevFormat])) > print_framefmt(fmt.format); > @@ -441,6 +619,7 @@ void subdev_set(cv4l_fd &_fd) > > memset(&sel, 0, sizeof(sel)); > sel.pad = vsel.pad; > + sel.stream = vsel.stream; > sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; > sel.target = vsel.target; > > @@ -461,7 +640,7 @@ void subdev_set(cv4l_fd &_fd) > else > sel.which = V4L2_SUBDEV_FORMAT_TRY; > > - printf("ioctl: VIDIOC_SUBDEV_S_SELECTION (pad=%u)\n", sel.pad); > + printf("ioctl: VIDIOC_SUBDEV_S_SELECTION (pad=%u,stream=%u)\n", sel.pad, sel.stream); > int ret = doioctl(fd, VIDIOC_SUBDEV_S_SELECTION, &sel); > if (ret == 0 && (verbose || !options[OptSetSubDevSelection])) > print_subdev_selection(sel); > @@ -472,6 +651,7 @@ void subdev_set(cv4l_fd &_fd) > > memset(&fival, 0, sizeof(fival)); > fival.pad = set_fps_pad; > + fival.stream = set_fps_stream; > > if (set_fps <= 0) { > fprintf(stderr, "invalid fps %f\n", set_fps); > @@ -482,7 +662,7 @@ void subdev_set(cv4l_fd &_fd) > fival.interval.denominator = static_cast<uint32_t>(set_fps * fival.interval.numerator); > printf("Note: --set-subdev-fps is only for testing.\n" > "Normally media-ctl is used to configure the video pipeline.\n"); > - printf("ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL (pad=%u)\n", fival.pad); > + printf("ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL (pad=%u,stream=%u)\n", fival.pad, fival.stream); > if (doioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) == 0) { > if (!fival.interval.denominator || !fival.interval.numerator) > printf("\tFrames per second: invalid (%d/%d)\n", > @@ -493,6 +673,55 @@ void subdev_set(cv4l_fd &_fd) > fival.interval.denominator, fival.interval.numerator); > } > } > + if (options[OptSetRouting]) { > + if (doioctl(fd, VIDIOC_SUBDEV_S_ROUTING, &routing) == 0) > + printf("Routing set\n"); > + } > +} > + > +struct flag_name { > + __u32 flag; > + const char *name; > +}; > + > +static void print_flags(const struct flag_name *flag_names, unsigned int num_entries, __u32 flags) > +{ > + bool first = true; > + unsigned int i; > + > + for (i = 0; i < num_entries; i++) { > + if (!(flags & flag_names[i].flag)) > + continue; > + if (!first) > + printf(","); > + printf("%s", flag_names[i].name); > + flags &= ~flag_names[i].flag; > + first = false; > + } > + > + if (flags) { > + if (!first) > + printf(","); > + printf("0x%x", flags); > + } > +} This could also be a helper shared by multiple source files. > + > +static void print_routes(const struct v4l2_subdev_routing *r) > +{ > + unsigned int i; > + struct v4l2_subdev_route *routes = (struct v4l2_subdev_route *)r->routes; > + > + static const struct flag_name route_flags[] = { > + { V4L2_SUBDEV_ROUTE_FL_ACTIVE, "ACTIVE" }, > + }; > + > + for (i = 0; i < r->num_routes; i++) { > + printf("%d/%d -> %d/%d [", The values are unsigned, %u. > + routes[i].sink_pad, routes[i].sink_stream, > + routes[i].source_pad, routes[i].source_stream); > + print_flags(route_flags, ARRAY_SIZE(route_flags), routes[i].flags); > + printf("]\n"); > + } > } > > void subdev_get(cv4l_fd &_fd) > @@ -505,8 +734,9 @@ void subdev_get(cv4l_fd &_fd) > memset(&fmt, 0, sizeof(fmt)); > fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; > fmt.pad = get_fmt_pad; > + fmt.stream = get_fmt_stream; > > - printf("ioctl: VIDIOC_SUBDEV_G_FMT (pad=%u)\n", fmt.pad); > + printf("ioctl: VIDIOC_SUBDEV_G_FMT (pad=%u, stream=%u)\n", fmt.pad, fmt.stream); In some places you have a space after the comma, in some places you don't. I prefer the space personally but I'm fine with either as long as it's consistent. > if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0) > print_framefmt(fmt.format); > } > @@ -518,8 +748,9 @@ void subdev_get(cv4l_fd &_fd) > memset(&sel, 0, sizeof(sel)); > sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; > sel.pad = get_sel_pad; > + sel.stream = get_sel_stream; > > - printf("ioctl: VIDIOC_SUBDEV_G_SELECTION (pad=%u)\n", sel.pad); > + printf("ioctl: VIDIOC_SUBDEV_G_SELECTION (pad=%u,stream=%u)\n", sel.pad, sel.stream); > if (options[OptAll] || get_sel_target == -1) { > while (valid_seltarget_at_idx(idx)) { > sel.target = seltarget_at_idx(idx); > @@ -538,8 +769,9 @@ void subdev_get(cv4l_fd &_fd) > > memset(&fival, 0, sizeof(fival)); > fival.pad = get_fps_pad; > + fival.stream = get_fps_stream; > > - printf("ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL (pad=%u)\n", fival.pad); > + printf("ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL (pad=%u,stream=%u)\n", fival.pad, fival.stream); > if (doioctl(fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) == 0) { > if (!fival.interval.denominator || !fival.interval.numerator) > printf("\tFrames per second: invalid (%d/%d)\n", > @@ -550,6 +782,17 @@ void subdev_get(cv4l_fd &_fd) > fival.interval.denominator, fival.interval.numerator); > } > } > + > + if (options[OptGetRouting]) { > + memset(&routing, 0, sizeof(routing)); > + memset(routes, 0, sizeof(routes[0]) * NUM_ROUTES_MAX); > + routing.which = V4L2_SUBDEV_FORMAT_ACTIVE; > + routing.num_routes = NUM_ROUTES_MAX; > + routing.routes = (__u64)routes; > + > + if (doioctl(fd, VIDIOC_SUBDEV_G_ROUTING, &routing) == 0) > + print_routes(&routing); > + } > } > > static void print_mbus_code(__u32 code) > @@ -566,11 +809,12 @@ static void print_mbus_code(__u32 code) > printf("\t0x%04x", code); > } > > -static void print_mbus_codes(int fd, __u32 pad) > +static void print_mbus_codes(int fd, __u32 pad, __u32 stream) > { > struct v4l2_subdev_mbus_code_enum mbus_code = {}; > > mbus_code.pad = pad; > + mbus_code.stream = stream; > mbus_code.which = V4L2_SUBDEV_FORMAT_TRY; > > for (;;) { > @@ -623,13 +867,13 @@ void subdev_list(cv4l_fd &_fd) > int fd = _fd.g_fd(); > > if (options[OptListSubDevMBusCodes]) { > - printf("ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE (pad=%u)\n", > - list_mbus_codes_pad); > - print_mbus_codes(fd, list_mbus_codes_pad); > + printf("ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE (pad=%u,stream=%u)\n", > + list_mbus_codes_pad, list_mbus_codes_stream); > + print_mbus_codes(fd, list_mbus_codes_pad, list_mbus_codes_stream); > } > if (options[OptListSubDevFrameSizes]) { > - printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE (pad=%u)\n", > - frmsize.pad); > + printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE (pad=%u,stream=%u)\n", > + frmsize.pad, frmsize.stream); > frmsize.index = 0; > frmsize.which = V4L2_SUBDEV_FORMAT_TRY; > while (test_ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &frmsize) >= 0) { > @@ -638,8 +882,8 @@ void subdev_list(cv4l_fd &_fd) > } > } > if (options[OptListSubDevFrameIntervals]) { > - printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL (pad=%u)\n", > - frmival.pad); > + printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL (pad=%u,stream=%u)\n", > + frmival.pad, frmival.stream); > frmival.index = 0; > frmival.which = V4L2_SUBDEV_FORMAT_TRY; > while (test_ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &frmival) >= 0) { > diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp > index 8585278f..1cfb50f7 100644 > --- a/utils/v4l2-ctl/v4l2-ctl.cpp > +++ b/utils/v4l2-ctl/v4l2-ctl.cpp > @@ -64,6 +64,8 @@ static struct option long_options[] = { > {"get-fmt-video-out", no_argument, nullptr, OptGetVideoOutFormat}, > {"set-fmt-video-out", required_argument, nullptr, OptSetVideoOutFormat}, > {"try-fmt-video-out", required_argument, nullptr, OptTryVideoOutFormat}, > + {"set-routing", required_argument, 0, OptSetRouting}, > + {"get-routing", no_argument, 0, OptGetRouting}, Maybe get before set as for other options ? > {"help", no_argument, nullptr, OptHelp}, > {"help-tuner", no_argument, nullptr, OptHelpTuner}, > {"help-io", no_argument, nullptr, OptHelpIO}, > diff --git a/utils/v4l2-ctl/v4l2-ctl.h b/utils/v4l2-ctl/v4l2-ctl.h > index 8f2726ea..9396c974 100644 > --- a/utils/v4l2-ctl/v4l2-ctl.h > +++ b/utils/v4l2-ctl/v4l2-ctl.h > @@ -191,6 +191,8 @@ enum Option { > OptInfoEdid, > OptShowEdid, > OptFixEdidChecksums, > + OptSetRouting, > + OptGetRouting, > OptFreqSeek, > OptEncoderCmd, > OptTryEncoderCmd, -- Regards, Laurent Pinchart