| /* |
| V4L2 API subdev ioctl tests. |
| |
| Copyright (C) 2018 Hans Verkuil <hans.verkuil@cisco.com> |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
| */ |
| |
| #include <map> |
| #include <set> |
| |
| #include <sys/types.h> |
| |
| #include "v4l2-compliance.h" |
| |
| #define VALID_SUBDEV_CAPS (V4L2_SUBDEV_CAP_RO_SUBDEV) |
| |
| int testSubDevCap(struct node *node) |
| { |
| v4l2_subdev_capability caps; |
| |
| memset(&caps, 0xff, sizeof(caps)); |
| // Must always be there |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_QUERYCAP, &caps)); |
| if (has_mmu) |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_QUERYCAP, nullptr) != EFAULT); |
| fail_on_test(check_0(caps.reserved, sizeof(caps.reserved))); |
| fail_on_test((caps.version >> 16) < 5); |
| fail_on_test(caps.capabilities & ~VALID_SUBDEV_CAPS); |
| node->is_ro_subdev = caps.capabilities & V4L2_SUBDEV_CAP_RO_SUBDEV; |
| return 0; |
| } |
| |
| static int testSubDevEnumFrameInterval(struct node *node, unsigned which, |
| unsigned pad, unsigned code, |
| unsigned width, unsigned height) |
| { |
| struct v4l2_subdev_frame_interval_enum fie; |
| unsigned num_ivals; |
| int ret; |
| |
| memset(&fie, 0, sizeof(fie)); |
| fie.which = which; |
| fie.pad = pad; |
| fie.code = code; |
| fie.width = width; |
| fie.height = height; |
| ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie); |
| node->has_subdev_enum_fival |= (ret != ENOTTY) << which; |
| if (ret == ENOTTY) |
| return ret; |
| if (which) |
| fail_on_test(node->enum_frame_interval_pad != (int)pad); |
| else |
| fail_on_test(node->enum_frame_interval_pad >= 0); |
| node->enum_frame_interval_pad = pad; |
| fie.which = ~0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL); |
| fie.which = which; |
| fie.index = ~0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL); |
| fie.pad = node->entity.pads; |
| fie.index = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL); |
| fie.width = ~0; |
| fie.pad = pad; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL); |
| fie.height = ~0; |
| fie.width = width; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL); |
| memset(&fie, 0xff, sizeof(fie)); |
| fie.which = which; |
| fie.pad = pad; |
| fie.code = code; |
| fie.width = width; |
| fie.height = height; |
| fie.index = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie)); |
| fail_on_test(check_0(fie.reserved, sizeof(fie.reserved))); |
| fail_on_test(fie.which != which); |
| fail_on_test(fie.pad != pad); |
| fail_on_test(fie.code != code); |
| fail_on_test(fie.width != width); |
| fail_on_test(fie.height != height); |
| fail_on_test(fie.index); |
| fail_on_test(fie.interval.numerator == ~0U || fie.interval.denominator == ~0U); |
| do { |
| fie.index++; |
| ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie); |
| } while (!ret); |
| fail_on_test(ret != EINVAL); |
| |
| num_ivals = fie.index; |
| for (unsigned i = 0; i < num_ivals; i++) { |
| fie.index = i; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie)); |
| fail_on_test(fie.which != which); |
| fail_on_test(fie.pad != pad); |
| fail_on_test(fie.code != code); |
| fail_on_test(fie.width != width); |
| fail_on_test(fie.height != height); |
| fail_on_test(fie.index != i); |
| fail_on_test(!fie.interval.numerator); |
| fail_on_test(!fie.interval.denominator); |
| } |
| return 0; |
| } |
| |
| static int testSubDevEnumFrameSize(struct node *node, unsigned which, |
| unsigned pad, unsigned code) |
| { |
| struct v4l2_subdev_frame_size_enum fse; |
| unsigned num_sizes; |
| int ret; |
| |
| memset(&fse, 0, sizeof(fse)); |
| fse.which = which; |
| fse.pad = pad; |
| fse.code = code; |
| ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse); |
| node->has_subdev_enum_fsize |= (ret != ENOTTY) << which; |
| if (ret == ENOTTY) { |
| struct v4l2_subdev_frame_interval_enum fie; |
| |
| memset(&fie, 0, sizeof(fie)); |
| fie.which = which; |
| fie.pad = pad; |
| fie.code = code; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != ENOTTY); |
| return ret; |
| } |
| fse.which = ~0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != EINVAL); |
| fse.which = which; |
| fse.index = ~0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != EINVAL); |
| fse.pad = node->entity.pads; |
| fse.index = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != EINVAL); |
| memset(&fse, 0xff, sizeof(fse)); |
| fse.which = which; |
| fse.pad = pad; |
| fse.code = code; |
| fse.index = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse)); |
| fail_on_test(check_0(fse.reserved, sizeof(fse.reserved))); |
| fail_on_test(fse.which != which); |
| fail_on_test(fse.pad != pad); |
| fail_on_test(fse.code != code); |
| fail_on_test(fse.index); |
| fail_on_test(!fse.min_width || !fse.min_height); |
| fail_on_test(fse.min_width == ~0U || fse.min_height == ~0U); |
| fail_on_test(!fse.max_width || !fse.max_height); |
| fail_on_test(fse.max_width == ~0U || fse.max_height == ~0U); |
| fail_on_test(fse.min_width > fse.max_width); |
| fail_on_test(fse.min_height > fse.max_height); |
| do { |
| fse.index++; |
| ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse); |
| } while (!ret); |
| fail_on_test(ret != EINVAL); |
| |
| num_sizes = fse.index; |
| for (unsigned i = 0; i < num_sizes; i++) { |
| fse.index = i; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse)); |
| fail_on_test(fse.which != which); |
| fail_on_test(fse.pad != pad); |
| fail_on_test(fse.code != code); |
| fail_on_test(fse.index != i); |
| fail_on_test(!fse.min_width || !fse.min_height); |
| fail_on_test(!fse.max_width || !fse.max_height); |
| fail_on_test(fse.min_width > fse.max_width); |
| fail_on_test(fse.min_height > fse.max_height); |
| |
| ret = testSubDevEnumFrameInterval(node, which, pad, code, |
| fse.min_width, fse.min_height); |
| fail_on_test(ret && ret != ENOTTY); |
| ret = testSubDevEnumFrameInterval(node, which, pad, code, |
| fse.max_width, fse.max_height); |
| fail_on_test(ret && ret != ENOTTY); |
| } |
| return 0; |
| } |
| |
| int testSubDevEnum(struct node *node, unsigned which, unsigned pad) |
| { |
| struct v4l2_subdev_mbus_code_enum mbus_core_enum; |
| unsigned num_codes; |
| int ret; |
| |
| memset(&mbus_core_enum, 0, sizeof(mbus_core_enum)); |
| mbus_core_enum.which = which; |
| mbus_core_enum.pad = pad; |
| ret = doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum); |
| node->has_subdev_enum_code |= (ret != ENOTTY) << which; |
| if (ret == ENOTTY) { |
| struct v4l2_subdev_frame_size_enum fse; |
| struct v4l2_subdev_frame_interval_enum fie; |
| |
| memset(&fse, 0, sizeof(fse)); |
| memset(&fie, 0, sizeof(fie)); |
| fse.which = which; |
| fse.pad = pad; |
| fie.which = which; |
| fie.pad = pad; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != ENOTTY); |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != ENOTTY); |
| return ret; |
| } |
| mbus_core_enum.which = ~0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL); |
| mbus_core_enum.which = which; |
| mbus_core_enum.index = ~0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL); |
| mbus_core_enum.pad = node->entity.pads; |
| mbus_core_enum.index = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL); |
| memset(&mbus_core_enum, 0xff, sizeof(mbus_core_enum)); |
| mbus_core_enum.which = which; |
| mbus_core_enum.pad = pad; |
| mbus_core_enum.index = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum)); |
| fail_on_test(check_0(mbus_core_enum.reserved, sizeof(mbus_core_enum.reserved))); |
| fail_on_test(mbus_core_enum.code == ~0U); |
| fail_on_test(mbus_core_enum.pad != pad); |
| fail_on_test(mbus_core_enum.index); |
| fail_on_test(mbus_core_enum.which != which); |
| do { |
| mbus_core_enum.index++; |
| ret = doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum); |
| } while (!ret); |
| fail_on_test(ret != EINVAL); |
| |
| num_codes = mbus_core_enum.index; |
| for (unsigned i = 0; i < num_codes; i++) { |
| mbus_core_enum.index = i; |
| mbus_core_enum.code = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum)); |
| fail_on_test(!mbus_core_enum.code); |
| fail_on_test(mbus_core_enum.which != which); |
| fail_on_test(mbus_core_enum.pad != pad); |
| fail_on_test(mbus_core_enum.index != i); |
| |
| ret = testSubDevEnumFrameSize(node, which, pad, mbus_core_enum.code); |
| fail_on_test(ret && ret != ENOTTY); |
| } |
| return 0; |
| } |
| |
| int testSubDevFrameInterval(struct node *node, unsigned pad) |
| { |
| struct v4l2_subdev_frame_interval fival; |
| struct v4l2_fract ival; |
| int ret; |
| |
| memset(&fival, 0xff, sizeof(fival)); |
| fival.pad = pad; |
| ret = doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival); |
| if (ret == ENOTTY) { |
| fail_on_test(node->enum_frame_interval_pad >= 0); |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != ENOTTY); |
| return ret; |
| } |
| fail_on_test(node->frame_interval_pad >= 0); |
| fail_on_test(node->enum_frame_interval_pad != (int)pad); |
| node->frame_interval_pad = pad; |
| fail_on_test(check_0(fival.reserved, sizeof(fival.reserved))); |
| fail_on_test(fival.pad != pad); |
| fail_on_test(!fival.interval.numerator); |
| fail_on_test(!fival.interval.denominator); |
| fail_on_test(fival.interval.numerator == ~0U || fival.interval.denominator == ~0U); |
| ival = fival.interval; |
| memset(fival.reserved, 0xff, sizeof(fival.reserved)); |
| if (node->is_ro_subdev) { |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != EPERM); |
| return 0; |
| } |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival)); |
| fail_on_test(fival.pad != pad); |
| fail_on_test(ival.numerator != fival.interval.numerator); |
| fail_on_test(ival.denominator != fival.interval.denominator); |
| fail_on_test(check_0(fival.reserved, sizeof(fival.reserved))); |
| memset(&fival, 0, sizeof(fival)); |
| fival.pad = pad; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival)); |
| fail_on_test(fival.pad != pad); |
| fail_on_test(ival.numerator != fival.interval.numerator); |
| fail_on_test(ival.denominator != fival.interval.denominator); |
| |
| fival.pad = node->entity.pads; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) != EINVAL); |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != EINVAL); |
| fival.pad = pad; |
| fival.interval = ival; |
| fival.interval.numerator = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival)); |
| fail_on_test(!fival.interval.numerator); |
| fail_on_test(!fival.interval.denominator); |
| fival.interval = ival; |
| fival.interval.denominator = 0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival)); |
| fail_on_test(!fival.interval.numerator); |
| fail_on_test(!fival.interval.denominator); |
| fival.interval = ival; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival)); |
| return 0; |
| } |
| |
| static int checkMBusFrameFmt(struct node *node, struct v4l2_mbus_framefmt &fmt) |
| { |
| fail_on_test(check_0(fmt.reserved, sizeof(fmt.reserved))); |
| fail_on_test(fmt.width == 0 || fmt.width > 65536); |
| fail_on_test(fmt.height == 0 || fmt.height > 65536); |
| fail_on_test(fmt.code == 0 || fmt.code == ~0U); |
| fail_on_test(fmt.field == ~0U); |
| if (!node->is_passthrough_subdev) { |
| // Passthrough subdevs just copy this info, they don't validate |
| // it. TODO: this does not take colorspace converters into account! |
| fail_on_test(fmt.colorspace == ~0U); |
| //TBD fail_on_test(!fmt.colorspace); |
| fail_on_test(fmt.ycbcr_enc == 0xffff); |
| fail_on_test(fmt.quantization == 0xffff); |
| fail_on_test(fmt.xfer_func == 0xffff); |
| fail_on_test(!fmt.colorspace && |
| (fmt.ycbcr_enc || fmt.quantization || fmt.xfer_func)); |
| } |
| return 0; |
| } |
| |
| int testSubDevFormat(struct node *node, unsigned which, unsigned pad) |
| { |
| struct v4l2_subdev_format fmt; |
| struct v4l2_subdev_format s_fmt; |
| int ret; |
| |
| memset(&fmt, 0, sizeof(fmt)); |
| fmt.which = which; |
| fmt.pad = pad; |
| ret = doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt); |
| node->has_subdev_fmt |= (ret != ENOTTY) << which; |
| if (ret == ENOTTY) { |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY); |
| return ret; |
| } |
| fmt.which = ~0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt) != EINVAL); |
| fmt.which = 0; |
| fmt.pad = node->entity.pads; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt) != EINVAL); |
| memset(&fmt, 0xff, sizeof(fmt)); |
| fmt.which = which; |
| fmt.pad = pad; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt)); |
| fail_on_test(check_0(fmt.reserved, sizeof(fmt.reserved))); |
| fail_on_test(fmt.which != which); |
| fail_on_test(fmt.pad != pad); |
| fail_on_test(checkMBusFrameFmt(node, fmt.format)); |
| s_fmt = fmt; |
| memset(s_fmt.reserved, 0xff, sizeof(s_fmt.reserved)); |
| memset(s_fmt.format.reserved, 0xff, sizeof(s_fmt.format.reserved)); |
| ret = doioctl(node, VIDIOC_SUBDEV_S_FMT, &s_fmt); |
| if (node->is_ro_subdev && which == V4L2_SUBDEV_FORMAT_ACTIVE) { |
| fail_on_test(ret != EPERM); |
| return 0; |
| } |
| fail_on_test(ret && ret != ENOTTY); |
| fail_on_test(s_fmt.which != which); |
| fail_on_test(s_fmt.pad != pad); |
| if (ret) { |
| warn("VIDIOC_SUBDEV_G_FMT is supported but not VIDIOC_SUBDEV_S_FMT\n"); |
| return 0; |
| } |
| fail_on_test(check_0(s_fmt.reserved, sizeof(s_fmt.reserved))); |
| fail_on_test(checkMBusFrameFmt(node, s_fmt.format)); |
| fail_on_test(s_fmt.format.width != fmt.format.width); |
| fail_on_test(s_fmt.format.height != fmt.format.height); |
| fail_on_test(s_fmt.format.code != fmt.format.code); |
| fail_on_test(s_fmt.format.field != fmt.format.field); |
| fail_on_test(s_fmt.format.colorspace != fmt.format.colorspace); |
| fail_on_test(s_fmt.format.ycbcr_enc != fmt.format.ycbcr_enc); |
| fail_on_test(s_fmt.format.quantization != fmt.format.quantization); |
| fail_on_test(s_fmt.format.xfer_func != fmt.format.xfer_func); |
| |
| s_fmt.format.code = ~0U; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FMT, &s_fmt)); |
| fail_on_test(s_fmt.format.code == ~0U); |
| fail_on_test(!s_fmt.format.code); |
| |
| // Restore fmt |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt)); |
| return 0; |
| } |
| |
| struct target_info { |
| __u32 target; |
| bool allowed; |
| bool readonly; |
| bool found; |
| }; |
| |
| static target_info targets[] = { |
| { V4L2_SEL_TGT_CROP, true }, |
| { V4L2_SEL_TGT_CROP_DEFAULT, true, true }, |
| { V4L2_SEL_TGT_CROP_BOUNDS, true, true }, |
| { V4L2_SEL_TGT_NATIVE_SIZE, true }, |
| { V4L2_SEL_TGT_COMPOSE, true }, |
| { V4L2_SEL_TGT_COMPOSE_DEFAULT, false, true }, |
| { V4L2_SEL_TGT_COMPOSE_BOUNDS, true, true }, |
| { V4L2_SEL_TGT_COMPOSE_PADDED, false, true }, |
| { ~0U }, |
| }; |
| |
| int testSubDevSelection(struct node *node, unsigned which, unsigned pad) |
| { |
| struct v4l2_subdev_selection sel; |
| struct v4l2_subdev_selection s_sel; |
| struct v4l2_subdev_crop crop; |
| bool is_sink = node->pads[pad].flags & MEDIA_PAD_FL_SINK; |
| bool have_sel = false; |
| int ret; |
| |
| targets[V4L2_SEL_TGT_NATIVE_SIZE].readonly = is_sink; |
| memset(&crop, 0, sizeof(crop)); |
| crop.pad = pad; |
| crop.which = which; |
| memset(&sel, 0, sizeof(sel)); |
| sel.which = which; |
| sel.pad = pad; |
| sel.target = V4L2_SEL_TGT_CROP; |
| ret = doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel); |
| node->has_subdev_selection |= (ret != ENOTTY) << which; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_CROP, &crop) != ret); |
| if (ret == ENOTTY) { |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_SELECTION, &sel) != ENOTTY); |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_CROP, &crop) != ENOTTY); |
| return ret; |
| } |
| fail_on_test(check_0(crop.reserved, sizeof(crop.reserved))); |
| fail_on_test(crop.which != which); |
| fail_on_test(crop.pad != pad); |
| fail_on_test(memcmp(&crop.rect, &sel.r, sizeof(sel.r))); |
| |
| for (unsigned tgt = 0; targets[tgt].target != ~0U; tgt++) { |
| targets[tgt].found = false; |
| memset(&sel, 0xff, sizeof(sel)); |
| sel.which = which; |
| sel.pad = pad; |
| sel.target = tgt; |
| ret = doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel); |
| targets[tgt].found = !ret; |
| fail_on_test(ret && ret != EINVAL); |
| if (ret) |
| continue; |
| have_sel = true; |
| fail_on_test(!targets[tgt].allowed); |
| fail_on_test(check_0(sel.reserved, sizeof(sel.reserved))); |
| fail_on_test(sel.which != which); |
| fail_on_test(sel.pad != pad); |
| fail_on_test(sel.target != tgt); |
| fail_on_test(!sel.r.width); |
| fail_on_test(sel.r.width == ~0U); |
| fail_on_test(!sel.r.height); |
| fail_on_test(sel.r.height == ~0U); |
| fail_on_test(sel.r.top == ~0); |
| fail_on_test(sel.r.left == ~0); |
| sel.which = ~0; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel) != EINVAL); |
| sel.which = 0; |
| sel.pad = node->entity.pads; |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel) != EINVAL); |
| sel.which = which; |
| sel.pad = pad; |
| s_sel = sel; |
| memset(s_sel.reserved, 0xff, sizeof(s_sel.reserved)); |
| ret = doioctl(node, VIDIOC_SUBDEV_S_SELECTION, &s_sel); |
| if (node->is_ro_subdev && which == V4L2_SUBDEV_FORMAT_ACTIVE) |
| fail_on_test(ret != EPERM); |
| if (tgt == V4L2_SEL_TGT_CROP) { |
| crop.rect = sel.r; |
| memset(crop.reserved, 0xff, sizeof(crop.reserved)); |
| fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_CROP, &crop) != ret); |
| if (!ret) { |
| fail_on_test(check_0(crop.reserved, sizeof(crop.reserved))); |
| fail_on_test(crop.which != which); |
| fail_on_test(crop.pad != pad); |
| fail_on_test(memcmp(&crop.rect, &sel.r, sizeof(sel.r))); |
| } |
| } |
| if (node->is_ro_subdev && which == V4L2_SUBDEV_FORMAT_ACTIVE) |
| continue; |
| fail_on_test(!ret && targets[tgt].readonly); |
| fail_on_test(s_sel.which != which); |
| fail_on_test(s_sel.pad != pad); |
| if (ret && !targets[tgt].readonly && tgt != V4L2_SEL_TGT_NATIVE_SIZE) |
| warn("VIDIOC_SUBDEV_G_SELECTION is supported for target %u but not VIDIOC_SUBDEV_S_SELECTION\n", tgt); |
| if (ret) |
| continue; |
| fail_on_test(check_0(s_sel.reserved, sizeof(s_sel.reserved))); |
| fail_on_test(s_sel.flags != sel.flags); |
| fail_on_test(s_sel.r.top != sel.r.top); |
| fail_on_test(s_sel.r.left != sel.r.left); |
| fail_on_test(s_sel.r.width != sel.r.width); |
| fail_on_test(s_sel.r.height != sel.r.height); |
| } |
| |
| return have_sel ? 0 : ENOTTY; |
| } |