server.
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>
# include "psi.h"
#endif
+#ifdef ENABLE_HTTPD
+# include "vlc_httpd.h"
+#endif
+
#include "dvb.h"
/*****************************************************************************
#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") );
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" );
else
p_sys->i_read_once = DVB_READ_ONCE_START;
+#ifdef ENABLE_HTTPD
+ E_(HTTPOpen)( p_access );
+#endif
+
return VLC_SUCCESS;
}
E_(FrontendClose)( p_access );
E_(CAMClose)( p_access );
+#ifdef ENABLE_HTTPD
+ E_(HTTPClose)( p_access );
+#endif
+
free( p_sys );
}
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" );
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>
/*****************************************************************************
* Local structures
*****************************************************************************/
-typedef struct
+typedef struct demux_handle_t
{
int i_pid;
int i_handle;
typedef struct frontend_t frontend_t;
-typedef struct
+typedef struct en50221_session_t
{
int i_slot;
int i_resource_id;
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
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];
/* */
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
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 );
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
+
* 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
# include "psi.h"
#endif
+#ifdef ENABLE_HTTPD
+# include "vlc_httpd.h"
+#endif
+
#include "dvb.h"
#undef DEBUG_TPDU
}
}
+#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
*****************************************************************************/
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;
}
}
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;
{
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;
* 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
*****************************************************************************/
}
}
+/*****************************************************************************
+ * 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
*****************************************************************************/
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) );
}
}
+/*****************************************************************************
+ * 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
*****************************************************************************/
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) );
#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
*****************************************************************************/
/*****************************************************************************
* 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';
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
*****************************************************************************/
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:
}
}
+/*****************************************************************************
+ * 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;
}
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++ )
{
}
}
- /* 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. */
}
+
--- /dev/null
+/*****************************************************************************
+ * 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>
# include "psi.h"
#endif
+#ifdef ENABLE_HTTPD
+# include "vlc_httpd.h"
+#endif
+
#include "dvb.h"
/*
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 ( i_diff & (x) ) \
+ { \
+ if ( i_status & (x) )
- {
IF_UP( FE_HAS_SIGNAL )
msg_Dbg( p_access, "frontend has acquired signal" );
else
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
*****************************************************************************/
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 )
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 :
*****************************************************************************/