Skip to content

Commit f57fa29

Browse files
tombamchehab
authored andcommitted
media: v4l2-subdev: Add new ioctl for client capabilities
Add new ioctls to set and get subdev client capabilities. Client in this context means the userspace application which opens the subdev device node. The client capabilities are stored in the file handle of the opened subdev device node, and the client must set the capabilities for each opened subdev. For now we only add a single flag, V4L2_SUBDEV_CLIENT_CAP_STREAMS, which indicates that the client is streams-aware. The reason for needing such a flag is as follows: Many structs passed via ioctls, e.g. struct v4l2_subdev_format, contain reserved fields (usually a single array field). These reserved fields can be used to extend the ioctl. The userspace is required to zero the reserved fields. We recently added a new 'stream' field to many of these structs, and the space for the field was taken from these reserved arrays. The assumption was that these new 'stream' fields are always initialized to zero if the userspace does not use them. This was a mistake, as, as mentioned above, the userspace is required to zero the _reserved_ fields. In other words, there is no requirement to zero this new stream field, and if the userspace doesn't use the field (which is the case for all userspace applications at the moment), the field may contain random data. This shows that the way the reserved fields are defined in v4l2 is, in my opinion, somewhat broken, but there is nothing to do about that. To fix this issue we need a way for the userspace to tell the kernel that the userspace has indeed set the 'stream' field, and it's fine for the kernel to access it. This is achieved with the new ioctl, which the userspace should usually use right after opening the subdev device node. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
1 parent 5fe4230 commit f57fa29

File tree

5 files changed

+169
-0
lines changed

5 files changed

+169
-0
lines changed

Documentation/userspace-api/media/v4l/user-func.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ Function Reference
7272
vidioc-subdev-g-frame-interval
7373
vidioc-subdev-g-routing
7474
vidioc-subdev-g-selection
75+
vidioc-subdev-g-client-cap
7576
vidioc-subdev-querycap
7677
vidioc-subscribe-event
7778
func-mmap
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
2+
.. c:namespace:: V4L
3+
4+
.. _VIDIOC_SUBDEV_G_CLIENT_CAP:
5+
6+
************************************************************
7+
ioctl VIDIOC_SUBDEV_G_CLIENT_CAP, VIDIOC_SUBDEV_S_CLIENT_CAP
8+
************************************************************
9+
10+
Name
11+
====
12+
13+
VIDIOC_SUBDEV_G_CLIENT_CAP - VIDIOC_SUBDEV_S_CLIENT_CAP - Get or set client
14+
capabilities.
15+
16+
Synopsis
17+
========
18+
19+
.. c:macro:: VIDIOC_SUBDEV_G_CLIENT_CAP
20+
21+
``int ioctl(int fd, VIDIOC_SUBDEV_G_CLIENT_CAP, struct v4l2_subdev_client_capability *argp)``
22+
23+
.. c:macro:: VIDIOC_SUBDEV_S_CLIENT_CAP
24+
25+
``int ioctl(int fd, VIDIOC_SUBDEV_S_CLIENT_CAP, struct v4l2_subdev_client_capability *argp)``
26+
27+
Arguments
28+
=========
29+
30+
``fd``
31+
File descriptor returned by :ref:`open() <func-open>`.
32+
33+
``argp``
34+
Pointer to struct :c:type:`v4l2_subdev_client_capability`.
35+
36+
Description
37+
===========
38+
39+
These ioctls are used to get and set the client (the application using the
40+
subdevice ioctls) capabilities. The client capabilities are stored in the file
41+
handle of the opened subdev device node, and the client must set the
42+
capabilities for each opened subdev separately.
43+
44+
By default no client capabilities are set when a subdev device node is opened.
45+
46+
The purpose of the client capabilities are to inform the kernel of the behavior
47+
of the client, mainly related to maintaining compatibility with different
48+
kernel and userspace versions.
49+
50+
The ``VIDIOC_SUBDEV_G_CLIENT_CAP`` ioctl returns the current client capabilities
51+
associated with the file handle ``fd``.
52+
53+
The ``VIDIOC_SUBDEV_S_CLIENT_CAP`` ioctl sets client capabilities for the file
54+
handle ``fd``. The new capabilities fully replace the current capabilities, the
55+
ioctl can therefore also be used to remove capabilities that have previously
56+
been set.
57+
58+
``VIDIOC_SUBDEV_S_CLIENT_CAP`` modifies the struct
59+
:c:type:`v4l2_subdev_client_capability` to reflect the capabilities that have
60+
been accepted. A common case for the kernel not accepting a capability is that
61+
the kernel is older than the headers the userspace uses, and thus the capability
62+
is unknown to the kernel.
63+
64+
.. flat-table:: Client Capabilities
65+
:header-rows: 1
66+
67+
* - Capability
68+
- Description
69+
* - ``V4L2_SUBDEV_CLIENT_CAP_STREAMS``
70+
- The client is aware of streams. Setting this flag enables the use
71+
of 'stream' fields (referring to the stream number) with various
72+
ioctls. If this is not set (which is the default), the 'stream' fields
73+
will be forced to 0 by the kernel.
74+
75+
Return Value
76+
============
77+
78+
On success 0 is returned, on error -1 and the ``errno`` variable is set
79+
appropriately. The generic error codes are described at the
80+
:ref:`Generic Error Codes <gen-errors>` chapter.
81+
82+
ENOIOCTLCMD
83+
The kernel does not support this ioctl.

drivers/media/v4l2-core/v4l2-subdev.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,11 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
510510
struct video_device *vdev = video_devdata(file);
511511
struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
512512
struct v4l2_fh *vfh = file->private_data;
513+
struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
513514
bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
514515
bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS;
516+
bool client_supports_streams = subdev_fh->client_caps &
517+
V4L2_SUBDEV_CLIENT_CAP_STREAMS;
515518
int rval;
516519

517520
switch (cmd) {
@@ -636,6 +639,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
636639
case VIDIOC_SUBDEV_G_FMT: {
637640
struct v4l2_subdev_format *format = arg;
638641

642+
if (!client_supports_streams)
643+
format->stream = 0;
644+
639645
memset(format->reserved, 0, sizeof(format->reserved));
640646
memset(format->format.reserved, 0, sizeof(format->format.reserved));
641647
return v4l2_subdev_call(sd, pad, get_fmt, state, format);
@@ -647,6 +653,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
647653
if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
648654
return -EPERM;
649655

656+
if (!client_supports_streams)
657+
format->stream = 0;
658+
650659
memset(format->reserved, 0, sizeof(format->reserved));
651660
memset(format->format.reserved, 0, sizeof(format->format.reserved));
652661
return v4l2_subdev_call(sd, pad, set_fmt, state, format);
@@ -656,6 +665,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
656665
struct v4l2_subdev_crop *crop = arg;
657666
struct v4l2_subdev_selection sel;
658667

668+
if (!client_supports_streams)
669+
crop->stream = 0;
670+
659671
memset(crop->reserved, 0, sizeof(crop->reserved));
660672
memset(&sel, 0, sizeof(sel));
661673
sel.which = crop->which;
@@ -677,6 +689,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
677689
if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
678690
return -EPERM;
679691

692+
if (!client_supports_streams)
693+
crop->stream = 0;
694+
680695
memset(crop->reserved, 0, sizeof(crop->reserved));
681696
memset(&sel, 0, sizeof(sel));
682697
sel.which = crop->which;
@@ -695,6 +710,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
695710
case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
696711
struct v4l2_subdev_mbus_code_enum *code = arg;
697712

713+
if (!client_supports_streams)
714+
code->stream = 0;
715+
698716
memset(code->reserved, 0, sizeof(code->reserved));
699717
return v4l2_subdev_call(sd, pad, enum_mbus_code, state,
700718
code);
@@ -703,6 +721,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
703721
case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
704722
struct v4l2_subdev_frame_size_enum *fse = arg;
705723

724+
if (!client_supports_streams)
725+
fse->stream = 0;
726+
706727
memset(fse->reserved, 0, sizeof(fse->reserved));
707728
return v4l2_subdev_call(sd, pad, enum_frame_size, state,
708729
fse);
@@ -711,6 +732,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
711732
case VIDIOC_SUBDEV_G_FRAME_INTERVAL: {
712733
struct v4l2_subdev_frame_interval *fi = arg;
713734

735+
if (!client_supports_streams)
736+
fi->stream = 0;
737+
714738
memset(fi->reserved, 0, sizeof(fi->reserved));
715739
return v4l2_subdev_call(sd, video, g_frame_interval, arg);
716740
}
@@ -721,13 +745,19 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
721745
if (ro_subdev)
722746
return -EPERM;
723747

748+
if (!client_supports_streams)
749+
fi->stream = 0;
750+
724751
memset(fi->reserved, 0, sizeof(fi->reserved));
725752
return v4l2_subdev_call(sd, video, s_frame_interval, arg);
726753
}
727754

728755
case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
729756
struct v4l2_subdev_frame_interval_enum *fie = arg;
730757

758+
if (!client_supports_streams)
759+
fie->stream = 0;
760+
731761
memset(fie->reserved, 0, sizeof(fie->reserved));
732762
return v4l2_subdev_call(sd, pad, enum_frame_interval, state,
733763
fie);
@@ -736,6 +766,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
736766
case VIDIOC_SUBDEV_G_SELECTION: {
737767
struct v4l2_subdev_selection *sel = arg;
738768

769+
if (!client_supports_streams)
770+
sel->stream = 0;
771+
739772
memset(sel->reserved, 0, sizeof(sel->reserved));
740773
return v4l2_subdev_call(
741774
sd, pad, get_selection, state, sel);
@@ -747,6 +780,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
747780
if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
748781
return -EPERM;
749782

783+
if (!client_supports_streams)
784+
sel->stream = 0;
785+
750786
memset(sel->reserved, 0, sizeof(sel->reserved));
751787
return v4l2_subdev_call(
752788
sd, pad, set_selection, state, sel);
@@ -888,6 +924,33 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
888924
routing->which, &krouting);
889925
}
890926

927+
case VIDIOC_SUBDEV_G_CLIENT_CAP: {
928+
struct v4l2_subdev_client_capability *client_cap = arg;
929+
930+
client_cap->capabilities = subdev_fh->client_caps;
931+
932+
return 0;
933+
}
934+
935+
case VIDIOC_SUBDEV_S_CLIENT_CAP: {
936+
struct v4l2_subdev_client_capability *client_cap = arg;
937+
938+
/*
939+
* Clear V4L2_SUBDEV_CLIENT_CAP_STREAMS if streams API is not
940+
* enabled. Remove this when streams API is no longer
941+
* experimental.
942+
*/
943+
if (!v4l2_subdev_enable_streams_api)
944+
client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS;
945+
946+
/* Filter out unsupported capabilities */
947+
client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS;
948+
949+
subdev_fh->client_caps = client_cap->capabilities;
950+
951+
return 0;
952+
}
953+
891954
default:
892955
return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
893956
}

include/media/v4l2-subdev.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,7 @@ struct v4l2_subdev_fh {
11251125
struct module *owner;
11261126
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
11271127
struct v4l2_subdev_state *state;
1128+
u64 client_caps;
11281129
#endif
11291130
};
11301131

include/uapi/linux/v4l2-subdev.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,24 @@ struct v4l2_subdev_routing {
233233
__u32 reserved[6];
234234
};
235235

236+
/*
237+
* The client is aware of streams. Setting this flag enables the use of 'stream'
238+
* fields (referring to the stream number) with various ioctls. If this is not
239+
* set (which is the default), the 'stream' fields will be forced to 0 by the
240+
* kernel.
241+
*/
242+
#define V4L2_SUBDEV_CLIENT_CAP_STREAMS (1U << 0)
243+
244+
/**
245+
* struct v4l2_subdev_client_capability - Capabilities of the client accessing
246+
* the subdev
247+
*
248+
* @capabilities: A bitmask of V4L2_SUBDEV_CLIENT_CAP_* flags.
249+
*/
250+
struct v4l2_subdev_client_capability {
251+
__u64 capabilities;
252+
};
253+
236254
/* Backwards compatibility define --- to be removed */
237255
#define v4l2_subdev_edid v4l2_edid
238256

@@ -250,6 +268,9 @@ struct v4l2_subdev_routing {
250268
#define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection)
251269
#define VIDIOC_SUBDEV_G_ROUTING _IOWR('V', 38, struct v4l2_subdev_routing)
252270
#define VIDIOC_SUBDEV_S_ROUTING _IOWR('V', 39, struct v4l2_subdev_routing)
271+
#define VIDIOC_SUBDEV_G_CLIENT_CAP _IOR('V', 101, struct v4l2_subdev_client_capability)
272+
#define VIDIOC_SUBDEV_S_CLIENT_CAP _IOWR('V', 102, struct v4l2_subdev_client_capability)
273+
253274
/* The following ioctls are identical to the ioctls in videodev2.h */
254275
#define VIDIOC_SUBDEV_G_STD _IOR('V', 23, v4l2_std_id)
255276
#define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id)

0 commit comments

Comments
 (0)