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