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