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