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