Factor out common code
[dvblast.git] / dvblastctl.c
blob968830d6340f165bf076674921d29b3fca7264fd
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 <bitstream/mpeg/psi.h>
42 #include <bitstream/dvb/si.h>
43 #include <bitstream/dvb/si_print.h>
44 #include <bitstream/mpeg/psi_print.h>
46 #include "dvblast.h"
47 #include "en50221.h"
48 #include "comm.h"
49 #include "version.h"
51 int i_verbose = 3;
52 int i_syslog = 0;
54 print_type_t i_print_type = PRINT_TEXT;
55 mtime_t now;
57 int i_fd = -1;
58 char psz_client_socket[PATH_MAX] = {0};
60 static void clean_client_socket() {
61 if ( i_fd > -1 )
63 close( i_fd );
64 i_fd = -1;
66 if ( psz_client_socket[0] )
68 unlink( psz_client_socket );
69 psz_client_socket[0] = '\0';
73 /*****************************************************************************
74 * The following two functions are from biTStream's examples and are under the
75 * WTFPL (see LICENSE.WTFPL).
76 ****************************************************************************/
77 __attribute__ ((format(printf, 2, 3)))
78 static void psi_print(void *_unused, const char *psz_format, ...)
80 char psz_fmt[strlen(psz_format) + 2];
81 va_list args;
82 va_start(args, psz_format);
83 strcpy(psz_fmt, psz_format);
84 strcat(psz_fmt, "\n");
85 vprintf(psz_fmt, args);
88 __attribute__ ((format(printf, 1, 2)))
89 void return_error( const char *psz_format, ... )
91 va_list args;
92 char psz_fmt[1024];
94 clean_client_socket();
96 va_start( args, psz_format );
97 if ( i_print_type == PRINT_XML )
98 snprintf( psz_fmt, sizeof(psz_fmt) - 1, "<ERROR msg=\"%s\"/>\n", psz_format );
99 else
100 snprintf( psz_fmt, sizeof(psz_fmt) - 1, "ERROR: %s\n", psz_format );
101 psz_fmt[sizeof(psz_fmt) - 1] = '\0';
102 vfprintf( stderr, psz_fmt, args );
103 exit(255);
106 static char *iconv_append_null(const char *p_string, size_t i_length)
108 char *psz_string = malloc(i_length + 1);
109 memcpy(psz_string, p_string, i_length);
110 psz_string[i_length] = '\0';
111 return psz_string;
114 char *psi_iconv(void *_unused, const char *psz_encoding,
115 char *p_string, size_t i_length)
117 return iconv_append_null(p_string, i_length);
120 void print_pids_header( void )
122 if ( i_print_type == PRINT_XML )
123 printf("<PIDS>\n");
126 void print_pids_footer( void )
128 if ( i_print_type == PRINT_XML )
129 printf("</PIDS>\n");
132 void print_pid(uint16_t i_pid, ts_pid_info_t *p_info)
134 if ( p_info->i_packets == 0 )
135 return;
136 if ( i_print_type == PRINT_TEXT )
137 printf("pid %d packn %lu ccerr %lu tserr %lu scramble %d Bps %lu seen %"PRId64"\n",
138 i_pid,
139 p_info->i_packets,
140 p_info->i_cc_errors,
141 p_info->i_transport_errors,
142 p_info->i_scrambling,
143 p_info->i_bytes_per_sec,
144 now - p_info->i_last_packet_ts
146 else
147 printf("<PID pid=\"%d\" packn=\"%lu\" ccerr=\"%lu\" tserr=\"%lu\" scramble=\"%d\" Bps=\"%lu\" seen=\"%"PRId64"\" />\n",
148 i_pid,
149 p_info->i_packets,
150 p_info->i_cc_errors,
151 p_info->i_transport_errors,
152 p_info->i_scrambling,
153 p_info->i_bytes_per_sec,
154 now - p_info->i_last_packet_ts
158 void print_pids( uint8_t *p_data )
160 int i_pid;
161 print_pids_header();
162 for ( i_pid = 0; i_pid < MAX_PIDS; i_pid++ ) {
163 ts_pid_info_t *p_info = (ts_pid_info_t *)(p_data + i_pid * sizeof(ts_pid_info_t));
164 print_pid( i_pid, p_info );
166 print_pids_footer();
169 struct dvblastctl_option {
170 char * opt;
171 int nparams;
172 ctl_cmd_t cmd;
175 static const struct dvblastctl_option options[] =
177 { "reload", 0, CMD_RELOAD },
178 { "shutdown", 0, CMD_SHUTDOWN },
180 { "fe_status", 0, CMD_FRONTEND_STATUS },
181 { "mmi_status", 0, CMD_MMI_STATUS },
183 { "mmi_slot_status", 1, CMD_MMI_SLOT_STATUS }, /* arg: slot */
184 { "mmi_open", 1, CMD_MMI_OPEN }, /* arg: slot */
185 { "mmi_close", 1, CMD_MMI_CLOSE }, /* arg: slot */
186 { "mmi_get", 1, CMD_MMI_RECV }, /* arg: slot */
187 { "mmi_send_text", 1, CMD_MMI_SEND_TEXT }, /* arg: slot, en50221_mmi_object_t */
188 { "mmi_send_choice", 2, CMD_MMI_SEND_CHOICE }, /* arg: slot, en50221_mmi_object_t */
190 { "get_pat", 0, CMD_GET_PAT },
191 { "get_cat", 0, CMD_GET_CAT },
192 { "get_nit", 0, CMD_GET_NIT },
193 { "get_sdt", 0, CMD_GET_SDT },
194 { "get_pmt", 1, CMD_GET_PMT }, /* arg: service_id (uint16_t) */
195 { "get_pids", 0, CMD_GET_PIDS },
196 { "get_pid", 1, CMD_GET_PID }, /* arg: pid (uint16_t) */
198 { NULL, 0, 0 }
201 void usage()
203 printf("DVBlastctl %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR,
204 VERSION_REVISION, VERSION_EXTRA );
205 printf("Usage: dvblastctl -r <remote socket> [-x <text|xml>] [cmd]\n");
206 printf("Options:\n");
207 printf(" -r --remote-socket <name> Set socket name to <name>.\n" );
208 printf(" -x --print <text|xml> Choose output format for info commands.\n" );
209 printf("Control commands:\n");
210 printf(" reload Reload configuration.\n");
211 printf(" shutdown Shutdown DVBlast.\n");
212 printf("Status commands:\n");
213 printf(" fe_status Read frontend status information.\n");
214 printf(" mmi_status Read CAM status.\n");
215 printf("MMI commands:\n");
216 printf(" mmi_slot_status <slot> Read MMI slot status.\n");
217 printf(" mmi_open <slot> Open MMI slot.\n");
218 printf(" mmi_close <slot> Close MMI slot.\n");
219 printf(" mmi_get <slot> Read MMI slot.\n");
220 printf(" mmi_send_text <slot> <text> Send text to MMI slot.\n");
221 printf(" mmi_send_choice <slot> <choice> Send choice to MMI slot.\n");
222 printf("Demux info commands:\n");
223 printf(" get_pat Return last PAT table.\n");
224 printf(" get_cat Return last CAT table.\n");
225 printf(" get_nit Return last NIT table.\n");
226 printf(" get_sdt Return last SDT table.\n");
227 printf(" get_pmt <service_id> Return last PMT table.\n");
228 printf(" get_pids Return info about all pids.\n");
229 printf(" get_pid <pid> Return info for chosen pid only.\n");
230 printf("\n");
231 exit(1);
234 int main( int i_argc, char **ppsz_argv )
236 char *client_socket_tmpl = "dvblastctl.clientsock.XXXXXX";
237 char *psz_srv_socket = NULL;
238 int i;
239 char *p_cmd, *p_arg1 = NULL, *p_arg2 = NULL;
240 ssize_t i_size;
241 struct sockaddr_un sun_client, sun_server;
242 uint8_t p_buffer[COMM_BUFFER_SIZE];
243 uint8_t *p_data = p_buffer + COMM_HEADER_SIZE;
244 uint16_t i_pid = 0;
245 struct dvblastctl_option opt = { 0, 0, 0 };
247 for ( ; ; )
249 int c;
251 static const struct option long_options[] =
253 {"remote-socket", required_argument, NULL, 'r'},
254 {"print", required_argument, NULL, 'x'},
255 {"help", no_argument, NULL, 'h'},
256 {0, 0, 0, 0}
259 if ( (c = getopt_long(i_argc, ppsz_argv, "r:x:h", long_options, NULL)) == -1 )
260 break;
262 switch ( c )
264 case 'r':
265 psz_srv_socket = optarg;
266 break;
268 case 'x':
269 if ( !strcmp(optarg, "text") )
270 i_print_type = PRINT_TEXT;
271 else if ( !strcmp(optarg, "xml") )
272 i_print_type = PRINT_XML;
273 else
274 msg_Warn( NULL, "unrecognized print type %s", optarg );
275 /* Make stdout line-buffered */
276 setvbuf(stdout, NULL, _IOLBF, 0);
277 break;
279 case 'h':
280 default:
281 usage();
285 /* Validate commands */
286 #define usage_error(msg, ...) \
287 do { \
288 msg_Err( NULL, msg, ##__VA_ARGS__ ); \
289 usage(); \
290 } while(0)
291 p_cmd = ppsz_argv[optind];
292 p_arg1 = ppsz_argv[optind + 1];
293 p_arg2 = ppsz_argv[optind + 2];
295 if ( !psz_srv_socket )
296 usage_error( "Remote socket is not set.\n" );
298 if ( !p_cmd )
299 usage_error( "Command is not set.\n" );
301 i = 0;
302 do {
303 if ( streq(ppsz_argv[optind], options[i].opt) )
305 opt = options[i];
306 break;
308 } while ( options[++i].opt );
310 if ( !opt.opt )
311 usage_error( "Unknown command: %s\n", p_cmd );
313 if ( opt.nparams == 1 && !p_arg1 )
314 usage_error( "%s option needs parameter.\n", opt.opt );
316 if ( opt.nparams == 2 && (!p_arg1 || !p_arg2) )
317 usage_error( "%s option needs two parameters.\n", opt.opt );
318 #undef usage_error
320 /* Create client socket name */
321 char *tmpdir = getenv("TMPDIR");
322 snprintf( psz_client_socket, PATH_MAX - 1, "%s/%s",
323 tmpdir ? tmpdir : "/tmp", client_socket_tmpl );
324 psz_client_socket[PATH_MAX - 1] = '\0';
326 int tmp_fd = mkstemp(psz_client_socket);
327 if ( tmp_fd > -1 ) {
328 close(tmp_fd);
329 unlink(psz_client_socket);
330 } else {
331 return_error( "Cannot build UNIX socket %s (%s)", psz_client_socket, strerror(errno) );
334 if ( (i_fd = socket( AF_UNIX, SOCK_DGRAM, 0 )) < 0 )
335 return_error( "Cannot create UNIX socket (%s)", strerror(errno) );
337 i = COMM_MAX_MSG_CHUNK;
338 setsockopt( i_fd, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i) );
340 memset( &sun_client, 0, sizeof(sun_client) );
341 sun_client.sun_family = AF_UNIX;
342 strncpy( sun_client.sun_path, psz_client_socket,
343 sizeof(sun_client.sun_path) );
344 sun_client.sun_path[sizeof(sun_client.sun_path) - 1] = '\0';
346 if ( bind( i_fd, (struct sockaddr *)&sun_client,
347 SUN_LEN(&sun_client) ) < 0 )
348 return_error( "Cannot bind (%s)", strerror(errno) );
350 memset( &sun_server, 0, sizeof(sun_server) );
351 sun_server.sun_family = AF_UNIX;
352 strncpy( sun_server.sun_path, psz_srv_socket, sizeof(sun_server.sun_path) );
353 sun_server.sun_path[sizeof(sun_server.sun_path) - 1] = '\0';
355 p_buffer[0] = COMM_HEADER_MAGIC;
356 p_buffer[1] = opt.cmd;
357 memset( p_buffer + 2, 0, COMM_HEADER_SIZE - 2 );
358 i_size = COMM_HEADER_SIZE;
360 /* Handle commands that send parameters */
361 switch ( opt.cmd )
363 case CMD_INVALID:
364 case CMD_RELOAD:
365 case CMD_SHUTDOWN:
366 case CMD_FRONTEND_STATUS:
367 case CMD_MMI_STATUS:
368 case CMD_GET_PAT:
369 case CMD_GET_CAT:
370 case CMD_GET_NIT:
371 case CMD_GET_SDT:
372 case CMD_GET_PIDS:
373 /* These commands need no special handling because they have no parameters */
374 break;
375 case CMD_GET_PMT:
377 uint16_t i_sid = atoi(p_arg1);
378 i_size = COMM_HEADER_SIZE + 2;
379 p_data[0] = (uint8_t)((i_sid >> 8) & 0xff);
380 p_data[1] = (uint8_t)(i_sid & 0xff);
381 break;
383 case CMD_GET_PID:
385 i_pid = (uint16_t)atoi(p_arg1);
386 i_size = COMM_HEADER_SIZE + 2;
387 p_data[0] = (uint8_t)((i_pid >> 8) & 0xff);
388 p_data[1] = (uint8_t)(i_pid & 0xff);
389 break;
391 case CMD_MMI_SEND_TEXT:
393 struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data;
394 p_cmd->i_slot = atoi(p_arg1);
396 en50221_mmi_object_t object;
397 object.i_object_type = EN50221_MMI_ANSW;
398 if ( !p_arg2 || p_arg2[0] == '\0' )
400 object.u.answ.b_ok = 0;
401 object.u.answ.psz_answ = "";
403 else
405 object.u.answ.b_ok = 1;
406 object.u.answ.psz_answ = p_arg2;
408 i_size = COMM_BUFFER_SIZE - COMM_HEADER_SIZE
409 - ((void *)&p_cmd->object - (void *)p_cmd);
410 if ( en50221_SerializeMMIObject( (uint8_t *)&p_cmd->object,
411 &i_size, &object ) == -1 )
412 return_error( "Comm buffer is too small" );
414 i_size += COMM_HEADER_SIZE
415 + ((void *)&p_cmd->object - (void *)p_cmd);
416 break;
418 case CMD_MMI_SEND_CHOICE:
420 struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_data;
421 p_cmd->i_slot = atoi(p_arg1);
423 i_size = COMM_HEADER_SIZE + sizeof(struct cmd_mmi_send);
424 p_cmd->object.i_object_type = EN50221_MMI_MENU_ANSW;
425 p_cmd->object.u.menu_answ.i_choice = atoi(p_arg2);
426 break;
428 case CMD_MMI_SLOT_STATUS:
429 case CMD_MMI_OPEN:
430 case CMD_MMI_CLOSE:
431 case CMD_MMI_RECV:
433 p_data[0] = atoi(p_arg1);
434 i_size = COMM_HEADER_SIZE + 1;
435 break;
437 default:
438 /* This should not happen */
439 return_error( "Unhandled option (%d)", opt.cmd );
442 /* Send command and receive answer */
443 if ( sendto( i_fd, p_buffer, i_size, 0, (struct sockaddr *)&sun_server,
444 SUN_LEN(&sun_server) ) < 0 )
445 return_error( "Cannot send comm socket (%s)", strerror(errno) );
447 uint32_t i_packet_size = 0, i_received = 0;
448 do {
449 i_size = recv( i_fd, p_buffer + i_received, COMM_MAX_MSG_CHUNK, 0 );
450 if ( i_size == -1 )
451 break;
452 if ( !i_packet_size ) {
453 i_packet_size = *((uint32_t *)&p_buffer[4]);
454 if ( i_packet_size > COMM_BUFFER_SIZE ) {
455 i_size = -1;
456 break;
459 i_received += i_size;
460 } while ( i_received < i_packet_size );
462 clean_client_socket();
463 if ( i_size < COMM_HEADER_SIZE )
464 return_error( "Cannot recv from comm socket, size:%zd (%s)", i_size, strerror(errno) );
466 /* Process answer */
467 if ( p_buffer[0] != COMM_HEADER_MAGIC )
468 return_error( "Wrong protocol version 0x%x", p_buffer[0] );
470 now = mdate();
472 ctl_cmd_answer_t c_answer = p_buffer[1];
473 switch ( c_answer )
475 case RET_OK:
476 break;
478 case RET_MMI_WAIT:
479 exit(252);
480 break;
482 case RET_ERR:
483 return_error( "Request failed" );
484 break;
486 case RET_HUH:
487 return_error( "Internal error" );
488 break;
490 case RET_NODATA:
491 return_error( "No data" );
492 break;
494 case RET_PAT:
495 case RET_CAT:
496 case RET_NIT:
497 case RET_SDT:
499 uint8_t *p_flat_data = p_buffer + COMM_HEADER_SIZE;
500 unsigned int i_flat_data_size = i_size - COMM_HEADER_SIZE;
501 uint8_t **pp_sections = psi_unpack_sections( p_flat_data, i_flat_data_size );
503 switch( c_answer )
505 case RET_PAT: pat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
506 case RET_CAT: cat_table_print( pp_sections, psi_print, NULL, i_print_type ); break;
507 case RET_NIT: nit_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
508 case RET_SDT: sdt_table_print( pp_sections, psi_print, NULL, psi_iconv, NULL, i_print_type ); break;
509 default: break; /* Can't happen */
512 psi_table_free( pp_sections );
513 free( pp_sections );
514 break;
517 case RET_PMT:
519 pmt_print( p_data, psi_print, NULL, psi_iconv, NULL, i_print_type );
520 break;
523 case RET_PID:
525 print_pids_header();
526 print_pid( i_pid, (ts_pid_info_t *)p_data );
527 print_pids_footer();
528 break;
531 case RET_PIDS:
533 print_pids( p_data );
534 break;
537 case RET_FRONTEND_STATUS:
539 int ret = 1;
540 struct ret_frontend_status *p_ret =
541 (struct ret_frontend_status *)&p_buffer[COMM_HEADER_SIZE];
542 if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_frontend_status) )
543 return_error( "Bad frontend status" );
545 if ( i_print_type == PRINT_XML )
546 printf("<FRONTEND>\n");
548 #define PRINT_TYPE( x ) \
549 do { \
550 if ( i_print_type == PRINT_XML ) \
551 printf( " <TYPE type=\"%s\"/>\n", STRINGIFY(x) ); \
552 else \
553 printf( "type: %s\n", STRINGIFY(x) ); \
554 } while(0)
555 switch ( p_ret->info.type )
557 case FE_QPSK: PRINT_TYPE(QPSK); break;
558 case FE_QAM : PRINT_TYPE(QAM); break;
559 case FE_OFDM: PRINT_TYPE(OFDM); break;
560 case FE_ATSC: PRINT_TYPE(ATSC); break;
561 default : PRINT_TYPE(UNKNOWN); break;
563 #undef PRINT_TYPE
565 #define PRINT_INFO( x ) \
566 do { \
567 if ( i_print_type == PRINT_XML ) \
568 printf( " <SETTING %s=\"%u\"/>\n", STRINGIFY(x), p_ret->info.x ); \
569 else \
570 printf( "%s: %u\n", STRINGIFY(x), p_ret->info.x ); \
571 } while(0)
572 PRINT_INFO( frequency_min );
573 PRINT_INFO( frequency_max );
574 PRINT_INFO( frequency_stepsize );
575 PRINT_INFO( frequency_tolerance );
576 PRINT_INFO( symbol_rate_min );
577 PRINT_INFO( symbol_rate_max );
578 PRINT_INFO( symbol_rate_tolerance );
579 PRINT_INFO( notifier_delay );
580 #undef PRINT_INFO
582 if ( i_print_type == PRINT_TEXT )
583 printf("\ncapability list:\n");
585 #define PRINT_CAPS( x ) \
586 do { \
587 if ( p_ret->info.caps & (FE_##x) ) { \
588 if ( i_print_type == PRINT_XML ) { \
589 printf( " <CAPABILITY %s=\"1\"/>\n", STRINGIFY(x) ); \
590 } else { \
591 printf( "%s\n", STRINGIFY(x) ); \
594 } while(0)
595 PRINT_CAPS( IS_STUPID );
596 PRINT_CAPS( CAN_INVERSION_AUTO );
597 PRINT_CAPS( CAN_FEC_1_2 );
598 PRINT_CAPS( CAN_FEC_2_3 );
599 PRINT_CAPS( CAN_FEC_3_4 );
600 PRINT_CAPS( CAN_FEC_4_5 );
601 PRINT_CAPS( CAN_FEC_5_6 );
602 PRINT_CAPS( CAN_FEC_6_7 );
603 PRINT_CAPS( CAN_FEC_7_8 );
604 PRINT_CAPS( CAN_FEC_8_9 );
605 PRINT_CAPS( CAN_FEC_AUTO );
606 PRINT_CAPS( CAN_QPSK );
607 PRINT_CAPS( CAN_QAM_16 );
608 PRINT_CAPS( CAN_QAM_32 );
609 PRINT_CAPS( CAN_QAM_64 );
610 PRINT_CAPS( CAN_QAM_128 );
611 PRINT_CAPS( CAN_QAM_256 );
612 PRINT_CAPS( CAN_QAM_AUTO );
613 PRINT_CAPS( CAN_TRANSMISSION_MODE_AUTO );
614 PRINT_CAPS( CAN_BANDWIDTH_AUTO );
615 PRINT_CAPS( CAN_GUARD_INTERVAL_AUTO );
616 PRINT_CAPS( CAN_HIERARCHY_AUTO );
617 PRINT_CAPS( CAN_MUTE_TS );
619 #define DVBAPI_VERSION ((DVB_API_VERSION)*100+(DVB_API_VERSION_MINOR))
621 #if DVBAPI_VERSION >= 301
622 PRINT_CAPS( CAN_8VSB );
623 PRINT_CAPS( CAN_16VSB );
624 PRINT_CAPS( NEEDS_BENDING );
625 PRINT_CAPS( CAN_RECOVER );
626 #endif
627 #if DVBAPI_VERSION >= 500
628 PRINT_CAPS( HAS_EXTENDED_CAPS );
629 #endif
630 #if DVBAPI_VERSION >= 501
631 PRINT_CAPS( CAN_2G_MODULATION );
632 #endif
633 #undef PRINT_CAPS
635 if ( i_print_type == PRINT_TEXT )
636 printf("\nstatus:\n");
638 #define PRINT_STATUS( x ) \
639 do { \
640 if ( p_ret->i_status & (FE_##x) ) { \
641 if ( i_print_type == PRINT_XML ) { \
642 printf( " <STATUS status=\"%s\"/>\n", STRINGIFY(x) ); \
643 } else { \
644 printf( "%s\n", STRINGIFY(x) ); \
647 } while(0)
648 PRINT_STATUS( HAS_SIGNAL );
649 PRINT_STATUS( HAS_CARRIER );
650 PRINT_STATUS( HAS_VITERBI );
651 PRINT_STATUS( HAS_SYNC );
652 PRINT_STATUS( HAS_LOCK );
653 PRINT_STATUS( REINIT );
654 #undef PRINT_STATUS
656 if ( p_ret->i_status & FE_HAS_LOCK )
658 if ( i_print_type == PRINT_XML )
660 printf(" <VALUE bit_error_rate=\"%d\"/>\n", p_ret->i_ber);
661 printf(" <VALUE signal_strength=\"%d\"/>\n", p_ret->i_strength);
662 printf(" <VALUE SNR=\"%d\"/>\n", p_ret->i_snr);
663 } else {
664 printf("\nBit error rate: %d\n", p_ret->i_ber);
665 printf("Signal strength: %d\n", p_ret->i_strength);
666 printf("SNR: %d\n", p_ret->i_snr);
668 ret = 0;
671 if ( i_print_type == PRINT_XML )
672 printf("</FRONTEND>\n" );
674 exit(ret);
675 break;
678 case RET_MMI_STATUS:
680 struct ret_mmi_status *p_ret =
681 (struct ret_mmi_status *)&p_buffer[COMM_HEADER_SIZE];
682 if ( i_size != COMM_HEADER_SIZE + sizeof(struct ret_mmi_status) )
683 return_error( "Bad MMI status" );
685 printf("CA interface with %d %s, type:\n", p_ret->caps.slot_num,
686 p_ret->caps.slot_num == 1 ? "slot" : "slots");
687 #define PRINT_CAPS( x, s ) \
688 if ( p_ret->caps.slot_type & (CA_##x) ) \
689 printf(s "\n");
690 PRINT_CAPS( CI, "CI high level interface" );
691 PRINT_CAPS( CI_LINK, "CI link layer level interface" );
692 PRINT_CAPS( CI_PHYS, "CI physical layer level interface (not supported)" );
693 PRINT_CAPS( DESCR, "built-in descrambler" );
694 PRINT_CAPS( SC, "simple smartcard interface" );
695 #undef PRINT_CAPS
697 printf("\n%d available %s\n", p_ret->caps.descr_num,
698 p_ret->caps.descr_num == 1 ? "descrambler (key)" :
699 "descramblers (keys)");
700 #define PRINT_DESC( x ) \
701 if ( p_ret->caps.descr_type & (CA_##x) ) \
702 printf( STRINGIFY(x) "\n" );
703 PRINT_DESC( ECD );
704 PRINT_DESC( NDS );
705 PRINT_DESC( DSS );
706 #undef PRINT_DESC
708 exit( p_ret->caps.slot_num );
709 break;
712 case RET_MMI_SLOT_STATUS:
714 struct ret_mmi_slot_status *p_ret =
715 (struct ret_mmi_slot_status *)&p_buffer[COMM_HEADER_SIZE];
716 if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_slot_status) )
717 return_error( "Bad MMI slot status" );
719 printf("CA slot #%u: ", p_ret->sinfo.num);
721 #define PRINT_TYPE( x, s ) \
722 if ( p_ret->sinfo.type & (CA_##x) ) \
723 printf(s);
725 PRINT_TYPE( CI, "high level, " );
726 PRINT_TYPE( CI_LINK, "link layer level, " );
727 PRINT_TYPE( CI_PHYS, "physical layer level, " );
728 #undef PRINT_TYPE
730 if ( p_ret->sinfo.flags & CA_CI_MODULE_READY )
732 printf("module present and ready\n");
733 exit(0);
736 if ( p_ret->sinfo.flags & CA_CI_MODULE_PRESENT )
737 printf("module present, not ready\n");
738 else
739 printf("module not present\n");
741 exit(1);
742 break;
745 case RET_MMI_RECV:
747 struct ret_mmi_recv *p_ret =
748 (struct ret_mmi_recv *)&p_buffer[COMM_HEADER_SIZE];
749 if ( i_size < COMM_HEADER_SIZE + sizeof(struct ret_mmi_recv) )
750 return_error( "Bad MMI recv" );
752 en50221_UnserializeMMIObject( &p_ret->object, i_size
753 - COMM_HEADER_SIZE - ((void *)&p_ret->object - (void *)p_ret) );
755 switch ( p_ret->object.i_object_type )
757 case EN50221_MMI_ENQ:
758 printf("%s\n", p_ret->object.u.enq.psz_text);
759 printf("(empty to cancel)\n");
760 exit(p_ret->object.u.enq.b_blind ? 253 : 254);
761 break;
763 case EN50221_MMI_MENU:
764 printf("%s\n", p_ret->object.u.menu.psz_title);
765 printf("%s\n", p_ret->object.u.menu.psz_subtitle);
766 printf("0 - Cancel\n");
767 for ( i = 0; i < p_ret->object.u.menu.i_choices; i++ )
768 printf("%d - %s\n", i + 1,
769 p_ret->object.u.menu.ppsz_choices[i]);
770 printf("%s\n", p_ret->object.u.menu.psz_bottom);
771 exit(p_ret->object.u.menu.i_choices);
772 break;
774 case EN50221_MMI_LIST:
775 printf("%s\n", p_ret->object.u.menu.psz_title);
776 printf("%s\n", p_ret->object.u.menu.psz_subtitle);
777 for ( i = 0; i < p_ret->object.u.menu.i_choices; i++ )
778 printf("%s\n", p_ret->object.u.menu.ppsz_choices[i]);
779 printf("%s\n", p_ret->object.u.menu.psz_bottom);
780 printf("(0 to cancel)\n");
781 exit(0);
782 break;
784 default:
785 return_error( "Unknown MMI object" );
786 break;
789 exit(255);
790 break;
793 default:
794 return_error( "Unknown command answer: %u", c_answer );
797 return 0;