xref: /netbsd-src/usr.bin/videoctl/videoctl.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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@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.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