1/* $NetBSD: videoctl.c,v 1.1 2010/12/26 10:37:15 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__COPYRIGHT("@(#) Copyright (c) 2010\ 31 Jared D. McNeill <jmcneill@invisible.ca>. All rights reserved."); 32__RCSID("$NetBSD: videoctl.c,v 1.1 2010/12/26 10:37:15 jmcneill Exp $"); 33 34#include <sys/types.h> 35#include <sys/ioctl.h> 36#include <sys/videoio.h> 37 38#include <err.h> 39#include <errno.h> 40#include <fcntl.h> 41#include <limits.h> 42#include <paths.h> 43#include <stdio.h> 44#include <string.h> 45#include <stdbool.h> 46#include <stdlib.h> 47#include <unistd.h> 48#include <util.h> 49 50__dead static void usage(void); 51static void video_print(const char *); 52static void video_print_all(void); 53static bool video_print_caps(const char *); 54static bool video_print_formats(const char *); 55static bool video_print_inputs(const char *); 56static bool video_print_audios(const char *); 57static bool video_print_standards(const char *); 58static bool video_print_tuners(const char *); 59static bool video_print_ctrl(uint32_t); 60static void video_set(const char *); 61static bool video_set_ctrl(uint32_t, int32_t); 62static const char * video_cid2name(uint32_t); 63static uint32_t video_name2cid(const char *); 64 65static const char *video_dev = NULL; 66static int video_fd = -1; 67static bool aflag = false; 68static bool wflag = false; 69 70static const struct { 71 uint32_t id; 72 const char *name; 73} videoctl_cid_names[] = { 74 { V4L2_CID_BRIGHTNESS, "brightness" }, 75 { V4L2_CID_CONTRAST, "contrast" }, 76 { V4L2_CID_SATURATION, "saturation" }, 77 { V4L2_CID_HUE, "hue" }, 78 { V4L2_CID_AUDIO_VOLUME, "audio_volume" }, 79 { V4L2_CID_AUDIO_BALANCE, "audio_balance" }, 80 { V4L2_CID_AUDIO_BASS, "audio_bass" }, 81 { V4L2_CID_AUDIO_TREBLE, "audio_treble" }, 82 { V4L2_CID_AUDIO_MUTE, "audio_mute" }, 83 { V4L2_CID_AUDIO_LOUDNESS, "audio_loudness" }, 84 { V4L2_CID_BLACK_LEVEL, "black_level" }, 85 { V4L2_CID_AUTO_WHITE_BALANCE, "auto_white_balance" }, 86 { V4L2_CID_DO_WHITE_BALANCE, "do_white_balance" }, 87 { V4L2_CID_RED_BALANCE, "red_balance" }, 88 { V4L2_CID_BLUE_BALANCE, "blue_balance" }, 89 { V4L2_CID_GAMMA, "gamma" }, 90 { V4L2_CID_WHITENESS, "whiteness" }, 91 { V4L2_CID_EXPOSURE, "exposure" }, 92 { V4L2_CID_AUTOGAIN, "autogain" }, 93 { V4L2_CID_GAIN, "gain" }, 94 { V4L2_CID_HFLIP, "hflip" }, 95 { V4L2_CID_VFLIP, "vflip" }, 96 { V4L2_CID_HCENTER, "hcenter" }, 97 { V4L2_CID_VCENTER, "vcenter" }, 98 { V4L2_CID_POWER_LINE_FREQUENCY, "power_line_frequency" }, 99 { V4L2_CID_HUE_AUTO, "hue_auto" }, 100 { V4L2_CID_WHITE_BALANCE_TEMPERATURE, "white_balance_temperature" }, 101 { V4L2_CID_SHARPNESS, "sharpness" }, 102 { V4L2_CID_BACKLIGHT_COMPENSATION, "backlight_compensation" }, 103}; 104 105int 106main(int argc, char *argv[]) 107{ 108 int ch; 109 110 setprogname(argv[0]); 111 112 while ((ch = getopt(argc, argv, "ad:w")) != -1) { 113 switch (ch) { 114 case 'a': 115 aflag = true; 116 break; 117 case 'd': 118 video_dev = strdup(optarg); 119 break; 120 case 'w': 121 wflag = true; 122 break; 123 case 'h': 124 default: 125 usage(); 126 /* NOTREACHED */ 127 } 128 } 129 argc -= optind; 130 argv += optind; 131 132 if (wflag && aflag) 133 usage(); 134 /* NOTREACHED */ 135 if (wflag && argc == 0) 136 usage(); 137 /* NOTREACHED */ 138 if (aflag && argc > 0) 139 usage(); 140 /* NOTREACHED */ 141 if (!wflag && !aflag && argc == 0) 142 usage(); 143 /* NOTREACHED */ 144 145 if (video_dev == NULL) 146 video_dev = _PATH_VIDEO0; 147 148 video_fd = open(video_dev, wflag ? O_RDWR : O_RDONLY); 149 if (video_fd == -1) 150 err(EXIT_FAILURE, "couldn't open '%s'", video_dev); 151 152 if (aflag) { 153 video_print_all(); 154 } else if (wflag) { 155 while (argc > 0) { 156 video_set(argv[0]); 157 --argc; 158 ++argv; 159 } 160 } else { 161 while (argc > 0) { 162 video_print(argv[0]); 163 --argc; 164 ++argv; 165 } 166 } 167 168 close(video_fd); 169 170 return EXIT_SUCCESS; 171} 172 173static void 174usage(void) 175{ 176 fprintf(stderr, "usage: %s [-d file] name ...\n", getprogname()); 177 fprintf(stderr, "usage: %s [-d file] -w name=value ...\n", 178 getprogname()); 179 fprintf(stderr, "usage: %s [-d file] -a\n", getprogname()); 180 exit(EXIT_FAILURE); 181} 182 183static void 184video_print_all(void) 185{ 186 video_print_caps(NULL); 187 video_print_formats(NULL); 188 video_print_inputs(NULL); 189 video_print_audios(NULL); 190 video_print_standards(NULL); 191 video_print_tuners(NULL); 192 video_print_ctrl(0); 193} 194 195static bool 196video_print_caps(const char *name) 197{ 198 struct v4l2_capability cap; 199 char capbuf[128]; 200 int error; 201 bool found = false; 202 203 if (strtok(NULL, ".") != NULL) 204 return false; 205 206 /* query capabilities */ 207 error = ioctl(video_fd, VIDIOC_QUERYCAP, &cap); 208 if (error == -1) 209 err(EXIT_FAILURE, "VIDIOC_QUERYCAP failed"); 210 211 if (!name || strcmp(name, "card") == 0) { 212 printf("info.cap.card=%s\n", cap.card); 213 found = true; 214 } 215 if (!name || strcmp(name, "driver") == 0) { 216 printf("info.cap.driver=%s\n", cap.driver); 217 found = true; 218 } 219 if (!name || strcmp(name, "bus_info") == 0) { 220 printf("info.cap.bus_info=%s\n", cap.bus_info); 221 found = true; 222 } 223 if (!name || strcmp(name, "version") == 0) { 224 printf("info.cap.version=%u.%u.%u\n", 225 (cap.version >> 16) & 0xff, 226 (cap.version >> 8) & 0xff, 227 cap.version & 0xff); 228 found = true; 229 } 230 if (!name || strcmp(name, "capabilities") == 0) { 231 snprintb(capbuf, sizeof(capbuf), V4L2_CAP_BITMASK, 232 cap.capabilities); 233 printf("info.cap.capabilities=%s\n", capbuf); 234 found = true; 235 } 236 237 return found; 238} 239 240static bool 241video_print_formats(const char *name) 242{ 243 struct v4l2_fmtdesc fmtdesc; 244 int error; 245 246 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 247 if (name == NULL) { 248 /* enumerate formats */ 249 for (fmtdesc.index = 0; ; fmtdesc.index++) { 250 error = ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc); 251 if (error) 252 break; 253 printf("info.format.%u=%s\n", fmtdesc.index, 254 fmtdesc.description); 255 } 256 } else { 257 unsigned long n; 258 259 if (strtok(NULL, ".") != NULL) 260 return false; 261 262 n = strtoul(name, NULL, 10); 263 if (n == ULONG_MAX) 264 return false; 265 fmtdesc.index = n; 266 error = ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc); 267 if (error) 268 return false; 269 printf("info.format.%u=%s\n", fmtdesc.index, 270 fmtdesc.description); 271 } 272 273 return true; 274} 275 276static bool 277video_print_inputs(const char *name) 278{ 279 struct v4l2_input input; 280 int error; 281 282 if (name == NULL) { 283 /* enumerate inputs */ 284 for (input.index = 0; ; input.index++) { 285 error = ioctl(video_fd, VIDIOC_ENUMINPUT, &input); 286 if (error) 287 break; 288 printf("info.input.%u=%s\n", input.index, input.name); 289 printf("info.input.%u.type=", input.index); 290 switch (input.type) { 291 case V4L2_INPUT_TYPE_TUNER: 292 printf("tuner\n"); 293 break; 294 case V4L2_INPUT_TYPE_CAMERA: 295 printf("baseband\n"); 296 break; 297 default: 298 printf("unknown (%d)\n", input.type); 299 break; 300 } 301 } 302 } else { 303 unsigned long n; 304 char *s; 305 306 n = strtoul(name, NULL, 10); 307 if (n == ULONG_MAX) 308 return false; 309 input.index = n; 310 error = ioctl(video_fd, VIDIOC_ENUMINPUT, &input); 311 if (error) 312 return false; 313 314 s = strtok(NULL, "."); 315 if (s == NULL) { 316 printf("info.input.%u=%s\n", input.index, input.name); 317 } else if (strcmp(s, "type") == 0) { 318 if (strtok(NULL, ".") != NULL) 319 return false; 320 printf("info.input.%u.type=", input.index); 321 switch (input.type) { 322 case V4L2_INPUT_TYPE_TUNER: 323 printf("tuner\n"); 324 break; 325 case V4L2_INPUT_TYPE_CAMERA: 326 printf("baseband\n"); 327 break; 328 default: 329 printf("unknown (%d)\n", input.type); 330 break; 331 } 332 } else 333 return false; 334 } 335 336 return true; 337} 338 339static bool 340video_print_audios(const char *name) 341{ 342 struct v4l2_audio audio; 343 int error; 344 345 if (name == NULL) { 346 /* enumerate audio */ 347 for (audio.index = 0; ; audio.index++) { 348 error = ioctl(video_fd, VIDIOC_ENUMAUDIO, &audio); 349 if (error) 350 break; 351 printf("info.audio.%u=%s\n", audio.index, audio.name); 352 printf("info.audio.%u.stereo=%d\n", audio.index, 353 audio.capability & V4L2_AUDCAP_STEREO ? 1 : 0); 354 printf("info.audio.%u.avl=%d\n", audio.index, 355 audio.capability & V4L2_AUDCAP_AVL ? 1 : 0); 356 } 357 } else { 358 unsigned long n; 359 char *s; 360 361 n = strtoul(name, NULL, 10); 362 if (n == ULONG_MAX) 363 return false; 364 audio.index = n; 365 error = ioctl(video_fd, VIDIOC_ENUMAUDIO, &audio); 366 if (error) 367 return false; 368 369 s = strtok(NULL, "."); 370 if (s == NULL) { 371 printf("info.audio.%u=%s\n", audio.index, audio.name); 372 } else if (strcmp(s, "stereo") == 0) { 373 if (strtok(NULL, ".") != NULL) 374 return false; 375 printf("info.audio.%u.stereo=%d\n", audio.index, 376 audio.capability & V4L2_AUDCAP_STEREO ? 1 : 0); 377 } else if (strcmp(s, "avl") == 0) { 378 if (strtok(NULL, ".") != NULL) 379 return false; 380 printf("info.audio.%u.avl=%d\n", audio.index, 381 audio.capability & V4L2_AUDCAP_AVL ? 1 : 0); 382 } else 383 return false; 384 } 385 386 return true; 387} 388 389static bool 390video_print_standards(const char *name) 391{ 392 struct v4l2_standard std; 393 int error; 394 395 if (name == NULL) { 396 /* enumerate standards */ 397 for (std.index = 0; ; std.index++) { 398 error = ioctl(video_fd, VIDIOC_ENUMSTD, &std); 399 if (error) 400 break; 401 printf("info.standard.%u=%s\n", std.index, std.name); 402 } 403 } else { 404 unsigned long n; 405 406 if (strtok(NULL, ".") != NULL) 407 return false; 408 409 n = strtoul(name, NULL, 10); 410 if (n == ULONG_MAX) 411 return false; 412 std.index = n; 413 error = ioctl(video_fd, VIDIOC_ENUMSTD, &std); 414 if (error) 415 return false; 416 printf("info.standard.%u=%s\n", std.index, std.name); 417 } 418 419 return true; 420} 421 422static bool 423video_print_tuners(const char *name) 424{ 425 struct v4l2_tuner tuner; 426 int error; 427 428 if (name == NULL) { 429 /* enumerate tuners */ 430 for (tuner.index = 0; ; tuner.index++) { 431 error = ioctl(video_fd, VIDIOC_G_TUNER, &tuner); 432 if (error) 433 break; 434 printf("info.tuner.%u=%s\n", tuner.index, tuner.name); 435 } 436 } else { 437 unsigned long n; 438 439 if (strtok(NULL, ".") != NULL) 440 return false; 441 442 n = strtoul(name, NULL, 10); 443 if (n == ULONG_MAX) 444 return false; 445 tuner.index = n; 446 error = ioctl(video_fd, VIDIOC_G_TUNER, &tuner); 447 if (error) 448 return false; 449 printf("info.tuner.%u=%s\n", tuner.index, tuner.name); 450 } 451 452 return true; 453} 454 455static void 456video_print(const char *name) 457{ 458 char *buf, *s, *s2 = NULL; 459 bool found = false; 460 461 buf = strdup(name); 462 s = strtok(buf, "."); 463 if (s == NULL) 464 return; 465 466 if (strcmp(s, "info") == 0) { 467 s = strtok(NULL, "."); 468 if (s) 469 s2 = strtok(NULL, "."); 470 if (s == NULL || strcmp(s, "cap") == 0) { 471 found = video_print_caps(s2); 472 } 473 if (s == NULL || strcmp(s, "format") == 0) { 474 found = video_print_formats(s2); 475 } 476 if (s == NULL || strcmp(s, "input") == 0) { 477 found = video_print_inputs(s2); 478 } 479 if (s == NULL || strcmp(s, "audio") == 0) { 480 found = video_print_audios(s2); 481 } 482 if (s == NULL || strcmp(s, "standard") == 0) { 483 found = video_print_standards(s2); 484 } 485 if (s == NULL || strcmp(s, "tuner") == 0) { 486 found = video_print_tuners(s2); 487 } 488 } else if (strcmp(s, "ctrl") == 0) { 489 s = strtok(NULL, "."); 490 if (s) 491 s2 = strtok(NULL, "."); 492 493 if (s == NULL) 494 found = video_print_ctrl(0); 495 else if (s && !s2) 496 found = video_print_ctrl(video_name2cid(s)); 497 } 498 499 free(buf); 500 if (!found) 501 fprintf(stderr, "%s: field %s does not exist\n", 502 getprogname(), name); 503} 504 505static bool 506video_print_ctrl(uint32_t ctrl_id) 507{ 508 struct v4l2_control ctrl; 509 const char *ctrlname; 510 bool found = false; 511 int error; 512 513 for (ctrl.id = V4L2_CID_BASE; ctrl.id != V4L2_CID_LASTP1; ctrl.id++) { 514 if (ctrl_id != 0 && ctrl_id != ctrl.id) 515 continue; 516 error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl); 517 if (error) 518 continue; 519 ctrlname = video_cid2name(ctrl.id); 520 if (ctrlname) 521 printf("ctrl.%s=%d\n", ctrlname, ctrl.value); 522 else 523 printf("ctrl.%08x=%d\n", ctrl.id, ctrl.value); 524 found = true; 525 } 526 527 return found; 528} 529 530static void 531video_set(const char *name) 532{ 533 char *buf, *key, *value; 534 bool found = false; 535 long n; 536 537 if (strchr(name, '=') == NULL) { 538 fprintf(stderr, "%s: No '=' in %s\n", getprogname(), name); 539 exit(EXIT_FAILURE); 540 } 541 542 buf = strdup(name); 543 key = strtok(buf, "="); 544 if (key == NULL) 545 usage(); 546 /* NOTREACHED */ 547 value = strtok(NULL, ""); 548 if (value == NULL) 549 usage(); 550 /* NOTREACHED */ 551 552 if (strncmp(key, "info.", strlen("info.")) == 0) { 553 fprintf(stderr, "'info' subtree read-only\n"); 554 found = true; 555 goto done; 556 } 557 if (strncmp(key, "ctrl.", strlen("ctrl.")) == 0) { 558 char *ctrlname = key + strlen("ctrl."); 559 uint32_t ctrl_id = video_name2cid(ctrlname); 560 561 n = strtol(value, NULL, 0); 562 if (n == LONG_MIN || n == LONG_MAX) 563 goto done; 564 found = video_set_ctrl(ctrl_id, n); 565 } 566 567done: 568 free(buf); 569 if (!found) 570 fprintf(stderr, "%s: field %s does not exist\n", 571 getprogname(), name); 572} 573 574static bool 575video_set_ctrl(uint32_t ctrl_id, int32_t value) 576{ 577 struct v4l2_control ctrl; 578 const char *ctrlname; 579 int32_t ovalue; 580 int error; 581 582 ctrlname = video_cid2name(ctrl_id); 583 584 ctrl.id = ctrl_id; 585 error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl); 586 if (error) 587 return false; 588 ovalue = ctrl.value; 589 ctrl.value = value; 590 error = ioctl(video_fd, VIDIOC_S_CTRL, &ctrl); 591 if (error) 592 err(EXIT_FAILURE, "VIDIOC_S_CTRL failed for '%s'", ctrlname); 593 error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl); 594 if (error) 595 err(EXIT_FAILURE, "VIDIOC_G_CTRL failed for '%s'", ctrlname); 596 597 if (ctrlname) 598 printf("ctrl.%s: %d -> %d\n", ctrlname, ovalue, ctrl.value); 599 else 600 printf("ctrl.%08x: %d -> %d\n", ctrl.id, ovalue, ctrl.value); 601 602 return true; 603} 604 605static const char * 606video_cid2name(uint32_t id) 607{ 608 unsigned int i; 609 610 for (i = 0; i < __arraycount(videoctl_cid_names); i++) 611 if (videoctl_cid_names[i].id == id) 612 return videoctl_cid_names[i].name; 613 614 return NULL; 615} 616 617static uint32_t 618video_name2cid(const char *name) 619{ 620 unsigned int i; 621 622 for (i = 0; i < __arraycount(videoctl_cid_names); i++) 623 if (strcmp(name, videoctl_cid_names[i].name) == 0) 624 return videoctl_cid_names[i].id; 625 626 return (uint32_t)-1; 627} 628