Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tomi Valkeinen and committed by
Mauro Carvalho Chehab
f57fa295 5fe4230d

+169
+1
Documentation/userspace-api/media/v4l/user-func.rst
··· 72 72 vidioc-subdev-g-frame-interval 73 73 vidioc-subdev-g-routing 74 74 vidioc-subdev-g-selection 75 + vidioc-subdev-g-client-cap 75 76 vidioc-subdev-querycap 76 77 vidioc-subscribe-event 77 78 func-mmap
+83
Documentation/userspace-api/media/v4l/vidioc-subdev-g-client-cap.rst
··· 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.
+63
drivers/media/v4l2-core/v4l2-subdev.c
··· 510 510 struct video_device *vdev = video_devdata(file); 511 511 struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); 512 512 struct v4l2_fh *vfh = file->private_data; 513 + struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); 513 514 bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); 514 515 bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS; 516 + bool client_supports_streams = subdev_fh->client_caps & 517 + V4L2_SUBDEV_CLIENT_CAP_STREAMS; 515 518 int rval; 516 519 517 520 switch (cmd) { ··· 639 636 case VIDIOC_SUBDEV_G_FMT: { 640 637 struct v4l2_subdev_format *format = arg; 641 638 639 + if (!client_supports_streams) 640 + format->stream = 0; 641 + 642 642 memset(format->reserved, 0, sizeof(format->reserved)); 643 643 memset(format->format.reserved, 0, sizeof(format->format.reserved)); 644 644 return v4l2_subdev_call(sd, pad, get_fmt, state, format); ··· 653 647 if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) 654 648 return -EPERM; 655 649 650 + if (!client_supports_streams) 651 + format->stream = 0; 652 + 656 653 memset(format->reserved, 0, sizeof(format->reserved)); 657 654 memset(format->format.reserved, 0, sizeof(format->format.reserved)); 658 655 return v4l2_subdev_call(sd, pad, set_fmt, state, format); ··· 664 655 case VIDIOC_SUBDEV_G_CROP: { 665 656 struct v4l2_subdev_crop *crop = arg; 666 657 struct v4l2_subdev_selection sel; 658 + 659 + if (!client_supports_streams) 660 + crop->stream = 0; 667 661 668 662 memset(crop->reserved, 0, sizeof(crop->reserved)); 669 663 memset(&sel, 0, sizeof(sel)); ··· 689 677 if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) 690 678 return -EPERM; 691 679 680 + if (!client_supports_streams) 681 + crop->stream = 0; 682 + 692 683 memset(crop->reserved, 0, sizeof(crop->reserved)); 693 684 memset(&sel, 0, sizeof(sel)); 694 685 sel.which = crop->which; ··· 710 695 case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { 711 696 struct v4l2_subdev_mbus_code_enum *code = arg; 712 697 698 + if (!client_supports_streams) 699 + code->stream = 0; 700 + 713 701 memset(code->reserved, 0, sizeof(code->reserved)); 714 702 return v4l2_subdev_call(sd, pad, enum_mbus_code, state, 715 703 code); ··· 721 703 case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: { 722 704 struct v4l2_subdev_frame_size_enum *fse = arg; 723 705 706 + if (!client_supports_streams) 707 + fse->stream = 0; 708 + 724 709 memset(fse->reserved, 0, sizeof(fse->reserved)); 725 710 return v4l2_subdev_call(sd, pad, enum_frame_size, state, 726 711 fse); ··· 731 710 732 711 case VIDIOC_SUBDEV_G_FRAME_INTERVAL: { 733 712 struct v4l2_subdev_frame_interval *fi = arg; 713 + 714 + if (!client_supports_streams) 715 + fi->stream = 0; 734 716 735 717 memset(fi->reserved, 0, sizeof(fi->reserved)); 736 718 return v4l2_subdev_call(sd, video, g_frame_interval, arg); ··· 745 721 if (ro_subdev) 746 722 return -EPERM; 747 723 724 + if (!client_supports_streams) 725 + fi->stream = 0; 726 + 748 727 memset(fi->reserved, 0, sizeof(fi->reserved)); 749 728 return v4l2_subdev_call(sd, video, s_frame_interval, arg); 750 729 } 751 730 752 731 case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { 753 732 struct v4l2_subdev_frame_interval_enum *fie = arg; 733 + 734 + if (!client_supports_streams) 735 + fie->stream = 0; 754 736 755 737 memset(fie->reserved, 0, sizeof(fie->reserved)); 756 738 return v4l2_subdev_call(sd, pad, enum_frame_interval, state, ··· 765 735 766 736 case VIDIOC_SUBDEV_G_SELECTION: { 767 737 struct v4l2_subdev_selection *sel = arg; 738 + 739 + if (!client_supports_streams) 740 + sel->stream = 0; 768 741 769 742 memset(sel->reserved, 0, sizeof(sel->reserved)); 770 743 return v4l2_subdev_call( ··· 779 746 780 747 if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) 781 748 return -EPERM; 749 + 750 + if (!client_supports_streams) 751 + sel->stream = 0; 782 752 783 753 memset(sel->reserved, 0, sizeof(sel->reserved)); 784 754 return v4l2_subdev_call( ··· 922 886 923 887 return v4l2_subdev_call(sd, pad, set_routing, state, 924 888 routing->which, &krouting); 889 + } 890 + 891 + case VIDIOC_SUBDEV_G_CLIENT_CAP: { 892 + struct v4l2_subdev_client_capability *client_cap = arg; 893 + 894 + client_cap->capabilities = subdev_fh->client_caps; 895 + 896 + return 0; 897 + } 898 + 899 + case VIDIOC_SUBDEV_S_CLIENT_CAP: { 900 + struct v4l2_subdev_client_capability *client_cap = arg; 901 + 902 + /* 903 + * Clear V4L2_SUBDEV_CLIENT_CAP_STREAMS if streams API is not 904 + * enabled. Remove this when streams API is no longer 905 + * experimental. 906 + */ 907 + if (!v4l2_subdev_enable_streams_api) 908 + client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS; 909 + 910 + /* Filter out unsupported capabilities */ 911 + client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS; 912 + 913 + subdev_fh->client_caps = client_cap->capabilities; 914 + 915 + return 0; 925 916 } 926 917 927 918 default:
+1
include/media/v4l2-subdev.h
··· 1125 1125 struct module *owner; 1126 1126 #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) 1127 1127 struct v4l2_subdev_state *state; 1128 + u64 client_caps; 1128 1129 #endif 1129 1130 }; 1130 1131
+21
include/uapi/linux/v4l2-subdev.h
··· 233 233 __u32 reserved[6]; 234 234 }; 235 235 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 + 236 254 /* Backwards compatibility define --- to be removed */ 237 255 #define v4l2_subdev_edid v4l2_edid 238 256 ··· 268 250 #define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection) 269 251 #define VIDIOC_SUBDEV_G_ROUTING _IOWR('V', 38, struct v4l2_subdev_routing) 270 252 #define VIDIOC_SUBDEV_S_ROUTING _IOWR('V', 39, struct v4l2_subdev_routing) 253 + #define VIDIOC_SUBDEV_G_CLIENT_CAP _IOR('V', 101, struct v4l2_subdev_client_capability) 254 + #define VIDIOC_SUBDEV_S_CLIENT_CAP _IOWR('V', 102, struct v4l2_subdev_client_capability) 255 + 271 256 /* The following ioctls are identical to the ioctls in videodev2.h */ 272 257 #define VIDIOC_SUBDEV_G_STD _IOR('V', 23, v4l2_std_id) 273 258 #define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id)