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