Print video device node names in dot diagrams
[media-ctl.git] / media.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/ioctl.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <errno.h>
30
31 #include <linux/videodev2.h>
32 #include <linux/media.h>
33
34 #include "media.h"
35 #include "subdev.h"
36 #include "tools.h"
37
38 static const char *media_entity_type_to_string(unsigned type)
39 {
40         static const struct {
41                 __u32 type;
42                 const char *name;
43         } types[] = {
44                 { MEDIA_ENTITY_TYPE_NODE, "Node" },
45                 { MEDIA_ENTITY_TYPE_SUBDEV, "V4L2 subdev" },
46         };
47
48         unsigned int i;
49
50         for (i = 0; i < ARRAY_SIZE(types); i++) {
51                 if (types[i].type == type)
52                         return types[i].name;
53         }
54
55         return "Unknown";
56 }
57
58 static const char *media_entity_subtype_to_string(unsigned type, unsigned subtype)
59 {
60         static const char *node_types[] = {
61                 "Unknown",
62                 "V4L",
63                 "FB",
64                 "ALSA",
65                 "DVB",
66         };
67         static const char *subdev_types[] = {
68                 "Unknown",
69                 "Video Decoder",
70                 "Video Encoder",
71                 "Miscellaneous",
72         };
73
74         switch (type) {
75         case MEDIA_ENTITY_TYPE_NODE:
76                 if (subtype > 4)
77                         subtype = 0;
78                 return node_types[subtype];
79
80         case MEDIA_ENTITY_TYPE_SUBDEV:
81                 if (subtype > 3)
82                         subtype = 0;
83                 return subdev_types[subtype];
84         default:
85                 return node_types[0];
86         }
87 }
88
89 static const char *media_pad_type_to_string(unsigned type)
90 {
91         static const struct {
92                 __u32 type;
93                 const char *name;
94         } types[] = {
95                 { MEDIA_PAD_TYPE_INPUT, "Input" },
96                 { MEDIA_PAD_TYPE_OUTPUT, "Output" },
97         };
98
99         unsigned int i;
100
101         for (i = 0; i < ARRAY_SIZE(types); i++) {
102                 if (types[i].type == type)
103                         return types[i].name;
104         }
105
106         return "Unknown";
107 }
108
109 /*
110  * media_entity_remote_pad -
111  */
112 struct media_entity_pad *media_entity_remote_pad(struct media_entity_pad *pad)
113 {
114         unsigned int i;
115
116         for (i = 0; i < pad->entity->info.links; ++i) {
117                 struct media_entity_link *link = &pad->entity->links[i];
118
119                 if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE))
120                         continue;
121
122                 if (link->source == pad)
123                         return link->sink;
124
125                 if (link->sink == pad)
126                         return link->source;
127         }
128
129         return NULL;
130 }
131
132 /*
133  * media_get_entity_by_name -
134  */
135 struct media_entity *media_get_entity_by_name(struct media_device *media,
136                                               const char *name, size_t length)
137 {
138         unsigned int i;
139
140         for (i = 0; i < media->entities_count; ++i) {
141                 struct media_entity *entity = &media->entities[i];
142
143                 if (strncmp(entity->info.name, name, length) == 0)
144                         return entity;
145         }
146
147         return NULL;
148 }
149
150 /*
151  * media_get_entity_by_id -
152  */
153 struct media_entity *media_get_entity_by_id(struct media_device *media,
154                                             __u32 id)
155 {
156         unsigned int i;
157
158         for (i = 0; i < media->entities_count; ++i) {
159                 struct media_entity *entity = &media->entities[i];
160
161                 if (entity->info.id == id)
162                         return entity;
163         }
164
165         return NULL;
166 }
167
168 /*
169  * media_setup_link -
170  */
171 int media_setup_link(struct media_device *media,
172                      struct media_entity_pad *source,
173                      struct media_entity_pad *sink,
174                      __u32 flags)
175 {
176         struct media_entity_link *link;
177         struct media_user_link ulink;
178         unsigned int i;
179         int ret;
180
181         for (i = 0; i < source->entity->info.links; i++) {
182                 link = &source->entity->links[i];
183
184                 if (link->source->entity == source->entity &&
185                     link->source->index == source->index &&
186                     link->sink->entity == sink->entity &&
187                     link->sink->index == sink->index)
188                         break;
189         }
190
191         if (i == source->entity->info.links) {
192                 printf("%s: Link not found\n", __func__);
193                 return -EINVAL;
194         }
195
196         /* source pad */
197         ulink.source.entity = source->entity->info.id;
198         ulink.source.index = source->index;
199         ulink.source.type = MEDIA_PAD_TYPE_OUTPUT;
200
201         /* sink pad */
202         ulink.sink.entity = sink->entity->info.id;
203         ulink.sink.index = sink->index;
204         ulink.sink.type = MEDIA_PAD_TYPE_INPUT;
205
206         ulink.flags = flags | (link->flags & MEDIA_LINK_FLAG_IMMUTABLE);
207
208         ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink);
209         if (ret < 0) {
210                 printf("%s: Unable to setup link (%s)\n", __func__,
211                         strerror(errno));
212                 return ret;
213         }
214
215         link->flags = flags;
216         return 0;
217 }
218
219 int media_reset_links(struct media_device *media)
220 {
221         unsigned int i, j;
222         int ret;
223
224         for (i = 0; i < media->entities_count; ++i) {
225                 struct media_entity *entity = &media->entities[i];
226
227                 for (j = 0; j < entity->info.links; j++) {
228                         struct media_entity_link *link = &entity->links[j];
229
230                         if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
231                                 continue;
232
233                         ret = media_setup_link(media, link->source, link->sink,
234                                                link->flags & ~MEDIA_LINK_FLAG_ACTIVE);
235                         if (ret < 0)
236                                 return ret;
237                 }
238         }
239
240         return 0;
241 }
242
243 static void media_print_topology_dot(struct media_device *media)
244 {
245         unsigned int i, j;
246
247         printf("digraph board {\n");
248         printf("\trankdir=TB\n");
249
250         for (i = 0; i < media->entities_count; ++i) {
251                 struct media_entity *entity = &media->entities[i];
252                 unsigned int npads;
253
254                 switch (entity->info.type) {
255                 case MEDIA_ENTITY_TYPE_NODE:
256                         printf("\tn%08x [label=\"%s\\n%s\", shape=box, style=filled, "
257                                "fillcolor=yellow]\n",
258                                entity->info.id, entity->info.name, entity->devname);
259                         break;
260
261                 case MEDIA_ENTITY_TYPE_SUBDEV:
262                         printf("\tn%08x [label=\"{{", entity->info.id);
263
264                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
265                                 if (entity->pads[j].type != MEDIA_PAD_TYPE_INPUT)
266                                         continue;
267
268                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
269                                 npads++;
270                         }
271
272                         printf("} | %s | {", entity->info.name);
273
274                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
275                                 if (entity->pads[j].type != MEDIA_PAD_TYPE_OUTPUT)
276                                         continue;
277
278                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
279                                 npads++;
280                         }
281
282                         printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n");
283                         break;
284
285                 default:
286                         continue;
287                 }
288
289                 for (j = 0; j < entity->info.links; j++) {
290                         struct media_entity_link *link = &entity->links[j];
291
292                         if (link->source->entity != entity)
293                                 continue;
294
295                         printf("\tn%08x", link->source->entity->info.id);
296                         if (link->source->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV)
297                                 printf(":port%u", link->source->index);
298                         printf(" -> ");
299                         printf("n%08x", link->sink->entity->info.id);
300                         if (link->sink->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV)
301                                 printf(":port%u", link->sink->index);
302
303                         if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
304                                 printf(" [style=bold]");
305                         else if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE))
306                                 printf(" [style=dashed]");
307                         printf("\n");
308                 }
309         }
310
311         printf("}\n");
312 }
313
314 static void media_print_topology_text(struct media_device *media)
315 {
316         unsigned int i, j, k;
317         unsigned int padding;
318         int ret;
319
320         printf("Device topology\n");
321
322         for (i = 0; i < media->entities_count; ++i) {
323                 struct media_entity *entity = &media->entities[i];
324
325                 padding = printf("- entity %u: ", entity->info.id);
326                 printf("%s (%u pad%s, %u link%s)\n", entity->info.name,
327                         entity->info.pads, entity->info.pads > 1 ? "s" : "",
328                         entity->info.links, entity->info.links > 1 ? "s" : "");
329                 printf("%*ctype %s subtype %s\n", padding, ' ',
330                         media_entity_type_to_string(entity->info.type),
331                         media_entity_subtype_to_string(entity->info.type, entity->info.subtype));
332                 if (entity->devname[0])
333                         printf("%*cdevice node name %s\n", padding, ' ', entity->devname);
334
335                 for (j = 0; j < entity->info.pads; j++) {
336                         struct media_entity_pad *pad = &entity->pads[j];
337                         struct v4l2_mbus_framefmt format;
338                         struct v4l2_rect rect;
339
340                         printf("\tpad%u: %s", j, media_pad_type_to_string(pad->type));
341
342                         if (entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV) {
343                                 ret = v4l2_subdev_get_format(entity, &format, j,
344                                                      V4L2_SUBDEV_FORMAT_ACTIVE);
345                                 if (ret == 0) {
346                                         printf(" [%s %ux%u", pixelcode_to_string(format.code),
347                                                format.width, format.height);
348
349                                         ret = v4l2_subdev_get_crop(entity, &rect, j,
350                                                              V4L2_SUBDEV_FORMAT_ACTIVE);
351                                         if (ret == 0)
352                                                 printf(" (%u,%u)/%ux%u", rect.left, rect.top,
353                                                        rect.width, rect.height);
354                                         printf("]");
355                                 }
356                         }
357
358                         printf("\n");
359
360                         for (k = 0; k < entity->info.links; k++) {
361                                 struct media_entity_link *link = &entity->links[k];
362
363                                 if (link->source->entity != entity ||
364                                     link->source->index != j)
365                                         continue;
366
367                                 printf("\t\t-> '%s':pad%u [",
368                                         link->sink->entity->info.name, link->sink->index);
369
370                                 if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
371                                         printf("IMMUTABLE,");
372                                 if (link->flags & MEDIA_LINK_FLAG_ACTIVE)
373                                         printf("ACTIVE");
374
375                                 printf("]\n");
376                         }
377                 }
378                 printf("\n");
379         }
380 }
381
382 void media_print_topology(struct media_device *media, int dot)
383 {
384         if (dot)
385                 media_print_topology_dot(media);
386         else
387                 media_print_topology_text(media);
388 }
389
390 static int media_enum_links(struct media_device *media)
391 {
392         __u32 id;
393         int ret = 0;
394
395         for (id = 1; id <= media->entities_count; id++) {
396                 struct media_entity *entity = &media->entities[id - 1];
397                 struct media_user_links links;
398                 unsigned int i;
399
400                 links.entity = entity->info.id;
401                 links.pads = malloc(entity->info.pads * sizeof(struct media_user_pad));
402                 links.links = malloc(entity->info.links * sizeof(struct media_user_link));
403
404                 if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
405                         printf("%s: Unable to enumerate pads and links (%s).\n",
406                                 __func__, strerror(errno));
407                         free(links.pads);
408                         free(links.links);
409                         return -errno;
410                 }
411
412                 for (i = 0; i < entity->info.pads; ++i) {
413                         entity->pads[i].entity = entity;
414                         entity->pads[i].type = links.pads[i].type;
415                         entity->pads[i].index = links.pads[i].index;
416                 }
417
418                 for (i = 0; i < entity->info.links; ++i) {
419                         struct media_user_link *link = &links.links[i];
420                         struct media_entity *source;
421                         struct media_entity *sink;
422
423                         source = media_get_entity_by_id(media, link->source.entity);
424                         sink = media_get_entity_by_id(media, link->sink.entity);
425
426                         if (source == NULL || sink == NULL) {
427                                 printf("WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
428                                         id, i, link->source.entity, link->source.index,
429                                         link->sink.entity, link->sink.index);
430                                 ret = -EINVAL;
431                         }
432
433                         entity->links[i].source = &source->pads[link->source.index];
434                         entity->links[i].sink = &sink->pads[link->sink.index];
435                         entity->links[i].flags = links.links[i].flags;
436                 }
437
438                 free(links.pads);
439                 free(links.links);
440         }
441
442         return ret;
443 }
444
445 static int media_enum_entities(struct media_device *media)
446 {
447         struct media_entity *entity;
448         struct stat devstat;
449         char devname[32];
450         unsigned int size;
451         unsigned int i;
452         __u32 id;
453         int ret;
454
455         for (id = 0; ; id = entity->info.id) {
456                 size = (media->entities_count + 1) * sizeof(*media->entities);
457                 media->entities = realloc(media->entities, size);
458
459                 entity = &media->entities[media->entities_count];
460                 memset(entity, 0, sizeof(*entity));
461                 entity->fd = -1;
462                 entity->info.id = id | MEDIA_ENTITY_ID_FLAG_NEXT;
463
464                 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
465                 if (ret < 0) {
466                         if (errno == EINVAL)
467                                 break;
468                         return -errno;
469                 }
470
471                 entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));
472                 entity->links = malloc(entity->info.links * sizeof(*entity->links));
473                 if (entity->pads == NULL || entity->links == NULL)
474                         return -ENOMEM;
475
476                 media->entities_count++;
477
478                 /* Find the corresponding device name. */
479                 if ((entity->info.type != MEDIA_ENTITY_TYPE_NODE ||
480                      entity->info.type != MEDIA_NODE_TYPE_V4L) &&
481                     (entity->info.type != MEDIA_ENTITY_TYPE_SUBDEV))
482                         continue;
483
484                 for (i = 0; i < 256; ++i) {
485                         if (entity->info.type == MEDIA_ENTITY_TYPE_NODE)
486                                 sprintf(devname, "/dev/video%u", i);
487                         else
488                                 sprintf(devname, "/dev/subdev%u", i);
489
490                         ret = stat(devname, &devstat);
491                         if (ret < 0)
492                                 continue;
493
494                         if (major(devstat.st_rdev) == entity->info.v4l.major &&
495                             minor(devstat.st_rdev) == entity->info.v4l.minor) {
496                                 strcpy(entity->devname, devname);
497                                 break;
498                         }
499                 }
500
501                 id = entity->info.id;
502         }
503
504         return 0;
505 }
506
507 /*
508  * media_open -
509  */
510 struct media_device *media_open(const char *name, int verbose)
511 {
512         struct media_device *media;
513         int ret;
514
515         media = malloc(sizeof(*media));
516         if (media == NULL) {
517                 printf("%s: unable to allocate memory\n", __func__);
518                 return NULL;
519         }
520         memset(media, 0, sizeof(*media));
521
522         if (verbose)
523                 printf("Opening media device %s\n", name);
524         media->fd = open(name, O_RDWR);
525         if (media->fd < 0) {
526                 media_close(media);
527                 printf("%s: Can't open media device %s\n", __func__, name);
528                 return NULL;
529         }
530
531         if (verbose)
532                 printf("Enumerating entities\n");
533
534         ret = media_enum_entities(media);
535         if (ret < 0) {
536                 printf("%s: Unable to enumerate entities for device %s (%s)\n",
537                         __func__, name, strerror(-ret));
538                 media_close(media);
539                 return NULL;
540         }
541
542         if (verbose) {
543                 printf("Found %u entities\n", media->entities_count);
544                 printf("Enumerating pads and links\n");
545         }
546
547         ret = media_enum_links(media);
548         if (ret < 0) {
549                 printf("%s: Unable to enumerate pads and linksfor device %s\n",
550                         __func__, name);
551                 media_close(media);
552                 return NULL;
553         }
554
555         return media;
556 }
557
558 /*
559  * media_close -
560  */
561 void media_close(struct media_device *media)
562 {
563         unsigned int i;
564
565         if (media->fd != -1)
566                 close(media->fd);
567
568         for (i = 0; i < media->entities_count; ++i) {
569                 struct media_entity *entity = &media->entities[i];
570
571                 free(entity->pads);
572                 free(entity->links);
573                 if (entity->fd != -1)
574                         close(entity->fd);
575         }
576
577         free(media->entities);
578         free(media);
579 }
580