diff options
author | Matthew Nicholson <mnicholson@digium.com> | 2011-06-30 18:22:28 +0000 |
---|---|---|
committer | Matthew Nicholson <mnicholson@digium.com> | 2011-06-30 18:22:28 +0000 |
commit | 0f0956e67a86222298a06a01bf5f19e0bc219283 (patch) | |
tree | 2fe2a4f50430e0ba19e13a213fd366e4dcef8ba6 /res/res_fax_spandsp.c | |
parent | 82d28452cac71a9ad1948714d541c96e96a8e7b4 (diff) |
Fax gateway functionality (i.e. translating between a T.30 terminal and a T.38
terminal). Can be enabled on a channel by setting FAXOPT(gateway)=yes in the
dialplan.
Big thanks to irroot for porting this code to use the framehooks api.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@325816 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/res_fax_spandsp.c')
-rw-r--r-- | res/res_fax_spandsp.c | 308 |
1 files changed, 279 insertions, 29 deletions
diff --git a/res/res_fax_spandsp.c b/res/res_fax_spandsp.c index 851382b68..9ecd78305 100644 --- a/res/res_fax_spandsp.c +++ b/res/res_fax_spandsp.c @@ -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, "\nData Statistics:\n"); + ast_cli(fd, "\nData 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"); |