1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <inttypes.h>
5 #include <getopt.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <ctype.h>
10 #include <errno.h>
11 #include <sys/ioctl.h>
12 #include <sys/types.h>
13 #include <sys/time.h>
14 #include <dirent.h>
15 #include <math.h>
16 #include <linux/media.h>
17
18 #include "v4l2-ctl.h"
19
20 #ifdef HAVE_SYS_KLOG_H
21 #include <sys/klog.h>
22 #endif
23
24 #include <cstring>
25 #include <list>
26 #include <vector>
27 #include <map>
28 #include <algorithm>
29
30 struct ctrl_subset {
31 unsigned offset[V4L2_CTRL_MAX_DIMS];
32 unsigned size[V4L2_CTRL_MAX_DIMS];
33 };
34
35 typedef std::map<unsigned, std::vector<struct v4l2_ext_control> > class2ctrls_map;
36
37 typedef std::map<std::string, struct v4l2_query_ext_ctrl> ctrl_qmap;
38 static ctrl_qmap ctrl_str2q;
39 typedef std::map<unsigned, std::string> ctrl_idmap;
40 static ctrl_idmap ctrl_id2str;
41
42 typedef std::map<std::string, ctrl_subset> ctrl_subset_map;
43 static ctrl_subset_map ctrl_subsets;
44
45 typedef std::list<std::string> ctrl_get_list;
46 static ctrl_get_list get_ctrls;
47
48 typedef std::map<std::string, std::string> ctrl_set_map;
49 static ctrl_set_map set_ctrls;
50
51 typedef std::vector<std::string> dev_vec;
52 typedef std::map<std::string, std::string> dev_map;
53
54 static enum v4l2_priority prio = V4L2_PRIORITY_UNSET;
55
56 static bool have_query_ext_ctrl;
57
common_usage()58 void common_usage()
59 {
60 printf("\nGeneral/Common options:\n"
61 " --all display all information available\n"
62 " -C, --get-ctrl <ctrl>[,<ctrl>...]\n"
63 " get the value of the controls [VIDIOC_G_EXT_CTRLS]\n"
64 " -c, --set-ctrl <ctrl>=<val>[,<ctrl>=<val>...]\n"
65 " set the value of the controls [VIDIOC_S_EXT_CTRLS]\n"
66 " -D, --info show driver info [VIDIOC_QUERYCAP]\n"
67 " -d, --device <dev> use device <dev> instead of /dev/video0\n"
68 " if <dev> starts with a digit, then /dev/video<dev> is used\n"
69 " Otherwise if -z was specified earlier, then <dev> is the entity name\n"
70 " or interface ID (if prefixed with 0x) as found in the topology of the\n"
71 " media device with the bus info string as specified by the -z option.\n"
72 " -e, --out-device <dev> use device <dev> for output streams instead of the\n"
73 " default device as set with --device\n"
74 " if <dev> starts with a digit, then /dev/video<dev> is used\n"
75 " Otherwise if -z was specified earlier, then <dev> is the entity name\n"
76 " or interface ID (if prefixed with 0x) as found in the topology of the\n"
77 " media device with the bus info string as specified by the -z option.\n"
78 " -E, --export-device <dev> use device <dev> for exporting DMA buffers\n"
79 " if <dev> starts with a digit, then /dev/video<dev> is used\n"
80 " Otherwise if -z was specified earlier, then <dev> is the entity name\n"
81 " or interface ID (if prefixed with 0x) as found in the topology of the\n"
82 " media device with the bus info string as specified by the -z option.\n"
83 " -z, --media-bus-info <bus-info>\n"
84 " find the media device with the given bus info string. If set, then\n"
85 " -d, -e and -E options can use the entity name or interface ID to refer\n"
86 " to the device nodes.\n"
87 " -h, --help display this help message\n"
88 " --help-all all options\n"
89 " --help-io input/output options\n"
90 " --help-meta metadata format options\n"
91 " --help-misc miscellaneous options\n"
92 " --help-overlay overlay format options\n"
93 " --help-sdr SDR format options\n"
94 " --help-selection crop/selection options\n"
95 " --help-stds standards and other video timings options\n"
96 " --help-streaming streaming options\n"
97 " --help-subdev sub-device options\n"
98 " --help-tuner tuner/modulator options\n"
99 " --help-vbi VBI format options\n"
100 " --help-vidcap video capture format options\n"
101 " --help-vidout vidout output format options\n"
102 " --help-edid edid handling options\n"
103 " -k, --concise be more concise if possible.\n"
104 " -l, --list-ctrls display all controls and their values [VIDIOC_QUERYCTRL]\n"
105 " -L, --list-ctrls-menus\n"
106 " display all controls and their menus [VIDIOC_QUERYMENU]\n"
107 " -r, --subset <ctrl>[,<offset>,<size>]+\n"
108 " the subset of the N-dimensional array to get/set for control <ctrl>,\n"
109 " for every dimension an (<offset>, <size>) tuple is given.\n"
110 #ifndef NO_LIBV4L2
111 " -w, --wrapper use the libv4l2 wrapper library.\n"
112 #endif
113 " --list-devices list all v4l devices. If -z was given, then list just the\n"
114 " devices of the media device with the bus info string as\n"
115 " specified by the -z option.\n"
116 " --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"
117 " --get-priority query the current access priority [VIDIOC_G_PRIORITY]\n"
118 " --set-priority <prio>\n"
119 " set the new access priority [VIDIOC_S_PRIORITY]\n"
120 " <prio> is 1 (background), 2 (interactive) or 3 (record)\n"
121 " --silent only set the result code, do not print any messages\n"
122 " --sleep <secs> sleep <secs>, call QUERYCAP and close the file handle\n"
123 " --verbose turn on verbose ioctl status reporting\n"
124 );
125 }
126
127 static const char *prefixes[] = {
128 "video",
129 "radio",
130 "vbi",
131 "swradio",
132 "v4l-subdev",
133 "v4l-touch",
134 "media",
135 NULL
136 };
137
is_v4l_dev(const char * name)138 static bool is_v4l_dev(const char *name)
139 {
140 for (unsigned i = 0; prefixes[i]; i++) {
141 unsigned l = strlen(prefixes[i]);
142
143 if (!memcmp(name, prefixes[i], l)) {
144 if (isdigit(name[l]))
145 return true;
146 }
147 }
148 return false;
149 }
150
calc_node_val(const char * s)151 static int calc_node_val(const char *s)
152 {
153 int n = 0;
154
155 s = std::strrchr(s, '/') + 1;
156
157 for (unsigned i = 0; prefixes[i]; i++) {
158 unsigned l = strlen(prefixes[i]);
159
160 if (!memcmp(s, prefixes[i], l)) {
161 n = i << 8;
162 n += atol(s + l);
163 return n;
164 }
165 }
166 return 0;
167 }
168
sort_on_device_name(const std::string & s1,const std::string & s2)169 static bool sort_on_device_name(const std::string &s1, const std::string &s2)
170 {
171 int n1 = calc_node_val(s1.c_str());
172 int n2 = calc_node_val(s2.c_str());
173
174 return n1 < n2;
175 }
176
list_media_devices(const std::string & media_bus_info)177 static void list_media_devices(const std::string &media_bus_info)
178 {
179 DIR *dp;
180 struct dirent *ep;
181 int media_fd = -1;
182 std::map<dev_t, std::string> devices;
183
184 dp = opendir("/dev");
185 if (dp == NULL) {
186 perror ("Couldn't open the directory");
187 return;
188 }
189 while ((ep = readdir(dp))) {
190 std::string s("/dev/");
191
192 s += ep->d_name;
193 if (memcmp(ep->d_name, "media", 5)) {
194 if (!is_v4l_dev(ep->d_name))
195 continue;
196 struct stat st;
197 if (stat(s.c_str(), &st))
198 continue;
199 devices[st.st_rdev] = s;
200 continue;
201 }
202 int fd = open(s.c_str(), O_RDWR);
203
204 if (fd < 0)
205 continue;
206 struct media_device_info mdi;
207
208 if (!ioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi) &&
209 media_bus_info == mdi.bus_info) {
210 media_fd = fd;
211 printf("%s\n", s.c_str());
212 } else {
213 close(fd);
214 }
215 }
216 closedir(dp);
217 if (media_fd < 0)
218 return;
219
220 media_v2_topology topology;
221 memset(&topology, 0, sizeof(topology));
222 if (ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology)) {
223 close(media_fd);
224 return;
225 }
226
227 media_v2_interface *ifaces = new media_v2_interface[topology.num_interfaces];
228 topology.ptr_interfaces = (uintptr_t)ifaces;
229
230 if (!ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology))
231 for (unsigned i = 0; i < topology.num_interfaces; i++) {
232 dev_t dev = makedev(ifaces[i].devnode.major,
233 ifaces[i].devnode.minor);
234
235 if (devices.find(dev) != devices.end())
236 printf("%s\n", devices[dev].c_str());
237 }
238 close(media_fd);
239 }
240
list_devices()241 static void list_devices()
242 {
243 DIR *dp;
244 struct dirent *ep;
245 dev_vec files;
246 dev_map links;
247 dev_map cards;
248 struct v4l2_capability vcap;
249
250 dp = opendir("/dev");
251 if (dp == NULL) {
252 perror ("Couldn't open the directory");
253 return;
254 }
255 while ((ep = readdir(dp)))
256 if (is_v4l_dev(ep->d_name))
257 files.push_back(std::string("/dev/") + ep->d_name);
258 closedir(dp);
259
260 /* Find device nodes which are links to other device nodes */
261 for (dev_vec::iterator iter = files.begin();
262 iter != files.end(); ) {
263 char link[64+1];
264 int link_len;
265 std::string target;
266
267 link_len = readlink(iter->c_str(), link, 64);
268 if (link_len < 0) { /* Not a link or error */
269 iter++;
270 continue;
271 }
272 link[link_len] = '\0';
273
274 /* Only remove from files list if target itself is in list */
275 if (link[0] != '/') /* Relative link */
276 target = std::string("/dev/");
277 target += link;
278 if (find(files.begin(), files.end(), target) == files.end()) {
279 iter++;
280 continue;
281 }
282
283 /* Move the device node from files to links */
284 if (links[target].empty())
285 links[target] = *iter;
286 else
287 links[target] += ", " + *iter;
288 iter = files.erase(iter);
289 }
290
291 std::sort(files.begin(), files.end(), sort_on_device_name);
292
293 for (dev_vec::iterator iter = files.begin();
294 iter != files.end(); ++iter) {
295 int fd = open(iter->c_str(), O_RDWR);
296 std::string bus_info;
297 std::string card;
298
299 if (fd < 0)
300 continue;
301 int err = ioctl(fd, VIDIOC_QUERYCAP, &vcap);
302 if (err) {
303 struct media_device_info mdi;
304
305 err = ioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi);
306 if (!err) {
307 if (mdi.bus_info[0])
308 bus_info = mdi.bus_info;
309 else
310 bus_info = std::string("platform:") + mdi.driver;
311 if (mdi.model[0])
312 card = mdi.model;
313 else
314 card = mdi.driver;
315 }
316 } else {
317 bus_info = reinterpret_cast<const char *>(vcap.bus_info);
318 card = reinterpret_cast<const char *>(vcap.card);
319 }
320 close(fd);
321 if (err)
322 continue;
323 if (cards[bus_info].empty())
324 cards[bus_info] += card + " (" + bus_info + "):\n";
325 cards[bus_info] += "\t" + (*iter);
326 if (!(links[*iter].empty()))
327 cards[bus_info] += " <- " + links[*iter];
328 cards[bus_info] += "\n";
329 }
330 for (dev_map::iterator iter = cards.begin();
331 iter != cards.end(); ++iter) {
332 printf("%s\n", iter->second.c_str());
333 }
334 }
335
name2var(const char * name)336 static std::string name2var(const char *name)
337 {
338 std::string s;
339 int add_underscore = 0;
340
341 while (*name) {
342 if (isalnum(*name)) {
343 if (add_underscore)
344 s += '_';
345 add_underscore = 0;
346 s += std::string(1, tolower(*name));
347 }
348 else if (s.length()) add_underscore = 1;
349 name++;
350 }
351 return s;
352 }
353
safename(const unsigned char * name)354 static std::string safename(const unsigned char *name)
355 {
356 std::string s;
357
358 while (*name) {
359 if (*name == '\n') {
360 s += "\\n";
361 }
362 else if (*name == '\r') {
363 s += "\\r";
364 }
365 else if (*name == '\f') {
366 s += "\\f";
367 }
368 else if (*name == '\\') {
369 s += "\\\\";
370 }
371 else if ((*name & 0x7f) < 0x20) {
372 char buf[3];
373
374 sprintf(buf, "%02x", *name);
375 s += "\\x";
376 s += buf;
377 }
378 else {
379 s += *name;
380 }
381 name++;
382 }
383 return s;
384 }
385
safename(const char * name)386 static std::string safename(const char *name)
387 {
388 return safename(reinterpret_cast<const unsigned char *>(name));
389 }
390
print_qctrl(int fd,struct v4l2_query_ext_ctrl * queryctrl,struct v4l2_ext_control * ctrl,int show_menus)391 static void print_qctrl(int fd, struct v4l2_query_ext_ctrl *queryctrl,
392 struct v4l2_ext_control *ctrl, int show_menus)
393 {
394 struct v4l2_querymenu qmenu;
395 std::string s = name2var(queryctrl->name);
396 unsigned i;
397
398 memset(&qmenu, 0, sizeof(qmenu));
399 qmenu.id = queryctrl->id;
400 switch (queryctrl->type) {
401 case V4L2_CTRL_TYPE_INTEGER:
402 printf("%31s %#8.8x (int) : min=%lld max=%lld step=%lld default=%lld",
403 s.c_str(), queryctrl->id,
404 queryctrl->minimum, queryctrl->maximum,
405 queryctrl->step, queryctrl->default_value);
406 break;
407 case V4L2_CTRL_TYPE_INTEGER64:
408 printf("%31s %#8.8x (int64) : min=%lld max=%lld step=%lld default=%lld",
409 s.c_str(), queryctrl->id,
410 queryctrl->minimum, queryctrl->maximum,
411 queryctrl->step, queryctrl->default_value);
412 break;
413 case V4L2_CTRL_TYPE_STRING:
414 printf("%31s %#8.8x (str) : min=%lld max=%lld step=%lld",
415 s.c_str(), queryctrl->id,
416 queryctrl->minimum, queryctrl->maximum,
417 queryctrl->step);
418 break;
419 case V4L2_CTRL_TYPE_BOOLEAN:
420 printf("%31s %#8.8x (bool) : default=%lld",
421 s.c_str(), queryctrl->id, queryctrl->default_value);
422 break;
423 case V4L2_CTRL_TYPE_MENU:
424 printf("%31s %#8.8x (menu) : min=%lld max=%lld default=%lld",
425 s.c_str(), queryctrl->id,
426 queryctrl->minimum, queryctrl->maximum,
427 queryctrl->default_value);
428 break;
429 case V4L2_CTRL_TYPE_INTEGER_MENU:
430 printf("%31s %#8.8x (intmenu): min=%lld max=%lld default=%lld",
431 s.c_str(), queryctrl->id,
432 queryctrl->minimum, queryctrl->maximum,
433 queryctrl->default_value);
434 break;
435 case V4L2_CTRL_TYPE_BUTTON:
436 printf("%31s %#8.8x (button) :", s.c_str(), queryctrl->id);
437 break;
438 case V4L2_CTRL_TYPE_BITMASK:
439 printf("%31s %#8.8x (bitmask): max=0x%08llx default=0x%08llx",
440 s.c_str(), queryctrl->id, queryctrl->maximum,
441 queryctrl->default_value);
442 break;
443 case V4L2_CTRL_TYPE_U8:
444 printf("%31s %#8.8x (u8) : min=%lld max=%lld step=%lld default=%lld",
445 s.c_str(), queryctrl->id,
446 queryctrl->minimum, queryctrl->maximum,
447 queryctrl->step, queryctrl->default_value);
448 break;
449 case V4L2_CTRL_TYPE_U16:
450 printf("%31s %#8.8x (u16) : min=%lld max=%lld step=%lld default=%lld",
451 s.c_str(), queryctrl->id,
452 queryctrl->minimum, queryctrl->maximum,
453 queryctrl->step, queryctrl->default_value);
454 break;
455 case V4L2_CTRL_TYPE_U32:
456 printf("%31s %#8.8x (u32) : min=%lld max=%lld step=%lld default=%lld",
457 s.c_str(), queryctrl->id,
458 queryctrl->minimum, queryctrl->maximum,
459 queryctrl->step, queryctrl->default_value);
460 break;
461 case V4L2_CTRL_TYPE_AREA:
462 printf("%31s %#8.8x (area) :", s.c_str(), queryctrl->id);
463 break;
464 default:
465 printf("%31s %#8.8x (unknown): type=%x",
466 s.c_str(), queryctrl->id, queryctrl->type);
467 break;
468 }
469 if (queryctrl->nr_of_dims == 0) {
470 switch (queryctrl->type) {
471 case V4L2_CTRL_TYPE_INTEGER:
472 case V4L2_CTRL_TYPE_BOOLEAN:
473 case V4L2_CTRL_TYPE_MENU:
474 case V4L2_CTRL_TYPE_INTEGER_MENU:
475 printf(" value=%d", ctrl->value);
476 break;
477 case V4L2_CTRL_TYPE_BITMASK:
478 printf(" value=0x%08x", ctrl->value);
479 break;
480 case V4L2_CTRL_TYPE_INTEGER64:
481 printf(" value=%lld", ctrl->value64);
482 break;
483 case V4L2_CTRL_TYPE_STRING:
484 printf(" value='%s'", safename(ctrl->string).c_str());
485 break;
486 default:
487 break;
488 }
489 }
490 if (queryctrl->nr_of_dims) {
491 printf(" ");
492 for (i = 0; i < queryctrl->nr_of_dims; i++)
493 printf("[%u]", queryctrl->dims[i]);
494 }
495 if (queryctrl->flags)
496 printf(" flags=%s", ctrlflags2s(queryctrl->flags).c_str());
497 printf("\n");
498 if ((queryctrl->type == V4L2_CTRL_TYPE_MENU ||
499 queryctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) && show_menus) {
500 for (i = queryctrl->minimum; i <= queryctrl->maximum; i++) {
501 qmenu.index = i;
502 if (test_ioctl(fd, VIDIOC_QUERYMENU, &qmenu))
503 continue;
504 if (queryctrl->type == V4L2_CTRL_TYPE_MENU)
505 printf("\t\t\t\t%d: %s\n", i, qmenu.name);
506 else
507 printf("\t\t\t\t%d: %lld (0x%llx)\n", i, qmenu.value, qmenu.value);
508 }
509 }
510 }
511
print_control(int fd,struct v4l2_query_ext_ctrl & qctrl,int show_menus)512 static int print_control(int fd, struct v4l2_query_ext_ctrl &qctrl, int show_menus)
513 {
514 struct v4l2_control ctrl;
515 struct v4l2_ext_control ext_ctrl;
516 struct v4l2_ext_controls ctrls;
517
518 memset(&ctrl, 0, sizeof(ctrl));
519 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
520 memset(&ctrls, 0, sizeof(ctrls));
521 if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
522 return 1;
523 if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) {
524 printf("\n%s\n\n", qctrl.name);
525 return 1;
526 }
527 ext_ctrl.id = qctrl.id;
528 if ((qctrl.flags & V4L2_CTRL_FLAG_WRITE_ONLY) ||
529 qctrl.type == V4L2_CTRL_TYPE_BUTTON) {
530 print_qctrl(fd, &qctrl, &ext_ctrl, show_menus);
531 return 1;
532 }
533 if (qctrl.type >= V4L2_CTRL_COMPOUND_TYPES) {
534 print_qctrl(fd, &qctrl, NULL, show_menus);
535 return 1;
536 }
537 ctrls.which = V4L2_CTRL_ID2WHICH(qctrl.id);
538 ctrls.count = 1;
539 ctrls.controls = &ext_ctrl;
540 if (qctrl.type == V4L2_CTRL_TYPE_INTEGER64 ||
541 qctrl.type == V4L2_CTRL_TYPE_STRING ||
542 (V4L2_CTRL_ID2WHICH(qctrl.id) != V4L2_CTRL_CLASS_USER &&
543 qctrl.id < V4L2_CID_PRIVATE_BASE)) {
544 if (qctrl.type == V4L2_CTRL_TYPE_STRING) {
545 ext_ctrl.size = qctrl.maximum + 1;
546 ext_ctrl.string = static_cast<char *>(malloc(ext_ctrl.size));
547 ext_ctrl.string[0] = 0;
548 }
549 if (test_ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) {
550 printf("error %d getting ext_ctrl %s\n",
551 errno, qctrl.name);
552 return 0;
553 }
554 }
555 else {
556 ctrl.id = qctrl.id;
557 if (test_ioctl(fd, VIDIOC_G_CTRL, &ctrl)) {
558 printf("error %d getting ctrl %s\n",
559 errno, qctrl.name);
560 return 0;
561 }
562 ext_ctrl.value = ctrl.value;
563 }
564 print_qctrl(fd, &qctrl, &ext_ctrl, show_menus);
565 if (qctrl.type == V4L2_CTRL_TYPE_STRING)
566 free(ext_ctrl.string);
567 return 1;
568 }
569
query_ext_ctrl_ioctl(int fd,struct v4l2_query_ext_ctrl & qctrl)570 static int query_ext_ctrl_ioctl(int fd, struct v4l2_query_ext_ctrl &qctrl)
571 {
572 struct v4l2_queryctrl qc;
573 int rc;
574
575 if (have_query_ext_ctrl) {
576 rc = test_ioctl(fd, VIDIOC_QUERY_EXT_CTRL, &qctrl);
577 if (rc != ENOTTY)
578 return rc;
579 }
580 qc.id = qctrl.id;
581 rc = test_ioctl(fd, VIDIOC_QUERYCTRL, &qc);
582 if (rc == 0) {
583 qctrl.type = qc.type;
584 memcpy(qctrl.name, qc.name, sizeof(qctrl.name));
585 qctrl.minimum = qc.minimum;
586 if (qc.type == V4L2_CTRL_TYPE_BITMASK) {
587 qctrl.maximum = static_cast<uint32_t>(qc.maximum);
588 qctrl.default_value = static_cast<uint32_t>(qc.default_value);
589 } else {
590 qctrl.maximum = qc.maximum;
591 qctrl.default_value = qc.default_value;
592 }
593 qctrl.step = qc.step;
594 qctrl.flags = qc.flags;
595 qctrl.elems = 1;
596 qctrl.nr_of_dims = 0;
597 memset(qctrl.dims, 0, sizeof(qctrl.dims));
598 switch (qctrl.type) {
599 case V4L2_CTRL_TYPE_INTEGER64:
600 qctrl.elem_size = sizeof(int64_t);
601 break;
602 case V4L2_CTRL_TYPE_STRING:
603 qctrl.elem_size = qc.maximum + 1;
604 qctrl.flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
605 break;
606 default:
607 qctrl.elem_size = sizeof(int32_t);
608 break;
609 }
610 memset(qctrl.reserved, 0, sizeof(qctrl.reserved));
611 }
612 qctrl.id = qc.id;
613 return rc;
614 }
615
list_controls(int fd,int show_menus)616 static void list_controls(int fd, int show_menus)
617 {
618 const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
619 struct v4l2_query_ext_ctrl qctrl;
620 int id;
621
622 memset(&qctrl, 0, sizeof(qctrl));
623 qctrl.id = next_fl;
624 while (query_ext_ctrl_ioctl(fd, qctrl) == 0) {
625 print_control(fd, qctrl, show_menus);
626 qctrl.id |= next_fl;
627 }
628 if (qctrl.id != next_fl)
629 return;
630 for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
631 qctrl.id = id;
632 if (query_ext_ctrl_ioctl(fd, qctrl) == 0)
633 print_control(fd, qctrl, show_menus);
634 }
635 for (qctrl.id = V4L2_CID_PRIVATE_BASE;
636 query_ext_ctrl_ioctl(fd, qctrl) == 0; qctrl.id++) {
637 print_control(fd, qctrl, show_menus);
638 }
639 }
640
find_controls(cv4l_fd & _fd)641 static void find_controls(cv4l_fd &_fd)
642 {
643 const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
644 struct v4l2_query_ext_ctrl qctrl;
645 int fd = _fd.g_fd();
646 int id;
647
648 memset(&qctrl, 0, sizeof(qctrl));
649 qctrl.id = next_fl;
650 while (query_ext_ctrl_ioctl(fd, qctrl) == 0) {
651 if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS &&
652 !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
653 ctrl_str2q[name2var(qctrl.name)] = qctrl;
654 ctrl_id2str[qctrl.id] = name2var(qctrl.name);
655 }
656 qctrl.id |= next_fl;
657 }
658 if (qctrl.id != next_fl)
659 return;
660 for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
661 qctrl.id = id;
662 if (query_ext_ctrl_ioctl(fd, qctrl) == 0 &&
663 !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
664 ctrl_str2q[name2var(qctrl.name)] = qctrl;
665 ctrl_id2str[qctrl.id] = name2var(qctrl.name);
666 }
667 }
668 for (qctrl.id = V4L2_CID_PRIVATE_BASE;
669 query_ext_ctrl_ioctl(fd, qctrl) == 0; qctrl.id++) {
670 if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
671 ctrl_str2q[name2var(qctrl.name)] = qctrl;
672 ctrl_id2str[qctrl.id] = name2var(qctrl.name);
673 }
674 }
675 }
676
common_find_ctrl_id(const char * name)677 int common_find_ctrl_id(const char *name)
678 {
679 if (ctrl_str2q.find(name) == ctrl_str2q.end())
680 return 0;
681 return ctrl_str2q[name].id;
682 }
683
common_process_controls(cv4l_fd & fd)684 void common_process_controls(cv4l_fd &fd)
685 {
686 struct v4l2_query_ext_ctrl qc = {
687 V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND
688 };
689 int rc;
690
691 rc = test_ioctl(fd.g_fd(), VIDIOC_QUERY_EXT_CTRL, &qc);
692 have_query_ext_ctrl = rc == 0;
693
694 find_controls(fd);
695 for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) {
696 if (ctrl_str2q.find(*iter) == ctrl_str2q.end()) {
697 fprintf(stderr, "unknown control '%s'\n", iter->c_str());
698 std::exit(EXIT_FAILURE);
699 }
700 }
701 for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) {
702 if (ctrl_str2q.find(iter->first) == ctrl_str2q.end()) {
703 fprintf(stderr, "unknown control '%s'\n", iter->first.c_str());
704 std::exit(EXIT_FAILURE);
705 }
706 }
707 }
708
common_control_event(const struct v4l2_event * ev)709 void common_control_event(const struct v4l2_event *ev)
710 {
711 const struct v4l2_event_ctrl *ctrl;
712
713 ctrl = &ev->u.ctrl;
714 printf("ctrl: %s\n", ctrl_id2str[ev->id].c_str());
715 if (ctrl->changes & V4L2_EVENT_CTRL_CH_VALUE) {
716 if (ctrl->type == V4L2_CTRL_TYPE_INTEGER64)
717 printf("\tvalue: %lld 0x%llx\n", ctrl->value64, ctrl->value64);
718 else if (ctrl->type == V4L2_CTRL_TYPE_BITMASK)
719 printf("\tvalue: %u 0x%08x\n", ctrl->value, ctrl->value);
720 else
721 printf("\tvalue: %d 0x%x\n", ctrl->value, ctrl->value);
722 }
723 if (ctrl->changes & V4L2_EVENT_CTRL_CH_FLAGS)
724 printf("\tflags: %s\n", ctrlflags2s(ctrl->flags).c_str());
725 if (ctrl->changes & V4L2_EVENT_CTRL_CH_RANGE) {
726 if (ctrl->type == V4L2_CTRL_TYPE_BITMASK)
727 printf("\trange: max=0x%08x default=0x%08x\n",
728 ctrl->maximum, ctrl->default_value);
729 else
730 printf("\trange: min=%d max=%d step=%d default=%d\n",
731 ctrl->minimum, ctrl->maximum, ctrl->step, ctrl->default_value);
732 }
733 }
734
parse_subset(char * optarg)735 static bool parse_subset(char *optarg)
736 {
737 struct ctrl_subset subset;
738 std::string ctrl_name;
739 unsigned idx = 0;
740 char *p;
741
742 memset(&subset, 0, sizeof(subset));
743 while (*optarg) {
744 p = std::strchr(optarg, ',');
745 if (p)
746 *p = 0;
747 if (optarg[0] == 0) {
748 fprintf(stderr, "empty string\n");
749 return true;
750 }
751 if (idx == 0) {
752 ctrl_name = optarg;
753 } else {
754 if (idx > V4L2_CTRL_MAX_DIMS * 2) {
755 fprintf(stderr, "too many dimensions\n");
756 return true;
757 }
758 if (idx & 1)
759 subset.offset[idx / 2] = strtoul(optarg, 0, 0);
760 else {
761 subset.size[idx / 2 - 1] = strtoul(optarg, 0, 0);
762 if (subset.size[idx / 2 - 1] == 0) {
763 fprintf(stderr, "<size> cannot be 0\n");
764 return true;
765 }
766 }
767 }
768 idx++;
769 if (p == NULL)
770 break;
771 optarg = p + 1;
772 }
773 if (idx == 1) {
774 fprintf(stderr, "no <offset>, <size> tuples given\n");
775 return true;
776 }
777 if ((idx & 1) == 0) {
778 fprintf(stderr, "<offset> without <size>\n");
779 return true;
780 }
781 ctrl_subsets[ctrl_name] = subset;
782
783 return false;
784 }
785
parse_next_subopt(char ** subs,char ** value)786 static bool parse_next_subopt(char **subs, char **value)
787 {
788 static char *const subopts[] = {
789 NULL
790 };
791 int opt = getsubopt(subs, subopts, value);
792
793 if (opt < 0 || *value)
794 return false;
795 fprintf(stderr, "Missing suboption value\n");
796 return true;
797 }
798
common_cmd(const std::string & media_bus_info,int ch,char * optarg)799 void common_cmd(const std::string &media_bus_info, int ch, char *optarg)
800 {
801 char *value, *subs;
802
803 switch (ch) {
804 case OptGetCtrl:
805 subs = optarg;
806 while (*subs != '\0') {
807 if (parse_next_subopt(&subs, &value)) {
808 common_usage();
809 std::exit(EXIT_FAILURE);
810 }
811 if (std::strchr(value, '=')) {
812 common_usage();
813 std::exit(EXIT_FAILURE);
814 }
815 else {
816 get_ctrls.push_back(value);
817 }
818 }
819 break;
820 case OptSetCtrl:
821 subs = optarg;
822 while (*subs != '\0') {
823 if (parse_next_subopt(&subs, &value)) {
824 common_usage();
825 std::exit(EXIT_FAILURE);
826 }
827 if (const char *equal = std::strchr(value, '=')) {
828 set_ctrls[std::string(value, (equal - value))] = equal + 1;
829 }
830 else {
831 fprintf(stderr, "control '%s' without '='\n", value);
832 std::exit(EXIT_FAILURE);
833 }
834 }
835 break;
836 case OptSubset:
837 if (parse_subset(optarg)) {
838 common_usage();
839 std::exit(EXIT_FAILURE);
840 }
841 break;
842 case OptSetPriority:
843 prio = static_cast<enum v4l2_priority>(strtoul(optarg, 0L, 0));
844 break;
845 case OptListDevices:
846 if (media_bus_info.empty())
847 list_devices();
848 else
849 list_media_devices(media_bus_info);
850 break;
851 }
852 }
853
fill_subset(const struct v4l2_query_ext_ctrl & qc,ctrl_subset & subset)854 static bool fill_subset(const struct v4l2_query_ext_ctrl &qc, ctrl_subset &subset)
855 {
856 unsigned d;
857
858 if (qc.nr_of_dims == 0)
859 return false;
860
861 for (d = 0; d < qc.nr_of_dims; d++) {
862 subset.offset[d] = 0;
863 subset.size[d] = qc.dims[d];
864 }
865
866 std::string s = name2var(qc.name);
867
868 if (ctrl_subsets.find(s) != ctrl_subsets.end()) {
869 unsigned ss_dims;
870
871 subset = ctrl_subsets[s];
872 for (ss_dims = 0; ss_dims < V4L2_CTRL_MAX_DIMS && subset.size[ss_dims]; ss_dims++) ;
873 if (ss_dims != qc.nr_of_dims) {
874 fprintf(stderr, "expected %d dimensions but --subset specified %d\n",
875 qc.nr_of_dims, ss_dims);
876 return true;
877 }
878 for (d = 0; d < qc.nr_of_dims; d++) {
879 if (subset.offset[d] + subset.size[d] > qc.dims[d]) {
880 fprintf(stderr, "the subset offset+size for dimension %d is out of range\n", d);
881 return true;
882 }
883 }
884 }
885 return false;
886 }
887
idx_in_subset(const struct v4l2_query_ext_ctrl & qc,const ctrl_subset & subset,const unsigned * divide,unsigned idx)888 static bool idx_in_subset(const struct v4l2_query_ext_ctrl &qc, const ctrl_subset &subset,
889 const unsigned *divide, unsigned idx)
890 {
891 for (unsigned d = 0; d < qc.nr_of_dims; d++) {
892 unsigned i = (idx / divide[d]) % qc.dims[d];
893
894 if (i < subset.offset[d] || i >= subset.offset[d] + subset.size[d])
895 return false;
896 }
897 return true;
898 }
899
common_set(cv4l_fd & _fd)900 void common_set(cv4l_fd &_fd)
901 {
902 int fd = _fd.g_fd();
903
904 if (options[OptSetPriority]) {
905 if (doioctl(fd, VIDIOC_S_PRIORITY, &prio) >= 0) {
906 printf("Priority set: %d\n", prio);
907 }
908 }
909
910 if (options[OptSetCtrl] && !set_ctrls.empty()) {
911 struct v4l2_ext_controls ctrls;
912 class2ctrls_map class2ctrls;
913 bool use_ext_ctrls = false;
914
915 memset(&ctrls, 0, sizeof(ctrls));
916 for (ctrl_set_map::iterator iter = set_ctrls.begin();
917 iter != set_ctrls.end(); ++iter) {
918 struct v4l2_ext_control ctrl;
919 struct v4l2_query_ext_ctrl &qc = ctrl_str2q[iter->first];
920
921 memset(&ctrl, 0, sizeof(ctrl));
922 ctrl.id = qc.id;
923 if (qc.type == V4L2_CTRL_TYPE_INTEGER64 ||
924 qc.flags & V4L2_CTRL_FLAG_UPDATE)
925 use_ext_ctrls = true;
926 if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
927 struct v4l2_ext_controls ctrls = { { 0 }, 1 };
928 unsigned divide[V4L2_CTRL_MAX_DIMS] = { 0 };
929 ctrl_subset subset;
930 long long v;
931 unsigned d, i;
932
933 use_ext_ctrls = true;
934 ctrl.size = qc.elems * qc.elem_size;
935 ctrl.ptr = malloc(ctrl.size);
936
937 ctrls.controls = &ctrl;
938 ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls);
939
940 if (fill_subset(qc, subset))
941 return;
942
943 if (qc.nr_of_dims) {
944 divide[qc.nr_of_dims - 1] = 1;
945 for (d = 0; d < qc.nr_of_dims - 1; d++) {
946 divide[d] = qc.dims[d + 1];
947 for (i = 0; i < d; i++)
948 divide[i] *= divide[d];
949 }
950 }
951
952
953 switch (qc.type) {
954 case V4L2_CTRL_TYPE_U8:
955 v = strtoul(iter->second.c_str(), NULL, 0);
956 for (i = 0; i < qc.elems; i++)
957 if (idx_in_subset(qc, subset, divide, i))
958 ctrl.p_u8[i] = v;
959 break;
960 case V4L2_CTRL_TYPE_U16:
961 v = strtoul(iter->second.c_str(), NULL, 0);
962 for (i = 0; i < qc.elems; i++)
963 if (idx_in_subset(qc, subset, divide, i))
964 ctrl.p_u16[i] = v;
965 break;
966 case V4L2_CTRL_TYPE_U32:
967 v = strtoul(iter->second.c_str(), NULL, 0);
968 for (i = 0; i < qc.elems; i++)
969 if (idx_in_subset(qc, subset, divide, i))
970 ctrl.p_u32[i] = v;
971 break;
972 case V4L2_CTRL_TYPE_STRING:
973 strncpy(ctrl.string, iter->second.c_str(), qc.maximum);
974 ctrl.string[qc.maximum] = 0;
975 break;
976 case V4L2_CTRL_TYPE_AREA:
977 sscanf(iter->second.c_str(), "%ux%u",
978 &ctrl.p_area->width, &ctrl.p_area->height);
979 break;
980 default:
981 fprintf(stderr, "%s: unsupported payload type\n",
982 qc.name);
983 break;
984 }
985 } else {
986 if (V4L2_CTRL_DRIVER_PRIV(ctrl.id))
987 use_ext_ctrls = true;
988 ctrl.value = strtol(iter->second.c_str(), NULL, 0);
989 }
990 class2ctrls[V4L2_CTRL_ID2WHICH(ctrl.id)].push_back(ctrl);
991 }
992 for (class2ctrls_map::iterator iter = class2ctrls.begin();
993 iter != class2ctrls.end(); ++iter) {
994 if (!use_ext_ctrls &&
995 (iter->first == V4L2_CTRL_CLASS_USER ||
996 iter->first == V4L2_CID_PRIVATE_BASE)) {
997 for (unsigned i = 0; i < iter->second.size(); i++) {
998 struct v4l2_control ctrl;
999
1000 ctrl.id = iter->second[i].id;
1001 ctrl.value = iter->second[i].value;
1002 if (doioctl(fd, VIDIOC_S_CTRL, &ctrl)) {
1003 fprintf(stderr, "%s: %s\n",
1004 ctrl_id2str[ctrl.id].c_str(),
1005 strerror(errno));
1006 }
1007 }
1008 continue;
1009 }
1010 if (!iter->second.empty()) {
1011 ctrls.which = iter->first;
1012 ctrls.count = iter->second.size();
1013 ctrls.controls = &iter->second[0];
1014 if (doioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls)) {
1015 if (ctrls.error_idx >= ctrls.count) {
1016 fprintf(stderr, "Error setting controls: %s\n",
1017 strerror(errno));
1018 }
1019 else {
1020 fprintf(stderr, "%s: %s\n",
1021 ctrl_id2str[iter->second[ctrls.error_idx].id].c_str(),
1022 strerror(errno));
1023 }
1024 }
1025 }
1026 }
1027 }
1028 }
1029
print_array(const struct v4l2_query_ext_ctrl & qc,void * p)1030 static void print_array(const struct v4l2_query_ext_ctrl &qc, void *p)
1031 {
1032 ctrl_subset subset;
1033 unsigned divide[V4L2_CTRL_MAX_DIMS] = { 0 };
1034 unsigned from, to;
1035 unsigned d, i;
1036
1037 if (fill_subset(qc, subset))
1038 return;
1039
1040 divide[qc.nr_of_dims - 1] = 1;
1041 for (d = 0; d < qc.nr_of_dims - 1; d++) {
1042 divide[d] = qc.dims[d + 1];
1043 for (i = 0; i < d; i++)
1044 divide[i] *= divide[d];
1045 }
1046
1047 from = subset.offset[qc.nr_of_dims - 1];
1048 to = subset.offset[qc.nr_of_dims - 1] + subset.size[qc.nr_of_dims - 1] - 1;
1049
1050 for (unsigned idx = 0; idx < qc.elems; idx += qc.dims[qc.nr_of_dims - 1]) {
1051 for (d = 0; d < qc.nr_of_dims - 1; d++) {
1052 unsigned i = (idx / divide[d]) % qc.dims[d];
1053
1054 if (i < subset.offset[d] || i >= subset.offset[d] + subset.size[d])
1055 break;
1056 }
1057 if (d < qc.nr_of_dims - 1)
1058 continue;
1059
1060 printf("%s", qc.name);
1061 for (d = 0; d < qc.nr_of_dims - 1; d++)
1062 printf("[%u]", (idx / divide[d]) % qc.dims[d]);
1063 printf(": ");
1064 switch (qc.type) {
1065 case V4L2_CTRL_TYPE_U8:
1066 for (i = from; i <= to; i++) {
1067 printf("%4d", (static_cast<uint8_t *>(p))[idx + i]);
1068 if (i < to)
1069 printf(", ");
1070 }
1071 printf("\n");
1072 break;
1073 case V4L2_CTRL_TYPE_U16:
1074 for (i = from; i <= to; i++) {
1075 printf("%6d", (static_cast<uint16_t *>(p))[idx + i]);
1076 if (i < to)
1077 printf(", ");
1078 }
1079 printf("\n");
1080 break;
1081 case V4L2_CTRL_TYPE_U32:
1082 for (i = from; i <= to; i++) {
1083 printf("%6d", (static_cast<uint32_t *>(p))[idx + i]);
1084 if (i < to)
1085 printf(", ");
1086 }
1087 printf("\n");
1088 break;
1089 }
1090 }
1091 }
1092
common_get(cv4l_fd & _fd)1093 void common_get(cv4l_fd &_fd)
1094 {
1095 int fd = _fd.g_fd();
1096
1097 if (options[OptGetCtrl] && !get_ctrls.empty()) {
1098 struct v4l2_ext_controls ctrls;
1099 class2ctrls_map class2ctrls;
1100 bool use_ext_ctrls = false;
1101
1102 memset(&ctrls, 0, sizeof(ctrls));
1103 for (ctrl_get_list::iterator iter = get_ctrls.begin();
1104 iter != get_ctrls.end(); ++iter) {
1105 struct v4l2_ext_control ctrl;
1106 struct v4l2_query_ext_ctrl &qc = ctrl_str2q[*iter];
1107
1108 memset(&ctrl, 0, sizeof(ctrl));
1109 ctrl.id = qc.id;
1110 if (qc.type == V4L2_CTRL_TYPE_INTEGER64 ||
1111 qc.flags & V4L2_CTRL_FLAG_UPDATE)
1112 use_ext_ctrls = true;
1113 if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
1114 use_ext_ctrls = true;
1115 ctrl.size = qc.elems * qc.elem_size;
1116 ctrl.ptr = calloc(1, ctrl.size);
1117 }
1118 if (V4L2_CTRL_DRIVER_PRIV(ctrl.id))
1119 use_ext_ctrls = true;
1120 class2ctrls[V4L2_CTRL_ID2WHICH(ctrl.id)].push_back(ctrl);
1121 }
1122 for (class2ctrls_map::iterator iter = class2ctrls.begin();
1123 iter != class2ctrls.end(); ++iter) {
1124 if (!use_ext_ctrls &&
1125 (iter->first == V4L2_CTRL_CLASS_USER ||
1126 iter->first == V4L2_CID_PRIVATE_BASE)) {
1127 for (unsigned i = 0; i < iter->second.size(); i++) {
1128 struct v4l2_control ctrl;
1129
1130 ctrl.id = iter->second[i].id;
1131 doioctl(fd, VIDIOC_G_CTRL, &ctrl);
1132 printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value);
1133 }
1134 continue;
1135 }
1136 if (!iter->second.empty()) {
1137 ctrls.which = iter->first;
1138 ctrls.count = iter->second.size();
1139 ctrls.controls = &iter->second[0];
1140 doioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls);
1141 for (unsigned i = 0; i < iter->second.size(); i++) {
1142 struct v4l2_ext_control ctrl = iter->second[i];
1143 std::string &name = ctrl_id2str[ctrl.id];
1144 struct v4l2_query_ext_ctrl &qc = ctrl_str2q[name];
1145
1146 if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
1147 switch (qc.type) {
1148 case V4L2_CTRL_TYPE_U8:
1149 case V4L2_CTRL_TYPE_U16:
1150 case V4L2_CTRL_TYPE_U32:
1151 print_array(qc, ctrl.ptr);
1152 break;
1153 case V4L2_CTRL_TYPE_STRING:
1154 printf("%s: '%s'\n", name.c_str(),
1155 safename(ctrl.string).c_str());
1156 break;
1157 case V4L2_CTRL_TYPE_AREA:
1158 printf("%s: %dx%d\n", name.c_str(),
1159 ctrl.p_area->width,
1160 ctrl.p_area->height);
1161 break;
1162 default:
1163 fprintf(stderr, "%s: unsupported payload type\n",
1164 qc.name);
1165 break;
1166 }
1167 } else
1168 printf("%s: %d\n", name.c_str(), ctrl.value);
1169 }
1170 }
1171 }
1172 }
1173
1174 if (options[OptGetPriority]) {
1175 if (doioctl(fd, VIDIOC_G_PRIORITY, &prio) == 0)
1176 printf("Priority: %d\n", prio);
1177 }
1178
1179 if (options[OptLogStatus]) {
1180 static char buf[40960];
1181 int len = -1;
1182
1183 if (doioctl(fd, VIDIOC_LOG_STATUS, NULL) == 0) {
1184 printf("\nStatus Log:\n\n");
1185 #ifdef HAVE_KLOGCTL
1186 len = klogctl(3, buf, sizeof(buf) - 1);
1187 #endif
1188 if (len >= 0) {
1189 char *p = buf;
1190 char *q;
1191
1192 buf[len] = 0;
1193 while ((q = strstr(p, "START STATUS"))) {
1194 p = q + 1;
1195 }
1196 if (p) {
1197 while (p > buf && *p != '<') p--;
1198 q = p;
1199 while ((q = strstr(q, "<6>"))) {
1200 memcpy(q, " ", 3);
1201 }
1202 printf("%s", p);
1203 }
1204 }
1205 }
1206 }
1207 }
1208
common_list(cv4l_fd & fd)1209 void common_list(cv4l_fd &fd)
1210 {
1211 if (options[OptListCtrls] || options[OptListCtrlsMenus]) {
1212 list_controls(fd.g_fd(), options[OptListCtrlsMenus]);
1213 }
1214 }
1215