+ else
+ {
+ p_data[i + 3] = 0;
+ p_data[i + 4] = 0;
+ }
+
+ return p_data;
+}
+
+static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
+ dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
+ uint8_t i_cmd, int *pi_capmt_size )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ system_ids_t *p_ids =
+ (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+ dvbpsi_pmt_es_t *p_es;
+ int i_cad_size, i_cad_program_size;
+ uint8_t *p_capmt;
+
+ i_cad_size = i_cad_program_size =
+ GetCADSize( p_ids, p_pmt->p_first_descriptor );
+ for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
+ {
+ i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
+ }
+
+ if ( !i_cad_size )
+ {
+ msg_Warn( p_access,
+ "no compatible scrambling system for SID %d on session %d",
+ p_pmt->i_program_number, i_session_id );
+ *pi_capmt_size = 0;
+ return NULL;
+ }
+
+ p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
+ p_pmt->i_version, i_cad_program_size,
+ p_pmt->p_first_descriptor, i_cmd );
+
+ if ( i_cad_program_size )
+ *pi_capmt_size = 7 + i_cad_program_size;
+ else
+ *pi_capmt_size = 6;
+
+ for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
+ {
+ i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
+
+ if ( i_cad_size || i_cad_program_size )
+ {
+ p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
+ p_es->i_pid, i_cad_size,
+ p_es->p_first_descriptor, i_cmd );
+ if ( i_cad_size )
+ *pi_capmt_size += 6 + i_cad_size;
+ else
+ *pi_capmt_size += 5;
+ }
+ }
+
+ return p_capmt;
+}
+
+/*****************************************************************************
+ * CAPMTFirst
+ *****************************************************************************/
+static void CAPMTFirst( access_t * p_access, int i_session_id,
+ dvbpsi_pmt_t *p_pmt )
+{
+ uint8_t *p_capmt;
+ int i_capmt_size;
+
+ msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
+ p_pmt->i_program_number, i_session_id );
+
+ p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
+ 0x3 /* only */, 0x1 /* ok_descrambling */,
+ &i_capmt_size );
+
+ if ( i_capmt_size )
+ APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
+}
+
+/*****************************************************************************
+ * CAPMTAdd
+ *****************************************************************************/
+static void CAPMTAdd( access_t * p_access, int i_session_id,
+ dvbpsi_pmt_t *p_pmt )
+{
+ uint8_t *p_capmt;
+ int i_capmt_size;
+
+ if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
+ {
+ msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
+ p_pmt->i_program_number );
+ return;
+ }
+ p_access->p_sys->i_selected_programs++;
+ if( p_access->p_sys->i_selected_programs == 1 )
+ {
+ CAPMTFirst( p_access, i_session_id, p_pmt );
+ return;
+ }
+
+
+ msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
+ p_pmt->i_program_number, i_session_id );
+
+ p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
+ 0x4 /* add */, 0x1 /* ok_descrambling */,
+ &i_capmt_size );
+
+ if ( i_capmt_size )
+ APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
+}
+
+/*****************************************************************************
+ * CAPMTUpdate
+ *****************************************************************************/
+static void CAPMTUpdate( access_t * p_access, int i_session_id,
+ dvbpsi_pmt_t *p_pmt )
+{
+ uint8_t *p_capmt;
+ int i_capmt_size;
+
+ msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
+ p_pmt->i_program_number, i_session_id );
+
+ p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
+ 0x5 /* update */, 0x1 /* ok_descrambling */,
+ &i_capmt_size );
+
+ if ( i_capmt_size )
+ APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
+}
+
+/*****************************************************************************
+ * CAPMTDelete
+ *****************************************************************************/
+static void CAPMTDelete( access_t * p_access, int i_session_id,
+ dvbpsi_pmt_t *p_pmt )
+{
+ uint8_t *p_capmt;
+ int i_capmt_size;
+
+ p_access->p_sys->i_selected_programs--;
+ msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
+ p_pmt->i_program_number, i_session_id );
+
+ p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
+ 0x5 /* update */, 0x4 /* not selected */,
+ &i_capmt_size );
+
+ if ( i_capmt_size )
+ APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
+}
+
+/*****************************************************************************
+ * ConditionalAccessHandle
+ *****************************************************************************/
+static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
+ uint8_t *p_apdu, int i_size )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ system_ids_t *p_ids =
+ (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+ int i_tag = APDUGetTag( p_apdu, i_size );
+
+ switch ( i_tag )
+ {
+ case AOT_CA_INFO:
+ {
+ int i;
+ int l = 0;
+ uint8_t *d = APDUGetLength( p_apdu, &l );
+ msg_Dbg( p_access, "CA system IDs supported by the application :" );
+
+ for ( i = 0; i < l / 2; i++ )
+ {
+ p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
+ d += 2;
+ msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
+ }
+ p_ids->pi_system_ids[i] = 0;
+
+ for ( i = 0; i < MAX_PROGRAMS; i++ )
+ {
+ if ( p_sys->pp_selected_programs[i] != NULL )
+ {
+ CAPMTAdd( p_access, i_session_id,
+ p_sys->pp_selected_programs[i] );
+ }
+ }
+ break;
+ }
+
+ default:
+ msg_Err( p_access,
+ "unexpected tag in ConditionalAccessHandle (0x%x)",
+ i_tag );
+ }
+}
+
+/*****************************************************************************
+ * ConditionalAccessClose
+ *****************************************************************************/
+static void ConditionalAccessClose( access_t * p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+
+ msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
+
+ free( p_sys->p_sessions[i_session_id - 1].p_sys );
+}
+
+/*****************************************************************************
+ * ConditionalAccessOpen
+ *****************************************************************************/
+static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+
+ msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
+
+ p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
+ p_sys->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
+ p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
+ memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
+ sizeof(system_ids_t) );
+
+ APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
+}
+
+/*
+ * Date Time
+ */
+
+typedef struct
+{
+ int i_interval;
+ mtime_t i_last;
+} date_time_t;
+
+/*****************************************************************************
+ * DateTimeSend
+ *****************************************************************************/
+static void DateTimeSend( access_t * p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ date_time_t *p_date =
+ (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+
+ time_t t = time(NULL);
+ struct tm tm_gmt;
+ struct tm tm_loc;
+
+ if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
+ {
+ int Y = tm_gmt.tm_year;
+ int M = tm_gmt.tm_mon + 1;
+ int D = tm_gmt.tm_mday;
+ int L = (M == 1 || M == 2) ? 1 : 0;
+ int MJD = 14956 + D + (int)((Y - L) * 365.25)
+ + (int)((M + 1 + L * 12) * 30.6001);
+ uint8_t p_response[7];
+
+#define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
+
+ p_response[0] = htons(MJD) >> 8;
+ p_response[1] = htons(MJD) & 0xff;
+ p_response[2] = DEC2BCD(tm_gmt.tm_hour);
+ p_response[3] = DEC2BCD(tm_gmt.tm_min);
+ p_response[4] = DEC2BCD(tm_gmt.tm_sec);
+ p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
+ p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
+
+ APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
+
+ p_date->i_last = mdate();
+ }
+}
+
+/*****************************************************************************
+ * DateTimeHandle
+ *****************************************************************************/
+static void DateTimeHandle( access_t * p_access, int i_session_id,
+ uint8_t *p_apdu, int i_size )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ date_time_t *p_date =
+ (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+
+ int i_tag = APDUGetTag( p_apdu, i_size );
+
+ switch ( i_tag )
+ {
+ case AOT_DATE_TIME_ENQ:
+ {
+ int l;
+ const uint8_t *d = APDUGetLength( p_apdu, &l );
+
+ if ( l > 0 )
+ {
+ p_date->i_interval = *d;
+ msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
+ p_date->i_interval );
+ }
+ else
+ p_date->i_interval = 0;
+
+ DateTimeSend( p_access, i_session_id );
+ break;
+ }
+ default:
+ msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
+ }
+}
+
+/*****************************************************************************
+ * DateTimeManage
+ *****************************************************************************/
+static void DateTimeManage( access_t * p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ date_time_t *p_date =
+ (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+
+ if ( p_date->i_interval
+ && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
+ {
+ DateTimeSend( p_access, i_session_id );
+ }
+}
+
+/*****************************************************************************
+ * DateTimeClose
+ *****************************************************************************/
+static void DateTimeClose( access_t * p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+
+ msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
+
+ free( p_sys->p_sessions[i_session_id - 1].p_sys );
+}
+
+/*****************************************************************************
+ * DateTimeOpen
+ *****************************************************************************/
+static void DateTimeOpen( access_t * p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+
+ msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
+
+ p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
+ p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
+ p_sys->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
+ p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
+ memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
+
+ DateTimeSend( p_access, i_session_id );
+}
+
+/*
+ * MMI
+ */
+
+/* Display Control Commands */
+
+#define DCC_SET_MMI_MODE 0x01
+#define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
+#define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
+#define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
+#define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
+
+/* MMI Modes */
+
+#define MM_HIGH_LEVEL 0x01
+#define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
+#define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
+
+/* Display Reply IDs */
+
+#define DRI_MMI_MODE_ACK 0x01
+#define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
+#define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
+#define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
+#define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
+#define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
+#define DRI_UNKNOWN_MMI_MODE 0xF1
+#define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
+
+/* Enquiry Flags */
+
+#define EF_BLIND 0x01
+
+/* Answer IDs */
+
+#define AI_CANCEL 0x00
+#define AI_ANSWER 0x01
+
+typedef struct
+{
+ en50221_mmi_object_t last_object;
+} mmi_t;
+
+/*****************************************************************************
+ * MMISendObject
+ *****************************************************************************/
+static void MMISendObject( access_t *p_access, int i_session_id,
+ en50221_mmi_object_t *p_object )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+ uint8_t *p_data;
+ int i_size, i_tag;
+
+ switch ( p_object->i_object_type )
+ {
+ case EN50221_MMI_ANSW:
+ i_tag = AOT_ANSW;
+ i_size = 1 + strlen( p_object->u.answ.psz_answ );
+ p_data = malloc( i_size );
+ p_data[0] = (p_object->u.answ.b_ok == VLC_TRUE) ? 0x1 : 0x0;
+ strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
+ break;
+
+ case EN50221_MMI_MENU_ANSW:
+ i_tag = AOT_MENU_ANSW;
+ i_size = 1;
+ p_data = malloc( i_size );
+ p_data[0] = p_object->u.menu_answ.i_choice;
+ break;
+
+ default:
+ msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
+ return;
+ }
+
+ APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
+ free( p_data );
+
+ p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
+}
+
+/*****************************************************************************
+ * MMISendClose
+ *****************************************************************************/
+static void MMISendClose( access_t *p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+
+ APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
+
+ p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
+}
+
+/*****************************************************************************
+ * MMIDisplayReply
+ *****************************************************************************/
+static void MMIDisplayReply( access_t *p_access, int i_session_id )
+{
+ uint8_t p_response[2];
+
+ p_response[0] = DRI_MMI_MODE_ACK;
+ p_response[1] = MM_HIGH_LEVEL;
+
+ APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
+
+ msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
+}
+
+/*****************************************************************************
+ * MMIGetText
+ *****************************************************************************/
+static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
+{
+ int i_tag = APDUGetTag( *pp_apdu, *pi_size );
+ int l;
+ uint8_t *d;
+
+ if ( i_tag != AOT_TEXT_LAST )
+ {
+ msg_Err( p_access, "unexpected text tag: %06x", i_tag );
+ *pi_size = 0;
+ return strdup( "" );
+ }
+
+ d = APDUGetLength( *pp_apdu, &l );
+
+ *pp_apdu += l + 4;
+ *pi_size -= l + 4;
+
+ return dvbsi_to_utf8((char*)d,l);