xref: /netbsd-src/usr.bin/videoctl/videoctl.c (revision ded935ea33c9ba1167549b4d4f39f72a91a12a0f)
1 /* $NetBSD: videoctl.c,v 1.3 2021/02/19 11:39:11 rillig 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.3 2021/02/19 11:39:11 rillig 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
main(int argc,char * argv[])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 		default:
124 			usage();
125 			/* NOTREACHED */
126 		}
127 	}
128 	argc -= optind;
129 	argv += optind;
130 
131 	if (wflag && aflag)
132 		usage();
133 		/* NOTREACHED */
134 	if (wflag && argc == 0)
135 		usage();
136 		/* NOTREACHED */
137 	if (aflag && argc > 0)
138 		usage();
139 		/* NOTREACHED */
140 	if (!wflag && !aflag && argc == 0)
141 		usage();
142 		/* NOTREACHED */
143 
144 	if (video_dev == NULL)
145 		video_dev = _PATH_VIDEO0;
146 
147 	video_fd = open(video_dev, wflag ? O_RDWR : O_RDONLY);
148 	if (video_fd == -1)
149 		err(EXIT_FAILURE, "couldn't open '%s'", video_dev);
150 
151 	if (aflag) {
152 		video_print_all();
153 	} else if (wflag) {
154 		while (argc > 0) {
155 			video_set(argv[0]);
156 			--argc;
157 			++argv;
158 		}
159 	} else {
160 		while (argc > 0) {
161 			video_print(argv[0]);
162 			--argc;
163 			++argv;
164 		}
165 	}
166 
167 	close(video_fd);
168 
169 	return EXIT_SUCCESS;
170 }
171 
172 static void
usage(void)173 usage(void)
174 {
175 	fprintf(stderr, "usage: %s [-d file] name ...\n", getprogname());
176 	fprintf(stderr, "usage: %s [-d file] -w name=value ...\n",
177 	    getprogname());
178 	fprintf(stderr, "usage: %s [-d file] -a\n", getprogname());
179 	exit(EXIT_FAILURE);
180 }
181 
182 static void
video_print_all(void)183 video_print_all(void)
184 {
185 	video_print_caps(NULL);
186 	video_print_formats(NULL);
187 	video_print_inputs(NULL);
188 	video_print_audios(NULL);
189 	video_print_standards(NULL);
190 	video_print_tuners(NULL);
191 	video_print_ctrl(0);
192 }
193 
194 static bool
video_print_caps(const char * name)195 video_print_caps(const char *name)
196 {
197 	struct v4l2_capability cap;
198 	char capbuf[128];
199 	int error;
200 	bool found = false;
201 
202 	if (strtok(NULL, ".") != NULL)
203 		return false;
204 
205 	/* query capabilities */
206 	error = ioctl(video_fd, VIDIOC_QUERYCAP, &cap);
207 	if (error == -1)
208 		err(EXIT_FAILURE, "VIDIOC_QUERYCAP failed");
209 
210 	if (!name || strcmp(name, "card") == 0) {
211 		printf("info.cap.card=%s\n", cap.card);
212 		found = true;
213 	}
214 	if (!name || strcmp(name, "driver") == 0) {
215 		printf("info.cap.driver=%s\n", cap.driver);
216 		found = true;
217 	}
218 	if (!name || strcmp(name, "bus_info") == 0) {
219 		printf("info.cap.bus_info=%s\n", cap.bus_info);
220 		found = true;
221 	}
222 	if (!name || strcmp(name, "version") == 0) {
223 		printf("info.cap.version=%u.%u.%u\n",
224 		    (cap.version >> 16) & 0xff,
225 		    (cap.version >> 8) & 0xff,
226 		    cap.version & 0xff);
227 		found = true;
228 	}
229 	if (!name || strcmp(name, "capabilities") == 0) {
230 		snprintb(capbuf, sizeof(capbuf), V4L2_CAP_BITMASK,
231 		    cap.capabilities);
232 		printf("info.cap.capabilities=%s\n", capbuf);
233 		found = true;
234 	}
235 
236 	return found;
237 }
238 
239 static bool
video_print_formats(const char * name)240 video_print_formats(const char *name)
241 {
242 	struct v4l2_fmtdesc fmtdesc;
243 	int error;
244 
245 	fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
246 	if (name == NULL) {
247 		/* enumerate formats */
248 		for (fmtdesc.index = 0; ; fmtdesc.index++) {
249 			error = ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc);
250 			if (error)
251 				break;
252 			printf("info.format.%u=%s\n", fmtdesc.index,
253 			    fmtdesc.description);
254 		}
255 	} else {
256 		unsigned long n;
257 
258 		if (strtok(NULL, ".") != NULL)
259 			return false;
260 
261 		n = strtoul(name, NULL, 10);
262 		if (n == ULONG_MAX)
263 			return false;
264 		fmtdesc.index = n;
265 		error = ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc);
266 		if (error)
267 			return false;
268 		printf("info.format.%u=%s\n", fmtdesc.index,
269 		    fmtdesc.description);
270 	}
271 
272 	return true;
273 }
274 
275 static bool
video_print_inputs(const char * name)276 video_print_inputs(const char *name)
277 {
278 	struct v4l2_input input;
279 	int error;
280 
281 	if (name == NULL) {
282 		/* enumerate inputs */
283 		for (input.index = 0; ; input.index++) {
284 			error = ioctl(video_fd, VIDIOC_ENUMINPUT, &input);
285 			if (error)
286 				break;
287 			printf("info.input.%u=%s\n", input.index, input.name);
288 			printf("info.input.%u.type=", input.index);
289 			switch (input.type) {
290 			case V4L2_INPUT_TYPE_TUNER:
291 				printf("tuner\n");
292 				break;
293 			case V4L2_INPUT_TYPE_CAMERA:
294 				printf("baseband\n");
295 				break;
296 			default:
297 				printf("unknown (%d)\n", input.type);
298 				break;
299 			}
300 		}
301 	} else {
302 		unsigned long n;
303 		char *s;
304 
305 		n = strtoul(name, NULL, 10);
306 		if (n == ULONG_MAX)
307 			return false;
308 		input.index = n;
309 		error = ioctl(video_fd, VIDIOC_ENUMINPUT, &input);
310 		if (error)
311 			return false;
312 
313 		s = strtok(NULL, ".");
314 		if (s == NULL) {
315 			printf("info.input.%u=%s\n", input.index, input.name);
316 		} else if (strcmp(s, "type") == 0) {
317 			if (strtok(NULL, ".") != NULL)
318 				return false;
319 			printf("info.input.%u.type=", input.index);
320 			switch (input.type) {
321 			case V4L2_INPUT_TYPE_TUNER:
322 				printf("tuner\n");
323 				break;
324 			case V4L2_INPUT_TYPE_CAMERA:
325 				printf("baseband\n");
326 				break;
327 			default:
328 				printf("unknown (%d)\n", input.type);
329 				break;
330 			}
331 		} else
332 			return false;
333 	}
334 
335 	return true;
336 }
337 
338 static bool
video_print_audios(const char * name)339 video_print_audios(const char *name)
340 {
341 	struct v4l2_audio audio;
342 	int error;
343 
344 	if (name == NULL) {
345 		/* enumerate audio */
346 		for (audio.index = 0; ; audio.index++) {
347 			error = ioctl(video_fd, VIDIOC_ENUMAUDIO, &audio);
348 			if (error)
349 				break;
350 			printf("info.audio.%u=%s\n", audio.index, audio.name);
351 			printf("info.audio.%u.stereo=%d\n", audio.index,
352 			    audio.capability & V4L2_AUDCAP_STEREO ? 1 : 0);
353 			printf("info.audio.%u.avl=%d\n", audio.index,
354 			    audio.capability & V4L2_AUDCAP_AVL ? 1 : 0);
355 		}
356 	} else {
357 		unsigned long n;
358 		char *s;
359 
360 		n = strtoul(name, NULL, 10);
361 		if (n == ULONG_MAX)
362 			return false;
363 		audio.index = n;
364 		error = ioctl(video_fd, VIDIOC_ENUMAUDIO, &audio);
365 		if (error)
366 			return false;
367 
368 		s = strtok(NULL, ".");
369 		if (s == NULL) {
370 			printf("info.audio.%u=%s\n", audio.index, audio.name);
371 		} else if (strcmp(s, "stereo") == 0) {
372 			if (strtok(NULL, ".") != NULL)
373 				return false;
374 			printf("info.audio.%u.stereo=%d\n", audio.index,
375 			    audio.capability & V4L2_AUDCAP_STEREO ? 1 : 0);
376 		} else if (strcmp(s, "avl") == 0) {
377 			if (strtok(NULL, ".") != NULL)
378 				return false;
379 			printf("info.audio.%u.avl=%d\n", audio.index,
380 			    audio.capability & V4L2_AUDCAP_AVL ? 1 : 0);
381 		} else
382 			return false;
383 	}
384 
385 	return true;
386 }
387 
388 static bool
video_print_standards(const char * name)389 video_print_standards(const char *name)
390 {
391 	struct v4l2_standard std;
392 	int error;
393 
394 	if (name == NULL) {
395 		/* enumerate standards */
396 		for (std.index = 0; ; std.index++) {
397 			error = ioctl(video_fd, VIDIOC_ENUMSTD, &std);
398 			if (error)
399 				break;
400 			printf("info.standard.%u=%s\n", std.index, std.name);
401 		}
402 	} else {
403 		unsigned long n;
404 
405 		if (strtok(NULL, ".") != NULL)
406 			return false;
407 
408 		n = strtoul(name, NULL, 10);
409 		if (n == ULONG_MAX)
410 			return false;
411 		std.index = n;
412 		error = ioctl(video_fd, VIDIOC_ENUMSTD, &std);
413 		if (error)
414 			return false;
415 		printf("info.standard.%u=%s\n", std.index, std.name);
416 	}
417 
418 	return true;
419 }
420 
421 static bool
video_print_tuners(const char * name)422 video_print_tuners(const char *name)
423 {
424 	struct v4l2_tuner tuner;
425 	int error;
426 
427 	if (name == NULL) {
428 		/* enumerate tuners */
429 		for (tuner.index = 0; ; tuner.index++) {
430 			error = ioctl(video_fd, VIDIOC_G_TUNER, &tuner);
431 			if (error)
432 				break;
433 			printf("info.tuner.%u=%s\n", tuner.index, tuner.name);
434 		}
435 	} else {
436 		unsigned long n;
437 
438 		if (strtok(NULL, ".") != NULL)
439 			return false;
440 
441 		n = strtoul(name, NULL, 10);
442 		if (n == ULONG_MAX)
443 			return false;
444 		tuner.index = n;
445 		error = ioctl(video_fd, VIDIOC_G_TUNER, &tuner);
446 		if (error)
447 			return false;
448 		printf("info.tuner.%u=%s\n", tuner.index, tuner.name);
449 	}
450 
451 	return true;
452 }
453 
454 static void
video_print(const char * name)455 video_print(const char *name)
456 {
457 	char *buf, *s, *s2 = NULL;
458 	bool found = false;
459 
460 	buf = strdup(name);
461 	s = strtok(buf, ".");
462 	if (s == NULL)
463 		return;
464 
465 	if (strcmp(s, "info") == 0) {
466 		s = strtok(NULL, ".");
467 		if (s)
468 			s2 = strtok(NULL, ".");
469 		if (s == NULL || strcmp(s, "cap") == 0) {
470 			found = video_print_caps(s2);
471 		}
472 		if (s == NULL || strcmp(s, "format") == 0) {
473 			found = video_print_formats(s2);
474 		}
475 		if (s == NULL || strcmp(s, "input") == 0) {
476 			found = video_print_inputs(s2);
477 		}
478 		if (s == NULL || strcmp(s, "audio") == 0) {
479 			found = video_print_audios(s2);
480 		}
481 		if (s == NULL || strcmp(s, "standard") == 0) {
482 			found = video_print_standards(s2);
483 		}
484 		if (s == NULL || strcmp(s, "tuner") == 0) {
485 			found = video_print_tuners(s2);
486 		}
487 	} else if (strcmp(s, "ctrl") == 0) {
488 		s = strtok(NULL, ".");
489 		if (s)
490 			s2 = strtok(NULL, ".");
491 
492 		if (s == NULL)
493 			found = video_print_ctrl(0);
494 		else if (s && !s2)
495 			found = video_print_ctrl(video_name2cid(s));
496 	}
497 
498 	free(buf);
499 	if (!found)
500 		fprintf(stderr, "%s: field %s does not exist\n",
501 		    getprogname(), name);
502 }
503 
504 static bool
video_print_ctrl(uint32_t ctrl_id)505 video_print_ctrl(uint32_t ctrl_id)
506 {
507 	struct v4l2_control ctrl;
508 	const char *ctrlname;
509 	bool found = false;
510 	int error;
511 
512 	for (ctrl.id = V4L2_CID_BASE; ctrl.id != V4L2_CID_LASTP1; ctrl.id++) {
513 		if (ctrl_id != 0 && ctrl_id != ctrl.id)
514 			continue;
515 		error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl);
516 		if (error)
517 			continue;
518 		ctrlname = video_cid2name(ctrl.id);
519 		if (ctrlname)
520 			printf("ctrl.%s=%d\n", ctrlname, ctrl.value);
521 		else
522 			printf("ctrl.%08x=%d\n", ctrl.id, ctrl.value);
523 		found = true;
524 	}
525 
526 	return found;
527 }
528 
529 static void
video_set(const char * name)530 video_set(const char *name)
531 {
532 	char *buf, *key, *value;
533 	bool found = false;
534 	long n;
535 
536 	if (strchr(name, '=') == NULL) {
537 		fprintf(stderr, "%s: No '=' in %s\n", getprogname(), name);
538 		exit(EXIT_FAILURE);
539 	}
540 
541 	buf = strdup(name);
542 	key = strtok(buf, "=");
543 	if (key == NULL)
544 		usage();
545 		/* NOTREACHED */
546 	value = strtok(NULL, "");
547 	if (value == NULL)
548 		usage();
549 		/* NOTREACHED */
550 
551 	if (strncmp(key, "info.", strlen("info.")) == 0) {
552 		fprintf(stderr, "'info' subtree read-only\n");
553 		found = true;
554 		goto done;
555 	}
556 	if (strncmp(key, "ctrl.", strlen("ctrl.")) == 0) {
557 		char *ctrlname = key + strlen("ctrl.");
558 		uint32_t ctrl_id = video_name2cid(ctrlname);
559 
560 		n = strtol(value, NULL, 0);
561 		if (n == LONG_MIN || n == LONG_MAX)
562 			goto done;
563 		found = video_set_ctrl(ctrl_id, n);
564 	}
565 
566 done:
567 	free(buf);
568 	if (!found)
569 		fprintf(stderr, "%s: field %s does not exist\n",
570 		    getprogname(), name);
571 }
572 
573 static bool
video_set_ctrl(uint32_t ctrl_id,int32_t value)574 video_set_ctrl(uint32_t ctrl_id, int32_t value)
575 {
576 	struct v4l2_control ctrl;
577 	const char *ctrlname;
578 	int32_t ovalue;
579 	int error;
580 
581 	ctrlname = video_cid2name(ctrl_id);
582 
583 	ctrl.id = ctrl_id;
584 	error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl);
585 	if (error)
586 		return false;
587 	ovalue = ctrl.value;
588 	ctrl.value = value;
589 	error = ioctl(video_fd, VIDIOC_S_CTRL, &ctrl);
590 	if (error)
591 		err(EXIT_FAILURE, "VIDIOC_S_CTRL failed for '%s'", ctrlname);
592 	error = ioctl(video_fd, VIDIOC_G_CTRL, &ctrl);
593 	if (error)
594 		err(EXIT_FAILURE, "VIDIOC_G_CTRL failed for '%s'", ctrlname);
595 
596 	if (ctrlname)
597 		printf("ctrl.%s: %d -> %d\n", ctrlname, ovalue, ctrl.value);
598 	else
599 		printf("ctrl.%08x: %d -> %d\n", ctrl.id, ovalue, ctrl.value);
600 
601 	return true;
602 }
603 
604 static const char *
video_cid2name(uint32_t id)605 video_cid2name(uint32_t id)
606 {
607 	unsigned int i;
608 
609 	for (i = 0; i < __arraycount(videoctl_cid_names); i++)
610 		if (videoctl_cid_names[i].id == id)
611 			return videoctl_cid_names[i].name;
612 
613 	return NULL;
614 }
615 
616 static uint32_t
video_name2cid(const char * name)617 video_name2cid(const char *name)
618 {
619 	unsigned int i;
620 
621 	for (i = 0; i < __arraycount(videoctl_cid_names); i++)
622 		if (strcmp(name, videoctl_cid_names[i].name) == 0)
623 			return videoctl_cid_names[i].id;
624 
625 	return (uint32_t)-1;
626 }
627