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