Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | |
| 70 | int 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)); |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 91 | ALOGD("try node: %s, minor: %d", filename, minor); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 92 | /* open sysfs entry */ |
| 93 | sprintf(filename, "/sys/class/video4linux/v4l-subdev%d/name", minor); |
| 94 | stream_fd = fopen(filename, "r"); |
Jiho Chang | d970bdc | 2012-04-26 15:55:33 -0700 | [diff] [blame] | 95 | if (stream_fd == NULL) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 96 | ALOGE("failed to open sysfs entry for subdev"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 106 | ALOGE("failed to read sysfs entry for subdev"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 107 | } else { |
| 108 | /* matched */ |
| 109 | if (strncmp(name, devname, strlen(devname)) == 0) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 110 | ALOGI("node found for device %s: /dev/v4l-subdev%d", devname, minor); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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) |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 124 | ALOGI("open subdev device %s", filename); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 125 | else |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 126 | ALOGE("failed to open subdev device %s", filename); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 127 | } else { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 128 | ALOGE("no subdev device found"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 138 | int 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) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 143 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 144 | return ret; |
| 145 | } |
| 146 | |
| 147 | if (!frame_size_enum) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 148 | ALOGE("%s: frame_size_enum is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 149 | return ret; |
| 150 | } |
| 151 | |
| 152 | ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, frame_size_enum); |
| 153 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 154 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 165 | int exynos_subdev_g_fmt(int fd, struct v4l2_subdev_format *fmt) |
| 166 | { |
| 167 | int ret = -1; |
| 168 | |
| 169 | if (fd < 0) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 170 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 171 | return ret; |
| 172 | } |
| 173 | |
| 174 | if (!fmt) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 175 | ALOGE("%s: fmt is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 176 | return ret; |
| 177 | } |
| 178 | |
| 179 | ret = ioctl(fd, VIDIOC_SUBDEV_G_FMT, fmt); |
| 180 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 181 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_FMT"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 192 | int exynos_subdev_s_fmt(int fd, struct v4l2_subdev_format *fmt) |
| 193 | { |
| 194 | int ret = -1; |
| 195 | |
| 196 | if (fd < 0) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 197 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 198 | return ret; |
| 199 | } |
| 200 | |
| 201 | if (!fmt) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 202 | ALOGE("%s: fmt is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 203 | return ret; |
| 204 | } |
| 205 | |
| 206 | ret = ioctl(fd, VIDIOC_SUBDEV_S_FMT, fmt); |
| 207 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 208 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_FMT"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 219 | int exynos_subdev_g_crop(int fd, struct v4l2_subdev_crop *crop) |
| 220 | { |
| 221 | int ret = -1; |
| 222 | |
| 223 | if (fd < 0) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 224 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 225 | return ret; |
| 226 | } |
| 227 | |
| 228 | if (!crop) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 229 | ALOGE("%s: crop is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 230 | return ret; |
| 231 | } |
| 232 | |
| 233 | ret = ioctl(fd, VIDIOC_SUBDEV_G_CROP, crop); |
| 234 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 235 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_CROP"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 246 | int exynos_subdev_s_crop(int fd, struct v4l2_subdev_crop *crop) |
| 247 | { |
| 248 | int ret = -1; |
| 249 | |
| 250 | if (fd < 0) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 251 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 252 | return ret; |
| 253 | } |
| 254 | |
| 255 | if (!crop) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 256 | ALOGE("%s: crop is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 257 | return ret; |
| 258 | } |
| 259 | |
| 260 | ret = ioctl(fd, VIDIOC_SUBDEV_S_CROP, crop); |
| 261 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 262 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_CROP"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 273 | int 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) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 278 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 279 | return ret; |
| 280 | } |
| 281 | |
| 282 | if (!frame_internval_enum) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 283 | ALOGE("%s: frame_internval_enum is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 284 | return ret; |
| 285 | } |
| 286 | |
| 287 | ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, frame_internval_enum); |
| 288 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 289 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 300 | int exynos_subdev_g_frame_interval(int fd, struct v4l2_subdev_frame_interval *frame_internval) |
| 301 | { |
| 302 | int ret = -1; |
| 303 | |
| 304 | if (fd < 0) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 305 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 306 | return ret; |
| 307 | } |
| 308 | |
| 309 | if (!frame_internval) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 310 | ALOGE("%s: frame_internval is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 311 | return ret; |
| 312 | } |
| 313 | |
| 314 | ret = ioctl(fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, frame_internval); |
| 315 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 316 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 327 | int exynos_subdev_s_frame_interval(int fd, struct v4l2_subdev_frame_interval *frame_internval) |
| 328 | { |
| 329 | int ret = -1; |
| 330 | |
| 331 | if (fd < 0) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 332 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 333 | return ret; |
| 334 | } |
| 335 | |
| 336 | if (!frame_internval) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 337 | ALOGE("%s: frame_internval is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 338 | return ret; |
| 339 | } |
| 340 | |
| 341 | ret = ioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, frame_internval); |
| 342 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 343 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 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 | */ |
| 354 | int 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) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 359 | ALOGE("%s: invalid fd: %d", __func__, fd); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 360 | return ret; |
| 361 | } |
| 362 | |
| 363 | if (!mbus_code_enum) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 364 | ALOGE("%s: mbus_code_enum is NULL", __func__); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 365 | return ret; |
| 366 | } |
| 367 | |
| 368 | ret = ioctl(fd, VIDIOC_SUBDEV_ENUM_MBUS_CODE, mbus_code_enum); |
| 369 | if (ret) { |
Dima Zavin | 7642c64 | 2012-04-02 23:45:46 -0700 | [diff] [blame] | 370 | ALOGE("failed to ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE"); |
Jiho Chang | e5931c3 | 2012-03-24 05:59:38 +0900 | [diff] [blame] | 371 | return ret; |
| 372 | } |
| 373 | |
| 374 | return ret; |
| 375 | } |