4bc0d391828a3d81c0084886185a25eb07289820
[media-ctl.git] / 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_NODE, "Node" },
115                 { MEDIA_ENTITY_TYPE_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_NODE:
150                 if (subtype >= ARRAY_SIZE(node_types))
151                         subtype = 0;
152                 return node_types[subtype];
153
154         case MEDIA_ENTITY_TYPE_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_NODE:
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_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_entity_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_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_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_ACTIVE))
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->info.links, entity->info.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_entity_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_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_entity_link *link = &entity->links[k];
289
290                                 if (link->source->entity != entity ||
291                                     link->source->index != j)
292                                         continue;
293
294                                 printf("\t\t-> '%s':pad%u [",
295                                         link->sink->entity->info.name, link->sink->index);
296
297                                 if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
298                                         printf("IMMUTABLE,");
299                                 if (link->flags & MEDIA_LINK_FLAG_ACTIVE)
300                                         printf("ACTIVE");
301
302                                 printf("]\n");
303                         }
304                 }
305                 printf("\n");
306         }
307 }
308
309 void media_print_topology(struct media_device *media, int dot)
310 {
311         if (dot)
312                 media_print_topology_dot(media);
313         else
314                 media_print_topology_text(media);
315 }
316
317 /* -----------------------------------------------------------------------------
318  * Links setup
319  */
320
321 static struct media_entity_pad *parse_pad(struct media_device *media, const char *p, char **endp)
322 {
323         unsigned int entity_id, pad;
324         struct media_entity *entity;
325         char *end;
326
327         for (; isspace(*p); ++p);
328
329         if (*p == '"') {
330                 for (end = (char *)p + 1; *end && *end != '"'; ++end);
331                 if (*end != '"')
332                         return NULL;
333
334                 entity = media_get_entity_by_name(media, p + 1, end - p - 1);
335                 if (entity == NULL)
336                         return NULL;
337
338                 ++end;
339         } else {
340                 entity_id = strtoul(p, &end, 10);
341                 entity = media_get_entity_by_id(media, entity_id);
342                 if (entity == NULL)
343                         return NULL;
344         }
345         for (; isspace(*end); ++end);
346
347         if (*end != ':')
348                 return NULL;
349         for (p = end + 1; isspace(*p); ++p);
350
351         pad = strtoul(p, &end, 10);
352         for (p = end; isspace(*p); ++p);
353
354         if (pad >= entity->info.pads)
355                 return NULL;
356
357         for (p = end; isspace(*p); ++p);
358         if (endp)
359                 *endp = (char *)p;
360
361         return &entity->pads[pad];
362 }
363
364 static struct media_entity_link *parse_link(struct media_device *media, const char *p, char **endp)
365 {
366         struct media_entity_link *link;
367         struct media_entity_pad *source;
368         struct media_entity_pad *sink;
369         unsigned int i;
370         char *end;
371
372         source = parse_pad(media, p, &end);
373         if (source == NULL)
374                 return NULL;
375
376         if (end[0] != '-' || end[1] != '>')
377                 return NULL;
378         p = end + 2;
379
380         sink = parse_pad(media, p, &end);
381         if (sink == NULL)
382                 return NULL;
383
384         *endp = end;
385
386         for (i = 0; i < source->entity->num_links; i++) {
387                 link = &source->entity->links[i];
388
389                 if (link->source == source && link->sink == sink)
390                         return link;
391         }
392
393         return NULL;
394 }
395
396 static int setup_link(struct media_device *media, const char *p, char **endp)
397 {
398         struct media_entity_link *link;
399         __u32 flags;
400         char *end;
401
402         link = parse_link(media, p, &end);
403         if (link == NULL) {
404                 printf("Unable to parse link\n");
405                 return -EINVAL;
406         }
407
408         p = end;
409         if (*p++ != '[') {
410                 printf("Unable to parse link flags\n");
411                 return -EINVAL;
412         }
413
414         flags = strtoul(p, &end, 10);
415         for (p = end; isspace(*p); p++);
416         if (*p++ != ']') {
417                 printf("Unable to parse link flags\n");
418                 return -EINVAL;
419         }
420
421         for (; isspace(*p); p++);
422         *endp = (char *)p;
423
424         printf("Setting up link %u:%u -> %u:%u [%u]\n",
425                 link->source->entity->info.id, link->source->index,
426                 link->sink->entity->info.id, link->sink->index,
427                 flags);
428
429         return media_setup_link(media, link->source, link->sink, flags);
430 }
431
432 static int setup_links(struct media_device *media, const char *p)
433 {
434         char *end;
435         int ret;
436
437         do {
438                 ret = setup_link(media, p, &end);
439                 if (ret < 0)
440                         return ret;
441
442                 p = end + 1;
443         } while (*end == ',');
444
445         return *end ? -EINVAL : 0;
446 }
447
448 /* -----------------------------------------------------------------------------
449  * Formats setup
450  */
451
452 static int parse_format(struct v4l2_mbus_framefmt *format, const char *p, char **endp)
453 {
454         enum v4l2_mbus_pixelcode code;
455         unsigned int width, height;
456         char *end;
457
458         for (; isspace(*p); ++p);
459         for (end = (char *)p; !isspace(*end) && *end != '\0'; ++end);
460
461         code = string_to_pixelcode(p, end - p);
462         if (code == (enum v4l2_mbus_pixelcode)-1)
463                 return -EINVAL;
464
465         for (p = end; isspace(*p); ++p);
466         width = strtoul(p, &end, 10);
467         if (*end != 'x')
468                 return -EINVAL;
469
470         p = end + 1;
471         height = strtoul(p, &end, 10);
472         *endp = end;
473
474         memset(format, 0, sizeof(*format));
475         format->width = width;
476         format->height = height;
477         format->code = code;
478
479         return 0;
480 }
481
482 static int parse_crop(struct v4l2_rect *crop, const char *p, char **endp)
483 {
484         char *end;
485
486         if (*p++ != '(')
487                 return -EINVAL;
488
489         crop->left = strtoul(p, &end, 10);
490         if (*end != ',')
491                 return -EINVAL;
492
493         p = end + 1;
494         crop->top = strtoul(p, &end, 10);
495         if (*end++ != ')')
496                 return -EINVAL;
497         if (*end != '/')
498                 return -EINVAL;
499
500         p = end + 1;
501         crop->width = strtoul(p, &end, 10);
502         if (*end != 'x')
503                 return -EINVAL;
504
505         p = end + 1;
506         crop->height = strtoul(p, &end, 10);
507         *endp = end;
508
509         return 0;
510 }
511
512 static int parse_frame_interval(struct v4l2_fract *interval, const char *p, char **endp)
513 {
514         char *end;
515
516         for (; isspace(*p); ++p);
517
518         interval->numerator = strtoul(p, &end, 10);
519
520         for (p = end; isspace(*p); ++p);
521         if (*p++ != '/')
522                 return -EINVAL;
523
524         for (; isspace(*p); ++p);
525         interval->denominator = strtoul(p, &end, 10);
526
527         *endp = end;
528         return 0;
529 }
530
531 static struct media_entity_pad *parse_pad_format(struct media_device *media,
532         struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop,
533         struct v4l2_fract *interval, const char *p, char **endp)
534 {
535         struct media_entity_pad *pad;
536         char *end;
537         int ret;
538
539         for (; isspace(*p); ++p);
540
541         pad = parse_pad(media, p, &end);
542         if (pad == NULL)
543                 return NULL;
544
545         for (p = end; isspace(*p); ++p);
546         if (*p++ != '[')
547                 return NULL;
548
549         for (; isspace(*p); ++p);
550
551         if (isalnum(*p)) {
552                 ret = parse_format(format, p, &end);
553                 if (ret < 0)
554                         return NULL;
555
556                 for (p = end; isspace(*p); p++);
557         }
558
559         if (*p == '(') {
560                 ret = parse_crop(crop, p, &end);
561                 if (ret < 0)
562                         return NULL;
563
564                 for (p = end; isspace(*p); p++);
565         }
566
567         if (*p == '@') {
568                 ret = parse_frame_interval(interval, ++p, &end);
569                 if (ret < 0)
570                         return NULL;
571
572                 for (p = end; isspace(*p); p++);
573         }
574
575         if (*p != ']')
576                 return NULL;
577
578         *endp = (char *)p + 1;
579         return pad;
580 }
581
582 static int set_format(struct media_entity_pad *pad, struct v4l2_mbus_framefmt *format)
583 {
584         int ret;
585
586         if (format->width == 0 || format->height == 0)
587                 return 0;
588
589         printf("Setting up format %s %ux%u on pad %s/%u\n",
590                 pixelcode_to_string(format->code), format->width, format->height,
591                 pad->entity->info.name, pad->index);
592
593         ret = v4l2_subdev_set_format(pad->entity, format, pad->index,
594                                      V4L2_SUBDEV_FORMAT_ACTIVE);
595         if (ret < 0) {
596                 printf("Unable to set format: %s (%d)\n", strerror(-ret), ret);
597                 return ret;
598         }
599
600         printf("Format set: %s %ux%u\n",
601                 pixelcode_to_string(format->code), format->width, format->height);
602
603         return 0;
604 }
605
606 static int set_crop(struct media_entity_pad *pad, struct v4l2_rect *crop)
607 {
608         int ret;
609
610         if (crop->left == -1 || crop->top == -1)
611                 return 0;
612
613         printf("Setting up crop rectangle (%u,%u)/%ux%u on pad %s/%u\n",
614                 crop->left, crop->top, crop->width, crop->height,
615                 pad->entity->info.name, pad->index);
616
617         ret = v4l2_subdev_set_crop(pad->entity, crop, pad->index,
618                                    V4L2_SUBDEV_FORMAT_ACTIVE);
619         if (ret < 0) {
620                 printf("Unable to set crop rectangle: %s (%d)\n", strerror(-ret), ret);
621                 return ret;
622         }
623
624         printf("Crop rectangle set: (%u,%u)/%ux%u\n",
625                 crop->left, crop->top, crop->width, crop->height);
626
627         return 0;
628 }
629
630 static int set_frame_interval(struct media_entity *entity, struct v4l2_fract *interval)
631 {
632         int ret;
633
634         if (interval->numerator == 0)
635                 return 0;
636
637         printf("Setting up frame interval %u/%u on entity %s\n",
638                 interval->numerator, interval->denominator, entity->info.name);
639
640         ret = v4l2_subdev_set_frame_interval(entity, interval);
641         if (ret < 0) {
642                 printf("Unable to set frame interval: %s (%d)", strerror(-ret), ret);
643                 return ret;
644         }
645
646         printf("Frame interval set: %u/%u\n",
647                 interval->numerator, interval->denominator);
648
649         return 0;
650 }
651
652
653 static int setup_format(struct media_device *media, const char *p, char **endp)
654 {
655         struct v4l2_mbus_framefmt format = { 0, 0, 0 };
656         struct media_entity_pad *pad;
657         struct v4l2_rect crop = { -1, -1, -1, -1 };
658         struct v4l2_fract interval = { 0, 0 };
659         unsigned int i;
660         char *end;
661         int ret;
662
663         pad = parse_pad_format(media, &format, &crop, &interval, p, &end);
664         if (pad == NULL) {
665                 printf("Unable to parse format\n");
666                 return -EINVAL;
667         }
668
669         if (pad->flags & MEDIA_PAD_FLAG_OUTPUT) {
670                 ret = set_crop(pad, &crop);
671                 if (ret < 0)
672                         return ret;
673         }
674
675         ret = set_format(pad, &format);
676         if (ret < 0)
677                 return ret;
678
679         if (pad->flags & MEDIA_PAD_FLAG_INPUT) {
680                 ret = set_crop(pad, &crop);
681                 if (ret < 0)
682                         return ret;
683         }
684
685         ret = set_frame_interval(pad->entity, &interval);
686         if (ret < 0)
687                 return ret;
688
689
690         /* If the pad is an output pad, automatically set the same format on
691          * the remote subdev input pads, if any.
692          */
693         if (pad->flags & MEDIA_PAD_FLAG_OUTPUT) {
694                 for (i = 0; i < pad->entity->num_links; ++i) {
695                         struct media_entity_link *link = &pad->entity->links[i];
696                         struct v4l2_mbus_framefmt remote_format;
697
698                         if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE))
699                                 continue;
700
701                         if (link->source == pad &&
702                             link->sink->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV) {
703                                 remote_format = format;
704                                 set_format(link->sink, &remote_format);
705                         }
706                 }
707         }
708
709         *endp = end;
710         return 0;
711 }
712
713 static int setup_formats(struct media_device *media, const char *p)
714 {
715         char *end;
716         int ret;
717
718         do {
719                 ret = setup_format(media, p, &end);
720                 if (ret < 0)
721                         return ret;
722
723                 p = end + 1;
724         } while (*end == ',');
725
726         return *end ? -EINVAL : 0;
727 }
728
729 int main(int argc, char **argv)
730 {
731         struct media_device *media;
732         int ret = -1;
733
734         if (parse_cmdline(argc, argv))
735                 return EXIT_FAILURE;
736
737         /* Open the media device and enumerate entities, pads and links. */
738         media = media_open(media_opts.devname, media_opts.verbose);
739         if (media == NULL)
740                 goto out;
741
742         if (media_opts.entity) {
743                 struct media_entity *entity;
744
745                 entity = media_get_entity_by_name(media, media_opts.entity,
746                                                   strlen(media_opts.entity));
747                 if (entity == NULL) {
748                         printf("Entity '%s' not found\n", media_opts.entity);
749                         goto out;
750                 }
751
752                 printf("%s\n", entity->devname);
753         }
754
755         if (media_opts.pad) {
756                 struct media_entity_pad *pad;
757
758                 pad = parse_pad(media, media_opts.pad, NULL);
759                 if (pad == NULL) {
760                         printf("Pad '%s' not found\n", media_opts.pad);
761                         goto out;
762                 }
763
764                 v4l2_subdev_print_format(pad->entity, pad->index,
765                                          V4L2_SUBDEV_FORMAT_ACTIVE);
766                 printf("\n");
767         }
768
769         if (media_opts.print || media_opts.print_dot) {
770                 media_print_topology(media, media_opts.print_dot);
771                 printf("\n");
772         }
773
774         if (media_opts.reset) {
775                 printf("Resetting all links to inactive\n");
776                 media_reset_links(media);
777         }
778
779         if (media_opts.links)
780                 setup_links(media, media_opts.links);
781
782         if (media_opts.formats)
783                 setup_formats(media, media_opts.formats);
784
785         if (media_opts.interactive) {
786                 while (1) {
787                         char buffer[32];
788                         char *end;
789
790                         printf("Enter a link to modify or enter to stop\n");
791                         if (fgets(buffer, sizeof buffer, stdin) == NULL)
792                                 break;
793
794                         if (buffer[0] == '\n')
795                                 break;
796
797                         setup_link(media, buffer, &end);
798                 }
799         }
800
801         ret = 0;
802
803 out:
804         if (media)
805                 media_close(media);
806
807         return ret ? EXIT_FAILURE : EXIT_SUCCESS;
808 }
809