* en50221.c : implementation of the transport, session and applications
* layers of EN 50 221
*****************************************************************************
- * Copyright (C) 2004 VideoLAN
+ * Copyright (C) 2004-2005 the VideoLAN team
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
*****************************************************************************/
#include <vlc/vlc.h>
-#include <vlc/input.h>
+#include <vlc_access.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/ca.h>
+/* Include dvbpsi headers */
+#ifdef HAVE_DVBPSI_DR_H
+# include <dvbpsi/dvbpsi.h>
+# include <dvbpsi/descriptor.h>
+# include <dvbpsi/pat.h>
+# include <dvbpsi/pmt.h>
+# include <dvbpsi/dr.h>
+# include <dvbpsi/psi.h>
+#else
+# include "dvbpsi.h"
+# include "descriptor.h"
+# include "tables/pat.h"
+# include "tables/pmt.h"
+# include "descriptors/dr.h"
+# include "psi.h"
+#endif
+
+#ifdef ENABLE_HTTPD
+# include "vlc_httpd.h"
+#endif
+
#include "dvb.h"
+#include <vlc_charset.h>
+
#undef DEBUG_TPDU
+#define HLCI_WAIT_CAM_READY 0
+#define CAM_PROG_MAX MAX_PROGRAMS
static void ResourceManagerOpen( access_t * p_access, int i_session_id );
static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
static void DateTimeOpen( access_t * p_access, int i_session_id );
static void MMIOpen( access_t * p_access, int i_session_id );
+static char *dvbsi_to_utf8( char *psz_instring, size_t i_length );
/*****************************************************************************
* Utility functions
switch ( i_resource_id )
{
case RI_RESOURCE_MANAGER:
- ResourceManagerOpen( p_access, i_session_id ); break;
+ ResourceManagerOpen( p_access, i_session_id ); break;
+ case RI_APPLICATION_INFORMATION:
+ ApplicationInformationOpen( p_access, i_session_id ); break;
+ case RI_CONDITIONAL_ACCESS_SUPPORT:
+ ConditionalAccessOpen( p_access, i_session_id ); break;
+ case RI_DATE_TIME:
+ DateTimeOpen( p_access, i_session_id ); break;
+ case RI_MMI:
+ MMIOpen( p_access, i_session_id ); break;
+
+ case RI_HOST_CONTROL:
+ default:
+ msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
+ p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
+ }
+}
+
+#if 0
+/* unused code for the moment - commented out to keep gcc happy */
+/*****************************************************************************
+ * SessionCreate
+ *****************************************************************************/
+static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ uint8_t p_response[16];
+ uint8_t i_tag;
+ int i_session_id;
+
+ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
+ {
+ if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
+ break;
+ }
+ if ( i_session_id == MAX_SESSIONS )
+ {
+ msg_Err( p_access, "too many sessions !" );
+ return;
+ }
+ p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
+ p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
+ p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
+ p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
+ p_sys->p_sessions[i_session_id - 1].p_sys = NULL;
+
+ p_response[0] = ST_CREATE_SESSION;
+ p_response[1] = 0x6;
+ p_response[2] = i_resource_id >> 24;
+ p_response[3] = (i_resource_id >> 16) & 0xff;
+ p_response[4] = (i_resource_id >> 8) & 0xff;
+ p_response[5] = i_resource_id & 0xff;
+ p_response[6] = i_session_id >> 8;
+ p_response[7] = i_session_id & 0xff;
+
+ if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
+ VLC_SUCCESS )
+ {
+ msg_Err( p_access,
+ "SessionCreate: couldn't send TPDU on slot %d", i_slot );
+ return;
+ }
+ if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
+ {
+ msg_Err( p_access,
+ "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
+ return;
+ }
+}
+#endif
+
+/*****************************************************************************
+ * SessionCreateResponse
+ *****************************************************************************/
+static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
+ uint8_t *p_spdu, int i_size )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int i_status = p_spdu[2];
+ int i_resource_id = ResourceIdToInt( &p_spdu[3] );
+ int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
+
+ if ( i_status != SS_OK )
+ {
+ msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
+ " resource=0x%x status=0x%x", i_session_id, i_resource_id,
+ i_status );
+ p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
+ return;
+ }
+
+ switch ( i_resource_id )
+ {
+ case RI_RESOURCE_MANAGER:
+ ResourceManagerOpen( p_access, i_session_id ); break;
case RI_APPLICATION_INFORMATION:
- ApplicationInformationOpen( p_access, i_session_id ); break;
+ ApplicationInformationOpen( p_access, i_session_id ); break;
case RI_CONDITIONAL_ACCESS_SUPPORT:
- ConditionalAccessOpen( p_access, i_session_id ); break;
+ ConditionalAccessOpen( p_access, i_session_id ); break;
case RI_DATE_TIME:
- DateTimeOpen( p_access, i_session_id ); break;
+ DateTimeOpen( p_access, i_session_id ); break;
case RI_MMI:
- MMIOpen( p_access, i_session_id ); break;
+ MMIOpen( p_access, i_session_id ); break;
case RI_HOST_CONTROL:
default:
}
}
+/*****************************************************************************
+ * SessionSendClose
+ *****************************************************************************/
+static void SessionSendClose( access_t * p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ uint8_t p_response[16];
+ uint8_t i_tag;
+ uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+
+ p_response[0] = ST_CLOSE_SESSION_REQUEST;
+ p_response[1] = 0x2;
+ p_response[2] = i_session_id >> 8;
+ p_response[3] = i_session_id & 0xff;
+
+ if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
+ VLC_SUCCESS )
+ {
+ msg_Err( p_access,
+ "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
+ return;
+ }
+ if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
+ {
+ msg_Err( p_access,
+ "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
+ return;
+ }
+}
+
/*****************************************************************************
* SessionClose
*****************************************************************************/
VLC_SUCCESS )
{
msg_Err( p_access,
- "SessionOpen: couldn't send TPDU on slot %d", i_slot );
+ "SessionClose: couldn't send TPDU on slot %d", i_slot );
return;
}
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
{
msg_Err( p_access,
- "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
+ "SessionClose: couldn't recv TPDU on slot %d", i_slot );
return;
}
}
SessionOpen( p_access, i_slot, p_spdu, i_size );
break;
+ case ST_CREATE_SESSION_RESPONSE:
+ if ( i_size != 9 || p_spdu[1] != 0x7 )
+ return;
+ SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
+ break;
+
case ST_CLOSE_SESSION_REQUEST:
+ if ( i_size != 4 || p_spdu[1] != 0x2 )
+ return;
i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
SessionClose( p_access, i_session_id );
break;
+ case ST_CLOSE_SESSION_RESPONSE:
+ if ( i_size != 5 || p_spdu[1] != 0x3 )
+ return;
+ i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
+ if ( p_spdu[2] )
+ {
+ msg_Err( p_access, "closing a session which is not allocated (%d)",
+ i_session_id );
+ }
+ else
+ {
+ if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
+ p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
+ i_session_id );
+ p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
+ }
+ break;
+
default:
+ msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
break;
}
}
static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
uint8_t *p_data, int i_size )
{
+ access_sys_t *p_sys = p_access->p_sys;
uint8_t *p_apdu = malloc( i_size + 12 );
uint8_t *p = p_apdu;
+ ca_msg_t ca_msg;
int i_ret;
*p++ = (i_tag >> 16);
p = SetLength( p, i_size );
if ( i_size )
memcpy( p, p_data, i_size );
-
- i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
+ if ( p_sys->i_ca_type == CA_CI_LINK )
+ {
+ i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
+ }
+ else
+ {
+ if ( i_size + p - p_apdu > 256 )
+ {
+ msg_Err( p_access, "CAM: apdu overflow" );
+ i_ret = VLC_EGENERIC;
+ }
+ else
+ {
+ char *psz_hex;
+ ca_msg.length = i_size + p - p_apdu;
+ if ( i_size == 0 ) ca_msg.length=3;
+ psz_hex = (char*)malloc( ca_msg.length*3 + 1);
+ memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
+ i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
+ if ( i_ret < 0 )
+ {
+ msg_Err( p_access, "Error sending to CAM: %s", strerror(errno) );
+ i_ret = VLC_EGENERIC;
+ }
+ }
+ }
free( p_apdu );
return i_ret;
}
+/*
+ * Resource Manager
+ */
+
/*****************************************************************************
* ResourceManagerHandle
*****************************************************************************/
APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
}
+/*
+ * Application Information
+ */
+
+/*****************************************************************************
+ * ApplicationInformationEnterMenu
+ *****************************************************************************/
+static void ApplicationInformationEnterMenu( 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;
+
+ msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
+ APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
+ p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
+}
+
/*****************************************************************************
* ApplicationInformationHandle
*****************************************************************************/
APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
}
+/*
+ * Conditional Access
+ */
+
+#define MAX_CASYSTEM_IDS 16
+
+typedef struct
+{
+ uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
+} system_ids_t;
+
+static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
+{
+ int i = 0;
+ if( !p_ids ) return VLC_TRUE;
+
+ while ( p_ids->pi_system_ids[i] )
+ {
+ if ( p_ids->pi_system_ids[i] == i_id )
+ return VLC_TRUE;
+ i++;
+ }
+
+ return VLC_FALSE;
+}
+
/*****************************************************************************
- * ConditionalAccessHandle
+ * CAPMTNeedsDescrambling
*****************************************************************************/
-static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
- uint8_t *p_apdu, int i_size )
+static vlc_bool_t CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
{
- access_sys_t *p_sys = p_access->p_sys;
- int i_tag = APDUGetTag( p_apdu, i_size );
+ dvbpsi_descriptor_t *p_dr;
+ dvbpsi_pmt_es_t *p_es;
- switch ( i_tag )
+ for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
{
- case AOT_CA_INFO:
+ if( p_dr->i_tag == 0x9 )
+ {
+ return VLC_TRUE;
+ }
+ }
+
+ for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
{
- if ( p_sys->i_nb_capmts )
+ for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
+ p_dr = p_dr->p_next )
{
- int i;
- msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
- for ( i = 0; i < p_sys->i_nb_capmts; i++ )
+ if( p_dr->i_tag == 0x9 )
{
- int i_size;
- uint8_t *p;
- p = GetLength( &p_sys->pp_capmts[i][3], &i_size );
- SPDUSend( p_access, i_session_id, p_sys->pp_capmts[i],
- i_size + (p - p_sys->pp_capmts[i]) );
+ return VLC_TRUE;
}
- p_sys->i_ca_next_pmt = 0;
}
- break;
- }
- default:
- msg_Err( p_access,
- "unexpected tag in ConditionalAccessHandle (0x%x)",
- i_tag );
}
+
+ return VLC_FALSE;
}
/*****************************************************************************
- * ConditionalAccessOpen
+ * CAPMTBuild
*****************************************************************************/
-static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
+static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
{
- access_sys_t *p_sys = p_access->p_sys;
-
- msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
+ int i_cad_size = 0;
- p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
+ while ( p_dr != NULL )
+ {
+ if( p_dr->i_tag == 0x9 )
+ {
+ uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
+ | p_dr->p_data[1];
+ if ( CheckSystemID( p_ids, i_sysid ) )
+ i_cad_size += p_dr->i_length + 2;
+ }
+ p_dr = p_dr->p_next;
+ }
- APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
+ return i_cad_size;
}
-typedef struct
+static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
+ uint16_t i_program_number, uint8_t i_version,
+ int i_size, dvbpsi_descriptor_t *p_dr,
+ uint8_t i_cmd )
{
- int i_interval;
- mtime_t i_last;
-} date_time_t;
+ uint8_t *p_data;
-/*****************************************************************************
- * 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;
+ if ( i_size )
+ p_data = malloc( 7 + i_size );
+ else
+ p_data = malloc( 6 );
- time_t t = time(NULL);
- struct tm tm_gmt;
- struct tm tm_loc;
+ p_data[0] = i_list_mgt;
+ p_data[1] = i_program_number >> 8;
+ p_data[2] = i_program_number & 0xff;
+ p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
- if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
+ if ( i_size )
{
- 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;
+ int i;
- APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
+ p_data[4] = (i_size + 1) >> 8;
+ p_data[5] = (i_size + 1) & 0xff;
+ p_data[6] = i_cmd;
+ i = 7;
- p_date->i_last = mdate();
+ while ( p_dr != NULL )
+ {
+ if( p_dr->i_tag == 0x9 )
+ {
+ uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
+ | p_dr->p_data[1];
+ if ( CheckSystemID( p_ids, i_sysid ) )
+ {
+ p_data[i] = 0x9;
+ p_data[i + 1] = p_dr->i_length;
+ memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
+// p_data[i+4] &= 0x1f;
+ i += p_dr->i_length + 2;
+ }
+ }
+ p_dr = p_dr->p_next;
+ }
+ }
+ else
+ {
+ p_data[4] = 0;
+ p_data[5] = 0;
}
+
+ return p_data;
}
-/*****************************************************************************
- * DateTimeHandle
- *****************************************************************************/
-static void DateTimeHandle( access_t * p_access, int i_session_id,
- uint8_t *p_apdu, int i_size )
+static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
+ int i_capmt_size, uint8_t i_type, uint16_t i_pid,
+ int i_size, dvbpsi_descriptor_t *p_dr,
+ uint8_t i_cmd )
{
- 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;
+ uint8_t *p_data;
+ int i;
+
+ if ( i_size )
+ p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
+ else
+ p_data = realloc( p_capmt, i_capmt_size + 5 );
- int i_tag = APDUGetTag( p_apdu, i_size );
+ i = i_capmt_size;
- switch ( i_tag )
- {
- case AOT_DATE_TIME_ENQ:
+ p_data[i] = i_type;
+ p_data[i + 1] = i_pid >> 8;
+ p_data[i + 2] = i_pid & 0xff;
+
+ if ( i_size )
{
- int l;
- const uint8_t *d = APDUGetLength( p_apdu, &l );
+ p_data[i + 3] = (i_size + 1) >> 8;
+ p_data[i + 4] = (i_size + 1) & 0xff;
+ p_data[i + 5] = i_cmd;
+ i += 6;
- if ( l > 0 )
+ while ( p_dr != NULL )
{
- p_date->i_interval = *d;
- msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
- p_date->i_interval );
+ if( p_dr->i_tag == 0x9 )
+ {
+ uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
+ | p_dr->p_data[1];
+ if ( CheckSystemID( p_ids, i_sysid ) )
+ {
+ p_data[i] = 0x9;
+ p_data[i + 1] = p_dr->i_length;
+ memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
+ i += p_dr->i_length + 2;
+ }
+ }
+ p_dr = p_dr->p_next;
}
- 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 );
+ else
+ {
+ p_data[i + 3] = 0;
+ p_data[i + 4] = 0;
}
+
+ return p_data;
}
-/*****************************************************************************
- * DateTimeManage
+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;
+ 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 ( p_date->i_interval
- && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
+ if ( i_tag != AOT_TEXT_LAST )
{
- DateTimeSend( p_access, i_session_id );
+ 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);
}
/*****************************************************************************
- * DateTimeOpen
+ * MMIHandleEnq
*****************************************************************************/
-static void DateTimeOpen( access_t * p_access, int i_session_id )
+static void MMIHandleEnq( access_t *p_access, int i_session_id,
+ uint8_t *p_apdu, int i_size )
{
access_sys_t *p_sys = p_access->p_sys;
+ mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+ int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+ int l;
+ uint8_t *d = APDUGetLength( p_apdu, &l );
+
+ en50221_MMIFree( &p_mmi->last_object );
+ p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
+ p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? VLC_TRUE : VLC_FALSE;
+ d += 2; /* skip answer_text_length because it is not mandatory */
+ l -= 2;
+ p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
+ strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
+ p_mmi->last_object.u.enq.psz_text[l] = '\0';
+
+ msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
+ p_mmi->last_object.u.enq.b_blind == VLC_TRUE ? " (blind)" : "" );
+ p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
+ p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
+}
- msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
+/*****************************************************************************
+ * MMIHandleMenu
+ *****************************************************************************/
+static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
+ uint8_t *p_apdu, int i_size )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+ int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+ int l;
+ uint8_t *d = APDUGetLength( p_apdu, &l );
+
+ en50221_MMIFree( &p_mmi->last_object );
+ p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
+ EN50221_MMI_MENU : EN50221_MMI_LIST;
+ p_mmi->last_object.u.menu.i_choices = 0;
+ p_mmi->last_object.u.menu.ppsz_choices = NULL;
+
+ if ( l > 0 )
+ {
+ l--; d++; /* choice_nb */
+
+#define GET_FIELD( x ) \
+ if ( l > 0 ) \
+ { \
+ p_mmi->last_object.u.menu.psz_##x \
+ = MMIGetText( p_access, &d, &l ); \
+ msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
+ p_mmi->last_object.u.menu.psz_##x ); \
+ }
- 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].p_sys = malloc(sizeof(date_time_t));
- memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
+ GET_FIELD( title );
+ GET_FIELD( subtitle );
+ GET_FIELD( bottom );
+#undef GET_FIELD
- DateTimeSend( p_access, i_session_id );
+ while ( l > 0 )
+ {
+ char *psz_text = MMIGetText( p_access, &d, &l );
+ TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
+ p_mmi->last_object.u.menu.ppsz_choices,
+ psz_text );
+ msg_Dbg( p_access, "MMI choice: %s", psz_text );
+ }
+ }
+ p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
+ p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
}
/*****************************************************************************
* MMIHandle
*****************************************************************************/
-static void MMIHandle( access_t * p_access, int i_session_id,
- uint8_t *p_apdu, int i_size )
+static void MMIHandle( access_t *p_access, int i_session_id,
+ uint8_t *p_apdu, int i_size )
{
int i_tag = APDUGetTag( p_apdu, i_size );
switch ( i_tag )
{
+ case AOT_DISPLAY_CONTROL:
+ {
+ int l;
+ uint8_t *d = APDUGetLength( p_apdu, &l );
+
+ if ( l > 0 )
+ {
+ switch ( *d )
+ {
+ case DCC_SET_MMI_MODE:
+ if ( l == 2 && d[1] == MM_HIGH_LEVEL )
+ MMIDisplayReply( p_access, i_session_id );
+ else
+ msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
+ break;
+
+ default:
+ msg_Err( p_access, "unsupported display control command %02x",
+ *d );
+ break;
+ }
+ }
+ break;
+ }
+
+ case AOT_ENQ:
+ MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
+ break;
+
+ case AOT_LIST_LAST:
+ case AOT_MENU_LAST:
+ MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
+ break;
+
+ case AOT_CLOSE_MMI:
+ SessionSendClose( p_access, i_session_id );
+ break;
+
default:
msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
}
}
+/*****************************************************************************
+ * MMIClose
+ *****************************************************************************/
+static void MMIClose( 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;
+ mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+
+ en50221_MMIFree( &p_mmi->last_object );
+ free( p_sys->p_sessions[i_session_id - 1].p_sys );
+
+ msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
+ p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
+ p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
+}
+
/*****************************************************************************
* MMIOpen
*****************************************************************************/
-static void MMIOpen( access_t * p_access, int i_session_id )
+static void MMIOpen( access_t *p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
+ mmi_t *p_mmi;
msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
+ p_sys->p_sessions[i_session_id - 1].pf_close = MMIClose;
+ p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t));
+ p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+ p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
}
continue;
}
}
+
if ( p_sys->pb_active_slot[i_slot] )
{
p_sys->i_ca_timeout = 100000;
* External entry points
*/
+/*****************************************************************************
+ * en50221_Init : Initialize the CAM for en50221
+ *****************************************************************************/
+int E_(en50221_Init)( access_t * p_access )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+
+ if( p_sys->i_ca_type & CA_CI_LINK )
+ {
+ int i_slot;
+ for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
+ {
+ if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
+ {
+ msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
+ i_slot );
+ }
+ }
+
+ p_sys->i_ca_timeout = 100000;
+ /* Wait a bit otherwise it doesn't initialize properly... */
+ msleep( 1000000 );
+
+ return VLC_SUCCESS;
+ }
+ else
+ {
+ struct ca_slot_info info;
+ info.num = 0;
+
+ /* We don't reset the CAM in that case because it's done by the
+ * ASIC. */
+ if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
+ {
+ msg_Err( p_access, "en50221_Init: couldn't get slot info" );
+ close( p_sys->i_ca_handle );
+ p_sys->i_ca_handle = 0;
+ return VLC_EGENERIC;
+ }
+ if( info.flags == 0 )
+ {
+ msg_Err( p_access, "en50221_Init: no CAM inserted" );
+ close( p_sys->i_ca_handle );
+ p_sys->i_ca_handle = 0;
+ return VLC_EGENERIC;
+ }
+
+ /* Allocate a dummy sessions */
+ p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
+
+ /* Get application info to find out which cam we are using and make
+ sure everything is ready to play */
+ ca_msg_t ca_msg;
+ ca_msg.length=3;
+ ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
+ ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
+ ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
+ memset( &ca_msg.msg[3], 0, 253 );
+ APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
+ if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
+ {
+ msg_Err( p_access, "en50221_Init: failed getting message" );
+ return VLC_EGENERIC;
+ }
+
+#if HLCI_WAIT_CAM_READY
+ while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
+ {
+ if( p_access->b_die ) return VLC_EGENERIC;
+ msleep(1);
+ msg_Dbg( p_access, "CAM: please wait" );
+ APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
+ ca_msg.length=3;
+ ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
+ ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
+ ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
+ memset( &ca_msg.msg[3], 0, 253 );
+ if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
+ {
+ msg_Err( p_access, "en50221_Init: failed getting message" );
+ return VLC_EGENERIC;
+ }
+ msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
+ }
+#else
+ if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
+ {
+ msg_Err( p_access, "CAM returns garbage as application info!" );
+ return VLC_EGENERIC;
+ }
+#endif
+ msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
+ (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
+ return VLC_SUCCESS;
+ }
+}
+
/*****************************************************************************
* en50221_Poll : Poll the CAM for TPDUs
*****************************************************************************/
for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
{
uint8_t i_tag;
+ ca_slot_info_t sinfo;
+
+ sinfo.num = i_slot;
+ if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
+ {
+ msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
+ i_slot );
+ continue;
+ }
- if ( !p_sys->pb_active_slot[i_slot] )
+ if ( !(sinfo.flags & CA_CI_MODULE_READY) )
{
- ca_slot_info_t sinfo;
- sinfo.num = i_slot;
- if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
+ if ( p_sys->pb_active_slot[i_slot] )
{
- msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
+ msg_Dbg( p_access, "en50221_Poll: slot %d has been removed",
i_slot );
- continue;
+ p_sys->pb_active_slot[i_slot] = VLC_FALSE;
+ p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
+ p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_FALSE;
+
+ /* Close all sessions for this slot. */
+ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
+ i_session_id++ )
+ {
+ if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
+ && p_sys->p_sessions[i_session_id - 1].i_slot
+ == i_slot )
+ {
+ if ( p_sys->p_sessions[i_session_id - 1].pf_close
+ != NULL )
+ {
+ p_sys->p_sessions[i_session_id - 1].pf_close(
+ p_access, i_session_id );
+ }
+ p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
+ }
+ }
}
- if ( sinfo.flags & CA_CI_MODULE_READY )
+ continue;
+ }
+ else if ( !p_sys->pb_active_slot[i_slot] )
+ {
+ InitSlot( p_access, i_slot );
+
+ if ( !p_sys->pb_active_slot[i_slot] )
{
- msg_Dbg( p_access, "en50221_Poll: slot %d is active",
- i_slot );
- p_sys->pb_active_slot[i_slot] = VLC_TRUE;
- }
- else
+ msg_Dbg( p_access, "en50221_Poll: resetting slot %d", i_slot );
+
+ if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
+ {
+ msg_Err( p_access, "en50221_Poll: couldn't reset slot %d",
+ i_slot );
+ }
continue;
+ }
- InitSlot( p_access, i_slot );
+ msg_Dbg( p_access, "en50221_Poll: slot %d is active",
+ i_slot );
}
if ( !p_sys->pb_tc_has_data[i_slot] )
}
}
- if ( p_sys->i_ca_next_pmt && p_sys->i_ca_next_pmt <= mdate() )
+ return VLC_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * en50221_SetCAPMT :
+ *****************************************************************************/
+int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int i, i_session_id;
+ vlc_bool_t b_update = VLC_FALSE;
+ vlc_bool_t b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
+
+ for ( i = 0; i < MAX_PROGRAMS; i++ )
+ {
+ if ( p_sys->pp_selected_programs[i] != NULL
+ && p_sys->pp_selected_programs[i]->i_program_number
+ == p_pmt->i_program_number )
+ {
+ b_update = VLC_TRUE;
+
+ if ( !b_needs_descrambling )
+ {
+ dvbpsi_DeletePMT( p_pmt );
+ p_pmt = p_sys->pp_selected_programs[i];
+ p_sys->pp_selected_programs[i] = NULL;
+ }
+ else if( p_pmt != p_sys->pp_selected_programs[i] )
+ {
+ dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
+ p_sys->pp_selected_programs[i] = p_pmt;
+ }
+
+ break;
+ }
+ }
+
+ if ( !b_update && b_needs_descrambling )
+ {
+ for ( i = 0; i < MAX_PROGRAMS; i++ )
+ {
+ if ( p_sys->pp_selected_programs[i] == NULL )
+ {
+ p_sys->pp_selected_programs[i] = p_pmt;
+ break;
+ }
+ }
+ }
+
+ if ( b_update || b_needs_descrambling )
+ {
+ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
+ {
+ if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
+ == RI_CONDITIONAL_ACCESS_SUPPORT )
+ {
+ if ( b_update && b_needs_descrambling )
+ CAPMTUpdate( p_access, i_session_id, p_pmt );
+ else if ( b_update )
+ CAPMTDelete( p_access, i_session_id, p_pmt );
+ else
+ CAPMTAdd( p_access, i_session_id, p_pmt );
+ }
+ }
+ }
+
+ if ( !b_needs_descrambling )
+ {
+ dvbpsi_DeletePMT( p_pmt );
+ }
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * en50221_OpenMMI :
+ *****************************************************************************/
+int E_(en50221_OpenMMI)( access_t * p_access, int i_slot )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+
+ if( p_sys->i_ca_type & CA_CI_LINK )
{
+ int i_session_id;
for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
{
- int i;
+ if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
+ && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
+ {
+ msg_Dbg( p_access,
+ "MMI menu is already opened on slot %d (session=%d)",
+ i_slot, i_session_id );
+ return VLC_SUCCESS;
+ }
+ }
+ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
+ {
if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
- != RI_CONDITIONAL_ACCESS_SUPPORT )
- continue;
+ == RI_APPLICATION_INFORMATION
+ && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
+ {
+ ApplicationInformationEnterMenu( p_access, i_session_id );
+ return VLC_SUCCESS;
+ }
+ }
+
+ msg_Err( p_access, "no application information on slot %d", i_slot );
+ return VLC_EGENERIC;
+ }
+ else
+ {
+ msg_Err( p_access, "MMI menu not supported" );
+ return VLC_EGENERIC;
+ }
+}
+
+/*****************************************************************************
+ * en50221_CloseMMI :
+ *****************************************************************************/
+int E_(en50221_CloseMMI)( access_t * p_access, int i_slot )
+{
+ access_sys_t *p_sys = p_access->p_sys;
- msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
- for ( i = 0; i < p_sys->i_nb_capmts; i++ )
+ if( p_sys->i_ca_type & CA_CI_LINK )
+ {
+ int i_session_id;
+ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
+ {
+ if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
+ && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
{
- int i_size;
- uint8_t *p;
- p = GetLength( &p_sys->pp_capmts[i][3], &i_size );
- SPDUSend( p_access, i_session_id, p_sys->pp_capmts[i],
- i_size + (p - p_sys->pp_capmts[i]) );
+ MMISendClose( p_access, i_session_id );
+ return VLC_SUCCESS;
}
}
- p_sys->i_ca_next_pmt = 0;
+ msg_Warn( p_access, "closing a non-existing MMI session on slot %d",
+ i_slot );
+ return VLC_EGENERIC;
+ }
+ else
+ {
+ msg_Err( p_access, "MMI menu not supported" );
+ return VLC_EGENERIC;
}
+}
- return VLC_SUCCESS;
+/*****************************************************************************
+ * en50221_GetMMIObject :
+ *****************************************************************************/
+en50221_mmi_object_t *E_(en50221_GetMMIObject)( access_t * p_access,
+ int i_slot )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int i_session_id;
+
+ if ( p_sys->pb_slot_mmi_expected[i_slot] == VLC_TRUE )
+ return NULL; /* should not happen */
+
+ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
+ {
+ if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
+ && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
+ {
+ mmi_t *p_mmi =
+ (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+ if ( p_mmi == NULL )
+ return NULL; /* should not happen */
+ return &p_mmi->last_object;
+ }
+ }
+
+ return NULL;
}
/*****************************************************************************
- * en50221_SetCAPMT :
+ * en50221_SendMMIObject :
*****************************************************************************/
-int E_(en50221_SetCAPMT)( access_t * p_access, uint8_t **pp_capmts,
- int i_nb_capmts )
+void E_(en50221_SendMMIObject)( access_t * p_access, int i_slot,
+ en50221_mmi_object_t *p_object )
{
access_sys_t *p_sys = p_access->p_sys;
+ int i_session_id;
- if ( p_sys->i_nb_capmts )
+ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
{
- int i;
- for ( i = 0; i < p_sys->i_nb_capmts; i++ )
+ if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
+ && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
{
- free( p_sys->pp_capmts[i] );
+ MMISendObject( p_access, i_session_id, p_object );
+ return;
}
- free( p_sys->pp_capmts );
}
- p_sys->pp_capmts = pp_capmts;
- p_sys->i_nb_capmts = i_nb_capmts;
- p_sys->i_ca_next_pmt = mdate() + 1000000;
- return VLC_SUCCESS;
+ msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
}
/*****************************************************************************
void E_(en50221_End)( access_t * p_access )
{
access_sys_t *p_sys = p_access->p_sys;
+ int i_session_id, i;
- if ( p_sys->i_nb_capmts )
+ for ( i = 0; i < MAX_PROGRAMS; i++ )
{
- int i;
- for ( i = 0; i < p_sys->i_nb_capmts; i++ )
+ if ( p_sys->pp_selected_programs[i] != NULL )
+ {
+ dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
+ }
+ }
+
+ for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
+ {
+ if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
+ && p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
{
- free( p_sys->pp_capmts[i] );
+ p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
+ i_session_id );
}
- free( p_sys->pp_capmts );
}
- /* TODO */
+ /* Leave the CAM configured, so that it can be reused in another
+ * program. */
+}
+
+static inline void *FixUTF8( char *p )
+{
+ EnsureUTF8( p );
+ return p;
+}
+
+static char *dvbsi_to_utf8( char *psz_instring, size_t i_length )
+{
+ const char *psz_encoding;
+ char *psz_stringstart, *psz_outstring, *psz_tmp;
+ char psz_encbuf[12];
+ size_t i_in, i_out;
+ vlc_iconv_t iconv_handle;
+ if( i_length < 1 ) return NULL;
+ if( psz_instring[0] < 0 || psz_instring[0] >= 0x20 )
+ {
+ psz_stringstart = psz_instring;
+ psz_encoding = "ISO_8859-1"; /* should be ISO6937 according to spec, but this seems to be the one used */
+ } else switch( psz_instring[0] )
+ {
+ case 0x01:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-5";
+ break;
+ case 0x02:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-6";
+ break;
+ case 0x03:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-7";
+ break;
+ case 0x04:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-8";
+ break;
+ case 0x05:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-9";
+ break;
+ case 0x06:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-10";
+ break;
+ case 0x07:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-11";
+ break;
+ case 0x08:
+ psz_stringstart = &psz_instring[1]; /*possibly reserved?*/
+ psz_encoding = "ISO_8859-12";
+ break;
+ case 0x09:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-13";
+ break;
+ case 0x0a:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-14";
+ break;
+ case 0x0b:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "ISO_8859-15";
+ break;
+ case 0x10:
+ if( i_length < 3 || psz_instring[1] != '\0' || psz_instring[2] > 0x0f
+ || psz_instring[2] == 0 )
+ return FixUTF8(strndup(psz_instring,i_length));
+ sprintf( psz_encbuf, "ISO_8859-%d", psz_instring[2] );
+ psz_stringstart = &psz_instring[3];
+ psz_encoding = psz_encbuf;
+ break;
+ case 0x11:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "UTF-16";
+ break;
+ case 0x12:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "KSC5601-1987";
+ break;
+ case 0x13:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "GB2312";/*GB-2312-1980 */
+ break;
+ case 0x14:
+ psz_stringstart = &psz_instring[1];
+ psz_encoding = "BIG-5";
+ break;
+ case 0x15:
+ return FixUTF8(strndup(&psz_instring[1],i_length-1));
+ break;
+ default:
+ /* invalid */
+ return FixUTF8(strndup(psz_instring,i_length));
+ }
+ iconv_handle = vlc_iconv_open( "UTF-8", psz_encoding );
+ i_in = i_length - (psz_stringstart - psz_instring );
+ i_out = i_in * 6;
+ psz_outstring = psz_tmp = (char*)malloc( i_out * sizeof(char) + 1 );
+ vlc_iconv( iconv_handle, &psz_stringstart, &i_in, &psz_tmp, &i_out );
+ vlc_iconv_close( iconv_handle );
+ *psz_tmp = '\0';
+ return psz_outstring;
}