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