From patchwork Tue Aug 8 06:19:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 711795 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 73E65C05052 for ; Tue, 8 Aug 2023 16:14:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229503AbjHHQO1 (ORCPT ); Tue, 8 Aug 2023 12:14:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51668 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232157AbjHHQNS (ORCPT ); Tue, 8 Aug 2023 12:13:18 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C3B877EF5 for ; Tue, 8 Aug 2023 08:47:26 -0700 (PDT) Received: from [127.0.1.1] (91-154-35-171.elisa-laajakaista.fi [91.154.35.171]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 500B110FE; Tue, 8 Aug 2023 08:18:45 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1691475525; bh=KaOBUJNw8+Vv4Fk6CQnOU73QDtqXE9uQbHHSa6I3XNs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fhQC+KmVPRZ5n9T3PSSPse7CZCaJcMiZFdHdTUUyolfk/5f/EbczlBNnbjqvXQLDs 11QkkVGQk8kf2/js7VC0daQ7Lhg3as5aj3U31fr0opHm4fVU4Vf/o37JWgeNi05Bla v7YLW2Kx5Agr5D94kjdca+0EOXrXLVLGfxdnXuIs= From: Tomi Valkeinen Date: Tue, 08 Aug 2023 09:19:20 +0300 Subject: [PATCH v7 1/8] v4l2-ctl: Improve sub-device options help text MIME-Version: 1.0 Message-Id: <20230808-streams-support-v7-1-bd0b42a5826d@ideasonboard.com> References: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> In-Reply-To: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com, Jacopo Mondi , Laurent Pinchart , niklas.soderlund+renesas@ragnatech.se, Mauro Carvalho Chehab , Hans Verkuil , satish.nagireddy@getcruise.com Cc: Tomi Valkeinen X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1197; i=tomi.valkeinen@ideasonboard.com; h=from:subject:message-id; bh=KaOBUJNw8+Vv4Fk6CQnOU73QDtqXE9uQbHHSa6I3XNs=; b=owEBbQKS/ZANAwAIAfo9qoy8lh71AcsmYgBk0d6EyKIDm5JOCkEhD6k6UV/9uqgv44kvTpsOI ro+ifJpqZuJAjMEAAEIAB0WIQTEOAw+ll79gQef86f6PaqMvJYe9QUCZNHehAAKCRD6PaqMvJYe 9X5PD/4maDOarNkDGv4lRTqlkc05yOXYNZsbcKm8mkFV3SPu6QlKye1AUb3UUzjOz9fyltPhVam WbeW5hkDs06LeOjt3vCpn6e0qDcjTpxIjzCCfmVigmk6hBCobUNyC1KIqCuh/eDse5nbaNP8UqU wxNuCCArU6wc5JnmEERxxLhZawHEK6zXXYOJkfn5OKn/0kgGPchYLj2sg3UJzGiFTBsPICErlWa x7sobPjNTMRck1GyZm5+xfflBVmqrVIefQRCITNL9lChPs15TXr3evfoiPL0U3zEyONw/zNVKUo AptdpQOjFiRrnUwfwFzDh4HEqTLpJTTz9li15x4U4sUn/Q6bpH9KyQadO5HFM0J+5YW49r+TtEk FQiMwxOsPTNNHoGm8DaDTkUNBw5DrQIWmtvNuBe+Z+1OH1tq81jr0MCWrj//RzZ5EuV0+KtENtK rpoGRTLAXVjRXboDD1vxfnPE0QK5e3535PYSyuehYfSI7pGZcYxs37GGf5rgFxz+pLM1BLbgKPn 9ChtIZ9dJM0+5wHICfzocLGmM6kwyEqHlDn571/8saR8yaYasq35OFeyduFgtmHuR6kz8WyYxy5 DqUMdQ9p/VYO4b5zVr66dxtcMBY7Q7mvG6NwDiLFDb033rWqekxPEi3LVIcw4LRNjUA9QuRM330 Yk/iT0kCboXpliw== X-Developer-Key: i=tomi.valkeinen@ideasonboard.com; a=openpgp; fpr=C4380C3E965EFD81079FF3A7FA3DAA8CBC961EF5 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Explicitly mention that all the parameters to the options are optional, and default to 0, and remove the one place where the default of 0 was mentioned. Signed-off-by: Tomi Valkeinen --- utils/v4l2-ctl/v4l2-ctl-subdev.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp index 33cc1342..22fd9052 100644 --- a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp @@ -36,8 +36,9 @@ static double set_fps; void subdev_usage() { printf("\nSub-Device options:\n" + "Note: all parameters below (pad, code, etc.) are optional unless otherwise noted and default to 0\n" " --list-subdev-mbus-codes \n" - " display supported mediabus codes for this pad (0 is default)\n" + " display supported mediabus codes for this pad\n" " [VIDIOC_SUBDEV_ENUM_MBUS_CODE]\n" " --list-subdev-framesizes pad=,code=\n" " list supported framesizes for this pad and code\n" From patchwork Tue Aug 8 06:19:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 712188 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C678BC41513 for ; Tue, 8 Aug 2023 16:14:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232129AbjHHQOZ (ORCPT ); Tue, 8 Aug 2023 12:14:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52658 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232137AbjHHQNL (ORCPT ); Tue, 8 Aug 2023 12:13:11 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3E3613C04 for ; Tue, 8 Aug 2023 08:47:13 -0700 (PDT) Received: from [127.0.1.1] (91-154-35-171.elisa-laajakaista.fi [91.154.35.171]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 11FB711A9; Tue, 8 Aug 2023 08:18:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1691475526; bh=T1GjWaxrrH5i5nchvcRSkamrGPfH9v2sa8aAXXl84II=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=MsBl/yeXijIiYg2GnxyMxsAOIgAVy8hayYrFcU9eJw2y+P/QSDhO7xudKYnxowhdc RLMU4ir0ewDfm4acsWNq4xmM7+5ufTk0ewZk0XzBarp6kdo/lbVbvOx6okUAJ3thbm 5UHzF1iDmP2i7Nz4b9E9/gQAdtLM5yLuHpKNY9Gw= From: Tomi Valkeinen Date: Tue, 08 Aug 2023 09:19:21 +0300 Subject: [PATCH v7 2/8] v4l2-ctl: Add routing and streams support MIME-Version: 1.0 Message-Id: <20230808-streams-support-v7-2-bd0b42a5826d@ideasonboard.com> References: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> In-Reply-To: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com, Jacopo Mondi , Laurent Pinchart , niklas.soderlund+renesas@ragnatech.se, Mauro Carvalho Chehab , Hans Verkuil , satish.nagireddy@getcruise.com Cc: Tomi Valkeinen X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=22636; i=tomi.valkeinen@ideasonboard.com; h=from:subject:message-id; bh=T1GjWaxrrH5i5nchvcRSkamrGPfH9v2sa8aAXXl84II=; b=owEBbQKS/ZANAwAIAfo9qoy8lh71AcsmYgBk0d6F8pkytbr/s6QikgYDfft9kikPWcEdpGkQG BEKdfeoBK2JAjMEAAEIAB0WIQTEOAw+ll79gQef86f6PaqMvJYe9QUCZNHehQAKCRD6PaqMvJYe 9Z4pD/0ZXtXKFJ5LDh4+Krk/LXOl5dpRv5oCrUz+G8vg19OvILpFB7H/nwGnaOcZo6XolQxXccu hKhkrAyKj2t2Fge9U3okRv/K+QoKvZ0pvxW/COaCJlXF0PA4XIefT+VhPzYtLAB35AcjxHPqA6h jJTqwE+sLrNt8wAn323KV8Az79lXOzd+ZRpQPPo1o1tYebQ0HQ4s34YRJD+2dJxgO/R4haTYX4s VB1sQ2Qj/Q8ZWOahayiiThrdv0NJENtu3nI5sLTxXNuE1cEVEsOcsShuGsxqLRFJ/10ymAKv4Z/ gj3Id55KdOmYFbZtskeK0zebIVfCGlUHEZi+nNBFtATrm1TIBVOgjy1ul05ri1QoAKY+bZVJYnK UKbuXlxU1pr+gLsbdpJZEjEmaqTFQAFyJDe9EAMebOZZP2eYe7WI4z0eet+lWQw8Vm9GVGGApek g0KmU5bIKKTsFz0pnRkb/Mio6dFudJs/a7kl2RuDg09f6f6/RPZ53elvS8ZCsJQ+jbuoHnq4n6z rp2ep/EklJiGFQnjLuGOCKGI4HNeHC0iIIqouVL2LFdkPu6RjJ9BDAY9sEsafotoquEZn/infTS nc3JA7nfyaNts64yffwRvZojCx5N+1JfQOgnJB0ko/W1L07AwUp28sK2oNPqe7p49e8l/Pidsiv vLasz0yQG56vcTw== X-Developer-Key: i=tomi.valkeinen@ideasonboard.com; a=openpgp; fpr=C4380C3E965EFD81079FF3A7FA3DAA8CBC961EF5 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add support to get and set subdev routes and to get and set configurations per stream. Based on work from Jacopo Mondi and Niklas Söderlund . Signed-off-by: Tomi Valkeinen --- utils/common/v4l2-info.h | 6 + utils/v4l2-ctl/v4l2-ctl-subdev.cpp | 308 ++++++++++++++++++++++++++++++++----- utils/v4l2-ctl/v4l2-ctl.cpp | 2 + utils/v4l2-ctl/v4l2-ctl.h | 2 + 4 files changed, 282 insertions(+), 36 deletions(-) diff --git a/utils/common/v4l2-info.h b/utils/common/v4l2-info.h index 4a9aa3e8..6de5654c 100644 --- a/utils/common/v4l2-info.h +++ b/utils/common/v4l2-info.h @@ -11,6 +11,12 @@ #include #include +/* + * 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 flag_def { unsigned flag; const char *str; diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp index 22fd9052..d906b72d 100644 --- a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp @@ -1,5 +1,7 @@ #include "v4l2-ctl.h" +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + struct mbus_name { const char *name; __u32 code; @@ -19,46 +21,56 @@ 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" "Note: all parameters below (pad, code, etc.) are optional unless otherwise noted and default to 0\n" - " --list-subdev-mbus-codes \n" - " display supported mediabus codes for this pad\n" + " --list-subdev-mbus-codes pad=,stream=\n" + " display supported mediabus codes for this pad and stream\n" " [VIDIOC_SUBDEV_ENUM_MBUS_CODE]\n" - " --list-subdev-framesizes pad=,code=\n" - " list supported framesizes for this pad and code\n" + " --list-subdev-framesizes pad=,stream=,code=\n" + " list supported framesizes for this pad, stream and code\n" " [VIDIOC_SUBDEV_ENUM_FRAME_SIZE]\n" " is the value of the mediabus code\n" - " --list-subdev-frameintervals pad=,width=,height=,code=\n" - " list supported frame intervals for this pad and code and\n" + " --list-subdev-frameintervals pad=,stream=,width=,height=,code=\n" + " list supported frame intervals for this pad, stream, code and\n" " the given width and height [VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL]\n" " is the value of the mediabus code\n" - " --get-subdev-fmt []\n" - " query the frame format for the given pad [VIDIOC_SUBDEV_G_FMT]\n" - " --get-subdev-selection pad=,target=\n" + " --get-subdev-fmt pad=,stream=\n" + " query the frame format for the given pad and stream [VIDIOC_SUBDEV_G_FMT]\n" + " --get-subdev-selection pad=,stream=,target=\n" " query the frame selection rectangle [VIDIOC_SUBDEV_G_SELECTION]\n" " See --set-subdev-selection command for the valid values.\n" - " --get-subdev-fps []\n" + " --get-subdev-fps pad=,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=,width=,height=,code=,field=,colorspace=,\n" + " --try-subdev-fmt pad=,stream=,width=,height=,code=,field=,colorspace=,\n" " xfer=,ycbcr=,hsv=,quantization=\n" - " set the frame format [VIDIOC_SUBDEV_S_FMT]\n" + " set the frame format for the given pad and stream [VIDIOC_SUBDEV_S_FMT]\n" + " the pad to get the format from\n" + " the stream to get the format\n" " is the value of the mediabus code\n" " can be one of the following field layouts:\n" " any, none, top, bottom, interlaced, seq_tb, seq_bt,\n" @@ -75,31 +87,74 @@ void subdev_usage() " 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=,target=,flags=,\n" + " --try-subdev-selection pad=,stream=,target=,flags=,\n" " top=,left=,width=,height=\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=,fps= (for testing only, otherwise use media-ctl)\n" + " --set-subdev-fps pad=,stream=,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 \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 }; @@ -109,6 +164,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: @@ -122,6 +180,7 @@ void subdev_cmd(int ch, char *optarg) while (*subs != '\0') { static constexpr const char *subopts[] = { "pad", + "stream", "code", "width", "height", @@ -133,12 +192,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: @@ -148,14 +210,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 }; @@ -166,6 +254,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(); @@ -180,8 +271,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: @@ -199,6 +315,7 @@ void subdev_cmd(int ch, char *optarg) "quantization", "xfer", "pad", + "stream", nullptr }; @@ -245,6 +362,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(); @@ -265,6 +385,7 @@ void subdev_cmd(int ch, char *optarg) "width", "height", "pad", + "stream", nullptr }; @@ -299,6 +420,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(); @@ -312,6 +436,7 @@ void subdev_cmd(int ch, char *optarg) while (*subs != '\0') { static constexpr const char *subopts[] = { "pad", + "stream", "fps", nullptr }; @@ -321,6 +446,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: @@ -330,6 +458,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) { + 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; } @@ -395,6 +564,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) { @@ -431,7 +601,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); @@ -442,6 +612,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; @@ -462,7 +633,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); @@ -473,6 +644,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); @@ -483,7 +655,7 @@ void subdev_set(cv4l_fd &_fd) fival.interval.denominator = static_cast(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", @@ -494,6 +666,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); + } +} + +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("%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); + printf("]\n"); + } } void subdev_get(cv4l_fd &_fd) @@ -506,8 +727,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); if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0) print_framefmt(fmt.format); } @@ -519,8 +741,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); @@ -539,8 +762,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", @@ -551,6 +775,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) @@ -567,11 +802,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 (;;) { @@ -624,13 +860,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) { @@ -639,8 +875,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..52974b40 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}, + {"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 8f2726ea..bf519c3f 100644 --- a/utils/v4l2-ctl/v4l2-ctl.h +++ b/utils/v4l2-ctl/v4l2-ctl.h @@ -191,6 +191,8 @@ enum Option { OptInfoEdid, OptShowEdid, OptFixEdidChecksums, + OptGetRouting, + OptSetRouting, OptFreqSeek, OptEncoderCmd, OptTryEncoderCmd, From patchwork Tue Aug 8 06:19:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 712186 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 010E1C07E8D for ; Tue, 8 Aug 2023 16:14:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232280AbjHHQOa (ORCPT ); Tue, 8 Aug 2023 12:14:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52638 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232132AbjHHQNJ (ORCPT ); Tue, 8 Aug 2023 12:13:09 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1DA1D3AA8 for ; Tue, 8 Aug 2023 08:47:12 -0700 (PDT) Received: from [127.0.1.1] (91-154-35-171.elisa-laajakaista.fi [91.154.35.171]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D96781BAD; Tue, 8 Aug 2023 08:18:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1691475527; bh=wRTs3OGY7ARAGTbz/mqjYcLg4lB/FRp1cvJQe12//24=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=qgvIne2tXSaygH92JmzN7B2VotxDugAwcrfn/AQDKnrM9j+jdNYkFSyQ11JkaNIsf TgCYQGUpNp5jPKlfl5qvieZd57zPie/LyfpGiEOJRElMD4haqIzXoa87rFB09wsiid 0nsQ9Xzo414qUpMGpillrT4bciCX5Uu4nYSfCgHQ= From: Tomi Valkeinen Date: Tue, 08 Aug 2023 09:19:22 +0300 Subject: [PATCH v7 3/8] media-ctl: Add support for routes and streams MIME-Version: 1.0 Message-Id: <20230808-streams-support-v7-3-bd0b42a5826d@ideasonboard.com> References: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> In-Reply-To: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com, Jacopo Mondi , Laurent Pinchart , niklas.soderlund+renesas@ragnatech.se, Mauro Carvalho Chehab , Hans Verkuil , satish.nagireddy@getcruise.com Cc: Tomi Valkeinen X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=34535; i=tomi.valkeinen@ideasonboard.com; h=from:subject:message-id; bh=wRTs3OGY7ARAGTbz/mqjYcLg4lB/FRp1cvJQe12//24=; b=owEBbQKS/ZANAwAIAfo9qoy8lh71AcsmYgBk0d6FK80nIxhA58snlpa7Q4ZGZmNs1wAZCOODl RqdW7HNwqGJAjMEAAEIAB0WIQTEOAw+ll79gQef86f6PaqMvJYe9QUCZNHehQAKCRD6PaqMvJYe 9VvxD/9WI9Qp9jxmscJMUB/rbdA1ogxB0DhRBiTz+o84eT8/viEkxq/FDr+POdOAwi5+iSm3VWl KK2Z5zexeqxiXFFbLMRle70ybNjpoL3xyQST1goA7vSGNUiVlz+qztuqAjlNx/U0LF1dtJybXqj gg6chgBXCTEqVuD12z5RZry30hsn+4CszaGArg21zqRP54E0869dzZqe7qgmaKTC9W9KrYEKCfe zqK+2dT6eBcDkG3A+mxV5gc0t057cIGHTrCAjw9S/t9PWSVeuthrRxzedEJkXO/BxtfMmiv6JGH 6jzQzxvVp5ppNc4eIk4VKvVV9nsCxcHxjBbCZyc2JXsnqOwweTJbmH5lkdLvD+CNlFrKmdtLfT+ SV131Sq9mnv+i+Oq9khIiugrOzl4sX8CGIBFW3O9GMjrVU7zbxqNNpQUa+Nfma+tQ918/c+HeaR VWVJt290OoFWGxneolzLAiH0/3PJp8VGsFtQG9s/limm3N/HOUWy7ZEKuj8hAn0uCfpa6m2TPjK CGb4vbFMJerHfybkYU4bPStEZthdoY4MOqEW/7h7tbnLegmfrpEXnkDgOAaLJLPmGK6hTrYzOAS hHxI9k5H2otffsI74FzvmkrfKhTOt8PxwIG0GHLFNKmfs/+jjIY+B8+l06Uc79Ha9PZ2EbbVtzj FcGJLe9Htdos93w== X-Developer-Key: i=tomi.valkeinen@ideasonboard.com; a=openpgp; fpr=C4380C3E965EFD81079FF3A7FA3DAA8CBC961EF5 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add support to get and set subdev routes and to get and set configurations per stream. Based on work from Sakari Ailus . Signed-off-by: Tomi Valkeinen --- utils/media-ctl/libmediactl.c | 43 ++++++ utils/media-ctl/libv4l2subdev.c | 289 +++++++++++++++++++++++++++++++++++----- utils/media-ctl/media-ctl.c | 113 ++++++++++++++-- utils/media-ctl/mediactl.h | 16 +++ utils/media-ctl/options.c | 15 ++- utils/media-ctl/options.h | 1 + utils/media-ctl/v4l2subdev.h | 66 ++++++++- 7 files changed, 490 insertions(+), 53 deletions(-) diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c index a18b063e..64ac8cf1 100644 --- a/utils/media-ctl/libmediactl.c +++ b/utils/media-ctl/libmediactl.c @@ -874,6 +874,49 @@ struct media_pad *media_parse_pad(struct media_device *media, return &entity->pads[pad]; } +struct media_pad *media_parse_pad_stream(struct media_device *media, + const char *p, unsigned int *stream, + char **endp) +{ + struct media_pad *pad; + const char *orig_p = p; + char *ep; + + pad = media_parse_pad(media, p, &ep); + if (pad == NULL) + return NULL; + + p = ep; + + if (*p == '/') { + unsigned int s; + + p++; + + s = strtoul(p, &ep, 10); + + if (ep == p) { + media_dbg(media, "Unable to parse stream: '%s'\n", orig_p); + if (endp) + *endp = (char*)p; + return NULL; + } + + *stream = s; + + p++; + } else { + *stream = 0; + } + + for (; isspace(*p); ++p); + + if (endp) + *endp = (char*)p; + + return pad; +} + struct media_link *media_parse_link(struct media_device *media, const char *p, char **endp) { diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c index 63bb3d75..2144a527 100644 --- a/utils/media-ctl/libv4l2subdev.c +++ b/utils/media-ctl/libv4l2subdev.c @@ -40,6 +40,12 @@ #include "tools.h" #include "v4l2subdev.h" +/* + * 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 + int v4l2_subdev_open(struct media_entity *entity) { if (entity->fd != -1) @@ -64,7 +70,7 @@ void v4l2_subdev_close(struct media_entity *entity) } int v4l2_subdev_get_format(struct media_entity *entity, - struct v4l2_mbus_framefmt *format, unsigned int pad, + struct v4l2_mbus_framefmt *format, unsigned int pad, unsigned int stream, enum v4l2_subdev_format_whence which) { struct v4l2_subdev_format fmt; @@ -76,6 +82,7 @@ int v4l2_subdev_get_format(struct media_entity *entity, memset(&fmt, 0, sizeof(fmt)); fmt.pad = pad; + fmt.stream = stream; fmt.which = which; ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt); @@ -88,6 +95,7 @@ int v4l2_subdev_get_format(struct media_entity *entity, int v4l2_subdev_set_format(struct media_entity *entity, struct v4l2_mbus_framefmt *format, unsigned int pad, + unsigned int stream, enum v4l2_subdev_format_whence which) { struct v4l2_subdev_format fmt; @@ -99,6 +107,7 @@ int v4l2_subdev_set_format(struct media_entity *entity, memset(&fmt, 0, sizeof(fmt)); fmt.pad = pad; + fmt.stream = stream; fmt.which = which; fmt.format = *format; @@ -111,8 +120,8 @@ int v4l2_subdev_set_format(struct media_entity *entity, } int v4l2_subdev_get_selection(struct media_entity *entity, - struct v4l2_rect *rect, unsigned int pad, unsigned int target, - enum v4l2_subdev_format_whence which) + struct v4l2_rect *rect, unsigned int pad, unsigned int stream, + unsigned int target, enum v4l2_subdev_format_whence which) { union { struct v4l2_subdev_selection sel; @@ -150,8 +159,8 @@ int v4l2_subdev_get_selection(struct media_entity *entity, } int v4l2_subdev_set_selection(struct media_entity *entity, - struct v4l2_rect *rect, unsigned int pad, unsigned int target, - enum v4l2_subdev_format_whence which) + struct v4l2_rect *rect, unsigned int pad, unsigned int stream, + unsigned int target, enum v4l2_subdev_format_whence which) { union { struct v4l2_subdev_selection sel; @@ -165,6 +174,7 @@ int v4l2_subdev_set_selection(struct media_entity *entity, memset(&u.sel, 0, sizeof(u.sel)); u.sel.pad = pad; + u.sel.stream = stream; u.sel.target = target; u.sel.which = which; u.sel.r = *rect; @@ -179,6 +189,7 @@ int v4l2_subdev_set_selection(struct media_entity *entity, memset(&u.crop, 0, sizeof(u.crop)); u.crop.pad = pad; + u.crop.stream = stream; u.crop.which = which; u.crop.rect = *rect; @@ -190,6 +201,69 @@ int v4l2_subdev_set_selection(struct media_entity *entity, return 0; } +int v4l2_subdev_get_routing(struct media_entity *entity, + struct v4l2_subdev_route **routes, + unsigned int *num_routes) +{ + struct v4l2_subdev_routing routing = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev_route *r; + int ret; + + ret = v4l2_subdev_open(entity); + if (ret < 0) + return ret; + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing); + if (ret == -1 && errno != ENOSPC) + return -errno; + + if (!routing.num_routes) { + *routes = NULL; + *num_routes = 0; + return 0; + } + + r = calloc(routing.num_routes, sizeof(*r)); + if (!r) + return -ENOMEM; + + routing.routes = (uintptr_t)r; + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing); + if (ret) { + free(r); + return ret; + } + + *num_routes = routing.num_routes; + *routes = r; + + 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; + + 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) { @@ -264,7 +338,7 @@ int v4l2_subdev_set_dv_timings(struct media_entity *entity, int v4l2_subdev_get_frame_interval(struct media_entity *entity, struct v4l2_fract *interval, - unsigned int pad) + unsigned int pad, unsigned int stream) { struct v4l2_subdev_frame_interval ival; int ret; @@ -275,6 +349,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity, memset(&ival, 0, sizeof(ival)); ival.pad = pad; + ival.stream = stream; ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival); if (ret < 0) @@ -286,7 +361,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity, int v4l2_subdev_set_frame_interval(struct media_entity *entity, struct v4l2_fract *interval, - unsigned int pad) + unsigned int pad, unsigned int stream) { struct v4l2_subdev_frame_interval ival; int ret; @@ -297,6 +372,7 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity, memset(&ival, 0, sizeof(ival)); ival.pad = pad; + ival.stream = stream; ival.interval = *interval; ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival); @@ -307,6 +383,155 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity, return 0; } +static int v4l2_subdev_parse_setup_route(struct media_device *media, + struct v4l2_subdev_route *r, + const char *p, char **endp) +{ + char *end; + + /* sink pad/stream */ + + r->sink_pad = strtoul(p, &end, 10); + if (*end != '/') { + media_dbg(media, "Expected '/'\n"); + return -EINVAL; + } + + p = end + 1; + + r->sink_stream = strtoul(p, &end, 10); + + for (; isspace(*end); ++end); + + if (end[0] != '-' || end[1] != '>') { + media_dbg(media, "Expected '->'\n"); + return -EINVAL; + } + p = end + 2; + + /* source pad/stream */ + + r->source_pad = strtoul(p, &end, 10); + if (*end != '/') { + media_dbg(media, "Expected '/'\n"); + return -EINVAL; + } + + p = end + 1; + + r->source_stream = strtoul(p, &end, 10); + + /* flags */ + + for (; isspace(*end); ++end); + + if (*end != '[') { + media_dbg(media, "Expected '['\n"); + return -EINVAL; + } + + for (end++; isspace(*end); ++end); + + p = end; + + r->flags = strtoul(p, &end, 0); + if (r->flags & ~(V4L2_SUBDEV_ROUTE_FL_ACTIVE)) { + media_dbg(media, "Bad route flags %#x\n", r->flags); + return -EINVAL; + } + + for (; isspace(*end); ++end); + + if (*end != ']') { + media_dbg(media, "Expected ']'\n"); + return -EINVAL; + } + end++; + + for (; isspace(*end); ++end); + + *endp = end; + + return 0; +} + +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; + + entity = media_parse_entity(media, p, &end); + if (!entity) + return -EINVAL; + + p = end; + + if (*p != '[') { + media_dbg(media, "Expected '['\n"); + return -EINVAL; + } + + p++; + + routes = calloc(NUM_ROUTES_MAX, sizeof(routes[0])); + if (!routes) + return -ENOMEM; + + num_routes = 0; + + while (*p != 0) { + struct v4l2_subdev_route *r = &routes[num_routes]; + + ret = v4l2_subdev_parse_setup_route(media, r, p, &end); + if (ret) + goto out; + + p = end; + + num_routes++; + + if (*p == ',') { + p++; + continue; + } + + break; + } + + if (*p != ']') { + media_dbg(media, "Expected ']'\n"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < num_routes; ++i) { + struct v4l2_subdev_route *r = &routes[i]; + + media_dbg(entity->media, + "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, + r->flags); + } + + ret = v4l2_subdev_set_routing(entity, routes, num_routes); + if (ret) { + media_dbg(entity->media, "VIDIOC_SUBDEV_S_ROUTING failed: %d\n", + ret); + goto out; + } + +out: + free(routes); + + return ret; +} + static int v4l2_subdev_parse_format(struct media_device *media, struct v4l2_mbus_framefmt *format, const char *p, char **endp) @@ -442,7 +667,8 @@ static bool strhazit(const char *str, const char **p) } static struct media_pad *v4l2_subdev_parse_pad_format( - struct media_device *media, struct v4l2_mbus_framefmt *format, + struct media_device *media, unsigned int *stream, + struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop, struct v4l2_rect *compose, struct v4l2_fract *interval, const char *p, char **endp) { @@ -453,7 +679,7 @@ static struct media_pad *v4l2_subdev_parse_pad_format( for (; isspace(*p); ++p); - pad = media_parse_pad(media, p, &end); + pad = media_parse_pad_stream(media, p, stream, &end); if (pad == NULL) { *endp = end; return NULL; @@ -675,6 +901,7 @@ static struct media_pad *v4l2_subdev_parse_pad_format( } static int set_format(struct media_pad *pad, + unsigned int stream, struct v4l2_mbus_framefmt *format) { int ret; @@ -683,12 +910,12 @@ static int set_format(struct media_pad *pad, return 0; media_dbg(pad->entity->media, - "Setting up format %s %ux%u on pad %s/%u\n", + "Setting up format %s %ux%u on pad %s/%u/%u\n", v4l2_subdev_pixelcode_to_string(format->code), format->width, format->height, - pad->entity->info.name, pad->index); + pad->entity->info.name, pad->index, stream); - ret = v4l2_subdev_set_format(pad->entity, format, pad->index, + ret = v4l2_subdev_set_format(pad->entity, format, pad->index, stream, V4L2_SUBDEV_FORMAT_ACTIVE); if (ret < 0) { media_dbg(pad->entity->media, @@ -705,8 +932,8 @@ static int set_format(struct media_pad *pad, return 0; } -static int set_selection(struct media_pad *pad, unsigned int target, - struct v4l2_rect *rect) +static int set_selection(struct media_pad *pad, unsigned int stream, + unsigned int target, struct v4l2_rect *rect) { int ret; @@ -714,11 +941,11 @@ static int set_selection(struct media_pad *pad, unsigned int target, return 0; media_dbg(pad->entity->media, - "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u\n", + "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u/%u\n", target, rect->left, rect->top, rect->width, rect->height, - pad->entity->info.name, pad->index); + pad->entity->info.name, pad->index, stream); - ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index, + ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index, stream, target, V4L2_SUBDEV_FORMAT_ACTIVE); if (ret < 0) { media_dbg(pad->entity->media, @@ -734,7 +961,7 @@ static int set_selection(struct media_pad *pad, unsigned int target, return 0; } -static int set_frame_interval(struct media_pad *pad, +static int set_frame_interval(struct media_pad *pad, unsigned int stream, struct v4l2_fract *interval) { int ret; @@ -743,11 +970,12 @@ static int set_frame_interval(struct media_pad *pad, return 0; media_dbg(pad->entity->media, - "Setting up frame interval %u/%u on pad %s/%u\n", + "Setting up frame interval %u/%u on pad %s/%u/%u\n", interval->numerator, interval->denominator, - pad->entity->info.name, pad->index); + pad->entity->info.name, pad->index, stream); - ret = v4l2_subdev_set_frame_interval(pad->entity, interval, pad->index); + ret = v4l2_subdev_set_frame_interval(pad->entity, interval, pad->index, + stream); if (ret < 0) { media_dbg(pad->entity->media, "Unable to set frame interval: %s (%d)", @@ -770,11 +998,13 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media, struct v4l2_rect crop = { -1, -1, -1, -1 }; struct v4l2_rect compose = crop; struct v4l2_fract interval = { 0, 0 }; + unsigned int stream; unsigned int i; char *end; int ret; - pad = v4l2_subdev_parse_pad_format(media, &format, &crop, &compose, + pad = v4l2_subdev_parse_pad_format(media, &stream, + &format, &crop, &compose, &interval, p, &end); if (pad == NULL) { media_print_streampos(media, p, end); @@ -783,30 +1013,29 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media, } if (pad->flags & MEDIA_PAD_FL_SINK) { - ret = set_format(pad, &format); + ret = set_format(pad, stream, &format); if (ret < 0) return ret; } - ret = set_selection(pad, V4L2_SEL_TGT_CROP, &crop); + ret = set_selection(pad, stream, V4L2_SEL_TGT_CROP, &crop); if (ret < 0) return ret; - ret = set_selection(pad, V4L2_SEL_TGT_COMPOSE, &compose); + ret = set_selection(pad, stream, V4L2_SEL_TGT_COMPOSE, &compose); if (ret < 0) return ret; if (pad->flags & MEDIA_PAD_FL_SOURCE) { - ret = set_format(pad, &format); + ret = set_format(pad, stream, &format); if (ret < 0) return ret; } - ret = set_frame_interval(pad, &interval); + ret = set_frame_interval(pad, stream, &interval); if (ret < 0) return ret; - /* If the pad is an output pad, automatically set the same format and * frame interval on the remote subdev input pads, if any. */ @@ -821,9 +1050,9 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media, if (link->source == pad && link->sink->entity->info.type == MEDIA_ENT_T_V4L2_SUBDEV) { remote_format = format; - set_format(link->sink, &remote_format); + set_format(link->sink, stream, &remote_format); - ret = set_frame_interval(link->sink, &interval); + ret = set_frame_interval(link->sink, stream, &interval); if (ret < 0 && ret != -EINVAL && ret != -ENOTTY) return ret; } diff --git a/utils/media-ctl/media-ctl.c b/utils/media-ctl/media-ctl.c index 84ee7a83..1531cffa 100644 --- a/utils/media-ctl/media-ctl.c +++ b/utils/media-ctl/media-ctl.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -75,23 +76,43 @@ static void print_flags(const struct flag_name *flag_names, unsigned int num_ent } } +static void v4l2_subdev_print_routes(struct media_entity *entity, + struct v4l2_subdev_route *routes, + unsigned int num_routes) +{ + unsigned int i; + + if (num_routes) + 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", + route->sink_pad, route->sink_stream, + route->source_pad, route->source_stream, + route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE ? "ACTIVE" : "INACTIVE"); + } +} + static void v4l2_subdev_print_format(struct media_entity *entity, - unsigned int pad, enum v4l2_subdev_format_whence which) + unsigned int pad, unsigned int stream, + enum v4l2_subdev_format_whence which) { struct v4l2_mbus_framefmt format; struct v4l2_fract interval = { 0, 0 }; struct v4l2_rect rect; int ret; - ret = v4l2_subdev_get_format(entity, &format, pad, which); + ret = v4l2_subdev_get_format(entity, &format, pad, stream, which); if (ret != 0) return; - ret = v4l2_subdev_get_frame_interval(entity, &interval, pad); + ret = v4l2_subdev_get_frame_interval(entity, &interval, pad, stream); if (ret != 0 && ret != -ENOTTY && ret != -EINVAL) return; - printf("\t\t[fmt:%s/%ux%u", + printf("\t\t[stream:%u fmt:%s/%ux%u", stream, v4l2_subdev_pixelcode_to_string(format.code), format.width, format.height); @@ -118,28 +139,28 @@ static void v4l2_subdev_print_format(struct media_entity *entity, v4l2_subdev_quantization_to_string(format.quantization)); } - ret = v4l2_subdev_get_selection(entity, &rect, pad, + ret = v4l2_subdev_get_selection(entity, &rect, pad, stream, V4L2_SEL_TGT_CROP_BOUNDS, which); if (ret == 0) printf("\n\t\t crop.bounds:(%u,%u)/%ux%u", rect.left, rect.top, rect.width, rect.height); - ret = v4l2_subdev_get_selection(entity, &rect, pad, + ret = v4l2_subdev_get_selection(entity, &rect, pad, stream, V4L2_SEL_TGT_CROP, which); if (ret == 0) printf("\n\t\t crop:(%u,%u)/%ux%u", rect.left, rect.top, rect.width, rect.height); - ret = v4l2_subdev_get_selection(entity, &rect, pad, + ret = v4l2_subdev_get_selection(entity, &rect, pad, stream, V4L2_SEL_TGT_COMPOSE_BOUNDS, which); if (ret == 0) printf("\n\t\t compose.bounds:(%u,%u)/%ux%u", rect.left, rect.top, rect.width, rect.height); - ret = v4l2_subdev_get_selection(entity, &rect, pad, + ret = v4l2_subdev_get_selection(entity, &rect, pad, stream, V4L2_SEL_TGT_COMPOSE, which); if (ret == 0) @@ -455,12 +476,49 @@ static void media_print_topology_dot(struct media_device *media) } static void media_print_pad_text(struct media_entity *entity, - const struct media_pad *pad) + const struct media_pad *pad, + struct v4l2_subdev_route *routes, + unsigned int num_routes) { + uint64_t printed_streams_mask = 0; + unsigned int i; + if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) return; - v4l2_subdev_print_format(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE); + if (!routes) { + 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; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (pad->flags & MEDIA_PAD_FL_SINK) { + if (route->sink_pad != pad->index) + continue; + + stream = route->sink_stream; + } else { + if (route->source_pad != pad->index) + continue; + + stream = route->source_stream; + } + + if (printed_streams_mask & (1 << stream)) + continue; + + v4l2_subdev_print_format(entity, pad->index, stream, + V4L2_SUBDEV_FORMAT_ACTIVE); + + printed_streams_mask |= (1 << stream); + } + } + v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE); if (pad->flags & MEDIA_PAD_FL_SOURCE) @@ -478,13 +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); + 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)\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" : ""); + + 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), @@ -492,12 +561,15 @@ static void media_print_topology_text_entity(struct media_device *media, if (devname) printf("%*cdevice node name %s\n", padding, ' ', devname); + if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV) + v4l2_subdev_print_routes(entity, routes, num_routes); + for (j = 0; j < info->pads; j++) { const struct media_pad *pad = media_entity_get_pad(entity, j); printf("\tpad%u: %s\n", j, media_pad_type_to_string(pad->flags)); - media_print_pad_text(entity, pad); + media_print_pad_text(entity, pad, routes, num_routes); for (k = 0; k < num_links; k++) { const struct media_link *link = media_entity_get_link(entity, k); @@ -521,6 +593,8 @@ static void media_print_topology_text_entity(struct media_device *media, } } printf("\n"); + + free(routes); } static void media_print_topology_text(struct media_device *media) @@ -594,14 +668,16 @@ int main(int argc, char **argv) if (media_opts.fmt_pad) { struct media_pad *pad; + unsigned int stream; + char *p; - pad = media_parse_pad(media, media_opts.fmt_pad, NULL); + pad = media_parse_pad_stream(media, media_opts.fmt_pad, &stream, &p); if (pad == NULL) { printf("Pad '%s' not found\n", media_opts.fmt_pad); goto out; } - v4l2_subdev_print_format(pad->entity, pad->index, + v4l2_subdev_print_format(pad->entity, pad->index, stream, V4L2_SUBDEV_FORMAT_ACTIVE); } @@ -685,6 +761,15 @@ int main(int argc, char **argv) } } + if (media_opts.routes) { + ret = v4l2_subdev_parse_setup_routes(media, media_opts.routes); + if (ret) { + printf("Unable to setup routes: %s (%d)\n", + strerror(-ret), -ret); + goto out; + } + } + if (media_opts.interactive) { while (1) { char buffer[32]; diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h index af360518..c0fc2962 100644 --- a/utils/media-ctl/mediactl.h +++ b/utils/media-ctl/mediactl.h @@ -394,6 +394,22 @@ struct media_entity *media_parse_entity(struct media_device *media, struct media_pad *media_parse_pad(struct media_device *media, const char *p, char **endp); +/** + * @brief Parse string to a pad and stream on the media device. + * @param media - media device. + * @param p - input string + * @param stream - pointer to uint where the stream number is stored + * @param endp - pointer to string where parsing ended + * + * Parse NULL terminated string describing a pad and stream and return its struct + * media_pad instance and the stream number. + * + * @return Pointer to struct media_pad on success, NULL on failure. + */ +struct media_pad *media_parse_pad_stream(struct media_device *media, + const char *p, unsigned int *stream, + char **endp); + /** * @brief Parse string to a link on the media device. * @param media - media device. diff --git a/utils/media-ctl/options.c b/utils/media-ctl/options.c index 6d30d3dc..3c408a1b 100644 --- a/utils/media-ctl/options.c +++ b/utils/media-ctl/options.c @@ -63,6 +63,7 @@ static void usage(const char *argv0) printf(" --get-v4l2 pad Print the active format on a given pad\n"); printf(" --get-dv pad Print detected and current DV timings on a given pad\n"); printf(" --set-dv pad Configure DV timings on a given pad\n"); + printf("-R, --set-routes routes Configure routes on a given subdev entity\n"); printf("-h, --help Show verbose help and exit\n"); printf("-i, --interactive Modify links interactively\n"); printf("-l, --links links Comma-separated list of link descriptors to setup\n"); @@ -78,7 +79,7 @@ static void usage(const char *argv0) printf("Links and formats are defined as\n"); printf("\tlinks = link { ',' link } ;\n"); printf("\tlink = pad '->' pad '[' flags ']' ;\n"); - printf("\tpad = entity ':' pad-number ;\n"); + printf("\tpad = entity ':' pad-number { '/' stream-number } ;\n"); printf("\tentity = entity-number | ( '\"' entity-name '\"' ) ;\n"); printf("\n"); printf("\tv4l2 = pad '[' v4l2-properties ']' ;\n"); @@ -95,11 +96,16 @@ static void usage(const char *argv0) printf("\trectangle = '(' left ',' top, ')' '/' size ;\n"); printf("\tsize = width 'x' height ;\n"); printf("\n"); + printf("\troutes = entity '[' route { ',' route } ']' ;\n"); + printf("\troute = pad-number '/' stream-number '->' pad-number '/' stream-number '[' route-flags ']' ;\n"); + printf("\n"); printf("where the fields are\n"); printf("\tentity-number Entity numeric identifier\n"); printf("\tentity-name Entity name (string) \n"); printf("\tpad-number Pad numeric identifier\n"); + printf("\tstream-number Stream numeric identifier\n"); printf("\tflags Link flags (0: inactive, 1: active)\n"); + printf("\troute-flags Route flags (bitmask of route flags: active - 0x1)\n"); printf("\tfcc Format FourCC\n"); printf("\twidth Image width in pixels\n"); printf("\theight Image height in pixels\n"); @@ -152,6 +158,7 @@ static struct option opts[] = { {"get-v4l2", 1, 0, OPT_GET_FORMAT}, {"get-dv", 1, 0, OPT_GET_DV}, {"set-dv", 1, 0, OPT_SET_DV}, + {"set-routes", 1, 0, 'R'}, {"help", 0, 0, 'h'}, {"interactive", 0, 0, 'i'}, {"links", 1, 0, 'l'}, @@ -237,7 +244,7 @@ int parse_cmdline(int argc, char **argv) } /* parse options */ - while ((opt = getopt_long(argc, argv, "d:e:f:hil:prvV:", + while ((opt = getopt_long(argc, argv, "d:e:f:hil:prvV:R:", opts, NULL)) != -1) { switch (opt) { case 'd': @@ -283,6 +290,10 @@ int parse_cmdline(int argc, char **argv) media_opts.verbose = 1; break; + case 'R': + media_opts.routes = optarg; + break; + case OPT_PRINT_DOT: media_opts.print_dot = 1; break; diff --git a/utils/media-ctl/options.h b/utils/media-ctl/options.h index 7e0556fc..753d0934 100644 --- a/utils/media-ctl/options.h +++ b/utils/media-ctl/options.h @@ -36,6 +36,7 @@ struct media_options const char *fmt_pad; const char *get_dv_pad; const char *dv_pad; + const char *routes; }; extern struct media_options media_opts; diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h index a1813911..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 @@ -64,7 +65,7 @@ void v4l2_subdev_close(struct media_entity *entity); * @return 0 on success, or a negative error code on failure. */ int v4l2_subdev_get_format(struct media_entity *entity, - struct v4l2_mbus_framefmt *format, unsigned int pad, + struct v4l2_mbus_framefmt *format, unsigned int pad, unsigned int stream, enum v4l2_subdev_format_whence which); /** @@ -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 @@ -86,6 +88,7 @@ int v4l2_subdev_get_format(struct media_entity *entity, */ int v4l2_subdev_set_format(struct media_entity *entity, struct v4l2_mbus_framefmt *format, unsigned int pad, + unsigned int stream, enum v4l2_subdev_format_whence which); /** @@ -93,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. * @@ -107,14 +111,15 @@ int v4l2_subdev_set_format(struct media_entity *entity, * @return 0 on success, or a negative error code on failure. */ int v4l2_subdev_get_selection(struct media_entity *entity, - struct v4l2_rect *rect, unsigned int pad, unsigned int target, - enum v4l2_subdev_format_whence which); + struct v4l2_rect *rect, unsigned int pad, unsigned int stream, + unsigned int target, enum v4l2_subdev_format_whence which); /** * @brief Set a selection rectangle on a pad. * @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. * @@ -129,8 +134,40 @@ int v4l2_subdev_get_selection(struct media_entity *entity, * @return 0 on success, or a negative error code on failure. */ int v4l2_subdev_set_selection(struct media_entity *entity, - struct v4l2_rect *rect, unsigned int pad, unsigned int target, - enum v4l2_subdev_format_whence which); + struct v4l2_rect *rect, unsigned int pad, unsigned int stream, + unsigned int target, enum v4l2_subdev_format_whence which); + +/** + * @brief Get the routing table of a subdev media entity. + * @param entity - subdev-device media entity. + * @param routes - routes of the subdev. + * @param num_routes - number of routes. + * + * Get the routes of @a entity and return them in an allocated array in @a routes + * and the number of routes in @a num_routes. + * + * The caller is responsible for freeing the routes array after use. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_get_routing(struct media_entity *entity, + struct v4l2_subdev_route **routes, + unsigned int *num_routes); + +/** + * @brief Set the routing table of a subdev media entity. + * @param entity - subdev-device media entity. + * @param routes - routes of the subdev. + * @param num_routes - number of routes. + * + * Set the routes of @a entity. The routes are given in @a routes with the + * length of @a num_routes. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_set_routing(struct media_entity *entity, + struct v4l2_subdev_route *route, + unsigned int num_routes); /** * @brief Query the digital video capabilities of a pad. @@ -189,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. @@ -200,12 +239,14 @@ int v4l2_subdev_set_dv_timings(struct media_entity *entity, */ int v4l2_subdev_get_frame_interval(struct media_entity *entity, - struct v4l2_fract *interval, unsigned int pad); + struct v4l2_fract *interval, unsigned int pad, unsigned int stream); /** * @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 @@ -217,7 +258,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity, * @return 0 on success, or a negative error code on failure. */ int v4l2_subdev_set_frame_interval(struct media_entity *entity, - struct v4l2_fract *interval, unsigned int pad); + struct v4l2_fract *interval, unsigned int pad, unsigned int stream); /** * @brief Parse a string and apply format, crop and frame interval settings. @@ -235,6 +276,17 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity, */ int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p); +/** + * @brief Parse a string and apply route settings. + * @param media - media device. + * @param p - input string + * + * Parse string @a p and apply route settings to a subdev. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_parse_setup_routes(struct media_device *media, const char *p); + /** * @brief Convert media bus pixel code to string. * @param code - input string From patchwork Tue Aug 8 06:19:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 711792 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C3C67C41513 for ; Tue, 8 Aug 2023 16:15:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232192AbjHHQO5 (ORCPT ); Tue, 8 Aug 2023 12:14:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45178 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232095AbjHHQNX (ORCPT ); Tue, 8 Aug 2023 12:13:23 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 81BB57EFA for ; Tue, 8 Aug 2023 08:47:30 -0700 (PDT) Received: from [127.0.1.1] (91-154-35-171.elisa-laajakaista.fi [91.154.35.171]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9F9DF208C; Tue, 8 Aug 2023 08:18:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1691475528; bh=BxBuu7NdmKqbSqB+Bircs+myhDQvOE4ABsQudHBFvHE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=kEn69MPJ8Cq0QpOR1U2E/Q8mVl8G4ItwV2TMR/NeVSWXZu3BAIQr7KX6loAIyZYec 4bxaNWzJGi1AL3mnF9wYS/KNNhook5BOUTML5w/5R6Qq1mMcs1UvzyIk7ztPB8czyk vxKTuU4j2WL8OK0cL1hqjybFpSZBILhnwGte/rJs= From: Tomi Valkeinen Date: Tue, 08 Aug 2023 09:19:23 +0300 Subject: [PATCH v7 4/8] v4l2-ctl/compliance: Add routing and streams multiplexed streams MIME-Version: 1.0 Message-Id: <20230808-streams-support-v7-4-bd0b42a5826d@ideasonboard.com> References: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> In-Reply-To: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com, Jacopo Mondi , Laurent Pinchart , niklas.soderlund+renesas@ragnatech.se, Mauro Carvalho Chehab , Hans Verkuil , satish.nagireddy@getcruise.com Cc: Tomi Valkeinen X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=19526; i=tomi.valkeinen@ideasonboard.com; h=from:subject:message-id; bh=BxBuu7NdmKqbSqB+Bircs+myhDQvOE4ABsQudHBFvHE=; b=owEBbQKS/ZANAwAIAfo9qoy8lh71AcsmYgBk0d6FJISCdV/3A2u4pWfyZTxfDmXxTzAhU679C WAXzfQms+yJAjMEAAEIAB0WIQTEOAw+ll79gQef86f6PaqMvJYe9QUCZNHehQAKCRD6PaqMvJYe 9UnMD/9hT0sOmxB566OlXcQaqMh+oY5CW5zqHJkdr5SIb0M+FhLIIlNJY4aeJ1lyLKUcjIk4gEO w3E4SUAld3F7jQRgrfaGbFyoXgDYQIbF3/zqzUP3/G8sP1vagks2sYlwJxkbAgkjvHUHx/Qr/Af Hotn4Nn5uee800F6gpzpS08XXL8EiY+SI1HvhcWYLTie9+nT9f90Kw4mEkrGB85vY4a/rkORCX2 gMbZIDiLws3sb7lu4RuEC06kdtRvSAKtjolf8mKVTH8H023I/7CYDrxqfDtScN9DLiFS0P+eOC3 3lkF8rgqcX2CdH0mkHddBx5RE0GenN2pip7L7fOgAkHWaWRBrsM+KCygkYxwVFPX+P++kKGGTeQ P9oDg73YpcHbzdGr8o16+Ce7hIlfbZZtb7XFAQRw/vlfxbw+RIv5WK5dtWY6eSKdJie4GWWXl/s fvr8DdAGmXIyRBFYYNletujifrn7rqdEnFsgb2P/yOzeK0b24cKWKHjYmrDCtu6DCwy3t6keloe XqBXhU4zFa8VonWpICKpWxmd1oYophjIa+j6MtYMTB4vZ0hwdiWWy/oSBLLMG9UGMJvnrr8rB+Y xKkfvS4+xWzJ90+ZbbJt9YKVO3pO1mWpgx+gxd53y4oNPM9PIqR7A9EUZbUji5jBMF5LKAO4HIz IZNQpzWhRnDDrSA== X-Developer-Key: i=tomi.valkeinen@ideasonboard.com; a=openpgp; fpr=C4380C3E965EFD81079FF3A7FA3DAA8CBC961EF5 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add basic support for routing and streams. Signed-off-by: Tomi Valkeinen --- utils/v4l2-compliance/v4l2-compliance.cpp | 120 ++++++++++++++++++++++------ utils/v4l2-compliance/v4l2-compliance.h | 8 +- utils/v4l2-compliance/v4l2-test-subdevs.cpp | 43 ++++++++-- 3 files changed, 137 insertions(+), 34 deletions(-) diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp index e3556b1f..e8359b2f 100644 --- a/utils/v4l2-compliance/v4l2-compliance.cpp +++ b/utils/v4l2-compliance/v4l2-compliance.cpp @@ -1233,6 +1233,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][NUM_ROUTES_MAX] = {}; + bool has_routes = !!(subdevcap.capabilities & V4L2_SUBDEV_CAP_STREAMS); + int ret; node.frame_interval_pad = -1; node.enum_frame_interval_pad = -1; @@ -1244,6 +1248,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 = NUM_ROUTES_MAX; + + 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) ? @@ -1253,32 +1273,82 @@ 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->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"); } } diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h index 88b792cd..0cd43980 100644 --- a/utils/v4l2-compliance/v4l2-compliance.h +++ b/utils/v4l2-compliance/v4l2-compliance.h @@ -371,10 +371,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); diff --git a/utils/v4l2-compliance/v4l2-test-subdevs.cpp b/utils/v4l2-compliance/v4l2-test-subdevs.cpp index 27f26576..5545b0dc 100644 --- a/utils/v4l2-compliance/v4l2-test-subdevs.cpp +++ b/utils/v4l2-compliance/v4l2-test-subdevs.cpp @@ -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_STREAMS) int testSubDevCap(struct node *node) { @@ -54,6 +54,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; @@ -83,6 +84,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; @@ -128,6 +130,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; @@ -137,6 +140,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; @@ -152,6 +156,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)); @@ -195,7 +200,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; @@ -204,6 +209,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) { @@ -214,8 +220,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; @@ -226,16 +234,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 { @@ -252,6 +263,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); @@ -260,7 +272,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; @@ -268,6 +280,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); @@ -279,6 +292,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); @@ -290,20 +304,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)); @@ -340,7 +359,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; @@ -349,6 +368,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) { @@ -359,14 +379,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)); @@ -379,6 +402,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; @@ -423,7 +447,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; @@ -435,10 +459,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; @@ -451,6 +477,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++) { @@ -458,6 +485,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; @@ -469,6 +497,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); @@ -480,9 +509,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); @@ -496,6 +527,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))); } } @@ -504,6 +536,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) From patchwork Tue Aug 8 06:19:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 711793 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E9520C04FDF for ; Tue, 8 Aug 2023 16:15:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232332AbjHHQPA (ORCPT ); Tue, 8 Aug 2023 12:15:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57598 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232181AbjHHQN0 (ORCPT ); Tue, 8 Aug 2023 12:13:26 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 04DA93C0E for ; Tue, 8 Aug 2023 08:47:34 -0700 (PDT) Received: from [127.0.1.1] (91-154-35-171.elisa-laajakaista.fi [91.154.35.171]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 63DA32090; Tue, 8 Aug 2023 08:18:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1691475528; bh=E3JGyBVQU2JsqzU8CPu88Pxvdao6yJRYM+76tiyNuJA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=S6kQH+dCr011qcX+q9p7K9zN+E5BRFLT4Nc2LH4ZF7NwMOwkwubij/voWpcsiIcBB +n+WGD06MS2Vt1RSBYAk0dwUnHuJL7K0G+1PE9Bt2C+6agwIDmlovY7M7M4PD4AvBg De/3y1kXBjFGlORJMae6AXxnTluaOGins5p/jlOc= From: Tomi Valkeinen Date: Tue, 08 Aug 2023 09:19:24 +0300 Subject: [PATCH v7 5/8] v4l2-ctl/compliance: Add simple routing test MIME-Version: 1.0 Message-Id: <20230808-streams-support-v7-5-bd0b42a5826d@ideasonboard.com> References: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> In-Reply-To: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com, Jacopo Mondi , Laurent Pinchart , niklas.soderlund+renesas@ragnatech.se, Mauro Carvalho Chehab , Hans Verkuil , satish.nagireddy@getcruise.com Cc: Tomi Valkeinen X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=5173; i=tomi.valkeinen@ideasonboard.com; h=from:subject:message-id; bh=E3JGyBVQU2JsqzU8CPu88Pxvdao6yJRYM+76tiyNuJA=; b=owEBbQKS/ZANAwAIAfo9qoy8lh71AcsmYgBk0d6GYa3LOEAftMrjPpL1888/syyOPIL9gavJB zZr6KlcGyqJAjMEAAEIAB0WIQTEOAw+ll79gQef86f6PaqMvJYe9QUCZNHehgAKCRD6PaqMvJYe 9RN+D/4/JEL7TZ+f4gVmBOFATRRf7n9aYt0G0CV3h7gOJkLL3PYbf5G54BOymX9KT12xu40Vo+e /jiS5GuElUae0Ew43Uqc1ITQDCQLlHvSJ2XoNczTnRLue4m3E/SgtEWBTTMqKBuviVPvVbOQnoN ianj6MVPNlUc5Ex2gzgBfNCGXtARAr+pF3xTYooHg6QksUZSqHSfxP5Z3rYPlBdOGul6UiEH3qn w33iv3wcWB7vVS29rVNWUVGbR4faxgIBytFLlRW5/qU7GB+1QncFoihkGjrUx7GNxBiF9ey+z10 ZCfciYvlqL9ajhsbN7S5NCxsFJIf3wOpFrtx1s7yooE4pL+twYCj0AU9l9xGapIJ0x8SRUFd+r9 QspRxcjKRxmYg0YpzhFZY9C+PGv7ugWVuGaIsgdEmHdd/ad6D4e5qxvFyRZvEX6wLstMe6bXU6E ghDPu947uUvXoJSfitEqHGMpZtbbIF3+hF0QbaKaS8H9i5cd3uBZ9DAKBCS1FzsXt3kHVdL+GqM f6ZLvCq1/kHrMJiXHNGfzST8kWgt7G872FbeH0jgLlMIGPu4EVSeMA6RGqWXP/6AkuBV5bPBc2Y 9lsFLsR/w02c+/FQeDrRNaXBdSnSyuUDuDc/8+TrKNjxc5AdajLagMghzRG/RcLSNms8hCdbZMk iTmz2NqHwKJA45A== X-Developer-Key: i=tomi.valkeinen@ideasonboard.com; a=openpgp; fpr=C4380C3E965EFD81079FF3A7FA3DAA8CBC961EF5 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org 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 --- 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 --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; +} From patchwork Tue Aug 8 06:19:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 711794 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D6139C07E8B for ; Tue, 8 Aug 2023 16:14:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232130AbjHHQO3 (ORCPT ); Tue, 8 Aug 2023 12:14:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51674 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232161AbjHHQNU (ORCPT ); Tue, 8 Aug 2023 12:13:20 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BC2597EF4 for ; Tue, 8 Aug 2023 08:47:26 -0700 (PDT) Received: from [127.0.1.1] (91-154-35-171.elisa-laajakaista.fi [91.154.35.171]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2D96F29C2; Tue, 8 Aug 2023 08:18:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1691475529; bh=lCm58VbDkbfK5KGtuZSnxvgNUZOCx2S+nYJf8oWy7WI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=qo+NWYPr4H0bVPvlozcZVdiNWpUnvIXE2ygoxyAzCfRackNRLpTNGUXUP31cGwcfn LE5SmsRvOQrXLWfjAWATni50CFnOLINQdpDHNXkDLFe9YXLZuWVWXehEHx99BUDofg lk5i/2QxN9eBUHPQKlXxSnGArtmxZbH00ML0LX3U= From: Tomi Valkeinen Date: Tue, 08 Aug 2023 09:19:25 +0300 Subject: [PATCH v7 6/8] media-ctl: Check for Streams API support MIME-Version: 1.0 Message-Id: <20230808-streams-support-v7-6-bd0b42a5826d@ideasonboard.com> References: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> In-Reply-To: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com, Jacopo Mondi , Laurent Pinchart , niklas.soderlund+renesas@ragnatech.se, Mauro Carvalho Chehab , Hans Verkuil , satish.nagireddy@getcruise.com Cc: Tomi Valkeinen , Laurent Pinchart X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4698; i=tomi.valkeinen@ideasonboard.com; h=from:subject:message-id; bh=lCm58VbDkbfK5KGtuZSnxvgNUZOCx2S+nYJf8oWy7WI=; b=owEBbQKS/ZANAwAIAfo9qoy8lh71AcsmYgBk0d6GBENQvEf6/vYICHgfdv+PbWYtv9NF/lV+u uMDIzqeUUmJAjMEAAEIAB0WIQTEOAw+ll79gQef86f6PaqMvJYe9QUCZNHehgAKCRD6PaqMvJYe 9WuZD/9Gt9zbzsFwtcWH4MXcFpTXmUDHlqw0SafAQIepCx7t2MM0brxBV9zN+MstjacKhLCuH/L NwxPW2ASqQL6qOPiCjptQ9GygYgcrhUaUghbsyTL8xEec4ZIdEBq/VejcNlPYJu4Yivup//tMpj 9bYrgeOlySDiFqZr+ITZdsHBH0zY992o0nKkj/VNIhubAQXvQct8XMK//qHD7b6cYKRbc494403 uUZr0ZkREgUiKNxwaI8xku2atq/UTeAqK3lE/PlGZ2mr+W6JZtMFLjP+IV+bek6HglnSLIFvZKJ MM6/RyZJ3Tvi2QtGjEWuaJmbgP3YjVhvC1wi1z4letZoWrwwSNp3hYlcZR1ZddQbLl8JSuQmeXD 54TmAOfL7rE9lMSndkqpOF/K9NxIaLIMXpk5N1lSmuzuQMFRC5C0J+zXu6oq6AoyVHDscXgFE5o HYW/k2OZ1l2JWYKd5q1fQgXn/OuXQ9zM3W6XOrlk1l0qtEypd9Tygcm+ZVHfjaPcKtUof+R8x7u z7l5iPFLpPPiAGIthSEiWTRW5Icrplay5NGjccO0jmLR+ty5ieFMX0h+1p/CiOm6ui9/FfRXHCx nT7cTbHZGB8OPxPn2Kt+QIgypwcMtdhJuyVx9IN43BRgRA/Hpl9QKeR0MeClt9orv4LorGoeIVW VLU/kAmKFfguRUw== X-Developer-Key: i=tomi.valkeinen@ideasonboard.com; a=openpgp; fpr=C4380C3E965EFD81079FF3A7FA3DAA8CBC961EF5 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Use the new VIDIOC_SUBDEV_S_CLIENT_CAP ioctl to inform the kernel that we support streams, and use the return values of VIDIOC_SUBDEV_S_CLIENT_CAP and VIDIOC_SUBDEV_QUERYCAP to decide if the entity supports streams. If not, return an error in case the user tries to use streams. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- utils/media-ctl/libv4l2subdev.c | 56 +++++++++++++++++++++++++++++++++++++++++ utils/media-ctl/mediactl-priv.h | 1 + 2 files changed, 57 insertions(+) diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c index 2144a527..16e1284c 100644 --- a/utils/media-ctl/libv4l2subdev.c +++ b/utils/media-ctl/libv4l2subdev.c @@ -48,6 +48,12 @@ int v4l2_subdev_open(struct media_entity *entity) { + struct v4l2_subdev_client_capability clientcap = {}; + struct v4l2_subdev_capability subdevcap = {}; + bool subdev_streams; + bool client_streams; + int ret; + if (entity->fd != -1) return 0; @@ -60,6 +66,16 @@ int v4l2_subdev_open(struct media_entity *entity) return ret; } + ret = ioctl(entity->fd, VIDIOC_SUBDEV_QUERYCAP, &subdevcap); + subdev_streams = !ret && (subdevcap.capabilities & V4L2_SUBDEV_CAP_STREAMS); + + clientcap.capabilities = V4L2_SUBDEV_CLIENT_CAP_STREAMS; + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_CLIENT_CAP, &clientcap); + client_streams = !ret && (clientcap.capabilities & V4L2_SUBDEV_CLIENT_CAP_STREAMS); + + entity->supports_streams = subdev_streams && client_streams; + return 0; } @@ -80,6 +96,11 @@ int v4l2_subdev_get_format(struct media_entity *entity, if (ret < 0) return ret; + if (!entity->supports_streams && stream) { + media_dbg(entity->media, "Streams API not supported\n"); + return -ENOTSUP; + } + memset(&fmt, 0, sizeof(fmt)); fmt.pad = pad; fmt.stream = stream; @@ -105,6 +126,11 @@ int v4l2_subdev_set_format(struct media_entity *entity, if (ret < 0) return ret; + if (!entity->supports_streams && stream) { + media_dbg(entity->media, "Streams API not supported\n"); + return -ENOTSUP; + } + memset(&fmt, 0, sizeof(fmt)); fmt.pad = pad; fmt.stream = stream; @@ -133,6 +159,11 @@ int v4l2_subdev_get_selection(struct media_entity *entity, if (ret < 0) return ret; + if (!entity->supports_streams && stream) { + media_dbg(entity->media, "Streams API not supported\n"); + return -ENOTSUP; + } + memset(&u.sel, 0, sizeof(u.sel)); u.sel.pad = pad; u.sel.target = target; @@ -172,6 +203,11 @@ int v4l2_subdev_set_selection(struct media_entity *entity, if (ret < 0) return ret; + if (!entity->supports_streams && stream) { + media_dbg(entity->media, "Streams API not supported\n"); + return -ENOTSUP; + } + memset(&u.sel, 0, sizeof(u.sel)); u.sel.pad = pad; u.sel.stream = stream; @@ -215,6 +251,11 @@ int v4l2_subdev_get_routing(struct media_entity *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_G_ROUTING, &routing); if (ret == -1 && errno != ENOSPC) return -errno; @@ -257,6 +298,11 @@ int v4l2_subdev_set_routing(struct media_entity *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; @@ -347,6 +393,11 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity, if (ret < 0) return ret; + if (!entity->supports_streams && stream) { + media_dbg(entity->media, "Streams API not supported\n"); + return -ENOTSUP; + } + memset(&ival, 0, sizeof(ival)); ival.pad = pad; ival.stream = stream; @@ -370,6 +421,11 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity, if (ret < 0) return ret; + if (!entity->supports_streams && stream) { + media_dbg(entity->media, "Streams API not supported\n"); + return -ENOTSUP; + } + memset(&ival, 0, sizeof(ival)); ival.pad = pad; ival.stream = stream; diff --git a/utils/media-ctl/mediactl-priv.h b/utils/media-ctl/mediactl-priv.h index a0d3a55a..eb55e07e 100644 --- a/utils/media-ctl/mediactl-priv.h +++ b/utils/media-ctl/mediactl-priv.h @@ -33,6 +33,7 @@ struct media_entity { struct media_link *links; unsigned int max_links; unsigned int num_links; + bool supports_streams; char devname[32]; int fd; From patchwork Tue Aug 8 06:19:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 712185 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 09850C04FE1 for ; Tue, 8 Aug 2023 16:15:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232195AbjHHQO7 (ORCPT ); Tue, 8 Aug 2023 12:14:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45184 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232133AbjHHQNX (ORCPT ); Tue, 8 Aug 2023 12:13:23 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id ACD103C11 for ; Tue, 8 Aug 2023 08:47:32 -0700 (PDT) Received: from [127.0.1.1] (91-154-35-171.elisa-laajakaista.fi [91.154.35.171]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id F2B273348; Tue, 8 Aug 2023 08:18:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1691475530; bh=GsspOjXZfVUwLjEEzLRR1gtnOd9Ywcd84/KazWpZWvM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=FQ84imOI6+dC5FDwPeD5uvoPY9Z/3Dmp9TNwsA+lWaFfmd7ZzHBeisZd2VuAmjRJl Jy3iSfgj5Ex06FhPscMgt3lj+1jBFrwoJaoJgdH4gFWy8K/+CDNZDMHNCR+wvDNawB jn04VpspXeqORIiRplvASs68ZvQoTy8HRv5grOTA= From: Tomi Valkeinen Date: Tue, 08 Aug 2023 09:19:26 +0300 Subject: [PATCH v7 7/8] utils/common: Set V4L2_SUBDEV_CLIENT_CAP_STREAMS for subdevs MIME-Version: 1.0 Message-Id: <20230808-streams-support-v7-7-bd0b42a5826d@ideasonboard.com> References: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> In-Reply-To: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com, Jacopo Mondi , Laurent Pinchart , niklas.soderlund+renesas@ragnatech.se, Mauro Carvalho Chehab , Hans Verkuil , satish.nagireddy@getcruise.com Cc: Tomi Valkeinen , Laurent Pinchart X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2607; i=tomi.valkeinen@ideasonboard.com; h=from:subject:message-id; bh=GsspOjXZfVUwLjEEzLRR1gtnOd9Ywcd84/KazWpZWvM=; b=owEBbQKS/ZANAwAIAfo9qoy8lh71AcsmYgBk0d6Gjs5kwsuPAF5CzqhIs7PXSepbpr8kn+wLZ qftrhl3OzWJAjMEAAEIAB0WIQTEOAw+ll79gQef86f6PaqMvJYe9QUCZNHehgAKCRD6PaqMvJYe 9TYCD/9Z2xFMA/AEvT+Y09tMwLnojf6PVTTINYUk+cB61GSF2ksMKh/i4dy2By0gHwi+b5ILoKO A/K91E+CPNWSC+PqXzMeLJTPdxmndzvCX1paXVQLzNBFTgAMfBKyV69PwzNn4H0RYz3eVzv8lv5 Uphf6xGu8FwJuSEiELVCFVSMbmHUh83RmaBjUKI0hEdypDQILI4eS4kuGRDW8GhVyTF60A4t3AY RxP5NHsnMzHN/scEt9QvWLoFPyj7PQPZMtBoIHi9FcpMt1P+CnYTTQLPSUE1q+F8dvXEBsd2wv4 g/V9AibzmS5ssmzIJC015/NlRjnoj/sUyVztBPwyLCDHMEVUzQgqlbz/dHW8wUVYkLGHtugSjx4 Che89O7DBoQDf0ial3UDmiPL4BYbWJSrEWy1D5YxWsq+E/yZ5J7V1lIwHwlXL6O2jEo8JGDpBdy Bj8v7pLaWVeFMrmc5rX2h5mhZEbjqM2ShWY6Zb7HqQZCH2nBdDD12tKuTiZKaPt8lAM1FsE20jP b1z+eLXKNgGpeQxuwPZ5qxkBUbN6emoaFgOVKDlJZjHclpTNVM5Ob3AT/JN64RSXMR9yskuRq/w Z7domN0Xr8IUcr8DpBYm4LSgS5vIs19aIdXBcMl2NB2Pq4YBxd5zSGkXxpXuaQwx+2CDVLR/07d kPrrYHUu4MCFQ6Q== X-Developer-Key: i=tomi.valkeinen@ideasonboard.com; a=openpgp; fpr=C4380C3E965EFD81079FF3A7FA3DAA8CBC961EF5 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Use the new VIDIOC_SUBDEV_S_CLIENT_CAP ioctl to inform the kernel that we support streams, and use the return values of VIDIOC_SUBDEV_S_CLIENT_CAP and VIDIOC_SUBDEV_QUERYCAP to decide if the entity supports streams. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- utils/common/cv4l-helpers.h | 1 + utils/common/v4l-helpers.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/utils/common/cv4l-helpers.h b/utils/common/cv4l-helpers.h index 3cee372b..91a04146 100644 --- a/utils/common/cv4l-helpers.h +++ b/utils/common/cv4l-helpers.h @@ -82,6 +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() 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 c09cd987..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 #include #include #include @@ -39,6 +40,7 @@ struct v4l_fd { bool have_selection; bool is_subdev; bool is_media; + bool have_streams; int (*open)(struct v4l_fd *f, const char *file, int oflag, ...); int (*close)(struct v4l_fd *f); @@ -507,6 +509,12 @@ static inline int v4l_open(struct v4l_fd *f, const char *devname, bool non_block static inline int v4l_subdev_s_fd(struct v4l_fd *f, int fd, const char *devname) { + struct v4l2_subdev_client_capability clientcap = {}; + struct v4l2_subdev_capability subdevcap = {}; + bool subdev_streams; + bool client_streams; + int ret; + if (f->fd >= 0) f->close(f); @@ -528,6 +536,16 @@ static inline int v4l_subdev_s_fd(struct v4l_fd *f, int fd, const char *devname) f->have_next_ctrl = false; f->have_selection = false; + ret = ioctl(f->fd, VIDIOC_SUBDEV_QUERYCAP, &subdevcap); + subdev_streams = !ret && (subdevcap.capabilities & V4L2_SUBDEV_CAP_STREAMS); + + clientcap.capabilities = V4L2_SUBDEV_CLIENT_CAP_STREAMS; + + ret = ioctl(f->fd, VIDIOC_SUBDEV_S_CLIENT_CAP, &clientcap); + client_streams = !ret && (clientcap.capabilities & V4L2_SUBDEV_CLIENT_CAP_STREAMS); + + f->have_streams = subdev_streams && client_streams; + return f->fd; } From patchwork Tue Aug 8 06:19:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 711796 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6DA2EC3DA40 for ; Tue, 8 Aug 2023 16:13:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232010AbjHHQNW (ORCPT ); Tue, 8 Aug 2023 12:13:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57450 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232166AbjHHQMH (ORCPT ); Tue, 8 Aug 2023 12:12:07 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 439007AB5 for ; Tue, 8 Aug 2023 08:46:57 -0700 (PDT) Received: from [127.0.1.1] (91-154-35-171.elisa-laajakaista.fi [91.154.35.171]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C05AD38A8; Tue, 8 Aug 2023 08:18:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1691475531; bh=y4SyayDObfOHUfDN/RQOYZPlUD/FGwsC9Zp+PiL2rM8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=DR62jbrGN5jGi8eJ/HZ9kiIV981oP0y/C/21lM6ECFb7GaShMMjXxREDs3x8X4p4c 9VhJu+K4xLBhAUNKsMy5vXsDaUdVDxKTBR1Jn52fXPDMR6fjwGm0xrYCJW6TeJF1xC KttCqpS9yQCxPtIVPcnkKQEn1xlWANxVQRmmesZE= From: Tomi Valkeinen Date: Tue, 08 Aug 2023 09:19:27 +0300 Subject: [PATCH v7 8/8] v4l2-ctl: Check for Streams API support MIME-Version: 1.0 Message-Id: <20230808-streams-support-v7-8-bd0b42a5826d@ideasonboard.com> References: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> In-Reply-To: <20230808-streams-support-v7-0-bd0b42a5826d@ideasonboard.com> To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com, Jacopo Mondi , Laurent Pinchart , niklas.soderlund+renesas@ragnatech.se, Mauro Carvalho Chehab , Hans Verkuil , satish.nagireddy@getcruise.com Cc: Tomi Valkeinen , Laurent Pinchart X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4441; i=tomi.valkeinen@ideasonboard.com; h=from:subject:message-id; bh=y4SyayDObfOHUfDN/RQOYZPlUD/FGwsC9Zp+PiL2rM8=; b=owEBbQKS/ZANAwAIAfo9qoy8lh71AcsmYgBk0d6Ho73QlbUSUAD/AXpoGVgHp3vE9ta4vsOCo BrnWb7N6u+JAjMEAAEIAB0WIQTEOAw+ll79gQef86f6PaqMvJYe9QUCZNHehwAKCRD6PaqMvJYe 9VPBD/95aA5fkHJ0IAuNxJ79TyRPqsL+QYpx/MZBIBk1FqhoKywVb0YBq550BotsQD7Qwq0E3Iq /1mvTZpXK225wVrpTIpuuSdgCvovMDmxsy4NzmRPFJJszzg0VcNdIS47yxSIBd8unVYhW96hjrh pjMraKvCTVFjJKuFMes39uetwh2gsFfLPwspKti5weBdwUof27/AJd076jVk+k+WRVMtKZzNn5G AwKLoSylBfzgwk4WUA0rJwNIp/9ZR7kZVyhSHyyDl0/eisKtV4wJzsfirL824viP+uQi81zVRJR sYsEZ/Ppsb3/w3TIC9PWN2OCSgCa6o1/Wtgg8XTLgfgUawwOYNS5wcF0//ZJ0mtrFmhyBi5Mdow gQPDhDyXVabwJT9oARCHLL5lrgAzGWXNIuJZnxuiXARBt2ZFhw9pDVOOwCATOwFwlxKCSGbB10K RTkT2bPy33U4CzeVhPay7MNam01jH1QVfRjbbm0wR7mLGzrFG6kaiWV6VO5uZa8I/yUke2AIQM1 3gbfbjcBBukRNK1FuGyW7w8fy3oIeLnP7mUZ+VLt+D2n0G5dgIWoHz2NKnMwRouoA4wHXJthJzZ +M63bCvvxGxHL2kCRSg89pZNu2rmlqg/lselJNv9AwerBfKFZYva48YSzbf1OIZBZG/8BRQtozQ naKflGEw/VFVOTg== X-Developer-Key: i=tomi.valkeinen@ideasonboard.com; a=openpgp; fpr=C4380C3E965EFD81079FF3A7FA3DAA8CBC961EF5 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Return an error if the user tries to use streams related features, but streams are not supported. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- utils/v4l2-ctl/v4l2-ctl-subdev.cpp | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp index d906b72d..b6d115fe 100644 --- a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp @@ -562,6 +562,11 @@ void subdev_set(cv4l_fd &_fd) if (options[OptSetSubDevFormat] || options[OptTrySubDevFormat]) { struct v4l2_subdev_format fmt; + if (!_fd.has_streams() && set_fmt_stream) { + printf("Streams API not supported.\n"); + return; + } + memset(&fmt, 0, sizeof(fmt)); fmt.pad = set_fmt_pad; fmt.stream = set_fmt_stream; @@ -610,6 +615,11 @@ void subdev_set(cv4l_fd &_fd) if (options[OptSetSubDevSelection] || options[OptTrySubDevSelection]) { struct v4l2_subdev_selection sel; + if (!_fd.has_streams() && vsel.stream) { + printf("Streams API not supported.\n"); + return; + } + memset(&sel, 0, sizeof(sel)); sel.pad = vsel.pad; sel.stream = vsel.stream; @@ -642,6 +652,11 @@ void subdev_set(cv4l_fd &_fd) if (options[OptSetSubDevFPS]) { struct v4l2_subdev_frame_interval fival; + if (!_fd.has_streams() && set_fps_stream) { + printf("Streams API not supported.\n"); + return; + } + memset(&fival, 0, sizeof(fival)); fival.pad = set_fps_pad; fival.stream = set_fps_stream; @@ -667,6 +682,11 @@ void subdev_set(cv4l_fd &_fd) } } if (options[OptSetRouting]) { + if (!_fd.has_streams()) { + printf("Streams API not supported.\n"); + return; + } + if (doioctl(fd, VIDIOC_SUBDEV_S_ROUTING, &routing) == 0) printf("Routing set\n"); } @@ -724,6 +744,11 @@ void subdev_get(cv4l_fd &_fd) if (options[OptGetSubDevFormat]) { struct v4l2_subdev_format fmt; + if (!_fd.has_streams() && get_fmt_stream) { + printf("Streams API not supported.\n"); + return; + } + memset(&fmt, 0, sizeof(fmt)); fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt.pad = get_fmt_pad; @@ -738,6 +763,11 @@ void subdev_get(cv4l_fd &_fd) struct v4l2_subdev_selection sel; unsigned idx = 0; + if (!_fd.has_streams() && get_sel_stream) { + printf("Streams API not supported.\n"); + return; + } + memset(&sel, 0, sizeof(sel)); sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; sel.pad = get_sel_pad; @@ -760,6 +790,11 @@ void subdev_get(cv4l_fd &_fd) if (options[OptGetSubDevFPS]) { struct v4l2_subdev_frame_interval fival; + if (!_fd.has_streams() && get_fps_stream) { + printf("Streams API not supported.\n"); + return; + } + memset(&fival, 0, sizeof(fival)); fival.pad = get_fps_pad; fival.stream = get_fps_stream; @@ -777,6 +812,11 @@ void subdev_get(cv4l_fd &_fd) } if (options[OptGetRouting]) { + if (!_fd.has_streams()) { + printf("Streams API not supported.\n"); + return; + } + memset(&routing, 0, sizeof(routing)); memset(routes, 0, sizeof(routes[0]) * NUM_ROUTES_MAX); routing.which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -860,11 +900,21 @@ void subdev_list(cv4l_fd &_fd) int fd = _fd.g_fd(); if (options[OptListSubDevMBusCodes]) { + if (!_fd.has_streams() && list_mbus_codes_stream) { + printf("Streams API not supported.\n"); + return; + } + 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]) { + if (!_fd.has_streams() && frmsize.stream) { + printf("Streams API not supported.\n"); + return; + } + printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE (pad=%u,stream=%u)\n", frmsize.pad, frmsize.stream); frmsize.index = 0; @@ -875,6 +925,11 @@ void subdev_list(cv4l_fd &_fd) } } if (options[OptListSubDevFrameIntervals]) { + if (!_fd.has_streams() && frmival.stream) { + printf("Streams API not supported.\n"); + return; + } + printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL (pad=%u,stream=%u)\n", frmival.pad, frmival.stream); frmival.index = 0;