• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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