1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <inttypes.h>
6 #include <getopt.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <sys/ioctl.h>
13 #include <sys/time.h>
14 #include <dirent.h>
15 #include <math.h>
16 #include <cstdlib>
17 
18 #include "v4l2-ctl.h"
19 
20 struct mbus_name {
21 	const char *name;
22 	uint32_t code;
23 };
24 
25 static struct mbus_name mbus_names[] = {
26 	{ "Fixed", MEDIA_BUS_FMT_FIXED },
27 #include "media-bus-format-names.h"
28 	{ NULL, 0 }
29 };
30 
31 /* selection specified */
32 #define SelectionWidth		(1L<<0)
33 #define SelectionHeight		(1L<<1)
34 #define SelectionLeft		(1L<<2)
35 #define SelectionTop 		(1L<<3)
36 #define SelectionFlags 		(1L<<4)
37 
38 static uint32_t list_mbus_codes_pad;
39 static uint32_t get_fmt_pad;
40 static uint32_t get_sel_pad;
41 static uint32_t get_fps_pad;
42 static int get_sel_target = -1;
43 static unsigned int set_selection;
44 static struct v4l2_subdev_selection vsel;
45 static unsigned int set_fmt;
46 static uint32_t set_fmt_pad;
47 static struct v4l2_mbus_framefmt ffmt;
48 static struct v4l2_subdev_frame_size_enum frmsize;
49 static struct v4l2_subdev_frame_interval_enum frmival;
50 static uint32_t set_fps_pad;
51 static double set_fps;
52 
subdev_usage()53 void subdev_usage()
54 {
55 	printf("\nSub-Device options:\n"
56 	       "  --list-subdev-mbus-codes <pad>\n"
57 	       "                      display supported mediabus codes for this pad (0 is default)\n"
58 	       "                      [VIDIOC_SUBDEV_ENUM_MBUS_CODE]\n"
59 	       "  --list-subdev-framesizes pad=<pad>,code=<code>\n"
60 	       "                     list supported framesizes for this pad and code\n"
61 	       "                     [VIDIOC_SUBDEV_ENUM_FRAME_SIZE]\n"
62 	       "                     <code> is the value of the mediabus code\n"
63 	       "  --list-subdev-frameintervals pad=<pad>,width=<w>,height=<h>,code=<code>\n"
64 	       "                     list supported frame intervals for this pad and code and\n"
65 	       "                     the given width and height [VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL]\n"
66 	       "                     <code> is the value of the mediabus code\n"
67 	       "  --get-subdev-fmt [<pad>]\n"
68 	       "     		     query the frame format for the given pad [VIDIOC_SUBDEV_G_FMT]\n"
69 	       "  --get-subdev-selection pad=<pad>,target=<target>\n"
70 	       "                     query the frame selection rectangle [VIDIOC_SUBDEV_G_SELECTION]\n"
71 	       "                     See --set-subdev-selection command for the valid <target> values.\n"
72 	       "  --get-subdev-fps [<pad>]\n"
73 	       "                     query the frame rate [VIDIOC_SUBDEV_G_FRAME_INTERVAL]\n"
74 	       "  --set-subdev-fmt   (for testing only, otherwise use media-ctl)\n"
75 	       "  --try-subdev-fmt pad=<pad>,width=<w>,height=<h>,code=<code>,field=<f>,colorspace=<c>,\n"
76 	       "                   xfer=<xf>,ycbcr=<y>,hsv=<hsv>,quantization=<q>\n"
77 	       "                     set the frame format [VIDIOC_SUBDEV_S_FMT]\n"
78 	       "                     <code> is the value of the mediabus code\n"
79 	       "                     <f> can be one of the following field layouts:\n"
80 	       "                       any, none, top, bottom, interlaced, seq_tb, seq_bt,\n"
81 	       "                       alternate, interlaced_tb, interlaced_bt\n"
82 	       "                     <c> can be one of the following colorspaces:\n"
83 	       "                       smpte170m, smpte240m, rec709, 470m, 470bg, jpeg, srgb,\n"
84 	       "                       oprgb, bt2020, dcip3\n"
85 	       "                     <xf> can be one of the following transfer functions:\n"
86 	       "                       default, 709, srgb, oprgb, smpte240m, smpte2084, dcip3, none\n"
87 	       "                     <y> can be one of the following Y'CbCr encodings:\n"
88 	       "                       default, 601, 709, xv601, xv709, bt2020, bt2020c, smpte240m\n"
89 	       "                     <hsv> can be one of the following HSV encodings:\n"
90 	       "                       default, 180, 256\n"
91 	       "                     <q> can be one of the following quantization methods:\n"
92 	       "                       default, full-range, lim-range\n"
93 	       "  --set-subdev-selection (for testing only, otherwise use media-ctl)\n"
94 	       "  --try-subdev-selection pad=<pad>,target=<target>,flags=<flags>,\n"
95 	       "                         top=<x>,left=<y>,width=<w>,height=<h>\n"
96 	       "                     set the video capture selection rectangle [VIDIOC_SUBDEV_S_SELECTION]\n"
97 	       "                     target=crop|crop_bounds|crop_default|compose|compose_bounds|\n"
98 	       "                            compose_default|compose_padded|native_size\n"
99 	       "                     flags=le|ge|keep-config\n"
100 	       "  --set-subdev-fps pad=<pad>,fps=<fps> (for testing only, otherwise use media-ctl)\n"
101 	       "                     set the frame rate [VIDIOC_SUBDEV_S_FRAME_INTERVAL]\n"
102 	       );
103 }
104 
subdev_cmd(int ch,char * optarg)105 void subdev_cmd(int ch, char *optarg)
106 {
107 	char *value, *subs;
108 
109 	switch (ch) {
110 	case OptListSubDevMBusCodes:
111 		if (optarg)
112 			list_mbus_codes_pad = strtoul(optarg, 0L, 0);
113 		break;
114 	case OptListSubDevFrameSizes:
115 		subs = optarg;
116 		while (*subs != '\0') {
117 			static const char *const subopts[] = {
118 				"pad",
119 				"code",
120 				NULL
121 			};
122 
123 			switch (parse_subopt(&subs, subopts, &value)) {
124 			case 0:
125 				frmsize.pad = strtoul(value, 0L, 0);
126 				break;
127 			case 1:
128 				frmsize.code = strtoul(value, 0L, 0);
129 				break;
130 			default:
131 				subdev_usage();
132 				std::exit(EXIT_FAILURE);
133 			}
134 		}
135 		break;
136 	case OptListSubDevFrameIntervals:
137 		subs = optarg;
138 		while (*subs != '\0') {
139 			static const char *const subopts[] = {
140 				"pad",
141 				"code",
142 				"width",
143 				"height",
144 				NULL
145 			};
146 
147 			switch (parse_subopt(&subs, subopts, &value)) {
148 			case 0:
149 				frmival.pad = strtoul(value, 0L, 0);
150 				break;
151 			case 1:
152 				frmival.code = strtoul(value, 0L, 0);
153 				break;
154 			case 2:
155 				frmival.width = strtoul(value, 0L, 0);
156 				break;
157 			case 3:
158 				frmival.height = strtoul(value, 0L, 0);
159 				break;
160 			default:
161 				subdev_usage();
162 				std::exit(EXIT_FAILURE);
163 			}
164 		}
165 		break;
166 	case OptGetSubDevFormat:
167 		if (optarg)
168 			get_fmt_pad = strtoul(optarg, 0L, 0);
169 		break;
170 	case OptGetSubDevSelection:
171 		subs = optarg;
172 		while (*subs != '\0') {
173 			static const char *const subopts[] = {
174 				"pad",
175 				"target",
176 				NULL
177 			};
178 			unsigned int target;
179 
180 			switch (parse_subopt(&subs, subopts, &value)) {
181 			case 0:
182 				get_sel_pad = strtoul(value, 0L, 0);
183 				break;
184 			case 1:
185 				if (parse_selection_target(value, target)) {
186 					fprintf(stderr, "Unknown selection target\n");
187 					subdev_usage();
188 					std::exit(EXIT_FAILURE);
189 				}
190 				get_sel_target = target;
191 				break;
192 			default:
193 				subdev_usage();
194 				std::exit(EXIT_FAILURE);
195 			}
196 		}
197 		break;
198 	case OptGetSubDevFPS:
199 		if (optarg)
200 			get_fps_pad = strtoul(optarg, 0L, 0);
201 		break;
202 	case OptSetSubDevFormat:
203 	case OptTrySubDevFormat:
204 		ffmt.field = V4L2_FIELD_ANY;
205 		subs = optarg;
206 		while (*subs != '\0') {
207 			static const char *const subopts[] = {
208 				"width",
209 				"height",
210 				"code",
211 				"field",
212 				"colorspace",
213 				"ycbcr",
214 				"hsv",
215 				"quantization",
216 				"xfer",
217 				"pad",
218 				NULL
219 			};
220 
221 			switch (parse_subopt(&subs, subopts, &value)) {
222 			case 0:
223 				ffmt.width = strtoul(value, 0L, 0);
224 				set_fmt |= FmtWidth;
225 				break;
226 			case 1:
227 				ffmt.height = strtoul(value, 0L, 0);
228 				set_fmt |= FmtHeight;
229 				break;
230 			case 2:
231 				ffmt.code = strtoul(value, 0L, 0);
232 				set_fmt |= FmtPixelFormat;
233 				break;
234 			case 3:
235 				ffmt.field = parse_field(value);
236 				set_fmt |= FmtField;
237 				break;
238 			case 4:
239 				ffmt.colorspace = parse_colorspace(value);
240 				if (ffmt.colorspace)
241 					set_fmt |= FmtColorspace;
242 				else
243 					fprintf(stderr, "unknown colorspace %s\n", value);
244 				break;
245 			case 5:
246 				ffmt.ycbcr_enc = parse_ycbcr(value);
247 				set_fmt |= FmtYCbCr;
248 				break;
249 			case 6:
250 				ffmt.ycbcr_enc = parse_hsv(value);
251 				set_fmt |= FmtYCbCr;
252 				break;
253 			case 7:
254 				ffmt.quantization = parse_quantization(value);
255 				set_fmt |= FmtQuantization;
256 				break;
257 			case 8:
258 				ffmt.xfer_func = parse_xfer_func(value);
259 				set_fmt |= FmtXferFunc;
260 				break;
261 			case 9:
262 				set_fmt_pad = strtoul(value, 0L, 0);
263 				break;
264 			default:
265 				fprintf(stderr, "Unknown option\n");
266 				subdev_usage();
267 				std::exit(EXIT_FAILURE);
268 			}
269 		}
270 		break;
271 	case OptSetSubDevSelection:
272 	case OptTrySubDevSelection:
273 		subs = optarg;
274 
275 		while (*subs != '\0') {
276 			static const char *const subopts[] = {
277 				"target",
278 				"flags",
279 				"left",
280 				"top",
281 				"width",
282 				"height",
283 				"pad",
284 				NULL
285 			};
286 
287 			switch (parse_subopt(&subs, subopts, &value)) {
288 			case 0:
289 				if (parse_selection_target(value, vsel.target)) {
290 					fprintf(stderr, "Unknown selection target\n");
291 					subdev_usage();
292 					std::exit(EXIT_FAILURE);
293 				}
294 				break;
295 			case 1:
296 				vsel.flags = parse_selection_flags(value);
297 				set_selection |= SelectionFlags;
298 				break;
299 			case 2:
300 				vsel.r.left = strtol(value, 0L, 0);
301 				set_selection |= SelectionLeft;
302 				break;
303 			case 3:
304 				vsel.r.top = strtol(value, 0L, 0);
305 				set_selection |= SelectionTop;
306 				break;
307 			case 4:
308 				vsel.r.width = strtoul(value, 0L, 0);
309 				set_selection |= SelectionWidth;
310 				break;
311 			case 5:
312 				vsel.r.height = strtoul(value, 0L, 0);
313 				set_selection |= SelectionHeight;
314 				break;
315 			case 6:
316 				vsel.pad = strtoul(value, 0L, 0);
317 				break;
318 			default:
319 				fprintf(stderr, "Unknown option\n");
320 				subdev_usage();
321 				std::exit(EXIT_FAILURE);
322 			}
323 		}
324 		break;
325 	case OptSetSubDevFPS:
326 		subs = optarg;
327 
328 		while (*subs != '\0') {
329 			static const char *const subopts[] = {
330 				"pad",
331 				"fps",
332 				NULL
333 			};
334 
335 			switch (parse_subopt(&subs, subopts, &value)) {
336 			case 0:
337 				set_fps_pad = strtoul(value, 0L, 0);
338 				break;
339 			case 1:
340 				set_fps = strtod(value, NULL);
341 				break;
342 			default:
343 				fprintf(stderr, "Unknown option\n");
344 				subdev_usage();
345 				std::exit(EXIT_FAILURE);
346 			}
347 		}
348 		break;
349 	default:
350 		break;
351 	}
352 }
353 
is_rgb_or_hsv_code(uint32_t code)354 static bool is_rgb_or_hsv_code(uint32_t code)
355 {
356 	return code < 0x2000 || code >= 0x3000;
357 }
358 
print_framefmt(const struct v4l2_mbus_framefmt & fmt)359 static void print_framefmt(const struct v4l2_mbus_framefmt &fmt)
360 {
361 	uint32_t colsp = fmt.colorspace;
362 	uint32_t ycbcr_enc = fmt.ycbcr_enc;
363 	unsigned int i;
364 
365 	for (i = 0; mbus_names[i].name; i++)
366 		if (mbus_names[i].code == fmt.code)
367 			break;
368 	printf("\tWidth/Height      : %u/%u\n", fmt.width, fmt.height);
369 	printf("\tMediabus Code     : ");
370 	if (mbus_names[i].name)
371 		printf("0x%04x (MEDIA_BUS_FMT_%s)\n",
372 		       fmt.code, mbus_names[i].name);
373 	else
374 		printf("0x%04x\n", fmt.code);
375 
376 	printf("\tField             : %s\n", field2s(fmt.field).c_str());
377 	printf("\tColorspace        : %s\n", colorspace2s(colsp).c_str());
378 	printf("\tTransfer Function : %s", xfer_func2s(fmt.xfer_func).c_str());
379 	if (fmt.xfer_func == V4L2_XFER_FUNC_DEFAULT)
380 		printf(" (maps to %s)",
381 		       xfer_func2s(V4L2_MAP_XFER_FUNC_DEFAULT(colsp)).c_str());
382 	printf("\n");
383 	printf("\tYCbCr/HSV Encoding: %s", ycbcr_enc2s(ycbcr_enc).c_str());
384 	if (ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
385 		ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(colsp);
386 		printf(" (maps to %s)", ycbcr_enc2s(ycbcr_enc).c_str());
387 	}
388 	printf("\n");
389 	printf("\tQuantization      : %s", quantization2s(fmt.quantization).c_str());
390 	if (fmt.quantization == V4L2_QUANTIZATION_DEFAULT)
391 		printf(" (maps to %s)",
392 		       quantization2s(V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb_or_hsv_code(fmt.code),
393 								    colsp, ycbcr_enc)).c_str());
394 	printf("\n");
395 }
396 
print_subdev_selection(const struct v4l2_subdev_selection & sel)397 static void print_subdev_selection(const struct v4l2_subdev_selection &sel)
398 {
399 	printf("Selection: %s, Left %d, Top %d, Width %d, Height %d, Flags: %s\n",
400 			seltarget2s(sel.target).c_str(),
401 			sel.r.left, sel.r.top, sel.r.width, sel.r.height,
402 			selflags2s(sel.flags).c_str());
403 }
404 
subdev_set(cv4l_fd & _fd)405 void subdev_set(cv4l_fd &_fd)
406 {
407 	int fd = _fd.g_fd();
408 
409 	if (options[OptSetSubDevFormat] || options[OptTrySubDevFormat]) {
410 		struct v4l2_subdev_format fmt;
411 
412 		memset(&fmt, 0, sizeof(fmt));
413 		fmt.pad = set_fmt_pad;
414 		fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
415 
416 		if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0) {
417 			int ret;
418 
419 			if (set_fmt & FmtWidth)
420 				fmt.format.width = ffmt.width;
421 			if (set_fmt & FmtHeight)
422 				fmt.format.height = ffmt.height;
423 			if (set_fmt & FmtPixelFormat)
424 				fmt.format.code = ffmt.code;
425 			if (set_fmt & FmtField)
426 				fmt.format.field = ffmt.field;
427 			if (set_fmt & FmtColorspace)
428 				fmt.format.colorspace = ffmt.colorspace;
429 			if (set_fmt & FmtXferFunc)
430 				fmt.format.xfer_func = ffmt.xfer_func;
431 			if (set_fmt & FmtYCbCr)
432 				fmt.format.ycbcr_enc = ffmt.ycbcr_enc;
433 			if (set_fmt & FmtQuantization)
434 				fmt.format.quantization = ffmt.quantization;
435 
436 			if (options[OptSetSubDevFormat])
437 				printf("Note: --set-subdev-fmt is only for testing.\n"
438 				       "Normally media-ctl is used to configure the video pipeline.\n");
439 			else
440 				fmt.which = V4L2_SUBDEV_FORMAT_TRY;
441 
442 			printf("ioctl: VIDIOC_SUBDEV_S_FMT (pad=%u)\n", fmt.pad);
443 			ret = doioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt);
444 			if (ret == 0 && (verbose || !options[OptSetSubDevFormat]))
445 				print_framefmt(fmt.format);
446 		}
447 	}
448 	if (options[OptSetSubDevSelection] || options[OptTrySubDevSelection]) {
449 		struct v4l2_subdev_selection sel;
450 
451 		memset(&sel, 0, sizeof(sel));
452 		sel.pad = vsel.pad;
453 		sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
454 		sel.target = vsel.target;
455 
456 		if (doioctl(fd, VIDIOC_SUBDEV_G_SELECTION, &sel) == 0) {
457 			if (set_selection & SelectionWidth)
458 				sel.r.width = vsel.r.width;
459 			if (set_selection & SelectionHeight)
460 				sel.r.height = vsel.r.height;
461 			if (set_selection & SelectionLeft)
462 				sel.r.left = vsel.r.left;
463 			if (set_selection & SelectionTop)
464 				sel.r.top = vsel.r.top;
465 			sel.flags = (set_selection & SelectionFlags) ? vsel.flags : 0;
466 
467 			if (options[OptSetSubDevSelection])
468 				printf("Note: --set-subdev-selection is only for testing.\n"
469 				       "Normally media-ctl is used to configure the video pipeline.\n");
470 			else
471 				sel.which = V4L2_SUBDEV_FORMAT_TRY;
472 
473 			printf("ioctl: VIDIOC_SUBDEV_S_SELECTION (pad=%u)\n", sel.pad);
474 			int ret = doioctl(fd, VIDIOC_SUBDEV_S_SELECTION, &sel);
475 			if (ret == 0 && (verbose || !options[OptSetSubDevSelection]))
476 				print_subdev_selection(sel);
477 		}
478 	}
479 	if (options[OptSetSubDevFPS]) {
480 		struct v4l2_subdev_frame_interval fival;
481 
482 		memset(&fival, 0, sizeof(fival));
483 		fival.pad = set_fps_pad;
484 
485 		if (set_fps <= 0) {
486 			fprintf(stderr, "invalid fps %f\n", set_fps);
487 			subdev_usage();
488 			std::exit(EXIT_FAILURE);
489 		}
490 		fival.interval.numerator = 1000;
491 		fival.interval.denominator = static_cast<uint32_t>(set_fps * fival.interval.numerator);
492 		printf("Note: --set-subdev-fps is only for testing.\n"
493 		       "Normally media-ctl is used to configure the video pipeline.\n");
494 		printf("ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL (pad=%u)\n", fival.pad);
495 		if (doioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) == 0) {
496 			if (!fival.interval.denominator || !fival.interval.numerator)
497 				printf("\tFrames per second: invalid (%d/%d)\n",
498 					fival.interval.denominator, fival.interval.numerator);
499 			else
500 				printf("\tFrames per second: %.3f (%d/%d)\n",
501 					(1.0 * fival.interval.denominator) / fival.interval.numerator,
502 					fival.interval.denominator, fival.interval.numerator);
503 		}
504 	}
505 }
506 
subdev_get(cv4l_fd & _fd)507 void subdev_get(cv4l_fd &_fd)
508 {
509 	int fd = _fd.g_fd();
510 
511 	if (options[OptGetSubDevFormat]) {
512 		struct v4l2_subdev_format fmt;
513 
514 		memset(&fmt, 0, sizeof(fmt));
515 		fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
516 		fmt.pad = get_fmt_pad;
517 
518 		printf("ioctl: VIDIOC_SUBDEV_G_FMT (pad=%u)\n", fmt.pad);
519 		if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0)
520 			print_framefmt(fmt.format);
521 	}
522 
523 	if (options[OptGetSubDevSelection]) {
524 		struct v4l2_subdev_selection sel;
525 		unsigned idx = 0;
526 
527 		memset(&sel, 0, sizeof(sel));
528 		sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
529 		sel.pad = get_sel_pad;
530 
531 		printf("ioctl: VIDIOC_SUBDEV_G_SELECTION (pad=%u)\n", sel.pad);
532 		if (options[OptAll] || get_sel_target == -1) {
533 			while (valid_seltarget_at_idx(idx)) {
534 				sel.target = seltarget_at_idx(idx);
535 				if (test_ioctl(fd, VIDIOC_SUBDEV_G_SELECTION, &sel) == 0)
536 					print_subdev_selection(sel);
537 				idx++;
538 			}
539 		} else {
540 			sel.target = get_sel_target;
541 			if (doioctl(fd, VIDIOC_SUBDEV_G_SELECTION, &sel) == 0)
542 				print_subdev_selection(sel);
543 		}
544 	}
545 	if (options[OptGetSubDevFPS]) {
546 		struct v4l2_subdev_frame_interval fival;
547 
548 		memset(&fival, 0, sizeof(fival));
549 		fival.pad = get_fps_pad;
550 
551 		printf("ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL (pad=%u)\n", fival.pad);
552 		if (doioctl(fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) == 0) {
553 			if (!fival.interval.denominator || !fival.interval.numerator)
554 				printf("\tFrames per second: invalid (%d/%d)\n",
555 					fival.interval.denominator, fival.interval.numerator);
556 			else
557 				printf("\tFrames per second: %.3f (%d/%d)\n",
558 					(1.0 * fival.interval.denominator) / fival.interval.numerator,
559 					fival.interval.denominator, fival.interval.numerator);
560 		}
561 	}
562 }
563 
print_mbus_code(uint32_t code)564 static void print_mbus_code(uint32_t code)
565 {
566 	unsigned int i;
567 
568 	for (i = 0; mbus_names[i].name; i++)
569 		if (mbus_names[i].code == code)
570 			break;
571 	if (mbus_names[i].name)
572 		printf("\t0x%04x: MEDIA_BUS_FMT_%s\n",
573 		       mbus_names[i].code, mbus_names[i].name);
574 	else
575 		printf("\t0x%04x\n", code);
576 }
577 
print_mbus_codes(int fd,uint32_t pad)578 static void print_mbus_codes(int fd, uint32_t pad)
579 {
580 	struct v4l2_subdev_mbus_code_enum mbus_code;
581 
582 	memset(&mbus_code, 0, sizeof(mbus_code));
583 	mbus_code.pad = pad;
584 	mbus_code.which = V4L2_SUBDEV_FORMAT_TRY;
585 
586 	for (;;) {
587 		int ret = test_ioctl(fd, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_code);
588 
589 		if (ret)
590 			break;
591 		print_mbus_code(mbus_code.code);
592 		mbus_code.index++;
593 	}
594 }
595 
fract2sec(const struct v4l2_fract & f)596 static std::string fract2sec(const struct v4l2_fract &f)
597 {
598 	char buf[100];
599 
600 	sprintf(buf, "%.3f", (1.0 * f.numerator) / f.denominator);
601 	return buf;
602 }
603 
fract2fps(const struct v4l2_fract & f)604 static std::string fract2fps(const struct v4l2_fract &f)
605 {
606 	char buf[100];
607 
608 	sprintf(buf, "%.3f", (1.0 * f.denominator) / f.numerator);
609 	return buf;
610 }
611 
print_frmsize(const struct v4l2_subdev_frame_size_enum & frmsize)612 static void print_frmsize(const struct v4l2_subdev_frame_size_enum &frmsize)
613 {
614 	printf("\tSize Range: %dx%d - %dx%d\n",
615 	       frmsize.min_width, frmsize.min_height,
616 	       frmsize.max_width, frmsize.max_height);
617 }
618 
print_frmival(const struct v4l2_subdev_frame_interval_enum & frmival)619 static void print_frmival(const struct v4l2_subdev_frame_interval_enum &frmival)
620 {
621 	printf("\tInterval: %ss (%s fps)\n", fract2sec(frmival.interval).c_str(),
622 	       fract2fps(frmival.interval).c_str());
623 }
624 
subdev_list(cv4l_fd & _fd)625 void subdev_list(cv4l_fd &_fd)
626 {
627 	int fd = _fd.g_fd();
628 
629 	if (options[OptListSubDevMBusCodes]) {
630 		printf("ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE (pad=%u)\n",
631 		       list_mbus_codes_pad);
632 		print_mbus_codes(fd, list_mbus_codes_pad);
633 	}
634 	if (options[OptListSubDevFrameSizes]) {
635 		printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE (pad=%u)\n",
636 		       frmsize.pad);
637 		frmsize.index = 0;
638 		frmsize.which = V4L2_SUBDEV_FORMAT_TRY;
639 		while (test_ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &frmsize) >= 0) {
640 			print_frmsize(frmsize);
641 			frmsize.index++;
642 		}
643 	}
644 	if (options[OptListSubDevFrameIntervals]) {
645 		printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL (pad=%u)\n",
646 		       frmival.pad);
647 		frmival.index = 0;
648 		frmival.which = V4L2_SUBDEV_FORMAT_TRY;
649 		while (test_ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &frmival) >= 0) {
650 			print_frmival(frmival);
651 			frmival.index++;
652 		}
653 	}
654 }
655