Mercurial > hg > audiostuff
comparison spandsp-0.0.3/spandsp-0.0.3/src/t38_terminal.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 | 
   comparison
  equal
  deleted
  inserted
  replaced
| 4:26cd8f1ef0b1 | 5:f762bf195c4b | 
|---|---|
| 1 /* | |
| 2 * SpanDSP - a series of DSP components for telephony | |
| 3 * | |
| 4 * t38_terminal.c - An implementation of a T.38 terminal, less the packet exchange part | |
| 5 * | |
| 6 * Written by Steve Underwood <steveu@coppice.org> | |
| 7 * | |
| 8 * Copyright (C) 2005, 2006 Steve Underwood | |
| 9 * | |
| 10 * All rights reserved. | |
| 11 * | |
| 12 * This program is free software; you can redistribute it and/or modify | |
| 13 * it under the terms of the GNU General Public License version 2, as | |
| 14 * published by the Free Software Foundation. | |
| 15 * | |
| 16 * This program is distributed in the hope that it will be useful, | |
| 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 19 * GNU General Public License for more details. | |
| 20 * | |
| 21 * You should have received a copy of the GNU General Public License | |
| 22 * along with this program; if not, write to the Free Software | |
| 23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 24 * | |
| 25 * $Id: t38_terminal.c,v 1.50 2006/12/08 12:47:29 steveu Exp $ | |
| 26 */ | |
| 27 | |
| 28 /*! \file */ | |
| 29 | |
| 30 #ifdef HAVE_CONFIG_H | |
| 31 #include "config.h" | |
| 32 #endif | |
| 33 | |
| 34 #include <stdlib.h> | |
| 35 #include <stdio.h> | |
| 36 #include <inttypes.h> | |
| 37 #include <fcntl.h> | |
| 38 #include <time.h> | |
| 39 #include <string.h> | |
| 40 #if defined(HAVE_TGMATH_H) | |
| 41 #include <tgmath.h> | |
| 42 #endif | |
| 43 #if defined(HAVE_MATH_H) | |
| 44 #include <math.h> | |
| 45 #endif | |
| 46 #include <assert.h> | |
| 47 #include <tiffio.h> | |
| 48 | |
| 49 #include "spandsp/telephony.h" | |
| 50 #include "spandsp/logging.h" | |
| 51 #include "spandsp/bit_operations.h" | |
| 52 #include "spandsp/queue.h" | |
| 53 #include "spandsp/power_meter.h" | |
| 54 #include "spandsp/complex.h" | |
| 55 #include "spandsp/tone_generate.h" | |
| 56 #include "spandsp/async.h" | |
| 57 #include "spandsp/hdlc.h" | |
| 58 #include "spandsp/fsk.h" | |
| 59 #include "spandsp/v29rx.h" | |
| 60 #include "spandsp/v29tx.h" | |
| 61 #include "spandsp/v27ter_rx.h" | |
| 62 #include "spandsp/v27ter_tx.h" | |
| 63 #if defined(ENABLE_V17) | |
| 64 #include "spandsp/v17rx.h" | |
| 65 #include "spandsp/v17tx.h" | |
| 66 #endif | |
| 67 #include "spandsp/t4.h" | |
| 68 | |
| 69 #include "spandsp/t30_fcf.h" | |
| 70 #include "spandsp/t35.h" | |
| 71 #include "spandsp/t30.h" | |
| 72 | |
| 73 #include "spandsp/t38_core.h" | |
| 74 #include "spandsp/t38_terminal.h" | |
| 75 | |
| 76 #define MS_PER_TX_CHUNK 30 | |
| 77 | |
| 78 #define INDICATOR_TX_COUNT 3 | |
| 79 /* Backstop timeout if reception of packets stops in the middle of a burst */ | |
| 80 #define MID_RX_TIMEOUT 15000 | |
| 81 | |
| 82 enum | |
| 83 { | |
| 84 T38_TIMED_STEP_NONE = 0, | |
| 85 T38_TIMED_STEP_NON_ECM_MODEM, | |
| 86 T38_TIMED_STEP_NON_ECM_MODEM_2, | |
| 87 T38_TIMED_STEP_NON_ECM_MODEM_3, | |
| 88 T38_TIMED_STEP_HDLC_MODEM, | |
| 89 T38_TIMED_STEP_HDLC_MODEM_2, | |
| 90 T38_TIMED_STEP_HDLC_MODEM_3, | |
| 91 T38_TIMED_STEP_CED, | |
| 92 T38_TIMED_STEP_CNG, | |
| 93 T38_TIMED_STEP_PAUSE | |
| 94 }; | |
| 95 | |
| 96 static int get_non_ecm_image_chunk(t38_terminal_state_t *s, uint8_t *buf, int len) | |
| 97 { | |
| 98 int i; | |
| 99 int j; | |
| 100 int bit; | |
| 101 int byte; | |
| 102 | |
| 103 for (i = 0; i < len; i++) | |
| 104 { | |
| 105 byte = 0; | |
| 106 for (j = 0; j < 8; j++) | |
| 107 { | |
| 108 if ((bit = t30_non_ecm_get_bit(&s->t30_state)) == PUTBIT_END_OF_DATA) | |
| 109 { | |
| 110 if (j > 0) | |
| 111 { | |
| 112 byte <<= (8 - j); | |
| 113 buf[i++] = (uint8_t) byte; | |
| 114 } | |
| 115 return -i; | |
| 116 } | |
| 117 byte = (byte << 1) | (bit & 0x01); | |
| 118 } | |
| 119 buf[i] = (uint8_t) byte; | |
| 120 } | |
| 121 return i; | |
| 122 } | |
| 123 /*- End of function --------------------------------------------------------*/ | |
| 124 | |
| 125 static int process_rx_missing(t38_core_state_t *s, void *user_data, int rx_seq_no, int expected_seq_no) | |
| 126 { | |
| 127 t38_terminal_state_t *t; | |
| 128 | |
| 129 t = (t38_terminal_state_t *) user_data; | |
| 130 t->missing_data = TRUE; | |
| 131 return 0; | |
| 132 } | |
| 133 /*- End of function --------------------------------------------------------*/ | |
| 134 | |
| 135 static int process_rx_indicator(t38_core_state_t *s, void *user_data, int indicator) | |
| 136 { | |
| 137 t38_terminal_state_t *t; | |
| 138 | |
| 139 t = (t38_terminal_state_t *) user_data; | |
| 140 /* In termination mode we don't care very much about indicators telling us training | |
| 141 is starting. We only care about the actual data. */ | |
| 142 switch (indicator) | |
| 143 { | |
| 144 case T38_IND_NO_SIGNAL: | |
| 145 if (t->t38.current_rx_indicator == T38_IND_V21_PREAMBLE | |
| 146 && | |
| 147 (t->current_rx_type == T30_MODEM_V21 || t->current_rx_type == T30_MODEM_CNG)) | |
| 148 { | |
| 149 t30_hdlc_accept(&(t->t30_state), TRUE, NULL, PUTBIT_CARRIER_DOWN); | |
| 150 } | |
| 151 t->timeout_rx_samples = 0; | |
| 152 break; | |
| 153 case T38_IND_CNG: | |
| 154 break; | |
| 155 case T38_IND_CED: | |
| 156 break; | |
| 157 case T38_IND_V21_PREAMBLE: | |
| 158 if (t->current_rx_type == T30_MODEM_V21) | |
| 159 { | |
| 160 t30_hdlc_accept(&(t->t30_state), TRUE, NULL, PUTBIT_CARRIER_UP); | |
| 161 t30_hdlc_accept(&(t->t30_state), TRUE, NULL, PUTBIT_FRAMING_OK); | |
| 162 } | |
| 163 break; | |
| 164 case T38_IND_V27TER_2400_TRAINING: | |
| 165 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 166 break; | |
| 167 case T38_IND_V27TER_4800_TRAINING: | |
| 168 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 169 break; | |
| 170 case T38_IND_V29_7200_TRAINING: | |
| 171 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 172 break; | |
| 173 case T38_IND_V29_9600_TRAINING: | |
| 174 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 175 break; | |
| 176 case T38_IND_V17_7200_SHORT_TRAINING: | |
| 177 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 178 break; | |
| 179 case T38_IND_V17_7200_LONG_TRAINING: | |
| 180 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 181 break; | |
| 182 case T38_IND_V17_9600_SHORT_TRAINING: | |
| 183 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 184 break; | |
| 185 case T38_IND_V17_9600_LONG_TRAINING: | |
| 186 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 187 break; | |
| 188 case T38_IND_V17_12000_SHORT_TRAINING: | |
| 189 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 190 break; | |
| 191 case T38_IND_V17_12000_LONG_TRAINING: | |
| 192 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 193 break; | |
| 194 case T38_IND_V17_14400_SHORT_TRAINING: | |
| 195 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 196 break; | |
| 197 case T38_IND_V17_14400_LONG_TRAINING: | |
| 198 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 199 break; | |
| 200 case T38_IND_V8_ANSAM: | |
| 201 break; | |
| 202 case T38_IND_V8_SIGNAL: | |
| 203 break; | |
| 204 case T38_IND_V34_CNTL_CHANNEL_1200: | |
| 205 break; | |
| 206 case T38_IND_V34_PRI_CHANNEL: | |
| 207 break; | |
| 208 case T38_IND_V34_CC_RETRAIN: | |
| 209 break; | |
| 210 case T38_IND_V33_12000_TRAINING: | |
| 211 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 212 break; | |
| 213 case T38_IND_V33_14400_TRAINING: | |
| 214 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 215 break; | |
| 216 default: | |
| 217 break; | |
| 218 } | |
| 219 t->tx_out_bytes = 0; | |
| 220 t->missing_data = FALSE; | |
| 221 return 0; | |
| 222 } | |
| 223 /*- End of function --------------------------------------------------------*/ | |
| 224 | |
| 225 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) | |
| 226 { | |
| 227 int i; | |
| 228 t38_terminal_state_t *t; | |
| 229 | |
| 230 t = (t38_terminal_state_t *) user_data; | |
| 231 #if 0 | |
| 232 /* In termination mode we don't care very much what the data type is. */ | |
| 233 switch (data_type) | |
| 234 { | |
| 235 case T38_DATA_V21: | |
| 236 case T38_DATA_V27TER_2400: | |
| 237 case T38_DATA_V27TER_4800: | |
| 238 case T38_DATA_V29_7200: | |
| 239 case T38_DATA_V29_9600: | |
| 240 case T38_DATA_V17_7200: | |
| 241 case T38_DATA_V17_9600: | |
| 242 case T38_DATA_V17_12000: | |
| 243 case T38_DATA_V17_14400: | |
| 244 case T38_DATA_V8: | |
| 245 case T38_DATA_V34_PRI_RATE: | |
| 246 case T38_DATA_V34_CC_1200: | |
| 247 case T38_DATA_V34_PRI_CH: | |
| 248 case T38_DATA_V33_12000: | |
| 249 case T38_DATA_V33_14400: | |
| 250 default: | |
| 251 break; | |
| 252 } | |
| 253 #endif | |
| 254 | |
| 255 switch (field_type) | |
| 256 { | |
| 257 case T38_FIELD_HDLC_DATA: | |
| 258 if (t->tx_out_bytes + len <= T38_MAX_HDLC_LEN) | |
| 259 { | |
| 260 for (i = 0; i < len; i++) | |
| 261 t->tx_data[t->tx_out_bytes++] = bit_reverse8(buf[i]); | |
| 262 } | |
| 263 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 264 break; | |
| 265 case T38_FIELD_HDLC_FCS_OK: | |
| 266 if (len > 0) | |
| 267 { | |
| 268 span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_OK!\n"); | |
| 269 /* The sender has incorrectly included data in this message. It is unclear what we should do | |
| 270 with it, to maximise tolerance of buggy implementations. */ | |
| 271 } | |
| 272 span_log(&t->logging, SPAN_LOG_FLOW, "Type %s - CRC OK (%s)\n", (t->tx_out_bytes >= 3) ? t30_frametype(t->tx_data[2]) : "???", (t->missing_data) ? "missing octets" : "clean"); | |
| 273 /* Don't deal with zero length frames. Some T.38 implementations send multiple T38_FIELD_HDLC_FCS_OK | |
| 274 packets, when they have sent no data for the body of the frame. */ | |
| 275 if (t->tx_out_bytes > 0) | |
| 276 t30_hdlc_accept(&(t->t30_state), !t->missing_data, t->tx_data, t->tx_out_bytes); | |
| 277 t->tx_out_bytes = 0; | |
| 278 t->missing_data = FALSE; | |
| 279 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 280 break; | |
| 281 case T38_FIELD_HDLC_FCS_BAD: | |
| 282 if (len > 0) | |
| 283 { | |
| 284 span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_BAD!\n"); | |
| 285 /* The sender has incorrectly included data in this message. We can safely ignore it, as the | |
| 286 bad FCS means we will throw away the whole message, anyway. */ | |
| 287 } | |
| 288 span_log(&t->logging, SPAN_LOG_FLOW, "Type %s - CRC bad (%s)\n", (t->tx_out_bytes >= 3) ? t30_frametype(t->tx_data[2]) : "???", (t->missing_data) ? "missing octets" : "clean"); | |
| 289 if (t->tx_out_bytes > 0) | |
| 290 t30_hdlc_accept(&(t->t30_state), FALSE, t->tx_data, t->tx_out_bytes); | |
| 291 t->tx_out_bytes = 0; | |
| 292 t->missing_data = FALSE; | |
| 293 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 294 break; | |
| 295 case T38_FIELD_HDLC_FCS_OK_SIG_END: | |
| 296 if (len > 0) | |
| 297 { | |
| 298 span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_OK_SIG_END!\n"); | |
| 299 /* The sender has incorrectly included data in this message. It is unclear what we should do | |
| 300 with it, to maximise tolerance of buggy implementations. */ | |
| 301 } | |
| 302 span_log(&t->logging, SPAN_LOG_FLOW, "Type %s - CRC OK, sig end (%s)\n", (t->tx_out_bytes >= 3) ? t30_frametype(t->tx_data[2]) : "???", (t->missing_data) ? "missing octets" : "clean"); | |
| 303 /* Don't deal with zero length frames. Some T.38 implementations send multiple T38_FIELD_HDLC_FCS_OK | |
| 304 packets, when they have sent no data for the body of the frame. */ | |
| 305 if (t->tx_out_bytes > 0) | |
| 306 t30_hdlc_accept(&(t->t30_state), !t->missing_data, t->tx_data, t->tx_out_bytes); | |
| 307 t30_hdlc_accept(&(t->t30_state), TRUE, NULL, PUTBIT_CARRIER_DOWN); | |
| 308 t->tx_out_bytes = 0; | |
| 309 t->missing_data = FALSE; | |
| 310 t->timeout_rx_samples = 0; | |
| 311 break; | |
| 312 case T38_FIELD_HDLC_FCS_BAD_SIG_END: | |
| 313 if (len > 0) | |
| 314 { | |
| 315 span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_FCS_BAD_SIG_END!\n"); | |
| 316 /* The sender has incorrectly included data in this message. We can safely ignore it, as the | |
| 317 bad FCS means we will throw away the whole message, anyway. */ | |
| 318 } | |
| 319 span_log(&t->logging, SPAN_LOG_FLOW, "Type %s - CRC bad, sig end (%s)\n", (t->tx_out_bytes >= 3) ? t30_frametype(t->tx_data[2]) : "???", (t->missing_data) ? "missing octets" : "clean"); | |
| 320 if (t->tx_out_bytes > 0) | |
| 321 t30_hdlc_accept(&(t->t30_state), FALSE, t->tx_data, t->tx_out_bytes); | |
| 322 t30_hdlc_accept(&(t->t30_state), TRUE, NULL, PUTBIT_CARRIER_DOWN); | |
| 323 t->tx_out_bytes = 0; | |
| 324 t->missing_data = FALSE; | |
| 325 t->timeout_rx_samples = 0; | |
| 326 break; | |
| 327 case T38_FIELD_HDLC_SIG_END: | |
| 328 if (len > 0) | |
| 329 { | |
| 330 span_log(&t->logging, SPAN_LOG_WARNING, "There is data in a T38_FIELD_HDLC_SIG_END!\n"); | |
| 331 /* The sender has incorrectly included data in this message, but there seems nothing meaningful | |
| 332 it could be. There could not be an FCS good/bad report beyond this. */ | |
| 333 } | |
| 334 /* This message is expected under 2 circumstances. One is as an alternative to T38_FIELD_HDLC_FCS_OK_SIG_END - | |
| 335 i.e. they send T38_FIELD_HDLC_FCS_OK, and then T38_FIELD_HDLC_SIG_END when the carrier actually drops. | |
| 336 The other is because the HDLC signal drops unexpectedly - i.e. not just after a final frame. */ | |
| 337 /* WORKAROUND: At least some Mediatrix boxes have a bug, where they can send this message at the | |
| 338 end of non-ECM data. We need to tolerate this. We use the generic receive complete | |
| 339 indication, rather than the specific HDLC carrier down. */ | |
| 340 t->tx_out_bytes = 0; | |
| 341 t->missing_data = FALSE; | |
| 342 t->timeout_rx_samples = 0; | |
| 343 t30_receive_complete(&(t->t30_state)); | |
| 344 break; | |
| 345 case T38_FIELD_T4_NON_ECM_DATA: | |
| 346 if (!t->rx_signal_present) | |
| 347 { | |
| 348 t30_non_ecm_put_bit(&(t->t30_state), PUTBIT_TRAINING_SUCCEEDED); | |
| 349 t->rx_signal_present = TRUE; | |
| 350 } | |
| 351 for (i = 0; i < len; i++) | |
| 352 t30_non_ecm_putbyte(&(t->t30_state), buf[i]); | |
| 353 t->timeout_rx_samples = t->samples + ms_to_samples(MID_RX_TIMEOUT); | |
| 354 break; | |
| 355 case T38_FIELD_T4_NON_ECM_SIG_END: | |
| 356 if (len > 0) | |
| 357 { | |
| 358 if (!t->rx_signal_present) | |
| 359 { | |
| 360 t30_non_ecm_put_bit(&(t->t30_state), PUTBIT_TRAINING_SUCCEEDED); | |
| 361 t->rx_signal_present = TRUE; | |
| 362 } | |
| 363 for (i = 0; i < len; i++) | |
| 364 t30_non_ecm_putbyte(&(t->t30_state), buf[i]); | |
| 365 } | |
| 366 /* WORKAROUND: At least some Mediatrix boxes have a bug, where they can send HDLC signal end where | |
| 367 they should send non-ECM signal end. It is possible they also do the opposite. | |
| 368 We need to tolerate this, so we use the generic receive complete | |
| 369 indication, rather than the specific non-ECM carrier down. */ | |
| 370 t30_receive_complete(&(t->t30_state)); | |
| 371 t->rx_signal_present = FALSE; | |
| 372 t->timeout_rx_samples = 0; | |
| 373 break; | |
| 374 case T38_FIELD_CM_MESSAGE: | |
| 375 case T38_FIELD_JM_MESSAGE: | |
| 376 case T38_FIELD_CI_MESSAGE: | |
| 377 case T38_FIELD_V34RATE: | |
| 378 default: | |
| 379 break; | |
| 380 } | |
| 381 return 0; | |
| 382 } | |
| 383 /*- End of function --------------------------------------------------------*/ | |
| 384 | |
| 385 static void send_hdlc(void *user_data, const uint8_t *msg, int len) | |
| 386 { | |
| 387 t38_terminal_state_t *s; | |
| 388 int j; | |
| 389 | |
| 390 s = (t38_terminal_state_t *) user_data; | |
| 391 if (len <= 0) | |
| 392 { | |
| 393 s->hdlc_tx_len = -1; | |
| 394 } | |
| 395 else | |
| 396 { | |
| 397 for (j = 0; j < len; j++) | |
| 398 s->hdlc_tx_buf[j] = bit_reverse8(msg[j]); | |
| 399 s->hdlc_tx_len = len; | |
| 400 s->hdlc_tx_ptr = 0; | |
| 401 } | |
| 402 } | |
| 403 /*- End of function --------------------------------------------------------*/ | |
| 404 | |
| 405 int t38_terminal_send_timeout(t38_terminal_state_t *s, int samples) | |
| 406 { | |
| 407 int len; | |
| 408 int i; | |
| 409 int previous; | |
| 410 uint8_t buf[100]; | |
| 411 /* Training times for all the modem options, with and without TEP */ | |
| 412 static const int training_time[] = | |
| 413 { | |
| 414 0, 0, /* T38_IND_NO_SIGNAL */ | |
| 415 0, 0, /* T38_IND_CNG */ | |
| 416 0, 0, /* T38_IND_CED */ | |
| 417 1000, 1000, /* T38_IND_V21_PREAMBLE */ | |
| 418 943, 1158, /* T38_IND_V27TER_2400_TRAINING */ | |
| 419 708, 923, /* T38_IND_V27TER_4800_TRAINING */ | |
| 420 234, 454, /* T38_IND_V29_7200_TRAINING */ | |
| 421 234, 454, /* T38_IND_V29_9600_TRAINING */ | |
| 422 142, 367, /* T38_IND_V17_7200_SHORT_TRAINING */ | |
| 423 1393, 1618, /* T38_IND_V17_7200_LONG_TRAINING */ | |
| 424 142, 367, /* T38_IND_V17_9600_SHORT_TRAINING */ | |
| 425 1393, 1618, /* T38_IND_V17_9600_LONG_TRAINING */ | |
| 426 142, 367, /* T38_IND_V17_12000_SHORT_TRAINING */ | |
| 427 1393, 1618, /* T38_IND_V17_12000_LONG_TRAINING */ | |
| 428 142, 367, /* T38_IND_V17_14400_SHORT_TRAINING */ | |
| 429 1393, 1618, /* T38_IND_V17_14400_LONG_TRAINING */ | |
| 430 0, 0, /* T38_IND_V8_ANSAM */ | |
| 431 0, 0, /* T38_IND_V8_SIGNAL */ | |
| 432 0, 0, /* T38_IND_V34_CNTL_CHANNEL_1200 */ | |
| 433 0, 0, /* T38_IND_V34_PRI_CHANNEL */ | |
| 434 0, 0, /* T38_IND_V34_CC_RETRAIN */ | |
| 435 0, 0, /* T38_IND_V33_12000_TRAINING */ | |
| 436 0, 0, /* T38_IND_V33_14400_TRAINING */ | |
| 437 }; | |
| 438 | |
| 439 if (s->current_rx_type == T30_MODEM_DONE || s->current_tx_type == T30_MODEM_DONE) | |
| 440 return TRUE; | |
| 441 | |
| 442 s->samples += samples; | |
| 443 t30_timer_update(&s->t30_state, samples); | |
| 444 if (s->timeout_rx_samples && s->samples > s->timeout_rx_samples) | |
| 445 { | |
| 446 span_log(&s->logging, SPAN_LOG_FLOW, "Timeout mid-receive\n"); | |
| 447 s->timeout_rx_samples = 0; | |
| 448 t30_receive_complete(&(s->t30_state)); | |
| 449 } | |
| 450 if (s->timed_step == T38_TIMED_STEP_NONE) | |
| 451 return FALSE; | |
| 452 if (s->samples < s->next_tx_samples) | |
| 453 return FALSE; | |
| 454 /* Its time to send something */ | |
| 455 switch (s->timed_step) | |
| 456 { | |
| 457 case T38_TIMED_STEP_NON_ECM_MODEM: | |
| 458 /* Create a 75ms silence */ | |
| 459 if (s->t38.current_tx_indicator != T38_IND_NO_SIGNAL) | |
| 460 t38_core_send_indicator(&s->t38, T38_IND_NO_SIGNAL, INDICATOR_TX_COUNT); | |
| 461 s->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_2; | |
| 462 s->next_tx_samples += ms_to_samples(75); | |
| 463 break; | |
| 464 case T38_TIMED_STEP_NON_ECM_MODEM_2: | |
| 465 /* Switch on a fast modem, and give the training time to complete */ | |
| 466 t38_core_send_indicator(&s->t38, s->next_tx_indicator, INDICATOR_TX_COUNT); | |
| 467 s->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_3; | |
| 468 s->next_tx_samples += ms_to_samples(training_time[s->next_tx_indicator << 1]); | |
| 469 break; | |
| 470 case T38_TIMED_STEP_NON_ECM_MODEM_3: | |
| 471 /* Send a chunk of non-ECM image data */ | |
| 472 /* T.38 says it is OK to send the last of the non-ECM data in the signal end message. | |
| 473 However, I think the early versions of T.38 said the signal end message should not | |
| 474 contain data. Hopefully, following the current spec will not cause compatibility | |
| 475 issues. */ | |
| 476 if ((len = get_non_ecm_image_chunk(s, buf, s->octets_per_data_packet)) > 0) | |
| 477 { | |
| 478 s->next_tx_samples += ms_to_samples(MS_PER_TX_CHUNK); | |
| 479 t38_core_send_data(&s->t38, s->current_tx_data_type, T38_FIELD_T4_NON_ECM_DATA, buf, len); | |
| 480 } | |
| 481 else | |
| 482 { | |
| 483 t38_core_send_data(&s->t38, s->current_tx_data_type, T38_FIELD_T4_NON_ECM_SIG_END, buf, -len); | |
| 484 /* This should not be needed, since the message above indicates the end of the signal, but it | |
| 485 seems like it can improve compatibility with quirky implementations. */ | |
| 486 t38_core_send_indicator(&s->t38, T38_IND_NO_SIGNAL, INDICATOR_TX_COUNT); | |
| 487 s->timed_step = T38_TIMED_STEP_NONE; | |
| 488 t30_send_complete(&(s->t30_state)); | |
| 489 } | |
| 490 break; | |
| 491 case T38_TIMED_STEP_HDLC_MODEM: | |
| 492 /* Send HDLC preambling */ | |
| 493 t38_core_send_indicator(&s->t38, s->next_tx_indicator, INDICATOR_TX_COUNT); | |
| 494 s->next_tx_samples += ms_to_samples(training_time[s->next_tx_indicator << 1]); | |
| 495 s->timed_step = T38_TIMED_STEP_HDLC_MODEM_2; | |
| 496 break; | |
| 497 case T38_TIMED_STEP_HDLC_MODEM_2: | |
| 498 /* Send a chunk of HDLC data */ | |
| 499 i = s->octets_per_data_packet; | |
| 500 if (i >= (s->hdlc_tx_len - s->hdlc_tx_ptr)) | |
| 501 { | |
| 502 i = s->hdlc_tx_len - s->hdlc_tx_ptr; | |
| 503 s->timed_step = T38_TIMED_STEP_HDLC_MODEM_3; | |
| 504 } | |
| 505 t38_core_send_data(&s->t38, s->current_tx_data_type, T38_FIELD_HDLC_DATA, &s->hdlc_tx_buf[s->hdlc_tx_ptr], i); | |
| 506 s->hdlc_tx_ptr += i; | |
| 507 s->next_tx_samples += ms_to_samples(MS_PER_TX_CHUNK); | |
| 508 break; | |
| 509 case T38_TIMED_STEP_HDLC_MODEM_3: | |
| 510 /* End of HDLC frame */ | |
| 511 previous = s->current_tx_data_type; | |
| 512 s->hdlc_tx_ptr = 0; | |
| 513 s->hdlc_tx_len = 0; | |
| 514 t30_send_complete(&s->t30_state); | |
| 515 if (s->hdlc_tx_len < 0) | |
| 516 { | |
| 517 t38_core_send_data(&s->t38, previous, T38_FIELD_HDLC_FCS_OK_SIG_END, NULL, 0); | |
| 518 /* We have already sent T38_FIELD_HDLC_FCS_OK_SIG_END. It seems some boxes may not like | |
| 519 us sending a T38_FIELD_HDLC_SIG_END at this point. Just say there is no signal. */ | |
| 520 t38_core_send_indicator(&s->t38, T38_IND_NO_SIGNAL, INDICATOR_TX_COUNT); | |
| 521 s->hdlc_tx_len = 0; | |
| 522 t30_send_complete(&s->t30_state); | |
| 523 if (s->hdlc_tx_len) | |
| 524 s->timed_step = T38_TIMED_STEP_HDLC_MODEM; | |
| 525 } | |
| 526 else | |
| 527 { | |
| 528 t38_core_send_data(&s->t38, previous, T38_FIELD_HDLC_FCS_OK, NULL, 0); | |
| 529 if (s->hdlc_tx_len) | |
| 530 s->timed_step = T38_TIMED_STEP_HDLC_MODEM_2; | |
| 531 } | |
| 532 s->next_tx_samples += ms_to_samples(MS_PER_TX_CHUNK); | |
| 533 break; | |
| 534 case T38_TIMED_STEP_CED: | |
| 535 /* Initial 200ms delay over. Send the CED indicator */ | |
| 536 s->next_tx_samples = s->samples + ms_to_samples(3000); | |
| 537 s->timed_step = T38_TIMED_STEP_PAUSE; | |
| 538 t38_core_send_indicator(&s->t38, T38_IND_CED, INDICATOR_TX_COUNT); | |
| 539 s->current_tx_data_type = T38_DATA_NONE; | |
| 540 break; | |
| 541 case T38_TIMED_STEP_CNG: | |
| 542 /* Initial short delay over. Send the CNG indicator */ | |
| 543 s->timed_step = T38_TIMED_STEP_NONE; | |
| 544 t38_core_send_indicator(&s->t38, T38_IND_CNG, INDICATOR_TX_COUNT); | |
| 545 s->current_tx_data_type = T38_DATA_NONE; | |
| 546 break; | |
| 547 case T38_TIMED_STEP_PAUSE: | |
| 548 /* End of timed pause */ | |
| 549 s->timed_step = T38_TIMED_STEP_NONE; | |
| 550 t30_send_complete(&s->t30_state); | |
| 551 break; | |
| 552 } | |
| 553 return FALSE; | |
| 554 } | |
| 555 /*- End of function --------------------------------------------------------*/ | |
| 556 | |
| 557 static void set_rx_type(void *user_data, int type, int short_train, int use_hdlc) | |
| 558 { | |
| 559 t38_terminal_state_t *s; | |
| 560 | |
| 561 s = (t38_terminal_state_t *) user_data; | |
| 562 span_log(&s->logging, SPAN_LOG_FLOW, "Set rx type %d\n", type); | |
| 563 s->current_rx_type = type; | |
| 564 } | |
| 565 /*- End of function --------------------------------------------------------*/ | |
| 566 | |
| 567 static void set_tx_type(void *user_data, int type, int short_train, int use_hdlc) | |
| 568 { | |
| 569 t38_terminal_state_t *s; | |
| 570 | |
| 571 s = (t38_terminal_state_t *) user_data; | |
| 572 span_log(&s->logging, SPAN_LOG_FLOW, "Set tx type %d\n", type); | |
| 573 if (s->current_tx_type == type) | |
| 574 return; | |
| 575 | |
| 576 switch (type) | |
| 577 { | |
| 578 case T30_MODEM_NONE: | |
| 579 s->timed_step = T38_TIMED_STEP_NONE; | |
| 580 s->current_tx_data_type = T38_DATA_NONE; | |
| 581 break; | |
| 582 case T30_MODEM_PAUSE: | |
| 583 s->next_tx_samples = s->samples + ms_to_samples(short_train); | |
| 584 s->timed_step = T38_TIMED_STEP_PAUSE; | |
| 585 s->current_tx_data_type = T38_DATA_NONE; | |
| 586 break; | |
| 587 case T30_MODEM_CED: | |
| 588 /* A 200ms initial delay is specified. Delay this amount before the CED indicator is sent. */ | |
| 589 s->next_tx_samples = s->samples + ms_to_samples(200); | |
| 590 s->timed_step = T38_TIMED_STEP_CED; | |
| 591 s->current_tx_data_type = T38_DATA_NONE; | |
| 592 break; | |
| 593 case T30_MODEM_CNG: | |
| 594 /* Allow a short initial delay, so the chances of the other end actually being ready to receive | |
| 595 the CNG indicator are improved. */ | |
| 596 s->next_tx_samples = s->samples + ms_to_samples(200); | |
| 597 s->timed_step = T38_TIMED_STEP_CNG; | |
| 598 s->current_tx_data_type = T38_DATA_NONE; | |
| 599 break; | |
| 600 case T30_MODEM_V21: | |
| 601 if (s->current_tx_type > T30_MODEM_V21) | |
| 602 { | |
| 603 /* Pause before switching from phase C, as per T.30. If we omit this, the receiver | |
| 604 might not see the carrier fall between the high speed and low speed sections. */ | |
| 605 s->next_tx_samples = s->samples + ms_to_samples(75); | |
| 606 } | |
| 607 else | |
| 608 { | |
| 609 s->next_tx_samples = s->samples; | |
| 610 } | |
| 611 s->octets_per_data_packet = MS_PER_TX_CHUNK*300/(8*1000); | |
| 612 s->next_tx_indicator = T38_IND_V21_PREAMBLE; | |
| 613 s->current_tx_data_type = T38_DATA_V21; | |
| 614 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 615 break; | |
| 616 case T30_MODEM_V27TER_2400: | |
| 617 s->octets_per_data_packet = MS_PER_TX_CHUNK*2400/(8*1000); | |
| 618 s->next_tx_indicator = T38_IND_V27TER_2400_TRAINING; | |
| 619 s->current_tx_data_type = T38_DATA_V27TER_2400; | |
| 620 s->next_tx_samples = s->samples + ms_to_samples(MS_PER_TX_CHUNK); | |
| 621 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 622 break; | |
| 623 case T30_MODEM_V27TER_4800: | |
| 624 s->octets_per_data_packet = MS_PER_TX_CHUNK*4800/(8*1000); | |
| 625 s->next_tx_indicator = T38_IND_V27TER_4800_TRAINING; | |
| 626 s->current_tx_data_type = T38_DATA_V27TER_4800; | |
| 627 s->next_tx_samples = s->samples + ms_to_samples(MS_PER_TX_CHUNK); | |
| 628 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 629 break; | |
| 630 case T30_MODEM_V29_7200: | |
| 631 s->octets_per_data_packet = MS_PER_TX_CHUNK*7200/(8*1000); | |
| 632 s->next_tx_indicator = T38_IND_V29_7200_TRAINING; | |
| 633 s->current_tx_data_type = T38_DATA_V29_7200; | |
| 634 s->next_tx_samples = s->samples + ms_to_samples(MS_PER_TX_CHUNK); | |
| 635 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 636 break; | |
| 637 case T30_MODEM_V29_9600: | |
| 638 s->octets_per_data_packet = MS_PER_TX_CHUNK*9600/(8*1000); | |
| 639 s->next_tx_indicator = T38_IND_V29_9600_TRAINING; | |
| 640 s->current_tx_data_type = T38_DATA_V29_9600; | |
| 641 s->next_tx_samples = s->samples + ms_to_samples(MS_PER_TX_CHUNK); | |
| 642 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 643 break; | |
| 644 case T30_MODEM_V17_7200: | |
| 645 s->octets_per_data_packet = MS_PER_TX_CHUNK*7200/(8*1000); | |
| 646 s->next_tx_indicator = (short_train) ? T38_IND_V17_7200_SHORT_TRAINING : T38_IND_V17_7200_LONG_TRAINING; | |
| 647 s->current_tx_data_type = T38_DATA_V17_7200; | |
| 648 s->next_tx_samples = s->samples + ms_to_samples(MS_PER_TX_CHUNK); | |
| 649 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 650 break; | |
| 651 case T30_MODEM_V17_9600: | |
| 652 s->octets_per_data_packet = MS_PER_TX_CHUNK*9600/(8*1000); | |
| 653 s->next_tx_indicator = (short_train) ? T38_IND_V17_9600_SHORT_TRAINING : T38_IND_V17_9600_LONG_TRAINING; | |
| 654 s->current_tx_data_type = T38_DATA_V17_9600; | |
| 655 s->next_tx_samples = s->samples + ms_to_samples(MS_PER_TX_CHUNK); | |
| 656 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 657 break; | |
| 658 case T30_MODEM_V17_12000: | |
| 659 s->octets_per_data_packet = MS_PER_TX_CHUNK*12000/(8*1000); | |
| 660 s->next_tx_indicator = (short_train) ? T38_IND_V17_12000_SHORT_TRAINING : T38_IND_V17_12000_LONG_TRAINING; | |
| 661 s->current_tx_data_type = T38_DATA_V17_12000; | |
| 662 s->next_tx_samples = s->samples + ms_to_samples(MS_PER_TX_CHUNK); | |
| 663 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 664 break; | |
| 665 case T30_MODEM_V17_14400: | |
| 666 s->octets_per_data_packet = MS_PER_TX_CHUNK*14400/(8*1000); | |
| 667 s->next_tx_indicator = (short_train) ? T38_IND_V17_14400_SHORT_TRAINING : T38_IND_V17_14400_LONG_TRAINING; | |
| 668 s->current_tx_data_type = T38_DATA_V17_14400; | |
| 669 s->next_tx_samples = s->samples + ms_to_samples(MS_PER_TX_CHUNK); | |
| 670 s->timed_step = (use_hdlc) ? T38_TIMED_STEP_HDLC_MODEM : T38_TIMED_STEP_NON_ECM_MODEM; | |
| 671 break; | |
| 672 case T30_MODEM_DONE: | |
| 673 span_log(&s->logging, SPAN_LOG_FLOW, "FAX exchange complete\n"); | |
| 674 s->timed_step = T38_TIMED_STEP_NONE; | |
| 675 s->current_tx_data_type = T38_DATA_NONE; | |
| 676 break; | |
| 677 } | |
| 678 s->current_tx_type = type; | |
| 679 } | |
| 680 /*- End of function --------------------------------------------------------*/ | |
| 681 | |
| 682 t38_terminal_state_t *t38_terminal_init(t38_terminal_state_t *s, | |
| 683 int calling_party, | |
| 684 t38_tx_packet_handler_t *tx_packet_handler, | |
| 685 void *tx_packet_user_data) | |
| 686 { | |
| 687 if (tx_packet_handler == NULL) | |
| 688 return NULL; | |
| 689 | |
| 690 memset(s, 0, sizeof(*s)); | |
| 691 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
| 692 span_log_set_protocol(&s->logging, "T.38T"); | |
| 693 s->rx_signal_present = FALSE; | |
| 694 | |
| 695 s->timed_step = T38_TIMED_STEP_NONE; | |
| 696 s->hdlc_tx_ptr = 0; | |
| 697 | |
| 698 t38_core_init(&s->t38, process_rx_indicator, process_rx_data, process_rx_missing, (void *) s); | |
| 699 s->t38.iaf = TRUE; | |
| 700 s->t38.tx_packet_handler = tx_packet_handler; | |
| 701 s->t38.tx_packet_user_data = tx_packet_user_data; | |
| 702 s->t38.fastest_image_data_rate = 14400; | |
| 703 | |
| 704 s->timed_step = T38_TIMED_STEP_NONE; | |
| 705 s->current_tx_data_type = T38_DATA_NONE; | |
| 706 s->next_tx_samples = 0; | |
| 707 | |
| 708 t30_init(&(s->t30_state), | |
| 709 calling_party, | |
| 710 set_rx_type, | |
| 711 (void *) s, | |
| 712 set_tx_type, | |
| 713 (void *) s, | |
| 714 send_hdlc, | |
| 715 (void *) s); | |
| 716 t30_set_supported_modems(&(s->t30_state), | |
| 717 T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17); | |
| 718 t30_restart(&s->t30_state); | |
| 719 return s; | |
| 720 } | |
| 721 /*- End of function --------------------------------------------------------*/ | |
| 722 /*- End of file ------------------------------------------------------------*/ | 
