Commit 23f5aa02 authored by Christophe Massiot's avatar Christophe Massiot

* modules/access/dvb: Full support for DVB MMI menus via an optional HTTP

   server.
parent 88aa4077
......@@ -2,5 +2,6 @@ SOURCES_dvb = \
access.c \
linux_dvb.c \
en50221.c \
http.c \
dvb.h \
$(NULL)
/*****************************************************************************
* access.c: DVB card input v4l2 only
*****************************************************************************
* Copyright (C) 1998-2004 the VideoLAN team
* Copyright (C) 1998-2005 the VideoLAN team
*
* Authors: Johan Bilien <jobi@via.ecp.fr>
* Jean-Paul Saman <jpsaman@wxs.nl>
......@@ -56,6 +56,10 @@
# include "psi.h"
#endif
#ifdef ENABLE_HTTPD
# include "vlc_httpd.h"
#endif
#include "dvb.h"
/*****************************************************************************
......@@ -139,6 +143,40 @@ static void Close( vlc_object_t *p_this );
#define HIERARCHY_TEXT N_("Terrestrial hierarchy mode")
#define HIERARCHY_LONGTEXT ""
#define HOST_TEXT N_( "HTTP Host address" )
#define HOST_LONGTEXT N_( \
"To enable the internal HTTP server, set its address and port here." )
#define USER_TEXT N_( "HTTP user name" )
#define USER_LONGTEXT N_( \
"You can set the user name the administrator will use to log into " \
"the internal HTTP server." )
#define PASSWORD_TEXT N_( "HTTP password" )
#define PASSWORD_LONGTEXT N_( \
"You can set the password the administrator will use to log into " \
"the internal HTTP server." )
#define ACL_TEXT N_( "HTTP ACL" )
#define ACL_LONGTEXT N_( \
"You can set the access control list (equivalent to .hosts) file path, " \
"which will limit the range of IPs entitled to log into the internal " \
"HTTP server." )
#define CERT_TEXT N_( "Certificate file" )
#define CERT_LONGTEXT N_( "HTTP interface x509 PEM certificate file " \
"(enables SSL)" )
#define KEY_TEXT N_( "Private key file" )
#define KEY_LONGTEXT N_( "HTTP interface x509 PEM private key file" )
#define CA_TEXT N_( "Root CA file" )
#define CA_LONGTEXT N_( "HTTP interface x509 PEM trusted root CA " \
"certificates file" )
#define CRL_TEXT N_( "CRL file" )
#define CRL_LONGTEXT N_( "HTTP interface Certificates Revocation List file" )
vlc_module_begin();
set_shortname( _("DVB") );
set_description( N_("DVB input with v4l2 support") );
......@@ -191,6 +229,26 @@ vlc_module_begin();
TRANSMISSION_LONGTEXT, VLC_TRUE );
add_integer( "dvb-hierarchy", 0, NULL, HIERARCHY_TEXT, HIERARCHY_LONGTEXT,
VLC_TRUE );
#ifdef ENABLE_HTTPD
/* MMI HTTP interface */
set_section( N_("HTTP server" ), 0 );
add_string( "dvb-http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT,
VLC_TRUE );
add_string( "dvb-http-user", NULL, NULL, USER_TEXT, USER_LONGTEXT,
VLC_TRUE );
add_string( "dvb-http-password", NULL, NULL, PASSWORD_TEXT,
PASSWORD_LONGTEXT, VLC_TRUE );
add_string( "dvb-http-acl", NULL, NULL, ACL_TEXT, ACL_LONGTEXT,
VLC_TRUE );
add_string( "dvb-http-intf-cert", NULL, NULL, CERT_TEXT, CERT_LONGTEXT,
VLC_TRUE );
add_string( "dvb-http-intf-key", NULL, NULL, KEY_TEXT, KEY_LONGTEXT,
VLC_TRUE );
add_string( "dvb-http-intf-ca", NULL, NULL, CA_TEXT, CA_LONGTEXT,
VLC_TRUE );
add_string( "dvb-http-intf-crl", NULL, NULL, CRL_TEXT, CRL_LONGTEXT,
VLC_TRUE );
#endif
set_capability( "access2", 0 );
add_shortcut( "dvb" );
......@@ -303,6 +361,10 @@ static int Open( vlc_object_t *p_this )
else
p_sys->i_read_once = DVB_READ_ONCE_START;
#ifdef ENABLE_HTTPD
E_(HTTPOpen)( p_access );
#endif
return VLC_SUCCESS;
}
......@@ -320,6 +382,10 @@ static void Close( vlc_object_t *p_this )
E_(FrontendClose)( p_access );
E_(CAMClose)( p_access );
#ifdef ENABLE_HTTPD
E_(HTTPClose)( p_access );
#endif
free( p_sys );
}
......@@ -376,6 +442,37 @@ static block_t *Block( access_t *p_access )
E_(FrontendPoll)( p_access );
}
#ifdef ENABLE_HTTPD
if ( p_sys->i_httpd_timeout && mdate() > p_sys->i_httpd_timeout )
{
vlc_mutex_lock( &p_sys->httpd_mutex );
if ( p_sys->b_request_frontend_info )
{
msg_Warn( p_access, "frontend timeout for HTTP interface" );
p_sys->b_request_frontend_info = VLC_FALSE;
p_sys->psz_frontend_info = strdup( "Timeout getting info\n" );
}
if ( p_sys->b_request_mmi_info )
{
msg_Warn( p_access, "MMI timeout for HTTP interface" );
p_sys->b_request_mmi_info = VLC_FALSE;
p_sys->psz_mmi_info = strdup( "Timeout getting info\n" );
}
vlc_cond_signal( &p_sys->httpd_cond );
vlc_mutex_unlock( &p_sys->httpd_mutex );
}
if ( p_sys->b_request_frontend_info )
{
E_(FrontendStatus)( p_access );
}
if ( p_sys->b_request_mmi_info )
{
E_(CAMStatus)( p_access );
}
#endif
if ( p_sys->i_frontend_timeout && mdate() > p_sys->i_frontend_timeout )
{
msg_Warn( p_access, "no lock, tuning again" );
......@@ -577,6 +674,17 @@ static void VarInit( access_t *p_access )
var_Create( p_access, "dvb-transmission", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-guard", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-hierarchy", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
#ifdef ENABLE_HTTPD
var_Create( p_access, "dvb-http-host", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-http-user", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-http-password", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-http-acl", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-http-intf-cert", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-http-intf-key", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-http-intf-ca", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-http-intf-crl", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
#endif
}
/* */
......
/*****************************************************************************
* dvb.h : functions to control a DVB card under Linux with v4l2
*****************************************************************************
* Copyright (C) 1998-2004 the VideoLAN team
* Copyright (C) 1998-2005 the VideoLAN team
*
* Authors: Johan Bilien <jobi@via.ecp.fr>
* Jean-Paul Saman <jpsaman@saman>
......@@ -35,7 +35,7 @@
/*****************************************************************************
* Local structures
*****************************************************************************/
typedef struct
typedef struct demux_handle_t
{
int i_pid;
int i_handle;
......@@ -44,7 +44,7 @@ typedef struct
typedef struct frontend_t frontend_t;
typedef struct
typedef struct en50221_session_t
{
int i_slot;
int i_resource_id;
......@@ -54,6 +54,84 @@ typedef struct
void *p_sys;
} en50221_session_t;
#define EN50221_MMI_NONE 0
#define EN50221_MMI_ENQ 1
#define EN50221_MMI_ANSW 2
#define EN50221_MMI_MENU 3
#define EN50221_MMI_MENU_ANSW 4
#define EN50221_MMI_LIST 5
typedef struct en50221_mmi_object_t
{
int i_object_type;
union
{
struct
{
vlc_bool_t b_blind;
char *psz_text;
} enq;
struct
{
vlc_bool_t b_ok;
char *psz_answ;
} answ;
struct
{
char *psz_title, *psz_subtitle, *psz_bottom;
char **ppsz_choices;
int i_choices;
} menu; /* menu and list are the same */
struct
{
int i_choice;
} menu_answ;
} u;
} en50221_mmi_object_t;
static __inline__ void en50221_MMIFree( en50221_mmi_object_t *p_object )
{
int i;
#define FREE( x ) \
if ( x != NULL ) \
free( x );
switch ( p_object->i_object_type )
{
case EN50221_MMI_ENQ:
FREE( p_object->u.enq.psz_text );
break;
case EN50221_MMI_ANSW:
if ( p_object->u.answ.b_ok )
{
FREE( p_object->u.answ.psz_answ );
}
break;
case EN50221_MMI_MENU:
case EN50221_MMI_LIST:
FREE( p_object->u.menu.psz_title );
FREE( p_object->u.menu.psz_subtitle );
FREE( p_object->u.menu.psz_bottom );
for ( i = 0; i < p_object->u.menu.i_choices; i++ )
{
FREE( p_object->u.menu.ppsz_choices[i] );
}
FREE( p_object->u.menu.ppsz_choices );
break;
default:
break;
}
#undef FREE
}
#define MAX_DEMUX 256
#define MAX_CI_SLOTS 16
#define MAX_SESSIONS 32
......@@ -72,6 +150,8 @@ struct access_sys_t
int i_nb_slots;
vlc_bool_t pb_active_slot[MAX_CI_SLOTS];
vlc_bool_t pb_tc_has_data[MAX_CI_SLOTS];
vlc_bool_t pb_slot_mmi_expected[MAX_CI_SLOTS];
vlc_bool_t pb_slot_mmi_undisplayed[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];
......@@ -79,6 +159,20 @@ struct access_sys_t
/* */
int i_read_once;
#ifdef ENABLE_HTTPD
/* Local HTTP server */
httpd_host_t *p_httpd_host;
httpd_file_sys_t *p_httpd_file;
httpd_redirect_t *p_httpd_redir;
vlc_mutex_t httpd_mutex;
vlc_cond_t httpd_cond;
mtime_t i_httpd_timeout;
vlc_bool_t b_request_frontend_info, b_request_mmi_info;
char *psz_frontend_info, *psz_mmi_info;
char *psz_request;
#endif
};
#define VIDEO0_TYPE 1
......@@ -96,6 +190,9 @@ int E_(FrontendOpen)( access_t * );
void E_(FrontendPoll)( access_t *p_access );
int E_(FrontendSet)( access_t * );
void E_(FrontendClose)( access_t * );
#ifdef ENABLE_HTTPD
void E_(FrontendStatus)( access_t * );
#endif
int E_(DMXSetFilter)( access_t *, int i_pid, int * pi_fd, int i_type );
int E_(DMXUnsetFilter)( access_t *, int i_fd );
......@@ -107,9 +204,25 @@ int E_(CAMOpen)( access_t * );
int E_(CAMPoll)( access_t * );
int E_(CAMSet)( access_t *, dvbpsi_pmt_t * );
void E_(CAMClose)( access_t * );
#ifdef ENABLE_HTTPD
void E_(CAMStatus)( access_t * );
#endif
int E_(en50221_Init)( access_t * );
int E_(en50221_Poll)( access_t * );
int E_(en50221_SetCAPMT)( access_t *, dvbpsi_pmt_t * );
int E_(en50221_OpenMMI)( access_t * p_access, int i_slot );
int E_(en50221_CloseMMI)( access_t * p_access, int i_slot );
en50221_mmi_object_t *E_(en50221_GetMMIObject)( access_t * p_access,
int i_slot );
void E_(en50221_SendMMIObject)( access_t * p_access, int i_slot,
en50221_mmi_object_t *p_object );
void E_(en50221_End)( access_t * );
#ifdef ENABLE_HTTPD
int E_(HTTPOpen)( access_t *p_access );
void E_(HTTPClose)( access_t *p_access );
char *E_(HTTPExtractValue)( char *psz_uri, const char *psz_name,
char *psz_value, int i_value_max );
#endif
......@@ -2,7 +2,7 @@
* en50221.c : implementation of the transport, session and applications
* layers of EN 50 221
*****************************************************************************
* Copyright (C) 2004 the VideoLAN team
* Copyright (C) 2004-2005 the VideoLAN team
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
......@@ -59,6 +59,10 @@
# include "psi.h"
#endif
#ifdef ENABLE_HTTPD
# include "vlc_httpd.h"
#endif
#include "dvb.h"
#undef DEBUG_TPDU
......@@ -471,6 +475,99 @@ static void SessionOpen( access_t * p_access, uint8_t i_slot,
}
}
#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;
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;
}
}
/*****************************************************************************
* SessionSendClose
*****************************************************************************/
......@@ -561,20 +658,39 @@ static void SPDUHandle( access_t * p_access, uint8_t i_slot,
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:
i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
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;
}
}
......@@ -675,13 +791,13 @@ 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 );
if( p_sys->i_ca_type == CA_CI_LINK )
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 )
if ( i_size + p - p_apdu > 256 )
{
msg_Err( p_access, "CAM: apdu overflow" );
i_ret = VLC_EGENERIC;
......@@ -690,11 +806,11 @@ static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
{
char *psz_hex;
ca_msg.length = i_size + p - p_apdu;
if( i_size == 0 ) ca_msg.length=3;
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 )
if ( i_ret < 0 )
{
msg_Err( p_access, "Error sending to CAM: %s", strerror(errno) );
i_ret = VLC_EGENERIC;
......@@ -759,6 +875,20 @@ static void ResourceManagerOpen( access_t * p_access, int i_session_id )
* 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
*****************************************************************************/
......@@ -1190,6 +1320,18 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
}
}
/*****************************************************************************
* 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
*****************************************************************************/
......@@ -1200,6 +1342,7 @@ static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
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) );
......@@ -1308,6 +1451,18 @@ static void DateTimeManage( access_t * p_access, int 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
*****************************************************************************/
......@@ -1319,6 +1474,7 @@ static void DateTimeOpen( access_t * p_access, int 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) );
......@@ -1363,6 +1519,63 @@ static void DateTimeOpen( access_t * p_access, int i_session_id )
#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( &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
*****************************************************************************/
......@@ -1381,22 +1594,22 @@ static void MMIDisplayReply( access_t *p_access, int i_session_id )
/*****************************************************************************
* MMIGetText
*****************************************************************************/
static char *MMIGetText( access_t *p_access, char *psz_text,
uint8_t **pp_apdu, int *pi_size )
static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
{
int i_tag = APDUGetTag( *pp_apdu, *pi_size );
char *psz_text;
int l;
uint8_t *d;
if ( i_tag != AOT_TEXT_LAST )
{
msg_Err( p_access, "unexpected text tag: %06x", i_tag );
psz_text[0] = '\0';
*pi_size = 0;
return psz_text;
return strdup( "" );
}
d = APDUGetLength( *pp_apdu, &l );
psz_text = malloc( l + 1 );
strncpy( psz_text, (char *)d, l );
psz_text[l] = '\0';
......@@ -1405,6 +1618,82 @@ static char *MMIGetText( access_t *p_access, char *psz_text,
return psz_text;
}
/*****************************************************************************
* MMIHandleEnq
*****************************************************************************/
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;
}
/*****************************************************************************
* 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 ); \
}
GET_FIELD( title );
GET_FIELD( subtitle );
GET_FIELD( bottom );
#undef GET_FIELD
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
*****************************************************************************/
......@@ -1440,38 +1729,17 @@ static void MMIHandle( access_t *p_access, int i_session_id,
break;
}
case AOT_ENQ:
MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
break;
case AOT_LIST_LAST:
case AOT_MENU_LAST:
{
int l;
uint8_t *d = APDUGetLength( p_apdu, &l );
char psz_text[255];
if ( l > 0 )
{
l--; d++; /* choice_nb */
if ( l > 0 )
msg_Info( p_access, "MMI title: %s",
MMIGetText( p_access, psz_text, &d, &l ) );
if ( l > 0 )
msg_Info( p_access, "MMI subtitle: %s",
MMIGetText( p_access, psz_text, &d, &l ) );
if ( l > 0 )
msg_Info( p_access, "MMI bottom: %s",
MMIGetText( p_access, psz_text, &d, &l ) );
while ( l > 0 )
{
msg_Info( p_access, "MMI: %s",
MMIGetText( p_access, psz_text, &d, &l ) );
}
}
MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
break;
}
case AOT_CLOSE_MMI:
SessionSendClose( p_access, i_session_id );
msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
break;
default:
......@@ -1479,16 +1747,38 @@ static void MMIHandle( access_t *p_access, int i_session_id,
}
}
/*****************************************************************************
* 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 )
{
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;
}
......@@ -1824,13 +2114,138 @@ int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *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++ )
{
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_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;
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 )
{
MMISendClose( p_access, i_session_id );
return VLC_SUCCESS;
}
}
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;
}
}
/*****************************************************************************
* 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_SendMMIObject :
*****************************************************************************/
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;
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 )
{
MMISendObject( p_access, i_session_id, p_object );
return;
}
}
msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
}
/*****************************************************************************
* en50221_End :
*****************************************************************************/
void E_(en50221_End)( access_t * p_access )
{
access_sys_t *p_sys = p_access->p_sys;
int i;
int i_session_id, i;
for ( i = 0; i < MAX_PROGRAMS; i++ )
{
......@@ -1840,5 +2255,17 @@ void E_(en50221_End)( access_t * p_access )
}
}
/* TODO */
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 )
{
p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
i_session_id );
}
}
/* Leave the CAM configured, so that it can be reused in another
* program. */
}
/*****************************************************************************
* http.c: HTTP interface
*****************************************************************************
* Copyright (C) 2005 the VideoLAN team
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/input.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <errno.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"
# include "vlc_acl.h"
#endif
#include "dvb.h"
#ifdef ENABLE_HTTPD
struct httpd_file_sys_t
{
access_t *p_access;
httpd_file_t *p_file;
};
static int HttpCallback( httpd_file_sys_t *p_args,
httpd_file_t *p_file,
uint8_t *_p_request,
uint8_t **_pp_data, int *pi_data );
/*****************************************************************************
* HTTPOpen: Start the internal HTTP server
*****************************************************************************/
int E_(HTTPOpen)( access_t *p_access )
{
#define FREE( x ) \
if ( (x) != NULL ) \
free( x );
access_sys_t *p_sys = p_access->p_sys;
char *psz_address, *psz_cert = NULL, *psz_key = NULL,
*psz_ca = NULL, *psz_crl = NULL, *psz_user = NULL,
*psz_password = NULL, *psz_acl = NULL;
int i_port = 0;
char psz_tmp[10];
vlc_acl_t *p_acl = NULL;
httpd_file_sys_t *f;
vlc_mutex_init( p_access, &p_sys->httpd_mutex );
vlc_cond_init( p_access, &p_sys->httpd_cond );
p_sys->b_request_frontend_info = p_sys->b_request_mmi_info = VLC_FALSE;
p_sys->i_httpd_timeout = 0;
psz_address = var_GetString( p_access, "dvb-http-host" );
if( psz_address != NULL && *psz_address )
{
char *psz_parser = strchr( psz_address, ':' );
if( psz_parser )
{
*psz_parser++ = '\0';
i_port = atoi( psz_parser );
}
}
else
{
if ( psz_address != NULL ) free( psz_address );
return VLC_SUCCESS;
}
/* determine SSL configuration */
psz_cert = var_GetString( p_access, "dvb-http-intf-cert" );
if ( psz_cert != NULL && *psz_cert )
{
msg_Dbg( p_access, "enabling TLS for HTTP interface (cert file: %s)",
psz_cert );
psz_key = config_GetPsz( p_access, "dvb-http-intf-key" );
psz_ca = config_GetPsz( p_access, "dvb-http-intf-ca" );
psz_crl = config_GetPsz( p_access, "dvb-http-intf-crl" );
if ( i_port <= 0 )
i_port = 8443;
}
else
{
if ( !*psz_cert )
{
free( psz_cert );
psz_cert = NULL;
}
if ( i_port <= 0 )
i_port= 8082;
}
/* Ugly hack to allow to run several HTTP servers on different ports. */
sprintf( psz_tmp, ":%d", i_port + 1 );
config_PutPsz( p_access, "dvb-http-host", psz_tmp );
msg_Dbg( p_access, "base %s:%d", psz_address, i_port );
p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_access), psz_address,
i_port, psz_cert, psz_key, psz_ca,
psz_crl );
FREE( psz_cert );
FREE( psz_key );
FREE( psz_ca );
FREE( psz_crl );
if ( p_sys->p_httpd_host == NULL )
{
msg_Err( p_access, "cannot listen on %s:%d", psz_address, i_port );
free( psz_address );
return VLC_EGENERIC;
}
free( psz_address );
psz_user = var_GetString( p_access, "dvb-http-user" );
psz_password = var_GetString( p_access, "dvb-http-password" );
psz_acl = var_GetString( p_access, "dvb-http-acl" );
if ( psz_acl != NULL )
{
p_acl = ACL_Create( p_access, VLC_FALSE );
if( ACL_LoadFile( p_acl, psz_acl ) )
{
ACL_Destroy( p_acl );
p_acl = NULL;
}
}
/* Declare an index.html file. */
f = malloc( sizeof(httpd_file_sys_t) );
f->p_access = p_access;
f->p_file = httpd_FileNew( p_sys->p_httpd_host, "/index.html",
"text/html; charset=UTF-8",
psz_user, psz_password, p_acl,
HttpCallback, f );
FREE( psz_user );
FREE( psz_password );
FREE( psz_acl );
if ( p_acl != NULL )
ACL_Destroy( p_acl );
if ( f->p_file == NULL )
{
free( f );
p_sys->p_httpd_file = NULL;
return VLC_EGENERIC;
}
p_sys->p_httpd_file = f;
p_sys->p_httpd_redir = httpd_RedirectNew( p_sys->p_httpd_host,
"/index.html", "/" );
#undef FREE
return VLC_SUCCESS;
}
/*****************************************************************************
* HTTPClose: Stop the internal HTTP server
*****************************************************************************/
void E_(HTTPClose)( access_t *p_access )
{
access_sys_t *p_sys = p_access->p_sys;
if ( p_sys->p_httpd_host != NULL )
{
if ( p_sys->p_httpd_file != NULL )
{
/* Unlock the thread if it is stuck in HttpCallback */
vlc_mutex_lock( &p_sys->httpd_mutex );
if ( p_sys->b_request_frontend_info == VLC_TRUE )
{
p_sys->b_request_frontend_info = VLC_FALSE;
p_sys->psz_frontend_info = strdup("");
}
if ( p_sys->b_request_mmi_info == VLC_TRUE )
{
p_sys->b_request_mmi_info = VLC_FALSE;
p_sys->psz_mmi_info = strdup("");
}
vlc_cond_signal( &p_sys->httpd_cond );
vlc_mutex_unlock( &p_sys->httpd_mutex );
httpd_FileDelete( p_sys->p_httpd_file->p_file );
httpd_RedirectDelete( p_sys->p_httpd_redir );
}
httpd_HostDelete( p_sys->p_httpd_host );
}
vlc_mutex_destroy( &p_sys->httpd_mutex );
vlc_cond_destroy( &p_sys->httpd_cond );
}
static const char *psz_constant_header =
"<html>\n"
"<head><title>VLC DVB monitoring interface</title></head>\n"
"<body><a href=\"index.html\">Reload this page</a>\n"
"<h1>CAM info</h1>\n";
static const char *psz_constant_middle =
"<hr><h1>Frontend Info</h1>\n";
static const char *psz_constant_footer =
"</body></html>\n";
/****************************************************************************
* HttpCallback: Return the index.html file
****************************************************************************/
static int HttpCallback( httpd_file_sys_t *p_args,
httpd_file_t *p_file,
uint8_t *_psz_request,
uint8_t **_pp_data, int *pi_data )
{
access_sys_t *p_sys = p_args->p_access->p_sys;
char *psz_request = (char *)_psz_request;
char **pp_data = (char **)_pp_data;
vlc_mutex_lock( &p_sys->httpd_mutex );
p_sys->i_httpd_timeout = mdate() + I64C(3000000); /* 3 s */
p_sys->psz_request = psz_request;
p_sys->b_request_frontend_info = VLC_TRUE;
if ( p_sys->i_ca_handle )
{
p_sys->b_request_mmi_info = VLC_TRUE;
}
else
{
p_sys->psz_mmi_info = strdup( "No available CAM interface\n" );
}
do
{
vlc_cond_wait( &p_sys->httpd_cond, &p_sys->httpd_mutex );
}
while ( p_sys->b_request_frontend_info || p_sys->b_request_mmi_info );
p_sys->i_httpd_timeout = 0;
vlc_mutex_unlock( &p_sys->httpd_mutex );
*pi_data = strlen( psz_constant_header )
+ strlen( p_sys->psz_mmi_info )
+ strlen( psz_constant_middle )
+ strlen( p_sys->psz_frontend_info )
+ strlen( psz_constant_footer ) + 1;
*pp_data = malloc( *pi_data );
sprintf( *pp_data, "%s%s%s%s%s", psz_constant_header,
p_sys->psz_mmi_info, psz_constant_middle,
p_sys->psz_frontend_info, psz_constant_footer );
free( p_sys->psz_frontend_info );
free( p_sys->psz_mmi_info );
return VLC_SUCCESS;
}
/****************************************************************************
* HTTPExtractValue: Extract a GET variable from psz_request
****************************************************************************/
char *E_(HTTPExtractValue)( char *psz_uri, const char *psz_name,
char *psz_value, int i_value_max )
{
char *p = psz_uri;
while( (p = strstr( p, psz_name )) )
{
/* Verify that we are dealing with a post/get argument */
if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n')
&& p[strlen(psz_name)] == '=' )
break;
p++;
}
if( p )
{
int i_len;
p += strlen( psz_name );
if( *p == '=' ) p++;
if( strchr( p, '&' ) )
{
i_len = strchr( p, '&' ) - p;
}
else
{
/* for POST method */
if( strchr( p, '\n' ) )
{
i_len = strchr( p, '\n' ) - p;
if( i_len && *(p+i_len-1) == '\r' ) i_len--;
}
else
{
i_len = strlen( p );
}
}
i_len = __MIN( i_value_max - 1, i_len );
if( i_len > 0 )
{
strncpy( psz_value, p, i_len );
psz_value[i_len] = '\0';
}
else
{
strncpy( psz_value, "", i_value_max );
}
p += i_len;
}
else
{
strncpy( psz_value, "", i_value_max );
}
return p;
}
#endif /* ENABLE_HTTPD */
/*****************************************************************************
* linux_dvb.c : functions to control a DVB card under Linux with v4l2
*****************************************************************************
* Copyright (C) 1998-2004 the VideoLAN team
* Copyright (C) 1998-2005 the VideoLAN team
*
* Authors: Damien Lucas <nitrox@via.ecp.fr>
* Johan Bilien <jobi@via.ecp.fr>
......@@ -61,6 +61,10 @@
# include "psi.h"
#endif
#ifdef ENABLE_HTTPD
# include "vlc_httpd.h"
#endif
#include "dvb.h"
/*
......@@ -268,24 +272,24 @@ void E_(FrontendPoll)( access_t *p_access )
if( i_ret < 0 )
{
if( errno == EWOULDBLOCK )
return;
return; /* no more events */
msg_Err( p_access, "reading frontend status failed (%d) %s",
msg_Err( p_access, "reading frontend event failed (%d) %s",
i_ret, strerror(errno) );
continue;
return;
}
i_status = event.status;
i_diff = i_status ^ p_frontend->i_last_status;
p_frontend->i_last_status = i_status;
{
#define IF_UP( x ) \
} \
if ( i_diff & (x) ) \
{ \
if ( i_status & (x) )
{
IF_UP( FE_HAS_SIGNAL )
msg_Dbg( p_access, "frontend has acquired signal" );
else
......@@ -333,8 +337,153 @@ void E_(FrontendPoll)( access_t *p_access )
E_(FrontendSet)( p_access );
}
}
#undef IF_UP
}
}
#ifdef ENABLE_HTTPD
/*****************************************************************************
* FrontendStatus : Read frontend status
*****************************************************************************/
void E_(FrontendStatus)( access_t *p_access )
{
access_sys_t *p_sys = p_access->p_sys;
frontend_t *p_frontend = p_sys->p_frontend;
char *p = p_sys->psz_frontend_info = malloc( 10000 );
fe_status_t i_status;
int i_ret;
/* Determine type of frontend */
if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_GET_INFO,
&p_frontend->info )) < 0 )
{
p += sprintf( p, "ioctl FE_GET_INFO failed (%d) %s\n", i_ret,
strerror(errno) );
goto out;
}
/* Print out frontend capabilities. */
p += sprintf( p, "<table border=1><tr><th>name</th><td>%s</td></tr>\n",
p_frontend->info.name );
switch( p_frontend->info.type )
{
case FE_QPSK:
p += sprintf( p, "<tr><th>type</th><td>QPSK (DVB-S)</td></tr>\n" );
break;
case FE_QAM:
p += sprintf( p, "<tr><th>type</th><td>QAM (DVB-C)</td></tr>\n" );
break;
case FE_OFDM:
p += sprintf( p, "<tr><th>type</th><td>OFDM (DVB-T)</td></tr>\n" );
break;
#if 0 /* DVB_API_VERSION == 3 */
case FE_MEMORY:
p += sprintf( p, "<tr><th>type</th><td>MEMORY</td></tr>\n" );
break;
case FE_NET:
p += sprintf( p, "<tr><th>type</th><td>NETWORK</td></tr>\n" );
break;
#endif
default:
p += sprintf( p, "<tr><th>type</th><td>UNKNOWN (%d)</td></tr>\n",
p_frontend->info.type );
goto out;
}
#define CHECK_INFO( x ) \
p += sprintf( p, \
"<tr><th>" STRINGIFY(x) "</th><td>%u</td></tr>\n", \
p_frontend->info.x );
CHECK_INFO( frequency_min );
CHECK_INFO( frequency_max );
CHECK_INFO( frequency_stepsize );
CHECK_INFO( frequency_tolerance );
CHECK_INFO( symbol_rate_min );
CHECK_INFO( symbol_rate_max );
CHECK_INFO( symbol_rate_tolerance );
CHECK_INFO( notifier_delay );
#undef CHECK_INFO
p += sprintf( p, "</table><p>Frontend capability list:\n<table border=1>" );
#define CHECK_CAPS( x ) \
if ( p_frontend->info.caps & (FE_##x) ) \
p += sprintf( p, "<tr><td>" STRINGIFY(x) "</td></tr>\n" );
CHECK_CAPS( IS_STUPID );
CHECK_CAPS( CAN_INVERSION_AUTO );
CHECK_CAPS( CAN_FEC_1_2 );
CHECK_CAPS( CAN_FEC_2_3 );
CHECK_CAPS( CAN_FEC_3_4 );
CHECK_CAPS( CAN_FEC_4_5 );
CHECK_CAPS( CAN_FEC_5_6 );
CHECK_CAPS( CAN_FEC_6_7 );
CHECK_CAPS( CAN_FEC_7_8 );
CHECK_CAPS( CAN_FEC_8_9 );
CHECK_CAPS( CAN_FEC_AUTO );
CHECK_CAPS( CAN_QPSK );
CHECK_CAPS( CAN_QAM_16 );
CHECK_CAPS( CAN_QAM_32 );
CHECK_CAPS( CAN_QAM_64 );
CHECK_CAPS( CAN_QAM_128 );
CHECK_CAPS( CAN_QAM_256 );
CHECK_CAPS( CAN_QAM_AUTO );
CHECK_CAPS( CAN_TRANSMISSION_MODE_AUTO );
CHECK_CAPS( CAN_BANDWIDTH_AUTO );
CHECK_CAPS( CAN_GUARD_INTERVAL_AUTO );
CHECK_CAPS( CAN_HIERARCHY_AUTO );
CHECK_CAPS( CAN_MUTE_TS );
CHECK_CAPS( CAN_RECOVER );
CHECK_CAPS( CAN_CLEAN_SETUP );
#undef CHECK_CAPS
p += sprintf( p, "</table><p>Current frontend status:\n<table border=1>" );
if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_READ_STATUS, &i_status ))
< 0 )
{
p += sprintf( p, "</table>ioctl FE_READ_STATUS failed (%d) %s\n", i_ret,
strerror(errno) );
goto out;
}
#define CHECK_STATUS( x ) \
if ( i_status & (FE_##x) ) \
p += sprintf( p, "<tr><td>" STRINGIFY(x) "</td></tr>\n" );
CHECK_STATUS( HAS_SIGNAL );
CHECK_STATUS( HAS_CARRIER );
CHECK_STATUS( HAS_VITERBI );
CHECK_STATUS( HAS_SYNC );
CHECK_STATUS( HAS_LOCK );
CHECK_STATUS( REINIT );
#undef CHECK_STATUS
if ( i_status & FE_HAS_LOCK )
{
int32_t i_value;
p += sprintf( p, "</table><p>Signal status:\n<table border=1>" );
if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &i_value ) >= 0 )
p += sprintf( p, "<tr><th>Bit error rate</th><td>%d</td></tr>\n",
i_value );
if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH,
&i_value ) >= 0 )
p += sprintf( p, "<tr><th>Signal strength</th><td>%d</td></tr>\n",
i_value );
if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &i_value ) >= 0 )
p += sprintf( p, "<tr><th>SNR</th><td>%d</td></tr>\n",
i_value );
}
p += sprintf( p, "</table>" );
out:
vlc_mutex_lock( &p_sys->httpd_mutex );
p_sys->b_request_frontend_info = VLC_FALSE;
vlc_cond_signal( &p_sys->httpd_cond );
vlc_mutex_unlock( &p_sys->httpd_mutex );
}
#endif
/*****************************************************************************
* FrontendInfo : Return information about given frontend
*****************************************************************************/
......@@ -1242,7 +1391,7 @@ int E_(CAMOpen)( access_t *p_access )
msg_Dbg( p_access, "CAMInit: CA interface with %d %s", caps.slot_num,
caps.slot_num == 1 ? "slot" : "slots" );
if ( caps.slot_type & CA_CI )
msg_Dbg( p_access, "CAMInit: CI high level interface type (not supported)" );
msg_Dbg( p_access, "CAMInit: CI high level interface type" );
if ( caps.slot_type & CA_CI_LINK )
msg_Dbg( p_access, "CAMInit: CI link layer level interface type" );
if ( caps.slot_type & CA_CI_PHYS )
......@@ -1314,6 +1463,265 @@ int E_(CAMPoll)( access_t * p_access )
return i_ret;
}
#ifdef ENABLE_HTTPD
/*****************************************************************************
* CAMStatus :
*****************************************************************************/
void E_(CAMStatus)( access_t * p_access )
{
access_sys_t *p_sys = p_access->p_sys;
char *p;
ca_caps_t caps;
int i_slot, i;
if ( p_sys->psz_request != NULL && *p_sys->psz_request )
{
/* Check if we have an undisplayed MMI message : in that case we ignore
* the user input to avoid confusing the CAM. */
for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
{
if ( p_sys->pb_slot_mmi_undisplayed[i_slot] == VLC_TRUE )
{
p_sys->psz_request = NULL;
msg_Dbg( p_access,
"ignoring user request because of a new MMI object" );
break;
}
}
}
if ( p_sys->psz_request != NULL && *p_sys->psz_request )
{
/* We have a mission to accomplish. */
en50221_mmi_object_t mmi_object;
char *psz_request = p_sys->psz_request;
char psz_value[255];
int i_slot;
vlc_bool_t b_ok = VLC_FALSE;
p_sys->psz_request = NULL;
if ( E_(HTTPExtractValue)( psz_request, "slot", psz_value,
sizeof(psz_value) ) == NULL )
{
p_sys->psz_mmi_info = strdup( "invalid request parameter\n" );
goto out;
}
i_slot = atoi(psz_value);
if ( E_(HTTPExtractValue)( psz_request, "open", psz_value,
sizeof(psz_value) ) != NULL )
{
E_(en50221_OpenMMI)( p_access, i_slot );
return;
}
if ( E_(HTTPExtractValue)( psz_request, "close", psz_value,
sizeof(psz_value) ) != NULL )
{
E_(en50221_CloseMMI)( p_access, i_slot );
return;
}
if ( E_(HTTPExtractValue)( psz_request, "cancel", psz_value,
sizeof(psz_value) ) == NULL )
{
b_ok = VLC_TRUE;
}
if ( E_(HTTPExtractValue)( psz_request, "type", psz_value,
sizeof(psz_value) ) == NULL )
{
p_sys->psz_mmi_info = strdup( "invalid request parameter\n" );
goto out;
}
if ( !strcmp( psz_value, "enq" ) )
{
mmi_object.i_object_type = EN50221_MMI_ANSW;
mmi_object.u.answ.b_ok = b_ok;
if ( b_ok == VLC_FALSE )
{
mmi_object.u.answ.psz_answ = strdup("");
}
else
{
if ( E_(HTTPExtractValue)( psz_request, "answ", psz_value,
sizeof(psz_value) ) == NULL )
{
p_sys->psz_mmi_info = strdup( "invalid request parameter\n" );
goto out;
}
mmi_object.u.answ.psz_answ = strdup(psz_value);
}
}
else
{
mmi_object.i_object_type = EN50221_MMI_MENU_ANSW;
if ( b_ok == VLC_FALSE )
{
mmi_object.u.menu_answ.i_choice = 0;
}
else
{
if ( E_(HTTPExtractValue)( psz_request, "choice", psz_value,
sizeof(psz_value) ) == NULL )
mmi_object.u.menu_answ.i_choice = 0;
else
mmi_object.u.menu_answ.i_choice = atoi(psz_value);
}
}
E_(en50221_SendMMIObject)( p_access, i_slot, &mmi_object );
return;
}
/* Check that we have all necessary MMI information. */
for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
{
if ( p_sys->pb_slot_mmi_expected[i_slot] == VLC_TRUE )
return;
}
p = p_sys->psz_mmi_info = malloc( 10000 );
if ( ioctl( p_sys->i_ca_handle, CA_GET_CAP, &caps ) != 0 )
{
p += sprintf( p, "ioctl CA_GET_CAP failed (%s)\n",
strerror(errno) );
goto out;
}
/* Output CA capabilities */
p += sprintf( p, "CA interface with %d %s, type:\n<table>", caps.slot_num,
caps.slot_num == 1 ? "slot" : "slots" );
#define CHECK_CAPS( x, s ) \
if ( caps.slot_type & (CA_##x) ) \
p += sprintf( p, "<tr><td>" s "</td></tr>\n" );
CHECK_CAPS( CI, "CI high level interface" );
CHECK_CAPS( CI_LINK, "CI link layer level interface" );
CHECK_CAPS( CI_PHYS, "CI physical layer level interface (not supported)" );
CHECK_CAPS( DESCR, "built-in descrambler" );
CHECK_CAPS( SC, "simple smartcard interface" );
#undef CHECK_CAPS
p += sprintf( p, "</table>%d available %s\n<table>", caps.descr_num,
caps.descr_num == 1 ? "descrambler (key)" : "descramblers (keys)" );
#define CHECK_DESC( x ) \
if ( caps.descr_type & (CA_##x) ) \
p += sprintf( p, "<tr><td>" STRINGIFY(x) "</td></tr>\n" );
CHECK_DESC( ECD );
CHECK_DESC( NDS );
CHECK_DESC( DSS );
#undef CHECK_DESC
p += sprintf( p, "</table>" );
for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
{
ca_slot_info_t sinfo;
p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_FALSE;
p += sprintf( p, "<p>CA slot #%d: ", i_slot );
sinfo.num = i_slot;
if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
{
p += sprintf( p, "ioctl CA_GET_SLOT_INFO failed (%s)<br>\n",
strerror(errno) );
continue;
}
#define CHECK_TYPE( x, s ) \
if ( sinfo.type & (CA_##x) ) \
p += sprintf( p, s );
CHECK_TYPE( CI, "high level, " );
CHECK_TYPE( CI_LINK, "link layer level, " );
CHECK_TYPE( CI_PHYS, "physical layer level, " );
#undef CHECK_TYPE
if ( sinfo.flags & CA_CI_MODULE_READY )
{
en50221_mmi_object_t *p_object = E_(en50221_GetMMIObject)( p_access,
i_slot );
p += sprintf( p, "module present and ready<p>\n" );
p += sprintf( p, "<form action=index.html method=get>\n" );
p += sprintf( p, "<input type=hidden name=slot value=\"%d\">\n",
i_slot );
if ( p_object == NULL )
{
p += sprintf( p, "<input type=submit name=open value=\"Open session\">\n" );
}
else
{
switch ( p_object->i_object_type )
{
case EN50221_MMI_ENQ:
p += sprintf( p, "<input type=hidden name=type value=enq>\n" );
p += sprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
p_object->u.enq.psz_text );
if ( p_object->u.enq.b_blind == VLC_FALSE )
p += sprintf( p, "<tr><td><input type=text name=answ></td></tr>\n" );
else
p += sprintf( p, "<tr><td><input type=password name=answ></td></tr>\n" );
break;
case EN50221_MMI_MENU:
p += sprintf( p, "<input type=hidden name=type value=menu>\n" );
p += sprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
p_object->u.menu.psz_title );
p += sprintf( p, "<tr><td>%s</td></tr><tr><td>\n",
p_object->u.menu.psz_subtitle );
for ( i = 0; i < p_object->u.menu.i_choices; i++ )
p += sprintf( p, "<input type=radio name=choice value=\"%d\">%s<br>\n", i + 1, p_object->u.menu.ppsz_choices[i] );
p += sprintf( p, "</td></tr><tr><td>%s</td></tr>\n",
p_object->u.menu.psz_bottom );
break;
case EN50221_MMI_LIST:
p += sprintf( p, "<input type=hidden name=type value=menu>\n" );
p += sprintf( p, "<input type=hidden name=choice value=0>\n" );
p += sprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
p_object->u.menu.psz_title );
p += sprintf( p, "<tr><td>%s</td></tr><tr><td>\n",
p_object->u.menu.psz_subtitle );
for ( i = 0; i < p_object->u.menu.i_choices; i++ )
p += sprintf( p, "%s<br>\n",
p_object->u.menu.ppsz_choices[i] );
p += sprintf( p, "</td></tr><tr><td>%s</td></tr>\n",
p_object->u.menu.psz_bottom );
break;
default:
p += sprintf( p, "<table><tr><th>Unknown MMI object type</th></tr>\n" );
}
p += sprintf( p, "</table><p><input type=submit name=ok value=\"OK\">\n" );
p += sprintf( p, "<input type=submit name=cancel value=\"Cancel\">\n" );
p += sprintf( p, "<input type=submit name=close value=\"Close Session\">\n" );
}
p += sprintf( p, "</form>\n" );
}
else if ( sinfo.flags & CA_CI_MODULE_PRESENT )
p += sprintf( p, "module present, not ready<br>\n" );
else
p += sprintf( p, "module not present<br>\n" );
}
out:
vlc_mutex_lock( &p_sys->httpd_mutex );
p_sys->b_request_mmi_info = VLC_FALSE;
vlc_cond_signal( &p_sys->httpd_cond );
vlc_mutex_unlock( &p_sys->httpd_mutex );
}
#endif
/*****************************************************************************
* CAMSet :
*****************************************************************************/
......
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