Remove excessive debug, extend search for start
[dvblastpn.git] / dvblastctl.c
blobd410d6ad78b20824a5d66aa81ec13a0194bb6a60
1 /*****************************************************************************
2 * dvblastctl.c
3 *****************************************************************************
4 * Copyright (C) 2008 VideoLAN
6 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <stdint.h>
28 #include <stdbool.h>
29 #include <string.h>
30 #include <inttypes.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/param.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <sys/un.h>
38 #include <errno.h>
39 #include <getopt.h>
41 #include <iconv.h>
43 #include <bitstream/mpeg/psi.h>
44 #include <bitstream/dvb/si.h>
45 #include <bitstream/dvb/si_print.h>
46 #include <bitstream/mpeg/psi_print.h>
48 #include "dvblast.h"
49 #include "comm.h"
51 #define STRINGIFY( z ) UGLY_KLUDGE( z )
52 #define UGLY_KLUDGE( z ) #z
54 int i_verbose = 3;
55 int i_syslog = 0;
56 unsigned int i_timeout_seconds = 15;
58 print_type_t i_print_type = PRINT_TEXT;
59 mtime_t now;
61 int i_fd = -1;
62 char psz_client_socket[PATH_MAX] = {0};
64 static iconv_t iconv_handle = (iconv_t)-1;
66 static void clean_client_socket() {
67 if ( i_fd > -1 )
69 close( i_fd );
70 i_fd = -1;
72 if ( psz_client_socket[0] )
74 unlink( psz_client_socket );
75 psz_client_socket[0] = '\0';
79 /*****************************************************************************
80 * The following two functions are from biTStream's examples and are under the
81 * WTFPL (see LICENSE.WTFPL).
82 ****************************************************************************/
83 __attribute__ ((format(printf, 2, 3)))
84 static void psi_print(void *_unused, const char *psz_format, ...)
86 char psz_fmt[strlen(psz_format) + 2];
87 va_list args;
88 va_start(args, psz_format);
89 strcpy(psz_fmt, psz_format);
90 strcat(psz_fmt, "\n");
91 vprintf(psz_fmt, args);
92 va_end(args);
95 __attribute__ ((format(printf, 1, 2)))
96 void return_error( const char *psz_format, ... )
98 va_list args;
99 char psz_fmt[1024];
101 clean_client_socket();
103 va_start( args, psz_format );
104 if ( i_print_type == PRINT_XML )
105 snprintf( psz_fmt, sizeof(psz_fmt) - 1, "<ERROR msg=\"%s\"/>\n", psz_format );
106 else
107 snprintf( psz_fmt, sizeof(psz_fmt) - 1, "ERROR: %s\n", psz_format );
108 psz_fmt[sizeof(psz_fmt) - 1] = '\0';
109 vfprintf( stderr, psz_fmt, args );
110 va_end(args);
111 exit(255);
114 static char *iconv_append_null(const char *p_string, size_t i_length)
116 char *psz_string = malloc(i_length + 1);
117 memcpy(psz_string, p_string, i_length);
118 psz_string[i_length] = '\0';
119 return psz_string;
122 const char *psz_native_charset = "UTF-8//IGNORE";
124 char *psi_iconv(void *_unused, const char *psz_encoding,
125 char *p_string, size_t i_length)
127 static const char *psz_current_encoding = "";
129 char *psz_string, *p;
130 size_t i_out_length;
132 if (!strcmp(psz_encoding, psz_native_charset))
133 return iconv_append_null(p_string, i_length);
135 if (iconv_handle != (iconv_t)-1 &&
136 strcmp(psz_encoding, psz_current_encoding)) {
137 iconv_close(iconv_handle);
138 iconv_handle = (iconv_t)-1;
141 if (iconv_handle == (iconv_t)-1)
142 iconv_handle = iconv_open(psz_native_charset, psz_encoding);
143 if (iconv_handle == (iconv_t)-1) {
144 msg_Warn(NULL, "couldn't open converter from %s to %s (%m)", psz_encoding,
145 psz_native_charset);
146 return iconv_append_null(p_string, i_length);
148 psz_current_encoding = psz_encoding;
150 /* converted strings can be up to six times larger */
151 i_out_length = i_length * 6;
152 p = psz_string = malloc(i_out_length);
153 if (iconv(iconv_handle, &p_string, &i_length, &p, &i_out_length) == (size_t)-1) {
154 msg_Warn(NULL, "couldn't convert from %s to %s (%m)", psz_encoding,
155 psz_native_charset);
156 free(psz_string);
157 return iconv_append_null(p_string, i_length);
159 if (i_length)
160 msg_Warn(NULL, "partial conversion from %s to %s", psz_encoding,
161 psz_native_charset);
163 *p = '\0';
164 return psz_string;
167 void print_pids_header( void )
169 if ( i_print_type == PRINT_XML )
170 printf("<PIDS>\n");
173 void print_pids_footer( void )
175 if ( i_print_type == PRINT_XML )
176 printf("</PIDS>\n");
179 void print_pid(uint16_t i_pid, ts_pid_info_t *p_info)
181 if ( p_info->i_packets == 0 )
182 return;
183 if ( i_print_type == PRINT_TEXT )
184 printf("pid %d packn %lu ccerr %lu tserr %lu scramble %d Bps %lu seen %"PRId64"\n",
185 i_pid,
186 p_info->i_packets,
187 p_info->i_cc_errors,
188 p_info->i_transport_errors,
189 p_info->i_scrambling,
190 p_info->i_bytes_per_sec,
191 now - p_info->i_last_packet_ts
193 else
194 printf("<PID pid=\"%d\" packn=\"%lu\" ccerr=\"%lu\" tserr=\"%lu\" scramble=\"%d\" Bps=\"%lu\" seen=\"%"PRId64"\" />\n",
195 i_pid,
196 p_info->i_packets,
197 p_info->i_cc_errors,
198 p_info->i_transport_errors,
199 p_info->i_scrambling,
200 p_info->i_bytes_per_sec,
201 now - p_info->i_last_packet_ts
205 void print_pids( uint8_t *p_data )
207 int i_pid;
208 print_pids_header();
209 for ( i_pid = 0; i_pid < MAX_PIDS; i_pid++ ) {
210 ts_pid_info_t *p_info = (ts_pid_info_t *)(p_data + i_pid * sizeof(ts_pid_info_t));
211 print_pid( i_pid, p_info );
213 print_pids_footer();
216 void print_eit_events(uint8_t *p_eit, f_print pf_print, void *print_opaque, f_iconv pf_iconv, void *iconv_opaque, print_type_t i_print_type)
218 uint8_t *p_event;
219 uint8_t j = 0;
220 while ((p_event = eit_get_event(p_eit, j)) != NULL) {
221 j++;
222 char start_str[25], duration_str[20];
223 int duration, hour, min, sec;
224 time_t start_ts;
226 start_ts = dvb_time_format_UTC(eitn_get_start_time(p_event), NULL, start_str);
228 dvb_time_decode_bcd(eitn_get_duration_bcd(p_event), &duration, &hour, &min, &sec);
229 sprintf(duration_str, "%02d:%02d:%02d", hour, min, sec);
231 switch (i_print_type) {
232 case PRINT_XML:
233 pf_print(print_opaque, "<EVENT id=\"%u\" start_time=\"%ld\" start_time_dec=\"%s\""
234 " duration=\"%u\" duration_dec=\"%s\""
235 " running=\"%d\" free_CA=\"%d\">",
236 eitn_get_event_id(p_event),
237 start_ts, start_str,
238 duration, duration_str,
239 eitn_get_running(p_event),
240 eitn_get_ca(p_event)
242 break;
243 default:
244 pf_print(print_opaque, " * EVENT id=%u start_time=%ld start_time_dec=\"%s\" duration=%u duration_dec=%s running=%d free_CA=%d",
245 eitn_get_event_id(p_event),
246 start_ts, start_str,
247 duration, duration_str,
248 eitn_get_running(p_event),
249 eitn_get_ca(p_event)
253 descs_print(eitn_get_descs(p_event), pf_print, print_opaque,
254 pf_iconv, iconv_opaque, i_print_type);
256 switch (i_print_type) {
257 case PRINT_XML:
258 pf_print(print_opaque, "</EVENT>");
259 break;
260 default:
261 break;
266 void print_eit(uint8_t *p_eit, unsigned int i_eit_size, f_print pf_print, void *print_opaque, f_iconv pf_iconv, void *iconv_opaque, print_type_t i_print_type)
268 uint8_t *p_eit_end = p_eit + i_eit_size;
269 uint8_t i_tid = psi_get_tableid(p_eit);
270 const char *psz_tid = "unknown";
272 if (i_tid == EIT_TABLE_ID_PF_ACTUAL)
273 psz_tid = "actual_pf";
274 else if (i_tid == EIT_TABLE_ID_PF_OTHER)
275 psz_tid = "other_pf";
276 else if (i_tid >= EIT_TABLE_ID_SCHED_ACTUAL_FIRST && i_tid <= EIT_TABLE_ID_SCHED_ACTUAL_LAST)
277 psz_tid = "actual_schedule";
278 else if (i_tid >= EIT_TABLE_ID_SCHED_OTHER_FIRST && i_tid <= EIT_TABLE_ID_SCHED_OTHER_LAST)
279 psz_tid = "other_schedule";
281 switch (i_print_type) {
282 case PRINT_XML:
283 pf_print(print_opaque,
284 "<EIT tableid=\"0x%02x\" type=\"%s\" service_id=\"%u\""
285 " version=\"%u\""
286 " current_next=\"%u\""
287 " tsid=\"%u\" onid=\"%u\">",
288 i_tid, psz_tid,
289 eit_get_sid(p_eit),
290 psi_get_version(p_eit),
291 !psi_get_current(p_eit) ? 0 : 1,
292 eit_get_tsid(p_eit),
293 eit_get_onid(p_eit)
295 break;
296 default:
297 pf_print(print_opaque,
298 "new EIT tableid=0x%02x type=%s service_id=%u version=%u%s"
299 " tsid=%u"
300 " onid=%u",
301 i_tid, psz_tid,
302 eit_get_sid(p_eit),
303 psi_get_version(p_eit),
304 !psi_get_current(p_eit) ? " (next)" : "",
305 eit_get_tsid(p_eit),
306 eit_get_onid(p_eit)
310 while (p_eit < p_eit_end) {
311 print_eit_events(p_eit, pf_print, print_opaque, pf_iconv, iconv_opaque, i_print_type);
312 p_eit += psi_get_length( p_eit ) + PSI_HEADER_SIZE;
315 switch (i_print_type) {
316 case PRINT_XML:
317 pf_print(print_opaque, "</EIT>");
318 break;
319 default:
320 pf_print(print_opaque, "end EIT");
324 struct dvblastctl_option {
325 char * opt;
326 int nparams;
327 ctl_cmd_t cmd;
330 static const struct dvblastctl_option options[] =
332 { "reload", 0, CMD_RELOAD },
333 { "shutdown", 0, CMD_SHUTDOWN },
335 { "fe_status", 0, CMD_FRONTEND_STATUS },
336 { "get_pat", 0, CMD_GET_PAT },
337 { "get_cat", 0, CMD_GET_CAT },
338 { "get_nit", 0, CMD_GET_NIT },
339 { "get_sdt", 0, CMD_GET_SDT },
340 { "get_eit_pf", 1, CMD_GET_EIT_PF }, /* arg: service_id (uint16_t) */
341 { "get_eit_schedule", 1, CMD_GET_EIT_SCHEDULE }, /* arg: service_id (uint16_t) */
342 { "get_pmt", 1, CMD_GET_PMT }, /* arg: service_id (uint16_t) */
343 { "get_pids", 0, CMD_GET_PIDS },
344 { "get_pid", 1, CMD_GET_PID }, /* arg: pid (uint16_t) */
346 { NULL, 0, 0 }
349 void usage()
351 printf("DVBlastctl %s (%s)\n", VERSION, VERSION_EXTRA );
352 printf("Usage: dvblastctl -r <remote socket> [-x <text|xml>] [cmd]\n");
353 printf("Options:\n");
354 printf(" -r --remote-socket <name> Set socket name to <name>.\n" );
355 printf(" -t --timeout <seconds> Set socket read/write timeout in seconds (default 15).\n" );
356 printf(" -j --system-charset <name> Character set used for output (default UTF-8//IGNORE)\n" );
357 printf(" -x --print <text|xml> Choose output format for info commands.\n" );
358 printf("Control commands:\n");
359 printf(" reload Reload configuration.\n");
360 printf(" shutdown Shutdown DVBlast.\n");
361 #ifdef HAVE_DVB_SUPPORT
362 printf("Status commands:\n");
363 printf(" fe_status Read frontend status information.\n");
364 #endif
365 printf("Demux info commands:\n");
366 printf(" get_pat Return last PAT table.\n");
367 printf(" get_cat Return last CAT table.\n");
368 printf(" get_nit Return last NIT table.\n");
369 printf(" get_sdt Return last SDT table.\n");
370 printf(" get_eit_pf <service_id> Return last EIT present/following data.\n");
371 printf(" get_eit_schedule <service_id> Return last EIT schedule data.\n");
372 printf(" get_pmt <service_id> Return last PMT table.\n");
373 printf(" get_pids Return info about all pids.\n");
374 printf(" get_pid <pid> Return info for chosen pid only.\n");
375 printf("\n");
376 exit(1);
379 int main( int i_argc, char **ppsz_argv )
381 char *client_socket_tmpl = "dvblastctl.clientsock.XXXXXX";
382 char *psz_srv_socket = NULL;
383 int i;
384 char *p_cmd, *p_arg1 = NULL, *p_arg2 = NULL;
385 ssize_t i_size;
386 struct sockaddr_un sun_client, sun_server;
387 uint8_t p_buffer[COMM_BUFFER_SIZE];
388 uint8_t *p_data = p_buffer + COMM_HEADER_SIZE;
389 uint16_t i_pid = 0;
390 struct dvblastctl_option opt = { 0, 0, 0 };
392 for ( ; ; )
394 int c;
396 static const struct option long_options[] =
398 {"remote-socket", required_argument, NULL, 'r'},
399 {"timeout", required_argument, NULL, 't'},
400 {"system-charset", required_argument, NULL, 'j'},
401 {"print", required_argument, NULL, 'x'},
402 {"help", no_argument, NULL, 'h'},
403 {0, 0, 0, 0}
406 if ( (c = getopt_long(i_argc, ppsz_argv, "r:t:x:j:h", long_options, NULL)) == -1 )
407 break;
409 switch ( c )
411 case 'r':
412 psz_srv_socket = optarg;
413 break;
415 case 't':
416 i_timeout_seconds = (unsigned int)strtoul(optarg, NULL, 10);
417 break;
419 case 'j':
420 psz_native_charset = optarg;
421 break;
423 case 'x':
424 if ( !strcmp(optarg, "text") )
425 i_print_type = PRINT_TEXT;
426 else if ( !strcmp(optarg, "xml") )
427 i_print_type = PRINT_XML;
428 else
429 msg_Warn( NULL, "unrecognized print type %s", optarg );
430 /* Make stdout line-buffered */
431 setvbuf(stdout, NULL, _IOLBF, 0);
432 break;
434 case 'h':
435 default:
436 usage();
440 /* Validate commands */
441 #define usage_error(msg, ...) \
442 do { \
443 msg_Err( NULL, msg, ##__VA_ARGS__ ); \
444 usage(); \
445 } while(0)
446 p_cmd = ppsz_argv[optind];
447 p_arg1 = ppsz_argv[optind + 1];
448 p_arg2 = ppsz_argv[optind + 2];
450 if ( !psz_srv_socket )
451 usage_error( "Remote socket is not set.\n" );
453 if ( !p_cmd )
454 usage_error( "Command is not set.\n" );
456 i = 0;
457 do {
458 if ( streq(ppsz_argv[optind], options[i].opt) )
460 opt = options[i];
461 break;
463 } while ( options[++i].opt );
465 if ( !opt.opt )
466 usage_error( "Unknown command: %s\n", p_cmd );
468 if ( opt.nparams == 1 && !p_arg1 )
469 usage_error( "%s option needs parameter.\n", opt.opt );
471 if ( opt.nparams == 2 && (!p_arg1 || !p_arg2) )
472 usage_error( "%s option needs two parameters.\n", opt.opt );
473 #undef usage_error
475 /* Create client socket name */
476 char *tmpdir = getenv("TMPDIR");
477 snprintf( psz_client_socket, PATH_MAX - 1, "%s/%s",
478 tmpdir ? tmpdir : "/tmp", client_socket_tmpl );
479 psz_client_socket[PATH_MAX - 1] = '\0';
481 int tmp_fd = mkstemp(psz_client_socket);
482 if ( tmp_fd > -1 ) {
483 close(tmp_fd);
484 unlink(psz_client_socket);
485 } else {
486 return_error( "Cannot build UNIX socket %s (%s)", psz_client_socket, strerror(errno) );
489 if ( (i_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) < 0 )
490 return_error( "Cannot create UNIX socket (%s)", strerror(errno) );
492 i = COMM_MAX_MSG_CHUNK;
493 setsockopt( i_fd, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i) );
495 memset( &sun_client, 0, sizeof(sun_client) );
496 sun_client.sun_family = AF_UNIX;
497 strncpy( sun_client.sun_path, psz_client_socket,
498 sizeof(sun_client.sun_path) );
499 sun_client.sun_path[sizeof(sun_client.sun_path) - 1] = '\0';
501 if ( bind( i_fd, (struct sockaddr *)&sun_client,
502 SUN_LEN(&sun_client) ) < 0 )
503 return_error( "Cannot bind (%s)", strerror(errno) );
505 memset( &sun_server, 0, sizeof(sun_server) );
506 sun_server.sun_family = AF_UNIX;
507 strncpy( sun_server.sun_path, psz_srv_socket, sizeof(sun_server.sun_path) );
508 sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0';
510 p_buffer[0] = COMM_HEADER_MAGIC;
511 p_buffer[1] = opt.cmd;
512 memset( p_buffer + 2, 0, COMM_HEADER_SIZE - 2 );
513 i_size = COMM_HEADER_SIZE;
515 /* Handle commands that send parameters */
516 switch ( opt.cmd )
518 case CMD_INVALID:
519 case CMD_RELOAD:
520 case CMD_SHUTDOWN:
521 case CMD_FRONTEND_STATUS:
522 case CMD_GET_PAT:
523 case CMD_GET_CAT:
524 case CMD_GET_NIT:
525 case CMD_GET_SDT:
526 case CMD_GET_PIDS:
527 /* These commands need no special handling because they have no parameters */
528 break;
529 case CMD_GET_EIT_PF:
530 case CMD_GET_EIT_SCHEDULE:
531 case CMD_GET_PMT:
533 uint16_t i_sid = atoi(p_arg1);
534 i_size = COMM_HEADER_SIZE + 2;
535 p_data[0] = (uint8_t)((i_sid >> 8) & 0xff);
536 p_data[1] = (uint8_t)(i_sid & 0xff);
537 break;
539 case CMD_GET_PID:
541 i_pid = (uint16_t)atoi(p_arg1);
542 i_size = COMM_HEADER_SIZE + 2;
543 p_data[0] = (uint8_t)((i_pid >> 8) & 0xff);
544 p_data[1] = (uint8_t)(i_pid & 0xff);
545 break;
547 default:
548 /* This should not happen */
549 return_error( "Unhandled option (%d)", opt.cmd );
552 if ( i_timeout_seconds > 0 ) {
553 struct timeval tv_timeout = {
554 .tv_sec = i_timeout_seconds,
555 .tv_usec = 0,
557 if ( setsockopt( i_fd, SOL_SOCKET, SO_SNDTIMEO, &tv_timeout, sizeof( tv_timeout ) ) != 0 )
558 return_error( "Cannot set SO_SNDTIMEO (%s)", strerror(errno) );
560 if ( setsockopt( i_fd, SOL_SOCKET, SO_RCVTIMEO, &tv_timeout, sizeof( tv_timeout ) ) != 0 )
561 return_error( "Cannot set SO_RCVTIMEO (%s)", strerror(errno) );
564 /* Send command and receive answer */
565 if ( sendto( i_fd, p_buffer, i_size, 0, (struct sockaddr *)&sun_server,
566 SUN_LEN(&sun_server) ) < 0 )
567 return_error( "Cannot send comm socket (%s)", strerror(errno) );
569 uint32_t i_packet_size = 0, i_received = 0;
570 do {
571 i_size = recv( i_fd, p_buffer + i_received, COMM_MAX_MSG_CHUNK, 0 );
572 if ( i_size == -1 )
573 break;
574 if ( !i_packet_size ) {
575 uint32_t *p_packet_size = (uint32_t *)&p_buffer[4];
576 i_packet_size = *p_packet_size;
577 if ( i_packet_size > COMM_BUFFER_SIZE ) {
578 i_size = -1;
579 break;
582 i_received += i_size;
583 } while ( i_received < i_packet_size );
585 clean_client_socket();
586 if ( i_packet_size < COMM_HEADER_SIZE )
587 return_error( "Cannot recv from comm socket, size:%"PRIu32" (%s)", i_packet_size, strerror(errno) );
589 /* Process answer */
590 if ( p_buffer[0] != COMM_HEADER_MAGIC )
591 return_error( "Wrong protocol version 0x%x", p_buffer[0] );
593 now = mdate();
595 ctl_cmd_answer_t c_answer = p_buffer[1];
596 switch ( c_answer )
598 case RET_OK:
599 break;
601 case RET_ERR:
602 return_error( "Request failed" );
603 break;
605 case RET_HUH:
606 return_error( "Internal error" );
607 break;
609 case RET_NODATA:
610 return_error( "No data" );
611 break;
613 case RET_PAT:
614 case RET_CAT:
615 case RET_NIT:
616 case RET_SDT:
618 uint8_t *p_flat_data = p_buffer + COMM_HEADER_SIZE;
619 unsigned int i_flat_data_size = i_packet_size - COMM_HEADER_SIZE;
620 uint8_t **pp_sections = psi_unpack_sections( p_flat_data, i_flat_data_size );
621 if ( !pp_sections )
623 return_error( "Error unpacking PSI" );
624 break;
627 switch( c_answer )
629 case RET_PAT: pat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
630 case RET_CAT: cat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
631 case RET_NIT: nit_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
632 case RET_SDT: sdt_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
633 default: break; /* Can't happen */
636 psi_table_free( pp_sections );
637 free( pp_sections );
638 break;
641 case RET_EIT_PF:
642 case RET_EIT_SCHEDULE:
644 uint8_t *p_eit_data = p_buffer + COMM_HEADER_SIZE;
645 unsigned int i_eit_data_size = i_received - COMM_HEADER_SIZE;
646 print_eit( p_eit_data, i_eit_data_size, psi_print, NULL, psi_iconv, NULL, i_print_type );
647 break;
650 case RET_PMT:
652 pmt_print( p_data, psi_print, NULL, psi_iconv, NULL, i_print_type );
653 break;
656 case RET_PID:
658 print_pids_header();
659 print_pid( i_pid, (ts_pid_info_t *)p_data );
660 print_pids_footer();
661 break;
664 case RET_PIDS:
666 print_pids( p_data );
667 break;
670 #ifdef HAVE_DVB_SUPPORT
671 case RET_FRONTEND_STATUS:
673 int ret = 1;
674 struct ret_frontend_status *p_ret =
675 (struct ret_frontend_status *)&p_buffer[COMM_HEADER_SIZE];
676 if ( i_packet_size != COMM_HEADER_SIZE + sizeof(struct ret_frontend_status) )
677 return_error( "Bad frontend status" );
679 if ( i_print_type == PRINT_XML )
680 printf("<FRONTEND>\n");
682 #define PRINT_TYPE( x ) \
683 do { \
684 if ( i_print_type == PRINT_XML ) \
685 printf( " <TYPE type=\"%s\"/>\n", STRINGIFY(x) ); \
686 else \
687 printf( "type: %s\n", STRINGIFY(x) ); \
688 } while(0)
689 switch ( p_ret->info.type )
691 case FE_QPSK: PRINT_TYPE(QPSK); break;
692 case FE_QAM : PRINT_TYPE(QAM); break;
693 case FE_OFDM: PRINT_TYPE(OFDM); break;
694 case FE_ATSC: PRINT_TYPE(ATSC); break;
695 default : PRINT_TYPE(UNKNOWN); break;
697 #undef PRINT_TYPE
699 #define PRINT_INFO( x ) \
700 do { \
701 if ( i_print_type == PRINT_XML ) \
702 printf( " <SETTING %s=\"%u\"/>\n", STRINGIFY(x), p_ret->info.x ); \
703 else \
704 printf( "%s: %u\n", STRINGIFY(x), p_ret->info.x ); \
705 } while(0)
707 if ( i_print_type == PRINT_XML )
708 printf( " <SETTING name=\"%s\"/>\n", p_ret->info.name );
709 else
710 printf( "name: %s\n", p_ret->info.name );
712 PRINT_INFO( frequency_min );
713 PRINT_INFO( frequency_max );
714 PRINT_INFO( frequency_stepsize );
715 PRINT_INFO( frequency_tolerance );
716 PRINT_INFO( symbol_rate_min );
717 PRINT_INFO( symbol_rate_max );
718 PRINT_INFO( symbol_rate_tolerance );
719 PRINT_INFO( notifier_delay );
720 #undef PRINT_INFO
722 if ( i_print_type == PRINT_TEXT )
723 printf("\ncapability list:\n");
725 #define PRINT_CAPS( x ) \
726 do { \
727 if ( p_ret->info.caps & (FE_##x) ) { \
728 if ( i_print_type == PRINT_XML ) { \
729 printf( " <CAPABILITY %s=\"1\"/>\n", STRINGIFY(x) ); \
730 } else { \
731 printf( "%s\n", STRINGIFY(x) ); \
734 } while(0)
735 PRINT_CAPS( IS_STUPID );
736 PRINT_CAPS( CAN_INVERSION_AUTO );
737 PRINT_CAPS( CAN_FEC_1_2 );
738 PRINT_CAPS( CAN_FEC_2_3 );
739 PRINT_CAPS( CAN_FEC_3_4 );
740 PRINT_CAPS( CAN_FEC_4_5 );
741 PRINT_CAPS( CAN_FEC_5_6 );
742 PRINT_CAPS( CAN_FEC_6_7 );
743 PRINT_CAPS( CAN_FEC_7_8 );
744 PRINT_CAPS( CAN_FEC_8_9 );
745 PRINT_CAPS( CAN_FEC_AUTO );
746 PRINT_CAPS( CAN_QPSK );
747 PRINT_CAPS( CAN_QAM_16 );
748 PRINT_CAPS( CAN_QAM_32 );
749 PRINT_CAPS( CAN_QAM_64 );
750 PRINT_CAPS( CAN_QAM_128 );
751 PRINT_CAPS( CAN_QAM_256 );
752 PRINT_CAPS( CAN_QAM_AUTO );
753 PRINT_CAPS( CAN_TRANSMISSION_MODE_AUTO );
754 PRINT_CAPS( CAN_BANDWIDTH_AUTO );
755 PRINT_CAPS( CAN_GUARD_INTERVAL_AUTO );
756 PRINT_CAPS( CAN_HIERARCHY_AUTO );
757 PRINT_CAPS( CAN_MUTE_TS );
759 #if DVBAPI_VERSION >= 301
760 PRINT_CAPS( CAN_8VSB );
761 PRINT_CAPS( CAN_16VSB );
762 PRINT_CAPS( NEEDS_BENDING );
763 PRINT_CAPS( CAN_RECOVER );
764 #endif
765 #if DVBAPI_VERSION >= 500
766 PRINT_CAPS( HAS_EXTENDED_CAPS );
767 #endif
768 #if DVBAPI_VERSION >= 501
769 PRINT_CAPS( CAN_2G_MODULATION );
770 #endif
771 #if DVBAPI_VERSION >= 508
772 PRINT_CAPS( CAN_TURBO_FEC );
773 PRINT_CAPS( CAN_MULTISTREAM );
774 #endif
775 #undef PRINT_CAPS
777 if ( i_print_type == PRINT_TEXT )
778 printf("\nstatus:\n");
780 #define PRINT_STATUS( x ) \
781 do { \
782 if ( p_ret->i_status & (FE_##x) ) { \
783 if ( i_print_type == PRINT_XML ) { \
784 printf( " <STATUS status=\"%s\"/>\n", STRINGIFY(x) ); \
785 } else { \
786 printf( "%s\n", STRINGIFY(x) ); \
789 } while(0)
790 PRINT_STATUS( HAS_SIGNAL );
791 PRINT_STATUS( HAS_CARRIER );
792 PRINT_STATUS( HAS_VITERBI );
793 PRINT_STATUS( HAS_SYNC );
794 PRINT_STATUS( HAS_LOCK );
795 PRINT_STATUS( REINIT );
796 #undef PRINT_STATUS
798 if ( p_ret->i_status & FE_HAS_LOCK )
800 if ( i_print_type == PRINT_XML )
802 printf(" <VALUE bit_error_rate=\"%u\"/>\n", p_ret->i_ber);
803 printf(" <VALUE signal_strength=\"%u\"/>\n", p_ret->i_strength);
804 printf(" <VALUE SNR=\"%u\"/>\n", p_ret->i_snr);
805 } else {
806 printf("\nBit error rate: %u\n", p_ret->i_ber);
807 printf("Signal strength: %u\n", p_ret->i_strength);
808 printf("SNR: %u\n", p_ret->i_snr);
810 ret = 0;
813 if ( i_print_type == PRINT_XML )
814 printf("</FRONTEND>\n" );
816 exit(ret);
817 break;
819 #endif
821 default:
822 return_error( "Unknown command answer: %u", c_answer );
825 if (iconv_handle != (iconv_t)-1) {
826 iconv_close(iconv_handle);
827 iconv_handle = (iconv_t)-1;
830 return 0;