--- /dev/null
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.\r
+\r
+ Apache License\r
+ Version 2.0, January 2004\r
+ http://www.apache.org/licenses/\r
+\r
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r
+\r
+ 1. Definitions.\r
+\r
+ "License" shall mean the terms and conditions for use, reproduction,\r
+ and distribution as defined by Sections 1 through 9 of this document.\r
+\r
+ "Licensor" shall mean the copyright owner or entity authorized by\r
+ the copyright owner that is granting the License.\r
+\r
+ "Legal Entity" shall mean the union of the acting entity and all\r
+ other entities that control, are controlled by, or are under common\r
+ control with that entity. For the purposes of this definition,\r
+ "control" means (i) the power, direct or indirect, to cause the\r
+ direction or management of such entity, whether by contract or\r
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the\r
+ outstanding shares, or (iii) beneficial ownership of such entity.\r
+\r
+ "You" (or "Your") shall mean an individual or Legal Entity\r
+ exercising permissions granted by this License.\r
+\r
+ "Source" form shall mean the preferred form for making modifications,\r
+ including but not limited to software source code, documentation\r
+ source, and configuration files.\r
+\r
+ "Object" form shall mean any form resulting from mechanical\r
+ transformation or translation of a Source form, including but\r
+ not limited to compiled object code, generated documentation,\r
+ and conversions to other media types.\r
+\r
+ "Work" shall mean the work of authorship, whether in Source or\r
+ Object form, made available under the License, as indicated by a\r
+ copyright notice that is included in or attached to the work\r
+ (an example is provided in the Appendix below).\r
+\r
+ "Derivative Works" shall mean any work, whether in Source or Object\r
+ form, that is based on (or derived from) the Work and for which the\r
+ editorial revisions, annotations, elaborations, or other modifications\r
+ represent, as a whole, an original work of authorship. For the purposes\r
+ of this License, Derivative Works shall not include works that remain\r
+ separable from, or merely link (or bind by name) to the interfaces of,\r
+ the Work and Derivative Works thereof.\r
+\r
+ "Contribution" shall mean any work of authorship, including\r
+ the original version of the Work and any modifications or additions\r
+ to that Work or Derivative Works thereof, that is intentionally\r
+ submitted to Licensor for inclusion in the Work by the copyright owner\r
+ or by an individual or Legal Entity authorized to submit on behalf of\r
+ the copyright owner. For the purposes of this definition, "submitted"\r
+ means any form of electronic, verbal, or written communication sent\r
+ to the Licensor or its representatives, including but not limited to\r
+ communication on electronic mailing lists, source code control systems,\r
+ and issue tracking systems that are managed by, or on behalf of, the\r
+ Licensor for the purpose of discussing and improving the Work, but\r
+ excluding communication that is conspicuously marked or otherwise\r
+ designated in writing by the copyright owner as "Not a Contribution."\r
+\r
+ "Contributor" shall mean Licensor and any individual or Legal Entity\r
+ on behalf of whom a Contribution has been received by Licensor and\r
+ subsequently incorporated within the Work.\r
+\r
+ 2. Grant of Copyright License. Subject to the terms and conditions of\r
+ this License, each Contributor hereby grants to You a perpetual,\r
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+ copyright license to reproduce, prepare Derivative Works of,\r
+ publicly display, publicly perform, sublicense, and distribute the\r
+ Work and such Derivative Works in Source or Object form.\r
+\r
+ 3. Grant of Patent License. Subject to the terms and conditions of\r
+ this License, each Contributor hereby grants to You a perpetual,\r
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+ (except as stated in this section) patent license to make, have made,\r
+ use, offer to sell, sell, import, and otherwise transfer the Work,\r
+ where such license applies only to those patent claims licensable\r
+ by such Contributor that are necessarily infringed by their\r
+ Contribution(s) alone or by combination of their Contribution(s)\r
+ with the Work to which such Contribution(s) was submitted. If You\r
+ institute patent litigation against any entity (including a\r
+ cross-claim or counterclaim in a lawsuit) alleging that the Work\r
+ or a Contribution incorporated within the Work constitutes direct\r
+ or contributory patent infringement, then any patent licenses\r
+ granted to You under this License for that Work shall terminate\r
+ as of the date such litigation is filed.\r
+\r
+ 4. Redistribution. You may reproduce and distribute copies of the\r
+ Work or Derivative Works thereof in any medium, with or without\r
+ modifications, and in Source or Object form, provided that You\r
+ meet the following conditions:\r
+\r
+ (a) You must give any other recipients of the Work or\r
+ Derivative Works a copy of this License; and\r
+\r
+ (b) You must cause any modified files to carry prominent notices\r
+ stating that You changed the files; and\r
+\r
+ (c) You must retain, in the Source form of any Derivative Works\r
+ that You distribute, all copyright, patent, trademark, and\r
+ attribution notices from the Source form of the Work,\r
+ excluding those notices that do not pertain to any part of\r
+ the Derivative Works; and\r
+\r
+ (d) If the Work includes a "NOTICE" text file as part of its\r
+ distribution, then any Derivative Works that You distribute must\r
+ include a readable copy of the attribution notices contained\r
+ within such NOTICE file, excluding those notices that do not\r
+ pertain to any part of the Derivative Works, in at least one\r
+ of the following places: within a NOTICE text file distributed\r
+ as part of the Derivative Works; within the Source form or\r
+ documentation, if provided along with the Derivative Works; or,\r
+ within a display generated by the Derivative Works, if and\r
+ wherever such third-party notices normally appear. The contents\r
+ of the NOTICE file are for informational purposes only and\r
+ do not modify the License. You may add Your own attribution\r
+ notices within Derivative Works that You distribute, alongside\r
+ or as an addendum to the NOTICE text from the Work, provided\r
+ that such additional attribution notices cannot be construed\r
+ as modifying the License.\r
+\r
+ You may add Your own copyright statement to Your modifications and\r
+ may provide additional or different license terms and conditions\r
+ for use, reproduction, or distribution of Your modifications, or\r
+ for any such Derivative Works as a whole, provided Your use,\r
+ reproduction, and distribution of the Work otherwise complies with\r
+ the conditions stated in this License.\r
+\r
+ 5. Submission of Contributions. Unless You explicitly state otherwise,\r
+ any Contribution intentionally submitted for inclusion in the Work\r
+ by You to the Licensor shall be under the terms and conditions of\r
+ this License, without any additional terms or conditions.\r
+ Notwithstanding the above, nothing herein shall supersede or modify\r
+ the terms of any separate license agreement you may have executed\r
+ with Licensor regarding such Contributions.\r
+\r
+ 6. Trademarks. This License does not grant permission to use the trade\r
+ names, trademarks, service marks, or product names of the Licensor,\r
+ except as required for reasonable and customary use in describing the\r
+ origin of the Work and reproducing the content of the NOTICE file.\r
+\r
+ 7. Disclaimer of Warranty. Unless required by applicable law or\r
+ agreed to in writing, Licensor provides the Work (and each\r
+ Contributor provides its Contributions) on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r
+ implied, including, without limitation, any warranties or conditions\r
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r
+ PARTICULAR PURPOSE. You are solely responsible for determining the\r
+ appropriateness of using or redistributing the Work and assume any\r
+ risks associated with Your exercise of permissions under this License.\r
+\r
+ 8. Limitation of Liability. In no event and under no legal theory,\r
+ whether in tort (including negligence), contract, or otherwise,\r
+ unless required by applicable law (such as deliberate and grossly\r
+ negligent acts) or agreed to in writing, shall any Contributor be\r
+ liable to You for damages, including any direct, indirect, special,\r
+ incidental, or consequential damages of any character arising as a\r
+ result of this License or out of the use or inability to use the\r
+ Work (including but not limited to damages for loss of goodwill,\r
+ work stoppage, computer failure or malfunction, or any and all\r
+ other commercial damages or losses), even if such Contributor\r
+ has been advised of the possibility of such damages.\r
+\r
+ 9. Accepting Warranty or Additional Liability. While redistributing\r
+ the Work or Derivative Works thereof, You may choose to offer,\r
+ and charge a fee for, acceptance of support, warranty, indemnity,\r
+ or other liability obligations and/or rights consistent with this\r
+ License. However, in accepting such obligations, You may act only\r
+ on Your own behalf and on Your sole responsibility, not on behalf\r
+ of any other Contributor, and only if You agree to indemnify,\r
+ defend, and hold each Contributor harmless for any liability\r
+ incurred by, or claims asserted against, such Contributor by reason\r
+ of your accepting any such warranty or additional liability.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+ APPENDIX: How to apply the Apache License to your work.\r
+\r
+ To apply the Apache License to your work, attach the following\r
+ boilerplate notice, with the fields enclosed by brackets "[]"\r
+ replaced with your own identifying information. (Don't include\r
+ the brackets!) The text should be enclosed in the appropriate\r
+ comment syntax for the file format. We also recommend that a\r
+ file or class name and description of purpose be included on the\r
+ same "printed page" as the copyright notice for easier\r
+ identification within third-party archives.\r
+\r
+ Copyright [yyyy] [name of copyright owner]\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+\r
+\r
--- /dev/null
+/*
+ * tizen_camera_v4l2.c
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glob.h>
+#include <dlog.h>
+#include <sched.h>
+#include "tizen_camera_v4l2_private.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif /* LOG_TAG */
+#define LOG_TAG "CAMERA_HAL"
+
+#define TEST_JPEG_PATH "/home/owner/media/Images/test.jpg"
+#define DEVICE_NODE_PATH_MAX 16
+#define DEVICE_NODE_PATH_PREFIX "/dev/video"
+#define FOURCC_FORMAT "%c%c%c%c"
+#define FOURCC_CONVERT(fourcc) \
+ fourcc & 0xff,\
+ (fourcc >> 8) & 0xff,\
+ (fourcc >> 16) & 0xff,\
+ (fourcc >> 24) & 0xff
+
+
+static camera_device_info_list_t *g_device_info_list;
+static guint32 device_caps;
+static GMutex g_device_info_lock;
+
+
+static void _camera_hal_v4l2_destructor(void) __attribute__((destructor));
+
+static void _camera_hal_v4l2_destructor(void)
+{
+ LOGD("release device info list %p", g_device_info_list);
+
+ g_free(g_device_info_list);
+
+ return;
+}
+
+static int _camera_v4l2_wait_frame(int device_fd, int wait_time)
+{
+ int ret = CAMERA_ERROR_NONE;
+ fd_set fds;
+ struct timeval timeout;
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(device_fd, &fds);
+
+ memset(&timeout, 0x0, sizeof(struct timeval));
+
+ timeout.tv_sec = wait_time;
+ timeout.tv_usec = 0;
+
+ LOGD("select : %d sec", wait_time);
+
+ ret = select(device_fd + 1, &fds, NULL, NULL, &timeout);
+ if (ret == -1) {
+ if (EINTR == errno) {
+ LOGD("select error : EINTR");
+ return CAMERA_ERROR_NONE;
+ }
+ LOGE("select failed. errno %d", errno);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ if (ret == 0) {
+ LOGE("select timeout.");
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ LOGD("select done");
+
+ return CAMERA_ERROR_NONE;
+}
+
+
+static int _camera_v4l2_stream(int device_fd, int type, gboolean onoff)
+{
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (ioctl(device_fd, onoff ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type) < 0) {
+ LOGE("stream %d failed. [t:%d] errno %d", onoff, type, errno);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ LOGD("stream %d done [t:%d]", onoff, type);
+
+ return CAMERA_ERROR_NONE;
+}
+
+
+static int _camera_v4l2_reqbufs(int device_fd, int type, int memory, int count, int *result_count)
+{
+ struct v4l2_requestbuffers v4l2_reqbuf;
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!result_count) {
+ LOGE("NULL parameter");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&v4l2_reqbuf, 0x0, sizeof(struct v4l2_requestbuffers));
+
+ v4l2_reqbuf.type = type;
+ v4l2_reqbuf.memory = memory;
+ v4l2_reqbuf.count = count;
+
+ if (ioctl(device_fd, VIDIOC_REQBUFS, &v4l2_reqbuf) < 0) {
+ LOGE("REQBUFS[count %d] failed. errno %d", count, errno);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ if (v4l2_reqbuf.count != count)
+ LOGW("different count [req:%d, result:%d]", count, v4l2_reqbuf.count);
+
+ *result_count = v4l2_reqbuf.count;
+
+ return CAMERA_ERROR_NONE;
+}
+
+
+static int _camera_v4l2_qbuf(int device_fd, int type, int memory, int index)
+{
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.index = index;
+ v4l2_buf.type = type;
+ v4l2_buf.memory = memory;
+ v4l2_buf.m.planes = v4l2_planes;
+ v4l2_buf.length = 460800;
+ v4l2_buf.bytesused = 460800;
+
+ if (ioctl(device_fd, VIDIOC_QBUF, &v4l2_buf) < 0) {
+ LOGE("qbuf failed. [i: %d, t: %d, m: %d] errno %d",
+ index, type, memory, errno);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ LOGD("QBUF done [i: %d, t: %d, m: %d]",
+ index, type, memory);
+
+ return CAMERA_ERROR_NONE;
+}
+
+
+static int _camera_v4l2_dqbuf(int device_fd, int type, int memory, int *index)
+{
+ int ret = CAMERA_ERROR_NONE;
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!index) {
+ LOGE("NULL parameter");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.type = type;
+ v4l2_buf.memory = memory;
+ v4l2_buf.m.planes = v4l2_planes;
+
+ ret = ioctl(device_fd, VIDIOC_DQBUF, &v4l2_buf);
+ if (ret < 0) {
+ if (errno != EIO) {
+ LOGE("dqbuf failed. [t: %d, m: %d] errno %d",
+ type, memory, errno);
+ return CAMERA_ERROR_DEVICE_READ;
+ }
+ }
+
+ *index = v4l2_buf.index;
+
+ LOGD("dqbuf index %d", *index);
+
+ return CAMERA_ERROR_NONE;
+}
+
+
+static int _camera_get_format(guint32 fourcc, int *pixel_format)
+{
+ if (!pixel_format) {
+ LOGE("NULL parameter");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12M:
+ case V4L2_PIX_FMT_NV12MT:
+ *pixel_format = CAMERA_PIXEL_FORMAT_NV12;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV21M:
+ *pixel_format = CAMERA_PIXEL_FORMAT_NV21;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ *pixel_format = CAMERA_PIXEL_FORMAT_I420;
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ *pixel_format = CAMERA_PIXEL_FORMAT_YV12;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ *pixel_format = CAMERA_PIXEL_FORMAT_YUYV;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ *pixel_format = CAMERA_PIXEL_FORMAT_UYVY;
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ *pixel_format = CAMERA_PIXEL_FORMAT_JPEG;
+ break;
+ case V4L2_PIX_FMT_H264:
+ *pixel_format = CAMERA_PIXEL_FORMAT_H264;
+ break;
+ default:
+ LOGE("unknown fourcc "FOURCC_FORMAT, FOURCC_CONVERT(fourcc));
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ LOGD("fourcc "FOURCC_FORMAT" -> %d",
+ FOURCC_CONVERT(fourcc), *pixel_format);
+
+ return CAMERA_ERROR_NONE;
+}
+
+static int _camera_get_fourcc_plane_num(int pixel_format, guint32 *fourcc, uint8_t *plane_num)
+{
+ if (!fourcc || !plane_num) {
+ LOGE("NULL parameter %p %p", fourcc, plane_num);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ switch (pixel_format) {
+ case CAMERA_PIXEL_FORMAT_NV12:
+ *fourcc = V4L2_PIX_FMT_NV12;
+ *plane_num = 2;
+ break;
+ case CAMERA_PIXEL_FORMAT_NV21:
+ *fourcc = V4L2_PIX_FMT_NV21;
+ *plane_num = 2;
+ break;
+ case CAMERA_PIXEL_FORMAT_I420:
+ *fourcc = V4L2_PIX_FMT_YUV420;
+ *plane_num = 3;
+ break;
+ case CAMERA_PIXEL_FORMAT_YV12:
+ *fourcc = V4L2_PIX_FMT_YVU420;
+ *plane_num = 3;
+ break;
+ case CAMERA_PIXEL_FORMAT_YUYV:
+ *fourcc = V4L2_PIX_FMT_YUYV;
+ *plane_num = 1;
+ break;
+ case CAMERA_PIXEL_FORMAT_UYVY:
+ *fourcc = V4L2_PIX_FMT_UYVY;
+ *plane_num = 1;
+ break;
+ case CAMERA_PIXEL_FORMAT_JPEG:
+ *fourcc = V4L2_PIX_FMT_JPEG;
+ *plane_num = 1;
+ break;
+ case CAMERA_PIXEL_FORMAT_H264:
+ *fourcc = V4L2_PIX_FMT_H264;
+ *plane_num = 1;
+ break;
+ default:
+ LOGE("unknown format %d", pixel_format);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ LOGD("format %d -> fourcc "FOURCC_FORMAT,
+ pixel_format, FOURCC_CONVERT(*fourcc));
+
+ return CAMERA_ERROR_NONE;
+}
+
+
+static int _camera_get_device_info(int device_index, int device_fd, camera_device_info_t *device_info, char *node_path)
+{
+ int i = 0;
+ int j = 0;
+ int format_count = 0;
+ int resolution_count = 0;
+ int camera_format = 0;
+ struct v4l2_fmtdesc v4l2_format;
+ struct v4l2_frmsizeenum v4l2_frame;
+
+ if (device_fd < 0 || !device_info || !node_path) {
+ LOGE("invalid param %d %p %p", device_fd, device_info, node_path);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("Get Supported format and resolution");
+
+ for (i = 0 ; ; i++) {
+ memset(&v4l2_format, 0x0, sizeof(struct v4l2_fmtdesc));
+
+ v4l2_format.index = i;
+ v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (ioctl(device_fd, VIDIOC_ENUM_FMT, &v4l2_format) < 0) {
+ LOGW("\tformat : end of enumeration");
+ break;
+ }
+
+ LOGD("\tformat[%d] "FOURCC_FORMAT, i, FOURCC_CONVERT(v4l2_format.pixelformat));
+
+ if (_camera_get_format(v4l2_format.pixelformat, &camera_format) != CAMERA_ERROR_NONE)
+ continue;
+
+ device_info->format_list.formats[format_count] = camera_format;
+
+ resolution_count = 0;
+
+ for (j = 0 ; ; j++) {
+ memset(&v4l2_frame, 0x0, sizeof(struct v4l2_frmsizeenum));
+
+ v4l2_frame.index = j;
+ v4l2_frame.pixel_format = v4l2_format.pixelformat;
+
+ if (ioctl(device_fd, VIDIOC_ENUM_FRAMESIZES, &v4l2_frame) < 0) {
+ LOGW("\t\tframe : end of enumeration ");
+ break;
+ }
+
+ switch (v4l2_frame.type) {
+ case V4L2_FRMSIZE_TYPE_DISCRETE:
+ device_info->preview_list.resolutions[resolution_count].width = v4l2_frame.discrete.width;
+ device_info->preview_list.resolutions[resolution_count].height = v4l2_frame.discrete.height;
+ device_info->capture_list.resolutions[resolution_count].width = v4l2_frame.discrete.width;
+ device_info->capture_list.resolutions[resolution_count].height = v4l2_frame.discrete.height;
+ device_info->video_list.resolutions[resolution_count].width = v4l2_frame.discrete.width;
+ device_info->video_list.resolutions[resolution_count].height = v4l2_frame.discrete.height;
+
+ resolution_count++;
+
+ LOGD("\t\tsize[%d] %ux%u", j,
+ v4l2_frame.discrete.width,
+ v4l2_frame.discrete.height);
+ break;
+ case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+ LOGW("\t\tsize[%d] %ux%u - %ux%u", j,
+ v4l2_frame.stepwise.min_width,
+ v4l2_frame.stepwise.min_height,
+ v4l2_frame.stepwise.max_width,
+ v4l2_frame.stepwise.max_height);
+ break;
+ case V4L2_FRMSIZE_TYPE_STEPWISE:
+ LOGW("\t\tsize[%d] %ux%u - %ux%u (step %ux%u)",
+ v4l2_frame.stepwise.min_width,
+ v4l2_frame.stepwise.min_height,
+ v4l2_frame.stepwise.max_width,
+ v4l2_frame.stepwise.max_height,
+ v4l2_frame.stepwise.step_width,
+ v4l2_frame.stepwise.step_height);
+ break;
+ default:
+ LOGE("\t\tunknown frame type %d", v4l2_frame.type);
+ break;
+ }
+ }
+
+ device_info->preview_list.count = resolution_count;
+ device_info->capture_list.count = resolution_count;
+ device_info->video_list.count = resolution_count;
+
+ LOGD("\t\tresolution count [%d]", resolution_count);
+
+ format_count++;
+ }
+
+ device_info->index = device_index;
+ device_info->format_list.count = format_count;
+ device_info->facing_direction = CAMERA_FACING_DIRECTION_EXTERNAL;
+ snprintf(device_info->name, sizeof(device_info->name), "V4L2_CAMERA");
+ snprintf(device_info->node_path, sizeof(device_info->node_path), "%s", node_path);
+
+ LOGD("\tformat count [%d]", format_count);
+
+ return CAMERA_ERROR_NONE;
+}
+
+static int _camera_get_device_info_list(void)
+{
+ int i = 0;
+ int ret = 0;
+ int device_count = 0;
+ int device_fd = CAMERA_HAL_INITIAL_FD;
+ glob_t glob_buf;
+ struct v4l2_capability v4l2_cap;
+ camera_device_info_list_t *device_info_list = NULL;
+
+ g_mutex_lock(&g_device_info_lock);
+
+ if (g_device_info_list) {
+ LOGD("device info list is already existed");
+ ret = CAMERA_ERROR_NONE;
+ goto _GET_DEVICE_INFO_LIST_DONE;
+ }
+
+ device_info_list = g_new0(camera_device_info_list_t, 1);
+ if (!device_info_list) {
+ LOGE("failed to alloc device info structure");
+ ret = CAMERA_ERROR_OUT_OF_MEMORY;
+ goto _GET_DEVICE_INFO_LIST_DONE;
+ }
+
+ memset(&glob_buf, 0x0, sizeof(glob_t));
+
+ ret = glob(DEVICE_NODE_PATH_PREFIX"*", 0, 0, &glob_buf);
+ if (ret != 0) {
+ switch (ret) {
+ case GLOB_NOSPACE:
+ LOGE("out of memory");
+ ret = CAMERA_ERROR_OUT_OF_MEMORY;
+ goto _GET_DEVICE_INFO_LIST_DONE;
+ case GLOB_ABORTED:
+ LOGE("read error");
+ ret = CAMERA_ERROR_INTERNAL;
+ goto _GET_DEVICE_INFO_LIST_DONE;
+ case GLOB_NOMATCH:
+ LOGE("match not found");
+ ret = CAMERA_ERROR_INTERNAL;
+ goto _GET_DEVICE_INFO_LIST_DONE;
+ default:
+ LOGE("unknown eror : %d", ret);
+ ret = CAMERA_ERROR_INTERNAL;
+ goto _GET_DEVICE_INFO_LIST_DONE;
+ }
+ }
+
+ LOGD("device node count : %d", glob_buf.gl_pathc);
+
+ for (i = 0 ; i < glob_buf.gl_pathc ; i++) {
+ LOGD("[%d] check device [%s]", i, glob_buf.gl_pathv[i]);
+
+ device_fd = open(glob_buf.gl_pathv[i], O_RDWR);
+ if (device_fd < 0) {
+ LOGE("open failed [%s] errno %d", glob_buf.gl_pathv[i], errno);
+ continue;
+ }
+
+ memset(&v4l2_cap, 0x0, sizeof(struct v4l2_capability));
+
+ if (ioctl(device_fd, VIDIOC_QUERYCAP, &v4l2_cap) < 0) {
+ LOGE("querycap failed. errno %d", errno);
+ close(device_fd);
+ continue;
+ }
+
+ if (v4l2_cap.capabilities & V4L2_CAP_DEVICE_CAPS)
+ device_caps = v4l2_cap.device_caps;
+ else
+ device_caps = v4l2_cap.capabilities;
+
+ if (!(device_caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)) ||
+ (device_caps & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE))) {
+ LOGW("[%s] is not a capture device 0x%x", glob_buf.gl_pathv[i], device_caps);
+ close(device_fd);
+ continue;
+ }
+
+ ret = _camera_get_device_info(device_count, device_fd,
+ &device_info_list->device_info[device_count], glob_buf.gl_pathv[i]);
+
+ close(device_fd);
+
+ if (ret == CAMERA_ERROR_NONE)
+ device_count++;
+ }
+
+ device_info_list->count = device_count;
+ g_device_info_list = device_info_list;
+
+ LOGD("new g_device_info_list %p - device count %d",
+ g_device_info_list, device_count);
+
+_GET_DEVICE_INFO_LIST_DONE:
+ g_mutex_unlock(&g_device_info_lock);
+ LOGD("ret 0x%x", ret);
+
+ if (ret != CAMERA_ERROR_NONE)
+ g_free(device_info_list);
+
+ return ret;
+}
+
+
+static void *_camera_preview_handler_func(gpointer data)
+{
+ int index = 0;
+ camera_hal_handle *handle = (camera_hal_handle *)data;
+
+ if (!handle) {
+ LOGE("NULL handle for preview handler");
+ return NULL;
+ }
+
+ LOGD("enter - preview handler thread");
+
+ /* run buffer thread */
+ g_mutex_lock(&handle->preview_cb_lock);
+
+ while (handle->preview_cb_run) {
+ if (_camera_v4l2_wait_frame(handle->device_fd, 5) != CAMERA_ERROR_NONE) {
+ LOGE("frame wait failed");
+ break;
+ }
+
+ if (_camera_v4l2_dqbuf(handle->device_fd, handle->v4l2_type, V4L2_MEMORY_MMAP, &index) != CAMERA_ERROR_NONE) {
+ LOGE("dqbuf failed");
+ break;
+ }
+
+ handle->live_buffer_num++;
+
+ LOGD("live buffer num %d", handle->live_buffer_num);
+
+ g_mutex_unlock(&handle->preview_cb_lock);
+
+ if (handle->preview_cb) {
+ ((camera_preview_frame_cb)handle->preview_cb)(&handle->preview_buffer[index], NULL, handle->preview_cb_data);
+ } else {
+ LOGW("preview callback is NULL");
+ camera_release_preview_buffer((void *)handle, index);
+ }
+
+ sched_yield();
+
+ g_mutex_lock(&handle->preview_cb_lock);
+ }
+
+ g_mutex_unlock(&handle->preview_cb_lock);
+
+ LOGD("leave - preview handler thread");
+
+ return NULL;
+}
+
+
+static void _camera_message_release_func(gpointer data)
+{
+ camera_message_t *message = (camera_message_t *)data;
+
+ if (!message) {
+ LOGW("NULL message");
+ return;
+ }
+
+ LOGD("release message %p, type %d", message, message->type);
+
+ g_free(message);
+
+ return;
+}
+
+
+static void *_camera_message_handler_func(gpointer data)
+{
+ int i = 0;
+ camera_message_t *message = NULL;
+ camera_hal_handle *handle = (camera_hal_handle *)data;
+
+ if (!handle) {
+ LOGE("NULL handle for capture thread");
+ return NULL;
+ }
+
+ LOGD("enter - message thread");
+
+ g_mutex_lock(&handle->message_cb_lock);
+
+ while (handle->message_cb_run) {
+ if (g_queue_is_empty(handle->message_list)) {
+ LOGD("wait for message");
+ g_cond_wait(&handle->message_cb_cond, &handle->message_cb_lock);
+ LOGD("message signal received");
+ }
+
+ if (!handle->message_cb_run) {
+ LOGW("break message thread");
+ break;
+ }
+
+ message = g_queue_pop_head(handle->message_list);
+ if (!message) {
+ LOGW("NULL message");
+ continue;
+ }
+
+ g_mutex_unlock(&handle->message_cb_lock);
+
+ for (i = 0 ; i < MESSAGE_CALLBACK_MAX ; i++) {
+ if (handle->message_cb[i]) {
+ LOGD("call message callback type %d", message->type);
+ ((camera_message_cb)handle->message_cb[i])(message, handle->message_cb_data[i]);
+ }
+ }
+
+ g_free(message);
+ message = NULL;
+
+ g_mutex_lock(&handle->message_cb_lock);
+ }
+
+ g_mutex_unlock(&handle->message_cb_lock);
+
+ LOGD("leave - message thread");
+
+ return NULL;
+}
+
+
+void _camera_release_handle(camera_hal_handle *handle)
+{
+ if (!handle) {
+ LOGW("NULL handle");
+ return;
+ }
+
+ if (handle->message_thread) {
+ g_mutex_lock(&handle->message_cb_lock);
+ handle->message_cb_run = FALSE;
+ g_cond_signal(&handle->message_cb_cond);
+ g_mutex_unlock(&handle->message_cb_lock);
+ g_thread_join(handle->message_thread);
+ g_queue_free_full(handle->message_list, (GDestroyNotify)_camera_message_release_func);
+ handle->message_list = NULL;
+ }
+
+ g_mutex_clear(&handle->lock);
+ g_mutex_clear(&handle->preview_cb_lock);
+ g_mutex_clear(&handle->message_cb_lock);
+ g_cond_clear(&handle->preview_cb_cond);
+ g_cond_clear(&handle->message_cb_cond);
+
+ if (handle->bufmgr) {
+ tbm_bufmgr_deinit(handle->bufmgr);
+ handle->bufmgr = NULL;
+ }
+
+ LOGD("camera HAL handle %p destroy", handle);
+
+ g_free(handle);
+
+ return;
+}
+
+
+int camera_init(void **camera_handle)
+{
+ int ret = CAMERA_ERROR_NONE;
+ camera_hal_handle *new_handle = NULL;
+ tbm_bufmgr bufmgr = NULL;
+
+ LOGD("enter");
+
+ if (!camera_handle) {
+ LOGE("NULL pointer for handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ bufmgr = tbm_bufmgr_init(-1);
+ if (bufmgr == NULL) {
+ LOGE("get tbm bufmgr failed");
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ new_handle = g_new0(camera_hal_handle, 1);
+ if (!new_handle) {
+ LOGE("failed to alloc camera hal handle");
+ tbm_bufmgr_deinit(bufmgr);
+ return CAMERA_ERROR_OUT_OF_MEMORY;
+ }
+
+ new_handle->bufmgr = bufmgr;
+
+ g_mutex_init(&new_handle->lock);
+ g_mutex_init(&new_handle->preview_cb_lock);
+ g_mutex_init(&new_handle->message_cb_lock);
+ g_cond_init(&new_handle->preview_cb_cond);
+ g_cond_init(&new_handle->message_cb_cond);
+
+ /* message thread */
+ new_handle->message_list = g_queue_new();
+ new_handle->message_cb_run = TRUE;
+ new_handle->message_thread = g_thread_try_new("camera_hal_message_thread",
+ _camera_message_handler_func, (gpointer)new_handle, NULL);
+ if (!new_handle->message_thread) {
+ LOGE("failed to create message thread");
+ ret = CAMERA_ERROR_INTERNAL;
+ goto _INIT_ERROR;
+ }
+
+ new_handle->device_index = CAMERA_HAL_INITIAL_INDEX;
+ new_handle->device_fd = CAMERA_HAL_INITIAL_FD;
+ new_handle->state = CAMERA_STATE_INITIALIZED;
+
+ ret = _camera_get_device_info_list();
+ if (ret != CAMERA_ERROR_NONE) {
+ LOGE("get device info failed");
+ goto _INIT_ERROR;
+ }
+
+ *camera_handle = new_handle;
+
+ LOGD("camera HAL handle %p", new_handle);
+
+ return CAMERA_ERROR_NONE;
+
+_INIT_ERROR:
+ _camera_release_handle(new_handle);
+
+ return ret;
+}
+
+int camera_deinit(void *camera_handle)
+{
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CAMERA_STATE_INITIALIZED) {
+ LOGE("invalid state %d, can not destroy handle", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_STATE;
+ }
+
+ g_mutex_unlock(&handle->lock);
+
+ _camera_release_handle(handle);
+
+ return CAMERA_ERROR_NONE;
+}
+
+int camera_get_device_info_list(camera_device_info_list_t *device_info_list)
+{
+ int ret = 0;
+
+ if (!device_info_list) {
+ LOGE("NULL pointer for device_info_list");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ ret = _camera_get_device_info_list();
+ if (ret != CAMERA_ERROR_NONE) {
+ LOGE("get device info failed");
+ return ret;
+ }
+
+ memcpy(device_info_list, g_device_info_list, sizeof(camera_device_info_list_t));
+
+ return CAMERA_ERROR_NONE;
+}
+
+int camera_open_device(void *camera_handle, int device_index)
+{
+ int ret = CAMERA_ERROR_NONE;
+ int device_fd = CAMERA_HAL_INITIAL_FD;
+ char *node_path = NULL;
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CAMERA_STATE_INITIALIZED) {
+ LOGE("invalid state %d", handle->state);
+ ret = CAMERA_ERROR_INVALID_STATE;
+ goto _OPEN_DEVICE_DONE;
+ }
+
+ if (!g_device_info_list) {
+ LOGE("NO DEVICE INFO");
+ ret = CAMERA_ERROR_INTERNAL;
+ goto _OPEN_DEVICE_DONE;
+ }
+
+ if (device_index >= g_device_info_list->count) {
+ LOGE("invalid index %d [info:%d]", device_index, g_device_info_list->count);
+ ret = CAMERA_ERROR_INVALID_PARAMETER;
+ goto _OPEN_DEVICE_DONE;
+ }
+
+ node_path = g_device_info_list->device_info[device_index].node_path;
+
+ device_fd = open(node_path, O_RDWR);
+ if (device_fd < 0) {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ ret = CAMERA_ERROR_PERMISSION_DENIED;
+ break;
+ case ENOENT:
+ ret = CAMERA_ERROR_DEVICE_NOT_FOUND;
+ break;
+ case EBUSY:
+ ret = CAMERA_ERROR_DEVICE_BUSY;
+ break;
+ default:
+ ret = CAMERA_ERROR_DEVICE_OPEN;
+ break;
+ }
+
+ LOGE("open [%s] failed 0x%x [errno %d]",
+ node_path, ret, errno);
+
+ goto _OPEN_DEVICE_DONE;
+ }
+
+ if (device_caps & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
+ handle->v4l2_type = V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+ else
+ handle->v4l2_type = V4L2_CAP_VIDEO_CAPTURE;
+
+ handle->state = CAMERA_STATE_OPENED;
+ handle->device_index = device_index;
+ handle->device_fd = device_fd;
+
+ LOGD("[%d] device[%s] opened [fd %d, type %d]",
+ device_index, node_path, device_fd, handle->v4l2_type);
+
+_OPEN_DEVICE_DONE:
+ g_mutex_unlock(&handle->lock);
+
+ return ret;
+}
+
+int camera_close_device(void *camera_handle)
+{
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CAMERA_STATE_OPENED) {
+ LOGE("invalid state %d", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_STATE;
+ }
+
+ if (handle->device_fd >= 0) {
+ LOGD("close fd %d", handle->device_fd);
+
+ close(handle->device_fd);
+ handle->device_fd = CAMERA_HAL_INITIAL_FD;
+ } else {
+ LOGW("invalid fd %d", handle->device_fd);
+ }
+
+ handle->state = CAMERA_STATE_INITIALIZED;
+
+ LOGD("device [%d] closed", handle->device_index);
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+}
+
+int camera_add_message_callback(void *camera_handle, camera_message_cb callback, void *user_data, uint32_t *cb_id)
+{
+ uint32_t i = 0;
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!callback || !cb_id) {
+ LOGE("NULL pointer for callback %p or cb_id %p", callback, cb_id);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CAMERA_STATE_OPENED) {
+ LOGE("invalid state %d", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_STATE;
+ }
+
+ for (i = 0 ; i < MESSAGE_CALLBACK_MAX ; i++) {
+ if (handle->message_cb[i] == NULL) {
+ handle->message_cb[i] = callback;
+ handle->message_cb_data[i] = user_data;
+ *cb_id = i;
+ LOGD("message cb [%p] added, user data %p - id %u", callback, user_data, i);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_NONE;
+ }
+ }
+
+ g_mutex_unlock(&handle->lock);
+
+ LOGE("no available message cb slot");
+
+ return CAMERA_ERROR_INTERNAL;
+}
+
+int camera_remove_message_callback(void *camera_handle, uint32_t cb_id)
+{
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (cb_id >= MESSAGE_CALLBACK_MAX) {
+ LOGE("invalid cb_id %u", cb_id);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CAMERA_STATE_OPENED) {
+ LOGE("invalid state %d", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_STATE;
+ }
+
+ if (cb_id >= MESSAGE_CALLBACK_MAX) {
+ LOGE("invalid id %u", cb_id);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (handle->message_cb[cb_id]) {
+ LOGD("remove message callback %p, user data %p - cb_id %u",
+ handle->message_cb[cb_id], handle->message_cb_data[cb_id], cb_id);
+
+ handle->message_cb[cb_id] = NULL;
+ handle->message_cb_data[cb_id] = NULL;
+ } else {
+ LOGE("already removed message cb");
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+}
+
+int camera_set_preview_stream_format(void *camera_handle, camera_format_t *format)
+{
+ int i = 0;
+ int j = 0;
+ int ret = CAMERA_ERROR_NONE;
+ gboolean capability_check = FALSE;
+ guint32 fourcc = 0;
+ camera_hal_handle *handle = NULL;
+ camera_device_info_t *device_info = NULL;
+ struct v4l2_format v4l2_fmt;
+ struct v4l2_streamparm v4l2_parm;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!format) {
+ LOGE("NULL format");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!g_device_info_list) {
+ LOGE("no device info list");
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CAMERA_STATE_OPENED) {
+ LOGE("invalid state %d", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_STATE;
+ }
+
+ /* check capability */
+ device_info = &g_device_info_list->device_info[handle->device_index];
+
+ /* format */
+ for (i = 0 ; i < device_info->format_list.count ; i++) {
+ if (format->stream_format == device_info->format_list.formats[i]) {
+ LOGD("format matched %d, check resolution.", format->stream_format);
+
+ /* resolution */
+ for (j = 0 ; j < device_info->preview_list.count ; j++) {
+ if (format->stream_resolution.width == device_info->preview_list.resolutions[j].width &&
+ format->stream_resolution.height == device_info->preview_list.resolutions[j].height) {
+ LOGD("resolution matched %dx%d",
+ format->stream_resolution.width,
+ format->stream_resolution.height);
+ capability_check = TRUE;
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ if (!capability_check) {
+ LOGE("capability failed - %d, %dx%d",
+ format->stream_format,
+ format->stream_resolution.width,
+ format->stream_resolution.height);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ /* S_FMT */
+ ret = _camera_get_fourcc_plane_num(format->stream_format, &fourcc, &handle->plane_num);
+ if (ret != CAMERA_ERROR_NONE) {
+ LOGE("get fourcc failed [format %d]", format->stream_format);
+ g_mutex_unlock(&handle->lock);
+ return ret;
+ }
+
+ memset(&v4l2_fmt, 0x0, sizeof(struct v4l2_format));
+
+ v4l2_fmt.type = handle->v4l2_type;
+ if (V4L2_TYPE_IS_MULTIPLANAR(handle->v4l2_type)) {
+ v4l2_fmt.fmt.pix_mp.width = format->stream_resolution.width;
+ v4l2_fmt.fmt.pix_mp.height = format->stream_resolution.height;
+ v4l2_fmt.fmt.pix_mp.pixelformat = fourcc;
+ v4l2_fmt.fmt.pix_mp.num_planes = handle->plane_num;
+ } else {
+ v4l2_fmt.fmt.pix.width = format->stream_resolution.width;
+ v4l2_fmt.fmt.pix.height = format->stream_resolution.height;
+ v4l2_fmt.fmt.pix.pixelformat = fourcc;
+ v4l2_fmt.fmt.pix.bytesperline = format->stream_resolution.width;
+ }
+
+ if (ioctl(handle->device_fd, VIDIOC_S_FMT, &v4l2_fmt) < 0) {
+ LOGE("S_FMT failed. errno %d", errno);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(handle->v4l2_type)) {
+ for (i = 0 ; i < v4l2_fmt.fmt.pix_mp.num_planes ; i++) {
+ LOGD("plane[%d] stride %d, sizeimage %d",
+ v4l2_fmt.fmt.pix_mp.plane_fmt[i].bytesperline,
+ v4l2_fmt.fmt.pix_mp.plane_fmt[i].sizeimage);
+ }
+ } else {
+ LOGD("stride %d, sizeimage %d",
+ v4l2_fmt.fmt.pix.bytesperline,
+ v4l2_fmt.fmt.pix.sizeimage);
+ }
+
+ /* G_PARM */
+ memset(&v4l2_parm, 0x0, sizeof(struct v4l2_streamparm));
+
+ v4l2_parm.type = handle->v4l2_type;
+
+ if (ioctl(handle->device_fd, VIDIOC_G_PARM, &v4l2_parm) < 0) {
+ LOGE("G_PARM failed. errno %d", errno);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ /* S_PARM to set fps */
+ v4l2_parm.parm.capture.timeperframe.numerator = 1;
+ v4l2_parm.parm.capture.timeperframe.denominator = format->stream_fps;
+
+ if (ioctl(handle->device_fd, VIDIOC_S_PARM, &v4l2_parm) < 0) {
+ LOGE("S_PARM failed. errno %d", errno);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INTERNAL;
+ }
+
+ memcpy(&handle->preview_format, format, sizeof(camera_format_t));
+
+ LOGD("set preview stream [%d: %dx%d, fps %d]",
+ format->stream_format,
+ format->stream_resolution.width,
+ format->stream_resolution.height,
+ format->stream_fps);
+
+ LOGW("CAPTURE STREAM [%d: %dx%d] IS NOT SUPPORTED",
+ format->capture_format,
+ format->capture_resolution.width,
+ format->capture_resolution.height);
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+}
+
+int camera_get_preview_stream_format(void *camera_handle, camera_format_t *format)
+{
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!format) {
+ LOGE("NULL format");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ memcpy(format, &handle->preview_format, sizeof(camera_format_t));
+
+ LOGD("get stream format %d, %dx%d", format->stream_format,
+ format->stream_resolution.width, format->stream_resolution.height);
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+}
+
+
+int camera_start_preview(void *camera_handle, camera_preview_frame_cb callback, void *user_data)
+{
+ int i = 0;
+ int ret = 0;
+ int result_count = 0;
+ camera_hal_handle *handle = NULL;
+ camera_buffer_t *buffer = NULL;
+
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!callback) {
+ LOGE("NULL callback for preview");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CAMERA_STATE_OPENED) {
+ LOGE("invalid state %d", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_STATE;
+ }
+
+ /* request buffer */
+ ret = _camera_v4l2_reqbufs(handle->device_fd,
+ handle->v4l2_type, V4L2_MEMORY_MMAP, PREVIEW_BUFFER_MAX, &result_count);
+ if (ret != CAMERA_ERROR_NONE) {
+ g_mutex_unlock(&handle->lock);
+ return ret;
+ }
+
+ LOGD("REQUESTED buffer count %d", result_count);
+
+ handle->preview_buffer_num = result_count;
+
+ /* query buffer, mmap and qbuf */
+ for (i = 0 ; i < handle->preview_buffer_num ; i++) {
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.type = handle->v4l2_type;
+ v4l2_buf.memory = V4L2_MEMORY_MMAP;
+ v4l2_buf.index = i;
+ v4l2_buf.m.planes = v4l2_planes;
+ v4l2_buf.length = handle->plane_num;
+
+ if (ioctl(handle->device_fd, VIDIOC_QUERYBUF, &v4l2_buf) < 0) {
+ LOGE("[%d] query buf failed. errno %d", i, errno);
+ goto _START_PREVIEW_FAILED;
+ }
+
+ buffer = &handle->preview_buffer[i];
+
+ buffer->index = i;
+ buffer->format = handle->preview_format.stream_format;
+ buffer->resolution = handle->preview_format.stream_resolution;
+ buffer->total_size = v4l2_buf.length;
+ buffer->num_planes = handle->plane_num;
+ buffer->planes[0].size = v4l2_buf.length;
+ buffer->planes[0].data = mmap(0,
+ v4l2_buf.length,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ handle->device_fd,
+ v4l2_buf.m.offset);
+ if (buffer->planes[0].data == MAP_FAILED) {
+ LOGE("[%d] mmap failed. errno %d", i, errno);
+ goto _START_PREVIEW_FAILED;
+ }
+ }
+
+ for (i = 0 ; i < handle->preview_buffer_num ; i++) {
+ if (_camera_v4l2_qbuf(handle->device_fd, handle->v4l2_type, V4L2_MEMORY_MMAP, i) != CAMERA_ERROR_NONE) {
+ LOGE("qbuf failed");
+ goto _START_PREVIEW_FAILED;
+ }
+ }
+
+ /* stream on */
+ ret = _camera_v4l2_stream(handle->device_fd, handle->v4l2_type, TRUE);
+ if (ret != CAMERA_ERROR_NONE) {
+ LOGE("stream on failed");
+ goto _START_PREVIEW_FAILED;
+ }
+
+ g_mutex_lock(&handle->preview_cb_lock);
+
+ handle->preview_cb_run = TRUE;
+
+ handle->preview_thread = g_thread_try_new("camera_hal_preview_thread",
+ _camera_preview_handler_func, (gpointer)handle, NULL);
+ if (!handle->preview_thread) {
+ LOGE("failed to create preview callback thread");
+ g_mutex_unlock(&handle->preview_cb_lock);
+ goto _START_PREVIEW_FAILED;
+ }
+
+ handle->preview_cb = callback;
+ handle->preview_cb_data = user_data;
+
+ g_mutex_unlock(&handle->preview_cb_lock);
+
+ handle->state = CAMERA_STATE_PREVIEWING;
+
+ LOGD("start preview done");
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+
+_START_PREVIEW_FAILED:
+ /* stream off */
+ if (ioctl(handle->device_fd, VIDIOC_STREAMOFF, &handle->v4l2_type) < 0)
+ LOGE("stream off failed. errno %d", errno);
+
+ /* munmap */
+ for (i = 0 ; i < handle->preview_buffer_num ; i++) {
+ buffer = &handle->preview_buffer[i];
+ if (buffer->planes[0].data != NULL &&
+ buffer->planes[0].data != MAP_FAILED) {
+ LOGW("munmap %p", buffer->planes[0].data);
+ munmap(buffer->planes[0].data, buffer->planes[0].size);
+ }
+ memset(buffer, 0x0, sizeof(camera_buffer_t));
+ }
+
+ /* reqbufs 0 */
+ if (_camera_v4l2_reqbufs(handle->device_fd,
+ handle->v4l2_type, V4L2_MEMORY_MMAP, 0, &result_count) != CAMERA_ERROR_NONE)
+ LOGE("reqbufs 0 failed");
+
+ g_mutex_unlock(&handle->lock);
+
+ return ret;
+}
+
+int camera_release_preview_buffer(void *camera_handle, int buffer_index)
+{
+ int ret = CAMERA_ERROR_NONE;
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->preview_cb_lock);
+
+ if (buffer_index >= handle->preview_buffer_num) {
+ LOGE("invalid buffer index %d", buffer_index);
+ g_mutex_unlock(&handle->preview_cb_lock);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ ret = _camera_v4l2_qbuf(handle->device_fd,
+ handle->v4l2_type, V4L2_MEMORY_MMAP, buffer_index);
+
+ if (ret == CAMERA_ERROR_NONE) {
+ handle->live_buffer_num--;
+ LOGD("qbud done : index %d, live buffer num %d",
+ buffer_index, handle->live_buffer_num);
+ } else {
+ LOGE("qbuf failed [index %d]", buffer_index);
+ }
+
+ g_cond_signal(&handle->preview_cb_cond);
+
+ g_mutex_unlock(&handle->preview_cb_lock);
+
+ return ret;
+}
+
+int camera_stop_preview(void *camera_handle)
+{
+ int ret = CAMERA_ERROR_NONE;
+ int i = 0;
+ int result_count = 0;
+ gint64 end_time;
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ LOGD("start");
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CAMERA_STATE_PREVIEWING) {
+ LOGE("invalid state %d", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_STATE;
+ }
+
+ g_mutex_lock(&handle->preview_cb_lock);
+
+ handle->preview_cb_run = FALSE;
+
+ while (handle->live_buffer_num > 0) {
+ LOGD("wait for live buffer [num %d]", handle->live_buffer_num);
+ end_time = g_get_monotonic_time() + 3 * G_TIME_SPAN_SECOND;
+ if (!g_cond_wait_until(&handle->preview_cb_cond, &handle->preview_cb_lock, end_time)) {
+ LOGE("buffer wait failed");
+ break;
+ } else {
+ LOGD("signal received. check again...");
+ }
+ }
+
+ g_mutex_unlock(&handle->preview_cb_lock);
+
+ /* stream off */
+ ret = _camera_v4l2_stream(handle->device_fd, handle->v4l2_type, FALSE);
+
+ LOGD("stream off : 0x%x", ret);
+
+ /* munmap */
+ for (i = 0 ; i < handle->preview_buffer_num ; i++) {
+ if (handle->preview_buffer[i].planes[0].data != NULL) {
+ LOGW("munmap %p", handle->preview_buffer[i].planes[0].data);
+
+ munmap(handle->preview_buffer[i].planes[0].data, handle->preview_buffer[i].planes[0].size);
+
+ handle->preview_buffer[i].planes[0].data = 0;
+ handle->preview_buffer[i].planes[0].size = 0;
+ } else {
+ LOGW("NULL data [index %d]", i);
+ }
+ }
+
+ /* reqbufs 0 */
+ ret = _camera_v4l2_reqbufs(handle->device_fd,
+ handle->v4l2_type, V4L2_MEMORY_MMAP, 0, &result_count);
+
+ LOGD("reqbufs 0 : 0x%x", ret);
+
+ /* wait for preview thread exit */
+ g_thread_join(handle->preview_thread);
+ handle->preview_thread = NULL;
+
+ handle->state = CAMERA_STATE_OPENED;
+
+ LOGD("stop preview done");
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+}
+
+int camera_start_auto_focus(void *camera_handle)
+{
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* auto focus is not supported */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+int camera_stop_auto_focus(void *camera_handle)
+{
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* auto focus is not supported */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+
+int camera_start_capture(void *camera_handle, camera_capture_cb callback, void *user_data)
+{
+ if (!camera_handle || !callback) {
+ LOGE("NULL handle %p or callback %p", camera_handle, callback);
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* capture function is not supported */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+int camera_stop_capture(void *camera_handle)
+{
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* capture function is not supported */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+int camera_set_video_stream_format(void *camera_handle, camera_format_t *format)
+{
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!format) {
+ LOGE("NULL format");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* single stream device can not support video stream */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+int camera_get_video_stream_format(void *camera_handle, camera_format_t *format)
+{
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!format) {
+ LOGE("NULL format");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* single stream device can not support video stream */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+int camera_start_record(void *camera_handle, camera_video_frame_cb callback, void *user_data)
+{
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!callback) {
+ LOGE("NULL callback for video");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* single stream device can not support video stream */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+int camera_release_video_buffer(void *camera_handle, int buffer_index)
+{
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* single stream device can not support video stream */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+int camera_stop_record(void *camera_handle)
+{
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("NOT SUPPORTED");
+
+ /* single stream device can not support video stream */
+ return CAMERA_ERROR_DEVICE_NOT_SUPPORTED;
+}
+
+int camera_set_command(void *camera_handle, int64_t command, void *value)
+{
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!value) {
+ LOGE("invalid pointer for value");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state < CAMERA_STATE_OPENED) {
+ LOGE("invalid state %d", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CAMERA_ERROR_INVALID_STATE;
+ }
+
+ LOGD("set command %lld - state %d", command, handle->state);
+
+ /* TODO: to be implemented */
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+}
+
+int camera_get_command(void *camera_handle, int64_t command, void **value)
+{
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!value) {
+ LOGE("invalid pointer for value");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ LOGD("get command %lld - state %d", command, handle->state);
+
+ /* TODO: to be implemented */
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+}
+
+int camera_set_batch_command(void *camera_handle, camera_batch_command_control_t *batch_command, int64_t *error_command)
+{
+ camera_hal_handle *handle = NULL;
+
+ if (!camera_handle) {
+ LOGE("NULL handle");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!batch_command) {
+ LOGE("invalid pointer for batch_command");
+ return CAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ handle = (camera_hal_handle *)camera_handle;
+
+ g_mutex_lock(&handle->lock);
+
+ LOGD("set batch command - flag 0x"PRIx64", state %d",
+ batch_command->command_set_flag, handle->state);
+
+ /* TODO: to be implemented */
+
+ g_mutex_unlock(&handle->lock);
+
+ return CAMERA_ERROR_NONE;
+}