• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2011-2012 - Mauro Carvalho Chehab
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation version 2
7  * of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
18  *
19  * Based on dvbv5-tzap utility.
20  */
21 
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <argp.h>
32 
33 #ifdef ENABLE_NLS
34 # define _(string) gettext(string)
35 # include "gettext.h"
36 # include <locale.h>
37 # include <langinfo.h>
38 # include <iconv.h>
39 #else
40 # define _(string) string
41 #endif
42 
43 # define N_(string) string
44 
45 #include <linux/dvb/dmx.h>
46 #include "libdvbv5/dvb-file.h"
47 #include "libdvbv5/dvb-demux.h"
48 #include "libdvbv5/dvb-dev.h"
49 #include "libdvbv5/dvb-v5-std.h"
50 #include "libdvbv5/dvb-scan.h"
51 #include "libdvbv5/countries.h"
52 
53 #define PROGRAM_NAME	"dvbv5-scan"
54 #define DEFAULT_OUTPUT  "dvb_channel.conf"
55 
56 const char *argp_program_version = PROGRAM_NAME " version " V4L_UTILS_VERSION;
57 const char *argp_program_bug_address = "Mauro Carvalho Chehab <mchehab@kernel.org>";
58 
59 struct arguments {
60 	char *confname, *lnb_name, *output, *demux_dev;
61 	unsigned adapter, n_adapter, adapter_fe, adapter_dmx, frontend, demux, get_detected, get_nit;
62 	int lna, lnb, sat_number, freq_bpf;
63 	unsigned diseqc_wait, dont_add_new_freqs, timeout_multiply;
64 	unsigned other_nit;
65 	enum dvb_file_formats input_format, output_format;
66 	const char *cc;
67 
68 	/* Used by status print */
69 	unsigned n_status_lines;
70 };
71 
72 static const struct argp_option options[] = {
73 	{"adapter",	'a',	N_("adapter#"),		0, N_("use given adapter (default 0)"), 0},
74 	{"frontend",	'f',	N_("frontend#"),	0, N_("use given frontend (default 0)"), 0},
75 	{"demux",	'd',	N_("demux#"),		0, N_("use given demux (default 0)"), 0},
76 	{"lnbf",	'l',	N_("LNBf_type"),	0, N_("type of LNBf to use. 'help' lists the available ones"), 0},
77 	{"lna",		'w',	N_("LNA (0, 1, -1)"),	0, N_("enable/disable/auto LNA power"), 0},
78 	{"sat_number",	'S',	N_("satellite_number"),	0, N_("satellite number. If not specified, disable DISEqC"), 0},
79 	{"freq_bpf",	'U',	N_("frequency"),	0, N_("SCR/Unicable band-pass filter frequency to use, in kHz"), 0},
80 	{"wait",	'W',	N_("time"),		0, N_("adds additional wait time for DISEqC command completion"), 0},
81 	{"nit",		'N',	NULL,			0, N_("use data from NIT table on the output file"), 0},
82 	{"get_frontend",'G',	NULL,			0, N_("use data from get_frontend on the output file"), 0},
83 	{"verbose",	'v',	NULL,			0, N_("be (very) verbose"), 0},
84 	{"output",	'o',	N_("file"),		0, N_("output filename (default: ") DEFAULT_OUTPUT ")", 0},
85 	{"file-freqs-only", 'F', NULL,			0, N_("don't use the other frequencies discovered during scan"), 0},
86 	{"timeout-multiply", 'T', N_("factor"),		0, N_("Multiply scan timeouts by this factor"), 0},
87 	{"parse-other-nit", 'p', NULL,			0, N_("Parse the other NIT/SDT tables"), 0},
88 	{"input-format", 'I',	N_("format"),		0, N_("Input format: CHANNEL, DVBV5 (default: DVBV5)"), 0},
89 	{"output-format", 'O',	N_("format"),		0, N_("Output format: VDR, CHANNEL, ZAP, DVBV5 (default: DVBV5)"), 0},
90 	{"cc",		'C',	N_("country_code"),	0, N_("Set the default country to be used (in ISO 3166-1 two letter code)"), 0},
91 	{"help",        '?',	0,		0,	N_("Give this help list"), -1},
92 	{"usage",	-3,	0,		0,	N_("Give a short usage message")},
93 	{"version",	'V',	0,		0,	N_("Print program version"), -1},
94 	{ 0, 0, 0, 0, 0, 0 }
95 };
96 
97 static int verbose = 0;
98 #define CHANNEL_FILE "channels.conf"
99 
100 #define ERROR(x...)                                                     \
101 	do {                                                            \
102 		fprintf(stderr, _("ERROR: "));                             \
103 		fprintf(stderr, x);                                     \
104 		fprintf(stderr, "\n");                                 \
105 	} while (0)
106 
107 #define PERROR(x...)                                                    \
108 	do {                                                            \
109 		fprintf(stderr, _("ERROR: "));                             \
110 		fprintf(stderr, x);                                     \
111 		fprintf(stderr, " (%s)\n", strerror(errno));		\
112 	} while (0)
113 
print_frontend_stats(struct arguments * args,struct dvb_v5_fe_parms * parms)114 static int print_frontend_stats(struct arguments *args,
115 				struct dvb_v5_fe_parms *parms)
116 {
117 	char buf[512], *p;
118 	int rc, i, len, show;
119 	uint32_t status = 0;
120 
121 	/* Move cursor up and cleans down */
122 	if (isatty(STDERR_FILENO) && args->n_status_lines)
123 		fprintf(stderr, "\r\x1b[%dA\x1b[J", args->n_status_lines);
124 
125 	args->n_status_lines = 0;
126 
127 	if (isatty(STDERR_FILENO)) {
128 		rc = dvb_fe_retrieve_stats(parms, DTV_STATUS, &status);
129 		if (rc)
130 			status = 0;
131 		if (status & FE_HAS_LOCK)
132 			fprintf(stderr, "\x1b[1;32m");
133 		else
134 			fprintf(stderr, "\x1b[33m");
135 	}
136 
137 	p = buf;
138 	len = sizeof(buf);
139 	dvb_fe_snprintf_stat(parms,  DTV_STATUS, NULL, 0, &p, &len, &show);
140 
141 	for (i = 0; i < MAX_DTV_STATS; i++) {
142 		show = 1;
143 
144 		dvb_fe_snprintf_stat(parms, DTV_QUALITY, _("Quality"),
145 				     i, &p, &len, &show);
146 
147 		dvb_fe_snprintf_stat(parms, DTV_STAT_SIGNAL_STRENGTH, _("Signal"),
148 				     i, &p, &len, &show);
149 
150 		dvb_fe_snprintf_stat(parms, DTV_STAT_CNR, _("C/N"),
151 				     i, &p, &len, &show);
152 
153 		dvb_fe_snprintf_stat(parms, DTV_STAT_ERROR_BLOCK_COUNT, _("UCB"),
154 				     i,  &p, &len, &show);
155 
156 		dvb_fe_snprintf_stat(parms, DTV_BER, _("postBER"),
157 				     i,  &p, &len, &show);
158 
159 		dvb_fe_snprintf_stat(parms, DTV_PRE_BER, _("preBER"),
160 				     i,  &p, &len, &show);
161 
162 		dvb_fe_snprintf_stat(parms, DTV_PER, _("PER"),
163 				     i,  &p, &len, &show);
164 
165 		if (p != buf) {
166 			if (args->n_status_lines)
167 				fprintf(stderr, "\t%s\n", buf);
168 			else
169 				fprintf(stderr, "%s\n", buf);
170 
171 			args->n_status_lines++;
172 
173 			p = buf;
174 			len = sizeof(buf);
175 		}
176 	}
177 
178 	fflush(stderr);
179 
180 	return 0;
181 }
182 
check_frontend(void * __args,struct dvb_v5_fe_parms * parms)183 static int check_frontend(void *__args,
184 			  struct dvb_v5_fe_parms *parms)
185 {
186 	struct arguments *args = __args;
187 	int rc, i;
188 	fe_status_t status;
189 
190 	args->n_status_lines = 0;
191 	for (i = 0; i < args->timeout_multiply * 40; i++) {
192 		if (parms->abort)
193 			return 0;
194 		rc = dvb_fe_get_stats(parms);
195 		if (rc)
196 			PERROR(_("dvb_fe_get_stats failed"));
197 
198 		rc = dvb_fe_retrieve_stats(parms, DTV_STATUS, &status);
199 		if (rc)
200 			status = 0;
201 		print_frontend_stats(args, parms);
202 		if (status & FE_HAS_LOCK)
203 			break;
204 		usleep(100000);
205 	};
206 
207 	if (isatty(STDERR_FILENO)) {
208 		fprintf(stderr, "\x1b[37m");
209 	}
210 
211 	return (status & FE_HAS_LOCK) ? 0 : -1;
212 }
213 
run_scan(struct arguments * args,struct dvb_device * dvb)214 static int run_scan(struct arguments *args, struct dvb_device *dvb)
215 {
216 	struct dvb_v5_fe_parms *parms = dvb->fe_parms;
217 	struct dvb_file *dvb_file = NULL, *dvb_file_new = NULL;
218 	struct dvb_entry *entry;
219 	struct dvb_open_descriptor *dmx_fd;
220 	int count = 0, shift;
221 	uint32_t freq, sys;
222 	enum dvb_sat_polarization pol;
223 
224 	/* This is used only when reading old formats */
225 	switch (parms->current_sys) {
226 	case SYS_DVBT:
227 	case SYS_DVBS:
228 	case SYS_DVBC_ANNEX_A:
229 	case SYS_ATSC:
230 		sys = parms->current_sys;
231 		break;
232 	case SYS_DVBC_ANNEX_C:
233 		sys = SYS_DVBC_ANNEX_A;
234 		break;
235 	case SYS_DVBC_ANNEX_B:
236 		sys = SYS_ATSC;
237 		break;
238 	case SYS_ISDBT:
239 	case SYS_DTMB:
240 		sys = SYS_DVBT;
241 		break;
242 	default:
243 		sys = SYS_UNDEFINED;
244 		break;
245 	}
246 	dvb_file = dvb_read_file_format(args->confname, sys,
247 				    args->input_format);
248 	if (!dvb_file)
249 		return -2;
250 
251 	/* FIXME: should be replaced by dvb_dev_open() */
252 	dmx_fd = dvb_dev_open(dvb, args->demux_dev, O_RDWR);
253 	if (!dmx_fd) {
254 		perror(_("opening demux failed"));
255 		return -3;
256 	}
257 
258 	for (entry = dvb_file->first_entry; entry != NULL; entry = entry->next) {
259 		struct dvb_v5_descriptors *dvb_scan_handler = NULL;
260 		uint32_t stream_id;
261 
262 		/*
263 		 * If the channel file has duplicated frequencies, or some
264 		 * entries without any frequency at all, discard.
265 		 */
266 		if (dvb_retrieve_entry_prop(entry, DTV_FREQUENCY, &freq))
267 			continue;
268 		shift = dvb_estimate_freq_shift(parms);
269 
270 		if (dvb_retrieve_entry_prop(entry, DTV_POLARIZATION, &pol))
271 			pol = POLARIZATION_OFF;
272 
273 		if (dvb_retrieve_entry_prop(entry, DTV_STREAM_ID, &stream_id))
274 			stream_id = NO_STREAM_ID_FILTER;
275 
276 		if (!dvb_new_entry_is_needed(dvb_file->first_entry, entry,
277 						  freq, shift, pol, stream_id))
278 			continue;
279 
280 		count++;
281 		dvb_log(_("Scanning frequency #%d %d"), count, freq);
282 
283 		/*
284 		 * update params->lnb only if it differs from entry->lnb
285 		 * (and "--lnbf" option was not provided),
286 		 * to avoid linear search of LNB types for every entries.
287 		 */
288 		if (!args->lnb_name && entry->lnb &&
289 		    (!parms->lnb || strcasecmp(entry->lnb, parms->lnb->alias)))
290 			parms->lnb = dvb_sat_get_lnb(dvb_sat_search_lnb(entry->lnb));
291 
292 		/*
293 		 * Run the scanning logic
294 		 */
295 
296 		dvb_scan_handler = dvb_dev_scan(dmx_fd, entry,
297 						&check_frontend, args,
298 						args->other_nit,
299 						args->timeout_multiply);
300 
301 		if (parms->abort) {
302 			dvb_scan_free_handler_table(dvb_scan_handler);
303 			break;
304 		}
305 		if (!dvb_scan_handler)
306 			continue;
307 
308 		/*
309 		 * Store the service entry
310 		 */
311 		dvb_store_channel(&dvb_file_new, parms, dvb_scan_handler,
312 				  args->get_detected, args->get_nit);
313 
314 		/*
315 		 * Add new transponders based on NIT table information
316 		 */
317 		if (!args->dont_add_new_freqs)
318 			dvb_add_scaned_transponders(parms, dvb_scan_handler,
319 						    dvb_file->first_entry, entry);
320 
321 		/*
322 		 * Free the scan handler associated with the transponder
323 		 */
324 
325 		dvb_scan_free_handler_table(dvb_scan_handler);
326 	}
327 
328 	if (dvb_file_new)
329 		dvb_write_file_format(args->output, dvb_file_new,
330 				      parms->current_sys, args->output_format);
331 
332 	dvb_file_free(dvb_file);
333 	if (dvb_file_new)
334 		dvb_file_free(dvb_file_new);
335 
336 	dvb_dev_close(dmx_fd);
337 	return 0;
338 }
339 
parse_opt(int k,char * optarg,struct argp_state * state)340 static error_t parse_opt(int k, char *optarg, struct argp_state *state)
341 {
342 	struct arguments *args = state->input;
343 	switch (k) {
344 	case 'a':
345 		args->adapter = strtoul(optarg, NULL, 0);
346 		args->n_adapter++;
347 		break;
348 	case 'f':
349 		args->frontend = strtoul(optarg, NULL, 0);
350 		args->adapter_fe = args->adapter;
351 		break;
352 	case 'd':
353 		args->demux = strtoul(optarg, NULL, 0);
354 		args->adapter_dmx = args->adapter;
355 		break;
356 	case 'w':
357 		if (!strcasecmp(optarg,"on")) {
358 			args->lna = 1;
359 		} else if (!strcasecmp(optarg,"off")) {
360 			args->lna = 0;
361 		} else if (!strcasecmp(optarg,"auto")) {
362 			args->lna = LNA_AUTO;
363 		} else {
364 			int val = strtoul(optarg, NULL, 0);
365 			if (!val)
366 				args->lna = 0;
367 			else if (val > 0)
368 				args->lna = 1;
369 			else
370 				args->lna = LNA_AUTO;
371 		}
372 		break;
373 	case 'l':
374 		args->lnb_name = optarg;
375 		break;
376 	case 'S':
377 		args->sat_number = strtoul(optarg, NULL, 0);
378 		break;
379 	case 'U':
380 		args->freq_bpf = strtoul(optarg, NULL, 0);
381 		break;
382 	case 'W':
383 		args->diseqc_wait = strtoul(optarg, NULL, 0);
384 		break;
385 	case 'N':
386 		args->get_nit++;
387 		break;
388 	case 'G':
389 		args->get_detected++;
390 		break;
391 	case 'F':
392 		args->dont_add_new_freqs++;
393 		break;
394 	case 'p':
395 		args->other_nit++;
396 		break;
397 	case 'v':
398 		verbose++;
399 		break;
400 	case 'T':
401 		args->timeout_multiply = strtoul(optarg, NULL, 0);
402 		break;
403 	case 'I':
404 		args->input_format = dvb_parse_format(optarg);
405 		break;
406 	case 'O':
407 		args->output_format = dvb_parse_format(optarg);
408 		break;
409 	case 'o':
410 		args->output = optarg;
411 		break;
412 	case 'C':
413 		args->cc = strndup(optarg, 2);
414 		break;
415 	case '?':
416 		argp_state_help(state, state->out_stream,
417 				ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG
418 				| ARGP_HELP_DOC);
419 		fprintf(state->out_stream, _("\nReport bugs to %s.\n"), argp_program_bug_address);
420 		exit(0);
421 	case 'V':
422 		fprintf (state->out_stream, "%s\n", argp_program_version);
423 		exit(0);
424 	case -3:
425 		argp_state_help(state, state->out_stream, ARGP_HELP_USAGE);
426 		exit(0);
427 	default:
428 		return ARGP_ERR_UNKNOWN;
429 	};
430 	return 0;
431 }
432 
433 static int *timeout_flag;
434 
do_timeout(int x)435 static void do_timeout(int x)
436 {
437 	(void)x;
438 	if (*timeout_flag == 0) {
439 		*timeout_flag = 1;
440 		alarm(5);
441 		signal(SIGALRM, do_timeout);
442 	} else {
443 		/* something has gone wrong ... exit */
444 		exit(1);
445 	}
446 }
447 
448 
main(int argc,char ** argv)449 int main(int argc, char **argv)
450 {
451 	struct arguments args = {};
452 	int err, lnb = -1,idx = -1;
453 	struct dvb_device *dvb;
454 	struct dvb_dev_list *dvb_dev;
455 	struct dvb_v5_fe_parms *parms;
456 	const struct argp argp = {
457 		.options = options,
458 		.parser = parse_opt,
459 		.doc = N_("scan DVB services using the channel file"),
460 		.args_doc = N_("<initial file>"),
461 	};
462 
463 #ifdef ENABLE_NLS
464 	setlocale (LC_ALL, "");
465 	bindtextdomain (PACKAGE, LOCALEDIR);
466 	textdomain (PACKAGE);
467 #endif
468 
469 	memset(&args, 0, sizeof(args));
470 	args.sat_number = -1;
471 	args.output = DEFAULT_OUTPUT;
472 	args.input_format = FILE_DVBV5;
473 	args.output_format = FILE_DVBV5;
474 	args.timeout_multiply = 1;
475 	args.adapter = (unsigned)-1;
476 	args.lna = LNA_AUTO;
477 
478 	if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, &idx, &args)) {
479 		argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME);
480 		return -1;
481 	}
482 
483 	if (args.timeout_multiply == 0)
484 		args.timeout_multiply = 1;
485 
486 	if (args.n_adapter == 1) {
487 		args.adapter_fe = args.adapter;
488 		args.adapter_dmx = args.adapter;
489 	}
490 
491 	if (args.lnb_name) {
492 		lnb = dvb_sat_search_lnb(args.lnb_name);
493 		if (lnb < 0) {
494 			printf(_("Please select one of the LNBf's below:\n"));
495 			dvb_print_all_lnb();
496 			exit(1);
497 		} else {
498 			printf(_("Using LNBf "));
499 			dvb_print_lnb(lnb);
500 		}
501 	}
502 
503 	if (idx < argc)
504 		args.confname = argv[idx];
505 
506 	if (!args.confname || idx < 0) {
507 		argp_help(&argp, stderr, ARGP_HELP_STD_HELP, PROGRAM_NAME);
508 		return -1;
509 	}
510 	if ((args.input_format == FILE_ZAP) ||
511 		   (args.input_format == FILE_UNKNOWN) ||
512 		   (args.output_format == FILE_UNKNOWN)) {
513 		fprintf(stderr, _("ERROR: Please specify a valid format\n"));
514 		argp_help(&argp, stderr, ARGP_HELP_STD_HELP, PROGRAM_NAME);
515 		return -1;
516 	}
517 
518 	dvb = dvb_dev_alloc();
519 	if (!dvb)
520 		return -1;
521 	dvb_dev_set_log(dvb, verbose, NULL);
522 	dvb_dev_find(dvb, NULL, NULL);
523 	parms = dvb->fe_parms;
524 
525 	dvb_dev = dvb_dev_seek_by_adapter(dvb, args.adapter_dmx, args.demux, DVB_DEVICE_DEMUX);
526 	if (!dvb_dev) {
527 		fprintf(stderr, _("Couldn't find demux device node\n"));
528 		dvb_dev_free(dvb);
529 		return -1;
530 	}
531 	args.demux_dev = dvb_dev->sysname;
532 
533 	if (verbose)
534 		fprintf(stderr, _("using demux '%s'\n"), args.demux_dev);
535 
536 	dvb_dev = dvb_dev_seek_by_adapter(dvb, args.adapter_fe, args.frontend,
537 					  DVB_DEVICE_FRONTEND);
538 	if (!dvb_dev)
539 		return -1;
540 
541 	if (!dvb_dev_open(dvb, dvb_dev->sysname, O_RDWR)) {
542 		free(args.demux_dev);
543 		return -1;
544 	}
545 	if (lnb >= 0)
546 		parms->lnb = dvb_sat_get_lnb(lnb);
547 	if (args.sat_number >= 0)
548 		parms->sat_number = args.sat_number;
549 	parms->diseqc_wait = args.diseqc_wait;
550 	parms->freq_bpf = args.freq_bpf;
551 	parms->lna = args.lna;
552 	err = dvb_fe_set_default_country(parms, args.cc);
553 	if (err < 0)
554 		fprintf(stderr, _("Failed to set the country code:%s\n"), args.cc);
555 
556 	timeout_flag = &parms->abort;
557 	signal(SIGTERM, do_timeout);
558 	signal(SIGINT, do_timeout);
559 
560 	err = run_scan(&args, dvb);
561 
562 	dvb_dev_free(dvb);
563 
564 	return err;
565 }
566