summaryrefslogtreecommitdiff
path: root/res/res_fax_spandsp.c
diff options
context:
space:
mode:
authorMatthew Nicholson <mnicholson@digium.com>2011-06-30 18:22:28 +0000
committerMatthew Nicholson <mnicholson@digium.com>2011-06-30 18:22:28 +0000
commit0f0956e67a86222298a06a01bf5f19e0bc219283 (patch)
tree2fe2a4f50430e0ba19e13a213fd366e4dcef8ba6 /res/res_fax_spandsp.c
parent82d28452cac71a9ad1948714d541c96e96a8e7b4 (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.c308
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");