1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*!
18 * \file exynos_mc.c
19 * \brief source file for libexynosv4l2
20 * \author Jinsung Yang (jsgood.yang@samsung.com)
21 * \author Sangwoo Park (sw5771.park@samsung.com)
22 * \date 2012/01/17
23 *
24 * <b>Revision History: </b>
25 * - 2012/01/17: Jinsung Yang (jsgood.yang@samsung.com) \n
26 * Initial version
27 *
28 */
29
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/stat.h>
40 #include <media.h>
41 #include <linux/kdev_t.h>
42 #include <linux/types.h>
43
44 #include "exynos_v4l2.h"
45
46 //#define LOG_NDEBUG 0
47 #define LOG_TAG "libexynosv4l2-mc"
48 #include <utils/Log.h>
49
__media_entity_type(struct media_entity * entity)50 static inline unsigned int __media_entity_type(struct media_entity *entity)
51 {
52 return entity->info.type & MEDIA_ENT_TYPE_MASK;
53 }
54
__media_debug_default(void * ptr,...)55 static void __media_debug_default(void *ptr, ...)
56 {
57 va_list argptr;
58 va_start(argptr, ptr);
59 vprintf((const char*)ptr, argptr);
60 va_end(argptr);
61 }
62
__media_debug_set_handler(struct media_device * media,void (* debug_handler)(void *,...),void * debug_priv)63 static void __media_debug_set_handler(
64 struct media_device *media,
65 void (*debug_handler)(void *, ...),
66 void *debug_priv)
67 {
68 if (debug_handler) {
69 media->debug_handler = debug_handler;
70 media->debug_priv = debug_priv;
71 } else {
72 media->debug_handler = __media_debug_default;
73 media->debug_priv = NULL;
74 }
75 }
76
__media_entity_add_link(struct media_entity * entity)77 static struct media_link *__media_entity_add_link(struct media_entity *entity)
78 {
79 if (entity->num_links >= entity->max_links) {
80 struct media_link *links = entity->links;
81 unsigned int max_links = entity->max_links * 2;
82 unsigned int i;
83
84 links = (struct media_link*)realloc(links, max_links * sizeof *links);
85 if (links == NULL)
86 return NULL;
87
88 for (i = 0; i < entity->num_links; ++i)
89 links[i].twin->twin = &links[i];
90
91 entity->max_links = max_links;
92 entity->links = links;
93 }
94
95 return &entity->links[entity->num_links++];
96 }
97
98
__media_enum_links(struct media_device * media)99 static int __media_enum_links(struct media_device *media)
100 {
101 ALOGD("%s: start", __func__);
102 __u32 id;
103 int ret = 0;
104
105 for (id = 1; id <= media->entities_count; id++) {
106 struct media_entity *entity = &media->entities[id - 1];
107 struct media_links_enum links;
108 unsigned int i;
109
110 links.entity = entity->info.id;
111 links.pads = (struct media_pad_desc*)malloc(entity->info.pads * sizeof(struct media_pad_desc));
112 links.links = (struct media_link_desc*)malloc(entity->info.links * sizeof(struct media_link_desc));
113
114 if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
115 ALOGE("Unable to enumerate pads and links (%s)", strerror(errno));
116 free(links.pads);
117 free(links.links);
118 return -errno;
119 }
120
121 for (i = 0; i < entity->info.pads; ++i) {
122 entity->pads[i].entity = entity;
123 entity->pads[i].index = links.pads[i].index;
124 entity->pads[i].flags = links.pads[i].flags;
125 }
126
127 for (i = 0; i < entity->info.links; ++i) {
128 struct media_link_desc *link = &links.links[i];
129 struct media_link *fwdlink;
130 struct media_link *backlink;
131 struct media_entity *source;
132 struct media_entity *sink;
133
134 source = exynos_media_get_entity_by_id(media, link->source.entity);
135 sink = exynos_media_get_entity_by_id(media, link->sink.entity);
136 if (source == NULL || sink == NULL) {
137 ALOGE("WARNING entity %u link %u from %u/%u to %u/%u is invalid!",
138 id, i, link->source.entity,
139 link->source.index,
140 link->sink.entity,
141 link->sink.index);
142 ret = -EINVAL;
143 } else {
144 fwdlink = __media_entity_add_link(source);
145 fwdlink->source = &source->pads[link->source.index];
146 fwdlink->sink = &sink->pads[link->sink.index];
147 fwdlink->flags = link->flags;
148
149 backlink = __media_entity_add_link(sink);
150 backlink->source = &source->pads[link->source.index];
151 backlink->sink = &sink->pads[link->sink.index];
152 backlink->flags = link->flags;
153
154 fwdlink->twin = backlink;
155 backlink->twin = fwdlink;
156 }
157 }
158
159 free(links.pads);
160 free(links.links);
161 }
162 return ret;
163 }
164
__media_get_devname_sysfs(struct media_entity * entity)165 static int __media_get_devname_sysfs(struct media_entity *entity)
166 {
167 //struct stat devstat;
168 char devname[32];
169 char sysname[32];
170 char target[1024];
171 char *p;
172 int ret;
173
174 sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major,
175 entity->info.v4l.minor);
176
177 ret = readlink(sysname, target, sizeof(target));
178 if (ret < 0)
179 return -errno;
180
181 target[ret] = '\0';
182 p = strrchr(target, '/');
183 if (p == NULL)
184 return -EINVAL;
185
186 sprintf(devname, "/tmp/%s", p + 1);
187
188 ret = mknod(devname, 0666 | S_IFCHR, MKDEV(81, entity->info.v4l.minor));
189 strcpy(entity->devname, devname);
190
191 return 0;
192 }
193
__media_get_media_fd(const char * filename,struct media_device * media)194 static int __media_get_media_fd(const char *filename, struct media_device *media)
195 {
196 ssize_t num;
197 int media_node;
198 char *ptr;
199 char media_buf[6];
200
201 ALOGD("%s: %s", __func__, filename);
202
203 media->fd = open(filename, O_RDWR, 0);
204 if (media->fd < 0) {
205 ALOGE("Open sysfs media device failed, media->fd: %d", media->fd);
206 return -1;
207 }
208
209 ALOGD("%s: media->fd: %d", __func__, media->fd);
210
211 return media->fd;
212
213 }
214
__media_enum_entities(struct media_device * media)215 static int __media_enum_entities(struct media_device *media)
216 {
217 struct media_entity *entity, *temp_entity;
218 unsigned int size;
219 __u32 id;
220 int ret;
221
222 temp_entity = entity = (struct media_entity*)calloc(1, sizeof(struct media_entity));
223 for (id = 0, ret = 0; ; id = entity->info.id) {
224 size = (media->entities_count + 1) * sizeof(*media->entities);
225 media->entities = (struct media_entity*)realloc(media->entities, size);
226
227 entity = &media->entities[media->entities_count];
228 memset(entity, 0, sizeof(*entity));
229 entity->fd = -1;
230 entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT;
231 entity->media = media;
232
233 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
234
235 if (ret < 0) {
236 ret = errno != EINVAL ? -errno : 0;
237 break;
238 }
239
240 /* Number of links (for outbound links) plus number of pads (for
241 * inbound links) is a good safe initial estimate of the total
242 * number of links.
243 */
244 entity->max_links = entity->info.pads + entity->info.links;
245
246 entity->pads = (struct media_pad*)malloc(entity->info.pads * sizeof(*entity->pads));
247 entity->links = (struct media_link*)malloc(entity->max_links * sizeof(*entity->links));
248 if (entity->pads == NULL || entity->links == NULL) {
249 ret = -ENOMEM;
250 break;
251 }
252
253 media->entities_count++;
254
255 /* Find the corresponding device name. */
256 if (__media_entity_type(entity) != MEDIA_ENT_T_DEVNODE &&
257 __media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
258 continue;
259
260 /* Fall back to get the device name via sysfs */
261 __media_get_devname_sysfs(entity);
262 if (ret < 0)
263 ALOGE("media_get_devname failed");
264 }
265 free(temp_entity);
266
267 return ret;
268 }
269
__media_open_debug(const char * filename,void (* debug_handler)(void *,...),void * debug_priv)270 static struct media_device *__media_open_debug(
271 const char *filename,
272 void (*debug_handler)(void *, ...),
273 void *debug_priv)
274 {
275 struct media_device *media;
276 int ret;
277
278 media = (struct media_device *)calloc(1, sizeof(struct media_device));
279 if (media == NULL) {
280 ALOGE("media: %p", media);
281 return NULL;
282 }
283
284 __media_debug_set_handler(media, debug_handler, debug_priv);
285
286 ALOGD("%s: Opening media device %s", __func__, filename);
287 ALOGD("%s: media: %p", __func__, media);
288
289 media->fd = __media_get_media_fd(filename, media);
290 if (media->fd < 0) {
291 exynos_media_close(media);
292 ALOGE("failed __media_get_media_fd %s", filename);
293 return NULL;
294 }
295
296 ALOGD("%s: media->fd: %d", __func__, media->fd);
297 ret = __media_enum_entities(media);
298
299 if (ret < 0) {
300 ALOGE("Unable to enumerate entities for device %s (%s)", filename, strerror(-ret));
301 exynos_media_close(media);
302 return NULL;
303 }
304
305 ALOGD("%s: Found %u entities", __func__, media->entities_count);
306 ALOGD("%s: Enumerating pads and links", __func__);
307
308 ret = __media_enum_links(media);
309 if (ret < 0) {
310 ALOGE("Unable to enumerate pads and links for device %s", filename);
311 exynos_media_close(media);
312 return NULL;
313 }
314
315 return media;
316 }
317
318 /**
319 * @brief Open a media device.
320 * @param filename - name (including path) of the device node.
321 *
322 * Open the media device referenced by @a filename and enumerate entities, pads and
323 * links.
324 *
325 * @return A pointer to a newly allocated media_device structure instance on
326 * success and NULL on failure. The returned pointer must be freed with
327 * exynos_media_close when the device isn't needed anymore.
328 */
exynos_media_open(const char * filename)329 struct media_device *exynos_media_open(const char *filename)
330 {
331 return __media_open_debug(filename, (void (*)(void *, ...))fprintf, stdout);
332 }
333
334 /**
335 * @brief Close a media device.
336 * @param media - device instance.
337 *
338 * Close the @a media device instance and free allocated resources. Access to the
339 * device instance is forbidden after this function returns.
340 */
exynos_media_close(struct media_device * media)341 void exynos_media_close(struct media_device *media)
342 {
343 unsigned int i;
344
345 if (media->fd != -1)
346 close(media->fd);
347
348 for (i = 0; i < media->entities_count; ++i) {
349 struct media_entity *entity = &media->entities[i];
350
351 free(entity->pads);
352 free(entity->links);
353 if (entity->fd != -1)
354 close(entity->fd);
355 }
356
357 free(media->entities);
358 free(media);
359 }
360
361 /**
362 * @brief Locate the pad at the other end of a link.
363 * @param pad - sink pad at one end of the link.
364 *
365 * Locate the source pad connected to @a pad through an enabled link. As only one
366 * link connected to a sink pad can be enabled at a time, the connected source
367 * pad is guaranteed to be unique.
368 *
369 * @return A pointer to the connected source pad, or NULL if all links connected
370 * to @a pad are disabled. Return NULL also if @a pad is not a sink pad.
371 */
exynos_media_entity_remote_source(struct media_pad * pad)372 struct media_pad *exynos_media_entity_remote_source(struct media_pad *pad)
373 {
374 unsigned int i;
375
376 if (!(pad->flags & MEDIA_PAD_FL_SINK))
377 return NULL;
378
379 for (i = 0; i < pad->entity->num_links; ++i) {
380 struct media_link *link = &pad->entity->links[i];
381
382 if (!(link->flags & MEDIA_LNK_FL_ENABLED))
383 continue;
384
385 if (link->sink == pad)
386 return link->source;
387 }
388
389 return NULL;
390 }
391
392 /**
393 * @brief Find an entity by its name.
394 * @param media - media device.
395 * @param name - entity name.
396 * @param length - size of @a name.
397 *
398 * Search for an entity with a name equal to @a name.
399 *
400 * @return A pointer to the entity if found, or NULL otherwise.
401 */
exynos_media_get_entity_by_name(struct media_device * media,const char * name,size_t length)402 struct media_entity *exynos_media_get_entity_by_name(struct media_device *media,
403 const char *name, size_t length)
404 {
405 unsigned int i;
406 struct media_entity *entity;
407
408 for (i = 0; i < media->entities_count; ++i) {
409 entity = &media->entities[i];
410
411 if (strncmp(entity->info.name, name, length) == 0)
412 return entity;
413 }
414
415 return NULL;
416 }
417
418 /**
419 * @brief Find an entity by its ID.
420 * @param media - media device.
421 * @param id - entity ID.
422 *
423 * Search for an entity with an ID equal to @a id.
424 *
425 * @return A pointer to the entity if found, or NULL otherwise.
426 */
exynos_media_get_entity_by_id(struct media_device * media,__u32 id)427 struct media_entity *exynos_media_get_entity_by_id(struct media_device *media,
428 __u32 id)
429 {
430 unsigned int i;
431
432 for (i = 0; i < media->entities_count; ++i) {
433 struct media_entity *entity = &media->entities[i];
434
435 if (entity->info.id == id)
436 return entity;
437 }
438
439 return NULL;
440 }
441
442 /**
443 * @brief Configure a link.
444 * @param media - media device.
445 * @param source - source pad at the link origin.
446 * @param sink - sink pad at the link target.
447 * @param flags - configuration flags.
448 *
449 * Locate the link between @a source and @a sink, and configure it by applying
450 * the new @a flags.
451 *
452 * Only the MEDIA_LINK_FLAG_ENABLED flag is writable.
453 *
454 * @return 0 on success, -1 on failure:
455 * -ENOENT: link not found
456 * - other error codes returned by MEDIA_IOC_SETUP_LINK
457 */
exynos_media_setup_link(struct media_device * media,struct media_pad * source,struct media_pad * sink,__u32 flags)458 int exynos_media_setup_link(struct media_device *media,
459 struct media_pad *source,
460 struct media_pad *sink,
461 __u32 flags)
462 {
463 struct media_link *link;
464 struct media_link_desc ulink;
465 unsigned int i;
466 int ret;
467
468 for (i = 0; i < source->entity->num_links; i++) {
469 link = &source->entity->links[i];
470
471 if (link->source->entity == source->entity &&
472 link->source->index == source->index &&
473 link->sink->entity == sink->entity &&
474 link->sink->index == sink->index)
475 break;
476 }
477
478 if (i == source->entity->num_links) {
479 ALOGE("Link not found");
480 return -ENOENT;
481 }
482
483 /* source pad */
484 ulink.source.entity = source->entity->info.id;
485 ulink.source.index = source->index;
486 ulink.source.flags = MEDIA_PAD_FL_SOURCE;
487
488 /* sink pad */
489 ulink.sink.entity = sink->entity->info.id;
490 ulink.sink.index = sink->index;
491 ulink.sink.flags = MEDIA_PAD_FL_SINK;
492
493 ulink.flags = flags | (link->flags & MEDIA_LNK_FL_IMMUTABLE);
494
495 ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink);
496 if (ret == -1) {
497 ALOGE("Unable to setup link (%s)", strerror(errno));
498 return -errno;
499 }
500
501 link->flags = ulink.flags;
502 link->twin->flags = ulink.flags;
503 return 0;
504 }
505
506 /**
507 * @brief Reset all links to the disabled state.
508 * @param media - media device.
509 *
510 * Disable all links in the media device. This function is usually used after
511 * opening a media device to reset all links to a known state.
512 *
513 * @return 0 on success, or a negative error code on failure.
514 */
exynos_media_reset_links(struct media_device * media)515 int exynos_media_reset_links(struct media_device *media)
516 {
517 unsigned int i, j;
518 int ret;
519
520 for (i = 0; i < media->entities_count; ++i) {
521 struct media_entity *entity = &media->entities[i];
522
523 for (j = 0; j < entity->num_links; j++) {
524 struct media_link *link = &entity->links[j];
525
526 if (link->flags & MEDIA_LNK_FL_IMMUTABLE ||
527 link->source->entity != entity)
528 continue;
529
530 ret = exynos_media_setup_link(media, link->source, link->sink,
531 link->flags & ~MEDIA_LNK_FL_ENABLED);
532 if (ret < 0)
533 return ret;
534 }
535 }
536
537 return 0;
538 }
539
540 #ifdef HAVE_LIBUDEV
541
542 #include <libudev.h>
543
__media_udev_open(struct udev ** udev)544 static inline int __media_udev_open(struct udev **udev)
545 {
546 *udev = udev_new();
547 if (*udev == NULL)
548 return -ENOMEM;
549 return 0;
550 }
551
__media_udev_close(struct udev * udev)552 static inline void __media_udev_close(struct udev *udev)
553 {
554 if (udev != NULL)
555 udev_unref(udev);
556 }
557
__media_get_devname_udev(struct udev * udev,struct media_entity * entity)558 static int __media_get_devname_udev(struct udev *udev,
559 struct media_entity *entity)
560 {
561 struct udev_device *device;
562 dev_t devnum;
563 const char *p;
564 int ret = -ENODEV;
565
566 if (udev == NULL)
567 return -EINVAL;
568
569 devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
570 ALOGE("looking up device: %u:%u",
571 major(devnum), minor(devnum));
572 device = udev_device_new_from_devnum(udev, 'c', devnum);
573 if (device) {
574 p = udev_device_get_devnode(device);
575 if (p) {
576 strncpy(entity->devname, p, sizeof(entity->devname));
577 entity->devname[sizeof(entity->devname) - 1] = '\0';
578 }
579 ret = 0;
580 }
581
582 udev_device_unref(device);
583
584 return ret;
585 }
586
587 #else /* HAVE_LIBUDEV */
588
589 struct udev;
590
__media_udev_open(struct udev ** udev)591 static inline int __media_udev_open(struct udev **udev) { return 0; }
592
__media_udev_close(struct udev * udev)593 static inline void __media_udev_close(struct udev *udev) { }
594
__media_get_devname_udev(struct udev * udev,struct media_entity * entity)595 static inline int __media_get_devname_udev(struct udev *udev,
596 struct media_entity *entity)
597 {
598 return -ENOTSUP;
599 }
600
601 #endif /* HAVE_LIBUDEV */
602
603 /**
604 * @brief Parse string to a pad on the media device.
605 * @param media - media device.
606 * @param p - input string
607 * @param endp - pointer to string where parsing ended
608 *
609 * Parse NULL terminated string describing a pad and return its struct
610 * media_pad instance.
611 *
612 * @return Pointer to struct media_pad on success, NULL on failure.
613 */
exynos_media_parse_pad(struct media_device * media,const char * p,char ** endp)614 struct media_pad *exynos_media_parse_pad(struct media_device *media,
615 const char *p, char **endp)
616 {
617 unsigned int entity_id, pad;
618 struct media_entity *entity;
619 char *end;
620
621 for (; isspace(*p); ++p);
622
623 if (*p == '"') {
624 for (end = (char *)p + 1; *end && *end != '"'; ++end);
625 if (*end != '"')
626 return NULL;
627
628 entity = exynos_media_get_entity_by_name(media, p + 1, end - p - 1);
629 if (entity == NULL)
630 return NULL;
631
632 ++end;
633 } else {
634 entity_id = strtoul(p, &end, 10);
635 entity = exynos_media_get_entity_by_id(media, entity_id);
636 if (entity == NULL)
637 return NULL;
638 }
639 for (; isspace(*end); ++end);
640
641 if (*end != ':')
642 return NULL;
643 for (p = end + 1; isspace(*p); ++p);
644
645 pad = strtoul(p, &end, 10);
646 for (p = end; isspace(*p); ++p);
647
648 if (pad >= entity->info.pads)
649 return NULL;
650
651 for (p = end; isspace(*p); ++p);
652 if (endp)
653 *endp = (char *)p;
654
655 return &entity->pads[pad];
656 }
657
658 /**
659 * @brief Parse string to a link on the media device.
660 * @param media - media device.
661 * @param p - input string
662 * @param endp - pointer to p where parsing ended
663 *
664 * Parse NULL terminated string p describing a link and return its struct
665 * media_link instance.
666 *
667 * @return Pointer to struct media_link on success, NULL on failure.
668 */
exynos_media_parse_link(struct media_device * media,const char * p,char ** endp)669 struct media_link *exynos_media_parse_link(
670 struct media_device *media,
671 const char *p,
672 char **endp)
673 {
674 struct media_link *link;
675 struct media_pad *source;
676 struct media_pad *sink;
677 unsigned int i;
678 char *end;
679
680 source = exynos_media_parse_pad(media, p, &end);
681 if (source == NULL)
682 return NULL;
683
684 if (end[0] != '-' || end[1] != '>')
685 return NULL;
686 p = end + 2;
687
688 sink = exynos_media_parse_pad(media, p, &end);
689 if (sink == NULL)
690 return NULL;
691
692 *endp = end;
693
694 for (i = 0; i < source->entity->num_links; i++) {
695 link = &source->entity->links[i];
696
697 if (link->source == source && link->sink == sink)
698 return link;
699 }
700
701 return NULL;
702 }
703
704 /**
705 * @brief Parse string to a link on the media device and set it up.
706 * @param media - media device.
707 * @param p - input string
708 *
709 * Parse NULL terminated string p describing a link and its configuration
710 * and configure the link.
711 *
712 * @return 0 on success, or a negative error code on failure.
713 */
exynos_media_parse_setup_link(struct media_device * media,const char * p,char ** endp)714 int exynos_media_parse_setup_link(
715 struct media_device *media,
716 const char *p,
717 char **endp)
718 {
719 struct media_link *link;
720 __u32 flags;
721 char *end;
722
723 link = exynos_media_parse_link(media, p, &end);
724 if (link == NULL) {
725 ALOGE("Unable to parse link");
726 return -EINVAL;
727 }
728
729 p = end;
730 if (*p++ != '[') {
731 ALOGE("Unable to parse link flags");
732 return -EINVAL;
733 }
734
735 flags = strtoul(p, &end, 10);
736 for (p = end; isspace(*p); p++);
737 if (*p++ != ']') {
738 ALOGE("Unable to parse link flags");
739 return -EINVAL;
740 }
741
742 for (; isspace(*p); p++);
743 *endp = (char *)p;
744
745 ALOGD("%s: Setting up link %u:%u -> %u:%u [%u]", __func__,
746 link->source->entity->info.id, link->source->index,
747 link->sink->entity->info.id, link->sink->index,
748 flags);
749
750 return exynos_media_setup_link(media, link->source, link->sink, flags);
751 }
752
753 /**
754 * @brief Parse string to link(s) on the media device and set it up.
755 * @param media - media device.
756 * @param p - input string
757 *
758 * Parse NULL terminated string p describing link(s) separated by
759 * commas (,) and configure the link(s).
760 *
761 * @return 0 on success, or a negative error code on failure.
762 */
exynos_media_parse_setup_links(struct media_device * media,const char * p)763 int exynos_media_parse_setup_links(struct media_device *media, const char *p)
764 {
765 char *end;
766 int ret;
767
768 do {
769 ret = exynos_media_parse_setup_link(media, p, &end);
770 if (ret < 0)
771 return ret;
772
773 p = end + 1;
774 } while (*end == ',');
775
776 return *end ? -EINVAL : 0;
777 }
778