diff spandsp-0.0.6pre17/src/t31.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/t31.c	Fri Jun 25 15:50:58 2010 +0200
@@ -0,0 +1,2648 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t31.c - A T.31 compatible class 1 FAX modem interface.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Special thanks to Lee Howard <faxguy@howardsilvan.com>
+ * for his great work debugging and polishing this code.
+ *
+ * Copyright (C) 2004, 2005, 2006, 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: t31.c,v 1.155.4.1 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 <memory.h>
+#include <string.h>
+#include <ctype.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>
+#include <tiffio.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/bit_operations.h"
+#include "spandsp/dc_restore.h"
+#include "spandsp/queue.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.h"
+#include "spandsp/t30_logging.h"
+#include "spandsp/t38_core.h"
+
+#include "spandsp/at_interpreter.h"
+#include "spandsp/fax_modems.h"
+#include "spandsp/t31.h"
+#include "spandsp/t30_fcf.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/t38_core.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/at_interpreter.h"
+#include "spandsp/private/t31.h"
+
+/* Settings suitable for paced transmission over a UDP transport */
+/*! The default number of milliseconds per transmitted IFP when sending bulk T.38 data */
+#define MS_PER_TX_CHUNK                         30
+/*! 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
+/*! The default DTE timeout, in seconds */
+#define DEFAULT_DTE_TIMEOUT                     5
+
+/* Settings suitable for unpaced transmission over a TCP transport */
+#define MAX_OCTETS_PER_UNPACED_CHUNK            300
+
+/* Backstop timeout if reception of packets stops in the middle of a burst */
+#define MID_RX_TIMEOUT                          15000
+
+#define HDLC_FRAMING_OK_THRESHOLD               5
+
+typedef const char *(*at_cmd_service_t)(t31_state_t *s, const char *cmd);
+
+enum
+{
+    ETX = 0x03,
+    DLE = 0x10,
+    SUB = 0x1A
+};
+
+enum
+{
+    DISBIT1 = 0x01,
+    DISBIT2 = 0x02,
+    DISBIT3 = 0x04,
+    DISBIT4 = 0x08,
+    DISBIT5 = 0x10,
+    DISBIT6 = 0x20,
+    DISBIT7 = 0x40,
+    DISBIT8 = 0x80
+};
+
+enum
+{
+    T38_CHUNKING_MERGE_FCS_WITH_DATA    = 0x0001,
+    T38_CHUNKING_WHOLE_FRAMES           = 0x0002,
+    T38_CHUNKING_ALLOW_TEP_TIME         = 0x0004
+};
+
+enum
+{
+    T38_TIMED_STEP_NONE = 0,
+    T38_TIMED_STEP_NON_ECM_MODEM = 0x10,
+    T38_TIMED_STEP_NON_ECM_MODEM_2 = 0x11,
+    T38_TIMED_STEP_NON_ECM_MODEM_3 = 0x12,
+    T38_TIMED_STEP_NON_ECM_MODEM_4 = 0x13,
+    T38_TIMED_STEP_NON_ECM_MODEM_5 = 0x14,
+    T38_TIMED_STEP_HDLC_MODEM = 0x20,
+    T38_TIMED_STEP_HDLC_MODEM_2 = 0x21,
+    T38_TIMED_STEP_HDLC_MODEM_3 = 0x22,
+    T38_TIMED_STEP_HDLC_MODEM_4 = 0x23,
+    T38_TIMED_STEP_HDLC_MODEM_5 = 0x24,
+    T38_TIMED_STEP_FAKE_HDLC_MODEM = 0x30,
+    T38_TIMED_STEP_FAKE_HDLC_MODEM_2 = 0x31,
+    T38_TIMED_STEP_FAKE_HDLC_MODEM_3 = 0x32,
+    T38_TIMED_STEP_FAKE_HDLC_MODEM_4 = 0x33,
+    T38_TIMED_STEP_FAKE_HDLC_MODEM_5 = 0x34,
+    T38_TIMED_STEP_CED = 0x40,
+    T38_TIMED_STEP_CED_2 = 0x41,
+    T38_TIMED_STEP_CED_3 = 0x42,
+    T38_TIMED_STEP_CNG = 0x50,
+    T38_TIMED_STEP_CNG_2 = 0x51,
+    T38_TIMED_STEP_PAUSE = 0x60
+};
+
+static int restart_modem(t31_state_t *s, int new_modem);
+static void hdlc_accept_frame(void *user_data, const uint8_t *msg, int len, int ok);
+static void set_rx_handler(t31_state_t *s, span_rx_handler_t *rx_handler, span_rx_fillin_handler_t *fillin_handler, void *user_data);
+static void set_tx_handler(t31_state_t *s, span_tx_handler_t *handler, void *user_data);
+static void set_next_tx_handler(t31_state_t *s, span_tx_handler_t *handler, void *user_data);
+static int v17_v21_rx(void *user_data, const int16_t amp[], int len);
+static int v17_v21_rx_fillin(void *user_data, int len);
+static int v27ter_v21_rx(void *user_data, const int16_t amp[], int len);
+static int v27ter_v21_rx_fillin(void *user_data, int len);
+static int v29_v21_rx(void *user_data, const int16_t amp[], int len);
+static int v29_v21_rx_fillin(void *user_data, int len);
+static int silence_rx(void *user_data, const int16_t amp[], int len);
+static int cng_rx(void *user_data, const int16_t amp[], int len);
+static void non_ecm_put_bit(void *user_data, int bit);
+static void non_ecm_put_chunk(void *user_data, const uint8_t buf[], int len);
+static int non_ecm_get_chunk(void *user_data, uint8_t buf[], int len);
+static void non_ecm_rx_status(void *user_data, int status);
+static void hdlc_rx_status(void *user_data, int status);
+
+static __inline__ void t31_set_at_rx_mode(t31_state_t *s, int new_mode)
+{
+    s->at_state.at_rx_mode = new_mode;
+}
+/*- End of function --------------------------------------------------------*/
+
+#if 0
+static void monitor_control_messages(t31_state_t *s, const uint8_t *buf, int len)
+{
+    /* 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*/
+    switch (buf[2])
+    {
+    case T30_DCS:
+    case T30_DCS | 1:
+        /* We need to know if ECM is about to be used, so we can fake HDLC stuff. */
+        s->t38_fe.ecm_mode = (len >= 7)  &&  (buf[6] & DISBIT3);
+        break;
+    default:
+        break;
+    }
+    /*endswitch*/
+}
+/*- End of function --------------------------------------------------------*/
+#endif
+
+static void front_end_status(t31_state_t *s, int status)
+{
+    span_log(&s->logging, SPAN_LOG_FLOW, "Front end status %d\n", status);
+    switch (status)
+    {
+    case T30_FRONT_END_SEND_STEP_COMPLETE:
+        switch (s->modem)
+        {
+        case FAX_MODEM_SILENCE_TX:
+            s->modem = FAX_MODEM_NONE;
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK);
+            if (s->at_state.do_hangup)
+            {
+                at_modem_control(&s->at_state, AT_MODEM_CONTROL_HANGUP, NULL);
+                t31_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+                s->at_state.do_hangup = FALSE;
+            }
+            else
+            {
+                t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+            }
+            break;
+        case FAX_MODEM_CED_TONE:
+            /* Go directly to V.21/HDLC transmit. */
+            s->modem = FAX_MODEM_NONE;
+            restart_modem(s, FAX_MODEM_V21_TX);
+            t31_set_at_rx_mode(s, AT_MODE_HDLC);
+            break;
+        case FAX_MODEM_V21_TX:
+        case FAX_MODEM_V17_TX:
+        case FAX_MODEM_V27TER_TX:
+        case FAX_MODEM_V29_TX:
+            s->modem = FAX_MODEM_NONE;
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK);
+            t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+            restart_modem(s, FAX_MODEM_SILENCE_TX);
+            break;
+        }
+        break;
+    case T30_FRONT_END_RECEIVE_COMPLETE:
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int extra_bits_in_stuffed_frame(const uint8_t buf[], int len)
+{
+    int bitstream;
+    int ones;
+    int stuffed;
+    int i;
+    int j;
+    
+    bitstream = 0;
+    ones = 0;
+    stuffed = 0;
+    /* We should really append the CRC, and include the stuffed bits for that, to get
+       the exact number of bits in the frame. */
+    //len = crc_itu16_append(buf, len);
+    for (i = 0;  i < len;  i++)
+    {
+        bitstream = buf[i];
+        for (j = 0;  j < 8;  j++)
+        {
+            if ((bitstream & 1))
+            {
+                if (++ones >= 5)
+                {
+                    ones = 0;
+                    stuffed++;
+                }
+            }
+            else
+            {
+                ones = 0;
+            }
+            bitstream >>= 1;
+        }
+    }
+    /* The total length of the frame is:
+          the number of bits in the body
+        + the number of additional bits in the body due to stuffing
+        + the number of bits in the CRC
+        + the number of additional bits in the CRC due to stuffing
+        + 16 bits for the two terminating flag octets.
+       Lets just allow 3 bits for the CRC, which is the worst case. It
+       avoids calculating the real CRC, and the worst it can do is cause
+       a flag octet's worth of additional output.
+    */
+    return stuffed + 16 + 3 + 16;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int process_rx_missing(t38_core_state_t *t, void *user_data, int rx_seq_no, int expected_seq_no)
+{
+    t31_state_t *s;
+    
+    s = (t31_state_t *) user_data;
+    s->t38_fe.rx_data_missing = TRUE;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int process_rx_indicator(t38_core_state_t *t, void *user_data, int indicator)
+{
+    t31_state_t *s;
+    t31_t38_front_end_state_t *fe;
+
+    s = (t31_state_t *) user_data;
+    fe = &s->t38_fe;
+
+    if (t->current_rx_indicator == indicator)
+    {
+        /* This is probably due to the far end repeating itself, or slipping
+           preamble messages in between HDLC frames. T.38/V.1.3 tells us to
+           ignore it. Its harmless. */
+        return 0;
+    }
+    /* In termination mode we don't care very much about indicators telling us training
+       is starting. We only care about V.21 preamble starting, for timeout control, and
+       the actual data. */
+    switch (indicator)
+    {
+    case T38_IND_NO_SIGNAL:
+        if (t->current_rx_indicator == T38_IND_V21_PREAMBLE
+            &&
+            (fe->current_rx_type == T30_MODEM_V21  ||  fe->current_rx_type == T30_MODEM_CNG))
+        {
+            hdlc_rx_status(s, SIG_STATUS_CARRIER_DOWN);
+        }
+        fe->timeout_rx_samples = 0;
+        front_end_status(s, T30_FRONT_END_SIGNAL_ABSENT);
+        break;
+    case T38_IND_CNG:
+        front_end_status(s, T30_FRONT_END_CNG_PRESENT);
+        break;
+    case T38_IND_CED:
+        front_end_status(s, T30_FRONT_END_CED_PRESENT);
+        break;
+    case T38_IND_V21_PREAMBLE:
+        /* Some T.38 implementations insert these preamble indicators between HDLC frames, so
+           we need to be tolerant of that. */
+        fe->timeout_rx_samples = fe->samples + ms_to_samples(MID_RX_TIMEOUT);
+        front_end_status(s, T30_FRONT_END_SIGNAL_PRESENT);
+        break;
+    case T38_IND_V27TER_2400_TRAINING:
+    case T38_IND_V27TER_4800_TRAINING:
+    case T38_IND_V29_7200_TRAINING:
+    case T38_IND_V29_9600_TRAINING:
+    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:
+    case T38_IND_V33_12000_TRAINING:
+    case T38_IND_V33_14400_TRAINING:
+        /* We really don't care what kind of modem is delivering the following image data.
+           We only care that some kind of fast modem signal is coming next. */
+        fe->timeout_rx_samples = fe->samples + ms_to_samples(MID_RX_TIMEOUT);
+        front_end_status(s, T30_FRONT_END_SIGNAL_PRESENT);
+        break;
+    case T38_IND_V8_ANSAM:
+    case T38_IND_V8_SIGNAL:
+    case T38_IND_V34_CNTL_CHANNEL_1200:
+    case T38_IND_V34_PRI_CHANNEL:
+    case T38_IND_V34_CC_RETRAIN:
+        /* V.34 support is a work in progress. */
+        front_end_status(s, T30_FRONT_END_SIGNAL_PRESENT);
+        break;
+    default:
+        front_end_status(s, T30_FRONT_END_SIGNAL_ABSENT);
+        break;
+    }
+    fe->hdlc_rx.len = 0;
+    fe->rx_data_missing = FALSE;
+    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)
+{
+    t31_state_t *s;
+    t31_t38_front_end_state_t *fe;
+#if defined(_MSC_VER)
+    uint8_t *buf2 = (uint8_t *) _alloca(len);
+#else
+    uint8_t buf2[len];
+#endif
+
+    s = (t31_state_t *) user_data;
+    fe = &s->t38_fe;
+#if 0
+    /* In termination mode we don't care very much what the data type is. */
+    switch (data_type)
+    {
+    case T38_DATA_V21:
+    case T38_DATA_V27TER_2400:
+    case T38_DATA_V27TER_4800:
+    case T38_DATA_V29_7200:
+    case T38_DATA_V29_9600:
+    case T38_DATA_V17_7200:
+    case T38_DATA_V17_9600:
+    case T38_DATA_V17_12000:
+    case T38_DATA_V17_14400:
+    case T38_DATA_V8:
+    case T38_DATA_V34_PRI_RATE:
+    case T38_DATA_V34_CC_1200:
+    case T38_DATA_V34_PRI_CH:
+    case T38_DATA_V33_12000:
+    case T38_DATA_V33_14400:
+    default:
+        break;
+    }
+#endif
+    switch (field_type)
+    {
+    case T38_FIELD_HDLC_DATA:
+        if (fe->timeout_rx_samples == 0)
+        {
+            /* HDLC can just start without any signal indicator on some platforms, even when
+               there is zero packet lost. Nasty, but true. Its a good idea to be tolerant of
+               loss, though, so accepting a sudden start of HDLC data is the right thing to do. */
+            fe->timeout_rx_samples = fe->samples + ms_to_samples(MID_RX_TIMEOUT);
+            front_end_status(s, T30_FRONT_END_SIGNAL_PRESENT);
+            /* 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)
+                fe->rx_data_missing = TRUE;
+        }
+        if (len > 0  &&  fe->hdlc_rx.len + len <= T31_T38_MAX_HDLC_LEN)
+        {
+            bit_reverse(fe->hdlc_rx.buf + fe->hdlc_rx.len, buf, len);
+            fe->hdlc_rx.len += len;
+        }
+        fe->timeout_rx_samples = fe->samples + ms_to_samples(MID_RX_TIMEOUT);
+        break;
+    case T38_FIELD_HDLC_FCS_OK:
+        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. */
+        }
+        /* 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, "Type %s - CRC OK (%s)\n", (fe->hdlc_rx.len >= 3)  ?  t30_frametype(fe->hdlc_rx.buf[2])  :  "???", (fe->rx_data_missing)  ?  "missing octets"  :  "clean");
+            crc_itu16_append(fe->hdlc_rx.buf, fe->hdlc_rx.len);
+            hdlc_accept_frame(s, fe->hdlc_rx.buf, fe->hdlc_rx.len, !fe->rx_data_missing);
+        }
+        fe->hdlc_rx.len = 0;
+        fe->rx_data_missing = FALSE;
+        fe->timeout_rx_samples = fe->samples + ms_to_samples(MID_RX_TIMEOUT);
+        break;
+    case T38_FIELD_HDLC_FCS_BAD:
+        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. */
+        }
+        /* 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, "Type %s - CRC bad (%s)\n", (fe->hdlc_rx.len >= 3)  ?  t30_frametype(fe->hdlc_rx.buf[2])  :  "???", (fe->rx_data_missing)  ?  "missing octets"  :  "clean");
+            hdlc_accept_frame(s, fe->hdlc_rx.buf, fe->hdlc_rx.len, FALSE);
+        }
+        fe->hdlc_rx.len = 0;
+        fe->rx_data_missing = FALSE;
+        fe->timeout_rx_samples = fe->samples + ms_to_samples(MID_RX_TIMEOUT);
+        break;
+    case T38_FIELD_HDLC_FCS_OK_SIG_END:
+        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. */
+        }
+        /* 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, "Type %s - CRC OK, sig end (%s)\n", (fe->hdlc_rx.len >= 3)  ?  t30_frametype(fe->hdlc_rx.buf[2])  :  "???", (fe->rx_data_missing)  ?  "missing octets"  :  "clean");
+            crc_itu16_append(fe->hdlc_rx.buf, fe->hdlc_rx.len);
+            hdlc_accept_frame(s, fe->hdlc_rx.buf, fe->hdlc_rx.len, !fe->rx_data_missing);
+            hdlc_rx_status(s, SIG_STATUS_CARRIER_DOWN);
+        }
+        fe->hdlc_rx.len = 0;
+        fe->rx_data_missing = FALSE;
+        fe->timeout_rx_samples = 0;
+        break;
+    case T38_FIELD_HDLC_FCS_BAD_SIG_END:
+        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. */
+        }
+        /* 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, "Type %s - CRC bad, sig end (%s)\n", (fe->hdlc_rx.len >= 3)  ?  t30_frametype(fe->hdlc_rx.buf[2])  :  "???", (fe->rx_data_missing)  ?  "missing octets"  :  "clean");
+            hdlc_accept_frame(s, fe->hdlc_rx.buf, fe->hdlc_rx.len, FALSE);
+            hdlc_rx_status(s, SIG_STATUS_CARRIER_DOWN);
+        }
+        fe->hdlc_rx.len = 0;
+        fe->rx_data_missing = FALSE;
+        fe->timeout_rx_samples = 0;
+        break;
+    case T38_FIELD_HDLC_SIG_END:
+        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. */
+        }
+        /* 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)
+        {
+            /* 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. We use the generic receive complete
+                           indication, rather than the specific HDLC carrier down. */
+            /* 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. */
+            fe->hdlc_rx.len = 0;
+            fe->rx_data_missing = FALSE;
+            fe->timeout_rx_samples = 0;
+            hdlc_rx_status(s, SIG_STATUS_CARRIER_DOWN);
+        }
+        break;
+    case T38_FIELD_T4_NON_ECM_DATA:
+        if (!s->at_state.rx_signal_present)
+        {
+            non_ecm_rx_status(s, SIG_STATUS_TRAINING_SUCCEEDED);
+            s->at_state.rx_signal_present = TRUE;
+        }
+        if (len > 0)
+        {
+            bit_reverse(buf2, buf, len);
+            non_ecm_put_chunk(s, buf, len);
+        }
+        fe->timeout_rx_samples = fe->samples + ms_to_samples(MID_RX_TIMEOUT);
+        break;
+    case T38_FIELD_T4_NON_ECM_SIG_END:
+        /* 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)
+        {
+            if (len > 0)
+            {
+                if (!s->at_state.rx_signal_present)
+                {
+                    non_ecm_rx_status(s, SIG_STATUS_TRAINING_SUCCEEDED);
+                    s->at_state.rx_signal_present = TRUE;
+                }
+                bit_reverse(buf2, buf, len);
+                non_ecm_put_chunk(s, buf, len);
+            }
+            /* 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, so we use the generic receive complete
+                           indication, rather than the specific non-ECM carrier down. */
+            non_ecm_rx_status(s, SIG_STATUS_CARRIER_DOWN);
+        }
+        s->at_state.rx_signal_present = FALSE;
+        fe->timeout_rx_samples = 0;
+        break;
+    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);
+        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);
+        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);
+        break;
+    case T38_FIELD_V34RATE:
+        if (len >= 3)
+        {
+            fe->t38.v34_rate = t38_v34rate_to_bps(buf, len);
+            span_log(&s->logging, SPAN_LOG_FLOW, "V.34 rate %d bps\n", fe->t38.v34_rate);
+        }
+        else
+        {
+            span_log(&s->logging, SPAN_LOG_FLOW, "Bad length for V34rate message - %d\n", len);
+        }
+        break;
+    default:
+        break;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void send_hdlc(void *user_data, const uint8_t *msg, int len)
+{
+    t31_state_t *s;
+
+    s = (t31_state_t *) user_data;
+    if (len <= 0)
+    {
+        s->hdlc_tx.len = -1;
+    }
+    else
+    {
+        s->t38_fe.hdlc_tx.extra_bits = extra_bits_in_stuffed_frame(msg, len);
+        bit_reverse(s->hdlc_tx.buf, msg, len);
+        s->hdlc_tx.len = len;
+        s->hdlc_tx.ptr = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ int bits_to_us(t31_state_t *s, int bits)
+{
+    if (s->t38_fe.ms_per_tx_chunk == 0  ||  s->t38_fe.tx_bit_rate == 0)
+        return 0;
+    return bits*1000000/s->t38_fe.tx_bit_rate;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void set_octets_per_data_packet(t31_state_t *s, int bit_rate)
+{
+    s->t38_fe.tx_bit_rate = bit_rate;
+    if (s->t38_fe.ms_per_tx_chunk)
+    {
+        s->t38_fe.octets_per_data_packet = s->t38_fe.ms_per_tx_chunk*bit_rate/(8*1000);
+        /* Make sure we have a positive number (i.e. we didn't truncate to zero). */
+        if (s->t38_fe.octets_per_data_packet < 1)
+            s->t38_fe.octets_per_data_packet = 1;
+    }
+    else
+    {
+        s->t38_fe.octets_per_data_packet = MAX_OCTETS_PER_UNPACED_CHUNK;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int stream_non_ecm(t31_state_t *s)
+{
+    t31_t38_front_end_state_t *fe;
+    uint8_t buf[MAX_OCTETS_PER_UNPACED_CHUNK + 50];
+    int delay;
+    int len;
+
+    fe = &s->t38_fe;
+    for (delay = 0;  delay == 0;  )
+    {
+        switch (fe->timed_step)
+        {
+        case T38_TIMED_STEP_NON_ECM_MODEM:
+            /* Create a 75ms silence */
+            if (fe->t38.current_tx_indicator != T38_IND_NO_SIGNAL)
+                delay = t38_core_send_indicator(&fe->t38, T38_IND_NO_SIGNAL);
+            fe->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_2;
+            fe->next_tx_samples = fe->samples;
+            break;
+        case T38_TIMED_STEP_NON_ECM_MODEM_2:
+            /* Switch on a fast modem, and give the training time to complete */
+            delay = t38_core_send_indicator(&fe->t38, fe->next_tx_indicator);
+            fe->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_3;
+            break;
+        case T38_TIMED_STEP_NON_ECM_MODEM_3:
+            /* Send a chunk of non-ECM image data */
+            /* T.38 says it is OK to send the last of the non-ECM data in the signal end message.
+               However, I think the early versions of T.38 said the signal end message should not
+               contain data. Hopefully, following the current spec will not cause compatibility
+               issues. */
+            len = non_ecm_get_chunk(s, buf, fe->octets_per_data_packet);
+            if (len > 0)
+                bit_reverse(buf, buf, len);
+            if (len < fe->octets_per_data_packet)
+            {
+                /* That's the end of the image data. */
+                if (s->t38_fe.ms_per_tx_chunk)
+                {
+                    /* Pad the end of the data with some zeros. If we just stop abruptly
+                       at the end of the EOLs, some ATAs fail to clean up properly before
+                       shutting down their transmit modem, and the last few rows of the image
+                       are lost or corrupted. Simply delaying the no-signal message does not
+                       help for all implentations. It is usually ignored, which is probably
+                       the right thing to do after receiving a message saying the signal has
+                       ended. */
+                    memset(buf + len, 0, fe->octets_per_data_packet - len);
+                    fe->non_ecm_trailer_bytes = 3*fe->octets_per_data_packet + len;
+                    len = fe->octets_per_data_packet;
+                    fe->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_4;
+                }
+                else
+                {
+                    /* If we are sending quickly there seems no point in doing any padding */
+                    t38_core_send_data(&fe->t38, fe->current_tx_data_type, T38_FIELD_T4_NON_ECM_SIG_END, buf, len, T38_PACKET_CATEGORY_IMAGE_DATA_END);
+                    fe->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_5;
+                    delay = 0;
+                }
+            }
+            t38_core_send_data(&fe->t38, fe->current_tx_data_type, T38_FIELD_T4_NON_ECM_DATA, buf, len, T38_PACKET_CATEGORY_IMAGE_DATA);
+            delay = bits_to_us(s, 8*len);
+            break;
+        case T38_TIMED_STEP_NON_ECM_MODEM_4:
+            /* Send padding */
+            len = fe->octets_per_data_packet;
+            fe->non_ecm_trailer_bytes -= len;
+            if (fe->non_ecm_trailer_bytes <= 0)
+            {
+                len += fe->non_ecm_trailer_bytes;
+                memset(buf, 0, len);
+                t38_core_send_data(&fe->t38, fe->current_tx_data_type, T38_FIELD_T4_NON_ECM_SIG_END, buf, len, T38_PACKET_CATEGORY_IMAGE_DATA_END);
+                fe->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_5;
+                /* Allow a bit more time than the data will take to play out, to ensure the far ATA does not
+                   cut things short. */
+                delay = bits_to_us(s, 8*len);
+                if (s->t38_fe.ms_per_tx_chunk)
+                    delay += 60000;
+                front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+                break;
+            }
+            memset(buf, 0, len);
+            t38_core_send_data(&fe->t38, fe->current_tx_data_type, T38_FIELD_T4_NON_ECM_DATA, buf, len, T38_PACKET_CATEGORY_IMAGE_DATA);
+            delay = bits_to_us(s, 8*len);
+            break;
+        case T38_TIMED_STEP_NON_ECM_MODEM_5:
+            /* This should not be needed, since the message above indicates the end of the signal, but it
+               seems like it can improve compatibility with quirky implementations. */
+            delay = t38_core_send_indicator(&fe->t38, T38_IND_NO_SIGNAL);
+            fe->timed_step = T38_TIMED_STEP_NONE;
+            return delay;
+        }
+    }
+    return delay;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int stream_hdlc(t31_state_t *s)
+{
+    t31_t38_front_end_state_t *fe;
+    uint8_t buf[MAX_OCTETS_PER_UNPACED_CHUNK + 50];
+    t38_data_field_t data_fields[2];
+    int previous;
+    int delay;
+    int i;
+    int category;
+
+    fe = &s->t38_fe;
+    for (delay = 0;  delay == 0;  )
+    {
+        switch (fe->timed_step)
+        {
+        case T38_TIMED_STEP_HDLC_MODEM:
+            /* Create a 75ms silence */
+            if (fe->t38.current_tx_indicator != T38_IND_NO_SIGNAL)
+                delay = t38_core_send_indicator(&fe->t38, T38_IND_NO_SIGNAL);
+            fe->timed_step = T38_TIMED_STEP_HDLC_MODEM_2;
+            fe->next_tx_samples = fe->samples + ms_to_samples(75);
+            break;
+        case T38_TIMED_STEP_HDLC_MODEM_2:
+            /* Send HDLC preambling */
+            delay = t38_core_send_indicator(&fe->t38, fe->next_tx_indicator);
+            delay += t38_core_send_flags_delay(&fe->t38, fe->next_tx_indicator);
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+            fe->timed_step = T38_TIMED_STEP_HDLC_MODEM_3;
+            break;
+        case T38_TIMED_STEP_HDLC_MODEM_3:
+            /* Send a chunk of HDLC data */
+            if (s->hdlc_tx.len == 0)
+            {
+                /* We don't have a frame ready yet, so wait a little */
+                delay = MS_PER_TX_CHUNK*1000;
+                break;
+            }
+            i = s->hdlc_tx.len - s->hdlc_tx.ptr;
+            if (fe->octets_per_data_packet >= i)
+            {
+                /* The last part of an HDLC frame */
+                if (fe->chunking_modes & T38_CHUNKING_MERGE_FCS_WITH_DATA)
+                {
+                    /* Copy the data, as we might be about to refill the buffer it is in */
+                    memcpy(buf, &s->hdlc_tx.buf[s->hdlc_tx.ptr], i);
+                    data_fields[0].field_type = T38_FIELD_HDLC_DATA;
+                    data_fields[0].field = buf;
+                    data_fields[0].field_len = i;
+
+                    /* Now see about the next HDLC frame. This will tell us whether to send FCS_OK or FCS_OK_SIG_END */
+                    previous = fe->current_tx_data_type;
+                    s->hdlc_tx.ptr = 0;
+                    s->hdlc_tx.len = 0;
+                    front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+                    if (s->hdlc_tx.final)
+                    {
+                        data_fields[1].field_type = T38_FIELD_HDLC_FCS_OK_SIG_END;
+                        data_fields[1].field = NULL;
+                        data_fields[1].field_len = 0;
+                        category = (s->t38_fe.current_tx_data_type == T38_DATA_V21)  ?  T38_PACKET_CATEGORY_CONTROL_DATA_END  :  T38_PACKET_CATEGORY_IMAGE_DATA_END;
+                        t38_core_send_data_multi_field(&fe->t38, fe->current_tx_data_type, data_fields, 2, category);
+                        fe->timed_step = T38_TIMED_STEP_HDLC_MODEM_5;
+                        /* We add a bit of extra time here, as with some implementations
+                           the carrier falling too abruptly causes data loss. */
+                        delay = bits_to_us(s, i*8 + fe->hdlc_tx.extra_bits);
+                        if (s->t38_fe.ms_per_tx_chunk)
+                            delay += 100000;
+                        at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK);
+                        t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+                    }
+                    else
+                    {
+                        data_fields[1].field_type = T38_FIELD_HDLC_FCS_OK;
+                        data_fields[1].field = NULL;
+                        data_fields[1].field_len = 0;
+                        category = (s->t38_fe.current_tx_data_type == T38_DATA_V21)  ?  T38_PACKET_CATEGORY_CONTROL_DATA  :  T38_PACKET_CATEGORY_IMAGE_DATA;
+                        t38_core_send_data_multi_field(&fe->t38, fe->current_tx_data_type, data_fields, 2, category);
+                        fe->timed_step = T38_TIMED_STEP_HDLC_MODEM_3;
+                        delay = bits_to_us(s, i*8 + fe->hdlc_tx.extra_bits);
+                        at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+                    }
+                    break;
+                }
+                category = (s->t38_fe.current_tx_data_type == T38_DATA_V21)  ?  T38_PACKET_CATEGORY_CONTROL_DATA  :  T38_PACKET_CATEGORY_IMAGE_DATA;
+                t38_core_send_data(&fe->t38, fe->current_tx_data_type, T38_FIELD_HDLC_DATA, &s->hdlc_tx.buf[s->hdlc_tx.ptr], i, category);
+                fe->timed_step = T38_TIMED_STEP_HDLC_MODEM_4;
+            }
+            else
+            {
+                i = fe->octets_per_data_packet;
+                category = (s->t38_fe.current_tx_data_type == T38_DATA_V21)  ?  T38_PACKET_CATEGORY_CONTROL_DATA  :  T38_PACKET_CATEGORY_IMAGE_DATA;
+                t38_core_send_data(&fe->t38, fe->current_tx_data_type, T38_FIELD_HDLC_DATA, &s->hdlc_tx.buf[s->hdlc_tx.ptr], i, category);
+                s->hdlc_tx.ptr += i;
+            }
+            delay = bits_to_us(s, i*8);
+            break;
+        case T38_TIMED_STEP_HDLC_MODEM_4:
+            /* End of HDLC frame */
+            previous = fe->current_tx_data_type;
+            s->hdlc_tx.ptr = 0;
+            s->hdlc_tx.len = 0;
+            if (s->hdlc_tx.final)
+            {
+                /* End of transmission */
+                s->hdlc_tx.len = 0;
+                s->hdlc_tx.final = FALSE;
+                category = (s->t38_fe.current_tx_data_type == T38_DATA_V21)  ?  T38_PACKET_CATEGORY_CONTROL_DATA  :  T38_PACKET_CATEGORY_IMAGE_DATA;
+                t38_core_send_data(&fe->t38, previous, T38_FIELD_HDLC_FCS_OK, NULL, 0, category);
+                fe->timed_step = T38_TIMED_STEP_HDLC_MODEM_5;
+                /* We add a bit of extra time here, as with some implementations
+                   the carrier falling too abruptly causes data loss. */
+                delay = bits_to_us(s, fe->hdlc_tx.extra_bits);
+                if (s->t38_fe.ms_per_tx_chunk)
+                    delay += 100000;
+                front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+                break;
+            }
+            /* Finish the current frame off, and prepare for the next one. */
+            category = (s->t38_fe.current_tx_data_type == T38_DATA_V21)  ?  T38_PACKET_CATEGORY_CONTROL_DATA  :  T38_PACKET_CATEGORY_IMAGE_DATA;
+            t38_core_send_data(&fe->t38, previous, T38_FIELD_HDLC_FCS_OK, NULL, 0, category);
+            fe->timed_step = T38_TIMED_STEP_HDLC_MODEM_3;
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+            /* We should now wait enough time for everything to clear through an analogue modem at the far end. */
+            delay = bits_to_us(s, fe->hdlc_tx.extra_bits);
+            if (s->hdlc_tx.len == 0)
+                span_log(&s->logging, SPAN_LOG_FLOW, "No new frame or end transmission condition.\n");
+            break;
+        case T38_TIMED_STEP_HDLC_MODEM_5:
+            /* Note that some boxes do not like us sending a T38_FIELD_HDLC_SIG_END at this point.
+               A T38_IND_NO_SIGNAL should always be OK. */
+            category = (s->t38_fe.current_tx_data_type == T38_DATA_V21)  ?  T38_PACKET_CATEGORY_CONTROL_DATA_END  :  T38_PACKET_CATEGORY_IMAGE_DATA_END;
+            t38_core_send_data(&fe->t38, fe->current_tx_data_type, T38_FIELD_HDLC_SIG_END, NULL, 0, category);
+            delay = t38_core_send_indicator(&fe->t38, T38_IND_NO_SIGNAL);
+            fe->timed_step = T38_TIMED_STEP_NONE;
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK);
+            t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+            return 0;
+        }
+    }
+    return delay;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int stream_ced(t31_state_t *s)
+{
+    t31_t38_front_end_state_t *fe;
+    int delay;
+
+    fe = &s->t38_fe;
+    for (delay = 0;  delay == 0;  )
+    {
+        switch (fe->timed_step)
+        {
+        case T38_TIMED_STEP_CED:
+            /* It seems common practice to start with a no signal indicator, though
+               this is not a specified requirement. Since we should be sending 200ms
+               of silence, starting the delay with a no signal indication makes sense.
+               We do need a 200ms delay, as that is a specification requirement. */
+            fe->timed_step = T38_TIMED_STEP_CED_2;
+            delay = t38_core_send_indicator(&fe->t38, T38_IND_NO_SIGNAL);
+            delay = 200000;
+            fe->next_tx_samples = fe->samples;
+            break;
+        case T38_TIMED_STEP_CED_2:
+            /* Initial 200ms delay over. Send the CED indicator */
+            fe->timed_step = T38_TIMED_STEP_CED_3;
+            delay = t38_core_send_indicator(&fe->t38, T38_IND_CED);
+            fe->current_tx_data_type = T38_DATA_NONE;
+            break;
+        case T38_TIMED_STEP_CED_3:
+            /* End of CED */
+            fe->timed_step = T38_TIMED_STEP_NONE;
+            front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+            return 0;
+        }
+    }
+    return delay;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int stream_cng(t31_state_t *s)
+{
+    t31_t38_front_end_state_t *fe;
+    int delay;
+
+    fe = &s->t38_fe;
+    for (delay = 0;  delay == 0;  )
+    {
+        switch (fe->timed_step)
+        {
+        case T38_TIMED_STEP_CNG:
+            /* It seems common practice to start with a no signal indicator, though
+               this is not a specified requirement of the T.38 spec. Since we should
+               be sending 200ms of silence, according to T.30, starting that delay with
+               a no signal indication makes sense. */
+            fe->timed_step = T38_TIMED_STEP_CNG_2;
+            delay = t38_core_send_indicator(&fe->t38, T38_IND_NO_SIGNAL);
+            delay = 200000;
+            fe->next_tx_samples = fe->samples;
+            break;
+        case T38_TIMED_STEP_CNG_2:
+            /* Initial short delay over. Send the CNG indicator. CNG persists until something
+               coming the other way interrupts it, or a long timeout controlled by the T.30 engine
+               expires. */
+            fe->timed_step = T38_TIMED_STEP_NONE;
+            delay = t38_core_send_indicator(&fe->t38, T38_IND_CNG);
+            fe->current_tx_data_type = T38_DATA_NONE;
+            return delay;
+        }
+    }
+    return delay;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t31_t38_send_timeout(t31_state_t *s, int samples)
+{
+    t31_t38_front_end_state_t *fe;
+    int delay;
+
+    fe = &s->t38_fe;
+    if (fe->current_rx_type == T30_MODEM_DONE  ||  fe->current_tx_type == T30_MODEM_DONE)
+        return TRUE;
+
+    fe->samples += samples;
+    if (fe->timeout_rx_samples  &&  fe->samples > fe->timeout_rx_samples)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Timeout mid-receive\n");
+        fe->timeout_rx_samples = 0;
+        front_end_status(s, T30_FRONT_END_RECEIVE_COMPLETE);
+    }
+    if (fe->timed_step == T38_TIMED_STEP_NONE)
+        return FALSE;
+    /* Wait until the right time comes along, unless we are working in "no delays" mode, while talking to an
+       IAF terminal. */
+    if (fe->ms_per_tx_chunk  &&  fe->samples < fe->next_tx_samples)
+        return FALSE;
+    /* Its time to send something */
+    delay = 0;
+    switch (fe->timed_step & 0xFFF0)
+    {
+    case T38_TIMED_STEP_NON_ECM_MODEM:
+        delay = stream_non_ecm(s);
+        break;
+    case T38_TIMED_STEP_HDLC_MODEM:
+        delay = stream_hdlc(s);
+        break;
+    //case T38_TIMED_STEP_FAKE_HDLC_MODEM:
+    //    delay = stream_fake_hdlc(s);
+    //    break;
+    case T38_TIMED_STEP_CED:
+        delay = stream_ced(s);
+        break;
+    case T38_TIMED_STEP_CNG:
+        delay = stream_cng(s);
+        break;
+    case T38_TIMED_STEP_PAUSE:
+        /* End of timed pause */
+        fe->timed_step = T38_TIMED_STEP_NONE;
+        front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+        break;
+    }
+    fe->next_tx_samples += us_to_samples(delay);
+    return FALSE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int t31_modem_control_handler(at_state_t *s, void *user_data, int op, const char *num)
+{
+    t31_state_t *t;
+    
+    t = (t31_state_t *) user_data;
+    switch (op)
+    {
+    case AT_MODEM_CONTROL_CALL:
+        t->call_samples = 0;
+        break;
+    case AT_MODEM_CONTROL_ANSWER:
+        t->call_samples = 0;
+        break;
+    case AT_MODEM_CONTROL_ONHOOK:
+        if (t->tx.holding)
+        {
+            t->tx.holding = FALSE;
+            /* Tell the application to release further data */
+            at_modem_control(&t->at_state, AT_MODEM_CONTROL_CTS, (void *) 1);
+        }
+        if (t->at_state.rx_signal_present)
+        {
+            t->at_state.rx_data[t->at_state.rx_data_bytes++] = DLE;
+            t->at_state.rx_data[t->at_state.rx_data_bytes++] = ETX;
+            t->at_state.at_tx_handler(&t->at_state,
+                                      t->at_state.at_tx_user_data,
+                                      t->at_state.rx_data,
+                                      t->at_state.rx_data_bytes);
+            t->at_state.rx_data_bytes = 0;
+        }
+        restart_modem(t, FAX_MODEM_SILENCE_TX);
+        break;
+    case AT_MODEM_CONTROL_RESTART:
+        restart_modem(t, (int) (intptr_t) num);
+        return 0;
+    case AT_MODEM_CONTROL_DTE_TIMEOUT:
+        if (num)
+            t->dte_data_timeout = t->call_samples + ms_to_samples((intptr_t) num);
+        else
+            t->dte_data_timeout = 0;
+        return 0;
+    }
+    return t->modem_control_handler(t, t->modem_control_user_data, op, num);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void non_ecm_rx_status(void *user_data, int status)
+{
+    t31_state_t *s;
+
+    s = (t31_state_t *) user_data;
+    switch (status)
+    {
+    case SIG_STATUS_TRAINING_IN_PROGRESS:
+        break;
+    case SIG_STATUS_TRAINING_FAILED:
+        s->at_state.rx_trained = FALSE;
+        break;
+    case SIG_STATUS_TRAINING_SUCCEEDED:
+        /* The modem is now trained */
+        at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+        s->at_state.rx_signal_present = TRUE;
+        s->at_state.rx_trained = TRUE;
+        break;
+    case SIG_STATUS_CARRIER_UP:
+        break;
+    case SIG_STATUS_CARRIER_DOWN:
+        if (s->at_state.rx_signal_present)
+        {
+            s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE;
+            s->at_state.rx_data[s->at_state.rx_data_bytes++] = ETX;
+            s->at_state.at_tx_handler(&s->at_state,
+                                      s->at_state.at_tx_user_data,
+                                      s->at_state.rx_data,
+                                      s->at_state.rx_data_bytes);
+            s->at_state.rx_data_bytes = 0;
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_NO_CARRIER);
+            t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+        }
+        s->at_state.rx_signal_present = FALSE;
+        s->at_state.rx_trained = FALSE;
+        break;
+    default:
+        if (s->at_state.p.result_code_format)
+            span_log(&s->logging, SPAN_LOG_FLOW, "Eh!\n");
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void non_ecm_put_bit(void *user_data, int bit)
+{
+    t31_state_t *s;
+
+    if (bit < 0)
+    {
+        non_ecm_rx_status(user_data, bit);
+        return;
+    }
+    s = (t31_state_t *) user_data;
+    s->audio.current_byte = (s->audio.current_byte >> 1) | (bit << 7);
+    if (++s->audio.bit_no >= 8)
+    {
+        if (s->audio.current_byte == DLE)
+            s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE;
+        s->at_state.rx_data[s->at_state.rx_data_bytes++] = (uint8_t) s->audio.current_byte;
+        if (s->at_state.rx_data_bytes >= 250)
+        {
+            s->at_state.at_tx_handler(&s->at_state,
+                                      s->at_state.at_tx_user_data,
+                                      s->at_state.rx_data,
+                                      s->at_state.rx_data_bytes);
+            s->at_state.rx_data_bytes = 0;
+        }
+        s->audio.bit_no = 0;
+        s->audio.current_byte = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void non_ecm_put_chunk(void *user_data, const uint8_t buf[], int len)
+{
+    t31_state_t *s;
+    int i;
+
+    s = (t31_state_t *) user_data;
+    /* Ignore any fractional bytes which may have accumulated */
+    for (i = 0;  i < len;  i++)
+    {
+        if (buf[i] == DLE)
+            s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE;
+        s->at_state.rx_data[s->at_state.rx_data_bytes++] = buf[i];
+        if (s->at_state.rx_data_bytes >= 250)
+        {
+            s->at_state.at_tx_handler(&s->at_state,
+                                      s->at_state.at_tx_user_data,
+                                      s->at_state.rx_data,
+                                      s->at_state.rx_data_bytes);
+            s->at_state.rx_data_bytes = 0;
+        }
+    }
+    s->audio.bit_no = 0;
+    s->audio.current_byte = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int non_ecm_get_bit(void *user_data)
+{
+    t31_state_t *s;
+    int bit;
+
+    s = (t31_state_t *) user_data;
+    if (s->audio.bit_no <= 0)
+    {
+        if (s->tx.out_bytes != s->tx.in_bytes)
+        {
+            /* There is real data available to send */
+            s->audio.current_byte = s->tx.data[s->tx.out_bytes++];
+            if (s->tx.out_bytes > T31_TX_BUF_LEN - 1)
+            {
+                s->tx.out_bytes = T31_TX_BUF_LEN - 1;
+                span_log(&s->logging, SPAN_LOG_FLOW, "End of transmit buffer reached!\n");
+            }
+            if (s->tx.holding)
+            {
+                /* See if the buffer is approaching empty. It might be time to
+                   release flow control. */
+                if (s->tx.out_bytes > T31_TX_BUF_LOW_TIDE)
+                {
+                    s->tx.holding = FALSE;
+                    /* Tell the application to release further data */
+                    at_modem_control(&s->at_state, AT_MODEM_CONTROL_CTS, (void *) 1);
+                }
+            }
+            s->tx.data_started = TRUE;
+        }
+        else
+        {
+            if (s->tx.final)
+            {
+                s->tx.final = FALSE;
+                /* This will put the modem into its shutdown sequence. When
+                   it has finally shut down, an OK response will be sent. */
+                return SIG_STATUS_END_OF_DATA;
+            }
+            /* Fill with 0xFF bytes at the start of transmission, or 0x00 if we are in
+               the middle of transmission. This follows T.31 and T.30 practice. */
+            s->audio.current_byte = (s->tx.data_started)  ?  0x00  :  0xFF;
+        }
+        s->audio.bit_no = 8;
+    }
+    s->audio.bit_no--;
+    bit = s->audio.current_byte & 1;
+    s->audio.current_byte >>= 1;
+    return bit;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int non_ecm_get_chunk(void *user_data, uint8_t buf[], int len)
+{
+    t31_state_t *s;
+    int i;
+
+    s = (t31_state_t *) user_data;
+    for (i = 0;  i < len;  i++)
+    {
+        if (s->tx.out_bytes != s->tx.in_bytes)
+        {
+            /* There is real data available to send */
+            buf[i] = s->tx.data[s->tx.out_bytes++];
+            if (s->tx.out_bytes > T31_TX_BUF_LEN - 1)
+            {
+                s->tx.out_bytes = T31_TX_BUF_LEN - 1;
+                span_log(&s->logging, SPAN_LOG_FLOW, "End of transmit buffer reached!\n");
+            }
+            if (s->tx.holding)
+            {
+                /* See if the buffer is approaching empty. It might be time to release flow control. */
+                if (s->tx.out_bytes > T31_TX_BUF_LOW_TIDE)
+                {
+                    s->tx.holding = FALSE;
+                    /* Tell the application to release further data */
+                    at_modem_control(&s->at_state, AT_MODEM_CONTROL_CTS, (void *) 1);
+                }
+            }
+            s->tx.data_started = TRUE;
+        }
+        else
+        {
+            if (s->tx.final)
+            {
+                s->tx.final = FALSE;
+                /* This will put the modem into its shutdown sequence. When
+                   it has finally shut down, an OK response will be sent. */
+                //return SIG_STATUS_END_OF_DATA;
+                return i;
+            }
+            /* Fill with 0xFF bytes at the start of transmission, or 0x00 if we are in
+               the middle of transmission. This follows T.31 and T.30 practice. */
+            buf[i] = (s->tx.data_started)  ?  0x00  :  0xFF;
+        }
+    }
+    s->audio.bit_no = 0;
+    s->audio.current_byte = 0;
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void tone_detected(void *user_data, int tone, int level, int delay)
+{
+    t31_state_t *s;
+
+    s = (t31_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_tx_underflow(void *user_data)
+{
+    t31_state_t *s;
+
+    s = (t31_state_t *) user_data;
+    if (s->hdlc_tx.final)
+    {
+        s->hdlc_tx.final = FALSE;
+        /* Schedule an orderly shutdown of the modem */
+        hdlc_tx_frame(&(s->audio.modems.hdlc_tx), NULL, 0);
+    }
+    else
+    {
+        at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void hdlc_rx_status(void *user_data, int status)
+{
+    t31_state_t *s;
+    uint8_t buf[2];
+
+    s = (t31_state_t *) user_data;
+    switch (status)
+    {
+    case SIG_STATUS_TRAINING_IN_PROGRESS:
+        break;
+    case SIG_STATUS_TRAINING_FAILED:
+        s->at_state.rx_trained = FALSE;
+        break;
+    case SIG_STATUS_TRAINING_SUCCEEDED:
+        /* The modem is now trained */
+        s->at_state.rx_signal_present = TRUE;
+        s->at_state.rx_trained = TRUE;
+        break;
+    case SIG_STATUS_CARRIER_UP:
+        if (s->modem == FAX_MODEM_CNG_TONE  ||  s->modem == FAX_MODEM_NOCNG_TONE  ||  s->modem == FAX_MODEM_V21_RX)
+        {
+            s->at_state.rx_signal_present = TRUE;
+            s->rx_frame_received = FALSE;
+        }
+        break;
+    case SIG_STATUS_CARRIER_DOWN:
+        if (s->rx_frame_received)
+        {
+            if (s->at_state.dte_is_waiting)
+            {
+                if (s->at_state.ok_is_pending)
+                {
+                    at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK);
+                    s->at_state.ok_is_pending = FALSE;
+                }
+                else
+                {
+                    at_put_response_code(&s->at_state, AT_RESPONSE_CODE_NO_CARRIER);
+                }
+                s->at_state.dte_is_waiting = FALSE;
+                t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+            }
+            else
+            {
+                buf[0] = AT_RESPONSE_CODE_NO_CARRIER;
+                queue_write_msg(s->rx_queue, buf, 1);
+            }
+        }
+        s->at_state.rx_signal_present = FALSE;
+        s->at_state.rx_trained = FALSE;
+        break;
+    case SIG_STATUS_FRAMING_OK:
+        if (s->modem == FAX_MODEM_CNG_TONE  ||  s->modem == FAX_MODEM_NOCNG_TONE)
+        {
+            /* Once we get any valid HDLC the CNG tone stops, and we drop
+               to the V.21 receive modem on its own. */
+            s->modem = FAX_MODEM_V21_RX;
+            s->at_state.transmit = FALSE;
+        }
+        if (s->modem == FAX_MODEM_V17_RX  ||  s->modem == FAX_MODEM_V27TER_RX  ||  s->modem == FAX_MODEM_V29_RX)
+        {
+            /* V.21 has been detected while expecting a different carrier.
+               If +FAR=0 then result +FCERROR and return to command-mode.
+               If +FAR=1 then report +FRH:3 and CONNECT, switching to
+               V.21 receive mode. */
+            if (s->at_state.p.adaptive_receive)
+            {
+                s->at_state.rx_signal_present = TRUE;
+                s->rx_frame_received = TRUE;
+                s->modem = FAX_MODEM_V21_RX;
+                s->at_state.transmit = FALSE;
+                s->at_state.dte_is_waiting = TRUE;
+                at_put_response_code(&s->at_state, AT_RESPONSE_CODE_FRH3);
+                at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+            }
+            else
+            {
+                s->modem = FAX_MODEM_SILENCE_TX;
+                t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+                s->rx_frame_received = FALSE;
+                at_put_response_code(&s->at_state, AT_RESPONSE_CODE_FCERROR);
+            }
+        }
+        else
+        {
+            if (!s->rx_frame_received)
+            {
+                if (s->at_state.dte_is_waiting)
+                {
+                    /* Report CONNECT as soon as possible to avoid a timeout. */
+                    at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+                    s->rx_frame_received = TRUE;
+                }
+                else
+                {
+                    buf[0] = AT_RESPONSE_CODE_CONNECT;
+                    queue_write_msg(s->rx_queue, buf, 1);
+                }
+            }
+        }
+        break;
+    case SIG_STATUS_ABORT:
+        /* Just ignore these */
+        break;
+    default:
+        span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected HDLC rx status - %d!\n", status);
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void hdlc_accept_frame(void *user_data, const uint8_t *msg, int len, int ok)
+{
+    t31_state_t *s;
+    uint8_t buf[256];
+    int i;
+
+    if (len < 0)
+    {
+        hdlc_rx_status(user_data, len);
+        return;
+    }
+    s = (t31_state_t *) user_data;
+    if (!s->rx_frame_received)
+    {
+        if (s->at_state.dte_is_waiting)
+        {
+            /* Report CONNECT as soon as possible to avoid a timeout. */
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+            s->rx_frame_received = TRUE;
+        }
+        else
+        {
+            buf[0] = AT_RESPONSE_CODE_CONNECT;
+            queue_write_msg(s->rx_queue, buf, 1);
+        }
+    }
+    /* If OK is pending then we just ignore whatever comes in */
+    if (!s->at_state.ok_is_pending)
+    {
+        if (s->at_state.dte_is_waiting)
+        {
+            /* Send straight away */
+            /* It is safe to look at the two bytes beyond the length of the message,
+               and expect to find the FCS there. */
+            for (i = 0;  i < len + 2;  i++)
+            {
+                if (msg[i] == DLE)
+                    s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE;
+                s->at_state.rx_data[s->at_state.rx_data_bytes++] = msg[i];
+            }
+            s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE;
+            s->at_state.rx_data[s->at_state.rx_data_bytes++] = ETX;
+            s->at_state.at_tx_handler(&s->at_state, s->at_state.at_tx_user_data, s->at_state.rx_data, s->at_state.rx_data_bytes);
+            s->at_state.rx_data_bytes = 0;
+            if (msg[1] == 0x13  &&  ok)
+            {
+                /* This is the last frame.  We don't send OK until the carrier drops to avoid
+                   redetecting it later. */
+                s->at_state.ok_is_pending = TRUE;
+            }
+            else
+            {
+                at_put_response_code(&s->at_state, (ok)  ?  AT_RESPONSE_CODE_OK  :  AT_RESPONSE_CODE_ERROR);
+                s->at_state.dte_is_waiting = FALSE;
+                s->rx_frame_received = FALSE;
+            }
+        }
+        else
+        {
+            /* Queue it */
+            buf[0] = (ok)  ?  AT_RESPONSE_CODE_OK  :  AT_RESPONSE_CODE_ERROR;
+            /* It is safe to look at the two bytes beyond the length of the message,
+               and expect to find the FCS there. */
+            memcpy(buf + 1, msg, len + 2);
+            queue_write_msg(s->rx_queue, buf, len + 3);
+        }
+    }
+    t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void t31_v21_rx(t31_state_t *s)
+{
+    s->at_state.ok_is_pending = FALSE;
+    s->hdlc_tx.final = FALSE;
+    s->hdlc_tx.len = 0;
+    s->dled = FALSE;
+    hdlc_rx_init(&(s->audio.modems.hdlc_rx), FALSE, TRUE, HDLC_FRAMING_OK_THRESHOLD, hdlc_accept_frame, s);
+    fsk_rx_init(&(s->audio.modems.v21_rx), &preset_fsk_specs[FSK_V21CH2], FSK_FRAME_MODE_SYNC, (put_bit_func_t) hdlc_rx_put_bit, &(s->audio.modems.hdlc_rx));
+    fsk_rx_signal_cutoff(&(s->audio.modems.v21_rx), -39.09f);
+    s->at_state.transmit = TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int restart_modem(t31_state_t *s, int new_modem)
+{
+    int use_hdlc;
+    fax_modems_state_t *t;
+
+    t = &s->audio.modems;
+    span_log(&s->logging, SPAN_LOG_FLOW, "Restart modem %d\n", new_modem);
+    if (s->modem == new_modem)
+        return 0;
+    queue_flush(s->rx_queue);
+    s->modem = new_modem;
+    s->tx.final = FALSE;
+    s->at_state.rx_signal_present = FALSE;
+    s->at_state.rx_trained = FALSE;
+    s->rx_frame_received = FALSE;
+    set_rx_handler(s, (span_rx_handler_t *) &span_dummy_rx, (span_rx_fillin_handler_t *) &span_dummy_rx_fillin, NULL);
+    use_hdlc = FALSE;
+    switch (s->modem)
+    {
+    case FAX_MODEM_CNG_TONE:
+        if (s->t38_mode)
+        {
+            s->t38_fe.next_tx_samples = s->t38_fe.samples;
+            s->t38_fe.timed_step = T38_TIMED_STEP_CNG;
+            s->t38_fe.current_tx_data_type = T38_DATA_NONE;
+        }
+        else
+        {
+            modem_connect_tones_tx_init(&t->connect_tx, MODEM_CONNECT_TONES_FAX_CNG);
+            /* CNG is special, since we need to receive V.21 HDLC messages while sending the
+               tone. Everything else in FAX processing sends only one way at a time. */
+            /* Do V.21/HDLC receive in parallel. The other end may send its
+               first message at any time. The CNG tone will continue until
+               we get a valid preamble. */
+            set_rx_handler(s, (span_rx_handler_t *) &cng_rx, (span_rx_fillin_handler_t *) &span_dummy_rx_fillin, s);
+            t31_v21_rx(s);
+            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);
+        }
+        s->at_state.transmit = TRUE;
+        break;
+    case FAX_MODEM_NOCNG_TONE:
+        if (s->t38_mode)
+        {
+        }
+        else
+        {
+            set_rx_handler(s, (span_rx_handler_t *) &cng_rx, (span_rx_fillin_handler_t *) &span_dummy_rx_fillin, s);
+            t31_v21_rx(s);
+            silence_gen_set(&t->silence_gen, 0);
+            set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+        }
+        s->at_state.transmit = FALSE;
+        break;
+    case FAX_MODEM_CED_TONE:
+        if (s->t38_mode)
+        {
+            s->t38_fe.next_tx_samples = s->t38_fe.samples;
+            s->t38_fe.timed_step = T38_TIMED_STEP_CED;
+            s->t38_fe.current_tx_data_type = T38_DATA_NONE;
+        }
+        else
+        {
+            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);
+        }
+        s->at_state.transmit = TRUE;
+        break;
+    case FAX_MODEM_V21_TX:
+        if (s->t38_mode)
+        {
+            s->t38_fe.next_tx_indicator = T38_IND_V21_PREAMBLE;
+            s->t38_fe.current_tx_data_type = T38_DATA_V21;
+            use_hdlc = TRUE;
+            s->t38_fe.timed_step = (use_hdlc)  ?  T38_TIMED_STEP_HDLC_MODEM  :  T38_TIMED_STEP_NON_ECM_MODEM;
+            set_octets_per_data_packet(s, 300);
+        }
+        else
+        {
+            hdlc_tx_init(&t->hdlc_tx, FALSE, 2, FALSE, hdlc_tx_underflow, s);
+            /* The spec says 1s +-15% of preamble. So, the minimum is 32 octets. */
+            hdlc_tx_flags(&t->hdlc_tx, 32);
+            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 *) &fsk_tx, &t->v21_tx);
+            set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        }
+        s->hdlc_tx.final = FALSE;
+        s->hdlc_tx.len = 0;
+        s->dled = FALSE;
+        s->at_state.transmit = TRUE;
+        break;
+    case FAX_MODEM_V21_RX:
+        if (s->t38_mode)
+        {
+        }
+        else
+        {
+            set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &t->v21_rx);
+            t31_v21_rx(s);
+        }
+        break;
+    case FAX_MODEM_V17_TX:
+        if (s->t38_mode)
+        {
+            switch (s->bit_rate)
+            {
+            case 7200:
+                s->t38_fe.next_tx_indicator = (s->short_train)  ?  T38_IND_V17_7200_SHORT_TRAINING  :  T38_IND_V17_7200_LONG_TRAINING;
+                s->t38_fe.current_tx_data_type = T38_DATA_V17_7200;
+                break;
+            case 9600:
+                s->t38_fe.next_tx_indicator = (s->short_train)  ?  T38_IND_V17_9600_SHORT_TRAINING  :  T38_IND_V17_9600_LONG_TRAINING;
+                s->t38_fe.current_tx_data_type = T38_DATA_V17_9600;
+                break;
+            case 12000:
+                s->t38_fe.next_tx_indicator = (s->short_train)  ?  T38_IND_V17_12000_SHORT_TRAINING  :  T38_IND_V17_12000_LONG_TRAINING;
+                s->t38_fe.current_tx_data_type = T38_DATA_V17_12000;
+                break;
+            case 14400:
+                s->t38_fe.next_tx_indicator = (s->short_train)  ?  T38_IND_V17_14400_SHORT_TRAINING  :  T38_IND_V17_14400_LONG_TRAINING;
+                s->t38_fe.current_tx_data_type = T38_DATA_V17_14400;
+                break;
+            }
+            set_octets_per_data_packet(s, s->bit_rate);
+            s->t38_fe.timed_step = (use_hdlc)  ?  T38_TIMED_STEP_HDLC_MODEM  :  T38_TIMED_STEP_NON_ECM_MODEM;
+        }
+        else
+        {
+            v17_tx_restart(&t->v17_tx, s->bit_rate, FALSE, s->short_train);
+            set_tx_handler(s, (span_tx_handler_t *) &v17_tx, &t->v17_tx);
+            set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        }
+        s->tx.out_bytes = 0;
+        s->tx.data_started = FALSE;
+        s->at_state.transmit = TRUE;
+        break;
+    case FAX_MODEM_V17_RX:
+        if (!s->t38_mode)
+        {
+            set_rx_handler(s, (span_rx_handler_t *) &v17_v21_rx, (span_rx_fillin_handler_t *) &v17_v21_rx_fillin, s);
+            v17_rx_restart(&t->v17_rx, s->bit_rate, s->short_train);
+            /* Allow for +FCERROR/+FRH:3 */
+            t31_v21_rx(s);
+        }
+        s->at_state.transmit = FALSE;
+        break;
+    case FAX_MODEM_V27TER_TX:
+        if (s->t38_mode)
+        {
+            switch (s->bit_rate)
+            {
+            case 2400:
+                s->t38_fe.next_tx_indicator = T38_IND_V27TER_2400_TRAINING;
+                s->t38_fe.current_tx_data_type = T38_DATA_V27TER_2400;
+                break;
+            case 4800:
+                s->t38_fe.next_tx_indicator = T38_IND_V27TER_4800_TRAINING;
+                s->t38_fe.current_tx_data_type = T38_DATA_V27TER_4800;
+                break;
+            }
+            set_octets_per_data_packet(s, s->bit_rate);
+            s->t38_fe.timed_step = (use_hdlc)  ?  T38_TIMED_STEP_HDLC_MODEM  :  T38_TIMED_STEP_NON_ECM_MODEM;
+        }
+        else
+        {
+            v27ter_tx_restart(&t->v27ter_tx, s->bit_rate, FALSE);
+            set_tx_handler(s, (span_tx_handler_t *) &v27ter_tx, &t->v27ter_tx);
+            set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        }
+        s->tx.out_bytes = 0;
+        s->tx.data_started = FALSE;
+        s->at_state.transmit = TRUE;
+        break;
+    case FAX_MODEM_V27TER_RX:
+        if (!s->t38_mode)
+        {
+            set_rx_handler(s, (span_rx_handler_t *) &v27ter_v21_rx, (span_rx_fillin_handler_t *) &v27ter_v21_rx_fillin, s);
+            v27ter_rx_restart(&t->v27ter_rx, s->bit_rate, FALSE);
+            /* Allow for +FCERROR/+FRH:3 */
+            t31_v21_rx(s);
+        }
+        s->at_state.transmit = FALSE;
+        break;
+    case FAX_MODEM_V29_TX:
+        if (s->t38_mode)
+        {
+            switch (s->bit_rate)
+            {
+            case 7200:
+                s->t38_fe.next_tx_indicator = T38_IND_V29_7200_TRAINING;
+                s->t38_fe.current_tx_data_type = T38_DATA_V29_7200;
+                break;
+            case 9600:
+                s->t38_fe.next_tx_indicator = T38_IND_V29_9600_TRAINING;
+                s->t38_fe.current_tx_data_type = T38_DATA_V29_9600;
+                break;
+            }
+            set_octets_per_data_packet(s, s->bit_rate);
+            s->t38_fe.timed_step = (use_hdlc)  ?  T38_TIMED_STEP_HDLC_MODEM  :  T38_TIMED_STEP_NON_ECM_MODEM;
+        }
+        else
+        {
+            v29_tx_restart(&t->v29_tx, s->bit_rate, FALSE);
+            set_tx_handler(s, (span_tx_handler_t *) &v29_tx, &t->v29_tx);
+            set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        }
+        s->tx.out_bytes = 0;
+        s->tx.data_started = FALSE;
+        s->at_state.transmit = TRUE;
+        break;
+    case FAX_MODEM_V29_RX:
+        if (!s->t38_mode)
+        {
+            set_rx_handler(s, (span_rx_handler_t *) &v29_v21_rx, (span_rx_fillin_handler_t *) &v29_v21_rx_fillin, s);
+            v29_rx_restart(&t->v29_rx, s->bit_rate, FALSE);
+            /* Allow for +FCERROR/+FRH:3 */
+            t31_v21_rx(s);
+        }
+        s->at_state.transmit = FALSE;
+        break;
+    case FAX_MODEM_SILENCE_TX:
+        if (s->t38_mode)
+        {
+            t38_core_send_indicator(&s->t38_fe.t38, T38_IND_NO_SIGNAL);
+            s->t38_fe.next_tx_samples = s->t38_fe.samples + ms_to_samples(700);
+            s->t38_fe.timed_step = T38_TIMED_STEP_PAUSE;
+            s->t38_fe.current_tx_data_type = T38_DATA_NONE;
+        }
+        else
+        {
+            silence_gen_set(&t->silence_gen, 0);
+            set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+            set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        }
+        s->at_state.transmit = FALSE;
+        break;
+    case FAX_MODEM_SILENCE_RX:
+        if (!s->t38_mode)
+        {
+            set_rx_handler(s, (span_rx_handler_t *) &silence_rx, (span_rx_fillin_handler_t *) &span_dummy_rx_fillin, s);
+            silence_gen_set(&t->silence_gen, 0);
+            set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+            set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        }
+        s->at_state.transmit = FALSE;
+        break;
+    case FAX_MODEM_FLUSH:
+        /* Send 200ms of silence to "push" the last audio out */
+        if (s->t38_mode)
+        {
+            t38_core_send_indicator(&s->t38_fe.t38, T38_IND_NO_SIGNAL);
+        }
+        else
+        {
+            s->modem = FAX_MODEM_SILENCE_TX;
+            silence_gen_alter(&t->silence_gen, ms_to_samples(200));
+            set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &t->silence_gen);
+            set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+            s->at_state.transmit = TRUE;
+        }
+        break;
+    }
+    s->audio.bit_no = 0;
+    s->audio.current_byte = 0xFF;
+    s->tx.in_bytes = 0;
+    s->tx.out_bytes = 0;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void dle_unstuff_hdlc(t31_state_t *s, const char *stuffed, int len)
+{
+    int i;
+
+    for (i = 0;  i < len;  i++)
+    {
+        if (s->dled)
+        {
+            s->dled = FALSE;
+            if (stuffed[i] == ETX)
+            {
+                s->hdlc_tx.final = (s->hdlc_tx.buf[1] & 0x10);
+                if (s->t38_mode)
+                {
+                    send_hdlc(s, s->hdlc_tx.buf, s->hdlc_tx.len);
+                }
+                else
+                {
+                    hdlc_tx_frame(&(s->audio.modems.hdlc_tx), s->hdlc_tx.buf, s->hdlc_tx.len);
+                    s->hdlc_tx.len = 0;
+                }
+            }
+            else if (stuffed[i] == SUB)
+            {
+                s->hdlc_tx.buf[s->hdlc_tx.len++] = DLE;
+                s->hdlc_tx.buf[s->hdlc_tx.len++] = DLE;
+            }
+            else
+            {
+                s->hdlc_tx.buf[s->hdlc_tx.len++] = stuffed[i];
+            }
+        }
+        else
+        {
+            if (stuffed[i] == DLE)
+                s->dled = TRUE;
+            else
+                s->hdlc_tx.buf[s->hdlc_tx.len++] = stuffed[i];
+        }
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void dle_unstuff(t31_state_t *s, const char *stuffed, int len)
+{
+    int i;
+    
+    for (i = 0;  i < len;  i++)
+    {
+        if (s->dled)
+        {
+            s->dled = FALSE;
+            if (stuffed[i] == ETX)
+            {
+                s->tx.final = TRUE;
+                t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+                return;
+            }
+        }
+        else if (stuffed[i] == DLE)
+        {
+            s->dled = TRUE;
+            continue;
+        }
+        s->tx.data[s->tx.in_bytes++] = stuffed[i];
+        if (s->tx.in_bytes > T31_TX_BUF_LEN - 1)
+        {
+            /* Oops. We hit the end of the buffer. Give up. Loose stuff. :-( */
+            span_log(&s->logging, SPAN_LOG_FLOW, "No room in buffer for new data!\n");
+            return;
+        }
+    }
+    if (!s->tx.holding)
+    {
+        /* See if the buffer is approaching full. We might need to apply flow control. */
+        if (s->tx.in_bytes > T31_TX_BUF_HIGH_TIDE)
+        {
+            s->tx.holding = TRUE;
+            /* Tell the application to hold further data */
+            at_modem_control(&s->at_state, AT_MODEM_CONTROL_CTS, (void *) 0);
+        }
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int process_class1_cmd(at_state_t *t, void *user_data, int direction, int operation, int val)
+{
+    int new_modem;
+    int new_transmit;
+    int i;
+    int len;
+    int immediate_response;
+    t31_state_t *s;
+    uint8_t msg[256];
+
+    s = (t31_state_t *) user_data;
+    new_transmit = direction;
+    immediate_response = TRUE;
+    switch (operation)
+    {
+    case 'S':
+        s->at_state.transmit = new_transmit;
+        if (new_transmit)
+        {
+            /* Send a specified period of silence, to space transmissions. */
+            restart_modem(s, FAX_MODEM_SILENCE_TX);
+            if (s->t38_mode)
+                s->t38_fe.next_tx_samples = s->t38_fe.samples + ms_to_samples(val*10);
+            else
+                silence_gen_alter(&(s->audio.modems.silence_gen), ms_to_samples(val*10));
+            s->at_state.transmit = TRUE;
+        }
+        else
+        {
+            /* Wait until we have received a specified period of silence. */
+            queue_flush(s->rx_queue);
+            s->silence_awaited = ms_to_samples(val*10);
+            t31_set_at_rx_mode(s, AT_MODE_DELIVERY);
+            if (s->t38_mode)
+            {
+                at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK);
+                t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+            }
+            else
+            {
+                restart_modem(s, FAX_MODEM_SILENCE_RX);
+            }
+        }
+        immediate_response = FALSE;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Silence %dms\n", val*10);
+        break;
+    case 'H':
+        switch (val)
+        {
+        case 3:
+            new_modem = (new_transmit)  ?  FAX_MODEM_V21_TX  :  FAX_MODEM_V21_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 300;
+            break;
+        default:
+            return -1;
+        }
+        span_log(&s->logging, SPAN_LOG_FLOW, "HDLC\n");
+        if (new_modem != s->modem)
+        {
+            restart_modem(s, new_modem);
+            immediate_response = FALSE;
+        }
+        s->at_state.transmit = new_transmit;
+        if (new_transmit)
+        {
+            t31_set_at_rx_mode(s, AT_MODE_HDLC);
+            if (!s->t38_mode)
+                at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+        }
+        else
+        {
+            /* Send straight away, if there is something queued. */
+            t31_set_at_rx_mode(s, AT_MODE_DELIVERY);
+            s->rx_frame_received = FALSE;
+            do
+            {
+                if (!queue_empty(s->rx_queue))
+                {
+                    len = queue_read_msg(s->rx_queue, msg, 256);
+                    if (len > 1)
+                    {
+                        if (msg[0] == AT_RESPONSE_CODE_OK)
+                            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+                        for (i = 1;  i < len;  i++)
+                        {
+                            if (msg[i] == DLE)
+                                s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE;
+                            s->at_state.rx_data[s->at_state.rx_data_bytes++] = msg[i];
+                        }
+                        s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE;
+                        s->at_state.rx_data[s->at_state.rx_data_bytes++] = ETX;
+                        s->at_state.at_tx_handler(&s->at_state, s->at_state.at_tx_user_data, s->at_state.rx_data, s->at_state.rx_data_bytes);
+                        s->at_state.rx_data_bytes = 0;
+                    }
+                    at_put_response_code(&s->at_state, msg[0]);
+                }
+                else
+                {
+                    s->at_state.dte_is_waiting = TRUE;
+                    break;
+                }
+            }
+            while (msg[0] == AT_RESPONSE_CODE_CONNECT);
+        }
+        immediate_response = FALSE;
+        break;
+    default:
+        switch (val)
+        {
+        case 24:
+            s->t38_fe.next_tx_indicator = T38_IND_V27TER_2400_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V27TER_2400;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V27TER_TX  :  FAX_MODEM_V27TER_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 2400;
+            break;
+        case 48:
+            s->t38_fe.next_tx_indicator = T38_IND_V27TER_4800_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V27TER_4800;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V27TER_TX  :  FAX_MODEM_V27TER_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 4800;
+            break;
+        case 72:
+            s->t38_fe.next_tx_indicator = T38_IND_V29_7200_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V29_7200;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V29_TX  :  FAX_MODEM_V29_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 7200;
+            break;
+        case 96:
+            s->t38_fe.next_tx_indicator = T38_IND_V29_9600_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V29_9600;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V29_TX  :  FAX_MODEM_V29_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 9600;
+            break;
+        case 73:
+            s->t38_fe.next_tx_indicator = T38_IND_V17_7200_LONG_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V17_7200;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V17_TX  :  FAX_MODEM_V17_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 7200;
+            break;
+        case 74:
+            s->t38_fe.next_tx_indicator = T38_IND_V17_7200_SHORT_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V17_7200;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V17_TX  :  FAX_MODEM_V17_RX;
+            s->short_train = TRUE;
+            s->bit_rate = 7200;
+            break;
+        case 97:
+            s->t38_fe.next_tx_indicator = T38_IND_V17_9600_LONG_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V17_9600;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V17_TX  :  FAX_MODEM_V17_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 9600;
+            break;
+        case 98:
+            s->t38_fe.next_tx_indicator = T38_IND_V17_9600_SHORT_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V17_9600;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V17_TX  :  FAX_MODEM_V17_RX;
+            s->short_train = TRUE;
+            s->bit_rate = 9600;
+            break;
+        case 121:
+            s->t38_fe.next_tx_indicator = T38_IND_V17_12000_LONG_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V17_12000;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V17_TX  :  FAX_MODEM_V17_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 12000;
+            break;
+        case 122:
+            s->t38_fe.next_tx_indicator = T38_IND_V17_12000_SHORT_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V17_12000;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V17_TX  :  FAX_MODEM_V17_RX;
+            s->short_train = TRUE;
+            s->bit_rate = 12000;
+            break;
+        case 145:
+            s->t38_fe.next_tx_indicator = T38_IND_V17_14400_LONG_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V17_14400;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V17_TX  :  FAX_MODEM_V17_RX;
+            s->short_train = FALSE;
+            s->bit_rate = 14400;
+            break;
+        case 146:
+            s->t38_fe.next_tx_indicator = T38_IND_V17_14400_SHORT_TRAINING;
+            s->t38_fe.current_tx_data_type = T38_DATA_V17_14400;
+            new_modem = (new_transmit)  ?  FAX_MODEM_V17_TX  :  FAX_MODEM_V17_RX;
+            s->short_train = TRUE;
+            s->bit_rate = 14400;
+            break;
+        default:
+            return -1;
+        }
+        span_log(&s->logging, SPAN_LOG_FLOW, "Short training = %d, bit rate = %d\n", s->short_train, s->bit_rate);
+        if (new_transmit)
+        {
+            t31_set_at_rx_mode(s, AT_MODE_STUFFED);
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT);
+        }
+        else
+        {
+            t31_set_at_rx_mode(s, AT_MODE_DELIVERY);
+        }
+        restart_modem(s, new_modem);
+        immediate_response = FALSE;
+        break;
+    }
+    return immediate_response;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t31_call_event(t31_state_t *s, int event)
+{
+    span_log(&s->logging, SPAN_LOG_FLOW, "Call event %d received\n", event);
+    at_call_event(&s->at_state, event);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t31_at_rx(t31_state_t *s, const char *t, int len)
+{
+    if (s->dte_data_timeout)
+        s->dte_data_timeout = s->call_samples + ms_to_samples(5000);
+    switch (s->at_state.at_rx_mode)
+    {
+    case AT_MODE_ONHOOK_COMMAND:
+    case AT_MODE_OFFHOOK_COMMAND:
+        at_interpreter(&s->at_state, t, len);
+        break;
+    case AT_MODE_DELIVERY:
+        /* Data from the DTE in this state returns us to command mode */
+        if (len)
+        {
+            if (s->at_state.rx_signal_present)
+            {
+                s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE;
+                s->at_state.rx_data[s->at_state.rx_data_bytes++] = ETX;
+                s->at_state.at_tx_handler(&s->at_state, s->at_state.at_tx_user_data, s->at_state.rx_data, s->at_state.rx_data_bytes);
+            }
+            s->at_state.rx_data_bytes = 0;
+            s->at_state.transmit = FALSE;
+            s->modem = FAX_MODEM_SILENCE_TX;
+            set_rx_handler(s, (span_rx_handler_t *) &span_dummy_rx, (span_rx_fillin_handler_t *) &span_dummy_rx_fillin, NULL);
+            t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+            at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK);
+        }
+        break;
+    case AT_MODE_HDLC:
+        dle_unstuff_hdlc(s, t, len);
+        break;
+    case AT_MODE_STUFFED:
+        if (s->tx.out_bytes)
+        {
+            /* Make room for new data in existing data buffer. */
+            s->tx.in_bytes = &(s->tx.data[s->tx.in_bytes]) - &(s->tx.data[s->tx.out_bytes]);
+            memmove(&(s->tx.data[0]), &(s->tx.data[s->tx.out_bytes]), s->tx.in_bytes);
+            s->tx.out_bytes = 0;
+        }
+        dle_unstuff(s, t, len);
+        break;
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void set_rx_handler(t31_state_t *s,
+                           span_rx_handler_t *rx_handler,
+                           span_rx_fillin_handler_t *fillin_handler,
+                           void *user_data)
+{
+    s->audio.modems.rx_handler = rx_handler;
+    s->audio.modems.rx_fillin_handler = fillin_handler;
+    s->audio.modems.rx_user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void set_tx_handler(t31_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(t31_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 int silence_rx(void *user_data, const int16_t amp[], int len)
+{
+    t31_state_t *s;
+
+    /* Searching for a specified minimum period of silence. */
+    s = (t31_state_t *) user_data;
+    if (s->silence_awaited  &&  s->audio.silence_heard >= s->silence_awaited)
+    {
+        at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK);
+        t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+        s->audio.silence_heard = 0;
+        s->silence_awaited = 0;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int cng_rx(void *user_data, const int16_t amp[], int len)
+{
+    t31_state_t *s;
+
+    s = (t31_state_t *) user_data;
+    if (s->call_samples > ms_to_samples(s->at_state.p.s_regs[7]*1000))
+    {
+        /* After calling, S7 has elapsed... no carrier found. */
+        at_put_response_code(&s->at_state, AT_RESPONSE_CODE_NO_CARRIER);
+        restart_modem(s, FAX_MODEM_SILENCE_TX);
+        at_modem_control(&s->at_state, AT_MODEM_CONTROL_HANGUP, NULL);
+        t31_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+    }
+    else
+    {
+        fsk_rx(&(s->audio.modems.v21_rx), amp, len);
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v17_v21_rx(void *user_data, const int16_t amp[], int len)
+{
+    t31_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (t31_state_t *) user_data;
+    s = &t->audio.modems;
+    v17_rx(&s->v17_rx, amp, len);
+    if (t->at_state.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, (span_rx_fillin_handler_t *) &v17_rx_fillin, &s->v17_rx);
+    }
+    else
+    {
+        fsk_rx(&s->v21_rx, amp, len);
+        if (t->rx_frame_received)
+        {
+            /* We have received something, and the fast modem has not trained. We must
+               be receiving valid V.21 */
+            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, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
+        }
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v17_v21_rx_fillin(void *user_data, int len)
+{
+    t31_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (t31_state_t *) user_data;
+    s = &t->audio.modems;
+    v17_rx_fillin(&s->v17_rx, len);
+    fsk_rx_fillin(&s->v21_rx, len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v27ter_v21_rx(void *user_data, const int16_t amp[], int len)
+{
+    t31_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (t31_state_t *) user_data;
+    s = &t->audio.modems;
+    v27ter_rx(&s->v27ter_rx, amp, len);
+    if (t->at_state.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, (span_rx_fillin_handler_t *) &v27ter_rx_fillin, &s->v27ter_rx);
+    }
+    else
+    {
+        fsk_rx(&s->v21_rx, amp, len);
+        if (t->rx_frame_received)
+        {
+            /* We have received something, and the fast modem has not trained. We must
+               be receiving valid V.21 */
+            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, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
+        }
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v27ter_v21_rx_fillin(void *user_data, int len)
+{
+    t31_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (t31_state_t *) user_data;
+    s = &t->audio.modems;
+    v27ter_rx_fillin(&s->v27ter_rx, len);
+    fsk_rx_fillin(&s->v21_rx, len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v29_v21_rx(void *user_data, const int16_t amp[], int len)
+{
+    t31_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (t31_state_t *) user_data;
+    s = &t->audio.modems;
+    v29_rx(&s->v29_rx, amp, len);
+    if (t->at_state.rx_trained)
+    {
+        /* The fast modem has trained, so we no longer need to run the slow
+           one in parallel. */
+        span_log(&s->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, (span_rx_fillin_handler_t *) &v29_rx_fillin, &s->v29_rx);
+    }
+    else
+    {
+        fsk_rx(&s->v21_rx, amp, len);
+        if (t->rx_frame_received)
+        {
+            /* We have received something, and the fast modem has not trained. We must
+               be receiving valid V.21 */
+            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, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &s->v21_rx);
+        }
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v29_v21_rx_fillin(void *user_data, int len)
+{
+    t31_state_t *t;
+    fax_modems_state_t *s;
+
+    t = (t31_state_t *) user_data;
+    s = &t->audio.modems;
+    v29_rx_fillin(&s->v29_rx, len);
+    fsk_rx_fillin(&s->v21_rx, len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t31_rx(t31_state_t *s, int16_t amp[], int len)
+{
+    int i;
+    int32_t power;
+
+    /* Monitor for received silence.  Maximum needed detection is AT+FRS=255 (255*10ms). */
+    /* We could probably only run this loop if (s->modem == FAX_MODEM_SILENCE_RX), however,
+       the spec says "when silence has been present on the line for the amount of
+       time specified".  That means some of the silence may have occurred before
+       the AT+FRS=n command. This condition, however, is not likely to ever be the
+       case.  (AT+FRS=n will usually be issued before the remote goes silent.) */
+    for (i = 0;  i < len;  i++)
+    {
+        /* Clean up any DC influence. */
+        power = power_meter_update(&(s->audio.rx_power), amp[i] - s->audio.last_sample);
+        s->audio.last_sample = amp[i];
+        if (power > s->audio.silence_threshold_power)
+        {
+            s->audio.silence_heard = 0;
+        }
+        else
+        {        
+            if (s->audio.silence_heard <= ms_to_samples(255*10))
+                s->audio.silence_heard++;
+        }
+    }
+
+    /* Time is determined by counting the samples in audio packets coming in. */
+    s->call_samples += len;
+
+    /* In HDLC transmit mode, if 5 seconds elapse without data from the DTE
+       we must treat this as an error. We return the result ERROR, and change
+       to command-mode. */
+    if (s->dte_data_timeout  &&  s->call_samples > s->dte_data_timeout)
+    {
+        t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+        at_put_response_code(&s->at_state, AT_RESPONSE_CODE_ERROR);
+        restart_modem(s, FAX_MODEM_SILENCE_TX);
+    }
+
+    if (!s->at_state.transmit  ||  s->modem == FAX_MODEM_CNG_TONE)
+        s->audio.modems.rx_handler(s->audio.modems.rx_user_data, amp, len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t31_rx_fillin(t31_state_t *s, int len)
+{
+    /* To mitigate the effect of lost packets on a packet network we should
+       try to sustain the status quo. If there is no receive modem running, keep
+       things that way. If there is a receive modem running, try to sustain its
+       operation, without causing a phase hop, or letting its adaptive functions
+       diverge. */
+    /* Time is determined by counting the samples in audio packets coming in. */
+    s->call_samples += len;
+
+    /* In HDLC transmit mode, if 5 seconds elapse without data from the DTE
+       we must treat this as an error. We return the result ERROR, and change
+       to command-mode. */
+    if (s->dte_data_timeout  &&  s->call_samples > s->dte_data_timeout)
+    {
+        t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+        at_put_response_code(&s->at_state, AT_RESPONSE_CODE_ERROR);
+        restart_modem(s, FAX_MODEM_SILENCE_TX);
+    }
+    /* Call the fillin function of the current modem (if there is one). */
+    switch (s->modem)
+    {
+    case FAX_MODEM_V21_RX:
+        len = fsk_rx_fillin(&s->audio.modems.v21_rx, len);
+        break;
+    case FAX_MODEM_V27TER_RX:
+        /* TODO: what about FSK in the early stages */
+        len = v27ter_rx_fillin(&s->audio.modems.v27ter_rx, len);
+        break;
+    case FAX_MODEM_V29_RX:
+        /* TODO: what about FSK in the early stages */
+        len = v29_rx_fillin(&s->audio.modems.v29_rx, len);
+        break;
+    case FAX_MODEM_V17_RX:
+        /* TODO: what about FSK in the early stages */
+        len = v17_rx_fillin(&s->audio.modems.v17_rx, len);
+        break;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int set_next_tx_type(t31_state_t *s)
+{
+    if (s->audio.next_tx_handler)
+    {
+        set_tx_handler(s, s->audio.next_tx_handler, s->audio.next_tx_user_data);
+        set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+        return 0;
+    }
+    /* There is nothing else to change to, so use zero length silence */
+    silence_gen_alter(&(s->audio.modems.silence_gen), 0);
+    set_tx_handler(s, (span_tx_handler_t *) &silence_gen, &s->audio.modems.silence_gen);
+    set_next_tx_handler(s, (span_tx_handler_t *) NULL, NULL);
+    return -1;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t31_tx(t31_state_t *s, int16_t amp[], int max_len)
+{
+    int len;
+
+    len = 0;
+    if (s->at_state.transmit)
+    {
+        if ((len = s->audio.modems.tx_handler(s->audio.modems.tx_user_data, amp, max_len)) < max_len)
+        {
+            /* Allow for one change of tx handler within a block */
+            set_next_tx_type(s);
+            if ((len += s->audio.modems.tx_handler(s->audio.modems.tx_user_data, amp + len, max_len - len)) < max_len)
+                front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+        }
+    }
+    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;        
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t31_set_transmit_on_idle(t31_state_t *s, int transmit_on_idle)
+{
+    s->audio.modems.transmit_on_idle = transmit_on_idle;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t31_set_tep_mode(t31_state_t *s, int use_tep)
+{
+    s->audio.modems.use_tep = use_tep;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t31_set_t38_config(t31_state_t *s, int without_pacing)
+{
+    if (without_pacing)
+    {
+        /* Continuous streaming mode, as used for TPKT over TCP transport */
+        /* Inhibit indicator packets */
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_INDICATOR, 0);
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_CONTROL_DATA, 1);
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_CONTROL_DATA_END, 1);
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_IMAGE_DATA, 1);
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_IMAGE_DATA_END, 1);
+        s->t38_fe.ms_per_tx_chunk = 0;
+    }
+    else
+    {
+        /* Paced streaming mode, as used for UDP transports */
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_INDICATOR, INDICATOR_TX_COUNT);
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_CONTROL_DATA, DATA_TX_COUNT);
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_CONTROL_DATA_END, DATA_END_TX_COUNT);
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_IMAGE_DATA, DATA_TX_COUNT);
+        t38_set_redundancy_control(&s->t38_fe.t38, T38_PACKET_CATEGORY_IMAGE_DATA_END, DATA_END_TX_COUNT);
+        s->t38_fe.ms_per_tx_chunk = MS_PER_TX_CHUNK;
+    }
+    set_octets_per_data_packet(s, 300);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t31_set_mode(t31_state_t *s, int t38_mode)
+{
+    s->t38_mode = t38_mode;
+    span_log(&s->logging, SPAN_LOG_FLOW, "Mode set to %d\n", s->t38_mode);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(logging_state_t *) t31_get_logging_state(t31_state_t *s)
+{
+    return &s->logging;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t38_core_state_t *) t31_get_t38_core_state(t31_state_t *s)
+{
+    return &s->t38_fe.t38;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int t31_t38_fe_init(t31_state_t *t,
+                           t38_tx_packet_handler_t *tx_packet_handler,
+                           void *tx_packet_user_data)
+{
+    t31_t38_front_end_state_t *s;
+    
+    s = &t->t38_fe;
+
+    t38_core_init(&s->t38,
+                  process_rx_indicator,
+                  process_rx_data,
+                  process_rx_missing,
+                  (void *) t,
+                  tx_packet_handler,
+                  tx_packet_user_data);
+    s->t38.fastest_image_data_rate = 14400;
+
+    s->timed_step = T38_TIMED_STEP_NONE;
+    //s->iaf = T30_IAF_MODE_T37 | T30_IAF_MODE_T38;
+    s->iaf = T30_IAF_MODE_T38;
+
+    s->current_tx_data_type = T38_DATA_NONE;
+    s->next_tx_samples = 0;
+    s->chunking_modes = T38_CHUNKING_ALLOW_TEP_TIME;
+
+    t->hdlc_tx.ptr = 0;
+
+    hdlc_tx_init(&s->hdlc_tx_term,
+                 FALSE,
+                 1,
+                 FALSE,
+                 NULL,
+                 NULL);
+    hdlc_rx_init(&s->hdlc_rx_term,
+                 FALSE,
+                 TRUE,
+                 2,
+                 NULL,
+                 NULL);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t31_state_t *) t31_init(t31_state_t *s,
+                                     at_tx_handler_t *at_tx_handler,
+                                     void *at_tx_user_data,
+                                     t31_modem_control_handler_t *modem_control_handler,
+                                     void *modem_control_user_data,
+                                     t38_tx_packet_handler_t *tx_t38_packet_handler,
+                                     void *tx_t38_packet_user_data)
+{
+    int alloced;
+    
+    if (at_tx_handler == NULL  ||  modem_control_handler == NULL)
+        return NULL;
+
+    alloced = FALSE;
+    if (s == NULL)
+    {
+        if ((s = (t31_state_t *) malloc(sizeof (*s))) == NULL)
+            return NULL;
+        alloced = TRUE;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "T.31");
+
+    s->modem_control_handler = modem_control_handler;
+    s->modem_control_user_data = modem_control_user_data;
+    fax_modems_init(&s->audio.modems,
+                    FALSE,
+                    hdlc_accept_frame,
+                    hdlc_tx_underflow,
+                    non_ecm_put_bit,
+                    non_ecm_get_bit,
+                    tone_detected,
+                    (void *) s);
+    power_meter_init(&(s->audio.rx_power), 4);
+    s->audio.last_sample = 0;
+    s->audio.silence_threshold_power = power_meter_level_dbm0(-36);
+    s->at_state.rx_signal_present = FALSE;
+    s->at_state.rx_trained = FALSE;
+
+    s->at_state.do_hangup = FALSE;
+    s->at_state.line_ptr = 0;
+    s->audio.silence_heard = 0;
+    s->silence_awaited = 0;
+    s->call_samples = 0;
+    s->modem = FAX_MODEM_NONE;
+    s->at_state.transmit = TRUE;
+
+    if ((s->rx_queue = queue_init(NULL, 4096, QUEUE_WRITE_ATOMIC | QUEUE_READ_ATOMIC)) == NULL)
+    {
+        if (alloced)
+            free(s);
+        return NULL;
+    }
+    at_init(&s->at_state, at_tx_handler, at_tx_user_data, t31_modem_control_handler, s);
+    at_set_class1_handler(&s->at_state, process_class1_cmd, s);
+    s->at_state.dte_inactivity_timeout = DEFAULT_DTE_TIMEOUT;
+    if (tx_t38_packet_handler)
+    {
+        t31_t38_fe_init(s,
+                        tx_t38_packet_handler,
+                        tx_t38_packet_user_data);
+        t31_set_t38_config(s, FALSE);
+    }
+    s->t38_mode = FALSE;
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t31_release(t31_state_t *s)
+{
+    at_reset_call_info(&s->at_state);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t31_free(t31_state_t *s)
+{
+    t31_release(s);
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.