1 /*****************************************************************************
2 * en50221.c : implementation of the transport, session and applications
4 *****************************************************************************
5 * Copyright (C) 2004-2005 the VideoLAN team
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_charset.h>
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
38 #include <netinet/in.h>
40 /* DVB Card Drivers */
41 #include <linux/dvb/ca.h>
43 /* Include dvbpsi headers */
44 # include <dvbpsi/dvbpsi.h>
45 # include <dvbpsi/descriptor.h>
46 # include <dvbpsi/pat.h>
47 # include <dvbpsi/pmt.h>
48 # include <dvbpsi/dr.h>
49 # include <dvbpsi/psi.h>
50 # include <dvbpsi/demux.h>
51 # include <dvbpsi/sdt.h>
54 # include <vlc_httpd.h>
58 #include "../../demux/dvb-text.h"
61 typedef struct en50221_session_t
65 void (* pf_handle)( cam_t *, int, uint8_t *, int );
66 void (* pf_close)( cam_t *, int );
67 void (* pf_manage)( cam_t *, int );
71 #define EN50221_MMI_NONE 0
72 #define EN50221_MMI_ENQ 1
73 #define EN50221_MMI_ANSW 2
74 #define EN50221_MMI_MENU 3
75 #define EN50221_MMI_MENU_ANSW 4
76 #define EN50221_MMI_LIST 5
78 typedef struct en50221_mmi_object_t
98 char *psz_title, *psz_subtitle, *psz_bottom;
101 } menu; /* menu and list are the same */
112 #define HLCI_WAIT_CAM_READY 0
113 #define CAM_PROG_MAX MAX_PROGRAMS
114 //#define CAPMT_WAIT 100 /* uncomment this for slow CAMs */
116 static void ResourceManagerOpen( cam_t *, unsigned i_session_id );
117 static void ApplicationInformationOpen( cam_t *, unsigned i_session_id );
118 static void ConditionalAccessOpen( cam_t *, unsigned i_session_id );
119 static void DateTimeOpen( cam_t *, unsigned i_session_id );
120 static void MMIOpen( cam_t *, unsigned i_session_id );
122 #define MAX_CI_SLOTS 16
123 #define MAX_SESSIONS 32
124 #define MAX_PROGRAMS 24
131 mtime_t i_timeout, i_next_event;
134 bool pb_active_slot[MAX_CI_SLOTS];
135 bool pb_tc_has_data[MAX_CI_SLOTS];
136 bool pb_slot_mmi_expected[MAX_CI_SLOTS];
137 bool pb_slot_mmi_undisplayed[MAX_CI_SLOTS];
138 en50221_session_t p_sessions[MAX_SESSIONS];
140 dvbpsi_pmt_t *pp_selected_programs[MAX_PROGRAMS];
141 int i_selected_programs;
144 /*****************************************************************************
146 *****************************************************************************/
147 #define SIZE_INDICATOR 0x80
149 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
151 *pi_length = *p_data++;
153 if ( (*pi_length & SIZE_INDICATOR) != 0 )
155 int l = *pi_length & ~SIZE_INDICATOR;
159 for ( i = 0; i < l; i++ )
160 *pi_length = (*pi_length << 8) | *p_data++;
166 static uint8_t *SetLength( uint8_t *p_data, int i_length )
170 if ( i_length < 128 )
174 else if ( i_length < 256 )
176 *p++ = SIZE_INDICATOR | 0x1;
179 else if ( i_length < 65536 )
181 *p++ = SIZE_INDICATOR | 0x2;
182 *p++ = i_length >> 8;
183 *p++ = i_length & 0xff;
185 else if ( i_length < 16777216 )
187 *p++ = SIZE_INDICATOR | 0x3;
188 *p++ = i_length >> 16;
189 *p++ = (i_length >> 8) & 0xff;
190 *p++ = i_length & 0xff;
194 *p++ = SIZE_INDICATOR | 0x4;
195 *p++ = i_length >> 24;
196 *p++ = (i_length >> 16) & 0xff;
197 *p++ = (i_length >> 8) & 0xff;
198 *p++ = i_length & 0xff;
209 #define MAX_TPDU_SIZE 4096
210 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
212 #define DATA_INDICATOR 0x80
216 #define T_CREATE_TC 0x82
217 #define T_CTC_REPLY 0x83
218 #define T_DELETE_TC 0x84
219 #define T_DTC_REPLY 0x85
220 #define T_REQUEST_TC 0x86
221 #define T_NEW_TC 0x87
222 #define T_TC_ERROR 0x88
223 #define T_DATA_LAST 0xA0
224 #define T_DATA_MORE 0xA1
226 static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
231 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
232 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
233 fprintf(stderr, "%02X ", p_data[i]);
234 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
236 VLC_UNUSED(b_outgoing); VLC_UNUSED(p_data); VLC_UNUSED(i_size);
240 /*****************************************************************************
242 *****************************************************************************/
243 static int TPDUSend( cam_t * p_cam, uint8_t i_slot, uint8_t i_tag,
244 const uint8_t *p_content, int i_length )
246 uint8_t i_tcid = i_slot + 1;
247 uint8_t p_data[MAX_TPDU_SIZE];
263 p_data[3] = 1; /* length */
270 p_data[3] = 2; /* length */
272 p_data[5] = p_content[0];
279 /* i_length <= MAX_TPDU_DATA */
280 uint8_t *p = p_data + 3;
281 p = SetLength( p, i_length + 1 );
285 memcpy( p, p_content, i_length );
286 i_size = i_length + (p - p_data);
293 Dump( true, p_data, i_size );
295 if ( write( p_cam->fd, p_data, i_size ) != i_size )
297 msg_Err( p_cam->obj, "cannot write to CAM device (%m)" );
305 /*****************************************************************************
307 *****************************************************************************/
308 #define CAM_READ_TIMEOUT 3500 // ms
310 static int TPDURecv( cam_t *p_cam, uint8_t i_slot, uint8_t *pi_tag,
311 uint8_t *p_data, int *pi_size )
313 uint8_t i_tcid = i_slot + 1;
315 struct pollfd pfd[1];
317 pfd[0].fd = p_cam->fd;
318 pfd[0].events = POLLIN;
320 while( poll(pfd, 1, CAM_READ_TIMEOUT ) == -1 )
323 msg_Err( p_cam->obj, "poll error: %m" );
327 if ( !(pfd[0].revents & POLLIN) )
329 msg_Err( p_cam->obj, "CAM device poll time-out" );
333 if ( pi_size == NULL )
335 p_data = xmalloc( MAX_TPDU_SIZE );
340 i_size = read( p_cam->fd, p_data, MAX_TPDU_SIZE );
342 if ( i_size >= 0 || errno != EINTR )
348 msg_Err( p_cam->obj, "cannot read from CAM device (%d:%m)", i_size );
349 if( pi_size == NULL )
354 if ( p_data[1] != i_tcid )
356 msg_Err( p_cam->obj, "invalid read from CAM device (%d instead of %d)",
358 if( pi_size == NULL )
364 p_cam->pb_tc_has_data[i_slot] = (i_size >= 4
365 && p_data[i_size - 4] == T_SB
366 && p_data[i_size - 3] == 2
367 && (p_data[i_size - 1] & DATA_INDICATOR))
370 Dump( false, p_data, i_size );
372 if ( pi_size == NULL )
385 #define ST_SESSION_NUMBER 0x90
386 #define ST_OPEN_SESSION_REQUEST 0x91
387 #define ST_OPEN_SESSION_RESPONSE 0x92
388 #define ST_CREATE_SESSION 0x93
389 #define ST_CREATE_SESSION_RESPONSE 0x94
390 #define ST_CLOSE_SESSION_REQUEST 0x95
391 #define ST_CLOSE_SESSION_RESPONSE 0x96
394 #define SS_NOT_ALLOCATED 0xF0
396 #define RI_RESOURCE_MANAGER 0x00010041
397 #define RI_APPLICATION_INFORMATION 0x00020041
398 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
399 #define RI_HOST_CONTROL 0x00200041
400 #define RI_DATE_TIME 0x00240041
401 #define RI_MMI 0x00400041
403 static int ResourceIdToInt( uint8_t *p_data )
405 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
406 | ((int)p_data[2] << 8) | p_data[3];
409 /*****************************************************************************
411 *****************************************************************************/
412 static int SPDUSend( cam_t * p_cam, int i_session_id,
413 uint8_t *p_data, int i_size )
415 uint8_t *p_spdu = xmalloc( i_size + 4 );
418 uint8_t i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
420 *p++ = ST_SESSION_NUMBER;
422 *p++ = (i_session_id >> 8);
423 *p++ = i_session_id & 0xff;
425 memcpy( p, p_data, i_size );
432 if ( i_size > MAX_TPDU_DATA )
434 if ( TPDUSend( p_cam, i_slot, T_DATA_MORE, p,
435 MAX_TPDU_DATA ) != VLC_SUCCESS )
437 msg_Err( p_cam->obj, "couldn't send TPDU on session %d",
443 i_size -= MAX_TPDU_DATA;
447 if ( TPDUSend( p_cam, i_slot, T_DATA_LAST, p, i_size )
450 msg_Err( p_cam->obj, "couldn't send TPDU on session %d",
458 if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
461 msg_Err( p_cam->obj, "couldn't recv TPDU on session %d",
472 /*****************************************************************************
474 *****************************************************************************/
475 static void SessionOpen( cam_t * p_cam, uint8_t i_slot,
476 uint8_t *p_spdu, int i_size )
478 VLC_UNUSED( i_size );
481 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
482 uint8_t p_response[16];
483 int i_status = SS_NOT_ALLOCATED;
486 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
488 if ( !p_cam->p_sessions[i_session_id - 1].i_resource_id )
491 if ( i_session_id > MAX_SESSIONS )
493 msg_Err( p_cam->obj, "too many sessions !" );
496 p_cam->p_sessions[i_session_id - 1].i_slot = i_slot;
497 p_cam->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
498 p_cam->p_sessions[i_session_id - 1].pf_close = NULL;
499 p_cam->p_sessions[i_session_id - 1].pf_manage = NULL;
501 if ( i_resource_id == RI_RESOURCE_MANAGER
502 || i_resource_id == RI_APPLICATION_INFORMATION
503 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
504 || i_resource_id == RI_DATE_TIME
505 || i_resource_id == RI_MMI )
510 p_response[0] = ST_OPEN_SESSION_RESPONSE;
512 p_response[2] = i_status;
513 p_response[3] = p_spdu[2];
514 p_response[4] = p_spdu[3];
515 p_response[5] = p_spdu[4];
516 p_response[6] = p_spdu[5];
517 p_response[7] = i_session_id >> 8;
518 p_response[8] = i_session_id & 0xff;
520 if ( TPDUSend( p_cam, i_slot, T_DATA_LAST, p_response, 9 ) !=
524 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
527 if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
530 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
534 switch ( i_resource_id )
536 case RI_RESOURCE_MANAGER:
537 ResourceManagerOpen( p_cam, i_session_id ); break;
538 case RI_APPLICATION_INFORMATION:
539 ApplicationInformationOpen( p_cam, i_session_id ); break;
540 case RI_CONDITIONAL_ACCESS_SUPPORT:
541 ConditionalAccessOpen( p_cam, i_session_id ); break;
543 DateTimeOpen( p_cam, i_session_id ); break;
545 MMIOpen( p_cam, i_session_id ); break;
547 case RI_HOST_CONTROL:
549 msg_Err( p_cam->obj, "unknown resource id (0x%x)", i_resource_id );
550 p_cam->p_sessions[i_session_id - 1].i_resource_id = 0;
555 /* unused code for the moment - commented out to keep gcc happy */
556 /*****************************************************************************
558 *****************************************************************************/
559 static void SessionCreate( cam_t * p_cam, int i_slot, int i_resource_id )
561 uint8_t p_response[16];
565 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
567 if ( !p_cam->p_sessions[i_session_id - 1].i_resource_id )
570 if ( i_session_id == MAX_SESSIONS )
572 msg_Err( p_cam->obj, "too many sessions !" );
575 p_cam->p_sessions[i_session_id - 1].i_slot = i_slot;
576 p_cam->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
577 p_cam->p_sessions[i_session_id - 1].pf_close = NULL;
578 p_cam->p_sessions[i_session_id - 1].pf_manage = NULL;
579 p_cam->p_sessions[i_session_id - 1].p_sys = NULL;
581 p_response[0] = ST_CREATE_SESSION;
583 SetDWBE( &p_resource[2], i_resource_id );
584 SetWBE( &p_response[6]. i_session_id);
586 if ( TPDUSend( p_cam, i_slot, T_DATA_LAST, p_response, 4 ) !=
590 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
593 if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
596 "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
602 /*****************************************************************************
603 * SessionCreateResponse
604 *****************************************************************************/
605 static void SessionCreateResponse( cam_t * p_cam, uint8_t i_slot,
606 uint8_t *p_spdu, int i_size )
608 VLC_UNUSED( i_size );
609 VLC_UNUSED( i_slot );
611 int i_status = p_spdu[2];
612 int i_resource_id = ResourceIdToInt( &p_spdu[3] );
613 int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
615 if ( i_status != SS_OK )
617 msg_Err( p_cam->obj, "SessionCreateResponse: failed to open session %d"
618 " resource=0x%x status=0x%x", i_session_id, i_resource_id,
620 p_cam->p_sessions[i_session_id - 1].i_resource_id = 0;
624 switch ( i_resource_id )
626 case RI_RESOURCE_MANAGER:
627 ResourceManagerOpen( p_cam, i_session_id ); break;
628 case RI_APPLICATION_INFORMATION:
629 ApplicationInformationOpen( p_cam, i_session_id ); break;
630 case RI_CONDITIONAL_ACCESS_SUPPORT:
631 ConditionalAccessOpen( p_cam, i_session_id ); break;
633 DateTimeOpen( p_cam, i_session_id ); break;
635 MMIOpen( p_cam, i_session_id ); break;
637 case RI_HOST_CONTROL:
639 msg_Err( p_cam->obj, "unknown resource id (0x%x)", i_resource_id );
640 p_cam->p_sessions[i_session_id - 1].i_resource_id = 0;
644 /*****************************************************************************
646 *****************************************************************************/
647 static void SessionSendClose( cam_t *p_cam, int i_session_id )
649 uint8_t p_response[16];
651 uint8_t i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
653 p_response[0] = ST_CLOSE_SESSION_REQUEST;
655 SetWBE( &p_response[2], i_session_id );
657 if ( TPDUSend( p_cam, i_slot, T_DATA_LAST, p_response, 4 ) !=
661 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
664 if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
667 "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
672 /*****************************************************************************
674 *****************************************************************************/
675 static void SessionClose( cam_t * p_cam, int i_session_id )
677 uint8_t p_response[16];
679 uint8_t i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
681 if ( p_cam->p_sessions[i_session_id - 1].pf_close != NULL )
682 p_cam->p_sessions[i_session_id - 1].pf_close( p_cam, i_session_id );
683 p_cam->p_sessions[i_session_id - 1].i_resource_id = 0;
685 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
687 p_response[2] = SS_OK;
688 p_response[3] = i_session_id >> 8;
689 p_response[4] = i_session_id & 0xff;
691 if ( TPDUSend( p_cam, i_slot, T_DATA_LAST, p_response, 5 ) !=
695 "SessionClose: couldn't send TPDU on slot %d", i_slot );
698 if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
701 "SessionClose: couldn't recv TPDU on slot %d", i_slot );
706 /*****************************************************************************
708 *****************************************************************************/
709 static void SPDUHandle( cam_t * p_cam, uint8_t i_slot,
710 uint8_t *p_spdu, int i_size )
716 case ST_SESSION_NUMBER:
719 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
720 p_cam->p_sessions[i_session_id - 1].pf_handle( p_cam, i_session_id,
721 p_spdu + 4, i_size - 4 );
724 case ST_OPEN_SESSION_REQUEST:
725 if ( i_size != 6 || p_spdu[1] != 0x4 )
727 SessionOpen( p_cam, i_slot, p_spdu, i_size );
730 case ST_CREATE_SESSION_RESPONSE:
731 if ( i_size != 9 || p_spdu[1] != 0x7 )
733 SessionCreateResponse( p_cam, i_slot, p_spdu, i_size );
736 case ST_CLOSE_SESSION_REQUEST:
737 if ( i_size != 4 || p_spdu[1] != 0x2 )
739 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
740 SessionClose( p_cam, i_session_id );
743 case ST_CLOSE_SESSION_RESPONSE:
744 if ( i_size != 5 || p_spdu[1] != 0x3 )
746 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
750 "closing a session which is not allocated (%d)",
755 if ( p_cam->p_sessions[i_session_id - 1].pf_close != NULL )
756 p_cam->p_sessions[i_session_id - 1].pf_close( p_cam,
758 p_cam->p_sessions[i_session_id - 1].i_resource_id = 0;
763 msg_Err( p_cam->obj, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
773 #define AOT_NONE 0x000000
774 #define AOT_PROFILE_ENQ 0x9F8010
775 #define AOT_PROFILE 0x9F8011
776 #define AOT_PROFILE_CHANGE 0x9F8012
777 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
778 #define AOT_APPLICATION_INFO 0x9F8021
779 #define AOT_ENTER_MENU 0x9F8022
780 #define AOT_CA_INFO_ENQ 0x9F8030
781 #define AOT_CA_INFO 0x9F8031
782 #define AOT_CA_PMT 0x9F8032
783 #define AOT_CA_PMT_REPLY 0x9F8033
784 #define AOT_TUNE 0x9F8400
785 #define AOT_REPLACE 0x9F8401
786 #define AOT_CLEAR_REPLACE 0x9F8402
787 #define AOT_ASK_RELEASE 0x9F8403
788 #define AOT_DATE_TIME_ENQ 0x9F8440
789 #define AOT_DATE_TIME 0x9F8441
790 #define AOT_CLOSE_MMI 0x9F8800
791 #define AOT_DISPLAY_CONTROL 0x9F8801
792 #define AOT_DISPLAY_REPLY 0x9F8802
793 #define AOT_TEXT_LAST 0x9F8803
794 #define AOT_TEXT_MORE 0x9F8804
795 #define AOT_KEYPAD_CONTROL 0x9F8805
796 #define AOT_KEYPRESS 0x9F8806
797 #define AOT_ENQ 0x9F8807
798 #define AOT_ANSW 0x9F8808
799 #define AOT_MENU_LAST 0x9F8809
800 #define AOT_MENU_MORE 0x9F880A
801 #define AOT_MENU_ANSW 0x9F880B
802 #define AOT_LIST_LAST 0x9F880C
803 #define AOT_LIST_MORE 0x9F880D
804 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
805 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
806 #define AOT_DISPLAY_MESSAGE 0x9F8810
807 #define AOT_SCENE_END_MARK 0x9F8811
808 #define AOT_SCENE_DONE 0x9F8812
809 #define AOT_SCENE_CONTROL 0x9F8813
810 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
811 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
812 #define AOT_FLUSH_DOWNLOAD 0x9F8816
813 #define AOT_DOWNLOAD_REPLY 0x9F8817
814 #define AOT_COMMS_CMD 0x9F8C00
815 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
816 #define AOT_COMMS_REPLY 0x9F8C02
817 #define AOT_COMMS_SEND_LAST 0x9F8C03
818 #define AOT_COMMS_SEND_MORE 0x9F8C04
819 #define AOT_COMMS_RCV_LAST 0x9F8C05
820 #define AOT_COMMS_RCV_MORE 0x9F8C06
822 /*****************************************************************************
824 *****************************************************************************/
825 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
830 for ( i = 0; i < 3; i++ )
831 t = (t << 8) | *p_apdu++;
838 /*****************************************************************************
840 *****************************************************************************/
841 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
843 return GetLength( &p_apdu[3], pi_size );
846 /*****************************************************************************
848 *****************************************************************************/
849 static int APDUSend( cam_t * p_cam, int i_session_id, int i_tag,
850 uint8_t *p_data, int i_size )
852 uint8_t *p_apdu = xmalloc( i_size + 12 );
857 *p++ = (i_tag >> 16);
858 *p++ = (i_tag >> 8) & 0xff;
860 p = SetLength( p, i_size );
862 memcpy( p, p_data, i_size );
863 if ( p_cam->i_ca_type == CA_CI_LINK )
865 i_ret = SPDUSend( p_cam, i_session_id, p_apdu, i_size + p - p_apdu );
869 if ( i_size + p - p_apdu > 256 )
871 msg_Err( p_cam->obj, "CAM: apdu overflow" );
872 i_ret = VLC_EGENERIC;
876 ca_msg.length = i_size + p - p_apdu;
877 if ( i_size == 0 ) ca_msg.length=3;
878 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
879 i_ret = ioctl( p_cam->fd, CA_SEND_MSG, &ca_msg );
882 msg_Err( p_cam->obj, "Error sending to CAM: %m" );
883 i_ret = VLC_EGENERIC;
895 /*****************************************************************************
896 * ResourceManagerHandle
897 *****************************************************************************/
898 static void ResourceManagerHandle( cam_t * p_cam, int i_session_id,
899 uint8_t *p_apdu, int i_size )
901 int i_tag = APDUGetTag( p_apdu, i_size );
905 case AOT_PROFILE_ENQ:
907 int resources[] = { htonl(RI_RESOURCE_MANAGER),
908 htonl(RI_APPLICATION_INFORMATION),
909 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
913 APDUSend( p_cam, i_session_id, AOT_PROFILE, (uint8_t*)resources,
918 APDUSend( p_cam, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
922 msg_Err( p_cam->obj, "unexpected tag in ResourceManagerHandle (0x%x)",
927 /*****************************************************************************
928 * ResourceManagerOpen
929 *****************************************************************************/
930 static void ResourceManagerOpen( cam_t * p_cam, unsigned i_session_id )
932 msg_Dbg( p_cam->obj, "opening ResourceManager session (%u)",
934 p_cam->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
935 APDUSend( p_cam, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
939 * Application Information
942 /*****************************************************************************
943 * ApplicationInformationEnterMenu
944 *****************************************************************************/
945 static void ApplicationInformationEnterMenu( cam_t * p_cam, int i_session_id )
947 int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
949 msg_Dbg( p_cam->obj, "entering MMI menus on session %d", i_session_id );
950 APDUSend( p_cam, i_session_id, AOT_ENTER_MENU, NULL, 0 );
951 p_cam->pb_slot_mmi_expected[i_slot] = true;
954 /*****************************************************************************
955 * ApplicationInformationHandle
956 *****************************************************************************/
957 static void ApplicationInformationHandle( cam_t * p_cam, int i_session_id,
958 uint8_t *p_apdu, int i_size )
960 VLC_UNUSED(i_session_id);
961 int i_tag = APDUGetTag( p_apdu, i_size );
965 case AOT_APPLICATION_INFO:
967 int i_type, i_manufacturer, i_code;
969 uint8_t *d = APDUGetLength( p_apdu, &l );
972 p_apdu[l + 4] = '\0';
975 i_manufacturer = ((int)d[0] << 8) | d[1];
977 i_code = ((int)d[0] << 8) | d[1];
979 d = GetLength( d, &l );
981 msg_Info( p_cam->obj, "CAM: %s, %02X, %04X, %04X",
982 d, i_type, i_manufacturer, i_code );
987 "unexpected tag in ApplicationInformationHandle (0x%x)",
992 /*****************************************************************************
993 * ApplicationInformationOpen
994 *****************************************************************************/
995 static void ApplicationInformationOpen( cam_t * p_cam, unsigned i_session_id )
997 msg_Dbg( p_cam->obj, "opening ApplicationInformation session (%u)",
999 p_cam->p_sessions[i_session_id - 1].pf_handle =
1000 ApplicationInformationHandle;
1001 APDUSend( p_cam, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1005 * Conditional Access
1008 #define MAX_CASYSTEM_IDS 64
1012 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
1015 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
1018 if( !p_ids ) return true; /* dummy session for high-level CI intf */
1020 while ( p_ids->pi_system_ids[i] )
1022 if ( p_ids->pi_system_ids[i] == i_id )
1030 /*****************************************************************************
1031 * CAPMTNeedsDescrambling
1032 *****************************************************************************/
1033 static bool CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
1035 dvbpsi_descriptor_t *p_dr;
1036 dvbpsi_pmt_es_t *p_es;
1038 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
1040 if( p_dr->i_tag == 0x9 )
1046 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1048 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
1049 p_dr = p_dr->p_next )
1051 if( p_dr->i_tag == 0x9 )
1061 /*****************************************************************************
1063 *****************************************************************************/
1064 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1068 while ( p_dr != NULL )
1070 if( p_dr->i_tag == 0x9 )
1072 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1074 if ( CheckSystemID( p_ids, i_sysid ) )
1075 i_cad_size += p_dr->i_length + 2;
1077 p_dr = p_dr->p_next;
1083 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1084 uint16_t i_program_number, uint8_t i_version,
1085 int i_size, dvbpsi_descriptor_t *p_dr,
1091 p_data = xmalloc( 7 + i_size );
1093 p_data = xmalloc( 6 );
1095 p_data[0] = i_list_mgt;
1096 p_data[1] = i_program_number >> 8;
1097 p_data[2] = i_program_number & 0xff;
1098 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1104 p_data[4] = (i_size + 1) >> 8;
1105 p_data[5] = (i_size + 1) & 0xff;
1109 while ( p_dr != NULL )
1111 if( p_dr->i_tag == 0x9 )
1113 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1115 if ( CheckSystemID( p_ids, i_sysid ) )
1118 p_data[i + 1] = p_dr->i_length;
1119 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1120 // p_data[i+4] &= 0x1f;
1121 i += p_dr->i_length + 2;
1124 p_dr = p_dr->p_next;
1136 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1137 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1138 int i_size, dvbpsi_descriptor_t *p_dr,
1145 p_data = xrealloc( p_capmt, i_capmt_size + 6 + i_size );
1147 p_data = xrealloc( p_capmt, i_capmt_size + 5 );
1152 p_data[i + 1] = i_pid >> 8;
1153 p_data[i + 2] = i_pid & 0xff;
1157 p_data[i + 3] = (i_size + 1) >> 8;
1158 p_data[i + 4] = (i_size + 1) & 0xff;
1159 p_data[i + 5] = i_cmd;
1162 while ( p_dr != NULL )
1164 if( p_dr->i_tag == 0x9 )
1166 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1168 if ( CheckSystemID( p_ids, i_sysid ) )
1171 p_data[i + 1] = p_dr->i_length;
1172 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1173 i += p_dr->i_length + 2;
1176 p_dr = p_dr->p_next;
1188 static uint8_t *CAPMTBuild( cam_t * p_cam, int i_session_id,
1189 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1190 uint8_t i_cmd, int *pi_capmt_size )
1192 system_ids_t *p_ids =
1193 (system_ids_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1194 dvbpsi_pmt_es_t *p_es;
1195 int i_cad_size, i_cad_program_size;
1198 i_cad_size = i_cad_program_size =
1199 GetCADSize( p_ids, p_pmt->p_first_descriptor );
1200 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1202 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1207 msg_Warn( p_cam->obj,
1208 "no compatible scrambling system for SID %d on session %d",
1209 p_pmt->i_program_number, i_session_id );
1214 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1215 p_pmt->i_version, i_cad_program_size,
1216 p_pmt->p_first_descriptor, i_cmd );
1218 if ( i_cad_program_size )
1219 *pi_capmt_size = 7 + i_cad_program_size;
1223 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1225 i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1227 if ( i_cad_size || i_cad_program_size )
1229 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1230 p_es->i_pid, i_cad_size,
1231 p_es->p_first_descriptor, i_cmd );
1233 *pi_capmt_size += 6 + i_cad_size;
1235 *pi_capmt_size += 5;
1242 /*****************************************************************************
1244 *****************************************************************************/
1245 static void CAPMTFirst( cam_t * p_cam, int i_session_id,
1246 dvbpsi_pmt_t *p_pmt )
1251 msg_Dbg( p_cam->obj, "adding first CAPMT for SID %d on session %d",
1252 p_pmt->i_program_number, i_session_id );
1254 p_capmt = CAPMTBuild( p_cam, i_session_id, p_pmt,
1255 0x3 /* only */, 0x1 /* ok_descrambling */,
1260 APDUSend( p_cam, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1265 /*****************************************************************************
1267 *****************************************************************************/
1268 static void CAPMTAdd( cam_t * p_cam, int i_session_id,
1269 dvbpsi_pmt_t *p_pmt )
1274 if( p_cam->i_selected_programs >= CAM_PROG_MAX )
1276 msg_Warn( p_cam->obj, "Not adding CAPMT for SID %d, too many programs",
1277 p_pmt->i_program_number );
1280 p_cam->i_selected_programs++;
1281 if( p_cam->i_selected_programs == 1 )
1283 CAPMTFirst( p_cam, i_session_id, p_pmt );
1288 msleep( CAPMT_WAIT * 1000 );
1291 msg_Dbg( p_cam->obj, "adding CAPMT for SID %d on session %d",
1292 p_pmt->i_program_number, i_session_id );
1294 p_capmt = CAPMTBuild( p_cam, i_session_id, p_pmt,
1295 0x4 /* add */, 0x1 /* ok_descrambling */,
1300 APDUSend( p_cam, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1305 /*****************************************************************************
1307 *****************************************************************************/
1308 static void CAPMTUpdate( cam_t * p_cam, int i_session_id,
1309 dvbpsi_pmt_t *p_pmt )
1314 msg_Dbg( p_cam->obj, "updating CAPMT for SID %d on session %d",
1315 p_pmt->i_program_number, i_session_id );
1317 p_capmt = CAPMTBuild( p_cam, i_session_id, p_pmt,
1318 0x5 /* update */, 0x1 /* ok_descrambling */,
1323 APDUSend( p_cam, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1328 /*****************************************************************************
1330 *****************************************************************************/
1331 static void CAPMTDelete( cam_t * p_cam, int i_session_id,
1332 dvbpsi_pmt_t *p_pmt )
1337 p_cam->i_selected_programs--;
1338 msg_Dbg( p_cam->obj, "deleting CAPMT for SID %d on session %d",
1339 p_pmt->i_program_number, i_session_id );
1341 p_capmt = CAPMTBuild( p_cam, i_session_id, p_pmt,
1342 0x5 /* update */, 0x4 /* not selected */,
1347 APDUSend( p_cam, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1352 /*****************************************************************************
1353 * ConditionalAccessHandle
1354 *****************************************************************************/
1355 static void ConditionalAccessHandle( cam_t * p_cam, int i_session_id,
1356 uint8_t *p_apdu, int i_size )
1358 system_ids_t *p_ids =
1359 (system_ids_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1360 int i_tag = APDUGetTag( p_apdu, i_size );
1368 uint8_t *d = APDUGetLength( p_apdu, &l );
1369 msg_Dbg( p_cam->obj, "CA system IDs supported by the application :" );
1371 for ( i = 0; i < l / 2; i++ )
1373 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1375 msg_Dbg( p_cam->obj, "- 0x%x", p_ids->pi_system_ids[i] );
1377 p_ids->pi_system_ids[i] = 0;
1379 for ( i = 0; i < MAX_PROGRAMS; i++ )
1381 if ( p_cam->pp_selected_programs[i] != NULL )
1383 CAPMTAdd( p_cam, i_session_id,
1384 p_cam->pp_selected_programs[i] );
1391 msg_Err( p_cam->obj,
1392 "unexpected tag in ConditionalAccessHandle (0x%x)",
1397 /*****************************************************************************
1398 * ConditionalAccessClose
1399 *****************************************************************************/
1400 static void ConditionalAccessClose( cam_t * p_cam, int i_session_id )
1402 msg_Dbg( p_cam->obj, "closing ConditionalAccess session (%d)",
1404 free( p_cam->p_sessions[i_session_id - 1].p_sys );
1407 /*****************************************************************************
1408 * ConditionalAccessOpen
1409 *****************************************************************************/
1410 static void ConditionalAccessOpen( cam_t * p_cam, unsigned i_session_id )
1412 msg_Dbg( p_cam->obj, "opening ConditionalAccess session (%u)",
1414 p_cam->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1415 p_cam->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1416 p_cam->p_sessions[i_session_id - 1].p_sys = calloc( 1, sizeof(system_ids_t) );
1418 APDUSend( p_cam, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1431 /*****************************************************************************
1433 *****************************************************************************/
1434 static void DateTimeSend( cam_t * p_cam, int i_session_id )
1436 date_time_t *p_date =
1437 (date_time_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1439 time_t t = time(NULL);
1443 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1445 int Y = tm_gmt.tm_year;
1446 int M = tm_gmt.tm_mon + 1;
1447 int D = tm_gmt.tm_mday;
1448 int L = (M == 1 || M == 2) ? 1 : 0;
1449 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1450 + (int)((M + 1 + L * 12) * 30.6001);
1451 uint8_t p_response[7];
1453 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1455 SetWBE( &p_response[0], MJD );
1456 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1457 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1458 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1459 SetWBE( &p_response[5], tm_loc.tm_gmtoff / 60 );
1461 APDUSend( p_cam, i_session_id, AOT_DATE_TIME, p_response, 7 );
1463 p_date->i_last = mdate();
1467 /*****************************************************************************
1469 *****************************************************************************/
1470 static void DateTimeHandle( cam_t *p_cam, int i_session_id,
1471 uint8_t *p_apdu, int i_size )
1473 date_time_t *p_date =
1474 (date_time_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1476 int i_tag = APDUGetTag( p_apdu, i_size );
1480 case AOT_DATE_TIME_ENQ:
1483 const uint8_t *d = APDUGetLength( p_apdu, &l );
1487 p_date->i_interval = *d;
1488 msg_Dbg( p_cam->obj, "DateTimeHandle : interval set to %d",
1489 p_date->i_interval );
1492 p_date->i_interval = 0;
1494 DateTimeSend( p_cam, i_session_id );
1498 msg_Err( p_cam->obj, "unexpected tag in DateTimeHandle (0x%x)",
1503 /*****************************************************************************
1505 *****************************************************************************/
1506 static void DateTimeManage( cam_t * p_cam, int i_session_id )
1508 date_time_t *p_date =
1509 (date_time_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1511 if ( p_date->i_interval
1512 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1514 DateTimeSend( p_cam, i_session_id );
1518 /*****************************************************************************
1520 *****************************************************************************/
1521 static void DateTimeClose( cam_t * p_cam, int i_session_id )
1523 msg_Dbg( p_cam->obj, "closing DateTime session (%d)", i_session_id );
1525 free( p_cam->p_sessions[i_session_id - 1].p_sys );
1528 /*****************************************************************************
1530 *****************************************************************************/
1531 static void DateTimeOpen( cam_t * p_cam, unsigned i_session_id )
1533 msg_Dbg( p_cam->obj, "opening DateTime session (%u)", i_session_id );
1535 p_cam->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1536 p_cam->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1537 p_cam->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1538 p_cam->p_sessions[i_session_id - 1].p_sys = calloc( 1, sizeof(date_time_t) );
1540 DateTimeSend( p_cam, i_session_id );
1547 /* Display Control Commands */
1549 #define DCC_SET_MMI_MODE 0x01
1550 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1551 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1552 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1553 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1557 #define MM_HIGH_LEVEL 0x01
1558 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1559 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1561 /* Display Reply IDs */
1563 #define DRI_MMI_MODE_ACK 0x01
1564 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1565 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1566 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1567 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1568 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1569 #define DRI_UNKNOWN_MMI_MODE 0xF1
1570 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1574 #define EF_BLIND 0x01
1578 #define AI_CANCEL 0x00
1579 #define AI_ANSWER 0x01
1581 static void MMIFree( mmi_t *p_object )
1583 switch ( p_object->i_object_type )
1585 case EN50221_MMI_ENQ:
1586 FREENULL( p_object->u.enq.psz_text );
1589 case EN50221_MMI_ANSW:
1590 if ( p_object->u.answ.b_ok )
1592 FREENULL( p_object->u.answ.psz_answ );
1596 case EN50221_MMI_MENU:
1597 case EN50221_MMI_LIST:
1598 FREENULL( p_object->u.menu.psz_title );
1599 FREENULL( p_object->u.menu.psz_subtitle );
1600 FREENULL( p_object->u.menu.psz_bottom );
1601 for ( int i = 0; i < p_object->u.menu.i_choices; i++ )
1603 free( p_object->u.menu.ppsz_choices[i] );
1605 FREENULL( p_object->u.menu.ppsz_choices );
1614 /*****************************************************************************
1616 *****************************************************************************/
1617 static void MMISendObject( cam_t *p_cam, int i_session_id,
1620 int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1624 switch ( p_object->i_object_type )
1626 case EN50221_MMI_ANSW:
1628 i_size = 1 + strlen( p_object->u.answ.psz_answ );
1629 p_data = xmalloc( i_size );
1630 p_data[0] = (p_object->u.answ.b_ok == true) ? 0x1 : 0x0;
1631 strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1634 case EN50221_MMI_MENU_ANSW:
1635 i_tag = AOT_MENU_ANSW;
1637 p_data = xmalloc( i_size );
1638 p_data[0] = p_object->u.menu_answ.i_choice;
1642 msg_Err( p_cam->obj, "unknown MMI object %d", p_object->i_object_type );
1646 APDUSend( p_cam, i_session_id, i_tag, p_data, i_size );
1649 p_cam->pb_slot_mmi_expected[i_slot] = true;
1652 /*****************************************************************************
1654 *****************************************************************************/
1655 static void MMISendClose( cam_t *p_cam, int i_session_id )
1657 int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1659 APDUSend( p_cam, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1661 p_cam->pb_slot_mmi_expected[i_slot] = true;
1664 /*****************************************************************************
1666 *****************************************************************************/
1667 static void MMIDisplayReply( cam_t *p_cam, int i_session_id )
1669 uint8_t p_response[2];
1671 p_response[0] = DRI_MMI_MODE_ACK;
1672 p_response[1] = MM_HIGH_LEVEL;
1674 APDUSend( p_cam, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1676 msg_Dbg( p_cam->obj, "sending DisplayReply on session (%d)", i_session_id );
1679 /*****************************************************************************
1681 *****************************************************************************/
1682 static char *MMIGetText( cam_t *p_cam, uint8_t **pp_apdu, int *pi_size )
1684 int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1688 if ( i_tag != AOT_TEXT_LAST )
1690 msg_Err( p_cam->obj, "unexpected text tag: %06x", i_tag );
1692 return strdup( "" );
1695 d = APDUGetLength( *pp_apdu, &l );
1700 return vlc_from_EIT(d,l);
1703 /*****************************************************************************
1705 *****************************************************************************/
1706 static void MMIHandleEnq( cam_t *p_cam, int i_session_id,
1707 uint8_t *p_apdu, int i_size )
1709 VLC_UNUSED( i_size );
1711 mmi_t *p_mmi = (mmi_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1712 int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1714 uint8_t *d = APDUGetLength( p_apdu, &l );
1717 p_mmi->i_object_type = EN50221_MMI_ENQ;
1718 p_mmi->u.enq.b_blind = (*d & 0x1) ? true : false;
1719 d += 2; /* skip answer_text_length because it is not mandatory */
1721 p_mmi->u.enq.psz_text = xmalloc( l + 1 );
1722 strncpy( p_mmi->u.enq.psz_text, (char *)d, l );
1723 p_mmi->u.enq.psz_text[l] = '\0';
1725 msg_Dbg( p_cam->obj, "MMI enq: %s%s", p_mmi->u.enq.psz_text,
1726 p_mmi->u.enq.b_blind == true ? " (blind)" : "" );
1727 p_cam->pb_slot_mmi_expected[i_slot] = false;
1728 p_cam->pb_slot_mmi_undisplayed[i_slot] = true;
1731 /*****************************************************************************
1733 *****************************************************************************/
1734 static void MMIHandleMenu( cam_t *p_cam, int i_session_id, int i_tag,
1735 uint8_t *p_apdu, int i_size )
1739 mmi_t *p_mmi = (mmi_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1740 int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1742 uint8_t *d = APDUGetLength( p_apdu, &l );
1745 p_mmi->i_object_type = (i_tag == AOT_MENU_LAST) ?
1746 EN50221_MMI_MENU : EN50221_MMI_LIST;
1747 p_mmi->u.menu.i_choices = 0;
1748 p_mmi->u.menu.ppsz_choices = NULL;
1752 l--; d++; /* choice_nb */
1754 #define GET_FIELD( x ) \
1757 p_mmi->u.menu.psz_##x = MMIGetText( p_cam, &d, &l ); \
1758 msg_Dbg( p_cam->obj, "MMI " STRINGIFY( x ) ": %s", \
1759 p_mmi->u.menu.psz_##x ); \
1763 GET_FIELD( subtitle );
1764 GET_FIELD( bottom );
1769 char *psz_text = MMIGetText( p_cam, &d, &l );
1770 TAB_APPEND( p_mmi->u.menu.i_choices,
1771 p_mmi->u.menu.ppsz_choices,
1773 msg_Dbg( p_cam->obj, "MMI choice: %s", psz_text );
1776 p_cam->pb_slot_mmi_expected[i_slot] = false;
1777 p_cam->pb_slot_mmi_undisplayed[i_slot] = true;
1780 /*****************************************************************************
1782 *****************************************************************************/
1783 static void MMIHandle( cam_t *p_cam, int i_session_id,
1784 uint8_t *p_apdu, int i_size )
1786 int i_tag = APDUGetTag( p_apdu, i_size );
1790 case AOT_DISPLAY_CONTROL:
1793 uint8_t *d = APDUGetLength( p_apdu, &l );
1799 case DCC_SET_MMI_MODE:
1800 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1801 MMIDisplayReply( p_cam, i_session_id );
1803 msg_Err( p_cam->obj, "unsupported MMI mode %02x", d[1] );
1807 msg_Err( p_cam->obj, "unsupported display control command %02x",
1816 MMIHandleEnq( p_cam, i_session_id, p_apdu, i_size );
1821 MMIHandleMenu( p_cam, i_session_id, i_tag, p_apdu, i_size );
1825 SessionSendClose( p_cam, i_session_id );
1829 msg_Err( p_cam->obj, "unexpected tag in MMIHandle (0x%x)", i_tag );
1833 /*****************************************************************************
1835 *****************************************************************************/
1836 static void MMIClose( cam_t *p_cam, int i_session_id )
1838 int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1839 mmi_t *p_mmi = (mmi_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1842 free( p_cam->p_sessions[i_session_id - 1].p_sys );
1844 msg_Dbg( p_cam->obj, "closing MMI session (%d)", i_session_id );
1845 p_cam->pb_slot_mmi_expected[i_slot] = false;
1846 p_cam->pb_slot_mmi_undisplayed[i_slot] = true;
1849 /*****************************************************************************
1851 *****************************************************************************/
1852 static void MMIOpen( cam_t *p_cam, unsigned i_session_id )
1856 msg_Dbg( p_cam->obj, "opening MMI session (%u)", i_session_id );
1858 p_cam->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1859 p_cam->p_sessions[i_session_id - 1].pf_close = MMIClose;
1860 p_cam->p_sessions[i_session_id - 1].p_sys = xmalloc(sizeof(mmi_t));
1861 p_mmi = (mmi_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1862 p_mmi->i_object_type = EN50221_MMI_NONE;
1870 /*****************************************************************************
1871 * InitSlot: Open the transport layer
1872 *****************************************************************************/
1873 #define MAX_TC_RETRIES 20
1875 static int InitSlot( cam_t * p_cam, int i_slot )
1877 if ( TPDUSend( p_cam, i_slot, T_CREATE_TC, NULL, 0 ) != VLC_SUCCESS )
1879 msg_Err( p_cam->obj, "en50221_Init: couldn't send TPDU on slot %d",
1881 return VLC_EGENERIC;
1884 /* This is out of the spec */
1885 for ( int i = 0; i < MAX_TC_RETRIES; i++ )
1888 if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1889 && i_tag == T_CTC_REPLY )
1891 p_cam->pb_active_slot[i_slot] = true;
1895 if ( TPDUSend( p_cam, i_slot, T_CREATE_TC, NULL, 0 )
1898 msg_Err( p_cam->obj,
1899 "en50221_Init: couldn't send TPDU on slot %d",
1905 if ( p_cam->pb_active_slot[i_slot] )
1907 p_cam->i_timeout = CLOCK_FREQ / 10;
1911 return VLC_EGENERIC;
1916 * External entry points
1919 /*****************************************************************************
1920 * en50221_Init : Initialize the CAM for en50221
1921 *****************************************************************************/
1922 cam_t *en50221_Init( vlc_object_t *obj, int fd )
1926 memset( &caps, 0, sizeof( caps ));
1927 if( ioctl( fd, CA_GET_CAP, &caps ) < 0 )
1929 msg_Err( obj, "CAMInit: ioctl() error getting CAM capabilities" );
1933 /* Output CA capabilities */
1934 msg_Dbg( obj, "CA interface with %d slot(s)", caps.slot_num );
1935 if( caps.slot_type & CA_CI )
1936 msg_Dbg( obj, " CI high level interface type" );
1937 if( caps.slot_type & CA_CI_LINK )
1938 msg_Dbg( obj, " CI link layer level interface type" );
1939 if( caps.slot_type & CA_CI_PHYS )
1940 msg_Dbg( obj, " CI physical layer level interface type (not supported) " );
1941 if( caps.slot_type & CA_DESCR )
1942 msg_Dbg( obj, " built-in descrambler detected" );
1943 if( caps.slot_type & CA_SC )
1944 msg_Dbg( obj, " simple smart card interface" );
1946 msg_Dbg( obj, "%d available descrambler(s) (keys)", caps.descr_num );
1947 if( caps.descr_type & CA_ECD )
1948 msg_Dbg( obj, " ECD scrambling system supported" );
1949 if( caps.descr_type & CA_NDS )
1950 msg_Dbg( obj, " NDS scrambling system supported" );
1951 if( caps.descr_type & CA_DSS )
1952 msg_Dbg( obj, " DSS scrambling system supported" );
1954 if( caps.slot_num == 0 )
1956 msg_Err( obj, "CAM module without slots" );
1960 cam_t *p_cam = malloc( sizeof( *p_cam ) );
1961 if( unlikely(p_cam == NULL) )
1967 if( caps.slot_type & CA_CI_LINK )
1969 p_cam->i_ca_type = CA_CI_LINK;
1971 for ( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
1973 if ( ioctl( p_cam->fd, CA_RESET, 1 << i_slot) != 0 )
1975 msg_Err( p_cam->obj, "en50221_Init: couldn't reset slot %d",
1980 p_cam->i_timeout = CLOCK_FREQ / 10;
1981 /* Wait a bit otherwise it doesn't initialize properly... */
1982 msleep( CLOCK_FREQ / 10 );
1983 p_cam->i_next_event = 0;
1986 if( caps.slot_type & CA_CI )
1988 p_cam->i_ca_type = CA_CI;
1990 struct ca_slot_info info;
1992 /* We don't reset the CAM in that case because it's done by the
1994 if ( ioctl( fd, CA_GET_SLOT_INFO, &info ) < 0 )
1996 msg_Err( obj, "cannot get slot info: %m" );
1999 if( info.flags == 0 )
2001 msg_Err( obj, "no CAM inserted" );
2005 /* Allocate a dummy sessions */
2006 p_cam->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
2008 /* Get application info to find out which cam we are using and make
2009 sure everything is ready to play */
2012 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
2013 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
2014 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
2015 memset( &ca_msg.msg[3], 0, 253 );
2016 APDUSend( p_cam, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
2017 if ( ioctl( fd, CA_GET_MSG, &ca_msg ) < 0 )
2019 msg_Err( obj, "en50221_Init: failed getting message" );
2023 #if HLCI_WAIT_CAM_READY
2024 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
2026 if( !vlc_object_alive (obj) )
2029 msg_Dbg( obj, "CAM: please wait" );
2030 APDUSend( p_cam, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
2032 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
2033 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
2034 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
2035 memset( &ca_msg.msg[3], 0, 253 );
2036 if ( ioctl( fd, CA_GET_MSG, &ca_msg ) < 0 )
2038 msg_Err( obj, "en50221_Init: failed getting message" );
2041 msg_Dbg( p_cam->obj, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
2044 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
2046 msg_Err( obj, "CAM returns garbage as application info!" );
2050 msg_Dbg( obj, "found CAM %s using id 0x%x", &ca_msg.msg[12],
2051 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
2055 msg_Err( obj, "CAM interface incompatible" );
2066 /*****************************************************************************
2067 * en50221_Poll : Poll the CAM for TPDUs
2068 *****************************************************************************/
2069 void en50221_Poll( cam_t * p_cam )
2071 switch( p_cam->i_ca_type )
2074 if( mdate() > p_cam->i_next_event )
2082 for ( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
2085 ca_slot_info_t sinfo;
2088 if ( ioctl( p_cam->fd, CA_GET_SLOT_INFO, &sinfo ) != 0 )
2090 msg_Err( p_cam->obj, "en50221_Poll: couldn't get info on slot %d",
2095 if ( !(sinfo.flags & CA_CI_MODULE_READY) )
2097 if ( p_cam->pb_active_slot[i_slot] )
2099 msg_Dbg( p_cam->obj, "en50221_Poll: slot %d has been removed",
2101 p_cam->pb_active_slot[i_slot] = false;
2102 p_cam->pb_slot_mmi_expected[i_slot] = false;
2103 p_cam->pb_slot_mmi_undisplayed[i_slot] = false;
2105 /* Close all sessions for this slot. */
2106 for ( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2108 if ( p_cam->p_sessions[i - 1].i_resource_id
2109 && p_cam->p_sessions[i - 1].i_slot == i_slot )
2111 if ( p_cam->p_sessions[i - 1].pf_close != NULL )
2113 p_cam->p_sessions[i - 1].pf_close( p_cam, i );
2115 p_cam->p_sessions[i - 1].i_resource_id = 0;
2122 else if ( !p_cam->pb_active_slot[i_slot] )
2124 InitSlot( p_cam, i_slot );
2126 if ( !p_cam->pb_active_slot[i_slot] )
2128 msg_Dbg( p_cam->obj, "en50221_Poll: resetting slot %d", i_slot );
2130 if ( ioctl( p_cam->fd, CA_RESET, 1 << i_slot) != 0 )
2132 msg_Err( p_cam->obj, "en50221_Poll: couldn't reset slot %d",
2138 msg_Dbg( p_cam->obj, "en50221_Poll: slot %d is active",
2142 if ( !p_cam->pb_tc_has_data[i_slot] )
2144 if ( TPDUSend( p_cam, i_slot, T_DATA_LAST, NULL, 0 ) !=
2147 msg_Err( p_cam->obj,
2148 "en50221_Poll: couldn't send TPDU on slot %d",
2152 if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) !=
2155 msg_Err( p_cam->obj,
2156 "en50221_Poll: couldn't recv TPDU on slot %d",
2162 while ( p_cam->pb_tc_has_data[i_slot] )
2164 uint8_t p_tpdu[MAX_TPDU_SIZE];
2165 int i_size, i_session_size;
2168 if ( TPDUSend( p_cam, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2170 msg_Err( p_cam->obj,
2171 "en50221_Poll: couldn't send TPDU on slot %d",
2175 if ( TPDURecv( p_cam, i_slot, &i_tag, p_tpdu, &i_size ) !=
2178 msg_Err( p_cam->obj,
2179 "en50221_Poll: couldn't recv TPDU on slot %d",
2184 p_session = GetLength( &p_tpdu[3], &i_session_size );
2185 if ( i_session_size <= 1 )
2191 if ( i_tag != T_DATA_LAST )
2193 msg_Err( p_cam->obj,
2194 "en50221_Poll: fragmented TPDU not supported" );
2198 SPDUHandle( p_cam, i_slot, p_session, i_session_size );
2202 for ( int i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2204 if ( p_cam->p_sessions[i_session_id - 1].i_resource_id
2205 && p_cam->p_sessions[i_session_id - 1].pf_manage )
2207 p_cam->p_sessions[i_session_id - 1].pf_manage( p_cam,
2212 p_cam->i_next_event = mdate() + p_cam->i_timeout;
2216 /*****************************************************************************
2217 * en50221_SetCAPMT :
2218 *****************************************************************************/
2219 int en50221_SetCAPMT( cam_t * p_cam, dvbpsi_pmt_t *p_pmt )
2221 bool b_update = false;
2222 bool b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2224 for ( unsigned i = 0; i < MAX_PROGRAMS; i++ )
2226 if ( p_cam->pp_selected_programs[i] != NULL
2227 && p_cam->pp_selected_programs[i]->i_program_number
2228 == p_pmt->i_program_number )
2232 if ( !b_needs_descrambling )
2234 dvbpsi_DeletePMT( p_pmt );
2235 p_pmt = p_cam->pp_selected_programs[i];
2236 p_cam->pp_selected_programs[i] = NULL;
2238 else if( p_pmt != p_cam->pp_selected_programs[i] )
2240 dvbpsi_DeletePMT( p_cam->pp_selected_programs[i] );
2241 p_cam->pp_selected_programs[i] = p_pmt;
2248 if ( !b_update && b_needs_descrambling )
2250 for ( unsigned i = 0; i < MAX_PROGRAMS; i++ )
2252 if ( p_cam->pp_selected_programs[i] == NULL )
2254 p_cam->pp_selected_programs[i] = p_pmt;
2260 if ( b_update || b_needs_descrambling )
2262 for ( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2264 if ( p_cam->p_sessions[i - 1].i_resource_id
2265 == RI_CONDITIONAL_ACCESS_SUPPORT )
2267 if ( b_update && b_needs_descrambling )
2268 CAPMTUpdate( p_cam, i, p_pmt );
2269 else if ( b_update )
2270 CAPMTDelete( p_cam, i, p_pmt );
2272 CAPMTAdd( p_cam, i, p_pmt );
2277 if ( !b_needs_descrambling )
2279 dvbpsi_DeletePMT( p_pmt );
2285 /*****************************************************************************
2287 *****************************************************************************/
2288 static int en50221_OpenMMI( cam_t * p_cam, unsigned i_slot )
2290 if( p_cam->i_ca_type & CA_CI_LINK )
2292 for ( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2294 if ( p_cam->p_sessions[i - 1].i_resource_id == RI_MMI
2295 && p_cam->p_sessions[i - 1].i_slot == i_slot )
2297 msg_Dbg( p_cam->obj,
2298 "MMI menu is already opened on slot %d (session=%u)",
2304 for ( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2306 if ( p_cam->p_sessions[i - 1].i_resource_id
2307 == RI_APPLICATION_INFORMATION
2308 && p_cam->p_sessions[i - 1].i_slot == i_slot )
2310 ApplicationInformationEnterMenu( p_cam, i );
2315 msg_Err( p_cam->obj, "no application information on slot %d", i_slot );
2316 return VLC_EGENERIC;
2320 msg_Err( p_cam->obj, "MMI menu not supported" );
2321 return VLC_EGENERIC;
2325 /*****************************************************************************
2326 * en50221_CloseMMI :
2327 *****************************************************************************/
2328 static int en50221_CloseMMI( cam_t * p_cam, unsigned i_slot )
2330 if( p_cam->i_ca_type & CA_CI_LINK )
2332 for( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2334 if ( p_cam->p_sessions[i - 1].i_resource_id == RI_MMI
2335 && p_cam->p_sessions[i - 1].i_slot == i_slot )
2337 MMISendClose( p_cam, i );
2342 msg_Warn( p_cam->obj, "closing a non-existing MMI session on slot %d",
2344 return VLC_EGENERIC;
2348 msg_Err( p_cam->obj, "MMI menu not supported" );
2349 return VLC_EGENERIC;
2353 /*****************************************************************************
2354 * en50221_GetMMIObject :
2355 *****************************************************************************/
2356 static mmi_t *en50221_GetMMIObject( cam_t * p_cam, unsigned i_slot )
2358 if( p_cam->pb_slot_mmi_expected[i_slot] == true )
2359 return NULL; /* should not happen */
2361 for( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2363 if ( p_cam->p_sessions[i - 1].i_resource_id == RI_MMI
2364 && p_cam->p_sessions[i - 1].i_slot == i_slot )
2367 (mmi_t *)p_cam->p_sessions[i - 1].p_sys;
2368 if ( p_mmi == NULL )
2369 return NULL; /* should not happen */
2378 /*****************************************************************************
2379 * en50221_SendMMIObject :
2380 *****************************************************************************/
2381 static void en50221_SendMMIObject( cam_t * p_cam, unsigned i_slot,
2384 for( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2386 if ( p_cam->p_sessions[i - 1].i_resource_id == RI_MMI
2387 && p_cam->p_sessions[i - 1].i_slot == i_slot )
2389 MMISendObject( p_cam, i, p_object );
2394 msg_Err( p_cam->obj, "SendMMIObject when no MMI session is opened !" );
2398 char *en50221_Status( cam_t *p_cam, char *psz_request )
2400 if( psz_request != NULL && *psz_request )
2402 /* Check if we have an undisplayed MMI message : in that case we ignore
2403 * the user input to avoid confusing the CAM. */
2404 for ( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
2406 if ( p_cam->pb_slot_mmi_undisplayed[i_slot] == true )
2409 msg_Dbg( p_cam->obj,
2410 "ignoring user request because of a new MMI object" );
2416 if( psz_request != NULL && *psz_request )
2418 /* We have a mission to accomplish. */
2420 char psz_value[255];
2424 if ( HTTPExtractValue( psz_request, "slot", psz_value,
2425 sizeof(psz_value) ) == NULL )
2427 return strdup( "invalid request parameter\n" );
2429 i_slot = atoi(psz_value);
2431 if ( HTTPExtractValue( psz_request, "open", psz_value,
2432 sizeof(psz_value) ) != NULL )
2434 en50221_OpenMMI( p_cam, i_slot );
2438 if ( HTTPExtractValue( psz_request, "close", psz_value,
2439 sizeof(psz_value) ) != NULL )
2441 en50221_CloseMMI( p_cam, i_slot );
2445 if ( HTTPExtractValue( psz_request, "cancel", psz_value,
2446 sizeof(psz_value) ) == NULL )
2451 if ( HTTPExtractValue( psz_request, "type", psz_value,
2452 sizeof(psz_value) ) == NULL )
2454 return strdup( "invalid request parameter\n" );
2457 if ( !strcmp( psz_value, "enq" ) )
2459 mmi_object.i_object_type = EN50221_MMI_ANSW;
2460 mmi_object.u.answ.b_ok = b_ok;
2461 if ( b_ok == false )
2463 mmi_object.u.answ.psz_answ = strdup("");
2467 if ( HTTPExtractValue( psz_request, "answ", psz_value,
2468 sizeof(psz_value) ) == NULL )
2470 return strdup( "invalid request parameter\n" );
2473 mmi_object.u.answ.psz_answ = strdup(psz_value);
2478 mmi_object.i_object_type = EN50221_MMI_MENU_ANSW;
2479 if ( b_ok == false )
2481 mmi_object.u.menu_answ.i_choice = 0;
2485 if ( HTTPExtractValue( psz_request, "choice", psz_value,
2486 sizeof(psz_value) ) == NULL )
2487 mmi_object.u.menu_answ.i_choice = 0;
2489 mmi_object.u.menu_answ.i_choice = atoi(psz_value);
2493 en50221_SendMMIObject( p_cam, i_slot, &mmi_object );
2497 /* Check that we have all necessary MMI information. */
2498 for( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
2500 if ( p_cam->pb_slot_mmi_expected[i_slot] == true )
2506 FILE *p = open_memstream( &buf, &len );
2507 if( unlikely(p == NULL) )
2512 if( ioctl( p_cam->fd, CA_GET_CAP, &caps ) < 0 )
2514 fprintf( p, "ioctl CA_GET_CAP failed (%m)\n" );
2518 /* Output CA capabilities */
2519 fprintf( p, "CA interface with %d %s, type:\n<table>", caps.slot_num,
2520 caps.slot_num == 1 ? "slot" : "slots" );
2521 #define CHECK_CAPS( x, s ) \
2522 if ( caps.slot_type & (CA_##x) ) \
2523 fprintf( p, "<tr><td>%s</td></tr>\n", s )
2525 CHECK_CAPS( CI, "CI high level interface" );
2526 CHECK_CAPS( CI_LINK, "CI link layer level interface" );
2527 CHECK_CAPS( CI_PHYS, "CI physical layer level interface (not supported)" );
2528 CHECK_CAPS( DESCR, "built-in descrambler" );
2529 CHECK_CAPS( SC, "simple smartcard interface" );
2532 fprintf( p, "</table>%d available %s\n<table>", caps.descr_num,
2533 caps.descr_num == 1 ? "descrambler (key)" : "descramblers (keys)" );
2534 #define CHECK_DESC( x ) \
2535 if ( caps.descr_type & (CA_##x) ) \
2536 fprintf( p, "<tr><td>%s</td></tr>", STRINGIFY(x) )
2543 fputs( "</table>", p );
2545 for( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
2547 ca_slot_info_t sinfo;
2549 p_cam->pb_slot_mmi_undisplayed[i_slot] = false;
2550 fprintf( p, "<p>CA slot #%d: ", i_slot );
2553 if ( ioctl( p_cam->fd, CA_GET_SLOT_INFO, &sinfo ) < 0 )
2555 fprintf( p, "ioctl CA_GET_SLOT_INFO failed (%m)<br>\n" );
2559 #define CHECK_TYPE( x, s ) \
2560 if ( sinfo.type & (CA_##x) ) \
2563 CHECK_TYPE( CI, "high level, " );
2564 CHECK_TYPE( CI_LINK, "link layer level, " );
2565 CHECK_TYPE( CI_PHYS, "physical layer level, " );
2568 if ( sinfo.flags & CA_CI_MODULE_READY )
2570 mmi_t *p_object = en50221_GetMMIObject( p_cam, i_slot );
2572 fputs( "module present and ready<p>\n", p );
2573 fputs( "<form action=index.html method=get>\n", p );
2574 fprintf( p, "<input type=hidden name=slot value=\"%d\">\n",
2577 if ( p_object == NULL )
2579 fputs( "<input type=submit name=open"
2580 " value=\"Open session\">\n", p );
2584 switch ( p_object->i_object_type )
2586 case EN50221_MMI_ENQ:
2587 fputs( "<input type=hidden name=type value=enq>\n", p );
2588 fprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
2589 p_object->u.enq.psz_text );
2590 fprintf( p, "<tr><td><input type=%s name=answ>"
2592 p_object->u.enq.b_blind ? "password" : "text" );
2595 case EN50221_MMI_MENU:
2596 fputs( "<input type=hidden name=type value=menu>\n", p );
2597 fprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
2598 p_object->u.menu.psz_title );
2599 fprintf( p, "<tr><td>%s</td></tr><tr><td>\n",
2600 p_object->u.menu.psz_subtitle );
2601 for ( int i = 0; i < p_object->u.menu.i_choices; i++ )
2602 fprintf( p, "<input type=radio name=choice"
2603 " value=\"%d\">%s<br>\n", i + 1,
2604 p_object->u.menu.ppsz_choices[i] );
2605 fprintf( p, "</td></tr><tr><td>%s</td></tr>\n",
2606 p_object->u.menu.psz_bottom );
2609 case EN50221_MMI_LIST:
2610 fputs( "<input type=hidden name=type value=menu>\n", p );
2611 fputs( "<input type=hidden name=choice value=0>\n", p );
2612 fprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
2613 p_object->u.menu.psz_title );
2614 fprintf( p, "<tr><td>%s</td></tr><tr><td>\n",
2615 p_object->u.menu.psz_subtitle );
2616 for ( int i = 0; i < p_object->u.menu.i_choices; i++ )
2617 fprintf( p, "%s<br>\n",
2618 p_object->u.menu.ppsz_choices[i] );
2619 fprintf( p, "</td></tr><tr><td>%s</td></tr>\n",
2620 p_object->u.menu.psz_bottom );
2624 fputs( "<table><tr><th>Unknown MMI object type</th></tr>\n", p );
2627 fputs( "</table><p><input type=submit name=ok value=\"OK\">\n", p );
2628 fputs( "<input type=submit name=cancel value=\"Cancel\">\n", p );
2629 fputs( "<input type=submit name=close value=\"Close Session\">\n", p );
2631 fputs( "</form>\n", p );
2633 else if ( sinfo.flags & CA_CI_MODULE_PRESENT )
2634 fputs( "module present, not ready<br>\n", p );
2636 fputs( "module not present<br>\n", p );
2645 /*****************************************************************************
2647 *****************************************************************************/
2648 void en50221_End( cam_t * p_cam )
2650 for( unsigned i = 0; i < MAX_PROGRAMS; i++ )
2652 if( p_cam->pp_selected_programs[i] != NULL )
2654 dvbpsi_DeletePMT( p_cam->pp_selected_programs[i] );
2658 for( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2660 if( p_cam->p_sessions[i - 1].i_resource_id
2661 && p_cam->p_sessions[i - 1].pf_close != NULL )
2663 p_cam->p_sessions[i - 1].pf_close( p_cam, i );