1 /*
2 V4L2 API subdev ioctl tests.
3
4 Copyright (C) 2018 Hans Verkuil <hverkuil-cisco@xs4all.nl>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
19 */
20
21 #include <map>
22 #include <set>
23
24 #include <sys/types.h>
25
26 #include "v4l2-compliance.h"
27
28 #define VALID_SUBDEV_CAPS (V4L2_SUBDEV_CAP_RO_SUBDEV | V4L2_SUBDEV_CAP_STREAMS)
29
testSubDevCap(struct node * node)30 int testSubDevCap(struct node *node)
31 {
32 v4l2_subdev_capability caps;
33
34 memset(&caps, 0xff, sizeof(caps));
35 // Must always be there
36 fail_on_test(doioctl(node, VIDIOC_SUBDEV_QUERYCAP, &caps));
37 if (has_mmu)
38 fail_on_test(doioctl(node, VIDIOC_SUBDEV_QUERYCAP, nullptr) != EFAULT);
39 fail_on_test(check_0(caps.reserved, sizeof(caps.reserved)));
40 fail_on_test((caps.version >> 16) < 5);
41 fail_on_test(caps.capabilities & ~VALID_SUBDEV_CAPS);
42 node->is_ro_subdev = caps.capabilities & V4L2_SUBDEV_CAP_RO_SUBDEV;
43 return 0;
44 }
45
testSubDevEnumFrameInterval(struct node * node,unsigned which,unsigned pad,unsigned code,unsigned width,unsigned height)46 static int testSubDevEnumFrameInterval(struct node *node, unsigned which,
47 unsigned pad, unsigned code,
48 unsigned width, unsigned height)
49 {
50 struct v4l2_subdev_frame_interval_enum fie;
51 unsigned num_ivals;
52 int ret;
53
54 memset(&fie, 0, sizeof(fie));
55 fie.which = which;
56 fie.pad = pad;
57 fie.stream = 0;
58 fie.code = code;
59 fie.width = width;
60 fie.height = height;
61 ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie);
62 node->has_subdev_enum_fival |= (ret != ENOTTY) << which;
63 if (ret == ENOTTY)
64 return ret;
65 if (which)
66 fail_on_test(node->enum_frame_interval_pad != (int)pad);
67 else
68 fail_on_test(node->enum_frame_interval_pad >= 0);
69 node->enum_frame_interval_pad = pad;
70 fie.which = ~0;
71 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL);
72 fie.which = which;
73 fie.index = ~0;
74 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL);
75 fie.pad = node->entity.pads;
76 fie.index = 0;
77 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL);
78 fie.width = ~0;
79 fie.pad = pad;
80 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL);
81 fie.height = ~0;
82 fie.width = width;
83 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != EINVAL);
84 memset(&fie, 0xff, sizeof(fie));
85 fie.which = which;
86 fie.pad = pad;
87 fie.stream = 0;
88 fie.code = code;
89 fie.width = width;
90 fie.height = height;
91 fie.index = 0;
92 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie));
93 fail_on_test(check_0(fie.reserved, sizeof(fie.reserved)));
94 fail_on_test(fie.which != which);
95 fail_on_test(fie.pad != pad);
96 fail_on_test(fie.code != code);
97 fail_on_test(fie.width != width);
98 fail_on_test(fie.height != height);
99 fail_on_test(fie.index);
100 fail_on_test(fie.interval.numerator == ~0U || fie.interval.denominator == ~0U);
101 do {
102 fie.index++;
103 ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie);
104 } while (!ret);
105 fail_on_test(ret != EINVAL);
106
107 num_ivals = fie.index;
108 for (unsigned i = 0; i < num_ivals; i++) {
109 fie.index = i;
110 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie));
111 fail_on_test(fie.which != which);
112 fail_on_test(fie.pad != pad);
113 fail_on_test(fie.code != code);
114 fail_on_test(fie.width != width);
115 fail_on_test(fie.height != height);
116 fail_on_test(fie.index != i);
117 fail_on_test(!fie.interval.numerator);
118 fail_on_test(!fie.interval.denominator);
119 }
120 return 0;
121 }
122
testSubDevEnumFrameSize(struct node * node,unsigned which,unsigned pad,unsigned stream,unsigned code)123 static int testSubDevEnumFrameSize(struct node *node, unsigned which,
124 unsigned pad, unsigned stream, unsigned code)
125 {
126 struct v4l2_subdev_frame_size_enum fse;
127 unsigned num_sizes;
128 int ret;
129
130 memset(&fse, 0, sizeof(fse));
131 fse.which = which;
132 fse.pad = pad;
133 fse.stream = stream;
134 fse.code = code;
135 ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse);
136 node->has_subdev_enum_fsize |= (ret != ENOTTY) << which;
137 if (ret == ENOTTY) {
138 struct v4l2_subdev_frame_interval_enum fie;
139
140 memset(&fie, 0, sizeof(fie));
141 fie.which = which;
142 fie.pad = pad;
143 fie.stream = stream;
144 fie.code = code;
145 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != ENOTTY);
146 return ret;
147 }
148 fse.which = ~0;
149 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != EINVAL);
150 fse.which = which;
151 fse.index = ~0;
152 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != EINVAL);
153 fse.pad = node->entity.pads;
154 fse.index = 0;
155 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != EINVAL);
156 memset(&fse, 0xff, sizeof(fse));
157 fse.which = which;
158 fse.pad = pad;
159 fse.stream = stream;
160 fse.code = code;
161 fse.index = 0;
162 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse));
163 fail_on_test(check_0(fse.reserved, sizeof(fse.reserved)));
164 fail_on_test(fse.which != which);
165 fail_on_test(fse.pad != pad);
166 fail_on_test(fse.code != code);
167 fail_on_test(fse.index);
168 fail_on_test(!fse.min_width || !fse.min_height);
169 fail_on_test(fse.min_width == ~0U || fse.min_height == ~0U);
170 fail_on_test(!fse.max_width || !fse.max_height);
171 fail_on_test(fse.max_width == ~0U || fse.max_height == ~0U);
172 fail_on_test(fse.min_width > fse.max_width);
173 fail_on_test(fse.min_height > fse.max_height);
174 do {
175 fse.index++;
176 ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse);
177 } while (!ret);
178 fail_on_test(ret != EINVAL);
179
180 num_sizes = fse.index;
181 for (unsigned i = 0; i < num_sizes; i++) {
182 fse.index = i;
183 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse));
184 fail_on_test(fse.which != which);
185 fail_on_test(fse.pad != pad);
186 fail_on_test(fse.code != code);
187 fail_on_test(fse.index != i);
188 fail_on_test(!fse.min_width || !fse.min_height);
189 fail_on_test(!fse.max_width || !fse.max_height);
190 fail_on_test(fse.min_width > fse.max_width);
191 fail_on_test(fse.min_height > fse.max_height);
192
193 ret = testSubDevEnumFrameInterval(node, which, pad, code,
194 fse.min_width, fse.min_height);
195 fail_on_test(ret && ret != ENOTTY);
196 ret = testSubDevEnumFrameInterval(node, which, pad, code,
197 fse.max_width, fse.max_height);
198 fail_on_test(ret && ret != ENOTTY);
199 }
200 return 0;
201 }
202
testSubDevEnum(struct node * node,unsigned which,unsigned pad,unsigned stream)203 int testSubDevEnum(struct node *node, unsigned which, unsigned pad, unsigned stream)
204 {
205 struct v4l2_subdev_mbus_code_enum mbus_core_enum;
206 unsigned num_codes;
207 int ret;
208
209 memset(&mbus_core_enum, 0, sizeof(mbus_core_enum));
210 mbus_core_enum.which = which;
211 mbus_core_enum.pad = pad;
212 mbus_core_enum.stream = stream;
213 ret = doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum);
214 node->has_subdev_enum_code |= (ret != ENOTTY) << which;
215 if (ret == ENOTTY) {
216 struct v4l2_subdev_frame_size_enum fse;
217 struct v4l2_subdev_frame_interval_enum fie;
218
219 memset(&fse, 0, sizeof(fse));
220 memset(&fie, 0, sizeof(fie));
221 fse.which = which;
222 fse.pad = pad;
223 fse.stream = stream;
224 fie.which = which;
225 fie.pad = pad;
226 fie.stream = stream;
227 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != ENOTTY);
228 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != ENOTTY);
229 return ret;
230 }
231 mbus_core_enum.which = ~0;
232 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL);
233 mbus_core_enum.which = which;
234 mbus_core_enum.index = ~0;
235 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL);
236 mbus_core_enum.pad = node->entity.pads;
237 mbus_core_enum.stream = stream;
238 mbus_core_enum.index = 0;
239 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL);
240 memset(&mbus_core_enum, 0xff, sizeof(mbus_core_enum));
241 mbus_core_enum.which = which;
242 mbus_core_enum.pad = pad;
243 mbus_core_enum.stream = stream;
244 mbus_core_enum.index = 0;
245 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum));
246 fail_on_test(check_0(mbus_core_enum.reserved, sizeof(mbus_core_enum.reserved)));
247 fail_on_test(mbus_core_enum.code == ~0U);
248 fail_on_test(mbus_core_enum.pad != pad);
249 fail_on_test(mbus_core_enum.stream != stream);
250 fail_on_test(mbus_core_enum.index);
251 fail_on_test(mbus_core_enum.which != which);
252 do {
253 mbus_core_enum.index++;
254 ret = doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum);
255 } while (!ret);
256 fail_on_test(ret != EINVAL);
257
258 num_codes = mbus_core_enum.index;
259 for (unsigned i = 0; i < num_codes; i++) {
260 mbus_core_enum.index = i;
261 mbus_core_enum.code = 0;
262 fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum));
263 fail_on_test(!mbus_core_enum.code);
264 fail_on_test(mbus_core_enum.which != which);
265 fail_on_test(mbus_core_enum.pad != pad);
266 fail_on_test(mbus_core_enum.stream != stream);
267 fail_on_test(mbus_core_enum.index != i);
268
269 ret = testSubDevEnumFrameSize(node, which, pad, stream, mbus_core_enum.code);
270 fail_on_test(ret && ret != ENOTTY);
271 }
272 return 0;
273 }
274
testSubDevFrameInterval(struct node * node,unsigned which,unsigned pad,unsigned stream)275 int testSubDevFrameInterval(struct node *node, unsigned which, unsigned pad, unsigned stream)
276 {
277 struct v4l2_subdev_frame_interval fival;
278 struct v4l2_fract ival;
279 bool has_which = node->has_ival_uses_which();
280 int ret;
281
282 if (!has_which)
283 which = V4L2_SUBDEV_FORMAT_ACTIVE;
284 memset(&fival, 0xff, sizeof(fival));
285 fival.pad = pad;
286 fival.stream = stream;
287 if (has_which)
288 fival.which = which;
289 ret = doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival);
290 if (has_which)
291 node->has_subdev_frame_interval |= (ret != ENOTTY) << which;
292 if (ret == ENOTTY) {
293 fail_on_test(node->enum_frame_interval_pad >= 0);
294 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != ENOTTY);
295 return ret;
296 }
297
298 if (!has_which)
299 warn_once("V4L2_SUBDEV_CLIENT_CAP_INTERVAL_USES_WHICH is not supported\n");
300
301 fail_on_test(node->frame_interval_pad >= 0);
302 fail_on_test(node->enum_frame_interval_pad != (int)pad);
303 node->frame_interval_pad = pad;
304 fail_on_test(check_0(fival.reserved, sizeof(fival.reserved)));
305 if (has_which)
306 fail_on_test(fival.which != which);
307 fail_on_test(fival.pad != pad);
308 fail_on_test(fival.stream != stream);
309 fail_on_test(!fival.interval.numerator);
310 fail_on_test(!fival.interval.denominator);
311 fail_on_test(fival.interval.numerator == ~0U || fival.interval.denominator == ~0U);
312 ival = fival.interval;
313 memset(fival.reserved, 0xff, sizeof(fival.reserved));
314 if (node->is_ro_subdev) {
315 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != EPERM);
316 return 0;
317 }
318 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival));
319 if (has_which)
320 fail_on_test(fival.which != which);
321 fail_on_test(fival.pad != pad);
322 fail_on_test(fival.stream != stream);
323 fail_on_test(ival.numerator != fival.interval.numerator);
324 fail_on_test(ival.denominator != fival.interval.denominator);
325 fail_on_test(check_0(fival.reserved, sizeof(fival.reserved)));
326 memset(&fival, 0, sizeof(fival));
327 fival.pad = pad;
328 fival.stream = stream;
329 if (has_which)
330 fival.which = which;
331 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival));
332 if (has_which)
333 fail_on_test(fival.which != which);
334 fail_on_test(fival.pad != pad);
335 fail_on_test(fival.stream != stream);
336 fail_on_test(ival.numerator != fival.interval.numerator);
337 fail_on_test(ival.denominator != fival.interval.denominator);
338
339 if (has_which) {
340 fival.which = ~0;
341 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) != EINVAL);
342 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != EINVAL);
343 fival.which = which;
344 }
345 fival.pad = node->entity.pads;
346 fival.stream = stream;
347 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) != EINVAL);
348 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != EINVAL);
349 fival.pad = pad;
350 fival.stream = stream;
351 fival.interval = ival;
352 fival.interval.numerator = 0;
353 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival));
354 fail_on_test(!fival.interval.numerator);
355 fail_on_test(!fival.interval.denominator);
356 fival.interval = ival;
357 fival.interval.denominator = 0;
358 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival));
359 fail_on_test(!fival.interval.numerator);
360 fail_on_test(!fival.interval.denominator);
361 fival.interval = ival;
362 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival));
363 return 0;
364 }
365
checkMBusFrameFmt(struct node * node,struct v4l2_mbus_framefmt & fmt)366 static int checkMBusFrameFmt(struct node *node, struct v4l2_mbus_framefmt &fmt)
367 {
368 fail_on_test(check_0(fmt.reserved, sizeof(fmt.reserved)));
369 fail_on_test(fmt.width == 0 || fmt.width > 65536);
370 fail_on_test(fmt.height == 0 || fmt.height > 65536);
371 fail_on_test(fmt.code == 0 || fmt.code == ~0U);
372 fail_on_test(fmt.field == ~0U);
373 if (!node->is_passthrough_subdev) {
374 // Passthrough subdevs just copy this info, they don't validate
375 // it. TODO: this does not take colorspace converters into account!
376 fail_on_test(fmt.colorspace == ~0U);
377 //TBD fail_on_test(!fmt.colorspace);
378 fail_on_test(fmt.ycbcr_enc == 0xffff);
379 fail_on_test(fmt.quantization == 0xffff);
380 fail_on_test(fmt.xfer_func == 0xffff);
381 fail_on_test(!fmt.colorspace &&
382 (fmt.ycbcr_enc || fmt.quantization || fmt.xfer_func));
383 }
384 return 0;
385 }
386
testSubDevFormat(struct node * node,unsigned which,unsigned pad,unsigned stream)387 int testSubDevFormat(struct node *node, unsigned which, unsigned pad, unsigned stream)
388 {
389 struct v4l2_subdev_format fmt;
390 struct v4l2_subdev_format s_fmt;
391 int ret;
392
393 memset(&fmt, 0, sizeof(fmt));
394 fmt.which = which;
395 fmt.pad = pad;
396 fmt.stream = stream;
397 ret = doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt);
398 node->has_subdev_fmt |= (ret != ENOTTY) << which;
399 if (ret == ENOTTY) {
400 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt) != ENOTTY);
401 return ret;
402 }
403 fmt.which = ~0;
404 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt) != EINVAL);
405 fmt.which = 0;
406 fmt.pad = node->entity.pads;
407 fmt.stream = stream;
408 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt) != EINVAL);
409 memset(&fmt, 0xff, sizeof(fmt));
410 fmt.which = which;
411 fmt.pad = pad;
412 fmt.stream = stream;
413 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt));
414 fail_on_test(check_0(fmt.reserved, sizeof(fmt.reserved)));
415 fail_on_test(fmt.which != which);
416 fail_on_test(fmt.pad != pad);
417 fail_on_test(fmt.stream != stream);
418 fail_on_test(checkMBusFrameFmt(node, fmt.format));
419 s_fmt = fmt;
420 memset(s_fmt.reserved, 0xff, sizeof(s_fmt.reserved));
421 memset(s_fmt.format.reserved, 0xff, sizeof(s_fmt.format.reserved));
422 ret = doioctl(node, VIDIOC_SUBDEV_S_FMT, &s_fmt);
423 if (node->is_ro_subdev && which == V4L2_SUBDEV_FORMAT_ACTIVE) {
424 fail_on_test(ret != EPERM);
425 return 0;
426 }
427 fail_on_test(ret && ret != ENOTTY);
428 fail_on_test(s_fmt.which != which);
429 fail_on_test(s_fmt.pad != pad);
430 fail_on_test(s_fmt.stream != stream);
431 if (ret) {
432 warn("VIDIOC_SUBDEV_G_FMT is supported but not VIDIOC_SUBDEV_S_FMT\n");
433 return 0;
434 }
435 fail_on_test(check_0(s_fmt.reserved, sizeof(s_fmt.reserved)));
436 fail_on_test(checkMBusFrameFmt(node, s_fmt.format));
437 fail_on_test(s_fmt.format.width != fmt.format.width);
438 fail_on_test(s_fmt.format.height != fmt.format.height);
439 fail_on_test(s_fmt.format.code != fmt.format.code);
440 fail_on_test(s_fmt.format.field != fmt.format.field);
441 fail_on_test(s_fmt.format.colorspace != fmt.format.colorspace);
442 fail_on_test(s_fmt.format.ycbcr_enc != fmt.format.ycbcr_enc);
443 fail_on_test(s_fmt.format.quantization != fmt.format.quantization);
444 fail_on_test(s_fmt.format.xfer_func != fmt.format.xfer_func);
445
446 s_fmt.format.code = ~0U;
447 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FMT, &s_fmt));
448 fail_on_test(s_fmt.format.code == ~0U);
449 fail_on_test(!s_fmt.format.code);
450
451 // Restore fmt
452 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FMT, &fmt));
453 return 0;
454 }
455
456 struct target_info {
457 __u32 target;
458 bool allowed;
459 bool readonly;
460 bool found;
461 };
462
463 static target_info targets[] = {
464 { V4L2_SEL_TGT_CROP, true },
465 { V4L2_SEL_TGT_CROP_DEFAULT, true, true },
466 { V4L2_SEL_TGT_CROP_BOUNDS, true, true },
467 { V4L2_SEL_TGT_NATIVE_SIZE, true },
468 { V4L2_SEL_TGT_COMPOSE, true },
469 { V4L2_SEL_TGT_COMPOSE_DEFAULT, false, true },
470 { V4L2_SEL_TGT_COMPOSE_BOUNDS, true, true },
471 { V4L2_SEL_TGT_COMPOSE_PADDED, false, true },
472 { ~0U },
473 };
474
testSubDevSelection(struct node * node,unsigned which,unsigned pad,unsigned stream)475 int testSubDevSelection(struct node *node, unsigned which, unsigned pad, unsigned stream)
476 {
477 struct v4l2_subdev_selection sel;
478 struct v4l2_subdev_selection s_sel;
479 struct v4l2_subdev_crop crop;
480 bool is_sink = node->pads[pad].flags & MEDIA_PAD_FL_SINK;
481 bool have_sel = false;
482 int ret;
483
484 targets[V4L2_SEL_TGT_NATIVE_SIZE].readonly = is_sink;
485 memset(&crop, 0, sizeof(crop));
486 crop.pad = pad;
487 crop.stream = stream;
488 crop.which = which;
489 memset(&sel, 0, sizeof(sel));
490 sel.which = which;
491 sel.pad = pad;
492 sel.stream = stream;
493 sel.target = V4L2_SEL_TGT_CROP;
494 ret = doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel);
495 node->has_subdev_selection |= (ret != ENOTTY) << which;
496 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_CROP, &crop) != ret);
497 if (ret == ENOTTY) {
498 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_SELECTION, &sel) != ENOTTY);
499 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_CROP, &crop) != ENOTTY);
500 return ret;
501 }
502 fail_on_test(check_0(crop.reserved, sizeof(crop.reserved)));
503 fail_on_test(crop.which != which);
504 fail_on_test(crop.pad != pad);
505 fail_on_test(crop.stream != stream);
506 fail_on_test(memcmp(&crop.rect, &sel.r, sizeof(sel.r)));
507
508 for (unsigned tgt = 0; targets[tgt].target != ~0U; tgt++) {
509 targets[tgt].found = false;
510 memset(&sel, 0xff, sizeof(sel));
511 sel.which = which;
512 sel.pad = pad;
513 sel.stream = stream;
514 sel.target = tgt;
515 ret = doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel);
516 targets[tgt].found = !ret;
517 fail_on_test(ret && ret != EINVAL);
518 if (ret)
519 continue;
520 have_sel = true;
521 fail_on_test(!targets[tgt].allowed);
522 fail_on_test(check_0(sel.reserved, sizeof(sel.reserved)));
523 fail_on_test(sel.which != which);
524 fail_on_test(sel.pad != pad);
525 fail_on_test(sel.stream != stream);
526 fail_on_test(sel.target != tgt);
527 fail_on_test(!sel.r.width);
528 fail_on_test(sel.r.width == ~0U);
529 fail_on_test(!sel.r.height);
530 fail_on_test(sel.r.height == ~0U);
531 fail_on_test(sel.r.top == ~0);
532 fail_on_test(sel.r.left == ~0);
533 sel.which = ~0;
534 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel) != EINVAL);
535 sel.which = 0;
536 sel.pad = node->entity.pads;
537 sel.stream = stream;
538 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel) != EINVAL);
539 sel.which = which;
540 sel.pad = pad;
541 sel.stream = stream;
542 s_sel = sel;
543 memset(s_sel.reserved, 0xff, sizeof(s_sel.reserved));
544 ret = doioctl(node, VIDIOC_SUBDEV_S_SELECTION, &s_sel);
545 if (node->is_ro_subdev && which == V4L2_SUBDEV_FORMAT_ACTIVE)
546 fail_on_test(ret != EPERM);
547 if (tgt == V4L2_SEL_TGT_CROP) {
548 crop.rect = sel.r;
549 memset(crop.reserved, 0xff, sizeof(crop.reserved));
550 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_CROP, &crop) != ret);
551 if (!ret) {
552 fail_on_test(check_0(crop.reserved, sizeof(crop.reserved)));
553 fail_on_test(crop.which != which);
554 fail_on_test(crop.pad != pad);
555 fail_on_test(crop.stream != stream);
556 fail_on_test(memcmp(&crop.rect, &sel.r, sizeof(sel.r)));
557 }
558 }
559 if (node->is_ro_subdev && which == V4L2_SUBDEV_FORMAT_ACTIVE)
560 continue;
561 fail_on_test(!ret && targets[tgt].readonly);
562 fail_on_test(s_sel.which != which);
563 fail_on_test(s_sel.pad != pad);
564 fail_on_test(s_sel.stream != stream);
565 if (ret && !targets[tgt].readonly && tgt != V4L2_SEL_TGT_NATIVE_SIZE)
566 warn("VIDIOC_SUBDEV_G_SELECTION is supported for target %u but not VIDIOC_SUBDEV_S_SELECTION\n", tgt);
567 if (ret)
568 continue;
569 fail_on_test(check_0(s_sel.reserved, sizeof(s_sel.reserved)));
570 fail_on_test(s_sel.flags != sel.flags);
571 fail_on_test(s_sel.r.top != sel.r.top);
572 fail_on_test(s_sel.r.left != sel.r.left);
573 fail_on_test(s_sel.r.width != sel.r.width);
574 fail_on_test(s_sel.r.height != sel.r.height);
575 }
576
577 return have_sel ? 0 : ENOTTY;
578 }
579
testSubDevRouting(struct node * node,unsigned which)580 int testSubDevRouting(struct node *node, unsigned which)
581 {
582 const uint32_t all_route_flags_mask = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
583 struct v4l2_subdev_routing routing = {};
584 struct v4l2_subdev_route routes[NUM_ROUTES_MAX] = {};
585 unsigned int i;
586 int ret;
587
588 routing.which = which;
589 routing.routes = (uintptr_t)&routes;
590 routing.len_routes = 0;
591 routing.num_routes = 0;
592 memset(routing.reserved, 0xff, sizeof(routing.reserved));
593
594 /* First test that G_ROUTING returns success even when len_routes is 0. */
595
596 ret = doioctl(node, VIDIOC_SUBDEV_G_ROUTING, &routing);
597 fail_on_test(ret);
598 fail_on_test(routing.num_routes > NUM_ROUTES_MAX);
599 fail_on_test(check_0(routing.reserved, sizeof(routing.reserved)));
600
601 if (!routing.num_routes)
602 return 0;
603
604 /*
605 * Then get the actual routes, and verify that the num_routes gets
606 * updated to the correct number.
607 */
608
609 uint32_t num_routes = routing.num_routes;
610 routing.len_routes = NUM_ROUTES_MAX;
611 routing.num_routes = 0;
612 fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_ROUTING, &routing));
613 fail_on_test(routing.num_routes != num_routes);
614
615 /* Check the validity of route pads and flags */
616
617 if (node->pads) {
618 for (i = 0; i < routing.num_routes; ++i) {
619 const struct v4l2_subdev_route *route = &routes[i];
620 const struct media_pad_desc *sink;
621 const struct media_pad_desc *source;
622
623 fail_on_test(route->sink_pad >= node->entity.pads);
624 fail_on_test(route->source_pad >= node->entity.pads);
625
626 sink = &node->pads[route->sink_pad];
627 source = &node->pads[route->source_pad];
628
629 fail_on_test(!(sink->flags & MEDIA_PAD_FL_SINK));
630 fail_on_test(!(source->flags & MEDIA_PAD_FL_SOURCE));
631 fail_on_test(route->flags & ~all_route_flags_mask);
632 }
633 }
634
635 /*
636 * Set the same routes back, which should always succeed and report the
637 * same number of routes.
638 */
639
640 memset(routing.reserved, 0xff, sizeof(routing.reserved));
641 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_ROUTING, &routing));
642 fail_on_test(routing.num_routes != num_routes);
643 fail_on_test(check_0(routing.reserved, sizeof(routing.reserved)));
644
645 /* Test setting invalid pads */
646
647 if (node->pads) {
648 for (i = 0; i < routing.num_routes; ++i) {
649 struct v4l2_subdev_route *route = &routes[i];
650
651 route->sink_pad = node->entity.pads + 1;
652 }
653
654 memset(routing.reserved, 0xff, sizeof(routing.reserved));
655 fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_ROUTING, &routing) != EINVAL);
656 }
657
658 return 0;
659 }
660