Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/src/t38_gateway.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 15:50:58 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.6pre17/src/t38_gateway.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,2352 @@ +//#define LOG_FAX_AUDIO +/* + * SpanDSP - a series of DSP components for telephony + * + * t38_gateway.c - A T.38 gateway, less the packet exchange part + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2005, 2006, 2007, 2008 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1, + * as published by the Free Software Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: t38_gateway.c,v 1.171.4.2 2009/12/19 10:44:10 steveu Exp $ + */ + +/*! \file */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <string.h> +#if defined(HAVE_TGMATH_H) +#include <tgmath.h> +#endif +#if defined(HAVE_MATH_H) +#include <math.h> +#endif +#include "floating_fudge.h" +#include <assert.h> +#if defined(LOG_FAX_AUDIO) +#include <unistd.h> +#endif +#include <tiffio.h> + +#include "spandsp/telephony.h" +#include "spandsp/logging.h" +#include "spandsp/queue.h" +#include "spandsp/dc_restore.h" +#include "spandsp/bit_operations.h" +#include "spandsp/power_meter.h" +#include "spandsp/complex.h" +#include "spandsp/tone_detect.h" +#include "spandsp/tone_generate.h" +#include "spandsp/async.h" +#include "spandsp/crc.h" +#include "spandsp/hdlc.h" +#include "spandsp/silence_gen.h" +#include "spandsp/fsk.h" +#include "spandsp/v29tx.h" +#include "spandsp/v29rx.h" +#include "spandsp/v27ter_tx.h" +#include "spandsp/v27ter_rx.h" +#include "spandsp/v17tx.h" +#include "spandsp/v17rx.h" +#include "spandsp/super_tone_rx.h" +#include "spandsp/modem_connect_tones.h" +#include "spandsp/t4_rx.h" +#include "spandsp/t4_tx.h" +#include "spandsp/t30_fcf.h" +#include "spandsp/t35.h" +#include "spandsp/t30.h" +#include "spandsp/t30_logging.h" +#include "spandsp/fax_modems.h" +#include "spandsp/t38_core.h" +#include "spandsp/t38_non_ecm_buffer.h" +#include "spandsp/t38_gateway.h" + +#include "spandsp/private/logging.h" +#include "spandsp/private/silence_gen.h" +#include "spandsp/private/fsk.h" +#include "spandsp/private/v17tx.h" +#include "spandsp/private/v17rx.h" +#include "spandsp/private/v27ter_tx.h" +#include "spandsp/private/v27ter_rx.h" +#include "spandsp/private/v29tx.h" +#include "spandsp/private/v29rx.h" +#include "spandsp/private/modem_connect_tones.h" +#include "spandsp/private/hdlc.h" +#include "spandsp/private/fax_modems.h" +#include "spandsp/private/t4_rx.h" +#include "spandsp/private/t4_tx.h" +#include "spandsp/private/t30.h" +#include "spandsp/private/t38_core.h" +#include "spandsp/private/t38_non_ecm_buffer.h" +#include "spandsp/private/t38_gateway.h" + +/* This is the target time per transmission chunk. The actual + packet timing will sync to the data octets. */ +/*! The default number of milliseconds per transmitted IFP when sending bulk T.38 data */ +#define MS_PER_TX_CHUNK 30 +/*! The number of bytes which must be in the audio to T.38 HDLC buffer before we start + outputting them as IFP messages. */ +#define HDLC_START_BUFFER_LEVEL 8 + +/*! The number of transmissions of indicator IFP packets */ +#define INDICATOR_TX_COUNT 3 +/*! The number of transmissions of data IFP packets */ +#define DATA_TX_COUNT 1 +/*! The number of transmissions of terminating data IFP packets */ +#define DATA_END_TX_COUNT 3 + +enum +{ + DISBIT1 = 0x01, + DISBIT2 = 0x02, + DISBIT3 = 0x04, + DISBIT4 = 0x08, + DISBIT5 = 0x10, + DISBIT6 = 0x20, + DISBIT7 = 0x40, + DISBIT8 = 0x80 +}; + +enum +{ + T38_NONE, + T38_V27TER_RX, + T38_V29_RX, + T38_V17_RX +}; + +enum +{ + HDLC_FLAG_FINISHED = 0x01, + HDLC_FLAG_CORRUPT_CRC = 0x02, + HDLC_FLAG_PROCEED_WITH_OUTPUT = 0x04, + HDLC_FLAG_MISSING_DATA = 0x08 +}; + +enum +{ + FLAG_INDICATOR = 0x100, + FLAG_DATA = 0x200 +}; + +enum +{ + TIMED_MODE_STARTUP = 0, + TIMED_MODE_IDLE, + TIMED_MODE_TCF_PREDICTABLE_MODEM_START_FAST_MODEM_ANNOUNCED, + TIMED_MODE_TCF_PREDICTABLE_MODEM_START_FAST_MODEM_SEEN, + TIMED_MODE_TCF_PREDICTABLE_MODEM_START_PAST_V21_MODEM, + TIMED_MODE_TCF_PREDICTABLE_MODEM_START_BEGIN, +}; + +/*! The maximum number of bytes to be zapped, in order to corrupt NSF, + NSS and NSC messages, so the receiver does not recognise them. */ +#define MAX_NSX_SUPPRESSION 10 + +/*! The number of consecutive flags to declare HDLC framing is OK. */ +#define HDLC_FRAMING_OK_THRESHOLD 5 + +static uint8_t nsx_overwrite[2][MAX_NSX_SUPPRESSION] = +{ + {0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +static int restart_rx_modem(t38_gateway_state_t *s); +static int process_rx_indicator(t38_core_state_t *t, void *user_data, int indicator); +static void hdlc_underflow_handler(void *user_data); +static void to_t38_buffer_init(t38_gateway_to_t38_state_t *s); +static void t38_hdlc_rx_put_bit(hdlc_rx_state_t *t, int new_bit); +static void non_ecm_put_bit(void *user_data, int bit); +static void non_ecm_remove_fill_and_put_bit(void *user_data, int bit); +static void non_ecm_push_residue(t38_gateway_state_t *s); +static void tone_detected(void *user_data, int tone, int level, int delay); + +static void set_rx_handler(t38_gateway_state_t *s, span_rx_handler_t *handler, void *user_data) +{ + if (s->audio.modems.rx_handler != span_dummy_rx) + s->audio.modems.rx_handler = handler; + s->audio.base_rx_handler = handler; + s->audio.modems.rx_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +static void set_tx_handler(t38_gateway_state_t *s, span_tx_handler_t *handler, void *user_data) +{ + s->audio.modems.tx_handler = handler; + s->audio.modems.tx_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +static void set_next_tx_handler(t38_gateway_state_t *s, span_tx_handler_t *handler, void *user_data) +{ + s->audio.modems.next_tx_handler = handler; + s->audio.modems.next_tx_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +static void set_rx_active(t38_gateway_state_t *s, int active) +{ + s->audio.modems.rx_handler = (active) ? s->audio.base_rx_handler : span_dummy_rx; +} +/*- End of function --------------------------------------------------------*/ + +static int v17_v21_rx(void *user_data, const int16_t amp[], int len) +{ + t38_gateway_state_t *t; + fax_modems_state_t *s; + + t = (t38_gateway_state_t *) user_data; + s = &t->audio.modems; + v17_rx(&s->v17_rx, amp, len); + if (s->rx_trained) + { + /* The fast modem has trained, so we no longer need to run the slow + one in parallel. */ + span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.17 + V.21 to V.17 (%.2fdBm0)\n", v17_rx_signal_power(&s->v17_rx)); + set_rx_handler(t, (span_rx_handler_t *) &v17_rx, &s->v17_rx); + } + else + { + fsk_rx(&s->v21_rx, amp, len); + if (s->rx_signal_present) + { + span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.17 + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx)); + set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, &s->v21_rx); + } + /*endif*/ + } + /*endif*/ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int v27ter_v21_rx(void *user_data, const int16_t amp[], int len) +{ + t38_gateway_state_t *t; + fax_modems_state_t *s; + + t = (t38_gateway_state_t *) user_data; + s = &t->audio.modems; + v27ter_rx(&s->v27ter_rx, amp, len); + if (s->rx_trained) + { + /* The fast modem has trained, so we no longer need to run the slow + one in parallel. */ + span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.27ter + V.21 to V.27ter (%.2fdBm0)\n", v27ter_rx_signal_power(&s->v27ter_rx)); + set_rx_handler(t, (span_rx_handler_t *) &v27ter_rx, &s->v27ter_rx); + } + else + { + fsk_rx(&s->v21_rx, amp, len); + if (s->rx_signal_present) + { + span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.27ter + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx)); + set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, &s->v21_rx); + } + /*endif*/ + } + /*endif*/ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int v29_v21_rx(void *user_data, const int16_t amp[], int len) +{ + t38_gateway_state_t *t; + fax_modems_state_t *s; + + t = (t38_gateway_state_t *) user_data; + s = &t->audio.modems; + v29_rx(&s->v29_rx, amp, len); + if (s->rx_trained) + { + /* The fast modem has trained, so we no longer need to run the slow + one in parallel. */ + span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.29 + V.21 to V.29 (%.2fdBm0)\n", v29_rx_signal_power(&s->v29_rx)); + set_rx_handler(t, (span_rx_handler_t *) &v29_rx, &s->v29_rx); + } + else + { + fsk_rx(&s->v21_rx, amp, len); + if (s->rx_signal_present) + { + span_log(&t->logging, SPAN_LOG_FLOW, "Switching from V.29 + V.21 to V.21 (%.2fdBm0)\n", fsk_rx_signal_power(&s->v21_rx)); + set_rx_handler(t, (span_rx_handler_t *) &fsk_rx, &s->v21_rx); + } + /*endif*/ + } + /*endif*/ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static void tone_detected(void *user_data, int tone, int level, int delay) +{ + t38_gateway_state_t *s; + + s = (t38_gateway_state_t *) user_data; + span_log(&s->logging, SPAN_LOG_FLOW, "%s detected (%ddBm0)\n", modem_connect_tone_to_str(tone), level); +} +/*- End of function --------------------------------------------------------*/ + +static void hdlc_underflow_handler(void *user_data) +{ + t38_gateway_state_t *s; + t38_gateway_hdlc_state_t *t; + int old_data_type; + + s = (t38_gateway_state_t *) user_data; + t = &s->core.hdlc_to_modem; + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC underflow at %d\n", t->out); + /* If the current HDLC buffer is not at the HDLC_FLAG_PROCEED_WITH_OUTPUT stage, this + underflow must be an end of preamble condition. */ + if ((t->buf[t->out].flags & HDLC_FLAG_PROCEED_WITH_OUTPUT)) + { + old_data_type = t->buf[t->out].contents; + t->buf[t->out].len = 0; + t->buf[t->out].flags = 0; + t->buf[t->out].contents = 0; + if (++t->out >= T38_TX_HDLC_BUFS) + t->out = 0; + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC next is 0x%X\n", t->buf[t->out].contents); + if ((t->buf[t->out].contents & FLAG_INDICATOR)) + { + /* The next thing in the queue is an indicator, so we need to stop this modem. */ + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC shutdown\n"); + hdlc_tx_frame(&s->audio.modems.hdlc_tx, NULL, 0); + } + else if ((t->buf[t->out].contents & FLAG_DATA)) + { + /* Check if we should start sending the next frame */ + if ((t->buf[t->out].flags & HDLC_FLAG_PROCEED_WITH_OUTPUT)) + { + /* This frame is ready to go, and uses the same modem we are running now. So, send + whatever we have. This might or might not be an entire frame. */ + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC start next frame\n"); + hdlc_tx_frame(&s->audio.modems.hdlc_tx, t->buf[t->out].buf, t->buf[t->out].len); + if ((t->buf[t->out].flags & HDLC_FLAG_CORRUPT_CRC)) + hdlc_tx_corrupt_frame(&s->audio.modems.hdlc_tx); + /*endif*/ + } + /*endif*/ + } + /*endif*/ + } + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +static int set_next_tx_type(t38_gateway_state_t *s) +{ + get_bit_func_t get_bit_func; + void *get_bit_user_data; + int indicator; + int short_train; + fax_modems_state_t *t; + t38_gateway_hdlc_state_t *u; + + t = &s->audio.modems; + u = &s->core.hdlc_to_modem; + t38_non_ecm_buffer_report_output_status(&s->core.non_ecm_to_modem, &s->logging); + if (t->next_tx_handler) + { + /* There is a handler queued, so that is the next one. */ + set_tx_handler(s, t->next_tx_handler, t->next_tx_user_data); + set_next_tx_handler(s, NULL, NULL); + if (t->tx_handler == (span_tx_handler_t *) &(silence_gen) + || + t->tx_handler == (span_tx_handler_t *) &(tone_gen)) + { + set_rx_active(s, TRUE); + } + else + { + set_rx_active(s, FALSE); + } + /*endif*/ + return TRUE; + } + /*endif*/ + if (u->in == u->out) + return FALSE; + /*endif*/ + if ((u->buf[u->out].contents & FLAG_INDICATOR) == 0) + return FALSE; + /*endif*/ + indicator = (u->buf[u->out].contents & 0xFF); + u->buf[u->out].len = 0; + u->buf[u->out].flags = 0; + u->buf[u->out].contents = 0; + if (++u->out >= T38_TX_HDLC_BUFS) + u->out = 0; + /*endif*/ + span_log(&s->logging, SPAN_LOG_FLOW, "Changing to %s\n", t38_indicator_to_str(indicator)); + if (s->core.image_data_mode && s->core.ecm_mode) + { + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC mode\n"); + hdlc_tx_init(&t->hdlc_tx, FALSE, 2, TRUE, hdlc_underflow_handler, s); + get_bit_func = (get_bit_func_t) hdlc_tx_get_bit; + get_bit_user_data = (void *) &t->hdlc_tx; + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM mode\n"); + get_bit_func = t38_non_ecm_buffer_get_bit; + get_bit_user_data = (void *) &s->core.non_ecm_to_modem; + } + /*endif*/ + switch (indicator) + { + case T38_IND_NO_SIGNAL: + t->tx_bit_rate = 0; + /* Impose 75ms minimum on transmitted silence */ + //silence_gen_set(&t->silence_gen, ms_to_samples(75)); + set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen); + set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL); + set_rx_active(s, TRUE); + break; + case T38_IND_CNG: + t->tx_bit_rate = 0; + modem_connect_tones_tx_init(&t->connect_tx, MODEM_CONNECT_TONES_FAX_CNG); + set_tx_handler(s, (span_tx_handler_t *) &modem_connect_tones_tx, &t->connect_tx); + silence_gen_set(&t->silence_gen, 0); + set_next_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen); + set_rx_active(s, TRUE); + break; + case T38_IND_CED: + t->tx_bit_rate = 0; + modem_connect_tones_tx_init(&t->connect_tx, MODEM_CONNECT_TONES_FAX_CED); + set_tx_handler(s, (span_tx_handler_t *) &modem_connect_tones_tx, &t->connect_tx); + set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL); + set_rx_active(s, TRUE); + break; + case T38_IND_V21_PREAMBLE: + t->tx_bit_rate = 300; + hdlc_tx_init(&t->hdlc_tx, FALSE, 2, TRUE, hdlc_underflow_handler, s); + hdlc_tx_flags(&t->hdlc_tx, 32); + silence_gen_alter(&t->silence_gen, ms_to_samples(75)); + u->buf[u->in].len = 0; + fsk_tx_init(&t->v21_tx, &preset_fsk_specs[FSK_V21CH2], (get_bit_func_t) hdlc_tx_get_bit, &t->hdlc_tx); + set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen); + set_next_tx_handler(s, (span_tx_handler_t *) &fsk_tx, &t->v21_tx); + set_rx_active(s, TRUE); + break; + case T38_IND_V27TER_2400_TRAINING: + case T38_IND_V27TER_4800_TRAINING: + switch (indicator) + { + case T38_IND_V27TER_2400_TRAINING: + t->tx_bit_rate = 2400; + break; + case T38_IND_V27TER_4800_TRAINING: + t->tx_bit_rate = 2400; + break; + } + /*endswitch*/ + silence_gen_alter(&t->silence_gen, ms_to_samples(75)); + v27ter_tx_restart(&t->v27ter_tx, t->tx_bit_rate, t->use_tep); + v27ter_tx_set_get_bit(&t->v27ter_tx, get_bit_func, get_bit_user_data); + set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen); + set_next_tx_handler(s, (span_tx_handler_t *) &v27ter_tx, &t->v27ter_tx); + set_rx_active(s, TRUE); + break; + case T38_IND_V29_7200_TRAINING: + case T38_IND_V29_9600_TRAINING: + switch (indicator) + { + case T38_IND_V29_7200_TRAINING: + t->tx_bit_rate = 7200; + break; + case T38_IND_V29_9600_TRAINING: + t->tx_bit_rate = 9600; + break; + } + /*endswitch*/ + silence_gen_alter(&t->silence_gen, ms_to_samples(75)); + v29_tx_restart(&t->v29_tx, t->tx_bit_rate, t->use_tep); + v29_tx_set_get_bit(&t->v29_tx, get_bit_func, get_bit_user_data); + set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen); + set_next_tx_handler(s, (span_tx_handler_t *) &v29_tx, &t->v29_tx); + set_rx_active(s, TRUE); + break; + case T38_IND_V17_7200_SHORT_TRAINING: + case T38_IND_V17_7200_LONG_TRAINING: + case T38_IND_V17_9600_SHORT_TRAINING: + case T38_IND_V17_9600_LONG_TRAINING: + case T38_IND_V17_12000_SHORT_TRAINING: + case T38_IND_V17_12000_LONG_TRAINING: + case T38_IND_V17_14400_SHORT_TRAINING: + case T38_IND_V17_14400_LONG_TRAINING: + short_train = FALSE; + switch (indicator) + { + case T38_IND_V17_7200_SHORT_TRAINING: + short_train = TRUE; + t->tx_bit_rate = 7200; + break; + case T38_IND_V17_7200_LONG_TRAINING: + t->tx_bit_rate = 7200; + break; + case T38_IND_V17_9600_SHORT_TRAINING: + short_train = TRUE; + t->tx_bit_rate = 9600; + break; + case T38_IND_V17_9600_LONG_TRAINING: + t->tx_bit_rate = 9600; + break; + case T38_IND_V17_12000_SHORT_TRAINING: + short_train = TRUE; + t->tx_bit_rate = 12000; + break; + case T38_IND_V17_12000_LONG_TRAINING: + t->tx_bit_rate = 12000; + break; + case T38_IND_V17_14400_SHORT_TRAINING: + short_train = TRUE; + t->tx_bit_rate = 14400; + break; + case T38_IND_V17_14400_LONG_TRAINING: + t->tx_bit_rate = 14400; + break; + } + /*endswitch*/ + silence_gen_alter(&t->silence_gen, ms_to_samples(75)); + v17_tx_restart(&t->v17_tx, t->tx_bit_rate, t->use_tep, short_train); + v17_tx_set_get_bit(&t->v17_tx, get_bit_func, get_bit_user_data); + set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen); + set_next_tx_handler(s, (span_tx_handler_t *) &v17_tx, &t->v17_tx); + set_rx_active(s, TRUE); + break; + case T38_IND_V8_ANSAM: + t->tx_bit_rate = 300; + break; + case T38_IND_V8_SIGNAL: + t->tx_bit_rate = 300; + break; + case T38_IND_V34_CNTL_CHANNEL_1200: + t->tx_bit_rate = 1200; + break; + case T38_IND_V34_PRI_CHANNEL: + t->tx_bit_rate = 33600; + break; + case T38_IND_V34_CC_RETRAIN: + t->tx_bit_rate = 0; + break; + case T38_IND_V33_12000_TRAINING: + t->tx_bit_rate = 12000; + break; + case T38_IND_V33_14400_TRAINING: + t->tx_bit_rate = 14400; + break; + default: + break; + } + /*endswitch*/ + /* For any fast modem, set 200ms of preamble flags */ + if (t->tx_bit_rate > 300) + hdlc_tx_flags(&t->hdlc_tx, t->tx_bit_rate/(8*5)); + /*endif*/ + s->t38x.in_progress_rx_indicator = indicator; + return TRUE; +} +/*- End of function --------------------------------------------------------*/ + +static void finalise_hdlc_frame(t38_gateway_state_t *s, int good_fcs) +{ + t38_gateway_hdlc_buf_t *hdlc_buf; + + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + if (!good_fcs || (hdlc_buf->flags & HDLC_FLAG_MISSING_DATA)) + hdlc_buf->flags |= HDLC_FLAG_CORRUPT_CRC; + /*endif*/ + if (s->core.hdlc_to_modem.in == s->core.hdlc_to_modem.out) + { + /* This is the frame in progress at the output. */ + if ((hdlc_buf->flags & HDLC_FLAG_PROCEED_WITH_OUTPUT) == 0) + { + /* Output of this frame has not yet begun. Throw it all out now. */ + hdlc_tx_frame(&s->audio.modems.hdlc_tx, hdlc_buf->buf, hdlc_buf->len); + } + /*endif*/ + if ((hdlc_buf->flags & HDLC_FLAG_CORRUPT_CRC)) + hdlc_tx_corrupt_frame(&s->audio.modems.hdlc_tx); + /*endif*/ + } + /*endif*/ + hdlc_buf->flags |= (HDLC_FLAG_PROCEED_WITH_OUTPUT | HDLC_FLAG_FINISHED); + if (++s->core.hdlc_to_modem.in >= T38_TX_HDLC_BUFS) + s->core.hdlc_to_modem.in = 0; + /*endif*/ + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + hdlc_buf->len = 0; + hdlc_buf->flags = 0; + hdlc_buf->contents = 0; +} +/*- End of function --------------------------------------------------------*/ + +static void edit_control_messages(t38_gateway_state_t *s, int from_modem, uint8_t *buf, int len) +{ + /* Frames need to be fed to this routine byte by byte as they arrive. It basically just + edits the last byte received, based on the frame up to that point. */ + if (s->t38x.corrupt_current_frame[from_modem]) + { + /* We simply need to overwrite a section of the message, so it is not recognisable at + the receiver. This is used for the NSF, NSC, and NSS messages. Several strategies are + possible for the replacement data. If you have a manufacturer code of your own, the + sane thing is to overwrite the original data with that. */ + if (len <= s->t38x.suppress_nsx_len[from_modem]) + buf[len - 1] = nsx_overwrite[from_modem][len - 4]; + /*endif*/ + return; + } + /*endif*/ + /* Edit the message, if we need to control the communication between the end points. */ + switch (len) + { + case 3: + switch (buf[2]) + { + case T30_NSF: + case T30_NSC: + case T30_NSS: + if (s->t38x.suppress_nsx_len[from_modem]) + { + /* Corrupt the message, so it will be ignored by the far end. If it were + processed, 2 machines which recognise each other might do special things + we cannot handle as a middle man. */ + span_log(&s->logging, SPAN_LOG_FLOW, "Corrupting %s message to prevent recognition\n", t30_frametype(buf[2])); + s->t38x.corrupt_current_frame[from_modem] = TRUE; + } + /*endif*/ + break; + } + /*endswitch*/ + break; + case 4: + switch (buf[2]) + { + case T30_DIS: + /* Make sure the V.8 capability doesn't pass through. If it + did then two V.34 capable FAX machines might start some + V.8 re-negotiation. */ + buf[3] &= ~DISBIT6; + break; + } + /*endswitch*/ + break; + case 5: + switch (buf[2]) + { + case T30_DIS: + /* We may need to adjust the capabilities, so they do not exceed our own */ + span_log(&s->logging, SPAN_LOG_FLOW, "Applying fast modem type constraints.\n"); + switch (buf[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3)) + { + case 0: + case DISBIT4: + /* V.27ter only */ + break; + case DISBIT3: + case (DISBIT4 | DISBIT3): + /* V.27ter and V.29 */ + if (!(s->core.supported_modems & T30_SUPPORT_V29)) + buf[4] &= ~DISBIT3; + /*endif*/ + break; + case (DISBIT6 | DISBIT4 | DISBIT3): + /* V.27ter, V.29 and V.17 */ + if (!(s->core.supported_modems & T30_SUPPORT_V17)) + buf[4] &= ~DISBIT6; + /*endif*/ + if (!(s->core.supported_modems & T30_SUPPORT_V29)) + buf[4] &= ~DISBIT3; + /*endif*/ + break; + case (DISBIT5 | DISBIT4): + case (DISBIT6 | DISBIT4): + case (DISBIT6 | DISBIT5 | DISBIT4): + case (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3): + /* Reserved */ + buf[4] &= ~(DISBIT6 | DISBIT5); + buf[4] |= (DISBIT4 | DISBIT3); + break; + default: + /* Not used */ + buf[4] &= ~(DISBIT6 | DISBIT5); + buf[4] |= (DISBIT4 | DISBIT3); + break; + } + /*endswitch*/ + break; + } + /*endswitch*/ + break; + case 7: + switch (buf[2]) + { + case T30_DIS: + if (!s->core.ecm_allowed) + { + /* Do not allow ECM or T.6 coding */ + span_log(&s->logging, SPAN_LOG_FLOW, "Inhibiting ECM\n"); + buf[6] &= ~(DISBIT3 | DISBIT7); + } + /*endif*/ + break; + } + /*endswitch*/ + break; + } + /*endswitch*/ +} +/*- End of function --------------------------------------------------------*/ + +static void monitor_control_messages(t38_gateway_state_t *s, + int from_modem, + const uint8_t *buf, + int len) +{ + static const struct + { + int bit_rate; + int modem_type; + uint8_t dcs_code; + } modem_codes[] = + { + {14400, T38_V17_RX, DISBIT6}, + {12000, T38_V17_RX, (DISBIT6 | DISBIT4)}, + { 9600, T38_V17_RX, (DISBIT6 | DISBIT3)}, + { 9600, T38_V29_RX, DISBIT3}, + { 7200, T38_V17_RX, (DISBIT6 | DISBIT4 | DISBIT3)}, + { 7200, T38_V29_RX, (DISBIT4 | DISBIT3)}, + { 4800, T38_V27TER_RX, DISBIT4}, + { 2400, T38_V27TER_RX, 0}, + { 0, T38_NONE, 0} + }; + static const int minimum_scan_line_times[8] = + { + 20, + 5, + 10, + 0, + 40, + 0, + 0, + 0 + }; + int dcs_code; + int i; + int j; + + /* Monitor the control messages, at the point where we have the whole message, so we can + see what is happening to things like training success/failure. */ + span_log(&s->logging, SPAN_LOG_FLOW, "Monitoring %s\n", t30_frametype(buf[2])); + if (len < 3) + return; + /*endif*/ + s->core.timed_mode = TIMED_MODE_IDLE; + switch (buf[2]) + { + case T30_CFR: + /* We are changing from TCF exchange to image exchange */ + /* Successful training means we should change to short training */ + s->core.image_data_mode = TRUE; + s->core.short_train = TRUE; + span_log(&s->logging, SPAN_LOG_FLOW, "CFR - short train = %d, ECM = %d\n", s->core.short_train, s->core.ecm_mode); + if (!from_modem) + restart_rx_modem(s); + /*endif*/ + break; + case T30_RTN: + case T30_RTP: + /* We are going back to the exchange of fresh TCF */ + s->core.image_data_mode = FALSE; + s->core.short_train = FALSE; + break; + case T30_CTR: + /* T.30 says the first image data after this does full training, yet does not + return to TCF. This seems to be the sole case of long training for image + data. */ + s->core.short_train = FALSE; + break; + case T30_DTC: + case T30_DCS: + case T30_DCS | 1: + /* We need to check which modem type is about to be used, so we can start the + correct modem. */ + s->core.fast_bit_rate = 0; + s->core.fast_rx_modem = T38_NONE; + s->core.image_data_mode = FALSE; + s->core.short_train = FALSE; + if (from_modem) + s->core.timed_mode = TIMED_MODE_TCF_PREDICTABLE_MODEM_START_BEGIN; + /*endif*/ + if (len >= 5) + { + /* The table is short, and not searched often, so a brain-dead linear scan seems OK */ + dcs_code = buf[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3); + for (i = 0; modem_codes[i].bit_rate; i++) + { + if (modem_codes[i].dcs_code == dcs_code) + break; + /*endif*/ + } + /*endfor*/ + /* If we are processing a message from the modem side, the contents determine the fast receive modem. + we are to use. If it comes from the T.38 side the contents do not. */ + s->core.fast_bit_rate = modem_codes[i].bit_rate; + if (from_modem) + s->core.fast_rx_modem = modem_codes[i].modem_type; + /*endif*/ + } + /*endif*/ + if (len >= 6) + { + j = (buf[5] & (DISBIT7 | DISBIT6 | DISBIT5)) >> 4; + span_log(&s->logging, SPAN_LOG_FLOW, "Min bits test = 0x%X\n", buf[5]); + s->core.min_row_bits = (s->core.fast_bit_rate*minimum_scan_line_times[j])/1000; + } + else + { + s->core.min_row_bits = 0; + } + /*endif*/ + s->core.ecm_mode = (len >= 7) && (buf[6] & DISBIT3); + span_log(&s->logging, SPAN_LOG_FLOW, "Fast rx modem = %d/%d, ECM = %d, Min bits per row = %d\n", s->core.fast_rx_modem, s->core.fast_bit_rate, s->core.ecm_mode, s->core.min_row_bits); + break; + case T30_PPS: + case T30_PPS | 1: + switch (buf[3] & 0xFE) + { + case T30_EOP: + case T30_PRI_EOP: + case T30_EOM: + case T30_PRI_EOM: + case T30_EOS: +#if 0 + /* If we are hitting one of these conditions, it will take another DCS/DTC to select + the fast modem again, so abandon our idea of it. */ + s->core.fast_bit_rate = 0; + s->core.fast_rx_modem = T38_NONE; + s->core.image_data_mode = FALSE; + s->core.short_train = FALSE; +#endif + /* Fall through */ + case T30_MPS: + case T30_PRI_MPS: + s->core.count_page_on_mcf = TRUE; + break; + } + /*endswitch*/ + break; + case T30_EOP: + case T30_EOP | 1: + case T30_PRI_EOP: + case T30_PRI_EOP | 1: + case T30_EOM: + case T30_EOM | 1: + case T30_PRI_EOM: + case T30_PRI_EOM | 1: + case T30_EOS: + case T30_EOS | 1: +#if 0 + /* If we are hitting one of these conditions, it will take another DCS/DTC to select + the fast modem again, so abandon our idea of t. */ + s->core.fast_bit_rate = 0; + s->core.fast_rx_modem = T38_NONE; + s->core.image_data_mode = FALSE; + s->core.short_train = FALSE; +#endif + /* Fall through */ + case T30_MPS: + case T30_MPS | 1: + case T30_PRI_MPS: + case T30_PRI_MPS | 1: + s->core.count_page_on_mcf = TRUE; + break; + case T30_MCF: + case T30_MCF | 1: + if (s->core.count_page_on_mcf) + { + s->core.pages_confirmed++; + span_log(&s->logging, SPAN_LOG_FLOW, "Pages confirmed = %d\n", s->core.pages_confirmed); + s->core.count_page_on_mcf = FALSE; + } + /*endif*/ + break; + default: + break; + } + /*endswitch*/ +} +/*- End of function --------------------------------------------------------*/ + +static void queue_missing_indicator(t38_gateway_state_t *s, int data_type) +{ + t38_core_state_t *t; + int expected; + int expected_alt; + + t = &s->t38x.t38; + expected = -1; + expected_alt = -1; + /* Missing packets might have lost us the indicator that should have put us in + the required mode of operation. It might be a bit late to fill in such a gap + now, but we should try. We may also want to force indicators into the queue, + such as when the data says 'end of signal'. */ + /* We have an expectation of whether long or short training should occur, but be + tolerant of either kind of indicator being present. */ + switch (data_type) + { + case T38_DATA_NONE: + expected = T38_IND_NO_SIGNAL; + break; + case T38_DATA_V21: + expected = T38_IND_V21_PREAMBLE; + break; + case T38_DATA_V27TER_2400: + expected = T38_IND_V27TER_2400_TRAINING; + break; + case T38_DATA_V27TER_4800: + expected = T38_IND_V27TER_4800_TRAINING; + break; + case T38_DATA_V29_7200: + expected = T38_IND_V29_7200_TRAINING; + break; + case T38_DATA_V29_9600: + expected = T38_IND_V29_9600_TRAINING; + break; + case T38_DATA_V17_7200: + expected = (s->core.short_train) ? T38_IND_V17_7200_SHORT_TRAINING : T38_IND_V17_7200_LONG_TRAINING; + expected_alt = (s->core.short_train) ? T38_IND_V17_7200_LONG_TRAINING : T38_IND_V17_7200_SHORT_TRAINING; + break; + case T38_DATA_V17_9600: + expected = (s->core.short_train) ? T38_IND_V17_9600_SHORT_TRAINING : T38_IND_V17_9600_LONG_TRAINING; + expected_alt = (s->core.short_train) ? T38_IND_V17_9600_LONG_TRAINING : T38_IND_V17_9600_SHORT_TRAINING; + break; + case T38_DATA_V17_12000: + expected = (s->core.short_train) ? T38_IND_V17_12000_SHORT_TRAINING : T38_IND_V17_12000_LONG_TRAINING; + expected_alt = (s->core.short_train) ? T38_IND_V17_12000_LONG_TRAINING : T38_IND_V17_12000_SHORT_TRAINING; + break; + case T38_DATA_V17_14400: + expected = (s->core.short_train) ? T38_IND_V17_14400_SHORT_TRAINING : T38_IND_V17_14400_LONG_TRAINING; + expected_alt = (s->core.short_train) ? T38_IND_V17_14400_LONG_TRAINING : T38_IND_V17_14400_SHORT_TRAINING; + break; + case T38_DATA_V8: + break; + case T38_DATA_V34_PRI_RATE: + break; + case T38_DATA_V34_CC_1200: + break; + case T38_DATA_V34_PRI_CH: + break; + case T38_DATA_V33_12000: + break; + case T38_DATA_V33_14400: + break; + } + /*endswitch*/ + if (expected < 0) + return; + if (t->current_rx_indicator == expected) + return; + if (expected_alt >= 0 && t->current_rx_indicator == expected_alt) + return; + span_log(&s->logging, + SPAN_LOG_FLOW, + "Queuing missing indicator - %s\n", + t38_indicator_to_str(expected)); + process_rx_indicator(t, (void *) s, expected); + /* Force the indicator setting here, as the core won't set in when its missing. */ + t->current_rx_indicator = expected; +} +/*- End of function --------------------------------------------------------*/ + +static int process_rx_missing(t38_core_state_t *t, void *user_data, int rx_seq_no, int expected_seq_no) +{ + t38_gateway_state_t *s; + + s = (t38_gateway_state_t *) user_data; + s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].flags |= HDLC_FLAG_MISSING_DATA; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int process_rx_indicator(t38_core_state_t *t, void *user_data, int indicator) +{ + t38_gateway_state_t *s; + + s = (t38_gateway_state_t *) user_data; + + t38_non_ecm_buffer_report_input_status(&s->core.non_ecm_to_modem, &s->logging); + if (t->current_rx_indicator == indicator) + { + /* This is probably due to the far end repeating itself. Ignore it. Its harmless */ + return 0; + } + /*endif*/ + if (s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].contents) + { + if (++s->core.hdlc_to_modem.in >= T38_TX_HDLC_BUFS) + s->core.hdlc_to_modem.in = 0; + /*endif*/ + } + /*endif*/ + s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].contents = (indicator | FLAG_INDICATOR); + if (++s->core.hdlc_to_modem.in >= T38_TX_HDLC_BUFS) + s->core.hdlc_to_modem.in = 0; + /*endif*/ + t38_non_ecm_buffer_set_mode(&s->core.non_ecm_to_modem, s->core.image_data_mode, s->core.min_row_bits); + + span_log(&s->logging, + SPAN_LOG_FLOW, + "Queued change - (%d) %s -> %s\n", + silence_gen_remainder(&(s->audio.modems.silence_gen)), + t38_indicator_to_str(t->current_rx_indicator), + t38_indicator_to_str(indicator)); + s->t38x.current_rx_field_class = T38_FIELD_CLASS_NONE; + /* We need to set this here, since we might have been called as a fake + indication when the real one was missing */ + t->current_rx_indicator = indicator; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int process_rx_data(t38_core_state_t *t, void *user_data, int data_type, int field_type, const uint8_t *buf, int len) +{ + int i; + t38_gateway_state_t *s; + t38_gateway_t38_state_t *xx; + t38_gateway_hdlc_buf_t *hdlc_buf; + + s = (t38_gateway_state_t *) user_data; + xx = &s->t38x; + /* There are a couple of special cases of data type that need their own treatment. */ + switch (data_type) + { + case T38_DATA_V8: + switch (field_type) + { + case T38_FIELD_CM_MESSAGE: + if (len >= 1) + span_log(&s->logging, SPAN_LOG_FLOW, "CM profile %d - %s\n", buf[0] - '0', t38_cm_profile_to_str(buf[0])); + else + span_log(&s->logging, SPAN_LOG_FLOW, "Bad length for CM message - %d\n", len); + /*endif*/ + break; + case T38_FIELD_JM_MESSAGE: + if (len >= 2) + span_log(&s->logging, SPAN_LOG_FLOW, "JM - %s\n", t38_jm_to_str(buf, len)); + else + span_log(&s->logging, SPAN_LOG_FLOW, "Bad length for JM message - %d\n", len); + /*endif*/ + break; + case T38_FIELD_CI_MESSAGE: + if (len >= 1) + span_log(&s->logging, SPAN_LOG_FLOW, "CI 0x%X\n", buf[0]); + else + span_log(&s->logging, SPAN_LOG_FLOW, "Bad length for CI message - %d\n", len); + /*endif*/ + break; + default: + break; + } + /*endswitch*/ + return 0; + case T38_DATA_V34_PRI_RATE: + switch (field_type) + { + case T38_FIELD_V34RATE: + if (len >= 3) + { + xx->t38.v34_rate = t38_v34rate_to_bps(buf, len); + span_log(&s->logging, SPAN_LOG_FLOW, "V.34 rate %d bps\n", xx->t38.v34_rate); + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "Bad length for V34rate message - %d\n", len); + } + /*endif*/ + break; + default: + break; + } + /*endswitch*/ + return 0; + default: + break; + } + /*endswitch*/ + switch (field_type) + { + case T38_FIELD_HDLC_DATA: + xx->current_rx_field_class = T38_FIELD_CLASS_HDLC; + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + /* All real HDLC messages in the FAX world start with 0xFF. If this one is not starting + with 0xFF it would appear some octets must have been missed before this one. */ + if (len <= 0 || buf[0] != 0xFF) + s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].flags |= HDLC_FLAG_MISSING_DATA; + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /*endif*/ + /* Check if this data would overflow the buffer. */ + if (len <= 0 || hdlc_buf->len + len > T38_MAX_HDLC_LEN) + break; + /*endif*/ + hdlc_buf->contents = (data_type | FLAG_DATA); + bit_reverse(&hdlc_buf->buf[hdlc_buf->len], buf, len); + /* We need to send out the control messages as they are arriving. They are + too slow to capture a whole frame before starting to pass it on. + For the faster frames, take in the whole frame before sending it out. Also, there + is no need to monitor, or modify, the contents of the faster frames. */ + if (data_type == T38_DATA_V21) + { + for (i = 1; i <= len; i++) + edit_control_messages(s, 0, hdlc_buf->buf, hdlc_buf->len + i); + /*endfor*/ + /* Don't start pumping data into the actual output stream until there is + enough backlog to create some elasticity for jitter tolerance. */ + if (hdlc_buf->len + len >= HDLC_START_BUFFER_LEVEL) + { + if (s->core.hdlc_to_modem.in == s->core.hdlc_to_modem.out) + { + /* Output is not running, so kick it into life. */ + if ((hdlc_buf->flags & HDLC_FLAG_PROCEED_WITH_OUTPUT) == 0) + hdlc_tx_frame(&s->audio.modems.hdlc_tx, hdlc_buf->buf, hdlc_buf->len + len); + else + hdlc_tx_frame(&s->audio.modems.hdlc_tx, hdlc_buf->buf + hdlc_buf->len, len); + /*endif*/ + } + /*endif*/ + hdlc_buf->flags |= HDLC_FLAG_PROCEED_WITH_OUTPUT; + } + /*endif*/ + } + /*endif*/ + s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].len += len; + break; + case T38_FIELD_HDLC_FCS_OK: + xx->current_rx_field_class = T38_FIELD_CLASS_HDLC; + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + if (len > 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_OK!\n"); + /* The sender has incorrectly included data in this message. It is unclear what we should do + with it, to maximise tolerance of buggy implementations. */ + } + /*endif*/ + /* Some T.38 implementations send multiple T38_FIELD_HDLC_FCS_OK messages, in IFP packets with + incrementing sequence numbers, which are actually repeats. They get through to this point because + of the incrementing sequence numbers. We need to filter them here in a context sensitive manner. */ + if (t->current_rx_data_type != data_type || t->current_rx_field_type != field_type) + { + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC frame type %s - CRC good\n", t30_frametype(hdlc_buf->buf[2])); + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /*endif*/ + if (data_type == T38_DATA_V21) + { + if ((hdlc_buf->flags & HDLC_FLAG_MISSING_DATA) == 0) + { + monitor_control_messages(s, FALSE, hdlc_buf->buf, hdlc_buf->len); + if (s->core.real_time_frame_handler) + s->core.real_time_frame_handler(s, s->core.real_time_frame_user_data, FALSE, hdlc_buf->buf, hdlc_buf->len); + /*endif*/ + } + /*endif*/ + } + else + { + /* Make sure we go back to short training if CTC/CTR has kicked us into + long training. There has to be more than one value HDLC frame in a + chunk of image data, so just setting short training mode here should + be enough. */ + s->core.short_train = TRUE; + } + /*endif*/ + hdlc_buf->contents = (data_type | FLAG_DATA); + finalise_hdlc_frame(s, TRUE); + } + /*endif*/ + xx->corrupt_current_frame[0] = FALSE; + break; + case T38_FIELD_HDLC_FCS_BAD: + xx->current_rx_field_class = T38_FIELD_CLASS_HDLC; + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + if (len > 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_BAD!\n"); + /* The sender has incorrectly included data in this message. We can safely ignore it, as the + bad FCS means we will throw away the whole message, anyway. */ + } + /*endif*/ + /* Some T.38 implementations send multiple T38_FIELD_HDLC_FCS_BAD messages, in IFP packets with + incrementing sequence numbers, which are actually repeats. They get through to this point because + of the incrementing sequence numbers. We need to filter them here in a context sensitive manner. */ + if (t->current_rx_data_type != data_type || t->current_rx_field_type != field_type) + { + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC frame type %s - CRC bad\n", t30_frametype(hdlc_buf->buf[2])); + /* Only bother with frames that have a bad CRC, if they also have some content. */ + if (hdlc_buf->len > 0) + { + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /*endif*/ + hdlc_buf->contents = (data_type | FLAG_DATA); + finalise_hdlc_frame(s, FALSE); + } + else + { + /* Just restart using the current frame buffer */ + hdlc_buf->contents = 0; + } + /*endif*/ + } + /*endif*/ + xx->corrupt_current_frame[0] = FALSE; + break; + case T38_FIELD_HDLC_FCS_OK_SIG_END: + xx->current_rx_field_class = T38_FIELD_CLASS_HDLC; + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + if (len > 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_OK_SIG_END!\n"); + /* The sender has incorrectly included data in this message. It is unclear what we should do + with it, to maximise tolerance of buggy implementations. */ + } + /*endif*/ + /* Some T.38 implementations send multiple T38_FIELD_HDLC_FCS_OK_SIG_END messages, in IFP packets with + incrementing sequence numbers, which are actually repeats. They get through to this point because + of the incrementing sequence numbers. We need to filter them here in a context sensitive manner. */ + if (t->current_rx_data_type != data_type || t->current_rx_field_type != field_type) + { + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC frame type %s - CRC OK, sig end\n", t30_frametype(hdlc_buf->buf[2])); + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /*endif*/ + if (data_type == T38_DATA_V21) + { + if ((hdlc_buf->flags & HDLC_FLAG_MISSING_DATA) == 0) + { + monitor_control_messages(s, FALSE, hdlc_buf->buf, hdlc_buf->len); + if (s->core.real_time_frame_handler) + s->core.real_time_frame_handler(s, s->core.real_time_frame_user_data, FALSE, hdlc_buf->buf, hdlc_buf->len); + /*endif*/ + } + /*endif*/ + } + else + { + /* Make sure we go back to short training if CTC/CTR has kicked us into + long training. There has to be more than one value HDLC frame in a + chunk of image data, so just setting short training mode here should + be enough. */ + s->core.short_train = TRUE; + } + /*endif*/ + hdlc_buf->contents = (data_type | FLAG_DATA); + finalise_hdlc_frame(s, TRUE); + queue_missing_indicator(s, T38_DATA_NONE); + xx->current_rx_field_class = T38_FIELD_CLASS_NONE; + } + /*endif*/ + xx->corrupt_current_frame[0] = FALSE; + break; + case T38_FIELD_HDLC_FCS_BAD_SIG_END: + xx->current_rx_field_class = T38_FIELD_CLASS_HDLC; + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + if (len > 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_BAD_SIG_END!\n"); + /* The sender has incorrectly included data in this message. We can safely ignore it, as the + bad FCS means we will throw away the whole message, anyway. */ + } + /*endif*/ + /* Some T.38 implementations send multiple T38_FIELD_HDLC_FCS_BAD_SIG_END messages, in IFP packets with + incrementing sequence numbers, which are actually repeats. They get through to this point because + of the incrementing sequence numbers. We need to filter them here in a context sensitive manner. */ + if (t->current_rx_data_type != data_type || t->current_rx_field_type != field_type) + { + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC frame type %s - CRC bad, sig end\n", t30_frametype(hdlc_buf->buf[2])); + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /*endif*/ + /* Only bother with frames that have a bad CRC, if they also have some content. */ + if (hdlc_buf->len > 0) + { + hdlc_buf->contents = (data_type | FLAG_DATA); + finalise_hdlc_frame(s, FALSE); + } + else + { + /* Just restart using the current frame buffer */ + hdlc_buf->contents = 0; + } + /*endif*/ + queue_missing_indicator(s, T38_DATA_NONE); + xx->current_rx_field_class = T38_FIELD_CLASS_NONE; + } + /*endif*/ + xx->corrupt_current_frame[0] = FALSE; + break; + case T38_FIELD_HDLC_SIG_END: + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + if (len > 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_SIG_END!\n"); + /* The sender has incorrectly included data in this message, but there seems nothing meaningful + it could be. There could not be an FCS good/bad report beyond this. */ + } + /*endif*/ + /* Some T.38 implementations send multiple T38_FIELD_HDLC_SIG_END messages, in IFP packets with + incrementing sequence numbers, which are actually repeats. They get through to this point because + of the incrementing sequence numbers. We need to filter them here in a context sensitive manner. */ + if (t->current_rx_data_type != data_type || t->current_rx_field_type != field_type) + { + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /* WORKAROUND: At least some Mediatrix boxes have a bug, where they can send this message at the + end of non-ECM data. We need to tolerate this. */ + if (xx->current_rx_field_class == T38_FIELD_CLASS_NON_ECM) + { + span_log(&s->logging, SPAN_LOG_WARNING, "T38_FIELD_HDLC_SIG_END received at the end of non-ECM data!\n"); + /* Don't flow control the data any more. Just pump out the remainder as fast as we can. */ + t38_non_ecm_buffer_push(&s->core.non_ecm_to_modem); + } + else + { + /* This message is expected under 2 circumstances. One is as an alternative to T38_FIELD_HDLC_FCS_OK_SIG_END - + i.e. they send T38_FIELD_HDLC_FCS_OK, and then T38_FIELD_HDLC_SIG_END when the carrier actually drops. + The other is because the HDLC signal drops unexpectedly - i.e. not just after a final frame. In + this case we just clear out any partial frame data that might be in the buffer. */ + /* TODO: what if any junk in the buffer has reached the HDLC_FLAG_PROCEED_WITH_OUTPUT stage? */ + hdlc_buf->len = 0; + hdlc_buf->flags = 0; + hdlc_buf->contents = 0; + } + /*endif*/ + queue_missing_indicator(s, T38_DATA_NONE); + xx->current_rx_field_class = T38_FIELD_CLASS_NONE; + } + /*endif*/ + xx->corrupt_current_frame[0] = FALSE; + break; + case T38_FIELD_T4_NON_ECM_DATA: + xx->current_rx_field_class = T38_FIELD_CLASS_NON_ECM; + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + if (len > 0) + t38_non_ecm_buffer_inject(&s->core.non_ecm_to_modem, buf, len); + xx->corrupt_current_frame[0] = FALSE; + break; + case T38_FIELD_T4_NON_ECM_SIG_END: + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + /* Some T.38 implementations send multiple T38_FIELD_T4_NON_ECM_SIG_END messages, in IFP packets with + incrementing sequence numbers, which are actually repeats. They get through to this point because + of the incrementing sequence numbers. We need to filter them here in a context sensitive manner. */ + if (t->current_rx_data_type != data_type || t->current_rx_field_type != field_type) + { + /* WORKAROUND: At least some Mediatrix boxes have a bug, where they can send HDLC signal end where + they should send non-ECM signal end. It is possible they also do the opposite. + We need to tolerate this. */ + if (xx->current_rx_field_class == T38_FIELD_CLASS_NON_ECM) + { + if (len > 0) + { + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /*endif*/ + t38_non_ecm_buffer_inject(&s->core.non_ecm_to_modem, buf, len); + } + /*endif*/ + if (hdlc_buf->contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /*endif*/ + /* Don't flow control the data any more. Just pump out the remainder as fast as we can. */ + t38_non_ecm_buffer_push(&s->core.non_ecm_to_modem); + } + else + { + span_log(&s->logging, SPAN_LOG_WARNING, "T38_FIELD_NON_ECM_SIG_END received at the end of HDLC data!\n"); + if (s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in].contents != (data_type | FLAG_DATA)) + { + queue_missing_indicator(s, data_type); + hdlc_buf = &s->core.hdlc_to_modem.buf[s->core.hdlc_to_modem.in]; + } + /*endif*/ + /* TODO: what if any junk in the buffer has reached the HDLC_FLAG_PROCEED_WITH_OUTPUT stage? */ + hdlc_buf->len = 0; + hdlc_buf->flags = 0; + hdlc_buf->contents = 0; + } + /*endif*/ + queue_missing_indicator(s, T38_DATA_NONE); + xx->current_rx_field_class = T38_FIELD_CLASS_NONE; + } + /*endif*/ + xx->corrupt_current_frame[0] = FALSE; + break; + default: + break; + } + /*endswitch*/ + +#if 0 + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + int i; + + if (len > 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Data: "); + for (i = 0; i < len; i++) + span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, " %02X", buf[i]); + /*endfor*/ + } + /*endif*/ + } + /*endif*/ + span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "\n"); +#endif + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static void set_octets_per_data_packet(t38_gateway_state_t *s, int bit_rate) +{ + int octets; + + octets = MS_PER_TX_CHUNK*bit_rate/(8*1000); + if (octets < 1) + octets = 1; + /*endif*/ + s->core.to_t38.octets_per_data_packet = octets; +} +/*- End of function --------------------------------------------------------*/ + +static int set_slow_packetisation(t38_gateway_state_t *s) +{ + set_octets_per_data_packet(s, 300); + s->t38x.current_tx_data_type = T38_DATA_V21; + return T38_IND_V21_PREAMBLE; +} +/*- End of function --------------------------------------------------------*/ + +static int set_fast_packetisation(t38_gateway_state_t *s) +{ + int ind; + + ind = T38_IND_NO_SIGNAL; + switch (s->core.fast_rx_active) + { + case T38_V17_RX: + set_octets_per_data_packet(s, s->core.fast_bit_rate); + switch (s->core.fast_bit_rate) + { + case 7200: + ind = (s->core.short_train) ? T38_IND_V17_7200_SHORT_TRAINING : T38_IND_V17_7200_LONG_TRAINING; + s->t38x.current_tx_data_type = T38_DATA_V17_7200; + break; + case 9600: + ind = (s->core.short_train) ? T38_IND_V17_9600_SHORT_TRAINING : T38_IND_V17_9600_LONG_TRAINING; + s->t38x.current_tx_data_type = T38_DATA_V17_9600; + break; + case 12000: + ind = (s->core.short_train) ? T38_IND_V17_12000_SHORT_TRAINING : T38_IND_V17_12000_LONG_TRAINING; + s->t38x.current_tx_data_type = T38_DATA_V17_12000; + break; + default: + case 14400: + ind = (s->core.short_train) ? T38_IND_V17_14400_SHORT_TRAINING : T38_IND_V17_14400_LONG_TRAINING; + s->t38x.current_tx_data_type = T38_DATA_V17_14400; + break; + } + break; + case T38_V27TER_RX: + set_octets_per_data_packet(s, s->core.fast_bit_rate); + switch (s->core.fast_bit_rate) + { + case 2400: + ind = T38_IND_V27TER_2400_TRAINING; + s->t38x.current_tx_data_type = T38_DATA_V27TER_2400; + break; + default: + case 4800: + ind = T38_IND_V27TER_4800_TRAINING; + s->t38x.current_tx_data_type = T38_DATA_V27TER_4800; + break; + } + break; + case T38_V29_RX: + set_octets_per_data_packet(s, s->core.fast_bit_rate); + switch (s->core.fast_bit_rate) + { + case 7200: + ind = T38_IND_V29_7200_TRAINING; + s->t38x.current_tx_data_type = T38_DATA_V29_7200; + break; + default: + case 9600: + ind = T38_IND_V29_9600_TRAINING; + s->t38x.current_tx_data_type = T38_DATA_V29_9600; + break; + } + break; + } + return ind; +} +/*- End of function --------------------------------------------------------*/ + +static void announce_training(t38_gateway_state_t *s) +{ + t38_core_send_indicator(&s->t38x.t38, set_fast_packetisation(s)); +} +/*- End of function --------------------------------------------------------*/ + +static void non_ecm_rx_status(void *user_data, int status) +{ + t38_gateway_state_t *s; + + s = (t38_gateway_state_t *) user_data; + span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM signal status is %s (%d)\n", signal_status_to_str(status), status); + switch (status) + { + case SIG_STATUS_TRAINING_IN_PROGRESS: + if (s->core.timed_mode == TIMED_MODE_IDLE) + { + announce_training(s); + } + else + { + if (s->core.timed_mode == TIMED_MODE_TCF_PREDICTABLE_MODEM_START_PAST_V21_MODEM) + s->core.timed_mode = TIMED_MODE_TCF_PREDICTABLE_MODEM_START_FAST_MODEM_SEEN; + else + s->core.samples_to_timeout = ms_to_samples(500); + set_fast_packetisation(s); + } + break; + case SIG_STATUS_TRAINING_FAILED: + break; + case SIG_STATUS_TRAINING_SUCCEEDED: + /* The modem is now trained */ + s->audio.modems.rx_signal_present = TRUE; + s->audio.modems.rx_trained = TRUE; + s->core.timed_mode = TIMED_MODE_IDLE; + s->core.samples_to_timeout = 0; + to_t38_buffer_init(&s->core.to_t38); + break; + case SIG_STATUS_CARRIER_UP: + break; + case SIG_STATUS_CARRIER_DOWN: + switch (s->t38x.current_tx_data_type) + { + case T38_DATA_V17_7200: + case T38_DATA_V17_9600: + case T38_DATA_V17_12000: + case T38_DATA_V17_14400: + case T38_DATA_V27TER_2400: + case T38_DATA_V27TER_4800: + case T38_DATA_V29_7200: + case T38_DATA_V29_9600: + if (s->core.timed_mode != TIMED_MODE_TCF_PREDICTABLE_MODEM_START_FAST_MODEM_ANNOUNCED) + { + /* TODO: If the carrier really did fall for good during the 500ms TEP blocking timeout, we + won't declare the no-signal condition. */ + non_ecm_push_residue(s); + t38_core_send_indicator(&s->t38x.t38, T38_IND_NO_SIGNAL); + } + restart_rx_modem(s); + break; + } + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected non-ECM special bit - %d!\n", status); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void to_t38_buffer_init(t38_gateway_to_t38_state_t *s) +{ + s->data_ptr = 0; + s->bit_stream = 0xFFFF; + s->bit_no = 0; + + s->in_bits = 0; + s->out_octets = 0; +} +/*- End of function --------------------------------------------------------*/ + +static void non_ecm_push_residue(t38_gateway_state_t *t) +{ + t38_gateway_to_t38_state_t *s; + + s = &t->core.to_t38; + if (s->bit_no) + { + /* There is a fractional octet in progress. We might as well send every last bit we can. */ + s->data[s->data_ptr++] = (uint8_t) (s->bit_stream << (8 - s->bit_no)); + } + t38_core_send_data(&t->t38x.t38, t->t38x.current_tx_data_type, T38_FIELD_T4_NON_ECM_SIG_END, s->data, s->data_ptr, T38_PACKET_CATEGORY_IMAGE_DATA_END); + s->in_bits += s->bits_absorbed; + s->out_octets += s->data_ptr; + s->data_ptr = 0; +} +/*- End of function --------------------------------------------------------*/ + +static void non_ecm_push(t38_gateway_state_t *t) +{ + t38_gateway_to_t38_state_t *s; + + s = &t->core.to_t38; + if (s->data_ptr) + { + t38_core_send_data(&t->t38x.t38, t->t38x.current_tx_data_type, T38_FIELD_T4_NON_ECM_DATA, s->data, s->data_ptr, T38_PACKET_CATEGORY_IMAGE_DATA); + s->in_bits += s->bits_absorbed; + s->out_octets += s->data_ptr; + s->bits_absorbed = 0; + s->data_ptr = 0; + } + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +static void non_ecm_put_bit(void *user_data, int bit) +{ + t38_gateway_state_t *t; + t38_gateway_to_t38_state_t *s; + + if (bit < 0) + { + non_ecm_rx_status(user_data, bit); + return; + } + t = (t38_gateway_state_t *) user_data; + s = &t->core.to_t38; + + s->in_bits++; + bit &= 1; + s->bit_stream = (s->bit_stream << 1) | bit; + if (++s->bit_no >= 8) + { + s->data[s->data_ptr++] = (uint8_t) s->bit_stream & 0xFF; + if (s->data_ptr >= s->octets_per_data_packet) + non_ecm_push(t); + s->bit_no = 0; + } +} +/*- End of function --------------------------------------------------------*/ + +static void non_ecm_remove_fill_and_put_bit(void *user_data, int bit) +{ + t38_gateway_state_t *t; + t38_gateway_to_t38_state_t *s; + + if (bit < 0) + { + non_ecm_rx_status(user_data, bit); + return; + } + t = (t38_gateway_state_t *) user_data; + s = &t->core.to_t38; + + s->bits_absorbed++; + bit &= 1; + /* Drop any extra zero bits when we already have enough for an EOL symbol. */ + /* The snag here is that if we just look for 11 bits, a line ending with + a code that has trailing zero bits will cause problems. The longest run of + trailing zeros for any code is 3, so we need to look for at least 14 zeros + if we don't want to actually analyse the compressed data in depth. This means + we do not strip every fill bit, but we strip most of them. */ + if ((s->bit_stream & 0x3FFF) == 0 && bit == 0) + { + if (s->bits_absorbed > 2*8*s->octets_per_data_packet) + { + /* We need to pump out what we have, even though we have not accumulated a full + buffer of data. If we don't, we stand to delay rows excessively, so the far + end gateway (assuming the far end is a gateway) cannot play them out. */ + non_ecm_push(t); + } + return; + } + s->bit_stream = (s->bit_stream << 1) | bit; + if (++s->bit_no >= 8) + { + s->data[s->data_ptr++] = (uint8_t) s->bit_stream & 0xFF; + if (s->data_ptr >= s->octets_per_data_packet) + non_ecm_push(t); + s->bit_no = 0; + } +} +/*- End of function --------------------------------------------------------*/ + +static void hdlc_rx_status(hdlc_rx_state_t *t, int status) +{ + t38_gateway_state_t *s; + int category; + + s = (t38_gateway_state_t *) t->frame_user_data; + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC signal status is %s (%d)\n", signal_status_to_str(status), status); + switch (status) + { + case SIG_STATUS_TRAINING_IN_PROGRESS: + announce_training(s); + break; + case SIG_STATUS_TRAINING_FAILED: + break; + case SIG_STATUS_TRAINING_SUCCEEDED: + /* The modem is now trained. */ + s->audio.modems.rx_signal_present = TRUE; + s->audio.modems.rx_trained = TRUE; + /* Behave like HDLC preamble has been announced. */ + t->framing_ok_announced = TRUE; + to_t38_buffer_init(&s->core.to_t38); + break; + case SIG_STATUS_CARRIER_UP: + /* Reset the HDLC receiver. */ + t->raw_bit_stream = 0; + t->len = 0; + t->num_bits = 0; + t->flags_seen = 0; + t->framing_ok_announced = FALSE; + to_t38_buffer_init(&s->core.to_t38); + break; + case SIG_STATUS_CARRIER_DOWN: + if (t->framing_ok_announced) + { + category = (s->t38x.current_tx_data_type == T38_DATA_V21) ? T38_PACKET_CATEGORY_CONTROL_DATA_END : T38_PACKET_CATEGORY_IMAGE_DATA_END; + t38_core_send_data(&s->t38x.t38, s->t38x.current_tx_data_type, T38_FIELD_HDLC_SIG_END, NULL, 0, category); + t38_core_send_indicator(&s->t38x.t38, T38_IND_NO_SIGNAL); + t->framing_ok_announced = FALSE; + } + restart_rx_modem(s); + if (s->core.timed_mode == TIMED_MODE_TCF_PREDICTABLE_MODEM_START_BEGIN) + { + /* If we are doing TCF, we need to announce the fast carrier training very + quickly, to ensure it starts 75+-20ms after the HDLC carrier ends. Waiting until + it trains will be too late. We need to announce the fast modem a fixed time after + the end of the V.21 carrier, in anticipation of its arrival. If we announce it, + and it doesn't arrive, we will worry about that later. */ + s->core.samples_to_timeout = ms_to_samples(75); + s->core.timed_mode = TIMED_MODE_TCF_PREDICTABLE_MODEM_START_PAST_V21_MODEM; + } + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected HDLC special bit - %d!\n", status); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void rx_flag_or_abort(hdlc_rx_state_t *t) +{ + t38_gateway_state_t *s; + t38_gateway_to_t38_state_t *u; + int category; + + s = (t38_gateway_state_t *) t->frame_user_data; + u = &s->core.to_t38; + if ((t->raw_bit_stream & 0x80)) + { + /* Hit HDLC abort */ + t->rx_aborts++; + if (t->flags_seen < t->framing_ok_threshold) + t->flags_seen = 0; + else + t->flags_seen = t->framing_ok_threshold - 1; + /*endif*/ + } + else + { + /* Hit HDLC flag */ + if (t->flags_seen >= t->framing_ok_threshold) + { + category = (s->t38x.current_tx_data_type == T38_DATA_V21) ? T38_PACKET_CATEGORY_CONTROL_DATA : T38_PACKET_CATEGORY_IMAGE_DATA; + if (t->len) + { + /* This is not back-to-back flags */ + if (t->len >= 2) + { + if (u->data_ptr) + { + bit_reverse(u->data, t->buffer + t->len - 2 - u->data_ptr, u->data_ptr); + t38_core_send_data(&s->t38x.t38, s->t38x.current_tx_data_type, T38_FIELD_HDLC_DATA, u->data, u->data_ptr, category); + } + /*endif*/ + if (t->num_bits != 7) + { + t->rx_crc_errors++; + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC frame type %s, misaligned terminating flag at %d\n", t30_frametype(t->buffer[2]), t->len); + /* It seems some boxes may not like us sending a _SIG_END here, and then another + when the carrier actually drops. Lets just send T38_FIELD_HDLC_FCS_OK here. */ + if (t->len > 2) + t38_core_send_data(&s->t38x.t38, s->t38x.current_tx_data_type, T38_FIELD_HDLC_FCS_BAD, NULL, 0, category); + /*endif*/ + } + else if ((u->crc & 0xFFFF) != 0xF0B8) + { + t->rx_crc_errors++; + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC frame type %s, bad CRC at %d\n", t30_frametype(t->buffer[2]), t->len); + /* It seems some boxes may not like us sending a _SIG_END here, and then another + when the carrier actually drops. Lets just send T38_FIELD_HDLC_FCS_OK here. */ + if (t->len > 2) + t38_core_send_data(&s->t38x.t38, s->t38x.current_tx_data_type, T38_FIELD_HDLC_FCS_BAD, NULL, 0, category); + /*endif*/ + } + else + { + t->rx_frames++; + t->rx_bytes += t->len - 2; + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC frame type %s, CRC OK\n", t30_frametype(t->buffer[2])); + if (s->t38x.current_tx_data_type == T38_DATA_V21) + { + monitor_control_messages(s, TRUE, t->buffer, t->len - 2); + if (s->core.real_time_frame_handler) + s->core.real_time_frame_handler(s, s->core.real_time_frame_user_data, TRUE, t->buffer, t->len - 2); + /*endif*/ + } + else + { + /* Make sure we go back to short training if CTC/CTR has kicked us into + long training. Any successful HDLC frame received at a rate other than + V.21 is an adequate indication we should change. */ + s->core.short_train = TRUE; + } + /*endif*/ + /* It seems some boxes may not like us sending a _SIG_END here, and then another + when the carrier actually drops. Lets just send T38_FIELD_HDLC_FCS_OK here. */ + t38_core_send_data(&s->t38x.t38, s->t38x.current_tx_data_type, T38_FIELD_HDLC_FCS_OK, NULL, 0, category); + } + /*endif*/ + } + else + { + /* Frame too short */ + t->rx_length_errors++; + } + /*endif*/ + } + /*endif*/ + } + else + { + /* Check the flags are back-to-back when testing for valid preamble. This + greatly reduces the chances of false preamble detection, and anything + which doesn't send them back-to-back is badly broken. */ + if (t->num_bits != 7) + t->flags_seen = 0; + /*endif*/ + if (++t->flags_seen >= t->framing_ok_threshold && !t->framing_ok_announced) + { + if (s->t38x.current_tx_data_type == T38_DATA_V21) + { + t38_core_send_indicator(&s->t38x.t38, set_slow_packetisation(s)); + s->audio.modems.rx_signal_present = TRUE; + } + /*endif*/ + if (s->t38x.in_progress_rx_indicator == T38_IND_CNG) + set_next_tx_type(s); + /*endif*/ + t->framing_ok_announced = TRUE; + } + /*endif*/ + } + /*endif*/ + } + /*endif*/ + t->len = 0; + t->num_bits = 0; + u->crc = 0xFFFF; + u->data_ptr = 0; + s->t38x.corrupt_current_frame[1] = FALSE; +} +/*- End of function --------------------------------------------------------*/ + +static void t38_hdlc_rx_put_bit(hdlc_rx_state_t *t, int new_bit) +{ + t38_gateway_state_t *s; + t38_gateway_to_t38_state_t *u; + int category; + + if (new_bit < 0) + { + hdlc_rx_status(t, new_bit); + return; + } + /*endif*/ + t->raw_bit_stream = (t->raw_bit_stream << 1) | (new_bit & 1); + if ((t->raw_bit_stream & 0x3F) == 0x3E) + { + /* Its time to either skip a bit, for stuffing, or process a flag or abort */ + if ((t->raw_bit_stream & 0x40)) + rx_flag_or_abort(t); + return; + } + /*endif*/ + t->num_bits++; + if (!t->framing_ok_announced) + return; + /*endif*/ + t->byte_in_progress = (t->byte_in_progress >> 1) | ((t->raw_bit_stream & 0x01) << 7); + if (t->num_bits != 8) + return; + /*endif*/ + t->num_bits = 0; + if (t->len >= (int) sizeof(t->buffer)) + { + /* This is too long. Abandon the frame, and wait for the next flag octet. */ + t->rx_length_errors++; + t->flags_seen = t->framing_ok_threshold - 1; + t->len = 0; + return; + } + /*endif*/ + s = (t38_gateway_state_t *) t->frame_user_data; + u = &s->core.to_t38; + t->buffer[t->len] = (uint8_t) t->byte_in_progress; + /* Calculate the CRC progressively, before we start altering the frame */ + u->crc = crc_itu16_calc(&t->buffer[t->len], 1, u->crc); + /* Make the transmission lag by two octets, so we do not send the CRC, and + do not report the CRC result too late. */ + if (++t->len <= 2) + return; + /*endif*/ + if (s->t38x.current_tx_data_type == T38_DATA_V21) + { + /* The V.21 control messages need to be monitored, and possibly corrupted, to manage the + man-in-the-middle role of T.38 */ + edit_control_messages(s, 1, t->buffer, t->len); + } + if (++u->data_ptr >= u->octets_per_data_packet) + { + bit_reverse(u->data, t->buffer + t->len - 2 - u->data_ptr, u->data_ptr); + category = (s->t38x.current_tx_data_type == T38_DATA_V21) ? T38_PACKET_CATEGORY_CONTROL_DATA : T38_PACKET_CATEGORY_IMAGE_DATA; + t38_core_send_data(&s->t38x.t38, s->t38x.current_tx_data_type, T38_FIELD_HDLC_DATA, u->data, u->data_ptr, category); + /* Since we delay transmission by 2 octets, we should now have sent the last of the data octets when + we have just received the last of the CRC octets. */ + u->data_ptr = 0; + } + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +static int restart_rx_modem(t38_gateway_state_t *s) +{ + put_bit_func_t put_bit_func; + void *put_bit_user_data; + + if (s->core.to_t38.in_bits || s->core.to_t38.out_octets) + { + span_log(&s->logging, + SPAN_LOG_FLOW, + "%d incoming audio bits. %d outgoing T.38 octets\n", + s->core.to_t38.in_bits, + s->core.to_t38.out_octets); + s->core.to_t38.in_bits = 0; + s->core.to_t38.out_octets = 0; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Restart rx modem - modem = %d, short train = %d, ECM = %d\n", s->core.fast_rx_modem, s->core.short_train, s->core.ecm_mode); + + hdlc_rx_init(&(s->audio.modems.hdlc_rx), FALSE, TRUE, HDLC_FRAMING_OK_THRESHOLD, NULL, s); + s->audio.modems.rx_signal_present = FALSE; + s->audio.modems.rx_trained = FALSE; + /* Default to the transmit data being V.21, unless a faster modem pops up trained. */ + s->t38x.current_tx_data_type = T38_DATA_V21; + fsk_rx_init(&(s->audio.modems.v21_rx), &preset_fsk_specs[FSK_V21CH2], FSK_FRAME_MODE_SYNC, (put_bit_func_t) t38_hdlc_rx_put_bit, &(s->audio.modems.hdlc_rx)); +#if 0 + fsk_rx_signal_cutoff(&(s->audio.modems.v21_rx), -45.5f); +#endif + if (s->core.image_data_mode && s->core.ecm_mode) + { + put_bit_func = (put_bit_func_t) t38_hdlc_rx_put_bit; + put_bit_user_data = (void *) &(s->audio.modems.hdlc_rx); + } + else + { + if (s->core.image_data_mode && s->core.to_t38.fill_bit_removal) + put_bit_func = non_ecm_remove_fill_and_put_bit; + else + put_bit_func = non_ecm_put_bit; + put_bit_user_data = (void *) s; + } + /*endif*/ + to_t38_buffer_init(&s->core.to_t38); + s->core.to_t38.octets_per_data_packet = 1; + switch (s->core.fast_rx_modem) + { + case T38_V17_RX: + v17_rx_restart(&s->audio.modems.v17_rx, s->core.fast_bit_rate, s->core.short_train); + v17_rx_set_put_bit(&s->audio.modems.v17_rx, put_bit_func, put_bit_user_data); + set_rx_handler(s, (span_rx_handler_t *) &v17_v21_rx, s); + s->core.fast_rx_active = T38_V17_RX; + break; + case T38_V27TER_RX: + v27ter_rx_restart(&s->audio.modems.v27ter_rx, s->core.fast_bit_rate, FALSE); + v27ter_rx_set_put_bit(&s->audio.modems.v27ter_rx, put_bit_func, put_bit_user_data); + set_rx_handler(s, (span_rx_handler_t *) &v27ter_v21_rx, s); + s->core.fast_rx_active = T38_V27TER_RX; + break; + case T38_V29_RX: + v29_rx_restart(&s->audio.modems.v29_rx, s->core.fast_bit_rate, FALSE); + v29_rx_set_put_bit(&s->audio.modems.v29_rx, put_bit_func, put_bit_user_data); + set_rx_handler(s, (span_rx_handler_t *) &v29_v21_rx, s); + s->core.fast_rx_active = T38_V29_RX; + break; + default: + set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, &(s->audio.modems.v21_rx)); + s->core.fast_rx_active = T38_NONE; + break; + } + /*endswitch*/ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len) +{ + int i; + +#if defined(LOG_FAX_AUDIO) + if (s->audio.modems.audio_rx_log >= 0) + write(s->audio.modems.audio_rx_log, amp, len*sizeof(int16_t)); + /*endif*/ +#endif + if (s->core.samples_to_timeout > 0) + { + if ((s->core.samples_to_timeout -= len) <= 0) + { + switch (s->core.timed_mode) + { + case TIMED_MODE_TCF_PREDICTABLE_MODEM_START_PAST_V21_MODEM: + /* Timed announcement of training, 75ms after the DCS carrier fell. */ + s->core.timed_mode = TIMED_MODE_TCF_PREDICTABLE_MODEM_START_FAST_MODEM_ANNOUNCED; + announce_training(s); + break; + case TIMED_MODE_TCF_PREDICTABLE_MODEM_START_FAST_MODEM_SEEN: + /* Timed announcement of training, 75ms after the DCS carrier fell. */ + /* Use a timeout to ride over TEP, if it is present */ + s->core.samples_to_timeout = ms_to_samples(500); + s->core.timed_mode = TIMED_MODE_TCF_PREDICTABLE_MODEM_START_FAST_MODEM_ANNOUNCED; + announce_training(s); + break; + case TIMED_MODE_TCF_PREDICTABLE_MODEM_START_FAST_MODEM_ANNOUNCED: + s->core.timed_mode = TIMED_MODE_IDLE; + span_log(&s->logging, SPAN_LOG_FLOW, "TEP jamming expired\n"); + break; + case TIMED_MODE_STARTUP: + /* Ensure a no-signal condition goes out the moment the received audio starts */ + t38_core_send_indicator(&s->t38x.t38, T38_IND_NO_SIGNAL); + s->core.timed_mode = TIMED_MODE_IDLE; + break; + } + /*endswitch*/ + } + /*endif*/ + } + /*endif*/ + for (i = 0; i < len; i++) + amp[i] = dc_restore(&(s->audio.modems.dc_restore), amp[i]); + /*endfor*/ + s->audio.modems.rx_handler(s->audio.modems.rx_user_data, amp, len); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t38_gateway_tx(t38_gateway_state_t *s, int16_t amp[], int max_len) +{ + int len; +#if defined(LOG_FAX_AUDIO) + int required_len; + + required_len = max_len; +#endif + if ((len = s->audio.modems.tx_handler(s->audio.modems.tx_user_data, amp, max_len)) < max_len) + { + if (set_next_tx_type(s)) + { + /* Give the new handler a chance to file the remaining buffer space */ + len += s->audio.modems.tx_handler(s->audio.modems.tx_user_data, amp + len, max_len - len); + if (len < max_len) + { + silence_gen_set(&(s->audio.modems.silence_gen), 0); + set_next_tx_type(s); + } + /*endif*/ + } + /*endif*/ + } + /*endif*/ + if (s->audio.modems.transmit_on_idle) + { + /* Pad to the requested length with silence */ + memset(amp + len, 0, (max_len - len)*sizeof(int16_t)); + len = max_len; + } + /*endif*/ +#if defined(LOG_FAX_AUDIO) + if (s->audio.modems.audio_tx_log >= 0) + { + if (len < required_len) + memset(amp + len, 0, (required_len - len)*sizeof(int16_t)); + /*endif*/ + write(s->audio.modems.audio_tx_log, amp, required_len*sizeof(int16_t)); + } + /*endif*/ +#endif + return len; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t38_gateway_get_transfer_statistics(t38_gateway_state_t *s, t38_stats_t *t) +{ + memset(t, 0, sizeof(*t)); + t->bit_rate = s->core.fast_bit_rate; + t->error_correcting_mode = s->core.ecm_mode; + t->pages_transferred = s->core.pages_confirmed; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(t38_core_state_t *) t38_gateway_get_t38_core_state(t38_gateway_state_t *s) +{ + return &s->t38x.t38; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(logging_state_t *) t38_gateway_get_logging_state(t38_gateway_state_t *s) +{ + return &s->logging; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t38_gateway_set_ecm_capability(t38_gateway_state_t *s, int ecm_allowed) +{ + s->core.ecm_allowed = ecm_allowed; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t38_gateway_set_transmit_on_idle(t38_gateway_state_t *s, int transmit_on_idle) +{ + s->audio.modems.transmit_on_idle = transmit_on_idle; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t38_gateway_set_supported_modems(t38_gateway_state_t *s, int supported_modems) +{ + s->core.supported_modems = supported_modems; + if ((s->core.supported_modems & T30_SUPPORT_V17)) + t38_set_fastest_image_data_rate(&s->t38x.t38, 14400); + else if ((s->core.supported_modems & T30_SUPPORT_V29)) + t38_set_fastest_image_data_rate(&s->t38x.t38, 9600); + else + t38_set_fastest_image_data_rate(&s->t38x.t38, 4800); + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t38_gateway_set_nsx_suppression(t38_gateway_state_t *s, + const uint8_t *from_t38, + int from_t38_len, + const uint8_t *from_modem, + int from_modem_len) +{ + s->t38x.suppress_nsx_len[0] = (from_t38_len < 0 || from_t38_len < MAX_NSX_SUPPRESSION) ? (from_t38_len + 3) : 0; + s->t38x.suppress_nsx_len[1] = (from_modem_len < 0 || from_modem_len < MAX_NSX_SUPPRESSION) ? (from_modem_len + 3) : 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t38_gateway_set_tep_mode(t38_gateway_state_t *s, int use_tep) +{ + s->audio.modems.use_tep = use_tep; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t38_gateway_set_fill_bit_removal(t38_gateway_state_t *s, int remove) +{ + s->core.to_t38.fill_bit_removal = remove; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t38_gateway_set_real_time_frame_handler(t38_gateway_state_t *s, + t38_gateway_real_time_frame_handler_t *handler, + void *user_data) +{ + s->core.real_time_frame_handler = handler; + s->core.real_time_frame_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +static int t38_gateway_audio_init(t38_gateway_state_t *s) +{ + fax_modems_init(&s->audio.modems, + FALSE, + NULL, + hdlc_underflow_handler, + non_ecm_put_bit, + t38_non_ecm_buffer_get_bit, + tone_detected, + s); + /* We need to use progressive HDLC transmit, and a special HDLC receiver, which is different + from the other uses of FAX modems. */ + hdlc_tx_init(&s->audio.modems.hdlc_tx, FALSE, 2, TRUE, hdlc_underflow_handler, s); + fsk_rx_set_put_bit(&s->audio.modems.v21_rx, (put_bit_func_t) t38_hdlc_rx_put_bit, &s->audio.modems.hdlc_rx); + /* TODO: Don't use the very low cutoff levels we would like to. We get some quirks if we do. + We need to sort this out. */ + fsk_rx_signal_cutoff(&s->audio.modems.v21_rx, -30.0f); + v29_rx_signal_cutoff(&s->audio.modems.v29_rx, -28.5f); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int t38_gateway_t38_init(t38_gateway_state_t *t, + t38_tx_packet_handler_t *tx_packet_handler, + void *tx_packet_user_data) +{ + t38_gateway_t38_state_t *s; + + s = &t->t38x; + t38_core_init(&s->t38, + process_rx_indicator, + process_rx_data, + process_rx_missing, + (void *) t, + tx_packet_handler, + tx_packet_user_data); + t38_set_redundancy_control(&s->t38, T38_PACKET_CATEGORY_INDICATOR, INDICATOR_TX_COUNT); + t38_set_redundancy_control(&s->t38, T38_PACKET_CATEGORY_CONTROL_DATA, DATA_TX_COUNT); + t38_set_redundancy_control(&s->t38, T38_PACKET_CATEGORY_CONTROL_DATA_END, DATA_END_TX_COUNT); + t38_set_redundancy_control(&s->t38, T38_PACKET_CATEGORY_IMAGE_DATA, DATA_TX_COUNT); + t38_set_redundancy_control(&s->t38, T38_PACKET_CATEGORY_IMAGE_DATA_END, DATA_END_TX_COUNT); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(t38_gateway_state_t *) t38_gateway_init(t38_gateway_state_t *s, + t38_tx_packet_handler_t *tx_packet_handler, + void *tx_packet_user_data) +{ + if (tx_packet_handler == NULL) + return NULL; + /*endif*/ + if (s == NULL) + { + if ((s = (t38_gateway_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + /*endif*/ + } + /*endif*/ + memset(s, 0, sizeof(*s)); + span_log_init(&s->logging, SPAN_LOG_NONE, NULL); + span_log_set_protocol(&s->logging, "T.38G"); + + t38_gateway_audio_init(s); + t38_gateway_t38_init(s, tx_packet_handler, tx_packet_user_data); + + set_rx_active(s, TRUE); + t38_gateway_set_supported_modems(s, T30_SUPPORT_V27TER | T30_SUPPORT_V29); + t38_gateway_set_nsx_suppression(s, (const uint8_t *) "\x00\x00\x00", 3, (const uint8_t *) "\x00\x00\x00", 3); + + s->core.to_t38.octets_per_data_packet = 1; + s->core.ecm_allowed = TRUE; + t38_non_ecm_buffer_init(&s->core.non_ecm_to_modem, FALSE, 0); + restart_rx_modem(s); + s->core.timed_mode = TIMED_MODE_STARTUP; + s->core.samples_to_timeout = 1; +#if defined(LOG_FAX_AUDIO) + { + char buf[100 + 1]; + struct tm *tm; + time_t now; + + time(&now); + tm = localtime(&now); + sprintf(buf, + "/tmp/t38-rx-audio-%p-%02d%02d%02d%02d%02d%02d", + s, + tm->tm_year%100, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + s->audio.modems.audio_rx_log = open(buf, O_CREAT | O_TRUNC | O_WRONLY, 0666); + sprintf(buf, + "/tmp/t38-tx-audio-%p-%02d%02d%02d%02d%02d%02d", + s, + tm->tm_year%100, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + s->audio.modems.audio_tx_log = open(buf, O_CREAT | O_TRUNC | O_WRONLY, 0666); + } +#endif + return s; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t38_gateway_release(t38_gateway_state_t *s) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t38_gateway_free(t38_gateway_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/