strerror takes a non-negative integer
[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_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_setup_link -
152  */
153 int media_setup_link(struct media_device *media,
154                      struct media_entity_pad *source,
155                      struct media_entity_pad *sink,
156                      __u32 flags)
157 {
158         struct media_entity_link *link;
159         struct media_user_link ulink;
160         unsigned int i;
161         int ret;
162
163         for (i = 0; i < source->entity->info.links; i++) {
164                 link = &source->entity->links[i];
165
166                 if (link->source->entity == source->entity &&
167                     link->source->index == source->index &&
168                     link->sink->entity == sink->entity &&
169                     link->sink->index == sink->index)
170                         break;
171         }
172
173         if (i == source->entity->info.links) {
174                 printf("%s: Link not found\n", __func__);
175                 return -EINVAL;
176         }
177
178         /* source pad */
179         ulink.source.entity = source->entity->info.id;
180         ulink.source.index = source->index;
181         ulink.source.type = MEDIA_PAD_TYPE_OUTPUT;
182
183         /* sink pad */
184         ulink.sink.entity = sink->entity->info.id;
185         ulink.sink.index = sink->index;
186         ulink.sink.type = MEDIA_PAD_TYPE_INPUT;
187
188         ulink.flags = flags | (link->flags & MEDIA_LINK_FLAG_IMMUTABLE);
189
190         ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink);
191         if (ret < 0) {
192                 printf("%s: Unable to setup link (%s)\n", __func__,
193                         strerror(errno));
194                 return ret;
195         }
196
197         link->flags = flags;
198         return 0;
199 }
200
201 int media_reset_links(struct media_device *media)
202 {
203         unsigned int i, j;
204         int ret;
205
206         for (i = 0; i < media->entities_count; ++i) {
207                 struct media_entity *entity = &media->entities[i];
208
209                 for (j = 0; j < entity->info.links; j++) {
210                         struct media_entity_link *link = &entity->links[j];
211
212                         if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
213                                 continue;
214
215                         ret = media_setup_link(media, link->source, link->sink,
216                                                link->flags & ~MEDIA_LINK_FLAG_ACTIVE);
217                         if (ret < 0)
218                                 return ret;
219                 }
220         }
221
222         return 0;
223 }
224
225 static void media_print_topology_dot(struct media_device *media)
226 {
227         unsigned int i, j;
228
229         printf("digraph board {\n");
230         printf("\trankdir=TB\n");
231
232         for (i = 0; i < media->entities_count; ++i) {
233                 struct media_entity *entity = &media->entities[i];
234                 unsigned int npads;
235
236                 switch (entity->info.type) {
237                 case MEDIA_ENTITY_TYPE_NODE:
238                         printf("\tn%08x [label=\"%s\", shape=box, style=filled, "
239                                "fillcolor=yellow]\n",
240                                entity->info.id, entity->info.name);
241                         break;
242
243                 case MEDIA_ENTITY_TYPE_SUBDEV:
244                         printf("\tn%08x [label=\"{{", entity->info.id);
245
246                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
247                                 if (entity->pads[j].type != MEDIA_PAD_TYPE_INPUT)
248                                         continue;
249
250                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
251                                 npads++;
252                         }
253
254                         printf("} | %s | {", entity->info.name);
255
256                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
257                                 if (entity->pads[j].type != MEDIA_PAD_TYPE_OUTPUT)
258                                         continue;
259
260                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
261                                 npads++;
262                         }
263
264                         printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n");
265                         break;
266
267                 default:
268                         continue;
269                 }
270
271                 for (j = 0; j < entity->info.links; j++) {
272                         struct media_entity_link *link = &entity->links[j];
273
274                         if (link->source->entity != entity)
275                                 continue;
276
277                         printf("\tn%08x", link->source->entity->info.id);
278                         if (link->source->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV)
279                                 printf(":port%u", link->source->index);
280                         printf(" -> ");
281                         printf("n%08x", link->sink->entity->info.id);
282                         if (link->sink->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV)
283                                 printf(":port%u", link->sink->index);
284
285                         if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
286                                 printf(" [style=bold]");
287                         else if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE))
288                                 printf(" [style=dashed]");
289                         printf("\n");
290                 }
291         }
292
293         printf("}\n");
294 }
295
296 static void media_print_topology_text(struct media_device *media)
297 {
298         unsigned int i, j, k;
299         unsigned int padding;
300         int ret;
301
302         printf("Device topology\n");
303
304         for (i = 0; i < media->entities_count; ++i) {
305                 struct media_entity *entity = &media->entities[i];
306
307                 padding = printf("- entity %u: ", entity->info.id);
308                 printf("%s (%u pad%s, %u link%s)\n", entity->info.name,
309                         entity->info.pads, entity->info.pads > 1 ? "s" : "",
310                         entity->info.links, entity->info.links > 1 ? "s" : "");
311                 printf("%*ctype %s subtype %s\n", padding, ' ',
312                         media_entity_type_to_string(entity->info.type),
313                         media_entity_subtype_to_string(entity->info.type, entity->info.subtype));
314                 if (entity->devname)
315                         printf("%*cdevice node name %s\n", padding, ' ', entity->devname);
316
317                 for (j = 0; j < entity->info.pads; j++) {
318                         struct media_entity_pad *pad = &entity->pads[j];
319                         struct v4l2_mbus_framefmt format;
320
321                         printf("\tpad%u: %s", j, media_pad_type_to_string(pad->type));
322
323                         if (entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV) {
324                                 ret = v4l2_subdev_get_format(entity, &format, j,
325                                                      V4L2_SUBDEV_FORMAT_ACTIVE);
326                                 if (ret == 0)
327                                         printf(" [%s %ux%u]", pixelcode_to_string(format.code),
328                                                format.width, format.height);
329                         }
330
331                         printf("\n");
332
333                         for (k = 0; k < entity->info.links; k++) {
334                                 struct media_entity_link *link = &entity->links[k];
335
336                                 if (link->source->entity != entity ||
337                                     link->source->index != j)
338                                         continue;
339
340                                 printf("\t\t-> '%s':pad%u [",
341                                         link->sink->entity->info.name, link->sink->index);
342
343                                 if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
344                                         printf("IMMUTABLE,");
345                                 if (link->flags & MEDIA_LINK_FLAG_ACTIVE)
346                                         printf("ACTIVE");
347
348                                 printf("]\n");
349                         }
350                 }
351                 printf("\n");
352         }
353 }
354
355 void media_print_topology(struct media_device *media, int dot)
356 {
357         if (dot)
358                 media_print_topology_dot(media);
359         else
360                 media_print_topology_text(media);
361 }
362
363 static int media_enum_links(struct media_device *media)
364 {
365         __u32 id;
366         int ret = 0;
367
368         for (id = 1; id <= media->entities_count; id++) {
369                 struct media_entity *entity = &media->entities[id - 1];
370                 struct media_user_links links;
371                 unsigned int i;
372
373                 links.entity = entity->info.id;
374                 links.pads = malloc(entity->info.pads * sizeof(struct media_user_pad));
375                 links.links = malloc(entity->info.links * sizeof(struct media_user_link));
376
377                 if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
378                         printf("%s: Unable to enumerate pads and links (%s).\n",
379                                 __func__, strerror(errno));
380                         free(links.pads);
381                         free(links.links);
382                         return -errno;
383                 }
384
385                 for (i = 0; i < entity->info.pads; ++i) {
386                         entity->pads[i].entity = entity;
387                         entity->pads[i].type = links.pads[i].type;
388                         entity->pads[i].index = links.pads[i].index;
389                 }
390
391                 for (i = 0; i < entity->info.links; ++i) {
392                         struct media_user_link *link = &links.links[i];
393                         struct media_entity *source;
394                         struct media_entity *sink;
395
396                         if (link->source.entity > media->entities_count ||
397                             link->sink.entity > media->entities_count) {
398                                 printf("WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
399                                         id, i, link->source.entity, link->source.index,
400                                         link->sink.entity, link->sink.index);
401                                 ret = -EINVAL;
402                         }
403
404                         source = &media->entities[link->source.entity - 1];
405                         sink = &media->entities[link->sink.entity - 1];
406                         entity->links[i].source = &source->pads[link->source.index];
407                         entity->links[i].sink = &sink->pads[link->sink.index];
408                         entity->links[i].flags = links.links[i].flags;
409                 }
410
411                 free(links.pads);
412                 free(links.links);
413         }
414
415         return ret;
416 }
417
418 static int media_enum_entities(struct media_device *media)
419 {
420         struct media_entity *entity;
421         struct stat devstat;
422         char devname[32];
423         unsigned int i;
424         __u32 id;
425         int ret;
426
427         for (id = 1; ; id++) {
428                 media->entities =
429                         realloc(media->entities, id * sizeof(*media->entities));
430                 entity = &media->entities[id - 1];
431                 memset(entity, 0, sizeof(*entity));
432                 entity->fd = -1;
433                 entity->info.id = id;
434
435                 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
436                 if (ret < 0) {
437                         if (errno == EINVAL)
438                                 break;
439                         return -errno;
440                 }
441
442                 entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));
443                 entity->links = malloc(entity->info.links * sizeof(*entity->links));
444                 if (entity->pads == NULL || entity->links == NULL)
445                         return -ENOMEM;
446
447                 media->entities_count++;
448
449                 /* Find the corresponding device name. */
450                 if ((entity->info.type != MEDIA_ENTITY_TYPE_NODE ||
451                      entity->info.type != MEDIA_NODE_TYPE_V4L) &&
452                     (entity->info.type != MEDIA_ENTITY_TYPE_SUBDEV))
453                         continue;
454
455                 for (i = 0; i < 256; ++i) {
456                         if (entity->info.type == MEDIA_ENTITY_TYPE_NODE)
457                                 sprintf(devname, "/dev/video%u", i);
458                         else
459                                 sprintf(devname, "/dev/subdev%u", i);
460
461                         ret = stat(devname, &devstat);
462                         if (ret < 0)
463                                 continue;
464
465                         if (major(devstat.st_rdev) == entity->info.v4l.major &&
466                             minor(devstat.st_rdev) == entity->info.v4l.minor) {
467                                 strcpy(entity->devname, devname);
468                                 break;
469                         }
470                 }
471         }
472
473         return 0;
474 }
475
476 /*
477  * media_open -
478  */
479 struct media_device *media_open(const char *name, int verbose)
480 {
481         struct media_device *media;
482         int ret;
483
484         media = malloc(sizeof(*media));
485         if (media == NULL) {
486                 printf("%s: unable to allocate memory\n", __func__);
487                 return NULL;
488         }
489         memset(media, 0, sizeof(*media));
490
491         if (verbose)
492                 printf("Opening media device %s\n", name);
493         media->fd = open(name, O_RDWR);
494         if (media->fd < 0) {
495                 media_close(media);
496                 printf("%s: Can't open media device %s\n", __func__, name);
497                 return NULL;
498         }
499
500         if (verbose)
501                 printf("Enumerating entities\n");
502
503         ret = media_enum_entities(media);
504         if (ret < 0) {
505                 printf("%s: Unable to enumerate entities for device %s (%s)\n",
506                         __func__, name, strerror(-ret));
507                 media_close(media);
508                 return NULL;
509         }
510
511         if (verbose) {
512                 printf("Found %u entities\n", media->entities_count);
513                 printf("Enumerating pads and links\n");
514         }
515
516         ret = media_enum_links(media);
517         if (ret < 0) {
518                 printf("%s: Unable to enumerate pads and linksfor device %s\n",
519                         __func__, name);
520                 media_close(media);
521                 return NULL;
522         }
523
524         return media;
525 }
526
527 /*
528  * media_close -
529  */
530 void media_close(struct media_device *media)
531 {
532         unsigned int i;
533
534         if (media->fd != -1)
535                 close(media->fd);
536
537         for (i = 0; i < media->entities_count; ++i) {
538                 struct media_entity *entity = &media->entities[i];
539
540                 free(entity->pads);
541                 free(entity->links);
542                 if (entity->fd != -1)
543                         close(entity->fd);
544         }
545
546         free(media->entities);
547         free(media);
548 }
549