Mercurial > hg > audiostuff
diff spandsp-0.0.3/spandsp-0.0.3/src/t31.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/t31.c Fri Jun 25 16:00:21 2010 +0200 @@ -0,0 +1,1706 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * t31.c - A T.31 compatible class 1 FAX modem interface. + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Special thanks to Lee Howard <faxguy@howardsilvan.com> + * for his great work debugging and polishing this code. + * + * Copyright (C) 2004, 2005, 2006 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: t31.c,v 1.83 2006/11/30 15:41:47 steveu Exp $ + */ + +/*! \file */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <memory.h> +#include <string.h> +#include <ctype.h> +#if defined(HAVE_TGMATH_H) +#include <tgmath.h> +#endif +#if defined(HAVE_MATH_H) +#include <math.h> +#endif +#include <assert.h> +#include <tiffio.h> + +#include "spandsp/telephony.h" +#include "spandsp/logging.h" +#include "spandsp/bit_operations.h" +#include "spandsp/dc_restore.h" +#include "spandsp/queue.h" +#include "spandsp/power_meter.h" +#include "spandsp/complex.h" +#include "spandsp/tone_generate.h" +#include "spandsp/async.h" +#include "spandsp/hdlc.h" +#include "spandsp/silence_gen.h" +#include "spandsp/fsk.h" +#include "spandsp/v29rx.h" +#include "spandsp/v29tx.h" +#include "spandsp/v27ter_rx.h" +#include "spandsp/v27ter_tx.h" +#if defined(ENABLE_V17) +#include "spandsp/v17rx.h" +#include "spandsp/v17tx.h" +#endif +#include "spandsp/t4.h" +#include "spandsp/t30.h" +#include "spandsp/t38_core.h" + +#include "spandsp/at_interpreter.h" +#include "spandsp/t31.h" + +#define MS_PER_TX_CHUNK 30 +#define INDICATOR_TX_COUNT 4 +#define DEFAULT_DTE_TIMEOUT 5 + +typedef const char *(*at_cmd_service_t)(t31_state_t *s, const char *cmd); + +#define ETX 0x03 +#define DLE 0x10 +#define SUB 0x1A + +enum +{ + T31_FLUSH, + T31_SILENCE_TX, + T31_SILENCE_RX, + T31_CED_TONE, + T31_CNG_TONE, + T31_NOCNG_TONE, + T31_V21_TX, + T31_V17_TX, + T31_V27TER_TX, + T31_V29_TX, + T31_V21_RX, + T31_V17_RX, + T31_V27TER_RX, + T31_V29_RX +}; + +enum +{ + T38_TIMED_STEP_NONE = 0, + T38_TIMED_STEP_NON_ECM_MODEM, + T38_TIMED_STEP_NON_ECM_MODEM_2, + T38_TIMED_STEP_NON_ECM_MODEM_3, + T38_TIMED_STEP_HDLC_MODEM, + T38_TIMED_STEP_HDLC_MODEM_2, + T38_TIMED_STEP_HDLC_MODEM_3, + T38_TIMED_STEP_HDLC_MODEM_4, + T38_TIMED_STEP_PAUSE +}; + +static int restart_modem(t31_state_t *s, int new_modem); +static void hdlc_accept(void *user_data, int ok, const uint8_t *msg, int len); +#if defined(ENABLE_V17) +static int early_v17_rx(void *user_data, const int16_t amp[], int len); +#endif +static int early_v27ter_rx(void *user_data, const int16_t amp[], int len); +static int early_v29_rx(void *user_data, const int16_t amp[], int len); +static int dummy_rx(void *s, const int16_t amp[], int len); +static int silence_rx(void *user_data, const int16_t amp[], int len); +static int cng_rx(void *user_data, const int16_t amp[], int len); + +static __inline__ void t31_set_at_rx_mode(t31_state_t *s, int new_mode) +{ + s->at_state.at_rx_mode = new_mode; +} +/*- End of function --------------------------------------------------------*/ + +static int process_rx_indicator(t38_core_state_t *s, void *user_data, int indicator) +{ + t31_state_t *t; + + t = (t31_state_t *) user_data; + switch (indicator) + { + case T38_IND_NO_SIGNAL: + break; + case T38_IND_CNG: + break; + case T38_IND_CED: + break; + case T38_IND_V21_PREAMBLE: + t->hdlc_rx_len = 0; + break; + case T38_IND_V27TER_2400_TRAINING: + break; + case T38_IND_V27TER_4800_TRAINING: + break; + case T38_IND_V29_7200_TRAINING: + break; + case T38_IND_V29_9600_TRAINING: + break; + case T38_IND_V17_7200_SHORT_TRAINING: + break; + case T38_IND_V17_7200_LONG_TRAINING: + break; + case T38_IND_V17_9600_SHORT_TRAINING: + break; + case T38_IND_V17_9600_LONG_TRAINING: + break; + case T38_IND_V17_12000_SHORT_TRAINING: + break; + case T38_IND_V17_12000_LONG_TRAINING: + break; + case T38_IND_V17_14400_SHORT_TRAINING: + break; + case T38_IND_V17_14400_LONG_TRAINING: + break; + case T38_IND_V8_ANSAM: + break; + case T38_IND_V8_SIGNAL: + break; + case T38_IND_V34_CNTL_CHANNEL_1200: + break; + case T38_IND_V34_PRI_CHANNEL: + break; + case T38_IND_V34_CC_RETRAIN: + break; + case T38_IND_V33_12000_TRAINING: + break; + case T38_IND_V33_14400_TRAINING: + break; + default: + break; + } + t->missing_data = FALSE; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int process_rx_data(t38_core_state_t *s, void *user_data, int data_type, int field_type, const uint8_t *buf, int len) +{ + t31_state_t *t; + int i; + + t = (t31_state_t *) user_data; + switch (data_type) + { + case T38_DATA_V21: + case T38_DATA_V27TER_2400: + case T38_DATA_V27TER_4800: + case T38_DATA_V29_7200: + case T38_DATA_V29_9600: + case T38_DATA_V17_7200: + case T38_DATA_V17_9600: + case T38_DATA_V17_12000: + case T38_DATA_V17_14400: + case T38_DATA_V8: + case T38_DATA_V34_PRI_RATE: + case T38_DATA_V34_CC_1200: + case T38_DATA_V34_PRI_CH: + case T38_DATA_V33_12000: + case T38_DATA_V33_14400: + default: + break; + } + switch (field_type) + { + case T38_FIELD_HDLC_DATA: + if (t->hdlc_rx_len + len <= 256 - 2) + { + for (i = 0; i < len; i++) + t->hdlc_rx_buf[t->hdlc_rx_len++] = bit_reverse8(buf[i]); + } + break; + case T38_FIELD_HDLC_FCS_OK: + if (len > 0) + span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_OK!\n"); + span_log(&t->logging, SPAN_LOG_FLOW, "Type %s - CRC OK (%s)\n", t30_frametype(t->tx_data[2]), (t->missing_data) ? "missing octets" : "clean"); + /* Don't deal with zero length frames. Some T.38 implementations send multiple T38_FIELD_HDLC_FCS_OK + packets, when they have sent no data for the body of the frame. */ + if (t->current_rx_type == T31_V21_RX && t->tx_out_bytes > 0 && !t->missing_data) + hdlc_accept((void *) t, TRUE, t->hdlc_rx_buf, t->hdlc_rx_len); + t->hdlc_rx_len = 0; + t->missing_data = FALSE; + break; + case T38_FIELD_HDLC_FCS_BAD: + if (len > 0) + span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_BAD!\n"); + span_log(&t->logging, SPAN_LOG_FLOW, "Type %s - CRC bad (%s)\n", t30_frametype(t->tx_data[2]), (t->missing_data) ? "missing octets" : "clean"); + t->hdlc_rx_len = 0; + t->missing_data = FALSE; + break; + case T38_FIELD_HDLC_FCS_OK_SIG_END: + if (len > 0) + span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_OK_SIG_END!\n"); + span_log(&t->logging, SPAN_LOG_FLOW, "Type %s - CRC OK, sig end (%s)\n", t30_frametype(t->tx_data[2]), (t->missing_data) ? "missing octets" : "clean"); + if (t->current_rx_type == T31_V21_RX) + { + /* Don't deal with zero length frames. Some T.38 implementations send multiple T38_FIELD_HDLC_FCS_OK + packets, when they have sent no data for the body of the frame. */ + if (t->tx_out_bytes > 0) + hdlc_accept((void *) t, TRUE, t->hdlc_rx_buf, t->hdlc_rx_len); + hdlc_accept((void *) t, TRUE, NULL, PUTBIT_CARRIER_DOWN); + } + t->tx_out_bytes = 0; + t->missing_data = FALSE; + t->hdlc_rx_len = 0; + t->missing_data = FALSE; + break; + case T38_FIELD_HDLC_FCS_BAD_SIG_END: + if (len > 0) + span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_BAD_SIG_END!\n"); + span_log(&t->logging, SPAN_LOG_FLOW, "Type %s - CRC bad, sig end (%s)\n", t30_frametype(t->tx_data[2]), (t->missing_data) ? "missing octets" : "clean"); + if (t->current_rx_type == T31_V21_RX) + hdlc_accept((void *) t, TRUE, NULL, PUTBIT_CARRIER_DOWN); + t->hdlc_rx_len = 0; + t->missing_data = FALSE; + break; + case T38_FIELD_HDLC_SIG_END: + if (len > 0) + span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_SIG_END!\n"); + /* This message is expected under 2 circumstances. One is as an alternative to T38_FIELD_HDLC_FCS_OK_SIG_END - + i.e. they send T38_FIELD_HDLC_FCS_OK, and then T38_FIELD_HDLC_SIG_END when the carrier actually drops. + The other is because the HDLC signal drops unexpectedly - i.e. not just after a final frame. */ + if (t->current_rx_type == T31_V21_RX) + hdlc_accept((void *) t, TRUE, NULL, PUTBIT_CARRIER_DOWN); + t->hdlc_rx_len = 0; + t->missing_data = FALSE; + break; + case T38_FIELD_T4_NON_ECM_DATA: + break; + case T38_FIELD_T4_NON_ECM_SIG_END: + break; + case T38_FIELD_CM_MESSAGE: + case T38_FIELD_JM_MESSAGE: + case T38_FIELD_CI_MESSAGE: + case T38_FIELD_V34RATE: + default: + break; + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int process_rx_missing(t38_core_state_t *s, void *user_data, int rx_seq_no, int expected_seq_no) +{ + t31_state_t *t; + + t = (t31_state_t *) user_data; + t->missing_data = TRUE; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int t31_t38_send_timeout(t31_state_t *s, int samples) +{ + int len; + uint8_t buf[100]; + /* Training times for all the modem options, with and without TEP */ + static const int training_time[] = + { + 0, 0, /* T38_IND_NO_SIGNAL */ + 0, 0, /* T38_IND_CNG */ + 0, 0, /* T38_IND_CED */ + 1000, 1000, /* T38_IND_V21_PREAMBLE */ + 943, 1158, /* T38_IND_V27TER_2400_TRAINING */ + 708, 923, /* T38_IND_V27TER_4800_TRAINING */ + 234, 454, /* T38_IND_V29_7200_TRAINING */ + 234, 454, /* T38_IND_V29_9600_TRAINING */ + 142, 367, /* T38_IND_V17_7200_SHORT_TRAINING */ + 1393, 1618, /* T38_IND_V17_7200_LONG_TRAINING */ + 142, 367, /* T38_IND_V17_9600_SHORT_TRAINING */ + 1393, 1618, /* T38_IND_V17_9600_LONG_TRAINING */ + 142, 367, /* T38_IND_V17_12000_SHORT_TRAINING */ + 1393, 1618, /* T38_IND_V17_12000_LONG_TRAINING */ + 142, 367, /* T38_IND_V17_14400_SHORT_TRAINING */ + 1393, 1618, /* T38_IND_V17_14400_LONG_TRAINING */ + 0, 0, /* T38_IND_V8_ANSAM */ + 0, 0, /* T38_IND_V8_SIGNAL */ + 0, 0, /* T38_IND_V34_CNTL_CHANNEL_1200 */ + 0, 0, /* T38_IND_V34_PRI_CHANNEL */ + 0, 0, /* T38_IND_V34_CC_RETRAIN */ + 0, 0, /* T38_IND_V33_12000_TRAINING */ + 0, 0, /* T38_IND_V33_14400_TRAINING */ + }; + + s->call_samples += samples; + + if (s->timed_step == T38_TIMED_STEP_NONE) + return 0; + if (s->call_samples < s->next_send_samples) + return 0; + len = 0; + switch (s->timed_step) + { + case T38_TIMED_STEP_NON_ECM_MODEM: + /* Create a 75ms silence */ + t38_core_send_indicator(&s->t38, T38_IND_NO_SIGNAL, INDICATOR_TX_COUNT); + s->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_2; + s->next_send_samples += ms_to_samples(75); + break; + case T38_TIMED_STEP_NON_ECM_MODEM_2: + /* Switch on a fast modem, and give the training time to complete */ + t38_core_send_indicator(&s->t38, s->next_tx_indicator, INDICATOR_TX_COUNT); + s->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_3; + s->next_send_samples += ms_to_samples(training_time[s->next_tx_indicator << 1]); + break; + case T38_TIMED_STEP_NON_ECM_MODEM_3: + /* Send a chunk of non-ECM image data */ + //if ((len = get_non_ecm_image_chunk(s, buf, s->octets_per_non_ecm_packet))) + // t38_core_send_data(&s->t38, s->current_tx_data, T38_FIELD_T4_NON_ECM_DATA, buf, abs(len)); + if (len > 0) + { + s->next_send_samples += ms_to_samples(MS_PER_TX_CHUNK); + } + else + { + t38_core_send_data(&s->t38, s->current_tx_data, T38_FIELD_T4_NON_ECM_SIG_END, NULL, 0); + t38_core_send_indicator(&s->t38, T38_IND_NO_SIGNAL, INDICATOR_TX_COUNT); + s->timed_step = T38_TIMED_STEP_NONE; + } + break; + case T38_TIMED_STEP_HDLC_MODEM: + /* Send HDLC preambling */ + t38_core_send_indicator(&s->t38, s->next_tx_indicator, INDICATOR_TX_COUNT); + s->next_send_samples += ms_to_samples(training_time[s->next_tx_indicator << 1]); + s->timed_step = T38_TIMED_STEP_HDLC_MODEM_2; + break; + case T38_TIMED_STEP_HDLC_MODEM_2: + /* Send HDLC octet */ + buf[0] = bit_reverse8(s->hdlc_tx_buf[s->hdlc_tx_ptr++]); + t38_core_send_data(&s->t38, s->current_tx_data, T38_FIELD_HDLC_DATA, buf, 1); + if (s->hdlc_tx_ptr >= s->hdlc_tx_len) + s->timed_step = T38_TIMED_STEP_HDLC_MODEM_3; + s->next_send_samples += ms_to_samples(MS_PER_TX_CHUNK); + break; + case T38_TIMED_STEP_HDLC_MODEM_3: + /* End of HDLC frame */ + s->hdlc_tx_ptr = 0; + if (s->hdlc_final) + { + s->hdlc_tx_len = 0; + t38_core_send_data(&s->t38, s->current_tx_data, T38_FIELD_HDLC_FCS_OK_SIG_END, NULL, 0); + s->timed_step = T38_TIMED_STEP_HDLC_MODEM_4; + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK); + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + s->hdlc_final = FALSE; + restart_modem(s, T31_SILENCE_TX); + } + else + { + t38_core_send_data(&s->t38, s->current_tx_data, T38_FIELD_HDLC_FCS_OK, NULL, 0); + s->timed_step = T38_TIMED_STEP_HDLC_MODEM_2; + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT); + } + s->next_send_samples += ms_to_samples(MS_PER_TX_CHUNK); + break; + case T38_TIMED_STEP_HDLC_MODEM_4: + /* End of HDLC transmission */ + /* We have already sent T38_FIELD_HDLC_FCS_OK_SIG_END. It seems some boxes may not like + us sending a T38_FIELD_HDLC_SIG_END at this point. Just say there is no signal. */ + //t38_core_send_data(&s->t38, s->current_tx_data, T38_FIELD_HDLC_SIG_END, NULL, 0); + t38_core_send_indicator(&s->t38, T38_IND_NO_SIGNAL, INDICATOR_TX_COUNT); + s->timed_step = T38_TIMED_STEP_NONE; + break; + case T38_TIMED_STEP_PAUSE: + /* End of timed pause */ + s->timed_step = T38_TIMED_STEP_NONE; + break; + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int t31_modem_control_handler(at_state_t *s, void *user_data, int op, const char *num) +{ + t31_state_t *t; + + t = (t31_state_t *) user_data; + switch (op) + { + case AT_MODEM_CONTROL_ANSWER: + t->call_samples = 0; + break; + case AT_MODEM_CONTROL_CALL: + t->call_samples = 0; + break; + case AT_MODEM_CONTROL_ONHOOK: + if (t->tx_holding) + { + t->tx_holding = FALSE; + /* Tell the application to release further data */ + at_modem_control(&t->at_state, AT_MODEM_CONTROL_CTS, (void *) 1); + } + if (t->at_state.rx_signal_present) + { + t->at_state.rx_data[t->at_state.rx_data_bytes++] = DLE; + t->at_state.rx_data[t->at_state.rx_data_bytes++] = ETX; + t->at_state.at_tx_handler(&t->at_state, t->at_state.at_tx_user_data, t->at_state.rx_data, t->at_state.rx_data_bytes); + t->at_state.rx_data_bytes = 0; + } + restart_modem(t, T31_SILENCE_TX); + break; + case AT_MODEM_CONTROL_RESTART: + restart_modem(t, (int) (intptr_t) num); + return 0; + case AT_MODEM_CONTROL_DTE_TIMEOUT: + if (num) + t->dte_data_timeout = t->call_samples + ms_to_samples((intptr_t) num); + else + t->dte_data_timeout = 0; + return 0; + } + return t->modem_control_handler(t, t->modem_control_user_data, op, num); +} +/*- End of function --------------------------------------------------------*/ + +static void non_ecm_put_bit(void *user_data, int bit) +{ + t31_state_t *s; + + s = (t31_state_t *) user_data; + if (bit < 0) + { + /* Special conditions */ + switch (bit) + { + case PUTBIT_TRAINING_FAILED: + s->at_state.rx_trained = FALSE; + break; + case PUTBIT_TRAINING_SUCCEEDED: + /* The modem is now trained */ + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT); + s->at_state.rx_signal_present = TRUE; + s->at_state.rx_trained = TRUE; + break; + case PUTBIT_CARRIER_UP: + break; + case PUTBIT_CARRIER_DOWN: + if (s->at_state.rx_signal_present) + { + s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE; + s->at_state.rx_data[s->at_state.rx_data_bytes++] = ETX; + s->at_state.at_tx_handler(&s->at_state, s->at_state.at_tx_user_data, s->at_state.rx_data, s->at_state.rx_data_bytes); + s->at_state.rx_data_bytes = 0; + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_NO_CARRIER); + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + } + s->at_state.rx_signal_present = FALSE; + s->at_state.rx_trained = FALSE; + break; + default: + if (s->at_state.p.result_code_format) + span_log(&s->logging, SPAN_LOG_FLOW, "Eh!\n"); + break; + } + return; + } + s->current_byte = (s->current_byte >> 1) | (bit << 7); + if (++s->bit_no >= 8) + { + if (s->current_byte == DLE) + s->at_state.rx_data[s->at_state.rx_data_bytes++] = (uint8_t) s->current_byte; + s->at_state.rx_data[s->at_state.rx_data_bytes++] = (uint8_t) s->current_byte; + if (s->at_state.rx_data_bytes >= 250) + { + s->at_state.at_tx_handler(&s->at_state, s->at_state.at_tx_user_data, s->at_state.rx_data, s->at_state.rx_data_bytes); + s->at_state.rx_data_bytes = 0; + } + s->bit_no = 0; + s->current_byte = 0; + } +} +/*- End of function --------------------------------------------------------*/ + +static int non_ecm_get_bit(void *user_data) +{ + t31_state_t *s; + int bit; + int fill; + + s = (t31_state_t *) user_data; + if (s->bit_no <= 0) + { + if (s->tx_out_bytes != s->tx_in_bytes) + { + /* There is real data available to send */ + s->current_byte = s->tx_data[s->tx_out_bytes]; + s->tx_out_bytes = (s->tx_out_bytes + 1) & (T31_TX_BUF_LEN - 1); + if (s->tx_holding) + { + /* See if the buffer is approaching empty. It might be time to release flow control. */ + fill = (s->tx_in_bytes - s->tx_out_bytes); + if (s->tx_in_bytes < s->tx_out_bytes) + fill += (T31_TX_BUF_LEN + 1); + if (fill < T31_TX_BUF_LOW_TIDE) + { + s->tx_holding = FALSE; + /* Tell the application to release further data */ + at_modem_control(&s->at_state, AT_MODEM_CONTROL_CTS, (void *) 1); + } + } + s->tx_data_started = TRUE; + } + else + { + if (s->data_final) + { + s->data_final = FALSE; + /* This will put the modem into its shutdown sequence. When + it has finally shut down, an OK response will be sent. */ + return PUTBIT_END_OF_DATA; + } + /* Fill with 0xFF bytes at the start of transmission, or 0x00 if we are in + the middle of transmission. This follows T.31 and T.30 practice. */ + s->current_byte = (s->tx_data_started) ? 0x00 : 0xFF; + } + s->bit_no = 8; + } + s->bit_no--; + bit = s->current_byte & 1; + s->current_byte >>= 1; + return bit; +} +/*- End of function --------------------------------------------------------*/ + +static void hdlc_tx_underflow(void *user_data) +{ + t31_state_t *s; + + s = (t31_state_t *) user_data; + if (s->hdlc_final) + { + s->hdlc_final = FALSE; + /* Schedule an orderly shutdown of the modem */ + hdlc_tx_frame(&(s->hdlctx), NULL, 0); + } + else + { + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT); + } +} +/*- End of function --------------------------------------------------------*/ + +static void hdlc_accept(void *user_data, int ok, const uint8_t *msg, int len) +{ + uint8_t buf[256]; + t31_state_t *s; + int i; + + s = (t31_state_t *) user_data; + if (len < 0) + { + /* Special conditions */ + switch (len) + { + case PUTBIT_TRAINING_FAILED: + s->at_state.rx_trained = FALSE; + break; + case PUTBIT_TRAINING_SUCCEEDED: + /* The modem is now trained */ + s->at_state.rx_signal_present = TRUE; + s->at_state.rx_trained = TRUE; + break; + case PUTBIT_CARRIER_UP: + if (s->modem == T31_CNG_TONE || s->modem == T31_NOCNG_TONE || s->modem == T31_V21_RX) + { + s->at_state.rx_signal_present = TRUE; + s->rx_message_received = FALSE; + } + break; + case PUTBIT_CARRIER_DOWN: + if (s->rx_message_received) + { + if (s->at_state.dte_is_waiting) + { + if (s->at_state.ok_is_pending) + { + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK); + s->at_state.ok_is_pending = FALSE; + } + else + { + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_NO_CARRIER); + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + } + s->at_state.dte_is_waiting = FALSE; + } + else + { + buf[0] = AT_RESPONSE_CODE_NO_CARRIER; + queue_write_msg(&(s->rx_queue), buf, 1); + } + } + s->at_state.rx_signal_present = FALSE; + s->at_state.rx_trained = FALSE; + break; + case PUTBIT_FRAMING_OK: + if (s->modem == T31_CNG_TONE || s->modem == T31_NOCNG_TONE) + { + /* Once we get any valid HDLC the CNG tone stops, and we drop + to the V.21 receive modem on its own. */ + s->modem = T31_V21_RX; + s->at_state.transmit = FALSE; + } + if (s->modem != T31_V21_RX) + { + /* V.21 has been detected while expecting a different carrier. + If +FAR=0 then result +FCERROR and return to command-mode. + If +FAR=1 then report +FRH:3 and CONNECT, switching to + V.21 receive mode. */ + if (s->at_state.p.adaptive_receive) + { + s->at_state.rx_signal_present = TRUE; + s->rx_message_received = TRUE; + s->modem = T31_V21_RX; + s->at_state.transmit = FALSE; + s->at_state.dte_is_waiting = TRUE; + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_FRH3); + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT); + } + else + { + s->modem = T31_SILENCE_TX; + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + s->rx_message_received = FALSE; + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_FCERROR); + } + } + else + { + if (!s->rx_message_received) + { + /* Report CONNECT as soon as possible to avoid a timeout. */ + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT); + s->rx_message_received = TRUE; + } + } + break; + case PUTBIT_ABORT: + /* Just ignore these */ + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected HDLC special length - %d!\n", len); + break; + } + return; + } + /* If OK is pending then we just ignore whatever comes in */ + if (!s->at_state.ok_is_pending) + { + if (s->at_state.dte_is_waiting) + { + /* Send straight away */ + /* It is safe to look at the two bytes beyond the length of the message, + and expect to find the FCS there. */ + for (i = 0; i < len + 2; i++) + { + if (msg[i] == DLE) + s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE; + s->at_state.rx_data[s->at_state.rx_data_bytes++] = msg[i]; + } + s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE; + s->at_state.rx_data[s->at_state.rx_data_bytes++] = ETX; + s->at_state.at_tx_handler(&s->at_state, s->at_state.at_tx_user_data, s->at_state.rx_data, s->at_state.rx_data_bytes); + s->at_state.rx_data_bytes = 0; + if (msg[1] == 0x13 && ok) + { + /* This is the last frame. We don't send OK until the carrier drops to avoid + redetecting it later. */ + s->at_state.ok_is_pending = TRUE; + } + else + { + at_put_response_code(&s->at_state, (ok) ? AT_RESPONSE_CODE_OK : AT_RESPONSE_CODE_ERROR); + s->at_state.dte_is_waiting = FALSE; + s->rx_message_received = FALSE; + } + } + else + { + /* Queue it */ + buf[0] = (ok) ? AT_RESPONSE_CODE_OK : AT_RESPONSE_CODE_ERROR; + /* It is safe to look at the two bytes beyond the length of the message, + and expect to find the FCS there. */ + memcpy(buf + 1, msg, len + 2); + queue_write_msg(&(s->rx_queue), buf, len + 3); + } + } + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); +} +/*- End of function --------------------------------------------------------*/ + +static void t31_v21_rx(t31_state_t *s) +{ + hdlc_rx_init(&(s->hdlcrx), FALSE, TRUE, 5, hdlc_accept, s); + s->at_state.ok_is_pending = FALSE; + s->hdlc_final = FALSE; + s->hdlc_tx_len = 0; + s->dled = FALSE; + fsk_rx_init(&(s->v21rx), &preset_fsk_specs[FSK_V21CH2], TRUE, (put_bit_func_t) hdlc_rx_put_bit, &(s->hdlcrx)); + s->at_state.transmit = TRUE; +} +/*- End of function --------------------------------------------------------*/ + +static int restart_modem(t31_state_t *s, int new_modem) +{ + tone_gen_descriptor_t tone_desc; + int ind; + + span_log(&s->logging, SPAN_LOG_FLOW, "Restart modem %d\n", new_modem); + if (s->modem == new_modem) + return 0; + queue_flush(&(s->rx_queue)); + s->modem = new_modem; + s->data_final = FALSE; + s->at_state.rx_signal_present = FALSE; + s->at_state.rx_trained = FALSE; + s->rx_message_received = FALSE; + s->rx_handler = (span_rx_handler_t *) &dummy_rx; + s->rx_user_data = NULL; + switch (s->modem) + { + case T31_CNG_TONE: + if (s->t38_mode) + { + t38_core_send_indicator(&s->t38, T38_IND_CNG, INDICATOR_TX_COUNT); + } + else + { + /* CNG is special, since we need to receive V.21 HDLC messages while sending the + tone. Everything else in FAX processing sends only one way at a time. */ + /* 0.5s of 1100Hz + 3.0s of silence repeating */ + make_tone_gen_descriptor(&tone_desc, + 1100, + -11, + 0, + 0, + 500, + 3000, + 0, + 0, + TRUE); + tone_gen_init(&(s->tone_gen), &tone_desc); + /* Do V.21/HDLC receive in parallel. The other end may send its + first message at any time. The CNG tone will continue until + we get a valid preamble. */ + s->rx_handler = (span_rx_handler_t *) &cng_rx; + s->rx_user_data = s; + t31_v21_rx(s); + s->tx_handler = (span_tx_handler_t *) &tone_gen; + s->tx_user_data = &(s->tone_gen); + s->next_tx_handler = NULL; + } + s->at_state.transmit = TRUE; + break; + case T31_NOCNG_TONE: + if (s->t38_mode) + { + } + else + { + s->rx_handler = (span_rx_handler_t *) &cng_rx; + s->rx_user_data = s; + t31_v21_rx(s); + silence_gen_set(&(s->silence_gen), 0); + s->tx_handler = (span_tx_handler_t *) &silence_gen; + s->tx_user_data = &(s->silence_gen); + } + s->at_state.transmit = FALSE; + break; + case T31_CED_TONE: + if (s->t38_mode) + { + t38_core_send_indicator(&s->t38, T38_IND_CED, INDICATOR_TX_COUNT); + } + else + { + silence_gen_alter(&(s->silence_gen), ms_to_samples(200)); + make_tone_gen_descriptor(&tone_desc, + 2100, + -11, + 0, + 0, + 2600, + 75, + 0, + 0, + FALSE); + tone_gen_init(&(s->tone_gen), &tone_desc); + s->tx_handler = (span_tx_handler_t *) &silence_gen; + s->tx_user_data = &(s->silence_gen); + s->next_tx_handler = (span_tx_handler_t *) &tone_gen; + s->next_tx_user_data = &(s->tone_gen); + } + s->at_state.transmit = TRUE; + break; + case T31_V21_TX: + if (s->t38_mode) + { + t38_core_send_indicator(&s->t38, T38_IND_V21_PREAMBLE, INDICATOR_TX_COUNT); + } + else + { + hdlc_tx_init(&(s->hdlctx), FALSE, 2, FALSE, hdlc_tx_underflow, s); + /* The spec says 1s +-15% of preamble. So, the minimum is 32 octets. */ + hdlc_tx_preamble(&(s->hdlctx), 32); + fsk_tx_init(&(s->v21tx), &preset_fsk_specs[FSK_V21CH2], (get_bit_func_t) hdlc_tx_get_bit, &(s->hdlctx)); + s->tx_handler = (span_tx_handler_t *) &fsk_tx; + s->tx_user_data = &(s->v21tx); + s->next_tx_handler = NULL; + } + s->hdlc_final = FALSE; + s->hdlc_tx_len = 0; + s->dled = FALSE; + s->at_state.transmit = TRUE; + break; + case T31_V21_RX: + if (s->t38_mode) + { + } + else + { + s->rx_handler = (span_rx_handler_t *) &fsk_rx; + s->rx_user_data = &(s->v21rx); + t31_v21_rx(s); + } + break; +#if defined(ENABLE_V17) + case T31_V17_TX: + if (s->t38_mode) + { + switch (s->bit_rate) + { + case 7200: + ind = (s->short_train) ? T38_IND_V17_7200_SHORT_TRAINING : T38_IND_V17_7200_LONG_TRAINING; + break; + case 9600: + ind = (s->short_train) ? T38_IND_V17_9600_SHORT_TRAINING : T38_IND_V17_9600_LONG_TRAINING; + break; + case 12000: + ind = (s->short_train) ? T38_IND_V17_12000_SHORT_TRAINING : T38_IND_V17_12000_LONG_TRAINING; + break; + case 14400: + default: + ind = (s->short_train) ? T38_IND_V17_14400_SHORT_TRAINING : T38_IND_V17_14400_LONG_TRAINING; + break; + } + t38_core_send_indicator(&s->t38, ind, INDICATOR_TX_COUNT); + } + else + { + v17_tx_restart(&(s->v17tx), s->bit_rate, FALSE, s->short_train); + s->tx_handler = (span_tx_handler_t *) &v17_tx; + s->tx_user_data = &(s->v17tx); + s->next_tx_handler = NULL; + } + s->tx_out_bytes = 0; + s->tx_data_started = FALSE; + s->at_state.transmit = TRUE; + break; + case T31_V17_RX: + if (!s->t38_mode) + { + s->rx_handler = (span_rx_handler_t *) &early_v17_rx; + s->rx_user_data = s; + v17_rx_restart(&(s->v17rx), s->bit_rate, s->short_train); + /* Allow for +FCERROR/+FRH:3 */ + t31_v21_rx(s); + } + s->at_state.transmit = FALSE; + break; +#endif + case T31_V27TER_TX: + if (s->t38_mode) + { + switch (s->bit_rate) + { + case 2400: + ind = T38_IND_V27TER_2400_TRAINING; + break; + case 4800: + default: + ind = T38_IND_V27TER_4800_TRAINING; + break; + } + t38_core_send_indicator(&s->t38, ind, INDICATOR_TX_COUNT); + } + else + { + v27ter_tx_restart(&(s->v27ter_tx), s->bit_rate, FALSE); + s->tx_handler = (span_tx_handler_t *) &v27ter_tx; + s->tx_user_data = &(s->v27ter_tx); + s->next_tx_handler = NULL; + } + s->tx_out_bytes = 0; + s->tx_data_started = FALSE; + s->at_state.transmit = TRUE; + break; + case T31_V27TER_RX: + if (!s->t38_mode) + { + s->rx_handler = (span_rx_handler_t *) &early_v27ter_rx; + s->rx_user_data = s; + v27ter_rx_restart(&(s->v27ter_rx), s->bit_rate, FALSE); + /* Allow for +FCERROR/+FRH:3 */ + t31_v21_rx(s); + } + s->at_state.transmit = FALSE; + break; + case T31_V29_TX: + if (s->t38_mode) + { + switch (s->bit_rate) + { + case 7200: + ind = T38_IND_V29_7200_TRAINING; + break; + case 9600: + default: + ind = T38_IND_V29_9600_TRAINING; + break; + } + t38_core_send_indicator(&s->t38, ind, INDICATOR_TX_COUNT); + } + else + { + v29_tx_restart(&(s->v29tx), s->bit_rate, FALSE); + s->tx_handler = (span_tx_handler_t *) &v29_tx; + s->tx_user_data = &(s->v29tx); + s->next_tx_handler = NULL; + } + s->tx_out_bytes = 0; + s->tx_data_started = FALSE; + s->at_state.transmit = TRUE; + break; + case T31_V29_RX: + if (!s->t38_mode) + { + s->rx_handler = (span_rx_handler_t *) &early_v29_rx; + s->rx_user_data = s; + v29_rx_restart(&(s->v29rx), s->bit_rate, FALSE); + /* Allow for +FCERROR/+FRH:3 */ + t31_v21_rx(s); + } + s->at_state.transmit = FALSE; + break; + case T31_SILENCE_TX: + if (s->t38_mode) + { + t38_core_send_indicator(&s->t38, T38_IND_NO_SIGNAL, INDICATOR_TX_COUNT); + } + else + { + silence_gen_set(&(s->silence_gen), 0); + s->tx_handler = (span_tx_handler_t *) &silence_gen; + s->tx_user_data = &(s->silence_gen); + s->next_tx_handler = NULL; + } + s->at_state.transmit = FALSE; + break; + case T31_SILENCE_RX: + if (!s->t38_mode) + { + s->rx_handler = (span_rx_handler_t *) &silence_rx; + s->rx_user_data = s; + + silence_gen_set(&(s->silence_gen), 0); + s->tx_handler = (span_tx_handler_t *) &silence_gen; + s->tx_user_data = &(s->silence_gen); + s->next_tx_handler = NULL; + } + s->at_state.transmit = FALSE; + break; + case T31_FLUSH: + /* Send 200ms of silence to "push" the last audio out */ + if (s->t38_mode) + { + t38_core_send_indicator(&s->t38, T38_IND_NO_SIGNAL, INDICATOR_TX_COUNT); + } + else + { + s->modem = T31_SILENCE_TX; + silence_gen_alter(&(s->silence_gen), ms_to_samples(200)); + s->tx_handler = (span_tx_handler_t *) &silence_gen; + s->tx_user_data = &(s->silence_gen); + s->next_tx_handler = NULL; + s->at_state.transmit = TRUE; + } + break; + } + s->bit_no = 0; + s->current_byte = 0xFF; + s->tx_in_bytes = 0; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void dle_unstuff_hdlc(t31_state_t *s, const char *stuffed, int len) +{ + int i; + + for (i = 0; i < len; i++) + { + if (s->dled) + { + s->dled = FALSE; + if (stuffed[i] == ETX) + { + if (s->t38_mode) + { + } + else + { + hdlc_tx_frame(&(s->hdlctx), s->hdlc_tx_buf, s->hdlc_tx_len); + } + s->hdlc_final = (s->hdlc_tx_buf[1] & 0x10); + s->hdlc_tx_len = 0; + } + else if (stuffed[i] == SUB) + { + s->hdlc_tx_buf[s->hdlc_tx_len++] = DLE; + s->hdlc_tx_buf[s->hdlc_tx_len++] = DLE; + } + else + { + s->hdlc_tx_buf[s->hdlc_tx_len++] = stuffed[i]; + } + } + else + { + if (stuffed[i] == DLE) + s->dled = TRUE; + else + s->hdlc_tx_buf[s->hdlc_tx_len++] = stuffed[i]; + } + } +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void dle_unstuff(t31_state_t *s, const char *stuffed, int len) +{ + int i; + int fill; + int next; + + for (i = 0; i < len; i++) + { + if (s->dled) + { + s->dled = FALSE; + if (stuffed[i] == ETX) + { + s->data_final = TRUE; + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + return; + } + } + else if (stuffed[i] == DLE) + { + s->dled = TRUE; + continue; + } + s->tx_data[s->tx_in_bytes] = stuffed[i]; + next = (s->tx_in_bytes + 1) & (T31_TX_BUF_LEN - 1); + if (next == s->tx_out_bytes) + { + /* Oops. We hit the end of the buffer. Give up. Loose stuff. :-( */ + return; + } + s->tx_in_bytes = next; + } + if (!s->tx_holding) + { + /* See if the buffer is approaching full. We might need to apply flow control. */ + fill = (s->tx_in_bytes - s->tx_out_bytes); + if (s->tx_in_bytes < s->tx_out_bytes) + fill += (T31_TX_BUF_LEN + 1); + if (fill > T31_TX_BUF_HIGH_TIDE) + { + s->tx_holding = TRUE; + /* Tell the application to hold further data */ + at_modem_control(&s->at_state, AT_MODEM_CONTROL_CTS, (void *) 0); + } + } +} +/*- End of function --------------------------------------------------------*/ + +static int process_class1_cmd(at_state_t *t, void *user_data, int direction, int operation, int val) +{ + int new_modem; + int new_transmit; + int i; + int len; + int immediate_response; + t31_state_t *s; + uint8_t msg[256]; + + s = (t31_state_t *) user_data; + new_transmit = direction; + immediate_response = TRUE; + switch (operation) + { + case 'S': + s->at_state.transmit = new_transmit; + if (new_transmit) + { + /* Send a specified period of silence, to space transmissions. */ + restart_modem(s, T31_SILENCE_TX); + silence_gen_alter(&(s->silence_gen), val*80); + s->at_state.transmit = TRUE; + } + else + { + /* Wait until we have received a specified period of silence. */ + queue_flush(&(s->rx_queue)); + s->silence_awaited = val*80; + t31_set_at_rx_mode(s, AT_MODE_DELIVERY); + restart_modem(s, T31_SILENCE_RX); + } + immediate_response = FALSE; + span_log(&s->logging, SPAN_LOG_FLOW, "Silence %dms\n", val*10); + break; + case 'H': + switch (val) + { + case 3: + new_modem = (new_transmit) ? T31_V21_TX : T31_V21_RX; + s->short_train = FALSE; + s->bit_rate = 300; + break; + default: + return -1; + } + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC\n"); + if (new_modem != s->modem) + { + restart_modem(s, new_modem); + immediate_response = FALSE; + } + s->at_state.transmit = new_transmit; + if (new_transmit) + { + t31_set_at_rx_mode(s, AT_MODE_HDLC); + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT); + } + else + { + /* Send straight away, if there is something queued. */ + t31_set_at_rx_mode(s, AT_MODE_DELIVERY); + s->rx_message_received = FALSE; + do + { + if (!queue_empty(&(s->rx_queue))) + { + len = queue_read_msg(&(s->rx_queue), msg, 256); + if (len > 1) + { + if (msg[0] == AT_RESPONSE_CODE_OK) + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT); + for (i = 1; i < len; i++) + { + if (msg[i] == DLE) + s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE; + s->at_state.rx_data[s->at_state.rx_data_bytes++] = msg[i]; + } + s->at_state.rx_data[s->at_state.rx_data_bytes++] = DLE; + s->at_state.rx_data[s->at_state.rx_data_bytes++] = ETX; + s->at_state.at_tx_handler(&s->at_state, s->at_state.at_tx_user_data, s->at_state.rx_data, s->at_state.rx_data_bytes); + s->at_state.rx_data_bytes = 0; + } + at_put_response_code(&s->at_state, msg[0]); + } + else + { + s->at_state.dte_is_waiting = TRUE; + break; + } + } + while (msg[0] == AT_RESPONSE_CODE_CONNECT); + } + immediate_response = FALSE; + break; + default: + switch (val) + { + case 24: + new_modem = (new_transmit) ? T31_V27TER_TX : T31_V27TER_RX; + s->short_train = FALSE; + s->bit_rate = 2400; + break; + case 48: + new_modem = (new_transmit) ? T31_V27TER_TX : T31_V27TER_RX; + s->short_train = FALSE; + s->bit_rate = 4800; + break; + case 72: + new_modem = (new_transmit) ? T31_V29_TX : T31_V29_RX; + s->short_train = FALSE; + s->bit_rate = 7200; + break; + case 96: + new_modem = (new_transmit) ? T31_V29_TX : T31_V29_RX; + s->short_train = FALSE; + s->bit_rate = 9600; + break; +#if defined(ENABLE_V17) + case 73: + new_modem = (new_transmit) ? T31_V17_TX : T31_V17_RX; + s->short_train = FALSE; + s->bit_rate = 7200; + break; + case 74: + new_modem = (new_transmit) ? T31_V17_TX : T31_V17_RX; + s->short_train = TRUE; + s->bit_rate = 7200; + break; + case 97: + new_modem = (new_transmit) ? T31_V17_TX : T31_V17_RX; + s->short_train = FALSE; + s->bit_rate = 9600; + break; + case 98: + new_modem = (new_transmit) ? T31_V17_TX : T31_V17_RX; + s->short_train = TRUE; + s->bit_rate = 9600; + break; + case 121: + new_modem = (new_transmit) ? T31_V17_TX : T31_V17_RX; + s->short_train = FALSE; + s->bit_rate = 12000; + break; + case 122: + new_modem = (new_transmit) ? T31_V17_TX : T31_V17_RX; + s->short_train = TRUE; + s->bit_rate = 12000; + break; + case 145: + new_modem = (new_transmit) ? T31_V17_TX : T31_V17_RX; + s->short_train = FALSE; + s->bit_rate = 14400; + break; + case 146: + new_modem = (new_transmit) ? T31_V17_TX : T31_V17_RX; + s->short_train = TRUE; + s->bit_rate = 14400; + break; +#endif + default: + return -1; + } + span_log(&s->logging, SPAN_LOG_FLOW, "Short training = %d, bit rate = %d\n", s->short_train, s->bit_rate); + if (new_transmit) + { + t31_set_at_rx_mode(s, AT_MODE_STUFFED); + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_CONNECT); + } + else + { + t31_set_at_rx_mode(s, AT_MODE_DELIVERY); + } + restart_modem(s, new_modem); + immediate_response = FALSE; + break; + } + return immediate_response; +} +/*- End of function --------------------------------------------------------*/ + +void t31_call_event(t31_state_t *s, int event) +{ + span_log(&s->logging, SPAN_LOG_FLOW, "Call event %d received\n", event); + at_call_event(&s->at_state, event); +} +/*- End of function --------------------------------------------------------*/ + +int t31_at_rx(t31_state_t *s, const char *t, int len) +{ + if (s->dte_data_timeout) + s->dte_data_timeout = s->call_samples + ms_to_samples(5000); + switch (s->at_state.at_rx_mode) + { + case AT_MODE_ONHOOK_COMMAND: + case AT_MODE_OFFHOOK_COMMAND: + at_interpreter(&s->at_state, t, len); + break; + case AT_MODE_DELIVERY: + /* Data from the DTE in this state returns us to command mode */ + if (len) + { + s->at_state.rx_data_bytes = 0; + s->at_state.transmit = FALSE; + s->modem = T31_SILENCE_TX; + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK); + } + break; + case AT_MODE_HDLC: + dle_unstuff_hdlc(s, t, len); + break; + case AT_MODE_STUFFED: + dle_unstuff(s, t, len); + break; + } + return len; +} +/*- End of function --------------------------------------------------------*/ + +static int dummy_rx(void *user_data, const int16_t amp[], int len) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int silence_rx(void *user_data, const int16_t amp[], int len) +{ + t31_state_t *s; + + /* Searching for a specified minimum period of silence. */ + s = (t31_state_t *) user_data; + if (s->silence_awaited && s->silence_heard >= s->silence_awaited) + { + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK); + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + s->silence_heard = 0; + s->silence_awaited = 0; + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int cng_rx(void *user_data, const int16_t amp[], int len) +{ + t31_state_t *s; + + s = (t31_state_t *) user_data; + if (s->call_samples > ms_to_samples(s->at_state.p.s_regs[7]*1000)) + { + /* After calling, S7 has elapsed... no carrier found. */ + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_NO_CARRIER); + restart_modem(s, T31_SILENCE_TX); + at_modem_control(&s->at_state, AT_MODEM_CONTROL_HANGUP, NULL); + t31_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND); + } + else + { + fsk_rx(&(s->v21rx), amp, len); + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +#if defined(ENABLE_V17) +static int early_v17_rx(void *user_data, const int16_t amp[], int len) +{ + t31_state_t *s; + + s = (t31_state_t *) user_data; + v17_rx(&(s->v17rx), amp, len); + if (s->at_state.rx_trained) + { + /* The fast modem has trained, so we no longer need to run the slow + one in parallel. */ + span_log(&s->logging, SPAN_LOG_FLOW, "Switching from V.17 + V.21 to V.17 (%.2fdBm0)\n", v17_rx_signal_power(&(s->v17rx))); + s->rx_handler = (span_rx_handler_t *) &v17_rx; + s->rx_user_data = &(s->v17rx); + } + else + { + fsk_rx(&(s->v21rx), amp, len); + if (s->rx_message_received) + { + /* We have received something, and the fast modem has not trained. We must + be receiving valid V.21 */ + span_log(&s->logging, SPAN_LOG_FLOW, "Switching from V.17 + V.21 to V.21\n"); + s->rx_handler = (span_rx_handler_t *) &fsk_rx; + s->rx_user_data = &(s->v21rx); + } + } + return len; +} +/*- End of function --------------------------------------------------------*/ +#endif + +static int early_v27ter_rx(void *user_data, const int16_t amp[], int len) +{ + t31_state_t *s; + + s = (t31_state_t *) user_data; + v27ter_rx(&(s->v27ter_rx), amp, len); + if (s->at_state.rx_trained) + { + /* The fast modem has trained, so we no longer need to run the slow + one in parallel. */ + span_log(&s->logging, SPAN_LOG_FLOW, "Switching from V.27ter + V.21 to V.27ter (%.2fdBm0)\n", v27ter_rx_signal_power(&(s->v27ter_rx))); + s->rx_handler = (span_rx_handler_t *) &v27ter_rx; + s->rx_user_data = &(s->v27ter_rx); + } + else + { + fsk_rx(&(s->v21rx), amp, len); + if (s->rx_message_received) + { + /* We have received something, and the fast modem has not trained. We must + be receiving valid V.21 */ + span_log(&s->logging, SPAN_LOG_FLOW, "Switching from V.27ter + V.21 to V.21\n"); + s->rx_handler = (span_rx_handler_t *) &fsk_rx; + s->rx_user_data = &(s->v21rx); + } + } + return len; +} +/*- End of function --------------------------------------------------------*/ + +static int early_v29_rx(void *user_data, const int16_t amp[], int len) +{ + t31_state_t *s; + + s = (t31_state_t *) user_data; + v29_rx(&(s->v29rx), amp, len); + if (s->at_state.rx_trained) + { + /* The fast modem has trained, so we no longer need to run the slow + one in parallel. */ + span_log(&s->logging, SPAN_LOG_FLOW, "Switching from V.29 + V.21 to V.29 (%.2fdBm0)\n", v29_rx_signal_power(&(s->v29rx))); + s->rx_handler = (span_rx_handler_t *) &v29_rx; + s->rx_user_data = &(s->v29rx); + } + else + { + fsk_rx(&(s->v21rx), amp, len); + if (s->rx_message_received) + { + /* We have received something, and the fast modem has not trained. We must + be receiving valid V.21 */ + span_log(&s->logging, SPAN_LOG_FLOW, "Switching from V.29 + V.21 to V.21\n"); + s->rx_handler = (span_rx_handler_t *) &fsk_rx; + s->rx_user_data = &(s->v21rx); + } + } + return len; +} +/*- End of function --------------------------------------------------------*/ + +int t31_rx(t31_state_t *s, int16_t amp[], int len) +{ + int i; + int32_t power; + + /* Monitor for received silence. Maximum needed detection is AT+FRS=255 (255*10ms). */ + /* We could probably only run this loop if (s->modem == T31_SILENCE_RX), however, + the spec says "when silence has been present on the line for the amount of + time specified". That means some of the silence may have occurred before + the AT+FRS=n command. This condition, however, is not likely to ever be the + case. (AT+FRS=n will usually be issued before the remote goes silent.) */ + for (i = 0; i < len; i++) + { + /* Clean up any DC influence. */ + power = power_meter_update(&(s->rx_power), amp[i] - s->last_sample); + s->last_sample = amp[i]; + if (power > s->silence_threshold_power) + { + s->silence_heard = 0; + } + else + { + if (s->silence_heard <= ms_to_samples(255*10)) + s->silence_heard++; + } + } + + /* Time is determined by counting the samples in audio packets coming in. */ + s->call_samples += len; + + /* In HDLC transmit mode, if 5 seconds elapse without data from the DTE + we must treat this as an error. We return the result ERROR, and change + to command-mode. */ + if (s->dte_data_timeout && s->call_samples > s->dte_data_timeout) + { + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_ERROR); + restart_modem(s, T31_SILENCE_TX); + } + + if (!s->at_state.transmit || s->modem == T31_CNG_TONE) + s->rx_handler(s->rx_user_data, amp, len); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int set_next_tx_type(t31_state_t *s) +{ + if (s->next_tx_handler) + { + s->tx_handler = s->next_tx_handler; + s->tx_user_data = s->next_tx_user_data; + s->next_tx_handler = NULL; + return 0; + } + /* If there is nothing else to change to, so use zero length silence */ + silence_gen_alter(&(s->silence_gen), 0); + s->tx_handler = (span_tx_handler_t *) &silence_gen; + s->tx_user_data = &(s->silence_gen); + s->next_tx_handler = NULL; + return -1; +} +/*- End of function --------------------------------------------------------*/ + +int t31_tx(t31_state_t *s, int16_t amp[], int max_len) +{ + int len; + + len = 0; + if (s->at_state.transmit) + { + if ((len = s->tx_handler(s->tx_user_data, amp, max_len)) < max_len) + { + /* Allow for one change of tx handler within a block */ + set_next_tx_type(s); + if ((len += s->tx_handler(s->tx_user_data, amp + len, max_len - len)) < max_len) + { + switch (s->modem) + { + case T31_SILENCE_TX: + s->modem = -1; + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK); + if (s->at_state.do_hangup) + { + at_modem_control(&s->at_state, AT_MODEM_CONTROL_HANGUP, NULL); + t31_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND); + s->at_state.do_hangup = FALSE; + } + else + { + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + } + break; + case T31_CED_TONE: + /* Go directly to V.21/HDLC transmit. */ + s->modem = -1; + restart_modem(s, T31_V21_TX); + t31_set_at_rx_mode(s, AT_MODE_HDLC); + break; + case T31_V21_TX: +#if defined(ENABLE_V17) + case T31_V17_TX: +#endif + case T31_V27TER_TX: + case T31_V29_TX: + s->modem = -1; + at_put_response_code(&s->at_state, AT_RESPONSE_CODE_OK); + t31_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND); + restart_modem(s, T31_SILENCE_TX); + break; + } + } + } + } + if (s->transmit_on_idle) + { + /* Pad to the requested length with silence */ + memset(amp, 0, max_len*sizeof(int16_t)); + len = max_len; + } + return len; +} +/*- End of function --------------------------------------------------------*/ + +void t31_set_transmit_on_idle(t31_state_t *s, int transmit_on_idle) +{ + s->transmit_on_idle = transmit_on_idle; +} +/*- End of function --------------------------------------------------------*/ + +t31_state_t *t31_init(t31_state_t *s, + at_tx_handler_t *at_tx_handler, + void *at_tx_user_data, + t31_modem_control_handler_t *modem_control_handler, + void *modem_control_user_data, + t38_tx_packet_handler_t *tx_t38_packet_handler, + void *tx_t38_packet_user_data) +{ + if (at_tx_handler == NULL || modem_control_handler == NULL) + return NULL; + + memset(s, 0, sizeof(*s)); + span_log_init(&s->logging, SPAN_LOG_NONE, NULL); + span_log_set_protocol(&s->logging, "T.31"); + + s->modem_control_handler = modem_control_handler; + s->modem_control_user_data = modem_control_user_data; +#if defined(ENABLE_V17) + v17_rx_init(&(s->v17rx), 14400, non_ecm_put_bit, s); + v17_tx_init(&(s->v17tx), 14400, FALSE, non_ecm_get_bit, s); +#endif + v29_rx_init(&(s->v29rx), 9600, non_ecm_put_bit, s); + v29_rx_signal_cutoff(&(s->v29rx), -45.5); + v29_tx_init(&(s->v29tx), 9600, FALSE, non_ecm_get_bit, s); + v27ter_rx_init(&(s->v27ter_rx), 4800, non_ecm_put_bit, s); + v27ter_tx_init(&(s->v27ter_tx), 4800, FALSE, non_ecm_get_bit, s); + silence_gen_init(&(s->silence_gen), 0); + power_meter_init(&(s->rx_power), 4); + s->last_sample = 0; + s->silence_threshold_power = power_meter_level_dbm0(-43); + s->at_state.rx_signal_present = FALSE; + s->at_state.rx_trained = FALSE; + + s->at_state.do_hangup = FALSE; + s->at_state.line_ptr = 0; + s->silence_heard = 0; + s->silence_awaited = 0; + s->call_samples = 0; + s->modem = -1; + s->at_state.transmit = TRUE; + s->rx_handler = dummy_rx; + s->rx_user_data = NULL; + s->tx_handler = (span_tx_handler_t *) &silence_gen; + s->tx_user_data = &(s->silence_gen); + + if (queue_create(&(s->rx_queue), 4096, QUEUE_WRITE_ATOMIC | QUEUE_READ_ATOMIC) < 0) + return NULL; + at_init(&s->at_state, at_tx_handler, at_tx_user_data, t31_modem_control_handler, s); + at_set_class1_handler(&s->at_state, process_class1_cmd, s); + s->at_state.dte_inactivity_timeout = DEFAULT_DTE_TIMEOUT; + if (tx_t38_packet_handler) + { + t38_core_init(&s->t38, process_rx_indicator, process_rx_data, process_rx_missing, (void *) s); + s->t38.tx_packet_handler = tx_t38_packet_handler; + s->t38.tx_packet_user_data = tx_t38_packet_user_data; + } + s->t38_mode = FALSE; + return s; +} +/*- End of function --------------------------------------------------------*/ + +int t31_release(t31_state_t *s) +{ + at_reset_call_info(&s->at_state); + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/