Add v4l2_subdev_open() and v4l2_subdev_close() to subdev.h
[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_source(struct media_entity_pad *pad)
41 {
42         unsigned int i;
43
44         if (!(pad->flags & MEDIA_PAD_FLAG_INPUT))
45                 return NULL;
46
47         for (i = 0; i < pad->entity->num_links; ++i) {
48                 struct media_entity_link *link = &pad->entity->links[i];
49
50                 if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE))
51                         continue;
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->num_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->num_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->num_links; j++) {
156                         struct media_entity_link *link = &entity->links[j];
157
158                         if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE ||
159                             link->source->entity != entity)
160                                 continue;
161
162                         ret = media_setup_link(media, link->source, link->sink,
163                                                link->flags & ~MEDIA_LINK_FLAG_ACTIVE);
164                         if (ret < 0)
165                                 return ret;
166                 }
167         }
168
169         return 0;
170 }
171
172 static struct media_entity_link *media_entity_add_link(struct media_entity *entity)
173 {
174         if (entity->num_links >= entity->max_links) {
175                 struct media_entity_link *links = entity->links;
176                 unsigned int max_links = entity->max_links * 2;
177                 unsigned int i;
178
179                 links = realloc(links, max_links * sizeof *links);
180                 if (links == NULL)
181                         return NULL;
182
183                 for (i = 0; i < entity->num_links; ++i)
184                         links[i].twin->twin = &links[i];
185
186                 entity->max_links = max_links;
187                 entity->links = links;
188         }
189
190         return &entity->links[entity->num_links++];
191 }
192
193 static int media_enum_links(struct media_device *media)
194 {
195         __u32 id;
196         int ret = 0;
197
198         for (id = 1; id <= media->entities_count; id++) {
199                 struct media_entity *entity = &media->entities[id - 1];
200                 struct media_links_enum links;
201                 unsigned int i;
202
203                 links.entity = entity->info.id;
204                 links.pads = malloc(entity->info.pads * sizeof(struct media_pad_desc));
205                 links.links = malloc(entity->info.links * sizeof(struct media_link_desc));
206
207                 if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
208                         printf("%s: Unable to enumerate pads and links (%s).\n",
209                                 __func__, strerror(errno));
210                         free(links.pads);
211                         free(links.links);
212                         return -errno;
213                 }
214
215                 for (i = 0; i < entity->info.pads; ++i) {
216                         entity->pads[i].entity = entity;
217                         entity->pads[i].index = links.pads[i].index;
218                         entity->pads[i].flags = links.pads[i].flags;
219                 }
220
221                 for (i = 0; i < entity->info.links; ++i) {
222                         struct media_link_desc *link = &links.links[i];
223                         struct media_entity_link *fwdlink;
224                         struct media_entity_link *backlink;
225                         struct media_entity *source;
226                         struct media_entity *sink;
227
228                         source = media_get_entity_by_id(media, link->source.entity);
229                         sink = media_get_entity_by_id(media, link->sink.entity);
230
231                         if (source == NULL || sink == NULL) {
232                                 printf("WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
233                                         id, i, link->source.entity, link->source.index,
234                                         link->sink.entity, link->sink.index);
235                                 ret = -EINVAL;
236                         } else {
237                                 fwdlink = media_entity_add_link(source);
238                                 fwdlink->source = &source->pads[link->source.index];
239                                 fwdlink->sink = &sink->pads[link->sink.index];
240                                 fwdlink->flags = links.links[i].flags;
241
242                                 backlink = media_entity_add_link(sink);
243                                 backlink->source = &source->pads[link->source.index];
244                                 backlink->sink = &sink->pads[link->sink.index];
245                                 backlink->flags = links.links[i].flags;
246
247                                 fwdlink->twin = backlink;
248                                 backlink->twin = fwdlink;
249                         }
250                 }
251
252                 free(links.pads);
253                 free(links.links);
254         }
255
256         return ret;
257 }
258
259 static int media_enum_entities(struct media_device *media)
260 {
261         struct media_entity *entity;
262         struct stat devstat;
263         unsigned int size;
264         char devname[32];
265         char sysname[32];
266         char target[1024];
267         char *p;
268         __u32 id;
269         int ret;
270
271         for (id = 0; ; id = entity->info.id) {
272                 size = (media->entities_count + 1) * sizeof(*media->entities);
273                 media->entities = realloc(media->entities, size);
274
275                 entity = &media->entities[media->entities_count];
276                 memset(entity, 0, sizeof(*entity));
277                 entity->fd = -1;
278                 entity->info.id = id | MEDIA_ENTITY_ID_FLAG_NEXT;
279
280                 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
281                 if (ret < 0) {
282                         if (errno == EINVAL)
283                                 break;
284                         return -errno;
285                 }
286
287                 /* Number of links (for outbound links) plus number of pads (for
288                  * inbound links) is a good safe initial estimate of the total
289                  * number of links.
290                  */
291                 entity->max_links = entity->info.pads + entity->info.links;
292
293                 entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));
294                 entity->links = malloc(entity->max_links * sizeof(*entity->links));
295                 if (entity->pads == NULL || entity->links == NULL)
296                         return -ENOMEM;
297
298                 media->entities_count++;
299
300                 /* Find the corresponding device name. */
301                 if (media_entity_type(entity) != MEDIA_ENTITY_TYPE_NODE &&
302                     media_entity_type(entity) != MEDIA_ENTITY_TYPE_SUBDEV)
303                         continue;
304
305                 sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major,
306                         entity->info.v4l.minor);
307                 ret = readlink(sysname, target, sizeof(target));
308                 if (ret < 0)
309                         continue;
310
311                 target[ret] = '\0';
312                 p = strrchr(target, '/');
313                 if (p == NULL)
314                         continue;
315
316                 sprintf(devname, "/dev/%s", p + 1);
317                 ret = stat(devname, &devstat);
318                 if (ret < 0)
319                         continue;
320
321                 /* Sanity check: udev might have reordered the device nodes.
322                  * Make sure the major/minor match. We should really use
323                  * libudev.
324                  */
325                 if (major(devstat.st_rdev) == entity->info.v4l.major &&
326                     minor(devstat.st_rdev) == entity->info.v4l.minor)
327                         strcpy(entity->devname, devname);
328         }
329
330         return 0;
331 }
332
333 /*
334  * media_open -
335  */
336 struct media_device *media_open(const char *name, int verbose)
337 {
338         struct media_device *media;
339         int ret;
340
341         media = malloc(sizeof(*media));
342         if (media == NULL) {
343                 printf("%s: unable to allocate memory\n", __func__);
344                 return NULL;
345         }
346         memset(media, 0, sizeof(*media));
347
348         if (verbose)
349                 printf("Opening media device %s\n", name);
350         media->fd = open(name, O_RDWR);
351         if (media->fd < 0) {
352                 media_close(media);
353                 printf("%s: Can't open media device %s\n", __func__, name);
354                 return NULL;
355         }
356
357         if (verbose)
358                 printf("Enumerating entities\n");
359
360         ret = media_enum_entities(media);
361         if (ret < 0) {
362                 printf("%s: Unable to enumerate entities for device %s (%s)\n",
363                         __func__, name, strerror(-ret));
364                 media_close(media);
365                 return NULL;
366         }
367
368         if (verbose) {
369                 printf("Found %u entities\n", media->entities_count);
370                 printf("Enumerating pads and links\n");
371         }
372
373         ret = media_enum_links(media);
374         if (ret < 0) {
375                 printf("%s: Unable to enumerate pads and linksfor device %s\n",
376                         __func__, name);
377                 media_close(media);
378                 return NULL;
379         }
380
381         return media;
382 }
383
384 /*
385  * media_close -
386  */
387 void media_close(struct media_device *media)
388 {
389         unsigned int i;
390
391         if (media->fd != -1)
392                 close(media->fd);
393
394         for (i = 0; i < media->entities_count; ++i) {
395                 struct media_entity *entity = &media->entities[i];
396
397                 free(entity->pads);
398                 free(entity->links);
399                 if (entity->fd != -1)
400                         close(entity->fd);
401         }
402
403         free(media->entities);
404         free(media);
405 }
406