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