blob: 82aa90ac9fb6856599e7cbf0d6a75acbcba0589b [file] [log] [blame]
codeworkx62f02ba2012-05-20 12:00:36 +02001/*
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"
Daniel Hillenbrand353fecb2012-07-22 16:14:08 +020023#define LOG_NDEBUG 0
24#define LOG_TAG "Mediactl"
codeworkx62f02ba2012-05-20 12:00:36 +020025
26#include <utils/Log.h>
27#include <sys/ioctl.h>
28#include <sys/stat.h>
29#include <sys/types.h>
30#include <sys/mman.h>
31#include <sys/time.h>
32
33#include <poll.h>
34#include <signal.h>
35#include <unistd.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <fcntl.h>
40#include <errno.h>
41#include <ctype.h>
42#include <stdarg.h>
43#include <linux/videodev2.h>
44#include <media.h>
45#include <linux/kdev_t.h>
46#include <linux/types.h>
47
48#include "mediactl.h"
49
50#define KF_MSG 0x1
51#define KF_ANY 0x2
52
53#define perror_exit(cond, func) \
54 if (cond) { \
55 fprintf(stderr, "[%s:%d]: ", __func__, __LINE__);\
56 perror(func);\
57 exit(EXIT_FAILURE);\
58 }
59
60struct media_pad *media_entity_remote_source(struct media_pad *pad)
61{
62 unsigned int i;
63
64 if (!(pad->flags & MEDIA_PAD_FL_SINK))
65 return NULL;
66
67 for (i = 0; i < pad->entity->num_links; ++i) {
68 struct media_link *link = &pad->entity->links[i];
69
70 if (!(link->flags & MEDIA_LNK_FL_ENABLED))
71 continue;
72
73 if (link->sink == pad)
74 return link->source;
75 }
76
77 return NULL;
78}
79
80struct media_entity *media_get_entity_by_name(struct media_device *media,
81 const char *name, size_t length)
82{
83 unsigned int i;
84 struct media_entity *entity;
85 entity = (struct media_entity*)calloc(1, sizeof(struct media_entity));
86 for (i = 0; i < media->entities_count; ++i) {
87 entity = &media->entities[i];
88
89 if (strncmp(entity->info.name, name, length) == 0)
90 return entity;
91 }
92
93 return NULL;
94}
95
96struct media_entity *media_get_entity_by_id(struct media_device *media,
97 __u32 id)
98{
99 unsigned int i;
100
101 for (i = 0; i < media->entities_count; ++i) {
102 struct media_entity *entity = &media->entities[i];
103
104 if (entity->info.id == id)
105 return entity;
106 }
107
108 return NULL;
109}
110
111int media_setup_link(struct media_device *media,
112 struct media_pad *source,
113 struct media_pad *sink,
114 __u32 flags)
115{
116 struct media_link *link;
117 struct media_link_desc ulink;
118 unsigned int i;
119 int ret;
120
121 for (i = 0; i < source->entity->num_links; i++) {
122 link = &source->entity->links[i];
123
124 if (link->source->entity == source->entity &&
125 link->source->index == source->index &&
126 link->sink->entity == sink->entity &&
127 link->sink->index == sink->index)
128 break;
129 }
130
131 if (i == source->entity->num_links) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200132 ALOGE("%s: Link not found", __func__);
codeworkx62f02ba2012-05-20 12:00:36 +0200133 return -ENOENT;
134 }
135
136 /* source pad */
137 ulink.source.entity = source->entity->info.id;
138 ulink.source.index = source->index;
139 ulink.source.flags = MEDIA_PAD_FL_SOURCE;
140
141 /* sink pad */
142 ulink.sink.entity = sink->entity->info.id;
143 ulink.sink.index = sink->index;
144 ulink.sink.flags = MEDIA_PAD_FL_SINK;
145
146 ulink.flags = flags | (link->flags & MEDIA_LNK_FL_IMMUTABLE);
147
148 ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink);
149 if (ret == -1) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200150 ALOGE("%s: Unable to setup link (%s)",
codeworkx62f02ba2012-05-20 12:00:36 +0200151 __func__, strerror(errno));
152 return -errno;
153 }
154
155 link->flags = ulink.flags;
156 link->twin->flags = ulink.flags;
157 return 0;
158}
159
160int media_reset_links(struct media_device *media)
161{
162 unsigned int i, j;
163 int ret;
164
165 for (i = 0; i < media->entities_count; ++i) {
166 struct media_entity *entity = &media->entities[i];
167
168 for (j = 0; j < entity->num_links; j++) {
169 struct media_link *link = &entity->links[j];
170
171 if (link->flags & MEDIA_LNK_FL_IMMUTABLE ||
172 link->source->entity != entity)
173 continue;
174
175 ret = media_setup_link(media, link->source, link->sink,
176 link->flags & ~MEDIA_LNK_FL_ENABLED);
177 if (ret < 0)
178 return ret;
179 }
180 }
181
182 return 0;
183}
184
185static struct media_link *media_entity_add_link(struct media_entity *entity)
186{
187 if (entity->num_links >= entity->max_links) {
188 struct media_link *links = entity->links;
189 unsigned int max_links = entity->max_links * 2;
190 unsigned int i;
191
192 links = (struct media_link*)realloc(links, max_links * sizeof *links);
193 if (links == NULL)
194 return NULL;
195
196 for (i = 0; i < entity->num_links; ++i)
197 links[i].twin->twin = &links[i];
198
199 entity->max_links = max_links;
200 entity->links = links;
201 }
202
203 return &entity->links[entity->num_links++];
204}
205
206static int media_enum_links(struct media_device *media)
207{
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200208 ALOGV("%s: start", __func__);
codeworkx62f02ba2012-05-20 12:00:36 +0200209 __u32 id;
210 int ret = 0;
211
212 for (id = 1; id <= media->entities_count; id++) {
213 struct media_entity *entity = &media->entities[id - 1];
214 struct media_links_enum links;
215 unsigned int i;
216
217 links.entity = entity->info.id;
218 links.pads = (struct media_pad_desc*)malloc(entity->info.pads * sizeof(struct media_pad_desc));
219 links.links = (struct media_link_desc*)malloc(entity->info.links * sizeof(struct media_link_desc));
220
221 if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200222 ALOGE(
codeworkx62f02ba2012-05-20 12:00:36 +0200223 "%s: Unable to enumerate pads and links (%s).",
224 __func__, strerror(errno));
225 free(links.pads);
226 free(links.links);
227 return -errno;
228 }
229
230 for (i = 0; i < entity->info.pads; ++i) {
231 entity->pads[i].entity = entity;
232 entity->pads[i].index = links.pads[i].index;
233 entity->pads[i].flags = links.pads[i].flags;
234 }
235
236 for (i = 0; i < entity->info.links; ++i) {
237 struct media_link_desc *link = &links.links[i];
238 struct media_link *fwdlink;
239 struct media_link *backlink;
240 struct media_entity *source;
241 struct media_entity *sink;
242
243 source = media_get_entity_by_id(media, link->source.entity);
244 sink = media_get_entity_by_id(media, link->sink.entity);
245 if (source == NULL || sink == NULL) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200246 ALOGE(
codeworkx62f02ba2012-05-20 12:00:36 +0200247 "WARNING entity %u link %u from %u/%u to %u/%u is invalid!",
248 id, i, link->source.entity,
249 link->source.index,
250 link->sink.entity,
251 link->sink.index);
252 ret = -EINVAL;
253 } else {
254 fwdlink = media_entity_add_link(source);
255 fwdlink->source = &source->pads[link->source.index];
256 fwdlink->sink = &sink->pads[link->sink.index];
257 fwdlink->flags = link->flags;
258
259 backlink = media_entity_add_link(sink);
260 backlink->source = &source->pads[link->source.index];
261 backlink->sink = &sink->pads[link->sink.index];
262 backlink->flags = link->flags;
263
264 fwdlink->twin = backlink;
265 backlink->twin = fwdlink;
266 }
267 }
268
269 free(links.pads);
270 free(links.links);
271 }
272 return ret;
273}
274
275#ifdef HAVE_LIBUDEV
276
277#include <libudev.h>
278
279static inline int media_udev_open(struct udev **udev)
280{
281 *udev = udev_new();
282 if (*udev == NULL)
283 return -ENOMEM;
284 return 0;
285}
286
287static inline void media_udev_close(struct udev *udev)
288{
289 if (udev != NULL)
290 udev_unref(udev);
291}
292
293static int media_get_devname_udev(struct udev *udev,
294 struct media_entity *entity)
295{
296 struct udev_device *device;
297 dev_t devnum;
298 const char *p;
299 int ret = -ENODEV;
300
301 if (udev == NULL)
302 return -EINVAL;
303
304 devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200305 ALOGE("looking up device: %u:%u",
codeworkx62f02ba2012-05-20 12:00:36 +0200306 major(devnum), minor(devnum));
307 device = udev_device_new_from_devnum(udev, 'c', devnum);
308 if (device) {
309 p = udev_device_get_devnode(device);
310 if (p) {
311 strncpy(entity->devname, p, sizeof(entity->devname));
312 entity->devname[sizeof(entity->devname) - 1] = '\0';
313 }
314 ret = 0;
315 }
316
317 udev_device_unref(device);
318
319 return ret;
320}
321
322#else /* HAVE_LIBUDEV */
323
324struct udev;
325
326static inline int media_udev_open(struct udev **udev) { return 0; }
327
328static inline void media_udev_close(struct udev *udev) { }
329
330static inline int media_get_devname_udev(struct udev *udev,
331 struct media_entity *entity)
332{
333 return -ENOTSUP;
334}
335
336#endif /* HAVE_LIBUDEV */
337
338static int media_get_devname_sysfs(struct media_entity *entity)
339{
340 //struct stat devstat;
341 char devname[32];
342 char sysname[32];
343 char target[1024];
344 char *p;
345 int ret;
346
347 sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major,
348 entity->info.v4l.minor);
349
350 ret = readlink(sysname, target, sizeof(target));
351 if (ret < 0)
352 return -errno;
353
354 target[ret] = '\0';
355 p = strrchr(target, '/');
356 if (p == NULL)
357 return -EINVAL;
358
359 sprintf(devname, "/tmp/%s", p + 1);
360
361 ret = mknod(devname, 0666 | S_IFCHR, MKDEV(81, entity->info.v4l.minor));
362 strcpy(entity->devname, devname);
363
364 return 0;
365}
366
367int get_media_fd(struct media_device *media)
368{
369 ssize_t num;
370 int media_node;
371 char *ptr;
372 char media_buf[6];
373
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200374 ALOGV("%s(%s)", __func__, MEDIA_DEV);
codeworkx62f02ba2012-05-20 12:00:36 +0200375
376 media->fd = open(MEDIA_DEV, O_RDWR, 0);
377 if( media->fd < 0) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200378 ALOGE("Open sysfs media device failed, media->fd : 0x%p", media->fd);
codeworkx62f02ba2012-05-20 12:00:36 +0200379 return -1;
380 }
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200381 ALOGV("media->fd : %p", media->fd);
codeworkx62f02ba2012-05-20 12:00:36 +0200382
383 return media->fd;
384
385}
386
387static int media_enum_entities(struct media_device *media)
388{
389 struct media_entity *entity;
390 unsigned int size;
391 __u32 id;
392 int ret;
393 entity = (struct media_entity*)calloc(1, sizeof(struct media_entity));
394 for (id = 0, ret = 0; ; id = entity->info.id) {
395 size = (media->entities_count + 1) * sizeof(*media->entities);
396 media->entities = (struct media_entity*)realloc(media->entities, size);
397
398 entity = &media->entities[media->entities_count];
399 memset(entity, 0, sizeof(*entity));
400 entity->fd = -1;
401 entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT;
402 entity->media = media;
403
404 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
405
406 if (ret < 0) {
407 ret = errno != EINVAL ? -errno : 0;
408 break;
409 }
410
411 /* Number of links (for outbound links) plus number of pads (for
412 * inbound links) is a good safe initial estimate of the total
413 * number of links.
414 */
415 entity->max_links = entity->info.pads + entity->info.links;
416
417 entity->pads = (struct media_pad*)malloc(entity->info.pads * sizeof(*entity->pads));
418 entity->links = (struct media_link*)malloc(entity->max_links * sizeof(*entity->links));
419 if (entity->pads == NULL || entity->links == NULL) {
420 ret = -ENOMEM;
421 break;
422 }
423
424 media->entities_count++;
425
426 /* Find the corresponding device name. */
427 if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE &&
428 media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
429 continue;
430
431 /* Fall back to get the device name via sysfs */
432 media_get_devname_sysfs(entity);
433 if (ret < 0)
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200434 ALOGE("media_get_devname failed");
codeworkx62f02ba2012-05-20 12:00:36 +0200435 }
436
437 return ret;
438}
439
440static void media_debug_default(void *ptr, ...)
441{
442 va_list argptr;
443 va_start(argptr, ptr);
444 vprintf((const char*)ptr, argptr);
445 va_end(argptr);
446}
447
448void media_debug_set_handler(struct media_device *media,
449 void (*debug_handler)(void *, ...),
450 void *debug_priv)
451{
452 if (debug_handler) {
453 media->debug_handler = debug_handler;
454 media->debug_priv = debug_priv;
455 } else {
456 media->debug_handler = media_debug_default;
457 media->debug_priv = NULL;
458 }
459}
460
461struct media_device *media_open_debug(
462 const char *name, void (*debug_handler)(void *, ...),
463 void *debug_priv)
464{
465 struct media_device *media;
466 int ret;
467
468 media = (struct media_device*)calloc(1, sizeof(struct media_device));
469 if (media == NULL) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200470 ALOGE("%s: media : %p", __func__, media);
codeworkx62f02ba2012-05-20 12:00:36 +0200471 return NULL;
472 }
473
474 media_debug_set_handler(media, debug_handler, debug_priv);
475
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200476 ALOGV("Opening media device %s", name);
477 ALOGV("%s: media : %p", __func__, media);
codeworkx62f02ba2012-05-20 12:00:36 +0200478
479 media->fd = get_media_fd(media);
480 if (media->fd < 0) {
481 media_close(media);
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200482 ALOGE("%s: failed get_media_fd %s",
codeworkx62f02ba2012-05-20 12:00:36 +0200483 __func__, name);
484 return NULL;
485 }
486
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200487 ALOGV("%s: media->fd : %p", __func__, media->fd);
codeworkx62f02ba2012-05-20 12:00:36 +0200488 ret = media_enum_entities(media);
489
490 if (ret < 0) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200491 ALOGE(
codeworkx62f02ba2012-05-20 12:00:36 +0200492 "%s: Unable to enumerate entities for device %s (%s)",
493 __func__, name, strerror(-ret));
494 media_close(media);
495 return NULL;
496 }
497
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200498 ALOGV("Found %u entities", media->entities_count);
499 ALOGV("Enumerating pads and links");
codeworkx62f02ba2012-05-20 12:00:36 +0200500
501 ret = media_enum_links(media);
502 if (ret < 0) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200503 ALOGE(
codeworkx62f02ba2012-05-20 12:00:36 +0200504 "%s: Unable to enumerate pads and linksfor device %s",
505 __func__, name);
506 media_close(media);
507 return NULL;
508 }
509
510 return media;
511}
512
513struct media_device *media_open(void)
514{
515 return media_open_debug(NULL, (void (*)(void *, ...))fprintf, stdout);
516}
517
518void media_close(struct media_device *media)
519{
520 unsigned int i;
521
522 if (media->fd != -1)
523 close(media->fd);
524
525 for (i = 0; i < media->entities_count; ++i) {
526 struct media_entity *entity = &media->entities[i];
527
528 free(entity->pads);
529 free(entity->links);
530 if (entity->fd != -1)
531 close(entity->fd);
532 }
533
534 free(media->entities);
535 free(media);
536}
537
538struct media_pad *media_parse_pad(struct media_device *media,
539 const char *p, char **endp)
540{
541 unsigned int entity_id, pad;
542 struct media_entity *entity;
543 char *end;
544
545 for (; isspace(*p); ++p);
546
547 if (*p == '"') {
548 for (end = (char *)p + 1; *end && *end != '"'; ++end);
549 if (*end != '"')
550 return NULL;
551
552 entity = media_get_entity_by_name(media, p + 1, end - p - 1);
553 if (entity == NULL)
554 return NULL;
555
556 ++end;
557 } else {
558 entity_id = strtoul(p, &end, 10);
559 entity = media_get_entity_by_id(media, entity_id);
560 if (entity == NULL)
561 return NULL;
562 }
563 for (; isspace(*end); ++end);
564
565 if (*end != ':')
566 return NULL;
567 for (p = end + 1; isspace(*p); ++p);
568
569 pad = strtoul(p, &end, 10);
570 for (p = end; isspace(*p); ++p);
571
572 if (pad >= entity->info.pads)
573 return NULL;
574
575 for (p = end; isspace(*p); ++p);
576 if (endp)
577 *endp = (char *)p;
578
579 return &entity->pads[pad];
580}
581
582struct media_link *media_parse_link(struct media_device *media,
583 const char *p, char **endp)
584{
585 struct media_link *link;
586 struct media_pad *source;
587 struct media_pad *sink;
588 unsigned int i;
589 char *end;
590
591 source = media_parse_pad(media, p, &end);
592 if (source == NULL)
593 return NULL;
594
595 if (end[0] != '-' || end[1] != '>')
596 return NULL;
597 p = end + 2;
598
599 sink = media_parse_pad(media, p, &end);
600 if (sink == NULL)
601 return NULL;
602
603 *endp = end;
604
605 for (i = 0; i < source->entity->num_links; i++) {
606 link = &source->entity->links[i];
607
608 if (link->source == source && link->sink == sink)
609 return link;
610 }
611
612 return NULL;
613}
614
615int media_parse_setup_link(struct media_device *media,
616 const char *p, char **endp)
617{
618 struct media_link *link;
619 __u32 flags;
620 char *end;
621
622 link = media_parse_link(media, p, &end);
623 if (link == NULL) {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200624 ALOGE(
codeworkx62f02ba2012-05-20 12:00:36 +0200625 "%s: Unable to parse link", __func__);
626 return -EINVAL;
627 }
628
629 p = end;
630 if (*p++ != '[') {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200631 ALOGE("Unable to parse link flags");
codeworkx62f02ba2012-05-20 12:00:36 +0200632 return -EINVAL;
633 }
634
635 flags = strtoul(p, &end, 10);
636 for (p = end; isspace(*p); p++);
637 if (*p++ != ']') {
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200638 ALOGE("Unable to parse link flags");
codeworkx62f02ba2012-05-20 12:00:36 +0200639 return -EINVAL;
640 }
641
642 for (; isspace(*p); p++);
643 *endp = (char *)p;
644
Daniel Hillenbrand0fdadca2012-07-22 15:45:33 +0200645 ALOGV(
codeworkx62f02ba2012-05-20 12:00:36 +0200646 "Setting up link %u:%u -> %u:%u [%u]",
647 link->source->entity->info.id, link->source->index,
648 link->sink->entity->info.id, link->sink->index,
649 flags);
650
651 return media_setup_link(media, link->source, link->sink, flags);
652}
653
654int media_parse_setup_links(struct media_device *media, const char *p)
655{
656 char *end;
657 int ret;
658
659 do {
660 ret = media_parse_setup_link(media, p, &end);
661 if (ret < 0)
662 return ret;
663
664 p = end + 1;
665 } while (*end == ',');
666
667 return *end ? -EINVAL : 0;
668}