Support for CAM modules on dvb-s cards using the high-level CI api from

linux-dvb. This currently mean Twinhan cards and clones.

There are three compile time switches currently available to people who 
want to test this:

RESET_CAM_SLOTS can be defined to 0 to disable the CA_RESET call at 
startup. This greatly improves startup time for my cam at least. 

HLCI_WAIT_CAM_READY can be defined to 1 to enable a loop that waits 
untill the cam module is ready. This may take a long time (at least if 
RESET_CAM_SLOTS is set), and does not happen at all under some error 
conditions.

CAM_PROG_MAX is a limit on the number of programs the code will send 
CAPMT messages for. This can be used to work around buggy cam/card 
combos that have issues when too many capmts are added.

Hopefully this doesn't break the low level CAM support
parent c5ec9fcf
...@@ -68,12 +68,14 @@ struct access_sys_t ...@@ -68,12 +68,14 @@ struct access_sys_t
/* CA management */ /* CA management */
int i_ca_handle; int i_ca_handle;
int i_ca_type;
int i_nb_slots; int i_nb_slots;
vlc_bool_t pb_active_slot[MAX_CI_SLOTS]; vlc_bool_t pb_active_slot[MAX_CI_SLOTS];
vlc_bool_t pb_tc_has_data[MAX_CI_SLOTS]; vlc_bool_t pb_tc_has_data[MAX_CI_SLOTS];
en50221_session_t p_sessions[MAX_SESSIONS]; en50221_session_t p_sessions[MAX_SESSIONS];
mtime_t i_ca_timeout, i_ca_next_event, i_frontend_timeout; mtime_t i_ca_timeout, i_ca_next_event, i_frontend_timeout;
dvbpsi_pmt_t *pp_selected_programs[MAX_PROGRAMS]; dvbpsi_pmt_t *pp_selected_programs[MAX_PROGRAMS];
int i_selected_programs;
/* */ /* */
int i_read_once; int i_read_once;
...@@ -106,6 +108,7 @@ int E_(CAMPoll)( access_t * ); ...@@ -106,6 +108,7 @@ int E_(CAMPoll)( access_t * );
int E_(CAMSet)( access_t *, dvbpsi_pmt_t * ); int E_(CAMSet)( access_t *, dvbpsi_pmt_t * );
void E_(CAMClose)( access_t * ); void E_(CAMClose)( access_t * );
int E_(en50221_Init)( access_t * );
int E_(en50221_Poll)( access_t * ); int E_(en50221_Poll)( access_t * );
int E_(en50221_SetCAPMT)( access_t *, dvbpsi_pmt_t * ); int E_(en50221_SetCAPMT)( access_t *, dvbpsi_pmt_t * );
void E_(en50221_End)( access_t * ); void E_(en50221_End)( access_t * );
......
...@@ -62,6 +62,8 @@ ...@@ -62,6 +62,8 @@
#include "dvb.h" #include "dvb.h"
#undef DEBUG_TPDU #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 ResourceManagerOpen( access_t * p_access, int i_session_id );
static void ApplicationInformationOpen( access_t * p_access, int i_session_id ); static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
...@@ -661,8 +663,10 @@ static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size ) ...@@ -661,8 +663,10 @@ static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
static int APDUSend( access_t * p_access, int i_session_id, int i_tag, static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
uint8_t *p_data, int i_size ) 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_apdu = malloc( i_size + 12 );
uint8_t *p = p_apdu; uint8_t *p = p_apdu;
ca_msg_t ca_msg;
int i_ret; int i_ret;
*p++ = (i_tag >> 16); *p++ = (i_tag >> 16);
...@@ -671,8 +675,32 @@ static int APDUSend( access_t * p_access, int i_session_id, int i_tag, ...@@ -671,8 +675,32 @@ static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
p = SetLength( p, i_size ); p = SetLength( p, i_size );
if ( i_size ) if ( i_size )
memcpy( p, p_data, i_size ); memcpy( p, p_data, i_size );
if( p_sys->i_ca_type == CA_CI_LINK )
i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu ); {
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 ); free( p_apdu );
return i_ret; return i_ret;
} }
...@@ -796,6 +824,8 @@ typedef struct ...@@ -796,6 +824,8 @@ typedef struct
static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id ) static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
{ {
int i = 0; int i = 0;
if( !p_ids ) return VLC_TRUE;
while ( p_ids->pi_system_ids[i] ) while ( p_ids->pi_system_ids[i] )
{ {
if ( p_ids->pi_system_ids[i] == i_id ) if ( p_ids->pi_system_ids[i] == i_id )
...@@ -896,6 +926,7 @@ static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt, ...@@ -896,6 +926,7 @@ static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
p_data[i] = 0x9; p_data[i] = 0x9;
p_data[i + 1] = p_dr->i_length; p_data[i + 1] = p_dr->i_length;
memcpy( &p_data[i + 2], p_dr->p_data, 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; i += p_dr->i_length + 2;
} }
} }
...@@ -1047,6 +1078,20 @@ static void CAPMTAdd( access_t * p_access, int i_session_id, ...@@ -1047,6 +1078,20 @@ static void CAPMTAdd( access_t * p_access, int i_session_id,
uint8_t *p_capmt; uint8_t *p_capmt;
int i_capmt_size; 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", msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); p_pmt->i_program_number, i_session_id );
...@@ -1087,6 +1132,7 @@ static void CAPMTDelete( access_t * p_access, int i_session_id, ...@@ -1087,6 +1132,7 @@ static void CAPMTDelete( access_t * p_access, int i_session_id,
uint8_t *p_capmt; uint8_t *p_capmt;
int i_capmt_size; int i_capmt_size;
p_access->p_sys->i_selected_programs++;
msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d", msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); p_pmt->i_program_number, i_session_id );
...@@ -1113,7 +1159,6 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id, ...@@ -1113,7 +1159,6 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
{ {
case AOT_CA_INFO: case AOT_CA_INFO:
{ {
vlc_bool_t b_inited = VLC_FALSE;
int i; int i;
int l = 0; int l = 0;
uint8_t *d = APDUGetLength( p_apdu, &l ); uint8_t *d = APDUGetLength( p_apdu, &l );
...@@ -1131,13 +1176,8 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id, ...@@ -1131,13 +1176,8 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
{ {
if ( p_sys->pp_selected_programs[i] != NULL ) if ( p_sys->pp_selected_programs[i] != NULL )
{ {
if ( b_inited ) CAPMTAdd( p_access, i_session_id,
CAPMTAdd( p_access, i_session_id, p_sys->pp_selected_programs[i] );
p_sys->pp_selected_programs[i] );
else
CAPMTFirst( p_access, i_session_id,
p_sys->pp_selected_programs[i] );
b_inited = VLC_TRUE;
} }
} }
break; break;
...@@ -1507,6 +1547,69 @@ static int InitSlot( access_t * p_access, int i_slot ) ...@@ -1507,6 +1547,69 @@ static int InitSlot( access_t * p_access, int i_slot )
/* /*
* External entry points * 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 level init is done in Poll() */
{
return VLC_SUCCESS;
}
else
{
/* 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, "CAM: 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, "CAM: failed getting message" );
return VLC_EGENERIC;
}
msg_Dbg( p_access, "CAM: 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 * en50221_Poll : Poll the CAM for TPDUs
...@@ -1642,7 +1745,7 @@ int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt ) ...@@ -1642,7 +1745,7 @@ int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
p_pmt = p_sys->pp_selected_programs[i]; p_pmt = p_sys->pp_selected_programs[i];
p_sys->pp_selected_programs[i] = NULL; p_sys->pp_selected_programs[i] = NULL;
} }
else else if( p_pmt != p_sys->pp_selected_programs[i] )
{ {
dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] ); dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
p_sys->pp_selected_programs[i] = p_pmt; p_sys->pp_selected_programs[i] = p_pmt;
......
...@@ -74,6 +74,8 @@ struct frontend_t ...@@ -74,6 +74,8 @@ struct frontend_t
#define FRONTEND_LOCK_TIMEOUT 10000000 /* 10 s */ #define FRONTEND_LOCK_TIMEOUT 10000000 /* 10 s */
#define RESET_CAM_SLOTS 1 /* Do we want to reset cam upon opening */
/* Local prototypes */ /* Local prototypes */
static int FrontendInfo( access_t * ); static int FrontendInfo( access_t * );
static int FrontendSetQPSK( access_t * ); static int FrontendSetQPSK( access_t * );
...@@ -1210,6 +1212,7 @@ int E_(CAMOpen)( access_t *p_access ) ...@@ -1210,6 +1212,7 @@ int E_(CAMOpen)( access_t *p_access )
char ca[128]; char ca[128];
int i_adapter, i_device, i_slot; int i_adapter, i_device, i_slot;
ca_caps_t caps; ca_caps_t caps;
struct ca_slot_info info;
i_adapter = var_GetInteger( p_access, "dvb-adapter" ); i_adapter = var_GetInteger( p_access, "dvb-adapter" );
i_device = var_GetInteger( p_access, "dvb-device" ); i_device = var_GetInteger( p_access, "dvb-device" );
...@@ -1219,6 +1222,7 @@ int E_(CAMOpen)( access_t *p_access ) ...@@ -1219,6 +1222,7 @@ int E_(CAMOpen)( access_t *p_access )
msg_Err( p_access, "snprintf() truncated string for CA" ); msg_Err( p_access, "snprintf() truncated string for CA" );
ca[sizeof(ca) - 1] = '\0'; ca[sizeof(ca) - 1] = '\0';
} }
memset( &caps, 0, sizeof( ca_caps_t ));
msg_Dbg( p_access, "Opening device %s", ca ); msg_Dbg( p_access, "Opening device %s", ca );
if( (p_sys->i_ca_handle = open(ca, O_RDWR | O_NONBLOCK)) < 0 ) if( (p_sys->i_ca_handle = open(ca, O_RDWR | O_NONBLOCK)) < 0 )
...@@ -1268,17 +1272,33 @@ int E_(CAMOpen)( access_t *p_access ) ...@@ -1268,17 +1272,33 @@ int E_(CAMOpen)( access_t *p_access )
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if ( !(caps.slot_type & CA_CI_LINK) ) p_sys->i_ca_type = caps.slot_type;
if( caps.slot_type != CA_CI_LINK &&
caps.slot_type != CA_CI )
{ {
msg_Err( p_access, "CAMInit: no compatible CAM module" ); msg_Err( p_access, "CAMInit: incompatible CAM module" );
close( p_sys->i_ca_handle ); close( p_sys->i_ca_handle );
p_sys->i_ca_handle = 0; p_sys->i_ca_handle = 0;
return VLC_EGENERIC; return VLC_EGENERIC;
} }
if( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
{
msg_Err( p_access, "CAMInit: 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, "CAMInit: No CAM inserted" );
close( p_sys->i_ca_handle );
p_sys->i_ca_handle = 0;
return VLC_EGENERIC;
}
p_sys->i_nb_slots = caps.slot_num; p_sys->i_nb_slots = caps.slot_num;
memset( p_sys->pb_active_slot, 0, sizeof(vlc_bool_t) * MAX_CI_SLOTS ); memset( p_sys->pb_active_slot, 0, sizeof(vlc_bool_t) * MAX_CI_SLOTS );
#if(RESET_CAM_SLOTS)
for ( i_slot = 0; i_slot < p_sys->i_nb_slots; 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 ) if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
...@@ -1286,12 +1306,13 @@ int E_(CAMOpen)( access_t *p_access ) ...@@ -1286,12 +1306,13 @@ int E_(CAMOpen)( access_t *p_access )
msg_Err( p_access, "CAMInit: couldn't reset slot %d", i_slot ); msg_Err( p_access, "CAMInit: couldn't reset slot %d", i_slot );
} }
} }
#endif
p_sys->i_ca_timeout = 100000; p_sys->i_ca_timeout = 100000;
/* Wait a bit otherwise it doesn't initialize properly... */ /* Wait a bit otherwise it doesn't initialize properly... */
msleep( 1000000 ); msleep( 1000000 );
return VLC_SUCCESS; return E_(en50221_Init)( p_access );
} }
/***************************************************************************** /*****************************************************************************
...@@ -1300,13 +1321,27 @@ int E_(CAMOpen)( access_t *p_access ) ...@@ -1300,13 +1321,27 @@ int E_(CAMOpen)( access_t *p_access )
int E_(CAMPoll)( access_t * p_access ) int E_(CAMPoll)( access_t * p_access )
{ {
access_sys_t *p_sys = p_access->p_sys; access_sys_t *p_sys = p_access->p_sys;
int i_ret = VLC_EGENERIC;
if ( p_sys->i_ca_handle == 0 ) if ( p_sys->i_ca_handle == 0 )
{ {
return VLC_EGENERIC; return VLC_EGENERIC;
} }
return E_(en50221_Poll)( p_access ); switch( p_sys->i_ca_type )
{
case CA_CI_LINK:
i_ret = E_(en50221_Poll)( p_access );
break;
case CA_CI:
i_ret = VLC_SUCCESS;
break;
default:
msg_Err( p_access, "CAMPoll: This should not happen" );
break;
}
return i_ret;
} }
/***************************************************************************** /*****************************************************************************
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment