/*
 * SpanDSP - a series of DSP components for telephony
 *
 * t30.c - ITU T.30 FAX transfer processing
 *
 * Written by Steve Underwood <steveu@coppice.org>
 *
 * Copyright (C) 2003, 2004, 2005, 2006, 2007 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: t30.c,v 1.284 2009/02/04 13:18:53 steveu Exp $
 */

/*! \file */

#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#if defined(HAVE_TGMATH_H)
#include <tgmath.h>
#endif
#if defined(HAVE_MATH_H)
#include <math.h>
#endif
#include "floating_fudge.h"
#include <tiffio.h>

#include "spandsp/telephony.h"
#include "spandsp/logging.h"
#include "spandsp/bit_operations.h"
#include "spandsp/queue.h"
#include "spandsp/power_meter.h"
#include "spandsp/complex.h"
#include "spandsp/tone_generate.h"
#include "spandsp/async.h"
#include "spandsp/hdlc.h"
#include "spandsp/fsk.h"
#include "spandsp/v29rx.h"
#include "spandsp/v29tx.h"
#include "spandsp/v27ter_rx.h"
#include "spandsp/v27ter_tx.h"
#include "spandsp/t4.h"
#include "spandsp/t30_fcf.h"
#include "spandsp/t35.h"
#include "spandsp/t30.h"
#include "spandsp/t30_api.h"
#include "spandsp/t30_logging.h"

#include "spandsp/private/logging.h"
#include "spandsp/private/t4.h"
#include "spandsp/private/t30.h"

#include "t30_local.h"

#define MAX_MESSAGE_TRIES   3

#define ms_to_samples(t)    (((t)*SAMPLE_RATE)/1000)

/* T.30 defines the following call phases:
   Phase A: Call set-up.
       Exchange of CNG, CED and the called terminal identification.
   Phase B: Pre-message procedure for identifying and selecting the required facilities.
       Capabilities negotiation, and training, up the the confirmation to receive.
   Phase C: Message transmission (includes phasing and synchronization where appropriate).
       Transfer of the message at high speed.
   Phase D: Post-message procedure, including end-of-message and confirmation and multi-document procedures.
       End of message and acknowledgement.
   Phase E: Call release
       Final call disconnect. */
enum
{
    T30_PHASE_IDLE = 0,     /* Freshly initialised */
    T30_PHASE_A_CED,        /* Doing the CED (answer) sequence */
    T30_PHASE_A_CNG,        /* Doing the CNG (caller) sequence */
    T30_PHASE_B_RX,         /* Receiving pre-message control messages */
    T30_PHASE_B_TX,         /* Transmitting pre-message control messages */
    T30_PHASE_C_NON_ECM_RX, /* Receiving a document message in non-ECM mode */
    T30_PHASE_C_NON_ECM_TX, /* Transmitting a document message in non-ECM mode */
    T30_PHASE_C_ECM_RX,     /* Receiving a document message in ECM (HDLC) mode */
    T30_PHASE_C_ECM_TX,     /* Transmitting a document message in ECM (HDLC) mode */
    T30_PHASE_D_RX,         /* Receiving post-message control messages */
    T30_PHASE_D_TX,         /* Transmitting post-message control messages */
    T30_PHASE_E,            /* In phase E */
    T30_PHASE_CALL_FINISHED /* Call completely finished */
};

static const char *phase_names[] =
{
    "T30_PHASE_IDLE",
    "T30_PHASE_A_CED",
    "T30_PHASE_A_CNG",
    "T30_PHASE_B_RX",
    "T30_PHASE_B_TX",
    "T30_PHASE_C_NON_ECM_RX",
    "T30_PHASE_C_NON_ECM_TX",
    "T30_PHASE_C_ECM_RX",
    "T30_PHASE_C_ECM_TX",
    "T30_PHASE_D_RX",
    "T30_PHASE_D_TX",
    "T30_PHASE_E",
    "T30_PHASE_CALL_FINISHED"
};

/* These state names are modelled after places in the T.30 flow charts. */
enum
{
    T30_STATE_ANSWERING = 1,
    T30_STATE_B,
    T30_STATE_C,
    T30_STATE_D,
    T30_STATE_D_TCF,
    T30_STATE_D_POST_TCF,
    T30_STATE_F_TCF,
    T30_STATE_F_CFR,
    T30_STATE_F_FTT,
    T30_STATE_F_DOC_NON_ECM,
    T30_STATE_F_POST_DOC_NON_ECM,
    T30_STATE_F_DOC_ECM,
    T30_STATE_F_POST_DOC_ECM,
    T30_STATE_F_POST_RCP_MCF,
    T30_STATE_F_POST_RCP_PPR,
    T30_STATE_F_POST_RCP_RNR,
    T30_STATE_R,
    T30_STATE_T,
    T30_STATE_I,
    T30_STATE_II,
    T30_STATE_II_Q,
    T30_STATE_III_Q_MCF,
    T30_STATE_III_Q_RTP,
    T30_STATE_III_Q_RTN,
    T30_STATE_IV,
    T30_STATE_IV_PPS_NULL,
    T30_STATE_IV_PPS_Q,
    T30_STATE_IV_PPS_RNR,
    T30_STATE_IV_CTC,
    T30_STATE_IV_EOR,
    T30_STATE_IV_EOR_RNR,
    T30_STATE_CALL_FINISHED
};

enum
{
    T30_MIN_SCAN_20MS = 0,
    T30_MIN_SCAN_5MS = 1,
    T30_MIN_SCAN_10MS = 2,
    T30_MIN_SCAN_40MS = 4,
    T30_MIN_SCAN_0MS = 7,
};

enum
{
    T30_MODE_SEND_DOC = 1,
    T30_MODE_RECEIVE_DOC
};

/*! These are internal assessments of received image quality, used to determine whether we
    continue, retrain, or abandon the call. */
enum
{
    T30_COPY_QUALITY_PERFECT = 0,
    T30_COPY_QUALITY_GOOD,
    T30_COPY_QUALITY_POOR,
    T30_COPY_QUALITY_BAD
};

enum
{
    DISBIT1 = 0x01,
    DISBIT2 = 0x02,
    DISBIT3 = 0x04,
    DISBIT4 = 0x08,
    DISBIT5 = 0x10,
    DISBIT6 = 0x20,
    DISBIT7 = 0x40,
    DISBIT8 = 0x80
};

/*! There are high level indications of what is happening at any instant, to guide the cleanup
    process if the call is abandoned. */
enum
{
    OPERATION_IN_PROGRESS_NONE = 0,
    OPERATION_IN_PROGRESS_T4_RX,
    OPERATION_IN_PROGRESS_T4_TX
};

/* All timers specified in milliseconds */

/* Time-out T0 defines the amount of time an automatic calling terminal waits for the called terminal
to answer the call.
T0 begins after the dialling of the number is completed and is reset:
a) when T0 times out; or
b) when timer T1 is started; or
c) if the terminal is capable of detecting any condition which indicates that the call will not be
   successful, when such a condition is detected.
The recommended value of T0 is 60+-5s. However, when it is anticipated that a long call set-up
time may be encountered, an alternative value of up to 120s may be used.
NOTE - National regulations may require the use of other values for T0. */
#define DEFAULT_TIMER_T0                60000

/* Time-out T1 defines the amount of time two terminals will continue to attempt to identify each
other. T1 is 35+-5s, begins upon entering phase B, and is reset upon detecting a valid signal or
when T1 times out.
For operating methods 3 and 4 (see 3.1), the calling terminal starts time-out T1 upon reception of
the V.21 modulation scheme.
For operating method 4 bis a (see 3.1), the calling terminal starts time-out T1 upon starting
transmission using the V.21 modulation scheme. */
#define DEFAULT_TIMER_T1                35000

/* Time-out T2 makes use of the tight control between commands and responses to detect the loss of
command/response synchronization. T2 is 6+-1s, and begins when initiating a command search
(e.g., the first entrance into the "command received" subroutine, reference flow diagram in section 5.2).
T2 is reset when an HDLC flag is received or when T2 times out. */
#define DEFAULT_TIMER_T2                7000

/* Once HDLC flags begin, T2 is reset, and a 3s timer begins. This timer is unnamed in T.30. Here we
term it T2A. No tolerance is specified for this timer. T2A specifies the maximum time to wait for the
end of a frame, after the initial flag has been seen. */
#define DEFAULT_TIMER_T2A               3000

/* If the HDLC carrier falls during reception, we need to apply a minimum time before continuing. if we
   don't, there are circumstances where we could continue and reply before the incoming signals have
   really finished. E.g. if a bad DCS is received in a DCS-TCF sequence, we need wait for the TCF
   carrier to pass, before continuing. This timer is specified as 200ms, but no tolerance is specified.
   It is unnamed in T.30. Here we termin it T2B */
#define DEFAULT_TIMER_T2B               200

/* Time-out T3 defines the amount of time a terminal will attempt to alert the local operator in
response to a procedural interrupt. Failing to achieve operator intervention, the terminal will
discontinue this attempt and shall issue other commands or responses. T3 is 10+-5s, begins on the
first detection of a procedural interrupt command/response signal (i.e., PIN/PIP or PRI-Q) and is
reset when T3 times out or when the operator initiates a line request. */
#define DEFAULT_TIMER_T3                15000

/* Time-out T4 defines the amount of time a terminal will wait for flags to begin, when waiting for a
response from a remote terminal. T2 is 3s +-15%, and begins when initiating a response search
(e.g., the first entrance into the "response received" subroutine, reference flow diagram in section 5.2).
T4 is reset when an HDLC flag is received or when T4 times out.
NOTE - For manual FAX units, the value of timer T4 may be either 3.0s +-15% or 4.5s +-15%.
If the value of 4.5s is used, then after detection of a valid response to the first DIS, it may
be reduced to 3.0s +-15%. T4 = 3.0s +-15% for automatic units. */
#define DEFAULT_TIMER_T4                3450

/* Once HDLC flags begin, T4 is reset, and a 3s timer begins. This timer is unnamed in T.30. Here we
term it T4A. No tolerance is specified for this timer. T4A specifies the maximum time to wait for the
end of a frame, after the initial flag has been seen. */
#define DEFAULT_TIMER_T4A               3000

/* If the HDLC carrier falls during reception, we need to apply a minimum time before continuing. if we
   don't, there are circumstances where we could continue and reply before the incoming signals have
   really finished. E.g. if a bad DCS is received in a DCS-TCF sequence, we need wait for the TCF
   carrier to pass, before continuing. This timer is specified as 200ms, but no tolerance is specified.
   It is unnamed in T.30. Here we termin it T4B */
#define DEFAULT_TIMER_T4B               200

/* Time-out T5 is defined for the optional T.4 error correction mode. Time-out T5 defines the amount
of time waiting for clearance of the busy condition of the receiving terminal. T5 is 60+-5s and
begins on the first detection of the RNR response. T5 is reset when T5 times out or the MCF or PIP
response is received or when the ERR or PIN response is received in the flow control process after
transmitting the EOR command. If the timer T5 has expired, the DCN command is transmitted for
call release. */
#define DEFAULT_TIMER_T5                65000

/* (Annex C - ISDN) Time-out T6 defines the amount of time two terminals will continue to attempt to
identify each other. T6 is 5+-0.5s. The timeout begins upon entering Phase B, and is reset upon
detecting a valid signal, or when T6 times out. */
#define DEFAULT_TIMER_T6                5000

/* (Annex C - ISDN) Time-out T7 is used to detect loss of command/response synchronization. T7 is 6+-1s.
The timeout begins when initiating a command search (e.g., the first entrance into the "command received"
subroutine - see flow diagram in C.5) and is reset upon detecting a valid signal or when T7 times out. */
#define DEFAULT_TIMER_T7                7000

/* (Annex C - ISDN) Time-out T8 defines the amount of time waiting for clearance of the busy condition
of the receiving terminal. T8 is 10+-1s. The timeout begins on the first detection of the combination
of no outstanding corrections and the RNR response. T8 is reset when T8 times out or MCF response is
received. If the timer T8 expires, a DCN command is transmitted for call release. */
#define DEFAULT_TIMER_T8                10000

/* Final time we allow for things to flush through the system, before we disconnect, in milliseconds.
   200ms should be fine for a PSTN call. For a T.38 call something longer is desirable. */
#define FINAL_FLUSH_TIME                1000

/* The number of PPRs received before CTC or EOR is sent in ECM mode. T.30 defines this as 4,
   but it could be varied, and the Japanese spec, for example, does make this value a
   variable. */
#define PPR_LIMIT_BEFORE_CTC_OR_EOR     4

enum
{
    TIMER_IS_T2 = 0,
    TIMER_IS_T2A = 1,
    TIMER_IS_T2B = 2,
    TIMER_IS_T2C = 3,
    TIMER_IS_T4 = 4,
    TIMER_IS_T4A = 5,
    TIMER_IS_T4B = 6,
    TIMER_IS_T4C = 7
};

/* Start points in the fallback table for different capabilities */
#define T30_V17_FALLBACK_START          0
#define T30_V29_FALLBACK_START          3
#define T30_V27TER_FALLBACK_START       6

static const struct
{
    int bit_rate;
    int modem_type;
    int which;
    uint8_t dcs_code;
} fallback_sequence[] =
{
    {14400, T30_MODEM_V17,      T30_SUPPORT_V17,    DISBIT6},
    {12000, T30_MODEM_V17,      T30_SUPPORT_V17,    (DISBIT6 | DISBIT4)},
    { 9600, T30_MODEM_V17,      T30_SUPPORT_V17,    (DISBIT6 | DISBIT3)},
    { 9600, T30_MODEM_V29,      T30_SUPPORT_V29,    DISBIT3},
    { 7200, T30_MODEM_V17,      T30_SUPPORT_V17,    (DISBIT6 | DISBIT4 | DISBIT3)},
    { 7200, T30_MODEM_V29,      T30_SUPPORT_V29,    (DISBIT4 | DISBIT3)},
    { 4800, T30_MODEM_V27TER,   T30_SUPPORT_V27TER, DISBIT4},
    { 2400, T30_MODEM_V27TER,   T30_SUPPORT_V27TER, 0},
    {    0, 0, 0, 0}
};

static void queue_phase(t30_state_t *s, int phase);
static void set_phase(t30_state_t *s, int phase);
static void set_state(t30_state_t *s, int state);
static void send_simple_frame(t30_state_t *s, int type);
static void send_frame(t30_state_t *s, const uint8_t *fr, int frlen);
static void send_dcn(t30_state_t *s);
static void repeat_last_command(t30_state_t *s);
static void disconnect(t30_state_t *s);
static void decode_20digit_msg(t30_state_t *s, char *msg, const uint8_t *pkt, int len);
static void decode_url_msg(t30_state_t *s, char *msg, const uint8_t *pkt, int len);
static int set_min_scan_time_code(t30_state_t *s);
static int send_cfr_sequence(t30_state_t *s, int start);
static void timer_t2_start(t30_state_t *s);
static void timer_t2a_start(t30_state_t *s);
static void timer_t2b_start(t30_state_t *s);
static void timer_t4_start(t30_state_t *s);
static void timer_t4a_start(t30_state_t *s);
static void timer_t4b_start(t30_state_t *s);

#define test_ctrl_bit(s,bit) ((s)[3 + ((bit - 1)/8)] & (1 << ((bit - 1)%8)))
#define set_ctrl_bit(s,bit) (s)[3 + ((bit - 1)/8)] |= (1 << ((bit - 1)%8))
#define set_ctrl_bits(s,val,bit) (s)[3 + ((bit - 1)/8)] |= ((val) << ((bit - 1)%8))
#define clr_ctrl_bit(s,bit) (s)[3 + ((bit - 1)/8)] &= ~(1 << ((bit - 1)%8))

static int tx_start_page(t30_state_t *s)
{
    if (t4_tx_start_page(&(s->t4)))
    {
        t4_tx_end(&(s->t4));
        s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
        return -1;
    }
    s->ecm_block = 0;
    s->error_correcting_mode_retries = 0;
    span_log(&s->logging, SPAN_LOG_FLOW, "Starting page %d of transfer\n", s->ecm_tx_page + 1);
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int tx_end_page(t30_state_t *s)
{
    s->retries = 0;
    if (t4_tx_end_page(&(s->t4)) == 0)
    {
        s->ecm_tx_page++;
        s->ecm_block = 0;
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int rx_start_page(t30_state_t *s)
{
    int i;

    t4_rx_set_image_width(&s->t4, s->image_width);
    t4_rx_set_sub_address(&s->t4, s->rx_info.sub_address);
    t4_rx_set_dcs(&s->t4, s->rx_dcs_string);
    t4_rx_set_far_ident(&s->t4, s->rx_info.ident);
    t4_rx_set_vendor(&s->t4, s->vendor);
    t4_rx_set_model(&s->t4, s->model);

    t4_rx_set_rx_encoding(&s->t4, s->line_encoding);
    t4_rx_set_x_resolution(&s->t4, s->x_resolution);
    t4_rx_set_y_resolution(&s->t4, s->y_resolution);

    if (t4_rx_start_page(&(s->t4)))
        return -1;
    /* Clear the buffer */
    for (i = 0;  i < 256;  i++)
        s->ecm_len[i] = -1;
    s->ecm_block = 0;
    s->ecm_frames = -1;
    s->ecm_frames_this_tx_burst = 0;
    s->error_correcting_mode_retries = 0;
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int rx_end_page(t30_state_t *s)
{
    if (t4_rx_end_page(&(s->t4)) == 0)
    {
        s->ecm_rx_page++;
        s->ecm_block = 0;
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int copy_quality(t30_state_t *s)
{
    t4_stats_t stats;
    int quality;

    t4_get_transfer_statistics(&s->t4, &stats);
    /* There is no specification for judging copy quality. However, we need to classify
       it at three levels, to control what we do next: OK; tolerable, but retrain;
       intolerable. */
    /* Based on the thresholds used in the TSB85 tests, we consider:
            <5% bad rows is OK
            <15% bad rows to be tolerable, but retrain
            >15% bad rows to be intolerable
     */
    span_log(&s->logging, SPAN_LOG_FLOW, "Page no = %d\n", stats.pages_transferred + 1);
    span_log(&s->logging, SPAN_LOG_FLOW, "Image size = %d x %d pixels\n", stats.width, stats.length);
    span_log(&s->logging, SPAN_LOG_FLOW, "Image resolution = %d/m x %d/m\n", stats.x_resolution, stats.y_resolution);
    span_log(&s->logging, SPAN_LOG_FLOW, "Bad rows = %d\n", stats.bad_rows);
    span_log(&s->logging, SPAN_LOG_FLOW, "Longest bad row run = %d\n", stats.longest_bad_row_run);
    if (stats.bad_rows == 0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Page quality is perfect\n");
        quality = T30_COPY_QUALITY_PERFECT;
    }
    else if (stats.bad_rows*20 < stats.length)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Page quality is good\n");
        quality = T30_COPY_QUALITY_GOOD;
    }
    else if (stats.bad_rows*20 < stats.length*3)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Page quality is poor\n");
        quality = T30_COPY_QUALITY_POOR;
    }
    else
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Page quality is bad\n");
        quality = T30_COPY_QUALITY_BAD;
    }
    return quality;
}
/*- End of function --------------------------------------------------------*/

static void release_resources(t30_state_t *s)
{
    if (s->tx_info.nsf)
    {
        free(s->tx_info.nsf);
        s->tx_info.nsf = NULL;
    }
    s->tx_info.nsf_len = 0;
    if (s->tx_info.nsc)
    {
        free(s->tx_info.nsc);
        s->tx_info.nsc = NULL;
    }
    s->tx_info.nsc_len = 0;
    if (s->tx_info.nss)
    {
        free(s->tx_info.nss);
        s->tx_info.nss = NULL;
    }
    s->tx_info.nss_len = 0;
    if (s->tx_info.tsa)
    {
        free(s->tx_info.tsa);
        s->tx_info.tsa = NULL;
    }
    if (s->tx_info.ira)
    {
        free(s->tx_info.ira);
        s->tx_info.ira = NULL;
    }
    if (s->tx_info.cia)
    {
        free(s->tx_info.cia);
        s->tx_info.cia = NULL;
    }
    if (s->tx_info.isp)
    {
        free(s->tx_info.isp);
        s->tx_info.isp = NULL;
    }
    if (s->tx_info.csa)
    {
        free(s->tx_info.csa);
        s->tx_info.csa = NULL;
    }

    if (s->rx_info.nsf)
    {
        free(s->rx_info.nsf);
        s->rx_info.nsf = NULL;
    }
    s->rx_info.nsf_len = 0;
    if (s->rx_info.nsc)
    {
        free(s->rx_info.nsc);
        s->rx_info.nsc = NULL;
    }
    s->rx_info.nsc_len = 0;
    if (s->rx_info.nss)
    {
        free(s->rx_info.nss);
        s->rx_info.nss = NULL;
    }
    s->rx_info.nss_len = 0;
    if (s->rx_info.tsa)
    {
        free(s->rx_info.tsa);
        s->rx_info.tsa = NULL;
    }
    if (s->rx_info.ira)
    {
        free(s->rx_info.ira);
        s->rx_info.ira = NULL;
    }
    if (s->rx_info.cia)
    {
        free(s->rx_info.cia);
        s->rx_info.cia = NULL;
    }
    if (s->rx_info.isp)
    {
        free(s->rx_info.isp);
        s->rx_info.isp = NULL;
    }
    if (s->rx_info.csa)
    {
        free(s->rx_info.csa);
        s->rx_info.csa = NULL;
    }
}
/*- End of function --------------------------------------------------------*/

static uint8_t check_next_tx_step(t30_state_t *s)
{
    int more;

    if (t4_tx_more_pages(&(s->t4)) == 0)
        return (s->local_interrupt_pending)  ?  T30_PRI_MPS  :  T30_MPS;
    /* Call a user handler, if one is set, to check if another document is to be sent.
       If so, we send an EOM, rather than an EOP. Then we will renegotiate, and the new
       document will begin. */
    if (s->document_handler)
        more = s->document_handler(s, s->document_user_data, 0);
    else
        more = FALSE;
    if (more)
    {
        //if (test_ctrl_bit(s->far_dis_dtc_frame, 34))
        //    return T30_EOS;
        return (s->local_interrupt_pending)  ?  T30_PRI_EOM  :  T30_EOM;
    }
    return (s->local_interrupt_pending)  ?  T30_PRI_EOP  :  T30_EOP;
}
/*- End of function --------------------------------------------------------*/

static int get_partial_ecm_page(t30_state_t *s)
{
    int i;
    int len;

    s->ppr_count = 0;
    s->ecm_progress = 0;
    /* Fill our partial page buffer with a partial page. Use the negotiated preferred frame size
       as the basis for the size of the frames produced. */
    /* We fill the buffer with complete HDLC frames, ready to send out. */
    /* The frames are all marked as not being final frames. When sent, the are followed by a partial
       page signal, which is marked as the final frame. */
    for (i = 3;  i < 32 + 3;  i++)
        s->ecm_frame_map[i] = 0xFF;
    for (i = 0;  i < 256;  i++)
    {
        s->ecm_len[i] = -1;
        s->ecm_data[i][0] = 0xFF;
        s->ecm_data[i][1] = 0x03;
        s->ecm_data[i][2] = T4_FCD;
        /* These frames contain a frame sequence number within the partial page (one octet) followed
           by some image data. */
        s->ecm_data[i][3] = (uint8_t) i;
        if ((len = t4_tx_get_chunk(&s->t4, &s->ecm_data[i][4], s->octets_per_ecm_frame)) < s->octets_per_ecm_frame)
        {
            /* The image is not big enough to fill the entire buffer */
            /* We need to pad to a full frame, as most receivers expect that. */
            if (len > 0)
            {
                memset(&s->ecm_data[i][4 + len], 0, s->octets_per_ecm_frame - len);
                s->ecm_len[i++] = (int16_t) (s->octets_per_ecm_frame + 4);
            }
            s->ecm_frames = i;
            span_log(&s->logging, SPAN_LOG_FLOW, "Partial page buffer contains %d frames (%d per frame)\n", i, s->octets_per_ecm_frame);
            s->ecm_at_page_end = TRUE;
            return i;
        }
        s->ecm_len[i] = (int16_t) (4 + len);
    }
    /* We filled the entire buffer */
    s->ecm_frames = 256;
    span_log(&s->logging, SPAN_LOG_FLOW, "Partial page buffer full (%d per frame)\n", s->octets_per_ecm_frame);
    s->ecm_at_page_end = ((t4_tx_check_bit(&(s->t4)) & 2) != 0);
    return 256;
}
/*- End of function --------------------------------------------------------*/

static int t30_ecm_commit_partial_page(t30_state_t *s)
{
    int i;
    int image_ended;

    span_log(&s->logging, SPAN_LOG_FLOW, "Commiting partial page - block %d, %d frames\n", s->ecm_block, s->ecm_frames);
    image_ended = FALSE;
    for (i = 0;  i < s->ecm_frames;  i++)
    {
        if (t4_rx_put_chunk(&s->t4, s->ecm_data[i], s->ecm_len[i]))
        {
            /* This is the end of the document */
            /* Clear the buffer */
            for (i = 0;  i < 256;  i++)
                s->ecm_len[i] = -1;
            s->ecm_frames = -1;
            image_ended = TRUE;
            break;
        }
    }
    /* Clear the buffer */
    for (i = 0;  i < 256;  i++)
        s->ecm_len[i] = -1;
    s->ecm_block++;
    s->ecm_frames = -1;
    return (image_ended)  ?  -1  :  0;
}
/*- End of function --------------------------------------------------------*/

static int send_next_ecm_frame(t30_state_t *s)
{
    int i;
    uint8_t frame[3];

    if (s->ecm_current_tx_frame < s->ecm_frames)
    {
        /* Search for the next frame, within the current partial page, which has
           not been tagged as transferred OK. */
        for (i = s->ecm_current_tx_frame;  i < s->ecm_frames;  i++)
        {
            if (s->ecm_len[i] >= 0)
            {
                send_frame(s, s->ecm_data[i], s->ecm_len[i]);
                s->ecm_current_tx_frame = i + 1;
                s->ecm_frames_this_tx_burst++;
                return 0;
            }
        }
        s->ecm_current_tx_frame = s->ecm_frames;
    }
    if (s->ecm_current_tx_frame <= s->ecm_frames + 3)
    {
        /* We have sent all the FCD frames. Send some RCP frames. Three seems to be
           a popular number, to minimise the risk of a bit error stopping the receiving
           end from recognising the RCP. */
        s->ecm_current_tx_frame++;
        /* The RCP frame is an odd man out, as its a simple 1 byte control
           frame, but is specified to not have the final bit set. It doesn't
           seem to have the DIS received bit set, either. */
        frame[0] = 0xFF;
        frame[1] = 0x03;
        frame[2] = T4_RCP;
        send_frame(s, frame, 3);
        /* In case we are just after a CTC/CTR exchange, which kicked us back to long training */
        s->short_train = TRUE;
        return 0;
    }
    return -1;
}
/*- End of function --------------------------------------------------------*/

static void send_rr(t30_state_t *s)
{
    if (s->current_status != T30_ERR_TX_T5EXP)
        send_simple_frame(s, T30_RR);
    else
        send_dcn(s);
}
/*- End of function --------------------------------------------------------*/

static int send_first_ecm_frame(t30_state_t *s)
{
    s->ecm_current_tx_frame = 0;
    s->ecm_frames_this_tx_burst = 0;
    return send_next_ecm_frame(s);
}
/*- End of function --------------------------------------------------------*/

static void print_frame(t30_state_t *s, const char *io, const uint8_t *msg, int len)
{
    span_log(&s->logging,
             SPAN_LOG_FLOW,
             "%s %s with%s final frame tag\n",
             io,
             t30_frametype(msg[2]),
             (msg[1] & 0x10)  ?  ""  :  "out");
    span_log_buf(&s->logging, SPAN_LOG_FLOW, io, msg, len);
}
/*- End of function --------------------------------------------------------*/

static void send_frame(t30_state_t *s, const uint8_t *msg, int len)
{
    print_frame(s, "Tx: ", msg, len);

    if (s->real_time_frame_handler)
        s->real_time_frame_handler(s, s->real_time_frame_user_data, FALSE, msg, len);
    if (s->send_hdlc_handler)
        s->send_hdlc_handler(s->send_hdlc_user_data, msg, len);
}
/*- End of function --------------------------------------------------------*/

static void send_simple_frame(t30_state_t *s, int type)
{
    uint8_t frame[3];

    /* The simple command/response frames are always final frames */
    frame[0] = 0xFF;
    frame[1] = 0x13;
    frame[2] = (uint8_t) (type | s->dis_received);
    send_frame(s, frame, 3);
}
/*- End of function --------------------------------------------------------*/

static void send_20digit_msg_frame(t30_state_t *s, int cmd, char *msg)
{
    size_t len;
    int p;
    uint8_t frame[23];

    len = strlen(msg);
    p = 0;
    frame[p++] = 0xFF;
    frame[p++] = 0x03;
    frame[p++] = (uint8_t) (cmd | s->dis_received);
    while (len > 0)
        frame[p++] = msg[--len];
    while (p < 23)
        frame[p++] = ' ';
    send_frame(s, frame, 23);
}
/*- End of function --------------------------------------------------------*/

static int send_nsf_frame(t30_state_t *s)
{
    /* Only send if there is an NSF message to send. */
    if (s->tx_info.nsf  &&  s->tx_info.nsf_len)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending user supplied NSF - %d octets\n", s->tx_info.nsf_len);
        s->tx_info.nsf[0] = 0xFF;
        s->tx_info.nsf[1] = 0x03;
        s->tx_info.nsf[2] = (uint8_t) (T30_NSF | s->dis_received);
        send_frame(s, s->tx_info.nsf, s->tx_info.nsf_len + 3);
        return TRUE;
    }
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_nss_frame(t30_state_t *s)
{
    /* Only send if there is an NSF message to send. */
    if (s->tx_info.nss  &&  s->tx_info.nss_len)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending user supplied NSS - %d octets\n", s->tx_info.nss_len);
        s->tx_info.nss[0] = 0xFF;
        s->tx_info.nss[1] = 0x03;
        s->tx_info.nss[2] = (uint8_t) (T30_NSS | s->dis_received);
        send_frame(s, s->tx_info.nss, s->tx_info.nss_len + 3);
        return TRUE;
    }
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_nsc_frame(t30_state_t *s)
{
    /* Only send if there is an NSF message to send. */
    if (s->tx_info.nsc  &&  s->tx_info.nsc_len)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending user supplied NSC - %d octets\n", s->tx_info.nsc_len);
        s->tx_info.nsc[0] = 0xFF;
        s->tx_info.nsc[1] = 0x03;
        s->tx_info.nsc[2] = (uint8_t) (T30_NSC | s->dis_received);
        send_frame(s, s->tx_info.nsc, s->tx_info.nsc_len + 3);
        return TRUE;
    }
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_ident_frame(t30_state_t *s, uint8_t cmd)
{
    if (s->tx_info.ident[0])
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending ident '%s'\n", s->tx_info.ident);
        /* 'cmd' should be T30_TSI, T30_CIG or T30_CSI */
        send_20digit_msg_frame(s, cmd, s->tx_info.ident);
        return TRUE;
    }
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_psa_frame(t30_state_t *s)
{
    if (test_ctrl_bit(s->far_dis_dtc_frame, 35)  &&  s->tx_info.polled_sub_address[0])
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending polled sub-address '%s'\n", s->tx_info.polled_sub_address);
        send_20digit_msg_frame(s, T30_PSA, s->tx_info.polled_sub_address);
        set_ctrl_bit(s->local_dis_dtc_frame, 35);
        return TRUE;
    }
    clr_ctrl_bit(s->local_dis_dtc_frame, 35);
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_sep_frame(t30_state_t *s)
{
    if (test_ctrl_bit(s->far_dis_dtc_frame, 47)  &&  s->tx_info.selective_polling_address[0])
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending selective polling address '%s'\n", s->tx_info.selective_polling_address);
        send_20digit_msg_frame(s, T30_SEP, s->tx_info.selective_polling_address);
        set_ctrl_bit(s->local_dis_dtc_frame, 47);
        return TRUE;
    }
    clr_ctrl_bit(s->local_dis_dtc_frame, 47);
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_sid_frame(t30_state_t *s)
{
    /* Only send if there is an ID to send. */
    if (test_ctrl_bit(s->far_dis_dtc_frame, 50)  &&  s->tx_info.sender_ident[0])
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending sender identification '%s'\n", s->tx_info.sender_ident);
        send_20digit_msg_frame(s, T30_SID, s->tx_info.sender_ident);
        set_ctrl_bit(s->dcs_frame, 50);
        return TRUE;
    }
    clr_ctrl_bit(s->dcs_frame, 50);
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_pwd_frame(t30_state_t *s)
{
    /* Only send if there is a password to send. */
    if (test_ctrl_bit(s->far_dis_dtc_frame, 50)  &&  s->tx_info.password[0])
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending password '%s'\n", s->tx_info.password);
        send_20digit_msg_frame(s, T30_PWD, s->tx_info.password);
        set_ctrl_bit(s->local_dis_dtc_frame, 50);
        return TRUE;
    }
    clr_ctrl_bit(s->local_dis_dtc_frame, 50);
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_sub_frame(t30_state_t *s)
{
    /* Only send if there is a sub-address to send. */
    if (test_ctrl_bit(s->far_dis_dtc_frame, 49)  &&  s->tx_info.sub_address[0])
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending sub-address '%s'\n", s->tx_info.sub_address);
        send_20digit_msg_frame(s, T30_SUB, s->tx_info.sub_address);
        set_ctrl_bit(s->dcs_frame, 49);
        return TRUE;
    }
    clr_ctrl_bit(s->dcs_frame, 49);
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_tsa_frame(t30_state_t *s)
{
    if ((test_ctrl_bit(s->far_dis_dtc_frame, 1)  ||  test_ctrl_bit(s->far_dis_dtc_frame, 3))  &&  0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending transmitting subscriber internet address '%s'\n", "");
        return TRUE;
    }
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_ira_frame(t30_state_t *s)
{
    if (test_ctrl_bit(s->far_dis_dtc_frame, 102)  &&  0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending internet routing address '%s'\n", "");
        set_ctrl_bit(s->dcs_frame, 102);
        return TRUE;
    }
    clr_ctrl_bit(s->dcs_frame, 102);
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_cia_frame(t30_state_t *s)
{
    if ((test_ctrl_bit(s->far_dis_dtc_frame, 1)  ||  test_ctrl_bit(s->far_dis_dtc_frame, 3))  &&  0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending calling subscriber internet address '%s'\n", "");
        return TRUE;
    }
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

static int send_isp_frame(t30_state_t *s)
{
    if (test_ctrl_bit(s->far_dis_dtc_frame, 101)  &&  0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending internet selective polling address '%s'\n", "");
        set_ctrl_bit(s->local_dis_dtc_frame, 101);
        return TRUE;
    }
    clr_ctrl_bit(s->local_dis_dtc_frame, 101);
    return FALSE;
}
/*- End of function --------------------------------------------------------*/

#if 0
static int send_csa_frame(t30_state_t *s)
{
#if 0
    if (("in T.37 mode"  ||  "in T.38 mode")  &&  0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Sending called subscriber internet address '%s'\n", "");
        return TRUE;
    }
#endif
    return FALSE;
}
/*- End of function --------------------------------------------------------*/
#endif

static int send_pps_frame(t30_state_t *s)
{
    uint8_t frame[7];
    
    frame[0] = 0xFF;
    frame[1] = 0x13;
    frame[2] = (uint8_t) (T30_PPS | s->dis_received);
    frame[3] = (s->ecm_at_page_end)  ?  ((uint8_t) (s->next_tx_step | s->dis_received))  :  T30_NULL;
    frame[4] = (uint8_t) (s->ecm_tx_page & 0xFF);
    frame[5] = (uint8_t) (s->ecm_block & 0xFF);
    frame[6] = (uint8_t) ((s->ecm_frames_this_tx_burst == 0)  ?  0  :  (s->ecm_frames_this_tx_burst - 1));
    span_log(&s->logging, SPAN_LOG_FLOW, "Sending PPS + %s\n", t30_frametype(frame[3]));
    send_frame(s, frame, 7);
    return frame[3] & 0xFE;
}
/*- End of function --------------------------------------------------------*/

static int set_dis_or_dtc(t30_state_t *s)
{
    /* Whether we use a DIS or a DTC is determined by whether we have received a DIS.
       We just need to edit the prebuilt message. */
    s->local_dis_dtc_frame[2] = (uint8_t) (T30_DIS | s->dis_received);
    /* If we have a file name to receive into, then we are receive capable */
    if (s->rx_file[0])
        set_ctrl_bit(s->local_dis_dtc_frame, 10);
    else
        clr_ctrl_bit(s->local_dis_dtc_frame, 10);
    /* If we have a file name to transmit, then we are ready to transmit (polling) */
    if (s->tx_file[0])
        set_ctrl_bit(s->local_dis_dtc_frame, 9);
    else
        clr_ctrl_bit(s->local_dis_dtc_frame, 9);
    return 0;
}
/*- End of function --------------------------------------------------------*/

int t30_build_dis_or_dtc(t30_state_t *s)
{
    int i;

    /* Build a skeleton for the DIS and DTC messages. This will be edited for
       the dynamically changing capabilities (e.g. can receive) just before
       it is sent. It might also be edited if the application changes our
       capabilities (e.g. disabling fine mode). Right now we set up all the
       unchanging stuff about what we are capable of doing. */
    s->local_dis_dtc_frame[0] = 0xFF;
    s->local_dis_dtc_frame[1] = 0x13;
    s->local_dis_dtc_frame[2] = (uint8_t) (T30_DIS | s->dis_received);
    for (i = 3;  i < 19;  i++)
        s->local_dis_dtc_frame[i] = 0x00;

    /* Always say 256 octets per ECM frame preferred, as 64 is never used in the
       real world. */
    if ((s->iaf & T30_IAF_MODE_T37))
        set_ctrl_bit(s->local_dis_dtc_frame, 1);
    if ((s->iaf & T30_IAF_MODE_T38))
        set_ctrl_bit(s->local_dis_dtc_frame, 3);
    /* No 3G mobile  */
    /* No V.8 */
    /* 256 octets preferred - don't bother making this optional, as everything uses 256 */
    /* Ready to transmit a fax (polling) will be determined separately, and this message edited. */
    /* Ready to receive a fax will be determined separately, and this message edited. */
    /* With no modems set we are actually selecting V.27ter fallback at 2400bps */
    if ((s->supported_modems & T30_SUPPORT_V27TER))
        set_ctrl_bit(s->local_dis_dtc_frame, 12);
    if ((s->supported_modems & T30_SUPPORT_V29))
        set_ctrl_bit(s->local_dis_dtc_frame, 11);
    /* V.17 is only valid when combined with V.29 and V.27ter, so if we enable V.17 we force the others too. */
    if ((s->supported_modems & T30_SUPPORT_V17))
        s->local_dis_dtc_frame[4] |= (DISBIT6 | DISBIT4 | DISBIT3);
    if ((s->supported_resolutions & T30_SUPPORT_FINE_RESOLUTION))
        set_ctrl_bit(s->local_dis_dtc_frame, 15);
    if ((s->supported_compressions & T30_SUPPORT_T4_2D_COMPRESSION))
        set_ctrl_bit(s->local_dis_dtc_frame, 16);
    /* 215mm wide is always supported */
    if ((s->supported_image_sizes & T30_SUPPORT_303MM_WIDTH))
        set_ctrl_bit(s->local_dis_dtc_frame, 18);
    else if ((s->supported_image_sizes & T30_SUPPORT_255MM_WIDTH))
        set_ctrl_bit(s->local_dis_dtc_frame, 17);
    /* A4 is always supported. */
    if ((s->supported_image_sizes & T30_SUPPORT_UNLIMITED_LENGTH))
        set_ctrl_bit(s->local_dis_dtc_frame, 20);
    else if ((s->supported_image_sizes & T30_SUPPORT_B4_LENGTH))
        set_ctrl_bit(s->local_dis_dtc_frame, 19);
    /* No scan-line padding required, but some may be specified by the application. */
    set_ctrl_bits(s->local_dis_dtc_frame, s->local_min_scan_time_code, 21);
    if ((s->supported_compressions & T30_SUPPORT_NO_COMPRESSION))
        set_ctrl_bit(s->local_dis_dtc_frame, 26);
    if (s->ecm_allowed)
    {
        /* ECM allowed */
        set_ctrl_bit(s->local_dis_dtc_frame, 27);
        /* Only offer the option of fancy compression schemes, if we are
           also offering the ECM option needed to support them. */
        if ((s->supported_compressions & T30_SUPPORT_T6_COMPRESSION))
            set_ctrl_bit(s->local_dis_dtc_frame, 31);
        if ((s->supported_compressions & T30_SUPPORT_T43_COMPRESSION))
            set_ctrl_bit(s->local_dis_dtc_frame, 36);
        if ((s->supported_compressions & T30_SUPPORT_T85_COMPRESSION))
            set_ctrl_bit(s->local_dis_dtc_frame, 78);
        /* No T.85 optional L0. */
        if ((s->supported_compressions & T30_SUPPORT_T45_COMPRESSION))
            set_ctrl_bit(s->local_dis_dtc_frame, 116);
    }
    if ((s->supported_t30_features & T30_SUPPORT_FIELD_NOT_VALID))
        set_ctrl_bit(s->local_dis_dtc_frame, 33);
    if ((s->supported_t30_features & T30_SUPPORT_MULTIPLE_SELECTIVE_POLLING))
        set_ctrl_bit(s->local_dis_dtc_frame, 34);
    if ((s->supported_t30_features & T30_SUPPORT_POLLED_SUB_ADDRESSING))
        set_ctrl_bit(s->local_dis_dtc_frame, 35);
    /* No plane interleave */
    /* No G.726 */
    /* No extended voice coding */
    if ((s->supported_resolutions & T30_SUPPORT_SUPERFINE_RESOLUTION))
        set_ctrl_bit(s->local_dis_dtc_frame, 41);
    if ((s->supported_resolutions & T30_SUPPORT_300_300_RESOLUTION))
        set_ctrl_bit(s->local_dis_dtc_frame, 42);
    if ((s->supported_resolutions & (T30_SUPPORT_400_400_RESOLUTION | T30_SUPPORT_R16_RESOLUTION)))
        set_ctrl_bit(s->local_dis_dtc_frame, 43);
    /* Metric */ 
    set_ctrl_bit(s->local_dis_dtc_frame, 45);
    /* Superfine minimum scan line time pattern follows fine */
    if ((s->supported_t30_features & T30_SUPPORT_SELECTIVE_POLLING))
        set_ctrl_bit(s->local_dis_dtc_frame, 47);
    if ((s->supported_t30_features & T30_SUPPORT_SUB_ADDRESSING))
        set_ctrl_bit(s->local_dis_dtc_frame, 49);
    if ((s->supported_t30_features & T30_SUPPORT_IDENTIFICATION))
        set_ctrl_bit(s->local_dis_dtc_frame, 50);
    /* Ready to transmit a data file (polling) */
    if (s->tx_file[0])
        set_ctrl_bit(s->local_dis_dtc_frame, 51);
    /* No Binary file transfer (BFT) */
    /* No Document transfer mode (DTM) */
    /* No Electronic data interchange (EDI) */
    /* No Basic transfer mode (BTM) */
    /* No mixed mode (polling) */
    /* No character mode */
    /* No mixed mode (Annex E/T.4) */
    /* No mode 26 (T.505) */
    /* No digital network capability */
    /* No duplex operation */
    /* No JPEG */
    /* No full colour */
    /* No 12bits/pel */
    /* No sub-sampling (1:1:1) */
    /* No custom illuminant */
    /* No custom gamut range */
    if ((s->supported_image_sizes & T30_SUPPORT_US_LETTER_LENGTH))
        set_ctrl_bit(s->local_dis_dtc_frame, 76);
    if ((s->supported_image_sizes & T30_SUPPORT_US_LEGAL_LENGTH))
        set_ctrl_bit(s->local_dis_dtc_frame, 77);
    /* No HKM key management */
    /* No RSA key management */
    /* No override */
    /* No HFX40 cipher */
    /* No alternative cipher number 2 */
    /* No alternative cipher number 3 */
    /* No HFX40-I hashing */
    /* No alternative hashing system number 2 */
    /* No alternative hashing system number 3 */
    /* No T.44 (mixed raster content) */
    /* No page length maximum strip size for T.44 (mixed raster content) */
    /* No colour/grey scale 300x300 or 400x400 */
    /* No colour/grey scale 100x100 */
    /* No simple phase C BFT negotiations */
    /* No extended BFT negotiations */
    if ((s->supported_t30_features & T30_SUPPORT_INTERNET_SELECTIVE_POLLING_ADDRESS))
        set_ctrl_bit(s->local_dis_dtc_frame, 101);
    if ((s->supported_t30_features & T30_SUPPORT_INTERNET_ROUTING_ADDRESS))
        set_ctrl_bit(s->local_dis_dtc_frame, 102);
    if ((s->supported_resolutions & T30_SUPPORT_600_600_RESOLUTION))
        set_ctrl_bit(s->local_dis_dtc_frame, 105);
    if ((s->supported_resolutions & T30_SUPPORT_1200_1200_RESOLUTION))
        set_ctrl_bit(s->local_dis_dtc_frame, 106);
    if ((s->supported_resolutions & T30_SUPPORT_300_600_RESOLUTION))
        set_ctrl_bit(s->local_dis_dtc_frame, 107);
    if ((s->supported_resolutions & T30_SUPPORT_400_800_RESOLUTION))
        set_ctrl_bit(s->local_dis_dtc_frame, 108);
    if ((s->supported_resolutions & T30_SUPPORT_600_1200_RESOLUTION))
        set_ctrl_bit(s->local_dis_dtc_frame, 109);
    /* No colour/grey scale 600x600 */
    /* No colour/grey scale 1200x1200 */
    /* No double sided printing (alternate mode) */
    /* No double sided printing (continuous mode) */
    /* No black and white mixed raster content profile */
    /* No shared data memory */
    /* No T.44 colour space */
    if ((s->iaf & T30_IAF_MODE_FLOW_CONTROL))
        set_ctrl_bit(s->local_dis_dtc_frame, 121);
    /* No k > 4 */
    if ((s->iaf & T30_IAF_MODE_CONTINUOUS_FLOW))
        set_ctrl_bit(s->local_dis_dtc_frame, 123);
    /* No T.89 profile */
    s->local_dis_dtc_len = 19;
    //t30_decode_dis_dtc_dcs(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int prune_dis_dtc(t30_state_t *s)
{
    int i;

    /* Find the last octet that is really needed, set the extension bits, and trim the message length */
    for (i = 18;  i >= 6;  i--)
    {
        /* Strip the top bit */
        s->local_dis_dtc_frame[i] &= (DISBIT1 | DISBIT2 | DISBIT3 | DISBIT4 | DISBIT5 | DISBIT6 | DISBIT7);
        /* Check if there is some real message content here */
        if (s->local_dis_dtc_frame[i])
            break;
    }
    s->local_dis_dtc_len = i + 1;
    /* Fill in any required extension bits */
    s->local_dis_dtc_frame[i] &= ~DISBIT8;
    for (i--;  i > 4;  i--)
        s->local_dis_dtc_frame[i] |= DISBIT8;
    t30_decode_dis_dtc_dcs(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
    return s->local_dis_dtc_len;
}
/*- End of function --------------------------------------------------------*/

static int build_dcs(t30_state_t *s)
{
    int i;
    int bad;
    
    /* Make a DCS frame based on local issues and the latest received DIS/DTC frame. Negotiate
       the result based on what both parties can do. */
    s->dcs_frame[0] = 0xFF;
    s->dcs_frame[1] = 0x13;
    s->dcs_frame[2] = (uint8_t) (T30_DCS | s->dis_received);
    for (i = 3;  i < 19;  i++)
        s->dcs_frame[i] = 0x00;

#if 0
    /* Check for T.37 simple mode. */
    if (test_ctrl_bit(s->far_dis_dtc_frame, 1))
        set_ctrl_bit(s->dcs_frame, 1);
    /* Check for T.38 mode. */
    if (test_ctrl_bit(s->far_dis_dtc_frame, 3))
        set_ctrl_bit(s->dcs_frame, 3);
#endif

    /* Set to required modem rate */
    s->dcs_frame[4] |= fallback_sequence[s->current_fallback].dcs_code;

    /* Select the compression to use. */
    switch (s->line_encoding)
    {
    case T4_COMPRESSION_ITU_T6:
        set_ctrl_bit(s->dcs_frame, 31);
        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
        break;
    case T4_COMPRESSION_ITU_T4_2D:
        set_ctrl_bit(s->dcs_frame, 16);
        set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, 21);
        break;
    case T4_COMPRESSION_ITU_T4_1D:
        set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, 21);
        break;
    default:
        set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
        break;
    }
    /* We have a file to send, so tell the far end to go into receive mode. */
    set_ctrl_bit(s->dcs_frame, 10);
    /* Set the Y resolution bits */
    bad = T30_ERR_OK;
    switch (s->y_resolution)
    {
    case T4_Y_RESOLUTION_1200:
        switch (s->x_resolution)
        {
        case T4_X_RESOLUTION_600:
            if (!(s->supported_resolutions & T30_SUPPORT_600_1200_RESOLUTION))
                bad = T30_ERR_NORESSUPPORT;
            else
                set_ctrl_bit(s->dcs_frame, 109);
            break;
        case T4_X_RESOLUTION_1200:
            if (!(s->supported_resolutions & T30_SUPPORT_1200_1200_RESOLUTION))
                bad = T30_ERR_NORESSUPPORT;
            else
                set_ctrl_bit(s->dcs_frame, 106);
            break;
        default:
            bad = T30_ERR_NORESSUPPORT;
            break;
        }
        break;
    case T4_Y_RESOLUTION_800:
        switch (s->x_resolution)
        {
        case T4_X_RESOLUTION_R16:
            if (!(s->supported_resolutions & T30_SUPPORT_400_800_RESOLUTION))
                bad = T30_ERR_NORESSUPPORT;
            else
                set_ctrl_bit(s->dcs_frame, 108);
            break;
        default:
            bad = T30_ERR_NORESSUPPORT;
            break;
        }
        break;
    case T4_Y_RESOLUTION_600:
        switch (s->x_resolution)
        {
        case T4_X_RESOLUTION_300:
            if (!(s->supported_resolutions & T30_SUPPORT_300_600_RESOLUTION))
                bad = T30_ERR_NORESSUPPORT;
            else
                set_ctrl_bit(s->dcs_frame, 107);
            break;
        case T4_X_RESOLUTION_600:
            if (!(s->supported_resolutions & T30_SUPPORT_600_600_RESOLUTION))
                bad = T30_ERR_NORESSUPPORT;
            else
                set_ctrl_bit(s->dcs_frame, 105);
            break;
        default:
            bad = T30_ERR_NORESSUPPORT;
            break;
        }
        break;
    case T4_Y_RESOLUTION_SUPERFINE:
        if (!(s->supported_resolutions & T30_SUPPORT_SUPERFINE_RESOLUTION))
        {
            bad = T30_ERR_NORESSUPPORT;
        }
        else
        {
            switch (s->x_resolution)
            {
            case T4_X_RESOLUTION_R8:
                set_ctrl_bit(s->dcs_frame, 41);
                break;
            case T4_X_RESOLUTION_R16:
                set_ctrl_bit(s->dcs_frame, 43);
                break;
            default:
                bad = T30_ERR_NORESSUPPORT;
                break;
            }
        }
        break;
    case T4_Y_RESOLUTION_300:
        switch (s->x_resolution)
        {
        case T4_X_RESOLUTION_300:
            if (!(s->supported_resolutions & T30_SUPPORT_300_300_RESOLUTION))
                bad = T30_ERR_NORESSUPPORT;
            else
                set_ctrl_bit(s->dcs_frame, 42);
            break;
        default:
            bad = T30_ERR_NORESSUPPORT;
            break;
        }
        break;
    case T4_Y_RESOLUTION_FINE:
        if (!(s->supported_resolutions & T30_SUPPORT_FINE_RESOLUTION))
        {
            bad = T30_ERR_NORESSUPPORT;
        }
        else
        {
            switch (s->x_resolution)
            {
            case T4_X_RESOLUTION_R8:
                set_ctrl_bit(s->dcs_frame, 15);
                break;
            default:
                bad = T30_ERR_NORESSUPPORT;
                break;
            }
        }
        break;
    default:
    case T4_Y_RESOLUTION_STANDARD:
        switch (s->x_resolution)
        {
        case T4_X_RESOLUTION_R8:
            /* No bits to set for this */
            break;
        default:
            bad = T30_ERR_NORESSUPPORT;
            break;
        }
        break;
    }
    if (bad != T30_ERR_OK)
    {
        s->current_status = bad;
        span_log(&s->logging, SPAN_LOG_FLOW, "Image resolution (%d x %d) not acceptable\n", s->x_resolution, s->y_resolution);
        return -1;
    }
    /* Deal with the image width. The X resolution will fall in line with any valid width. */
    /* Low (R4) res widths are not supported in recent versions of T.30 */
    bad = T30_ERR_OK;
    switch (s->image_width)
    {
    case T4_WIDTH_R8_A4:
    case T4_WIDTH_300_A4:
    case T4_WIDTH_R16_A4:
    case T4_WIDTH_600_A4:
    case T4_WIDTH_1200_A4:
        /* No width related bits need to be set. */
        break;
    case T4_WIDTH_R8_B4:
    case T4_WIDTH_300_B4:
    case T4_WIDTH_R16_B4:
    case T4_WIDTH_600_B4:
    case T4_WIDTH_1200_B4:
        if ((s->far_dis_dtc_frame[5] & (DISBIT2 | DISBIT1)) < 1)
            bad = T30_ERR_NOSIZESUPPORT;
        else if (!(s->supported_image_sizes & T30_SUPPORT_255MM_WIDTH))
            bad = T30_ERR_BADTIFF;
        else
            set_ctrl_bit(s->dcs_frame, 17);
        break;
    case T4_WIDTH_R8_A3:
    case T4_WIDTH_300_A3:
    case T4_WIDTH_R16_A3:
    case T4_WIDTH_600_A3:
    case T4_WIDTH_1200_A3:
        if ((s->far_dis_dtc_frame[5] & (DISBIT2 | DISBIT1)) < 2)
            bad = T30_ERR_NOSIZESUPPORT;
        else if (!(s->supported_image_sizes & T30_SUPPORT_303MM_WIDTH))    
            bad = T30_ERR_BADTIFF;
        else
            set_ctrl_bit(s->dcs_frame, 18);
        break;
    default:
        /* T.30 does not support this width */
        bad = T30_ERR_BADTIFF;
        break;
    }
    if (bad != T30_ERR_OK)
    {
        s->current_status = bad;
        span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) not a valid FAX image width\n", s->image_width);
        return -1;
    }
    switch (s->image_width)
    {
    case T4_WIDTH_R8_A4:
    case T4_WIDTH_R8_B4:
    case T4_WIDTH_R8_A3:
        /* These are always OK */
        break;
    case T4_WIDTH_300_A4:
    case T4_WIDTH_300_B4:
    case T4_WIDTH_300_A3:
        if (!test_ctrl_bit(s->far_dis_dtc_frame, 42)  &&  !test_ctrl_bit(s->far_dis_dtc_frame, 107))
            bad = T30_ERR_NOSIZESUPPORT;
        break;
    case T4_WIDTH_R16_A4:
    case T4_WIDTH_R16_B4:
    case T4_WIDTH_R16_A3:
        if (!test_ctrl_bit(s->far_dis_dtc_frame, 43))
            bad = T30_ERR_NOSIZESUPPORT;
        break;
    case T4_WIDTH_600_A4:
    case T4_WIDTH_600_B4:
    case T4_WIDTH_600_A3:
        if (!test_ctrl_bit(s->far_dis_dtc_frame, 105)  &&  !test_ctrl_bit(s->far_dis_dtc_frame, 109))
            bad = T30_ERR_NOSIZESUPPORT;
        break;
    case T4_WIDTH_1200_A4:
    case T4_WIDTH_1200_B4:
    case T4_WIDTH_1200_A3:
        if (!test_ctrl_bit(s->far_dis_dtc_frame, 106))
            bad = T30_ERR_NOSIZESUPPORT;
        break;
    default:
        /* T.30 does not support this width */
        bad = T30_ERR_BADTIFF;
        break;
    }
    if (bad != T30_ERR_OK)
    {
        s->current_status = bad;
        span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) not a valid FAX image width\n", s->image_width);
        return -1;
    }
    /* Deal with the image length */
    /* If the other end supports unlimited length, then use that. Otherwise, if the other end supports
       B4 use that, as its longer than the default A4 length. */
    if (test_ctrl_bit(s->far_dis_dtc_frame, 20))
        set_ctrl_bit(s->dcs_frame, 20);
    else if (test_ctrl_bit(s->far_dis_dtc_frame, 19))
        set_ctrl_bit(s->dcs_frame, 19);

    if (s->error_correcting_mode)
        set_ctrl_bit(s->dcs_frame, 27);

    if ((s->iaf & T30_IAF_MODE_FLOW_CONTROL)  &&  test_ctrl_bit(s->far_dis_dtc_frame, 121))
        set_ctrl_bit(s->dcs_frame, 121);
    if ((s->iaf & T30_IAF_MODE_CONTINUOUS_FLOW)  &&  test_ctrl_bit(s->far_dis_dtc_frame, 123))
        set_ctrl_bit(s->dcs_frame, 123);
    s->dcs_len = 19;
    //t30_decode_dis_dtc_dcs(s, s->dcs_frame, s->dcs_len);
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int prune_dcs(t30_state_t *s)
{
    int i;

    /* Find the last octet that is really needed, set the extension bits, and trim the message length */
    for (i = 18;  i >= 6;  i--)
    {
        /* Strip the top bit */
        s->dcs_frame[i] &= (DISBIT1 | DISBIT2 | DISBIT3 | DISBIT4 | DISBIT5 | DISBIT6 | DISBIT7);
        /* Check if there is some real message content here */
        if (s->dcs_frame[i])
            break;
    }
    s->dcs_len = i + 1;
    /* Fill in any required extension bits */
    s->local_dis_dtc_frame[i] &= ~DISBIT8;
    for (i--  ;  i > 4;  i--)
        s->dcs_frame[i] |= DISBIT8;
    t30_decode_dis_dtc_dcs(s, s->dcs_frame, s->dcs_len);
    return s->dcs_len;
}
/*- End of function --------------------------------------------------------*/

static int step_fallback_entry(t30_state_t *s)
{
    int min_row_bits;

    while (fallback_sequence[++s->current_fallback].which)
    {
        if ((fallback_sequence[s->current_fallback].which & s->current_permitted_modems))
            break;
    }
    if (fallback_sequence[s->current_fallback].which == 0)
        return -1;
    /* TODO: This only sets the minimum row time for future pages. It doesn't fix up the
             current page, though it is benign - fallback will only result in an excessive
             minimum. */
    min_row_bits = set_min_scan_time_code(s);
    t4_tx_set_min_row_bits(&s->t4, min_row_bits);
    /* We need to rebuild the DCS message we will send. */
    build_dcs(s);
    return s->current_fallback;
}
/*- End of function --------------------------------------------------------*/

static int find_fallback_entry(int dcs_code)
{
    int i;

    /* The table is short, and not searched often, so a brain-dead linear scan seems OK */
    for (i = 0;  fallback_sequence[i].bit_rate;  i++)
    {
        if (fallback_sequence[i].dcs_code == dcs_code)
            break;
    }
    if (fallback_sequence[i].bit_rate == 0)
        return -1;
    return i;
}
/*- End of function --------------------------------------------------------*/

static void send_dcn(t30_state_t *s)
{
    queue_phase(s, T30_PHASE_D_TX);
    set_state(s, T30_STATE_C);
    send_simple_frame(s, T30_DCN);
}
/*- End of function --------------------------------------------------------*/

static void return_to_phase_b(t30_state_t *s, int with_fallback)
{
    /* This is what we do after things like T30_EOM is exchanged. */
    if (step_fallback_entry(s) < 0)
    {
        /* We have fallen back as far as we can go. Give up. */
        s->current_fallback = 0;
        s->current_status = T30_ERR_CANNOT_TRAIN;
        send_dcn(s);
    }
    else
    {
        if (s->calling_party)
            set_state(s, T30_STATE_T);
        else
            set_state(s, T30_STATE_R);
    }
}
/*- End of function --------------------------------------------------------*/

static int send_dis_or_dtc_sequence(t30_state_t *s, int start)
{
    /* (NSF) (CSI) DIS */
    /* (NSC) (CIG) (PWD) (SEP) (PSA) (CIA) (ISP) DTC */
    if (start)
    {
        set_dis_or_dtc(s);
        set_state(s, T30_STATE_R);
        s->step = 0;
    }
    if (!s->dis_received)
    {
        /* DIS sequence */
        switch (s->step)
        {
        case 0:
            s->step++;
            if (send_nsf_frame(s))
                break;
            /* Fall through */
        case 1:
            s->step++;
            if (send_ident_frame(s, T30_CSI))
                break;
            /* Fall through */
        case 2:
            s->step++;
            prune_dis_dtc(s);
            send_frame(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
            break;
        case 3:
            s->step++;
            /* Shut down HDLC transmission. */
            if (s->send_hdlc_handler)
                s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
            break;
        default:
            return -1;
        }
    }
    else
    {
        /* DTC sequence */
        switch (s->step)
        {
        case 0:
            s->step++;
            if (send_nsc_frame(s))
                break;
            /* Fall through */
        case 1:
            s->step++;
            if (send_ident_frame(s, T30_CIG))
                break;
            /* Fall through */
        case 2:
            s->step++;
            if (send_pwd_frame(s))
                break;
            /* Fall through */
        case 3:
            s->step++;
            if (send_sep_frame(s))
                break;
            /* Fall through */
        case 4:
            s->step++;
            if (send_psa_frame(s))
                break;
            /* Fall through */
        case 5:
            s->step++;
            if (send_cia_frame(s))
                break;
            /* Fall through */
        case 6:
            s->step++;
            if (send_isp_frame(s))
                break;
            /* Fall through */
        case 7:
            s->step++;
            prune_dis_dtc(s);
            send_frame(s, s->local_dis_dtc_frame, s->local_dis_dtc_len);
            break;
        case 8:
            s->step++;
            /* Shut down HDLC transmission. */
            if (s->send_hdlc_handler)
                s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
            break;
        default:
            return -1;
        }
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int send_dcs_sequence(t30_state_t *s, int start)
{
    /* (NSS) (TSI) (SUB) (SID) (TSA) (IRA) DCS */
    /* Schedule training after the messages */
    if (start)
    {
        prune_dcs(s);
        set_state(s, T30_STATE_D);
        s->step = 0;
    }
    switch (s->step)
    {
    case 0:
        s->step++;
        if (send_nss_frame(s))
            break;
        /* Fall through */
    case 1:
        s->step++;
        if (send_ident_frame(s, T30_TSI))
            break;
        /* Fall through */
    case 2:
        s->step++;
        if (send_sub_frame(s))
            break;
        /* Fall through */
    case 3:
        s->step++;
        if (send_sid_frame(s))
            break;
        /* Fall through */
    case 4:
        s->step++;
        if (send_tsa_frame(s))
            break;
        /* Fall through */
    case 5:
        s->step++;
        if (send_ira_frame(s))
            break;
        /* Fall through */
    case 6:
        s->step++;
        prune_dcs(s);
        send_frame(s, s->dcs_frame, s->dcs_len);
        break;
    case 7:
        s->step++;
        /* Shut down HDLC transmission. */
        if (s->send_hdlc_handler)
            s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
        break;
    default:
        return -1;
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int send_cfr_sequence(t30_state_t *s, int start)
{
    /* (CSA) CFR */
    /* CFR is usually a simple frame, but can become a sequence with Internet
       FAXing. */
    send_simple_frame(s, T30_CFR);
    return 0;
}
/*- End of function --------------------------------------------------------*/

static void disconnect(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Disconnecting\n");
    /* Make sure any FAX in progress is tidied up. If the tidying up has
       already happened, repeating it here is harmless. */
    switch (s->operation_in_progress)
    {
    case OPERATION_IN_PROGRESS_T4_TX:
        t4_tx_end(&(s->t4));
        break;
    case OPERATION_IN_PROGRESS_T4_RX:
        t4_rx_end(&(s->t4));
        break;
    }
    s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
    s->timer_t0_t1 = 0;
    s->timer_t2_t4 = 0;
    s->timer_t3 = 0;
    s->timer_t5 = 0;
    set_phase(s, T30_PHASE_E);
    set_state(s, T30_STATE_B);
}
/*- End of function --------------------------------------------------------*/

static int set_min_scan_time_code(t30_state_t *s)
{
    /* Translation between the codes for the minimum scan times the other end needs,
       and the codes for what we say will be used. We need 0 minimum. */
    static const uint8_t translate_min_scan_time[3][8] =
    {
        {T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_0MS}, /* normal */
        {T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_0MS}, /* fine */
        {T30_MIN_SCAN_10MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_5MS,  T30_MIN_SCAN_20MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_5MS,  T30_MIN_SCAN_0MS}  /* superfine, when half fine time is selected */
    };
    /* Translation between the codes for the minimum scan time we will use, and milliseconds. */
    static const int min_scan_times[8] =
    {
        20, 5, 10, 0, 40, 0, 0, 0
    };
    int min_bits_field;

    /* Set the minimum scan time bits */
    if (s->error_correcting_mode)
        min_bits_field = T30_MIN_SCAN_0MS;
    else
        min_bits_field = (s->far_dis_dtc_frame[5] >> 4) & 7;
    switch (s->y_resolution)
    {
    case T4_Y_RESOLUTION_SUPERFINE:
        if (!test_ctrl_bit(s->far_dis_dtc_frame, 41))
        {
            s->current_status = T30_ERR_NORESSUPPORT;
            span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution.\n");
            return -1;
        }
        s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, 46))  ?  2  :  1][min_bits_field];
        break;
    case T4_Y_RESOLUTION_FINE:
        if (!test_ctrl_bit(s->far_dis_dtc_frame, 15))
        {
            s->current_status = T30_ERR_NORESSUPPORT;
            span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution.\n");
            return -1;
        }
        s->min_scan_time_code = translate_min_scan_time[1][min_bits_field];
        break;
    default:
    case T4_Y_RESOLUTION_STANDARD:
        s->min_scan_time_code = translate_min_scan_time[0][min_bits_field];
        break;
    }
    if (!s->error_correcting_mode  &&  (s->iaf & T30_IAF_MODE_NO_FILL_BITS))
        return 0;
    return fallback_sequence[s->current_fallback].bit_rate*min_scan_times[s->min_scan_time_code]/1000;
}
/*- End of function --------------------------------------------------------*/

static int start_sending_document(t30_state_t *s)
{
    int min_row_bits;

    if (s->tx_file[0] == '\0')
    {
        /* There is nothing to send */
        span_log(&s->logging, SPAN_LOG_FLOW, "No document to send\n");
        return -1;
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "Start sending document\n");
    if (t4_tx_init(&s->t4, s->tx_file, s->tx_start_page, s->tx_stop_page) == NULL)
    {
        span_log(&s->logging, SPAN_LOG_WARNING, "Cannot open source TIFF file '%s'\n", s->tx_file);
        s->current_status = T30_ERR_FILEERROR;
        return -1;
    }
    s->operation_in_progress = OPERATION_IN_PROGRESS_T4_TX;
    t4_tx_get_pages_in_file(&s->t4);
    t4_tx_set_tx_encoding(&s->t4, s->line_encoding);
    t4_tx_set_local_ident(&s->t4, s->tx_info.ident);
    t4_tx_set_header_info(&s->t4, s->header_info);

    s->x_resolution = t4_tx_get_x_resolution(&s->t4);
    s->y_resolution = t4_tx_get_y_resolution(&s->t4);
    /* The minimum scan time to be used can't be evaluated until we know the Y resolution, and
       must be evaluated before the minimum scan row bits can be evaluated. */
    if ((min_row_bits = set_min_scan_time_code(s)) < 0)
    {
        t4_tx_end(&(s->t4));
        s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
        return -1;
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "Minimum bits per row will be %d\n", min_row_bits);
    t4_tx_set_min_row_bits(&s->t4, min_row_bits);

    if (tx_start_page(s))
        return -1;
    s->image_width = t4_tx_get_image_width(&(s->t4));
    if (s->error_correcting_mode)
    {
        if (get_partial_ecm_page(s) == 0)
            span_log(&s->logging, SPAN_LOG_WARNING, "No image data to send\n");
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int restart_sending_document(t30_state_t *s)
{
    t4_tx_restart_page(&(s->t4));
    s->retries = 0;
    s->ecm_block = 0;
    send_dcs_sequence(s, TRUE);
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int start_receiving_document(t30_state_t *s)
{
    if (s->rx_file[0] == '\0')
    {
        /* There is nothing to receive to */
        span_log(&s->logging, SPAN_LOG_FLOW, "No document to receive\n");
        return -1;
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "Start receiving document\n");
    queue_phase(s, T30_PHASE_B_TX);
    s->ecm_block = 0;
    send_dis_or_dtc_sequence(s, TRUE);
    return 0;
}
/*- End of function --------------------------------------------------------*/

static void unexpected_frame(t30_state_t *s, const uint8_t *msg, int len)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s received in state %d\n", t30_frametype(msg[2]), s->state);
    switch (s->state)
    {
    case T30_STATE_F_DOC_ECM:
    case T30_STATE_F_DOC_NON_ECM:
        s->current_status = T30_ERR_RX_INVALCMD;
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void unexpected_non_final_frame(t30_state_t *s, const uint8_t *msg, int len)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame in state %d\n", t30_frametype(msg[2]), s->state);
    s->current_status = T30_ERR_UNEXPECTED;
}
/*- End of function --------------------------------------------------------*/

static void unexpected_final_frame(t30_state_t *s, const uint8_t *msg, int len)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s received in state %d\n", t30_frametype(msg[2]), s->state);
    s->current_status = T30_ERR_UNEXPECTED;
    send_dcn(s);
}
/*- End of function --------------------------------------------------------*/

static void unexpected_frame_length(t30_state_t *s, const uint8_t *msg, int len)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame length - %d\n", t30_frametype(msg[0]), len);
    s->current_status = T30_ERR_UNEXPECTED;
    send_dcn(s);
}
/*- End of function --------------------------------------------------------*/

static int process_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len)
{
    int new_status;

    t30_decode_dis_dtc_dcs(s, msg, len);
    if (len < 6)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Short DIS/DTC frame\n");
        return -1;
    }

    if (msg[2] == T30_DIS)
        s->dis_received = TRUE;
    /* Make a local copy of the message, padded to the maximum possible length with zeros. This allows
       us to simply pick out the bits, without worrying about whether they were set from the remote side. */
    s->far_dis_dtc_len = (len > T30_MAX_DIS_DTC_DCS_LEN)  ?  T30_MAX_DIS_DTC_DCS_LEN  :  len;
    memcpy(s->far_dis_dtc_frame, msg, s->far_dis_dtc_len);
    if (s->far_dis_dtc_len < T30_MAX_DIS_DTC_DCS_LEN)
        memset(s->far_dis_dtc_frame + s->far_dis_dtc_len, 0, T30_MAX_DIS_DTC_DCS_LEN - s->far_dis_dtc_len);
    s->error_correcting_mode = (s->ecm_allowed  &&  (s->far_dis_dtc_frame[6] & DISBIT3) != 0);
    /* 256 octets per ECM frame */
    s->octets_per_ecm_frame = 256;
    /* Select the compression to use. */
    if (s->error_correcting_mode  &&  (s->supported_compressions & T30_SUPPORT_T6_COMPRESSION)  &&  test_ctrl_bit(s->far_dis_dtc_frame, 31))
    {
        s->line_encoding = T4_COMPRESSION_ITU_T6;
    }
    else if ((s->supported_compressions & T30_SUPPORT_T4_2D_COMPRESSION)  &&  test_ctrl_bit(s->far_dis_dtc_frame, 16))
    {
        s->line_encoding = T4_COMPRESSION_ITU_T4_2D;
    }
    else
    {
        s->line_encoding = T4_COMPRESSION_ITU_T4_1D;
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "Selected compression %d\n", s->line_encoding);
    switch (s->far_dis_dtc_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3))
    {
    case (DISBIT6 | DISBIT4 | DISBIT3):
        if ((s->supported_modems & T30_SUPPORT_V17))
        {
            s->current_permitted_modems = T30_SUPPORT_V17 | T30_SUPPORT_V29 | T30_SUPPORT_V27TER;
            s->current_fallback = T30_V17_FALLBACK_START;
            break;
        }
        /* Fall through */
    case (DISBIT4 | DISBIT3):
        if ((s->supported_modems & T30_SUPPORT_V29))
        {
            s->current_permitted_modems = T30_SUPPORT_V29 | T30_SUPPORT_V27TER;
            s->current_fallback = T30_V29_FALLBACK_START;
            break;
        }
        /* Fall through */
    case DISBIT4:
        s->current_permitted_modems = T30_SUPPORT_V27TER;
        s->current_fallback = T30_V27TER_FALLBACK_START;
        break;
    case 0:
        s->current_permitted_modems = T30_SUPPORT_V27TER;
        s->current_fallback = T30_V27TER_FALLBACK_START + 1;
        break;
    case DISBIT3:
        if ((s->supported_modems & T30_SUPPORT_V29))
        {
            /* TODO: this doesn't allow for skipping the V.27ter modes */
            s->current_permitted_modems = T30_SUPPORT_V29;
            s->current_fallback = T30_V29_FALLBACK_START;
            break;
        }
        /* Fall through */
    default:
        span_log(&s->logging, SPAN_LOG_FLOW, "Remote does not support a compatible modem\n");
        /* We cannot talk to this machine! */
        s->current_status = T30_ERR_INCOMPATIBLE;
        return -1;
    }
    if (s->phase_b_handler)
    {
        new_status = s->phase_b_handler(s, s->phase_b_user_data, msg[2]);
        if (new_status != T30_ERR_OK)
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Application rejected DIS/DTC - '%s'\n", t30_completion_code_to_str(new_status));
            s->current_status = new_status;
            /* TODO: If FNV is allowed, process it here */
            send_dcn(s);
            return -1;
        }
    }
    queue_phase(s, T30_PHASE_B_TX);
    /* Try to send something */
    if (s->tx_file[0])
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Trying to send file '%s'\n", s->tx_file);
        if (!test_ctrl_bit(s->far_dis_dtc_frame, 10))
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot receive\n", t30_frametype(msg[2]));
            s->current_status = T30_ERR_RX_INCAPABLE;
            send_dcn(s);
        }
        if (start_sending_document(s))
        {
            send_dcn(s);
            return -1;
        }
        if (build_dcs(s))
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "The far end is incompatible\n", s->tx_file);
            send_dcn(s);
            return -1;
        }
        s->retries = 0;
        send_dcs_sequence(s, TRUE);
        return 0;
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "%s nothing to send\n", t30_frametype(msg[2]));
    /* ... then try to receive something */
    if (s->rx_file[0])
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Trying to receive file '%s'\n", s->rx_file);
        if (!test_ctrl_bit(s->far_dis_dtc_frame, 9))
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot transmit\n", t30_frametype(msg[2]));
            s->current_status = T30_ERR_TX_INCAPABLE;
            send_dcn(s);
            return -1;
        }
        if (start_receiving_document(s))
        {
            send_dcn(s);
            return -1;
        }
        if (set_dis_or_dtc(s))
        {
            s->current_status = T30_ERR_INCOMPATIBLE;
            send_dcn(s);
            return -1;
        }
        s->retries = 0;
        send_dis_or_dtc_sequence(s, TRUE);
        return 0;
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "%s nothing to receive\n", t30_frametype(msg[2]));
    /* There is nothing to do, or nothing we are able to do. */
    send_dcn(s);
    return -1;
}
/*- End of function --------------------------------------------------------*/

static int process_rx_dcs(t30_state_t *s, const uint8_t *msg, int len)
{
    static const int widths[6][4] =
    {
        {  T4_WIDTH_R4_A4,   T4_WIDTH_R4_B4,   T4_WIDTH_R4_A3, -1}, /* R4 resolution - no longer used in recent versions of T.30 */
        {  T4_WIDTH_R8_A4,   T4_WIDTH_R8_B4,   T4_WIDTH_R8_A3, -1}, /* R8 resolution */
        { T4_WIDTH_300_A4,  T4_WIDTH_300_B4,  T4_WIDTH_300_A3, -1}, /* 300/inch resolution */
        { T4_WIDTH_R16_A4,  T4_WIDTH_R16_B4,  T4_WIDTH_R16_A3, -1}, /* R16 resolution */
        { T4_WIDTH_600_A4,  T4_WIDTH_600_B4,  T4_WIDTH_600_A3, -1}, /* 600/inch resolution */
        {T4_WIDTH_1200_A4, T4_WIDTH_1200_B4, T4_WIDTH_1200_A3, -1}  /* 1200/inch resolution */
    };
    uint8_t dcs_frame[T30_MAX_DIS_DTC_DCS_LEN];
    int i;
    int new_status;

    t30_decode_dis_dtc_dcs(s, msg, len);

    /* Check DCS frame from remote */
    if (len < 6)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Short DCS frame\n");
        return -1;
    }

    /* Make an ASCII string format copy of the message, for logging in the
       received file. This string does not include the frame header octets. */
    sprintf(s->rx_dcs_string, "%02X", bit_reverse8(msg[3]));
    for (i = 4;  i < len;  i++)
        sprintf(s->rx_dcs_string + 3*i - 10, " %02X", bit_reverse8(msg[i]));
    /* Make a local copy of the message, padded to the maximum possible length with zeros. This allows
       us to simply pick out the bits, without worrying about whether they were set from the remote side. */
    if (len > T30_MAX_DIS_DTC_DCS_LEN)
    {
        memcpy(dcs_frame, msg, T30_MAX_DIS_DTC_DCS_LEN);
    }
    else
    {
        memcpy(dcs_frame, msg, len);
        if (len < T30_MAX_DIS_DTC_DCS_LEN)
            memset(dcs_frame + len, 0, T30_MAX_DIS_DTC_DCS_LEN - len);
    }

    s->octets_per_ecm_frame = test_ctrl_bit(dcs_frame, 28)  ?  256  :  64;

    if (test_ctrl_bit(dcs_frame, 106))
        s->x_resolution = T4_X_RESOLUTION_1200;
    else if (test_ctrl_bit(dcs_frame, 105)  ||  test_ctrl_bit(dcs_frame, 109))
        s->x_resolution = T4_X_RESOLUTION_600;
    else if (test_ctrl_bit(dcs_frame, 43)  ||  test_ctrl_bit(dcs_frame, 108))
        s->x_resolution = T4_X_RESOLUTION_R16;
    else if (test_ctrl_bit(dcs_frame, 42)  ||  test_ctrl_bit(dcs_frame, 107))
        s->x_resolution = T4_X_RESOLUTION_300;
    else
        s->x_resolution = T4_X_RESOLUTION_R8;

    if (test_ctrl_bit(dcs_frame, 106)  ||  test_ctrl_bit(dcs_frame, 109))
        s->y_resolution = T4_Y_RESOLUTION_1200;
    else if (test_ctrl_bit(dcs_frame, 108))
        s->y_resolution = T4_Y_RESOLUTION_800;
    else if (test_ctrl_bit(dcs_frame, 105)  ||  test_ctrl_bit(dcs_frame, 107))
        s->y_resolution = T4_Y_RESOLUTION_600;
    else if (test_ctrl_bit(dcs_frame, 41)  ||  test_ctrl_bit(dcs_frame, 43))
        s->y_resolution = T4_Y_RESOLUTION_SUPERFINE;
    else if (test_ctrl_bit(dcs_frame, 42))
        s->y_resolution = T4_Y_RESOLUTION_300;
    else if (test_ctrl_bit(dcs_frame, 15))
        s->y_resolution = T4_Y_RESOLUTION_FINE;
    else
        s->y_resolution = T4_Y_RESOLUTION_STANDARD;

    if (s->x_resolution == T4_X_RESOLUTION_1200)
        i = 5;
    else if (s->x_resolution == T4_X_RESOLUTION_600)
        i = 4;
    else if (s->x_resolution == T4_X_RESOLUTION_R16)
        i = 3;
    else if (s->x_resolution == T4_X_RESOLUTION_300)
        i = 2;
    else if (s->x_resolution == T4_X_RESOLUTION_R4)
        i = 0;
    else
        i = 1;

    s->image_width = widths[i][dcs_frame[5] & (DISBIT2 | DISBIT1)];

    /* Check which compression we will use. */
    if (test_ctrl_bit(dcs_frame, 31))
        s->line_encoding = T4_COMPRESSION_ITU_T6;
    else if (test_ctrl_bit(dcs_frame, 16))
        s->line_encoding = T4_COMPRESSION_ITU_T4_2D;
    else
        s->line_encoding = T4_COMPRESSION_ITU_T4_1D;
    span_log(&s->logging, SPAN_LOG_FLOW, "Selected compression %d\n", s->line_encoding);
    if (!test_ctrl_bit(dcs_frame, 10))
        span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Remote is not requesting receive in DCS\n");

    if ((s->current_fallback = find_fallback_entry(dcs_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3))) < 0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Remote asked for a modem standard we do not support\n");
        return -1;
    }
    s->error_correcting_mode = (test_ctrl_bit(dcs_frame, 27) != 0);

    if (s->phase_b_handler)
    {
        new_status = s->phase_b_handler(s, s->phase_b_user_data, msg[2]);
        if (new_status != T30_ERR_OK)
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Application rejected DCS - '%s'\n", t30_completion_code_to_str(new_status));
            s->current_status = new_status;
            /* TODO: If FNV is allowed, process it here */
            send_dcn(s);
            return -1;
        }
    }
    /* Start document reception */
    span_log(&s->logging,
             SPAN_LOG_FLOW, 
             "Get document at %dbps, modem %d\n",
             fallback_sequence[s->current_fallback].bit_rate,
             fallback_sequence[s->current_fallback].modem_type);
    if (s->rx_file[0] == '\0')
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "No document to receive\n");
        s->current_status = T30_ERR_FILEERROR;
        send_dcn(s);
        return -1;
    }
    if (!s->in_message)
    {
        if (t4_rx_init(&s->t4, s->rx_file, s->output_encoding) == NULL)
        {
            span_log(&s->logging, SPAN_LOG_WARNING, "Cannot open target TIFF file '%s'\n", s->rx_file);
            s->current_status = T30_ERR_FILEERROR;
            send_dcn(s);
            return -1;
        }
        s->operation_in_progress = OPERATION_IN_PROGRESS_T4_RX;
    }
    if (!(s->iaf & T30_IAF_MODE_NO_TCF))
    {
        /* TCF is always sent with long training */
        s->short_train = FALSE;
        set_state(s, T30_STATE_F_TCF);
        queue_phase(s, T30_PHASE_C_NON_ECM_RX);
        timer_t2_start(s);
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int send_deferred_pps_response(t30_state_t *s)
{
    queue_phase(s, T30_PHASE_D_TX);
    if (s->ecm_first_bad_frame >= s->ecm_frames)
    {
        /* Everything was OK. We can accept the data and move on. */
        t30_ecm_commit_partial_page(s);
        switch (s->last_pps_fcf2)
        {
        case T30_NULL:
            /* We can confirm this partial page. */
            break;
        default:
            /* We can confirm the whole page. */
            s->next_rx_step = s->last_pps_fcf2;
            rx_end_page(s);
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, s->last_pps_fcf2);
            rx_start_page(s);
            break;
        }
        set_state(s, T30_STATE_F_POST_RCP_MCF);
        send_simple_frame(s, T30_MCF);
    }
    else
    {
        /* We need to send the PPR frame we have created, to try to fill in the missing/bad data. */
        set_state(s, T30_STATE_F_POST_RCP_PPR);
        s->ecm_frame_map[0] = 0xFF;
        s->ecm_frame_map[1] = 0x13;
        s->ecm_frame_map[2] = (uint8_t) (T30_PPR | s->dis_received);
        send_frame(s, s->ecm_frame_map, 3 + 32);
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
{
    int page;
    int block;
    int frames;
    int i;
    int j;
    int frame_no;

    if (len < 7)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Bad PPS message length %d.\n", len);
        return -1;
    }
    s->last_pps_fcf2 = msg[3] & 0xFE;
    page = msg[4];
    block = msg[5];

    /* The frames count is not well specified in T.30. In practice it seems it might be the
       number of frames in the current block, or it might be the number of frames in the
       current burst of transmission. For a burst of resent frames this would make it smaller
       than the actual size of the block. If we only accept the number when it exceeds
       previous values, we should get the real number of frames in the block. */
    frames = msg[6] + 1;
    if (s->ecm_frames < 0)
    {
        /* First time. Take the number and believe in it. */
        s->ecm_frames = frames;
    }
    else
    {
        /* If things have gone wrong, the far end might try to send us zero FCD
           frames. It can't represent zero in the block count field, so it might
           put zero there, or it might simplistically insert (blocks - 1), and put
           0xFF there. Beware of this. */
        if (frames == 0xFF)
        {
            /* This is probably zero, erroneously rolled over to the maximum count */
            frames = 0;
        }
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "Received PPS + %s - page %d, block %d, %d frames\n", t30_frametype(msg[3]), page, block, frames);
    if (page != s->ecm_rx_page)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "ECM rx page mismatch - expected %d, but received %d.\n", s->ecm_rx_page, page);
    }
    if (block != s->ecm_block)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "ECM rx block mismatch - expected %d, but received %d.\n", s->ecm_block, block);
    }
    /* Build a bit map of which frames we now have stored OK */
    s->ecm_first_bad_frame = 256;
    for (i = 0;  i < 32;  i++)
    {
        s->ecm_frame_map[i + 3] = 0;
        for (j = 0;  j < 8;  j++)
        {
            frame_no = (i << 3) + j;
            if (s->ecm_len[frame_no] < 0)
            {
                s->ecm_frame_map[i + 3] |= (1 << j);
                if (frame_no < s->ecm_first_bad_frame)
                    s->ecm_first_bad_frame = frame_no;
                if (frame_no < s->ecm_frames)
                    s->error_correcting_mode_retries++;
            }
        }
    }
    /* Are there any bad frames, or does our scan represent things being OK? */
    switch (s->last_pps_fcf2)
    {
    case T30_NULL:
    case T30_EOP:
    case T30_PRI_EOP:
    case T30_EOM:
    case T30_PRI_EOM:
    case T30_EOS:
    case T30_MPS:
    case T30_PRI_MPS:
        if (s->receiver_not_ready_count > 0)
        {
            s->receiver_not_ready_count--;
            queue_phase(s, T30_PHASE_D_TX);
            set_state(s, T30_STATE_F_POST_RCP_RNR);
            send_simple_frame(s, T30_RNR);
        }
        else
        {
            send_deferred_pps_response(s);
        }
        break;
    default:
        unexpected_final_frame(s, msg, len);
        break;
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

static void process_rx_ppr(t30_state_t *s, const uint8_t *msg, int len)
{
    int i;
    int j;
    int frame_no;
    uint8_t frame[4];

    if (len != 3 + 32)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Bad length for PPR bits - %d\n", len);
        /* TODO: probably should send DCN */
        return;
    }
    /* Check which frames are OK, and mark them as OK. */
    for (i = 0;  i < 32;  i++)
    {
        for (j = 0;  j < 8;  j++)
        {
            frame_no = (i << 3) + j;
            /* Tick off the frames they are not complaining about as OK */
            if ((msg[i + 3] & (1 << j)) == 0)
            {
                if (s->ecm_len[frame_no] >= 0)
                    s->ecm_progress++;
                s->ecm_len[frame_no] = -1;
            }
            else
            {
                if (frame_no < s->ecm_frames)
                {
                    span_log(&s->logging, SPAN_LOG_FLOW, "Frame %d to be resent\n", frame_no);
                    s->error_correcting_mode_retries++;
                }
#if 0
                /* Diagnostic: See if the other end is complaining about something we didn't even send this time. */
                if (s->ecm_len[frame_no] < 0)
                    span_log(&s->logging, SPAN_LOG_FLOW, "PPR contains complaint about frame %d, which was not sent\n", frame_no);
#endif
            }
        }
    }
    if (++s->ppr_count >= PPR_LIMIT_BEFORE_CTC_OR_EOR)
    {
        /* Continue to correct? */
        /* Continue only if we have been making progress */
        s->ppr_count = 0;
        if (s->ecm_progress)
        {
            s->ecm_progress = 0;
            queue_phase(s, T30_PHASE_D_TX);
            set_state(s, T30_STATE_IV_CTC);
            send_simple_frame(s, T30_CTC);
        }
        else
        {
            set_state(s, T30_STATE_IV_EOR);
            queue_phase(s, T30_PHASE_D_TX);
            frame[0] = 0xFF;
            frame[1] = 0x13;
            frame[2] = (uint8_t) (T30_EOR | s->dis_received);
            frame[3] = (s->ecm_at_page_end)  ?  ((uint8_t) (s->next_tx_step | s->dis_received))  :  T30_NULL;
            span_log(&s->logging, SPAN_LOG_FLOW, "Sending EOR + %s\n", t30_frametype(frame[3]));
            send_frame(s, frame, 4);
        }
    }
    else
    {
        /* Initiate resending of the remainder of the frames. */
        set_state(s, T30_STATE_IV);
        queue_phase(s, T30_PHASE_C_ECM_TX);
        send_first_ecm_frame(s);
    }
}
/*- End of function --------------------------------------------------------*/

static void process_rx_fcd(t30_state_t *s, const uint8_t *msg, int len)
{
    int frame_no;

    /* Facsimile coded data */
    switch (s->state)
    {
    case T30_STATE_F_DOC_ECM:
        if (len <= 4 + 256)
        {
            frame_no = msg[3];
            /* Just store the actual image data, and record its length */
            span_log(&s->logging, SPAN_LOG_FLOW, "Storing ECM frame %d, length %d\n", frame_no, len - 4);
            memcpy(&s->ecm_data[frame_no][0], &msg[4], len - 4);
            s->ecm_len[frame_no] = (int16_t) (len - 4);
            /* In case we are just after a CTC/CTR exchange, which kicked us back to long training */
            s->short_train = TRUE;
        }
        else
        {
            unexpected_frame_length(s, msg, len);
        }
        break;
    default:
        unexpected_non_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_rx_rcp(t30_state_t *s, const uint8_t *msg, int len)
{
    /* Return to control for partial page. These might come through with or without the final frame tag,
       so we have this routine to deal with the "no final frame tag" case. */
    switch (s->state)
    {
    case T30_STATE_F_DOC_ECM:
        set_state(s, T30_STATE_F_POST_DOC_ECM);
        queue_phase(s, T30_PHASE_D_RX);
        break;
    case T30_STATE_F_POST_DOC_ECM:
        /* Just ignore this */
        break;
    default:
        unexpected_non_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_rx_fnv(t30_state_t *s, const uint8_t *msg, int len)
{
    logging_state_t *log;
    const char *x;

    /* Field not valid */
    /* TODO: analyse the message, as per 5.3.6.2.13 */
    if (!span_log_test(&s->logging, SPAN_LOG_FLOW))
        return;
    log = &s->logging;

    if ((msg[3] & 0x01))
        span_log(log, SPAN_LOG_FLOW, "  Incorrect password (PWD).\n");
    if ((msg[3] & 0x02))
        span_log(log, SPAN_LOG_FLOW, "  Selective polling reference (SEP) not known.\n");
    if ((msg[3] & 0x04))
        span_log(log, SPAN_LOG_FLOW, "  Sub-address (SUB) not known.\n");
    if ((msg[3] & 0x08))
        span_log(log, SPAN_LOG_FLOW, "  Sender identity (SID) not known.\n");
    if ((msg[3] & 0x10))
        span_log(log, SPAN_LOG_FLOW, "  Secure fax error.\n");
    if ((msg[3] & 0x20))
        span_log(log, SPAN_LOG_FLOW, "  Transmitting subscriber identity (TSI) not accepted.\n");
    if ((msg[3] & 0x40))
        span_log(log, SPAN_LOG_FLOW, "  Polled sub-address (PSA) not known.\n");
    if (len > 4  &&  (msg[3] & DISBIT8))
    {
        if ((msg[4] & 0x01))
            span_log(log, SPAN_LOG_FLOW, "  BFT negotiations request not accepted.\n");
        if ((msg[4] & 0x02))
            span_log(log, SPAN_LOG_FLOW, "  Internet routing address (IRA) not known.\n");
        if ((msg[4] & 0x04))
            span_log(log, SPAN_LOG_FLOW, "  Internet selective polling address (ISP) not known.\n");
    }
    if (len > 5)
    {
        span_log(log, SPAN_LOG_FLOW, "  FNV sequence number %d.\n", msg[5]);
    }
    if (len > 6)
    {
        switch (msg[6])
        {
        case T30_PWD:
            x = "Incorrect password (PWD)";
            break;
        case T30_SEP:
            x = "Selective polling reference (SEP) not known";
            break;
        case T30_SUB:
        case T30_SUB | 0x01:
            x = "Sub-address (SUB) not known";
            break;
        case T30_SID:
        case T30_SID | 0x01:
            x = "Sender identity (SID) not known";
            break;
        case T30_SPI:
            x = "Secure fax error";
            break;
        case T30_TSI:
        case T30_TSI | 0x01:
            x = "Transmitting subscriber identity (TSI) not accepted";
            break;
        case T30_PSA:
            x = "Polled sub-address (PSA) not known";
            break;
        default:
            x = "???";
            break;
        }
        span_log(log, SPAN_LOG_FLOW, "  FNV diagnostic info type %s.\n", x);
    }
    if (len > 7)
    {
        span_log(log, SPAN_LOG_FLOW, "  FNV length %d.\n", msg[7]);
    }
    /* We've decoded it, but we don't yet know how to deal with it, so treat it as unexpected */
    unexpected_final_frame(s, msg, len);
}
/*- End of function --------------------------------------------------------*/

static void process_state_answering(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_DIS:
        /* TODO: This is a fudge to allow for starting up in T.38, where the other end has
           seen DIS by analogue modem means, and has immediately sent DIS/DTC. We might have
           missed useful info, like TSI, but just accept things and carry on form now. */
        span_log(&s->logging, SPAN_LOG_FLOW, "DIS/DTC before DIS\n");
        process_rx_dis_dtc(s, msg, len);
        break;
    case T30_DCS:
        /* TODO: This is a fudge to allow for starting up in T.38, where the other end has
           seen DIS by analogue modem means, and has immediately sent DCS. We might have
           missed useful info, like TSI, but just accept things and carry on form now. */
        span_log(&s->logging, SPAN_LOG_FLOW, "DCS before DIS\n");
        process_rx_dcs(s, msg, len);
        break;
    case T30_DCN:
        s->current_status = T30_ERR_TX_GOTDCN;
        disconnect(s);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_b(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_DCN:
        /* Just ignore any DCN's which appear at this stage. */
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_c(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_DCN:
        /* Just ignore any DCN's which appear at this stage. */
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_d(t30_state_t *s, const uint8_t *msg, int len)
{
    /* We should be sending the DCS sequence right now */
    switch (msg[2] & 0xFE)
    {
    case T30_DCN:
        s->current_status = T30_ERR_TX_BADDCS;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_d_tcf(t30_state_t *s, const uint8_t *msg, int len)
{
    /* We should be sending the TCF data right now */
    switch (msg[2] & 0xFE)
    {
    case T30_DCN:
        s->current_status = T30_ERR_TX_BADDCS;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_d_post_tcf(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_CFR:
        /* Trainability test succeeded. Send the document. */
        span_log(&s->logging, SPAN_LOG_FLOW, "Trainability test succeeded\n");
        s->retries = 0;
        s->short_train = TRUE;
        if (s->error_correcting_mode)
        {
            set_state(s, T30_STATE_IV);
            queue_phase(s, T30_PHASE_C_ECM_TX);
            send_first_ecm_frame(s);
        }
        else
        {
            set_state(s, T30_STATE_I);
            queue_phase(s, T30_PHASE_C_NON_ECM_TX);
        }
        break;
    case T30_FTT:
        /* Trainability test failed. Try again. */
        span_log(&s->logging, SPAN_LOG_FLOW, "Trainability test failed\n");
        s->retries = 0;
        s->short_train = FALSE;
        if (step_fallback_entry(s) < 0)
        {
            /* We have fallen back as far as we can go. Give up. */
            s->current_fallback = 0;
            s->current_status = T30_ERR_CANNOT_TRAIN;
            send_dcn(s);
            break;
        }
        queue_phase(s, T30_PHASE_B_TX);
        send_dcs_sequence(s, TRUE);
        break;
    case T30_DIS:
        /* It appears they didn't see what we sent - retry the TCF */
        if (++s->retries >= MAX_MESSAGE_TRIES)
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Too many retries. Giving up.\n");
            s->current_status = T30_ERR_RETRYDCN;
            send_dcn(s);
            break;
        }
        span_log(&s->logging, SPAN_LOG_FLOW, "Retry number %d\n", s->retries);
        queue_phase(s, T30_PHASE_B_TX);
        /* TODO: should we reassess the new DIS message, and possibly adjust the DCS we use? */
        send_dcs_sequence(s, TRUE);
        break;
    case T30_DCN:
        s->current_status = T30_ERR_TX_BADDCS;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_tcf(t30_state_t *s, const uint8_t *msg, int len)
{
    /* We should be receiving TCF right now, not HDLC messages */
    switch (msg[2] & 0xFE)
    {
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_cfr(t30_state_t *s, const uint8_t *msg, int len)
{
    /* We're waiting for a response to the CFR we sent */
    switch (msg[2] & 0xFE)
    {
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_ftt(t30_state_t *s, const uint8_t *msg, int len)
{
    /* We're waiting for a response to the FTT we sent */
    switch (msg[2] & 0xFE)
    {
    case T30_DCS:
        process_rx_dcs(s, msg, len);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_doc_non_ecm(t30_state_t *s, const uint8_t *msg, int len)
{
    /* If we are getting HDLC messages, and we have not moved to the _POST_DOC_NON_ECM
       state, it looks like either:
        - we didn't see the image data carrier properly, or
        - they didn't see our T30_CFR, and are repeating the DCS/TCF sequence.
        - they didn't see out T30_MCF, and are repeating the end of page message. */
    switch (msg[2] & 0xFE)
    {
    case T30_DIS:
        process_rx_dis_dtc(s, msg, len);
        break;
    case T30_DCS:
        process_rx_dcs(s, msg, len);
        break;
    case T30_MPS:
        /* Treat this as a bad quality page. */
        if (s->phase_d_handler)
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
        s->next_rx_step = msg[2] & 0xFE;
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_III_Q_RTN);
        send_simple_frame(s, T30_RTN);
        break;
    case T30_PRI_MPS:
        /* Treat this as a bad quality page. */
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        s->next_rx_step = msg[2] & 0xFE;
        set_state(s, T30_STATE_III_Q_RTN);
        break;
    case T30_EOM:
    case T30_EOS:
        /* Treat this as a bad quality page. */
        if (s->phase_d_handler)
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
        s->next_rx_step = msg[2] & 0xFE;
        /* Return to phase B */
        queue_phase(s, T30_PHASE_B_TX);
        set_state(s, T30_STATE_III_Q_RTN);
        send_simple_frame(s, T30_RTN);
        break;
    case T30_PRI_EOM:
        /* Treat this as a bad quality page. */
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        s->next_rx_step = T30_PRI_EOM;
        set_state(s, T30_STATE_III_Q_RTN);
        break;
    case T30_EOP:
        /* Treat this as a bad quality page. */
        if (s->phase_d_handler)
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
        s->next_rx_step = msg[2] & 0xFE;
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_III_Q_RTN);
        send_simple_frame(s, T30_RTN);
        break;
    case T30_PRI_EOP:
        /* Treat this as a bad quality page. */
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        s->next_rx_step = msg[2] & 0xFE;
        set_state(s, T30_STATE_III_Q_RTN);
        break;
    case T30_DCN:
        s->current_status = T30_ERR_RX_DCNDATA;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_post_doc_non_ecm(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_MPS:
        if (s->phase_d_handler)
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
        s->next_rx_step = msg[2] & 0xFE;
        queue_phase(s, T30_PHASE_D_TX);
        switch (copy_quality(s))
        {
        case T30_COPY_QUALITY_PERFECT:
        case T30_COPY_QUALITY_GOOD:
            rx_end_page(s);
            rx_start_page(s);
            set_state(s, T30_STATE_III_Q_MCF);
            send_simple_frame(s, T30_MCF);
            break;
        case T30_COPY_QUALITY_POOR:
            rx_end_page(s);
            rx_start_page(s);
            set_state(s, T30_STATE_III_Q_RTP);
            send_simple_frame(s, T30_RTP);
            break;
        case T30_COPY_QUALITY_BAD:
            rx_start_page(s);
            set_state(s, T30_STATE_III_Q_RTN);
            send_simple_frame(s, T30_RTN);
            break;
        }
        break;
    case T30_PRI_MPS:
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        s->next_rx_step = msg[2] & 0xFE;
        switch (copy_quality(s))
        {
        case T30_COPY_QUALITY_PERFECT:
        case T30_COPY_QUALITY_GOOD:
            rx_end_page(s);
            t4_rx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            s->in_message = FALSE;
            set_state(s, T30_STATE_III_Q_MCF);
            break;
        case T30_COPY_QUALITY_POOR:
            rx_end_page(s);
            t4_rx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            s->in_message = FALSE;
            set_state(s, T30_STATE_III_Q_RTP);
            break;
        case T30_COPY_QUALITY_BAD:
            set_state(s, T30_STATE_III_Q_RTN);
            break;
        }
        break;
    case T30_EOM:
    case T30_EOS:
        if (s->phase_d_handler)
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
        s->next_rx_step = msg[2] & 0xFE;
        /* Return to phase B */
        queue_phase(s, T30_PHASE_B_TX);
        switch (copy_quality(s))
        {
        case T30_COPY_QUALITY_PERFECT:
        case T30_COPY_QUALITY_GOOD:
            rx_end_page(s);
            rx_start_page(s);
            set_state(s, T30_STATE_III_Q_MCF);
            send_simple_frame(s, T30_MCF);
            break;
        case T30_COPY_QUALITY_POOR:
            rx_end_page(s);
            rx_start_page(s);
            set_state(s, T30_STATE_III_Q_RTP);
            send_simple_frame(s, T30_RTP);
            break;
        case T30_COPY_QUALITY_BAD:
            rx_start_page(s);
            set_state(s, T30_STATE_III_Q_RTN);
            send_simple_frame(s, T30_RTN);
            break;
        }
        break;
    case T30_PRI_EOM:
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        s->next_rx_step = msg[2] & 0xFE;
        switch (copy_quality(s))
        {
        case T30_COPY_QUALITY_PERFECT:
        case T30_COPY_QUALITY_GOOD:
            rx_end_page(s);
            t4_rx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            s->in_message = FALSE;
            set_state(s, T30_STATE_III_Q_MCF);
            break;
        case T30_COPY_QUALITY_POOR:
            rx_end_page(s);
            t4_rx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            s->in_message = FALSE;
            set_state(s, T30_STATE_III_Q_RTP);
            break;
        case T30_COPY_QUALITY_BAD:
            set_state(s, T30_STATE_III_Q_RTN);
            break;
        }
        break;
    case T30_EOP:
        if (s->phase_d_handler)
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
        s->next_rx_step = msg[2] & 0xFE;
        queue_phase(s, T30_PHASE_D_TX);
        switch (copy_quality(s))
        {
        case T30_COPY_QUALITY_PERFECT:
        case T30_COPY_QUALITY_GOOD:
            rx_end_page(s);
            t4_rx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            s->in_message = FALSE;
            set_state(s, T30_STATE_III_Q_MCF);
            send_simple_frame(s, T30_MCF);
            break;
        case T30_COPY_QUALITY_POOR:
            rx_end_page(s);
            t4_rx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            s->in_message = FALSE;
            set_state(s, T30_STATE_III_Q_RTP);
            send_simple_frame(s, T30_RTP);
            break;
        case T30_COPY_QUALITY_BAD:
            set_state(s, T30_STATE_III_Q_RTN);
            send_simple_frame(s, T30_RTN);
            break;
        }
        break;
    case T30_PRI_EOP:
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, msg[2] & 0xFE);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        s->next_rx_step = msg[2] & 0xFE;
        switch (copy_quality(s))
        {
        case T30_COPY_QUALITY_PERFECT:
        case T30_COPY_QUALITY_GOOD:
            rx_end_page(s);
            t4_rx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            s->in_message = FALSE;
            set_state(s, T30_STATE_III_Q_MCF);
            break;
        case T30_COPY_QUALITY_POOR:
            rx_end_page(s);
            t4_rx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            s->in_message = FALSE;
            set_state(s, T30_STATE_III_Q_RTP);
            break;
        case T30_COPY_QUALITY_BAD:
            set_state(s, T30_STATE_III_Q_RTN);
            break;
        }
        break;
    case T30_DCN:
        s->current_status = T30_ERR_RX_DCNFAX;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_doc_ecm(t30_state_t *s, const uint8_t *msg, int len)
{
    uint8_t fcf2;
    
    /* This actually handles 2 states - _DOC_ECM and _POST_DOC_ECM - as they are very similar */
    switch (msg[2] & 0xFE)
    {
    case T30_DIS:
        process_rx_dis_dtc(s, msg, len);
        break;
    case T30_DCS:
        process_rx_dcs(s, msg, len);
        break;
    case T4_RCP:
        if (s->state == T30_STATE_F_DOC_ECM)
        {
            /* Return to control for partial page */
            queue_phase(s, T30_PHASE_D_RX);
            set_state(s, T30_STATE_F_POST_DOC_ECM);
        }
        else
        {
            /* Ignore extra RCP frames. The source will usually send several to maximise the chance of
               one getting through OK. */
        }
        break;
    case T30_EOR:
        if (len != 4)
        {
            unexpected_frame_length(s, msg, len);
            break;
        }
        fcf2 = msg[3] & 0xFE;
        span_log(&s->logging, SPAN_LOG_FLOW, "Received EOR + %s\n", t30_frametype(msg[3]));
        switch (fcf2)
        {
        case T30_PRI_EOP:
        case T30_PRI_EOM:
        case T30_PRI_MPS:
            /* TODO: Alert operator */
            /* Fall through */
        case T30_NULL:
        case T30_EOP:
        case T30_EOM:
        case T30_EOS:
        case T30_MPS:
            s->next_rx_step = fcf2;
            queue_phase(s, T30_PHASE_D_TX);
            set_state(s, T30_STATE_F_DOC_ECM);
            send_simple_frame(s, T30_ERR);
            break;
        default:
            unexpected_final_frame(s, msg, len);
            break;
        }
        break;
    case T30_PPS:
        process_rx_pps(s, msg, len);
        break;
    case T30_CTC:
        /* T.30 says we change back to long training here */
        s->short_train = FALSE;
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_F_DOC_ECM);
        send_simple_frame(s, T30_CTR);
        break;
    case T30_RR:
        break;
    case T30_DCN:
        s->current_status = T30_ERR_RX_DCNDATA;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_post_rcp_mcf(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    case T30_DCN:
        disconnect(s);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_post_rcp_ppr(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_f_post_rcp_rnr(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_RR:
        if (s->receiver_not_ready_count > 0)
        {
            s->receiver_not_ready_count--;
            queue_phase(s, T30_PHASE_D_TX);
            set_state(s, T30_STATE_F_POST_RCP_RNR);
            send_simple_frame(s, T30_RNR);
        }
        else
        {
            send_deferred_pps_response(s);
        }
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_r(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_DIS:
        process_rx_dis_dtc(s, msg, len);
        break;
    case T30_DCS:
        process_rx_dcs(s, msg, len);
        break;
    case T30_DCN:
        /* Received a DCN while waiting for a DIS */
        s->current_status = T30_ERR_TX_GOTDCN;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_t(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_DIS:
        process_rx_dis_dtc(s, msg, len);
        break;
    case T30_DCN:
        s->current_status = T30_ERR_RX_DCNWHY;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        s->current_status = T30_ERR_TX_NODIS;
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_i(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_ii(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_ii_q(t30_state_t *s, const uint8_t *msg, int len)
{
    t4_stats_t stats;

    switch (msg[2] & 0xFE)
    {
    case T30_MCF:
        switch (s->next_tx_step)
        {
        case T30_MPS:
        case T30_PRI_MPS:
            tx_end_page(s);
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
            if (tx_start_page(s))
            {
                /* TODO: recover */
                break;
            }
            set_state(s, T30_STATE_I);
            queue_phase(s, T30_PHASE_C_NON_ECM_TX);
            break;
        case T30_EOM:
        case T30_PRI_EOM:
        case T30_EOS:
            tx_end_page(s);
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
            t4_tx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            if (span_log_test(&s->logging, SPAN_LOG_FLOW))
            {
                t4_get_transfer_statistics(&s->t4, &stats);
                span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred);
            }
            return_to_phase_b(s, FALSE);
            break;
        case T30_EOP:
        case T30_PRI_EOP:
            tx_end_page(s);
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
            t4_tx_end(&(s->t4));
            s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
            send_dcn(s);
            if (span_log_test(&s->logging, SPAN_LOG_FLOW))
            {
                t4_get_transfer_statistics(&s->t4, &stats);
                span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred);
            }
            break;
        }
        break;
    case T30_RTP:
        switch (s->next_tx_step)
        {
        case T30_MPS:
        case T30_PRI_MPS:
            tx_end_page(s);
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_RTP);
            if (tx_start_page(s))
            {
                /* TODO: recover */
                break;
            }
            /* Send fresh training, and then the next page */
            if (step_fallback_entry(s) < 0)
            {
                /* We have fallen back as far as we can go. Give up. */
                s->current_fallback = 0;
                s->current_status = T30_ERR_CANNOT_TRAIN;
                send_dcn(s);
                break;
            }
            queue_phase(s, T30_PHASE_B_TX);
            restart_sending_document(s);
            break;
        case T30_EOM:
        case T30_PRI_EOM:
        case T30_EOS:
            tx_end_page(s);
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_RTP);
            t4_tx_end(&(s->t4));
            /* TODO: should go back to T, and resend */
            return_to_phase_b(s, TRUE);
            break;
        case T30_EOP:
        case T30_PRI_EOP:
            tx_end_page(s);
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_RTP);
            t4_tx_end(&(s->t4));
            s->current_status = T30_ERR_TX_INVALRSP;
            send_dcn(s);
            break;
        }
        break;
    case T30_RTN:
        switch (s->next_tx_step)
        {
        case T30_MPS:
        case T30_PRI_MPS:
            s->retries = 0;
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_RTN);
            /* Send fresh training, and then repeat the last page */
            if (step_fallback_entry(s) < 0)
            {
                /* We have fallen back as far as we can go. Give up. */
                s->current_fallback = 0;
                s->current_status = T30_ERR_CANNOT_TRAIN;
                send_dcn(s);
                break;
            }
            queue_phase(s, T30_PHASE_B_TX);
            restart_sending_document(s);
            break;
        case T30_EOM:
        case T30_PRI_EOM:
        case T30_EOS:
            s->retries = 0;
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_RTN);
            s->current_status = T30_ERR_TX_INVALRSP;
            return_to_phase_b(s, TRUE);
            break;
        case T30_EOP:
        case T30_PRI_EOP:
            s->retries = 0;
            if (s->phase_d_handler)
                s->phase_d_handler(s, s->phase_d_user_data, T30_RTN);
            s->current_status = T30_ERR_TX_INVALRSP;
            send_dcn(s);
            break;
        }
        break;
    case T30_PIP:
        s->retries = 0;
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, T30_PIP);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        break;
    case T30_PIN:
        s->retries = 0;
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, T30_PIN);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        break;
    case T30_DCN:
        switch (s->next_tx_step)
        {
        case T30_MPS:
        case T30_PRI_MPS:
        case T30_EOM:
        case T30_PRI_EOM:
        case T30_EOS:
            /* Unexpected DCN after EOM, EOS or MPS sequence */
            s->current_status = T30_ERR_RX_DCNPHD;
            break;
        default:
            s->current_status = T30_ERR_TX_BADPG;
            break;
        }
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iii_q_mcf(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_EOP:
    case T30_EOM:
    case T30_EOS:
    case T30_MPS:
        /* Looks like they didn't see our signal. Repeat it */
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_III_Q_MCF);
        send_simple_frame(s, T30_MCF);
        break;
    case T30_DIS:
        if (msg[2] == T30_DTC)
            process_rx_dis_dtc(s, msg, len);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    case T30_DCN:
        disconnect(s);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iii_q_rtp(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_EOP:
    case T30_EOM:
    case T30_EOS:
    case T30_MPS:
        /* Looks like they didn't see our signal. Repeat it */
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_III_Q_RTP);
        send_simple_frame(s, T30_RTP);
        break;
    case T30_DIS:
        if (msg[2] == T30_DTC)
            process_rx_dis_dtc(s, msg, len);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iii_q_rtn(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_EOP:
    case T30_EOM:
    case T30_EOS:
    case T30_MPS:
        /* Looks like they didn't see our signal. Repeat it */
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_III_Q_RTN);
        send_simple_frame(s, T30_RTN);
        break;
    case T30_DIS:
        if (msg[2] == T30_DTC)
            process_rx_dis_dtc(s, msg, len);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    case T30_DCN:
        s->current_status = T30_ERR_RX_DCNNORTN;
        disconnect(s);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iv(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iv_pps_null(t30_state_t *s, const uint8_t *msg, int len)
{
    t4_stats_t stats;

    switch (msg[2] & 0xFE)
    {
    case T30_MCF:
        s->retries = 0;
        s->timer_t5 = 0;
        /* Is there more of the current page to get, or do we move on? */
        span_log(&s->logging, SPAN_LOG_FLOW, "Is there more to send? - %d %d\n", s->ecm_frames, s->ecm_len[255]);
        if (!s->ecm_at_page_end  &&  get_partial_ecm_page(s) > 0)
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Additional image data to send\n");
            s->ecm_block++;
            set_state(s, T30_STATE_IV);
            queue_phase(s, T30_PHASE_C_ECM_TX);
            send_first_ecm_frame(s);
        }
        else
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
            switch (s->next_tx_step)
            {
            case T30_MPS:
            case T30_PRI_MPS:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                if (tx_start_page(s))
                {
                    /* TODO: recover */
                    break;
                }
                if (get_partial_ecm_page(s) > 0)
                {
                    set_state(s, T30_STATE_IV);
                    queue_phase(s, T30_PHASE_C_ECM_TX);
                    send_first_ecm_frame(s);
                }
                break;
            case T30_EOM:
            case T30_PRI_EOM:
            case T30_EOS:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                t4_tx_end(&(s->t4));
                s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
                if (span_log_test(&s->logging, SPAN_LOG_FLOW))
                {
                    t4_get_transfer_statistics(&s->t4, &stats);
                    span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred);
                }
                return_to_phase_b(s, FALSE);
                break;
            case T30_EOP:
            case T30_PRI_EOP:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                t4_tx_end(&(s->t4));
                s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
                send_dcn(s);
                if (span_log_test(&s->logging, SPAN_LOG_FLOW))
                {
                    t4_get_transfer_statistics(&s->t4, &stats);
                    span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred);
                }
                break;
            }
        }
        break;
    case T30_PPR:
        process_rx_ppr(s, msg, len);
        break;
    case T30_RNR:
        if (s->timer_t5 == 0)
            s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5);
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_IV_PPS_RNR);
        send_rr(s);
        break;
    case T30_DCN:
        s->current_status = T30_ERR_TX_BADPG;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        s->current_status = T30_ERR_TX_ECMPHD;
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len)
{
    t4_stats_t stats;

    switch (msg[2] & 0xFE)
    {
    case T30_MCF:
        s->retries = 0;
        s->timer_t5 = 0;
        /* Is there more of the current page to get, or do we move on? */
        span_log(&s->logging, SPAN_LOG_FLOW, "Is there more to send? - %d %d\n", s->ecm_frames, s->ecm_len[255]);
        if (!s->ecm_at_page_end  &&  get_partial_ecm_page(s) > 0)
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Additional image data to send\n");
            s->ecm_block++;
            set_state(s, T30_STATE_IV);
            queue_phase(s, T30_PHASE_C_ECM_TX);
            send_first_ecm_frame(s);
        }
        else
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
            switch (s->next_tx_step)
            {
            case T30_MPS:
            case T30_PRI_MPS:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                if (tx_start_page(s))
                {
                    /* TODO: recover */
                    break;
                }
                if (get_partial_ecm_page(s) > 0)
                {
                    set_state(s, T30_STATE_IV);
                    queue_phase(s, T30_PHASE_C_ECM_TX);
                    send_first_ecm_frame(s);
                }
                break;
            case T30_EOM:
            case T30_PRI_EOM:
            case T30_EOS:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                t4_tx_end(&(s->t4));
                s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
                if (span_log_test(&s->logging, SPAN_LOG_FLOW))
                {
                    t4_get_transfer_statistics(&s->t4, &stats);
                    span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred);
                }
                return_to_phase_b(s, FALSE);
                break;
            case T30_EOP:
            case T30_PRI_EOP:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                t4_tx_end(&(s->t4));
                s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
                send_dcn(s);
                if (span_log_test(&s->logging, SPAN_LOG_FLOW))
                {
                    t4_get_transfer_statistics(&s->t4, &stats);
                    span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred);
                }
                break;
            }
        }
        break;
    case T30_RNR:
        if (s->timer_t5 == 0)
            s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5);
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_IV_PPS_RNR);
        send_rr(s);
        break;
    case T30_PIP:
        s->retries = 0;
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, T30_PIP);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        break;
    case T30_PIN:
        s->retries = 0;
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, T30_PIN);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        break;
    case T30_PPR:
        process_rx_ppr(s, msg, len);
        break;
    case T30_DCN:
        s->current_status = T30_ERR_TX_BADPG;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        s->current_status = T30_ERR_TX_ECMPHD;
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len)
{
    t4_stats_t stats;

    switch (msg[2] & 0xFE)
    {
    case T30_MCF:
        s->retries = 0;
        s->timer_t5 = 0;
        /* Is there more of the current page to get, or do we move on? */
        span_log(&s->logging, SPAN_LOG_FLOW, "Is there more to send? - %d %d\n", s->ecm_frames, s->ecm_len[255]);
        if (!s->ecm_at_page_end  &&  get_partial_ecm_page(s) > 0)
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Additional image data to send\n");
            s->ecm_block++;
            set_state(s, T30_STATE_IV);
            queue_phase(s, T30_PHASE_C_ECM_TX);
            send_first_ecm_frame(s);
        }
        else
        {
            span_log(&s->logging, SPAN_LOG_FLOW, "Moving on to the next page\n");
            switch (s->next_tx_step)
            {
            case T30_MPS:
            case T30_PRI_MPS:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                if (tx_start_page(s))
                {
                    /* TODO: recover */
                    break;
                }
                if (get_partial_ecm_page(s) > 0)
                {
                    set_state(s, T30_STATE_IV);
                    queue_phase(s, T30_PHASE_C_ECM_TX);
                    send_first_ecm_frame(s);
                }
                break;
            case T30_EOM:
            case T30_PRI_EOM:
            case T30_EOS:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                t4_tx_end(&(s->t4));
                s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
                if (span_log_test(&s->logging, SPAN_LOG_FLOW))
                {
                    t4_get_transfer_statistics(&s->t4, &stats);
                    span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred);
                }
                return_to_phase_b(s, FALSE);
                break;
            case T30_EOP:
            case T30_PRI_EOP:
                tx_end_page(s);
                if (s->phase_d_handler)
                    s->phase_d_handler(s, s->phase_d_user_data, T30_MCF);
                t4_tx_end(&(s->t4));
                s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
                send_dcn(s);
                if (span_log_test(&s->logging, SPAN_LOG_FLOW))
                {
                    t4_get_transfer_statistics(&s->t4, &stats);
                    span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred);
                }
                break;
            }
        }
        break;
    case T30_RNR:
        if (s->timer_t5 == 0)
            s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5);
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_IV_PPS_RNR);
        send_rr(s);
        break;
    case T30_PIP:
        s->retries = 0;
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, T30_PIP);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        break;
    case T30_PIN:
        s->retries = 0;
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, T30_PIN);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        break;
    case T30_DCN:
        s->current_status = T30_ERR_RX_DCNRRD;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iv_ctc(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_CTR:
        /* Valid response to a CTC received */
        /* T.30 says we change back to long training here */
        s->short_train = FALSE;
        /* Initiate resending of the remainder of the frames. */
        set_state(s, T30_STATE_IV);
        queue_phase(s, T30_PHASE_C_ECM_TX);
        send_first_ecm_frame(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iv_eor(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_RNR:
        if (s->timer_t5 == 0)
            s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5);
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_IV_EOR_RNR);
        send_rr(s);
        break;
    case T30_PIN:
        s->retries = 0;
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, T30_PIN);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        break;
    case T30_ERR:
        /* TODO: Continue with the next message if MPS or EOM? */
        s->timer_t5 = 0;
        send_dcn(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_iv_eor_rnr(t30_state_t *s, const uint8_t *msg, int len)
{
    switch (msg[2] & 0xFE)
    {
    case T30_RNR:
        if (s->timer_t5 == 0)
            s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5);
        queue_phase(s, T30_PHASE_D_TX);
        set_state(s, T30_STATE_IV_EOR_RNR);
        send_rr(s);
        break;
    case T30_PIN:
        s->retries = 0;
        if (s->phase_d_handler)
        {
            s->phase_d_handler(s, s->phase_d_user_data, T30_PIN);
            s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
        }
        break;
    case T30_ERR:
        /* TODO: Continue with the next message if MPS or EOM? */
        s->timer_t5 = 0;
        send_dcn(s);
        break;
    case T30_DCN:
        s->current_status = T30_ERR_RX_DCNRRD;
        disconnect(s);
        break;
    case T30_CRP:
        repeat_last_command(s);
        break;
    case T30_FNV:
        process_rx_fnv(s, msg, len);
        break;
    default:
        /* We don't know what to do with this. */
        unexpected_final_frame(s, msg, len);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void process_state_call_finished(t30_state_t *s, const uint8_t *msg, int len)
{
    /* Simply ignore anything which comes in when we have declared the call
       to have finished. */
}
/*- End of function --------------------------------------------------------*/

static void process_rx_control_msg(t30_state_t *s, const uint8_t *msg, int len)
{
    /* We should only get good frames here. */
    print_frame(s, "Rx: ", msg, len);
    if (s->real_time_frame_handler)
        s->real_time_frame_handler(s, s->real_time_frame_user_data, TRUE, msg, len);

    if ((msg[1] & 0x10) == 0)
    {
        /* This is not a final frame */
        /* It seems we should not restart the command or response timer when exchanging HDLC image
           data. If the modem looses sync in the middle of the image, we should just wait until
           the carrier goes away before proceeding. */
        if (s->phase != T30_PHASE_C_ECM_RX)
        {
            /* Restart the command or response timer, T2 or T4 */
            switch (s->timer_t2_t4_is)
            {
            case TIMER_IS_T2:
            case TIMER_IS_T2A:
            case TIMER_IS_T2B:
                timer_t2a_start(s);
                break;
            case TIMER_IS_T4:
            case TIMER_IS_T4A:
            case TIMER_IS_T4B:
                timer_t4a_start(s);
                break;
            }
        }
        /* The following handles all the message types we expect to get without
           a final frame tag. If we get one that T.30 says we should not expect
           in a particular context, its pretty harmless, so don't worry. */
        switch (msg[2] & 0xFE)
        {
        case (T30_CSI & 0xFE):
            /* Called subscriber identification or Calling subscriber identification (T30_CIG) */
            /* OK in (NSF) (CSI) DIS */
            /* OK in (NSC) (CIG) DTC */
            /* OK in (PWD) (SEP) (CIG) DTC */
            decode_20digit_msg(s, s->rx_info.ident, &msg[2], len - 2);
            break;
        case (T30_NSF & 0xFE):
            if (msg[2] == T30_NSF)
            {
                /* Non-standard facilities */
                /* OK in (NSF) (CSI) DIS */
                t35_decode(&msg[3], len - 3, &s->country, &s->vendor, &s->model);
                if (s->country)
                    span_log(&s->logging, SPAN_LOG_FLOW, "The remote was made in '%s'\n", s->country);
                if (s->vendor)
                    span_log(&s->logging, SPAN_LOG_FLOW, "The remote was made by '%s'\n", s->vendor);
                if (s->model)
                    span_log(&s->logging, SPAN_LOG_FLOW, "The remote is a '%s'\n", s->model);
            }
            else
            {
                /* NSC - Non-standard facilities command */
                /* OK in (NSC) (CIG) DTC */
            }
            break;
        case (T30_PWD & 0xFE):
            if (msg[2] == T30_PWD)
            {
                /* Password */
                /* OK in (SUB) (SID) (SEP) (PWD) (TSI) DCS */
                /* OK in (SUB) (SID) (SEP) (PWD) (CIG) DTC */
                decode_20digit_msg(s, s->rx_info.password, &msg[2], len - 2);
            }
            else
            {
                unexpected_frame(s, msg, len);
            }
            break;
        case (T30_SEP & 0xFE):
            if (msg[2] == T30_SEP)
            {
                /* Selective polling address */
                /* OK in (PWD) (SEP) (CIG) DTC */
                decode_20digit_msg(s, s->rx_info.selective_polling_address, &msg[2], len - 2);
            }
            else
            {
                unexpected_frame(s, msg, len);
            }
            break;
        case (T30_PSA & 0xFE):
            if (msg[2] == T30_PSA)
            {
                /* Polled sub-address */
                decode_20digit_msg(s, s->rx_info.polled_sub_address, &msg[2], len - 2);
            }
            else
            {
                unexpected_frame(s, msg, len);
            }
            break;
        case (T30_CIA & 0xFE):
            if (msg[2] == T30_CIA)
            {
                /* Calling subscriber internet address */
                decode_url_msg(s, NULL, &msg[2], len - 2);
            }
            else
            {
                unexpected_frame(s, msg, len);
            }
            break;
        case (T30_ISP & 0xFE):
            if (msg[2] == T30_ISP)
            {
                /* Internet selective polling address */
                decode_url_msg(s, NULL, &msg[2], len - 2);
            }
            else
            {
                unexpected_frame(s, msg, len);
            }
            break;
        case (T30_TSI & 0xFE):
            /* Transmitting subscriber identity */
            /* OK in (PWD) (SUB) (TSI) DCS */
            decode_20digit_msg(s, s->rx_info.ident, &msg[2], len - 2);
            break;
        case (T30_NSS & 0xFE):
            /* Non-standard facilities set-up */
            break;
        case (T30_SUB & 0xFE):
            /* Sub-address */
            /* OK in (PWD) (SUB) (TSI) DCS */
            decode_20digit_msg(s, s->rx_info.sub_address, &msg[2], len - 2);
            break;
        case (T30_SID & 0xFE):
            /* Sender Identification */
            /* OK in (SUB) (SID) (SEP) (PWD) (TSI) DCS */
            /* OK in (SUB) (SID) (SEP) (PWD) (CIG) DTC */
            decode_20digit_msg(s, s->rx_info.sender_ident, &msg[2], len - 2);
            break;
        case (T30_CSA & 0xFE):
            /* Calling subscriber internet address */
            decode_url_msg(s, NULL, &msg[2], len - 2);
            break;
        case (T30_TSA & 0xFE):
            /* Transmitting subscriber internet address */
            decode_url_msg(s, NULL, &msg[2], len - 2);
            break;
        case (T30_IRA & 0xFE):
            /* Internet routing address */
            decode_url_msg(s, NULL, &msg[2], len - 2);
            break;
        case T4_FCD:
            process_rx_fcd(s, msg, len);
            break;
        case T4_RCP:
            process_rx_rcp(s, msg, len);
            break;
        default:
            unexpected_non_final_frame(s, msg, len);
            break;
        }
    }
    else
    {
        /* This is a final frame */
        /* Once we have any successful message from the far end, we
           cancel timer T1 */
        s->timer_t0_t1 = 0;

        /* The following handles context sensitive message types, which should
           occur at the end of message sequences. They should, therefore have
           the final frame flag set. */
        span_log(&s->logging, SPAN_LOG_FLOW, "In state %d\n", s->state);

        switch (s->state)
        {
        case T30_STATE_ANSWERING:
            process_state_answering(s, msg, len);
            break;
        case T30_STATE_B:
            process_state_b(s, msg, len);
            break;
        case T30_STATE_C:
            process_state_c(s, msg, len);
            break;
        case T30_STATE_D:
            process_state_d(s, msg, len);
            break;
        case T30_STATE_D_TCF:
            process_state_d_tcf(s, msg, len);
            break;
        case T30_STATE_D_POST_TCF:
            process_state_d_post_tcf(s, msg, len);
            break;
        case T30_STATE_F_TCF:
            process_state_f_tcf(s, msg, len);
            break;
        case T30_STATE_F_CFR:
            process_state_f_cfr(s, msg, len);
            break;
        case T30_STATE_F_FTT:
            process_state_f_ftt(s, msg, len);
            break;
        case T30_STATE_F_DOC_NON_ECM:
            process_state_f_doc_non_ecm(s, msg, len);
            break;
        case T30_STATE_F_POST_DOC_NON_ECM:
            process_state_f_post_doc_non_ecm(s, msg, len);
            break;
        case T30_STATE_F_DOC_ECM:
        case T30_STATE_F_POST_DOC_ECM:
            process_state_f_doc_ecm(s, msg, len);
            break;
        case T30_STATE_F_POST_RCP_MCF:
            process_state_f_post_rcp_mcf(s, msg, len);
            break;
        case T30_STATE_F_POST_RCP_PPR:
            process_state_f_post_rcp_ppr(s, msg, len);
            break;
        case T30_STATE_F_POST_RCP_RNR:
            process_state_f_post_rcp_rnr(s, msg, len);
            break;
        case T30_STATE_R:
            process_state_r(s, msg, len);
            break;
        case T30_STATE_T:
            process_state_t(s, msg, len);
            break;
        case T30_STATE_I:
            process_state_i(s, msg, len);
            break;
        case T30_STATE_II:
            process_state_ii(s, msg, len);
            break;
        case T30_STATE_II_Q:
            process_state_ii_q(s, msg, len);
            break;
        case T30_STATE_III_Q_MCF:
            process_state_iii_q_mcf(s, msg, len);
            break;
        case T30_STATE_III_Q_RTP:
            process_state_iii_q_rtp(s, msg, len);
            break;
        case T30_STATE_III_Q_RTN:
            process_state_iii_q_rtn(s, msg, len);
            break;
        case T30_STATE_IV:
            process_state_iv(s, msg, len);
            break;
        case T30_STATE_IV_PPS_NULL:
            process_state_iv_pps_null(s, msg, len);
            break;
        case T30_STATE_IV_PPS_Q:
            process_state_iv_pps_q(s, msg, len);
            break;
        case T30_STATE_IV_PPS_RNR:
            process_state_iv_pps_rnr(s, msg, len);
            break;
        case T30_STATE_IV_CTC:
            process_state_iv_ctc(s, msg, len);
            break;
        case T30_STATE_IV_EOR:
            process_state_iv_eor(s, msg, len);
            break;
        case T30_STATE_IV_EOR_RNR:
            process_state_iv_eor_rnr(s, msg, len);
            break;
        case T30_STATE_CALL_FINISHED:
            process_state_call_finished(s, msg, len);
            break;
        default:
            /* We don't know what to do with this. */
            unexpected_final_frame(s, msg, len);
            break;
        }
    }
}
/*- End of function --------------------------------------------------------*/

static void queue_phase(t30_state_t *s, int phase)
{
    if (s->rx_signal_present)
    {
        /* We need to wait for that signal to go away */
        s->next_phase = phase;
    }
    else
    {
        set_phase(s, phase);
        s->next_phase = T30_PHASE_IDLE;
    }
}
/*- End of function --------------------------------------------------------*/

static void set_phase(t30_state_t *s, int phase)
{
    //if (phase = s->phase)
    //    return;
    span_log(&s->logging, SPAN_LOG_FLOW, "Changing from phase %s to %s\n", phase_names[s->phase], phase_names[phase]);
    /* We may be killing a receiver before it has declared the end of the
       signal. Force the signal present indicator to off, because the
       receiver will never be able to. */
    if (s->phase != T30_PHASE_A_CED  &&  s->phase != T30_PHASE_A_CNG)
        s->rx_signal_present = FALSE;
    s->rx_trained = FALSE;
    s->rx_frame_received = FALSE;
    s->phase = phase;
    switch (phase)
    {
    case T30_PHASE_A_CED:
        if (s->set_rx_type_handler)
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_V21, 300, FALSE, TRUE);
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_CED, 0, FALSE, FALSE);
        break;
    case T30_PHASE_A_CNG:
        if (s->set_rx_type_handler)
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_V21, 300, FALSE, TRUE);
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_CNG, 0, FALSE, FALSE);
        break;
    case T30_PHASE_B_RX:
    case T30_PHASE_D_RX:
        if (s->set_rx_type_handler)
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_V21, 300, FALSE, TRUE);
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
        break;
    case T30_PHASE_B_TX:
    case T30_PHASE_D_TX:
        if (!s->far_end_detected  &&  s->timer_t0_t1 > 0)
        {
            s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T1);
            s->far_end_detected = TRUE;
        }
        if (s->set_rx_type_handler)
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_V21, 300, FALSE, TRUE);
        break;
    case T30_PHASE_C_NON_ECM_RX:
        if (s->set_rx_type_handler)
        {
            /* Momentarily stop the receive modem, so the next change is forced to happen. If we don't do this
               an HDLC message on the slow modem, which has disabled the fast modem, will prevent the same
               fast modem from restarting. */
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
            s->set_rx_type_handler(s->set_rx_type_user_data, fallback_sequence[s->current_fallback].modem_type, fallback_sequence[s->current_fallback].bit_rate, s->short_train, FALSE);
        }
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
        break;
    case T30_PHASE_C_NON_ECM_TX:
        /* Pause before switching from anything to phase C */
        /* Always prime the training count for 1.5s of data at the current rate. Its harmless if
           we prime it and are not doing TCF. */
        s->tcf_test_bits = (3*fallback_sequence[s->current_fallback].bit_rate)/2;
        if (s->set_rx_type_handler)
        {
            /* Momentarily stop the receive modem, so the next change is forced to happen. If we don't do this
               an HDLC message on the slow modem, which has disabled the fast modem, will prevent the same
               fast modem from restarting. */
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
        }
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, fallback_sequence[s->current_fallback].modem_type, fallback_sequence[s->current_fallback].bit_rate, s->short_train, FALSE);
        break;
    case T30_PHASE_C_ECM_RX:
        if (s->set_rx_type_handler)
            s->set_rx_type_handler(s->set_rx_type_user_data, fallback_sequence[s->current_fallback].modem_type, fallback_sequence[s->current_fallback].bit_rate, s->short_train, TRUE);
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
        break;
    case T30_PHASE_C_ECM_TX:
        /* Pause before switching from anything to phase C */
        if (s->set_rx_type_handler)
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, fallback_sequence[s->current_fallback].modem_type, fallback_sequence[s->current_fallback].bit_rate, s->short_train, TRUE);
        break;
    case T30_PHASE_E:
        /* Send a little silence before ending things, to ensure the
           buffers are all flushed through, and the far end has seen
           the last message we sent. */
        s->tcf_test_bits = 0;
        s->tcf_current_zeros = 0;
        s->tcf_most_zeros = 0;
        if (s->set_rx_type_handler)
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_NONE, 0, FALSE, FALSE);
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_PAUSE, 0, FINAL_FLUSH_TIME, FALSE);
        break;
    case T30_PHASE_CALL_FINISHED:
        if (s->set_rx_type_handler)
            s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_DONE, 0, FALSE, FALSE);
        if (s->set_tx_type_handler)
            s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_DONE, 0, FALSE, FALSE);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void set_state(t30_state_t *s, int state)
{
    if (s->state != state)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Changing from state %d to %d\n", s->state, state);
        s->state = state;
    }
    s->step = 0;
}
/*- End of function --------------------------------------------------------*/

static void repeat_last_command(t30_state_t *s)
{
    s->step = 0;
    if (++s->retries >= MAX_MESSAGE_TRIES)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Too many retries. Giving up.\n");
        switch (s->state)
        {
        case T30_STATE_D_POST_TCF:
            /* Received no response to DCS or TCF */
            s->current_status = T30_ERR_TX_PHBDEAD;
            break;
        case T30_STATE_II_Q:
        case T30_STATE_IV_PPS_NULL:
        case T30_STATE_IV_PPS_Q:
            /* No response after sending a page */
            s->current_status = T30_ERR_TX_PHDDEAD;
            break;
        default:
            /* Disconnected after permitted retries */
            s->current_status = T30_ERR_RETRYDCN;
            break;
        }
        send_dcn(s);
        return;
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "Retry number %d\n", s->retries);
    switch (s->state)
    {
    case T30_STATE_R:
        s->dis_received = FALSE;
        queue_phase(s, T30_PHASE_B_TX);
        send_dis_or_dtc_sequence(s, TRUE);
        break;
    case T30_STATE_III_Q_MCF:
        queue_phase(s, T30_PHASE_D_TX);
        send_simple_frame(s, T30_MCF);
        break;
    case T30_STATE_III_Q_RTP:
        queue_phase(s, T30_PHASE_D_TX);
        send_simple_frame(s, T30_RTP);
        break;
    case T30_STATE_III_Q_RTN:
        queue_phase(s, T30_PHASE_D_TX);
        send_simple_frame(s, T30_RTN);
        break;
    case T30_STATE_II_Q:
        queue_phase(s, T30_PHASE_D_TX);
        send_simple_frame(s, s->next_tx_step);
        break;
    case T30_STATE_IV_PPS_NULL:
    case T30_STATE_IV_PPS_Q:
        queue_phase(s, T30_PHASE_D_TX);
        send_pps_frame(s);
        break;
    case T30_STATE_IV_PPS_RNR:
    case T30_STATE_IV_EOR_RNR:
        queue_phase(s, T30_PHASE_D_TX);
        send_rr(s);
        break;
    case T30_STATE_D:
        queue_phase(s, T30_PHASE_B_TX);
        send_dcs_sequence(s, TRUE);
        break;
    case T30_STATE_F_FTT:
        queue_phase(s, T30_PHASE_B_TX);
        send_simple_frame(s, T30_FTT);
        break;
    case T30_STATE_F_CFR:
        queue_phase(s, T30_PHASE_B_TX);
        send_cfr_sequence(s, TRUE);
        break;
    case T30_STATE_D_POST_TCF:
        /* Need to send the whole training thing again */
        s->short_train = FALSE;
        queue_phase(s, T30_PHASE_B_TX);
        send_dcs_sequence(s, TRUE);
        break;
    case T30_STATE_F_POST_RCP_RNR:
        /* Just ignore */
        break;
    default:
        span_log(&s->logging,
                 SPAN_LOG_FLOW,
                 "Repeat command called with nothing to repeat - phase %s, state %d\n",
                 phase_names[s->phase],
                 s->state);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void timer_t2_start(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Start T2\n");
    s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2);
    s->timer_t2_t4_is = TIMER_IS_T2;
}
/*- End of function --------------------------------------------------------*/

static void timer_t2a_start(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Start T2A\n");
    s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2A);
    s->timer_t2_t4_is = TIMER_IS_T2A;
}
/*- End of function --------------------------------------------------------*/

static void timer_t2b_start(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Start T2B\n");
    s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2B);
    s->timer_t2_t4_is = TIMER_IS_T2B;
}
/*- End of function --------------------------------------------------------*/

static void timer_t4_start(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Start T4\n");
    s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T4);
    s->timer_t2_t4_is = TIMER_IS_T4;
}
/*- End of function --------------------------------------------------------*/

static void timer_t4a_start(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Start T4A\n");
    s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T4A);
    s->timer_t2_t4_is = TIMER_IS_T4A;
}
/*- End of function --------------------------------------------------------*/

static void timer_t4b_start(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Start T4B\n");
    s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T4B);
    s->timer_t2_t4_is = TIMER_IS_T4B;
}
/*- End of function --------------------------------------------------------*/

static void timer_t0_expired(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "T0 expired in state %d\n", s->state);
    s->current_status = T30_ERR_T0_EXPIRED;
    /* Just end the call */
    disconnect(s);
}
/*- End of function --------------------------------------------------------*/

static void timer_t1_expired(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "T1 expired in state %d\n", s->state);
    /* The initial connection establishment has timeout out. In other words, we
       have been unable to communicate successfully with a remote machine.
       It is time to abandon the call. */
    s->current_status = T30_ERR_T1_EXPIRED;
    switch (s->state)
    {
    case T30_STATE_T:
        /* Just end the call */
        disconnect(s);
        break;
    case T30_STATE_R:
        /* Send disconnect, and then end the call. Since we have not
           successfully contacted the far end, it is unclear why we should
           send a disconnect message at this point. However, it is what T.30
           says we should do. */
        send_dcn(s);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

static void timer_t2_expired(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "T2 expired in phase %s, state %d\n", phase_names[s->phase], s->state);
    switch (s->state)
    {
    case T30_STATE_III_Q_MCF:
    case T30_STATE_III_Q_RTP:
    case T30_STATE_III_Q_RTN:
    case T30_STATE_F_POST_RCP_PPR:
    case T30_STATE_F_POST_RCP_MCF:
        switch (s->next_rx_step)
        {
        case T30_EOM:
        case T30_PRI_EOM:
        case T30_EOS:
            /* We didn't receive a response to our T30_MCF after T30_EOM, so we must be OK
               to proceed to phase B, and pretty act like its the beginning of a call. */
            span_log(&s->logging, SPAN_LOG_FLOW, "Returning to phase B after %s\n", t30_frametype(s->next_rx_step));
            set_phase(s, T30_PHASE_B_TX);
            timer_t2_start(s);
            s->dis_received = FALSE;
            send_dis_or_dtc_sequence(s, TRUE);
            return;
        }
        break;
    case T30_STATE_F_TCF:
        span_log(&s->logging, SPAN_LOG_FLOW, "No TCF data received\n");
        set_phase(s, T30_PHASE_B_TX);
        set_state(s, T30_STATE_F_FTT);
        send_simple_frame(s, T30_FTT);
        return;
    case T30_STATE_F_DOC_ECM:
    case T30_STATE_F_DOC_NON_ECM:
        /* While waiting for FAX page */
        s->current_status = T30_ERR_RX_T2EXPFAX;
        break;
    case T30_STATE_F_POST_DOC_ECM:
    case T30_STATE_F_POST_DOC_NON_ECM:
        /* While waiting for next FAX page */
        s->current_status = T30_ERR_RX_T2EXPMPS;
        break;
#if 0
    case ??????:
        /* While waiting for DCN */
        s->current_status = T30_ERR_RX_T2EXPDCN;
        break;
    case ??????:
        /* While waiting for phase D */
        s->current_status = T30_ERR_RX_T2EXPD;
        break;
#endif
    case T30_STATE_IV_PPS_RNR:
    case T30_STATE_IV_EOR_RNR:
        /* While waiting for RR command */
        s->current_status = T30_ERR_RX_T2EXPRR;
        break;
    case T30_STATE_R:
        /* While waiting for NSS, DCS or MCF */
        s->current_status = T30_ERR_RX_T2EXP;
        break;
    case T30_STATE_F_FTT:
        break;
    }
    queue_phase(s, T30_PHASE_B_TX);
    start_receiving_document(s);
}
/*- End of function --------------------------------------------------------*/

static void timer_t2a_expired(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "T2A expired in phase %s, state %d. An HDLC frame lasted too long.\n", phase_names[s->phase], s->state);
    s->current_status = T30_ERR_HDLC_CARRIER;
    disconnect(s);
}
/*- End of function --------------------------------------------------------*/

static void timer_t2b_expired(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "T2B expired in phase %s, state %d. The line is now quiet.\n", phase_names[s->phase], s->state);
    timer_t2_expired(s);
}
/*- End of function --------------------------------------------------------*/

static void timer_t3_expired(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "T3 expired in phase %s, state %d\n", phase_names[s->phase], s->state);
    s->current_status = T30_ERR_T3_EXPIRED;
    disconnect(s);
}
/*- End of function --------------------------------------------------------*/

static void timer_t4_expired(t30_state_t *s)
{
    /* There was no response (or only a corrupt response) to a command,
       within the T4 timeout period. */
    span_log(&s->logging, SPAN_LOG_FLOW, "T4 expired in phase %s, state %d\n", phase_names[s->phase], s->state);
    /* Of course, things might just be a little late, especially if there are T.38
       links in the path. There is no point in simply timing out, and resending,
       if we are currently receiving something from the far end - its a half-duplex
       path, so the two transmissions will conflict. Our best strategy is to wait
       until there is nothing being received, or give up after a long backstop timeout.
       In the meantime, if we get a meaningful, if somewhat delayed, response, we
       should accept it and carry on. */
    repeat_last_command(s);
}
/*- End of function --------------------------------------------------------*/

static void timer_t4a_expired(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "T4A expired in phase %s, state %d. An HDLC frame lasted too long.\n", phase_names[s->phase], s->state);
    s->current_status = T30_ERR_HDLC_CARRIER;
    disconnect(s);
}
/*- End of function --------------------------------------------------------*/

static void timer_t4b_expired(t30_state_t *s)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "T4B expired in phase %s, state %d. The line is now quiet.\n", phase_names[s->phase], s->state);
    timer_t4_expired(s);
}
/*- End of function --------------------------------------------------------*/

static void timer_t5_expired(t30_state_t *s)
{
    /* Give up waiting for the receiver to become ready in error correction mode */
    span_log(&s->logging, SPAN_LOG_FLOW, "T5 expired in phase %s, state %d\n", phase_names[s->phase], s->state);
    s->current_status = T30_ERR_TX_T5EXP;
}
/*- End of function --------------------------------------------------------*/

static void decode_20digit_msg(t30_state_t *s, char *msg, const uint8_t *pkt, int len)
{
    int p;
    int k;
    char text[T30_MAX_IDENT_LEN + 1];

    if (msg == NULL)
        msg = text;
    if (len > T30_MAX_IDENT_LEN + 1)
    {
        unexpected_frame_length(s, pkt, len);
        msg[0] = '\0';
        return;
    }
    p = len;
    /* Strip trailing spaces */
    while (p > 1  &&  pkt[p - 1] == ' ')
        p--;
    /* The string is actually backwards in the message */
    k = 0;
    while (p > 1)
        msg[k++] = pkt[--p];
    msg[k] = '\0';
    span_log(&s->logging, SPAN_LOG_FLOW, "Remote gave %s as: \"%s\"\n", t30_frametype(pkt[0]), msg);
}
/*- End of function --------------------------------------------------------*/

static void decode_url_msg(t30_state_t *s, char *msg, const uint8_t *pkt, int len)
{
    char text[77 + 1];

    /* TODO: decode properly, as per T.30 5.3.6.2.12 */
    if (msg == NULL)
        msg = text;
    if (len < 3  ||  len > 77 + 3  ||  len != pkt[2] + 3)
    {
        unexpected_frame_length(s, pkt, len);
        msg[0] = '\0';
        return;
    }
    /* First octet is the sequence number of the packet.
            Bit 7 = 1 for more follows, 0 for last packet in the sequence.
            Bits 6-0 = The sequence number, 0 to 0x7F
       Second octet is the type of internet address.
            Bits 7-4 = reserved
            Bits 3-0 = type:
                    0 = reserved
                    1 = e-mail address
                    2 = URL
                    3 = TCP/IP V4
                    4 = TCP/IP V6
                    5 = international phone number, in the usual +... format
                    6-15 = reserved
       Third octet is the length of the internet address
            Bit 7 = 1 for more follows, 0 for last packet in the sequence.
            Bits 6-0 = length
     */
    memcpy(msg, &pkt[3], len - 3);
    msg[len - 3] = '\0';
    span_log(&s->logging, SPAN_LOG_FLOW, "Remote fax gave %s as: %d, %d, \"%s\"\n", t30_frametype(pkt[0]), pkt[0], pkt[1], msg);
}
/*- End of function --------------------------------------------------------*/

static void t30_non_ecm_rx_status(void *user_data, int status)
{
    t30_state_t *s;
    int was_trained;

    s = (t30_state_t *) user_data;
    span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM signal status is %s (%d) in state %d\n", signal_status_to_str(status), status, s->state);
    switch (status)
    {
    case SIG_STATUS_TRAINING_IN_PROGRESS:
        break;
    case SIG_STATUS_TRAINING_FAILED:
        s->rx_trained = FALSE;
        break;
    case SIG_STATUS_TRAINING_SUCCEEDED:
        /* The modem is now trained */
        /* In case we are in trainability test mode... */
        s->tcf_test_bits = 0;
        s->tcf_current_zeros = 0;
        s->tcf_most_zeros = 0;
        s->rx_signal_present = TRUE;
        s->rx_trained = TRUE;
        s->timer_t2_t4 = 0;
        break;
    case SIG_STATUS_CARRIER_UP:
        break;
    case SIG_STATUS_CARRIER_DOWN:
        was_trained = s->rx_trained;
        s->rx_signal_present = FALSE;
        s->rx_trained = FALSE;
        switch (s->state)
        {
        case T30_STATE_F_TCF:
            /* Only respond if we managed to actually sync up with the source. We don't
               want to respond just because we saw a click. These often occur just
               before the real signal, with many modems. Presumably this is due to switching
               within the far end modem. We also want to avoid the possibility of responding
               to the tail end of any slow modem signal. If there was a genuine data signal
               which we failed to train on it should not matter. If things are that bad, we
               do not stand much chance of good quality communications. */
            if (was_trained)
            {
                /* Although T.30 says the training test should be 1.5s of all 0's, some FAX
                   machines send a burst of all 1's before the all 0's. Tolerate this. */
                if (s->tcf_current_zeros > s->tcf_most_zeros)
                    s->tcf_most_zeros = s->tcf_current_zeros;
                span_log(&s->logging, SPAN_LOG_FLOW, "Trainability (TCF) test result - %d total bits. longest run of zeros was %d\n", s->tcf_test_bits, s->tcf_most_zeros);
                if (s->tcf_most_zeros < fallback_sequence[s->current_fallback].bit_rate)
                {
                    span_log(&s->logging, SPAN_LOG_FLOW, "Trainability (TCF) test failed - longest run of zeros was %d\n", s->tcf_most_zeros);
                    set_phase(s, T30_PHASE_B_TX);
                    set_state(s, T30_STATE_F_FTT);
                    send_simple_frame(s, T30_FTT);
                }
                else
                {
                    /* The training went OK */
                    s->short_train = TRUE;
                    s->in_message = TRUE;
                    rx_start_page(s);
                    set_phase(s, T30_PHASE_B_TX);
                    set_state(s, T30_STATE_F_CFR);
                    send_cfr_sequence(s, TRUE);
                }
            }
            break;
        case T30_STATE_F_POST_DOC_NON_ECM:
            /* Page ended cleanly */
            if (s->current_status == T30_ERR_RX_NOCARRIER)
                s->current_status = T30_ERR_OK;
            break;
        default:
            /* We should be receiving a document right now, but it did not end cleanly. */
            if (was_trained)
            {
                span_log(&s->logging, SPAN_LOG_WARNING, "Page did not end cleanly\n");
                /* We trained OK, so we should have some kind of received page, even though
                   it did not end cleanly. */
                set_state(s, T30_STATE_F_POST_DOC_NON_ECM);
                set_phase(s, T30_PHASE_D_RX);
                timer_t2_start(s);
                if (s->current_status == T30_ERR_RX_NOCARRIER)
                    s->current_status = T30_ERR_OK;
            }
            else
            {
                span_log(&s->logging, SPAN_LOG_WARNING, "Non-ECM carrier not found\n");
                s->current_status = T30_ERR_RX_NOCARRIER;
            }
            break;
        }
        if (s->next_phase != T30_PHASE_IDLE)
        {
            set_phase(s, s->next_phase);
            s->next_phase = T30_PHASE_IDLE;
        }
        break;
    default:
        span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected non-ECM rx status - %d!\n", status);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE_NONSTD(void) t30_non_ecm_put_bit(void *user_data, int bit)
{
    t30_state_t *s;

    if (bit < 0)
    {
        t30_non_ecm_rx_status(user_data, bit);
        return;
    }
    s = (t30_state_t *) user_data;
    switch (s->state)
    {
    case T30_STATE_F_TCF:
        /* Trainability test */
        s->tcf_test_bits++;
        if (bit)
        {
            if (s->tcf_current_zeros > s->tcf_most_zeros)
                s->tcf_most_zeros = s->tcf_current_zeros;
            s->tcf_current_zeros = 0;
        }
        else
        {
            s->tcf_current_zeros++;
        }
        break;
    case T30_STATE_F_DOC_NON_ECM:
        /* Document transfer */
        if (t4_rx_put_bit(&s->t4, bit))
        {
            /* That is the end of the document */
            set_state(s, T30_STATE_F_POST_DOC_NON_ECM);
            queue_phase(s, T30_PHASE_D_RX);
            timer_t2_start(s);
        }
        break;
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t30_non_ecm_put_byte(void *user_data, int byte)
{
    t30_state_t *s;

    s = (t30_state_t *) user_data;
    switch (s->state)
    {
    case T30_STATE_F_TCF:
        /* Trainability test */
        /* This makes counting zeros fast, but approximate. That really doesn't matter */
        s->tcf_test_bits += 8;
        if (byte)
        {
            if (s->tcf_current_zeros > s->tcf_most_zeros)
                s->tcf_most_zeros = s->tcf_current_zeros;
            s->tcf_current_zeros = 0;
        }
        else
        {
            s->tcf_current_zeros += 8;
        }
        break;
    case T30_STATE_F_DOC_NON_ECM:
        /* Document transfer */
        if (t4_rx_put_byte(&s->t4, (uint8_t) byte))
        {
            /* That is the end of the document */
            set_state(s, T30_STATE_F_POST_DOC_NON_ECM);
            queue_phase(s, T30_PHASE_D_RX);
            timer_t2_start(s);
        }
        break;
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t30_non_ecm_put_chunk(void *user_data, const uint8_t buf[], int len)
{
    t30_state_t *s;
    int i;

    s = (t30_state_t *) user_data;
    switch (s->state)
    {
    case T30_STATE_F_TCF:
        /* Trainability test */
        /* This makes counting zeros fast, but approximate. That really doesn't matter */
        s->tcf_test_bits += 8*len;
        for (i = 0;  i < len;  i++)
        {
            if (buf[i])
            {
                if (s->tcf_current_zeros > s->tcf_most_zeros)
                    s->tcf_most_zeros = s->tcf_current_zeros;
                s->tcf_current_zeros = 0;
            }
            else
            {
                s->tcf_current_zeros += 8;
            }
        }
        break;
    case T30_STATE_F_DOC_NON_ECM:
        /* Document transfer */
        if (t4_rx_put_chunk(&s->t4, buf, len))
        {
            /* That is the end of the document */
            set_state(s, T30_STATE_F_POST_DOC_NON_ECM);
            queue_phase(s, T30_PHASE_D_RX);
            timer_t2_start(s);
        }
        break;
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE_NONSTD(int) t30_non_ecm_get_bit(void *user_data)
{
    int bit;
    t30_state_t *s;

    s = (t30_state_t *) user_data;
    switch (s->state)
    {
    case T30_STATE_D_TCF:
        /* Trainability test. */
        bit = 0;
        if (s->tcf_test_bits-- < 0)
        {
            /* Finished sending training test. */
            bit = SIG_STATUS_END_OF_DATA;
        }
        break;
    case T30_STATE_I:
        /* Transferring real data. */
        bit = t4_tx_get_bit(&(s->t4));
        break;
    case T30_STATE_D_POST_TCF:
    case T30_STATE_II_Q:
        /* We should be padding out a block of samples if we are here */
        bit = 0;
        break;
    default:
        span_log(&s->logging, SPAN_LOG_WARNING, "t30_non_ecm_get_bit in bad state %d\n", s->state);
        bit = SIG_STATUS_END_OF_DATA;
        break;
    }
    return bit;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t30_non_ecm_get_byte(void *user_data)
{
    int byte;
    t30_state_t *s;

    s = (t30_state_t *) user_data;
    switch (s->state)
    {
    case T30_STATE_D_TCF:
        /* Trainability test. */
        byte = 0;
        if ((s->tcf_test_bits -= 8) < 0)
        {
            /* Finished sending training test. */
            byte = 0x100;
        }
        break;
    case T30_STATE_I:
        /* Transferring real data. */
        byte = t4_tx_get_byte(&(s->t4));
        break;
    case T30_STATE_D_POST_TCF:
    case T30_STATE_II_Q:
        /* We should be padding out a block of samples if we are here */
        byte = 0;
        break;
    default:
        span_log(&s->logging, SPAN_LOG_WARNING, "t30_non_ecm_get_byte in bad state %d\n", s->state);
        byte = 0x100;
        break;
    }
    return byte;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t30_non_ecm_get_chunk(void *user_data, uint8_t buf[], int max_len)
{
    int len;
    t30_state_t *s;

    s = (t30_state_t *) user_data;
    switch (s->state)
    {
    case T30_STATE_D_TCF:
        /* Trainability test. */
        for (len = 0;  len < max_len;  len++)
        {
            buf[len] = 0;
            if ((s->tcf_test_bits -= 8) < 0)
                break;
        }
        break;
    case T30_STATE_I:
        /* Transferring real data. */
        len = t4_tx_get_chunk(&s->t4, buf, max_len);
        break;
    case T30_STATE_D_POST_TCF:
    case T30_STATE_II_Q:
        /* We should be padding out a block of samples if we are here */
        len = 0;
        break;
    default:
        span_log(&s->logging, SPAN_LOG_WARNING, "t30_non_ecm_get_chunk in bad state %d\n", s->state);
        len = 0;
        break;
    }
    return len;
}
/*- End of function --------------------------------------------------------*/

static void t30_hdlc_rx_status(void *user_data, int status)
{
    t30_state_t *s;

    s = (t30_state_t *) user_data;
    span_log(&s->logging, SPAN_LOG_FLOW, "HDLC signal status is %s (%d) in state %d\n", signal_status_to_str(status), status, s->state);
    switch (status)
    {
    case SIG_STATUS_TRAINING_IN_PROGRESS:
        break;
    case SIG_STATUS_TRAINING_FAILED:
        s->rx_trained = FALSE;
        break;
    case SIG_STATUS_TRAINING_SUCCEEDED:
        /* The modem is now trained */
        s->rx_signal_present = TRUE;
        s->rx_trained = TRUE;
        break;
    case SIG_STATUS_CARRIER_UP:
        s->rx_signal_present = TRUE;
        switch (s->timer_t2_t4_is)
        {
        case TIMER_IS_T2B:
            s->timer_t2_t4_is = TIMER_IS_T2C;
            s->timer_t2_t4 = 0;
            break;
        case TIMER_IS_T4B:
            s->timer_t2_t4_is = TIMER_IS_T4C;
            s->timer_t2_t4 = 0;
            break;
        }
        break;
    case SIG_STATUS_CARRIER_DOWN:
        s->rx_signal_present = FALSE;
        s->rx_trained = FALSE;
        /* If a phase change has been queued to occur after the receive signal drops,
           its time to change. */
        if (s->next_phase != T30_PHASE_IDLE)
        {
            s->timer_t2_t4 = 0;
            set_phase(s, s->next_phase);
            if (s->next_phase == T30_PHASE_C_NON_ECM_RX)
                timer_t2_start(s);
            s->next_phase = T30_PHASE_IDLE;
        }
        else
        {
            switch (s->timer_t2_t4_is)
            {
            case TIMER_IS_T2A:
            case TIMER_IS_T2C:
                timer_t2b_start(s);
                break;
            case TIMER_IS_T4A:
            case TIMER_IS_T4C:
                timer_t4b_start(s);
                break;
            }
        }
        break;
    case SIG_STATUS_FRAMING_OK:
        if (!s->far_end_detected  &&  s->timer_t0_t1 > 0)
        {
            s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T1);
            s->far_end_detected = TRUE;
            if (s->phase == T30_PHASE_A_CED  ||  s->phase == T30_PHASE_A_CNG)
                set_phase(s, T30_PHASE_B_RX);
        }
        /* 5.4.3.1 Timer T2 is reset if flag is received. Timer T2A must be started. */
        /* Unstated, but implied, is that timer T4 and T4A are handled the same way. */
        if (s->timer_t2_t4 > 0)
        {
            switch(s->timer_t2_t4_is)
            {
            case TIMER_IS_T2:
            case TIMER_IS_T2A:
                timer_t2a_start(s);
                break;
            case TIMER_IS_T4:
            case TIMER_IS_T4A:
                timer_t4a_start(s);
                break;
            }
        }
        break;
    case SIG_STATUS_ABORT:
        /* Just ignore these */
        break;
    default:
        span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected HDLC special length - %d!\n", status);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE_NONSTD(void) t30_hdlc_accept(void *user_data, const uint8_t *msg, int len, int ok)
{
    t30_state_t *s;

    if (len < 0)
    {
        t30_hdlc_rx_status(user_data, len);
        return;
    }

    s = (t30_state_t *) user_data;
    /* The spec. says a command or response is not valid if:
        - any of the frames, optional or mandatory, have an FCS error.
        - any single frame exceeds 3s +- 15% (i.e. no frame should exceed 2.55s)
        - the final frame is not tagged as a final frame
        - the final frame is not a recognised one.
       The first point seems benign. If we accept an optional frame, and a later
       frame is bad, having accepted the optional frame should be harmless.
       The 2.55s maximum seems to limit signalling frames to no more than 95 octets,
       including FCS, and flag octets (assuming the use of V.21).
    */
    if (!ok  &&  s->phase != T30_PHASE_C_ECM_RX)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Bad CRC received\n");
        /* We either force a resend, or we wait until a resend occurs through a timeout. */
        if ((s->supported_t30_features & T30_SUPPORT_COMMAND_REPEAT))
        {
            s->step = 0;
            if (s->phase == T30_PHASE_B_RX)
                queue_phase(s, T30_PHASE_B_TX);
            else
                queue_phase(s, T30_PHASE_D_TX);
            send_simple_frame(s, T30_CRP);
        }
        return;
    }

    if (len < 3)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Bad HDLC frame length - %d\n", len);
        return;
    }
    if (msg[0] != 0xFF  ||  !(msg[1] == 0x03  ||  msg[1] == 0x13))
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "Bad HDLC frame header - %02x %02x\n", msg[0], msg[1]);
        return;
    }
    s->rx_frame_received = TRUE;
    /* Cancel the command or response timer */
    s->timer_t2_t4 = 0;
    process_rx_control_msg(s, msg, len);
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status)
{
    t30_state_t *s;
    
    s = (t30_state_t *) user_data;

    switch (status)
    {
    case T30_FRONT_END_SEND_STEP_COMPLETE:
        span_log(&s->logging, SPAN_LOG_FLOW, "Send complete in phase %s, state %d\n", phase_names[s->phase], s->state);
        /* We have finished sending our messages, so move on to the next operation. */
        switch (s->state)
        {
        case T30_STATE_ANSWERING:
            span_log(&s->logging, SPAN_LOG_FLOW, "Starting answer mode\n");
            set_phase(s, T30_PHASE_B_TX);
            timer_t2_start(s);
            s->dis_received = FALSE;
            send_dis_or_dtc_sequence(s, TRUE);
            break;
        case T30_STATE_R:
            if (send_dis_or_dtc_sequence(s, FALSE))
            {
                /* Wait for an acknowledgement. */
                set_phase(s, T30_PHASE_B_RX);
                timer_t4_start(s);
            }
            break;
        case T30_STATE_F_CFR:
            if (s->step == 0)
            {
                /* Shut down HDLC transmission. */
                if (s->send_hdlc_handler)
                    s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
                s->step++;
            }
            else
            {
                if (s->error_correcting_mode)
                {
                    set_state(s, T30_STATE_F_DOC_ECM);
                    queue_phase(s, T30_PHASE_C_ECM_RX);
                }
                else
                {
                    set_state(s, T30_STATE_F_DOC_NON_ECM);
                    queue_phase(s, T30_PHASE_C_NON_ECM_RX);
                }
                timer_t2_start(s);
                s->next_rx_step = T30_MPS;
            }
            break;
        case T30_STATE_F_FTT:
            if (s->step == 0)
            {
                /* Shut down HDLC transmission. */
                if (s->send_hdlc_handler)
                    s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
                s->step++;
            }
            else
            {
                set_phase(s, T30_PHASE_B_RX);
                timer_t2_start(s);
            }
            break;
        case T30_STATE_III_Q_MCF:
        case T30_STATE_III_Q_RTP:
        case T30_STATE_III_Q_RTN:
        case T30_STATE_F_POST_RCP_PPR:
        case T30_STATE_F_POST_RCP_MCF:
            if (s->step == 0)
            {
                /* Shut down HDLC transmission. */
                if (s->send_hdlc_handler)
                    s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
                s->step++;
            }
            else
            {
                switch (s->next_rx_step)
                {
                case T30_MPS:
                case T30_PRI_MPS:
                    /* We should now start to get another page */
                    if (s->error_correcting_mode)
                    {
                        set_state(s, T30_STATE_F_DOC_ECM);
                        queue_phase(s, T30_PHASE_C_ECM_RX);
                    }
                    else
                    {
                        set_state(s, T30_STATE_F_DOC_NON_ECM);
                        queue_phase(s, T30_PHASE_C_NON_ECM_RX);
                    }
                    timer_t2_start(s);
                    break;
                case T30_EOM:
                case T30_PRI_EOM:
                case T30_EOS:
                    /* See if we get something back, before moving to phase B. */
                    timer_t2_start(s);
                    set_phase(s, T30_PHASE_D_RX);
                    break;
                case T30_EOP:
                case T30_PRI_EOP:
                    /* Wait for a DCN. */
                    set_phase(s, T30_PHASE_D_RX);
                    timer_t4_start(s);
                    break;
                default:
                    span_log(&s->logging, SPAN_LOG_FLOW, "Unknown next rx step - %d\n", s->next_rx_step);
                    disconnect(s);
                    break;
                }
            }
            break;
        case T30_STATE_II_Q:
        case T30_STATE_IV_PPS_NULL:
        case T30_STATE_IV_PPS_Q:
        case T30_STATE_IV_PPS_RNR:
        case T30_STATE_IV_EOR_RNR:
        case T30_STATE_F_POST_RCP_RNR:
        case T30_STATE_IV_EOR:
        case T30_STATE_IV_CTC:
            if (s->step == 0)
            {
                /* Shut down HDLC transmission. */
                if (s->send_hdlc_handler)
                    s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
                s->step++;
            }
            else
            {
                /* We have finished sending the post image message. Wait for an
                   acknowledgement. */
                set_phase(s, T30_PHASE_D_RX);
                timer_t4_start(s);
            }
            break;
        case T30_STATE_B:
            /* We have now allowed time for the last message to flush
               through the system, so it is safe to report the end of the
               call. */
            if (s->phase_e_handler)
                s->phase_e_handler(s, s->phase_e_user_data, s->current_status);
            set_state(s, T30_STATE_CALL_FINISHED);
            set_phase(s, T30_PHASE_CALL_FINISHED);
            release_resources(s);
            break;
        case T30_STATE_C:
            if (s->step == 0)
            {
                /* Shut down HDLC transmission. */
                if (s->send_hdlc_handler)
                    s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
                s->step++;
            }
            else
            {
                /* We just sent the disconnect message. Now it is time to disconnect. */
                disconnect(s);
            }
            break;
        case T30_STATE_D:
            if (send_dcs_sequence(s, FALSE))
            {
                if ((s->iaf & T30_IAF_MODE_NO_TCF))
                {
                    /* Skip the trainability test */
                    s->retries = 0;
                    s->short_train = TRUE;
                    if (s->error_correcting_mode)
                    {
                        set_state(s, T30_STATE_IV);
                        queue_phase(s, T30_PHASE_C_ECM_TX);
                    }
                    else
                    {
                        set_state(s, T30_STATE_I);
                        queue_phase(s, T30_PHASE_C_NON_ECM_TX);
                    }
                }
                else
                {
                    /* Do the trainability test */
                    /* TCF is always sent with long training */
                    s->short_train = FALSE;
                    set_state(s, T30_STATE_D_TCF);
                    set_phase(s, T30_PHASE_C_NON_ECM_TX);
                }
            }
            break;
        case T30_STATE_D_TCF:
            /* Finished sending training test. Listen for the response. */
            set_phase(s, T30_PHASE_B_RX);
            timer_t4_start(s);
            set_state(s, T30_STATE_D_POST_TCF);
            break;
        case T30_STATE_I:
            /* Send the end of page message */
            set_phase(s, T30_PHASE_D_TX);
            set_state(s, T30_STATE_II_Q);
            /* We might need to resend the page we are on, but we need to check if there
               are any more pages to send, so we can send the correct signal right now. */
            send_simple_frame(s, s->next_tx_step = check_next_tx_step(s));
            break;
        case T30_STATE_IV:
            /* We have finished sending an FCD frame */
            if (s->step == 0)
            {
                if (send_next_ecm_frame(s))
                {
                    /* Shut down HDLC transmission. */
                    if (s->send_hdlc_handler)
                        s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
                    s->step++;
                }
            }
            else
            {
                /* Send the end of page or partial page message */
                set_phase(s, T30_PHASE_D_TX);
                s->next_tx_step = check_next_tx_step(s);
                if (send_pps_frame(s) == T30_NULL)
                    set_state(s, T30_STATE_IV_PPS_NULL);
                else
                    set_state(s, T30_STATE_IV_PPS_Q);
            }
            break;
        case T30_STATE_F_DOC_ECM:
            /* This should be the end of a CTR being sent. */
            if (s->step == 0)
            {
                /* Shut down HDLC transmission. */
                if (s->send_hdlc_handler)
                    s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0);
                s->step++;
            }
            else
            {
                /* We have finished sending the CTR. Wait for image data again. */
                queue_phase(s, T30_PHASE_C_ECM_RX);
                timer_t2_start(s);
            }
            break;
        case T30_STATE_CALL_FINISHED:
            /* Just ignore anything that happens now. We might get here if a premature
               disconnect from the far end overlaps something. */
            break;
        default:
            span_log(&s->logging, SPAN_LOG_FLOW, "Bad state for send complete in t30_front_end_status - %d\n", s->state);
            break;
        }
        break;
    case T30_FRONT_END_RECEIVE_COMPLETE:
        span_log(&s->logging, SPAN_LOG_FLOW, "Receive complete in phase %s, state %d\n", phase_names[s->phase], s->state);
        /* Usually receive complete is notified by a carrier down signal. However,
           in cases like a T.38 packet stream dying in the middle of reception
           there needs to be a means to stop things. */
        switch (s->phase)
        {
        case T30_PHASE_C_NON_ECM_RX:
            t30_non_ecm_rx_status(s, SIG_STATUS_CARRIER_DOWN);
            break;
        default:
            t30_hdlc_rx_status(s, SIG_STATUS_CARRIER_DOWN);
            break;
        }
        break;
    case T30_FRONT_END_SIGNAL_PRESENT:
        span_log(&s->logging, SPAN_LOG_FLOW, "A signal is present\n");
        /* The front end is explicitly telling us the signal we expect is present. This might
           be a premature indication from a T.38 implementation, but we have to believe it.
           if we don't we can time out improperly. For example, we might get an image modem
           carrier signal, but the first HDLC frame might only occur several seconds later.
           Many ECM senders idle on HDLC flags while waiting for the paper or filing system
           to become ready. T.38 offers no specific indication of correct carrier training, so
           if we don't kill the timer on the initial carrier starting signal, we will surely
           time out quite often before the next thing we receive. */
        switch (s->phase)
        {
        case T30_PHASE_A_CED:
        case T30_PHASE_A_CNG:
        case T30_PHASE_B_RX:
        case T30_PHASE_D_RX:
            /* We are running a V.21 receive modem, where an explicit training indication
               will not occur. */
            t30_hdlc_rx_status(s, SIG_STATUS_CARRIER_UP);
            t30_hdlc_rx_status(s, SIG_STATUS_FRAMING_OK);
            break;
        default:
            /* Cancel any receive timeout, and declare that a receive signal is present,
               since the front end is explicitly telling us we have seen something. */
            s->rx_signal_present = TRUE;
            s->timer_t2_t4 = 0;
            break;
        }
        break;
    case T30_FRONT_END_SIGNAL_ABSENT:
        span_log(&s->logging, SPAN_LOG_FLOW, "No signal is present\n");
        /* TODO: Should we do anything here? */
        break;
    case T30_FRONT_END_CED_PRESENT:
        span_log(&s->logging, SPAN_LOG_FLOW, "CED tone is present\n");
        /* TODO: Should we do anything here? */
        break;
    case T30_FRONT_END_CNG_PRESENT:
        span_log(&s->logging, SPAN_LOG_FLOW, "CNG tone is present\n");
        /* TODO: Should we do anything here? */
        break;
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t30_timer_update(t30_state_t *s, int samples)
{
    if (s->timer_t0_t1 > 0)
    {
        if ((s->timer_t0_t1 -= samples) <= 0)
        {
            if (s->far_end_detected)
                timer_t1_expired(s);
            else
                timer_t0_expired(s);
        }
    }
    if (s->timer_t3 > 0)
    {
        if ((s->timer_t3 -= samples) <= 0)
            timer_t3_expired(s);
    }
    if (s->timer_t2_t4 > 0)
    {
        if ((s->timer_t2_t4 -= samples) <= 0)
        {
            switch (s->timer_t2_t4_is)
            {
            case TIMER_IS_T2:
                timer_t2_expired(s);
                break;
            case TIMER_IS_T2A:
                timer_t2a_expired(s);
                break;
            case TIMER_IS_T2B:
                timer_t2b_expired(s);
                break;
            case TIMER_IS_T4:
                timer_t4_expired(s);
                break;
            case TIMER_IS_T4A:
                timer_t4a_expired(s);
                break;
            case TIMER_IS_T4B:
                timer_t4b_expired(s);
                break;
            }
        }
    }
    if (s->timer_t5 > 0)
    {
        if ((s->timer_t5 -= samples) <= 0)
            timer_t5_expired(s);
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t30_terminate(t30_state_t *s)
{
    if (s->phase != T30_PHASE_CALL_FINISHED)
    {
        /* The far end disconnected early, but was it just a tiny bit too early,
           as we were just tidying up, or seriously early as in a failure? */
        switch (s->state)
        {
        case T30_STATE_C:
            /* We were sending the final disconnect, so just hussle things along. */
            disconnect(s);
            break;
        case T30_STATE_B:
            /* We were in the final wait for everything to flush through, so just
               hussle things along. */
            break;
        default:
            /* The call terminated prematurely. */
            s->current_status = T30_ERR_CALLDROPPED;
            break;
        }
        if (s->phase_e_handler)
            s->phase_e_handler(s, s->phase_e_user_data, s->current_status);
        set_state(s, T30_STATE_CALL_FINISHED);
        set_phase(s, T30_PHASE_CALL_FINISHED);
        release_resources(s);
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t30_get_transfer_statistics(t30_state_t *s, t30_stats_t *t)
{
    t4_stats_t stats;

    t->bit_rate = fallback_sequence[s->current_fallback].bit_rate;
    t->error_correcting_mode = s->error_correcting_mode;
    t->error_correcting_mode_retries = s->error_correcting_mode_retries;
    t4_get_transfer_statistics(&s->t4, &stats);
    t->pages_transferred = stats.pages_transferred;
    t->pages_in_file = stats.pages_in_file;
    t->width = stats.width;
    t->length = stats.length;
    t->bad_rows = stats.bad_rows;
    t->longest_bad_row_run = stats.longest_bad_row_run;
    t->x_resolution = stats.x_resolution;
    t->y_resolution = stats.y_resolution;
    t->encoding = stats.encoding;
    t->image_size = stats.line_image_size;
    t->current_status = s->current_status;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t30_local_interrupt_request(t30_state_t *s, int state)
{
    if (s->timer_t3 > 0)
    {
        /* Accept the far end's outstanding request for interrupt. */
        /* TODO: */
        send_simple_frame(s, (state)  ?  T30_PIP  :  T30_PIN);
    }
    s->local_interrupt_pending = state;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t30_restart(t30_state_t *s)
{
    s->phase = T30_PHASE_IDLE;
    s->next_phase = T30_PHASE_IDLE;
    s->current_fallback = 0;
    s->rx_signal_present = FALSE;
    s->rx_trained = FALSE;
    s->rx_frame_received = FALSE;
    s->current_status = T30_ERR_OK;
    s->ppr_count = 0;
    s->ecm_progress = 0;
    s->receiver_not_ready_count = 0;
    s->far_dis_dtc_len = 0;
    memset(&s->far_dis_dtc_frame, 0, sizeof(s->far_dis_dtc_frame));
    t30_build_dis_or_dtc(s);
    if (s->calling_party)
    {
        set_state(s, T30_STATE_T);
        set_phase(s, T30_PHASE_A_CNG);
    }
    else
    {
        set_state(s, T30_STATE_ANSWERING);
        set_phase(s, T30_PHASE_A_CED);
    }
    memset(&s->rx_info, 0, sizeof(s->rx_info));
    s->far_end_detected = FALSE;
    s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T0);
    release_resources(s);
    s->ecm_rx_page = 0;
    s->ecm_tx_page = 0;
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(t30_state_t *) t30_init(t30_state_t *s,
                                     int calling_party,
                                     t30_set_handler_t *set_rx_type_handler,
                                     void *set_rx_type_user_data,
                                     t30_set_handler_t *set_tx_type_handler,
                                     void *set_tx_type_user_data,
                                     t30_send_hdlc_handler_t *send_hdlc_handler,
                                     void *send_hdlc_user_data)
{
    if (s == NULL)
    {
        if ((s = (t30_state_t *) malloc(sizeof(*s))) == NULL)
            return NULL;
    }
    memset(s, 0, sizeof(*s));
    s->calling_party = calling_party;
    s->set_rx_type_handler = set_rx_type_handler;
    s->set_rx_type_user_data = set_rx_type_user_data;
    s->set_tx_type_handler = set_tx_type_handler;
    s->set_tx_type_user_data = set_tx_type_user_data;
    s->send_hdlc_handler = send_hdlc_handler;
    s->send_hdlc_user_data = send_hdlc_user_data;

    /* Default to the basic modems. */
    s->supported_modems = T30_SUPPORT_V27TER | T30_SUPPORT_V29;
    s->supported_compressions = T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION;
    s->supported_resolutions = T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION
                             | T30_SUPPORT_R8_RESOLUTION;
    s->supported_image_sizes = T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH
                             | T30_SUPPORT_215MM_WIDTH;
    /* Set the output encoding to something safe. Most things get 1D and 2D
       encoding right. Quite a lot get other things wrong. */
    s->output_encoding = T4_COMPRESSION_ITU_T4_2D;
    s->local_min_scan_time_code = T30_MIN_SCAN_0MS;
    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
    span_log_set_protocol(&s->logging, "T.30");
    t30_restart(s);
    return s;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t30_release(t30_state_t *s)
{
    /* Make sure any FAX in progress is tidied up. If the tidying up has
       already happened, repeating it here is harmless. */
    switch (s->operation_in_progress)
    {
    case OPERATION_IN_PROGRESS_T4_TX:
        t4_tx_end(&(s->t4));
        break;
    case OPERATION_IN_PROGRESS_T4_RX:
        t4_rx_end(&(s->t4));
        break;
    }
    s->operation_in_progress = OPERATION_IN_PROGRESS_NONE;
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t30_free(t30_state_t *s)
{
    t30_release(s);
    free(s);
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t30_call_active(t30_state_t *s)
{
    return (s->phase != T30_PHASE_CALL_FINISHED);
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/
