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