]> git.sesse.net Git - vlc/commitdiff
Support for CAM modules on dvb-s cards using the high-level CI api from
authorSigmund Augdal Helberg <sigmunau@videolan.org>
Wed, 30 Nov 2005 10:04:44 +0000 (10:04 +0000)
committerSigmund Augdal Helberg <sigmunau@videolan.org>
Wed, 30 Nov 2005 10:04:44 +0000 (10:04 +0000)
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

modules/access/dvb/dvb.h
modules/access/dvb/en50221.c
modules/access/dvb/linux_dvb.c

index 188994147e9f45294c13f504bc20cfc74ff4d68a..bcf1f92070c2e584dac8c5ec936c290193ac88be 100644 (file)
@@ -68,12 +68,14 @@ struct access_sys_t
 
     /* CA management */
     int i_ca_handle;
+    int i_ca_type;
     int i_nb_slots;
     vlc_bool_t pb_active_slot[MAX_CI_SLOTS];
     vlc_bool_t pb_tc_has_data[MAX_CI_SLOTS];
     en50221_session_t p_sessions[MAX_SESSIONS];
     mtime_t i_ca_timeout, i_ca_next_event, i_frontend_timeout;
     dvbpsi_pmt_t *pp_selected_programs[MAX_PROGRAMS];
+    int i_selected_programs;
 
     /* */
     int i_read_once;
@@ -106,6 +108,7 @@ int  E_(CAMPoll)( access_t * );
 int  E_(CAMSet)( access_t *, dvbpsi_pmt_t * );
 void E_(CAMClose)( access_t * );
 
+int E_(en50221_Init)( access_t * );
 int E_(en50221_Poll)( access_t * );
 int E_(en50221_SetCAPMT)( access_t *, dvbpsi_pmt_t * );
 void E_(en50221_End)( access_t * );
index 15e6b0332fda3ef0541701c0c894bc02af9d43d9..75715eb6565407d3d3c31062a7c01866ffa9dd10 100644 (file)
@@ -62,6 +62,8 @@
 #include "dvb.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 );
@@ -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,
                      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);
@@ -671,8 +675,32 @@ static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
     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;
 }
@@ -796,6 +824,8 @@ typedef struct
 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 )
@@ -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 + 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;
                 }
             }
@@ -1047,6 +1078,20 @@ static void CAPMTAdd( access_t * p_access, int i_session_id,
     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 );
 
@@ -1087,6 +1132,7 @@ static void CAPMTDelete( access_t * p_access, int i_session_id,
     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 );
 
@@ -1113,7 +1159,6 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
     {
     case AOT_CA_INFO:
     {
-        vlc_bool_t b_inited = VLC_FALSE;
         int i;
         int l = 0;
         uint8_t *d = APDUGetLength( p_apdu, &l );
@@ -1131,13 +1176,8 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
         {
             if ( p_sys->pp_selected_programs[i] != NULL )
             {
-                if ( b_inited )
-                    CAPMTAdd( p_access, i_session_id,
-                              p_sys->pp_selected_programs[i] );
-                else
-                    CAPMTFirst( p_access, i_session_id,
-                                p_sys->pp_selected_programs[i] );
-                b_inited = VLC_TRUE;
+                CAPMTAdd( p_access, i_session_id,
+                          p_sys->pp_selected_programs[i] );
             }
         }
         break;
@@ -1507,6 +1547,69 @@ static int InitSlot( access_t * p_access, int i_slot )
 /*
  * 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
@@ -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_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] );
                 p_sys->pp_selected_programs[i] = p_pmt;
index ba93f9eb6fab677fe847f70b63075fb719525103..64aac74d04d3668bee1fb4ed1512f156474884de 100644 (file)
@@ -74,6 +74,8 @@ struct frontend_t
 
 #define FRONTEND_LOCK_TIMEOUT 10000000 /* 10 s */
 
+#define RESET_CAM_SLOTS 1 /* Do we want to reset cam upon opening */
+
 /* Local prototypes */
 static int FrontendInfo( access_t * );
 static int FrontendSetQPSK( access_t * );
@@ -1210,6 +1212,7 @@ int E_(CAMOpen)( access_t *p_access )
     char ca[128];
     int i_adapter, i_device, i_slot;
     ca_caps_t caps;
+    struct ca_slot_info info;
 
     i_adapter = var_GetInteger( p_access, "dvb-adapter" );
     i_device = var_GetInteger( p_access, "dvb-device" );
@@ -1219,6 +1222,7 @@ int E_(CAMOpen)( access_t *p_access )
         msg_Err( p_access, "snprintf() truncated string for CA" );
         ca[sizeof(ca) - 1] = '\0';
     }
+    memset( &caps, 0, sizeof( ca_caps_t ));
 
     msg_Dbg( p_access, "Opening device %s", ca );
     if( (p_sys->i_ca_handle = open(ca, O_RDWR | O_NONBLOCK)) < 0 )
@@ -1268,17 +1272,33 @@ int E_(CAMOpen)( access_t *p_access )
         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 );
         p_sys->i_ca_handle = 0;
         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;
     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++ )
     {
         if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
@@ -1286,12 +1306,13 @@ int E_(CAMOpen)( access_t *p_access )
             msg_Err( p_access, "CAMInit: couldn't reset slot %d", i_slot );
         }
     }
+#endif
 
     p_sys->i_ca_timeout = 100000;
     /* Wait a bit otherwise it doesn't initialize properly... */
     msleep( 1000000 );
 
-    return VLC_SUCCESS;
+    return E_(en50221_Init)( p_access );
 }
 
 /*****************************************************************************
@@ -1300,13 +1321,27 @@ int E_(CAMOpen)( access_t *p_access )
 int E_(CAMPoll)( access_t * p_access )
 {
     access_sys_t *p_sys = p_access->p_sys;
+    int i_ret = VLC_EGENERIC;
 
     if ( p_sys->i_ca_handle == 0 )
     {
         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;
 }
 
 /*****************************************************************************