Initial import
[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 void media_print_topology(struct media_device *media)
205 {
206         unsigned int i, j, k;
207         unsigned int padding;
208
209         printf("Device topology\n");
210
211         for (i = 0; i < media->entities_count; ++i) {
212                 struct media_entity *entity = &media->entities[i];
213
214                 padding = printf("- entity %u: ", entity->info.id);
215                 printf("%s (%u pad%s, %u link%s)\n", entity->info.name,
216                         entity->info.pads, entity->info.pads > 1 ? "s" : "",
217                         entity->info.links, entity->info.links > 1 ? "s" : "");
218                 printf("%*ctype %s subtype %s\n", padding, ' ',
219                         media_entity_type_to_string(entity->info.type),
220                         media_entity_subtype_to_string(entity->info.type, entity->info.subtype));
221                 if (entity->devname)
222                         printf("%*cdevice node name %s\n", padding, ' ', entity->devname);
223
224                 for (j = 0; j < entity->info.pads; j++) {
225                         for (k = 0; k < entity->info.links; k++) {
226                                 struct media_entity_link *link = &entity->links[k];
227
228                                 if (link->source->entity != entity ||
229                                     link->source->index != j)
230                                         continue;
231
232                                 printf("\tpad%u -> '%s':pad%u [", link->source->index,
233                                         link->sink->entity->info.name, link->sink->index);
234
235                                 if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
236                                         printf("IMMUTABLE,");
237                                 if (link->flags & MEDIA_LINK_FLAG_ACTIVE)
238                                         printf("ACTIVE");
239
240                                 printf("]\n");
241                         }
242                 }
243                 printf("\n");
244         }
245 }
246
247 static int media_enum_links(struct media_device *media)
248 {
249         __u32 id;
250         int ret = 0;
251
252         for (id = 1; id <= media->entities_count; id++) {
253                 struct media_entity *entity = &media->entities[id - 1];
254                 struct media_user_links links;
255                 unsigned int i;
256
257                 links.entity = entity->info.id;
258                 links.pads = malloc(entity->info.pads * sizeof(struct media_user_pad));
259                 links.links = malloc(entity->info.links * sizeof(struct media_user_link));
260
261                 if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
262                         printf("%s: Unable to enumerate pads and links (%s).\n",
263                                 __func__, strerror(errno));
264                         free(links.pads);
265                         free(links.links);
266                         return -errno;
267                 }
268
269                 for (i = 0; i < entity->info.pads; ++i) {
270                         entity->pads[i].entity = entity;
271                         entity->pads[i].type = links.pads[i].type;
272                         entity->pads[i].index = links.pads[i].index;
273                 }
274
275                 for (i = 0; i < entity->info.links; ++i) {
276                         struct media_user_link *link = &links.links[i];
277                         struct media_entity *source;
278                         struct media_entity *sink;
279
280                         if (link->source.entity > media->entities_count ||
281                             link->sink.entity > media->entities_count) {
282                                 printf("WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
283                                         id, i, link->source.entity, link->source.index,
284                                         link->sink.entity, link->sink.index);
285                                 ret = -EINVAL;
286                         }
287
288                         source = &media->entities[link->source.entity - 1];
289                         sink = &media->entities[link->sink.entity - 1];
290                         entity->links[i].source = &source->pads[link->source.index];
291                         entity->links[i].sink = &sink->pads[link->sink.index];
292                         entity->links[i].flags = links.links[i].flags;
293                 }
294
295                 free(links.pads);
296                 free(links.links);
297         }
298
299         return ret;
300 }
301
302 static int media_enum_entities(struct media_device *media)
303 {
304         struct media_entity *entity;
305         struct stat devstat;
306         char devname[32];
307         unsigned int i;
308         __u32 id;
309         int ret;
310
311         for (id = 1; ; id++) {
312                 media->entities =
313                         realloc(media->entities, id * sizeof(*media->entities));
314                 entity = &media->entities[id - 1];
315                 memset(entity, 0, sizeof(*entity));
316                 entity->fd = -1;
317                 entity->info.id = id;
318
319                 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
320                 if (ret < 0) {
321                         if (errno == EINVAL)
322                                 break;
323                         return -errno;
324                 }
325
326                 entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));
327                 entity->links = malloc(entity->info.links * sizeof(*entity->links));
328                 if (entity->pads == NULL || entity->links == NULL)
329                         return -ENOMEM;
330
331                 media->entities_count++;
332
333                 /* Find the corresponding device name. */
334                 if ((entity->info.type != MEDIA_ENTITY_TYPE_NODE ||
335                      entity->info.type != MEDIA_NODE_TYPE_V4L) &&
336                     (entity->info.type != MEDIA_ENTITY_TYPE_SUBDEV))
337                         continue;
338
339                 for (i = 0; i < 256; ++i) {
340                         if (entity->info.type == MEDIA_ENTITY_TYPE_NODE)
341                                 sprintf(devname, "/dev/video%u", i);
342                         else
343                                 sprintf(devname, "/dev/subdev%u", i);
344
345                         ret = stat(devname, &devstat);
346                         if (ret < 0)
347                                 continue;
348
349                         if (major(devstat.st_rdev) == entity->info.v4l.major &&
350                             minor(devstat.st_rdev) == entity->info.v4l.minor) {
351                                 strcpy(entity->devname, devname);
352                                 break;
353                         }
354                 }
355         }
356
357         return 0;
358 }
359
360 /*
361  * media_open -
362  */
363 struct media_device *media_open(const char *name, int verbose)
364 {
365         struct media_device *media;
366         int ret;
367
368         media = malloc(sizeof(*media));
369         if (media == NULL) {
370                 printf("%s: unable to allocate memory\n", __func__);
371                 return NULL;
372         }
373         memset(media, 0, sizeof(*media));
374
375         if (verbose)
376                 printf("Opening media device %s\n", name);
377         media->fd = open(name, O_RDWR);
378         if (media->fd < 0) {
379                 media_close(media);
380                 printf("%s: Can't open media device %s\n", __func__, name);
381                 return NULL;
382         }
383
384         if (verbose)
385                 printf("Enumerating entities\n");
386
387         ret = media_enum_entities(media);
388         if (ret < 0) {
389                 printf("%s: Unable to enumerate entities for device %s (%s)\n",
390                         __func__, name, strerror(ret));
391                 media_close(media);
392                 return NULL;
393         }
394
395         if (verbose) {
396                 printf("Found %u entities\n", media->entities_count);
397                 printf("Enumerating pads and links\n");
398         }
399
400         ret = media_enum_links(media);
401         if (ret < 0) {
402                 printf("%s: Unable to enumerate pads and linksfor device %s\n",
403                         __func__, name);
404                 media_close(media);
405                 return NULL;
406         }
407
408         return media;
409 }
410
411 /*
412  * media_close -
413  */
414 void media_close(struct media_device *media)
415 {
416         unsigned int i;
417
418         if (media->fd != -1)
419                 close(media->fd);
420
421         for (i = 0; i < media->entities_count; ++i) {
422                 struct media_entity *entity = &media->entities[i];
423
424                 free(entity->pads);
425                 free(entity->links);
426                 if (entity->fd != -1)
427                         close(entity->fd);
428         }
429
430         free(media->entities);
431         free(media);
432 }
433