@ -5,6 +5,22 @@
*
* Matthew Nicholson < mnicholson @ digium . com >
*
* Initial T .38 - gateway code
* 2008 , Daniel Ferenci < daniel . ferenci @ nethemba . com >
* Created by Nethemba s . r . o . http : //www.nethemba.com
* Sponsored by IPEX a . s . http : //www.ipex.cz
*
* T .38 - gateway integration into asterisk app_fax and rework
* 2008 , Gregory Hinton Nietsky < gregory @ dnstelecom . co . za >
* dns Telecom http : //www.dnstelecom.co.za
*
* Modified to make T .38 - gateway compatible with Asterisk 1.6 .2
* 2010 , Anton Verevkin < mymail @ verevkin . it >
* ViaNetTV http : //www.vianettv.com
*
* Modified to make T .38 - gateway work
* 2010 , Klaus Darilion , IPCom GmbH , www . ipcom . at
*
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
@ -46,9 +62,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# include "asterisk/timing.h"
# include "asterisk/astobj2.h"
# include "asterisk/res_fax.h"
# include "asterisk/channel.h"
# define SPANDSP_FAX_SAMPLES 160
# define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES /* 50 ticks per second, 20ms, 160 samples per second */
# define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
static void * spandsp_fax_new ( struct ast_fax_session * s , struct ast_fax_tech_token * token ) ;
static void spandsp_fax_destroy ( struct ast_fax_session * s ) ;
@ -57,6 +75,9 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
static int spandsp_fax_start ( struct ast_fax_session * s ) ;
static int spandsp_fax_cancel ( struct ast_fax_session * s ) ;
static int spandsp_fax_switch_to_t38 ( struct ast_fax_session * s ) ;
static int spandsp_fax_gateway_start ( struct ast_fax_session * s ) ;
static int spandsp_fax_gateway_process ( struct ast_fax_session * s , const struct ast_frame * f ) ;
static void spandsp_fax_gateway_cleanup ( struct ast_fax_session * s ) ;
static char * spandsp_fax_cli_show_capabilities ( int fd ) ;
static char * spandsp_fax_cli_show_session ( struct ast_fax_session * s , int fd ) ;
@ -75,7 +96,7 @@ static struct ast_fax_tech spandsp_fax_tech = {
*/
. version = " pre-20090220 " ,
# endif
. caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE ,
. caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY ,
. new_session = spandsp_fax_new ,
. destroy_session = spandsp_fax_destroy ,
. read = spandsp_fax_read ,
@ -114,6 +135,7 @@ static struct {
struct spandsp_pvt {
unsigned int ist38 : 1 ;
unsigned int isdone : 1 ;
enum ast_t38_state ast_t38_state ;
fax_state_t fax_state ;
t38_terminal_state_t t38_state ;
t30_state_t * t30_state ;
@ -121,6 +143,9 @@ struct spandsp_pvt {
struct spandsp_fax_stats * stats ;
struct spandsp_fax_gw_stats * t38stats ;
t38_gateway_state_t t38_gw_state ;
struct ast_timer * timer ;
AST_LIST_HEAD ( frame_queue , ast_frame ) read_frames ;
} ;
@ -158,7 +183,9 @@ static void session_destroy(struct spandsp_pvt *p)
*/
static int t38_tx_packet_handler ( t38_core_state_t * t38_core_state , void * data , const uint8_t * buf , int len , int count )
{
struct spandsp_pvt * p = data ;
int res = - 1 ;
struct ast_fax_session * s = data ;
struct spandsp_pvt * p = s - > tech_pvt ;
struct ast_frame fax_frame = {
. frametype = AST_FRAME_MODEM ,
. subclass . integer = AST_MODEM_T38 ,
@ -174,13 +201,23 @@ static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, c
AST_FRAME_SET_BUFFER ( f , buf , 0 , len ) ;
if ( ! ( f = ast_frisolate ( f ) ) ) {
return - 1 ;
return res ;
}
/* no need to lock, this all runs in the same thread */
AST_LIST_INSERT_TAIL ( & p - > read_frames , f , frame_list ) ;
if ( s - > details - > caps & AST_FAX_TECH_GATEWAY ) {
ast_set_flag ( f , AST_FAX_FRFLAG_GATEWAY ) ;
if ( p - > ast_t38_state = = T38_STATE_NEGOTIATED ) {
res = ast_write ( s - > chan , f ) ;
} else {
res = ast_queue_frame ( s - > chan , f ) ;
}
ast_frfree ( f ) ;
} else {
/* no need to lock, this all runs in the same thread */
AST_LIST_INSERT_TAIL ( & p - > read_frames , f , frame_list ) ;
}
return 0 ;
return res ;
}
static int update_stats ( struct spandsp_pvt * p , int completion_code )
@ -422,6 +459,11 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
goto e_return ;
}
if ( s - > details - > caps & AST_FAX_TECH_GATEWAY ) {
s - > state = AST_FAX_STATE_INITIALIZED ;
return p ;
}
AST_LIST_HEAD_INIT ( & p - > read_frames ) ;
if ( s - > details - > caps & AST_FAX_TECH_RECEIVE ) {
@ -450,7 +492,7 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
}
/* init t38 stuff */
t38_terminal_init ( & p - > t38_state , caller_mode , t38_tx_packet_handler , p ) ;
t38_terminal_init ( & p - > t38_state , caller_mode , t38_tx_packet_handler , s ) ;
set_logging ( & p - > t38_state . logging , s - > details ) ;
}
@ -475,7 +517,12 @@ static void spandsp_fax_destroy(struct ast_fax_session *s)
{
struct spandsp_pvt * p = s - > tech_pvt ;
session_destroy ( p ) ;
if ( s - > details - > caps & AST_FAX_TECH_GATEWAY ) {
spandsp_fax_gateway_cleanup ( s ) ;
} else {
session_destroy ( p ) ;
}
ast_free ( p ) ;
s - > tech_pvt = NULL ;
s - > fd = - 1 ;
@ -536,6 +583,10 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
{
struct spandsp_pvt * p = s - > tech_pvt ;
if ( s - > details - > caps & AST_FAX_TECH_GATEWAY ) {
return spandsp_fax_gateway_process ( s , f ) ;
}
/* XXX do we need to lock here? */
if ( s - > state = = AST_FAX_STATE_COMPLETE ) {
ast_log ( LOG_WARNING , " FAX session '%d' is in the '%s' state. \n " , s - > id , ast_fax_state_to_str ( s - > state ) ) ;
@ -549,6 +600,182 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
}
}
/*! \brief generate T.30 packets sent to the T.30 leg of gateway
* \ param chan T .30 channel
* \ param data fax session structure
* \ param len not used
* \ param samples no of samples generated
* \ return - 1 on failure or 0 on sucess */
static int spandsp_fax_gw_t30_gen ( struct ast_channel * chan , void * data , int len , int samples )
{
int res = - 1 ;
struct ast_fax_session * s = data ;
struct spandsp_pvt * p = s - > tech_pvt ;
uint8_t buffer [ AST_FRIENDLY_OFFSET + samples * sizeof ( uint16_t ) ] ;
struct ast_frame * f ;
struct ast_frame t30_frame = {
. frametype = AST_FRAME_VOICE ,
. src = " res_fax_spandsp_g711 " ,
. samples = samples ,
. flags = AST_FAX_FRFLAG_GATEWAY ,
} ;
AST_FRAME_SET_BUFFER ( & t30_frame , buffer , AST_FRIENDLY_OFFSET , t30_frame . samples * sizeof ( int16_t ) ) ;
ast_format_set ( & t30_frame . subclass . format , AST_FORMAT_SLINEAR , 0 ) ;
if ( ! ( f = ast_frisolate ( & t30_frame ) ) ) {
return p - > isdone ? - 1 : res ;
}
/* generate a T.30 packet */
if ( ( f - > samples = t38_gateway_tx ( & p - > t38_gw_state , f - > data . ptr , f - > samples ) ) ) {
f - > datalen = f - > samples * sizeof ( int16_t ) ;
res = ast_write ( chan , f ) ;
}
ast_frfree ( f ) ;
return p - > isdone ? - 1 : res ;
}
/*! \brief simple routine to allocate data to generator
* \ param chan channel
* \ param params generator data
* \ return data to use in generator call */
static void * spandsp_fax_gw_gen_alloc ( struct ast_channel * chan , void * params ) {
ao2_ref ( params , + 1 ) ;
return params ;
}
static void spandsp_fax_gw_gen_release ( struct ast_channel * chan , void * data ) {
ao2_ref ( data , - 1 ) ;
}
/*! \brief activate a spandsp gateway based on the information in the given fax session
* \ param s fax session
* \ return - 1 on error 0 on sucess */
static int spandsp_fax_gateway_start ( struct ast_fax_session * s ) {
struct spandsp_pvt * p = s - > tech_pvt ;
struct ast_fax_t38_parameters * t38_param ;
int i , modems = 0 ;
struct ast_channel * peer ;
static struct ast_generator t30_gen = {
alloc : spandsp_fax_gw_gen_alloc ,
release : spandsp_fax_gw_gen_release ,
generate : spandsp_fax_gw_t30_gen ,
} ;
# if SPANDSP_RELEASE_DATE >= 20081012
/* for spandsp shaphots 0.0.6 and higher */
p - > t38_core_state = & p - > t38_gw_state . t38x . t38 ;
# else
/* for spandsp release 0.0.5 */
p - > t38_core_state = & p - > t38_gw_state . t38 ;
# endif
if ( ! t38_gateway_init ( & p - > t38_gw_state , t38_tx_packet_handler , s ) ) {
return - 1 ;
}
p - > ist38 = 1 ;
p - > ast_t38_state = ast_channel_get_t38_state ( s - > chan ) ;
if ( ! ( peer = ast_bridged_channel ( s - > chan ) ) ) {
ast_channel_unlock ( s - > chan ) ;
return - 1 ;
}
ast_activate_generator ( p - > ast_t38_state = = T38_STATE_NEGOTIATED ? peer : s - > chan , & t30_gen , s ) ;
set_logging ( & p - > t38_gw_state . logging , s - > details ) ;
set_logging ( & p - > t38_core_state - > logging , s - > details ) ;
t38_param = ( p - > ast_t38_state = = T38_STATE_NEGOTIATED ) ? & s - > details - > our_t38_parameters : & s - > details - > their_t38_parameters ;
t38_set_t38_version ( p - > t38_core_state , t38_param - > version ) ;
t38_gateway_set_ecm_capability ( & p - > t38_gw_state , s - > details - > option . ecm ) ;
t38_set_max_datagram_size ( p - > t38_core_state , t38_param - > max_ifp ) ;
t38_set_fill_bit_removal ( p - > t38_core_state , t38_param - > fill_bit_removal ) ;
t38_set_mmr_transcoding ( p - > t38_core_state , t38_param - > transcoding_mmr ) ;
t38_set_jbig_transcoding ( p - > t38_core_state , t38_param - > transcoding_jbig ) ;
t38_set_data_rate_management_method ( p - > t38_core_state ,
( t38_param - > rate_management = = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF ) ? 1 : 2 ) ;
t38_gateway_set_transmit_on_idle ( & p - > t38_gw_state , TRUE ) ;
t38_set_sequence_number_handling ( p - > t38_core_state , TRUE ) ;
if ( AST_FAX_MODEM_V17 & s - > details - > modems ) {
modems | = T30_SUPPORT_V17 ;
}
if ( AST_FAX_MODEM_V27 & s - > details - > modems ) {
modems | = T30_SUPPORT_V27TER ;
}
if ( AST_FAX_MODEM_V29 & s - > details - > modems ) {
modems | = T30_SUPPORT_V29 ;
}
if ( AST_FAX_MODEM_V34 & s - > details - > modems ) {
# if defined(T30_SUPPORT_V34)
modems | = T30_SUPPORT_V34 ;
# elif defined(T30_SUPPORT_V34HDX)
modems | = T30_SUPPORT_V34HDX ;
# else
ast_log ( LOG_WARNING , " v34 not supported in this version of spandsp \n " ) ;
# endif
}
t38_gateway_set_supported_modems ( & p - > t38_gw_state , modems ) ;
/* engage udptl nat on other side of T38 line
* ( Asterisk changes media ports thus we send a few packets to reinitialize
* pinholes in NATs and FWs
*/
for ( i = 0 ; i < SPANDSP_ENGAGE_UDPTL_NAT_RETRY ; i + + ) {
# if SPANDSP_RELEASE_DATE >= 20091228
t38_core_send_indicator ( & p - > t38_gw_state . t38x . t38 , T38_IND_NO_SIGNAL ) ;
# elif SPANDSP_RELEASE_DATE >= 20081012
t38_core_send_indicator ( & p - > t38_gw_state . t38x . t38 , T38_IND_NO_SIGNAL , p - > t38_gw_state . t38x . t38 . indicator_tx_count ) ;
# else
t38_core_send_indicator ( & p - > t38_gw_state . t38 , T38_IND_NO_SIGNAL , p - > t38_gw_state . t38 . indicator_tx_count ) ;
# endif
}
s - > state = AST_FAX_STATE_ACTIVE ;
return 0 ;
}
/*! \brief process a frame from the bridge
* \ param s fax session
* \ param f frame to process
* \ return 1 on sucess 0 on incorect packet */
static int spandsp_fax_gateway_process ( struct ast_fax_session * s , const struct ast_frame * f )
{
struct spandsp_pvt * p = s - > tech_pvt ;
/*invalid frame*/
if ( ! f - > data . ptr | | ! f - > datalen ) {
return - 1 ;
}
/* Process a IFP packet */
if ( ( f - > frametype = = AST_FRAME_MODEM ) & & ( f - > subclass . integer = = AST_MODEM_T38 ) ) {
return t38_core_rx_ifp_packet ( p - > t38_core_state , f - > data . ptr , f - > datalen , f - > seqno ) ;
} else if ( ( f - > frametype = = AST_FRAME_VOICE ) & & ( f - > subclass . format . id = = AST_FORMAT_SLINEAR ) ) {
return t38_gateway_rx ( & p - > t38_gw_state , f - > data . ptr , f - > samples ) ;
}
return - 1 ;
}
/*! \brief gather data and clean up after gateway ends
* \ param s fax session */
static void spandsp_fax_gateway_cleanup ( struct ast_fax_session * s )
{
struct spandsp_pvt * p = s - > tech_pvt ;
t38_stats_t t38_stats ;
t38_gateway_get_transfer_statistics ( & p - > t38_gw_state , & t38_stats ) ;
s - > details - > option . ecm = t38_stats . error_correcting_mode ? AST_FAX_OPTFLAG_TRUE : AST_FAX_OPTFLAG_FALSE ;
s - > details - > pages_transferred = t38_stats . pages_transferred ;
ast_string_field_build ( s - > details , transfer_rate , " %d " , t38_stats . bit_rate ) ;
}
/*! \brief */
static int spandsp_fax_start ( struct ast_fax_session * s )
{
@ -556,6 +783,10 @@ static int spandsp_fax_start(struct ast_fax_session *s)
s - > state = AST_FAX_STATE_OPEN ;
if ( s - > details - > caps & AST_FAX_TECH_GATEWAY ) {
return spandsp_fax_gateway_start ( s ) ;
}
if ( p - > ist38 ) {
# if SPANDSP_RELEASE_DATE >= 20080725
/* for spandsp shaphots 0.0.6 and higher */
@ -625,6 +856,12 @@ static int spandsp_fax_start(struct ast_fax_session *s)
static int spandsp_fax_cancel ( struct ast_fax_session * s )
{
struct spandsp_pvt * p = s - > tech_pvt ;
if ( s - > details - > caps & AST_FAX_TECH_GATEWAY ) {
p - > isdone = 1 ;
return 0 ;
}
t30_terminate ( p - > t30_state ) ;
p - > isdone = 1 ;
return 0 ;
@ -653,7 +890,7 @@ static int spandsp_fax_switch_to_t38(struct ast_fax_session *s)
/*! \brief */
static char * spandsp_fax_cli_show_capabilities ( int fd )
{
ast_cli ( fd , " SEND RECEIVE T.38 G.711 \n \n " ) ;
ast_cli ( fd , " SEND RECEIVE T.38 G.711 GATEWAY \n \n " ) ;
return CLI_SUCCESS ;
}
@ -661,35 +898,48 @@ static char *spandsp_fax_cli_show_capabilities(int fd)
static char * spandsp_fax_cli_show_session ( struct ast_fax_session * s , int fd )
{
struct spandsp_pvt * p = s - > tech_pvt ;
t30_stats_t stats ;
ao2_lock ( s ) ;
ast_cli ( fd , " %-22s : %d \n " , " session " , s - > id ) ;
ast_cli ( fd , " %-22s : %s \n " , " operation " , ( s - > details - > caps & AST_FAX_TECH_RECEIVE ) ? " Receive " : " Transmit " ) ;
ast_cli ( fd , " %-22s : %s \n " , " state " , ast_fax_state_to_str ( s - > state ) ) ;
if ( s - > state ! = AST_FAX_STATE_UNINITIALIZED ) {
t30_get_transfer_statistics ( p - > t30_state , & stats ) ;
ast_cli ( fd , " %-22s : %s \n " , " Last Status " , t30_completion_code_to_str ( stats . current_status ) ) ;
ast_cli ( fd , " %-22s : %s \n " , " ECM Mode " , stats . error_correcting_mode ? " Yes " : " No " ) ;
ast_cli ( fd , " %-22s : %d \n " , " Data Rate " , stats . bit_rate ) ;
ast_cli ( fd , " %-22s : %dx%d \n " , " Image Resolution " , stats . x_resolution , stats . y_resolution ) ;
if ( s - > details - > caps & AST_FAX_TECH_GATEWAY ) {
ast_cli ( fd , " %-22s : %d \n " , " session " , s - > id ) ;
ast_cli ( fd , " %-22s : %s \n " , " operation " , " Gateway " ) ;
ast_cli ( fd , " %-22s : %s \n " , " state " , ast_fax_state_to_str ( s - > state ) ) ;
if ( s - > state ! = AST_FAX_STATE_UNINITIALIZED ) {
t38_stats_t stats ;
t38_gateway_get_transfer_statistics ( & p - > t38_gw_state , & stats ) ;
ast_cli ( fd , " %-22s : %s \n " , " ECM Mode " , stats . error_correcting_mode ? " Yes " : " No " ) ;
ast_cli ( fd , " %-22s : %d \n " , " Data Rate " , stats . bit_rate ) ;
ast_cli ( fd , " %-22s : %d \n " , " Page Number " , stats . pages_transferred + 1 ) ;
}
} else {
ast_cli ( fd , " %-22s : %d \n " , " session " , s - > id ) ;
ast_cli ( fd , " %-22s : %s \n " , " operation " , ( s - > details - > caps & AST_FAX_TECH_RECEIVE ) ? " Receive " : " Transmit " ) ;
ast_cli ( fd , " %-22s : %s \n " , " state " , ast_fax_state_to_str ( s - > state ) ) ;
if ( s - > state ! = AST_FAX_STATE_UNINITIALIZED ) {
t30_stats_t stats ;
t30_get_transfer_statistics ( p - > t30_state , & stats ) ;
ast_cli ( fd , " %-22s : %s \n " , " Last Status " , t30_completion_code_to_str ( stats . current_status ) ) ;
ast_cli ( fd , " %-22s : %s \n " , " ECM Mode " , stats . error_correcting_mode ? " Yes " : " No " ) ;
ast_cli ( fd , " %-22s : %d \n " , " Data Rate " , stats . bit_rate ) ;
ast_cli ( fd , " %-22s : %dx%d \n " , " Image Resolution " , stats . x_resolution , stats . y_resolution ) ;
# if SPANDSP_RELEASE_DATE >= 20090220
ast_cli ( fd , " %-22s : %d \n " , " Page Number " , ( ( s - > details - > caps & AST_FAX_TECH_RECEIVE ) ? stats . pages_rx : stats . pages_tx ) + 1 ) ;
ast_cli ( fd , " %-22s : %d \n " , " Page Number " , ( ( s - > details - > caps & AST_FAX_TECH_RECEIVE ) ? stats . pages_rx : stats . pages_tx ) + 1 ) ;
# else
ast_cli ( fd , " %-22s : %d \n " , " Page Number " , stats . pages_transferred + 1 ) ;
ast_cli ( fd , " %-22s : %d \n " , " Page Number " , stats . pages_transferred + 1 ) ;
# endif
ast_cli ( fd , " %-22s : %s \n " , " File Name " , s - > details - > caps & AST_FAX_TECH_RECEIVE ? p - > t30_state - > rx_file : p - > t30_state - > tx_file ) ;
ast_cli ( fd , " %-22s : %s \n " , " File Name " , s - > details - > caps & AST_FAX_TECH_RECEIVE ? p - > t30_state - > rx_file : p - > t30_state - > tx_file ) ;
ast_cli ( fd , " \n Data Statistics: \n " ) ;
ast_cli ( fd , " \n Data Statistics: \n " ) ;
# if SPANDSP_RELEASE_DATE >= 20090220
ast_cli ( fd , " %-22s : %d \n " , " Tx Pages " , stats . pages_tx ) ;
ast_cli ( fd , " %-22s : %d \n " , " Rx Pages " , stats . pages_rx ) ;
ast_cli ( fd , " %-22s : %d \n " , " Tx Pages " , stats . pages_tx ) ;
ast_cli ( fd , " %-22s : %d \n " , " Rx Pages " , stats . pages_rx ) ;
# else
ast_cli ( fd , " %-22s : %d \n " , " Tx Pages " , ( s - > details - > caps & AST_FAX_TECH_SEND ) ? stats . pages_transferred : 0 ) ;
ast_cli ( fd , " %-22s : %d \n " , " Rx Pages " , ( s - > details - > caps & AST_FAX_TECH_RECEIVE ) ? stats . pages_transferred : 0 ) ;
ast_cli ( fd , " %-22s : %d \n " , " Tx Pages " , ( s - > details - > caps & AST_FAX_TECH_SEND ) ? stats . pages_transferred : 0 ) ;
ast_cli ( fd , " %-22s : %d \n " , " Rx Pages " , ( s - > details - > caps & AST_FAX_TECH_RECEIVE ) ? stats . pages_transferred : 0 ) ;
# endif
ast_cli ( fd , " %-22s : %d \n " , " Longest Bad Line Run " , stats . longest_bad_row_run ) ;
ast_cli ( fd , " %-22s : %d \n " , " Total Bad Lines " , stats . bad_rows ) ;
ast_cli ( fd , " %-22s : %d \n " , " Longest Bad Line Run " , stats . longest_bad_row_run ) ;
ast_cli ( fd , " %-22s : %d \n " , " Total Bad Lines " , stats . bad_rows ) ;
}
}
ao2_unlock ( s ) ;
ast_cli ( fd , " \n \n " ) ;