Linux Media Controller development
 help / color / mirror / Atom feed
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

  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