freeswitch/libs/spandsp/src/t30.c

6410 lines
220 KiB
C

/*
* 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, 2008, 2009 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.
*/
/*! \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/timezone.h"
#include "spandsp/t4_rx.h"
#include "spandsp/t4_tx.h"
#if defined(SPANDSP_SUPPORT_T42) || defined(SPANDSP_SUPPORT_T43) || defined(SPANDSP_SUPPORT_T85)
#include "spandsp/t81_t82_arith_coding.h"
#endif
#if defined(SPANDSP_SUPPORT_T85)
#include "spandsp/t85.h"
#endif
#if defined(SPANDSP_SUPPORT_T42)
#include "spandsp/t42.h"
#endif
#if defined(SPANDSP_SUPPORT_T43)
#include "spandsp/t43.h"
#endif
#include "spandsp/t4_t6_decode.h"
#include "spandsp/t4_t6_encode.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/timezone.h"
#if defined(SPANDSP_SUPPORT_T42) || defined(SPANDSP_SUPPORT_T43) || defined(SPANDSP_SUPPORT_T85)
#include "spandsp/private/t81_t82_arith_coding.h"
#endif
#if defined(SPANDSP_SUPPORT_T85)
#include "spandsp/private/t85.h"
#endif
#if defined(SPANDSP_SUPPORT_T42)
#include "spandsp/private/t42.h"
#endif
#if defined(SPANDSP_SUPPORT_T43)
#include "spandsp/private/t43.h"
#endif
#include "spandsp/private/t4_t6_decode.h"
#include "spandsp/private/t4_t6_encode.h"
#include "spandsp/private/t4_rx.h"
#include "spandsp/private/t4_tx.h"
#include "spandsp/private/t30.h"
#include "spandsp/private/t30_dis_dtc_dcs_bits.h"
#include "t30_local.h"
/*! The maximum permitted number of retries of a single command allowed. */
#define MAX_COMMAND_TRIES 3
/*! The maximum permitted number of retries of a single response request allowed. This
is not specified in T.30. However, if you don't apply some limit a messed up FAX
terminal could keep you retrying all day. Its a backstop protection. */
#define MAX_RESPONSE_TRIES 6
/* 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. This is only relevant to non-ECM operation. */
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
continue, retrain, or abandoning of the call. */
enum
{
OPERATION_IN_PROGRESS_NONE = 0,
OPERATION_IN_PROGRESS_T4_RX,
OPERATION_IN_PROGRESS_T4_TX,
OPERATION_IN_PROGRESS_POST_T4_RX,
OPERATION_IN_PROGRESS_POST_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.
Annex A says T1 is also the timeout to be used for the receipt of the first HDLC frame after the
start of high speed flags in ECM mode. This seems a strange reuse of the T1 name, so we distinguish
it here by calling it T1A. */
#define DEFAULT_TIMER_T1 35000
#define DEFAULT_TIMER_T1A 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 term 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. Note that a different timer is used for the fast
HDLC in ECM mode, to provide time for physical paper handling. */
#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 term 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
/* HDLC message header byte values */
#define ADDRESS_FIELD 0xFF
#define CONTROL_FIELD_NON_FINAL_FRAME 0x03
#define CONTROL_FIELD_FINAL_FRAME 0x13
enum
{
TIMER_IS_IDLE = 0,
TIMER_IS_T2,
TIMER_IS_T1A,
TIMER_IS_T2A,
TIMER_IS_T2B,
TIMER_IS_T2C,
TIMER_IS_T4,
TIMER_IS_T4A,
TIMER_IS_T4B,
TIMER_IS_T4C
};
/* Start points in the fallback table for different capabilities */
/*! The starting point in the modem fallback sequence if V.17 is allowed */
#define T30_V17_FALLBACK_START 0
/*! The starting point in the modem fallback sequence if V.17 is not allowed */
#define T30_V29_FALLBACK_START 3
/*! The starting point in the modem fallback sequence if V.29 is not allowed */
#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 decode_nsf_nss_nsc(t30_state_t *s, uint8_t *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);
static void timer_t2_t4_stop(t30_state_t *s);
/*! Test a specified bit within a DIS, DTC or DCS frame */
#define test_ctrl_bit(s,bit) ((s)[3 + ((bit - 1)/8)] & (1 << ((bit - 1)%8)))
/*! Set a specified bit within a DIS, DTC or DCS frame */
#define set_ctrl_bit(s,bit) (s)[3 + ((bit - 1)/8)] |= (1 << ((bit - 1)%8))
/*! Set a specified block of bits within a DIS, DTC or DCS frame */
#define set_ctrl_bits(s,val,bit) (s)[3 + ((bit - 1)/8)] |= ((val) << ((bit - 1)%8))
/*! Clear a specified bit within a DIS, DTC or DCS frame */
#define clr_ctrl_bit(s,bit) (s)[3 + ((bit - 1)/8)] &= ~(1 << ((bit - 1)%8))
static int terminate_operation_in_progress(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_release(&s->t4.tx);
s->operation_in_progress = OPERATION_IN_PROGRESS_POST_T4_TX;
break;
case OPERATION_IN_PROGRESS_T4_RX:
t4_rx_release(&s->t4.rx);
s->operation_in_progress = OPERATION_IN_PROGRESS_POST_T4_RX;
break;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
static int tx_start_page(t30_state_t *s)
{
if (t4_tx_start_page(&s->t4.tx))
{
terminate_operation_in_progress(s);
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->tx_page_number + 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.tx) == 0)
{
s->tx_page_number++;
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.rx, s->image_width);
t4_rx_set_sub_address(&s->t4.rx, s->rx_info.sub_address);
t4_rx_set_dcs(&s->t4.rx, s->rx_dcs_string);
t4_rx_set_far_ident(&s->t4.rx, s->rx_info.ident);
t4_rx_set_vendor(&s->t4.rx, s->vendor);
t4_rx_set_model(&s->t4.rx, s->model);
t4_rx_set_rx_encoding(&s->t4.rx, s->line_encoding);
t4_rx_set_x_resolution(&s->t4.rx, s->x_resolution);
t4_rx_set_y_resolution(&s->t4.rx, s->y_resolution);
if (t4_rx_start_page(&s->t4.rx))
return -1;
/* Clear the ECM 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.rx) == 0)
{
s->rx_page_number++;
s->ecm_block = 0;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
static void report_rx_ecm_page_result(t30_state_t *s)
{
t4_stats_t stats;
/* This is only used for ECM pages, as copy_quality() does a similar job for non-ECM
pages as a byproduct of assessing copy quality. */
t4_rx_get_transfer_statistics(&s->t4.rx, &stats);
span_log(&s->logging, SPAN_LOG_FLOW, "Page no = %d\n", stats.pages_transferred);
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, "Compression = %s (%d)\n", t4_encoding_to_str(stats.encoding), stats.encoding);
span_log(&s->logging, SPAN_LOG_FLOW, "Compressed image size = %d bytes\n", stats.line_image_size);
}
/*- End of function --------------------------------------------------------*/
static int copy_quality(t30_state_t *s)
{
t4_stats_t stats;
int quality;
t4_rx_get_transfer_statistics(&s->t4.rx, &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
*/
/* This is called before the page is confirmed, so we need to add one to get the page
number right */
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, "Compression = %s (%d)\n", t4_encoding_to_str(stats.encoding), stats.encoding);
span_log(&s->logging, SPAN_LOG_FLOW, "Compressed image size = %d bytes\n", stats.line_image_size);
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);
/* Don't treat a page as perfect because it has zero bad rows out of zero total rows. A zero row
page has got to be some kind of total page failure. */
if (stats.bad_rows == 0 && stats.length != 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 report_tx_result(t30_state_t *s, int result)
{
t4_stats_t stats;
if (span_log_test(&s->logging, SPAN_LOG_FLOW))
{
t4_tx_get_transfer_statistics(&s->t4.tx, &stats);
span_log(&s->logging,
SPAN_LOG_FLOW,
"%s - delivered %d pages\n",
(result) ? "Success" : "Failure",
stats.pages_transferred);
}
}
/*- 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 res;
int more;
res = t4_tx_next_page_has_different_format(&s->t4.tx);
if (res == 0)
{
span_log(&s->logging, SPAN_LOG_FLOW, "More pages to come with the same format\n");
return (s->local_interrupt_pending) ? T30_PRI_MPS : T30_MPS;
}
if (res > 0)
{
span_log(&s->logging, SPAN_LOG_FLOW, "More pages to come with a different format\n");
s->tx_start_page = t4_tx_get_current_page_in_file(&s->t4.tx) + 1;
return (s->local_interrupt_pending) ? T30_PRI_EOM : T30_EOM;
}
/* 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, T30_DIS_BIT_MULTIPLE_SELECTIVE_POLLING_CAPABLE))
// 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] = ADDRESS_FIELD;
s->ecm_data[i][1] = CONTROL_FIELD_NON_FINAL_FRAME;
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.tx, &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.tx) & 2) != 0);
return 256;
}
/*- 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 three RCP frames, as per
T.4/A.1 and T.4/A.2. The repeats are 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] = ADDRESS_FIELD;
frame[1] = CONTROL_FIELD_NON_FINAL_FRAME;
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] = ADDRESS_FIELD;
frame[1] = CONTROL_FIELD_FINAL_FRAME;
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++] = ADDRESS_FIELD;
frame[p++] = CONTROL_FIELD_NON_FINAL_FRAME;
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] = ADDRESS_FIELD;
s->tx_info.nsf[1] = CONTROL_FIELD_NON_FINAL_FRAME;
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] = ADDRESS_FIELD;
s->tx_info.nss[1] = CONTROL_FIELD_NON_FINAL_FRAME;
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] = ADDRESS_FIELD;
s->tx_info.nsc[1] = CONTROL_FIELD_NON_FINAL_FRAME;
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, T30_DIS_BIT_POLLED_SUBADDRESSING_CAPABLE) && 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, T30_DIS_BIT_POLLED_SUBADDRESSING_CAPABLE);
return TRUE;
}
clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_POLLED_SUBADDRESSING_CAPABLE);
return FALSE;
}
/*- End of function --------------------------------------------------------*/
static int send_sep_frame(t30_state_t *s)
{
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_SELECTIVE_POLLING_CAPABLE) && 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, T30_DIS_BIT_SELECTIVE_POLLING_CAPABLE);
return TRUE;
}
clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_SELECTIVE_POLLING_CAPABLE);
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, T30_DIS_BIT_PASSWORD) && 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, T30_DCS_BIT_SENDER_ID_TRANSMISSION);
return TRUE;
}
clr_ctrl_bit(s->dcs_frame, T30_DCS_BIT_SENDER_ID_TRANSMISSION);
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, T30_DIS_BIT_PASSWORD) && 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, T30_DIS_BIT_PASSWORD);
return TRUE;
}
clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_PASSWORD);
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, T30_DIS_BIT_SUBADDRESSING_CAPABLE) && 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, T30_DCS_BIT_SUBADDRESS_TRANSMISSION);
return TRUE;
}
clr_ctrl_bit(s->dcs_frame, T30_DCS_BIT_SUBADDRESS_TRANSMISSION);
return FALSE;
}
/*- End of function --------------------------------------------------------*/
static int send_tsa_frame(t30_state_t *s)
{
if ((test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T37) || test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38)) && 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, T30_DIS_BIT_INTERNET_ROUTING_ADDRESS) && 0)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Sending internet routing address '%s'\n", "");
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INTERNET_ROUTING_ADDRESS_TRANSMISSION);
return TRUE;
}
clr_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INTERNET_ROUTING_ADDRESS_TRANSMISSION);
return FALSE;
}
/*- End of function --------------------------------------------------------*/
static int send_cia_frame(t30_state_t *s)
{
if ((test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T37) || test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38)) && 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, T30_DIS_BIT_INTERNET_SELECTIVE_POLLING_ADDRESS) && 0)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Sending internet selective polling address '%s'\n", "");
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_INTERNET_SELECTIVE_POLLING_ADDRESS);
return TRUE;
}
clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_INTERNET_SELECTIVE_POLLING_ADDRESS);
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] = ADDRESS_FIELD;
frame[1] = CONTROL_FIELD_FINAL_FRAME;
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->tx_page_number & 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, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT);
else
clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT);
/* 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, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT);
else
clr_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT);
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] = ADDRESS_FIELD;
s->local_dis_dtc_frame[1] = CONTROL_FIELD_FINAL_FRAME;
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, T30_DIS_BIT_T37);
if ((s->iaf & T30_IAF_MODE_T38))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T38);
/* 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, T30_DIS_BIT_UNCOMPRESSED_CAPABLE);
if (s->ecm_allowed)
{
/* ECM allowed */
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_ECM_CAPABLE);
/* 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, T30_DIS_BIT_T6_CAPABLE);
if ((s->supported_compressions & T30_SUPPORT_T43_COMPRESSION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T43_CAPABLE);
if ((s->supported_compressions & T30_SUPPORT_T45_COMPRESSION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T45_CAPABLE);
if ((s->supported_compressions & T30_SUPPORT_T81_COMPRESSION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T81_CAPABLE);
if ((s->supported_compressions & T30_SUPPORT_SYCC_T81_COMPRESSION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_SYCC_T81_CAPABLE);
if ((s->supported_compressions & T30_SUPPORT_T85_COMPRESSION))
{
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T85_CAPABLE);
if ((s->supported_compressions & T30_SUPPORT_T85_L0_COMPRESSION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T85_L0_CAPABLE);
}
//if ((s->supported_compressions & T30_SUPPORT_T89_COMPRESSION))
// set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T89_CAPABLE);
}
if ((s->supported_t30_features & T30_SUPPORT_FIELD_NOT_VALID))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_FNV_CAPABLE);
if ((s->supported_t30_features & T30_SUPPORT_MULTIPLE_SELECTIVE_POLLING))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_MULTIPLE_SELECTIVE_POLLING_CAPABLE);
if ((s->supported_t30_features & T30_SUPPORT_POLLED_SUB_ADDRESSING))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_POLLED_SUBADDRESSING_CAPABLE);
/* 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, T30_DIS_BIT_200_400_CAPABLE);
if ((s->supported_resolutions & T30_SUPPORT_300_300_RESOLUTION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_300_300_CAPABLE);
if ((s->supported_resolutions & (T30_SUPPORT_400_400_RESOLUTION | T30_SUPPORT_R16_RESOLUTION)))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_400_400_CAPABLE);
/* Metric */
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_METRIC_RESOLUTION_PREFERRED);
/* 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, T30_DIS_BIT_SELECTIVE_POLLING_CAPABLE);
if ((s->supported_t30_features & T30_SUPPORT_SUB_ADDRESSING))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_SUBADDRESSING_CAPABLE);
if ((s->supported_t30_features & T30_SUPPORT_IDENTIFICATION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_PASSWORD);
/* Ready to transmit a data file (polling) */
if (s->tx_file[0])
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_READY_TO_TRANSMIT_DATA_FILE);
/* 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 (T.4/Annex E) */
/* 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, T30_DIS_BIT_NORTH_AMERICAN_LETTER_CAPABLE);
if ((s->supported_image_sizes & T30_SUPPORT_US_LEGAL_LENGTH))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NORTH_AMERICAN_LEGAL_CAPABLE);
/* 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, T30_DIS_BIT_INTERNET_SELECTIVE_POLLING_ADDRESS);
if ((s->supported_t30_features & T30_SUPPORT_INTERNET_ROUTING_ADDRESS))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_INTERNET_ROUTING_ADDRESS);
if ((s->supported_resolutions & T30_SUPPORT_600_600_RESOLUTION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_600_600_CAPABLE);
if ((s->supported_resolutions & T30_SUPPORT_1200_1200_RESOLUTION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_1200_1200_CAPABLE);
if ((s->supported_resolutions & T30_SUPPORT_300_600_RESOLUTION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_300_600_CAPABLE);
if ((s->supported_resolutions & T30_SUPPORT_400_800_RESOLUTION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_400_800_CAPABLE);
if ((s->supported_resolutions & T30_SUPPORT_600_1200_RESOLUTION))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_600_1200_CAPABLE);
/* 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, T30_DIS_BIT_T38_FLOW_CONTROL_CAPABLE);
/* No k > 4 */
if ((s->iaf & T30_IAF_MODE_CONTINUOUS_FLOW))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T38_FAX_CAPABLE);
/* No T.88/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] = ADDRESS_FIELD;
s->dcs_frame[1] = CONTROL_FIELD_FINAL_FRAME;
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, T30_DIS_BIT_T37))
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T37);
/* Check for T.38 mode. */
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38))
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T38);
#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)
{
#if defined(SPANDSP_SUPPORT_T42)
case T4_COMPRESSION_ITU_T42:
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE);
set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
break;
#endif
#if defined(SPANDSP_SUPPORT_T43)
case T4_COMPRESSION_ITU_T43:
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T43_MODE);
set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
break;
#endif
#if defined(SPANDSP_SUPPORT_T85)
case T4_COMPRESSION_ITU_T85:
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_MODE);
clr_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_L0_MODE);
set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
break;
case T4_COMPRESSION_ITU_T85_L0:
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_MODE);
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_L0_MODE);
set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
break;
#endif
case T4_COMPRESSION_ITU_T6:
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T6_MODE);
set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, 21);
break;
case T4_COMPRESSION_ITU_T4_2D:
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_2D_MODE);
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, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT);
/* 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, T30_DCS_BIT_600_1200);
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, T30_DCS_BIT_1200_1200);
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, T30_DCS_BIT_400_800);
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, T30_DCS_BIT_300_600);
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, T30_DCS_BIT_600_600);
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, T30_DCS_BIT_200_400);
break;
case T4_X_RESOLUTION_R16:
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400);
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, T30_DCS_BIT_300_300);
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, T30_DCS_BIT_200_200);
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)
{
t30_set_status(s, 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;
/* The following treats a width field of 11 like 10, which does what note 6 of Table 2/T.30
says we should do with the invalid value 11. */
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_NOSIZESUPPORT;
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_NOSIZESUPPORT;
else
set_ctrl_bit(s->dcs_frame, 18);
break;
default:
/* T.30 does not support this width */
bad = T30_ERR_NOSIZESUPPORT;
break;
}
if (bad != T30_ERR_OK)
{
t30_set_status(s, bad);
span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) is not an acceptable 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, T30_DIS_BIT_300_300_CAPABLE) && !test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_300_600_CAPABLE))
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, T30_DIS_BIT_400_400_CAPABLE))
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, T30_DIS_BIT_600_600_CAPABLE) && !test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_600_1200_CAPABLE))
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, T30_DIS_BIT_1200_1200_CAPABLE))
bad = T30_ERR_NOSIZESUPPORT;
break;
default:
/* T.30 does not support this width */
bad = T30_ERR_NOSIZESUPPORT;
break;
}
if (bad != T30_ERR_OK)
{
t30_set_status(s, bad);
span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) is not an acceptable 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, T30_DCS_BIT_ECM);
if ((s->iaf & T30_IAF_MODE_FLOW_CONTROL) && test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38_FLOW_CONTROL_CAPABLE))
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T38_FLOW_CONTROL_CAPABLE);
if ((s->iaf & T30_IAF_MODE_CONTINUOUS_FLOW) && test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38_FAX_CAPABLE))
{
/* Clear the modem type bits, in accordance with note 77 of Table 2/T.30 */
clr_ctrl_bit(s->local_dis_dtc_frame, 11);
clr_ctrl_bit(s->local_dis_dtc_frame, 12);
clr_ctrl_bit(s->local_dis_dtc_frame, 13);
clr_ctrl_bit(s->local_dis_dtc_frame, 14);
set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T38_FAX_MODE);
}
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_bits_per_row(&s->t4.tx, 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 0
if (step_fallback_entry(s) < 0)
{
/* We have fallen back as far as we can go. Give up. */
s->current_fallback = 0;
t30_set_status(s, 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);
}
#else
if (s->calling_party)
set_state(s, T30_STATE_T);
else
set_state(s, T30_STATE_R);
#endif
}
/*- 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. */
terminate_operation_in_progress(s);
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, T30_DIS_BIT_200_400_CAPABLE))
{
t30_set_status(s, 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, T30_DIS_BIT_MIN_SCAN_TIME_HALVES)) ? 2 : 1][min_bits_field];
break;
case T4_Y_RESOLUTION_FINE:
if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE))
{
t30_set_status(s, 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.tx, 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);
t30_set_status(s, T30_ERR_FILEERROR);
return -1;
}
s->operation_in_progress = OPERATION_IN_PROGRESS_T4_TX;
t4_tx_get_pages_in_file(&s->t4.tx);
t4_tx_set_tx_encoding(&s->t4.tx, s->line_encoding);
t4_tx_set_local_ident(&s->t4.tx, s->tx_info.ident);
t4_tx_set_header_info(&s->t4.tx, s->header_info);
if (s->use_own_tz)
t4_tx_set_header_tz(&s->t4.tx, &s->tz);
s->x_resolution = t4_tx_get_x_resolution(&s->t4.tx);
s->y_resolution = t4_tx_get_y_resolution(&s->t4.tx);
/* 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)
{
terminate_operation_in_progress(s);
return -1;
}
span_log(&s->logging, SPAN_LOG_FLOW, "Minimum bits per row will be %d\n", min_row_bits);
t4_tx_set_min_bits_per_row(&s->t4.tx, min_row_bits);
if (tx_start_page(s))
return -1;
s->image_width = t4_tx_get_image_width(&s->t4.tx);
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.tx);
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_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);
if (s->current_status == T30_ERR_OK)
t30_set_status(s, 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 frame in state %d\n", t30_frametype(msg[2]), s->state);
if (s->current_status == T30_ERR_OK)
t30_set_status(s, 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);
if (s->current_status == T30_ERR_OK)
t30_set_status(s, 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_T85_COMPRESSION)
&&
test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T85_CAPABLE))
{
if (s->supported_compressions & T30_SUPPORT_T85_L0_COMPRESSION
&&
test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T85_L0_CAPABLE))
{
s->line_encoding = T4_COMPRESSION_ITU_T85_L0;
}
else
{
s->line_encoding = T4_COMPRESSION_ITU_T85;
}
}
else if (s->error_correcting_mode
&&
(s->supported_compressions & T30_SUPPORT_T6_COMPRESSION)
&&
test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T6_CAPABLE))
{
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, T30_DIS_BIT_2D_CAPABLE))
{
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 %s (%d)\n", t4_encoding_to_str(s->line_encoding), 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! */
t30_set_status(s, 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));
t30_set_status(s, new_status);
/* TODO: If FNV is allowed, process it here */
send_dcn(s);
return -1;
}
}
#if 0
/* T.4 1D is always available */
bi_level_support = T30_SUPPORT_T4_1D_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_2D_CAPABLE))
bi_level_support |= T30_SUPPORT_T4_2D_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T6_CAPABLE))
bi_level_support |= T30_SUPPORT_T6_COMPRESSION;
/* Bit 79 set with bit 78 clear is invalid, so let's completely ignore 79
if 78 is clear. */
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T85_CAPABLE))
{
bi_level_support |= T30_SUPPORT_T85_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T85_L0_CAPABLE)
bi_level_support |= T30_SUPPORT_T85_L0_COMPRESSION;
}
gray_support = 0;
colour_support = 0;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE) && test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T81_CAPABLE))
{
/* Multi-level coding available */
gray_support |= T30_SUPPORT_T81_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE))
gray_support |= T30_SUPPORT_T81_12BIT_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T43_CAPABLE))
{
gray_support |= T30_SUPPORT_T43_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE))
gray_support |= T30_SUPPORT_T43_COMPRESSION_12BIT;
}
if (test_ctrl_bit(s->far_dis_dtc_frame, bit69))
{
/* Colour coding available */
colour_support |= T30_SUPPORT_T81_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE))
colour_support |= T30_SUPPORT_T81_12BIT_COMPRESSION;
if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING))
{
colour_support |= T30_SUPPORT_T81_SUBSAMPLING_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE))
colour_support |= T30_SUPPORT_T81_SUBSAMPLING_COMPRESSION_12BIT;
}
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T43_CAPABLE))
{
colour_support |= T30_SUPPORT_T43_COMPRESSION;
if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE))
colour_support |= T30_SUPPORT_T43_12BIT_COMPRESSION;
}
}
/* bit74 custom illuminant */
/* bit75 custom gamut range */
}
#endif
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, T30_DIS_BIT_READY_TO_RECEIVE_FAX_DOCUMENT))
{
span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot receive\n", t30_frametype(msg[2]));
t30_set_status(s, 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, T30_DIS_BIT_READY_TO_TRANSMIT_FAX_DOCUMENT))
{
span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot transmit\n", t30_frametype(msg[2]));
t30_set_status(s, 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))
{
t30_set_status(s, 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, T30_DCS_BIT_64_OCTET_ECM_FRAMES) ? 256 : 64;
if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_1200_1200))
s->x_resolution = T4_X_RESOLUTION_1200;
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_600_600) || test_ctrl_bit(dcs_frame, T30_DCS_BIT_600_1200))
s->x_resolution = T4_X_RESOLUTION_600;
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_400) || test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_800))
s->x_resolution = T4_X_RESOLUTION_R16;
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_300) || test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_600))
s->x_resolution = T4_X_RESOLUTION_300;
else
s->x_resolution = T4_X_RESOLUTION_R8;
if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_1200_1200) || test_ctrl_bit(dcs_frame, T30_DCS_BIT_600_1200))
s->y_resolution = T4_Y_RESOLUTION_1200;
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_800))
s->y_resolution = T4_Y_RESOLUTION_800;
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_600_600) || test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_600))
s->y_resolution = T4_Y_RESOLUTION_600;
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_200_400) || test_ctrl_bit(dcs_frame, T30_DCS_BIT_400_400))
s->y_resolution = T4_Y_RESOLUTION_SUPERFINE;
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_300_300))
s->y_resolution = T4_Y_RESOLUTION_300;
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_200_200))
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, T30_DCS_BIT_T85_MODE))
{
if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T85_L0_MODE))
s->line_encoding = T4_COMPRESSION_ITU_T85_L0;
else
s->line_encoding = T4_COMPRESSION_ITU_T85;
}
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T6_MODE))
{
s->line_encoding = T4_COMPRESSION_ITU_T6;
}
else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_2D_MODE))
{
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 %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding);
if (!test_ctrl_bit(dcs_frame, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT))
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, T30_DCS_BIT_ECM) != 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));
t30_set_status(s, 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");
t30_set_status(s, T30_ERR_FILEERROR);
send_dcn(s);
return -1;
}
if (s->operation_in_progress != OPERATION_IN_PROGRESS_T4_RX)
{
if (t4_rx_init(&s->t4.rx, s->rx_file, s->output_encoding) == NULL)
{
span_log(&s->logging, SPAN_LOG_WARNING, "Cannot open target TIFF file '%s'\n", s->rx_file);
t30_set_status(s, 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_response_to_pps(t30_state_t *s)
{
queue_phase(s, T30_PHASE_D_TX);
if (s->rx_ecm_block_ok)
{
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] = ADDRESS_FIELD;
s->ecm_frame_map[1] = CONTROL_FIELD_FINAL_FRAME;
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;
int first_bad_frame;
int first;
int expected_len;
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;
/* 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;
block = msg[5];
page = msg[4];
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);
/* Check that we have received the page and block we expected. If the far end missed
our last response, it might have repeated the previous chunk. */
if ((s->rx_page_number & 0xFF) != page || (s->ecm_block & 0xFF) != block)
{
span_log(&s->logging,
SPAN_LOG_FLOW,
"ECM rx page/block mismatch - expected %d/%d, but received %d/%d.\n",
(s->rx_page_number & 0xFF),
(s->ecm_block & 0xFF),
page,
block);
/* Look for this being a repeat, because the other end missed the last response
we sent - which would have been a T30_MCF - If the block is for the previous
page, or the previous block of the current page, we can assume we have hit this
condition. */
if (((s->rx_page_number & 0xFF) == page && ((s->ecm_block - 1) & 0xFF) == block)
||
(((s->rx_page_number - 1) & 0xFF) == page && s->ecm_block == 0))
{
/* This must be a repeat of the last thing the far end sent, while we are expecting
the first transfer of a new block. */
span_log(&s->logging, SPAN_LOG_FLOW, "Looks like a repeat from the previous page/block - send MCF again.\n");
/* Clear the ECM buffer */
for (i = 0; i < 256; i++)
s->ecm_len[i] = -1;
s->ecm_frames = -1;
queue_phase(s, T30_PHASE_D_TX);
set_state(s, T30_STATE_F_POST_RCP_MCF);
send_simple_frame(s, T30_MCF);
}
else
{
/* Give up */
t30_set_status(s, T30_ERR_RX_ECMPHD);
send_dcn(s);
}
return 0;
}
/* Build a bit map of which frames we now have stored OK */
first_bad_frame = 256;
first = TRUE;
expected_len = 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)
{
/* The correct pattern of frame lengths is they will all be 64 or 256 octets long, except the
last one. The last one might the same length as all the others, or it might be exactly the
right length to contain the last chunk of the data. That is, some people pad at the end,
and some do not. */
/* Vet the frames which are present, to detect any with inappropriate lengths. This might seem
like overkill, as the frames must have had good CRCs to get this far. However, in the real
world there are systems, especially T.38 ones, which give bad frame lengths, and which screw
up communication unless you apply these checks. From experience, if you find a frame has a
suspect length, and demand retransmission, there is a good chance the new copy will be alright. */
if (frame_no < s->ecm_frames - 1)
{
/* Expect all frames, except the last one, to follow the length of the first one */
if (first)
{
/* Use the length of the first frame as our model for what the length should be */
if (s->ecm_len[frame_no] == 64)
expected_len = 64;
first = FALSE;
}
/* Check the length is consistent with the first frame */
if (s->ecm_len[frame_no] != expected_len)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Bad length ECM frame - %d\n", s->ecm_len[frame_no]);
s->ecm_len[frame_no] = -1;
}
}
}
if (s->ecm_len[frame_no] < 0)
{
s->ecm_frame_map[i + 3] |= (1 << j);
if (frame_no < first_bad_frame)
first_bad_frame = frame_no;
if (frame_no < s->ecm_frames)
s->error_correcting_mode_retries++;
}
}
}
s->rx_ecm_block_ok = (first_bad_frame >= s->ecm_frames);
if (s->rx_ecm_block_ok)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Partial page OK - committing block %d, %d frames\n", s->ecm_block, s->ecm_frames);
for (i = 0; i < s->ecm_frames; i++)
{
if (t4_rx_put_chunk(&s->t4.rx, s->ecm_data[i], s->ecm_len[i]))
{
/* This is the end of the document */
break;
}
}
/* Clear the ECM buffer */
for (i = 0; i < 256; i++)
s->ecm_len[i] = -1;
s->ecm_block++;
s->ecm_frames = -1;
switch (s->last_pps_fcf2)
{
case T30_NULL:
/* We can accept only this partial page. */
break;
default:
/* We can accept and confirm the whole page. */
s->next_rx_step = s->last_pps_fcf2;
rx_end_page(s);
report_rx_ecm_page_result(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;
}
}
switch (s->last_pps_fcf2)
{
case T30_PRI_MPS:
case T30_PRI_EOM:
case T30_PRI_EOP:
if (s->remote_interrupts_allowed)
{
}
/* Fall through */
case T30_NULL:
case T30_MPS:
case T30_EOM:
case T30_EOS:
case T30_EOP:
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_response_to_pps(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] = ADDRESS_FIELD;
frame[1] = CONTROL_FIELD_FINAL_FRAME;
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)
{
/* For other frame types we kill the call on an unexpected frame length. For FCD frames it is better to just ignore
the frame, and let retries sort things out. */
span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame length - %d\n", t30_frametype(msg[0]), len);
}
else
{
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;
}
/* We have received something, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER)
t30_set_status(s, T30_ERR_OK);
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.
Here we 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);
timer_t2_start(s);
/* We have received something, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER)
t30_set_status(s, T30_ERR_OK);
break;
case T30_STATE_F_POST_DOC_ECM:
/* Just ignore this. It must be an extra RCP. Several are usually sent, to maximise the chance
of receiving a correct one. */
timer_t2_start(s);
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)
{
uint8_t fcf;
/* We should be sending the TCF data right now */
fcf = msg[2] & 0xFE;
switch (fcf)
{
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:
t30_set_status(s, 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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
/* We should be sending the DCS sequence right now */
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_DCN:
t30_set_status(s, 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)
{
uint8_t fcf;
/* We should be sending the TCF data right now */
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_DCN:
t30_set_status(s, 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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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;
t30_set_status(s, 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_COMMAND_TRIES)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Too many retries. Giving up.\n");
t30_set_status(s, 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:
t30_set_status(s, 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)
{
uint8_t fcf;
/* We should be receiving TCF right now, not HDLC messages */
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
/* We're waiting for a response to the CFR we sent */
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_DCS:
/* If we received another DCS, they must have missed our CFR */
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_ftt(t30_state_t *s, const uint8_t *msg, int len)
{
uint8_t fcf;
/* We're waiting for a response to the FTT we sent */
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
/* 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. */
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_DIS:
process_rx_dis_dtc(s, msg, len);
break;
case T30_DCS:
process_rx_dcs(s, msg, len);
break;
case T30_PRI_MPS:
if (s->remote_interrupts_allowed)
{
}
/* Fall through */
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, fcf);
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_EOM:
if (s->remote_interrupts_allowed)
{
}
/* Fall through */
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, fcf);
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_EOP:
if (s->remote_interrupts_allowed)
{
}
/* Fall through */
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, fcf);
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_DCN:
t30_set_status(s, 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. */
t30_set_status(s, T30_ERR_RX_INVALCMD);
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_PRI_MPS:
if (s->remote_interrupts_allowed)
{
}
/* Fall through */
case T30_MPS:
s->next_rx_step = fcf;
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);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
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);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
rx_start_page(s);
set_state(s, T30_STATE_III_Q_RTP);
send_simple_frame(s, T30_RTP);
break;
case T30_COPY_QUALITY_BAD:
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
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->remote_interrupts_allowed)
{
}
/* Fall through */
case T30_EOM:
case T30_EOS:
s->next_rx_step = fcf;
/* 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);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
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);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
rx_start_page(s);
set_state(s, T30_STATE_III_Q_RTP);
send_simple_frame(s, T30_RTP);
break;
case T30_COPY_QUALITY_BAD:
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
rx_start_page(s);
set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN);
break;
}
break;
case T30_PRI_EOP:
if (s->remote_interrupts_allowed)
{
}
/* Fall through */
case T30_EOP:
s->next_rx_step = fcf;
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);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(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);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
set_state(s, T30_STATE_III_Q_RTP);
send_simple_frame(s, T30_RTP);
break;
case T30_COPY_QUALITY_BAD:
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN);
break;
}
break;
case T30_DCN:
t30_set_status(s, 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. */
t30_set_status(s, T30_ERR_RX_INVALCMD);
unexpected_final_frame(s, msg, len);
break;
}
}
/*- End of function --------------------------------------------------------*/
static void process_state_f_doc_and_post_doc_ecm(t30_state_t *s, const uint8_t *msg, int len)
{
uint8_t fcf;
uint8_t fcf2;
/* This actually handles 2 states - _DOC_ECM and _POST_DOC_ECM - as they are very similar */
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_DIS:
process_rx_dis_dtc(s, msg, len);
break;
case T30_DCS:
process_rx_dcs(s, msg, len);
break;
case T4_RCP:
/* Return to control for partial page. These might come through with or without the final frame tag.
Here we deal with the "final frame tag" case. */
process_rx_rcp(s, msg, len);
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:
if (s->remote_interrupts_allowed)
{
/* 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:
t30_set_status(s, 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. */
t30_set_status(s, T30_ERR_RX_INVALCMD);
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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
{
/* Now we send the deferred response */
send_response_to_pps(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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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 */
t30_set_status(s, 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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_DIS:
process_rx_dis_dtc(s, msg, len);
break;
case T30_DCN:
t30_set_status(s, 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);
t30_set_status(s, T30_ERR_TX_NODIS);
break;
}
}
/*- End of function --------------------------------------------------------*/
static void process_state_i(t30_state_t *s, const uint8_t *msg, int len)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_PIP:
if (s->remote_interrupts_allowed)
{
s->retries = 0;
if (s->phase_d_handler)
{
s->phase_d_handler(s, s->phase_d_user_data, fcf);
s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
}
/* Fall through */
case T30_MCF:
switch (s->next_tx_step)
{
case T30_PRI_MPS:
case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
/* Transmit the next page */
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_PRI_EOM:
case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE);
break;
case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
send_dcn(s);
report_tx_result(s, TRUE);
break;
}
break;
case T30_RTP:
s->rtp_events++;
switch (s->next_tx_step)
{
case T30_PRI_MPS:
case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
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;
t30_set_status(s, T30_ERR_CANNOT_TRAIN);
send_dcn(s);
break;
}
queue_phase(s, T30_PHASE_B_TX);
restart_sending_document(s);
break;
case T30_PRI_EOM:
case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
t4_tx_release(&s->t4.tx);
/* TODO: should go back to T, and resend */
return_to_phase_b(s, TRUE);
break;
case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
t4_tx_release(&s->t4.tx);
send_dcn(s);
break;
}
break;
case T30_PIN:
if (s->remote_interrupts_allowed)
{
s->retries = 0;
if (s->phase_d_handler)
{
s->phase_d_handler(s, s->phase_d_user_data, fcf);
s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
}
/* Fall through */
case T30_RTN:
s->rtn_events++;
switch (s->next_tx_step)
{
case T30_PRI_MPS:
case T30_MPS:
s->retries = 0;
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
if (!s->retransmit_capable)
{
/* Send the next page, regardless of the problem with the current one. */
if (tx_start_page(s))
{
/* TODO: recover */
break;
}
}
/* Send fresh training */
if (step_fallback_entry(s) < 0)
{
/* We have fallen back as far as we can go. Give up. */
s->current_fallback = 0;
t30_set_status(s, T30_ERR_CANNOT_TRAIN);
send_dcn(s);
break;
}
queue_phase(s, T30_PHASE_B_TX);
restart_sending_document(s);
break;
case T30_PRI_EOM:
case T30_EOM:
case T30_EOS:
s->retries = 0;
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
if (s->retransmit_capable)
{
/* Wait for DIS */
}
else
{
return_to_phase_b(s, TRUE);
}
break;
case T30_PRI_EOP:
case T30_EOP:
s->retries = 0;
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
if (s->retransmit_capable)
{
/* 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;
t30_set_status(s, T30_ERR_CANNOT_TRAIN);
send_dcn(s);
break;
}
queue_phase(s, T30_PHASE_B_TX);
restart_sending_document(s);
}
else
{
send_dcn(s);
}
break;
}
break;
case T30_DCN:
switch (s->next_tx_step)
{
case T30_PRI_MPS:
case T30_PRI_EOM:
case T30_MPS:
case T30_EOM:
case T30_EOS:
/* Unexpected DCN after EOM, EOS or MPS sequence */
t30_set_status(s, T30_ERR_RX_DCNPHD);
break;
default:
t30_set_status(s, 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. */
t30_set_status(s, T30_ERR_TX_INVALRSP);
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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:
t30_set_status(s, 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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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_PRI_MPS:
case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
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_PRI_EOM:
case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE);
break;
case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
send_dcn(s);
report_tx_result(s, TRUE);
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:
t30_set_status(s, 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);
t30_set_status(s, 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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_PIP:
if (s->remote_interrupts_allowed)
{
s->retries = 0;
if (s->phase_d_handler)
{
s->phase_d_handler(s, s->phase_d_user_data, fcf);
s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
}
/* Fall through */
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_PRI_MPS:
case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
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_PRI_EOM:
case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE);
break;
case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
send_dcn(s);
report_tx_result(s, TRUE);
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_PPR:
process_rx_ppr(s, msg, len);
break;
case T30_DCN:
t30_set_status(s, 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;
case T30_PIN:
if (s->remote_interrupts_allowed)
{
s->retries = 0;
if (s->phase_d_handler)
{
s->phase_d_handler(s, s->phase_d_user_data, fcf);
s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
}
/* Fall through */
default:
/* We don't know what to do with this. */
unexpected_final_frame(s, msg, len);
t30_set_status(s, 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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
case T30_PIP:
if (s->remote_interrupts_allowed)
{
s->retries = 0;
if (s->phase_d_handler)
{
s->phase_d_handler(s, s->phase_d_user_data, fcf);
s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
}
/* Fall through */
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_PRI_MPS:
case T30_MPS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
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_PRI_EOM:
case T30_EOM:
case T30_EOS:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
report_tx_result(s, TRUE);
return_to_phase_b(s, FALSE);
break;
case T30_PRI_EOP:
case T30_EOP:
tx_end_page(s);
if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf);
terminate_operation_in_progress(s);
send_dcn(s);
report_tx_result(s, TRUE);
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_DCN:
t30_set_status(s, 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;
case T30_PIN:
if (s->remote_interrupts_allowed)
{
s->retries = 0;
if (s->phase_d_handler)
{
s->phase_d_handler(s, s->phase_d_user_data, fcf);
s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
}
/* Fall through */
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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_ERR:
/* TODO: Continue with the next message if MPS or EOM? */
t30_set_status(s, T30_ERR_RETRYDCN);
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;
case T30_PIN:
if (s->remote_interrupts_allowed)
{
s->retries = 0;
if (s->phase_d_handler)
{
s->phase_d_handler(s, s->phase_d_user_data, fcf);
s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
}
/* Fall through */
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)
{
uint8_t fcf;
fcf = msg[2] & 0xFE;
switch (fcf)
{
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_ERR:
/* TODO: Continue with the next message if MPS or EOM? */
t30_set_status(s, T30_ERR_RETRYDCN);
s->timer_t5 = 0;
send_dcn(s);
break;
case T30_DCN:
t30_set_status(s, 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;
case T30_PIN:
if (s->remote_interrupts_allowed)
{
s->retries = 0;
if (s->phase_d_handler)
{
s->phase_d_handler(s, s->phase_d_user_data, fcf);
s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3);
}
}
/* Fall through */
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_T1A:
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);
s->rx_info.nsf_len = decode_nsf_nss_nsc(s, &s->rx_info.nsf, &msg[2], len - 2);
}
else
{
/* NSC - Non-standard facilities command */
/* OK in (NSC) (CIG) DTC */
s->rx_info.nsc_len = decode_nsf_nss_nsc(s, &s->rx_info.nsc, &msg[2], len - 2);
}
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_non_final_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_non_final_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_non_final_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_non_final_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_non_final_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 */
s->rx_info.nss_len = decode_nsf_nss_nsc(s, &s->rx_info.nss, &msg[2], len - 2);
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, "Rx final frame 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_and_post_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_COMMAND_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 */
t30_set_status(s, 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 */
t30_set_status(s, T30_ERR_TX_PHDDEAD);
break;
default:
/* Disconnected after permitted retries */
t30_set_status(s, 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)
{
/* T.30 Annex A says timeout T1 should be used in ECM phase C to time out the
first frame after the flags start. This seems a strange reuse of the name T1
for a different purpose, but there it is. We distinguish it by calling it T1A. */
if (s->phase == T30_PHASE_C_ECM_RX)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Start T1A\n");
s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T1A);
s->timer_t2_t4_is = TIMER_IS_T1A;
}
else
{
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_t2_t4_stop(t30_state_t *s)
{
const char *tag;
switch (s->timer_t2_t4_is)
{
case TIMER_IS_IDLE:
tag = "none";
break;
case TIMER_IS_T1A:
tag = "T1A";
break;
case TIMER_IS_T2:
tag = "T2";
break;
case TIMER_IS_T2A:
tag = "T2A";
break;
case TIMER_IS_T2B:
tag = "T2B";
break;
case TIMER_IS_T2C:
tag = "T2C";
break;
case TIMER_IS_T4:
tag = "T4";
break;
case TIMER_IS_T4A:
tag = "T4A";
break;
case TIMER_IS_T4B:
tag = "T4B";
break;
case TIMER_IS_T4C:
tag = "T4C";
break;
default:
tag = "T2/T4";
break;
}
span_log(&s->logging, SPAN_LOG_FLOW, "Stop %s (%d remaining)\n", tag, s->timer_t2_t4);
s->timer_t2_t4 = 0;
s->timer_t2_t4_is = TIMER_IS_IDLE;
}
/*- 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);
t30_set_status(s, 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. */
t30_set_status(s, 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)
{
if (s->timer_t2_t4_is != TIMER_IS_T2B)
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_PRI_EOM:
case T30_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 much 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 */
t30_set_status(s, 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 */
/* Figure 5-2b/T.30 and note 7 says we should allow 1 to 3 tries at this point.
The way we work now is effectively hard coding a 1 try limit */
t30_set_status(s, T30_ERR_RX_T2EXPMPS);
break;
#if 0
case ??????:
/* While waiting for DCN */
t30_set_status(s, T30_ERR_RX_T2EXPDCN);
break;
case ??????:
/* While waiting for phase D */
t30_set_status(s, T30_ERR_RX_T2EXPD);
break;
#endif
case T30_STATE_IV_PPS_RNR:
case T30_STATE_IV_EOR_RNR:
/* While waiting for RR command */
t30_set_status(s, T30_ERR_RX_T2EXPRR);
break;
case T30_STATE_R:
/* While waiting for NSS, DCS or MCF */
t30_set_status(s, 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_t1a_expired(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "T1A expired in phase %s, state %d. An HDLC frame lasted too long.\n", phase_names[s->phase], s->state);
t30_set_status(s, T30_ERR_HDLC_CARRIER);
disconnect(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);
t30_set_status(s, 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);
t30_set_status(s, 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);
t30_set_status(s, 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);
t30_set_status(s, 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 int decode_nsf_nss_nsc(t30_state_t *s, uint8_t *msg[], const uint8_t *pkt, int len)
{
uint8_t *t;
if ((t = malloc(len - 1)) == NULL)
return 0;
memcpy(t, pkt + 1, len - 1);
*msg = t;
return len - 1;
}
/*- 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;
timer_t2_t4_stop(s);
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;
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)
t30_set_status(s, 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)
t30_set_status(s, T30_ERR_OK);
}
else
{
span_log(&s->logging, SPAN_LOG_WARNING, "Non-ECM carrier not found\n");
t30_set_status(s, 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.rx, 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;
if (byte < 0)
{
t30_non_ecm_rx_status(user_data, byte);
return;
}
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.rx, (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.rx, 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.tx);
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.tx);
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.tx, 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;
int was_trained;
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:
timer_t2_t4_stop(s);
s->timer_t2_t4_is = TIMER_IS_T2C;
break;
case TIMER_IS_T4B:
timer_t2_t4_stop(s);
s->timer_t2_t4_is = TIMER_IS_T4C;
break;
}
break;
case SIG_STATUS_CARRIER_DOWN:
was_trained = s->rx_trained;
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->state == T30_STATE_F_DOC_ECM)
{
/* We should be receiving a document right now, but we haven't seen an RCP at the end of
transmission. */
if (was_trained)
{
/* We trained OK, so we should have some kind of received page, possibly with
zero good HDLC frames. It just did'nt end cleanly with an RCP. */
span_log(&s->logging, SPAN_LOG_WARNING, "ECM signal did not end cleanly\n");
/* Fake the existance of an RCP, and proceed */
set_state(s, T30_STATE_F_POST_DOC_ECM);
queue_phase(s, T30_PHASE_D_RX);
timer_t2_start(s);
/* We at least trained, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER)
t30_set_status(s, T30_ERR_OK);
}
else
{
/* Either there was no image carrier, or we failed to train to it. */
span_log(&s->logging, SPAN_LOG_WARNING, "ECM carrier not found\n");
t30_set_status(s, T30_ERR_RX_NOCARRIER);
}
}
if (s->next_phase != T30_PHASE_IDLE)
{
/* The appropriate timer for the next phase should already be in progress */
set_phase(s, s->next_phase);
s->next_phase = T30_PHASE_IDLE;
}
else
{
switch (s->timer_t2_t4_is)
{
case TIMER_IS_T1A:
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_T1A:
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)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Bad HDLC CRC received\n");
if (s->phase != T30_PHASE_C_ECM_RX)
{
/* 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);
}
else
{
/* Cancel the command or response timer (if one is running) */
span_log(&s->logging, SPAN_LOG_FLOW, "Bad CRC and timer is %d\n", s->timer_t2_t4_is);
if (s->timer_t2_t4_is == TIMER_IS_T2A)
timer_t2_t4_stop(s);
}
}
return;
}
if (len < 3)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Bad HDLC frame length - %d\n", len);
/* Cancel the command or response timer (if one is running) */
timer_t2_t4_stop(s);
return;
}
if (msg[0] != ADDRESS_FIELD
||
!(msg[1] == CONTROL_FIELD_NON_FINAL_FRAME || msg[1] == CONTROL_FIELD_FINAL_FRAME))
{
span_log(&s->logging, SPAN_LOG_FLOW, "Bad HDLC frame header - %02x %02x\n", msg[0], msg[1]);
/* Cancel the command or response timer (if one is running) */
timer_t2_t4_stop(s);
return;
}
s->rx_frame_received = TRUE;
/* Cancel the command or response timer (if one is running) */
timer_t2_t4_stop(s);
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_PRI_MPS:
case T30_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_PRI_EOM:
case T30_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_PRI_EOP:
case T30_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;
timer_t2_t4_stop(s);
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)
{
int previous;
if (s->timer_t0_t1 > 0)
{
if ((s->timer_t0_t1 -= samples) <= 0)
{
s->timer_t0_t1 = 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)
{
s->timer_t3 = 0;
timer_t3_expired(s);
}
}
if (s->timer_t2_t4 > 0)
{
if ((s->timer_t2_t4 -= samples) <= 0)
{
previous = s->timer_t2_t4_is;
/* Don't allow the count to be left at a small negative number.
It looks cosmetically bad in the logs. */
s->timer_t2_t4 = 0;
s->timer_t2_t4_is = TIMER_IS_IDLE;
switch (previous)
{
case TIMER_IS_T1A:
timer_t1a_expired(s);
break;
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)
{
s->timer_t5 = 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. */
t30_set_status(s, 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;
switch (s->operation_in_progress)
{
case OPERATION_IN_PROGRESS_T4_TX:
case OPERATION_IN_PROGRESS_POST_T4_TX:
t4_tx_get_transfer_statistics(&s->t4.tx, &stats);
break;
case OPERATION_IN_PROGRESS_T4_RX:
case OPERATION_IN_PROGRESS_POST_T4_RX:
t4_rx_get_transfer_statistics(&s->t4.rx, &stats);
break;
default:
memset(&stats, 0, sizeof(stats));
break;
}
t->pages_tx = s->tx_page_number;
t->pages_rx = s->rx_page_number;
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;
t->rtn_events = s->rtn_events;
t->rtp_events = s->rtp_events;
}
/*- 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(void) t30_remote_interrupts_allowed(t30_state_t *s, int state)
{
s->remote_interrupts_allowed = 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);
memset(&s->rx_info, 0, sizeof(s->rx_info));
release_resources(s);
/* The page number is only reset at call establishment */
s->rx_page_number = 0;
s->tx_page_number = 0;
s->rtn_events = 0;
s->rtp_events = 0;
s->local_interrupt_pending = FALSE;
s->far_end_detected = FALSE;
s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T0);
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);
}
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 | T30_SUPPORT_V17;
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. */
terminate_operation_in_progress(s);
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 ------------------------------------------------------------*/