Move printing functions to main.c
[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 /*
38  * media_entity_remote_pad -
39  */
40 struct media_entity_pad *media_entity_remote_pad(struct media_entity_pad *pad)
41 {
42         unsigned int i;
43
44         for (i = 0; i < pad->entity->info.links; ++i) {
45                 struct media_entity_link *link = &pad->entity->links[i];
46
47                 if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE))
48                         continue;
49
50                 if (link->source == pad)
51                         return link->sink;
52
53                 if (link->sink == pad)
54                         return link->source;
55         }
56
57         return NULL;
58 }
59
60 /*
61  * media_get_entity_by_name -
62  */
63 struct media_entity *media_get_entity_by_name(struct media_device *media,
64                                               const char *name, size_t length)
65 {
66         unsigned int i;
67
68         for (i = 0; i < media->entities_count; ++i) {
69                 struct media_entity *entity = &media->entities[i];
70
71                 if (strncmp(entity->info.name, name, length) == 0)
72                         return entity;
73         }
74
75         return NULL;
76 }
77
78 /*
79  * media_get_entity_by_id -
80  */
81 struct media_entity *media_get_entity_by_id(struct media_device *media,
82                                             __u32 id)
83 {
84         unsigned int i;
85
86         for (i = 0; i < media->entities_count; ++i) {
87                 struct media_entity *entity = &media->entities[i];
88
89                 if (entity->info.id == id)
90                         return entity;
91         }
92
93         return NULL;
94 }
95
96 /*
97  * media_setup_link -
98  */
99 int media_setup_link(struct media_device *media,
100                      struct media_entity_pad *source,
101                      struct media_entity_pad *sink,
102                      __u32 flags)
103 {
104         struct media_entity_link *link;
105         struct media_link_desc ulink;
106         unsigned int i;
107         int ret;
108
109         for (i = 0; i < source->entity->info.links; i++) {
110                 link = &source->entity->links[i];
111
112                 if (link->source->entity == source->entity &&
113                     link->source->index == source->index &&
114                     link->sink->entity == sink->entity &&
115                     link->sink->index == sink->index)
116                         break;
117         }
118
119         if (i == source->entity->info.links) {
120                 printf("%s: Link not found\n", __func__);
121                 return -EINVAL;
122         }
123
124         /* source pad */
125         ulink.source.entity = source->entity->info.id;
126         ulink.source.index = source->index;
127         ulink.source.flags = MEDIA_PAD_FLAG_OUTPUT;
128
129         /* sink pad */
130         ulink.sink.entity = sink->entity->info.id;
131         ulink.sink.index = sink->index;
132         ulink.sink.flags = MEDIA_PAD_FLAG_INPUT;
133
134         ulink.flags = flags | (link->flags & MEDIA_LINK_FLAG_IMMUTABLE);
135
136         ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink);
137         if (ret < 0) {
138                 printf("%s: Unable to setup link (%s)\n", __func__,
139                         strerror(errno));
140                 return ret;
141         }
142
143         link->flags = flags;
144         return 0;
145 }
146
147 int media_reset_links(struct media_device *media)
148 {
149         unsigned int i, j;
150         int ret;
151
152         for (i = 0; i < media->entities_count; ++i) {
153                 struct media_entity *entity = &media->entities[i];
154
155                 for (j = 0; j < entity->info.links; j++) {
156                         struct media_entity_link *link = &entity->links[j];
157
158                         if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE)
159                                 continue;
160
161                         ret = media_setup_link(media, link->source, link->sink,
162                                                link->flags & ~MEDIA_LINK_FLAG_ACTIVE);
163                         if (ret < 0)
164                                 return ret;
165                 }
166         }
167
168         return 0;
169 }
170
171 static int media_enum_links(struct media_device *media)
172 {
173         __u32 id;
174         int ret = 0;
175
176         for (id = 1; id <= media->entities_count; id++) {
177                 struct media_entity *entity = &media->entities[id - 1];
178                 struct media_links_enum links;
179                 unsigned int i;
180
181                 links.entity = entity->info.id;
182                 links.pads = malloc(entity->info.pads * sizeof(struct media_pad_desc));
183                 links.links = malloc(entity->info.links * sizeof(struct media_link_desc));
184
185                 if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
186                         printf("%s: Unable to enumerate pads and links (%s).\n",
187                                 __func__, strerror(errno));
188                         free(links.pads);
189                         free(links.links);
190                         return -errno;
191                 }
192
193                 for (i = 0; i < entity->info.pads; ++i) {
194                         entity->pads[i].entity = entity;
195                         entity->pads[i].index = links.pads[i].index;
196                         entity->pads[i].flags = links.pads[i].flags;
197                 }
198
199                 for (i = 0; i < entity->info.links; ++i) {
200                         struct media_link_desc *link = &links.links[i];
201                         struct media_entity *source;
202                         struct media_entity *sink;
203
204                         source = media_get_entity_by_id(media, link->source.entity);
205                         sink = media_get_entity_by_id(media, link->sink.entity);
206
207                         if (source == NULL || sink == NULL) {
208                                 printf("WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
209                                         id, i, link->source.entity, link->source.index,
210                                         link->sink.entity, link->sink.index);
211                                 ret = -EINVAL;
212                         } else {
213                                 entity->links[i].source = &source->pads[link->source.index];
214                                 entity->links[i].sink = &sink->pads[link->sink.index];
215                                 entity->links[i].flags = links.links[i].flags;
216                         }
217                 }
218
219                 free(links.pads);
220                 free(links.links);
221         }
222
223         return ret;
224 }
225
226 static int media_enum_entities(struct media_device *media)
227 {
228         struct media_entity *entity;
229         struct stat devstat;
230         unsigned int size;
231         char devname[32];
232         char sysname[32];
233         char target[1024];
234         char *p;
235         __u32 id;
236         int ret;
237
238         for (id = 0; ; id = entity->info.id) {
239                 size = (media->entities_count + 1) * sizeof(*media->entities);
240                 media->entities = realloc(media->entities, size);
241
242                 entity = &media->entities[media->entities_count];
243                 memset(entity, 0, sizeof(*entity));
244                 entity->fd = -1;
245                 entity->info.id = id | MEDIA_ENTITY_ID_FLAG_NEXT;
246
247                 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
248                 if (ret < 0) {
249                         if (errno == EINVAL)
250                                 break;
251                         return -errno;
252                 }
253
254                 entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));
255                 entity->links = malloc(entity->info.links * sizeof(*entity->links));
256                 if (entity->pads == NULL || entity->links == NULL)
257                         return -ENOMEM;
258
259                 media->entities_count++;
260
261                 /* Find the corresponding device name. */
262                 if (media_entity_type(entity) != MEDIA_ENTITY_TYPE_NODE &&
263                     media_entity_type(entity) != MEDIA_ENTITY_TYPE_SUBDEV)
264                         continue;
265
266                 sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major,
267                         entity->info.v4l.minor);
268                 ret = readlink(sysname, target, sizeof(target));
269                 if (ret < 0)
270                         continue;
271
272                 target[ret] = '\0';
273                 p = strrchr(target, '/');
274                 if (p == NULL)
275                         continue;
276
277                 sprintf(devname, "/dev/%s", p + 1);
278                 ret = stat(devname, &devstat);
279                 if (ret < 0)
280                         continue;
281
282                 /* Sanity check: udev might have reordered the device nodes.
283                  * Make sure the major/minor match. We should really use
284                  * libudev.
285                  */
286                 if (major(devstat.st_rdev) == entity->info.v4l.major &&
287                     minor(devstat.st_rdev) == entity->info.v4l.minor)
288                         strcpy(entity->devname, devname);
289         }
290
291         return 0;
292 }
293
294 /*
295  * media_open -
296  */
297 struct media_device *media_open(const char *name, int verbose)
298 {
299         struct media_device *media;
300         int ret;
301
302         media = malloc(sizeof(*media));
303         if (media == NULL) {
304                 printf("%s: unable to allocate memory\n", __func__);
305                 return NULL;
306         }
307         memset(media, 0, sizeof(*media));
308
309         if (verbose)
310                 printf("Opening media device %s\n", name);
311         media->fd = open(name, O_RDWR);
312         if (media->fd < 0) {
313                 media_close(media);
314                 printf("%s: Can't open media device %s\n", __func__, name);
315                 return NULL;
316         }
317
318         if (verbose)
319                 printf("Enumerating entities\n");
320
321         ret = media_enum_entities(media);
322         if (ret < 0) {
323                 printf("%s: Unable to enumerate entities for device %s (%s)\n",
324                         __func__, name, strerror(-ret));
325                 media_close(media);
326                 return NULL;
327         }
328
329         if (verbose) {
330                 printf("Found %u entities\n", media->entities_count);
331                 printf("Enumerating pads and links\n");
332         }
333
334         ret = media_enum_links(media);
335         if (ret < 0) {
336                 printf("%s: Unable to enumerate pads and linksfor device %s\n",
337                         __func__, name);
338                 media_close(media);
339                 return NULL;
340         }
341
342         return media;
343 }
344
345 /*
346  * media_close -
347  */
348 void media_close(struct media_device *media)
349 {
350         unsigned int i;
351
352         if (media->fd != -1)
353                 close(media->fd);
354
355         for (i = 0; i < media->entities_count; ++i) {
356                 struct media_entity *entity = &media->entities[i];
357
358                 free(entity->pads);
359                 free(entity->links);
360                 if (entity->fd != -1)
361                         close(entity->fd);
362         }
363
364         free(media->entities);
365         free(media);
366 }
367