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  */
20 
21 #include "libdvbv5/dvb-file.h"
22 #include "libdvbv5/dvb-dev.h"
23 #include <config.h>
24 #include <argp.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 
30 #ifdef ENABLE_NLS
31 # define _(string) gettext(string)
32 # include <stdio.h>
33 # include <locale.h>
34 # include <langinfo.h>
35 # include <iconv.h>
36 #else
37 # define _(string) string
38 #endif
39 
40 # define N_(string) string
41 
42 #define PROGRAM_NAME	"dvb-fe-tool"
43 
44 const char *argp_program_version = PROGRAM_NAME " version " V4L_UTILS_VERSION;
45 const char *argp_program_bug_address = "Mauro Carvalho Chehab <mchehab@kernel.org>";
46 
47 static const char doc[] = N_(
48 	"\nA DVB frontend tool using API version 5\n"
49 	"\nOn the options below, the arguments are:\n"
50 	"  ADAPTER      - the dvb adapter to control\n"
51 	"  FRONTEND     - the dvb frontend to control\n"
52 	"  SERVER       - server address which is running the dvb5-daemon\n"
53 	"  PORT         - server port used by the dvb5-daemon\n");
54 
55 static const struct argp_option options[] = {
56 	{"verbose",	'v',	0,		0,	N_("enables debug messages"), 0},
57 	{"adapter",	'a',	N_("ADAPTER"),	0,	N_("dvb adapter"), 0},
58 	{"frontend",	'f',	N_("FRONTEND"),	0,	N_("dvb frontend"), 0},
59 	{"set-delsys",	'd',	N_("PARAMS"),	0,	N_("set delivery system"), 0},
60 	{"femon",	'm',	0,		0,	N_("monitors frontend stats on an streaming frontend"), 0},
61 	{"acoustical",	'A',	0,		0,	N_("beeps if signal quality is good. Also enables femon mode. Please notice that console beep should be enabled on your wm."), 0},
62 #if 0 /* Currently not implemented */
63 	{"set",		's',	N_("PARAMS"),	0,	N_("set frontend"), 0},
64 #endif
65 	{"get",		'g',	0,		0,	N_("get frontend"), 0},
66 	{"server",	'H',	N_("SERVER"),	0, 	N_("dvbv5-daemon host IP address"), 0},
67 	{"tcp-port",	'T',	N_("PORT"),	0, 	N_("dvbv5-daemon host tcp port"), 0},
68 	{"device-mon",	'D',	0,		0,	N_("monitors device insert/removal"), 0},
69 	{"count",	'c',	N_("COUNT"),	0,	N_("samples to take (default 0 = infinite)"), 0},
70 	{"help",        '?',	0,		0,	N_("Give this help list"), -1},
71 	{"usage",	-3,	0,		0,	N_("Give a short usage message")},
72 	{"version",	'V',	0,		0,	N_("Print program version"), -1},
73 	{ 0, 0, 0, 0, 0, 0 }
74 };
75 
76 static int adapter = 0;
77 static int frontend = 0;
78 static unsigned get = 0;
79 static char *set_params = NULL;
80 static char *server = NULL;
81 static unsigned port = 0;
82 static int verbose = 0;
83 static int delsys = 0;
84 static int femon = 0;
85 static int acoustical = 0;
86 static int timeout_flag = 0;
87 static int device_mon = 0;
88 static int count = 0;
89 
do_timeout(int x)90 static void do_timeout(int x)
91 {
92 	(void)x;
93 
94 	if (timeout_flag == 0) {
95 		timeout_flag = 1;
96 		alarm(2);
97 		signal(SIGALRM, do_timeout);
98 	} else {
99 		/* something has gone wrong ... exit */
100 		exit(1);
101 	}
102 }
103 
104 #define ERROR(x...)                                                     \
105 	do {                                                            \
106 		fprintf(stderr, _("ERROR: "));                             \
107 		fprintf(stderr, x);                                     \
108 		fprintf(stderr, "\n");                                 \
109 	} while (0)
110 
111 
parse_opt(int k,char * arg,struct argp_state * state)112 static int parse_opt(int k, char *arg, struct argp_state *state)
113 {
114 	switch (k) {
115 	case 'a':
116 		adapter = atoi(arg);
117 		break;
118 	case 'f':
119 		frontend = atoi(arg);
120 		break;
121 	case 'd':
122 		delsys = dvb_parse_delsys(arg);
123 		if (delsys < 0)
124 			return ARGP_ERR_UNKNOWN;
125 		break;
126 	case 'm':
127 		femon++;
128 		break;
129 	case 'A':
130 		femon++;
131 		acoustical++;
132 		break;
133 #if 0
134 	case 's':
135 		set_params = arg;
136 		break;
137 #endif
138 	case 'g':
139 		get++;
140 		break;
141 	case 'D':
142 		device_mon++;
143 		break;
144 	case 'H':
145 		server = arg;
146 		break;
147 	case 'T':
148 		port = atoi(arg);
149 		break;
150 	case 'v':
151 		verbose	++;
152 		break;
153 	case 'c':
154 		count = atoi(arg);
155 		break;
156 	case '?':
157 		argp_state_help(state, state->out_stream,
158 				ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG
159 				| ARGP_HELP_DOC);
160 		fprintf(state->out_stream, _("\nReport bugs to %s.\n"), argp_program_bug_address);
161 		exit(0);
162 	case 'V':
163 		fprintf (state->out_stream, "%s\n", argp_program_version);
164 		exit(0);
165 	case -3:
166 		argp_state_help(state, state->out_stream, ARGP_HELP_USAGE);
167 		exit(0);
168 	default:
169 		return ARGP_ERR_UNKNOWN;
170 	}
171 	return 0;
172 }
173 
174 static struct argp argp = {
175 	.options = options,
176 	.parser = parse_opt,
177 	.doc = doc,
178 };
179 
print_frontend_stats(FILE * fd,struct dvb_v5_fe_parms * parms)180 static int print_frontend_stats(FILE *fd,
181 				struct dvb_v5_fe_parms *parms)
182 {
183 	char buf[512], *p;
184 	int rc, i, len, show, n_status_lines = 0;
185 
186 	rc = dvb_fe_get_stats(parms);
187 	if (rc) {
188 		ERROR(_("dvb_fe_get_stats failed"));
189 		return -1;
190 	}
191 
192 	p = buf;
193 	len = sizeof(buf);
194 	dvb_fe_snprintf_stat(parms,  DTV_STATUS, NULL, 0, &p, &len, &show);
195 
196 	for (i = 0; i < MAX_DTV_STATS; i++) {
197 		show = 1;
198 
199 		dvb_fe_snprintf_stat(parms, DTV_QUALITY, _("Quality"),
200 				     i, &p, &len, &show);
201 
202 		dvb_fe_snprintf_stat(parms, DTV_STAT_SIGNAL_STRENGTH, _("Signal"),
203 				     i, &p, &len, &show);
204 
205 		dvb_fe_snprintf_stat(parms, DTV_STAT_CNR, _("C/N"),
206 				     i, &p, &len, &show);
207 
208 		dvb_fe_snprintf_stat(parms, DTV_STAT_ERROR_BLOCK_COUNT, _("UCB"),
209 				     i,  &p, &len, &show);
210 
211 		dvb_fe_snprintf_stat(parms, DTV_BER, _("postBER"),
212 				     i,  &p, &len, &show);
213 
214 		dvb_fe_snprintf_stat(parms, DTV_PRE_BER, _("preBER"),
215 				     i,  &p, &len, &show);
216 
217 		dvb_fe_snprintf_stat(parms, DTV_PER, _("PER"),
218 				     i,  &p, &len, &show);
219 		if (p != buf) {
220 			if (isatty(fileno(fd))) {
221 				enum dvb_quality qual;
222 				int color;
223 
224 				qual = dvb_fe_retrieve_quality(parms, 0);
225 
226 				switch (qual) {
227 				case DVB_QUAL_POOR:
228 					color = 31;
229 					break;
230 				case DVB_QUAL_OK:
231 					color = 36;
232 					break;
233 				case DVB_QUAL_GOOD:
234 					color = 32;
235 					break;
236 				case DVB_QUAL_UNKNOWN:
237 				default:
238 					color = 0;
239 					break;
240 				}
241 				fprintf(fd, "\033[%dm", color);
242 				/*
243 				 * It would be great to change the BELL
244 				 * tone depending on the quality. The legacy
245 				 * femon used to to that, but this doesn't
246 				 * work anymore with modern Linux distros.
247 				 *
248 				 * So, just print a bell if quality is good.
249 				 *
250 				 * The console audio should be enabled
251 				 * at the window manater for this to
252 				 * work.
253 				 */
254 				if (acoustical) {
255 					if (qual == DVB_QUAL_GOOD)
256 						fprintf(fd, "\a");
257 				}
258 			}
259 
260 			if (n_status_lines)
261 				fprintf(fd, "\t%s\n", buf);
262 			else
263 				fprintf(fd, "%s\n", buf);
264 
265 			n_status_lines++;
266 
267 			p = buf;
268 			len = sizeof(buf);
269 		}
270 	}
271 
272 	fflush(fd);
273 
274 	return 0;
275 }
276 
get_show_stats(struct dvb_v5_fe_parms * parms)277 static void get_show_stats(struct dvb_v5_fe_parms *parms)
278 {
279 	int rc;
280 
281 	signal(SIGTERM, do_timeout);
282 	signal(SIGINT, do_timeout);
283 
284 	do {
285 		rc = dvb_fe_get_stats(parms);
286 		if (!rc)
287 			print_frontend_stats(stderr, parms);
288 		if (count > 0 && !--count)
289 			break;
290 		if (!timeout_flag)
291 			usleep(1000000);
292 	} while (!timeout_flag);
293 }
294 
295 static const char * const event_type[] = {
296 	[DVB_DEV_ADD] = "added",
297 	[DVB_DEV_CHANGE] = "changed",
298 	[DVB_DEV_REMOVE] = "removed",
299 };
300 
dev_change_monitor(char * sysname,enum dvb_dev_change_type type,void * user_priv)301 static int dev_change_monitor(char *sysname,
302 			       enum dvb_dev_change_type type, void *user_priv)
303 {
304 	if (type > ARRAY_SIZE(event_type))
305 		printf("unknown event on device %s\n", sysname);
306 	else
307 		printf("device %s was %s\n", sysname, event_type[type]);
308 	free(sysname);
309 
310 	return 0;
311 }
312 
main(int argc,char * argv[])313 int main(int argc, char *argv[])
314 {
315 	struct dvb_device *dvb;
316 	struct dvb_dev_list *dvb_dev;
317 	struct dvb_v5_fe_parms *parms;
318 	int ret, fe_flags = O_RDWR;
319 
320 #ifdef ENABLE_NLS
321 	setlocale (LC_ALL, "");
322 	bindtextdomain (PACKAGE, LOCALEDIR);
323 	textdomain (PACKAGE);
324 #endif
325 
326 	if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, 0, 0)) {
327 		argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME);
328 		return -1;
329 	}
330 
331 	/*
332 	 * If called without any option, be verbose, to print the
333 	 * DVB frontend information.
334 	 */
335 	if (!get && !delsys && !set_params && !femon)
336 		verbose++;
337 
338 	if (!delsys && !set_params)
339 		fe_flags = O_RDONLY;
340 
341 	dvb = dvb_dev_alloc();
342 	if (!dvb)
343 		return -1;
344 
345 	if (server && port) {
346 		printf(_("Connecting to %s:%d\n"), server, port);
347 		ret = dvb_dev_remote_init(dvb, server, port);
348 		if (ret < 0)
349 			return -1;
350 	}
351 
352 	dvb_dev_set_log(dvb, verbose, NULL);
353 	if (device_mon) {
354 		dvb_dev_find(dvb, &dev_change_monitor, NULL);
355 		while (1) {
356 			usleep(1000000);
357 		}
358 	}
359 	dvb_dev_find(dvb, NULL, NULL);
360 	parms = dvb->fe_parms;
361 
362 	dvb_dev = dvb_dev_seek_by_adapter(dvb, adapter, frontend,
363 					  DVB_DEVICE_FRONTEND);
364 	if (!dvb_dev)
365 		return -1;
366 
367 	if (!dvb_dev_open(dvb, dvb_dev->sysname, fe_flags))
368 		return -1;
369 
370 	if (delsys) {
371 		printf(_("Changing delivery system to: %s\n"),
372 			delivery_system_name[delsys]);
373 		dvb_set_sys(parms, delsys);
374 		goto ret;
375 	}
376 
377 #if 0
378 	if (set_params)
379 		do_something();
380 #endif
381 	if (get) {
382 		dvb_fe_get_parms(parms);
383 		dvb_fe_prt_parms(parms);
384 	}
385 
386 	if (femon)
387 		get_show_stats(parms);
388 
389 ret:
390 	dvb_dev_free(dvb);
391 
392 	return 0;
393 }
394