2005-05-17 08:54:41 +04:00
/*
CA - driver for TwinHan DST Frontend / Card
Copyright ( C ) 2004 , 2005 Manu Abraham ( manu @ kromtek . com )
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/dvb/ca.h>
# include "dvbdev.h"
# include "dvb_frontend.h"
# include "dst_ca.h"
# include "dst_common.h"
2005-09-10 00:03:00 +04:00
# define DST_CA_ERROR 0
# define DST_CA_NOTICE 1
# define DST_CA_INFO 2
# define DST_CA_DEBUG 3
# define dprintk(x, y, z, format, arg...) do { \
if ( z ) { \
if ( ( x > DST_CA_ERROR ) & & ( x > y ) ) \
printk ( KERN_ERR " %s: " format " \n " , __FUNCTION__ , # # arg ) ; \
else if ( ( x > DST_CA_NOTICE ) & & ( x > y ) ) \
printk ( KERN_NOTICE " %s: " format " \n " , __FUNCTION__ , # # arg ) ; \
else if ( ( x > DST_CA_INFO ) & & ( x > y ) ) \
printk ( KERN_INFO " %s: " format " \n " , __FUNCTION__ , # # arg ) ; \
else if ( ( x > DST_CA_DEBUG ) & & ( x > y ) ) \
printk ( KERN_DEBUG " %s: " format " \n " , __FUNCTION__ , # # arg ) ; \
} else { \
if ( x > y ) \
printk ( format , # # arg ) ; \
} \
} while ( 0 )
2005-07-08 04:57:50 +04:00
static unsigned int verbose = 5 ;
2005-05-17 08:54:41 +04:00
module_param ( verbose , int , 0644 ) ;
MODULE_PARM_DESC ( verbose , " verbose startup messages, default is 1 (yes) " ) ;
2005-05-17 08:54:45 +04:00
/* Need some more work */
2005-05-17 08:54:41 +04:00
static int ca_set_slot_descr ( void )
{
/* We could make this more graceful ? */
return - EOPNOTSUPP ;
}
2005-05-17 08:54:45 +04:00
/* Need some more work */
2005-05-17 08:54:41 +04:00
static int ca_set_pid ( void )
{
/* We could make this more graceful ? */
return - EOPNOTSUPP ;
}
2006-06-21 23:33:21 +04:00
static void put_command_and_length ( u8 * data , int command , int length )
{
data [ 0 ] = ( command > > 16 ) & 0xff ;
data [ 1 ] = ( command > > 8 ) & 0xff ;
data [ 2 ] = command & 0xff ;
data [ 3 ] = length ;
}
2005-05-17 08:54:41 +04:00
2005-11-09 08:35:23 +03:00
static void put_checksum ( u8 * check_string , int length )
2005-05-17 08:54:41 +04:00
{
2005-11-09 08:35:23 +03:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Computing string checksum. " ) ;
dprintk ( verbose , DST_CA_DEBUG , 1 , " -> string length : 0x%02x " , length ) ;
check_string [ length ] = dst_check_sum ( check_string , length ) ;
dprintk ( verbose , DST_CA_DEBUG , 1 , " -> checksum : 0x%02x " , check_string [ length ] ) ;
2005-05-17 08:54:41 +04:00
}
static int dst_ci_command ( struct dst_state * state , u8 * data , u8 * ca_string , u8 len , int read )
{
u8 reply ;
2006-02-07 11:49:14 +03:00
mutex_lock ( & state - > dst_mutex ) ;
2005-05-17 08:54:41 +04:00
dst_comm_init ( state ) ;
msleep ( 65 ) ;
if ( write_dst ( state , data , len ) ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_INFO , 1 , " Write not successful, trying to recover " ) ;
2005-05-17 08:54:41 +04:00
dst_error_recovery ( state ) ;
2005-11-09 08:35:36 +03:00
goto error ;
2005-05-17 08:54:41 +04:00
}
if ( ( dst_pio_disable ( state ) ) < 0 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " DST PIO disable failed. " ) ;
2005-11-09 08:35:36 +03:00
goto error ;
2005-05-17 08:54:41 +04:00
}
if ( read_dst ( state , & reply , GET_ACK ) < 0 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_INFO , 1 , " Read not successful, trying to recover " ) ;
2005-05-17 08:54:41 +04:00
dst_error_recovery ( state ) ;
2005-11-09 08:35:36 +03:00
goto error ;
2005-05-17 08:54:41 +04:00
}
if ( read ) {
if ( ! dst_wait_dst_ready ( state , LONG_DELAY ) ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_NOTICE , 1 , " 8820 not ready " ) ;
2005-11-09 08:35:36 +03:00
goto error ;
2005-05-17 08:54:41 +04:00
}
if ( read_dst ( state , ca_string , 128 ) < 0 ) { /* Try to make this dynamic */
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_INFO , 1 , " Read not successful, trying to recover " ) ;
2005-05-17 08:54:41 +04:00
dst_error_recovery ( state ) ;
2005-11-09 08:35:36 +03:00
goto error ;
2005-05-17 08:54:41 +04:00
}
}
2006-02-07 11:49:14 +03:00
mutex_unlock ( & state - > dst_mutex ) ;
2005-05-17 08:54:41 +04:00
return 0 ;
2005-11-09 08:35:36 +03:00
error :
2006-02-07 11:49:14 +03:00
mutex_unlock ( & state - > dst_mutex ) ;
2005-11-09 08:35:36 +03:00
return - EIO ;
2005-05-17 08:54:41 +04:00
}
static int dst_put_ci ( struct dst_state * state , u8 * data , int len , u8 * ca_string , int read )
{
u8 dst_ca_comm_err = 0 ;
while ( dst_ca_comm_err < RETRIES ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_NOTICE , 1 , " Put Command " ) ;
2005-05-17 08:54:41 +04:00
if ( dst_ci_command ( state , data , ca_string , len , read ) ) { // If error
dst_error_recovery ( state ) ;
dst_ca_comm_err + + ; // work required here.
2006-06-21 23:33:21 +04:00
} else {
break ;
2005-05-17 08:54:41 +04:00
}
}
2006-06-21 23:33:21 +04:00
if ( dst_ca_comm_err = = RETRIES )
return - 1 ;
2005-05-17 08:54:41 +04:00
return 0 ;
}
static int ca_get_app_info ( struct dst_state * state )
{
2006-06-21 23:33:21 +04:00
int length , str_length ;
2005-05-17 08:54:41 +04:00
static u8 command [ 8 ] = { 0x07 , 0x40 , 0x01 , 0x00 , 0x01 , 0x00 , 0x00 , 0xff } ;
put_checksum ( & command [ 0 ] , command [ 0 ] ) ;
if ( ( dst_put_ci ( state , command , sizeof ( command ) , state - > messages , GET_REPLY ) ) < 0 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " -->dst_put_ci FAILED ! " ) ;
2005-05-17 08:54:41 +04:00
return - 1 ;
}
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_INFO , 1 , " -->dst_put_ci SUCCESS ! " ) ;
dprintk ( verbose , DST_CA_INFO , 1 , " ================================ CI Module Application Info ====================================== " ) ;
dprintk ( verbose , DST_CA_INFO , 1 , " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d] \n %s: Application info=[%s] " ,
state - > messages [ 7 ] , ( state - > messages [ 8 ] < < 8 ) | state - > messages [ 9 ] ,
( state - > messages [ 10 ] < < 8 ) | state - > messages [ 11 ] , __FUNCTION__ , ( char * ) ( & state - > messages [ 12 ] ) ) ;
dprintk ( verbose , DST_CA_INFO , 1 , " ================================================================================================== " ) ;
2005-05-17 08:54:41 +04:00
2006-06-21 23:33:21 +04:00
// Transform dst message to correct application_info message
length = state - > messages [ 5 ] ;
str_length = length - 6 ;
if ( str_length < 0 ) {
str_length = 0 ;
dprintk ( verbose , DST_CA_ERROR , 1 , " Invalid string length returned in ca_get_app_info(). Recovering. " ) ;
}
// First, the command and length fields
put_command_and_length ( & state - > messages [ 0 ] , CA_APP_INFO , length ) ;
// Copy application_type, application_manufacturer and manufacturer_code
memcpy ( & state - > messages [ 4 ] , & state - > messages [ 7 ] , 5 ) ;
// Set string length and copy string
state - > messages [ 9 ] = str_length ;
memcpy ( & state - > messages [ 10 ] , & state - > messages [ 12 ] , str_length ) ;
return 0 ;
}
static int ca_get_ca_info ( struct dst_state * state )
{
int srcPtr , dstPtr , i , num_ids ;
static u8 slot_command [ 8 ] = { 0x07 , 0x40 , 0x00 , 0x00 , 0x02 , 0x00 , 0x00 , 0xff } ;
const int in_system_id_pos = 8 , out_system_id_pos = 4 , in_num_ids_pos = 7 ;
put_checksum ( & slot_command [ 0 ] , slot_command [ 0 ] ) ;
if ( ( dst_put_ci ( state , slot_command , sizeof ( slot_command ) , state - > messages , GET_REPLY ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->dst_put_ci FAILED ! " ) ;
return - 1 ;
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->dst_put_ci SUCCESS ! " ) ;
// Print raw data
dprintk ( verbose , DST_CA_INFO , 0 , " DST data = [ " ) ;
for ( i = 0 ; i < state - > messages [ 0 ] + 1 ; i + + ) {
dprintk ( verbose , DST_CA_INFO , 0 , " 0x%02x " , state - > messages [ i ] ) ;
}
dprintk ( verbose , DST_CA_INFO , 0 , " ] \n " ) ;
// Set the command and length of the output
num_ids = state - > messages [ in_num_ids_pos ] ;
if ( num_ids > = 100 ) {
num_ids = 100 ;
dprintk ( verbose , DST_CA_ERROR , 1 , " Invalid number of ids (>100). Recovering. " ) ;
}
put_command_and_length ( & state - > messages [ 0 ] , CA_INFO , num_ids * 2 ) ;
dprintk ( verbose , DST_CA_INFO , 0 , " CA_INFO = [ " ) ;
srcPtr = in_system_id_pos ;
dstPtr = out_system_id_pos ;
for ( i = 0 ; i < num_ids ; i + + ) {
dprintk ( verbose , DST_CA_INFO , 0 , " 0x%02x%02x " , state - > messages [ srcPtr + 0 ] , state - > messages [ srcPtr + 1 ] ) ;
// Append to output
state - > messages [ dstPtr + 0 ] = state - > messages [ srcPtr + 0 ] ;
state - > messages [ dstPtr + 1 ] = state - > messages [ srcPtr + 1 ] ;
srcPtr + = 2 ;
dstPtr + = 2 ;
}
dprintk ( verbose , DST_CA_INFO , 0 , " ] \n " ) ;
2005-05-17 08:54:41 +04:00
return 0 ;
}
2005-11-09 08:35:18 +03:00
static int ca_get_slot_caps ( struct dst_state * state , struct ca_caps * p_ca_caps , void __user * arg )
2005-05-17 08:54:41 +04:00
{
int i ;
u8 slot_cap [ 256 ] ;
static u8 slot_command [ 8 ] = { 0x07 , 0x40 , 0x02 , 0x00 , 0x02 , 0x00 , 0x00 , 0xff } ;
put_checksum ( & slot_command [ 0 ] , slot_command [ 0 ] ) ;
if ( ( dst_put_ci ( state , slot_command , sizeof ( slot_command ) , slot_cap , GET_REPLY ) ) < 0 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " -->dst_put_ci FAILED ! " ) ;
2005-05-17 08:54:41 +04:00
return - 1 ;
}
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_NOTICE , 1 , " -->dst_put_ci SUCCESS ! " ) ;
2005-05-17 08:54:41 +04:00
/* Will implement the rest soon */
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_INFO , 1 , " Slot cap = [%d] " , slot_cap [ 7 ] ) ;
dprintk ( verbose , DST_CA_INFO , 0 , " =================================== \n " ) ;
2006-06-21 23:33:21 +04:00
for ( i = 0 ; i < slot_cap [ 0 ] + 1 ; i + + )
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_INFO , 0 , " %d " , slot_cap [ i ] ) ;
dprintk ( verbose , DST_CA_INFO , 0 , " \n " ) ;
2005-05-17 08:54:41 +04:00
p_ca_caps - > slot_num = 1 ;
p_ca_caps - > slot_type = 1 ;
p_ca_caps - > descr_num = slot_cap [ 7 ] ;
p_ca_caps - > descr_type = 1 ;
2005-11-09 08:35:18 +03:00
if ( copy_to_user ( arg , p_ca_caps , sizeof ( struct ca_caps ) ) )
2005-05-17 08:54:41 +04:00
return - EFAULT ;
return 0 ;
}
2005-05-17 08:54:45 +04:00
/* Need some more work */
2005-11-09 08:35:18 +03:00
static int ca_get_slot_descr ( struct dst_state * state , struct ca_msg * p_ca_message , void __user * arg )
2005-05-17 08:54:41 +04:00
{
return - EOPNOTSUPP ;
}
2005-11-09 08:35:18 +03:00
static int ca_get_slot_info ( struct dst_state * state , struct ca_slot_info * p_ca_slot_info , void __user * arg )
2005-05-17 08:54:41 +04:00
{
int i ;
static u8 slot_command [ 8 ] = { 0x00 , 0x05 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xff } ;
2005-11-09 08:35:24 +03:00
u8 * slot_info = state - > messages ;
2005-05-17 08:54:41 +04:00
put_checksum ( & slot_command [ 0 ] , 7 ) ;
if ( ( dst_put_ci ( state , slot_command , sizeof ( slot_command ) , slot_info , GET_REPLY ) ) < 0 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " -->dst_put_ci FAILED ! " ) ;
2005-05-17 08:54:41 +04:00
return - 1 ;
}
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_INFO , 1 , " -->dst_put_ci SUCCESS ! " ) ;
2005-05-17 08:54:41 +04:00
/* Will implement the rest soon */
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_INFO , 1 , " Slot info = [%d] " , slot_info [ 3 ] ) ;
dprintk ( verbose , DST_CA_INFO , 0 , " =================================== \n " ) ;
for ( i = 0 ; i < 8 ; i + + )
dprintk ( verbose , DST_CA_INFO , 0 , " %d " , slot_info [ i ] ) ;
dprintk ( verbose , DST_CA_INFO , 0 , " \n " ) ;
2005-05-17 08:54:41 +04:00
if ( slot_info [ 4 ] & 0x80 ) {
p_ca_slot_info - > flags = CA_CI_MODULE_PRESENT ;
p_ca_slot_info - > num = 1 ;
p_ca_slot_info - > type = CA_CI ;
2005-09-10 00:03:00 +04:00
} else if ( slot_info [ 4 ] & 0x40 ) {
2005-05-17 08:54:41 +04:00
p_ca_slot_info - > flags = CA_CI_MODULE_READY ;
p_ca_slot_info - > num = 1 ;
p_ca_slot_info - > type = CA_CI ;
2005-09-10 00:03:00 +04:00
} else
2005-05-17 08:54:41 +04:00
p_ca_slot_info - > flags = 0 ;
2005-11-09 08:35:18 +03:00
if ( copy_to_user ( arg , p_ca_slot_info , sizeof ( struct ca_slot_info ) ) )
2005-05-17 08:54:41 +04:00
return - EFAULT ;
return 0 ;
}
2005-11-09 08:35:18 +03:00
static int ca_get_message ( struct dst_state * state , struct ca_msg * p_ca_message , void __user * arg )
2005-05-17 08:54:41 +04:00
{
u8 i = 0 ;
u32 command = 0 ;
2005-11-09 08:35:18 +03:00
if ( copy_from_user ( p_ca_message , arg , sizeof ( struct ca_msg ) ) )
2005-05-17 08:54:41 +04:00
return - EFAULT ;
if ( p_ca_message - > msg ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_NOTICE , 1 , " Message = [%02x %02x %02x] " , p_ca_message - > msg [ 0 ] , p_ca_message - > msg [ 1 ] , p_ca_message - > msg [ 2 ] ) ;
2005-05-17 08:54:41 +04:00
for ( i = 0 ; i < 3 ; i + + ) {
command = command | p_ca_message - > msg [ i ] ;
if ( i < 2 )
command = command < < 8 ;
}
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_NOTICE , 1 , " Command=[0x%x] " , command ) ;
2005-05-17 08:54:41 +04:00
switch ( command ) {
2005-09-10 00:03:00 +04:00
case CA_APP_INFO :
memcpy ( p_ca_message - > msg , state - > messages , 128 ) ;
2005-11-09 08:35:18 +03:00
if ( copy_to_user ( arg , p_ca_message , sizeof ( struct ca_msg ) ) )
2005-09-10 00:03:00 +04:00
return - EFAULT ;
2005-05-17 08:54:41 +04:00
break ;
2006-06-21 23:33:21 +04:00
case CA_INFO :
memcpy ( p_ca_message - > msg , state - > messages , 128 ) ;
if ( copy_to_user ( arg , p_ca_message , sizeof ( struct ca_msg ) ) )
return - EFAULT ;
break ;
2005-05-17 08:54:41 +04:00
}
}
return 0 ;
}
2005-07-08 04:57:50 +04:00
static int handle_dst_tag ( struct dst_state * state , struct ca_msg * p_ca_message , struct ca_msg * hw_buffer , u32 length )
2005-05-17 08:54:41 +04:00
{
2005-05-17 08:54:45 +04:00
if ( state - > dst_hw_cap & DST_TYPE_HAS_SESSION ) {
2005-09-10 00:02:59 +04:00
hw_buffer - > msg [ 2 ] = p_ca_message - > msg [ 1 ] ; /* MSB */
hw_buffer - > msg [ 3 ] = p_ca_message - > msg [ 2 ] ; /* LSB */
} else {
if ( length > 247 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " Message too long ! *** Bailing Out *** ! " ) ;
2005-09-10 00:02:59 +04:00
return - 1 ;
}
2005-07-08 04:57:50 +04:00
hw_buffer - > msg [ 0 ] = ( length & 0xff ) + 7 ;
hw_buffer - > msg [ 1 ] = 0x40 ;
2005-05-17 08:54:41 +04:00
hw_buffer - > msg [ 2 ] = 0x03 ;
hw_buffer - > msg [ 3 ] = 0x00 ;
2005-07-08 04:57:50 +04:00
hw_buffer - > msg [ 4 ] = 0x03 ;
hw_buffer - > msg [ 5 ] = length & 0xff ;
hw_buffer - > msg [ 6 ] = 0x00 ;
2006-01-09 20:25:34 +03:00
2005-09-10 00:02:59 +04:00
/*
* Need to compute length for EN50221 section 8.3 .2 , for the time being
* assuming 8.3 .2 is not applicable
*/
memcpy ( & hw_buffer - > msg [ 7 ] , & p_ca_message - > msg [ 4 ] , length ) ;
2005-05-17 08:54:41 +04:00
}
2006-01-09 20:25:34 +03:00
2005-05-17 08:54:41 +04:00
return 0 ;
}
2005-07-08 04:57:50 +04:00
static int write_to_8820 ( struct dst_state * state , struct ca_msg * hw_buffer , u8 length , u8 reply )
2005-05-17 08:54:41 +04:00
{
2005-07-08 04:57:50 +04:00
if ( ( dst_put_ci ( state , hw_buffer - > msg , length , hw_buffer - > msg , reply ) ) < 0 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " DST-CI Command failed. " ) ;
dprintk ( verbose , DST_CA_NOTICE , 1 , " Resetting DST. " ) ;
2005-05-17 08:54:41 +04:00
rdc_reset_state ( state ) ;
return - 1 ;
}
2006-06-21 23:33:21 +04:00
dprintk ( verbose , DST_CA_NOTICE , 1 , " DST-CI Command success. " ) ;
2005-05-17 08:54:41 +04:00
return 0 ;
}
2005-11-09 08:35:18 +03:00
static u32 asn_1_decode ( u8 * asn_1_array )
2005-05-17 08:54:41 +04:00
{
2005-07-08 04:57:50 +04:00
u8 length_field = 0 , word_count = 0 , count = 0 ;
u32 length = 0 ;
length_field = asn_1_array [ 0 ] ;
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Length field=[%02x] " , length_field ) ;
2005-07-08 04:57:50 +04:00
if ( length_field < 0x80 ) {
length = length_field & 0x7f ;
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Length=[%02x] \n " , length ) ;
2005-07-08 04:57:50 +04:00
} else {
word_count = length_field & 0x7f ;
for ( count = 0 ; count < word_count ; count + + ) {
2005-11-09 08:35:17 +03:00
length = length < < 8 ;
length + = asn_1_array [ count + 1 ] ;
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Length=[%04x] " , length ) ;
2005-05-17 08:54:41 +04:00
}
}
2005-07-08 04:57:50 +04:00
return length ;
}
2005-05-17 08:54:41 +04:00
2005-07-08 04:57:50 +04:00
static int debug_string ( u8 * msg , u32 length , u32 offset )
{
u32 i ;
2005-05-17 08:54:41 +04:00
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 0 , " String=[ " ) ;
2005-07-08 04:57:50 +04:00
for ( i = offset ; i < length ; i + + )
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 0 , " %02x " , msg [ i ] ) ;
dprintk ( verbose , DST_CA_DEBUG , 0 , " ] \n " ) ;
2005-05-17 08:54:41 +04:00
2005-07-08 04:57:50 +04:00
return 0 ;
}
2005-05-17 08:54:41 +04:00
2006-06-21 22:06:49 +04:00
2005-07-08 04:57:50 +04:00
static int ca_set_pmt ( struct dst_state * state , struct ca_msg * p_ca_message , struct ca_msg * hw_buffer , u8 reply , u8 query )
{
2005-09-10 00:02:59 +04:00
u32 length = 0 ;
u8 tag_length = 8 ;
2005-05-17 08:54:41 +04:00
2005-07-08 04:57:50 +04:00
length = asn_1_decode ( & p_ca_message - > msg [ 3 ] ) ;
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " CA Message length=[%d] " , length ) ;
2005-09-10 00:02:59 +04:00
debug_string ( & p_ca_message - > msg [ 4 ] , length , 0 ) ; /* length is excluding tag & length */
2005-05-17 08:54:41 +04:00
2005-09-10 00:02:59 +04:00
memset ( hw_buffer - > msg , ' \0 ' , length ) ;
2005-07-08 04:57:50 +04:00
handle_dst_tag ( state , p_ca_message , hw_buffer , length ) ;
2005-09-10 00:02:59 +04:00
put_checksum ( hw_buffer - > msg , hw_buffer - > msg [ 0 ] ) ;
2005-05-17 08:54:41 +04:00
2005-09-10 00:02:59 +04:00
debug_string ( hw_buffer - > msg , ( length + tag_length ) , 0 ) ; /* tags too */
write_to_8820 ( state , hw_buffer , ( length + tag_length ) , reply ) ;
2005-05-17 08:54:41 +04:00
return 0 ;
}
2005-07-08 04:57:50 +04:00
2005-05-17 08:54:41 +04:00
/* Board supports CA PMT reply ? */
static int dst_check_ca_pmt ( struct dst_state * state , struct ca_msg * p_ca_message , struct ca_msg * hw_buffer )
{
int ca_pmt_reply_test = 0 ;
/* Do test board */
/* Not there yet but soon */
/* CA PMT Reply capable */
if ( ca_pmt_reply_test ) {
if ( ( ca_set_pmt ( state , p_ca_message , hw_buffer , 1 , GET_REPLY ) ) < 0 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " ca_set_pmt.. failed ! " ) ;
2005-05-17 08:54:41 +04:00
return - 1 ;
}
/* Process CA PMT Reply */
/* will implement soon */
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " Not there yet " ) ;
2005-05-17 08:54:41 +04:00
}
/* CA PMT Reply not capable */
if ( ! ca_pmt_reply_test ) {
if ( ( ca_set_pmt ( state , p_ca_message , hw_buffer , 0 , NO_REPLY ) ) < 0 ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " ca_set_pmt.. failed ! " ) ;
2005-05-17 08:54:41 +04:00
return - 1 ;
}
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_NOTICE , 1 , " ca_set_pmt.. success ! " ) ;
2005-05-17 08:54:41 +04:00
/* put a dummy message */
}
return 0 ;
}
2005-11-09 08:35:18 +03:00
static int ca_send_message ( struct dst_state * state , struct ca_msg * p_ca_message , void __user * arg )
2005-05-17 08:54:41 +04:00
{
int i = 0 ;
unsigned int ca_message_header_len ;
u32 command = 0 ;
struct ca_msg * hw_buffer ;
2005-11-09 08:35:19 +03:00
int result = 0 ;
2005-05-17 08:54:41 +04:00
if ( ( hw_buffer = ( struct ca_msg * ) kmalloc ( sizeof ( struct ca_msg ) , GFP_KERNEL ) ) = = NULL ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " Memory allocation failure " ) ;
2005-05-17 08:54:41 +04:00
return - ENOMEM ;
}
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " " ) ;
2005-05-17 08:54:41 +04:00
2005-12-15 12:18:45 +03:00
if ( copy_from_user ( p_ca_message , arg , sizeof ( struct ca_msg ) ) ) {
2005-11-09 08:35:19 +03:00
result = - EFAULT ;
goto free_mem_and_exit ;
}
2005-05-17 08:54:41 +04:00
if ( p_ca_message - > msg ) {
ca_message_header_len = p_ca_message - > length ; /* Restore it back when you are done */
/* EN50221 tag */
command = 0 ;
for ( i = 0 ; i < 3 ; i + + ) {
command = command | p_ca_message - > msg [ i ] ;
if ( i < 2 )
command = command < < 8 ;
}
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Command=[0x%x] \n " , command ) ;
2005-05-17 08:54:41 +04:00
switch ( command ) {
2005-09-10 00:03:00 +04:00
case CA_PMT :
dprintk ( verbose , DST_CA_DEBUG , 1 , " Command = SEND_CA_PMT " ) ;
if ( ( ca_set_pmt ( state , p_ca_message , hw_buffer , 0 , 0 ) ) < 0 ) { // code simplification started
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_PMT Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_PMT Success ! " ) ;
break ;
case CA_PMT_REPLY :
dprintk ( verbose , DST_CA_INFO , 1 , " Command = CA_PMT_REPLY " ) ;
/* Have to handle the 2 basic types of cards here */
if ( ( dst_check_ca_pmt ( state , p_ca_message , hw_buffer ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_PMT_REPLY Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_PMT_REPLY Success ! " ) ;
break ;
case CA_APP_INFO_ENQUIRY : // only for debugging
dprintk ( verbose , DST_CA_INFO , 1 , " Getting Cam Application information " ) ;
if ( ( ca_get_app_info ( state ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_APP_INFO_ENQUIRY Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_APP_INFO_ENQUIRY Success ! " ) ;
break ;
2006-06-21 23:33:21 +04:00
case CA_INFO_ENQUIRY :
dprintk ( verbose , DST_CA_INFO , 1 , " Getting CA Information " ) ;
if ( ( ca_get_ca_info ( state ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_INFO_ENQUIRY Failed ! " ) ;
result = - 1 ;
goto free_mem_and_exit ;
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_INFO_ENQUIRY Success ! " ) ;
break ;
2005-05-17 08:54:41 +04:00
}
}
2005-11-09 08:35:19 +03:00
free_mem_and_exit :
kfree ( hw_buffer ) ;
return result ;
2005-05-17 08:54:41 +04:00
}
2005-11-09 08:35:18 +03:00
static int dst_ca_ioctl ( struct inode * inode , struct file * file , unsigned int cmd , unsigned long ioctl_arg )
2005-05-17 08:54:41 +04:00
{
struct dvb_device * dvbdev = ( struct dvb_device * ) file - > private_data ;
struct dst_state * state = ( struct dst_state * ) dvbdev - > priv ;
struct ca_slot_info * p_ca_slot_info ;
struct ca_caps * p_ca_caps ;
struct ca_msg * p_ca_message ;
2005-11-09 08:35:18 +03:00
void __user * arg = ( void __user * ) ioctl_arg ;
2005-11-09 08:35:19 +03:00
int result = 0 ;
2005-05-17 08:54:41 +04:00
2006-06-23 23:13:56 +04:00
p_ca_message = kmalloc ( sizeof ( struct ca_msg ) , GFP_KERNEL ) ;
p_ca_slot_info = kmalloc ( sizeof ( struct ca_slot_info ) , GFP_KERNEL ) ;
p_ca_caps = kmalloc ( sizeof ( struct ca_caps ) , GFP_KERNEL ) ;
if ( ! p_ca_message | | ! p_ca_slot_info | | ! p_ca_caps ) {
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " Memory allocation failure " ) ;
2006-06-23 23:13:56 +04:00
result = - ENOMEM ;
goto free_mem_and_exit ;
2005-05-17 08:54:41 +04:00
}
2006-06-23 23:13:56 +04:00
2005-05-17 08:54:41 +04:00
/* We have now only the standard ioctl's, the driver is upposed to handle internals. */
switch ( cmd ) {
2005-09-10 00:03:00 +04:00
case CA_SEND_MSG :
dprintk ( verbose , DST_CA_INFO , 1 , " Sending message " ) ;
if ( ( ca_send_message ( state , p_ca_message , arg ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_SEND_MSG Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
break ;
case CA_GET_MSG :
dprintk ( verbose , DST_CA_INFO , 1 , " Getting message " ) ;
if ( ( ca_get_message ( state , p_ca_message , arg ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_GET_MSG Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_GET_MSG Success ! " ) ;
break ;
case CA_RESET :
dprintk ( verbose , DST_CA_ERROR , 1 , " Resetting DST " ) ;
dst_error_bailout ( state ) ;
msleep ( 4000 ) ;
break ;
case CA_GET_SLOT_INFO :
dprintk ( verbose , DST_CA_INFO , 1 , " Getting Slot info " ) ;
if ( ( ca_get_slot_info ( state , p_ca_slot_info , arg ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_GET_SLOT_INFO Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_GET_SLOT_INFO Success ! " ) ;
break ;
case CA_GET_CAP :
dprintk ( verbose , DST_CA_INFO , 1 , " Getting Slot capabilities " ) ;
if ( ( ca_get_slot_caps ( state , p_ca_caps , arg ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_GET_CAP Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_GET_CAP Success ! " ) ;
break ;
case CA_GET_DESCR_INFO :
dprintk ( verbose , DST_CA_INFO , 1 , " Getting descrambler description " ) ;
if ( ( ca_get_slot_descr ( state , p_ca_message , arg ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_GET_DESCR_INFO Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_GET_DESCR_INFO Success ! " ) ;
break ;
case CA_SET_DESCR :
dprintk ( verbose , DST_CA_INFO , 1 , " Setting descrambler " ) ;
if ( ( ca_set_slot_descr ( ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_SET_DESCR Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_SET_DESCR Success ! " ) ;
break ;
case CA_SET_PID :
dprintk ( verbose , DST_CA_INFO , 1 , " Setting PID " ) ;
if ( ( ca_set_pid ( ) ) < 0 ) {
dprintk ( verbose , DST_CA_ERROR , 1 , " -->CA_SET_PID Failed ! " ) ;
2005-11-09 08:35:19 +03:00
result = - 1 ;
goto free_mem_and_exit ;
2005-09-10 00:03:00 +04:00
}
dprintk ( verbose , DST_CA_INFO , 1 , " -->CA_SET_PID Success ! " ) ;
default :
2005-11-09 08:35:19 +03:00
result = - EOPNOTSUPP ;
2005-09-10 00:03:00 +04:00
} ;
2005-11-09 08:35:19 +03:00
free_mem_and_exit :
kfree ( p_ca_message ) ;
kfree ( p_ca_slot_info ) ;
kfree ( p_ca_caps ) ;
2005-05-17 08:54:41 +04:00
2005-11-09 08:35:19 +03:00
return result ;
2005-05-17 08:54:41 +04:00
}
static int dst_ca_open ( struct inode * inode , struct file * file )
{
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Device opened [%p] " , file ) ;
2005-05-17 08:54:41 +04:00
try_module_get ( THIS_MODULE ) ;
return 0 ;
}
static int dst_ca_release ( struct inode * inode , struct file * file )
{
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Device closed. " ) ;
2005-05-17 08:54:41 +04:00
module_put ( THIS_MODULE ) ;
return 0 ;
}
2005-12-15 12:18:45 +03:00
static ssize_t dst_ca_read ( struct file * file , char __user * buffer , size_t length , loff_t * offset )
2005-05-17 08:54:41 +04:00
{
2006-05-22 17:31:57 +04:00
ssize_t bytes_read = 0 ;
2005-05-17 08:54:41 +04:00
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Device read. " ) ;
2005-05-17 08:54:41 +04:00
return bytes_read ;
}
2005-12-15 12:18:45 +03:00
static ssize_t dst_ca_write ( struct file * file , const char __user * buffer , size_t length , loff_t * offset )
2005-05-17 08:54:41 +04:00
{
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_DEBUG , 1 , " Device write. " ) ;
2005-05-17 08:54:41 +04:00
return 0 ;
}
static struct file_operations dst_ca_fops = {
. owner = THIS_MODULE ,
2005-11-09 08:35:18 +03:00
. ioctl = dst_ca_ioctl ,
2005-05-17 08:54:41 +04:00
. open = dst_ca_open ,
. release = dst_ca_release ,
. read = dst_ca_read ,
. write = dst_ca_write
} ;
static struct dvb_device dvbdev_ca = {
. priv = NULL ,
. users = 1 ,
. readers = 1 ,
. writers = 1 ,
. fops = & dst_ca_fops
} ;
2006-08-08 22:48:08 +04:00
struct dvb_device * dst_ca_attach ( struct dst_state * dst , struct dvb_adapter * dvb_adapter )
2005-05-17 08:54:41 +04:00
{
struct dvb_device * dvbdev ;
2006-08-08 22:48:08 +04:00
2005-09-10 00:03:00 +04:00
dprintk ( verbose , DST_CA_ERROR , 1 , " registering DST-CA device " ) ;
2006-08-08 22:48:08 +04:00
if ( dvb_register_device ( dvb_adapter , & dvbdev , & dvbdev_ca , dst , DVB_DEVICE_CA ) = = 0 ) {
dst - > dst_ca = dvbdev ;
return dst - > dst_ca ;
}
return NULL ;
2005-05-17 08:54:41 +04:00
}
EXPORT_SYMBOL ( dst_ca_attach ) ;
MODULE_DESCRIPTION ( " DST DVB-S/T/C Combo CA driver " ) ;
MODULE_AUTHOR ( " Manu Abraham " ) ;
MODULE_LICENSE ( " GPL " ) ;