From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Hans Verkuil <hverkuil+cisco@kernel.org>
Cc: linux-media@vger.kernel.org
Subject: Re: v4l2-compliance does not pick media device with -z and -u options
Date: Fri, 12 Jun 2026 18:16:10 +0300 [thread overview]
Message-ID: <20260612151610.GA2074789@killaraus.ideasonboard.com> (raw)
In-Reply-To: <d2625a27-bd99-4a87-98cb-346b6271380c@kernel.org>
Hi Hans,
On Fri, Jun 12, 2026 at 01:37:49PM +0200, Hans Verkuil wrote:
> On 10/06/2026 13:33, Laurent Pinchart wrote:
> > Hi Hans,
> >
> > I've noticed yesterday that v4l2-compliance does not pick a media device
> > when run on a subdev with the -z and -u options.
> >
> > The options are documented as
> >
> > -u, --subdev-device <dev>
> > Use device <dev> as the v4l-subdev device.
> > If <dev> starts with a digit, then /dev/v4l-subdev<dev> is used.
> > [...]
> > Otherwise if -z was specified earlier, then <dev> is the entity name
> > or interface ID (if prefixed with 0x) as found in the topology of the
> > media device with the bus info string as specified by the -z option.
> >
> > -z, --media-bus-info <bus-info>
> > Find the media device with the given bus info string. If set, then
> > the options above can use the entity name or interface ID to refer
> > to the device nodes.
> >
> >
> > I reproduced the issue on an i.MX8MP and a Raspberry Pi 5. On i.MX8MP, I
> > ran
> >
> > $ v4l2-compliance -z "platform:rkisp1" -u "imx219 1-0010"
> >
> > where "imx219 1-0010" is the sensor entity in the ISP media graph. This
> > resulted in the following output:
> >
> > --------
> > v4l2-compliance 1.33.0-5474, 64 bits, 64-bit time_t
> > v4l2-compliance SHA: 73e05fa3c79b 2026-06-02 06:15:45
> >
> > Compliance test for device /dev/v4l-subdev3:
> >
> > Driver Info:
> > Driver version : 7.1.0
> > Capabilities : 0x00000000
> > Client Capabilities: 0x0000000000000003
> > streams interval-uses-which media_fd 4294967295 ent_id 0x00000000
> >
> > Required ioctls:
> > test VIDIOC_SUDBEV_QUERYCAP: OK
> > test invalid ioctls: OK
> >
> > Allow for multiple opens:
> > test second /dev/v4l-subdev3 open: OK
> > test VIDIOC_SUBDEV_QUERYCAP: OK
> > test for unlimited opens: OK
> >
> > Debug ioctls:
> > test VIDIOC_LOG_STATUS: OK (Not Supported)
> >
> > Input ioctls:
> > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> > test VIDIOC_ENUMAUDIO: OK (Not Supported)
> > test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
> > test VIDIOC_G/S_AUDIO: OK (Not Supported)
> > Inputs: 0 Audio Inputs: 0 Tuners: 0
> >
> > Output ioctls:
> > test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> > test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> > Outputs: 0 Audio Outputs: 0 Modulators: 0
> >
> > Input/Output configuration ioctls:
> > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
> > test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
> > test VIDIOC_G/S_EDID: OK (Not Supported)
> >
> > Control ioctls:
> > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> > test VIDIOC_QUERYCTRL: OK
> > test VIDIOC_G/S_CTRL: OK
> > test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> > Standard Controls: 20 Private Controls: 0
> >
> > Format ioctls:
> > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
> > test VIDIOC_G/S_PARM: OK (Not Supported)
> > test VIDIOC_G_FBUF: OK (Not Supported)
> > test VIDIOC_G_FMT: OK (Not Supported)
> > test VIDIOC_TRY_FMT: OK (Not Supported)
> > test VIDIOC_S_FMT: OK (Not Supported)
> > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> > test Cropping: OK (Not Supported)
> > test Composing: OK (Not Supported)
> > test Scaling: OK (Not Supported)
> >
> > Codec ioctls:
> > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> > test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
> >
> > Buffer ioctls:
> > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
> > test CREATE_BUFS maximum buffers: OK
> > test VIDIOC_REMOVE_BUFS: OK
> > test VIDIOC_EXPBUF: OK (Not Supported)
> > test Requests: OK (Not Supported)
> > test blocking wait: OK (Not Supported)
> >
> > Total for device /dev/v4l-subdev3: 46, Succeeded: 46, Failed: 0, Warnings: 0
> > --------
> >
> > v4l2-compliance skipped pad-based ioctl tests, because it was unable to
> > count the number of pad of the entity. The root cause if
> > mi_get_media_fd() returning -1 in testNode(), because
> > /sys/dev/char/81:9/device/ does not contain any entry whose name starts
> > with "media".
> >
> > I couldn't reproduce the issue with vimc running
> >
> > $ v4l2-compliance -z platform:vimc.0 -u "Sensor A"
> >
> > so I investigated further, and realized there's a major difference: the
> > "Sensor A" entity in vimc is created by the vimc driver that registers
> > the media device, while the sensor entity in the rkisp1 media graph is
> > created by a sensor driver.
> >
> > Finding the media device through sysfs isn't guaranteed to work. We can
> > keep doing so as a last resort option when no -z option is specified,
> > but we should use the media device found by open_media_bus_info() when
> > running with -z.
>
> Please check this patch, I hope this fixes the issue.
Unfortunately it doesn't. Please read below for comments.
> diff --git a/utils/common/media-info.cpp b/utils/common/media-info.cpp
> index 95e4c74f..b9140727 100644
> --- a/utils/common/media-info.cpp
> +++ b/utils/common/media-info.cpp
> @@ -216,7 +216,7 @@ std::string mi_get_devpath_from_dev_t(dev_t dev)
> return devpath;
> }
>
> -int mi_get_media_fd(int fd, const char *bus_info)
> +int mi_get_media_fd(int fd)
> {
> int media_fd = -1;
> dev_t dev;
> @@ -243,6 +243,34 @@ int mi_get_media_fd(int fd, const char *bus_info)
> devname += ep->d_name;
> media_fd = open(devname.c_str(), O_RDWR);
>
> + if (ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, &mdinfo)) {
> + close(media_fd);
> + continue;
> + }
> + break;
> + }
> + }
> + closedir(dp);
> + return media_fd;
> +}
> +
> +int mi_get_media_bus_info(const char *bus_info)
> +{
Small improvement, you can return -1 here if bus_info is NULL.
> + int media_fd = -1;
> +
> + DIR *dp;
> + struct dirent *ep;
> + dp = opendir("/dev");
> + if (dp == nullptr)
> + return -1;
> + while ((ep = readdir(dp))) {
> + if (!memcmp(ep->d_name, "media", 5) && isdigit(ep->d_name[5])) {
> + struct media_device_info mdinfo;
> + std::string devname("/dev/");
> +
> + devname += ep->d_name;
> + media_fd = open(devname.c_str(), O_RDWR);
> +
> if (bus_info &&
And drop the bus_info check here.
> (ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, &mdinfo) ||
> strcmp(mdinfo.bus_info, bus_info))) {
> diff --git a/utils/common/media-info.h b/utils/common/media-info.h
> index c0dd6304..75eb29b4 100644
> --- a/utils/common/media-info.h
> +++ b/utils/common/media-info.h
> @@ -51,12 +51,15 @@ std::string mi_get_devpath_from_dev_t(dev_t dev);
>
> /*
> * For a given device fd return the corresponding media device
> - * or -1 if there is none.
> - *
> - * If bus_info is not NULL, then find the media device that
> - * matches the given bus_info.
> + * or -1 if there is none. This only works if the media device
> + * is discoverable from where fd sits in /sys.
> */
> -int mi_get_media_fd(int fd, const char *bus_info = NULL);
> +int mi_get_media_fd(int fd);
> +
> +/*
> + * Find the media device that matches the given bus_info.
> + */
> +int mi_get_media_bus_info(const char *bus_info);
>
> /* Return entity flags description */
> std::string mi_entflags2s(__u32 flags);
> diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp
> index 4e5c9d00..0108c8d4 100644
> --- a/utils/v4l2-compliance/v4l2-compliance.cpp
> +++ b/utils/v4l2-compliance/v4l2-compliance.cpp
> @@ -1007,7 +1007,7 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
> if (parent_media_fd >= 0)
> media_fd = parent_media_fd;
> else
> - media_fd = mi_get_media_fd(node.g_fd(), node.bus_info);
> + media_fd = mi_get_media_bus_info(node.bus_info);
This doesn't work, because node.bus_info is never set.
The node class has a bus_info field and a media_bus_info field. As
they're not documented, I'm not sure which one is supposed to be set
here. node.media_bus_info is set later in this function, retrieved from
the media_fd.
I'd fix this myself (I don't think it's a very difficult job), but all
this is lots of spaghetti code and I don't know how it's supposed to
look like :-/
> }
>
> int fd = node.is_media() ? node.g_fd() : media_fd;
> diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp
> index 7e08668b..233cf03f 100644
> --- a/utils/v4l2-compliance/v4l2-test-buffers.cpp
> +++ b/utils/v4l2-compliance/v4l2-test-buffers.cpp
> @@ -2296,7 +2296,7 @@ int testDmaBuf(struct node *expbuf_node, struct node *node, struct node *node_m2
> int testRequests(struct node *node, bool test_streaming)
> {
> filehandles fhs;
> - int media_fd = fhs.add(mi_get_media_fd(node->g_fd(), node->bus_info));
> + int media_fd = fhs.add(mi_get_media_bus_info(node->bus_info));
> int req_fd;
> struct test_query_ext_ctrl valid_qctrl;
> v4l2_ext_controls ctrls;
> @@ -2432,7 +2432,7 @@ int testRequests(struct node *node, bool test_streaming)
> fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_REINIT, nullptr) != EBADF);
>
> // Open media_fd and alloc a request again
> - media_fd = fhs.add(mi_get_media_fd(node->g_fd(), node->bus_info));
> + media_fd = fhs.add(mi_get_media_bus_info(node->bus_info));
> fail_on_test(doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &req_fd));
> fhs.add(req_fd);
> ctrls.count = 1;
> @@ -2507,7 +2507,7 @@ int testRequests(struct node *node, bool test_streaming)
> unsigned num_requests = 2 * num_bufs;
> last_seq.init();
>
> - media_fd = fhs.add(mi_get_media_fd(node->g_fd(), node->bus_info));
> + media_fd = fhs.add(mi_get_media_bus_info(node->bus_info));
>
> // Allocate the requests
> for (unsigned i = 0; i < num_requests; i++) {
> diff --git a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
> index 517fc7f2..fe5d0dfe 100644
> --- a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
> +++ b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
> @@ -1346,10 +1346,10 @@ static int do_setup_out_buffers(cv4l_fd &fd, cv4l_queue &q, FILE *fin, bool qbuf
> if (fmt.g_pixelformat() == V4L2_PIX_FMT_FWHT_STATELESS) {
> struct v4l2_capability vcap = {};
> fd.querycap(vcap);
> - int media_fd = mi_get_media_fd(fd.g_fd(), (const char *)vcap.bus_info);
> + int media_fd = mi_get_media_bus_info((const char *)vcap.bus_info);
>
> if (media_fd < 0) {
> - fprintf(stderr, "%s: mi_get_media_fd failed\n", __func__);
> + fprintf(stderr, "%s: mi_get_media_bus_info failed\n", __func__);
> return media_fd;
> }
>
> diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp
> index 842e22fd..14f5c51f 100644
> --- a/utils/v4l2-ctl/v4l2-ctl.cpp
> +++ b/utils/v4l2-ctl/v4l2-ctl.cpp
> @@ -1382,7 +1382,10 @@ int main(int argc, char **argv)
> capabilities = vcap.device_caps;
> }
>
> - media_fd = mi_get_media_fd(fd, is_subdev ? 0 : (const char *)vcap.bus_info);
> + if (is_subdev)
> + media_fd = mi_get_media_fd(fd);
> + else
> + media_fd = mi_get_media_bus_info((const char *)vcap.bus_info);
>
> priv_magic = (capabilities & V4L2_CAP_EXT_PIX_FORMAT) ?
> V4L2_PIX_FMT_PRIV_MAGIC : 0;
>
--
Regards,
Laurent Pinchart
next prev parent reply other threads:[~2026-06-12 15:16 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-10 11:33 v4l2-compliance does not pick media device with -z and -u options Laurent Pinchart
2026-06-12 11:37 ` Hans Verkuil
2026-06-12 15:16 ` Laurent Pinchart [this message]
2026-06-18 7:03 ` Hans Verkuil
2026-06-29 13:15 ` Laurent Pinchart
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260612151610.GA2074789@killaraus.ideasonboard.com \
--to=laurent.pinchart@ideasonboard.com \
--cc=hverkuil+cisco@kernel.org \
--cc=linux-media@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.