From 1eb637c1a8757bc2c03d0bd97dc7fd10092bbd1d Mon Sep 17 00:00:00 2001 From: Yunji Kim Date: Fri, 12 Oct 2012 08:49:12 +0000 Subject: [PATCH] libv4l2 has moved from hardware/samsung_slsi/exynos5 Change-Id: I19d6c714e9f810e5c4db680eee358c3022462d80 Signed-off-by: Yunji Kim --- Android.mk | 3 +- libv4l2/Android.mk | 37 ++ libv4l2/exynos_mc.c | 777 ++++++++++++++++++++++++++++++++++++ libv4l2/exynos_subdev.c | 375 ++++++++++++++++++ libv4l2/exynos_v4l2.c | 858 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 2049 insertions(+), 1 deletion(-) create mode 100644 libv4l2/Android.mk create mode 100644 libv4l2/exynos_mc.c create mode 100644 libv4l2/exynos_subdev.c create mode 100644 libv4l2/exynos_v4l2.c diff --git a/Android.mk b/Android.mk index a93a9a8..34cf63d 100644 --- a/Android.mk +++ b/Android.mk @@ -15,6 +15,7 @@ # common_exynos_dirs := \ - libcsc + libcsc \ + libv4l2 include $(call all-named-subdir-makefiles,$(common_exynos_dirs)) diff --git a/libv4l2/Android.mk b/libv4l2/Android.mk new file mode 100644 index 0000000..f7d06cf --- /dev/null +++ b/libv4l2/Android.mk @@ -0,0 +1,37 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + exynos_v4l2.c \ + exynos_subdev.c \ + exynos_mc.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../include \ + $(TOP)/hardware/samsung_slsi/exynos/libexynosutils + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libutils \ + libexynosutils + +LOCAL_MODULE := libexynosv4l2 +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) diff --git a/libv4l2/exynos_mc.c b/libv4l2/exynos_mc.c new file mode 100644 index 0000000..8c5230c --- /dev/null +++ b/libv4l2/exynos_mc.c @@ -0,0 +1,777 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! + * \file exynos_mc.c + * \brief source file for libexynosv4l2 + * \author Jinsung Yang (jsgood.yang@samsung.com) + * \author Sangwoo Park (sw5771.park@samsung.com) + * \date 2012/01/17 + * + * Revision History: + * - 2012/01/17: Jinsung Yang (jsgood.yang@samsung.com) \n + * Initial version + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exynos_v4l2.h" + +//#define LOG_NDEBUG 0 +#define LOG_TAG "libexynosv4l2-mc" +#include + +static inline unsigned int __media_entity_type(struct media_entity *entity) +{ + return entity->info.type & MEDIA_ENT_TYPE_MASK; +} + +static void __media_debug_default(void *ptr, ...) +{ + va_list argptr; + va_start(argptr, ptr); + vprintf((const char*)ptr, argptr); + va_end(argptr); +} + +static void __media_debug_set_handler( + struct media_device *media, + void (*debug_handler)(void *, ...), + void *debug_priv) +{ + if (debug_handler) { + media->debug_handler = debug_handler; + media->debug_priv = debug_priv; + } else { + media->debug_handler = __media_debug_default; + media->debug_priv = NULL; + } +} + +static struct media_link *__media_entity_add_link(struct media_entity *entity) +{ + if (entity->num_links >= entity->max_links) { + struct media_link *links = entity->links; + unsigned int max_links = entity->max_links * 2; + unsigned int i; + + links = (struct media_link*)realloc(links, max_links * sizeof *links); + if (links == NULL) + return NULL; + + for (i = 0; i < entity->num_links; ++i) + links[i].twin->twin = &links[i]; + + entity->max_links = max_links; + entity->links = links; + } + + return &entity->links[entity->num_links++]; +} + + +static int __media_enum_links(struct media_device *media) +{ + ALOGD("%s: start", __func__); + __u32 id; + int ret = 0; + + for (id = 1; id <= media->entities_count; id++) { + struct media_entity *entity = &media->entities[id - 1]; + struct media_links_enum links; + unsigned int i; + + links.entity = entity->info.id; + links.pads = (struct media_pad_desc*)malloc(entity->info.pads * sizeof(struct media_pad_desc)); + links.links = (struct media_link_desc*)malloc(entity->info.links * sizeof(struct media_link_desc)); + + if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) { + ALOGE("Unable to enumerate pads and links (%s)", strerror(errno)); + free(links.pads); + free(links.links); + return -errno; + } + + for (i = 0; i < entity->info.pads; ++i) { + entity->pads[i].entity = entity; + entity->pads[i].index = links.pads[i].index; + entity->pads[i].flags = links.pads[i].flags; + } + + for (i = 0; i < entity->info.links; ++i) { + struct media_link_desc *link = &links.links[i]; + struct media_link *fwdlink; + struct media_link *backlink; + struct media_entity *source; + struct media_entity *sink; + + source = exynos_media_get_entity_by_id(media, link->source.entity); + sink = exynos_media_get_entity_by_id(media, link->sink.entity); + if (source == NULL || sink == NULL) { + ALOGE("WARNING entity %u link %u from %u/%u to %u/%u is invalid!", + id, i, link->source.entity, + link->source.index, + link->sink.entity, + link->sink.index); + ret = -EINVAL; + } else { + fwdlink = __media_entity_add_link(source); + fwdlink->source = &source->pads[link->source.index]; + fwdlink->sink = &sink->pads[link->sink.index]; + fwdlink->flags = link->flags; + + backlink = __media_entity_add_link(sink); + backlink->source = &source->pads[link->source.index]; + backlink->sink = &sink->pads[link->sink.index]; + backlink->flags = link->flags; + + fwdlink->twin = backlink; + backlink->twin = fwdlink; + } + } + + free(links.pads); + free(links.links); + } + return ret; +} + +static int __media_get_devname_sysfs(struct media_entity *entity) +{ + //struct stat devstat; + char devname[32]; + char sysname[32]; + char target[1024]; + char *p; + int ret; + + sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major, + entity->info.v4l.minor); + + ret = readlink(sysname, target, sizeof(target)); + if (ret < 0) + return -errno; + + target[ret] = '\0'; + p = strrchr(target, '/'); + if (p == NULL) + return -EINVAL; + + sprintf(devname, "/tmp/%s", p + 1); + + ret = mknod(devname, 0666 | S_IFCHR, MKDEV(81, entity->info.v4l.minor)); + strcpy(entity->devname, devname); + + return 0; +} + +static int __media_get_media_fd(const char *filename, struct media_device *media) +{ + ssize_t num; + int media_node; + char *ptr; + char media_buf[6]; + + ALOGD("%s: %s", __func__, filename); + + media->fd = open(filename, O_RDWR, 0); + if (media->fd < 0) { + ALOGE("Open sysfs media device failed, media->fd: %d", media->fd); + return -1; + } + + ALOGD("%s: media->fd: %d", __func__, media->fd); + + return media->fd; + +} + +static int __media_enum_entities(struct media_device *media) +{ + struct media_entity *entity, *temp_entity; + unsigned int size; + __u32 id; + int ret; + + temp_entity = entity = (struct media_entity*)calloc(1, sizeof(struct media_entity)); + for (id = 0, ret = 0; ; id = entity->info.id) { + size = (media->entities_count + 1) * sizeof(*media->entities); + media->entities = (struct media_entity*)realloc(media->entities, size); + + entity = &media->entities[media->entities_count]; + memset(entity, 0, sizeof(*entity)); + entity->fd = -1; + entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT; + entity->media = media; + + ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info); + + if (ret < 0) { + ret = errno != EINVAL ? -errno : 0; + break; + } + + /* Number of links (for outbound links) plus number of pads (for + * inbound links) is a good safe initial estimate of the total + * number of links. + */ + entity->max_links = entity->info.pads + entity->info.links; + + entity->pads = (struct media_pad*)malloc(entity->info.pads * sizeof(*entity->pads)); + entity->links = (struct media_link*)malloc(entity->max_links * sizeof(*entity->links)); + if (entity->pads == NULL || entity->links == NULL) { + ret = -ENOMEM; + break; + } + + media->entities_count++; + + /* Find the corresponding device name. */ + if (__media_entity_type(entity) != MEDIA_ENT_T_DEVNODE && + __media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) + continue; + + /* Fall back to get the device name via sysfs */ + __media_get_devname_sysfs(entity); + if (ret < 0) + ALOGE("media_get_devname failed"); + } + free(temp_entity); + + return ret; +} + +static struct media_device *__media_open_debug( + const char *filename, + void (*debug_handler)(void *, ...), + void *debug_priv) +{ + struct media_device *media; + int ret; + + media = (struct media_device *)calloc(1, sizeof(struct media_device)); + if (media == NULL) { + ALOGE("media: %p", media); + return NULL; + } + + __media_debug_set_handler(media, debug_handler, debug_priv); + + ALOGD("%s: Opening media device %s", __func__, filename); + ALOGD("%s: media: %p", __func__, media); + + media->fd = __media_get_media_fd(filename, media); + if (media->fd < 0) { + exynos_media_close(media); + ALOGE("failed __media_get_media_fd %s", filename); + return NULL; + } + + ALOGD("%s: media->fd: %d", __func__, media->fd); + ret = __media_enum_entities(media); + + if (ret < 0) { + ALOGE("Unable to enumerate entities for device %s (%s)", filename, strerror(-ret)); + exynos_media_close(media); + return NULL; + } + + ALOGD("%s: Found %u entities", __func__, media->entities_count); + ALOGD("%s: Enumerating pads and links", __func__); + + ret = __media_enum_links(media); + if (ret < 0) { + ALOGE("Unable to enumerate pads and links for device %s", filename); + exynos_media_close(media); + return NULL; + } + + return media; +} + +/** + * @brief Open a media device. + * @param filename - name (including path) of the device node. + * + * Open the media device referenced by @a filename and enumerate entities, pads and + * links. + * + * @return A pointer to a newly allocated media_device structure instance on + * success and NULL on failure. The returned pointer must be freed with + * exynos_media_close when the device isn't needed anymore. + */ +struct media_device *exynos_media_open(const char *filename) +{ + return __media_open_debug(filename, (void (*)(void *, ...))fprintf, stdout); +} + +/** + * @brief Close a media device. + * @param media - device instance. + * + * Close the @a media device instance and free allocated resources. Access to the + * device instance is forbidden after this function returns. + */ +void exynos_media_close(struct media_device *media) +{ + unsigned int i; + + if (media->fd != -1) + close(media->fd); + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->entities[i]; + + free(entity->pads); + free(entity->links); + if (entity->fd != -1) + close(entity->fd); + } + + free(media->entities); + free(media); +} + +/** + * @brief Locate the pad at the other end of a link. + * @param pad - sink pad at one end of the link. + * + * Locate the source pad connected to @a pad through an enabled link. As only one + * link connected to a sink pad can be enabled at a time, the connected source + * pad is guaranteed to be unique. + * + * @return A pointer to the connected source pad, or NULL if all links connected + * to @a pad are disabled. Return NULL also if @a pad is not a sink pad. + */ +struct media_pad *exynos_media_entity_remote_source(struct media_pad *pad) +{ + unsigned int i; + + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return NULL; + + for (i = 0; i < pad->entity->num_links; ++i) { + struct media_link *link = &pad->entity->links[i]; + + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) + continue; + + if (link->sink == pad) + return link->source; + } + + return NULL; +} + +/** + * @brief Find an entity by its name. + * @param media - media device. + * @param name - entity name. + * @param length - size of @a name. + * + * Search for an entity with a name equal to @a name. + * + * @return A pointer to the entity if found, or NULL otherwise. + */ +struct media_entity *exynos_media_get_entity_by_name(struct media_device *media, + const char *name, size_t length) +{ + unsigned int i; + struct media_entity *entity; + + for (i = 0; i < media->entities_count; ++i) { + entity = &media->entities[i]; + + if (strncmp(entity->info.name, name, length) == 0) + return entity; + } + + return NULL; +} + +/** + * @brief Find an entity by its ID. + * @param media - media device. + * @param id - entity ID. + * + * Search for an entity with an ID equal to @a id. + * + * @return A pointer to the entity if found, or NULL otherwise. + */ +struct media_entity *exynos_media_get_entity_by_id(struct media_device *media, + __u32 id) +{ + unsigned int i; + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->entities[i]; + + if (entity->info.id == id) + return entity; + } + + return NULL; +} + +/** + * @brief Configure a link. + * @param media - media device. + * @param source - source pad at the link origin. + * @param sink - sink pad at the link target. + * @param flags - configuration flags. + * + * Locate the link between @a source and @a sink, and configure it by applying + * the new @a flags. + * + * Only the MEDIA_LINK_FLAG_ENABLED flag is writable. + * + * @return 0 on success, -1 on failure: + * -ENOENT: link not found + * - other error codes returned by MEDIA_IOC_SETUP_LINK + */ +int exynos_media_setup_link(struct media_device *media, + struct media_pad *source, + struct media_pad *sink, + __u32 flags) +{ + struct media_link *link; + struct media_link_desc ulink; + unsigned int i; + int ret; + + for (i = 0; i < source->entity->num_links; i++) { + link = &source->entity->links[i]; + + if (link->source->entity == source->entity && + link->source->index == source->index && + link->sink->entity == sink->entity && + link->sink->index == sink->index) + break; + } + + if (i == source->entity->num_links) { + ALOGE("Link not found"); + return -ENOENT; + } + + /* source pad */ + ulink.source.entity = source->entity->info.id; + ulink.source.index = source->index; + ulink.source.flags = MEDIA_PAD_FL_SOURCE; + + /* sink pad */ + ulink.sink.entity = sink->entity->info.id; + ulink.sink.index = sink->index; + ulink.sink.flags = MEDIA_PAD_FL_SINK; + + ulink.flags = flags | (link->flags & MEDIA_LNK_FL_IMMUTABLE); + + ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink); + if (ret == -1) { + ALOGE("Unable to setup link (%s)", strerror(errno)); + return -errno; + } + + link->flags = ulink.flags; + link->twin->flags = ulink.flags; + return 0; +} + +/** + * @brief Reset all links to the disabled state. + * @param media - media device. + * + * Disable all links in the media device. This function is usually used after + * opening a media device to reset all links to a known state. + * + * @return 0 on success, or a negative error code on failure. + */ +int exynos_media_reset_links(struct media_device *media) +{ + unsigned int i, j; + int ret; + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->entities[i]; + + for (j = 0; j < entity->num_links; j++) { + struct media_link *link = &entity->links[j]; + + if (link->flags & MEDIA_LNK_FL_IMMUTABLE || + link->source->entity != entity) + continue; + + ret = exynos_media_setup_link(media, link->source, link->sink, + link->flags & ~MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; + } + } + + return 0; +} + +#ifdef HAVE_LIBUDEV + +#include + +static inline int __media_udev_open(struct udev **udev) +{ + *udev = udev_new(); + if (*udev == NULL) + return -ENOMEM; + return 0; +} + +static inline void __media_udev_close(struct udev *udev) +{ + if (udev != NULL) + udev_unref(udev); +} + +static int __media_get_devname_udev(struct udev *udev, + struct media_entity *entity) +{ + struct udev_device *device; + dev_t devnum; + const char *p; + int ret = -ENODEV; + + if (udev == NULL) + return -EINVAL; + + devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor); + ALOGE("looking up device: %u:%u", + major(devnum), minor(devnum)); + device = udev_device_new_from_devnum(udev, 'c', devnum); + if (device) { + p = udev_device_get_devnode(device); + if (p) { + strncpy(entity->devname, p, sizeof(entity->devname)); + entity->devname[sizeof(entity->devname) - 1] = '\0'; + } + ret = 0; + } + + udev_device_unref(device); + + return ret; +} + +#else /* HAVE_LIBUDEV */ + +struct udev; + +static inline int __media_udev_open(struct udev **udev) { return 0; } + +static inline void __media_udev_close(struct udev *udev) { } + +static inline int __media_get_devname_udev(struct udev *udev, + struct media_entity *entity) +{ + return -ENOTSUP; +} + +#endif /* HAVE_LIBUDEV */ + +/** + * @brief Parse string to a pad on the media device. + * @param media - media device. + * @param p - input string + * @param endp - pointer to string where parsing ended + * + * Parse NULL terminated string describing a pad and return its struct + * media_pad instance. + * + * @return Pointer to struct media_pad on success, NULL on failure. + */ +struct media_pad *exynos_media_parse_pad(struct media_device *media, + const char *p, char **endp) +{ + unsigned int entity_id, pad; + struct media_entity *entity; + char *end; + + for (; isspace(*p); ++p); + + if (*p == '"') { + for (end = (char *)p + 1; *end && *end != '"'; ++end); + if (*end != '"') + return NULL; + + entity = exynos_media_get_entity_by_name(media, p + 1, end - p - 1); + if (entity == NULL) + return NULL; + + ++end; + } else { + entity_id = strtoul(p, &end, 10); + entity = exynos_media_get_entity_by_id(media, entity_id); + if (entity == NULL) + return NULL; + } + for (; isspace(*end); ++end); + + if (*end != ':') + return NULL; + for (p = end + 1; isspace(*p); ++p); + + pad = strtoul(p, &end, 10); + for (p = end; isspace(*p); ++p); + + if (pad >= entity->info.pads) + return NULL; + + for (p = end; isspace(*p); ++p); + if (endp) + *endp = (char *)p; + + return &entity->pads[pad]; +} + +/** + * @brief Parse string to a link on the media device. + * @param media - media device. + * @param p - input string + * @param endp - pointer to p where parsing ended + * + * Parse NULL terminated string p describing a link and return its struct + * media_link instance. + * + * @return Pointer to struct media_link on success, NULL on failure. + */ +struct media_link *exynos_media_parse_link( + struct media_device *media, + const char *p, + char **endp) +{ + struct media_link *link; + struct media_pad *source; + struct media_pad *sink; + unsigned int i; + char *end; + + source = exynos_media_parse_pad(media, p, &end); + if (source == NULL) + return NULL; + + if (end[0] != '-' || end[1] != '>') + return NULL; + p = end + 2; + + sink = exynos_media_parse_pad(media, p, &end); + if (sink == NULL) + return NULL; + + *endp = end; + + for (i = 0; i < source->entity->num_links; i++) { + link = &source->entity->links[i]; + + if (link->source == source && link->sink == sink) + return link; + } + + return NULL; +} + +/** + * @brief Parse string to a link on the media device and set it up. + * @param media - media device. + * @param p - input string + * + * Parse NULL terminated string p describing a link and its configuration + * and configure the link. + * + * @return 0 on success, or a negative error code on failure. + */ +int exynos_media_parse_setup_link( + struct media_device *media, + const char *p, + char **endp) +{ + struct media_link *link; + __u32 flags; + char *end; + + link = exynos_media_parse_link(media, p, &end); + if (link == NULL) { + ALOGE("Unable to parse link"); + return -EINVAL; + } + + p = end; + if (*p++ != '[') { + ALOGE("Unable to parse link flags"); + return -EINVAL; + } + + flags = strtoul(p, &end, 10); + for (p = end; isspace(*p); p++); + if (*p++ != ']') { + ALOGE("Unable to parse link flags"); + return -EINVAL; + } + + for (; isspace(*p); p++); + *endp = (char *)p; + + ALOGD("%s: Setting up link %u:%u -> %u:%u [%u]", __func__, + link->source->entity->info.id, link->source->index, + link->sink->entity->info.id, link->sink->index, + flags); + + return exynos_media_setup_link(media, link->source, link->sink, flags); +} + +/** + * @brief Parse string to link(s) on the media device and set it up. + * @param media - media device. + * @param p - input string + * + * Parse NULL terminated string p describing link(s) separated by + * commas (,) and configure the link(s). + * + * @return 0 on success, or a negative error code on failure. + */ +int exynos_media_parse_setup_links(struct media_device *media, const char *p) +{ + char *end; + int ret; + + do { + ret = exynos_media_parse_setup_link(media, p, &end); + if (ret < 0) + return ret; + + p = end + 1; + } while (*end == ','); + + return *end ? -EINVAL : 0; +} diff --git a/libv4l2/exynos_subdev.c b/libv4l2/exynos_subdev.c new file mode 100644 index 0000000..54b6e74 --- /dev/null +++ b/libv4l2/exynos_subdev.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! + * \file exynos_subdev.c + * \brief source file for libv4l2 + * \author Jinsung Yang (jsgood.yang@samsung.com) + * \author Sangwoo Park (sw5771.park@samsung.com) + * \date 2012/01/17 + * + * Revision History: + * - 2012/01/17: Jinsung Yang (jsgood.yang@samsung.com) \n + * Initial version + * + */ + +#include +#include +#include +#include +#include +#include + +#include "exynos_v4l2.h" + +//#define LOG_NDEBUG 0 +#define LOG_TAG "libexynosv4l2-subdev" +#include + +#define SUBDEV_MINOR_MAX 191 + +static int __subdev_open(const char *filename, int oflag, va_list ap) +{ + mode_t mode = 0; + int fd; + + if (oflag & O_CREAT) + mode = va_arg(ap, int); + + fd = open(filename, oflag, mode); + + return fd; +} + +int exynos_subdev_open(const char *filename, int oflag, ...) +{ + va_list ap; + int fd; + + va_start(ap, oflag); + fd = __subdev_open(filename, oflag, ap); + va_end(ap); + + return fd; +} + +int exynos_subdev_open_devname(const char *devname, int oflag, ...) +{ + bool found = false; + int fd = -1; + struct stat s; + va_list ap; + FILE *stream_fd; + char filename[64], name[64]; + int minor, size, i = 0; + + do { + if (i > (SUBDEV_MINOR_MAX - 128)) + break; + + /* video device node */ + sprintf(filename, "/dev/v4l-subdev%d", i++); + + /* if the node is video device */ + if ((lstat(filename, &s) == 0) && S_ISCHR(s.st_mode) && + ((int)((unsigned short)(s.st_rdev) >> 8) == 81)) { + minor = (int)((unsigned short)(s.st_rdev & 0x3f)); + ALOGD("try node: %s, minor: %d", filename, minor); + /* open sysfs entry */ + sprintf(filename, "/sys/class/video4linux/v4l-subdev%d/name", minor); + stream_fd = fopen(filename, "r"); + if (stream_fd == NULL) { + ALOGE("failed to open sysfs entry for subdev"); + continue; /* try next */ + } + + /* read sysfs entry for device name */ + size = (int)fgets(name, sizeof(name), stream_fd); + fclose(stream_fd); + + /* check read size */ + if (size == 0) { + ALOGE("failed to read sysfs entry for subdev"); + } else { + /* matched */ + if (strncmp(name, devname, strlen(devname)) == 0) { + ALOGI("node found for device %s: /dev/v4l-subdev%d", devname, minor); + found = true; + } + } + } + } while (found == false); + + if (found) { + sprintf(filename, "/dev/v4l-subdev%d", minor); + va_start(ap, oflag); + fd = __subdev_open(filename, oflag, ap); + va_end(ap); + + if (fd > 0) + ALOGI("open subdev device %s", filename); + else + ALOGE("failed to open subdev device %s", filename); + } else { + ALOGE("no subdev device found"); + } + + return fd; +} + +/** + * @brief enum frame size on a pad. + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_enum_frame_size(int fd, struct v4l2_subdev_frame_size_enum *frame_size_enum) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!frame_size_enum) { + ALOGE("%s: frame_size_enum is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, frame_size_enum); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE"); + return ret; + } + + return ret; +} + +/** + * @brief Retrieve the format on a pad. + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_g_fmt(int fd, struct v4l2_subdev_format *fmt) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!fmt) { + ALOGE("%s: fmt is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_G_FMT, fmt); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_FMT"); + return ret; + } + + return ret; +} + +/** + * @brief Set the format on a pad. + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_s_fmt(int fd, struct v4l2_subdev_format *fmt) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!fmt) { + ALOGE("%s: fmt is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_S_FMT, fmt); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_FMT"); + return ret; + } + + return ret; +} + +/** + * @brief Retrieve the crop rectangle on a pad. + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_g_crop(int fd, struct v4l2_subdev_crop *crop) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!crop) { + ALOGE("%s: crop is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_G_CROP, crop); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_CROP"); + return ret; + } + + return ret; +} + +/** + * @brief Set the crop rectangle on a pad. + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_s_crop(int fd, struct v4l2_subdev_crop *crop) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!crop) { + ALOGE("%s: crop is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_S_CROP, crop); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_CROP"); + return ret; + } + + return ret; +} + +/** + * @brief Retrieve the frame interval on a sub-device. + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_enum_frame_interval(int fd, struct v4l2_subdev_frame_interval_enum *frame_internval_enum) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!frame_internval_enum) { + ALOGE("%s: frame_internval_enum is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, frame_internval_enum); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL"); + return ret; + } + + return ret; +} + +/** + * @brief Retrieve the frame interval on a sub-device. + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_g_frame_interval(int fd, struct v4l2_subdev_frame_interval *frame_internval) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!frame_internval) { + ALOGE("%s: frame_internval is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, frame_internval); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL"); + return ret; + } + + return ret; +} + +/** + * @brief Set the frame interval on a sub-device. + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_s_frame_interval(int fd, struct v4l2_subdev_frame_interval *frame_internval) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!frame_internval) { + ALOGE("%s: frame_internval is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, frame_internval); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL"); + return ret; + } + + return ret; +} + +/** + * @brief enum mbus code + * @return 0 on success, or a negative error code on failure. + */ +int exynos_subdev_enum_mbus_code(int fd, struct v4l2_subdev_mbus_code_enum *mbus_code_enum) +{ + int ret = -1; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!mbus_code_enum) { + ALOGE("%s: mbus_code_enum is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_MBUS_CODE, mbus_code_enum); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE"); + return ret; + } + + return ret; +} diff --git a/libv4l2/exynos_v4l2.c b/libv4l2/exynos_v4l2.c new file mode 100644 index 0000000..c2b0131 --- /dev/null +++ b/libv4l2/exynos_v4l2.c @@ -0,0 +1,858 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! + * \file exynos_v4l2.c + * \brief source file for libv4l2 + * \author Jinsung Yang (jsgood.yang@samsung.com) + * \author Sangwoo Park (sw5771.park@samsung.com) + * \date 2012/01/17 + * + * Revision History: + * - 2012/01/17: Jinsung Yang (jsgood.yang@samsung.com) \n + * Initial version + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exynos_v4l2.h" + +//#define LOG_NDEBUG 0 +#define LOG_TAG "libexynosv4l2" +#include +#include "Exynos_log.h" + +#define VIDEODEV_MINOR_MAX 63 + +//#define EXYNOS_V4L2_TRACE 0 +#ifdef EXYNOS_V4L2_TRACE +#define Exynos_v4l2_In() Exynos_Log(EXYNOS_DEV_LOG_DEBUG, LOG_TAG, "%s In , Line: %d", __FUNCTION__, __LINE__) +#define Exynos_v4l2_Out() Exynos_Log(EXYNOS_DEV_LOG_DEBUG, LOG_TAG, "%s Out , Line: %d", __FUNCTION__, __LINE__) +#else +#define Exynos_v4l2_In() ((void *)0) +#define Exynos_v4l2_Out() ((void *)0) +#endif + +static bool __v4l2_check_buf_type(enum v4l2_buf_type type) +{ + bool supported; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + supported = true; + break; + + default: + supported = (type >= V4L2_BUF_TYPE_PRIVATE) ? true : false; + break; + } + + return supported; +} + +static int __v4l2_open(const char *filename, int oflag, va_list ap) +{ + mode_t mode = 0; + int fd; + + if (oflag & O_CREAT) + mode = va_arg(ap, int); + + fd = open(filename, oflag, mode); + + return fd; +} + +int exynos_v4l2_open(const char *filename, int oflag, ...) +{ + va_list ap; + int fd; + + Exynos_v4l2_In(); + + va_start(ap, oflag); + fd = __v4l2_open(filename, oflag, ap); + va_end(ap); + + Exynos_v4l2_Out(); + + return fd; +} + +int exynos_v4l2_open_devname(const char *devname, int oflag, ...) +{ + bool found = false; + int fd = -1; + struct stat s; + va_list ap; + FILE *stream_fd; + char filename[64], name[64]; + int minor, size, i = 0; + + Exynos_v4l2_In(); + + do { + if (i > VIDEODEV_MINOR_MAX) + break; + + /* video device node */ + sprintf(filename, "/dev/video%d", i++); + + /* if the node is video device */ + if ((lstat(filename, &s) == 0) && S_ISCHR(s.st_mode) && + ((int)((unsigned short)(s.st_rdev) >> 8) == 81)) { + minor = (int)((unsigned short)(s.st_rdev & 0x3f)); + ALOGD("try node: %s, minor: %d", filename, minor); + /* open sysfs entry */ + sprintf(filename, "/sys/class/video4linux/video%d/name", minor); + stream_fd = fopen(filename, "r"); + if (stream_fd == NULL) { + ALOGE("failed to open sysfs entry for videodev"); + continue; /* try next */ + } + + /* read sysfs entry for device name */ + size = (int)fgets(name, sizeof(name), stream_fd); + fclose(stream_fd); + + /* check read size */ + if (size == 0) { + ALOGE("failed to read sysfs entry for videodev"); + } else { + /* matched */ + if (strncmp(name, devname, strlen(devname)) == 0) { + ALOGI("node found for device %s: /dev/video%d", devname, minor); + found = true; + } + } + } + } while (found == false); + + if (found) { + sprintf(filename, "/dev/video%d", minor); + va_start(ap, oflag); + fd = __v4l2_open(filename, oflag, ap); + va_end(ap); + + if (fd > 0) + ALOGI("open video device %s", filename); + else + ALOGE("failed to open video device %s", filename); + } else { + ALOGE("no video device found"); + } + + Exynos_v4l2_Out(); + + return fd; +} + +int exynos_v4l2_close(int fd) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) + ALOGE("%s: invalid fd: %d", __func__, fd); + else + ret = close(fd); + + Exynos_v4l2_Out(); + + return ret; +} + +bool exynos_v4l2_enuminput(int fd, int index, char *input_name_buf) +{ + int ret = -1; + struct v4l2_input input; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return NULL; + } + + input.index = index; + ret = ioctl(fd, VIDIOC_ENUMINPUT, &input); + if (ret) { + ALOGE("%s: no matching index founds", __func__); + return false; + } + + ALOGI("Name of input channel[%d] is %s", input.index, input.name); + + strcpy(input_name_buf, (const char *)input.name); + + Exynos_v4l2_Out(); + + return true; +} + +int exynos_v4l2_s_input(int fd, int index) +{ + int ret = -1; + struct v4l2_input input; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + input.index = index; + + ret = ioctl(fd, VIDIOC_S_INPUT, &input); + if (ret){ + ALOGE("failed to ioctl: VIDIOC_S_INPUT (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +bool exynos_v4l2_querycap(int fd, unsigned int need_caps) +{ + struct v4l2_capability cap; + int ret; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return false; + } + + if (!(need_caps & V4L2_CAP_VIDEO_CAPTURE) && + !(need_caps & V4L2_CAP_VIDEO_CAPTURE_MPLANE) && + !(need_caps & V4L2_CAP_VIDEO_OUTPUT) && + !(need_caps & V4L2_CAP_VIDEO_OUTPUT_MPLANE) && + !(need_caps & V4L2_CAP_VIDEO_OVERLAY)) { + ALOGE("%s: unsupported capabilities", __func__); + return false; + } + + memset(&cap, 0, sizeof(cap)); + + ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_QUERYCAP (%d - %s)", errno, strerror(errno)); + return false; + } + + if ((need_caps & cap.capabilities) != need_caps) { + ALOGE("%s: unsupported capabilities", __func__); + return false; + } + + Exynos_v4l2_Out(); + + return true; +} + +bool exynos_v4l2_enum_fmt(int fd, enum v4l2_buf_type type, unsigned int fmt) +{ + struct v4l2_fmtdesc fmtdesc; + int found = 0; + + Exynos_v4l2_In(); + + fmtdesc.type = type; + fmtdesc.index = 0; + + while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) { + if (fmtdesc.pixelformat == fmt) { + ALOGE("Passed fmt = %#x found pixel format[%d]: %s", fmt, fmtdesc.index, fmtdesc.description); + found = 1; + break; + } + + fmtdesc.index++; + } + + if (!found) { + ALOGE("%s: unsupported pixel format", __func__); + return false; + } + + Exynos_v4l2_Out(); + + return true; +} + +int exynos_v4l2_g_fmt(int fd, struct v4l2_format *fmt) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!fmt) { + ALOGE("%s: fmt is NULL", __func__); + return ret; + } + + if (__v4l2_check_buf_type(fmt->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_G_FMT, fmt); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_G_FMT (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +static int __v4l2_s_fmt(int fd, unsigned int request, struct v4l2_format *fmt) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!fmt) { + ALOGE("%s: fmt is NULL", __func__); + return ret; + } + + if (__v4l2_check_buf_type(fmt->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } else { + ret = ioctl(fd, request, fmt); + if (ret) { + if (request == VIDIOC_TRY_FMT) + ALOGE("failed to ioctl: VIDIOC_TRY_FMT (%d - %s)", errno, strerror(errno)); + else + ALOGE("failed to ioctl: VIDIOC_S_FMT (%d - %s)", errno, strerror(errno)); + + return ret; + } + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_try_fmt(int fd, struct v4l2_format *fmt) +{ + return __v4l2_s_fmt(fd, VIDIOC_TRY_FMT, fmt); +} + +int exynos_v4l2_s_fmt(int fd, struct v4l2_format *fmt) +{ + return __v4l2_s_fmt(fd, VIDIOC_S_FMT, fmt); +} + +int exynos_v4l2_reqbufs(int fd, struct v4l2_requestbuffers *req) +{ + int ret = -1; + unsigned int count; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!req) { + ALOGE("%s: req is NULL", __func__); + return ret; + } + + if ((req->memory != V4L2_MEMORY_MMAP) && + (req->memory != V4L2_MEMORY_USERPTR) && + (req->memory != V4L2_MEMORY_DMABUF)) { + ALOGE("%s: unsupported memory type", __func__); + return ret; + } + + if (__v4l2_check_buf_type(req->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + count = req->count; + + ret = ioctl(fd, VIDIOC_REQBUFS, req); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_REQBUFS (%d - %s)", ret, strerror(errno)); + return ret; + } + + if (count != req->count) { + ALOGW("number of buffers had been changed: %d => %d", count, req->count); + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_querybuf(int fd, struct v4l2_buffer *buf) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!buf) { + ALOGE("%s: buf is NULL", __func__); + return ret; + } + + if ((buf->memory != V4L2_MEMORY_MMAP) && + (buf->memory != V4L2_MEMORY_DMABUF)) { + ALOGE("%s: unsupported memory type", __func__); + return ret; + } + + if (__v4l2_check_buf_type(buf->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_QUERYBUF, buf); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_QUERYBUF (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_qbuf(int fd, struct v4l2_buffer *buf) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!buf) { + ALOGE("%s: buf is NULL", __func__); + return ret; + } + + if ((buf->memory != V4L2_MEMORY_MMAP) && + (buf->memory != V4L2_MEMORY_USERPTR) && + (buf->memory != V4L2_MEMORY_DMABUF)) { + ALOGE("%s: unsupported memory type", __func__); + return ret; + } + + if (__v4l2_check_buf_type(buf->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_QBUF, buf); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_QBUF (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_dqbuf(int fd, struct v4l2_buffer *buf) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!buf) { + ALOGE("%s: buf is NULL", __func__); + return ret; + } + + if ((buf->memory != V4L2_MEMORY_MMAP) && + (buf->memory != V4L2_MEMORY_USERPTR) && + (buf->memory != V4L2_MEMORY_DMABUF)) { + ALOGE("%s: unsupported memory type", __func__); + return ret; + } + + if (__v4l2_check_buf_type(buf->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_DQBUF, buf); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_DQBUF (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_streamon(int fd, enum v4l2_buf_type type) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (__v4l2_check_buf_type(type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_STREAMON, &type); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_STREAMON (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_streamoff(int fd, enum v4l2_buf_type type) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (__v4l2_check_buf_type(type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_STREAMOFF, &type); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_STREAMOFF (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_cropcap(int fd, struct v4l2_cropcap *crop) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!crop) { + ALOGE("%s: crop is NULL", __func__); + return ret; + } + + if (__v4l2_check_buf_type(crop->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_CROPCAP, crop); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_CROPCAP (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_g_crop(int fd, struct v4l2_crop *crop) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!crop) { + ALOGE("%s: crop is NULL", __func__); + return ret; + } + + if (__v4l2_check_buf_type(crop->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_G_CROP, crop); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_G_CROP (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_s_crop(int fd, struct v4l2_crop *crop) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (!crop) { + ALOGE("%s: crop is NULL", __func__); + return ret; + } + + if (__v4l2_check_buf_type(crop->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_S_CROP, crop); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_S_CROP (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_g_ctrl(int fd, unsigned int id, int *value) +{ + int ret = -1; + struct v4l2_control ctrl; + + Exynos_v4l2_In(); + + ctrl.id = id; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + ret = ioctl(fd, VIDIOC_G_CTRL, &ctrl); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_G_CTRL (%d - %s)", errno, strerror(errno)); + return ret; + } + + *value = ctrl.value; + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_s_ctrl(int fd, unsigned int id, int value) +{ + int ret = -1; + struct v4l2_control ctrl; + + Exynos_v4l2_In(); + + ctrl.id = id; + ctrl.value = value; + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + ret = ioctl(fd, VIDIOC_S_CTRL, &ctrl); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_S_CTRL (%d)", errno); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_g_parm(int fd, struct v4l2_streamparm *streamparm) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (__v4l2_check_buf_type(streamparm->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_G_PARM, streamparm); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_G_PARM (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_s_parm(int fd, struct v4l2_streamparm *streamparm) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (__v4l2_check_buf_type(streamparm->type) == false) { + ALOGE("%s: unsupported buffer type", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_S_PARM, streamparm); + if (ret) { + ALOGE("failed to ioctl: VIDIOC_S_PARM (%d - %s)", errno, strerror(errno)); + return ret; + } + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_g_ext_ctrl(int fd, struct v4l2_ext_controls *ctrl) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (ctrl == NULL) { + ALOGE("%s: ctrl is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_G_EXT_CTRLS, ctrl); + if (ret) + ALOGE("failed to ioctl: VIDIOC_G_EXT_CTRLS (%d - %s)", errno, strerror(errno)); + + Exynos_v4l2_Out(); + + return ret; +} + +int exynos_v4l2_s_ext_ctrl(int fd, struct v4l2_ext_controls *ctrl) +{ + int ret = -1; + + Exynos_v4l2_In(); + + if (fd < 0) { + ALOGE("%s: invalid fd: %d", __func__, fd); + return ret; + } + + if (ctrl == NULL) { + ALOGE("%s: ctrl is NULL", __func__); + return ret; + } + + ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, ctrl); + if (ret) + ALOGE("failed to ioctl: VIDIOC_S_EXT_CTRLS (%d - %s)", errno, strerror(errno)); + + Exynos_v4l2_Out(); + + return ret; +} -- 2.20.1