1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3  * rds-ctl.cpp is based on v4l2-ctl.cpp
4  *
5  * the following applies for all RDS related parts:
6  * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
7  * Author: Konke Radlow <koradlow@gmail.com>
8  */
9 
10 #include <unistd.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <wchar.h>
15 #include <locale.h>
16 #include <inttypes.h>
17 #include <getopt.h>
18 #include <sys/types.h>
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <sys/ioctl.h>
22 #include <sys/time.h>
23 #include <dirent.h>
24 #include <config.h>
25 #include <signal.h>
26 
27 #include <linux/videodev2.h>
28 #include <libv4l2rds.h>
29 
30 #include <cctype>
31 #include <cmath>
32 #include <ctime>
33 #include <list>
34 #include <vector>
35 #include <map>
36 #include <string>
37 #include <algorithm>
38 
39 #define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0])))
40 
41 typedef std::vector<std::string> dev_vec;
42 typedef std::map<std::string, std::string> dev_map;
43 
44 /* Short option list
45 
46    Please keep in alphabetical order.
47    That makes it easier to see which short options are still free.
48 
49    In general the lower case is used to set something and the upper
50    case is used to retrieve a setting. */
51 enum Option {
52 	OptRBDS = 'b',
53 	OptSetDevice = 'd',
54 	OptGetDriverInfo = 'D',
55 	OptGetFreq = 'F',
56 	OptSetFreq = 'f',
57 	OptHelp = 'h',
58 	OptReadRds = 'R',
59 	OptGetTuner = 'T',
60 	OptAll = 128,
61 	OptFreqSeek,
62 	OptListDevices,
63 	OptListFreqBands,
64 	OptOpenFile,
65 	OptPrintBlock,
66 	OptSilent,
67 	OptTMC,
68 	OptTunerIndex,
69 	OptVerbose,
70 	OptWaitLimit,
71 	OptLast = 256
72 };
73 
74 struct ctl_parameters {
75 	bool terminate_decoding;
76 	char options[OptLast];
77 	char fd_name[80];
78 	bool filemode_active;
79 	double freq;
80 	uint32_t wait_limit;
81 	uint8_t tuner_index;
82 	struct v4l2_hw_freq_seek freq_seek;
83 };
84 
85 static struct ctl_parameters params;
86 static int app_result;
87 
88 static struct option long_options[] = {
89 	{"all", no_argument, 0, OptAll},
90 	{"rbds", no_argument, 0, OptRBDS},
91 	{"device", required_argument, 0, OptSetDevice},
92 	{"file", required_argument, 0, OptOpenFile},
93 	{"freq-seek", required_argument, 0, OptFreqSeek},
94 	{"get-freq", no_argument, 0, OptGetFreq},
95 	{"get-tuner", no_argument, 0, OptGetTuner},
96 	{"help", no_argument, 0, OptHelp},
97 	{"info", no_argument, 0, OptGetDriverInfo},
98 	{"list-devices", no_argument, 0, OptListDevices},
99 	{"list-freq-bands", no_argument, 0, OptListFreqBands},
100 	{"print-block", no_argument, 0, OptPrintBlock},
101 	{"read-rds", no_argument, 0, OptReadRds},
102 	{"set-freq", required_argument, 0, OptSetFreq},
103 	{"tmc", no_argument, 0, OptTMC},
104 	{"tuner-index", required_argument, 0, OptTunerIndex},
105 	{"silent", no_argument, 0, OptSilent},
106 	{"verbose", no_argument, 0, OptVerbose},
107 	{"wait-limit", required_argument, 0, OptWaitLimit},
108 	{0, 0, 0, 0}
109 };
110 
usage_hint()111 static void usage_hint()
112 {
113 	fprintf(stderr, "Try 'rds-ctl --help' for more information.\n");
114 }
115 
usage_common()116 static void usage_common()
117 {
118 	printf("\nGeneral/Common options:\n"
119 	       "  --all              display all device information available\n"
120 	       "  -D, --info         show driver info [VIDIOC_QUERYCAP]\n"
121 	       "  -d, --device <dev> use device <dev>\n"
122 	       "                     If <dev> starts with a digit, then /dev/radio<dev> is used\n"
123 	       "                     default: checks for RDS-capable devices,\n"
124 	       "                     uses device with lowest ID\n"
125 	       "  -h, --help         display this help message\n"
126 	       "  --list-devices     list all v4l radio devices with RDS capabilities\n"
127 	       );
128 }
129 
usage_tuner()130 static void usage_tuner()
131 {
132 	printf("\nTuner/Modulator options:\n"
133 	       "  -F, --get-freq     query the frequency [VIDIOC_G_FREQUENCY]\n"
134 	       "  -f, --set-freq <freq>\n"
135 	       "                     set the frequency to <freq> MHz [VIDIOC_S_FREQUENCY]\n"
136 	       "  -T, --get-tuner    query the tuner settings [VIDIOC_G_TUNER]\n"
137 	       "  --tuner-index <idx> Use idx as tuner idx for tuner/modulator commands\n"
138 	       "  --freq-seek dir=<0/1>,wrap=<0/1>,spacing=<hz>\n"
139 	       "                     perform a hardware frequency seek [VIDIOC_S_HW_FREQ_SEEK]\n"
140 	       "                     dir is 0 (seek downward) or 1 (seek upward)\n"
141 	       "                     wrap is 0 (do not wrap around) or 1 (wrap around)\n"
142 	       "                     spacing sets the seek resolution (use 0 for default)\n"
143 	       "  --list-freq-bands  display all frequency bands for the tuner/modulator\n"
144 	       "                     [VIDIOC_ENUM_FREQ_BANDS]\n"
145 	       );
146 }
147 
usage_rds()148 static void usage_rds()
149 {
150 	printf("\nRDS options: \n"
151 	       "  -b, --rbds         parse the RDS data according to the RBDS standard\n"
152 	       "  -R, --read-rds     enable reading of RDS data from device\n"
153 	       "  --file <path>      open a RDS stream file dump instead of a device\n"
154 	       "                     all General and Tuner Options are disabled in this mode\n"
155 	       "  --wait-limit <ms>  defines the maximum wait duration for avaibility of new\n"
156 	       "                     RDS data\n"
157 	       "                     <default>: 5000 ms\n"
158 	       "  --print-block      prints all valid RDS fields, whenever a value is updated\n"
159 	       "                     instead of printing only updated values\n"
160 	       "  --tmc              print information about TMC (Traffic Message Channel) messages\n"
161 	       "  --silent           only set the result code, do not print any messages\n"
162 	       "  --verbose          turn on verbose mode - every received RDS group\n"
163 	       "                     will be printed\n"
164 	       );
165 }
166 
usage()167 static void usage()
168 {
169 	printf("Usage:\n");
170 	usage_common();
171 	usage_tuner();
172 	usage_rds();
173 }
174 
signal_handler_interrupt(int signum)175 static void signal_handler_interrupt(int signum)
176 {
177 	fprintf(stderr, "Interrupt received: Terminating program\n");
178 	params.terminate_decoding = true;
179 }
180 
doioctl_name(int fd,unsigned long int request,void * parm,const char * name)181 static int doioctl_name(int fd, unsigned long int request, void *parm, const char *name)
182 {
183 	int retval = ioctl(fd, request, parm);
184 
185 	if (retval < 0) {
186 		app_result = -1;
187 	}
188 	if (params.options[OptSilent]) return retval;
189 	if (retval < 0)
190 		printf("%s: failed: %s\n", name, strerror(errno));
191 	else if (params.options[OptVerbose])
192 		printf("%s: ok\n", name);
193 
194 	return retval;
195 }
196 
197 #define doioctl(n, r, p) doioctl_name(n, r, p, #r)
198 
audmode2s(int audmode)199 static const char *audmode2s(int audmode)
200 {
201 	switch (audmode) {
202 		case V4L2_TUNER_MODE_STEREO: return "stereo";
203 		case V4L2_TUNER_MODE_LANG1: return "lang1";
204 		case V4L2_TUNER_MODE_LANG2: return "lang2";
205 		case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual";
206 		case V4L2_TUNER_MODE_MONO: return "mono";
207 		default: return "unknown";
208 	}
209 }
210 
rxsubchans2s(int rxsubchans)211 static std::string rxsubchans2s(int rxsubchans)
212 {
213 	std::string s;
214 
215 	if (rxsubchans & V4L2_TUNER_SUB_MONO)
216 		s += "mono ";
217 	if (rxsubchans & V4L2_TUNER_SUB_STEREO)
218 		s += "stereo ";
219 	if (rxsubchans & V4L2_TUNER_SUB_LANG1)
220 		s += "lang1 ";
221 	if (rxsubchans & V4L2_TUNER_SUB_LANG2)
222 		s += "lang2 ";
223 	if (rxsubchans & V4L2_TUNER_SUB_RDS)
224 		s += "rds ";
225 	return s;
226 }
227 
tcap2s(unsigned cap)228 static std::string tcap2s(unsigned cap)
229 {
230 	std::string s;
231 
232 	if (cap & V4L2_TUNER_CAP_LOW)
233 		s += "62.5 Hz ";
234 	else
235 		s += "62.5 kHz ";
236 	if (cap & V4L2_TUNER_CAP_NORM)
237 		s += "multi-standard ";
238 	if (cap & V4L2_TUNER_CAP_HWSEEK_BOUNDED)
239 		s += "hwseek-bounded ";
240 	if (cap & V4L2_TUNER_CAP_HWSEEK_WRAP)
241 		s += "hwseek-wrap ";
242 	if (cap & V4L2_TUNER_CAP_STEREO)
243 		s += "stereo ";
244 	if (cap & V4L2_TUNER_CAP_LANG1)
245 		s += "lang1 ";
246 	if (cap & V4L2_TUNER_CAP_LANG2)
247 		s += "lang2 ";
248 	if (cap & V4L2_TUNER_CAP_RDS)
249 		s += "rds ";
250 	if (cap & V4L2_TUNER_CAP_RDS_BLOCK_IO)
251 		s += "rds-block-I/O ";
252 	if (cap & V4L2_TUNER_CAP_RDS_CONTROLS)
253 		s += "rds-controls ";
254 	if (cap & V4L2_TUNER_CAP_FREQ_BANDS)
255 		s += "freq-bands ";
256 	if (cap & V4L2_TUNER_CAP_HWSEEK_PROG_LIM)
257 		s += "hwseek-prog-lim ";
258 	return s;
259 }
260 
cap2s(unsigned cap)261 static std::string cap2s(unsigned cap)
262 {
263 	std::string s;
264 
265 	if (cap & V4L2_CAP_RDS_CAPTURE)
266 		s += "\t\tRDS Capture\n";
267 	if (cap & V4L2_CAP_RDS_OUTPUT)
268 		s += "\t\tRDS Output\n";
269 	if (cap & V4L2_CAP_TUNER)
270 		s += "\t\tTuner\n";
271 	if (cap & V4L2_CAP_MODULATOR)
272 		s += "\t\tModulator\n";
273 	if (cap & V4L2_CAP_RADIO)
274 		s += "\t\tRadio\n";
275 	if (cap & V4L2_CAP_READWRITE)
276 		s += "\t\tRead/Write\n";
277 	if (cap & V4L2_CAP_STREAMING)
278 		s += "\t\tStreaming\n";
279 	if (cap & V4L2_CAP_DEVICE_CAPS)
280 		s += "\t\tDevice Capabilities\n";
281 	return s;
282 }
283 
modulation2s(unsigned modulation)284 static std::string modulation2s(unsigned modulation)
285 {
286 	switch (modulation) {
287 	case V4L2_BAND_MODULATION_VSB:
288 		return "VSB";
289 	case V4L2_BAND_MODULATION_FM:
290 		return "FM";
291 	case V4L2_BAND_MODULATION_AM:
292 		return "AM";
293 	}
294 	return "Unknown";
295 }
296 
is_radio_dev(const char * name)297 static bool is_radio_dev(const char *name)
298 {
299 	return !memcmp(name, "radio", 5);
300 }
301 
print_devices(dev_vec files)302 static void print_devices(dev_vec files)
303 {
304 	dev_map cards;
305 	int fd = -1;
306 	std::string bus_info;
307 	struct v4l2_capability vcap;
308 
309 	for (dev_vec::iterator iter = files.begin();
310 		iter != files.end(); ++iter) {
311 		fd = open(iter->c_str(), O_RDWR);
312 		memset(&vcap, 0, sizeof(vcap));
313 		if (fd < 0)
314 			continue;
315 		doioctl(fd, VIDIOC_QUERYCAP, &vcap);
316 		close(fd);
317 		bus_info = reinterpret_cast<const char *>(vcap.bus_info);
318 	if (cards[bus_info].empty())
319 			cards[bus_info] += std::string(reinterpret_cast<char *>(vcap.card))
320 				+ " (" + bus_info + "):\n";
321 		cards[bus_info] += "\t" + (*iter);
322 		cards[bus_info] += "\n";
323 	}
324 	for (dev_map::iterator iter = cards.begin();
325 			iter != cards.end(); ++iter) {
326 		printf("%s\n", iter->second.c_str());
327 	}
328 }
list_devices()329 static dev_vec list_devices()
330 {
331 	DIR *dp;
332 	struct dirent *ep;
333 	dev_vec files;
334 	dev_vec valid_devices;
335 	dev_map links;
336 
337 	struct v4l2_tuner vt;
338 
339 	dp = opendir("/dev");
340 	if (dp == NULL) {
341 		perror ("Couldn't open the directory");
342 		std::exit(EXIT_FAILURE);
343 	}
344 	while ((ep = readdir(dp)))
345 		if (is_radio_dev(ep->d_name))
346 			files.push_back(std::string("/dev/") + ep->d_name);
347 	closedir(dp);
348 
349 	/* Iterate through all devices, and remove all non-accessible devices
350 	 * and all devices that don't offer the RDS_BLOCK_IO capability */
351 	for (dev_vec::iterator iter = files.begin();
352 			iter != files.end();) {
353 		int fd = open(iter->c_str(), O_RDONLY | O_NONBLOCK);
354 		std::string bus_info;
355 
356 		if (fd < 0) {
357 			iter++;
358 			continue;
359 		}
360 		memset(&vt, 0, sizeof(vt));
361 		if (ioctl(fd, VIDIOC_G_TUNER, &vt) != 0) {
362 			close(fd);
363 			iter++;
364 			continue;
365 		}
366 		/* remove device if it doesn't support rds block I/O */
367 		if (vt.capability & V4L2_TUNER_CAP_RDS_BLOCK_IO)
368 			valid_devices.push_back(*iter++);
369 		else
370 			iter = files.erase(iter);
371 		close(fd);
372 	}
373 	return valid_devices;
374 }
375 
parse_subopt(char ** subs,const char * const * subopts,char ** value)376 static int parse_subopt(char **subs, const char * const *subopts, char **value)
377 {
378 	int opt = getsubopt(subs, const_cast<char * const *>(subopts), value);
379 
380 	if (opt == -1) {
381 		fprintf(stderr, "Invalid suboptions specified\n");
382 		return -1;
383 	}
384 	if (*value == NULL) {
385 		fprintf(stderr, "No value given to suboption <%s>\n",
386 				subopts[opt]);
387 		return -1;
388 	}
389 	return opt;
390 }
391 
parse_freq_seek(char * optarg,struct v4l2_hw_freq_seek & seek)392 static void parse_freq_seek(char *optarg, struct v4l2_hw_freq_seek &seek)
393 {
394 	char *value;
395 	char *subs = optarg;
396 
397 	while (*subs != '\0') {
398 		static const char *const subopts[] = {
399 			"dir",
400 			"wrap",
401 			"spacing",
402 			NULL
403 		};
404 
405 		switch (parse_subopt(&subs, subopts, &value)) {
406 		case 0:
407 			seek.seek_upward = strtol(value, 0L, 0);
408 			break;
409 		case 1:
410 			seek.wrap_around = strtol(value, 0L, 0);
411 			break;
412 		case 2:
413 			seek.spacing = strtol(value, 0L, 0);
414 			break;
415 		default:
416 			usage_tuner();
417 			std::exit(EXIT_FAILURE);
418 		}
419 	}
420 }
421 
print_byte(char byte,bool linebreak)422 static void print_byte(char byte, bool linebreak)
423 {
424 	int count = 8;
425 	printf(" ");
426 	while (count--) {
427 		printf("%d", (byte & 128) ? 1 : 0 );
428 		byte <<= 1;
429 	}
430 	if (linebreak)
431 		printf("\n");
432 }
433 
print_rds_group(const struct v4l2_rds_group * rds_group)434 static void print_rds_group(const struct v4l2_rds_group *rds_group)
435 {
436 	printf("\nComplete RDS data group received \n");
437 	printf("PI: %04x\n", rds_group->pi);
438 	printf("Group: %u%c \n", rds_group->group_id, rds_group->group_version);
439 	printf("Data B:");
440 	print_byte(rds_group->data_b_lsb, true);
441 	printf("Data C:");
442 	print_byte(rds_group->data_c_msb, false);
443 	print_byte(rds_group->data_c_lsb, true);
444 	printf("Data D:");
445 	print_byte(rds_group->data_d_msb, false);
446 	print_byte(rds_group->data_d_lsb, true);
447 	printf("\n");
448 }
449 
print_decoder_info(uint8_t di)450 static void print_decoder_info(uint8_t di)
451 {
452 	printf("\nDI: ");
453 	if (di & V4L2_RDS_FLAG_STEREO)
454 		printf("Stereo, ");
455 	else
456 		printf("Mono, ");
457 	if (di & V4L2_RDS_FLAG_ARTIFICIAL_HEAD)
458 		printf("Artificial Head, ");
459 	else
460 		printf("No Artificial Head, ");
461 	if (di & V4L2_RDS_FLAG_COMPRESSED)
462 		printf("Compressed, ");
463 	else
464 		printf("Not Compressed, ");
465 	if (di & V4L2_RDS_FLAG_DYNAMIC_PTY)
466 		printf("Dynamic PTY");
467 	else
468 		printf("Static PTY");
469 }
470 
print_rds_tmc(const struct v4l2_rds * handle,uint32_t updated_fields)471 static void print_rds_tmc(const struct v4l2_rds *handle, uint32_t updated_fields)
472 {
473 	const struct v4l2_rds_tmc_msg *msg = &handle->tmc.tmc_msg;
474 	const struct v4l2_tmc_additional_set *set = &msg->additional;
475 
476 	if (updated_fields & V4L2_RDS_TMC_SG) {
477 		printf("\nTMC Single-grp: location: %04x, event: %04x, extent: %02x "
478 			"duration: %02x", msg->location, msg->event,
479 			msg->extent, msg->dp);
480 		return;
481 	}
482 	if (updated_fields & V4L2_RDS_TMC_MG) {
483 		printf("\nTMC Multi-grp: length: %02d, location: %04x, event: %04x,\n"
484 		"               extent: %02x duration: %02x", msg->length, msg->location, msg->event,
485 			msg->extent, msg->dp);
486 		for (int i = 0; i < set->size; i++) {
487 			printf("\n               additional[%02d]: label: %02d, value: %04x",
488 			i, set->fields[i].label, set->fields[i].data);
489 		}
490 		return;
491 	}
492 }
493 
print_rds_tmc_tuning(const struct v4l2_rds * handle,uint32_t updated_fields)494 static void print_rds_tmc_tuning(const struct v4l2_rds *handle, uint32_t updated_fields)
495 {
496 	const struct v4l2_tmc_tuning *tuning = &handle->tmc.tuning;
497 	const struct v4l2_tmc_station *station;
498 
499 	if (updated_fields & V4L2_RDS_TMC_TUNING) {
500 		printf("\nTMC Service provider: %s, %u alternative stations\n", handle->tmc.spn, tuning->station_cnt);
501 		for (int i = 0; i < tuning->station_cnt; i++) {
502 			station = &tuning->station[i];
503 			printf("PI(ON %02u) = %04x, AFs: %u, mapped AFs: %u \n", i, station->pi,
504 					station->afi.af_size, station->afi.mapped_af_size);
505 			for (int j = 0; j < station->afi.af_size; j++)
506 				printf("        AF%02d: %.1fMHz\n", j, station->afi.af[j] / 1000000.0);
507 			for (int k = 0; k < station->afi.mapped_af_size; k++)
508 				printf("        m_AF%02d: %.1fMHz => %.1fMHz\n", k,
509 						station->afi.mapped_af_tuning[k] / 1000000.0,
510 						station->afi.mapped_af[k] / 1000000.0);
511 			if (station->ltn != 0 || station->msg != 0 || station-> sid != 0)
512 				printf("        ltn: %02x, msg: %02x, sid: %02x\n", station->ltn,
513 						station->msg, station->sid);
514 		}
515 	}
516 }
517 
print_rds_statistics(const struct v4l2_rds_statistics * statistics)518 static void print_rds_statistics(const struct v4l2_rds_statistics *statistics)
519 {
520 	printf("\n\nRDS Statistics: \n");
521 	printf("received blocks / received groups: %u / %u\n",
522 		statistics->block_cnt, statistics->group_cnt);
523 
524 	double block_error_percentage = 0;
525 
526 	if (statistics->block_cnt)
527 		block_error_percentage =
528 			(static_cast<float>(statistics->block_error_cnt) / statistics->block_cnt) * 100.0;
529 	printf("block errors / group errors: %u (%3.2f%%) / %u \n",
530 		statistics->block_error_cnt,
531 		block_error_percentage, statistics->group_error_cnt);
532 
533 	double block_corrected_percentage = 0;
534 
535 	if (statistics->block_cnt)
536 		block_corrected_percentage = (
537 			static_cast<float>(statistics->block_corrected_cnt) / statistics->block_cnt) * 100.0;
538 	printf("corrected blocks: %u (%3.2f%%)\n",
539 		statistics->block_corrected_cnt, block_corrected_percentage);
540 	for (int i = 0; i < 16; i++)
541 		printf("Group %02d: %u\n", i, statistics->group_type_cnt[i]);
542 }
543 
print_rds_af(const struct v4l2_rds_af_set * af_set)544 static void print_rds_af(const struct v4l2_rds_af_set *af_set)
545 {
546 	int counter = 0;
547 
548 	printf("\nAnnounced AFs: %u", af_set->announced_af);
549 	for (int i = 0; i < af_set->size && i < af_set->announced_af; i++, counter++) {
550 		if (af_set->af[i] >= 87500000 ) {
551 			printf("\nAF%02d: %.1fMHz", counter, af_set->af[i] / 1000000.0);
552 			continue;
553 		}
554 		printf("\nAF%02d: %.3fkHz", counter, af_set->af[i] / 1000.0);
555 	}
556 }
557 
print_rds_eon(const struct v4l2_rds_eon_set * eon_set)558 static void print_rds_eon(const struct v4l2_rds_eon_set *eon_set)
559 {
560 	int counter = 0;
561 
562 	printf("\n\nEnhanced Other Network information: %u channels", eon_set->size);
563 	for (int i = 0; i < eon_set->size; i++, counter++) {
564 		if (eon_set->eon[i].valid_fields & V4L2_RDS_PI)
565 			printf("\nPI(ON %02i) =  %04x", i, eon_set->eon[i].pi);
566 		if (eon_set->eon[i].valid_fields & V4L2_RDS_PS)
567 			printf("\nPS(ON %02i) =  %s", i, eon_set->eon[i].ps);
568 		if (eon_set->eon[i].valid_fields & V4L2_RDS_PTY)
569 			printf("\nPTY(ON %02i) =  %0u", i, eon_set->eon[i].pty);
570 		if (eon_set->eon[i].valid_fields & V4L2_RDS_LSF)
571 			printf("\nLSF(ON %02i) =  %0u", i, eon_set->eon[i].lsf);
572 		if (eon_set->eon[i].valid_fields & V4L2_RDS_AF)
573 			printf("\nPTY(ON %02i) =  %0u", i, eon_set->eon[i].pty);
574 		if (eon_set->eon[i].valid_fields & V4L2_RDS_TP)
575 			printf("\nTP(ON %02i): %s", i, eon_set->eon[i].tp? "yes":"no");
576 		if (eon_set->eon[i].valid_fields & V4L2_RDS_TA)
577 			printf("\nTA(ON %02i): %s", i, eon_set->eon[i].tp? "yes":"no");
578 		if (eon_set->eon[i].valid_fields & V4L2_RDS_AF) {
579 			printf("\nAF(ON %02i): size=%i", i, eon_set->eon[i].af.size);
580 			print_rds_af(&(eon_set->eon[i].af));
581 		}
582 	}
583 }
584 
print_rds_pi(const struct v4l2_rds * handle)585 static void print_rds_pi(const struct v4l2_rds *handle)
586 {
587 	uint16_t pi = handle->pi;
588 
589 	if (handle->is_rbds) {
590 		char callsign[5] = { 0 };
591 		uint8_t nibble = handle->pi >> 12;
592 
593 		if (pi >= 0xafa1 && pi <= 0xafa9)
594 			pi = (pi & 0xf) << 12;
595 		else if (pi > 0xaf11 && pi <= 0xaf1f)
596 			pi = (pi & 0xff) << 8;
597 		else if (pi > 0xaf21 && pi <= 0xafaf)
598 			pi = (pi & 0xff) << 8;
599 
600 		if (pi > 0xa100 && pi <= 0xa9ff)
601 			pi -= 0x9100;
602 		if (pi > 0x1000 && pi <= 0x54a7) {
603 			pi -= 4096;
604 			callsign[0] = 'K';
605 		} else if (pi >= 0x54a8 && pi <= 0x994f) {
606 			pi -= 21672;
607 			callsign[0] = 'W';
608 		}
609 		if (callsign[0]) {
610 			callsign[1] = 'A' + pi / 676;
611 			callsign[2] = 'A' + (pi / 26) % 26;
612 			callsign[3] = 'A' + pi % 26;
613 			printf("\nCall Sign: %s", callsign);
614 		}
615 		if (nibble != 0x0b && nibble != 0x0d && nibble != 0x0e)
616 			return;
617 	}
618 	printf("\nArea Coverage: %s", v4l2_rds_get_coverage_str(handle));
619 }
620 
print_rds_data(const struct v4l2_rds * handle,uint32_t updated_fields)621 static void print_rds_data(const struct v4l2_rds *handle, uint32_t updated_fields)
622 {
623 	if (params.options[OptPrintBlock])
624 		updated_fields = 0xffffffff;
625 
626 	if ((updated_fields & V4L2_RDS_PI) &&
627 			(handle->valid_fields & V4L2_RDS_PI)) {
628 		printf("\nPI: %04x", handle->pi);
629 		print_rds_pi(handle);
630 	}
631 
632 	if (updated_fields & V4L2_RDS_PS &&
633 			handle->valid_fields & V4L2_RDS_PS) {
634 		printf("\nPS: %s", handle->ps);
635 	}
636 
637 	if (updated_fields & V4L2_RDS_PTY && handle->valid_fields & V4L2_RDS_PTY)
638 		printf("\nPTY: %0u -> %s", handle->pty, v4l2_rds_get_pty_str(handle));
639 
640 	if (updated_fields & V4L2_RDS_PTYN && handle->valid_fields & V4L2_RDS_PTYN) {
641 		printf("\nPTYN: %s", handle->ptyn);
642 	}
643 
644 	if (updated_fields & V4L2_RDS_TIME) {
645 		printf("\nTime: %s", ctime(&handle->time));
646 	}
647 	if (updated_fields & V4L2_RDS_RT && handle->valid_fields & V4L2_RDS_RT) {
648 		printf("\nRT: %s", handle->rt);
649 	}
650 
651 	if ((updated_fields & (V4L2_RDS_TP | V4L2_RDS_TA)) &&
652 	    (handle->valid_fields & (V4L2_RDS_TP | V4L2_RDS_TA)))
653 		printf("\nTP: %s  TA: %s", (handle->tp) ? "yes" : "no",
654 			handle->ta ? "yes" : "no");
655 	if (updated_fields & V4L2_RDS_MS && handle->valid_fields & V4L2_RDS_MS)
656 		printf("\nMS Flag: %s", (handle->ms) ? "Music" : "Speech");
657 	if (updated_fields & V4L2_RDS_ECC && handle->valid_fields & V4L2_RDS_ECC)
658 		printf("\nECC: %X%x, Country: %u -> %s",
659 		handle->ecc >> 4, handle->ecc & 0x0f, handle->pi >> 12,
660 		v4l2_rds_get_country_str(handle));
661 	if (updated_fields & V4L2_RDS_LC && handle->valid_fields & V4L2_RDS_LC)
662 		printf("\nLanguage: %u -> %s ", handle->lc,
663 		v4l2_rds_get_language_str(handle));
664 	if (updated_fields & V4L2_RDS_DI && handle->valid_fields & V4L2_RDS_DI)
665 		print_decoder_info(handle->di);
666 	if (updated_fields & V4L2_RDS_ODA &&
667 			handle->decode_information & V4L2_RDS_ODA) {
668 		for (int i = 0; i < handle->rds_oda.size; ++i)
669 			printf("\nODA Group: %02u%c, AID: %08x", handle->rds_oda.oda[i].group_id,
670 			handle->rds_oda.oda[i].group_version, handle->rds_oda.oda[i].aid);
671 	}
672 	if (updated_fields & V4L2_RDS_AF && handle->valid_fields & V4L2_RDS_AF)
673 		print_rds_af(&handle->rds_af);
674 	if (updated_fields & V4L2_RDS_TMC_TUNING && handle->valid_fields & V4L2_RDS_TMC_TUNING)
675 		print_rds_tmc_tuning(handle, updated_fields);
676 	if (params.options[OptPrintBlock])
677 		printf("\n");
678 	if (params.options[OptTMC])
679 		print_rds_tmc(handle, updated_fields);
680 }
681 
read_rds(struct v4l2_rds * handle,const int fd,const int wait_limit)682 static void read_rds(struct v4l2_rds *handle, const int fd, const int wait_limit)
683 {
684 	int byte_cnt = 0;
685 	int error_cnt = 0;
686 	uint32_t updated_fields = 0x00;
687 	struct v4l2_rds_data rds_data; /* read buffer for rds blocks */
688 
689 	while (!params.terminate_decoding) {
690 		memset(&rds_data, 0, sizeof(rds_data));
691 		if ((byte_cnt=read(fd, &rds_data, 3)) != 3) {
692 			if (byte_cnt == 0) {
693 				printf("\nEnd of input file reached \n");
694 				break;
695 			}
696 			if (++error_cnt > 2) {
697 				fprintf(stderr, "\nError reading from "
698 					"device (no RDS data available)\n");
699 				break;
700 			}
701 			/* wait for new data to arrive: transmission of 1
702 			 * group takes ~88.7ms */
703 			usleep(wait_limit * 1000);
704 		}
705 		else if (byte_cnt == 3) {
706 			error_cnt = 0;
707 			/* true if a new group was decoded */
708 			if ((updated_fields = v4l2_rds_add(handle, &rds_data))) {
709 				print_rds_data(handle, updated_fields);
710 				if (params.options[OptVerbose])
711 					 print_rds_group(v4l2_rds_get_group(handle));
712 			}
713 		}
714 	}
715 	/* print a summary of all valid RDS-fields before exiting */
716 	printf("\nSummary of valid RDS-fields:");
717 	print_rds_data(handle, 0xFFFFFFFF);
718 }
719 
read_rds_from_fd(const int fd)720 static void read_rds_from_fd(const int fd)
721 {
722 	struct v4l2_rds *rds_handle;
723 
724 	/* create an rds handle for the current device */
725 	if (!(rds_handle = v4l2_rds_create(params.options[OptRBDS]))) {
726 		fprintf(stderr, "Failed to init RDS lib: %s\n", strerror(errno));
727 		std::exit(EXIT_FAILURE);
728 	}
729 
730 	/* try to receive and decode RDS data */
731 	read_rds(rds_handle, fd, params.wait_limit);
732 	if (rds_handle->valid_fields & V4L2_RDS_EON)
733 		print_rds_eon(&rds_handle->rds_eon);
734 	print_rds_statistics(&rds_handle->rds_statistics);
735 
736 	v4l2_rds_destroy(rds_handle);
737 }
738 
parse_cl(int argc,char ** argv)739 static int parse_cl(int argc, char **argv)
740 {
741 	int i = 0;
742 	int idx = 0;
743 	int opt = 0;
744 	/* 26 letters in the alphabet, case sensitive = 26 * 2 possible
745 	 * short options, where each option requires at most two chars
746 	 * {option, optional argument} */
747 	char short_options[26 * 2 * 2 + 1];
748 
749 	if (argc == 1) {
750 		usage_hint();
751 		std::exit(EXIT_FAILURE);
752 	}
753 	for (i = 0; long_options[i].name; i++) {
754 		if (!isalpha(long_options[i].val))
755 			continue;
756 		short_options[idx++] = long_options[i].val;
757 		if (long_options[i].has_arg == required_argument)
758 			short_options[idx++] = ':';
759 	}
760 	while (true) {
761 		int option_index = 0;
762 
763 		short_options[idx] = 0;
764 		opt = getopt_long(argc, argv, short_options,
765 				 long_options, &option_index);
766 		if (opt == -1)
767 			break;
768 
769 		params.options[opt] = 1;
770 		switch (opt) {
771 		case OptSetDevice:
772 			strncpy(params.fd_name, optarg, sizeof(params.fd_name) - 1);
773 			if (optarg[0] >= '0' && optarg[0] <= '9' && strlen(optarg) <= 3) {
774 				snprintf(params.fd_name, sizeof(params.fd_name), "/dev/radio%s", optarg);
775 			}
776 			params.fd_name[sizeof(params.fd_name) - 1] = '\0';
777 			break;
778 		case OptSetFreq:
779 			params.freq = strtod(optarg, NULL);
780 			break;
781 		case OptListDevices:
782 			print_devices(list_devices());
783 			break;
784 		case OptFreqSeek:
785 			parse_freq_seek(optarg, params.freq_seek);
786 			break;
787 		case OptTunerIndex:
788 			params.tuner_index = strtoul(optarg, NULL, 0);
789 			break;
790 		case OptOpenFile:
791 		{
792 			if (access(optarg, F_OK) != -1) {
793 				params.filemode_active = true;
794 				strncpy(params.fd_name, optarg, sizeof(params.fd_name));
795 				params.fd_name[sizeof(params.fd_name) - 1] = '\0';
796 			} else {
797 				fprintf(stderr, "Unable to open file: %s\n", optarg);
798 				return -1;
799 			}
800 			/* enable the read-rds option by default for convenience */
801 			params.options[OptReadRds] = 1;
802 			break;
803 		}
804 		case OptWaitLimit:
805 			params.wait_limit = strtoul(optarg, NULL, 0);
806 			break;
807 		case ':':
808 			fprintf(stderr, "Option '%s' requires a value\n",
809 				argv[optind]);
810 			usage_hint();
811 			return 1;
812 		case '?':
813 			if (argv[optind])
814 				fprintf(stderr, "Unknown argument '%s'\n", argv[optind]);
815 			usage_hint();
816 			return 1;
817 		}
818 	}
819 	if (optind < argc) {
820 		printf("unknown arguments: ");
821 		while (optind < argc)
822 			printf("%s ", argv[optind++]);
823 		printf("\n");
824 		usage_hint();
825 		return 1;
826 	}
827 	if (params.options[OptAll]) {
828 		params.options[OptGetDriverInfo] = 1;
829 		params.options[OptGetFreq] = 1;
830 		params.options[OptGetTuner] = 1;
831 		params.options[OptSilent] = 1;
832 	}
833 	/* set default value for wait limit, if not specified by user */
834 	if (!params.options[OptWaitLimit])
835 		params.wait_limit = 5000;
836 
837 	return 0;
838 }
839 
print_driver_info(const struct v4l2_capability * vcap)840 static void print_driver_info(const struct v4l2_capability *vcap)
841 {
842 
843 	printf("Driver Info:\n");
844 	printf("\tDriver name   : %s\n", vcap->driver);
845 	printf("\tCard type     : %s\n", vcap->card);
846 	printf("\tBus info      : %s\n", vcap->bus_info);
847 	printf("\tDriver version: %d.%d.%d\n",
848 			vcap->version >> 16,
849 			(vcap->version >> 8) & 0xff,
850 			vcap->version & 0xff);
851 	printf("\tCapabilities  : 0x%08X\n", vcap->capabilities);
852 	printf("%s", cap2s(vcap->capabilities).c_str());
853 	if (vcap->capabilities & V4L2_CAP_DEVICE_CAPS) {
854 		printf("\tDevice Caps   : 0x%08X\n", vcap->device_caps);
855 		printf("%s", cap2s(vcap->device_caps).c_str());
856 	}
857 }
858 
set_options(const int fd,const int capabilities,struct v4l2_frequency * vf,struct v4l2_tuner * tuner)859 static void set_options(const int fd, const int capabilities, struct v4l2_frequency *vf,
860 			struct v4l2_tuner *tuner)
861 {
862 	double fac = 16;		/* factor for frequency division */
863 
864 	if (params.options[OptSetFreq]) {
865 		vf->type = V4L2_TUNER_RADIO;
866 		tuner->index = params.tuner_index;
867 		if (doioctl(fd, VIDIOC_G_TUNER, tuner) == 0) {
868 			fac = (tuner->capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16;
869 			vf->type = tuner->type;
870 		}
871 
872 		vf->tuner = params.tuner_index;
873 		vf->frequency = uint32_t(params.freq * fac);
874 		if (doioctl(fd, VIDIOC_S_FREQUENCY, vf) == 0)
875 			printf("Frequency for tuner %d set to %d (%f MHz)\n",
876 				vf->tuner, vf->frequency, vf->frequency / fac);
877 	}
878 
879 	if (params.options[OptFreqSeek]) {
880 		params.freq_seek.tuner = params.tuner_index;
881 		params.freq_seek.type = V4L2_TUNER_RADIO;
882 		doioctl(fd, VIDIOC_S_HW_FREQ_SEEK, &params.freq_seek);
883 	}
884 }
885 
get_options(const int fd,const int capabilities,struct v4l2_frequency * vf,struct v4l2_tuner * tuner)886 static void get_options(const int fd, const int capabilities, struct v4l2_frequency *vf,
887 			struct v4l2_tuner *tuner)
888 {
889 	double fac = 16;		/* factor for frequency division */
890 
891 	if (params.options[OptGetFreq]) {
892 		vf->type = V4L2_TUNER_RADIO;
893 		tuner->index = params.tuner_index;
894 		if (doioctl(fd, VIDIOC_G_TUNER, tuner) == 0) {
895 			fac = (tuner->capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16;
896 			vf->type = tuner->type;
897 		}
898 		vf->tuner = params.tuner_index;
899 		if (doioctl(fd, VIDIOC_G_FREQUENCY, vf) == 0)
900 			printf("Frequency for tuner %d: %d (%f MHz)\n",
901 				   vf->tuner, vf->frequency, vf->frequency / fac);
902 	}
903 
904 	if (params.options[OptGetTuner]) {
905 		struct v4l2_tuner vt;
906 
907 		memset(&vt, 0, sizeof(struct v4l2_tuner));
908 		vt.index = params.tuner_index;
909 		if (doioctl(fd, VIDIOC_G_TUNER, &vt) == 0) {
910 			printf("Tuner %d:\n", vt.index);
911 			printf("\tName                 : %s\n", vt.name);
912 			printf("\tCapabilities         : %s\n",
913 				tcap2s(vt.capability).c_str());
914 			if (vt.capability & V4L2_TUNER_CAP_LOW)
915 				printf("\tFrequency range      : %.1f MHz - %.1f MHz\n",
916 					 vt.rangelow / 16000.0, vt.rangehigh / 16000.0);
917 			else
918 				printf("\tFrequency range      : %.1f MHz - %.1f MHz\n",
919 					 vt.rangelow / 16.0, vt.rangehigh / 16.0);
920 			printf("\tSignal strength/AFC  : %ld%%/%d\n",
921 				lround(vt.signal / 655.25), vt.afc);
922 			printf("\tCurrent audio mode   : %s\n", audmode2s(vt.audmode));
923 			printf("\tAvailable subchannels: %s\n",
924 					rxsubchans2s(vt.rxsubchans).c_str());
925 		}
926 	}
927 
928 	if (params.options[OptListFreqBands]) {
929 		struct v4l2_frequency_band band;
930 
931 		memset(&band, 0, sizeof(band));
932 		band.tuner = params.tuner_index;
933 		band.type = V4L2_TUNER_RADIO;
934 		band.index = 0;
935 		printf("ioctl: VIDIOC_ENUM_FREQ_BANDS\n");
936 		while (ioctl(fd, VIDIOC_ENUM_FREQ_BANDS, &band) >= 0) {
937 			if (band.index)
938 				printf("\n");
939 			printf("\tIndex          : %d\n", band.index);
940 			printf("\tModulation     : %s\n", modulation2s(band.modulation).c_str());
941 			printf("\tCapability     : %s\n", tcap2s(band.capability).c_str());
942 			if (band.capability & V4L2_TUNER_CAP_LOW)
943 				printf("\tFrequency Range: %.3f MHz - %.3f MHz\n",
944 				     band.rangelow / 16000.0, band.rangehigh / 16000.0);
945 			else
946 				printf("\tFrequency Range: %.3f MHz - %.3f MHz\n",
947 				     band.rangelow / 16.0, band.rangehigh / 16.0);
948 			band.index++;
949 		}
950 	}
951 }
952 
main(int argc,char ** argv)953 int main(int argc, char **argv)
954 {
955 	int fd = -1;
956 
957 	/* command args */
958 	struct v4l2_tuner tuner;	/* set_freq/get_freq */
959 	struct v4l2_capability vcap;	/* list_cap */
960 	struct v4l2_frequency vf;	/* get_freq/set_freq */
961 
962 	memset(&tuner, 0, sizeof(tuner));
963 	memset(&vcap, 0, sizeof(vcap));
964 	memset(&vf, 0, sizeof(vf));
965 	strcpy(params.fd_name, "/dev/radio0");
966 
967 	/* define locale for unicode support */
968 	if (!setlocale(LC_CTYPE, "")) {
969 		fprintf(stderr, "Can't set the specified locale!\n");
970 		return 1;
971 	}
972 	/* register signal handler for interrupt signal, to exit gracefully */
973 	signal(SIGINT, signal_handler_interrupt);
974 
975 	/* try to parse the command line */
976 	parse_cl(argc, argv);
977 	if (params.options[OptHelp]) {
978 		usage();
979 		std::exit(EXIT_SUCCESS);
980 	}
981 
982 	/* File Mode: disables all other features, except for RDS decoding */
983 	if (params.filemode_active) {
984 		if ((fd = open(params.fd_name, O_RDONLY|O_NONBLOCK)) < 0){
985 			perror("error opening file");
986 			std::exit(EXIT_FAILURE);
987 		}
988 		read_rds_from_fd(fd);
989 		close(fd);
990 		std::exit(EXIT_SUCCESS);
991 	}
992 
993 	/* Device Mode: open the radio device as read-only and non-blocking */
994 	if (!params.options[OptSetDevice]) {
995 		/* check the system for RDS capable devices */
996 		dev_vec devices = list_devices();
997 		if (devices.empty()) {
998 			fprintf(stderr, "No RDS-capable device found\n");
999 			std::exit(EXIT_FAILURE);
1000 		}
1001 		strncpy(params.fd_name, devices[0].c_str(), sizeof(params.fd_name));
1002 		params.fd_name[sizeof(params.fd_name) - 1] = '\0';
1003 		printf("Using device: %s\n", params.fd_name);
1004 	}
1005 	if ((fd = open(params.fd_name, O_RDONLY | O_NONBLOCK)) < 0) {
1006 		fprintf(stderr, "Failed to open %s: %s\n", params.fd_name,
1007 			strerror(errno));
1008 		std::exit(EXIT_FAILURE);
1009 	}
1010 	doioctl(fd, VIDIOC_QUERYCAP, &vcap);
1011 
1012 	/* Info options */
1013 	if (params.options[OptGetDriverInfo])
1014 		print_driver_info(&vcap);
1015 	/* Set options */
1016 	set_options(fd, vcap.capabilities, &vf, &tuner);
1017 	/* Get options */
1018 	get_options(fd, vcap.capabilities, &vf, &tuner);
1019 	/* RDS decoding */
1020 	if (params.options[OptReadRds])
1021 		read_rds_from_fd(fd);
1022 
1023 	close(fd);
1024 	std::exit(app_result);
1025 }
1026