1 /*
2  * Media controller test application
3  *
4  * Copyright (C) 2010-2014 Ideas on board SPRL
5  *
6  * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published
10  * by the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <sys/ioctl.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 
27 #include <ctype.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include <linux/media.h>
37 #include <linux/types.h>
38 #include <linux/v4l2-mediabus.h>
39 #include <linux/v4l2-subdev.h>
40 #include <linux/videodev2.h>
41 
42 #include "mediactl.h"
43 #include "options.h"
44 #include "tools.h"
45 #include "v4l2subdev.h"
46 
47 /* -----------------------------------------------------------------------------
48  * Printing
49  */
50 
51 struct flag_name {
52 	uint32_t flag;
53 	char *name;
54 };
55 
print_flags(const struct flag_name * flag_names,unsigned int num_entries,uint32_t flags)56 static void print_flags(const struct flag_name *flag_names, unsigned int num_entries, uint32_t flags)
57 {
58 	bool first = true;
59 	unsigned int i;
60 
61 	for (i = 0; i < num_entries; i++) {
62 		if (!(flags & flag_names[i].flag))
63 			continue;
64 		if (!first)
65 			printf(",");
66 		printf("%s", flag_names[i].name);
67 		flags &= ~flag_names[i].flag;
68 		first = false;
69 	}
70 
71 	if (flags) {
72 		if (!first)
73 			printf(",");
74 		printf("0x%x", flags);
75 	}
76 }
77 
v4l2_subdev_print_format(struct media_entity * entity,unsigned int pad,enum v4l2_subdev_format_whence which)78 static void v4l2_subdev_print_format(struct media_entity *entity,
79 	unsigned int pad, enum v4l2_subdev_format_whence which)
80 {
81 	struct v4l2_mbus_framefmt format;
82 	struct v4l2_fract interval = { 0, 0 };
83 	struct v4l2_rect rect;
84 	int ret;
85 
86 	ret = v4l2_subdev_get_format(entity, &format, pad, which);
87 	if (ret != 0)
88 		return;
89 
90 	ret = v4l2_subdev_get_frame_interval(entity, &interval, pad);
91 	if (ret != 0 && ret != -ENOTTY && ret != -EINVAL)
92 		return;
93 
94 	printf("\t\t[fmt:%s/%ux%u",
95 	       v4l2_subdev_pixelcode_to_string(format.code),
96 	       format.width, format.height);
97 
98 	if (interval.numerator || interval.denominator)
99 		printf("@%u/%u", interval.numerator, interval.denominator);
100 
101 	if (format.field)
102 		printf(" field:%s", v4l2_subdev_field_to_string(format.field));
103 
104 	if (format.colorspace) {
105 		printf(" colorspace:%s",
106 		       v4l2_subdev_colorspace_to_string(format.colorspace));
107 
108 		if (format.xfer_func)
109 			printf(" xfer:%s",
110 			       v4l2_subdev_xfer_func_to_string(format.xfer_func));
111 
112 		if (format.ycbcr_enc)
113 			printf(" ycbcr:%s",
114 			       v4l2_subdev_ycbcr_encoding_to_string(format.ycbcr_enc));
115 
116 		if (format.quantization)
117 			printf(" quantization:%s",
118 			       v4l2_subdev_quantization_to_string(format.quantization));
119 	}
120 
121 	ret = v4l2_subdev_get_selection(entity, &rect, pad,
122 					V4L2_SEL_TGT_CROP_BOUNDS,
123 					which);
124 	if (ret == 0)
125 		printf("\n\t\t crop.bounds:(%u,%u)/%ux%u", rect.left, rect.top,
126 		       rect.width, rect.height);
127 
128 	ret = v4l2_subdev_get_selection(entity, &rect, pad,
129 					V4L2_SEL_TGT_CROP,
130 					which);
131 	if (ret == 0)
132 		printf("\n\t\t crop:(%u,%u)/%ux%u", rect.left, rect.top,
133 		       rect.width, rect.height);
134 
135 	ret = v4l2_subdev_get_selection(entity, &rect, pad,
136 					V4L2_SEL_TGT_COMPOSE_BOUNDS,
137 					which);
138 	if (ret == 0)
139 		printf("\n\t\t compose.bounds:(%u,%u)/%ux%u",
140 		       rect.left, rect.top, rect.width, rect.height);
141 
142 	ret = v4l2_subdev_get_selection(entity, &rect, pad,
143 					V4L2_SEL_TGT_COMPOSE,
144 					which);
145 	if (ret == 0)
146 		printf("\n\t\t compose:(%u,%u)/%ux%u",
147 		       rect.left, rect.top, rect.width, rect.height);
148 
149 	printf("]\n");
150 }
151 
v4l2_dv_type_to_string(unsigned int type)152 static const char *v4l2_dv_type_to_string(unsigned int type)
153 {
154 	static const struct {
155 		uint32_t type;
156 		const char *name;
157 	} types[] = {
158 		{ V4L2_DV_BT_656_1120, "BT.656/1120" },
159 	};
160 
161 	static char unknown[20];
162 	unsigned int i;
163 
164 	for (i = 0; i < ARRAY_SIZE(types); i++) {
165 		if (types[i].type == type)
166 			return types[i].name;
167 	}
168 
169 	sprintf(unknown, "Unknown (%u)", type);
170 	return unknown;
171 }
172 
173 static const struct flag_name bt_standards[] = {
174 	{ V4L2_DV_BT_STD_CEA861, "CEA-861" },
175 	{ V4L2_DV_BT_STD_DMT, "DMT" },
176 	{ V4L2_DV_BT_STD_CVT, "CVT" },
177 	{ V4L2_DV_BT_STD_GTF, "GTF" },
178 	{ V4L2_DV_BT_STD_SDI, "SDI" },
179 };
180 
181 static const struct flag_name bt_capabilities[] = {
182 	{ V4L2_DV_BT_CAP_INTERLACED, "interlaced" },
183 	{ V4L2_DV_BT_CAP_PROGRESSIVE, "progressive" },
184 	{ V4L2_DV_BT_CAP_REDUCED_BLANKING, "reduced-blanking" },
185 	{ V4L2_DV_BT_CAP_CUSTOM, "custom" },
186 };
187 
188 static const struct flag_name bt_flags[] = {
189 	{ V4L2_DV_FL_REDUCED_BLANKING, "reduced-blanking" },
190 	{ V4L2_DV_FL_CAN_REDUCE_FPS, "can-reduce-fps" },
191 	{ V4L2_DV_FL_REDUCED_FPS, "reduced-fps" },
192 	{ V4L2_DV_FL_HALF_LINE, "half-line" },
193 	{ V4L2_DV_FL_IS_CE_VIDEO, "CE-video" },
194 	{ V4L2_DV_FL_FIRST_FIELD_EXTRA_LINE, "first-field-extra-line" },
195 	{ V4L2_DV_FL_HAS_PICTURE_ASPECT, "has-picture-aspect" },
196 	{ V4L2_DV_FL_HAS_CEA861_VIC, "has-cea861-vic" },
197 	{ V4L2_DV_FL_HAS_HDMI_VIC, "has-hdmi-vic" },
198 	{ V4L2_DV_FL_CAN_DETECT_REDUCED_FPS, "can-detect-reduced-fps" },
199 };
200 
v4l2_subdev_print_dv_timings(const struct v4l2_dv_timings * timings,const char * name)201 static void v4l2_subdev_print_dv_timings(const struct v4l2_dv_timings *timings,
202 					 const char *name)
203 {
204 	printf("\t\t[dv.%s:%s", name, v4l2_dv_type_to_string(timings->type));
205 
206 	switch (timings->type) {
207 	case V4L2_DV_BT_656_1120: {
208 		const struct v4l2_bt_timings *bt = &timings->bt;
209 		unsigned int htotal, vtotal;
210 
211 		htotal = V4L2_DV_BT_FRAME_WIDTH(bt);
212 		vtotal = V4L2_DV_BT_FRAME_HEIGHT(bt);
213 
214 		printf(" %ux%u%s%llu (%ux%u)",
215 		       bt->width, bt->height, bt->interlaced ? "i" : "p",
216 		       (htotal * vtotal) > 0 ? (bt->pixelclock / (htotal * vtotal)) : 0,
217 		       htotal, vtotal);
218 
219 		printf(" stds:");
220 		print_flags(bt_standards, ARRAY_SIZE(bt_standards),
221 			    bt->standards);
222 		printf(" flags:");
223 		print_flags(bt_flags, ARRAY_SIZE(bt_flags),
224 			    bt->flags);
225 
226 		break;
227 	}
228 	}
229 
230 	printf("]\n");
231 }
232 
v4l2_subdev_print_pad_dv(struct media_entity * entity,unsigned int pad,enum v4l2_subdev_format_whence which)233 static void v4l2_subdev_print_pad_dv(struct media_entity *entity,
234 	unsigned int pad, enum v4l2_subdev_format_whence which)
235 {
236 	struct v4l2_dv_timings_cap caps;
237 	int ret;
238 
239 	caps.pad = pad;
240 	ret = v4l2_subdev_get_dv_timings_caps(entity, &caps);
241 	if (ret != 0)
242 		return;
243 
244 	printf("\t\t[dv.caps:%s", v4l2_dv_type_to_string(caps.type));
245 
246 	switch (caps.type) {
247 	case V4L2_DV_BT_656_1120:
248 		printf(" min:%ux%u@%llu max:%ux%u@%llu",
249 		       caps.bt.min_width, caps.bt.min_height, caps.bt.min_pixelclock,
250 		       caps.bt.max_width, caps.bt.max_height, caps.bt.max_pixelclock);
251 
252 		printf(" stds:");
253 		print_flags(bt_standards, ARRAY_SIZE(bt_standards),
254 			    caps.bt.standards);
255 		printf(" caps:");
256 		print_flags(bt_capabilities, ARRAY_SIZE(bt_capabilities),
257 			    caps.bt.capabilities);
258 
259 		break;
260 	}
261 
262 	printf("]\n");
263 }
264 
v4l2_subdev_print_subdev_dv(struct media_entity * entity)265 static void v4l2_subdev_print_subdev_dv(struct media_entity *entity)
266 {
267 	struct v4l2_dv_timings timings;
268 	int ret;
269 
270 	ret = v4l2_subdev_query_dv_timings(entity, &timings);
271 	switch (ret) {
272 	case -ENOLINK:
273 		printf("\t\t[dv.query:no-link]\n");
274 		break;
275 	case -ENOLCK:
276 		printf("\t\t[dv.query:no-lock]\n");
277 		break;
278 	case -ERANGE:
279 		printf("\t\t[dv.query:out-of-range]\n");
280 		break;
281 	case 0:
282 		v4l2_subdev_print_dv_timings(&timings, "detect");
283 		break;
284 	default:
285 		return;
286 	}
287 
288 	ret = v4l2_subdev_get_dv_timings(entity, &timings);
289 	if (ret == 0)
290 		v4l2_subdev_print_dv_timings(&timings, "current");
291 }
292 
media_entity_type_to_string(unsigned type)293 static const char *media_entity_type_to_string(unsigned type)
294 {
295 	static const struct {
296 		uint32_t type;
297 		const char *name;
298 	} types[] = {
299 		{ MEDIA_ENT_T_DEVNODE, "Node" },
300 		{ MEDIA_ENT_T_V4L2_SUBDEV, "V4L2 subdev" },
301 	};
302 
303 	unsigned int i;
304 
305 	type &= MEDIA_ENT_TYPE_MASK;
306 
307 	for (i = 0; i < ARRAY_SIZE(types); i++) {
308 		if (types[i].type == type)
309 			return types[i].name;
310 	}
311 
312 	return "Unknown";
313 }
314 
media_entity_subtype_to_string(unsigned type)315 static const char *media_entity_subtype_to_string(unsigned type)
316 {
317 	static const char *node_types[] = {
318 		"Unknown",
319 		"V4L",
320 		"FB",
321 		"ALSA",
322 		"DVB",
323 	};
324 	static const char *subdev_types[] = {
325 		"Unknown",
326 		"Sensor",
327 		"Flash",
328 		"Lens",
329 		"Decoder",
330 		"Tuner",
331 	};
332 
333 	unsigned int subtype = type & MEDIA_ENT_SUBTYPE_MASK;
334 
335 	switch (type & MEDIA_ENT_TYPE_MASK) {
336 	case MEDIA_ENT_T_DEVNODE:
337 		if (subtype >= ARRAY_SIZE(node_types))
338 			subtype = 0;
339 		return node_types[subtype];
340 
341 	case MEDIA_ENT_T_V4L2_SUBDEV:
342 		if (subtype >= ARRAY_SIZE(subdev_types))
343 			subtype = 0;
344 		return subdev_types[subtype];
345 	default:
346 		return node_types[0];
347 	}
348 }
349 
media_pad_type_to_string(unsigned flag)350 static const char *media_pad_type_to_string(unsigned flag)
351 {
352 	static const struct {
353 		uint32_t flag;
354 		const char *name;
355 	} flags[] = {
356 		{ MEDIA_PAD_FL_SINK, "Sink" },
357 		{ MEDIA_PAD_FL_SOURCE, "Source" },
358 	};
359 
360 	unsigned int i;
361 
362 	for (i = 0; i < ARRAY_SIZE(flags); i++) {
363 		if (flags[i].flag & flag)
364 			return flags[i].name;
365 	}
366 
367 	return "Unknown";
368 }
369 
media_print_topology_dot(struct media_device * media)370 static void media_print_topology_dot(struct media_device *media)
371 {
372 	unsigned int nents = media_get_entities_count(media);
373 	unsigned int i, j;
374 
375 	printf("digraph board {\n");
376 	printf("\trankdir=TB\n");
377 
378 	for (i = 0; i < nents; ++i) {
379 		struct media_entity *entity = media_get_entity(media, i);
380 		const struct media_entity_desc *info = media_entity_get_info(entity);
381 		const char *devname = media_entity_get_devname(entity);
382 		unsigned int num_links = media_entity_get_links_count(entity);
383 		unsigned int npads;
384 
385 		if (!devname)
386 			devname = "";
387 
388 		switch (media_entity_type(entity)) {
389 		case MEDIA_ENT_T_DEVNODE:
390 			printf("\tn%08x [label=\"%s\\n%s\", shape=box, style=filled, "
391 			       "fillcolor=yellow]\n",
392 			       info->id, info->name, devname);
393 			break;
394 
395 		case MEDIA_ENT_T_V4L2_SUBDEV:
396 			printf("\tn%08x [label=\"{{", info->id);
397 
398 			for (j = 0, npads = 0; j < info->pads; ++j) {
399 				const struct media_pad *pad = media_entity_get_pad(entity, j);
400 
401 				if (!(pad->flags & MEDIA_PAD_FL_SINK))
402 					continue;
403 
404 				printf("%s<port%u> %u", npads ? " | " : "", j, j);
405 				npads++;
406 			}
407 
408 			printf("} | %s", info->name);
409 			if (devname)
410 				printf("\\n%s", devname);
411 			printf(" | {");
412 
413 			for (j = 0, npads = 0; j < info->pads; ++j) {
414 				const struct media_pad *pad = media_entity_get_pad(entity, j);
415 
416 				if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
417 					continue;
418 
419 				printf("%s<port%u> %u", npads ? " | " : "", j, j);
420 				npads++;
421 			}
422 
423 			printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n");
424 			break;
425 
426 		default:
427 			continue;
428 		}
429 
430 		for (j = 0; j < num_links; j++) {
431 			const struct media_link *link = media_entity_get_link(entity, j);
432 			const struct media_pad *source = link->source;
433 			const struct media_pad *sink = link->sink;
434 
435 			if (source->entity != entity)
436 				continue;
437 
438 			printf("\tn%08x", media_entity_get_info(source->entity)->id);
439 			if (media_entity_type(source->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
440 				printf(":port%u", source->index);
441 			printf(" -> ");
442 			printf("n%08x", media_entity_get_info(sink->entity)->id);
443 			if (media_entity_type(sink->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
444 				printf(":port%u", sink->index);
445 
446 			if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
447 				printf(" [style=bold]");
448 			else if (!(link->flags & MEDIA_LNK_FL_ENABLED))
449 				printf(" [style=dashed]");
450 			printf("\n");
451 		}
452 	}
453 
454 	printf("}\n");
455 }
456 
media_print_pad_text(struct media_entity * entity,const struct media_pad * pad)457 static void media_print_pad_text(struct media_entity *entity,
458 				 const struct media_pad *pad)
459 {
460 	if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
461 		return;
462 
463 	v4l2_subdev_print_format(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
464 	v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
465 
466 	if (pad->flags & MEDIA_PAD_FL_SOURCE)
467 		v4l2_subdev_print_subdev_dv(entity);
468 }
469 
media_print_topology_text_entity(struct media_device * media,struct media_entity * entity)470 static void media_print_topology_text_entity(struct media_device *media,
471 					     struct media_entity *entity)
472 {
473 	static const struct flag_name link_flags[] = {
474 		{ MEDIA_LNK_FL_ENABLED, "ENABLED" },
475 		{ MEDIA_LNK_FL_IMMUTABLE, "IMMUTABLE" },
476 		{ MEDIA_LNK_FL_DYNAMIC, "DYNAMIC" },
477 	};
478 	const struct media_entity_desc *info = media_entity_get_info(entity);
479 	const char *devname = media_entity_get_devname(entity);
480 	unsigned int num_links = media_entity_get_links_count(entity);
481 	unsigned int j, k;
482 	unsigned int padding;
483 
484 	padding = printf("- entity %u: ", info->id);
485 	printf("%s (%u pad%s, %u link%s)\n", info->name,
486 	       info->pads, info->pads > 1 ? "s" : "",
487 	       num_links, num_links > 1 ? "s" : "");
488 	printf("%*ctype %s subtype %s flags %x\n", padding, ' ',
489 	       media_entity_type_to_string(info->type),
490 	       media_entity_subtype_to_string(info->type),
491 	       info->flags);
492 	if (devname)
493 		printf("%*cdevice node name %s\n", padding, ' ', devname);
494 
495 	for (j = 0; j < info->pads; j++) {
496 		const struct media_pad *pad = media_entity_get_pad(entity, j);
497 
498 		printf("\tpad%u: %s\n", j, media_pad_type_to_string(pad->flags));
499 
500 		media_print_pad_text(entity, pad);
501 
502 		for (k = 0; k < num_links; k++) {
503 			const struct media_link *link = media_entity_get_link(entity, k);
504 			const struct media_pad *source = link->source;
505 			const struct media_pad *sink = link->sink;
506 
507 			if (source->entity == entity && source->index == j)
508 				printf("\t\t-> \"%s\":%u [",
509 				       media_entity_get_info(sink->entity)->name,
510 				       sink->index);
511 			else if (sink->entity == entity && sink->index == j)
512 				printf("\t\t<- \"%s\":%u [",
513 				       media_entity_get_info(source->entity)->name,
514 				       source->index);
515 			else
516 				continue;
517 
518 			print_flags(link_flags, ARRAY_SIZE(link_flags), link->flags);
519 
520 			printf("]\n");
521 		}
522 	}
523 	printf("\n");
524 }
525 
media_print_topology_text(struct media_device * media)526 static void media_print_topology_text(struct media_device *media)
527 {
528 	unsigned int nents = media_get_entities_count(media);
529 	unsigned int i;
530 
531 	printf("Device topology\n");
532 
533 	for (i = 0; i < nents; ++i)
534 		media_print_topology_text_entity(
535 			media, media_get_entity(media, i));
536 }
537 
main(int argc,char ** argv)538 int main(int argc, char **argv)
539 {
540 	struct media_device *media;
541 	struct media_entity *entity = NULL;
542 	int ret = -1;
543 
544 	if (parse_cmdline(argc, argv))
545 		return EXIT_FAILURE;
546 
547 	media = media_device_new(media_opts.devname);
548 	if (media == NULL) {
549 		printf("Failed to create media device\n");
550 		goto out;
551 	}
552 
553 	if (media_opts.verbose)
554 		media_debug_set_handler(media,
555 			(void (*)(void *, ...))fprintf, stdout);
556 
557 	/* Enumerate entities, pads and links. */
558 	ret = media_device_enumerate(media);
559 	if (ret < 0) {
560 		printf("Failed to enumerate %s (%d)\n", media_opts.devname, ret);
561 		goto out;
562 	}
563 
564 	if (media_opts.print) {
565 		const struct media_device_info *info = media_get_info(media);
566 
567 		printf("Media controller API version %u.%u.%u\n\n",
568 		       (info->media_version >> 16) & 0xff,
569 		       (info->media_version >> 8) & 0xff,
570 		       (info->media_version >> 0) & 0xff);
571 		printf("Media device information\n"
572 		       "------------------------\n"
573 		       "driver          %s\n"
574 		       "model           %s\n"
575 		       "serial          %s\n"
576 		       "bus info        %s\n"
577 		       "hw revision     0x%x\n"
578 		       "driver version  %u.%u.%u\n\n",
579 		       info->driver, info->model,
580 		       info->serial, info->bus_info,
581 		       info->hw_revision,
582 		       (info->driver_version >> 16) & 0xff,
583 		       (info->driver_version >> 8) & 0xff,
584 		       (info->driver_version >> 0) & 0xff);
585 	}
586 
587 	if (media_opts.entity) {
588 		entity = media_get_entity_by_name(media, media_opts.entity);
589 		if (entity == NULL) {
590 			printf("Entity '%s' not found\n", media_opts.entity);
591 			goto out;
592 		}
593 	}
594 
595 	if (media_opts.fmt_pad) {
596 		struct media_pad *pad;
597 
598 		pad = media_parse_pad(media, media_opts.fmt_pad, NULL);
599 		if (pad == NULL) {
600 			printf("Pad '%s' not found\n", media_opts.fmt_pad);
601 			goto out;
602 		}
603 
604 		v4l2_subdev_print_format(pad->entity, pad->index,
605 					 V4L2_SUBDEV_FORMAT_ACTIVE);
606 	}
607 
608 	if (media_opts.get_dv_pad) {
609 		struct media_pad *pad;
610 
611 		pad = media_parse_pad(media, media_opts.get_dv_pad, NULL);
612 		if (pad == NULL) {
613 			printf("Pad '%s' not found\n", media_opts.get_dv_pad);
614 			goto out;
615 		}
616 
617 		v4l2_subdev_print_subdev_dv(pad->entity);
618 	}
619 
620 	if (media_opts.dv_pad) {
621 		struct v4l2_dv_timings timings;
622 		struct media_pad *pad;
623 
624 		pad = media_parse_pad(media, media_opts.dv_pad, NULL);
625 		if (pad == NULL) {
626 			printf("Pad '%s' not found\n", media_opts.dv_pad);
627 			goto out;
628 		}
629 
630 		ret = v4l2_subdev_query_dv_timings(pad->entity, &timings);
631 		if (ret < 0) {
632 			printf("Failed to query DV timings: %s\n", strerror(-ret));
633 			goto out;
634 		}
635 
636 		ret = v4l2_subdev_set_dv_timings(pad->entity, &timings);
637 		if (ret < 0) {
638 			printf("Failed to set DV timings: %s\n", strerror(-ret));
639 			goto out;
640 		}
641 	}
642 
643 	if (media_opts.print_dot) {
644 		media_print_topology_dot(media);
645 	} else if (media_opts.print) {
646 		if (entity)
647 			media_print_topology_text_entity(media, entity);
648 		else
649 			media_print_topology_text(media);
650 	} else if (entity) {
651 		const char *devname;
652 
653 		devname = media_entity_get_devname(entity);
654 		if (devname)
655 			printf("%s\n", devname);
656 	}
657 
658 	if (media_opts.reset) {
659 		if (media_opts.verbose)
660 			printf("Resetting all links to inactive\n");
661 		ret = media_reset_links(media);
662 		if (ret) {
663 			printf("Unable to reset links: %s (%d)\n",
664 			       strerror(-ret), -ret);
665 			goto out;
666 		}
667 	}
668 
669 	if (media_opts.links) {
670 		ret = media_parse_setup_links(media, media_opts.links);
671 		if (ret) {
672 			printf("Unable to parse link: %s (%d)\n",
673 			       strerror(-ret), -ret);
674 			goto out;
675 		}
676 	}
677 
678 	if (media_opts.formats) {
679 		ret = v4l2_subdev_parse_setup_formats(media,
680 						      media_opts.formats);
681 		if (ret) {
682 			printf("Unable to setup formats: %s (%d)\n",
683 			       strerror(-ret), -ret);
684 			goto out;
685 		}
686 	}
687 
688 	if (media_opts.interactive) {
689 		while (1) {
690 			char buffer[32];
691 			char *end;
692 
693 			printf("Enter a link to modify or enter to stop\n");
694 			if (fgets(buffer, sizeof buffer, stdin) == NULL)
695 				break;
696 
697 			if (buffer[0] == '\n')
698 				break;
699 
700 			ret = media_parse_setup_link(media, buffer, &end);
701 			if (ret)
702 				printf("Unable to parse link: %s (%d)\n",
703 				       strerror(-ret), -ret);
704 		}
705 	}
706 
707 	ret = 0;
708 
709 out:
710 	if (media)
711 		media_device_unref(media);
712 
713 	return ret ? EXIT_FAILURE : EXIT_SUCCESS;
714 }
715 
716