bc6a713cf52807f42a39aeca0fc7e2b9746fe006
[media-ctl.git] / src / mediactl.c
1 /*
2  * Media controller interface library
3  *
4  * Copyright (C) 2010-2011 Ideas on board SPRL
5  *
6  * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published
10  * by the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "config.h"
23
24 #include <sys/ioctl.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <errno.h>
34
35 #include <linux/videodev2.h>
36 #include <linux/media.h>
37
38 #include "mediactl.h"
39 #include "tools.h"
40
41 struct media_pad *media_entity_remote_source(struct media_pad *pad)
42 {
43         unsigned int i;
44
45         if (!(pad->flags & MEDIA_PAD_FL_SINK))
46                 return NULL;
47
48         for (i = 0; i < pad->entity->num_links; ++i) {
49                 struct media_link *link = &pad->entity->links[i];
50
51                 if (!(link->flags & MEDIA_LNK_FL_ENABLED))
52                         continue;
53
54                 if (link->sink == pad)
55                         return link->source;
56         }
57
58         return NULL;
59 }
60
61 struct media_entity *media_get_entity_by_name(struct media_device *media,
62                                               const char *name, size_t length)
63 {
64         unsigned int i;
65
66         /* A match is impossible if the entity name is longer than the maximum
67          * size we can get from the kernel.
68          */
69         if (length >= FIELD_SIZEOF(struct media_entity_desc, name))
70                 return NULL;
71
72         for (i = 0; i < media->entities_count; ++i) {
73                 struct media_entity *entity = &media->entities[i];
74
75                 if (strncmp(entity->info.name, name, length) == 0 &&
76                     entity->info.name[length] == '\0')
77                         return entity;
78         }
79
80         return NULL;
81 }
82
83 struct media_entity *media_get_entity_by_id(struct media_device *media,
84                                             __u32 id)
85 {
86         unsigned int i;
87
88         for (i = 0; i < media->entities_count; ++i) {
89                 struct media_entity *entity = &media->entities[i];
90
91                 if (entity->info.id == id)
92                         return entity;
93         }
94
95         return NULL;
96 }
97
98 int media_setup_link(struct media_device *media,
99                      struct media_pad *source,
100                      struct media_pad *sink,
101                      __u32 flags)
102 {
103         struct media_link *link;
104         struct media_link_desc ulink;
105         unsigned int i;
106         int ret;
107
108         for (i = 0; i < source->entity->num_links; i++) {
109                 link = &source->entity->links[i];
110
111                 if (link->source->entity == source->entity &&
112                     link->source->index == source->index &&
113                     link->sink->entity == sink->entity &&
114                     link->sink->index == sink->index)
115                         break;
116         }
117
118         if (i == source->entity->num_links) {
119                 media_dbg(media, "%s: Link not found\n", __func__);
120                 return -ENOENT;
121         }
122
123         /* source pad */
124         ulink.source.entity = source->entity->info.id;
125         ulink.source.index = source->index;
126         ulink.source.flags = MEDIA_PAD_FL_SOURCE;
127
128         /* sink pad */
129         ulink.sink.entity = sink->entity->info.id;
130         ulink.sink.index = sink->index;
131         ulink.sink.flags = MEDIA_PAD_FL_SINK;
132
133         ulink.flags = flags | (link->flags & MEDIA_LNK_FL_IMMUTABLE);
134
135         ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink);
136         if (ret == -1) {
137                 media_dbg(media, "%s: Unable to setup link (%s)\n",
138                           __func__, strerror(errno));
139                 return -errno;
140         }
141
142         link->flags = ulink.flags;
143         link->twin->flags = ulink.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_link *link = &entity->links[j];
157
158                         if (link->flags & MEDIA_LNK_FL_IMMUTABLE ||
159                             link->source->entity != entity)
160                                 continue;
161
162                         ret = media_setup_link(media, link->source, link->sink,
163                                                link->flags & ~MEDIA_LNK_FL_ENABLED);
164                         if (ret < 0)
165                                 return ret;
166                 }
167         }
168
169         return 0;
170 }
171
172 static struct media_link *media_entity_add_link(struct media_entity *entity)
173 {
174         if (entity->num_links >= entity->max_links) {
175                 struct media_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                         media_dbg(media,
209                                   "%s: Unable to enumerate pads and links (%s).\n",
210                                   __func__, strerror(errno));
211                         free(links.pads);
212                         free(links.links);
213                         return -errno;
214                 }
215
216                 for (i = 0; i < entity->info.pads; ++i) {
217                         entity->pads[i].entity = entity;
218                         entity->pads[i].index = links.pads[i].index;
219                         entity->pads[i].flags = links.pads[i].flags;
220                 }
221
222                 for (i = 0; i < entity->info.links; ++i) {
223                         struct media_link_desc *link = &links.links[i];
224                         struct media_link *fwdlink;
225                         struct media_link *backlink;
226                         struct media_entity *source;
227                         struct media_entity *sink;
228
229                         source = media_get_entity_by_id(media, link->source.entity);
230                         sink = media_get_entity_by_id(media, link->sink.entity);
231
232                         if (source == NULL || sink == NULL) {
233                                 media_dbg(media,
234                                           "WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
235                                           id, i, link->source.entity,
236                                           link->source.index,
237                                           link->sink.entity,
238                                           link->sink.index);
239                                 ret = -EINVAL;
240                         } else {
241                                 fwdlink = media_entity_add_link(source);
242                                 fwdlink->source = &source->pads[link->source.index];
243                                 fwdlink->sink = &sink->pads[link->sink.index];
244                                 fwdlink->flags = link->flags;
245
246                                 backlink = media_entity_add_link(sink);
247                                 backlink->source = &source->pads[link->source.index];
248                                 backlink->sink = &sink->pads[link->sink.index];
249                                 backlink->flags = link->flags;
250
251                                 fwdlink->twin = backlink;
252                                 backlink->twin = fwdlink;
253                         }
254                 }
255
256                 free(links.pads);
257                 free(links.links);
258         }
259
260         return ret;
261 }
262
263 #ifdef HAVE_LIBUDEV
264
265 #include <libudev.h>
266
267 static inline int media_udev_open(struct udev **udev)
268 {
269         *udev = udev_new();
270         if (*udev == NULL)
271                 return -ENOMEM;
272         return 0;
273 }
274
275 static inline void media_udev_close(struct udev *udev)
276 {
277         if (udev != NULL)
278                 udev_unref(udev);
279 }
280
281 static int media_get_devname_udev(struct udev *udev,
282                 struct media_entity *entity)
283 {
284         struct udev_device *device;
285         dev_t devnum;
286         const char *p;
287         int ret = -ENODEV;
288
289         if (udev == NULL)
290                 return -EINVAL;
291
292         devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
293         media_dbg(entity->media, "looking up device: %u:%u\n",
294                   major(devnum), minor(devnum));
295         device = udev_device_new_from_devnum(udev, 'c', devnum);
296         if (device) {
297                 p = udev_device_get_devnode(device);
298                 if (p) {
299                         strncpy(entity->devname, p, sizeof(entity->devname));
300                         entity->devname[sizeof(entity->devname) - 1] = '\0';
301                 }
302                 ret = 0;
303         }
304
305         udev_device_unref(device);
306
307         return ret;
308 }
309
310 #else   /* HAVE_LIBUDEV */
311
312 struct udev;
313
314 static inline int media_udev_open(struct udev **udev) { return 0; }
315
316 static inline void media_udev_close(struct udev *udev) { }
317
318 static inline int media_get_devname_udev(struct udev *udev,
319                 struct media_entity *entity)
320 {
321         return -ENOTSUP;
322 }
323
324 #endif  /* HAVE_LIBUDEV */
325
326 static int media_get_devname_sysfs(struct media_entity *entity)
327 {
328         struct stat devstat;
329         char devname[32];
330         char sysname[32];
331         char target[1024];
332         char *p;
333         int ret;
334
335         sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major,
336                 entity->info.v4l.minor);
337         ret = readlink(sysname, target, sizeof(target));
338         if (ret < 0)
339                 return -errno;
340
341         target[ret] = '\0';
342         p = strrchr(target, '/');
343         if (p == NULL)
344                 return -EINVAL;
345
346         sprintf(devname, "/dev/%s", p + 1);
347         ret = stat(devname, &devstat);
348         if (ret < 0)
349                 return -errno;
350
351         /* Sanity check: udev might have reordered the device nodes.
352          * Make sure the major/minor match. We should really use
353          * libudev.
354          */
355         if (major(devstat.st_rdev) == entity->info.v4l.major &&
356             minor(devstat.st_rdev) == entity->info.v4l.minor)
357                 strcpy(entity->devname, devname);
358
359         return 0;
360 }
361
362 static int media_enum_entities(struct media_device *media)
363 {
364         struct media_entity *entity;
365         struct udev *udev;
366         unsigned int size;
367         __u32 id;
368         int ret;
369
370         ret = media_udev_open(&udev);
371         if (ret < 0)
372                 media_dbg(media, "Can't get udev context\n");
373
374         for (id = 0, ret = 0; ; id = entity->info.id) {
375                 size = (media->entities_count + 1) * sizeof(*media->entities);
376                 media->entities = realloc(media->entities, size);
377
378                 entity = &media->entities[media->entities_count];
379                 memset(entity, 0, sizeof(*entity));
380                 entity->fd = -1;
381                 entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT;
382                 entity->media = media;
383
384                 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
385                 if (ret < 0) {
386                         ret = errno != EINVAL ? -errno : 0;
387                         break;
388                 }
389
390                 /* Number of links (for outbound links) plus number of pads (for
391                  * inbound links) is a good safe initial estimate of the total
392                  * number of links.
393                  */
394                 entity->max_links = entity->info.pads + entity->info.links;
395
396                 entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));
397                 entity->links = malloc(entity->max_links * sizeof(*entity->links));
398                 if (entity->pads == NULL || entity->links == NULL) {
399                         ret = -ENOMEM;
400                         break;
401                 }
402
403                 media->entities_count++;
404
405                 /* Find the corresponding device name. */
406                 if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE &&
407                     media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
408                         continue;
409
410                 /* Try to get the device name via udev */
411                 if (!media_get_devname_udev(udev, entity))
412                         continue;
413
414                 /* Fall back to get the device name via sysfs */
415                 media_get_devname_sysfs(entity);
416         }
417
418         media_udev_close(udev);
419         return ret;
420 }
421
422 static void media_debug_default(void *ptr, ...)
423 {
424 }
425
426 void media_debug_set_handler(struct media_device *media,
427                              void (*debug_handler)(void *, ...),
428                              void *debug_priv)
429 {
430         if (debug_handler) {
431                 media->debug_handler = debug_handler;
432                 media->debug_priv = debug_priv;
433         } else {
434                 media->debug_handler = media_debug_default;
435                 media->debug_priv = NULL;
436         }
437 }
438
439 struct media_device *media_open_debug(
440         const char *name, void (*debug_handler)(void *, ...),
441         void *debug_priv)
442 {
443         struct media_device *media;
444         int ret;
445
446         media = calloc(1, sizeof(*media));
447         if (media == NULL)
448                 return NULL;
449
450         media_debug_set_handler(media, debug_handler, debug_priv);
451
452         media_dbg(media, "Opening media device %s\n", name);
453
454         media->fd = open(name, O_RDWR);
455         if (media->fd < 0) {
456                 media_close(media);
457                 media_dbg(media, "%s: Can't open media device %s\n",
458                           __func__, name);
459                 return NULL;
460         }
461
462         ret = ioctl(media->fd, MEDIA_IOC_DEVICE_INFO, &media->info);
463         if (ret < 0) {
464                 media_dbg(media, "%s: Unable to retrieve media device "
465                           "information for device %s (%s)\n", __func__,
466                           name, strerror(errno));
467                 media_close(media);
468                 return NULL;
469         }
470
471         media_dbg(media, "Enumerating entities\n");
472
473         ret = media_enum_entities(media);
474
475         if (ret < 0) {
476                 media_dbg(media,
477                           "%s: Unable to enumerate entities for device %s (%s)\n",
478                           __func__, name, strerror(-ret));
479                 media_close(media);
480                 return NULL;
481         }
482
483         media_dbg(media, "Found %u entities\n", media->entities_count);
484         media_dbg(media, "Enumerating pads and links\n");
485
486         ret = media_enum_links(media);
487         if (ret < 0) {
488                 media_dbg(media,
489                           "%s: Unable to enumerate pads and linksfor device %s\n",
490                           __func__, name);
491                 media_close(media);
492                 return NULL;
493         }
494
495         return media;
496 }
497
498 struct media_device *media_open(const char *name)
499 {
500         return media_open_debug(name, NULL, NULL);
501 }
502
503 void media_close(struct media_device *media)
504 {
505         unsigned int i;
506
507         if (media->fd != -1)
508                 close(media->fd);
509
510         for (i = 0; i < media->entities_count; ++i) {
511                 struct media_entity *entity = &media->entities[i];
512
513                 free(entity->pads);
514                 free(entity->links);
515                 if (entity->fd != -1)
516                         close(entity->fd);
517         }
518
519         free(media->entities);
520         free(media);
521 }
522
523 struct media_pad *media_parse_pad(struct media_device *media,
524                                   const char *p, char **endp)
525 {
526         unsigned int entity_id, pad;
527         struct media_entity *entity;
528         char *end;
529
530         for (; isspace(*p); ++p);
531
532         if (*p == '"') {
533                 for (end = (char *)p + 1; *end && *end != '"'; ++end);
534                 if (*end != '"')
535                         return NULL;
536
537                 entity = media_get_entity_by_name(media, p + 1, end - p - 1);
538                 if (entity == NULL)
539                         return NULL;
540
541                 ++end;
542         } else {
543                 entity_id = strtoul(p, &end, 10);
544                 entity = media_get_entity_by_id(media, entity_id);
545                 if (entity == NULL)
546                         return NULL;
547         }
548         for (; isspace(*end); ++end);
549
550         if (*end != ':')
551                 return NULL;
552         for (p = end + 1; isspace(*p); ++p);
553
554         pad = strtoul(p, &end, 10);
555         for (p = end; isspace(*p); ++p);
556
557         if (pad >= entity->info.pads)
558                 return NULL;
559
560         for (p = end; isspace(*p); ++p);
561         if (endp)
562                 *endp = (char *)p;
563
564         return &entity->pads[pad];
565 }
566
567 struct media_link *media_parse_link(struct media_device *media,
568                                     const char *p, char **endp)
569 {
570         struct media_link *link;
571         struct media_pad *source;
572         struct media_pad *sink;
573         unsigned int i;
574         char *end;
575
576         source = media_parse_pad(media, p, &end);
577         if (source == NULL)
578                 return NULL;
579
580         if (end[0] != '-' || end[1] != '>')
581                 return NULL;
582         p = end + 2;
583
584         sink = media_parse_pad(media, p, &end);
585         if (sink == NULL)
586                 return NULL;
587
588         *endp = end;
589
590         for (i = 0; i < source->entity->num_links; i++) {
591                 link = &source->entity->links[i];
592
593                 if (link->source == source && link->sink == sink)
594                         return link;
595         }
596
597         return NULL;
598 }
599
600 int media_parse_setup_link(struct media_device *media,
601                            const char *p, char **endp)
602 {
603         struct media_link *link;
604         __u32 flags;
605         char *end;
606
607         link = media_parse_link(media, p, &end);
608         if (link == NULL) {
609                 media_dbg(media,
610                           "%s: Unable to parse link\n", __func__);
611                 return -EINVAL;
612         }
613
614         p = end;
615         if (*p++ != '[') {
616                 media_dbg(media, "Unable to parse link flags\n");
617                 return -EINVAL;
618         }
619
620         flags = strtoul(p, &end, 10);
621         for (p = end; isspace(*p); p++);
622         if (*p++ != ']') {
623                 media_dbg(media, "Unable to parse link flags\n");
624                 return -EINVAL;
625         }
626
627         for (; isspace(*p); p++);
628         *endp = (char *)p;
629
630         media_dbg(media,
631                   "Setting up link %u:%u -> %u:%u [%u]\n",
632                   link->source->entity->info.id, link->source->index,
633                   link->sink->entity->info.id, link->sink->index,
634                   flags);
635
636         return media_setup_link(media, link->source, link->sink, flags);
637 }
638
639 int media_parse_setup_links(struct media_device *media, const char *p)
640 {
641         char *end;
642         int ret;
643
644         do {
645                 ret = media_parse_setup_link(media, p, &end);
646                 if (ret < 0)
647                         return ret;
648
649                 p = end + 1;
650         } while (*end == ',');
651
652         return *end ? -EINVAL : 0;
653 }