fix EIT schedule signaling in SDT
[dvblast.git] / dvblastctl.c
blob378cb47be91e5042625425aee13fe32cc36451e3
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 "en50221.h"
50 #include "comm.h"
52 int i_verbose = 3;
53 int i_syslog = 0;
55 print_type_t i_print_type = PRINT_TEXT;
56 mtime_t now;
58 int i_fd = -1;
59 char psz_client_socket[PATH_MAX] = {0};
61 static iconv_t iconv_handle = (iconv_t)-1;
63 static void clean_client_socket() {
64 if ( i_fd > -1 )
66 close( i_fd );
67 i_fd = -1;
69 if ( psz_client_socket[0] )
71 unlink( psz_client_socket );
72 psz_client_socket[0] = '\0';
76 /*****************************************************************************
77 * The following two functions are from biTStream's examples and are under the
78 * WTFPL (see LICENSE.WTFPL).
79 ****************************************************************************/
80 __attribute__ ((format(printf, 2, 3)))
81 static void psi_print(void *_unused, const char *psz_format, ...)
83 char psz_fmt[strlen(psz_format) + 2];
84 va_list args;
85 va_start(args, psz_format);
86 strcpy(psz_fmt, psz_format);
87 strcat(psz_fmt, "\n");
88 vprintf(psz_fmt, args);
89 va_end(args);
92 __attribute__ ((format(printf, 1, 2)))
93 void return_error( const char *psz_format, ... )
95 va_list args;
96 char psz_fmt[1024];
98 clean_client_socket();
100 va_start( args, psz_format );
101 if ( i_print_type == PRINT_XML )
102 snprintf( psz_fmt, sizeof(psz_fmt) - 1, "<ERROR msg=\"%s\"/>\n", psz_format );
103 else
104 snprintf( psz_fmt, sizeof(psz_fmt) - 1, "ERROR: %s\n", psz_format );
105 psz_fmt[sizeof(psz_fmt) - 1] = '\0';
106 vfprintf( stderr, psz_fmt, args );
107 va_end(args);
108 exit(255);
111 static char *iconv_append_null(const char *p_string, size_t i_length)
113 char *psz_string = malloc(i_length + 1);
114 memcpy(psz_string, p_string, i_length);
115 psz_string[i_length] = '\0';
116 return psz_string;
119 const char *psz_native_charset = "UTF-8";
121 char *psi_iconv(void *_unused, const char *psz_encoding,
122 char *p_string, size_t i_length)
124 static const char *psz_current_encoding = "";
126 char *psz_string, *p;
127 size_t i_out_length;
129 if (!strcmp(psz_encoding, psz_native_charset))
130 return iconv_append_null(p_string, i_length);
132 if (iconv_handle != (iconv_t)-1 &&
133 strcmp(psz_encoding, psz_current_encoding)) {
134 iconv_close(iconv_handle);
135 iconv_handle = (iconv_t)-1;
138 if (iconv_handle == (iconv_t)-1)
139 iconv_handle = iconv_open(psz_native_charset, psz_encoding);
140 if (iconv_handle == (iconv_t)-1) {
141 msg_Warn(NULL, "couldn't open converter from %s to %s (%m)", psz_encoding,
142 psz_native_charset);
143 return iconv_append_null(p_string, i_length);
145 psz_current_encoding = psz_encoding;
147 /* converted strings can be up to six times larger */
148 i_out_length = i_length * 6;
149 p = psz_string = malloc(i_out_length);
150 if (iconv(iconv_handle, &p_string, &i_length, &p, &i_out_length) == -1) {
151 msg_Warn(NULL, "couldn't convert from %s to %s (%m)", psz_encoding,
152 psz_native_charset);
153 free(psz_string);
154 return iconv_append_null(p_string, i_length);
156 if (i_length)
157 msg_Warn(NULL, "partial conversion from %s to %s", psz_encoding,
158 psz_native_charset);
160 *p = '\0';
161 return psz_string;
164 void print_pids_header( void )
166 if ( i_print_type == PRINT_XML )
167 printf("<PIDS>\n");
170 void print_pids_footer( void )
172 if ( i_print_type == PRINT_XML )
173 printf("</PIDS>\n");
176 void print_pid(uint16_t i_pid, ts_pid_info_t *p_info)
178 if ( p_info->i_packets == 0 )
179 return;
180 if ( i_print_type == PRINT_TEXT )
181 printf("pid %d packn %lu ccerr %lu tserr %lu scramble %d Bps %lu seen %"PRId64"\n",
182 i_pid,
183 p_info->i_packets,
184 p_info->i_cc_errors,
185 p_info->i_transport_errors,
186 p_info->i_scrambling,
187 p_info->i_bytes_per_sec,
188 now - p_info->i_last_packet_ts
190 else
191 printf("<PID pid=\"%d\" packn=\"%lu\" ccerr=\"%lu\" tserr=\"%lu\" scramble=\"%d\" Bps=\"%lu\" seen=\"%"PRId64"\" />\n",
192 i_pid,
193 p_info->i_packets,
194 p_info->i_cc_errors,
195 p_info->i_transport_errors,
196 p_info->i_scrambling,
197 p_info->i_bytes_per_sec,
198 now - p_info->i_last_packet_ts
202 void print_pids( uint8_t *p_data )
204 int i_pid;
205 print_pids_header();
206 for ( i_pid = 0; i_pid < MAX_PIDS; i_pid++ ) {
207 ts_pid_info_t *p_info = (ts_pid_info_t *)(p_data + i_pid * sizeof(ts_pid_info_t));
208 print_pid( i_pid, p_info );
210 print_pids_footer();
213 struct dvblastctl_option {
214 char * opt;
215 int nparams;
216 ctl_cmd_t cmd;
219 static const struct dvblastctl_option options[] =
221 { "reload", 0, CMD_RELOAD },
222 { "shutdown", 0, CMD_SHUTDOWN },
224 { "fe_status", 0, CMD_FRONTEND_STATUS },
225 { "mmi_status", 0, CMD_MMI_STATUS },
227 { "mmi_slot_status", 1, CMD_MMI_SLOT_STATUS }, /* arg: slot */
228 { "mmi_open", 1, CMD_MMI_OPEN }, /* arg: slot */
229 { "mmi_close", 1, CMD_MMI_CLOSE }, /* arg: slot */
230 { "mmi_get", 1, CMD_MMI_RECV }, /* arg: slot */
231 { "mmi_send_text", 1, CMD_MMI_SEND_TEXT }, /* arg: slot, en50221_mmi_object_t */
232 { "mmi_send_choice", 2, CMD_MMI_SEND_CHOICE }, /* arg: slot, en50221_mmi_object_t */
234 { "get_pat", 0, CMD_GET_PAT },
235 { "get_cat", 0, CMD_GET_CAT },
236 { "get_nit", 0, CMD_GET_NIT },
237 { "get_sdt", 0, CMD_GET_SDT },
238 { "get_pmt", 1, CMD_GET_PMT }, /* arg: service_id (uint16_t) */
239 { "get_pids", 0, CMD_GET_PIDS },
240 { "get_pid", 1, CMD_GET_PID }, /* arg: pid (uint16_t) */
242 { NULL, 0, 0 }
245 void usage()
247 printf("DVBlastctl %s (%s)\n", VERSION, VERSION_EXTRA );
248 printf("Usage: dvblastctl -r <remote socket> [-x <text|xml>] [cmd]\n");
249 printf("Options:\n");
250 printf(" -r --remote-socket <name> Set socket name to <name>.\n" );
251 printf(" -x --print <text|xml> Choose output format for info commands.\n" );
252 printf("Control commands:\n");
253 printf(" reload Reload configuration.\n");
254 printf(" shutdown Shutdown DVBlast.\n");
255 #ifdef HAVE_DVB_SUPPORT
256 printf("Status commands:\n");
257 printf(" fe_status Read frontend status information.\n");
258 printf(" mmi_status Read CAM status.\n");
259 printf("MMI commands:\n");
260 printf(" mmi_slot_status <slot> Read MMI slot status.\n");
261 printf(" mmi_open <slot> Open MMI slot.\n");
262 printf(" mmi_close <slot> Close MMI slot.\n");
263 printf(" mmi_get <slot> Read MMI slot.\n");
264 printf(" mmi_send_text <slot> <text> Send text to MMI slot.\n");
265 printf(" mmi_send_choice <slot> <choice> Send choice to MMI slot.\n");
266 #endif
267 printf("Demux info commands:\n");
268 printf(" get_pat Return last PAT table.\n");
269 printf(" get_cat Return last CAT table.\n");
270 printf(" get_nit Return last NIT table.\n");
271 printf(" get_sdt Return last SDT table.\n");
272 printf(" get_pmt <service_id> Return last PMT table.\n");
273 printf(" get_pids Return info about all pids.\n");
274 printf(" get_pid <pid> Return info for chosen pid only.\n");
275 printf("\n");
276 exit(1);
279 int main( int i_argc, char **ppsz_argv )
281 char *client_socket_tmpl = "dvblastctl.clientsock.XXXXXX";
282 char *psz_srv_socket = NULL;
283 int i;
284 char *p_cmd, *p_arg1 = NULL, *p_arg2 = NULL;
285 ssize_t i_size;
286 struct sockaddr_un sun_client, sun_server;
287 uint8_t p_buffer[COMM_BUFFER_SIZE];
288 uint8_t *p_data = p_buffer + COMM_HEADER_SIZE;
289 uint16_t i_pid = 0;
290 struct dvblastctl_option opt = { 0, 0, 0 };
292 for ( ; ; )
294 int c;
296 static const struct option long_options[] =
298 {"remote-socket", required_argument, NULL, 'r'},
299 {"print", required_argument, NULL, 'x'},
300 {"help", no_argument, NULL, 'h'},
301 {0, 0, 0, 0}
304 if ( (c = getopt_long(i_argc, ppsz_argv, "r:x:h", long_options, NULL)) == -1 )
305 break;
307 switch ( c )
309 case 'r':
310 psz_srv_socket = optarg;
311 break;
313 case 'x':
314 if ( !strcmp(optarg, "text") )
315 i_print_type = PRINT_TEXT;
316 else if ( !strcmp(optarg, "xml") )
317 i_print_type = PRINT_XML;
318 else
319 msg_Warn( NULL, "unrecognized print type %s", optarg );
320 /* Make stdout line-buffered */
321 setvbuf(stdout, NULL, _IOLBF, 0);
322 break;
324 case 'h':
325 default:
326 usage();
330 /* Validate commands */
331 #define usage_error(msg, ...) \
332 do { \
333 msg_Err( NULL, msg, ##__VA_ARGS__ ); \
334 usage(); \
335 } while(0)
336 p_cmd = ppsz_argv[optind];
337 p_arg1 = ppsz_argv[optind + 1];
338 p_arg2 = ppsz_argv[optind + 2];
340 if ( !psz_srv_socket )
341 usage_error( "Remote socket is not set.\n" );
343 if ( !p_cmd )
344 usage_error( "Command is not set.\n" );
346 i = 0;
347 do {
348 if ( streq(ppsz_argv[optind], options[i].opt) )
350 opt = options[i];
351 break;
353 } while ( options[++i].opt );
355 if ( !opt.opt )
356 usage_error( "Unknown command: %s\n", p_cmd );
358 if ( opt.nparams == 1 && !p_arg1 )
359 usage_error( "%s option needs parameter.\n", opt.opt );
361 if ( opt.nparams == 2 && (!p_arg1 || !p_arg2) )
362 usage_error( "%s option needs two parameters.\n", opt.opt );
363 #undef usage_error
365 /* Create client socket name */
366 char *tmpdir = getenv("TMPDIR");
367 snprintf( psz_client_socket, PATH_MAX - 1, "%s/%s",
368 tmpdir ? tmpdir : "/tmp", client_socket_tmpl );
369 psz_client_socket[PATH_MAX - 1] = '\0';
371 int tmp_fd = mkstemp(psz_client_socket);
372 if ( tmp_fd > -1 ) {
373 close(tmp_fd);
374 unlink(psz_client_socket);
375 } else {
376 return_error( "Cannot build UNIX socket %s (%s)", psz_client_socket, strerror(errno) );
379 if ( (i_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) < 0 )
380 return_error( "Cannot create UNIX socket (%s)", strerror(errno) );
382 i = COMM_MAX_MSG_CHUNK;
383 setsockopt( i_fd, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i) );
385 memset( &sun_client, 0, sizeof(sun_client) );
386 sun_client.sun_family = AF_UNIX;
387 strncpy( sun_client.sun_path, psz_client_socket,
388 sizeof(sun_client.sun_path) );
389 sun_client.sun_path[sizeof(sun_client.sun_path) - 1] = '\0';
391 if ( bind( i_fd, (struct sockaddr *)&sun_client,
392 SUN_LEN(&sun_client) ) < 0 )
393 return_error( "Cannot bind (%s)", strerror(errno) );
395 memset( &sun_server, 0, sizeof(sun_server) );
396 sun_server.sun_family = AF_UNIX;
397 strncpy( sun_server.sun_path, psz_srv_socket, sizeof(sun_server.sun_path) );
398 sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0';
400 p_buffer[0] = COMM_HEADER_MAGIC;
401 p_buffer[1] = opt.cmd;
402 memset( p_buffer + 2, 0, COMM_HEADER_SIZE - 2 );
403 i_size = COMM_HEADER_SIZE;
405 /* Handle commands that send parameters */
406 switch ( opt.cmd )
408 case CMD_INVALID:
409 case CMD_RELOAD:
410 case CMD_SHUTDOWN:
411 case CMD_FRONTEND_STATUS:
412 case CMD_MMI_STATUS:
413 case CMD_GET_PAT:
414 case CMD_GET_CAT:
415 case CMD_GET_NIT:
416 case CMD_GET_SDT:
417 case CMD_GET_PIDS:
418 /* These commands need no special handling because they have no parameters */
419 break;
420 case CMD_GET_PMT:
422 uint16_t i_sid = atoi(p_arg1);
423 i_size = COMM_HEADER_SIZE + 2;
424 p_data[0] = (uint8_t)((i_sid >> 8) & 0xff);
425 p_data[1] = (uint8_t)(i_sid & 0xff);
426 break;
428 case CMD_GET_PID:
430 i_pid = (uint16_t)atoi(p_arg1);
431 i_size = COMM_HEADER_SIZE + 2;
432 p_data[0] = (uint8_t)((i_pid >> 8) & 0xff);
433 p_data[1] = (uint8_t)(i_pid & 0xff);
434 break;
436 #ifdef HAVE_DVB_SUPPORT
437 case CMD_MMI_SEND_TEXT:
439 struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data;
440 p_cmd->i_slot = atoi(p_arg1);
442 en50221_mmi_object_t object;
443 object.i_object_type = EN50221_MMI_ANSW;
444 if ( !p_arg2 || p_arg2[0] == '\0' )
446 object.u.answ.b_ok = 0;
447 object.u.answ.psz_answ = "";
449 else
451 object.u.answ.b_ok = 1;
452 object.u.answ.psz_answ = p_arg2;
454 i_size = COMM_BUFFER_SIZE - COMM_HEADER_SIZE
455 - ((void *)&p_cmd->object - (void *)p_cmd);
456 if ( en50221_SerializeMMIObject( (uint8_t *)&p_cmd->object,
457 &i_size, &object ) == -1 )
458 return_error( "Comm buffer is too small" );
460 i_size += COMM_HEADER_SIZE
461 + ((void *)&p_cmd->object - (void *)p_cmd);
462 break;
464 case CMD_MMI_SEND_CHOICE:
466 struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data;
467 p_cmd->i_slot = atoi(p_arg1);
469 i_size = COMM_HEADER_SIZE + sizeof(struct cmd_mmi_send);
470 p_cmd->object.i_object_type = EN50221_MMI_MENU_ANSW;
471 p_cmd->object.u.menu_answ.i_choice = atoi(p_arg2);
472 break;
474 case CMD_MMI_SLOT_STATUS:
475 case CMD_MMI_OPEN:
476 case CMD_MMI_CLOSE:
477 case CMD_MMI_RECV:
479 p_data[0] = atoi(p_arg1);
480 i_size = COMM_HEADER_SIZE + 1;
481 break;
483 #endif
484 default:
485 /* This should not happen */
486 return_error( "Unhandled option (%d)", opt.cmd );
489 /* Send command and receive answer */
490 if ( sendto( i_fd, p_buffer, i_size, 0, (struct sockaddr *)&sun_server,
491 SUN_LEN(&sun_server) ) < 0 )
492 return_error( "Cannot send comm socket (%s)", strerror(errno) );
494 uint32_t i_packet_size = 0, i_received = 0;
495 do {
496 i_size = recv( i_fd, p_buffer + i_received, COMM_MAX_MSG_CHUNK, 0 );
497 if ( i_size == -1 )
498 break;
499 if ( !i_packet_size ) {
500 uint32_t *p_packet_size = (uint32_t *)&p_buffer[4];
501 i_packet_size = *p_packet_size;
502 if ( i_packet_size > COMM_BUFFER_SIZE ) {
503 i_size = -1;
504 break;
507 i_received += i_size;
508 } while ( i_received < i_packet_size );
510 clean_client_socket();
511 if ( i_size < COMM_HEADER_SIZE )
512 return_error( "Cannot recv from comm socket, size:%zd (%s)", i_size, strerror(errno) );
514 /* Process answer */
515 if ( p_buffer[0] != COMM_HEADER_MAGIC )
516 return_error( "Wrong protocol version 0x%x", p_buffer[0] );
518 now = mdate();
520 ctl_cmd_answer_t c_answer = p_buffer[1];
521 switch ( c_answer )
523 case RET_OK:
524 break;
526 case RET_MMI_WAIT:
527 exit(252);
528 break;
530 case RET_ERR:
531 return_error( "Request failed" );
532 break;
534 case RET_HUH:
535 return_error( "Internal error" );
536 break;
538 case RET_NODATA:
539 return_error( "No data" );
540 break;
542 case RET_PAT:
543 case RET_CAT:
544 case RET_NIT:
545 case RET_SDT:
547 uint8_t *p_flat_data = p_buffer + COMM_HEADER_SIZE;
548 unsigned int i_flat_data_size = i_size - COMM_HEADER_SIZE;
549 uint8_t **pp_sections = psi_unpack_sections( p_flat_data, i_flat_data_size );
550 if ( !pp_sections )
552 return_error( "Error unpacking PSI" );
553 break;
556 switch( c_answer )
558 case RET_PAT: pat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
559 case RET_CAT: cat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
560 case RET_NIT: nit_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
561 case RET_SDT: sdt_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
562 default: break; /* Can't happen */
565 psi_table_free( pp_sections );
566 free( pp_sections );
567 break;
570 case RET_PMT:
572 pmt_print( p_data, psi_print, NULL, psi_iconv, NULL, i_print_type );
573 break;
576 case RET_PID:
578 print_pids_header();
579 print_pid( i_pid, (ts_pid_info_t *)p_data );
580 print_pids_footer();
581 break;
584 case RET_PIDS:
586 print_pids( p_data );
587 break;
590 #ifdef HAVE_DVB_SUPPORT
591 case RET_FRONTEND_STATUS:
593 int ret = 1;
594 struct ret_frontend_status *p_ret =
595 (struct ret_frontend_status *)&p_buffer[COMM_HEADER_SIZE];
596 if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_frontend_status) )
597 return_error( "Bad frontend status" );
599 if ( i_print_type == PRINT_XML )
600 printf("<FRONTEND>\n");
602 #define PRINT_TYPE( x ) \
603 do { \
604 if ( i_print_type == PRINT_XML ) \
605 printf( " <TYPE type=\"%s\"/>\n", STRINGIFY(x) ); \
606 else \
607 printf( "type: %s\n", STRINGIFY(x) ); \
608 } while(0)
609 switch ( p_ret->info.type )
611 case FE_QPSK: PRINT_TYPE(QPSK); break;
612 case FE_QAM : PRINT_TYPE(QAM); break;
613 case FE_OFDM: PRINT_TYPE(OFDM); break;
614 case FE_ATSC: PRINT_TYPE(ATSC); break;
615 default : PRINT_TYPE(UNKNOWN); break;
617 #undef PRINT_TYPE
619 #define PRINT_INFO( x ) \
620 do { \
621 if ( i_print_type == PRINT_XML ) \
622 printf( " <SETTING %s=\"%u\"/>\n", STRINGIFY(x), p_ret->info.x ); \
623 else \
624 printf( "%s: %u\n", STRINGIFY(x), p_ret->info.x ); \
625 } while(0)
627 if ( i_print_type == PRINT_XML )
628 printf( " <SETTING name=\"%s\"/>\n", p_ret->info.name );
629 else
630 printf( "name: %s\n", p_ret->info.name );
632 PRINT_INFO( frequency_min );
633 PRINT_INFO( frequency_max );
634 PRINT_INFO( frequency_stepsize );
635 PRINT_INFO( frequency_tolerance );
636 PRINT_INFO( symbol_rate_min );
637 PRINT_INFO( symbol_rate_max );
638 PRINT_INFO( symbol_rate_tolerance );
639 PRINT_INFO( notifier_delay );
640 #undef PRINT_INFO
642 if ( i_print_type == PRINT_TEXT )
643 printf("\ncapability list:\n");
645 #define PRINT_CAPS( x ) \
646 do { \
647 if ( p_ret->info.caps & (FE_##x) ) { \
648 if ( i_print_type == PRINT_XML ) { \
649 printf( " <CAPABILITY %s=\"1\"/>\n", STRINGIFY(x) ); \
650 } else { \
651 printf( "%s\n", STRINGIFY(x) ); \
654 } while(0)
655 PRINT_CAPS( IS_STUPID );
656 PRINT_CAPS( CAN_INVERSION_AUTO );
657 PRINT_CAPS( CAN_FEC_1_2 );
658 PRINT_CAPS( CAN_FEC_2_3 );
659 PRINT_CAPS( CAN_FEC_3_4 );
660 PRINT_CAPS( CAN_FEC_4_5 );
661 PRINT_CAPS( CAN_FEC_5_6 );
662 PRINT_CAPS( CAN_FEC_6_7 );
663 PRINT_CAPS( CAN_FEC_7_8 );
664 PRINT_CAPS( CAN_FEC_8_9 );
665 PRINT_CAPS( CAN_FEC_AUTO );
666 PRINT_CAPS( CAN_QPSK );
667 PRINT_CAPS( CAN_QAM_16 );
668 PRINT_CAPS( CAN_QAM_32 );
669 PRINT_CAPS( CAN_QAM_64 );
670 PRINT_CAPS( CAN_QAM_128 );
671 PRINT_CAPS( CAN_QAM_256 );
672 PRINT_CAPS( CAN_QAM_AUTO );
673 PRINT_CAPS( CAN_TRANSMISSION_MODE_AUTO );
674 PRINT_CAPS( CAN_BANDWIDTH_AUTO );
675 PRINT_CAPS( CAN_GUARD_INTERVAL_AUTO );
676 PRINT_CAPS( CAN_HIERARCHY_AUTO );
677 PRINT_CAPS( CAN_MUTE_TS );
679 #if DVBAPI_VERSION >= 301
680 PRINT_CAPS( CAN_8VSB );
681 PRINT_CAPS( CAN_16VSB );
682 PRINT_CAPS( NEEDS_BENDING );
683 PRINT_CAPS( CAN_RECOVER );
684 #endif
685 #if DVBAPI_VERSION >= 500
686 PRINT_CAPS( HAS_EXTENDED_CAPS );
687 #endif
688 #if DVBAPI_VERSION >= 501
689 PRINT_CAPS( CAN_2G_MODULATION );
690 #endif
691 #if DVBAPI_VERSION >= 508
692 PRINT_CAPS( CAN_MULTISTREAM );
693 #endif
694 #undef PRINT_CAPS
696 if ( i_print_type == PRINT_TEXT )
697 printf("\nstatus:\n");
699 #define PRINT_STATUS( x ) \
700 do { \
701 if ( p_ret->i_status & (FE_##x) ) { \
702 if ( i_print_type == PRINT_XML ) { \
703 printf( " <STATUS status=\"%s\"/>\n", STRINGIFY(x) ); \
704 } else { \
705 printf( "%s\n", STRINGIFY(x) ); \
708 } while(0)
709 PRINT_STATUS( HAS_SIGNAL );
710 PRINT_STATUS( HAS_CARRIER );
711 PRINT_STATUS( HAS_VITERBI );
712 PRINT_STATUS( HAS_SYNC );
713 PRINT_STATUS( HAS_LOCK );
714 PRINT_STATUS( REINIT );
715 #undef PRINT_STATUS
717 if ( p_ret->i_status & FE_HAS_LOCK )
719 if ( i_print_type == PRINT_XML )
721 printf(" <VALUE bit_error_rate=\"%u\"/>\n", p_ret->i_ber);
722 printf(" <VALUE signal_strength=\"%u\"/>\n", p_ret->i_strength);
723 printf(" <VALUE SNR=\"%u\"/>\n", p_ret->i_snr);
724 } else {
725 printf("\nBit error rate: %u\n", p_ret->i_ber);
726 printf("Signal strength: %u\n", p_ret->i_strength);
727 printf("SNR: %u\n", p_ret->i_snr);
729 ret = 0;
732 if ( i_print_type == PRINT_XML )
733 printf("</FRONTEND>\n" );
735 exit(ret);
736 break;
739 case RET_MMI_STATUS:
741 struct ret_mmi_status *p_ret =
742 (struct ret_mmi_status *)&p_buffer[COMM_HEADER_SIZE];
743 if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_mmi_status) )
744 return_error( "Bad MMI status" );
746 printf("CA interface with %d %s, type:\n", p_ret->caps.slot_num,
747 p_ret->caps.slot_num == 1 ? "slot" : "slots");
748 #define PRINT_CAPS( x, s ) \
749 if ( p_ret->caps.slot_type & (CA_##x) ) \
750 printf(s "\n");
751 PRINT_CAPS( CI, "CI high level interface" );
752 PRINT_CAPS( CI_LINK, "CI link layer level interface" );
753 PRINT_CAPS( CI_PHYS, "CI physical layer level interface (not supported)" );
754 PRINT_CAPS( DESCR, "built-in descrambler" );
755 PRINT_CAPS( SC, "simple smartcard interface" );
756 #undef PRINT_CAPS
758 printf("\n%d available %s\n", p_ret->caps.descr_num,
759 p_ret->caps.descr_num == 1 ? "descrambler (key)" :
760 "descramblers (keys)");
761 #define PRINT_DESC( x ) \
762 if ( p_ret->caps.descr_type & (CA_##x) ) \
763 printf( STRINGIFY(x) "\n" );
764 PRINT_DESC( ECD );
765 PRINT_DESC( NDS );
766 PRINT_DESC( DSS );
767 #undef PRINT_DESC
769 exit( p_ret->caps.slot_num );
770 break;
773 case RET_MMI_SLOT_STATUS:
775 struct ret_mmi_slot_status *p_ret =
776 (struct ret_mmi_slot_status *)&p_buffer[COMM_HEADER_SIZE];
777 if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_slot_status) )
778 return_error( "Bad MMI slot status" );
780 printf("CA slot #%u: ", p_ret->sinfo.num);
782 #define PRINT_TYPE( x, s ) \
783 if ( p_ret->sinfo.type & (CA_##x) ) \
784 printf(s);
786 PRINT_TYPE( CI, "high level, " );
787 PRINT_TYPE( CI_LINK, "link layer level, " );
788 PRINT_TYPE( CI_PHYS, "physical layer level, " );
789 #undef PRINT_TYPE
791 if ( p_ret->sinfo.flags & CA_CI_MODULE_READY )
793 printf("module present and ready\n");
794 exit(0);
797 if ( p_ret->sinfo.flags & CA_CI_MODULE_PRESENT )
798 printf("module present, not ready\n");
799 else
800 printf("module not present\n");
802 exit(1);
803 break;
806 case RET_MMI_RECV:
808 struct ret_mmi_recv *p_ret =
809 (struct ret_mmi_recv *)&p_buffer[COMM_HEADER_SIZE];
810 if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_recv) )
811 return_error( "Bad MMI recv" );
813 en50221_UnserializeMMIObject( &p_ret->object, i_size
814 - COMM_HEADER_SIZE - ((void *)&p_ret->object - (void *)p_ret) );
816 switch ( p_ret->object.i_object_type )
818 case EN50221_MMI_ENQ:
819 printf("%s\n", p_ret->object.u.enq.psz_text);
820 printf("(empty to cancel)\n");
821 exit(p_ret->object.u.enq.b_blind ? 253 : 254);
822 break;
824 case EN50221_MMI_MENU:
825 printf("%s\n", p_ret->object.u.menu.psz_title);
826 printf("%s\n", p_ret->object.u.menu.psz_subtitle);
827 printf("0 - Cancel\n");
828 for ( i = 0; i < p_ret->object.u.menu.i_choices; i++ )
829 printf("%d - %s\n", i + 1,
830 p_ret->object.u.menu.ppsz_choices[i]);
831 printf("%s\n", p_ret->object.u.menu.psz_bottom);
832 exit(p_ret->object.u.menu.i_choices);
833 break;
835 case EN50221_MMI_LIST:
836 printf("%s\n", p_ret->object.u.menu.psz_title);
837 printf("%s\n", p_ret->object.u.menu.psz_subtitle);
838 for ( i = 0; i < p_ret->object.u.menu.i_choices; i++ )
839 printf("%s\n", p_ret->object.u.menu.ppsz_choices[i]);
840 printf("%s\n", p_ret->object.u.menu.psz_bottom);
841 printf("(0 to cancel)\n");
842 exit(0);
843 break;
845 default:
846 return_error( "Unknown MMI object" );
847 break;
850 exit(255);
851 break;
853 #endif
855 default:
856 return_error( "Unknown command answer: %u", c_answer );
859 if (iconv_handle != (iconv_t)-1) {
860 iconv_close(iconv_handle);
861 iconv_handle = (iconv_t)-1;
864 return 0;