videoctl.c revision 1.2 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 (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.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