1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*!
18 * \file      exynos_subdev.c
19 * \brief     source file for libv4l2
20 * \author    Jinsung Yang (jsgood.yang@samsung.com)
21 * \author    Sangwoo Park (sw5771.park@samsung.com)
22 * \date      2012/01/17
23 *
24 * <b>Revision History: </b>
25 * - 2012/01/17: Jinsung Yang (jsgood.yang@samsung.com) \n
26 *   Initial version
27 *
28 */
29
30#include <stdio.h>
31#include <stdarg.h>
32#include <fcntl.h>
33#include <sys/types.h>
34#include <sys/ioctl.h>
35#include <sys/stat.h>
36
37#include "exynos_v4l2.h"
38
39//#define LOG_NDEBUG 0
40#define LOG_TAG "libexynosv4l2-subdev"
41#include <utils/Log.h>
42
43#define SUBDEV_MINOR_MAX 191
44
45static int __subdev_open(const char *filename, int oflag, va_list ap)
46{
47    mode_t mode = 0;
48    int fd;
49
50    if (oflag & O_CREAT)
51        mode = va_arg(ap, int);
52
53    fd = open(filename, oflag, mode);
54
55    return fd;
56}
57
58int exynos_subdev_open(const char *filename, int oflag, ...)
59{
60    va_list ap;
61    int fd;
62
63    va_start(ap, oflag);
64    fd = __subdev_open(filename, oflag, ap);
65    va_end(ap);
66
67    return fd;
68}
69
70int exynos_subdev_open_devname(const char *devname, int oflag, ...)
71{
72    bool found = false;
73    int fd = -1;
74    struct stat s;
75    va_list ap;
76    FILE *stream_fd;
77    char filename[64], name[64];
78    int minor, size, i = 0;
79
80    do {
81        if (i > (SUBDEV_MINOR_MAX - 128))
82            break;
83
84        /* video device node */
85        sprintf(filename, "/dev/v4l-subdev%d", i++);
86
87        /* if the node is video device */
88        if ((lstat(filename, &s) == 0) && S_ISCHR(s.st_mode) &&
89                ((int)((unsigned short)(s.st_rdev) >> 8) == 81)) {
90            minor = (int)((unsigned short)(s.st_rdev & 0x3f));
91            ALOGD("try node: %s, minor: %d", filename, minor);
92            /* open sysfs entry */
93            sprintf(filename, "/sys/class/video4linux/v4l-subdev%d/name", minor);
94            stream_fd = fopen(filename, "r");
95            if (stream_fd == NULL) {
96                ALOGE("failed to open sysfs entry for subdev");
97                continue;   /* try next */
98            }
99
100            /* read sysfs entry for device name */
101            size = (int)fgets(name, sizeof(name), stream_fd);
102            fclose(stream_fd);
103
104            /* check read size */
105            if (size == 0) {
106                ALOGE("failed to read sysfs entry for subdev");
107            } else {
108                /* matched */
109                if (strncmp(name, devname, strlen(devname)) == 0) {
110                    ALOGI("node found for device %s: /dev/v4l-subdev%d", devname, minor);
111                    found = true;
112                }
113            }
114        }
115    } while (found == false);
116
117    if (found) {
118        sprintf(filename, "/dev/v4l-subdev%d", minor);
119        va_start(ap, oflag);
120        fd = __subdev_open(filename, oflag, ap);
121        va_end(ap);
122
123        if (fd > 0)
124            ALOGI("open subdev device %s", filename);
125        else
126            ALOGE("failed to open subdev device %s", filename);
127    } else {
128        ALOGE("no subdev device found");
129    }
130
131    return fd;
132}
133
134/**
135 * @brief enum frame size on a pad.
136 * @return 0 on success, or a negative error code on failure.
137 */
138int exynos_subdev_enum_frame_size(int fd, struct v4l2_subdev_frame_size_enum *frame_size_enum)
139{
140    int ret = -1;
141
142    if (fd < 0) {
143        ALOGE("%s: invalid fd: %d", __func__, fd);
144        return ret;
145    }
146
147    if (!frame_size_enum) {
148        ALOGE("%s: frame_size_enum is NULL", __func__);
149        return ret;
150    }
151
152    ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, frame_size_enum);
153    if (ret) {
154        ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE");
155        return ret;
156    }
157
158    return ret;
159}
160
161/**
162 * @brief Retrieve the format on a pad.
163 * @return 0 on success, or a negative error code on failure.
164 */
165int exynos_subdev_g_fmt(int fd, struct v4l2_subdev_format *fmt)
166{
167    int ret = -1;
168
169    if (fd < 0) {
170        ALOGE("%s: invalid fd: %d", __func__, fd);
171        return ret;
172    }
173
174    if (!fmt) {
175        ALOGE("%s: fmt is NULL", __func__);
176        return ret;
177    }
178
179    ret = ioctl(fd, VIDIOC_SUBDEV_G_FMT, fmt);
180    if (ret) {
181        ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_FMT");
182        return ret;
183    }
184
185    return ret;
186}
187
188/**
189 * @brief Set the format on a pad.
190 * @return 0 on success, or a negative error code on failure.
191 */
192int exynos_subdev_s_fmt(int fd, struct v4l2_subdev_format *fmt)
193{
194    int ret = -1;
195
196    if (fd < 0) {
197        ALOGE("%s: invalid fd: %d", __func__, fd);
198        return ret;
199    }
200
201    if (!fmt) {
202        ALOGE("%s: fmt is NULL", __func__);
203        return ret;
204    }
205
206    ret = ioctl(fd, VIDIOC_SUBDEV_S_FMT, fmt);
207    if (ret) {
208        ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_FMT");
209        return ret;
210    }
211
212    return ret;
213}
214
215/**
216 * @brief Retrieve the crop rectangle on a pad.
217 * @return 0 on success, or a negative error code on failure.
218 */
219int exynos_subdev_g_crop(int fd, struct v4l2_subdev_crop *crop)
220{
221    int ret = -1;
222
223    if (fd < 0) {
224        ALOGE("%s: invalid fd: %d", __func__, fd);
225        return ret;
226    }
227
228    if (!crop) {
229        ALOGE("%s: crop is NULL", __func__);
230        return ret;
231    }
232
233    ret = ioctl(fd, VIDIOC_SUBDEV_G_CROP, crop);
234    if (ret) {
235        ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_CROP");
236        return ret;
237    }
238
239    return ret;
240}
241
242/**
243 * @brief Set the crop rectangle on a pad.
244 * @return 0 on success, or a negative error code on failure.
245 */
246int exynos_subdev_s_crop(int fd, struct v4l2_subdev_crop *crop)
247{
248    int ret = -1;
249
250    if (fd < 0) {
251        ALOGE("%s: invalid fd: %d", __func__, fd);
252        return ret;
253    }
254
255    if (!crop) {
256        ALOGE("%s: crop is NULL", __func__);
257        return ret;
258    }
259
260    ret = ioctl(fd, VIDIOC_SUBDEV_S_CROP, crop);
261    if (ret) {
262        ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_CROP");
263        return ret;
264    }
265
266    return ret;
267}
268
269/**
270 * @brief Retrieve the frame interval on a sub-device.
271 * @return 0 on success, or a negative error code on failure.
272 */
273int exynos_subdev_enum_frame_interval(int fd, struct v4l2_subdev_frame_interval_enum *frame_internval_enum)
274{
275    int ret = -1;
276
277    if (fd < 0) {
278        ALOGE("%s: invalid fd: %d", __func__, fd);
279        return ret;
280    }
281
282    if (!frame_internval_enum) {
283        ALOGE("%s: frame_internval_enum is NULL", __func__);
284        return ret;
285    }
286
287    ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, frame_internval_enum);
288    if (ret) {
289        ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL");
290        return ret;
291    }
292
293    return ret;
294}
295
296/**
297 * @brief Retrieve the frame interval on a sub-device.
298 * @return 0 on success, or a negative error code on failure.
299 */
300int exynos_subdev_g_frame_interval(int fd, struct v4l2_subdev_frame_interval *frame_internval)
301{
302    int ret = -1;
303
304    if (fd < 0) {
305        ALOGE("%s: invalid fd: %d", __func__, fd);
306        return ret;
307    }
308
309    if (!frame_internval) {
310        ALOGE("%s: frame_internval is NULL", __func__);
311        return ret;
312    }
313
314    ret = ioctl(fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, frame_internval);
315    if (ret) {
316        ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL");
317        return ret;
318    }
319
320    return ret;
321}
322
323/**
324 * @brief Set the frame interval on a sub-device.
325 * @return 0 on success, or a negative error code on failure.
326 */
327int exynos_subdev_s_frame_interval(int fd, struct v4l2_subdev_frame_interval *frame_internval)
328{
329    int ret = -1;
330
331    if (fd < 0) {
332        ALOGE("%s: invalid fd: %d", __func__, fd);
333        return ret;
334    }
335
336    if (!frame_internval) {
337        ALOGE("%s: frame_internval is NULL", __func__);
338        return ret;
339    }
340
341    ret = ioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, frame_internval);
342    if (ret) {
343        ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL");
344        return ret;
345    }
346
347    return ret;
348}
349
350/**
351 * @brief enum mbus code
352 * @return 0 on success, or a negative error code on failure.
353 */
354int exynos_subdev_enum_mbus_code(int fd, struct v4l2_subdev_mbus_code_enum *mbus_code_enum)
355{
356    int ret = -1;
357
358    if (fd < 0) {
359        ALOGE("%s: invalid fd: %d", __func__, fd);
360        return ret;
361    }
362
363    if (!mbus_code_enum) {
364        ALOGE("%s: mbus_code_enum is NULL", __func__);
365        return ret;
366    }
367
368    ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_MBUS_CODE, mbus_code_enum);
369    if (ret) {
370        ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE");
371        return ret;
372    }
373
374    return ret;
375}
376