Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/src/t30.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 15:50:58 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.6pre17/src/t30.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,6278 @@ +/* + * 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. + * + * $Id: t30.c,v 1.305.4.4 2009/12/23 14:23:49 steveu Exp $ + */ + +/*! \file */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> +#if defined(HAVE_TGMATH_H) +#include <tgmath.h> +#endif +#if defined(HAVE_MATH_H) +#include <math.h> +#endif +#include "floating_fudge.h" +#include <tiffio.h> + +#include "spandsp/telephony.h" +#include "spandsp/logging.h" +#include "spandsp/bit_operations.h" +#include "spandsp/queue.h" +#include "spandsp/power_meter.h" +#include "spandsp/complex.h" +#include "spandsp/tone_generate.h" +#include "spandsp/async.h" +#include "spandsp/hdlc.h" +#include "spandsp/fsk.h" +#include "spandsp/v29rx.h" +#include "spandsp/v29tx.h" +#include "spandsp/v27ter_rx.h" +#include "spandsp/v27ter_tx.h" +#include "spandsp/t4_rx.h" +#include "spandsp/t4_tx.h" +#include "spandsp/t30_fcf.h" +#include "spandsp/t35.h" +#include "spandsp/t30.h" +#include "spandsp/t30_api.h" +#include "spandsp/t30_logging.h" + +#include "spandsp/private/logging.h" +#include "spandsp/private/t4_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. */ +enum +{ + T30_COPY_QUALITY_PERFECT = 0, + T30_COPY_QUALITY_GOOD, + T30_COPY_QUALITY_POOR, + T30_COPY_QUALITY_BAD +}; + +enum +{ + DISBIT1 = 0x01, + DISBIT2 = 0x02, + DISBIT3 = 0x04, + DISBIT4 = 0x08, + DISBIT5 = 0x10, + DISBIT6 = 0x20, + DISBIT7 = 0x40, + DISBIT8 = 0x80 +}; + +/*! There are high level indications of what is happening at any instant, to guide the cleanup + process if the call is abandoned. */ +enum +{ + OPERATION_IN_PROGRESS_NONE = 0, + OPERATION_IN_PROGRESS_T4_RX, + OPERATION_IN_PROGRESS_T4_TX +}; + +/* All timers specified in milliseconds */ + +/*! Time-out T0 defines the amount of time an automatic calling terminal waits for the called terminal + to answer the call. + T0 begins after the dialling of the number is completed and is reset: + a) when T0 times out; or + b) when timer T1 is started; or + c) if the terminal is capable of detecting any condition which indicates that the call will not be + successful, when such a condition is detected. + The recommended value of T0 is 60+-5s. However, when it is anticipated that a long call set-up + time may be encountered, an alternative value of up to 120s may be used. + NOTE - National regulations may require the use of other values for T0. */ +#define DEFAULT_TIMER_T0 60000 + +/*! Time-out T1 defines the amount of time two terminals will continue to attempt to identify each + other. T1 is 35+-5s, begins upon entering phase B, and is reset upon detecting a valid signal or + when T1 times out. + For operating methods 3 and 4 (see 3.1), the calling terminal starts time-out T1 upon reception of + the V.21 modulation scheme. + For operating method 4 bis a (see 3.1), the calling terminal starts time-out T1 upon starting + transmission using the V.21 modulation scheme. + 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 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); + break; + case OPERATION_IN_PROGRESS_T4_RX: + t4_rx_release(&s->t4); + break; + } + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int tx_start_page(t30_state_t *s) +{ + if (t4_tx_start_page(&s->t4)) + { + 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) == 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, s->image_width); + t4_rx_set_sub_address(&s->t4, s->rx_info.sub_address); + t4_rx_set_dcs(&s->t4, s->rx_dcs_string); + t4_rx_set_far_ident(&s->t4, s->rx_info.ident); + t4_rx_set_vendor(&s->t4, s->vendor); + t4_rx_set_model(&s->t4, s->model); + + t4_rx_set_rx_encoding(&s->t4, s->line_encoding); + t4_rx_set_x_resolution(&s->t4, s->x_resolution); + t4_rx_set_y_resolution(&s->t4, s->y_resolution); + + if (t4_rx_start_page(&s->t4)) + return -1; + /* Clear the buffer */ + for (i = 0; i < 256; i++) + s->ecm_len[i] = -1; + s->ecm_block = 0; + s->ecm_frames = -1; + s->ecm_frames_this_tx_burst = 0; + s->error_correcting_mode_retries = 0; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int rx_end_page(t30_state_t *s) +{ + if (t4_rx_end_page(&s->t4) == 0) + { + s->rx_page_number++; + s->ecm_block = 0; + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int copy_quality(t30_state_t *s) +{ + t4_stats_t stats; + int quality; + + t4_get_transfer_statistics(&s->t4, &stats); + /* There is no specification for judging copy quality. However, we need to classify + it at three levels, to control what we do next: OK; tolerable, but retrain; + intolerable. */ + /* Based on the thresholds used in the TSB85 tests, we consider: + <5% bad rows is OK + <15% bad rows to be tolerable, but retrain + >15% bad rows to be intolerable + */ + span_log(&s->logging, SPAN_LOG_FLOW, "Page no = %d\n", stats.pages_transferred + 1); + span_log(&s->logging, SPAN_LOG_FLOW, "Image size = %d x %d pixels\n", stats.width, stats.length); + span_log(&s->logging, SPAN_LOG_FLOW, "Image resolution = %d/m x %d/m\n", stats.x_resolution, stats.y_resolution); + span_log(&s->logging, SPAN_LOG_FLOW, "Bad rows = %d\n", stats.bad_rows); + span_log(&s->logging, SPAN_LOG_FLOW, "Longest bad row run = %d\n", stats.longest_bad_row_run); + /* 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 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); + 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) + 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, &s->ecm_data[i][4], s->octets_per_ecm_frame)) < s->octets_per_ecm_frame) + { + /* The image is not big enough to fill the entire buffer */ + /* We need to pad to a full frame, as most receivers expect that. */ + if (len > 0) + { + memset(&s->ecm_data[i][4 + len], 0, s->octets_per_ecm_frame - len); + s->ecm_len[i++] = (int16_t) (s->octets_per_ecm_frame + 4); + } + s->ecm_frames = i; + span_log(&s->logging, SPAN_LOG_FLOW, "Partial page buffer contains %d frames (%d per frame)\n", i, s->octets_per_ecm_frame); + s->ecm_at_page_end = TRUE; + return i; + } + s->ecm_len[i] = (int16_t) (4 + len); + } + /* We filled the entire buffer */ + s->ecm_frames = 256; + span_log(&s->logging, SPAN_LOG_FLOW, "Partial page buffer full (%d per frame)\n", s->octets_per_ecm_frame); + s->ecm_at_page_end = ((t4_tx_check_bit(&s->t4) & 2) != 0); + return 256; +} +/*- End of function --------------------------------------------------------*/ + +static int t30_ecm_commit_partial_page(t30_state_t *s) +{ + int i; + int image_ended; + + span_log(&s->logging, SPAN_LOG_FLOW, "Commiting partial page - block %d, %d frames\n", s->ecm_block, s->ecm_frames); + image_ended = FALSE; + for (i = 0; i < s->ecm_frames; i++) + { + if (t4_rx_put_chunk(&s->t4, s->ecm_data[i], s->ecm_len[i])) + { + /* This is the end of the document */ + /* Clear the buffer */ + for (i = 0; i < 256; i++) + s->ecm_len[i] = -1; + s->ecm_frames = -1; + image_ended = TRUE; + break; + } + } + /* Clear the buffer */ + for (i = 0; i < 256; i++) + s->ecm_len[i] = -1; + s->ecm_block++; + s->ecm_frames = -1; + return (image_ended) ? -1 : 0; +} +/*- End of function --------------------------------------------------------*/ + +static int send_next_ecm_frame(t30_state_t *s) +{ + int i; + uint8_t frame[3]; + + if (s->ecm_current_tx_frame < s->ecm_frames) + { + /* Search for the next frame, within the current partial page, which has + not been tagged as transferred OK. */ + for (i = s->ecm_current_tx_frame; i < s->ecm_frames; i++) + { + if (s->ecm_len[i] >= 0) + { + send_frame(s, s->ecm_data[i], s->ecm_len[i]); + s->ecm_current_tx_frame = i + 1; + s->ecm_frames_this_tx_burst++; + return 0; + } + } + s->ecm_current_tx_frame = s->ecm_frames; + } + if (s->ecm_current_tx_frame <= s->ecm_frames + 3) + { + /* We have sent all the FCD frames. Send some RCP frames. Three seems to be + a popular number, to minimise the risk of a bit error stopping the receiving + end from recognising the RCP. */ + s->ecm_current_tx_frame++; + /* The RCP frame is an odd man out, as its a simple 1 byte control + frame, but is specified to not have the final bit set. It doesn't + seem to have the DIS received bit set, either. */ + frame[0] = 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 0 + 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); + /* No T.85 optional L0. */ + //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); +#endif + } + 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.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 0 + case T4_COMPRESSION_ITU_T85: + 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_CODING); + 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) + { + s->current_status = bad; + span_log(&s->logging, SPAN_LOG_FLOW, "Image resolution (%d x %d) not acceptable\n", s->x_resolution, s->y_resolution); + return -1; + } + /* Deal with the image width. The X resolution will fall in line with any valid width. */ + /* Low (R4) res widths are not supported in recent versions of T.30 */ + bad = T30_ERR_OK; + /* 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) + { + s->current_status = bad; + span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) 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) + { + s->current_status = bad; + span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) 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_row_bits(&s->t4, min_row_bits); + /* We need to rebuild the DCS message we will send. */ + build_dcs(s); + return s->current_fallback; +} +/*- End of function --------------------------------------------------------*/ + +static int find_fallback_entry(int dcs_code) +{ + int i; + + /* The table is short, and not searched often, so a brain-dead linear scan seems OK */ + for (i = 0; fallback_sequence[i].bit_rate; i++) + { + if (fallback_sequence[i].dcs_code == dcs_code) + break; + } + if (fallback_sequence[i].bit_rate == 0) + return -1; + return i; +} +/*- End of function --------------------------------------------------------*/ + +static void send_dcn(t30_state_t *s) +{ + queue_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_C); + send_simple_frame(s, T30_DCN); +} +/*- End of function --------------------------------------------------------*/ + +static void return_to_phase_b(t30_state_t *s, int with_fallback) +{ + /* This is what we do after things like T30_EOM is exchanged. */ +#if 0 + if (step_fallback_entry(s) < 0) + { + /* We have fallen back as far as we can go. Give up. */ + s->current_fallback = 0; + s->current_status = T30_ERR_CANNOT_TRAIN; + send_dcn(s); + } + else + { + if (s->calling_party) + set_state(s, T30_STATE_T); + else + set_state(s, T30_STATE_R); + } +#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. */ + switch (s->operation_in_progress) + { + case OPERATION_IN_PROGRESS_T4_TX: + t4_tx_release(&s->t4); + break; + case OPERATION_IN_PROGRESS_T4_RX: + t4_rx_release(&s->t4); + break; + } + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->timer_t0_t1 = 0; + s->timer_t2_t4 = 0; + s->timer_t3 = 0; + s->timer_t5 = 0; + set_phase(s, T30_PHASE_E); + set_state(s, T30_STATE_B); +} +/*- End of function --------------------------------------------------------*/ + +static int set_min_scan_time_code(t30_state_t *s) +{ + /* Translation between the codes for the minimum scan times the other end needs, + and the codes for what we say will be used. We need 0 minimum. */ + static const uint8_t translate_min_scan_time[3][8] = + { + {T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_0MS}, /* normal */ + {T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_40MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_0MS}, /* fine */ + {T30_MIN_SCAN_10MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_20MS, T30_MIN_SCAN_10MS, T30_MIN_SCAN_5MS, T30_MIN_SCAN_0MS} /* superfine, when half fine time is selected */ + }; + /* Translation between the codes for the minimum scan time we will use, and milliseconds. */ + static const int min_scan_times[8] = + { + 20, 5, 10, 0, 40, 0, 0, 0 + }; + int min_bits_field; + + /* Set the minimum scan time bits */ + if (s->error_correcting_mode) + min_bits_field = T30_MIN_SCAN_0MS; + else + min_bits_field = (s->far_dis_dtc_frame[5] >> 4) & 7; + switch (s->y_resolution) + { + case T4_Y_RESOLUTION_SUPERFINE: + if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE)) + { + s->current_status = T30_ERR_NORESSUPPORT; + span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution.\n"); + return -1; + } + s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, 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)) + { + s->current_status = T30_ERR_NORESSUPPORT; + span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution.\n"); + return -1; + } + s->min_scan_time_code = translate_min_scan_time[1][min_bits_field]; + break; + default: + case T4_Y_RESOLUTION_STANDARD: + s->min_scan_time_code = translate_min_scan_time[0][min_bits_field]; + break; + } + if (!s->error_correcting_mode && (s->iaf & T30_IAF_MODE_NO_FILL_BITS)) + return 0; + return fallback_sequence[s->current_fallback].bit_rate*min_scan_times[s->min_scan_time_code]/1000; +} +/*- End of function --------------------------------------------------------*/ + +static int start_sending_document(t30_state_t *s) +{ + int min_row_bits; + + if (s->tx_file[0] == '\0') + { + /* There is nothing to send */ + span_log(&s->logging, SPAN_LOG_FLOW, "No document to send\n"); + return -1; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Start sending document\n"); + if (t4_tx_init(&s->t4, s->tx_file, s->tx_start_page, s->tx_stop_page) == NULL) + { + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot open source TIFF file '%s'\n", s->tx_file); + s->current_status = T30_ERR_FILEERROR; + return -1; + } + s->operation_in_progress = OPERATION_IN_PROGRESS_T4_TX; + t4_tx_get_pages_in_file(&s->t4); + t4_tx_set_tx_encoding(&s->t4, s->line_encoding); + t4_tx_set_local_ident(&s->t4, s->tx_info.ident); + t4_tx_set_header_info(&s->t4, s->header_info); + + s->x_resolution = t4_tx_get_x_resolution(&s->t4); + s->y_resolution = t4_tx_get_y_resolution(&s->t4); + /* The minimum scan time to be used can't be evaluated until we know the Y resolution, and + must be evaluated before the minimum scan row bits can be evaluated. */ + if ((min_row_bits = set_min_scan_time_code(s)) < 0) + { + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + return -1; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Minimum bits per row will be %d\n", min_row_bits); + t4_tx_set_min_row_bits(&s->t4, min_row_bits); + + if (tx_start_page(s)) + return -1; + s->image_width = t4_tx_get_image_width(&s->t4); + if (s->error_correcting_mode) + { + if (get_partial_ecm_page(s) == 0) + span_log(&s->logging, SPAN_LOG_WARNING, "No image data to send\n"); + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int restart_sending_document(t30_state_t *s) +{ + t4_tx_restart_page(&s->t4); + s->retries = 0; + s->ecm_block = 0; + send_dcs_sequence(s, TRUE); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int start_receiving_document(t30_state_t *s) +{ + if (s->rx_file[0] == '\0') + { + /* There is nothing to receive to */ + span_log(&s->logging, SPAN_LOG_FLOW, "No document to receive\n"); + return -1; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Start receiving document\n"); + queue_phase(s, T30_PHASE_B_TX); + s->ecm_block = 0; + send_dis_or_dtc_sequence(s, TRUE); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static void unexpected_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) + s->current_status = T30_ERR_UNEXPECTED; +} +/*- End of function --------------------------------------------------------*/ + +static void unexpected_final_frame(t30_state_t *s, const uint8_t *msg, int len) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame in state %d\n", t30_frametype(msg[2]), s->state); + if (s->current_status == T30_ERR_OK) + s->current_status = T30_ERR_UNEXPECTED; + send_dcn(s); +} +/*- End of function --------------------------------------------------------*/ + +static void unexpected_frame_length(t30_state_t *s, const uint8_t *msg, int len) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame length - %d\n", t30_frametype(msg[0]), len); + if (s->current_status == T30_ERR_OK) + s->current_status = T30_ERR_UNEXPECTED; + send_dcn(s); +} +/*- End of function --------------------------------------------------------*/ + +static int process_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) +{ + int new_status; + + t30_decode_dis_dtc_dcs(s, msg, len); + if (len < 6) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Short DIS/DTC frame\n"); + return -1; + } + + if (msg[2] == T30_DIS) + s->dis_received = TRUE; + /* Make a local copy of the message, padded to the maximum possible length with zeros. This allows + us to simply pick out the bits, without worrying about whether they were set from the remote side. */ + s->far_dis_dtc_len = (len > T30_MAX_DIS_DTC_DCS_LEN) ? T30_MAX_DIS_DTC_DCS_LEN : len; + memcpy(s->far_dis_dtc_frame, msg, s->far_dis_dtc_len); + if (s->far_dis_dtc_len < T30_MAX_DIS_DTC_DCS_LEN) + memset(s->far_dis_dtc_frame + s->far_dis_dtc_len, 0, T30_MAX_DIS_DTC_DCS_LEN - s->far_dis_dtc_len); + s->error_correcting_mode = (s->ecm_allowed && (s->far_dis_dtc_frame[6] & DISBIT3) != 0); + /* 256 octets per ECM frame */ + s->octets_per_ecm_frame = 256; + /* Select the compression to use. */ +#if 0 + 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)) + { + 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)) +#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)) +#endif + { + 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! */ + s->current_status = T30_ERR_INCOMPATIBLE; + return -1; + } + if (s->phase_b_handler) + { + new_status = s->phase_b_handler(s, s->phase_b_user_data, msg[2]); + if (new_status != T30_ERR_OK) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Application rejected DIS/DTC - '%s'\n", t30_completion_code_to_str(new_status)); + s->current_status = new_status; + /* TODO: If FNV is allowed, process it here */ + send_dcn(s); + return -1; + } + } + queue_phase(s, T30_PHASE_B_TX); + /* Try to send something */ + if (s->tx_file[0]) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Trying to send file '%s'\n", s->tx_file); + if (!test_ctrl_bit(s->far_dis_dtc_frame, 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])); + s->current_status = T30_ERR_RX_INCAPABLE; + send_dcn(s); + } + if (start_sending_document(s)) + { + send_dcn(s); + return -1; + } + if (build_dcs(s)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "The far end is incompatible\n", s->tx_file); + send_dcn(s); + return -1; + } + s->retries = 0; + send_dcs_sequence(s, TRUE); + return 0; + } + span_log(&s->logging, SPAN_LOG_FLOW, "%s nothing to send\n", t30_frametype(msg[2])); + /* ... then try to receive something */ + if (s->rx_file[0]) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Trying to receive file '%s'\n", s->rx_file); + if (!test_ctrl_bit(s->far_dis_dtc_frame, 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])); + s->current_status = T30_ERR_TX_INCAPABLE; + send_dcn(s); + return -1; + } + if (start_receiving_document(s)) + { + send_dcn(s); + return -1; + } + if (set_dis_or_dtc(s)) + { + s->current_status = T30_ERR_INCOMPATIBLE; + send_dcn(s); + return -1; + } + s->retries = 0; + send_dis_or_dtc_sequence(s, TRUE); + return 0; + } + span_log(&s->logging, SPAN_LOG_FLOW, "%s nothing to receive\n", t30_frametype(msg[2])); + /* There is nothing to do, or nothing we are able to do. */ + send_dcn(s); + return -1; +} +/*- End of function --------------------------------------------------------*/ + +static int process_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) +{ + static const int widths[6][4] = + { + { T4_WIDTH_R4_A4, T4_WIDTH_R4_B4, T4_WIDTH_R4_A3, -1}, /* R4 resolution - no longer used in recent versions of T.30 */ + { T4_WIDTH_R8_A4, T4_WIDTH_R8_B4, T4_WIDTH_R8_A3, -1}, /* R8 resolution */ + { T4_WIDTH_300_A4, T4_WIDTH_300_B4, T4_WIDTH_300_A3, -1}, /* 300/inch resolution */ + { T4_WIDTH_R16_A4, T4_WIDTH_R16_B4, T4_WIDTH_R16_A3, -1}, /* R16 resolution */ + { T4_WIDTH_600_A4, T4_WIDTH_600_B4, T4_WIDTH_600_A3, -1}, /* 600/inch resolution */ + {T4_WIDTH_1200_A4, T4_WIDTH_1200_B4, T4_WIDTH_1200_A3, -1} /* 1200/inch resolution */ + }; + uint8_t dcs_frame[T30_MAX_DIS_DTC_DCS_LEN]; + int i; + int new_status; + + t30_decode_dis_dtc_dcs(s, msg, len); + + /* Check DCS frame from remote */ + if (len < 6) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Short DCS frame\n"); + return -1; + } + + /* Make an ASCII string format copy of the message, for logging in the + received file. This string does not include the frame header octets. */ + sprintf(s->rx_dcs_string, "%02X", bit_reverse8(msg[3])); + for (i = 4; i < len; i++) + sprintf(s->rx_dcs_string + 3*i - 10, " %02X", bit_reverse8(msg[i])); + /* Make a local copy of the message, padded to the maximum possible length with zeros. This allows + us to simply pick out the bits, without worrying about whether they were set from the remote side. */ + if (len > T30_MAX_DIS_DTC_DCS_LEN) + { + memcpy(dcs_frame, msg, T30_MAX_DIS_DTC_DCS_LEN); + } + else + { + memcpy(dcs_frame, msg, len); + if (len < T30_MAX_DIS_DTC_DCS_LEN) + memset(dcs_frame + len, 0, T30_MAX_DIS_DTC_DCS_LEN - len); + } + + s->octets_per_ecm_frame = test_ctrl_bit(dcs_frame, 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 0 + if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T85_MODE)) + s->line_encoding = T4_COMPRESSION_ITU_T85; + else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T6_MODE)) +#else + if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T6_MODE)) +#endif + s->line_encoding = T4_COMPRESSION_ITU_T6; + else if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_2D_CODING)) + s->line_encoding = T4_COMPRESSION_ITU_T4_2D; + else + s->line_encoding = T4_COMPRESSION_ITU_T4_1D; + span_log(&s->logging, SPAN_LOG_FLOW, "Selected compression %d\n", s->line_encoding); + if (!test_ctrl_bit(dcs_frame, 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)); + s->current_status = new_status; + /* TODO: If FNV is allowed, process it here */ + send_dcn(s); + return -1; + } + } + /* Start document reception */ + span_log(&s->logging, + SPAN_LOG_FLOW, + "Get document at %dbps, modem %d\n", + fallback_sequence[s->current_fallback].bit_rate, + fallback_sequence[s->current_fallback].modem_type); + if (s->rx_file[0] == '\0') + { + span_log(&s->logging, SPAN_LOG_FLOW, "No document to receive\n"); + s->current_status = T30_ERR_FILEERROR; + send_dcn(s); + return -1; + } + if (!s->in_message) + { + if (t4_rx_init(&s->t4, s->rx_file, s->output_encoding) == NULL) + { + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot open target TIFF file '%s'\n", s->rx_file); + s->current_status = T30_ERR_FILEERROR; + send_dcn(s); + return -1; + } + s->operation_in_progress = OPERATION_IN_PROGRESS_T4_RX; + } + if (!(s->iaf & T30_IAF_MODE_NO_TCF)) + { + /* TCF is always sent with long training */ + s->short_train = FALSE; + set_state(s, T30_STATE_F_TCF); + queue_phase(s, T30_PHASE_C_NON_ECM_RX); + timer_t2_start(s); + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int send_deferred_pps_response(t30_state_t *s) +{ + queue_phase(s, T30_PHASE_D_TX); + if (s->ecm_first_bad_frame >= s->ecm_frames) + { + /* Everything was OK. We can accept the data and move on. */ + t30_ecm_commit_partial_page(s); + switch (s->last_pps_fcf2) + { + case T30_NULL: + /* We can confirm this partial page. */ + break; + default: + /* We can confirm the whole page. */ + s->next_rx_step = s->last_pps_fcf2; + rx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, s->last_pps_fcf2); + rx_start_page(s); + break; + } + set_state(s, T30_STATE_F_POST_RCP_MCF); + send_simple_frame(s, T30_MCF); + } + else + { + /* We need to send the PPR frame we have created, to try to fill in the missing/bad data. */ + set_state(s, T30_STATE_F_POST_RCP_PPR); + s->ecm_frame_map[0] = 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; + + if (len < 7) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Bad PPS message length %d.\n", len); + return -1; + } + s->last_pps_fcf2 = msg[3] & 0xFE; + page = msg[4]; + block = msg[5]; + + /* The frames count is not well specified in T.30. In practice it seems it might be the + number of frames in the current block, or it might be the number of frames in the + current burst of transmission. For a burst of resent frames this would make it smaller + than the actual size of the block. If we only accept the number when it exceeds + previous values, we should get the real number of frames in the block. */ + frames = msg[6] + 1; + if (s->ecm_frames < 0) + { + /* First time. Take the number and believe in it. */ + s->ecm_frames = frames; + } + else + { + /* If things have gone wrong, the far end might try to send us zero FCD + frames. It can't represent zero in the block count field, so it might + put zero there, or it might simplistically insert (blocks - 1), and put + 0xFF there. Beware of this. */ + if (frames == 0xFF) + { + /* This is probably zero, erroneously rolled over to the maximum count */ + frames = 0; + } + } + span_log(&s->logging, SPAN_LOG_FLOW, "Received PPS + %s - page %d, block %d, %d frames\n", t30_frametype(msg[3]), page, block, frames); + if (page != s->rx_page_number) + { + span_log(&s->logging, SPAN_LOG_FLOW, "ECM rx page mismatch - expected %d, but received %d.\n", s->rx_page_number, page); + } + if (block != s->ecm_block) + { + span_log(&s->logging, SPAN_LOG_FLOW, "ECM rx block mismatch - expected %d, but received %d.\n", s->ecm_block, block); + } + /* Build a bit map of which frames we now have stored OK */ + s->ecm_first_bad_frame = 256; + for (i = 0; i < 32; i++) + { + s->ecm_frame_map[i + 3] = 0; + for (j = 0; j < 8; j++) + { + frame_no = (i << 3) + j; + if (s->ecm_len[frame_no] < 0) + { + s->ecm_frame_map[i + 3] |= (1 << j); + if (frame_no < s->ecm_first_bad_frame) + s->ecm_first_bad_frame = frame_no; + if (frame_no < s->ecm_frames) + s->error_correcting_mode_retries++; + } + } + } + /* Are there any bad frames, or does our scan represent things being OK? */ + switch (s->last_pps_fcf2) + { + case T30_NULL: + case T30_EOP: + case T30_PRI_EOP: + case T30_EOM: + case T30_PRI_EOM: + case T30_EOS: + case T30_MPS: + case T30_PRI_MPS: + if (s->receiver_not_ready_count > 0) + { + s->receiver_not_ready_count--; + queue_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_F_POST_RCP_RNR); + send_simple_frame(s, T30_RNR); + } + else + { + send_deferred_pps_response(s); + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_ppr(t30_state_t *s, const uint8_t *msg, int len) +{ + int i; + int j; + int frame_no; + uint8_t frame[4]; + + if (len != 3 + 32) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Bad length for PPR bits - %d\n", len); + /* TODO: probably should send DCN */ + return; + } + /* Check which frames are OK, and mark them as OK. */ + for (i = 0; i < 32; i++) + { + for (j = 0; j < 8; j++) + { + frame_no = (i << 3) + j; + /* Tick off the frames they are not complaining about as OK */ + if ((msg[i + 3] & (1 << j)) == 0) + { + if (s->ecm_len[frame_no] >= 0) + s->ecm_progress++; + s->ecm_len[frame_no] = -1; + } + else + { + if (frame_no < s->ecm_frames) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Frame %d to be resent\n", frame_no); + s->error_correcting_mode_retries++; + } +#if 0 + /* Diagnostic: See if the other end is complaining about something we didn't even send this time. */ + if (s->ecm_len[frame_no] < 0) + span_log(&s->logging, SPAN_LOG_FLOW, "PPR contains complaint about frame %d, which was not sent\n", frame_no); +#endif + } + } + } + if (++s->ppr_count >= PPR_LIMIT_BEFORE_CTC_OR_EOR) + { + /* Continue to correct? */ + /* Continue only if we have been making progress */ + s->ppr_count = 0; + if (s->ecm_progress) + { + s->ecm_progress = 0; + queue_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_IV_CTC); + send_simple_frame(s, T30_CTC); + } + else + { + set_state(s, T30_STATE_IV_EOR); + queue_phase(s, T30_PHASE_D_TX); + frame[0] = 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) + { + frame_no = msg[3]; + /* Just store the actual image data, and record its length */ + span_log(&s->logging, SPAN_LOG_FLOW, "Storing ECM frame %d, length %d\n", frame_no, len - 4); + memcpy(&s->ecm_data[frame_no][0], &msg[4], len - 4); + s->ecm_len[frame_no] = (int16_t) (len - 4); + /* In case we are just after a CTC/CTR exchange, which kicked us back to long training */ + s->short_train = TRUE; + } + else + { + unexpected_frame_length(s, msg, len); + } + /* We have received something, so any missing carrier status is out of date */ + if (s->current_status == T30_ERR_RX_NOCARRIER) + s->current_status = 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) + s->current_status = 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. */ + 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: + s->current_status = T30_ERR_TX_GOTDCN; + disconnect(s); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_b(t30_state_t *s, const uint8_t *msg, int len) +{ + 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: + s->current_status = T30_ERR_TX_BADDCS; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_d_tcf(t30_state_t *s, const uint8_t *msg, int len) +{ + uint8_t fcf; + + /* We should be sending the TCF data right now */ + fcf = msg[2] & 0xFE; + switch (fcf) + { + case T30_DCN: + s->current_status = T30_ERR_TX_BADDCS; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_d_post_tcf(t30_state_t *s, const uint8_t *msg, int len) +{ + 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; + s->current_status = T30_ERR_CANNOT_TRAIN; + send_dcn(s); + break; + } + queue_phase(s, T30_PHASE_B_TX); + send_dcs_sequence(s, TRUE); + break; + case T30_DIS: + /* It appears they didn't see what we sent - retry the TCF */ + if (++s->retries >= MAX_COMMAND_TRIES) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Too many retries. Giving up.\n"); + s->current_status = T30_ERR_RETRYDCN; + send_dcn(s); + break; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Retry number %d\n", s->retries); + queue_phase(s, T30_PHASE_B_TX); + /* TODO: should we reassess the new DIS message, and possibly adjust the DCS we use? */ + send_dcs_sequence(s, TRUE); + break; + case T30_DCN: + s->current_status = T30_ERR_TX_BADDCS; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_f_tcf(t30_state_t *s, const uint8_t *msg, int len) +{ + 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_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_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->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + s->next_rx_step = msg[2] & 0xFE; + set_state(s, T30_STATE_III_Q_RTN); + break; + case T30_EOM: + case T30_EOS: + /* Treat this as a bad quality page. */ + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, 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_EOM: + /* Treat this as a bad quality page. */ + 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); + } + s->next_rx_step = T30_PRI_EOM; + set_state(s, T30_STATE_III_Q_RTN); + break; + case T30_EOP: + /* Treat this as a bad quality page. */ + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, 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_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->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + s->next_rx_step = msg[2] & 0xFE; + set_state(s, T30_STATE_III_Q_RTN); + break; + case T30_DCN: + s->current_status = T30_ERR_RX_DCNDATA; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + s->current_status = 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_MPS: + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + 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); + rx_start_page(s); + set_state(s, T30_STATE_III_Q_MCF); + send_simple_frame(s, T30_MCF); + break; + case T30_COPY_QUALITY_POOR: + rx_end_page(s); + rx_start_page(s); + set_state(s, T30_STATE_III_Q_RTP); + send_simple_frame(s, T30_RTP); + break; + case T30_COPY_QUALITY_BAD: + rx_start_page(s); + set_state(s, T30_STATE_III_Q_RTN); + send_simple_frame(s, T30_RTN); + break; + } + break; + case T30_PRI_MPS: + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + s->next_rx_step = fcf; + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_PERFECT: + case T30_COPY_QUALITY_GOOD: + rx_end_page(s); + t4_rx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_MCF); + break; + case T30_COPY_QUALITY_POOR: + rx_end_page(s); + t4_rx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_RTP); + break; + case T30_COPY_QUALITY_BAD: + set_state(s, T30_STATE_III_Q_RTN); + break; + } + break; + case T30_EOM: + case T30_EOS: + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + 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); + rx_start_page(s); + set_state(s, T30_STATE_III_Q_MCF); + send_simple_frame(s, T30_MCF); + break; + case T30_COPY_QUALITY_POOR: + rx_end_page(s); + rx_start_page(s); + set_state(s, T30_STATE_III_Q_RTP); + send_simple_frame(s, T30_RTP); + break; + case T30_COPY_QUALITY_BAD: + rx_start_page(s); + set_state(s, T30_STATE_III_Q_RTN); + send_simple_frame(s, T30_RTN); + break; + } + break; + case T30_PRI_EOM: + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + s->next_rx_step = fcf; + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_PERFECT: + case T30_COPY_QUALITY_GOOD: + rx_end_page(s); + t4_rx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_MCF); + break; + case T30_COPY_QUALITY_POOR: + rx_end_page(s); + t4_rx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_RTP); + break; + case T30_COPY_QUALITY_BAD: + set_state(s, T30_STATE_III_Q_RTN); + break; + } + break; + case T30_EOP: + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + 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); + t4_rx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_MCF); + send_simple_frame(s, T30_MCF); + break; + case T30_COPY_QUALITY_POOR: + rx_end_page(s); + t4_rx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_RTP); + send_simple_frame(s, T30_RTP); + break; + case T30_COPY_QUALITY_BAD: + set_state(s, T30_STATE_III_Q_RTN); + send_simple_frame(s, T30_RTN); + break; + } + break; + case T30_PRI_EOP: + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + s->next_rx_step = fcf; + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_PERFECT: + case T30_COPY_QUALITY_GOOD: + rx_end_page(s); + t4_rx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_MCF); + break; + case T30_COPY_QUALITY_POOR: + rx_end_page(s); + t4_rx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_RTP); + break; + case T30_COPY_QUALITY_BAD: + set_state(s, T30_STATE_III_Q_RTN); + break; + } + break; + case T30_DCN: + s->current_status = T30_ERR_RX_DCNFAX; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + s->current_status = 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. */ + if (s->state == T30_STATE_F_DOC_ECM) + { + /* Return to control for partial page */ + 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) + s->current_status = T30_ERR_OK; + } + else + { + /* Just ignore this. It must be an extra RCP. Several are usually sent, to maximise the chance + of receiving a correct one. */ + } + break; + case T30_EOR: + if (len != 4) + { + unexpected_frame_length(s, msg, len); + break; + } + fcf2 = msg[3] & 0xFE; + span_log(&s->logging, SPAN_LOG_FLOW, "Received EOR + %s\n", t30_frametype(msg[3])); + switch (fcf2) + { + case T30_PRI_EOP: + case T30_PRI_EOM: + case T30_PRI_MPS: + /* TODO: Alert operator */ + /* Fall through */ + case T30_NULL: + case T30_EOP: + case T30_EOM: + case T30_EOS: + case T30_MPS: + s->next_rx_step = fcf2; + queue_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_F_DOC_ECM); + send_simple_frame(s, T30_ERR); + break; + default: + unexpected_final_frame(s, msg, len); + break; + } + break; + case T30_PPS: + process_rx_pps(s, msg, len); + break; + case T30_CTC: + /* T.30 says we change back to long training here */ + s->short_train = FALSE; + queue_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_F_DOC_ECM); + send_simple_frame(s, T30_CTR); + break; + case T30_RR: + break; + case T30_DCN: + s->current_status = T30_ERR_RX_DCNDATA; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + s->current_status = 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 + { + send_deferred_pps_response(s); + } + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_r(t30_state_t *s, const uint8_t *msg, int len) +{ + 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 */ + s->current_status = T30_ERR_TX_GOTDCN; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_t(t30_state_t *s, const uint8_t *msg, int len) +{ + uint8_t fcf; + + fcf = msg[2] & 0xFE; + switch (fcf) + { + case T30_DIS: + process_rx_dis_dtc(s, msg, len); + break; + case T30_DCN: + s->current_status = T30_ERR_RX_DCNWHY; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + s->current_status = T30_ERR_TX_NODIS; + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_i(t30_state_t *s, const uint8_t *msg, int len) +{ + 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) +{ + t4_stats_t stats; + uint8_t fcf; + + fcf = msg[2] & 0xFE; + switch (fcf) + { + case T30_MCF: + switch (s->next_tx_step) + { + case T30_MPS: + case T30_PRI_MPS: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, 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_EOM: + case T30_PRI_EOM: + case T30_EOS: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + t4_get_transfer_statistics(&s->t4, &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred); + } + return_to_phase_b(s, FALSE); + break; + case T30_EOP: + case T30_PRI_EOP: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + send_dcn(s); + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + t4_get_transfer_statistics(&s->t4, &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred); + } + break; + } + break; + case T30_RTP: +#if 0 + s->rtp_events++; +#endif + switch (s->next_tx_step) + { + case T30_MPS: + case T30_PRI_MPS: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, 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; + s->current_status = T30_ERR_CANNOT_TRAIN; + send_dcn(s); + break; + } + queue_phase(s, T30_PHASE_B_TX); + restart_sending_document(s); + break; + case T30_EOM: + case T30_PRI_EOM: + case T30_EOS: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + /* TODO: should go back to T, and resend */ + return_to_phase_b(s, TRUE); + break; + case T30_EOP: + case T30_PRI_EOP: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + send_dcn(s); + break; + } + break; + case T30_RTN: +#if 0 + s->rtn_events++; +#endif + switch (s->next_tx_step) + { + case T30_MPS: + case T30_PRI_MPS: + s->retries = 0; + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); +#if 0 + if (!s->retransmit_capable) +#endif + { + /* 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; + s->current_status = T30_ERR_CANNOT_TRAIN; + send_dcn(s); + break; + } + queue_phase(s, T30_PHASE_B_TX); + restart_sending_document(s); + break; + case T30_EOM: + case T30_PRI_EOM: + case T30_EOS: + s->retries = 0; + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); +#if 0 + if (s->retransmit_capable) + { + /* Wait for DIS */ + } + else +#endif + { + return_to_phase_b(s, TRUE); + } + break; + case T30_EOP: + case T30_PRI_EOP: + s->retries = 0; + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); +#if 0 + 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; + s->current_status = T30_ERR_CANNOT_TRAIN; + send_dcn(s); + break; + } + queue_phase(s, T30_PHASE_B_TX); + restart_sending_document(s); + } + else +#endif + { + send_dcn(s); + } + break; + } + break; + case T30_PIP: + s->retries = 0; + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + break; + case T30_PIN: + s->retries = 0; + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + break; + case T30_DCN: + switch (s->next_tx_step) + { + case T30_MPS: + case T30_PRI_MPS: + case T30_EOM: + case T30_PRI_EOM: + case T30_EOS: + /* Unexpected DCN after EOM, EOS or MPS sequence */ + s->current_status = T30_ERR_RX_DCNPHD; + break; + default: + s->current_status = T30_ERR_TX_BADPG; + break; + } + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + s->current_status = 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: + s->current_status = T30_ERR_RX_DCNNORTN; + disconnect(s); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_iv(t30_state_t *s, const uint8_t *msg, int len) +{ + 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) +{ + t4_stats_t stats; + 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_MPS: + case T30_PRI_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_EOM: + case T30_PRI_EOM: + case T30_EOS: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + t4_get_transfer_statistics(&s->t4, &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred); + } + return_to_phase_b(s, FALSE); + break; + case T30_EOP: + case T30_PRI_EOP: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + send_dcn(s); + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + t4_get_transfer_statistics(&s->t4, &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred); + } + break; + } + } + break; + case T30_PPR: + process_rx_ppr(s, msg, len); + break; + case T30_RNR: + if (s->timer_t5 == 0) + s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5); + queue_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_IV_PPS_RNR); + send_rr(s); + break; + case T30_DCN: + s->current_status = T30_ERR_TX_BADPG; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + s->current_status = T30_ERR_TX_ECMPHD; + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_iv_pps_q(t30_state_t *s, const uint8_t *msg, int len) +{ + t4_stats_t stats; + 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_MPS: + case T30_PRI_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_EOM: + case T30_PRI_EOM: + case T30_EOS: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + t4_get_transfer_statistics(&s->t4, &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred); + } + return_to_phase_b(s, FALSE); + break; + case T30_EOP: + case T30_PRI_EOP: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + send_dcn(s); + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + t4_get_transfer_statistics(&s->t4, &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred); + } + break; + } + } + break; + case T30_RNR: + if (s->timer_t5 == 0) + s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5); + queue_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_IV_PPS_RNR); + send_rr(s); + break; + case T30_PIP: + s->retries = 0; + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + break; + case T30_PIN: + s->retries = 0; + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + break; + case T30_PPR: + process_rx_ppr(s, msg, len); + break; + case T30_DCN: + s->current_status = T30_ERR_TX_BADPG; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + s->current_status = T30_ERR_TX_ECMPHD; + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_iv_pps_rnr(t30_state_t *s, const uint8_t *msg, int len) +{ + t4_stats_t stats; + 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_MPS: + case T30_PRI_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_EOM: + case T30_PRI_EOM: + case T30_EOS: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + t4_get_transfer_statistics(&s->t4, &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred); + } + return_to_phase_b(s, FALSE); + break; + case T30_EOP: + case T30_PRI_EOP: + tx_end_page(s); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf); + t4_tx_release(&s->t4); + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + send_dcn(s); + if (span_log_test(&s->logging, SPAN_LOG_FLOW)) + { + t4_get_transfer_statistics(&s->t4, &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Success - delivered %d pages\n", stats.pages_transferred); + } + break; + } + } + break; + case T30_RNR: + if (s->timer_t5 == 0) + s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5); + queue_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_IV_PPS_RNR); + send_rr(s); + break; + case T30_PIP: + s->retries = 0; + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + break; + case T30_PIN: + s->retries = 0; + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, fcf); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + break; + case T30_DCN: + s->current_status = T30_ERR_RX_DCNRRD; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_iv_ctc(t30_state_t *s, const uint8_t *msg, int len) +{ + 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_PIN: + 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); + } + break; + case T30_ERR: + /* TODO: Continue with the next message if MPS or EOM? */ + s->timer_t5 = 0; + send_dcn(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_iv_eor_rnr(t30_state_t *s, const uint8_t *msg, int len) +{ + 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_PIN: + 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); + } + break; + case T30_ERR: + /* TODO: Continue with the next message if MPS or EOM? */ + s->timer_t5 = 0; + send_dcn(s); + break; + case T30_DCN: + s->current_status = T30_ERR_RX_DCNRRD; + disconnect(s); + break; + case T30_CRP: + repeat_last_command(s); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_state_call_finished(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Simply ignore anything which comes in when we have declared the call + to have finished. */ +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_control_msg(t30_state_t *s, const uint8_t *msg, int len) +{ + /* We should only get good frames here. */ + print_frame(s, "Rx: ", msg, len); + if (s->real_time_frame_handler) + s->real_time_frame_handler(s, s->real_time_frame_user_data, TRUE, msg, len); + + if ((msg[1] & 0x10) == 0) + { + /* This is not a final frame */ + /* It seems we should not restart the command or response timer when exchanging HDLC image + data. If the modem looses sync in the middle of the image, we should just wait until + the carrier goes away before proceeding. */ + if (s->phase != T30_PHASE_C_ECM_RX) + { + /* Restart the command or response timer, T2 or T4 */ + switch (s->timer_t2_t4_is) + { + case TIMER_IS_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); + } + else + { + /* NSC - Non-standard facilities command */ + /* OK in (NSC) (CIG) DTC */ + } + break; + case (T30_PWD & 0xFE): + if (msg[2] == T30_PWD) + { + /* Password */ + /* OK in (SUB) (SID) (SEP) (PWD) (TSI) DCS */ + /* OK in (SUB) (SID) (SEP) (PWD) (CIG) DTC */ + decode_20digit_msg(s, s->rx_info.password, &msg[2], len - 2); + } + else + { + unexpected_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 */ + break; + case (T30_SUB & 0xFE): + /* Sub-address */ + /* OK in (PWD) (SUB) (TSI) DCS */ + decode_20digit_msg(s, s->rx_info.sub_address, &msg[2], len - 2); + break; + case (T30_SID & 0xFE): + /* Sender Identification */ + /* OK in (SUB) (SID) (SEP) (PWD) (TSI) DCS */ + /* OK in (SUB) (SID) (SEP) (PWD) (CIG) DTC */ + decode_20digit_msg(s, s->rx_info.sender_ident, &msg[2], len - 2); + break; + case (T30_CSA & 0xFE): + /* Calling subscriber internet address */ + decode_url_msg(s, NULL, &msg[2], len - 2); + break; + case (T30_TSA & 0xFE): + /* Transmitting subscriber internet address */ + decode_url_msg(s, NULL, &msg[2], len - 2); + break; + case (T30_IRA & 0xFE): + /* Internet routing address */ + decode_url_msg(s, NULL, &msg[2], len - 2); + break; + case T4_FCD: + process_rx_fcd(s, msg, len); + break; + case T4_RCP: + process_rx_rcp(s, msg, len); + break; + default: + unexpected_non_final_frame(s, msg, len); + break; + } + } + else + { + /* This is a final frame */ + /* Once we have any successful message from the far end, we + cancel timer T1 */ + s->timer_t0_t1 = 0; + + /* The following handles context sensitive message types, which should + occur at the end of message sequences. They should, therefore have + the final frame flag set. */ + span_log(&s->logging, SPAN_LOG_FLOW, "In state %d\n", s->state); + + switch (s->state) + { + case T30_STATE_ANSWERING: + process_state_answering(s, msg, len); + break; + case T30_STATE_B: + process_state_b(s, msg, len); + break; + case T30_STATE_C: + process_state_c(s, msg, len); + break; + case T30_STATE_D: + process_state_d(s, msg, len); + break; + case T30_STATE_D_TCF: + process_state_d_tcf(s, msg, len); + break; + case T30_STATE_D_POST_TCF: + process_state_d_post_tcf(s, msg, len); + break; + case T30_STATE_F_TCF: + process_state_f_tcf(s, msg, len); + break; + case T30_STATE_F_CFR: + process_state_f_cfr(s, msg, len); + break; + case T30_STATE_F_FTT: + process_state_f_ftt(s, msg, len); + break; + case T30_STATE_F_DOC_NON_ECM: + process_state_f_doc_non_ecm(s, msg, len); + break; + case T30_STATE_F_POST_DOC_NON_ECM: + process_state_f_post_doc_non_ecm(s, msg, len); + break; + case T30_STATE_F_DOC_ECM: + case T30_STATE_F_POST_DOC_ECM: + process_state_f_doc_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 */ + s->current_status = T30_ERR_TX_PHBDEAD; + break; + case T30_STATE_II_Q: + case T30_STATE_IV_PPS_NULL: + case T30_STATE_IV_PPS_Q: + /* No response after sending a page */ + s->current_status = T30_ERR_TX_PHDDEAD; + break; + default: + /* Disconnected after permitted retries */ + s->current_status = T30_ERR_RETRYDCN; + break; + } + send_dcn(s); + return; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Retry number %d\n", s->retries); + switch (s->state) + { + case T30_STATE_R: + s->dis_received = FALSE; + queue_phase(s, T30_PHASE_B_TX); + send_dis_or_dtc_sequence(s, TRUE); + break; + case T30_STATE_III_Q_MCF: + queue_phase(s, T30_PHASE_D_TX); + send_simple_frame(s, T30_MCF); + break; + case T30_STATE_III_Q_RTP: + queue_phase(s, T30_PHASE_D_TX); + send_simple_frame(s, T30_RTP); + break; + case T30_STATE_III_Q_RTN: + queue_phase(s, T30_PHASE_D_TX); + send_simple_frame(s, T30_RTN); + break; + case T30_STATE_II_Q: + queue_phase(s, T30_PHASE_D_TX); + send_simple_frame(s, s->next_tx_step); + break; + case T30_STATE_IV_PPS_NULL: + case T30_STATE_IV_PPS_Q: + queue_phase(s, T30_PHASE_D_TX); + send_pps_frame(s); + break; + case T30_STATE_IV_PPS_RNR: + case T30_STATE_IV_EOR_RNR: + queue_phase(s, T30_PHASE_D_TX); + send_rr(s); + break; + case T30_STATE_D: + queue_phase(s, T30_PHASE_B_TX); + send_dcs_sequence(s, TRUE); + break; + case T30_STATE_F_FTT: + queue_phase(s, T30_PHASE_B_TX); + send_simple_frame(s, T30_FTT); + break; + case T30_STATE_F_CFR: + queue_phase(s, T30_PHASE_B_TX); + send_cfr_sequence(s, TRUE); + break; + case T30_STATE_D_POST_TCF: + /* Need to send the whole training thing again */ + s->short_train = FALSE; + queue_phase(s, T30_PHASE_B_TX); + send_dcs_sequence(s, TRUE); + break; + case T30_STATE_F_POST_RCP_RNR: + /* Just ignore */ + break; + default: + span_log(&s->logging, + SPAN_LOG_FLOW, + "Repeat command called with nothing to repeat - phase %s, state %d\n", + phase_names[s->phase], + s->state); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t2_start(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "Start T2\n"); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2); + s->timer_t2_t4_is = TIMER_IS_T2; +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t2a_start(t30_state_t *s) +{ + /* 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); + s->current_status = T30_ERR_T0_EXPIRED; + /* Just end the call */ + disconnect(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t1_expired(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "T1 expired in state %d\n", s->state); + /* The initial connection establishment has timeout out. In other words, we + have been unable to communicate successfully with a remote machine. + It is time to abandon the call. */ + s->current_status = T30_ERR_T1_EXPIRED; + switch (s->state) + { + case T30_STATE_T: + /* Just end the call */ + disconnect(s); + break; + case T30_STATE_R: + /* Send disconnect, and then end the call. Since we have not + successfully contacted the far end, it is unclear why we should + send a disconnect message at this point. However, it is what T.30 + says we should do. */ + send_dcn(s); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t2_expired(t30_state_t *s) +{ + 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_EOM: + case T30_PRI_EOM: + case T30_EOS: + /* We didn't receive a response to our T30_MCF after T30_EOM, so we must be OK + to proceed to phase B, and pretty act like its the beginning of a call. */ + span_log(&s->logging, SPAN_LOG_FLOW, "Returning to phase B after %s\n", t30_frametype(s->next_rx_step)); + set_phase(s, T30_PHASE_B_TX); + timer_t2_start(s); + s->dis_received = FALSE; + send_dis_or_dtc_sequence(s, TRUE); + return; + } + break; + case T30_STATE_F_TCF: + span_log(&s->logging, SPAN_LOG_FLOW, "No TCF data received\n"); + set_phase(s, T30_PHASE_B_TX); + set_state(s, T30_STATE_F_FTT); + send_simple_frame(s, T30_FTT); + return; + case T30_STATE_F_DOC_ECM: + case T30_STATE_F_DOC_NON_ECM: + /* While waiting for FAX page */ + s->current_status = T30_ERR_RX_T2EXPFAX; + break; + case T30_STATE_F_POST_DOC_ECM: + case T30_STATE_F_POST_DOC_NON_ECM: + /* While waiting for next FAX page */ + s->current_status = T30_ERR_RX_T2EXPMPS; + break; +#if 0 + case ??????: + /* While waiting for DCN */ + s->current_status = T30_ERR_RX_T2EXPDCN; + break; + case ??????: + /* While waiting for phase D */ + s->current_status = T30_ERR_RX_T2EXPD; + break; +#endif + case T30_STATE_IV_PPS_RNR: + case T30_STATE_IV_EOR_RNR: + /* While waiting for RR command */ + s->current_status = T30_ERR_RX_T2EXPRR; + break; + case T30_STATE_R: + /* While waiting for NSS, DCS or MCF */ + s->current_status = T30_ERR_RX_T2EXP; + break; + case T30_STATE_F_FTT: + break; + } + queue_phase(s, T30_PHASE_B_TX); + start_receiving_document(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_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); + s->current_status = 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); + s->current_status = T30_ERR_HDLC_CARRIER; + disconnect(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t2b_expired(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "T2B expired in phase %s, state %d. The line is now quiet.\n", phase_names[s->phase], s->state); + timer_t2_expired(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t3_expired(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "T3 expired in phase %s, state %d\n", phase_names[s->phase], s->state); + s->current_status = T30_ERR_T3_EXPIRED; + disconnect(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t4_expired(t30_state_t *s) +{ + /* There was no response (or only a corrupt response) to a command, + within the T4 timeout period. */ + span_log(&s->logging, SPAN_LOG_FLOW, "T4 expired in phase %s, state %d\n", phase_names[s->phase], s->state); + /* Of course, things might just be a little late, especially if there are T.38 + links in the path. There is no point in simply timing out, and resending, + if we are currently receiving something from the far end - its a half-duplex + path, so the two transmissions will conflict. Our best strategy is to wait + until there is nothing being received, or give up after a long backstop timeout. + In the meantime, if we get a meaningful, if somewhat delayed, response, we + should accept it and carry on. */ + repeat_last_command(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t4a_expired(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "T4A expired in phase %s, state %d. An HDLC frame lasted too long.\n", phase_names[s->phase], s->state); + s->current_status = T30_ERR_HDLC_CARRIER; + disconnect(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t4b_expired(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "T4B expired in phase %s, state %d. The line is now quiet.\n", phase_names[s->phase], s->state); + timer_t4_expired(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t5_expired(t30_state_t *s) +{ + /* Give up waiting for the receiver to become ready in error correction mode */ + span_log(&s->logging, SPAN_LOG_FLOW, "T5 expired in phase %s, state %d\n", phase_names[s->phase], s->state); + s->current_status = T30_ERR_TX_T5EXP; +} +/*- End of function --------------------------------------------------------*/ + +static void decode_20digit_msg(t30_state_t *s, char *msg, const uint8_t *pkt, int len) +{ + int p; + int k; + char text[T30_MAX_IDENT_LEN + 1]; + + if (msg == NULL) + msg = text; + if (len > T30_MAX_IDENT_LEN + 1) + { + unexpected_frame_length(s, pkt, len); + msg[0] = '\0'; + return; + } + p = len; + /* Strip trailing spaces */ + while (p > 1 && pkt[p - 1] == ' ') + p--; + /* The string is actually backwards in the message */ + k = 0; + while (p > 1) + msg[k++] = pkt[--p]; + msg[k] = '\0'; + span_log(&s->logging, SPAN_LOG_FLOW, "Remote gave %s as: \"%s\"\n", t30_frametype(pkt[0]), msg); +} +/*- End of function --------------------------------------------------------*/ + +static void decode_url_msg(t30_state_t *s, char *msg, const uint8_t *pkt, int len) +{ + char text[77 + 1]; + + /* TODO: decode properly, as per T.30 5.3.6.2.12 */ + if (msg == NULL) + msg = text; + if (len < 3 || len > 77 + 3 || len != pkt[2] + 3) + { + unexpected_frame_length(s, pkt, len); + msg[0] = '\0'; + return; + } + /* First octet is the sequence number of the packet. + Bit 7 = 1 for more follows, 0 for last packet in the sequence. + Bits 6-0 = The sequence number, 0 to 0x7F + Second octet is the type of internet address. + Bits 7-4 = reserved + Bits 3-0 = type: + 0 = reserved + 1 = e-mail address + 2 = URL + 3 = TCP/IP V4 + 4 = TCP/IP V6 + 5 = international phone number, in the usual +... format + 6-15 = reserved + Third octet is the length of the internet address + Bit 7 = 1 for more follows, 0 for last packet in the sequence. + Bits 6-0 = length + */ + memcpy(msg, &pkt[3], len - 3); + msg[len - 3] = '\0'; + span_log(&s->logging, SPAN_LOG_FLOW, "Remote fax gave %s as: %d, %d, \"%s\"\n", t30_frametype(pkt[0]), pkt[0], pkt[1], msg); +} +/*- End of function --------------------------------------------------------*/ + +static void t30_non_ecm_rx_status(void *user_data, int status) +{ + t30_state_t *s; + int was_trained; + + s = (t30_state_t *) user_data; + span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM signal status is %s (%d) in state %d\n", signal_status_to_str(status), status, s->state); + switch (status) + { + case SIG_STATUS_TRAINING_IN_PROGRESS: + break; + case SIG_STATUS_TRAINING_FAILED: + s->rx_trained = FALSE; + break; + case SIG_STATUS_TRAINING_SUCCEEDED: + /* The modem is now trained */ + /* In case we are in trainability test mode... */ + s->tcf_test_bits = 0; + s->tcf_current_zeros = 0; + s->tcf_most_zeros = 0; + s->rx_signal_present = TRUE; + s->rx_trained = TRUE; + 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; + s->in_message = TRUE; + rx_start_page(s); + set_phase(s, T30_PHASE_B_TX); + set_state(s, T30_STATE_F_CFR); + send_cfr_sequence(s, TRUE); + } + } + break; + case T30_STATE_F_POST_DOC_NON_ECM: + /* Page ended cleanly */ + if (s->current_status == T30_ERR_RX_NOCARRIER) + s->current_status = T30_ERR_OK; + break; + default: + /* We should be receiving a document right now, but it did not end cleanly. */ + if (was_trained) + { + span_log(&s->logging, SPAN_LOG_WARNING, "Page did not end cleanly\n"); + /* We trained OK, so we should have some kind of received page, even though + it did not end cleanly. */ + set_state(s, T30_STATE_F_POST_DOC_NON_ECM); + set_phase(s, T30_PHASE_D_RX); + timer_t2_start(s); + if (s->current_status == T30_ERR_RX_NOCARRIER) + s->current_status = T30_ERR_OK; + } + else + { + span_log(&s->logging, SPAN_LOG_WARNING, "Non-ECM carrier not found\n"); + s->current_status = T30_ERR_RX_NOCARRIER; + } + break; + } + if (s->next_phase != T30_PHASE_IDLE) + { + set_phase(s, s->next_phase); + s->next_phase = T30_PHASE_IDLE; + } + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected non-ECM rx status - %d!\n", status); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE_NONSTD(void) t30_non_ecm_put_bit(void *user_data, int bit) +{ + t30_state_t *s; + + if (bit < 0) + { + t30_non_ecm_rx_status(user_data, bit); + return; + } + s = (t30_state_t *) user_data; + switch (s->state) + { + case T30_STATE_F_TCF: + /* Trainability test */ + s->tcf_test_bits++; + if (bit) + { + if (s->tcf_current_zeros > s->tcf_most_zeros) + s->tcf_most_zeros = s->tcf_current_zeros; + s->tcf_current_zeros = 0; + } + else + { + s->tcf_current_zeros++; + } + break; + case T30_STATE_F_DOC_NON_ECM: + /* Document transfer */ + if (t4_rx_put_bit(&s->t4, bit)) + { + /* That is the end of the document */ + set_state(s, T30_STATE_F_POST_DOC_NON_ECM); + queue_phase(s, T30_PHASE_D_RX); + timer_t2_start(s); + } + break; + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t30_non_ecm_put_byte(void *user_data, int byte) +{ + t30_state_t *s; + + s = (t30_state_t *) user_data; + switch (s->state) + { + case T30_STATE_F_TCF: + /* Trainability test */ + /* This makes counting zeros fast, but approximate. That really doesn't matter */ + s->tcf_test_bits += 8; + if (byte) + { + if (s->tcf_current_zeros > s->tcf_most_zeros) + s->tcf_most_zeros = s->tcf_current_zeros; + s->tcf_current_zeros = 0; + } + else + { + s->tcf_current_zeros += 8; + } + break; + case T30_STATE_F_DOC_NON_ECM: + /* Document transfer */ + if (t4_rx_put_byte(&s->t4, (uint8_t) byte)) + { + /* That is the end of the document */ + set_state(s, T30_STATE_F_POST_DOC_NON_ECM); + queue_phase(s, T30_PHASE_D_RX); + timer_t2_start(s); + } + break; + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t30_non_ecm_put_chunk(void *user_data, const uint8_t buf[], int len) +{ + t30_state_t *s; + int i; + + s = (t30_state_t *) user_data; + switch (s->state) + { + case T30_STATE_F_TCF: + /* Trainability test */ + /* This makes counting zeros fast, but approximate. That really doesn't matter */ + s->tcf_test_bits += 8*len; + for (i = 0; i < len; i++) + { + if (buf[i]) + { + if (s->tcf_current_zeros > s->tcf_most_zeros) + s->tcf_most_zeros = s->tcf_current_zeros; + s->tcf_current_zeros = 0; + } + else + { + s->tcf_current_zeros += 8; + } + } + break; + case T30_STATE_F_DOC_NON_ECM: + /* Document transfer */ + if (t4_rx_put_chunk(&s->t4, buf, len)) + { + /* That is the end of the document */ + set_state(s, T30_STATE_F_POST_DOC_NON_ECM); + queue_phase(s, T30_PHASE_D_RX); + timer_t2_start(s); + } + break; + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE_NONSTD(int) t30_non_ecm_get_bit(void *user_data) +{ + int bit; + t30_state_t *s; + + s = (t30_state_t *) user_data; + switch (s->state) + { + case T30_STATE_D_TCF: + /* Trainability test. */ + bit = 0; + if (s->tcf_test_bits-- < 0) + { + /* Finished sending training test. */ + bit = SIG_STATUS_END_OF_DATA; + } + break; + case T30_STATE_I: + /* Transferring real data. */ + bit = t4_tx_get_bit(&s->t4); + break; + case T30_STATE_D_POST_TCF: + case T30_STATE_II_Q: + /* We should be padding out a block of samples if we are here */ + bit = 0; + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "t30_non_ecm_get_bit in bad state %d\n", s->state); + bit = SIG_STATUS_END_OF_DATA; + break; + } + return bit; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t30_non_ecm_get_byte(void *user_data) +{ + int byte; + t30_state_t *s; + + s = (t30_state_t *) user_data; + switch (s->state) + { + case T30_STATE_D_TCF: + /* Trainability test. */ + byte = 0; + if ((s->tcf_test_bits -= 8) < 0) + { + /* Finished sending training test. */ + byte = 0x100; + } + break; + case T30_STATE_I: + /* Transferring real data. */ + byte = t4_tx_get_byte(&s->t4); + break; + case T30_STATE_D_POST_TCF: + case T30_STATE_II_Q: + /* We should be padding out a block of samples if we are here */ + byte = 0; + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "t30_non_ecm_get_byte in bad state %d\n", s->state); + byte = 0x100; + break; + } + return byte; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t30_non_ecm_get_chunk(void *user_data, uint8_t buf[], int max_len) +{ + int len; + t30_state_t *s; + + s = (t30_state_t *) user_data; + switch (s->state) + { + case T30_STATE_D_TCF: + /* Trainability test. */ + for (len = 0; len < max_len; len++) + { + buf[len] = 0; + if ((s->tcf_test_bits -= 8) < 0) + break; + } + break; + case T30_STATE_I: + /* Transferring real data. */ + len = t4_tx_get_chunk(&s->t4, buf, max_len); + break; + case T30_STATE_D_POST_TCF: + case T30_STATE_II_Q: + /* We should be padding out a block of samples if we are here */ + len = 0; + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "t30_non_ecm_get_chunk in bad state %d\n", s->state); + len = 0; + break; + } + return len; +} +/*- End of function --------------------------------------------------------*/ + +static void t30_hdlc_rx_status(void *user_data, int status) +{ + t30_state_t *s; + 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) + s->current_status = 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"); + s->current_status = 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_MPS: + case T30_PRI_MPS: + /* We should now start to get another page */ + if (s->error_correcting_mode) + { + set_state(s, T30_STATE_F_DOC_ECM); + queue_phase(s, T30_PHASE_C_ECM_RX); + } + else + { + set_state(s, T30_STATE_F_DOC_NON_ECM); + queue_phase(s, T30_PHASE_C_NON_ECM_RX); + } + timer_t2_start(s); + break; + case T30_EOM: + case T30_PRI_EOM: + case T30_EOS: + /* See if we get something back, before moving to phase B. */ + timer_t2_start(s); + set_phase(s, T30_PHASE_D_RX); + break; + case T30_EOP: + case T30_PRI_EOP: + /* Wait for a DCN. */ + set_phase(s, T30_PHASE_D_RX); + timer_t4_start(s); + break; + default: + span_log(&s->logging, SPAN_LOG_FLOW, "Unknown next rx step - %d\n", s->next_rx_step); + disconnect(s); + break; + } + } + break; + case T30_STATE_II_Q: + case T30_STATE_IV_PPS_NULL: + case T30_STATE_IV_PPS_Q: + case T30_STATE_IV_PPS_RNR: + case T30_STATE_IV_EOR_RNR: + case T30_STATE_F_POST_RCP_RNR: + case T30_STATE_IV_EOR: + case T30_STATE_IV_CTC: + if (s->step == 0) + { + /* Shut down HDLC transmission. */ + if (s->send_hdlc_handler) + s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0); + s->step++; + } + else + { + /* We have finished sending the post image message. Wait for an + acknowledgement. */ + set_phase(s, T30_PHASE_D_RX); + timer_t4_start(s); + } + break; + case T30_STATE_B: + /* We have now allowed time for the last message to flush through + the system, so it is safe to report the end of the call. */ + if (s->phase_e_handler) + s->phase_e_handler(s, s->phase_e_user_data, s->current_status); + set_state(s, T30_STATE_CALL_FINISHED); + set_phase(s, T30_PHASE_CALL_FINISHED); + release_resources(s); + break; + case T30_STATE_C: + if (s->step == 0) + { + /* Shut down HDLC transmission. */ + if (s->send_hdlc_handler) + s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0); + s->step++; + } + else + { + /* We just sent the disconnect message. Now it is time to disconnect. */ + disconnect(s); + } + break; + case T30_STATE_D: + if (send_dcs_sequence(s, FALSE)) + { + if ((s->iaf & T30_IAF_MODE_NO_TCF)) + { + /* Skip the trainability test */ + s->retries = 0; + s->short_train = TRUE; + if (s->error_correcting_mode) + { + set_state(s, T30_STATE_IV); + queue_phase(s, T30_PHASE_C_ECM_TX); + } + else + { + set_state(s, T30_STATE_I); + queue_phase(s, T30_PHASE_C_NON_ECM_TX); + } + } + else + { + /* Do the trainability test */ + /* TCF is always sent with long training */ + s->short_train = FALSE; + set_state(s, T30_STATE_D_TCF); + set_phase(s, T30_PHASE_C_NON_ECM_TX); + } + } + break; + case T30_STATE_D_TCF: + /* Finished sending training test. Listen for the response. */ + set_phase(s, T30_PHASE_B_RX); + timer_t4_start(s); + set_state(s, T30_STATE_D_POST_TCF); + break; + case T30_STATE_I: + /* Send the end of page message */ + set_phase(s, T30_PHASE_D_TX); + set_state(s, T30_STATE_II_Q); + /* We might need to resend the page we are on, but we need to check if there + are any more pages to send, so we can send the correct signal right now. */ + send_simple_frame(s, s->next_tx_step = check_next_tx_step(s)); + break; + case T30_STATE_IV: + /* We have finished sending an FCD frame */ + if (s->step == 0) + { + if (send_next_ecm_frame(s)) + { + /* Shut down HDLC transmission. */ + if (s->send_hdlc_handler) + s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0); + s->step++; + } + } + else + { + /* Send the end of page or partial page message */ + set_phase(s, T30_PHASE_D_TX); + s->next_tx_step = check_next_tx_step(s); + if (send_pps_frame(s) == T30_NULL) + set_state(s, T30_STATE_IV_PPS_NULL); + else + set_state(s, T30_STATE_IV_PPS_Q); + } + break; + case T30_STATE_F_DOC_ECM: + /* This should be the end of a CTR being sent. */ + if (s->step == 0) + { + /* Shut down HDLC transmission. */ + if (s->send_hdlc_handler) + s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0); + s->step++; + } + else + { + /* We have finished sending the CTR. Wait for image data again. */ + queue_phase(s, T30_PHASE_C_ECM_RX); + timer_t2_start(s); + } + break; + case T30_STATE_CALL_FINISHED: + /* Just ignore anything that happens now. We might get here if a premature + disconnect from the far end overlaps something. */ + break; + default: + span_log(&s->logging, SPAN_LOG_FLOW, "Bad state for send complete in t30_front_end_status - %d\n", s->state); + break; + } + break; + case T30_FRONT_END_RECEIVE_COMPLETE: + span_log(&s->logging, SPAN_LOG_FLOW, "Receive complete in phase %s, state %d\n", phase_names[s->phase], s->state); + /* Usually receive complete is notified by a carrier down signal. However, + in cases like a T.38 packet stream dying in the middle of reception + there needs to be a means to stop things. */ + switch (s->phase) + { + case T30_PHASE_C_NON_ECM_RX: + t30_non_ecm_rx_status(s, SIG_STATUS_CARRIER_DOWN); + break; + default: + t30_hdlc_rx_status(s, SIG_STATUS_CARRIER_DOWN); + break; + } + break; + case T30_FRONT_END_SIGNAL_PRESENT: + span_log(&s->logging, SPAN_LOG_FLOW, "A signal is present\n"); + /* The front end is explicitly telling us the signal we expect is present. This might + be a premature indication from a T.38 implementation, but we have to believe it. + if we don't we can time out improperly. For example, we might get an image modem + carrier signal, but the first HDLC frame might only occur several seconds later. + Many ECM senders idle on HDLC flags while waiting for the paper or filing system + to become ready. T.38 offers no specific indication of correct carrier training, so + if we don't kill the timer on the initial carrier starting signal, we will surely + time out quite often before the next thing we receive. */ + switch (s->phase) + { + case T30_PHASE_A_CED: + case T30_PHASE_A_CNG: + case T30_PHASE_B_RX: + case T30_PHASE_D_RX: + /* We are running a V.21 receive modem, where an explicit training indication + will not occur. */ + t30_hdlc_rx_status(s, SIG_STATUS_CARRIER_UP); + t30_hdlc_rx_status(s, SIG_STATUS_FRAMING_OK); + break; + default: + /* Cancel any receive timeout, and declare that a receive signal is present, + since the front end is explicitly telling us we have seen something. */ + s->rx_signal_present = TRUE; + 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. */ + s->current_status = T30_ERR_CALLDROPPED; + break; + } + if (s->phase_e_handler) + s->phase_e_handler(s, s->phase_e_user_data, s->current_status); + set_state(s, T30_STATE_CALL_FINISHED); + set_phase(s, T30_PHASE_CALL_FINISHED); + release_resources(s); + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t30_get_transfer_statistics(t30_state_t *s, t30_stats_t *t) +{ + t4_stats_t stats; + + t->bit_rate = fallback_sequence[s->current_fallback].bit_rate; + t->error_correcting_mode = s->error_correcting_mode; + t->error_correcting_mode_retries = s->error_correcting_mode_retries; + t4_get_transfer_statistics(&s->t4, &stats); + t->pages_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; +#if 0 + t->rtn_events = s->rtn_events; + t->rtp_events = s->rtp_events; +#endif +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t30_local_interrupt_request(t30_state_t *s, int state) +{ + if (s->timer_t3 > 0) + { + /* Accept the far end's outstanding request for interrupt. */ + /* TODO: */ + send_simple_frame(s, (state) ? T30_PIP : T30_PIN); + } + s->local_interrupt_pending = state; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t30_restart(t30_state_t *s) +{ + s->phase = T30_PHASE_IDLE; + s->next_phase = T30_PHASE_IDLE; + s->current_fallback = 0; + s->rx_signal_present = FALSE; + s->rx_trained = FALSE; + s->rx_frame_received = FALSE; + s->current_status = T30_ERR_OK; + s->ppr_count = 0; + s->ecm_progress = 0; + s->receiver_not_ready_count = 0; + s->far_dis_dtc_len = 0; + memset(&s->far_dis_dtc_frame, 0, sizeof(s->far_dis_dtc_frame)); + t30_build_dis_or_dtc(s); + 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; +#if 0 + s->rtn_events = 0; + s->rtp_events = 0; +#endif + 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. */ + switch (s->operation_in_progress) + { + case OPERATION_IN_PROGRESS_T4_TX: + t4_tx_release(&s->t4); + break; + case OPERATION_IN_PROGRESS_T4_RX: + t4_rx_release(&s->t4); + break; + } + s->operation_in_progress = OPERATION_IN_PROGRESS_NONE; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t30_free(t30_state_t *s) +{ + t30_release(s); + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t30_call_active(t30_state_t *s) +{ + return (s->phase != T30_PHASE_CALL_FINISHED); +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/