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

* 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 ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
i_session_id );
p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
if ( 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 )