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