Hi, v4 can be found from: https://lore.kernel.org/all/20230421124428.393261-1-tomi.valkeinen@xxxxxxxxxxxxxxxx/ v5 is rebased on top of latest master, and I have addressed most of Laurent's comment. I did not start refactoring ARRAY_SIZE() nor print_flags(), nor did I rewrite everything so that a real parser would be used. A diff to v4 is included below to help the reviews. Tomi Tomi Valkeinen (7): v4l2-ctl: Add routing and streams support media-ctl: Add support for routes and streams v4l2-ctl/compliance: Add routing and streams multiplexed streams v4l2-ctl/compliance: Add simple routing test media-ctl: Check for Streams API support utils/common: Set V4L2_SUBDEV_CLIENT_CAP_STREAMS for subdevs v4l2-ctl: Check for Streams API support utils/common/cv4l-helpers.h | 1 + utils/common/v4l-helpers.h | 18 + utils/media-ctl/libmediactl.c | 43 +++ utils/media-ctl/libv4l2subdev.c | 339 ++++++++++++++++-- utils/media-ctl/media-ctl.c | 113 +++++- utils/media-ctl/mediactl-priv.h | 1 + utils/media-ctl/mediactl.h | 16 + utils/media-ctl/options.c | 15 +- utils/media-ctl/options.h | 1 + utils/media-ctl/v4l2subdev.h | 66 +++- utils/v4l2-compliance/v4l2-compliance.cpp | 132 +++++-- utils/v4l2-compliance/v4l2-compliance.h | 9 +- utils/v4l2-compliance/v4l2-test-subdevs.cpp | 59 +++- utils/v4l2-ctl/v4l2-ctl-subdev.cpp | 365 ++++++++++++++++++-- utils/v4l2-ctl/v4l2-ctl.cpp | 2 + utils/v4l2-ctl/v4l2-ctl.h | 2 + 16 files changed, 1062 insertions(+), 120 deletions(-) Interdiff against v4: diff --git a/utils/common/cv4l-helpers.h b/utils/common/cv4l-helpers.h index 502df6ac..91a04146 100644 --- a/utils/common/cv4l-helpers.h +++ b/utils/common/cv4l-helpers.h @@ -82,7 +82,7 @@ public: bool has_rw() const { return v4l_has_rw(this); } bool has_streaming() const { return v4l_has_streaming(this); } bool has_ext_pix_format() const { return v4l_has_ext_pix_format(this); } - bool has_streams_support() const { return subdev_supports_streams; } + bool has_streams() const { return have_streams; } int querycap(v4l2_capability &cap, bool force = false) { diff --git a/utils/common/v4l-helpers.h b/utils/common/v4l-helpers.h index 2dd7f061..f8e96d58 100644 --- a/utils/common/v4l-helpers.h +++ b/utils/common/v4l-helpers.h @@ -9,6 +9,7 @@ #ifndef _V4L_HELPERS_H_ #define _V4L_HELPERS_H_ +#include <linux/v4l2-subdev.h> #include <linux/videodev2.h> #include <string.h> #include <stdlib.h> @@ -39,7 +40,7 @@ struct v4l_fd { bool have_selection; bool is_subdev; bool is_media; - bool subdev_supports_streams; + bool have_streams; int (*open)(struct v4l_fd *f, const char *file, int oflag, ...); int (*close)(struct v4l_fd *f); @@ -543,7 +544,7 @@ static inline int v4l_subdev_s_fd(struct v4l_fd *f, int fd, const char *devname) ret = ioctl(f->fd, VIDIOC_SUBDEV_S_CLIENT_CAP, &clientcap); client_streams = !ret && (clientcap.capabilities & V4L2_SUBDEV_CLIENT_CAP_STREAMS); - f->subdev_supports_streams = subdev_streams && client_streams; + f->have_streams = subdev_streams && client_streams; return f->fd; } diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c index c32fe56a..64ac8cf1 100644 --- a/utils/media-ctl/libmediactl.c +++ b/utils/media-ctl/libmediactl.c @@ -909,6 +909,8 @@ struct media_pad *media_parse_pad_stream(struct media_device *media, *stream = 0; } + for (; isspace(*p); ++p); + if (endp) *endp = (char*)p; diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c index 186708ff..750796cc 100644 --- a/utils/media-ctl/libv4l2subdev.c +++ b/utils/media-ctl/libv4l2subdev.c @@ -231,33 +231,6 @@ int v4l2_subdev_set_selection(struct media_entity *entity, return 0; } -int v4l2_subdev_set_routing(struct media_entity *entity, - struct v4l2_subdev_route *routes, - unsigned int num_routes) -{ - struct v4l2_subdev_routing routing = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .routes = (uintptr_t)routes, - .num_routes = num_routes, - }; - int ret; - - ret = v4l2_subdev_open(entity); - if (ret < 0) - return ret; - - if (!entity->supports_streams) { - media_dbg(entity->media, "Streams API not supported\n"); - return -ENOTSUP; - } - - ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_ROUTING, &routing); - if (ret == -1) - return -errno; - - return 0; -} - int v4l2_subdev_get_routing(struct media_entity *entity, struct v4l2_subdev_route **routes, unsigned int *num_routes) @@ -272,8 +245,10 @@ int v4l2_subdev_get_routing(struct media_entity *entity, if (ret < 0) return ret; - if (!entity->supports_streams) + if (!entity->supports_streams) { + media_dbg(entity->media, "Streams API not supported\n"); return -ENOTSUP; + } ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing); if (ret == -1 && errno != ENOSPC) @@ -302,6 +277,33 @@ int v4l2_subdev_get_routing(struct media_entity *entity, return 0; } +int v4l2_subdev_set_routing(struct media_entity *entity, + struct v4l2_subdev_route *routes, + unsigned int num_routes) +{ + struct v4l2_subdev_routing routing = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .routes = (uintptr_t)routes, + .num_routes = num_routes, + }; + int ret; + + ret = v4l2_subdev_open(entity); + if (ret < 0) + return ret; + + if (!entity->supports_streams) { + media_dbg(entity->media, "Streams API not supported\n"); + return -ENOTSUP; + } + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_ROUTING, &routing); + if (ret == -1) + return -errno; + + return 0; +} + int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity, struct v4l2_dv_timings_cap *caps) { @@ -496,6 +498,8 @@ static int v4l2_subdev_parse_setup_route(struct media_device *media, } end++; + for (; isspace(*end); ++end); + *endp = end; return 0; @@ -506,9 +510,9 @@ int v4l2_subdev_parse_setup_routes(struct media_device *media, const char *p) struct media_entity *entity; struct v4l2_subdev_route *routes; unsigned int num_routes; + unsigned int i; char *end; int ret; - int i; entity = media_parse_entity(media, p, &end); if (!entity) @@ -558,7 +562,7 @@ int v4l2_subdev_parse_setup_routes(struct media_device *media, const char *p) struct v4l2_subdev_route *r = &routes[i]; media_dbg(entity->media, - "Setting up route %s : %u/%u -> %u/%u, flags 0x%8.8x\n", + "Setting up route %s : %u/%u -> %u/%u [0x%08x]\n", entity->info.name, r->sink_pad, r->sink_stream, r->source_pad, r->source_stream, diff --git a/utils/media-ctl/media-ctl.c b/utils/media-ctl/media-ctl.c index 831136a0..1531cffa 100644 --- a/utils/media-ctl/media-ctl.c +++ b/utils/media-ctl/media-ctl.c @@ -82,16 +82,16 @@ static void v4l2_subdev_print_routes(struct media_entity *entity, { unsigned int i; - for (i = 0; i < num_routes; i++) { - const struct v4l2_subdev_route *r = &routes[i]; + if (num_routes) + printf("\troutes:\n"); - if (i == 0) - printf("\troutes:\n"); + for (i = 0; i < num_routes; i++) { + const struct v4l2_subdev_route *route = &routes[i]; printf("\t\t%u/%u -> %u/%u [%s]\n", - r->sink_pad, r->sink_stream, - r->source_pad, r->source_stream, - r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE ? "ACTIVE" : "INACTIVE"); + route->sink_pad, route->sink_stream, + route->source_pad, route->source_stream, + route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE ? "ACTIVE" : "INACTIVE"); } } @@ -480,54 +480,49 @@ static void media_print_pad_text(struct media_entity *entity, struct v4l2_subdev_route *routes, unsigned int num_routes) { + uint64_t printed_streams_mask = 0; unsigned int i; - uint64_t printed_streams_mask; if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) return; if (!routes) { - v4l2_subdev_print_format(entity, pad->index, 0, V4L2_SUBDEV_FORMAT_ACTIVE); - v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE); - - if (pad->flags & MEDIA_PAD_FL_SOURCE) - v4l2_subdev_print_subdev_dv(entity); + v4l2_subdev_print_format(entity, pad->index, 0, + V4L2_SUBDEV_FORMAT_ACTIVE); + } else { + for (i = 0; i < num_routes; ++i) { + const struct v4l2_subdev_route *route = &routes[i]; + unsigned int stream; - return; - } + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; - printed_streams_mask = 0; + if (pad->flags & MEDIA_PAD_FL_SINK) { + if (route->sink_pad != pad->index) + continue; - for (i = 0; i < num_routes; ++i) { - const struct v4l2_subdev_route *r = &routes[i]; - unsigned int stream; + stream = route->sink_stream; + } else { + if (route->source_pad != pad->index) + continue; - if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) - continue; + stream = route->source_stream; + } - if (pad->flags & MEDIA_PAD_FL_SINK) { - if (r->sink_pad != pad->index) + if (printed_streams_mask & (1 << stream)) continue; - stream = r->sink_stream; - } else { - if (r->source_pad != pad->index) - continue; + v4l2_subdev_print_format(entity, pad->index, stream, + V4L2_SUBDEV_FORMAT_ACTIVE); - stream = r->source_stream; + printed_streams_mask |= (1 << stream); } + } - if (printed_streams_mask & (1 << stream)) - continue; - - v4l2_subdev_print_format(entity, pad->index, stream, V4L2_SUBDEV_FORMAT_ACTIVE); - v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE); - - if (pad->flags & MEDIA_PAD_FL_SOURCE) - v4l2_subdev_print_subdev_dv(entity); + v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE); - printed_streams_mask |= (1 << stream); - } + if (pad->flags & MEDIA_PAD_FL_SOURCE) + v4l2_subdev_print_subdev_dv(entity); } static void media_print_topology_text_entity(struct media_device *media, @@ -541,19 +536,24 @@ static void media_print_topology_text_entity(struct media_device *media, const struct media_entity_desc *info = media_entity_get_info(entity); const char *devname = media_entity_get_devname(entity); unsigned int num_links = media_entity_get_links_count(entity); - unsigned int j, k; - unsigned int padding; struct v4l2_subdev_route *routes = NULL; unsigned int num_routes = 0; + unsigned int j, k; + unsigned int padding; if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV) v4l2_subdev_get_routing(entity, &routes, &num_routes); padding = printf("- entity %u: ", info->id); - printf("%s (%u pad%s, %u link%s, %u route%s)\n", info->name, + printf("%s (%u pad%s, %u link%s", info->name, info->pads, info->pads > 1 ? "s" : "", - num_links, num_links > 1 ? "s" : "", - num_routes, num_routes > 1 ? "s" : ""); + num_links, num_links > 1 ? "s" : ""); + + if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV) + printf(", %u route%s", num_routes, num_routes != 1 ? "s" : ""); + + printf(")\n"); + printf("%*ctype %s subtype %s flags %x\n", padding, ' ', media_entity_type_to_string(info->type), media_entity_subtype_to_string(info->type), diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h index a8a6e7ad..1277040b 100644 --- a/utils/media-ctl/v4l2subdev.h +++ b/utils/media-ctl/v4l2subdev.h @@ -52,6 +52,7 @@ void v4l2_subdev_close(struct media_entity *entity); * @param entity - subdev-device media entity. * @param format - format to be filled. * @param pad - pad number. + * @param stream - stream number. * @param which - identifier of the format to get. * * Retrieve the current format on the @a entity @a pad and store it in the @@ -72,6 +73,7 @@ int v4l2_subdev_get_format(struct media_entity *entity, * @param entity - subdev-device media entity. * @param format - format. * @param pad - pad number. + * @param stream - stream number. * @param which - identifier of the format to set. * * Set the format on the @a entity @a pad to @a format. The driver is allowed to @@ -94,6 +96,7 @@ int v4l2_subdev_set_format(struct media_entity *entity, * @param entity - subdev-device media entity. * @param r - rectangle to be filled. * @param pad - pad number. + * @param stream - stream number. * @param target - selection target * @param which - identifier of the format to get. * @@ -116,6 +119,7 @@ int v4l2_subdev_get_selection(struct media_entity *entity, * @param entity - subdev-device media entity. * @param rect - crop rectangle. * @param pad - pad number. + * @param stream - stream number. * @param target - selection target * @param which - identifier of the format to set. * @@ -222,6 +226,8 @@ int v4l2_subdev_set_dv_timings(struct media_entity *entity, * @brief Retrieve the frame interval on a sub-device. * @param entity - subdev-device media entity. * @param interval - frame interval to be filled. + * @param pad - pad number. + * @param stream - stream number. * * Retrieve the current frame interval on subdev @a entity and store it in the * @a interval structure. @@ -239,6 +245,8 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity, * @brief Set the frame interval on a sub-device. * @param entity - subdev-device media entity. * @param interval - frame interval. + * @param pad - pad number. + * @param stream - stream number. * * Set the frame interval on subdev @a entity to @a interval. The driver is * allowed to modify the requested frame interval, in which case @a interval is diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp index f96f5972..f082f569 100644 --- a/utils/v4l2-compliance/v4l2-compliance.cpp +++ b/utils/v4l2-compliance/v4l2-compliance.cpp @@ -1255,8 +1255,8 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_ 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))); + which ? "Active" : "Try", + ok(testSubDevRouting(&node, which))); } printf("\n"); diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp index ec70b52b..8539c416 100644 --- a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp @@ -569,7 +569,7 @@ void subdev_set(cv4l_fd &_fd) if (options[OptSetSubDevFormat] || options[OptTrySubDevFormat]) { struct v4l2_subdev_format fmt; - if (!_fd.has_streams_support() && set_fmt_stream) { + if (!_fd.has_streams() && set_fmt_stream) { printf("Streams API not supported.\n"); return; } @@ -622,7 +622,7 @@ void subdev_set(cv4l_fd &_fd) if (options[OptSetSubDevSelection] || options[OptTrySubDevSelection]) { struct v4l2_subdev_selection sel; - if (!_fd.has_streams_support() && vsel.stream) { + if (!_fd.has_streams() && vsel.stream) { printf("Streams API not supported.\n"); return; } @@ -659,7 +659,7 @@ void subdev_set(cv4l_fd &_fd) if (options[OptSetSubDevFPS]) { struct v4l2_subdev_frame_interval fival; - if (!_fd.has_streams_support() && set_fps_stream) { + if (!_fd.has_streams() && set_fps_stream) { printf("Streams API not supported.\n"); return; } @@ -689,7 +689,7 @@ void subdev_set(cv4l_fd &_fd) } } if (options[OptSetRouting]) { - if (!_fd.has_streams_support()) { + if (!_fd.has_streams()) { printf("Streams API not supported.\n"); return; } @@ -736,7 +736,7 @@ static void print_routes(const struct v4l2_subdev_routing *r) }; for (i = 0; i < r->num_routes; i++) { - printf("%d/%d -> %d/%d [", + printf("%u/%u -> %u/%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); @@ -751,7 +751,7 @@ void subdev_get(cv4l_fd &_fd) if (options[OptGetSubDevFormat]) { struct v4l2_subdev_format fmt; - if (!_fd.has_streams_support() && get_fmt_stream) { + if (!_fd.has_streams() && get_fmt_stream) { printf("Streams API not supported.\n"); return; } @@ -761,7 +761,7 @@ void subdev_get(cv4l_fd &_fd) fmt.pad = get_fmt_pad; fmt.stream = get_fmt_stream; - printf("ioctl: VIDIOC_SUBDEV_G_FMT (pad=%u, stream=%u)\n", fmt.pad, fmt.stream); + printf("ioctl: VIDIOC_SUBDEV_G_FMT (pad=%u,stream=%u)\n", fmt.pad, fmt.stream); if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0) print_framefmt(fmt.format); } @@ -770,7 +770,7 @@ void subdev_get(cv4l_fd &_fd) struct v4l2_subdev_selection sel; unsigned idx = 0; - if (!_fd.has_streams_support() && get_sel_stream) { + if (!_fd.has_streams() && get_sel_stream) { printf("Streams API not supported.\n"); return; } @@ -797,7 +797,7 @@ void subdev_get(cv4l_fd &_fd) if (options[OptGetSubDevFPS]) { struct v4l2_subdev_frame_interval fival; - if (!_fd.has_streams_support() && get_fps_stream) { + if (!_fd.has_streams() && get_fps_stream) { printf("Streams API not supported.\n"); return; } @@ -819,7 +819,7 @@ void subdev_get(cv4l_fd &_fd) } if (options[OptGetRouting]) { - if (!_fd.has_streams_support()) { + if (!_fd.has_streams()) { printf("Streams API not supported.\n"); return; } @@ -907,7 +907,7 @@ void subdev_list(cv4l_fd &_fd) int fd = _fd.g_fd(); if (options[OptListSubDevMBusCodes]) { - if (!_fd.has_streams_support() && list_mbus_codes_stream) { + if (!_fd.has_streams() && list_mbus_codes_stream) { printf("Streams API not supported.\n"); return; } @@ -917,7 +917,7 @@ void subdev_list(cv4l_fd &_fd) print_mbus_codes(fd, list_mbus_codes_pad, list_mbus_codes_stream); } if (options[OptListSubDevFrameSizes]) { - if (!_fd.has_streams_support() && frmsize.stream) { + if (!_fd.has_streams() && frmsize.stream) { printf("Streams API not supported.\n"); return; } @@ -932,7 +932,7 @@ void subdev_list(cv4l_fd &_fd) } } if (options[OptListSubDevFrameIntervals]) { - if (!_fd.has_streams_support() && frmival.stream) { + if (!_fd.has_streams() && frmival.stream) { printf("Streams API not supported.\n"); return; } diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp index 1cfb50f7..52974b40 100644 --- a/utils/v4l2-ctl/v4l2-ctl.cpp +++ b/utils/v4l2-ctl/v4l2-ctl.cpp @@ -64,8 +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}, + {"set-routing", required_argument, 0, OptSetRouting}, {"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 9396c974..bf519c3f 100644 --- a/utils/v4l2-ctl/v4l2-ctl.h +++ b/utils/v4l2-ctl/v4l2-ctl.h @@ -191,8 +191,8 @@ enum Option { OptInfoEdid, OptShowEdid, OptFixEdidChecksums, - OptSetRouting, OptGetRouting, + OptSetRouting, OptFreqSeek, OptEncoderCmd, OptTryEncoderCmd, -- 2.34.1