1 #include "v4l2-ctl.h"
2
3 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
4
5 struct mbus_name {
6 const char *name;
7 __u32 code;
8 };
9
10 static const struct mbus_name mbus_names[] = {
11 { "Fixed", MEDIA_BUS_FMT_FIXED },
12 #include "media-bus-format-names.h"
13 { nullptr, 0 }
14 };
15
16 /* selection specified */
17 #define SelectionWidth (1L<<0)
18 #define SelectionHeight (1L<<1)
19 #define SelectionLeft (1L<<2)
20 #define SelectionTop (1L<<3)
21 #define SelectionFlags (1L<<4)
22
23 static __u32 list_mbus_codes_pad;
24 static __u32 list_mbus_codes_stream = 0;
25 static __u32 get_fmt_pad;
26 static __u32 get_fmt_stream = 0;
27 static __u32 get_sel_pad;
28 static __u32 get_sel_stream = 0;
29 static __u32 get_fps_pad;
30 static __u32 get_fps_stream = 0;
31 static int get_sel_target = -1;
32 static unsigned int set_selection;
33 static struct v4l2_subdev_selection vsel;
34 static unsigned int set_fmt;
35 static __u32 set_fmt_pad;
36 static __u32 set_fmt_stream = 0;
37 static struct v4l2_mbus_framefmt ffmt;
38 static struct v4l2_subdev_frame_size_enum frmsize;
39 static struct v4l2_subdev_frame_interval_enum frmival;
40 static __u32 set_fps_pad;
41 static __u32 set_fps_stream = 0;
42 static double set_fps;
43 static struct v4l2_subdev_routing routing;
44 static struct v4l2_subdev_route routes[NUM_ROUTES_MAX];
45
subdev_usage()46 void subdev_usage()
47 {
48 printf("\nSub-Device options:\n"
49 "Note: all parameters below (pad, code, etc.) are optional unless otherwise noted and default to 0\n"
50 " --list-subdev-mbus-codes pad=<pad>,stream=<stream>\n"
51 " display supported mediabus codes for this pad and stream\n"
52 " [VIDIOC_SUBDEV_ENUM_MBUS_CODE]\n"
53 " --list-subdev-framesizes pad=<pad>,stream=<stream>,code=<code>\n"
54 " list supported framesizes for this pad, stream and code\n"
55 " [VIDIOC_SUBDEV_ENUM_FRAME_SIZE]\n"
56 " <code> is the value of the mediabus code\n"
57 " --list-subdev-frameintervals pad=<pad>,stream=<stream>,width=<w>,height=<h>,code=<code>\n"
58 " list supported frame intervals for this pad, stream, code and\n"
59 " the given width and height [VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL]\n"
60 " <code> is the value of the mediabus code\n"
61 " --get-subdev-fmt pad=<pad>,stream=<stream>\n"
62 " query the frame format for the given pad and stream [VIDIOC_SUBDEV_G_FMT]\n"
63 " --get-subdev-selection pad=<pad>,stream=<stream>,target=<target>\n"
64 " query the frame selection rectangle [VIDIOC_SUBDEV_G_SELECTION]\n"
65 " See --set-subdev-selection command for the valid <target> values.\n"
66 " --get-subdev-fps pad=<pad>,stream=<stream>\n"
67 " query the frame rate [VIDIOC_SUBDEV_G_FRAME_INTERVAL]\n"
68 " --set-subdev-fmt (for testing only, otherwise use media-ctl)\n"
69 " --try-subdev-fmt pad=<pad>,stream=<stream>,width=<w>,height=<h>,code=<code>,field=<f>,colorspace=<c>,\n"
70 " xfer=<xf>,ycbcr=<y>,hsv=<hsv>,quantization=<q>\n"
71 " set the frame format for the given pad and stream [VIDIOC_SUBDEV_S_FMT]\n"
72 " <pad> the pad to get the format from\n"
73 " <stream> the stream to get the format\n"
74 " <code> is the value of the mediabus code\n"
75 " <f> can be one of the following field layouts:\n"
76 " any, none, top, bottom, interlaced, seq_tb, seq_bt,\n"
77 " alternate, interlaced_tb, interlaced_bt\n"
78 " <c> can be one of the following colorspaces:\n"
79 " smpte170m, smpte240m, rec709, 470m, 470bg, jpeg, srgb,\n"
80 " oprgb, bt2020, dcip3\n"
81 " <xf> can be one of the following transfer functions:\n"
82 " default, 709, srgb, oprgb, smpte240m, smpte2084, dcip3, none\n"
83 " <y> can be one of the following Y'CbCr encodings:\n"
84 " default, 601, 709, xv601, xv709, bt2020, bt2020c, smpte240m\n"
85 " <hsv> can be one of the following HSV encodings:\n"
86 " default, 180, 256\n"
87 " <q> can be one of the following quantization methods:\n"
88 " default, full-range, lim-range\n"
89 " --set-subdev-selection (for testing only, otherwise use media-ctl)\n"
90 " --try-subdev-selection pad=<pad>,stream=<stream>,target=<target>,flags=<flags>,\n"
91 " top=<x>,left=<y>,width=<w>,height=<h>\n"
92 " set the video capture selection rectangle [VIDIOC_SUBDEV_S_SELECTION]\n"
93 " target=crop|crop_bounds|crop_default|compose|compose_bounds|\n"
94 " compose_default|compose_padded|native_size\n"
95 " flags=le|ge|keep-config\n"
96 " --set-subdev-fps pad=<pad>,stream=<stream>,fps=<fps> (for testing only, otherwise use media-ctl)\n"
97 " set the frame rate [VIDIOC_SUBDEV_S_FRAME_INTERVAL]\n"
98 " --get-routing Print the route topology\n"
99 " --set-routing (for testing only, otherwise use media-ctl)\n"
100 " --try-routing <routes>\n"
101 " Comma-separated list of route descriptors to setup\n"
102 "\n"
103 "Routes are defined as\n"
104 " routes = route { ',' route } ;\n"
105 " route = sink '->' source '[' flags ']' ;\n"
106 " sink = sink-pad '/' sink-stream ;\n"
107 " source = source-pad '/' source-stream ;\n"
108 "\n"
109 "where\n"
110 " sink-pad = Pad numeric identifier for sink\n"
111 " sink-stream = Stream numeric identifier for sink\n"
112 " source-pad = Pad numeric identifier for source\n"
113 " source-stream = Stream numeric identifier for source\n"
114 " flags = Route flags (0: inactive, 1: active)\n"
115 );
116 }
117
subdev_cmd(int ch,char * optarg)118 void subdev_cmd(int ch, char *optarg)
119 {
120 char *value, *subs;
121 char *endp;
122
123 switch (ch) {
124 case OptListSubDevMBusCodes:
125 if (optarg) {
126 /* Legacy pad-only parsing */
127 list_mbus_codes_pad = strtoul(optarg, &endp, 0);
128 if (*endp == 0)
129 break;
130 }
131
132 subs = optarg;
133 while (subs && *subs != '\0') {
134 static constexpr const char *subopts[] = {
135 "pad",
136 "stream",
137 nullptr
138 };
139
140 switch (parse_subopt(&subs, subopts, &value)) {
141 case 0:
142 list_mbus_codes_pad = strtoul(value, nullptr, 0);
143 break;
144 case 1:
145 list_mbus_codes_stream = strtoul(value, nullptr, 0);
146 break;
147 default:
148 subdev_usage();
149 std::exit(EXIT_FAILURE);
150 }
151 }
152 break;
153 case OptListSubDevFrameSizes:
154 subs = optarg;
155 while (*subs != '\0') {
156 static constexpr const char *subopts[] = {
157 "pad",
158 "stream",
159 "code",
160 nullptr
161 };
162
163 switch (parse_subopt(&subs, subopts, &value)) {
164 case 0:
165 frmsize.pad = strtoul(value, nullptr, 0);
166 break;
167 case 1:
168 frmsize.stream = strtoul(value, nullptr, 0);
169 break;
170 case 2:
171 frmsize.code = strtoul(value, nullptr, 0);
172 break;
173 default:
174 subdev_usage();
175 std::exit(EXIT_FAILURE);
176 }
177 }
178 break;
179 case OptListSubDevFrameIntervals:
180 subs = optarg;
181 while (*subs != '\0') {
182 static constexpr const char *subopts[] = {
183 "pad",
184 "stream",
185 "code",
186 "width",
187 "height",
188 nullptr
189 };
190
191 switch (parse_subopt(&subs, subopts, &value)) {
192 case 0:
193 frmival.pad = strtoul(value, nullptr, 0);
194 break;
195 case 1:
196 frmival.stream = strtoul(value, nullptr, 0);
197 break;
198 case 2:
199 frmival.code = strtoul(value, nullptr, 0);
200 break;
201 case 3:
202 frmival.width = strtoul(value, nullptr, 0);
203 break;
204 case 4:
205 frmival.height = strtoul(value, nullptr, 0);
206 break;
207 default:
208 subdev_usage();
209 std::exit(EXIT_FAILURE);
210 }
211 }
212 break;
213 case OptGetSubDevFormat:
214 if (optarg) {
215 /* Legacy pad-only parsing */
216 get_fmt_pad = strtoul(optarg, &endp, 0);
217 if (*endp == 0)
218 break;
219 }
220
221 subs = optarg;
222 while (subs && *subs != '\0') {
223 static constexpr const char *subopts[] = {
224 "pad",
225 "stream",
226 nullptr
227 };
228
229 switch (parse_subopt(&subs, subopts, &value)) {
230 case 0:
231 get_fmt_pad = strtoul(value, nullptr, 0);
232 break;
233 case 1:
234 get_fmt_stream = strtoul(value, nullptr, 0);
235 break;
236 default:
237 subdev_usage();
238 std::exit(EXIT_FAILURE);
239 }
240 }
241 break;
242 case OptGetSubDevSelection:
243 subs = optarg;
244 while (*subs != '\0') {
245 static constexpr const char *subopts[] = {
246 "pad",
247 "stream",
248 "target",
249 nullptr
250 };
251 unsigned int target;
252
253 switch (parse_subopt(&subs, subopts, &value)) {
254 case 0:
255 get_sel_pad = strtoul(value, nullptr, 0);
256 break;
257 case 1:
258 get_sel_stream = strtoul(value, nullptr, 0);
259 break;
260 case 2:
261 if (parse_selection_target(value, target)) {
262 fprintf(stderr, "Unknown selection target\n");
263 subdev_usage();
264 std::exit(EXIT_FAILURE);
265 }
266 get_sel_target = target;
267 break;
268 default:
269 subdev_usage();
270 std::exit(EXIT_FAILURE);
271 }
272 }
273 break;
274 case OptGetSubDevFPS:
275 if (optarg) {
276 /* Legacy pad-only parsing */
277 get_fps_pad = strtoul(optarg, &endp, 0);
278 if (*endp == 0)
279 break;
280 }
281
282 subs = optarg;
283 while (subs && *subs != '\0') {
284 static constexpr const char *subopts[] = {
285 "pad",
286 "stream",
287 nullptr
288 };
289
290 switch (parse_subopt(&subs, subopts, &value)) {
291 case 0:
292 get_fps_pad = strtoul(value, nullptr, 0);
293 break;
294 case 1:
295 get_fps_stream = strtoul(value, nullptr, 0);
296 break;
297 default:
298 subdev_usage();
299 std::exit(EXIT_FAILURE);
300 }
301 }
302 break;
303 case OptSetSubDevFormat:
304 case OptTrySubDevFormat:
305 ffmt.field = V4L2_FIELD_ANY;
306 subs = optarg;
307 while (*subs != '\0') {
308 static constexpr const char *subopts[] = {
309 "width",
310 "height",
311 "code",
312 "field",
313 "colorspace",
314 "ycbcr",
315 "hsv",
316 "quantization",
317 "xfer",
318 "pad",
319 "stream",
320 nullptr
321 };
322
323 switch (parse_subopt(&subs, subopts, &value)) {
324 case 0:
325 ffmt.width = strtoul(value, nullptr, 0);
326 set_fmt |= FmtWidth;
327 break;
328 case 1:
329 ffmt.height = strtoul(value, nullptr, 0);
330 set_fmt |= FmtHeight;
331 break;
332 case 2:
333 ffmt.code = strtoul(value, nullptr, 0);
334 set_fmt |= FmtPixelFormat;
335 break;
336 case 3:
337 ffmt.field = parse_field(value);
338 set_fmt |= FmtField;
339 break;
340 case 4:
341 ffmt.colorspace = parse_colorspace(value);
342 if (ffmt.colorspace)
343 set_fmt |= FmtColorspace;
344 else
345 fprintf(stderr, "unknown colorspace %s\n", value);
346 break;
347 case 5:
348 ffmt.ycbcr_enc = parse_ycbcr(value);
349 set_fmt |= FmtYCbCr;
350 break;
351 case 6:
352 ffmt.ycbcr_enc = parse_hsv(value);
353 set_fmt |= FmtYCbCr;
354 break;
355 case 7:
356 ffmt.quantization = parse_quantization(value);
357 set_fmt |= FmtQuantization;
358 break;
359 case 8:
360 ffmt.xfer_func = parse_xfer_func(value);
361 set_fmt |= FmtXferFunc;
362 break;
363 case 9:
364 set_fmt_pad = strtoul(value, nullptr, 0);
365 break;
366 case 10:
367 set_fmt_stream = strtoul(value, nullptr, 0);
368 break;
369 default:
370 fprintf(stderr, "Unknown option\n");
371 subdev_usage();
372 std::exit(EXIT_FAILURE);
373 }
374 }
375 break;
376 case OptSetSubDevSelection:
377 case OptTrySubDevSelection:
378 subs = optarg;
379
380 while (*subs != '\0') {
381 static constexpr const char *subopts[] = {
382 "target",
383 "flags",
384 "left",
385 "top",
386 "width",
387 "height",
388 "pad",
389 "stream",
390 nullptr
391 };
392
393 switch (parse_subopt(&subs, subopts, &value)) {
394 case 0:
395 if (parse_selection_target(value, vsel.target)) {
396 fprintf(stderr, "Unknown selection target\n");
397 subdev_usage();
398 std::exit(EXIT_FAILURE);
399 }
400 break;
401 case 1:
402 vsel.flags = parse_selection_flags(value);
403 set_selection |= SelectionFlags;
404 break;
405 case 2:
406 vsel.r.left = strtol(value, nullptr, 0);
407 set_selection |= SelectionLeft;
408 break;
409 case 3:
410 vsel.r.top = strtol(value, nullptr, 0);
411 set_selection |= SelectionTop;
412 break;
413 case 4:
414 vsel.r.width = strtoul(value, nullptr, 0);
415 set_selection |= SelectionWidth;
416 break;
417 case 5:
418 vsel.r.height = strtoul(value, nullptr, 0);
419 set_selection |= SelectionHeight;
420 break;
421 case 6:
422 vsel.pad = strtoul(value, nullptr, 0);
423 break;
424 case 7:
425 vsel.stream = strtoul(value, nullptr, 0);
426 break;
427 default:
428 fprintf(stderr, "Unknown option\n");
429 subdev_usage();
430 std::exit(EXIT_FAILURE);
431 }
432 }
433 break;
434 case OptSetSubDevFPS:
435 subs = optarg;
436
437 while (*subs != '\0') {
438 static constexpr const char *subopts[] = {
439 "pad",
440 "stream",
441 "fps",
442 nullptr
443 };
444
445 switch (parse_subopt(&subs, subopts, &value)) {
446 case 0:
447 set_fps_pad = strtoul(value, nullptr, 0);
448 break;
449 case 1:
450 set_fps_stream = strtoul(value, nullptr, 0);
451 break;
452 case 2:
453 set_fps = strtod(value, nullptr);
454 break;
455 default:
456 fprintf(stderr, "Unknown option\n");
457 subdev_usage();
458 std::exit(EXIT_FAILURE);
459 }
460 }
461 break;
462 case OptSetRouting:
463 case OptTryRouting: {
464 struct v4l2_subdev_route *r;
465 char *end, *ref, *tok;
466 unsigned int flags;
467
468 memset(&routing, 0, sizeof(routing));
469 memset(routes, 0, sizeof(routes[0]) * NUM_ROUTES_MAX);
470 routing.which = ch == OptSetRouting ? V4L2_SUBDEV_FORMAT_ACTIVE :
471 V4L2_SUBDEV_FORMAT_TRY;
472 routing.num_routes = 0;
473 routing.routes = (__u64)routes;
474
475 if (!optarg)
476 break;
477
478 r = (v4l2_subdev_route *)routing.routes;
479 ref = end = strdup(optarg);
480 while ((tok = strsep(&end, ",")) != NULL) {
481 if (sscanf(tok, "%u/%u -> %u/%u [%u]",
482 &r->sink_pad, &r->sink_stream,
483 &r->source_pad, &r->source_stream,
484 &flags) != 5) {
485 free(ref);
486 fprintf(stderr, "Invalid route information specified\n");
487 subdev_usage();
488 std::exit(EXIT_FAILURE);
489 }
490
491 if (flags & ~(V4L2_SUBDEV_ROUTE_FL_ACTIVE)) {
492 fprintf(stderr, "Invalid route flags specified: %#x\n", flags);
493 subdev_usage();
494 std::exit(EXIT_FAILURE);
495 }
496
497 r->flags = flags;
498
499 r++;
500 routing.num_routes++;
501 }
502 free(ref);
503 break;
504 }
505 default:
506 break;
507 }
508 }
509
is_rgb_or_hsv_code(__u32 code)510 static bool is_rgb_or_hsv_code(__u32 code)
511 {
512 return code < 0x2000 || code >= 0x3000;
513 }
514
print_framefmt(const struct v4l2_mbus_framefmt & fmt)515 static void print_framefmt(const struct v4l2_mbus_framefmt &fmt)
516 {
517 __u32 colsp = fmt.colorspace;
518 __u32 ycbcr_enc = fmt.ycbcr_enc;
519 unsigned int i;
520
521 for (i = 0; mbus_names[i].name; i++)
522 if (mbus_names[i].code == fmt.code)
523 break;
524 printf("\tWidth/Height : %u/%u\n", fmt.width, fmt.height);
525 printf("\tMediabus Code : ");
526 if (mbus_names[i].name)
527 printf("0x%04x (MEDIA_BUS_FMT_%s)\n",
528 fmt.code, mbus_names[i].name);
529 else
530 printf("0x%04x\n", fmt.code);
531
532 printf("\tField : %s\n", field2s(fmt.field).c_str());
533 printf("\tColorspace : %s\n", colorspace2s(colsp).c_str());
534 printf("\tTransfer Function : %s", xfer_func2s(fmt.xfer_func).c_str());
535 if (fmt.xfer_func == V4L2_XFER_FUNC_DEFAULT)
536 printf(" (maps to %s)",
537 xfer_func2s(V4L2_MAP_XFER_FUNC_DEFAULT(colsp)).c_str());
538 printf("\n");
539 printf("\tYCbCr/HSV Encoding: %s", ycbcr_enc2s(ycbcr_enc).c_str());
540 if (ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
541 ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(colsp);
542 printf(" (maps to %s)", ycbcr_enc2s(ycbcr_enc).c_str());
543 }
544 printf("\n");
545 printf("\tQuantization : %s", quantization2s(fmt.quantization).c_str());
546 if (fmt.quantization == V4L2_QUANTIZATION_DEFAULT)
547 printf(" (maps to %s)",
548 quantization2s(V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb_or_hsv_code(fmt.code),
549 colsp, ycbcr_enc)).c_str());
550 printf("\n");
551 }
552
print_subdev_selection(const struct v4l2_subdev_selection & sel)553 static void print_subdev_selection(const struct v4l2_subdev_selection &sel)
554 {
555 printf("Selection: %s, Left %d, Top %d, Width %d, Height %d, Flags: %s\n",
556 seltarget2s(sel.target).c_str(),
557 sel.r.left, sel.r.top, sel.r.width, sel.r.height,
558 selflags2s(sel.flags).c_str());
559 }
560
subdev_set(cv4l_fd & _fd)561 void subdev_set(cv4l_fd &_fd)
562 {
563 int fd = _fd.g_fd();
564
565 if (options[OptSetSubDevFormat] || options[OptTrySubDevFormat]) {
566 struct v4l2_subdev_format fmt;
567
568 if (!_fd.has_streams() && set_fmt_stream) {
569 printf("Streams API not supported.\n");
570 return;
571 }
572
573 memset(&fmt, 0, sizeof(fmt));
574 fmt.pad = set_fmt_pad;
575 fmt.stream = set_fmt_stream;
576 fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
577
578 if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0) {
579 int ret;
580
581 if (set_fmt & FmtWidth)
582 fmt.format.width = ffmt.width;
583 if (set_fmt & FmtHeight)
584 fmt.format.height = ffmt.height;
585 if (set_fmt & FmtPixelFormat)
586 fmt.format.code = ffmt.code;
587 if (set_fmt & FmtField)
588 fmt.format.field = ffmt.field;
589 if (set_fmt & FmtColorspace) {
590 fmt.format.colorspace = ffmt.colorspace;
591 fmt.format.flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
592 }
593 if (set_fmt & FmtXferFunc) {
594 fmt.format.xfer_func = ffmt.xfer_func;
595 fmt.format.flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
596 }
597 if (set_fmt & FmtYCbCr) {
598 fmt.format.ycbcr_enc = ffmt.ycbcr_enc;
599 fmt.format.flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
600 }
601 if (set_fmt & FmtQuantization) {
602 fmt.format.quantization = ffmt.quantization;
603 fmt.format.flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
604 }
605
606 if (options[OptSetSubDevFormat])
607 printf("Note: --set-subdev-fmt is only for testing.\n"
608 "Normally media-ctl is used to configure the video pipeline.\n");
609 else
610 fmt.which = V4L2_SUBDEV_FORMAT_TRY;
611
612 printf("ioctl: VIDIOC_SUBDEV_S_FMT (pad=%u,stream=%u)\n", fmt.pad, fmt.stream);
613 ret = doioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt);
614 if (ret == 0 && (verbose || !options[OptSetSubDevFormat]))
615 print_framefmt(fmt.format);
616 }
617 }
618 if (options[OptSetSubDevSelection] || options[OptTrySubDevSelection]) {
619 struct v4l2_subdev_selection sel;
620
621 if (!_fd.has_streams() && vsel.stream) {
622 printf("Streams API not supported.\n");
623 return;
624 }
625
626 memset(&sel, 0, sizeof(sel));
627 sel.pad = vsel.pad;
628 sel.stream = vsel.stream;
629 sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
630 sel.target = vsel.target;
631
632 if (doioctl(fd, VIDIOC_SUBDEV_G_SELECTION, &sel) == 0) {
633 if (set_selection & SelectionWidth)
634 sel.r.width = vsel.r.width;
635 if (set_selection & SelectionHeight)
636 sel.r.height = vsel.r.height;
637 if (set_selection & SelectionLeft)
638 sel.r.left = vsel.r.left;
639 if (set_selection & SelectionTop)
640 sel.r.top = vsel.r.top;
641 sel.flags = (set_selection & SelectionFlags) ? vsel.flags : 0;
642
643 if (options[OptSetSubDevSelection])
644 printf("Note: --set-subdev-selection is only for testing.\n"
645 "Normally media-ctl is used to configure the video pipeline.\n");
646 else
647 sel.which = V4L2_SUBDEV_FORMAT_TRY;
648
649 printf("ioctl: VIDIOC_SUBDEV_S_SELECTION (pad=%u,stream=%u)\n", sel.pad, sel.stream);
650 int ret = doioctl(fd, VIDIOC_SUBDEV_S_SELECTION, &sel);
651 if (ret == 0 && (verbose || !options[OptSetSubDevSelection]))
652 print_subdev_selection(sel);
653 }
654 }
655 if (options[OptSetSubDevFPS]) {
656 struct v4l2_subdev_frame_interval fival;
657
658 if (!_fd.has_streams() && set_fps_stream) {
659 printf("Streams API not supported.\n");
660 return;
661 }
662
663 memset(&fival, 0, sizeof(fival));
664 fival.pad = set_fps_pad;
665 fival.stream = set_fps_stream;
666 if (_fd.has_ival_uses_which())
667 fival.which = V4L2_SUBDEV_FORMAT_ACTIVE;
668
669 if (set_fps <= 0) {
670 fprintf(stderr, "invalid fps %f\n", set_fps);
671 subdev_usage();
672 std::exit(EXIT_FAILURE);
673 }
674 fival.interval.numerator = 1000;
675 fival.interval.denominator = static_cast<uint32_t>(set_fps * fival.interval.numerator);
676 printf("Note: --set-subdev-fps is only for testing.\n"
677 "Normally media-ctl is used to configure the video pipeline.\n");
678 printf("ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL (pad=%u,stream=%u)\n", fival.pad, fival.stream);
679 if (doioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) == 0) {
680 if (!fival.interval.denominator || !fival.interval.numerator)
681 printf("\tFrames per second: invalid (%d/%d)\n",
682 fival.interval.denominator, fival.interval.numerator);
683 else
684 printf("\tFrames per second: %.3f (%d/%d)\n",
685 (1.0 * fival.interval.denominator) / fival.interval.numerator,
686 fival.interval.denominator, fival.interval.numerator);
687 }
688 }
689 if (options[OptSetRouting] || options[OptTryRouting]) {
690 if (!_fd.has_streams()) {
691 printf("Streams API not supported.\n");
692 return;
693 }
694
695 if (doioctl(fd, VIDIOC_SUBDEV_S_ROUTING, &routing) == 0)
696 printf("Routing set\n");
697 }
698 }
699
700 struct flag_name {
701 __u32 flag;
702 const char *name;
703 };
704
print_flags(const struct flag_name * flag_names,unsigned int num_entries,__u32 flags)705 static void print_flags(const struct flag_name *flag_names, unsigned int num_entries, __u32 flags)
706 {
707 bool first = true;
708 unsigned int i;
709
710 for (i = 0; i < num_entries; i++) {
711 if (!(flags & flag_names[i].flag))
712 continue;
713 if (!first)
714 printf(",");
715 printf("%s", flag_names[i].name);
716 flags &= ~flag_names[i].flag;
717 first = false;
718 }
719
720 if (flags) {
721 if (!first)
722 printf(",");
723 printf("0x%x", flags);
724 }
725 }
726
print_routes(const struct v4l2_subdev_routing * r)727 static void print_routes(const struct v4l2_subdev_routing *r)
728 {
729 unsigned int i;
730 struct v4l2_subdev_route *routes = (struct v4l2_subdev_route *)r->routes;
731
732 static const struct flag_name route_flags[] = {
733 { V4L2_SUBDEV_ROUTE_FL_ACTIVE, "ACTIVE" },
734 };
735
736 for (i = 0; i < r->num_routes; i++) {
737 printf("%u/%u -> %u/%u [",
738 routes[i].sink_pad, routes[i].sink_stream,
739 routes[i].source_pad, routes[i].source_stream);
740 print_flags(route_flags, ARRAY_SIZE(route_flags), routes[i].flags);
741 printf("]\n");
742 }
743 }
744
subdev_get(cv4l_fd & _fd)745 void subdev_get(cv4l_fd &_fd)
746 {
747 int fd = _fd.g_fd();
748
749 if (options[OptGetSubDevFormat]) {
750 struct v4l2_subdev_format fmt;
751
752 if (!_fd.has_streams() && get_fmt_stream) {
753 printf("Streams API not supported.\n");
754 return;
755 }
756
757 memset(&fmt, 0, sizeof(fmt));
758 fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
759 fmt.pad = get_fmt_pad;
760 fmt.stream = get_fmt_stream;
761
762 printf("ioctl: VIDIOC_SUBDEV_G_FMT (pad=%u,stream=%u)\n", fmt.pad, fmt.stream);
763 if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0)
764 print_framefmt(fmt.format);
765 }
766
767 if (options[OptGetSubDevSelection]) {
768 struct v4l2_subdev_selection sel;
769 unsigned idx = 0;
770
771 if (!_fd.has_streams() && get_sel_stream) {
772 printf("Streams API not supported.\n");
773 return;
774 }
775
776 memset(&sel, 0, sizeof(sel));
777 sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
778 sel.pad = get_sel_pad;
779 sel.stream = get_sel_stream;
780
781 printf("ioctl: VIDIOC_SUBDEV_G_SELECTION (pad=%u,stream=%u)\n", sel.pad, sel.stream);
782 if (options[OptAll] || get_sel_target == -1) {
783 while (valid_seltarget_at_idx(idx)) {
784 sel.target = seltarget_at_idx(idx);
785 if (test_ioctl(fd, VIDIOC_SUBDEV_G_SELECTION, &sel) == 0)
786 print_subdev_selection(sel);
787 idx++;
788 }
789 } else {
790 sel.target = get_sel_target;
791 if (doioctl(fd, VIDIOC_SUBDEV_G_SELECTION, &sel) == 0)
792 print_subdev_selection(sel);
793 }
794 }
795 if (options[OptGetSubDevFPS]) {
796 struct v4l2_subdev_frame_interval fival;
797
798 if (!_fd.has_streams() && get_fps_stream) {
799 printf("Streams API not supported.\n");
800 return;
801 }
802
803 memset(&fival, 0, sizeof(fival));
804 fival.pad = get_fps_pad;
805 fival.stream = get_fps_stream;
806 if (_fd.has_ival_uses_which())
807 fival.which = V4L2_SUBDEV_FORMAT_ACTIVE;
808
809 printf("ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL (pad=%u,stream=%u)\n", fival.pad, fival.stream);
810 if (doioctl(fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) == 0) {
811 if (!fival.interval.denominator || !fival.interval.numerator)
812 printf("\tFrames per second: invalid (%d/%d)\n",
813 fival.interval.denominator, fival.interval.numerator);
814 else
815 printf("\tFrames per second: %.3f (%d/%d)\n",
816 (1.0 * fival.interval.denominator) / fival.interval.numerator,
817 fival.interval.denominator, fival.interval.numerator);
818 }
819 }
820
821 if (options[OptGetRouting]) {
822 if (!_fd.has_streams()) {
823 printf("Streams API not supported.\n");
824 return;
825 }
826
827 memset(&routing, 0, sizeof(routing));
828 memset(routes, 0, sizeof(routes[0]) * NUM_ROUTES_MAX);
829 routing.which = V4L2_SUBDEV_FORMAT_ACTIVE;
830 routing.num_routes = NUM_ROUTES_MAX;
831 routing.routes = (__u64)routes;
832
833 if (doioctl(fd, VIDIOC_SUBDEV_G_ROUTING, &routing) == 0)
834 print_routes(&routing);
835 }
836 }
837
print_mbus_code(__u32 code)838 static void print_mbus_code(__u32 code)
839 {
840 unsigned int i;
841
842 for (i = 0; mbus_names[i].name; i++)
843 if (mbus_names[i].code == code)
844 break;
845 if (mbus_names[i].name)
846 printf("\t0x%04x: MEDIA_BUS_FMT_%s",
847 mbus_names[i].code, mbus_names[i].name);
848 else
849 printf("\t0x%04x", code);
850 }
851
print_mbus_codes(int fd,__u32 pad,__u32 stream)852 static void print_mbus_codes(int fd, __u32 pad, __u32 stream)
853 {
854 struct v4l2_subdev_mbus_code_enum mbus_code = {};
855
856 mbus_code.pad = pad;
857 mbus_code.stream = stream;
858 mbus_code.which = V4L2_SUBDEV_FORMAT_TRY;
859
860 for (;;) {
861 int ret = test_ioctl(fd, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_code);
862
863 if (ret)
864 break;
865 print_mbus_code(mbus_code.code);
866 if (mbus_code.flags) {
867 bool is_hsv = mbus_code.code == MEDIA_BUS_FMT_AHSV8888_1X32;
868
869 printf(", %s", mbus2s(mbus_code.flags, is_hsv).c_str());
870 }
871 printf("\n");
872 mbus_code.index++;
873 }
874 }
875
fract2sec(const struct v4l2_fract & f)876 static std::string fract2sec(const struct v4l2_fract &f)
877 {
878 char buf[100];
879
880 sprintf(buf, "%.3f", (1.0 * f.numerator) / f.denominator);
881 return buf;
882 }
883
fract2fps(const struct v4l2_fract & f)884 static std::string fract2fps(const struct v4l2_fract &f)
885 {
886 char buf[100];
887
888 sprintf(buf, "%.3f", (1.0 * f.denominator) / f.numerator);
889 return buf;
890 }
891
print_frmsize(const struct v4l2_subdev_frame_size_enum & frmsize)892 static void print_frmsize(const struct v4l2_subdev_frame_size_enum &frmsize)
893 {
894 printf("\tSize Range: %dx%d - %dx%d\n",
895 frmsize.min_width, frmsize.min_height,
896 frmsize.max_width, frmsize.max_height);
897 }
898
print_frmival(const struct v4l2_subdev_frame_interval_enum & frmival)899 static void print_frmival(const struct v4l2_subdev_frame_interval_enum &frmival)
900 {
901 printf("\tInterval: %ss (%s fps)\n", fract2sec(frmival.interval).c_str(),
902 fract2fps(frmival.interval).c_str());
903 }
904
subdev_list(cv4l_fd & _fd)905 void subdev_list(cv4l_fd &_fd)
906 {
907 int fd = _fd.g_fd();
908
909 if (options[OptListSubDevMBusCodes]) {
910 if (!_fd.has_streams() && list_mbus_codes_stream) {
911 printf("Streams API not supported.\n");
912 return;
913 }
914
915 printf("ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE (pad=%u,stream=%u)\n",
916 list_mbus_codes_pad, list_mbus_codes_stream);
917 print_mbus_codes(fd, list_mbus_codes_pad, list_mbus_codes_stream);
918 }
919 if (options[OptListSubDevFrameSizes]) {
920 if (!_fd.has_streams() && frmsize.stream) {
921 printf("Streams API not supported.\n");
922 return;
923 }
924
925 printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE (pad=%u,stream=%u)\n",
926 frmsize.pad, frmsize.stream);
927 frmsize.index = 0;
928 frmsize.which = V4L2_SUBDEV_FORMAT_TRY;
929 while (test_ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &frmsize) >= 0) {
930 print_frmsize(frmsize);
931 frmsize.index++;
932 }
933 }
934 if (options[OptListSubDevFrameIntervals]) {
935 if (!_fd.has_streams() && frmival.stream) {
936 printf("Streams API not supported.\n");
937 return;
938 }
939
940 printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL (pad=%u,stream=%u)\n",
941 frmival.pad, frmival.stream);
942 frmival.index = 0;
943 frmival.which = V4L2_SUBDEV_FORMAT_TRY;
944 while (test_ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &frmival) >= 0) {
945 print_frmival(frmival);
946 frmival.index++;
947 }
948 }
949 }
950