Commit f57fa295 authored by Tomi Valkeinen's avatar Tomi Valkeinen Committed by Mauro Carvalho Chehab

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: default avatarTomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Reviewed-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent 5fe4230d
...@@ -72,6 +72,7 @@ Function Reference ...@@ -72,6 +72,7 @@ Function Reference
vidioc-subdev-g-frame-interval vidioc-subdev-g-frame-interval
vidioc-subdev-g-routing vidioc-subdev-g-routing
vidioc-subdev-g-selection vidioc-subdev-g-selection
vidioc-subdev-g-client-cap
vidioc-subdev-querycap vidioc-subdev-querycap
vidioc-subscribe-event vidioc-subscribe-event
func-mmap func-mmap
......
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
.. c:namespace:: V4L
.. _VIDIOC_SUBDEV_G_CLIENT_CAP:
************************************************************
ioctl VIDIOC_SUBDEV_G_CLIENT_CAP, VIDIOC_SUBDEV_S_CLIENT_CAP
************************************************************
Name
====
VIDIOC_SUBDEV_G_CLIENT_CAP - VIDIOC_SUBDEV_S_CLIENT_CAP - Get or set client
capabilities.
Synopsis
========
.. c:macro:: VIDIOC_SUBDEV_G_CLIENT_CAP
``int ioctl(int fd, VIDIOC_SUBDEV_G_CLIENT_CAP, struct v4l2_subdev_client_capability *argp)``
.. c:macro:: VIDIOC_SUBDEV_S_CLIENT_CAP
``int ioctl(int fd, VIDIOC_SUBDEV_S_CLIENT_CAP, struct v4l2_subdev_client_capability *argp)``
Arguments
=========
``fd``
File descriptor returned by :ref:`open() <func-open>`.
``argp``
Pointer to struct :c:type:`v4l2_subdev_client_capability`.
Description
===========
These ioctls are used to get and set the client (the application using the
subdevice ioctls) capabilities. 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 separately.
By default no client capabilities are set when a subdev device node is opened.
The purpose of the client capabilities are to inform the kernel of the behavior
of the client, mainly related to maintaining compatibility with different
kernel and userspace versions.
The ``VIDIOC_SUBDEV_G_CLIENT_CAP`` ioctl returns the current client capabilities
associated with the file handle ``fd``.
The ``VIDIOC_SUBDEV_S_CLIENT_CAP`` ioctl sets client capabilities for the file
handle ``fd``. The new capabilities fully replace the current capabilities, the
ioctl can therefore also be used to remove capabilities that have previously
been set.
``VIDIOC_SUBDEV_S_CLIENT_CAP`` modifies the struct
:c:type:`v4l2_subdev_client_capability` to reflect the capabilities that have
been accepted. A common case for the kernel not accepting a capability is that
the kernel is older than the headers the userspace uses, and thus the capability
is unknown to the kernel.
.. flat-table:: Client Capabilities
:header-rows: 1
* - Capability
- Description
* - ``V4L2_SUBDEV_CLIENT_CAP_STREAMS``
- The client is aware of streams. Setting this flag enables the use
of 'stream' fields (referring to the stream number) with various
ioctls. If this is not set (which is the default), the 'stream' fields
will be forced to 0 by the kernel.
Return Value
============
On success 0 is returned, on error -1 and the ``errno`` variable is set
appropriately. The generic error codes are described at the
:ref:`Generic Error Codes <gen-errors>` chapter.
ENOIOCTLCMD
The kernel does not support this ioctl.
...@@ -510,8 +510,11 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -510,8 +510,11 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
struct video_device *vdev = video_devdata(file); struct video_device *vdev = video_devdata(file);
struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
struct v4l2_fh *vfh = file->private_data; struct v4l2_fh *vfh = file->private_data;
struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS; bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS;
bool client_supports_streams = subdev_fh->client_caps &
V4L2_SUBDEV_CLIENT_CAP_STREAMS;
int rval; int rval;
switch (cmd) { switch (cmd) {
...@@ -636,6 +639,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -636,6 +639,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_G_FMT: { case VIDIOC_SUBDEV_G_FMT: {
struct v4l2_subdev_format *format = arg; struct v4l2_subdev_format *format = arg;
if (!client_supports_streams)
format->stream = 0;
memset(format->reserved, 0, sizeof(format->reserved)); memset(format->reserved, 0, sizeof(format->reserved));
memset(format->format.reserved, 0, sizeof(format->format.reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved));
return v4l2_subdev_call(sd, pad, get_fmt, state, format); 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, ...@@ -647,6 +653,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM; return -EPERM;
if (!client_supports_streams)
format->stream = 0;
memset(format->reserved, 0, sizeof(format->reserved)); memset(format->reserved, 0, sizeof(format->reserved));
memset(format->format.reserved, 0, sizeof(format->format.reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved));
return v4l2_subdev_call(sd, pad, set_fmt, state, format); 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, ...@@ -656,6 +665,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_crop *crop = arg;
struct v4l2_subdev_selection sel; struct v4l2_subdev_selection sel;
if (!client_supports_streams)
crop->stream = 0;
memset(crop->reserved, 0, sizeof(crop->reserved)); memset(crop->reserved, 0, sizeof(crop->reserved));
memset(&sel, 0, sizeof(sel)); memset(&sel, 0, sizeof(sel));
sel.which = crop->which; sel.which = crop->which;
...@@ -677,6 +689,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -677,6 +689,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM; return -EPERM;
if (!client_supports_streams)
crop->stream = 0;
memset(crop->reserved, 0, sizeof(crop->reserved)); memset(crop->reserved, 0, sizeof(crop->reserved));
memset(&sel, 0, sizeof(sel)); memset(&sel, 0, sizeof(sel));
sel.which = crop->which; sel.which = crop->which;
...@@ -695,6 +710,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -695,6 +710,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
struct v4l2_subdev_mbus_code_enum *code = arg; struct v4l2_subdev_mbus_code_enum *code = arg;
if (!client_supports_streams)
code->stream = 0;
memset(code->reserved, 0, sizeof(code->reserved)); memset(code->reserved, 0, sizeof(code->reserved));
return v4l2_subdev_call(sd, pad, enum_mbus_code, state, return v4l2_subdev_call(sd, pad, enum_mbus_code, state,
code); code);
...@@ -703,6 +721,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -703,6 +721,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: { case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
struct v4l2_subdev_frame_size_enum *fse = arg; struct v4l2_subdev_frame_size_enum *fse = arg;
if (!client_supports_streams)
fse->stream = 0;
memset(fse->reserved, 0, sizeof(fse->reserved)); memset(fse->reserved, 0, sizeof(fse->reserved));
return v4l2_subdev_call(sd, pad, enum_frame_size, state, return v4l2_subdev_call(sd, pad, enum_frame_size, state,
fse); fse);
...@@ -711,6 +732,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -711,6 +732,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_G_FRAME_INTERVAL: { case VIDIOC_SUBDEV_G_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval *fi = arg; struct v4l2_subdev_frame_interval *fi = arg;
if (!client_supports_streams)
fi->stream = 0;
memset(fi->reserved, 0, sizeof(fi->reserved)); memset(fi->reserved, 0, sizeof(fi->reserved));
return v4l2_subdev_call(sd, video, g_frame_interval, arg); return v4l2_subdev_call(sd, video, g_frame_interval, arg);
} }
...@@ -721,6 +745,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -721,6 +745,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (ro_subdev) if (ro_subdev)
return -EPERM; return -EPERM;
if (!client_supports_streams)
fi->stream = 0;
memset(fi->reserved, 0, sizeof(fi->reserved)); memset(fi->reserved, 0, sizeof(fi->reserved));
return v4l2_subdev_call(sd, video, s_frame_interval, arg); return v4l2_subdev_call(sd, video, s_frame_interval, arg);
} }
...@@ -728,6 +755,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -728,6 +755,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval_enum *fie = arg; struct v4l2_subdev_frame_interval_enum *fie = arg;
if (!client_supports_streams)
fie->stream = 0;
memset(fie->reserved, 0, sizeof(fie->reserved)); memset(fie->reserved, 0, sizeof(fie->reserved));
return v4l2_subdev_call(sd, pad, enum_frame_interval, state, return v4l2_subdev_call(sd, pad, enum_frame_interval, state,
fie); fie);
...@@ -736,6 +766,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -736,6 +766,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_G_SELECTION: { case VIDIOC_SUBDEV_G_SELECTION: {
struct v4l2_subdev_selection *sel = arg; struct v4l2_subdev_selection *sel = arg;
if (!client_supports_streams)
sel->stream = 0;
memset(sel->reserved, 0, sizeof(sel->reserved)); memset(sel->reserved, 0, sizeof(sel->reserved));
return v4l2_subdev_call( return v4l2_subdev_call(
sd, pad, get_selection, state, sel); sd, pad, get_selection, state, sel);
...@@ -747,6 +780,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -747,6 +780,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM; return -EPERM;
if (!client_supports_streams)
sel->stream = 0;
memset(sel->reserved, 0, sizeof(sel->reserved)); memset(sel->reserved, 0, sizeof(sel->reserved));
return v4l2_subdev_call( return v4l2_subdev_call(
sd, pad, set_selection, state, sel); sd, pad, set_selection, state, sel);
...@@ -888,6 +924,33 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -888,6 +924,33 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
routing->which, &krouting); routing->which, &krouting);
} }
case VIDIOC_SUBDEV_G_CLIENT_CAP: {
struct v4l2_subdev_client_capability *client_cap = arg;
client_cap->capabilities = subdev_fh->client_caps;
return 0;
}
case VIDIOC_SUBDEV_S_CLIENT_CAP: {
struct v4l2_subdev_client_capability *client_cap = arg;
/*
* Clear V4L2_SUBDEV_CLIENT_CAP_STREAMS if streams API is not
* enabled. Remove this when streams API is no longer
* experimental.
*/
if (!v4l2_subdev_enable_streams_api)
client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS;
/* Filter out unsupported capabilities */
client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS;
subdev_fh->client_caps = client_cap->capabilities;
return 0;
}
default: default:
return v4l2_subdev_call(sd, core, ioctl, cmd, arg); return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
} }
......
...@@ -1125,6 +1125,7 @@ struct v4l2_subdev_fh { ...@@ -1125,6 +1125,7 @@ struct v4l2_subdev_fh {
struct module *owner; struct module *owner;
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
struct v4l2_subdev_state *state; struct v4l2_subdev_state *state;
u64 client_caps;
#endif #endif
}; };
......
...@@ -233,6 +233,24 @@ struct v4l2_subdev_routing { ...@@ -233,6 +233,24 @@ struct v4l2_subdev_routing {
__u32 reserved[6]; __u32 reserved[6];
}; };
/*
* The client is aware of streams. Setting this flag enables the use of 'stream'
* fields (referring to the stream number) with various ioctls. If this is not
* set (which is the default), the 'stream' fields will be forced to 0 by the
* kernel.
*/
#define V4L2_SUBDEV_CLIENT_CAP_STREAMS (1U << 0)
/**
* struct v4l2_subdev_client_capability - Capabilities of the client accessing
* the subdev
*
* @capabilities: A bitmask of V4L2_SUBDEV_CLIENT_CAP_* flags.
*/
struct v4l2_subdev_client_capability {
__u64 capabilities;
};
/* Backwards compatibility define --- to be removed */ /* Backwards compatibility define --- to be removed */
#define v4l2_subdev_edid v4l2_edid #define v4l2_subdev_edid v4l2_edid
...@@ -250,6 +268,9 @@ struct v4l2_subdev_routing { ...@@ -250,6 +268,9 @@ struct v4l2_subdev_routing {
#define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection) #define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection)
#define VIDIOC_SUBDEV_G_ROUTING _IOWR('V', 38, struct v4l2_subdev_routing) #define VIDIOC_SUBDEV_G_ROUTING _IOWR('V', 38, struct v4l2_subdev_routing)
#define VIDIOC_SUBDEV_S_ROUTING _IOWR('V', 39, struct v4l2_subdev_routing) #define VIDIOC_SUBDEV_S_ROUTING _IOWR('V', 39, struct v4l2_subdev_routing)
#define VIDIOC_SUBDEV_G_CLIENT_CAP _IOR('V', 101, struct v4l2_subdev_client_capability)
#define VIDIOC_SUBDEV_S_CLIENT_CAP _IOWR('V', 102, struct v4l2_subdev_client_capability)
/* The following ioctls are identical to the ioctls in videodev2.h */ /* The following ioctls are identical to the ioctls in videodev2.h */
#define VIDIOC_SUBDEV_G_STD _IOR('V', 23, v4l2_std_id) #define VIDIOC_SUBDEV_G_STD _IOR('V', 23, v4l2_std_id)
#define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id) #define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment