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