Mercurial > hg > audiostuff
diff spandsp-0.0.3/spandsp-0.0.3/src/t30.c @ 5:f762bf195c4b
import spandsp-0.0.3
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 16:00:21 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.3/spandsp-0.0.3/src/t30.c Fri Jun 25 16:00:21 2010 +0200 @@ -0,0 +1,4875 @@ +/* + * 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 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU 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.149 2006/11/30 15:41:47 steveu Exp $ + */ + +/*! \file */ + +#ifdef 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 <tiffio.h> + +#include "spandsp/telephony.h" +#include "spandsp/logging.h" +#include "spandsp/bit_operations.h" +#include "spandsp/queue.h" +#include "spandsp/power_meter.h" +#include "spandsp/complex.h" +#include "spandsp/tone_generate.h" +#include "spandsp/async.h" +#include "spandsp/hdlc.h" +#include "spandsp/fsk.h" +#include "spandsp/v29rx.h" +#include "spandsp/v29tx.h" +#include "spandsp/v27ter_rx.h" +#include "spandsp/v27ter_tx.h" +#include "spandsp/t4.h" + +#include "spandsp/t30_fcf.h" +#include "spandsp/t35.h" +#include "spandsp/t30.h" + +#define MAX_MESSAGE_TRIES 3 + +#define ms_to_samples(t) (((t)*SAMPLE_RATE)/1000) + +typedef struct +{ + int val; + const char *str; +} value_string_t; + +/* 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, + T30_STATE_F_POST_DOC_NON_ECM, + T30_STATE_F_POST_DOC_ECM, + T30_STATE_F_POST_RCP_MCF, + T30_STATE_F_POST_RCP_PPR, + 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_MODE_SEND_DOC = 1, + T30_MODE_RECEIVE_DOC +}; + +enum +{ + T30_COPY_QUALITY_GOOD = 0, + T30_COPY_QUALITY_POOR, + T30_COPY_QUALITY_BAD +}; + +#define DISBIT1 0x01 +#define DISBIT2 0x02 +#define DISBIT3 0x04 +#define DISBIT4 0x08 +#define DISBIT5 0x10 +#define DISBIT6 0x20 +#define DISBIT7 0x40 +#define DISBIT8 0x80 + +/* All timers specified in milliseconds */ + +/* Time-out T0 defines the amount of time an automatic calling terminal waits for the called terminal +to answer the call. +T0 begins after the dialling of the number is completed and is reset: +a) when T0 times out; or +b) when timer T1 is started; or +c) if the terminal is capable of detecting any condition which indicates that the call will not be + successful, when such a condition is detected. +The recommended value of T0 is 60+-5s; however, when it is anticipated that a long call set-up +time may be encountered, an alternative value of up to 120s may be used. +NOTE - National regulations may require the use of other values for T0. */ +#define DEFAULT_TIMER_T0 60000 + +/* Time-out T1 defines the amount of time two terminals will continue to attempt to identify each +other. T1 is 35+-5s, begins upon entering phase B, and is reset upon detecting a valid signal or +when T1 times out. +For operating methods 3 and 4 (see 3.1), the calling terminal starts time-out T1 upon reception of +the V.21 modulation scheme. +For operating method 4 bis a (see 3.1), the calling terminal starts time-out T1 upon starting +transmission using the V.21 modulation scheme. */ +#define DEFAULT_TIMER_T1 35000 + +/* Time-out T2 makes use of the tight control between commands and responses to detect the loss of +command/response synchronization. T2 is 6+-1s and begins when initiating a command search +(e.g., the first entrance into the "command received" subroutine, reference flow diagram in 5.2). +T2 is reset when an HDLC flag is received or when T2 times out. */ +#define DEFAULT_TIMER_T2 7000 + +/* 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 + +/* 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 + +/* 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 + +#define DEFAULT_TIMER_T6 5000 + +#define DEFAULT_TIMER_T7 6000 + +#define DEFAULT_TIMER_T8 10000 + +/* Exact widths in PELs for the difference resolutions, and page widths: + R4 864 pels/215mm for ISO A4, North American Letter and Legal + R4 1024 pels/255mm for ISO B4 + R4 1216 pels/303mm for ISO A3 + R8 1728 pels/215mm for ISO A4, North American Letter and Legal + R8 2048 pels/255mm for ISO B4 + R8 2432 pels/303mm for ISO A3 + R16 3456 pels/215mm for ISO A4, North American Letter and Legal + R16 4096 pels/255mm for ISO B4 + R16 4864 pels/303mm for ISO A3 +*/ + +#define T30_V17_FALLBACK_START 0 +#define T30_V29_FALLBACK_START 3 +#define T30_V27TER_FALLBACK_START 6 + +static const struct +{ + int bit_rate; + int modem_type; + uint8_t dcs_code; +} fallback_sequence[] = +{ + {14400, T30_MODEM_V17_14400, DISBIT6}, + {12000, T30_MODEM_V17_12000, (DISBIT6 | DISBIT4)}, + { 9600, T30_MODEM_V17_9600, (DISBIT6 | DISBIT3)}, + { 9600, T30_MODEM_V29_9600, DISBIT3}, + { 7200, T30_MODEM_V17_7200, (DISBIT6 | DISBIT4 | DISBIT3)}, + { 7200, T30_MODEM_V29_7200, (DISBIT4 | DISBIT3)}, + { 4800, T30_MODEM_V27TER_4800, DISBIT4}, + { 2400, T30_MODEM_V27TER_2400, 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 void 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->far_sub_address); + t4_rx_set_far_ident(&(s->t4), s->far_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_y_resolution(&(s->t4), s->y_resolution); + + t4_rx_start_page(&(s->t4)); + /* Clear the buffer */ + for (i = 0; i < 256; i++) + s->ecm_len[i] = -1; + s->ecm_frames = -1; + s->ecm_page++; + s->ecm_block = 0; +} +/*- End of function --------------------------------------------------------*/ + +static int copy_quality(t30_state_t *s) +{ + t4_stats_t 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, so retrain. */ + t4_get_transfer_statistics(&(s->t4), &stats); + span_log(&s->logging, SPAN_LOG_FLOW, "Pages = %d\n", stats.pages_transferred); + span_log(&s->logging, SPAN_LOG_FLOW, "Image size = %dx%d\n", stats.width, stats.length); + span_log(&s->logging, SPAN_LOG_FLOW, "Image resolution = %dx%d\n", stats.x_resolution, stats.y_resolution); + span_log(&s->logging, SPAN_LOG_FLOW, "Bad rows = %d\n", stats.bad_rows); + span_log(&s->logging, SPAN_LOG_FLOW, "Longest bad row run = %d\n", stats.longest_bad_row_run); + if (stats.bad_rows*50 < stats.length) + return T30_COPY_QUALITY_GOOD; + if (stats.bad_rows*20 < stats.length) + return T30_COPY_QUALITY_POOR; + return T30_COPY_QUALITY_BAD; +} +/*- End of function --------------------------------------------------------*/ + +const char *t30_completion_code_to_str(int result) +{ + switch (result) + { + case T30_ERR_OK: + return "OK"; + case T30_ERR_CEDTONE: + return "The CED tone exceeded 5s"; + case T30_ERR_T0EXPIRED: + return "Timed out waiting for initial communication"; + case T30_ERR_T1EXPIRED: + return "Timed out waiting for the first message"; + case T30_ERR_T3EXPIRED: + return "Timed out waiting for procedural interrupt"; + case T30_ERR_HDLCCARR: + return "The HDLC carrier did not stop in a timely manner"; + case T30_ERR_CANNOTTRAIN: + return "Failed to train with any of the compatible modems"; + case T30_ERR_OPERINTFAIL: + return "Operator intervention failed"; + case T30_ERR_INCOMPATIBLE: + return "Far end is not compatible"; + case T30_ERR_NOTRXCAPABLE: + return "Far end is not receive capable"; + case T30_ERR_NOTTXCAPABLE: + return "Far end is not transmit capable"; + case T30_ERR_UNEXPECTED: + return "Unexpected message received"; + case T30_ERR_NORESSUPPORT: + return "Far end cannot receive at the resolution of the image"; + case T30_ERR_NOSIZESUPPORT: + return "Far end cannot receive at the size of image"; + case T30_ERR_FILEERROR: + return "TIFF/F file cannot be opened"; + case T30_ERR_NOPAGE: + return "TIFF/F page not found"; + case T30_ERR_BADTIFF: + return "TIFF/F format is not compatible"; + case T30_ERR_UNSUPPORTED: + return "Unsupported feature"; + case T30_ERR_BADDCSTX: + return "Received bad response to DCS or training"; + case T30_ERR_BADPGTX: + return "Received a DCN from remote after sending a page"; + case T30_ERR_ECMPHDTX: + return "Invalid ECM response received from receiver"; + case T30_ERR_ECMRNRTX: + return "Timer T5 expired, receiver not ready"; + case T30_ERR_GOTDCNTX: + return "Received a DCN while waiting for a DIS"; + case T30_ERR_INVALRSPTX: + return "Invalid response after sending a page"; + case T30_ERR_NODISTX: + return "Received other than DIS while waiting for DIS"; + case T30_ERR_NXTCMDTX: + return "Timed out waiting for next send_page command from driver"; + case T30_ERR_PHBDEADTX: + return "Received no response to DCS, training or TCF"; + case T30_ERR_PHDDEADTX: + return "No response after sending a page"; + case T30_ERR_ECMPHDRX: + return "Invalid ECM response received from transmitter"; + case T30_ERR_GOTDCSRX: + return "DCS received while waiting for DTC"; + case T30_ERR_INVALCMDRX: + return "Unexpected command after page received"; + case T30_ERR_NOCARRIERRX: + return "Carrier lost during fax receive"; + case T30_ERR_NOEOLRX: + return "Timed out while waiting for EOL (end Of line)"; + case T30_ERR_NOFAXRX: + return "Timed out while waiting for first line"; + case T30_ERR_NXTCMDRX: + return "Timed out waiting for next receive page command"; + case T30_ERR_T2EXPDCNRX: + return "Timer T2 expired while waiting for DCN"; + case T30_ERR_T2EXPDRX: + return "Timer T2 expired while waiting for phase D"; + case T30_ERR_T2EXPFAXRX: + return "Timer T2 expired while waiting for fax page"; + case T30_ERR_T2EXPMPSRX: + return "Timer T2 expired while waiting for next fax page"; + case T30_ERR_T2EXPRRRX: + return "Timer T2 expired while waiting for RR command"; + case T30_ERR_T2EXPRX: + return "Timer T2 expired while waiting for NSS, DCS or MCF"; + case T30_ERR_DCNWHYRX: + return "Unexpected DCN while waiting for DCS or DIS"; + case T30_ERR_DCNDATARX: + return "Unexpected DCN while waiting for image data"; + case T30_ERR_DCNFAXRX: + return "Unexpected DCN while waiting for EOM, EOP or MPS"; + case T30_ERR_DCNPHDRX: + return "Unexpected DCN after EOM or MPS sequence"; + case T30_ERR_DCNRRDRX: + return "Unexpected DCN after RR/RNR sequence"; + case T30_ERR_DCNNORTNRX: + return "Unexpected DCN after requested retransmission"; + case T30_ERR_BADPAGE: + return "TIFF/F page number tag missing"; + case T30_ERR_BADTAG: + return "Incorrect values for TIFF/F tags"; + case T30_ERR_BADTIFFHDR: + return "Bad TIFF/F header - incorrect values in fields"; + case T30_ERR_BADPARM: + return "Invalid value for fax parameter"; + case T30_ERR_BADSTATE: + return "Invalid initial state value specified"; + case T30_ERR_CMDDATA: + return "Last command contained invalid data"; + case T30_ERR_DISCONNECT: + return "Fax call disconnected by the other station"; + case T30_ERR_INVALARG: + return "Illegal argument to function"; + case T30_ERR_INVALFUNC: + return "Illegal call to function"; + case T30_ERR_NODATA: + return "Data requested is not available (NSF, DIS, DCS)"; + case T30_ERR_NOMEM: + return "Cannot allocate memory for more pages"; + case T30_ERR_NOPOLL: + return "Poll not accepted"; + case T30_ERR_NOSTATE: + return "Initial state value not set"; + case T30_ERR_RETRYDCN: + return "Disconnected after permitted retries"; + case T30_ERR_CALLDROPPED: + return "The call dropped prematurely"; + } + return "???"; +} +/*- End of function --------------------------------------------------------*/ + +void t30_non_ecm_put_bit(void *user_data, int bit) +{ + t30_state_t *s; + int was_trained; + + s = (t30_state_t *) user_data; + if (bit < 0) + { + /* Special conditions */ + switch (bit) + { + case PUTBIT_TRAINING_FAILED: + span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM carrier training failed in state %d\n", s->state); + s->rx_trained = FALSE; + /* Cancel the timer, since we have actually seen something, and wait until the carrier drops + before proceeding. */ + // TODO: this is not a complete answer to handling failures to train + s->timer_t2_t4 = 0; + break; + case PUTBIT_TRAINING_SUCCEEDED: + /* The modem is now trained */ + span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM carrier trained in state %d\n", s->state); + /* In case we are in trainability test mode... */ + /* A FAX machine is supposed to send 1.5s of training test + data, but some send a little bit less. Lets just check + the first 1s, and be safe. */ + s->training_current_zeros = 0; + s->training_most_zeros = 0; + s->rx_signal_present = TRUE; + s->rx_trained = TRUE; + s->timer_t2_t4 = 0; + break; + case PUTBIT_CARRIER_UP: + span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM carrier up in state %d\n", s->state); + break; + case PUTBIT_CARRIER_DOWN: + span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM carrier down in state %d\n", s->state); + 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->training_current_zeros > s->training_most_zeros) + s->training_most_zeros = s->training_current_zeros; + if (s->training_most_zeros < fallback_sequence[s->current_fallback].bit_rate) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Trainability test failed - longest run of zeros was %d\n", s->training_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_simple_frame(s, T30_CFR); + } + } + break; + case T30_STATE_F_POST_DOC_NON_ECM: + /* Page ended cleanly */ + if (s->current_status == T30_ERR_NOCARRIERRX) + 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); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2); + s->timer_is_t4 = FALSE; + if (s->current_status == T30_ERR_NOCARRIERRX) + 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_NOCARRIERRX; + } + 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 special bit - %d!\n", bit); + break; + } + return; + } + switch (s->state) + { + case T30_STATE_F_TCF: + /* Trainability test */ + if (bit) + { + if (s->training_current_zeros > s->training_most_zeros) + s->training_most_zeros = s->training_current_zeros; + s->training_current_zeros = 0; + } + else + { + s->training_current_zeros++; + } + break; + case T30_STATE_F_DOC: + /* 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); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2); + s->timer_is_t4 = FALSE; + } + break; + } +} +/*- End of function --------------------------------------------------------*/ + +void t30_non_ecm_putbyte(void *user_data, int byte) +{ + t30_state_t *s; + int i; + + s = (t30_state_t *) user_data; + switch (s->state) + { + case T30_STATE_F_TCF: + /* Trainability test */ + if (byte == 0) + { + s->training_current_zeros += 8; + } + else + { + for (i = 7; i >= 0; i--) + { + if (((byte >> i) & 1)) + { + if (s->training_current_zeros > s->training_most_zeros) + s->training_most_zeros = s->training_current_zeros; + s->training_current_zeros = 0; + } + else + { + s->training_current_zeros++; + } + } + } + break; + case T30_STATE_F_DOC: + /* Document transfer */ + for (i = 7; i >= 0; i--) + { + if (t4_rx_put_bit(&(s->t4), (byte >> i) & 1)) + { + /* That is the end of the document */ + set_state(s, T30_STATE_F_POST_DOC_NON_ECM); + queue_phase(s, T30_PHASE_D_RX); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2); + s->timer_is_t4 = FALSE; + } + } + break; + } +} +/*- End of function --------------------------------------------------------*/ + +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->training_test_bits-- < 0) + { + /* Finished sending training test. */ + bit = PUTBIT_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 = 2; + break; + } + return bit; +} +/*- End of function --------------------------------------------------------*/ + +static int check_next_tx_step(t30_state_t *s) +{ + int more; + + if (t4_tx_more_pages(&(s->t4)) == 0) + return (s->local_interrupt_pending) ? T30_PRI_MPS : T30_MPS; + /* Call a user handler, if one is set, to check if another document is to be sent. + If so, we send an EOM, rather than an EOP. Then we will renegotiate, and the new + document will begin. */ + if (s->document_handler) + more = s->document_handler(s, s->document_user_data, 0); + else + more = FALSE; + if (more) + 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 j; + int k; + int bit; + uint8_t octet; + + s->ppr_count = 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 = 0; i < 256; i++) + s->ecm_len[i] = -1; + for (i = 3; i < 32 + 3; i++) + s->ecm_frame_map[i] = 0xFF; + for (i = 0; i < 256; i++) + { + s->ecm_data[i][0] = 0xFF; + s->ecm_data[i][1] = 0x03; + s->ecm_data[i][2] = T4_FCD; + /* These frames contain a frame sequence number within the partial page (one octet) followed + by some image data. */ + s->ecm_data[i][3] = i; + for (j = 4; j < s->octets_per_ecm_frame + 4; j++) + { + octet = 0; + for (k = 0; k < 8; k++) + { + if (((bit = t4_tx_get_bit(&(s->t4)))) == PUTBIT_END_OF_DATA) + { + if (k > 0) + s->ecm_data[i][j++] = (uint8_t) (octet >> (7 - k)); + if (j > 0) + { + memset(&s->ecm_data[i][j], 0, s->octets_per_ecm_frame + 4 - j); + s->ecm_len[i++] = (int16_t) (s->octets_per_ecm_frame + 4); + } + /* The image is not big enough to fill the entire buffer */ + /* We need to pad to a full frame, as most receivers expect + that. */ + 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; + } + octet = (uint8_t) ((octet >> 1) | ((bit & 1) << 7)); + } + s->ecm_data[i][j] = octet; + } + s->ecm_len[i] = (int16_t) j; + } + /* 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 j; + int k; + int bit; + + span_log(&s->logging, SPAN_LOG_FLOW, "Commiting partial page - %d frames\n", s->ecm_frames); + for (i = 0; i < s->ecm_frames; i++) + { + for (j = 0; j < s->ecm_len[i]; j++) + { + for (k = 0; k < 8; k++) + { + bit = (s->ecm_data[i][j] >> k) & 1; + if (t4_rx_put_bit(&(s->t4), bit)) + { + /* That is the end of the document */ + /* Clear the buffer */ + for (i = 0; i < 256; i++) + s->ecm_len[i] = -1; + s->ecm_frames = -1; + return -1; + } + } + } + } + /* Clear the buffer */ + for (i = 0; i < 256; i++) + s->ecm_len[i] = -1; + s->ecm_frames = -1; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int send_next_ecm_frame(t30_state_t *s) +{ + int i; + uint8_t frame[3]; + + if (s->ecm_current_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_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_frame = i + 1; + return 0; + } + } + } + if (s->ecm_current_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_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. */ + frame[0] = 0xFF; + frame[1] = 0x03; + frame[2] = (uint8_t) (T4_RCP | s->dis_received); + send_frame(s, frame, 3); + return 0; + } + return -1; +} +/*- End of function --------------------------------------------------------*/ + +static void print_frame(t30_state_t *s, const char *io, const uint8_t *fr, int frlen) +{ + span_log(&s->logging, + SPAN_LOG_FLOW, + "%s %s with%s final frame tag\n", + io, + t30_frametype(fr[2]), + (fr[1] & 0x10) ? "" : "out"); + span_log_buf(&s->logging, SPAN_LOG_FLOW, io, fr, frlen); +} +/*- End of function --------------------------------------------------------*/ + +static void send_frame(t30_state_t *s, const uint8_t *fr, int frlen) +{ + print_frame(s, "Tx: ", fr, frlen); + + if (s->send_hdlc_handler) + s->send_hdlc_handler(s->send_hdlc_user_data, fr, frlen); +} +/*- End of function --------------------------------------------------------*/ + +static void send_simple_frame(t30_state_t *s, int type) +{ + uint8_t frame[3]; + + /* The simple command/response frames are always final frames */ + frame[0] = 0xFF; + frame[1] = 0x13; + frame[2] = (uint8_t) (type | s->dis_received); + send_frame(s, frame, 3); +} +/*- End of function --------------------------------------------------------*/ + +static void send_20digit_msg_frame(t30_state_t *s, int cmd, char *msg) +{ + size_t len; + int p; + uint8_t frame[23]; + + len = strlen(msg); + p = 0; + frame[p++] = 0xFF; + frame[p++] = 0x03; + frame[p++] = (uint8_t) (cmd | s->dis_received); + while (len > 0) + frame[p++] = msg[--len]; + while (p < 23) + frame[p++] = ' '; + send_frame(s, frame, 23); +} +/*- End of function --------------------------------------------------------*/ + +static int send_ident_frame(t30_state_t *s, uint8_t cmd) +{ + /* Only send if there is an ident to send. */ + if (s->local_ident[0]) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Sending ident '%s'\n", s->local_ident); + /* 'cmd' should be T30_TSI, T30_CIG or T30_CSI */ + send_20digit_msg_frame(s, cmd, s->local_ident); + return TRUE; + } + return FALSE; +} +/*- End of function --------------------------------------------------------*/ + +static int send_pw_frame(t30_state_t *s) +{ + /* Only send if there is a password to send. */ + if (s->local_password[0]) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Sending password '%s'\n", s->local_password); + send_20digit_msg_frame(s, T30_PWD, s->local_password); + return TRUE; + } + 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 (s->local_sub_address[0]) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Sending sub address '%s'\n", s->local_sub_address); + send_20digit_msg_frame(s, T30_SUB, s->local_sub_address); + return TRUE; + } + return FALSE; +} +/*- End of function --------------------------------------------------------*/ + +static int send_nsf_frame(t30_state_t *s) +{ + int p; + uint8_t frame[100 + 3]; + + /* Only send if there is an NSF message to send. */ + if (s->local_nsf_len) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Sending user supplied NSF - %d octets\n", s->local_nsf_len); + p = 0; + frame[p++] = 0xFF; + frame[p++] = 0x03; + frame[p++] = (uint8_t) (T30_NSF | s->dis_received); + for ( ; p < s->local_nsf_len + 3; p++) + frame[p] = s->local_nsf[p - 3]; + send_frame(s, frame, s->local_nsf_len + 3); + return TRUE; + } + return FALSE; +} +/*- End of function --------------------------------------------------------*/ + +static int send_pps_frame(t30_state_t *s) +{ + uint8_t frame[100 + 3]; + + frame[0] = 0xFF; + frame[1] = 0x13; + frame[2] = T30_PPS; + frame[3] = (s->ecm_at_page_end) ? ((uint8_t) (s->next_tx_step | s->dis_received)) : T30_NULL; + frame[4] = (uint8_t) (s->ecm_page & 0xFF); + frame[5] = (uint8_t) (s->ecm_block & 0xFF); + frame[6] = (uint8_t) (s->ecm_frames - 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->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]) + s->dis_dtc_frame[4] |= DISBIT2; + else + s->dis_dtc_frame[4] &= ~DISBIT2; + /* If we have a file name to transmit, then we are ready to transmit (polling) */ + if (s->tx_file[0]) + s->dis_dtc_frame[4] |= DISBIT1; + else + s->dis_dtc_frame[4] &= ~DISBIT1; + t30_decode_dis_dtc_dcs(s, s->dis_dtc_frame, s->dis_dtc_len); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int 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->dis_dtc_frame[0] = 0xFF; + s->dis_dtc_frame[1] = 0x13; + s->dis_dtc_frame[2] = (uint8_t) (T30_DIS | s->dis_received); + s->dis_dtc_frame[3] = 0x00; + s->dis_dtc_frame[4] = 0x00; + for (i = 5; i < 18; i++) + s->dis_dtc_frame[i] = DISBIT8; + s->dis_dtc_frame[18] = 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)) + s->dis_dtc_frame[3] |= DISBIT1; + if ((s->iaf & T30_IAF_MODE_T38)) + s->dis_dtc_frame[3] |= DISBIT3; + /* 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)) + s->dis_dtc_frame[4] |= DISBIT4; + if ((s->supported_modems & T30_SUPPORT_V29)) + s->dis_dtc_frame[4] |= DISBIT3; + /* 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->dis_dtc_frame[4] |= (DISBIT6 | DISBIT4 | DISBIT3); + if ((s->supported_resolutions & T30_SUPPORT_FINE_RESOLUTION)) + s->dis_dtc_frame[4] |= DISBIT7; + if ((s->supported_compressions & T30_SUPPORT_T4_2D_COMPRESSION)) + s->dis_dtc_frame[4] |= DISBIT8; + /* 215mm wide is always supported */ + if ((s->supported_image_sizes & T30_SUPPORT_303MM_WIDTH)) + s->dis_dtc_frame[5] |= DISBIT2; + else if ((s->supported_image_sizes & T30_SUPPORT_255MM_WIDTH)) + s->dis_dtc_frame[5] |= DISBIT1; + /* A4 is always supported. */ + if ((s->supported_image_sizes & T30_SUPPORT_UNLIMITED_LENGTH)) + s->dis_dtc_frame[5] |= DISBIT4; + else if ((s->supported_image_sizes & T30_SUPPORT_B4_LENGTH)) + s->dis_dtc_frame[5] |= DISBIT3; + /* No scan-line padding required. */ + s->dis_dtc_frame[5] |= (DISBIT7 | DISBIT6 | DISBIT5); + if ((s->supported_compressions & T30_SUPPORT_NO_COMPRESSION)) + s->dis_dtc_frame[6] |= DISBIT2; + if (s->ecm_allowed) + s->dis_dtc_frame[6] |= DISBIT3; + if ((s->supported_compressions & T30_SUPPORT_T6_COMPRESSION)) + s->dis_dtc_frame[6] |= DISBIT7; +#if defined(SUPPORT_FNV) + s->dis_dtc_frame[7] |= DISBIT1; +#endif + if ((s->supported_polling_features & T30_SUPPORT_SEP)) + s->dis_dtc_frame[7] |= DISBIT3; + if ((s->supported_polling_features & T30_SUPPORT_PSA)) + s->dis_dtc_frame[7] |= DISBIT4; + if ((s->supported_compressions & T30_SUPPORT_T43_COMPRESSION)) + s->dis_dtc_frame[7] |= DISBIT4; + /* No plane interleave */ + /* No G.726 */ + /* No extended voice coding */ + if ((s->supported_resolutions & T30_SUPPORT_SUPERFINE_RESOLUTION)) + s->dis_dtc_frame[8] |= DISBIT1; + if ((s->supported_resolutions & T30_SUPPORT_300_300_RESOLUTION)) + s->dis_dtc_frame[8] |= DISBIT2; + if ((s->supported_resolutions & (T30_SUPPORT_400_400_RESOLUTION | T30_SUPPORT_R16_RESOLUTION))) + s->dis_dtc_frame[8] |= DISBIT3; + /* Metric */ + s->dis_dtc_frame[8] |= DISBIT4; + /* No sub-addressing */ + /* No password */ + /* No data file (polling) */ + /* No BFT */ + /* No DTM */ + /* No EDI */ + /* No BTM */ + /* No mixed mode (polling) */ + /* No character mode */ + /* No mixed mode */ + /* No mode 26 */ + /* No digital network capable */ + /* No JPEG */ + /* No full colour */ + /* No 12bits/pel */ + /* No sub-sampling */ + if ((s->supported_image_sizes & T30_SUPPORT_US_LETTER_LENGTH)) + s->dis_dtc_frame[12] |= DISBIT4; + if ((s->supported_image_sizes & T30_SUPPORT_US_LEGAL_LENGTH)) + s->dis_dtc_frame[12] |= DISBIT5; + if ((s->supported_compressions & T30_SUPPORT_T85_COMPRESSION)) + s->dis_dtc_frame[12] |= DISBIT6; + /* No T.85 optional. */ + if ((s->supported_resolutions & T30_SUPPORT_600_600_RESOLUTION)) + s->dis_dtc_frame[15] |= DISBIT1; + if ((s->supported_resolutions & T30_SUPPORT_1200_1200_RESOLUTION)) + s->dis_dtc_frame[15] |= DISBIT2; + if ((s->supported_resolutions & T30_SUPPORT_300_600_RESOLUTION)) + s->dis_dtc_frame[15] |= DISBIT3; + if ((s->supported_resolutions & T30_SUPPORT_400_800_RESOLUTION)) + s->dis_dtc_frame[15] |= DISBIT4; + if ((s->supported_resolutions & T30_SUPPORT_600_1200_RESOLUTION)) + s->dis_dtc_frame[15] |= DISBIT5; + if ((s->supported_compressions & T30_SUPPORT_T45_COMPRESSION)) + s->dis_dtc_frame[16] |= DISBIT4; + if ((s->iaf & T30_IAF_MODE_FLOW_CONTROL)) + s->dis_dtc_frame[18] |= DISBIT1; + if ((s->iaf & T30_IAF_MODE_CONTINUOUS_FLOW)) + s->dis_dtc_frame[18] |= DISBIT3; + s->dis_dtc_len = 19; + t30_decode_dis_dtc_dcs(s, s->dis_dtc_frame, s->dis_dtc_len); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int build_dcs(t30_state_t *s, const uint8_t *msg, int len) +{ + /* 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] = + { + /* 20 5 10 20 40 40 10 0ms */ + {0, 1, 2, 0, 4, 4, 2, 7}, /* normal */ + {0, 1, 2, 2, 4, 0, 1, 7}, /* fine */ + {2, 1, 1, 1, 0, 2, 1, 7} /* 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 + }; + uint8_t dis_dtc_frame[T30_MAX_DIS_DTC_DCS_LEN]; + uint8_t min_bits_field; + int i; + + if (len < 6) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Short DIS/DTC frame\n"); + return -1; + } + + /* 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(dis_dtc_frame, msg, T30_MAX_DIS_DTC_DCS_LEN); + } + else + { + memcpy(dis_dtc_frame, msg, len); + if (len < T30_MAX_DIS_DTC_DCS_LEN) + memset(dis_dtc_frame + len, 0, T30_MAX_DIS_DTC_DCS_LEN - len); + } + + /* Make a DCS frame based on local issues and a received DIS frame. Negotiate the result + based on what both parties can do. */ + s->dcs_frame[0] = 0xFF; + s->dcs_frame[1] = 0x13; + s->dcs_frame[2] = (uint8_t) (T30_DCS | s->dis_received); + s->dcs_frame[3] = 0x00; + s->dcs_frame[4] = 0x00; + for (i = 5; i < 18; i++) + s->dcs_frame[i] = DISBIT8; + s->dcs_frame[18] = 0x00; + /* Set to required modem rate; standard resolution */ + s->dcs_frame[4] |= fallback_sequence[s->current_fallback].dcs_code; + + if ((s->iaf & T30_IAF_MODE_NO_FILL_BITS)) + min_bits_field = 7; + else + min_bits_field = (dis_dtc_frame[5] >> 4) & 7; + /* Select the compression to use. */ + switch(s->line_encoding) + { + case T4_COMPRESSION_ITU_T6: + s->dcs_frame[6] |= DISBIT7; + break; + case T4_COMPRESSION_ITU_T4_2D: + s->dcs_frame[4] |= DISBIT8; + break; + default: + break; + } + /* If we have a file to send, tell the far end to go into receive mode. */ + if (s->tx_file[0]) + s->dcs_frame[4] |= DISBIT2; + /* Set the minimum scan time bits */ + switch (s->y_resolution) + { + case T4_Y_RESOLUTION_SUPERFINE: + if ((dis_dtc_frame[8] & DISBIT1)) + { + s->dcs_frame[8] |= DISBIT1; + if ((dis_dtc_frame[8] & DISBIT6)) + min_bits_field = translate_min_scan_time[2][min_bits_field]; + else + min_bits_field = translate_min_scan_time[1][min_bits_field]; + break; + } + /* Fall back */ + s->y_resolution = T4_Y_RESOLUTION_FINE; + span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution.\n"); + /* Fall through */ + case T4_Y_RESOLUTION_FINE: + if ((dis_dtc_frame[4] & DISBIT7)) + { + s->dcs_frame[4] |= DISBIT7; + min_bits_field = translate_min_scan_time[1][min_bits_field]; + break; + } + /* Fall back */ + s->y_resolution = T4_Y_RESOLUTION_STANDARD; + span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution.\n"); + /* Fall through */ + default: + case T4_Y_RESOLUTION_STANDARD: + min_bits_field = translate_min_scan_time[0][min_bits_field]; + break; + } + if (min_scan_times[min_bits_field] == 0) + s->min_row_bits = 0; + else + s->min_row_bits = fallback_sequence[s->current_fallback].bit_rate*min_scan_times[min_bits_field]/1000; + s->dcs_frame[5] |= min_bits_field << 4; + span_log(&s->logging, SPAN_LOG_FLOW, "Minimum bits per row will be %d\n", s->min_row_bits); + switch (s->image_width) + { + /* Low (R4) res widths are not supported in recent versions of T.30 */ + /* Medium (R8) res widths */ + case 1728: + break; + case 2048: + if ((s->dis_dtc_frame[5] & (DISBIT2 | DISBIT1)) < 1) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) not acceptable to far end\n", s->image_width); + return -1; + } + s->dcs_frame[5] |= DISBIT1; + break; + case 2432: + if ((s->dis_dtc_frame[5] & (DISBIT2 | DISBIT1)) < 2) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) not acceptable to far end\n", s->image_width); + return -1; + } + s->dcs_frame[5] |= DISBIT2; + break; + /* High (R16) res widths */ + case 3456: + if ((dis_dtc_frame[8] & DISBIT3) == 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) not acceptable to far end\n", s->image_width); + return -1; + } + s->dcs_frame[8] |= DISBIT3; + break; + case 4096: + if ((dis_dtc_frame[8] & DISBIT3) == 0 || (s->dis_dtc_frame[5] & (DISBIT2 | DISBIT1)) == 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) not acceptable to far end\n", s->image_width); + return -1; + } + s->dcs_frame[5] |= DISBIT1; + s->dcs_frame[8] |= DISBIT3; + break; + case 4864: + if ((dis_dtc_frame[8] & DISBIT3) == 0 || (s->dis_dtc_frame[5] & (DISBIT2 | DISBIT1)) != DISBIT2) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) not acceptable to far end\n", s->image_width); + return -1; + } + s->dcs_frame[5] |= DISBIT2; + s->dcs_frame[8] |= DISBIT3; + break; + default: + /* The image is of an unrecognised width. */ + span_log(&s->logging, SPAN_LOG_FLOW, "Image width (%d pixels) not a valid FAX image width\n", s->image_width); + return -1; + } + if (s->error_correcting_mode) + s->dcs_frame[6] |= DISBIT3; + s->dcs_len = 19; + t30_decode_dis_dtc_dcs(s, s->dcs_frame, s->dcs_len); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int check_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) +{ + uint8_t dis_dtc_frame[T30_MAX_DIS_DTC_DCS_LEN]; + + if (len < 6) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Short DIS/DTC frame\n"); + return -1; + } + + /* 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(dis_dtc_frame, msg, T30_MAX_DIS_DTC_DCS_LEN); + } + else + { + memcpy(dis_dtc_frame, msg, len); + if (len < T30_MAX_DIS_DTC_DCS_LEN) + memset(dis_dtc_frame + len, 0, T30_MAX_DIS_DTC_DCS_LEN - len); + } + s->error_correcting_mode = (s->ecm_allowed && (dis_dtc_frame[6] & DISBIT3) != 0); + /* 256 octets per ECM frame */ + s->octets_per_ecm_frame = 256; + /* Select the compression to use. */ + if ((s->supported_compressions & T30_SUPPORT_T6_COMPRESSION) && (dis_dtc_frame[6] & DISBIT7)) + { + s->line_encoding = T4_COMPRESSION_ITU_T6; + } + else if ((s->supported_compressions & T30_SUPPORT_T4_2D_COMPRESSION) && (dis_dtc_frame[4] & DISBIT8)) + { + s->line_encoding = T4_COMPRESSION_ITU_T4_2D; + } + else + { + s->line_encoding = T4_COMPRESSION_ITU_T4_1D; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Selected compression %d\n", s->line_encoding); + switch (dis_dtc_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3)) + { + case 0: + s->current_fallback = T30_V27TER_FALLBACK_START + 1; + break; + case DISBIT4: + s->current_fallback = T30_V27TER_FALLBACK_START; + break; + case DISBIT3: + /* TODO: this doesn't allow for skipping the V.27ter modes */ + s->current_fallback = T30_V29_FALLBACK_START; + break; + case (DISBIT4 | DISBIT3): + s->current_fallback = T30_V29_FALLBACK_START; + break; + case (DISBIT6 | DISBIT4 | DISBIT3): + if ((s->supported_modems & T30_SUPPORT_V17)) + s->current_fallback = T30_V17_FALLBACK_START; + else + s->current_fallback = T30_V29_FALLBACK_START; + break; + default: + span_log(&s->logging, SPAN_LOG_FLOW, "Remote does not support a compatible modem\n"); + /* We cannot talk to this machine! */ + return -1; + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int check_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) +{ + static const int widths[3][4] = + { + { 864, 1024, 1216, -1}, /* R4 resolution - no longer used in recent versions of T.30 */ + {1728, 2048, 2432, -1}, /* R8 resolution */ + {3456, 4096, 4864, -1} /* R16 resolution */ + }; + uint8_t dcs_frame[T30_MAX_DIS_DTC_DCS_LEN]; + int speed; + int i; + + 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 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 = (dcs_frame[6] & DISBIT4) ? 256 : 64; + if ((dcs_frame[8] & DISBIT1)) + s->y_resolution = T4_Y_RESOLUTION_SUPERFINE; + else if (dcs_frame[4] & DISBIT7) + s->y_resolution = T4_Y_RESOLUTION_FINE; + else + s->y_resolution = T4_Y_RESOLUTION_STANDARD; + s->image_width = widths[(dcs_frame[8] & DISBIT3) ? 2 : 1][dcs_frame[5] & (DISBIT2 | DISBIT1)]; + + /* Check which compression we will use. */ + if ((dcs_frame[6] & DISBIT7)) + s->line_encoding = T4_COMPRESSION_ITU_T6; + else if ((dcs_frame[4] & DISBIT8)) + 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 (!(dcs_frame[4] & DISBIT2)) + span_log(&s->logging, SPAN_LOG_FLOW, "Remote cannot receive\n"); + + speed = dcs_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3); + for (i = 0; fallback_sequence[i].bit_rate; i++) + { + if (fallback_sequence[i].dcs_code == speed) + break; + } + if (fallback_sequence[i].bit_rate == 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Remote asked for a modem standard we do not support\n"); + return -1; + } + s->current_fallback = i; + s->error_correcting_mode = ((dcs_frame[6] & DISBIT3) != 0); + return 0; +} +/*- 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 send_dis_or_dtc_sequence(t30_state_t *s) +{ + if (send_nsf_frame(s)) + { + s->step = 0; + return; + } + if (send_ident_frame(s, T30_CSI)) + { + s->step = 1; + return; + } + set_dis_or_dtc(s); + send_frame(s, s->dis_dtc_frame, s->dis_dtc_len); + s->step = 2; +} +/*- End of function --------------------------------------------------------*/ + +static void send_dcs_sequence(t30_state_t *s) +{ + if (send_pw_frame(s)) + { + s->step = 0; + return; + } + if (send_sub_frame(s)) + { + s->step = 1; + return; + } + if (send_ident_frame(s, T30_TSI)) + { + s->step = 2; + return; + } + send_frame(s, s->dcs_frame, s->dcs_len); + s->step = 3; +} +/*- 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. */ + t4_rx_end(&(s->t4)); + t4_tx_end(&(s->t4)); + 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 start_sending_document(t30_state_t *s) +{ + if (s->tx_file[0] == '\0') + { + /* There is nothing to send */ + span_log(&s->logging, SPAN_LOG_FLOW, "No document to send\n"); + return FALSE; + } + 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)) + { + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot open source TIFF file '%s'\n", s->tx_file); + s->current_status = T30_ERR_FILEERROR; + return FALSE; + } + t4_tx_set_tx_encoding(&(s->t4), s->line_encoding); + t4_tx_set_min_row_bits(&(s->t4), s->min_row_bits); + t4_tx_set_local_ident(&(s->t4), s->local_ident); + t4_tx_set_header_info(&(s->t4), s->header_info); + + s->y_resolution = t4_tx_get_y_resolution(&(s->t4)); + switch (s->y_resolution) + { + case T4_Y_RESOLUTION_STANDARD: + s->dcs_frame[4] &= ~DISBIT7; + s->dcs_frame[8] &= ~DISBIT1; + break; + case T4_Y_RESOLUTION_FINE: + s->dcs_frame[4] |= DISBIT7; + s->dcs_frame[8] &= ~DISBIT1; + break; + case T4_Y_RESOLUTION_SUPERFINE: + s->dcs_frame[4] &= ~DISBIT7; + s->dcs_frame[8] |= DISBIT1; + break; + } + s->image_width = t4_tx_get_image_width(&(s->t4)); + t4_tx_start_page(&(s->t4)); + s->ecm_page = 0; + s->ecm_block = 0; + 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"); + } + /* Schedule training after the messages */ + set_state(s, T30_STATE_D); + s->retries = 0; + send_dcs_sequence(s); + return TRUE; +} +/*- End of function --------------------------------------------------------*/ + +static int restart_sending_document(t30_state_t *s) +{ + /* Schedule training after the messages */ + t4_tx_restart_page(&(s->t4)); + set_state(s, T30_STATE_D); + s->retries = 0; + s->ecm_block = 0; + send_dis_or_dtc_sequence(s); + return TRUE; +} +/*- 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 FALSE; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Start receiving document\n"); + queue_phase(s, T30_PHASE_B_TX); + set_state(s, T30_STATE_R); + s->dis_received = FALSE; + s->ecm_page = 0; + s->ecm_block = 0; + send_dis_or_dtc_sequence(s); + return TRUE; +} +/*- End of function --------------------------------------------------------*/ + +static void unexpected_frame(t30_state_t *s, const uint8_t *msg, int len) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s received in state %d\n", t30_frametype(msg[2]), s->state); + if (s->state == T30_STATE_F_DOC) + s->current_status = T30_ERR_INVALCMDRX; +} +/*- End of function --------------------------------------------------------*/ + +static void unexpected_final_frame(t30_state_t *s, const uint8_t *msg, int len) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s received in state %d\n", t30_frametype(msg[2]), s->state); + s->current_status = T30_ERR_UNEXPECTED; + send_dcn(s); +} +/*- End of function --------------------------------------------------------*/ + +static void unexpected_frame_length(t30_state_t *s, const uint8_t *msg, int len) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame length - %d\n", t30_frametype(msg[0]), len); + s->current_status = T30_ERR_UNEXPECTED; + send_dcn(s); +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_dis_or_dtc(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Digital identification signal or digital transmit command */ + s->dis_received = TRUE; + check_rx_dis_dtc(s, msg, len); + switch (s->state) + { + case T30_STATE_D_POST_TCF: + /* It appears they didn't see what we sent - retry the TCF */ + if (++s->retries > MAX_MESSAGE_TRIES) + { + s->current_status = T30_ERR_RETRYDCN; + send_dcn(s); + break; + } + /* TODO: We should fall through at this point. However, if we do that + we need to allow for a send or receive already being in progress from + a previous try */ + queue_phase(s, T30_PHASE_B_TX); + /* Schedule training after the messages */ + set_state(s, T30_STATE_D); + send_dcs_sequence(s); + break; + case T30_STATE_R: + case T30_STATE_T: + case T30_STATE_F_DOC: + t30_decode_dis_dtc_dcs(s, msg, len); + if (s->phase_b_handler) + s->phase_b_handler(s, s->phase_d_user_data, msg[2]); + 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 ((msg[4] & DISBIT2)) + { + if (!start_sending_document(s)) + { + send_dcn(s); + break; + } + if (build_dcs(s, msg, len)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "The remote machine is incompatible\n", s->tx_file); + s->current_status = T30_ERR_INCOMPATIBLE; + send_dcn(s); + break; + } + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot receive\n", t30_frametype(msg[2])); + s->current_status = T30_ERR_NOTRXCAPABLE; + send_dcn(s); + } + break; + } + 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 ((msg[4] & DISBIT1)) + { + if (!start_receiving_document(s)) + { + send_dcn(s); + break; + } + if (set_dis_or_dtc(s)) + { + s->current_status = T30_ERR_INCOMPATIBLE; + send_dcn(s); + break; + } + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "%s far end cannot transmit\n", t30_frametype(msg[2])); + s->current_status = T30_ERR_NOTTXCAPABLE; + send_dcn(s); + } + break; + } + 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); + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Digital command signal */ + switch (s->state) + { + case T30_STATE_R: + case T30_STATE_F_DOC: + /* (TSI) DCS */ + /* (PWD) (SUB) (TSI) DCS */ + check_rx_dcs(s, msg, len); + if (s->phase_b_handler) + s->phase_b_handler(s, s->phase_d_user_data, T30_DCS); + /* 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); + break; + } + if (!s->in_message && t4_rx_init(&(s->t4), s->rx_file, T4_COMPRESSION_ITU_T4_2D)) + { + 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); + break; + } + if (!(s->iaf & T30_IAF_MODE_NO_TCF)) + { + set_state(s, T30_STATE_F_TCF); + set_phase(s, T30_PHASE_C_NON_ECM_RX); + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_cfr(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Confirmation to receive */ + switch (s->state) + { + case T30_STATE_D_POST_TCF: + /* 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); + s->ecm_current_frame = 0; + send_next_ecm_frame(s); + } + else + { + set_state(s, T30_STATE_I); + queue_phase(s, T30_PHASE_C_NON_ECM_TX); + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_ftt(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Failure to train */ + switch (s->state) + { + case T30_STATE_D_POST_TCF: + /* Trainability test failed. Try again. */ + span_log(&s->logging, SPAN_LOG_FLOW, "Trainability test failed\n"); + s->retries = 0; + s->short_train = FALSE; + if (fallback_sequence[++s->current_fallback].bit_rate == 0) + { + /* We have fallen back as far as we can go. Give up. */ + s->current_fallback = 0; + s->current_status = T30_ERR_CANNOTTRAIN; + send_dcn(s); + break; + } + /* Schedule training after the messages */ + set_state(s, T30_STATE_D); + s->retries = 0; + send_dcs_sequence(s); + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_eom(t30_state_t *s, const uint8_t *msg, int len) +{ + /* End of message */ + switch (s->state) + { + case T30_STATE_F_POST_DOC_NON_ECM: + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_EOM); + s->next_rx_step = T30_EOM; + /* Return to phase B */ + queue_phase(s, T30_PHASE_B_TX); + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_GOOD: + t4_rx_end_page(&(s->t4)); + rx_start_page(s); + set_state(s, T30_STATE_III_Q_MCF); + send_simple_frame(s, T30_MCF); + break; + case T30_COPY_QUALITY_POOR: + t4_rx_end_page(&(s->t4)); + 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; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_mps(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Multi-page signal */ + switch (s->state) + { + case T30_STATE_F_POST_DOC_NON_ECM: + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_MPS); + s->next_rx_step = T30_MPS; + /* Return to phase C */ + queue_phase(s, T30_PHASE_D_TX); + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_GOOD: + t4_rx_end_page(&(s->t4)); + rx_start_page(s); + set_state(s, T30_STATE_III_Q_MCF); + send_simple_frame(s, T30_MCF); + break; + case T30_COPY_QUALITY_POOR: + t4_rx_end_page(&(s->t4)); + 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; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_eop(t30_state_t *s, const uint8_t *msg, int len) +{ + /* End of procedure */ + switch (s->state) + { + case T30_STATE_F_POST_DOC_NON_ECM: + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_EOP); + s->next_rx_step = T30_EOP; + queue_phase(s, T30_PHASE_D_TX); + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_GOOD: + t4_rx_end_page(&(s->t4)); + t4_rx_end(&(s->t4)); + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_MCF); + send_simple_frame(s, T30_MCF); + break; + case T30_COPY_QUALITY_POOR: + t4_rx_end_page(&(s->t4)); + t4_rx_end(&(s->t4)); + 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; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_pri_eom(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Procedure interrupt - end of procedure */ + switch (s->state) + { + case T30_STATE_F_POST_DOC_NON_ECM: + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, T30_PRI_EOM); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + s->next_rx_step = T30_PRI_EOM; + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_GOOD: + t4_rx_end_page(&(s->t4)); + t4_rx_end(&(s->t4)); + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_MCF); + break; + case T30_COPY_QUALITY_POOR: + t4_rx_end_page(&(s->t4)); + t4_rx_end(&(s->t4)); + 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; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_pri_mps(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Procedure interrupt - multipage signal */ + switch (s->state) + { + case T30_STATE_F_POST_DOC_NON_ECM: + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, T30_PRI_MPS); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + s->next_rx_step = T30_PRI_MPS; + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_GOOD: + t4_rx_end_page(&(s->t4)); + t4_rx_end(&(s->t4)); + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_MCF); + break; + case T30_COPY_QUALITY_POOR: + t4_rx_end_page(&(s->t4)); + t4_rx_end(&(s->t4)); + 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; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_pri_eop(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Procedure interrupt - end of procedure */ + switch (s->state) + { + case T30_STATE_F_POST_DOC_NON_ECM: + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, T30_PRI_EOP); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + s->next_rx_step = T30_PRI_EOP; + switch (copy_quality(s)) + { + case T30_COPY_QUALITY_GOOD: + t4_rx_end_page(&(s->t4)); + t4_rx_end(&(s->t4)); + s->in_message = FALSE; + set_state(s, T30_STATE_III_Q_MCF); + break; + case T30_COPY_QUALITY_POOR: + t4_rx_end_page(&(s->t4)); + t4_rx_end(&(s->t4)); + 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; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_nss(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Non-standard facilities set-up */ + switch (s->state) + { + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_ctc(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Continue to correct */ + switch (s->state) + { + case T30_STATE_F_DOC: + case T30_STATE_F_POST_DOC_ECM: + send_simple_frame(s, T30_CTR); + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_ctr(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Response for continue to correct */ + switch (s->state) + { + case T30_STATE_IV_CTC: + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_err(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Response for end of retransmission */ + switch (s->state) + { + case T30_STATE_IV_EOR: + case T30_STATE_IV_EOR_RNR: + /* TODO: Continue with the next message if MPS or EOM? */ + send_dcn(s); + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- 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; + int mask; + uint8_t frame[100 + 3]; + + /* Partial page request */ + switch (s->state) + { + case T30_STATE_IV_PPS_NULL: + case T30_STATE_IV_PPS_Q: + if (++s->ppr_count >= 4) + { + /* Continue to correct? */ + /* TODO: Decide if we should continue */ + if (1) + { + set_state(s, T30_STATE_IV_CTC); + send_simple_frame(s, T30_CTC); + } + else + { + set_state(s, T30_STATE_IV_EOR); + frame[0] = 0xFF; + frame[1] = 0x13; + frame[2] = T30_EOR; + 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 + { + /* Check which frames are OK, and mark them as OK. */ + for (i = 0; i < 32; i++) + { + if (msg[i + 3] == 0) + { + s->ecm_frame_map[i + 3] = 0; + for (j = 0; j < 8; j++) + s->ecm_len[(i << 3) + j] = -1; + } + else + { + mask = 1; + 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] & mask) == 0) + 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); +#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 send\n", frame_no); +#endif + } + mask <<= 1; + } + } + } + /* Initiate resending of the remainder of the frames. */ + set_state(s, T30_STATE_IV); + queue_phase(s, T30_PHASE_C_ECM_TX); + s->ecm_current_frame = 0; + send_next_ecm_frame(s); + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_rr(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Receiver ready */ + switch (s->state) + { + case T30_STATE_F_POST_DOC_NON_ECM: + /* TODO: */ + s->timer_t5 = 0; + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_rnr(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Receive not ready */ + switch (s->state) + { + case T30_STATE_IV_PPS_NULL: + case T30_STATE_IV_PPS_Q: + set_state(s, T30_STATE_IV_PPS_RNR); + send_simple_frame(s, T30_RR); + break; + case T30_STATE_IV_PPS_RNR: + send_simple_frame(s, T30_RR); + /* TODO: */ + if (s->timer_t5 == 0) + s->timer_t5 = ms_to_samples(DEFAULT_TIMER_T5); + break; + case T30_STATE_IV_EOR: + set_state(s, T30_STATE_IV_EOR_RNR); + send_simple_frame(s, T30_RR); + break; + case T30_STATE_IV_EOR_RNR: + send_simple_frame(s, T30_RR); + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_eos(t30_state_t *s, const uint8_t *msg, int len) +{ + /* End of selection */ + switch (s->state) + { + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_tr(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Transmit ready */ + switch (s->state) + { + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_fcd(t30_state_t *s, const uint8_t *msg, int len) +{ + int frame_no; + int i; + + /* Facsimile coded data */ + switch (s->state) + { + case T30_STATE_F_DOC: + 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 image frame %d, length %d\n", frame_no, len - 4); + for (i = 0; i < len - 4; i++) + s->ecm_data[frame_no][i] = msg[i + 4]; + s->ecm_len[frame_no] = (int16_t) (len - 4); + span_log(&s->logging, SPAN_LOG_FLOW, "Storing ECM frame %d\n", frame_no); + } + else + { + unexpected_frame_length(s, msg, len); + } + break; + default: + unexpected_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 */ + switch (s->state) + { + case T30_STATE_F_DOC: + set_state(s, T30_STATE_F_POST_DOC_ECM); + queue_phase(s, T30_PHASE_D_RX); + break; + case T30_STATE_F_POST_DOC_ECM: + /* Ignore extra RCP frames. The source will usually send several to maximum the chance of + one getting through OK. */ + break; + default: + unexpected_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, " Subaddress (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 subaddress (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 0x83: + x = "Incorrect password (PWD)"; + break; + case 0x85: + x = "Selective polling reference (SEP) not known"; + break; + case 0x43: + case 0xC3: + x = "Subaddress (SUB) not known"; + break; + case 0x45: + case 0xC5: + x = "Sender identity (SID) not known"; + break; + case 0x10: + x = "Secure fax error"; + break; + case 0x42: + case 0xC2: + x = "Transmitting subscriber identity (TSI) not accepted"; + break; + case 0x86: + x = "Polled subaddress (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_rx_pps(t30_state_t *s, const uint8_t *msg, int len) +{ + int fcf2; + int page; + int block; + int i; + int j; + int frame_no; + int first_bad_frame; + + /* Partial page signal */ + switch (s->state) + { + case T30_STATE_F_DOC: + case T30_STATE_F_POST_DOC_ECM: + if (len >= 7) + { + fcf2 = msg[3] & 0xFE; + page = msg[4]; + block = msg[5]; + s->ecm_frames = msg[6] + 1; + span_log(&s->logging, SPAN_LOG_FLOW, "Received PPS + %s\n", t30_frametype(msg[3])); + /* Build a bit map of which frames we now have stored OK */ + frame_no = 0; + first_bad_frame = 256; + for (i = 3; i < 3 + 32; i++) + { + s->ecm_frame_map[i] = 0; + for (j = 0; j < 8; j++) + { + if (s->ecm_len[frame_no] < 0) + { + s->ecm_frame_map[i] |= (1 << j); + if (frame_no < first_bad_frame) + first_bad_frame = frame_no; + } + frame_no++; + } + } + /* Now, are there really bad frames, or does our scan represent things being OK? */ + queue_phase(s, T30_PHASE_D_TX); + if (first_bad_frame >= s->ecm_frames) + { + /* Everything was OK. We can accept the data and move on. */ + switch (fcf2) + { + case T30_NULL: + /* We can confirm this partial page. */ + t30_ecm_commit_partial_page(s); + set_state(s, T30_STATE_F_POST_RCP_MCF); + send_simple_frame(s, T30_MCF); + break; + case T30_EOP: + case T30_EOM: + case T30_MPS: + case T30_PRI_EOP: + case T30_PRI_EOM: + case T30_PRI_MPS: + /* We can confirm the whole page. */ + s->next_rx_step = fcf2; + t30_ecm_commit_partial_page(s); + t4_rx_end_page(&(s->t4)); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, fcf2); + rx_start_page(s); + set_state(s, T30_STATE_F_POST_RCP_MCF); + send_simple_frame(s, T30_MCF); + break; + default: + unexpected_final_frame(s, msg, len); + break; + } + } + else + { + /* We need to send the PPR frame we have created, to try to fill in the missing/bad data. */ + set_state(s, T30_STATE_F_POST_RCP_PPR); + s->ecm_frame_map[0] = 0xFF; + s->ecm_frame_map[1] = 0x13; + s->ecm_frame_map[2] = T30_PPR; + send_frame(s, s->ecm_frame_map, 3 + 32); + } + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "Bad PPS message length %d.\n", len); + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_tnr(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Transmit not ready */ + switch (s->state) + { + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_eor(t30_state_t *s, const uint8_t *msg, int len) +{ + int fcf2; + + /* End of retransmission */ + /* Partial page signal */ + switch (s->state) + { + case T30_STATE_F_DOC: + case T30_STATE_F_POST_DOC_ECM: + if (len == 4) + { + fcf2 = msg[3] & 0xFE; + span_log(&s->logging, SPAN_LOG_FLOW, "Received EOR + %s\n", t30_frametype(msg[3])); + switch (fcf2) + { + case T30_NULL: + break; + case T30_PRI_EOM: + case T30_PRI_MPS: + case T30_PRI_EOP: + /* TODO: Alert operator */ + /* Fall through */ + case T30_EOM: + case T30_MPS: + case T30_EOP: + s->next_rx_step = fcf2; + send_simple_frame(s, T30_ERR); + break; + default: + unexpected_final_frame(s, msg, len); + break; + } + } + else + { + unexpected_frame_length(s, msg, len); + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_fdm(t30_state_t *s, const uint8_t *msg, int len) +{ + /* File diagnostics message */ + switch (s->state) + { + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_mcf(t30_state_t *s, const uint8_t *msg, int len) +{ + t4_stats_t stats; + + /* Message confirmation */ + switch (s->state) + { + case T30_STATE_II_Q: + switch (s->next_tx_step) + { + case T30_MPS: + case T30_PRI_MPS: + s->retries = 0; + t4_tx_end_page(&(s->t4)); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_MCF); + t4_tx_start_page(&(s->t4)); + set_state(s, T30_STATE_I); + queue_phase(s, T30_PHASE_C_NON_ECM_TX); + break; + case T30_EOM: + case T30_PRI_EOM: + s->retries = 0; + t4_tx_end_page(&(s->t4)); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_MCF); + t4_tx_end(&(s->t4)); + set_state(s, T30_STATE_R); + 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; + case T30_EOP: + case T30_PRI_EOP: + s->retries = 0; + t4_tx_end_page(&(s->t4)); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_MCF); + t4_tx_end(&(s->t4)); + 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_STATE_IV_PPS_NULL: + case T30_STATE_IV_PPS_Q: + case T30_STATE_IV_PPS_RNR: + s->retries = 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_WARNING, "Additional image data to send\n"); + s->ecm_block++; + set_state(s, T30_STATE_IV); + queue_phase(s, T30_PHASE_C_ECM_TX); + s->ecm_current_frame = 0; + send_next_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: + s->retries = 0; + t4_tx_end_page(&(s->t4)); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_MCF); + t4_tx_start_page(&(s->t4)); + s->ecm_page++; + s->ecm_block = 0; + if (get_partial_ecm_page(s) > 0) + { + set_state(s, T30_STATE_IV); + queue_phase(s, T30_PHASE_C_ECM_TX); + s->ecm_current_frame = 0; + send_next_ecm_frame(s); + } + break; + case T30_EOM: + case T30_PRI_EOM: + s->retries = 0; + t4_tx_end_page(&(s->t4)); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_MCF); + t4_tx_end(&(s->t4)); + set_state(s, T30_STATE_R); + 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; + case T30_EOP: + case T30_PRI_EOP: + s->retries = 0; + t4_tx_end_page(&(s->t4)); + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_MCF); + t4_tx_end(&(s->t4)); + 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; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_rtp(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Retrain positive */ + s->short_train = FALSE; + switch (s->state) + { + case T30_STATE_II_Q: + switch (s->next_tx_step) + { + case T30_MPS: + case T30_PRI_MPS: + s->retries = 0; + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_RTP); + /* Send fresh training, and then the next page */ + queue_phase(s, T30_PHASE_B_TX); + restart_sending_document(s); + break; + case T30_EOM: + case T30_PRI_EOM: + s->retries = 0; + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_RTP); + /* TODO: should go back to T, and resend */ + set_state(s, T30_STATE_R); + break; + case T30_EOP: + case T30_PRI_EOP: + s->retries = 0; + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_RTN); + send_dcn(s); + break; + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_rtn(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Retrain negative */ + s->short_train = FALSE; + switch (s->state) + { + case T30_STATE_II_Q: + switch (s->next_tx_step) + { + case T30_MPS: + case T30_PRI_MPS: + s->retries = 0; + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_RTN); + /* Send fresh training, and then repeat the last page */ + queue_phase(s, T30_PHASE_B_TX); + restart_sending_document(s); + break; + case T30_EOM: + case T30_PRI_EOM: + case T30_EOP: + case T30_PRI_EOP: + s->retries = 0; + if (s->phase_d_handler) + s->phase_d_handler(s, s->phase_d_user_data, T30_RTN); + send_dcn(s); + break; + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_pip(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Procedure interrupt positive */ + switch (s->state) + { + case T30_STATE_II_Q: + case T30_STATE_IV_PPS_Q: + case T30_STATE_IV_PPS_RNR: + s->retries = 0; + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, T30_PIP); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_pin(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Procedure interrupt negative */ + switch (s->state) + { + case T30_STATE_II_Q: + case T30_STATE_IV_PPS_Q: + case T30_STATE_IV_PPS_RNR: + case T30_STATE_IV_EOR: + case T30_STATE_IV_EOR_RNR: + s->retries = 0; + if (s->phase_d_handler) + { + s->phase_d_handler(s, s->phase_d_user_data, T30_PIN); + s->timer_t3 = ms_to_samples(DEFAULT_TIMER_T3); + } + break; + default: + unexpected_final_frame(s, msg, len); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_dcn(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Disconnect */ + /* TODO: test if this is expected or unexpected */ + switch (s->state) + { +#if 0 + case ??????: + /* Unexpected DCN while waiting for image data */ + s->current_status = T30_ERR_DCNDATARX; + break; +#endif + case T30_STATE_F_POST_DOC_NON_ECM: + /* Unexpected DCN while waiting for EOM, EOP or MPS */ + s->current_status = T30_ERR_DCNFAXRX; + break; + case T30_STATE_II_Q: + switch (s->next_tx_step) + { + case T30_MPS: + case T30_PRI_MPS: + case T30_EOM: + case T30_PRI_EOM: + /* Unexpected DCN after EOM or MPS sequence */ + s->current_status = T30_ERR_DCNPHDRX; + break; + } + break; +#if 0 + case ??????: + /* Unexpected DCN after RR/RNR sequence */ + s->current_status = T30_ERR_DCNRRDRX; + break; + case ??????: + /* Unexpected DCN after requested retransmission */ + s->current_status = T30_ERR_DCNNORTNRX; + break; +#endif + } + /* Time to disconnect */ + disconnect(s); +} +/*- End of function --------------------------------------------------------*/ + +static void process_rx_crp(t30_state_t *s, const uint8_t *msg, int len) +{ + /* Command repeat */ + repeat_last_command(s); +} +/*- End of function --------------------------------------------------------*/ + +static void hdlc_accept_control_msg(t30_state_t *s, int ok, const uint8_t *msg, int len) +{ + int final_frame; + char far_password[T30_MAX_IDENT_LEN]; + + final_frame = msg[1] & 0x10; + if (!final_frame) + { + /* Restart the command or response timer, T2 or T4 */ + s->timer_t2_t4 = ms_to_samples((s->timer_is_t4) ? DEFAULT_TIMER_T4 : DEFAULT_TIMER_T2); + + /* 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: + if (msg[2] == T30_CSI) + { + /* Called subscriber identification */ + /* OK in (NSF) (CSI) DIS */ + decode_20digit_msg(s, s->far_ident, &msg[2], len - 2); + } + else + { + /* CIG - Calling subscriber identification */ + /* OK in (NSC) (CIG) DTC */ + /* OK in (PWD) (SEP) (CIG) DTC */ + decode_20digit_msg(s, s->far_ident, &msg[2], len - 2); + } + break; + case T30_NSF: + if (msg[2] == T30_NSF) + { + /* Non-standard facilities */ + /* OK in (NSF) (CSI) DIS */ + if (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: + if (msg[2] == T30_PWD) + { + /* Password */ + /* OK in (PWD) (SUB) (TSI) DCS */ + /* OK in (PWD) (SEP) (CIG) DTC */ + decode_20digit_msg(s, far_password, &msg[2], len - 2); + if (strcmp(s->far_password, far_password) == 0) + s->far_password_ok = TRUE; + } + else + { + unexpected_frame(s, msg, len); + } + break; + case T30_SEP: + if (msg[2] == T30_SEP) + { + /* Selective polling */ + /* OK in (PWD) (SEP) (CIG) DTC */ + decode_20digit_msg(s, s->sep_address, &msg[2], len - 2); + } + else + { + unexpected_frame(s, msg, len); + } + break; + case T30_PSA: + if (msg[2] == T30_PSA) + { + /* Polled subaddress */ + decode_20digit_msg(s, s->psa_address, &msg[2], len - 2); + } + else + { + unexpected_frame(s, msg, len); + } + break; + case T30_CIA: + if (msg[2] == T30_CIA) + { + /* Calling subscriber internet address */ + decode_url_msg(s, NULL, &msg[2], len - 2); + } + else + { + unexpected_frame(s, msg, len); + } + break; + case T30_ISP: + if (msg[2] == T30_ISP) + { + /* Internet selective polling address */ + decode_url_msg(s, NULL, &msg[2], len - 2); + } + else + { + unexpected_frame(s, msg, len); + } + break; + case T30_TSI: + /* Transmitting subscriber identity */ + /* OK in (TSI) DCS */ + /* OK in (PWD) (SUB) (TSI) DCS */ + decode_20digit_msg(s, s->far_ident, &msg[2], len - 2); + break; + case T30_SUB: + /* Subaddress */ + /* OK in (PWD) (SUB) (TSI) DCS */ + decode_20digit_msg(s, s->far_sub_address, &msg[2], len - 2); + break; + case T30_SID: + /* Sender Identification */ + /* T.30 does not say where this is OK */ + decode_20digit_msg(s, NULL, &msg[2], len - 2); + break; + case T30_CSA: + /* Calling subscriber internet address */ + decode_url_msg(s, NULL, &msg[2], len - 2); + break; + case T30_TSA: + /* Transmitting subscriber internet address */ + decode_url_msg(s, NULL, &msg[2], len - 2); + break; + case T30_IRA: + /* 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: + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected %s frame\n", t30_frametype(msg[2])); + break; + } + } + else + { + /* 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 (msg[2] & 0xFE) + { + case T30_DIS: + process_rx_dis_or_dtc(s, msg, len); + break; + case T30_DCS: + process_rx_dcs(s, msg, len); + break; + case T30_NSS: + process_rx_nss(s, msg, len); + break; + case T30_CTC: + process_rx_ctc(s, msg, len); + break; + case T30_CFR: + process_rx_cfr(s, msg, len); + break; + case T30_FTT: + process_rx_ftt(s, msg, len); + break; + case T30_CTR: + process_rx_ctr(s, msg, len); + break; + case T30_EOM: + process_rx_eom(s, msg, len); + break; + case T30_MPS: + process_rx_mps(s, msg, len); + break; + case T30_EOP: + process_rx_eop(s, msg, len); + break; + case T30_PRI_EOM: + process_rx_pri_eom(s, msg, len); + break; + case T30_PRI_MPS: + process_rx_pri_mps(s, msg, len); + break; + case T30_PRI_EOP: + process_rx_pri_eop(s, msg, len); + break; + case T30_EOS: + process_rx_eos(s, msg, len); + break; + case T30_PPS: + process_rx_pps(s, msg, len); + break; + case T30_EOR: + process_rx_eor(s, msg, len); + break; + case T30_RR: + process_rx_rr(s, msg, len); + break; + case T30_MCF: + process_rx_mcf(s, msg, len); + break; + case T30_RTP: + process_rx_rtp(s, msg, len); + break; + case T30_RTN: + process_rx_rtn(s, msg, len); + break; + case T30_PIP: + process_rx_pip(s, msg, len); + break; + case T30_PIN: + process_rx_pin(s, msg, len); + break; + case T30_PPR: + process_rx_ppr(s, msg, len); + break; + case T30_RNR: + process_rx_rnr(s, msg, len); + break; + case T30_ERR: + process_rx_err(s, msg, len); + break; + case T30_FDM: + process_rx_fdm(s, msg, len); + break; + case T30_DCN: + process_rx_dcn(s, msg, len); + break; + case T30_CRP: + process_rx_crp(s, msg, len); + break; + case T30_FNV: + process_rx_fnv(s, msg, len); + break; + case T30_TNR: + process_rx_tnr(s, msg, len); + break; + case T30_TR: + process_rx_tr(s, msg, len); + break; + case T4_RCP: + process_rx_rcp(s, msg, len); + break; + default: + /* We don't know what to do with this. */ + unexpected_final_frame(s, msg, len); + break; + } + } +} +/*- End of function --------------------------------------------------------*/ + +void t30_hdlc_accept(void *user_data, int ok, const uint8_t *msg, int len) +{ + t30_state_t *s; + + s = (t30_state_t *) user_data; + if (len < 0) + { + /* Special conditions */ + switch (len) + { + case PUTBIT_TRAINING_FAILED: + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC carrier training failed in state %d\n", s->state); + s->rx_trained = FALSE; + /* Cancel the timer, since we have actually seen something, and wait until the carrier drops + before proceeding. */ + // TODO: this is not a complete answer to handling failures to train + s->timer_t2_t4 = 0; + break; + case PUTBIT_TRAINING_SUCCEEDED: + /* The modem is now trained */ + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC carrier trained in state %d\n", s->state); + s->rx_signal_present = TRUE; + s->rx_trained = TRUE; + break; + case PUTBIT_CARRIER_UP: + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC carrier up in state %d\n", s->state); + s->rx_signal_present = TRUE; + break; + case PUTBIT_CARRIER_DOWN: + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC carrier down in state %d\n", s->state); + s->rx_signal_present = FALSE; + s->rx_trained = FALSE; + /* If a phase change has been queued to occur after the receive signal drops, + its time to change. */ + if (s->next_phase != T30_PHASE_IDLE) + { + set_phase(s, s->next_phase); + s->next_phase = T30_PHASE_IDLE; + } + break; + case PUTBIT_FRAMING_OK: + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC framing OK in state %d\n", s->state); + 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 */ + if (!s->timer_is_t4 && s->timer_t2_t4 > 0) + s->timer_t2_t4 = 0; + break; + case PUTBIT_ABORT: + /* Just ignore these */ + break; + default: + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected HDLC special length - %d!\n", len); + break; + } + return; + } + + /* 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 CRC received\n"); + if (s->crp_enabled) + send_simple_frame(s, T30_CRP); + return; + } + + /* Cancel the command or response timer */ + s->timer_t2_t4 = 0; + if (len < 3) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Bad HDLC frame length - %d\n", len); + return; + } + if (msg[0] != 0xFF || !(msg[1] == 0x03 || msg[1] == 0x13)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Bad HDLC frame header - %02x %02x\n", msg[0], msg[1]); + return; + } + print_frame(s, "Rx: ", msg, len); + + switch (s->phase) + { + case T30_PHASE_A_CED: + case T30_PHASE_A_CNG: + case T30_PHASE_B_RX: + case T30_PHASE_C_ECM_RX: + case T30_PHASE_D_RX: + break; + default: + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected HDLC frame received in phase %s, state %d\n", phase_names[s->phase], s->state); + break; + } + hdlc_accept_control_msg(s, ok, msg, len); +} +/*- 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) + { + 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->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, FALSE, TRUE); + if (s->set_tx_type_handler) + s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_CED, 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, FALSE, TRUE); + if (s->set_tx_type_handler) + s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_CNG, 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, FALSE, TRUE); + if (s->set_tx_type_handler) + s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_NONE, 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, FALSE, FALSE); + if (s->set_tx_type_handler) + s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_V21, FALSE, TRUE); + break; + case T30_PHASE_C_NON_ECM_RX: + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2); + s->timer_is_t4 = FALSE; + if (s->set_rx_type_handler) + s->set_rx_type_handler(s->set_rx_type_user_data, fallback_sequence[s->current_fallback].modem_type, s->short_train, FALSE); + if (s->set_tx_type_handler) + s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_NONE, 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->training_test_bits = (3*fallback_sequence[s->current_fallback].bit_rate)/2; + if (s->set_rx_type_handler) + s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_NONE, 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, s->short_train, FALSE); + break; + case T30_PHASE_C_ECM_RX: + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2); + s->timer_is_t4 = FALSE; + if (s->set_rx_type_handler) + s->set_rx_type_handler(s->set_rx_type_user_data, fallback_sequence[s->current_fallback].modem_type, s->short_train, TRUE); + if (s->set_tx_type_handler) + s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_NONE, 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, 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, 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->training_current_zeros = 0; + s->training_most_zeros = 0; + if (s->set_rx_type_handler) + s->set_rx_type_handler(s->set_rx_type_user_data, T30_MODEM_NONE, FALSE, FALSE); + if (s->set_tx_type_handler) + s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_PAUSE, 200, 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, FALSE, FALSE); + if (s->set_tx_type_handler) + s->set_tx_type_handler(s->set_tx_type_user_data, T30_MODEM_DONE, 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 --------------------------------------------------------*/ + +void t30_receive_complete(void *user_data) +{ + t30_state_t *s; + + s = (t30_state_t *) user_data; + + 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. */ + if (s->phase == T30_PHASE_C_NON_ECM_RX) + t30_non_ecm_put_bit(s, PUTBIT_CARRIER_DOWN); + else + t30_hdlc_accept(s, TRUE, NULL, PUTBIT_CARRIER_DOWN); +} +/*- End of function --------------------------------------------------------*/ + +void t30_send_complete(void *user_data) +{ + t30_state_t *s; + + s = (t30_state_t *) user_data; + + 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); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2); + s->timer_is_t4 = FALSE; + set_state(s, T30_STATE_R); + s->dis_received = FALSE; + send_dis_or_dtc_sequence(s); + break; + case T30_STATE_R: + switch (s->step) + { + case 0: + s->step++; + if (send_ident_frame(s, T30_CSI)) + break; + /* Fall through */ + case 1: + s->step++; + set_dis_or_dtc(s); + send_frame(s, s->dis_dtc_frame, s->dis_dtc_len); + break; + case 2: + s->step++; + if (s->send_hdlc_handler) + s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0); + break; + default: + /* Wait for an acknowledgement. */ + set_phase(s, T30_PHASE_B_RX); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T4); + s->timer_is_t4 = TRUE; + break; + } + break; + case T30_STATE_F_CFR: + if (s->step == 0) + { + if (s->send_hdlc_handler) + s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0); + s->step++; + } + else + { + set_state(s, T30_STATE_F_DOC); + set_phase(s, (s->error_correcting_mode) ? T30_PHASE_C_ECM_RX : T30_PHASE_C_NON_ECM_RX); + s->next_rx_step = T30_MPS; + } + break; + case T30_STATE_F_FTT: + if (s->step == 0) + { + 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); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T4); + s->timer_is_t4 = TRUE; + } + 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) + { + 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: + set_state(s, T30_STATE_F_DOC); + set_phase(s, (s->error_correcting_mode) ? T30_PHASE_C_ECM_RX : T30_PHASE_C_NON_ECM_RX); + break; + case T30_EOM: + case T30_PRI_EOM: + /* TODO: */ + disconnect(s); + break; + case T30_EOP: + case T30_PRI_EOP: + disconnect(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: + if (s->step == 0) + { + 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); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T4); + s->timer_is_t4 = TRUE; + } + 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); + break; + case T30_STATE_C: + if (s->step == 0) + { + 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: + switch (s->step) + { + case 0: + s->step++; + if (send_sub_frame(s)) + break; + /* Fall through */ + case 1: + s->step++; + if (send_ident_frame(s, T30_TSI)) + break; + /* Fall through */ + case 2: + s->step++; + send_frame(s, s->dcs_frame, s->dcs_len); + break; + case 3: + s->step++; + if (s->send_hdlc_handler) + s->send_hdlc_handler(s->send_hdlc_user_data, NULL, 0); + break; + default: + 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 */ + set_state(s, T30_STATE_D_TCF); + set_phase(s, T30_PHASE_C_NON_ECM_TX); + } + break; + } + break; + case T30_STATE_D_TCF: + /* Finished sending training test. Listen for the response. */ + set_phase(s, T30_PHASE_B_RX); + s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T4); + s->timer_is_t4 = TRUE; + 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)) + { + 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_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 in t30_send_complete - %d\n", s->state); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void repeat_last_command(t30_state_t *s) +{ + switch (s->state) + { + case T30_STATE_R: + set_phase(s, T30_PHASE_B_TX); + s->dis_received = FALSE; + send_dis_or_dtc_sequence(s); + break; + case T30_STATE_III_Q_MCF: + set_phase(s, T30_PHASE_D_TX); + send_simple_frame(s, T30_MCF); + break; + case T30_STATE_III_Q_RTP: + set_phase(s, T30_PHASE_D_TX); + send_simple_frame(s, T30_RTP); + break; + case T30_STATE_III_Q_RTN: + set_phase(s, T30_PHASE_D_TX); + send_simple_frame(s, T30_RTN); + break; + case T30_STATE_II_Q: + set_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: + set_phase(s, T30_PHASE_D_TX); + send_pps_frame(s); + break; + case T30_STATE_IV_PPS_RNR: + case T30_STATE_IV_EOR_RNR: + set_phase(s, T30_PHASE_D_TX); + send_simple_frame(s, T30_RNR); + break; + case T30_STATE_D: + send_dcs_sequence(s); + break; + case T30_STATE_F_FTT: + set_phase(s, T30_PHASE_B_TX); + send_simple_frame(s, T30_FTT); + break; + case T30_STATE_F_CFR: + set_phase(s, T30_PHASE_B_TX); + send_simple_frame(s, T30_CFR); + 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_t0_expired(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "T0 timeout in state %d\n", s->state); + s->current_status = T30_ERR_T0EXPIRED; + /* 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 timeout 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_T1EXPIRED; + switch (s->state) + { + case T30_STATE_T: + /* Just end the call */ + disconnect(s); + break; + case T30_STATE_R: + /* Send disconnect, and then end the call. Since we have not + successfully contacted the far end, it is unclear why we should + send a disconnect message at this point. However, it is what T.30 + says we should do. */ + send_dcn(s); + break; + } +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t2_expired(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "T2 timeout in phase %s, state %d\n", phase_names[s->phase], s->state); + switch (s->state) + { + case T30_STATE_F_DOC: + /* While waiting for FAX page */ + s->current_status = T30_ERR_T2EXPFAXRX; + break; + case T30_STATE_F_POST_DOC_NON_ECM: + /* While waiting for next FAX page */ + s->current_status = T30_ERR_T2EXPMPSRX; + break; +#if 0 + case ??????: + /* While waiting for DCN */ + s->current_status = T30_ERR_T2EXPDCNRX; + break; + case ??????: + /* While waiting for phase D */ + s->current_status = T30_ERR_T2EXPDRX; + break; +#endif + case T30_STATE_IV_PPS_RNR: + case T30_STATE_IV_EOR_RNR: + /* While waiting for RR command */ + s->current_status = T30_ERR_T2EXPRRRX; + break; + case T30_STATE_R: + /* While waiting for NSS, DCS or MCF */ + s->current_status = T30_ERR_T2EXPRX; + break; + } + set_phase(s, T30_PHASE_B_TX); + start_receiving_document(s); +} +/*- End of function --------------------------------------------------------*/ + +static void timer_t3_expired(t30_state_t *s) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "T3 timeout in phase %s, state %d\n", phase_names[s->phase], s->state); + s->current_status = T30_ERR_T3EXPIRED; + 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 */ + span_log(&s->logging, SPAN_LOG_FLOW, "T4 timeout in phase %s, state %d\n", phase_names[s->phase], s->state); + if (++s->retries > MAX_MESSAGE_TRIES) + { + s->current_status = T30_ERR_RETRYDCN; + send_dcn(s); + return; + } + repeat_last_command(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 */ + send_dcn(s); +} +/*- End of function --------------------------------------------------------*/ + +void t30_timer_update(t30_state_t *s, int samples) +{ + if (s->timer_t0_t1 > 0) + { + s->timer_t0_t1 -= samples; + if (s->timer_t0_t1 <= 0) + { + if (s->far_end_detected) + timer_t1_expired(s); + else + timer_t0_expired(s); + } + } + if (s->timer_t3 > 0) + { + s->timer_t3 -= samples; + if (s->timer_t3 <= 0) + timer_t3_expired(s); + } + if (s->timer_t2_t4 > 0) + { + s->timer_t2_t4 -= samples; + if (s->timer_t2_t4 <= 0) + { + if (s->timer_is_t4) + timer_t4_expired(s); + else + timer_t2_expired(s); + } + } + if (s->timer_t5 > 0) + { + s->timer_t5 -= samples; + if (s->timer_t5 <= 0) + timer_t5_expired(s); + } +} +/*- 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[20 + 1]; + + if (msg == NULL) + msg = text; + if (len > T30_MAX_IDENT_LEN) + { + 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 fax 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 --------------------------------------------------------*/ + +const char *t30_frametype(uint8_t x) +{ + switch (x & 0xFE) + { + case T30_DIS: + if (x == T30_DTC) + return "DTC"; + return "DIS"; + case T30_CSI: + if (x == T30_CIG) + return "CIG"; + return "CSI"; + case T30_NSF: + if (x == T30_NSC) + return "NSC"; + return "NSF"; + case T30_PWD & 0xFE: + if (x == T30_PWD) + return "PWD"; + break; + case T30_SEP & 0xFE: + if (x == T30_SEP) + return "SEP"; + break; + case T30_PSA & 0xFE: + if (x == T30_PSA) + return "PSA"; + break; + case T30_CIA & 0xFE: + if (x == T30_CIA) + return "CIA"; + break; + case T30_ISP & 0xFE: + if (x == T30_ISP) + return "ISP"; + break; + case T30_DCS: + return "DCS"; + case T30_TSI: + return "TSI"; + case T30_NSS: + return "NSS"; + case T30_SUB: + return "SUB"; + case T30_SID: + return "SID"; + case T30_CTC: + return "CTC"; + case T30_TSA: + return "TSA"; + case T30_IRA: + return "IRA"; + case T30_CFR: + return "CFR"; + case T30_FTT: + return "FTT"; + case T30_CTR: + return "CTR"; + case T30_CSA: + return "CSA"; + case T30_EOM: + return "EOM"; + case T30_MPS: + return "MPS"; + case T30_EOP: + return "EOP"; + case T30_PRI_EOM: + return "PRI_EOM"; + case T30_PRI_MPS: + return "PRI_MPS"; + case T30_PRI_EOP: + return "PRI_EOP"; + case T30_EOS: + return "EOS"; + case T30_PPS: + return "PPS"; + case T30_EOR: + return "EOR"; + case T30_RR: + return "RR"; + case T30_MCF: + return "MCF"; + case T30_RTP: + return "RTP"; + case T30_RTN: + return "RTN"; + case T30_PIP: + return "PIP"; + case T30_PIN: + return "PIN"; + case T30_PPR: + return "PPR"; + case T30_RNR: + return "RNR"; + case T30_ERR: + return "ERR"; + case T30_FDM: + return "FDM"; + case T30_DCN: + return "DCN"; + case T30_CRP: + return "CRP"; + case T30_FNV: + return "FNV"; + case T30_TNR: + return "TNR"; + case T30_TR: + return "TR"; + case T30_PID: + return "PID"; + case T30_NULL: + return "NULL"; + case T4_FCD: + return "FCD"; + case T4_RCP: + return "RCP"; + } + return "???"; +} +/*- End of function --------------------------------------------------------*/ + +static void octet_reserved_bit(logging_state_t *log, + const uint8_t *msg, + int bit_no, + int expected) +{ + char s[10] = ".... ...."; + int bit; + uint8_t octet; + + /* Break out the octet and the bit number within it. */ + octet = msg[((bit_no - 1) >> 3) + 3]; + bit_no = (bit_no - 1) & 7; + /* Now get the actual bit. */ + bit = (octet >> bit_no) & 1; + /* Is it what it should be. */ + if (bit ^ expected) + { + /* Only log unexpected values. */ + s[7 - bit_no + ((bit_no < 4) ? 1 : 0)] = (uint8_t) (bit + '0'); + span_log(log, SPAN_LOG_FLOW, " %s= Unexpected state for reserved bit: %d\n", s, bit); + } +} +/*- End of function --------------------------------------------------------*/ + +static void octet_bit_field(logging_state_t *log, + const uint8_t *msg, + int bit_no, + const char *desc, + const char *yeah, + const char *neigh) +{ + char s[10] = ".... ...."; + int bit; + uint8_t octet; + const char *tag; + + /* Break out the octet and the bit number within it. */ + octet = msg[((bit_no - 1) >> 3) + 3]; + bit_no = (bit_no - 1) & 7; + /* Now get the actual bit. */ + bit = (octet >> bit_no) & 1; + /* Edit the bit string for display. */ + s[7 - bit_no + ((bit_no < 4) ? 1 : 0)] = (uint8_t) (bit + '0'); + /* Find the right tag to display. */ + if (bit) + { + if ((tag = yeah) == NULL) + tag = "Set"; + } + else + { + if ((tag = neigh) == NULL) + tag = "Not set"; + } + /* Eh, voila! */ + span_log(log, SPAN_LOG_FLOW, " %s= %s: %s\n", s, desc, tag); +} +/*- End of function --------------------------------------------------------*/ + +static void octet_field(logging_state_t *log, + const uint8_t *msg, + int start, + int end, + const char *desc, + const value_string_t tags[]) +{ + char s[10] = ".... ...."; + int i; + uint8_t octet; + const char *tag; + + /* Break out the octet and the bit number range within it. */ + octet = msg[((start - 1) >> 3) + 3]; + start = (start - 1) & 7; + end = ((end - 1) & 7) + 1; + + /* Edit the bit string for display. */ + for (i = start; i < end; i++) + s[7 - i + ((i < 4) ? 1 : 0)] = (uint8_t) ((octet >> i) & 1) + '0'; + + /* Find the right tag to display. */ + octet = (uint8_t) ((octet >> start) & ((0xFF + (1 << (end - start))) & 0xFF)); + tag = "Invalid"; + for (i = 0; tags[i].str; i++) + { + if (octet == tags[i].val) + { + tag = tags[i].str; + break; + } + } + /* Eh, voila! */ + span_log(log, SPAN_LOG_FLOW, " %s= %s: %s\n", s, desc, tag); +} +/*- End of function --------------------------------------------------------*/ + +void t30_decode_dis_dtc_dcs(t30_state_t *s, const uint8_t *pkt, int len) +{ + logging_state_t *log; + uint8_t frame_type; + static const value_string_t available_signalling_rate_tags[] = + { + { 0x00, "V.27 ter fall-back mode" }, + { 0x01, "V.29" }, + { 0x02, "V.27 ter" }, + { 0x03, "V.27 ter and V.29" }, + { 0x0B, "V.27 ter, V.29, and V.17" }, + { 0x06, "Reserved" }, + { 0x0A, "Reserved" }, + { 0x0E, "Reserved" }, + { 0x0F, "Reserved" }, + { 0x04, "Not used" }, + { 0x05, "Not used" }, + { 0x08, "Not used" }, + { 0x09, "Not used" }, + { 0x0C, "Not used" }, + { 0x0D, "Not used" }, + { 0x00, NULL } + }; + static const value_string_t selected_signalling_rate_tags[] = + { + { 0x00, "V.27ter 2400bps" }, + { 0x01, "V.29, 9600bps" }, + { 0x02, "V.27ter 4800bps" }, + { 0x03, "V.29 7200bps" }, + { 0x08, "V.17 14400bps" }, + { 0x09, "V.17 9600bps" }, + { 0x0A, "V.17 12000bps" }, + { 0x0B, "V.17 7200bps" }, + { 0x05, "Reserved" }, + { 0x07, "Reserved" }, + { 0x0C, "Reserved" }, + { 0x0D, "Reserved" }, + { 0x0E, "Reserved" }, + { 0x0F, "Reserved" }, + { 0x00, NULL } + }; + static const value_string_t available_scan_line_length_tags[] = + { + { 0x00, "215 mm +- 1%" }, + { 0x01, "215 mm +- 1% and 255 mm +- 1%" }, + { 0x02, "215 mm +- 1% and 255 mm +- 1% and 303 mm +- 1%" }, + { 0x00, NULL } + }; + static const value_string_t selected_scan_line_length_tags[] = + { + { 0x00, "215 mm +- 1%" }, + { 0x01, "255 mm +- 1%" }, + { 0x02, "303 mm +- 1%" }, + { 0x00, NULL } + }; + static const value_string_t available_recording_length_tags[] = + { + { 0x00, "A4 (297 mm)" }, + { 0x01, "A4 (297 mm) and B4 (364 mm)" }, + { 0x02, "Unlimited" }, + { 0x00, NULL } + }; + static const value_string_t selected_recording_length_tags[] = + { + { 0x00, "A4 (297 mm)" }, + { 0x01, "B4 (364 mm)" }, + { 0x02, "Unlimited" }, + { 0x00, NULL } + }; + static const value_string_t available_minimum_scan_line_time_tags[] = + { + { 0x00, "20 ms at 3.85 l/mm: T7.7 = T3.85" }, + { 0x01, "5 ms at 3.85 l/mm: T7.7 = T3.85" }, + { 0x02, "10 ms at 3.85 l/mm: T7.7 = T3.85" }, + { 0x03, "20 ms at 3.85 l/mm: T7.7 = 1/2 T3.85" }, + { 0x04, "40 ms at 3.85 l/mm: T7.7 = T3.85" }, + { 0x05, "40 ms at 3.85 l/mm: T7.7 = 1/2 T3.85" }, + { 0x06, "10 ms at 3.85 l/mm: T7.7 = 1/2 T3.85" }, + { 0x07, "0 ms at 3.85 l/mm: T7.7 = T3.85" }, + { 0x00, NULL } + }; + static const value_string_t selected_minimum_scan_line_time_tags[] = + { + { 0x00, "20 ms" }, + { 0x01, "40 ms" }, + { 0x02, "10 ms" }, + { 0x04, "5 ms" }, + { 0x07, "0 ms" }, + { 0x00, NULL } + }; + static const value_string_t shared_data_memory_capacity_tags[] = + { + { 0x00, "Not available" }, + { 0x01, "Level 2 = 2.0 Mbytes" }, + { 0x02, "Level 1 = 1.0 Mbytes" }, + { 0x03, "Level 3 = unlimited (i.e. >= 32 Mbytes)" }, + { 0x00, NULL } + }; + static const value_string_t t89_profile_tags[] = + { + { 0x00, "Not used" }, + { 0x01, "Profiles 2 and 3" }, + { 0x02, "Profile 2" }, + { 0x04, "Profile 1" }, + { 0x06, "Profile 3" }, + { 0x03, "Reserved" }, + { 0x05, "Reserved" }, + { 0x07, "Reserved" }, + { 0x00, NULL } + }; + static const value_string_t t44_mixed_raster_content_tags[] = + { + { 0x00, "0" }, + { 0x01, "1" }, + { 0x02, "2" }, + { 0x32, "3" }, + { 0x04, "4" }, + { 0x05, "5" }, + { 0x06, "6" }, + { 0x07, "7" }, + { 0x00, NULL } + }; + + if (!span_log_test(&s->logging, SPAN_LOG_FLOW)) + return; + frame_type = pkt[2] & 0xFE; + log = &s->logging; + if (len <= 2) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + span_log(log, SPAN_LOG_FLOW, "%s:\n", t30_frametype(pkt[2])); + if (len <= 3) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + octet_bit_field(log, pkt, 1, "Store and forward Internet fax (T.37)", NULL, NULL); + octet_reserved_bit(log, pkt, 2, 0); + octet_bit_field(log, pkt, 3, "Real-time Internet fax (T.38)", NULL, NULL); + octet_bit_field(log, pkt, 4, "3G mobile network", NULL, NULL); + octet_reserved_bit(log, pkt, 5, 0); + if (frame_type == T30_DCS) + { + octet_reserved_bit(log, pkt, 6, 0); + octet_reserved_bit(log, pkt, 7, 0); + } + else + { + octet_bit_field(log, pkt, 6, "V.8 capabilities", NULL, NULL); + octet_bit_field(log, pkt, 7, "Preferred octets", "64 octets", "256 octets"); + } + octet_reserved_bit(log, pkt, 8, 0); + if (len <= 4) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + if (frame_type == T30_DCS) + octet_reserved_bit(log, pkt, 9, 0); + else + octet_bit_field(log, pkt, 9, "Ready to transmit a fax document (polling)", NULL, NULL); + octet_bit_field(log, pkt, 10, "Can receive fax", NULL, NULL); + if (frame_type == T30_DCS) + octet_field(log, pkt, 11, 14, "Selected data signalling rate", selected_signalling_rate_tags); + else + octet_field(log, pkt, 11, 14, "Supported data signalling rates", available_signalling_rate_tags); + octet_bit_field(log, pkt, 15, "R8x7.7lines/mm and/or 200x200pels/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 16, "2-D coding", NULL, NULL); + if (len <= 5) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + if (frame_type == T30_DCS) + { + octet_field(log, pkt, 17, 18, "Recording width", selected_scan_line_length_tags); + octet_field(log, pkt, 19, 20, "Recording length", selected_recording_length_tags); + octet_field(log, pkt, 21, 23, "Minimum scan line time", selected_minimum_scan_line_time_tags); + } + else + { + octet_field(log, pkt, 17, 18, "Recording width", available_scan_line_length_tags); + octet_field(log, pkt, 19, 20, "Recording length", available_recording_length_tags); + octet_field(log, pkt, 21, 23, "Receiver's minimum scan line time", available_minimum_scan_line_time_tags); + } + octet_bit_field(log, pkt, 24, "Extension indicator", NULL, NULL); + if (!(pkt[5] & DISBIT8)) + return; + if (len <= 6) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_reserved_bit(log, pkt, 25, 0); + octet_bit_field(log, pkt, 26, "Compressed/uncompressed mode", "Uncompressed", "Compressed"); + octet_bit_field(log, pkt, 27, "Error correction mode (ECM)", "ECM", "Non-ECM"); + if (frame_type == T30_DCS) + octet_bit_field(log, pkt, 28, "Frame size", "64 octets", "256 octets"); + else + octet_reserved_bit(log, pkt, 28, 0); + octet_reserved_bit(log, pkt, 29, 0); + octet_reserved_bit(log, pkt, 30, 0); + octet_bit_field(log, pkt, 31, "T.6 coding", NULL, NULL); + octet_bit_field(log, pkt, 32, "Extension indicator", NULL, NULL); + if (!(pkt[6] & DISBIT8)) + return; + if (len <= 7) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 33, "\"Field not valid\" supported", NULL, NULL); + if (frame_type == T30_DCS) + { + octet_reserved_bit(log, pkt, 34, 0); + octet_reserved_bit(log, pkt, 35, 0); + } + else + { + octet_bit_field(log, pkt, 34, "Multiple selective polling", NULL, NULL); + octet_bit_field(log, pkt, 35, "Polled subaddress", NULL, NULL); + } + octet_bit_field(log, pkt, 36, "T.43 coding", NULL, NULL); + octet_bit_field(log, pkt, 37, "Plane interleave", NULL, NULL); + octet_bit_field(log, pkt, 38, "Voice coding with 32kbit/s ADPCM (Rec. G.726)", NULL, NULL); + octet_bit_field(log, pkt, 39, "Reserved for the use of extended voice coding set", NULL, NULL); + octet_bit_field(log, pkt, 40, "Extension indicator", NULL, NULL); + if (!(pkt[7] & DISBIT8)) + return; + if (len <= 8) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 41, "R8x15.4lines/mm", NULL, NULL); + octet_bit_field(log, pkt, 42, "300x300pels/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 43, "R16x15.4lines/mm and/or 400x400pels/25.4 mm", NULL, NULL); + if (frame_type == T30_DCS) + { + octet_bit_field(log, pkt, 44, "Resolution type selection", "Inch", "Metric"); + octet_reserved_bit(log, pkt, 45, 0); + octet_reserved_bit(log, pkt, 46, 0); + octet_reserved_bit(log, pkt, 47, 0); + } + else + { + octet_bit_field(log, pkt, 44, "Inch-based resolution preferred", NULL, NULL); + octet_bit_field(log, pkt, 45, "Metric-based resolution preferred", NULL, NULL); + octet_bit_field(log, pkt, 46, "Minimum scan line time for higher resolutions", "T15.4 = 1/2 T7.7", "T15.4 = T7.7"); + octet_bit_field(log, pkt, 47, "Selective polling", NULL, NULL); + } + octet_bit_field(log, pkt, 48, "Extension indicator", NULL, NULL); + if (!(pkt[8] & DISBIT8)) + return; + if (len <= 9) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 49, "Subaddressing", NULL, NULL); + if (frame_type == T30_DCS) + { + octet_bit_field(log, pkt, 50, "Sender identification transmission", NULL, NULL); + octet_reserved_bit(log, pkt, 51, 0); + } + else + { + octet_bit_field(log, pkt, 50, "Password", NULL, NULL); + octet_bit_field(log, pkt, 51, "Ready to transmit a data file (polling)", NULL, NULL); + } + octet_reserved_bit(log, pkt, 52, 0); + octet_bit_field(log, pkt, 53, "Binary file transfer (BFT)", NULL, NULL); + octet_bit_field(log, pkt, 54, "Document transfer mode (DTM)", NULL, NULL); + octet_bit_field(log, pkt, 55, "Electronic data interchange (EDI)", NULL, NULL); + octet_bit_field(log, pkt, 56, "Extension indicator", NULL, NULL); + if (!(pkt[9] & DISBIT8)) + return; + if (len <= 10) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 57, "Basic transfer mode (BTM)", NULL, NULL); + octet_reserved_bit(log, pkt, 58, 0); + if (frame_type == T30_DCS) + octet_reserved_bit(log, pkt, 59, 0); + else + octet_bit_field(log, pkt, 59, "Ready to transfer a character or mixed mode document (polling)", NULL, NULL); + octet_bit_field(log, pkt, 60, "Character mode", NULL, NULL); + octet_reserved_bit(log, pkt, 61, 0); + octet_bit_field(log, pkt, 62, "Mixed mode (Annex E/T.4)", NULL, NULL); + octet_reserved_bit(log, pkt, 63, 0); + octet_bit_field(log, pkt, 64, "Extension indicator", NULL, NULL); + if (!(pkt[10] & DISBIT8)) + return; + if (len <= 11) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 65, "Processable mode 26 (Rec. T.505)", NULL, NULL); + octet_bit_field(log, pkt, 66, "Digital network capability", NULL, NULL); + octet_bit_field(log, pkt, 67, "Duplex capability", "Full", "Half only"); + if (frame_type == T30_DCS) + octet_bit_field(log, pkt, 68, "Full colour mode", NULL, NULL); + else + octet_bit_field(log, pkt, 68, "JPEG coding", NULL, NULL); + octet_bit_field(log, pkt, 69, "Full colour mode", NULL, NULL); + if (frame_type == T30_DCS) + octet_bit_field(log, pkt, 70, "Preferred Huffman tables", NULL, NULL); + else + octet_reserved_bit(log, pkt, 70, 0); + octet_bit_field(log, pkt, 71, "12bits/pel component", NULL, NULL); + octet_bit_field(log, pkt, 72, "Extension indicator", NULL, NULL); + if (!(pkt[11] & DISBIT8)) + return; + if (len <= 12) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 73, "No subsampling (1:1:1)", NULL, NULL); + octet_bit_field(log, pkt, 74, "Custom illuminant", NULL, NULL); + octet_bit_field(log, pkt, 75, "Custom gamut range", NULL, NULL); + octet_bit_field(log, pkt, 76, "North American Letter (215.9mm x 279.4mm)", NULL, NULL); + octet_bit_field(log, pkt, 77, "North American Legal (215.9mm x 355.6mm)", NULL, NULL); + octet_bit_field(log, pkt, 78, "Single-progression sequential coding (Rec. T.85) basic", NULL, NULL); + octet_bit_field(log, pkt, 79, "Single-progression sequential coding (Rec. T.85) optional L0", NULL, NULL); + octet_bit_field(log, pkt, 80, "Extension indicator", NULL, NULL); + if (!(pkt[12] & DISBIT8)) + return; + if (len <= 13) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 81, "HKM key management", NULL, NULL); + octet_bit_field(log, pkt, 82, "RSA key management", NULL, NULL); + octet_bit_field(log, pkt, 83, "Override", NULL, NULL); + octet_bit_field(log, pkt, 84, "HFX40 cipher", NULL, NULL); + octet_bit_field(log, pkt, 85, "Alternative cipher number 2", NULL, NULL); + octet_bit_field(log, pkt, 86, "Alternative cipher number 3", NULL, NULL); + octet_bit_field(log, pkt, 87, "HFX40-I hashing", NULL, NULL); + octet_bit_field(log, pkt, 88, "Extension indicator", NULL, NULL); + if (!(pkt[13] & DISBIT8)) + return; + if (len <= 14) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 89, "Alternative hashing system 2", NULL, NULL); + octet_bit_field(log, pkt, 90, "Alternative hashing system 3", NULL, NULL); + octet_bit_field(log, pkt, 91, "Reserved for future security features", NULL, NULL); + octet_field(log, pkt, 92, 94, "T.44 (Mixed Raster Content)", t44_mixed_raster_content_tags); + octet_bit_field(log, pkt, 95, "Page length maximum stripe size for T.44 (Mixed Raster Content)", NULL, NULL); + octet_bit_field(log, pkt, 96, "Extension indicator", NULL, NULL); + if (!(pkt[14] & DISBIT8)) + return; + if (len <= 15) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 97, "Colour/gray-scale 300pels/25.4mm x 300lines/25.4mm or 400pels/25.4mm x 400lines/25.4mm resolution", NULL, NULL); + octet_bit_field(log, pkt, 98, "100pels/25.4mm x 100lines/25.4mm for colour/gray scale", NULL, NULL); + octet_bit_field(log, pkt, 99, "Simple phase C BFT negotiations", NULL, NULL); + if (frame_type == T30_DCS) + { + octet_reserved_bit(log, pkt, 100, 0); + octet_reserved_bit(log, pkt, 101, 0); + } + else + { + octet_bit_field(log, pkt, 100, "Extended BFT Negotiations capable", NULL, NULL); + octet_bit_field(log, pkt, 101, "Internet Selective Polling address (ISP)", NULL, NULL); + } + octet_bit_field(log, pkt, 102, "Internet Routing Address (IRA)", NULL, NULL); + octet_reserved_bit(log, pkt, 103, 0); + octet_bit_field(log, pkt, 104, "Extension indicator", NULL, NULL); + if (!(pkt[15] & DISBIT8)) + return; + if (len <= 16) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 105, "600pels/25.4mm x 600lines/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 106, "1200pels/25.4mm x 1200lines/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 107, "300pels/25.4mm x 600lines/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 108, "400pels/25.4mm x 800lines/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 109, "600pels/25.4mm x 1200lines/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 110, "Colour/gray scale 600pels/25.4mm x 600lines/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 111, "Colour/gray scale 1200pels/25.4mm x 1200lines/25.4mm", NULL, NULL); + octet_bit_field(log, pkt, 112, "Extension indicator", NULL, NULL); + if (!(pkt[16] & DISBIT8)) + return; + if (len <= 17) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 113, "Double sided printing capability (alternate mode)", NULL, NULL); + octet_bit_field(log, pkt, 114, "Double sided printing capability (continuous mode)", NULL, NULL); + if (frame_type == T30_DCS) + octet_bit_field(log, pkt, 115, "Black and white mixed raster content profile (MRCbw)", NULL, NULL); + else + octet_reserved_bit(log, pkt, 115, 0); + octet_bit_field(log, pkt, 116, "T.45 (run length colour encoded)", NULL, NULL); + octet_field(log, pkt, 117, 118, "Shared memory", shared_data_memory_capacity_tags); + octet_bit_field(log, pkt, 119, "T.44 colour space", NULL, NULL); + octet_bit_field(log, pkt, 120, "Extension indicator", NULL, NULL); + if (!(pkt[17] & DISBIT8)) + return; + if (len <= 18) + { + span_log(log, SPAN_LOG_FLOW, " Frame is short\n"); + return; + } + + octet_bit_field(log, pkt, 121, "Flow control capability for T.38 communication", NULL, NULL); + octet_bit_field(log, pkt, 122, "K>4", NULL, NULL); + octet_bit_field(log, pkt, 123, "Internet aware T.38 mode fax (not affected by data signal rate bits)", NULL, NULL); + octet_field(log, pkt, 124, 126, "T.89 (Application profiles for ITU-T Rec T.8)", t89_profile_tags); + octet_bit_field(log, pkt, 127, "sYCC-JPEG coding", NULL, NULL); + octet_bit_field(log, pkt, 128, "Extension indicator", NULL, NULL); + if (!(pkt[18] & DISBIT8)) + return; + + span_log(log, SPAN_LOG_FLOW, " Extended beyond the current T.30 specification!\n"); +} +/*- End of function --------------------------------------------------------*/ + +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->current_status = T30_ERR_OK; + build_dis_or_dtc(s); + if (s->calling_party) + { + set_state(s, T30_STATE_T); + set_phase(s, T30_PHASE_A_CNG); + } + else + { + set_state(s, T30_STATE_ANSWERING); + set_phase(s, T30_PHASE_A_CED); + } + s->far_end_detected = FALSE; + s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T0); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int 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) +{ + memset(s, 0, sizeof(*s)); + s->calling_party = calling_party; + s->set_rx_type_handler = set_rx_type_handler; + s->set_rx_type_user_data = set_rx_type_user_data; + s->set_tx_type_handler = set_tx_type_handler; + s->set_tx_type_user_data = set_tx_type_user_data; + s->send_hdlc_handler = send_hdlc_handler; + s->send_hdlc_user_data = send_hdlc_user_data; + + /* Default to the basic modems. */ + s->supported_modems = T30_SUPPORT_V27TER | T30_SUPPORT_V29; + s->supported_compressions = T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION; + s->supported_resolutions = T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION + | T30_SUPPORT_R8_RESOLUTION; + s->supported_image_sizes = T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH + | T30_SUPPORT_215MM_WIDTH; + span_log_init(&s->logging, SPAN_LOG_NONE, NULL); + span_log_set_protocol(&s->logging, "T.30"); + t30_restart(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +void 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. */ + t4_rx_end(&(s->t4)); + t4_tx_end(&(s->t4)); +} +/*- End of function --------------------------------------------------------*/ + +t30_state_t *t30_create(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) +{ + t30_state_t *s; + + if ((s = (t30_state_t *) malloc(sizeof(t30_state_t))) == NULL) + return NULL; + if (t30_init(s, + calling_party, + set_rx_type_handler, + set_rx_type_user_data, + set_tx_type_handler, + set_tx_type_user_data, + send_hdlc_handler, + send_hdlc_user_data)) + { + free(s); + return NULL; + } + return s; +} +/*- End of function --------------------------------------------------------*/ + +void t30_free(t30_state_t *s) +{ + t30_release(s); + free(s); +} +/*- End of function --------------------------------------------------------*/ + +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); + } +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_iaf_mode(t30_state_t *s, int iaf) +{ + s->iaf = iaf; +} +/*- End of function --------------------------------------------------------*/ + +int t30_set_header_info(t30_state_t *s, const char *info) +{ + if (info == NULL) + { + s->header_info[0] = '\0'; + return 0; + } + if (strlen(info) > 50) + return -1; + strcpy(s->header_info, info); + t4_tx_set_header_info(&(s->t4), s->header_info); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int t30_set_local_ident(t30_state_t *s, const char *id) +{ + if (id == NULL) + { + s->local_ident[0] = '\0'; + return 0; + } + if (strlen(id) > 20) + return -1; + strcpy(s->local_ident, id); + t4_tx_set_local_ident(&(s->t4), s->local_ident); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int t30_set_local_nsf(t30_state_t *s, const uint8_t *nsf, int len) +{ + if (len > 100) + return -1; + memcpy(s->local_nsf, nsf, len); + s->local_nsf_len = len; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int t30_set_local_sub_address(t30_state_t *s, const char *sub_address) +{ + if (sub_address == NULL) + { + s->local_sub_address[0] = '\0'; + return 0; + } + if (strlen(sub_address) > 20) + return -1; + strcpy(s->local_sub_address, sub_address); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +size_t t30_get_sub_address(t30_state_t *s, char *sub_address) +{ + if (sub_address) + strcpy(sub_address, s->far_sub_address); + return strlen(s->far_sub_address); +} +/*- End of function --------------------------------------------------------*/ + +size_t t30_get_header_info(t30_state_t *s, char *info) +{ + if (info) + strcpy(info, s->header_info); + return strlen(s->header_info); +} +/*- End of function --------------------------------------------------------*/ + +size_t t30_get_local_ident(t30_state_t *s, char *id) +{ + if (id) + strcpy(id, s->local_ident); + return strlen(s->local_ident); +} +/*- End of function --------------------------------------------------------*/ + +size_t t30_get_far_ident(t30_state_t *s, char *id) +{ + if (id) + strcpy(id, s->far_ident); + return strlen(s->far_ident); +} +/*- End of function --------------------------------------------------------*/ + +const char *t30_get_far_country(t30_state_t *s) +{ + return s->country; +} +/*- End of function --------------------------------------------------------*/ + +const char *t30_get_far_vendor(t30_state_t *s) +{ + return s->vendor; +} +/*- End of function --------------------------------------------------------*/ + +const char *t30_get_far_model(t30_state_t *s) +{ + return s->model; +} +/*- End of function --------------------------------------------------------*/ + +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; + t4_get_transfer_statistics(&(s->t4), &stats); + t->pages_transferred = stats.pages_transferred; + 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.image_size; + t->current_status = s->current_status; +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_phase_b_handler(t30_state_t *s, t30_phase_b_handler_t *handler, void *user_data) +{ + s->phase_b_handler = handler; + s->phase_b_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_phase_d_handler(t30_state_t *s, t30_phase_d_handler_t *handler, void *user_data) +{ + s->phase_d_handler = handler; + s->phase_d_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_phase_e_handler(t30_state_t *s, t30_phase_e_handler_t *handler, void *user_data) +{ + s->phase_e_handler = handler; + s->phase_e_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_document_handler(t30_state_t *s, t30_document_handler_t *handler, void *user_data) +{ + s->document_handler = handler; + s->document_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_rx_file(t30_state_t *s, const char *file, int stop_page) +{ + strncpy(s->rx_file, file, sizeof(s->rx_file)); + s->rx_file[sizeof(s->rx_file) - 1] = '\0'; + s->rx_stop_page = stop_page; +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_tx_file(t30_state_t *s, const char *file, int start_page, int stop_page) +{ + strncpy(s->tx_file, file, sizeof(s->tx_file)); + s->tx_file[sizeof(s->tx_file) - 1] = '\0'; + s->tx_start_page = start_page; + s->tx_stop_page = stop_page; +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_supported_modems(t30_state_t *s, int supported_modems) +{ + s->supported_modems = supported_modems; + build_dis_or_dtc(s); +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_supported_compressions(t30_state_t *s, int supported_compressions) +{ + s->supported_compressions = supported_compressions; + build_dis_or_dtc(s); +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_supported_resolutions(t30_state_t *s, int supported_resolutions) +{ + s->supported_resolutions = supported_resolutions; + build_dis_or_dtc(s); +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_supported_image_sizes(t30_state_t *s, int supported_image_sizes) +{ + s->supported_image_sizes = supported_image_sizes; + build_dis_or_dtc(s); +} +/*- End of function --------------------------------------------------------*/ + +void t30_set_ecm_capability(t30_state_t *s, int enabled) +{ + s->ecm_allowed = enabled; + build_dis_or_dtc(s); +} +/*- End of function --------------------------------------------------------*/ + +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 --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/