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