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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox