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 <stdbool.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38
39 #include <linux/v4l2-subdev.h>
40
41 //#define LOG_NDEBUG 0
42 #define LOG_TAG "gscaler-subdev"
43 #include <utils/Log.h>
44 #include <string.h>
45
46 #include "libgscaler_media.h"
47
48 #define SUBDEV_MAX 191
49
__subdev_open(const char * filename,int oflag,va_list ap)50 static int __subdev_open(const char *filename, int oflag, va_list ap)
51 {
52 mode_t mode = 0;
53 int fd;
54
55 if (oflag & O_CREAT)
56 mode = va_arg(ap, int);
57
58 fd = open(filename, oflag, mode);
59
60 return fd;
61 }
62
exynos_subdev_open(const char * filename,int oflag,...)63 int exynos_subdev_open(const char *filename, int oflag, ...)
64 {
65 va_list ap;
66 int fd;
67
68 va_start(ap, oflag);
69 fd = __subdev_open(filename, oflag, ap);
70 va_end(ap);
71
72 return fd;
73 }
74
exynos_subdev_get_node_num(const char * devname,int __UNUSED__ oflag,...)75 int exynos_subdev_get_node_num(const char *devname, int __UNUSED__ oflag, ...)
76 {
77 bool found = false;
78 int ret = -1;
79 struct stat s;
80 FILE *stream_fd;
81 char filename[64], name[64];
82 int i = 0;
83
84 do {
85 if (i > (SUBDEV_MAX - 128))
86 break;
87
88 /* video device node */
89 snprintf(filename, sizeof(filename), "/dev/v4l-subdev%d", i);
90
91 /* if the node is video device */
92 if ((lstat(filename, &s) == 0) && S_ISCHR(s.st_mode) &&
93 ((int)((unsigned short)(s.st_rdev) >> 8) == 81)) {
94 ALOGD("try node: %s", filename);
95 /* open sysfs entry */
96 snprintf(filename, sizeof(filename), "/sys/class/video4linux/v4l-subdev%d/name", i);
97 if (S_ISLNK(s.st_mode)) {
98 ALOGE("symbolic link detected");
99 return -1;
100 }
101 stream_fd = fopen(filename, "r");
102 if (stream_fd == NULL) {
103 ALOGE("failed to open sysfs entry for subdev");
104 continue; /* try next */
105 }
106
107 /* read sysfs entry for device name */
108 char *p = fgets(name, sizeof(name), stream_fd);
109 fclose(stream_fd);
110
111 /* check read size */
112 if (p == NULL) {
113 ALOGE("failed to read sysfs entry for subdev");
114 } else {
115 /* matched */
116 if (strncmp(name, devname, strlen(devname)) == 0) {
117 ALOGI("node found for device %s: /dev/v4l-subdev%d", devname, i);
118 found = true;
119 break;
120 }
121 }
122 }
123 i++;
124 } while (found == false);
125
126 if (found)
127 ret = i;
128 else
129 ALOGE("no subdev device found");
130
131 return ret;
132 }
133
exynos_subdev_open_devname(const char * devname,int oflag,...)134 int exynos_subdev_open_devname(const char *devname, int oflag, ...)
135 {
136 bool found = false;
137 int fd = -1;
138 struct stat s;
139 va_list ap;
140 FILE *stream_fd;
141 char filename[64], name[64];
142 int i = 0;
143
144 do {
145 if (i > (SUBDEV_MAX - 128))
146 break;
147
148 /* video device node */
149 snprintf(filename, sizeof(filename), "/dev/v4l-subdev%d", i);
150
151 /* if the node is video device */
152 if ((lstat(filename, &s) == 0) && S_ISCHR(s.st_mode) &&
153 ((int)((unsigned short)(s.st_rdev) >> 8) == 81)) {
154 ALOGD("try node: %s", filename);
155 /* open sysfs entry */
156 snprintf(filename, sizeof(filename), "/sys/class/video4linux/v4l-subdev%d/name", i);
157 if (S_ISLNK(s.st_mode)) {
158 ALOGE("symbolic link detected");
159 return -1;
160 }
161 stream_fd = fopen(filename, "r");
162 if (stream_fd == NULL) {
163 ALOGE("failed to open sysfs entry for subdev");
164 continue; /* try next */
165 }
166
167 /* read sysfs entry for device name */
168 char *p = fgets(name, sizeof(name), stream_fd);
169 fclose(stream_fd);
170
171 /* check read size */
172 if (p == NULL) {
173 ALOGE("failed to read sysfs entry for subdev");
174 } else {
175 /* matched */
176 if (strncmp(name, devname, strlen(devname)) == 0) {
177 ALOGI("node found for device %s: /dev/v4l-subdev%d", devname, i);
178 found = true;
179 break;
180 }
181 }
182 }
183 i++;
184 } while (found == false);
185
186 if (found) {
187 snprintf(filename, sizeof(filename), "/dev/v4l-subdev%d", i);
188 va_start(ap, oflag);
189 fd = __subdev_open(filename, oflag, ap);
190 va_end(ap);
191
192 if (fd > 0)
193 ALOGI("open subdev device %s", filename);
194 else
195 ALOGE("failed to open subdev device %s", filename);
196 } else {
197 ALOGE("no subdev device found");
198 }
199
200 return fd;
201 }
202
exynos_subdev_close(int fd)203 int exynos_subdev_close(int fd)
204 {
205 int ret = -1;
206
207 if (fd < 0)
208 ALOGE("%s: invalid fd: %d", __func__, fd);
209 else
210 ret = close(fd);
211
212 return ret;
213 }
214
215 /**
216 * @brief enum frame size on a pad.
217 * @return 0 on success, or a negative error code on failure.
218 */
exynos_subdev_enum_frame_size(int fd,struct v4l2_subdev_frame_size_enum * frame_size_enum)219 int exynos_subdev_enum_frame_size(int fd, struct v4l2_subdev_frame_size_enum *frame_size_enum)
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 (!frame_size_enum) {
229 ALOGE("%s: frame_size_enum is NULL", __func__);
230 return ret;
231 }
232
233 ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, frame_size_enum);
234 if (ret) {
235 ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE");
236 return ret;
237 }
238
239 return ret;
240 }
241
242 /**
243 * @brief Retrieve the format on a pad.
244 * @return 0 on success, or a negative error code on failure.
245 */
exynos_subdev_g_fmt(int fd,struct v4l2_subdev_format * fmt)246 int exynos_subdev_g_fmt(int fd, struct v4l2_subdev_format *fmt)
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 (!fmt) {
256 ALOGE("%s: fmt is NULL", __func__);
257 return ret;
258 }
259
260 ret = ioctl(fd, VIDIOC_SUBDEV_G_FMT, fmt);
261 if (ret) {
262 ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_FMT");
263 return ret;
264 }
265
266 return ret;
267 }
268
269 /**
270 * @brief Set the format on a pad.
271 * @return 0 on success, or a negative error code on failure.
272 */
exynos_subdev_s_fmt(int fd,struct v4l2_subdev_format * fmt)273 int exynos_subdev_s_fmt(int fd, struct v4l2_subdev_format *fmt)
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 (!fmt) {
283 ALOGE("%s: fmt is NULL", __func__);
284 return ret;
285 }
286
287 ret = ioctl(fd, VIDIOC_SUBDEV_S_FMT, fmt);
288 if (ret) {
289 ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_FMT");
290 return ret;
291 }
292
293 return ret;
294 }
295
296 /**
297 * @brief Retrieve the crop rectangle on a pad.
298 * @return 0 on success, or a negative error code on failure.
299 */
exynos_subdev_g_crop(int fd,struct v4l2_subdev_crop * crop)300 int exynos_subdev_g_crop(int fd, struct v4l2_subdev_crop *crop)
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 (!crop) {
310 ALOGE("%s: crop is NULL", __func__);
311 return ret;
312 }
313
314 ret = ioctl(fd, VIDIOC_SUBDEV_G_CROP, crop);
315 if (ret) {
316 ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_CROP");
317 return ret;
318 }
319
320 return ret;
321 }
322
323 /**
324 * @brief Set the crop rectangle on a pad.
325 * @return 0 on success, or a negative error code on failure.
326 */
exynos_subdev_s_crop(int fd,struct v4l2_subdev_crop * crop)327 int exynos_subdev_s_crop(int fd, struct v4l2_subdev_crop *crop)
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 (!crop) {
337 ALOGE("%s: crop is NULL", __func__);
338 return ret;
339 }
340
341 ret = ioctl(fd, VIDIOC_SUBDEV_S_CROP, crop);
342 if (ret) {
343 ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_CROP");
344 return ret;
345 }
346
347 return ret;
348 }
349
350 /**
351 * @brief Retrieve the frame interval on a sub-device.
352 * @return 0 on success, or a negative error code on failure.
353 */
exynos_subdev_enum_frame_interval(int fd,struct v4l2_subdev_frame_interval_enum * frame_internval_enum)354 int exynos_subdev_enum_frame_interval(int fd, struct v4l2_subdev_frame_interval_enum *frame_internval_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 (!frame_internval_enum) {
364 ALOGE("%s: frame_internval_enum is NULL", __func__);
365 return ret;
366 }
367
368 ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, frame_internval_enum);
369 if (ret) {
370 ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL");
371 return ret;
372 }
373
374 return ret;
375 }
376
377 /**
378 * @brief Retrieve the frame interval on a sub-device.
379 * @return 0 on success, or a negative error code on failure.
380 */
exynos_subdev_g_frame_interval(int fd,struct v4l2_subdev_frame_interval * frame_internval)381 int exynos_subdev_g_frame_interval(int fd, struct v4l2_subdev_frame_interval *frame_internval)
382 {
383 int ret = -1;
384
385 if (fd < 0) {
386 ALOGE("%s: invalid fd: %d", __func__, fd);
387 return ret;
388 }
389
390 if (!frame_internval) {
391 ALOGE("%s: frame_internval is NULL", __func__);
392 return ret;
393 }
394
395 ret = ioctl(fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, frame_internval);
396 if (ret) {
397 ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL");
398 return ret;
399 }
400
401 return ret;
402 }
403
404 /**
405 * @brief Set the frame interval on a sub-device.
406 * @return 0 on success, or a negative error code on failure.
407 */
exynos_subdev_s_frame_interval(int fd,struct v4l2_subdev_frame_interval * frame_internval)408 int exynos_subdev_s_frame_interval(int fd, struct v4l2_subdev_frame_interval *frame_internval)
409 {
410 int ret = -1;
411
412 if (fd < 0) {
413 ALOGE("%s: invalid fd: %d", __func__, fd);
414 return ret;
415 }
416
417 if (!frame_internval) {
418 ALOGE("%s: frame_internval is NULL", __func__);
419 return ret;
420 }
421
422 ret = ioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, frame_internval);
423 if (ret) {
424 ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL");
425 return ret;
426 }
427
428 return ret;
429 }
430
431 /**
432 * @brief enum mbus code
433 * @return 0 on success, or a negative error code on failure.
434 */
exynos_subdev_enum_mbus_code(int fd,struct v4l2_subdev_mbus_code_enum * mbus_code_enum)435 int exynos_subdev_enum_mbus_code(int fd, struct v4l2_subdev_mbus_code_enum *mbus_code_enum)
436 {
437 int ret = -1;
438
439 if (fd < 0) {
440 ALOGE("%s: invalid fd: %d", __func__, fd);
441 return ret;
442 }
443
444 if (!mbus_code_enum) {
445 ALOGE("%s: mbus_code_enum is NULL", __func__);
446 return ret;
447 }
448
449 ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_MBUS_CODE, mbus_code_enum);
450 if (ret) {
451 ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE");
452 return ret;
453 }
454
455 return ret;
456 }
457