Home | History | Annotate | Line # | Download | only in videoctl
videoctl.c revision 1.2
      1 /* $NetBSD: videoctl.c,v 1.2 2011/09/16 15:39:30 joerg Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) 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 (at) invisible.ca>. All rights reserved.");
     32 __RCSID("$NetBSD: videoctl.c,v 1.2 2011/09/16 15:39:30 joerg 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);
     51 static void		video_print(const char *);
     52 static void		video_print_all(void);
     53 static bool		video_print_caps(const char *);
     54 static bool		video_print_formats(const char *);
     55 static bool		video_print_inputs(const char *);
     56 static bool		video_print_audios(const char *);
     57 static bool		video_print_standards(const char *);
     58 static bool		video_print_tuners(const char *);
     59 static bool		video_print_ctrl(uint32_t);
     60 static void		video_set(const char *);
     61 static bool		video_set_ctrl(uint32_t, int32_t);
     62 static const char *	video_cid2name(uint32_t);
     63 static uint32_t		video_name2cid(const char *);
     64 
     65 static const char *video_dev = NULL;
     66 static int video_fd = -1;
     67 static bool aflag = false;
     68 static bool wflag = false;
     69 
     70 static 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 
    105 int
    106 main(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 
    173 static void
    174 usage(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 
    183 static void
    184 video_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 
    195 static bool
    196 video_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 
    240 static bool
    241 video_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 
    276 static bool
    277 video_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 
    339 static bool
    340 video_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 
    389 static bool
    390 video_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 
    422 static bool
    423 video_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 
    455 static void
    456 video_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 
    505 static bool
    506 video_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 
    530 static void
    531 video_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 
    567 done:
    568 	free(buf);
    569 	if (!found)
    570 		fprintf(stderr, "%s: field %s does not exist\n",
    571 		    getprogname(), name);
    572 }
    573 
    574 static bool
    575 video_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 
    605 static const char *
    606 video_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 
    617 static uint32_t
    618 video_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