Home | History | Annotate | Line # | Download | only in videoctl
      1 /* $NetBSD: videoctl.c,v 1.4 2025/04/12 07:54:39 mlelstv 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.4 2025/04/12 07:54:39 mlelstv Exp $");
     33 
     34 #include <sys/types.h>
     35 #include <sys/endian.h>
     36 #include <sys/ioctl.h>
     37 #include <sys/videoio.h>
     38 
     39 #include <err.h>
     40 #include <errno.h>
     41 #include <fcntl.h>
     42 #include <limits.h>
     43 #include <paths.h>
     44 #include <stdio.h>
     45 #include <string.h>
     46 #include <stdbool.h>
     47 #include <stdlib.h>
     48 #include <unistd.h>
     49 #include <util.h>
     50 #include <vis.h>
     51 
     52 __dead static void	usage(void);
     53 static void		video_print(const char *);
     54 static void		video_print_all(void);
     55 static bool		video_print_caps(const char *);
     56 static bool		video_print_formats(const char *);
     57 static bool		video_print_inputs(const char *);
     58 static bool		video_print_audios(const char *);
     59 static bool		video_print_standards(const char *);
     60 static bool		video_print_tuners(const char *);
     61 static bool		video_print_ctrl(uint32_t);
     62 static void		video_set(const char *);
     63 static bool		video_set_ctrl(uint32_t, int32_t);
     64 static const char *	video_cid2name(uint32_t);
     65 static uint32_t		video_name2cid(const char *);
     66 
     67 static const char *video_dev = NULL;
     68 static int video_fd = -1;
     69 static bool aflag = false;
     70 static bool wflag = false;
     71 
     72 static const struct {
     73 	uint32_t	id;
     74 	const char	*name;
     75 } videoctl_cid_names[] = {
     76 	{ V4L2_CID_BRIGHTNESS,		"brightness" },
     77 	{ V4L2_CID_CONTRAST,		"contrast" },
     78 	{ V4L2_CID_SATURATION,		"saturation" },
     79 	{ V4L2_CID_HUE,			"hue" },
     80 	{ V4L2_CID_AUDIO_VOLUME,	"audio_volume" },
     81 	{ V4L2_CID_AUDIO_BALANCE,	"audio_balance" },
     82 	{ V4L2_CID_AUDIO_BASS,		"audio_bass" },
     83 	{ V4L2_CID_AUDIO_TREBLE,	"audio_treble" },
     84 	{ V4L2_CID_AUDIO_MUTE,		"audio_mute" },
     85 	{ V4L2_CID_AUDIO_LOUDNESS,	"audio_loudness" },
     86 	{ V4L2_CID_BLACK_LEVEL,		"black_level" },
     87 	{ V4L2_CID_AUTO_WHITE_BALANCE,	"auto_white_balance" },
     88 	{ V4L2_CID_DO_WHITE_BALANCE,	"do_white_balance" },
     89 	{ V4L2_CID_RED_BALANCE,		"red_balance" },
     90 	{ V4L2_CID_BLUE_BALANCE,	"blue_balance" },
     91 	{ V4L2_CID_GAMMA,		"gamma" },
     92 	{ V4L2_CID_WHITENESS,		"whiteness" },
     93 	{ V4L2_CID_EXPOSURE,		"exposure" },
     94 	{ V4L2_CID_AUTOGAIN,		"autogain" },
     95 	{ V4L2_CID_GAIN,		"gain" },
     96 	{ V4L2_CID_HFLIP,		"hflip" },
     97 	{ V4L2_CID_VFLIP,		"vflip" },
     98 	{ V4L2_CID_HCENTER,		"hcenter" },
     99 	{ V4L2_CID_VCENTER,		"vcenter" },
    100 	{ V4L2_CID_POWER_LINE_FREQUENCY, "power_line_frequency" },
    101 	{ V4L2_CID_HUE_AUTO,		"hue_auto" },
    102 	{ V4L2_CID_WHITE_BALANCE_TEMPERATURE, "white_balance_temperature" },
    103 	{ V4L2_CID_SHARPNESS,		"sharpness" },
    104 	{ V4L2_CID_BACKLIGHT_COMPENSATION, "backlight_compensation" },
    105 };
    106 
    107 int
    108 main(int argc, char *argv[])
    109 {
    110 	int ch;
    111 
    112 	setprogname(argv[0]);
    113 
    114 	while ((ch = getopt(argc, argv, "ad:w")) != -1) {
    115 		switch (ch) {
    116 		case 'a':
    117 			aflag = true;
    118 			break;
    119 		case 'd':
    120 			video_dev = strdup(optarg);
    121 			break;
    122 		case 'w':
    123 			wflag = true;
    124 			break;
    125 		default:
    126 			usage();
    127 			/* NOTREACHED */
    128 		}
    129 	}
    130 	argc -= optind;
    131 	argv += optind;
    132 
    133 	if (wflag && aflag)
    134 		usage();
    135 		/* NOTREACHED */
    136 	if (wflag && argc == 0)
    137 		usage();
    138 		/* NOTREACHED */
    139 	if (aflag && argc > 0)
    140 		usage();
    141 		/* NOTREACHED */
    142 	if (!wflag && !aflag && argc == 0)
    143 		usage();
    144 		/* NOTREACHED */
    145 
    146 	if (video_dev == NULL)
    147 		video_dev = _PATH_VIDEO0;
    148 
    149 	video_fd = open(video_dev, wflag ? O_RDWR : O_RDONLY);
    150 	if (video_fd == -1)
    151 		err(EXIT_FAILURE, "couldn't open '%s'", video_dev);
    152 
    153 	if (aflag) {
    154 		video_print_all();
    155 	} else if (wflag) {
    156 		while (argc > 0) {
    157 			video_set(argv[0]);
    158 			--argc;
    159 			++argv;
    160 		}
    161 	} else {
    162 		while (argc > 0) {
    163 			video_print(argv[0]);
    164 			--argc;
    165 			++argv;
    166 		}
    167 	}
    168 
    169 	close(video_fd);
    170 
    171 	return EXIT_SUCCESS;
    172 }
    173 
    174 static void
    175 usage(void)
    176 {
    177 	fprintf(stderr, "usage: %s [-d file] name ...\n", getprogname());
    178 	fprintf(stderr, "usage: %s [-d file] -w name=value ...\n",
    179 	    getprogname());
    180 	fprintf(stderr, "usage: %s [-d file] -a\n", getprogname());
    181 	exit(EXIT_FAILURE);
    182 }
    183 
    184 static void
    185 video_print_all(void)
    186 {
    187 	video_print_caps(NULL);
    188 	video_print_formats(NULL);
    189 	video_print_inputs(NULL);
    190 	video_print_audios(NULL);
    191 	video_print_standards(NULL);
    192 	video_print_tuners(NULL);
    193 	video_print_ctrl(0);
    194 }
    195 
    196 static bool
    197 video_print_caps(const char *name)
    198 {
    199 	struct v4l2_capability cap;
    200 	char capbuf[128];
    201 	int error;
    202 	bool found = false;
    203 
    204 	if (strtok(NULL, ".") != NULL)
    205 		return false;
    206 
    207 	/* query capabilities */
    208 	error = ioctl(video_fd, VIDIOC_QUERYCAP, &cap);
    209 	if (error == -1)
    210 		err(EXIT_FAILURE, "VIDIOC_QUERYCAP failed");
    211 
    212 	if (!name || strcmp(name, "card") == 0) {
    213 		printf("info.cap.card=%s\n", cap.card);
    214 		found = true;
    215 	}
    216 	if (!name || strcmp(name, "driver") == 0) {
    217 		printf("info.cap.driver=%s\n", cap.driver);
    218 		found = true;
    219 	}
    220 	if (!name || strcmp(name, "bus_info") == 0) {
    221 		printf("info.cap.bus_info=%s\n", cap.bus_info);
    222 		found = true;
    223 	}
    224 	if (!name || strcmp(name, "version") == 0) {
    225 		printf("info.cap.version=%u.%u.%u\n",
    226 		    (cap.version >> 16) & 0xff,
    227 		    (cap.version >> 8) & 0xff,
    228 		    cap.version & 0xff);
    229 		found = true;
    230 	}
    231 	if (!name || strcmp(name, "capabilities") == 0) {
    232 		snprintb(capbuf, sizeof(capbuf), V4L2_CAP_BITMASK,
    233 		    cap.capabilities);
    234 		printf("info.cap.capabilities=%s\n", capbuf);
    235 		found = true;
    236 	}
    237 
    238 	return found;
    239 }
    240 
    241 static bool
    242 video_print_one_format(unsigned long fmtnum, unsigned long sizenum)
    243 {
    244 	struct v4l2_fmtdesc fmtdesc;
    245 	struct v4l2_frmsizeenum framesize;
    246 	unsigned long n;
    247 	int error;
    248 
    249 	fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    250 	fmtdesc.index = fmtnum;
    251 	error = ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc);
    252 	if (error)
    253 		return false;
    254 
    255 	printf("info.format.%u=%s\n", fmtdesc.index,
    256 	    fmtdesc.description);
    257 
    258 	if (sizenum == ULONG_MAX)
    259 		n = 0;
    260 	else
    261 		n = sizenum;
    262 
    263 	while (n <= sizenum) {
    264 		framesize.index = n++;
    265 		framesize.pixel_format = fmtdesc.pixelformat;
    266 		error = ioctl(video_fd, VIDIOC_ENUM_FRAMESIZES, &framesize);
    267 		if (error)
    268 			break;
    269 
    270 		switch (framesize.type) {
    271 		case V4L2_FRMIVAL_TYPE_DISCRETE:
    272 		case V4L2_FRMIVAL_TYPE_CONTINUOUS:
    273 			printf("info.format.%u.size.%u=%ux%u\n",
    274 				fmtdesc.index, framesize.index,
    275 				framesize.discrete.width,
    276 				framesize.discrete.height);
    277 			break;
    278 		case V4L2_FRMIVAL_TYPE_STEPWISE:
    279 			printf("info.format.%u.size.%u=(%u,%u,%u)x(%u,%u,%u)\n",
    280 				fmtdesc.index, framesize.index,
    281 				framesize.stepwise.min_width,
    282 				framesize.stepwise.max_width,
    283 				framesize.stepwise.step_width,
    284 				framesize.stepwise.min_height,
    285 				framesize.stepwise.max_height,
    286 				framesize.stepwise.step_height);
    287 			break;
    288 		default:
    289 			printf("info.format.%u.size.%u=type %u\n",
    290 				fmtdesc.index, framesize.index,
    291 				framesize.type);
    292 		}
    293 	}
    294 
    295 	return true;
    296 }
    297 
    298 static bool
    299 video_print_formats(const char *name)
    300 {
    301 	unsigned long n, m = ULONG_MAX;
    302 	const char *p;
    303 
    304 	if (name == NULL) {
    305 		/* enumerate formats */
    306 		for (n = 0; ; n++) {
    307 			if (!video_print_one_format(n, m))
    308 				break;
    309 		}
    310 	} else {
    311 		p = strtok(NULL, ".");
    312 		n = strtoul(name, NULL, 10);
    313 		if (n == ULONG_MAX)
    314 			return false;
    315 
    316 		if (p != NULL) {
    317 			if (strcmp(p, "size") == 0) {
    318 				p = strtok(NULL, ".");
    319 				if (p != NULL) {
    320 					m = strtoul(p, NULL, 10);
    321 					if (m == ULONG_MAX)
    322 						return false;
    323 				}
    324 			} else
    325 				return false;
    326 		}
    327 
    328 
    329 		video_print_one_format(n, m);
    330 	}
    331 
    332 	return true;
    333 }
    334 
    335 static bool
    336 video_print_inputs(const char *name)
    337 {
    338 	struct v4l2_input input;
    339 	int error;
    340 
    341 	if (name == NULL) {
    342 		/* enumerate inputs */
    343 		for (input.index = 0; ; input.index++) {
    344 			error = ioctl(video_fd, VIDIOC_ENUMINPUT, &input);
    345 			if (error)
    346 				break;
    347 			printf("info.input.%u=%s\n", input.index, input.name);
    348 			printf("info.input.%u.type=", input.index);
    349 			switch (input.type) {
    350 			case V4L2_INPUT_TYPE_TUNER:
    351 				printf("tuner\n");
    352 				break;
    353 			case V4L2_INPUT_TYPE_CAMERA:
    354 				printf("baseband\n");
    355 				break;
    356 			default:
    357 				printf("unknown (%d)\n", input.type);
    358 				break;
    359 			}
    360 		}
    361 	} else {
    362 		unsigned long n;
    363 		char *s;
    364 
    365 		n = strtoul(name, NULL, 10);
    366 		if (n == ULONG_MAX)
    367 			return false;
    368 		input.index = n;
    369 		error = ioctl(video_fd, VIDIOC_ENUMINPUT, &input);
    370 		if (error)
    371 			return false;
    372 
    373 		s = strtok(NULL, ".");
    374 		if (s == NULL) {
    375 			printf("info.input.%u=%s\n", input.index, input.name);
    376 		} else if (strcmp(s, "type") == 0) {
    377 			if (strtok(NULL, ".") != NULL)
    378 				return false;
    379 			printf("info.input.%u.type=", input.index);
    380 			switch (input.type) {
    381 			case V4L2_INPUT_TYPE_TUNER:
    382 				printf("tuner\n");
    383 				break;
    384 			case V4L2_INPUT_TYPE_CAMERA:
    385 				printf("baseband\n");
    386 				break;
    387 			default:
    388 				printf("unknown (%d)\n", input.type);
    389 				break;
    390 			}
    391 		} else
    392 			return false;
    393 	}
    394 
    395 	return true;
    396 }
    397 
    398 static bool
    399 video_print_audios(const char *name)
    400 {
    401 	struct v4l2_audio audio;
    402 	int error;
    403 
    404 	if (name == NULL) {
    405 		/* enumerate audio */
    406 		for (audio.index = 0; ; audio.index++) {
    407 			error = ioctl(video_fd, VIDIOC_ENUMAUDIO, &audio);
    408 			if (error)
    409 				break;
    410 			printf("info.audio.%u=%s\n", audio.index, audio.name);
    411 			printf("info.audio.%u.stereo=%d\n", audio.index,
    412 			    audio.capability & V4L2_AUDCAP_STEREO ? 1 : 0);
    413 			printf("info.audio.%u.avl=%d\n", audio.index,
    414 			    audio.capability & V4L2_AUDCAP_AVL ? 1 : 0);
    415 		}
    416 	} else {
    417 		unsigned long n;
    418 		char *s;
    419 
    420 		n = strtoul(name, NULL, 10);
    421 		if (n == ULONG_MAX)
    422 			return false;
    423 		audio.index = n;
    424 		error = ioctl(video_fd, VIDIOC_ENUMAUDIO, &audio);
    425 		if (error)
    426 			return false;
    427 
    428 		s = strtok(NULL, ".");
    429 		if (s == NULL) {
    430 			printf("info.audio.%u=%s\n", audio.index, audio.name);
    431 		} else if (strcmp(s, "stereo") == 0) {
    432 			if (strtok(NULL, ".") != NULL)
    433 				return false;
    434 			printf("info.audio.%u.stereo=%d\n", audio.index,
    435 			    audio.capability & V4L2_AUDCAP_STEREO ? 1 : 0);
    436 		} else if (strcmp(s, "avl") == 0) {
    437 			if (strtok(NULL, ".") != NULL)
    438 				return false;
    439 			printf("info.audio.%u.avl=%d\n", audio.index,
    440 			    audio.capability & V4L2_AUDCAP_AVL ? 1 : 0);
    441 		} else
    442 			return false;
    443 	}
    444 
    445 	return true;
    446 }
    447 
    448 static bool
    449 video_print_standards(const char *name)
    450 {
    451 	struct v4l2_standard std;
    452 	int error;
    453 
    454 	if (name == NULL) {
    455 		/* enumerate standards */
    456 		for (std.index = 0; ; std.index++) {
    457 			error = ioctl(video_fd, VIDIOC_ENUMSTD, &std);
    458 			if (error)
    459 				break;
    460 			printf("info.standard.%u=%s\n", std.index, std.name);
    461 		}
    462 	} else {
    463 		unsigned long n;
    464 
    465 		if (strtok(NULL, ".") != NULL)
    466 			return false;
    467 
    468 		n = strtoul(name, NULL, 10);
    469 		if (n == ULONG_MAX)
    470 			return false;
    471 		std.index = n;
    472 		error = ioctl(video_fd, VIDIOC_ENUMSTD, &std);
    473 		if (error)
    474 			return false;
    475 		printf("info.standard.%u=%s\n", std.index, std.name);
    476 	}
    477 
    478 	return true;
    479 }
    480 
    481 static bool
    482 video_print_tuners(const char *name)
    483 {
    484 	struct v4l2_tuner tuner;
    485 	int error;
    486 
    487 	if (name == NULL) {
    488 		/* enumerate tuners */
    489 		for (tuner.index = 0; ; tuner.index++) {
    490 			error = ioctl(video_fd, VIDIOC_G_TUNER, &tuner);
    491 			if (error)
    492 				break;
    493 			printf("info.tuner.%u=%s\n", tuner.index, tuner.name);
    494 		}
    495 	} else {
    496 		unsigned long n;
    497 
    498 		if (strtok(NULL, ".") != NULL)
    499 			return false;
    500 
    501 		n = strtoul(name, NULL, 10);
    502 		if (n == ULONG_MAX)
    503 			return false;
    504 		tuner.index = n;
    505 		error = ioctl(video_fd, VIDIOC_G_TUNER, &tuner);
    506 		if (error)
    507 			return false;
    508 		printf("info.tuner.%u=%s\n", tuner.index, tuner.name);
    509 	}
    510 
    511 	return true;
    512 }
    513 
    514 static void
    515 video_print(const char *name)
    516 {
    517 	char *buf, *s, *s2 = NULL;
    518 	bool found = false;
    519 
    520 	buf = strdup(name);
    521 	s = strtok(buf, ".");
    522 	if (s == NULL)
    523 		return;
    524 
    525 	if (strcmp(s, "info") == 0) {
    526 		s = strtok(NULL, ".");
    527 		if (s)
    528 			s2 = strtok(NULL, ".");
    529 		if (s == NULL || strcmp(s, "cap") == 0) {
    530 			found = video_print_caps(s2);
    531 		}
    532 		if (s == NULL || strcmp(s, "format") == 0) {
    533 			found = video_print_formats(s2);
    534 		}
    535 		if (s == NULL || strcmp(s, "input") == 0) {
    536 			found = video_print_inputs(s2);
    537 		}
    538 		if (s == NULL || strcmp(s, "audio") == 0) {
    539 			found = video_print_audios(s2);
    540 		}
    541 		if (s == NULL || strcmp(s, "standard") == 0) {
    542 			found = video_print_standards(s2);
    543 		}
    544 		if (s == NULL || strcmp(s, "tuner") == 0) {
    545 			found = video_print_tuners(s2);
    546 		}
    547 	} else if (strcmp(s, "ctrl") == 0) {
    548 		s = strtok(NULL, ".");
    549 		if (s)
    550 			s2 = strtok(NULL, ".");
    551 
    552 		if (s == NULL)
    553 			found = video_print_ctrl(0);
    554 		else if (s && !s2)
    555 			found = video_print_ctrl(video_name2cid(s));
    556 	}
    557 
    558 	free(buf);
    559 	if (!found)
    560 		fprintf(stderr, "%s: field %s does not exist\n",
    561 		    getprogname(), name);
    562 }
    563 
    564 static bool
    565 video_print_ctrl(uint32_t ctrl_id)
    566 {
    567 	struct v4l2_control ctrl;
    568 	const char *ctrlname;
    569 	bool found = false;
    570 	int error;
    571 
    572 	for (ctrl.id = V4L2_CID_BASE; ctrl.id != V4L2_CID_LASTP1; ctrl.id++) {
    573 		if (ctrl_id != 0 && ctrl_id != ctrl.id)
    574 			continue;
    575 		error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl);
    576 		if (error)
    577 			continue;
    578 		ctrlname = video_cid2name(ctrl.id);
    579 		if (ctrlname)
    580 			printf("ctrl.%s=%d\n", ctrlname, ctrl.value);
    581 		else
    582 			printf("ctrl.%08x=%d\n", ctrl.id, ctrl.value);
    583 		found = true;
    584 	}
    585 
    586 	return found;
    587 }
    588 
    589 static void
    590 video_set(const char *name)
    591 {
    592 	char *buf, *key, *value;
    593 	bool found = false;
    594 	long n;
    595 
    596 	if (strchr(name, '=') == NULL) {
    597 		fprintf(stderr, "%s: No '=' in %s\n", getprogname(), name);
    598 		exit(EXIT_FAILURE);
    599 	}
    600 
    601 	buf = strdup(name);
    602 	key = strtok(buf, "=");
    603 	if (key == NULL)
    604 		usage();
    605 		/* NOTREACHED */
    606 	value = strtok(NULL, "");
    607 	if (value == NULL)
    608 		usage();
    609 		/* NOTREACHED */
    610 
    611 	if (strncmp(key, "info.", strlen("info.")) == 0) {
    612 		fprintf(stderr, "'info' subtree read-only\n");
    613 		found = true;
    614 		goto done;
    615 	}
    616 	if (strncmp(key, "ctrl.", strlen("ctrl.")) == 0) {
    617 		char *ctrlname = key + strlen("ctrl.");
    618 		uint32_t ctrl_id = video_name2cid(ctrlname);
    619 
    620 		n = strtol(value, NULL, 0);
    621 		if (n == LONG_MIN || n == LONG_MAX)
    622 			goto done;
    623 		found = video_set_ctrl(ctrl_id, n);
    624 	}
    625 
    626 done:
    627 	free(buf);
    628 	if (!found)
    629 		fprintf(stderr, "%s: field %s does not exist\n",
    630 		    getprogname(), name);
    631 }
    632 
    633 static bool
    634 video_set_ctrl(uint32_t ctrl_id, int32_t value)
    635 {
    636 	struct v4l2_control ctrl;
    637 	const char *ctrlname;
    638 	int32_t ovalue;
    639 	int error;
    640 
    641 	ctrlname = video_cid2name(ctrl_id);
    642 
    643 	ctrl.id = ctrl_id;
    644 	error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl);
    645 	if (error)
    646 		return false;
    647 	ovalue = ctrl.value;
    648 	ctrl.value = value;
    649 	error = ioctl(video_fd, VIDIOC_S_CTRL, &ctrl);
    650 	if (error)
    651 		err(EXIT_FAILURE, "VIDIOC_S_CTRL failed for '%s'", ctrlname);
    652 	error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl);
    653 	if (error)
    654 		err(EXIT_FAILURE, "VIDIOC_G_CTRL failed for '%s'", ctrlname);
    655 
    656 	if (ctrlname)
    657 		printf("ctrl.%s: %d -> %d\n", ctrlname, ovalue, ctrl.value);
    658 	else
    659 		printf("ctrl.%08x: %d -> %d\n", ctrl.id, ovalue, ctrl.value);
    660 
    661 	return true;
    662 }
    663 
    664 static const char *
    665 video_cid2name(uint32_t id)
    666 {
    667 	unsigned int i;
    668 
    669 	for (i = 0; i < __arraycount(videoctl_cid_names); i++)
    670 		if (videoctl_cid_names[i].id == id)
    671 			return videoctl_cid_names[i].name;
    672 
    673 	return NULL;
    674 }
    675 
    676 static uint32_t
    677 video_name2cid(const char *name)
    678 {
    679 	unsigned int i;
    680 
    681 	for (i = 0; i < __arraycount(videoctl_cid_names); i++)
    682 		if (strcmp(name, videoctl_cid_names[i].name) == 0)
    683 			return videoctl_cid_names[i].id;
    684 
    685 	return (uint32_t)-1;
    686 }
    687