35c34a269d987dbaa31fa84604a01870c5c9545e
[media-ctl.git] / src / main.c
1 /*
2  * Media controller test application
3  *
4  * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  */
19
20 #include <sys/mman.h>
21 #include <sys/ioctl.h>
22 #include <sys/time.h>
23 #include <sys/stat.h>
24
25 #include <ctype.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include <linux/types.h>
34 #include <linux/media.h>
35 #include <linux/v4l2-mediabus.h>
36 #include <linux/v4l2-subdev.h>
37 #include <linux/videodev2.h>
38
39 #include "media.h"
40 #include "options.h"
41 #include "subdev.h"
42 #include "tools.h"
43
44 /* -----------------------------------------------------------------------------
45  * Printing
46  */
47
48 static struct {
49         const char *name;
50         enum v4l2_mbus_pixelcode code;
51 } mbus_formats[] = {
52         { "Y8", V4L2_MBUS_FMT_Y8_1X8},
53         { "YUYV", V4L2_MBUS_FMT_YUYV8_1X16 },
54         { "UYVY", V4L2_MBUS_FMT_UYVY8_1X16 },
55         { "SBGGR8", V4L2_MBUS_FMT_SBGGR8_1X8 },
56         { "SGBRG8", V4L2_MBUS_FMT_SGBRG8_1X8 },
57         { "SGRBG8", V4L2_MBUS_FMT_SGRBG8_1X8 },
58         { "SRGGB8", V4L2_MBUS_FMT_SRGGB8_1X8 },
59         { "SBGGR10", V4L2_MBUS_FMT_SBGGR10_1X10 },
60         { "SGBRG10", V4L2_MBUS_FMT_SGBRG10_1X10 },
61         { "SGRBG10", V4L2_MBUS_FMT_SGRBG10_1X10 },
62         { "SRGGB10", V4L2_MBUS_FMT_SRGGB10_1X10 },
63         { "SBGGR10_DPCM8", V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 },
64         { "SGBRG10_DPCM8", V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 },
65         { "SGRBG10_DPCM8", V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 },
66         { "SRGGB10_DPCM8", V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 },
67         { "SBGGR12", V4L2_MBUS_FMT_SBGGR12_1X12 },
68         { "SGBRG12", V4L2_MBUS_FMT_SGBRG12_1X12 },
69         { "SGRBG12", V4L2_MBUS_FMT_SGRBG12_1X12 },
70         { "SRGGB12", V4L2_MBUS_FMT_SRGGB12_1X12 },
71 };
72
73 static const char *pixelcode_to_string(enum v4l2_mbus_pixelcode code)
74 {
75         unsigned int i;
76
77         for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) {
78                 if (mbus_formats[i].code == code)
79                         return mbus_formats[i].name;
80         }
81
82         return "unknown";
83 }
84
85 static enum v4l2_mbus_pixelcode string_to_pixelcode(const char *string,
86                                              unsigned int length)
87 {
88         unsigned int i;
89
90         for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) {
91                 if (strncmp(mbus_formats[i].name, string, length) == 0)
92                         break;
93         }
94
95         if (i == ARRAY_SIZE(mbus_formats))
96                 return (enum v4l2_mbus_pixelcode)-1;
97
98         return mbus_formats[i].code;
99 }
100
101 static void v4l2_subdev_print_format(struct media_entity *entity,
102         unsigned int pad, enum v4l2_subdev_format_whence which)
103 {
104         struct v4l2_mbus_framefmt format;
105         struct v4l2_rect rect;
106         int ret;
107
108         ret = v4l2_subdev_get_format(entity, &format, pad, which);
109         if (ret != 0)
110                 return;
111
112         printf("[%s %ux%u", pixelcode_to_string(format.code),
113                format.width, format.height);
114
115         ret = v4l2_subdev_get_crop(entity, &rect, pad, which);
116         if (ret == 0)
117                 printf(" (%u,%u)/%ux%u", rect.left, rect.top,
118                        rect.width, rect.height);
119         printf("]");
120 }
121
122 static const char *media_entity_type_to_string(unsigned type)
123 {
124         static const struct {
125                 __u32 type;
126                 const char *name;
127         } types[] = {
128                 { MEDIA_ENT_T_DEVNODE, "Node" },
129                 { MEDIA_ENT_T_V4L2_SUBDEV, "V4L2 subdev" },
130         };
131
132         unsigned int i;
133
134         type &= MEDIA_ENT_TYPE_MASK;
135
136         for (i = 0; i < ARRAY_SIZE(types); i++) {
137                 if (types[i].type == type)
138                         return types[i].name;
139         }
140
141         return "Unknown";
142 }
143
144 static const char *media_entity_subtype_to_string(unsigned type)
145 {
146         static const char *node_types[] = {
147                 "Unknown",
148                 "V4L",
149                 "FB",
150                 "ALSA",
151                 "DVB",
152         };
153         static const char *subdev_types[] = {
154                 "Unknown",
155                 "Sensor",
156                 "Flash",
157                 "Lens",
158         };
159
160         unsigned int subtype = type & MEDIA_ENT_SUBTYPE_MASK;
161
162         switch (type & MEDIA_ENT_TYPE_MASK) {
163         case MEDIA_ENT_T_DEVNODE:
164                 if (subtype >= ARRAY_SIZE(node_types))
165                         subtype = 0;
166                 return node_types[subtype];
167
168         case MEDIA_ENT_T_V4L2_SUBDEV:
169                 if (subtype >= ARRAY_SIZE(subdev_types))
170                         subtype = 0;
171                 return subdev_types[subtype];
172         default:
173                 return node_types[0];
174         }
175 }
176
177 static const char *media_pad_type_to_string(unsigned flag)
178 {
179         static const struct {
180                 __u32 flag;
181                 const char *name;
182         } flags[] = {
183                 { MEDIA_PAD_FL_SINK, "Input" },
184                 { MEDIA_PAD_FL_SOURCE, "Output" },
185         };
186
187         unsigned int i;
188
189         for (i = 0; i < ARRAY_SIZE(flags); i++) {
190                 if (flags[i].flag & flag)
191                         return flags[i].name;
192         }
193
194         return "Unknown";
195 }
196
197 static void media_print_topology_dot(struct media_device *media)
198 {
199         unsigned int i, j;
200
201         printf("digraph board {\n");
202         printf("\trankdir=TB\n");
203
204         for (i = 0; i < media->entities_count; ++i) {
205                 struct media_entity *entity = &media->entities[i];
206                 unsigned int npads;
207
208                 switch (media_entity_type(entity)) {
209                 case MEDIA_ENT_T_DEVNODE:
210                         printf("\tn%08x [label=\"%s\\n%s\", shape=box, style=filled, "
211                                "fillcolor=yellow]\n",
212                                entity->info.id, entity->info.name, entity->devname);
213                         break;
214
215                 case MEDIA_ENT_T_V4L2_SUBDEV:
216                         printf("\tn%08x [label=\"{{", entity->info.id);
217
218                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
219                                 if (!(entity->pads[j].flags & MEDIA_PAD_FL_SINK))
220                                         continue;
221
222                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
223                                 npads++;
224                         }
225
226                         printf("} | %s", entity->info.name);
227                         if (entity->devname)
228                                 printf("\\n%s", entity->devname);
229                         printf(" | {");
230
231                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
232                                 if (!(entity->pads[j].flags & MEDIA_PAD_FL_SOURCE))
233                                         continue;
234
235                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
236                                 npads++;
237                         }
238
239                         printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n");
240                         break;
241
242                 default:
243                         continue;
244                 }
245
246                 for (j = 0; j < entity->num_links; j++) {
247                         struct media_link *link = &entity->links[j];
248
249                         if (link->source->entity != entity)
250                                 continue;
251
252                         printf("\tn%08x", link->source->entity->info.id);
253                         if (media_entity_type(link->source->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
254                                 printf(":port%u", link->source->index);
255                         printf(" -> ");
256                         printf("n%08x", link->sink->entity->info.id);
257                         if (media_entity_type(link->sink->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
258                                 printf(":port%u", link->sink->index);
259
260                         if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
261                                 printf(" [style=bold]");
262                         else if (!(link->flags & MEDIA_LNK_FL_ENABLED))
263                                 printf(" [style=dashed]");
264                         printf("\n");
265                 }
266         }
267
268         printf("}\n");
269 }
270
271 static void media_print_topology_text(struct media_device *media)
272 {
273         unsigned int i, j, k;
274         unsigned int padding;
275
276         printf("Device topology\n");
277
278         for (i = 0; i < media->entities_count; ++i) {
279                 struct media_entity *entity = &media->entities[i];
280
281                 padding = printf("- entity %u: ", entity->info.id);
282                 printf("%s (%u pad%s, %u link%s)\n", entity->info.name,
283                         entity->info.pads, entity->info.pads > 1 ? "s" : "",
284                         entity->num_links, entity->num_links > 1 ? "s" : "");
285                 printf("%*ctype %s subtype %s\n", padding, ' ',
286                         media_entity_type_to_string(entity->info.type),
287                         media_entity_subtype_to_string(entity->info.type));
288                 if (entity->devname[0])
289                         printf("%*cdevice node name %s\n", padding, ' ', entity->devname);
290
291                 for (j = 0; j < entity->info.pads; j++) {
292                         struct media_pad *pad = &entity->pads[j];
293
294                         printf("\tpad%u: %s ", j, media_pad_type_to_string(pad->flags));
295
296                         if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV)
297                                 v4l2_subdev_print_format(entity, j, V4L2_SUBDEV_FORMAT_ACTIVE);
298
299                         printf("\n");
300
301                         for (k = 0; k < entity->num_links; k++) {
302                                 struct media_link *link = &entity->links[k];
303                                 struct media_pad *source = link->source;
304                                 struct media_pad *sink = link->sink;
305
306                                 if (source->entity == entity && source->index == j)
307                                         printf("\t\t-> '%s':pad%u [",
308                                                 sink->entity->info.name, sink->index);
309                                 else if (sink->entity == entity && sink->index == j)
310                                         printf("\t\t<- '%s':pad%u [",
311                                                 source->entity->info.name, source->index);
312                                 else
313                                         continue;
314
315                                 if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
316                                         printf("IMMUTABLE,");
317                                 if (link->flags & MEDIA_LNK_FL_ENABLED)
318                                         printf("ACTIVE");
319
320                                 printf("]\n");
321                         }
322                 }
323                 printf("\n");
324         }
325 }
326
327 void media_print_topology(struct media_device *media, int dot)
328 {
329         if (dot)
330                 media_print_topology_dot(media);
331         else
332                 media_print_topology_text(media);
333 }
334
335 /* -----------------------------------------------------------------------------
336  * Links setup
337  */
338
339 static struct media_pad *parse_pad(struct media_device *media, const char *p, char **endp)
340 {
341         unsigned int entity_id, pad;
342         struct media_entity *entity;
343         char *end;
344
345         for (; isspace(*p); ++p);
346
347         if (*p == '"') {
348                 for (end = (char *)p + 1; *end && *end != '"'; ++end);
349                 if (*end != '"')
350                         return NULL;
351
352                 entity = media_get_entity_by_name(media, p + 1, end - p - 1);
353                 if (entity == NULL)
354                         return NULL;
355
356                 ++end;
357         } else {
358                 entity_id = strtoul(p, &end, 10);
359                 entity = media_get_entity_by_id(media, entity_id);
360                 if (entity == NULL)
361                         return NULL;
362         }
363         for (; isspace(*end); ++end);
364
365         if (*end != ':')
366                 return NULL;
367         for (p = end + 1; isspace(*p); ++p);
368
369         pad = strtoul(p, &end, 10);
370         for (p = end; isspace(*p); ++p);
371
372         if (pad >= entity->info.pads)
373                 return NULL;
374
375         for (p = end; isspace(*p); ++p);
376         if (endp)
377                 *endp = (char *)p;
378
379         return &entity->pads[pad];
380 }
381
382 static struct media_link *parse_link(struct media_device *media, const char *p, char **endp)
383 {
384         struct media_link *link;
385         struct media_pad *source;
386         struct media_pad *sink;
387         unsigned int i;
388         char *end;
389
390         source = parse_pad(media, p, &end);
391         if (source == NULL)
392                 return NULL;
393
394         if (end[0] != '-' || end[1] != '>')
395                 return NULL;
396         p = end + 2;
397
398         sink = parse_pad(media, p, &end);
399         if (sink == NULL)
400                 return NULL;
401
402         *endp = end;
403
404         for (i = 0; i < source->entity->num_links; i++) {
405                 link = &source->entity->links[i];
406
407                 if (link->source == source && link->sink == sink)
408                         return link;
409         }
410
411         return NULL;
412 }
413
414 static int setup_link(struct media_device *media, const char *p, char **endp)
415 {
416         struct media_link *link;
417         __u32 flags;
418         char *end;
419
420         link = parse_link(media, p, &end);
421         if (link == NULL) {
422                 printf("Unable to parse link\n");
423                 return -EINVAL;
424         }
425
426         p = end;
427         if (*p++ != '[') {
428                 printf("Unable to parse link flags\n");
429                 return -EINVAL;
430         }
431
432         flags = strtoul(p, &end, 10);
433         for (p = end; isspace(*p); p++);
434         if (*p++ != ']') {
435                 printf("Unable to parse link flags\n");
436                 return -EINVAL;
437         }
438
439         for (; isspace(*p); p++);
440         *endp = (char *)p;
441
442         printf("Setting up link %u:%u -> %u:%u [%u]\n",
443                 link->source->entity->info.id, link->source->index,
444                 link->sink->entity->info.id, link->sink->index,
445                 flags);
446
447         return media_setup_link(media, link->source, link->sink, flags);
448 }
449
450 static int setup_links(struct media_device *media, const char *p)
451 {
452         char *end;
453         int ret;
454
455         do {
456                 ret = setup_link(media, p, &end);
457                 if (ret < 0)
458                         return ret;
459
460                 p = end + 1;
461         } while (*end == ',');
462
463         return *end ? -EINVAL : 0;
464 }
465
466 /* -----------------------------------------------------------------------------
467  * Formats setup
468  */
469
470 static int parse_format(struct v4l2_mbus_framefmt *format, const char *p, char **endp)
471 {
472         enum v4l2_mbus_pixelcode code;
473         unsigned int width, height;
474         char *end;
475
476         for (; isspace(*p); ++p);
477         for (end = (char *)p; !isspace(*end) && *end != '\0'; ++end);
478
479         code = string_to_pixelcode(p, end - p);
480         if (code == (enum v4l2_mbus_pixelcode)-1)
481                 return -EINVAL;
482
483         for (p = end; isspace(*p); ++p);
484         width = strtoul(p, &end, 10);
485         if (*end != 'x')
486                 return -EINVAL;
487
488         p = end + 1;
489         height = strtoul(p, &end, 10);
490         *endp = end;
491
492         memset(format, 0, sizeof(*format));
493         format->width = width;
494         format->height = height;
495         format->code = code;
496
497         return 0;
498 }
499
500 static int parse_crop(struct v4l2_rect *crop, const char *p, char **endp)
501 {
502         char *end;
503
504         if (*p++ != '(')
505                 return -EINVAL;
506
507         crop->left = strtoul(p, &end, 10);
508         if (*end != ',')
509                 return -EINVAL;
510
511         p = end + 1;
512         crop->top = strtoul(p, &end, 10);
513         if (*end++ != ')')
514                 return -EINVAL;
515         if (*end != '/')
516                 return -EINVAL;
517
518         p = end + 1;
519         crop->width = strtoul(p, &end, 10);
520         if (*end != 'x')
521                 return -EINVAL;
522
523         p = end + 1;
524         crop->height = strtoul(p, &end, 10);
525         *endp = end;
526
527         return 0;
528 }
529
530 static int parse_frame_interval(struct v4l2_fract *interval, const char *p, char **endp)
531 {
532         char *end;
533
534         for (; isspace(*p); ++p);
535
536         interval->numerator = strtoul(p, &end, 10);
537
538         for (p = end; isspace(*p); ++p);
539         if (*p++ != '/')
540                 return -EINVAL;
541
542         for (; isspace(*p); ++p);
543         interval->denominator = strtoul(p, &end, 10);
544
545         *endp = end;
546         return 0;
547 }
548
549 static struct media_pad *parse_pad_format(struct media_device *media,
550         struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop,
551         struct v4l2_fract *interval, const char *p, char **endp)
552 {
553         struct media_pad *pad;
554         char *end;
555         int ret;
556
557         for (; isspace(*p); ++p);
558
559         pad = parse_pad(media, p, &end);
560         if (pad == NULL)
561                 return NULL;
562
563         for (p = end; isspace(*p); ++p);
564         if (*p++ != '[')
565                 return NULL;
566
567         for (; isspace(*p); ++p);
568
569         if (isalnum(*p)) {
570                 ret = parse_format(format, p, &end);
571                 if (ret < 0)
572                         return NULL;
573
574                 for (p = end; isspace(*p); p++);
575         }
576
577         if (*p == '(') {
578                 ret = parse_crop(crop, p, &end);
579                 if (ret < 0)
580                         return NULL;
581
582                 for (p = end; isspace(*p); p++);
583         }
584
585         if (*p == '@') {
586                 ret = parse_frame_interval(interval, ++p, &end);
587                 if (ret < 0)
588                         return NULL;
589
590                 for (p = end; isspace(*p); p++);
591         }
592
593         if (*p != ']')
594                 return NULL;
595
596         *endp = (char *)p + 1;
597         return pad;
598 }
599
600 static int set_format(struct media_pad *pad, struct v4l2_mbus_framefmt *format)
601 {
602         int ret;
603
604         if (format->width == 0 || format->height == 0)
605                 return 0;
606
607         printf("Setting up format %s %ux%u on pad %s/%u\n",
608                 pixelcode_to_string(format->code), format->width, format->height,
609                 pad->entity->info.name, pad->index);
610
611         ret = v4l2_subdev_set_format(pad->entity, format, pad->index,
612                                      V4L2_SUBDEV_FORMAT_ACTIVE);
613         if (ret < 0) {
614                 printf("Unable to set format: %s (%d)\n", strerror(-ret), ret);
615                 return ret;
616         }
617
618         printf("Format set: %s %ux%u\n",
619                 pixelcode_to_string(format->code), format->width, format->height);
620
621         return 0;
622 }
623
624 static int set_crop(struct media_pad *pad, struct v4l2_rect *crop)
625 {
626         int ret;
627
628         if (crop->left == -1 || crop->top == -1)
629                 return 0;
630
631         printf("Setting up crop rectangle (%u,%u)/%ux%u on pad %s/%u\n",
632                 crop->left, crop->top, crop->width, crop->height,
633                 pad->entity->info.name, pad->index);
634
635         ret = v4l2_subdev_set_crop(pad->entity, crop, pad->index,
636                                    V4L2_SUBDEV_FORMAT_ACTIVE);
637         if (ret < 0) {
638                 printf("Unable to set crop rectangle: %s (%d)\n", strerror(-ret), ret);
639                 return ret;
640         }
641
642         printf("Crop rectangle set: (%u,%u)/%ux%u\n",
643                 crop->left, crop->top, crop->width, crop->height);
644
645         return 0;
646 }
647
648 static int set_frame_interval(struct media_entity *entity, struct v4l2_fract *interval)
649 {
650         int ret;
651
652         if (interval->numerator == 0)
653                 return 0;
654
655         printf("Setting up frame interval %u/%u on entity %s\n",
656                 interval->numerator, interval->denominator, entity->info.name);
657
658         ret = v4l2_subdev_set_frame_interval(entity, interval);
659         if (ret < 0) {
660                 printf("Unable to set frame interval: %s (%d)", strerror(-ret), ret);
661                 return ret;
662         }
663
664         printf("Frame interval set: %u/%u\n",
665                 interval->numerator, interval->denominator);
666
667         return 0;
668 }
669
670
671 static int setup_format(struct media_device *media, const char *p, char **endp)
672 {
673         struct v4l2_mbus_framefmt format = { 0, 0, 0 };
674         struct media_pad *pad;
675         struct v4l2_rect crop = { -1, -1, -1, -1 };
676         struct v4l2_fract interval = { 0, 0 };
677         unsigned int i;
678         char *end;
679         int ret;
680
681         pad = parse_pad_format(media, &format, &crop, &interval, p, &end);
682         if (pad == NULL) {
683                 printf("Unable to parse format\n");
684                 return -EINVAL;
685         }
686
687         if (pad->flags & MEDIA_PAD_FL_SOURCE) {
688                 ret = set_crop(pad, &crop);
689                 if (ret < 0)
690                         return ret;
691         }
692
693         ret = set_format(pad, &format);
694         if (ret < 0)
695                 return ret;
696
697         if (pad->flags & MEDIA_PAD_FL_SINK) {
698                 ret = set_crop(pad, &crop);
699                 if (ret < 0)
700                         return ret;
701         }
702
703         ret = set_frame_interval(pad->entity, &interval);
704         if (ret < 0)
705                 return ret;
706
707
708         /* If the pad is an output pad, automatically set the same format on
709          * the remote subdev input pads, if any.
710          */
711         if (pad->flags & MEDIA_PAD_FL_SOURCE) {
712                 for (i = 0; i < pad->entity->num_links; ++i) {
713                         struct media_link *link = &pad->entity->links[i];
714                         struct v4l2_mbus_framefmt remote_format;
715
716                         if (!(link->flags & MEDIA_LNK_FL_ENABLED))
717                                 continue;
718
719                         if (link->source == pad &&
720                             link->sink->entity->info.type == MEDIA_ENT_T_V4L2_SUBDEV) {
721                                 remote_format = format;
722                                 set_format(link->sink, &remote_format);
723                         }
724                 }
725         }
726
727         *endp = end;
728         return 0;
729 }
730
731 static int setup_formats(struct media_device *media, const char *p)
732 {
733         char *end;
734         int ret;
735
736         do {
737                 ret = setup_format(media, p, &end);
738                 if (ret < 0)
739                         return ret;
740
741                 p = end + 1;
742         } while (*end == ',');
743
744         return *end ? -EINVAL : 0;
745 }
746
747 int main(int argc, char **argv)
748 {
749         struct media_device *media;
750         int ret = -1;
751
752         if (parse_cmdline(argc, argv))
753                 return EXIT_FAILURE;
754
755         /* Open the media device and enumerate entities, pads and links. */
756         media = media_open(media_opts.devname, media_opts.verbose);
757         if (media == NULL)
758                 goto out;
759
760         if (media_opts.entity) {
761                 struct media_entity *entity;
762
763                 entity = media_get_entity_by_name(media, media_opts.entity,
764                                                   strlen(media_opts.entity));
765                 if (entity == NULL) {
766                         printf("Entity '%s' not found\n", media_opts.entity);
767                         goto out;
768                 }
769
770                 printf("%s\n", entity->devname);
771         }
772
773         if (media_opts.pad) {
774                 struct media_pad *pad;
775
776                 pad = parse_pad(media, media_opts.pad, NULL);
777                 if (pad == NULL) {
778                         printf("Pad '%s' not found\n", media_opts.pad);
779                         goto out;
780                 }
781
782                 v4l2_subdev_print_format(pad->entity, pad->index,
783                                          V4L2_SUBDEV_FORMAT_ACTIVE);
784                 printf("\n");
785         }
786
787         if (media_opts.print || media_opts.print_dot) {
788                 media_print_topology(media, media_opts.print_dot);
789                 printf("\n");
790         }
791
792         if (media_opts.reset) {
793                 printf("Resetting all links to inactive\n");
794                 media_reset_links(media);
795         }
796
797         if (media_opts.links)
798                 setup_links(media, media_opts.links);
799
800         if (media_opts.formats)
801                 setup_formats(media, media_opts.formats);
802
803         if (media_opts.interactive) {
804                 while (1) {
805                         char buffer[32];
806                         char *end;
807
808                         printf("Enter a link to modify or enter to stop\n");
809                         if (fgets(buffer, sizeof buffer, stdin) == NULL)
810                                 break;
811
812                         if (buffer[0] == '\n')
813                                 break;
814
815                         setup_link(media, buffer, &end);
816                 }
817         }
818
819         ret = 0;
820
821 out:
822         if (media)
823                 media_close(media);
824
825         return ret ? EXIT_FAILURE : EXIT_SUCCESS;
826 }
827