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