Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/t38_core.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
| author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
|---|---|
| date | Fri, 25 Jun 2010 15:50:58 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 3:c6c5a16ce2f2 | 4:26cd8f1ef0b1 |
|---|---|
| 1 /* | |
| 2 * SpanDSP - a series of DSP components for telephony | |
| 3 * | |
| 4 * t38_core.c - Encode and decode the ASN.1 of a T.38 IFP message | |
| 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 Lesser General Public License version 2.1, | |
| 14 * as 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 Lesser General Public License for more details. | |
| 20 * | |
| 21 * You should have received a copy of the GNU Lesser General Public | |
| 22 * License 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_core.c,v 1.54 2009/10/09 14:53:57 steveu Exp $ | |
| 26 */ | |
| 27 | |
| 28 /*! \file */ | |
| 29 | |
| 30 #if defined(HAVE_CONFIG_H) | |
| 31 #include "config.h" | |
| 32 #endif | |
| 33 | |
| 34 #include <inttypes.h> | |
| 35 #include <stdlib.h> | |
| 36 #include <stdio.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 "floating_fudge.h" | |
| 47 #include <assert.h> | |
| 48 #include <memory.h> | |
| 49 #include <tiffio.h> | |
| 50 | |
| 51 #include "spandsp/telephony.h" | |
| 52 #include "spandsp/logging.h" | |
| 53 #include "spandsp/bit_operations.h" | |
| 54 #include "spandsp/t38_core.h" | |
| 55 | |
| 56 #include "spandsp/private/logging.h" | |
| 57 #include "spandsp/private/t38_core.h" | |
| 58 | |
| 59 #define ACCEPTABLE_SEQ_NO_OFFSET 2000 | |
| 60 | |
| 61 /* The times for training, the optional TEP, and the HDLC preamble, for all the modem options, in ms. | |
| 62 Note that the preamble for V.21 is 1s+-15%, and for the other modems is 200ms+100ms. */ | |
| 63 static const struct | |
| 64 { | |
| 65 int tep; | |
| 66 int training; | |
| 67 int flags; | |
| 68 } modem_startup_time[] = | |
| 69 { | |
| 70 { 0, 75000, 0}, /* T38_IND_NO_SIGNAL */ | |
| 71 { 0, 0, 0}, /* T38_IND_CNG */ | |
| 72 { 0, 3000000, 0}, /* T38_IND_CED */ | |
| 73 { 0, 0, 1000000}, /* T38_IND_V21_PREAMBLE */ /* TODO: 850ms should be OK for this, but it causes trouble with some ATAs. Why? */ | |
| 74 { 215000, 943000, 200000}, /* T38_IND_V27TER_2400_TRAINING */ | |
| 75 { 215000, 708000, 200000}, /* T38_IND_V27TER_4800_TRAINING */ | |
| 76 { 215000, 234000, 200000}, /* T38_IND_V29_7200_TRAINING */ | |
| 77 { 215000, 234000, 200000}, /* T38_IND_V29_9600_TRAINING */ | |
| 78 { 215000, 142000, 200000}, /* T38_IND_V17_7200_SHORT_TRAINING */ | |
| 79 { 215000, 1393000, 200000}, /* T38_IND_V17_7200_LONG_TRAINING */ | |
| 80 { 215000, 142000, 200000}, /* T38_IND_V17_9600_SHORT_TRAINING */ | |
| 81 { 215000, 1393000, 200000}, /* T38_IND_V17_9600_LONG_TRAINING */ | |
| 82 { 215000, 142000, 200000}, /* T38_IND_V17_12000_SHORT_TRAINING */ | |
| 83 { 215000, 1393000, 200000}, /* T38_IND_V17_12000_LONG_TRAINING */ | |
| 84 { 215000, 142000, 200000}, /* T38_IND_V17_14400_SHORT_TRAINING */ | |
| 85 { 215000, 1393000, 200000}, /* T38_IND_V17_14400_LONG_TRAINING */ | |
| 86 { 215000, 0, 0}, /* T38_IND_V8_ANSAM */ | |
| 87 { 215000, 0, 0}, /* T38_IND_V8_SIGNAL */ | |
| 88 { 215000, 0, 0}, /* T38_IND_V34_CNTL_CHANNEL_1200 */ | |
| 89 { 215000, 0, 0}, /* T38_IND_V34_PRI_CHANNEL */ | |
| 90 { 215000, 0, 0}, /* T38_IND_V34_CC_RETRAIN */ | |
| 91 { 215000, 0, 0}, /* T38_IND_V33_12000_TRAINING */ | |
| 92 { 215000, 0, 0} /* T38_IND_V33_14400_TRAINING */ | |
| 93 }; | |
| 94 | |
| 95 SPAN_DECLARE(const char *) t38_indicator_to_str(int indicator) | |
| 96 { | |
| 97 switch (indicator) | |
| 98 { | |
| 99 case T38_IND_NO_SIGNAL: | |
| 100 return "no-signal"; | |
| 101 case T38_IND_CNG: | |
| 102 return "cng"; | |
| 103 case T38_IND_CED: | |
| 104 return "ced"; | |
| 105 case T38_IND_V21_PREAMBLE: | |
| 106 return "v21-preamble"; | |
| 107 case T38_IND_V27TER_2400_TRAINING: | |
| 108 return "v27-2400-training"; | |
| 109 case T38_IND_V27TER_4800_TRAINING: | |
| 110 return "v27-4800-training"; | |
| 111 case T38_IND_V29_7200_TRAINING: | |
| 112 return "v29-7200-training"; | |
| 113 case T38_IND_V29_9600_TRAINING: | |
| 114 return "v29-9600-training"; | |
| 115 case T38_IND_V17_7200_SHORT_TRAINING: | |
| 116 return "v17-7200-short-training"; | |
| 117 case T38_IND_V17_7200_LONG_TRAINING: | |
| 118 return "v17-7200-long-training"; | |
| 119 case T38_IND_V17_9600_SHORT_TRAINING: | |
| 120 return "v17-9600-short-training"; | |
| 121 case T38_IND_V17_9600_LONG_TRAINING: | |
| 122 return "v17-9600-long-training"; | |
| 123 case T38_IND_V17_12000_SHORT_TRAINING: | |
| 124 return "v17-12000-short-training"; | |
| 125 case T38_IND_V17_12000_LONG_TRAINING: | |
| 126 return "v17-12000-long-training"; | |
| 127 case T38_IND_V17_14400_SHORT_TRAINING: | |
| 128 return "v17-14400-short-training"; | |
| 129 case T38_IND_V17_14400_LONG_TRAINING: | |
| 130 return "v17-14400-long-training"; | |
| 131 case T38_IND_V8_ANSAM: | |
| 132 return "v8-ansam"; | |
| 133 case T38_IND_V8_SIGNAL: | |
| 134 return "v8-signal"; | |
| 135 case T38_IND_V34_CNTL_CHANNEL_1200: | |
| 136 return "v34-cntl-channel-1200"; | |
| 137 case T38_IND_V34_PRI_CHANNEL: | |
| 138 return "v34-pri-channel"; | |
| 139 case T38_IND_V34_CC_RETRAIN: | |
| 140 return "v34-CC-retrain"; | |
| 141 case T38_IND_V33_12000_TRAINING: | |
| 142 return "v33-12000-training"; | |
| 143 case T38_IND_V33_14400_TRAINING: | |
| 144 return "v33-14400-training"; | |
| 145 } | |
| 146 return "???"; | |
| 147 } | |
| 148 /*- End of function --------------------------------------------------------*/ | |
| 149 | |
| 150 SPAN_DECLARE(const char *) t38_data_type_to_str(int data_type) | |
| 151 { | |
| 152 switch (data_type) | |
| 153 { | |
| 154 case T38_DATA_V21: | |
| 155 return "v21"; | |
| 156 case T38_DATA_V27TER_2400: | |
| 157 return "v27-2400"; | |
| 158 case T38_DATA_V27TER_4800: | |
| 159 return "v27-4800"; | |
| 160 case T38_DATA_V29_7200: | |
| 161 return "v29-7200"; | |
| 162 case T38_DATA_V29_9600: | |
| 163 return "v29-9600"; | |
| 164 case T38_DATA_V17_7200: | |
| 165 return "v17-7200"; | |
| 166 case T38_DATA_V17_9600: | |
| 167 return "v17-9600"; | |
| 168 case T38_DATA_V17_12000: | |
| 169 return "v17-12000"; | |
| 170 case T38_DATA_V17_14400: | |
| 171 return "v17-14400"; | |
| 172 case T38_DATA_V8: | |
| 173 return "v8"; | |
| 174 case T38_DATA_V34_PRI_RATE: | |
| 175 return "v34-pri-rate"; | |
| 176 case T38_DATA_V34_CC_1200: | |
| 177 return "v34-CC-1200"; | |
| 178 case T38_DATA_V34_PRI_CH: | |
| 179 return "v34-pri-vh"; | |
| 180 case T38_DATA_V33_12000: | |
| 181 return "v33-12000"; | |
| 182 case T38_DATA_V33_14400: | |
| 183 return "v33-14400"; | |
| 184 } | |
| 185 return "???"; | |
| 186 } | |
| 187 /*- End of function --------------------------------------------------------*/ | |
| 188 | |
| 189 SPAN_DECLARE(const char *) t38_field_type_to_str(int field_type) | |
| 190 { | |
| 191 switch (field_type) | |
| 192 { | |
| 193 case T38_FIELD_HDLC_DATA: | |
| 194 return "hdlc-data"; | |
| 195 case T38_FIELD_HDLC_SIG_END: | |
| 196 return "hdlc-sig-end"; | |
| 197 case T38_FIELD_HDLC_FCS_OK: | |
| 198 return "hdlc-fcs-OK"; | |
| 199 case T38_FIELD_HDLC_FCS_BAD: | |
| 200 return "hdlc-fcs-BAD"; | |
| 201 case T38_FIELD_HDLC_FCS_OK_SIG_END: | |
| 202 return "hdlc-fcs-OK-sig-end"; | |
| 203 case T38_FIELD_HDLC_FCS_BAD_SIG_END: | |
| 204 return "hdlc-fcs-BAD-sig-end"; | |
| 205 case T38_FIELD_T4_NON_ECM_DATA: | |
| 206 return "t4-non-ecm-data"; | |
| 207 case T38_FIELD_T4_NON_ECM_SIG_END: | |
| 208 return "t4-non-ecm-sig-end"; | |
| 209 case T38_FIELD_CM_MESSAGE: | |
| 210 return "cm-message"; | |
| 211 case T38_FIELD_JM_MESSAGE: | |
| 212 return "jm-message"; | |
| 213 case T38_FIELD_CI_MESSAGE: | |
| 214 return "ci-message"; | |
| 215 case T38_FIELD_V34RATE: | |
| 216 return "v34rate"; | |
| 217 } | |
| 218 return "???"; | |
| 219 } | |
| 220 /*- End of function --------------------------------------------------------*/ | |
| 221 | |
| 222 SPAN_DECLARE(const char *) t38_cm_profile_to_str(int profile) | |
| 223 { | |
| 224 switch (profile) | |
| 225 { | |
| 226 case '1': | |
| 227 return "G3 FAX sending terminal"; | |
| 228 case '2': | |
| 229 return "G3 FAX receiving terminal"; | |
| 230 case '3': | |
| 231 return "V.34 HDX and G3 FAX sending terminal"; | |
| 232 case '4': | |
| 233 return "V.34 HDX and G3 FAX receiving terminal"; | |
| 234 case '5': | |
| 235 return "V.34 HDX-only FAX sending terminal"; | |
| 236 case '6': | |
| 237 return "V.34 HDX-only FAX receiving terminal"; | |
| 238 } | |
| 239 return "???"; | |
| 240 } | |
| 241 /*- End of function --------------------------------------------------------*/ | |
| 242 | |
| 243 SPAN_DECLARE(const char *) t38_jm_to_str(const uint8_t *data, int len) | |
| 244 { | |
| 245 if (len < 2) | |
| 246 return "???"; | |
| 247 switch (data[0]) | |
| 248 { | |
| 249 case 'A': | |
| 250 switch (data[1]) | |
| 251 { | |
| 252 case '0': | |
| 253 return "ACK"; | |
| 254 } | |
| 255 break; | |
| 256 case 'N': | |
| 257 switch (data[1]) | |
| 258 { | |
| 259 case '0': | |
| 260 return "NACK: No compatible mode available"; | |
| 261 case '1': | |
| 262 /* Response for profiles 1 and 2 */ | |
| 263 return "NACK: No V.34 FAX, use G3 FAX"; | |
| 264 case '2': | |
| 265 /* Response for profiles 5 and 6 */ | |
| 266 return "NACK: V.34 only FAX."; | |
| 267 } | |
| 268 break; | |
| 269 } | |
| 270 return "???"; | |
| 271 } | |
| 272 /*- End of function --------------------------------------------------------*/ | |
| 273 | |
| 274 SPAN_DECLARE(int) t38_v34rate_to_bps(const uint8_t *data, int len) | |
| 275 { | |
| 276 int i; | |
| 277 int rate; | |
| 278 | |
| 279 if (len < 3) | |
| 280 return -1; | |
| 281 for (i = 0, rate = 0; i < 3; i++) | |
| 282 { | |
| 283 if (data[i] < '0' || data[i] > '9') | |
| 284 return -1; | |
| 285 rate = rate*10 + data[i] - '0'; | |
| 286 } | |
| 287 return rate*100; | |
| 288 } | |
| 289 /*- End of function --------------------------------------------------------*/ | |
| 290 | |
| 291 static __inline__ int classify_seq_no_offset(int expected, int actual) | |
| 292 { | |
| 293 /* Classify the mismatch between expected and actual sequence numbers | |
| 294 according to whether the actual is a little in the past (late), a | |
| 295 little in the future (some packets have been lost), or a large jump | |
| 296 that represents the sequence being lost (possibly when some RTP | |
| 297 gets dumped to a UDPTL port). */ | |
| 298 /* This assumes they are not equal */ | |
| 299 if (expected > actual) | |
| 300 { | |
| 301 if (expected > actual + 0x10000 - ACCEPTABLE_SEQ_NO_OFFSET) | |
| 302 { | |
| 303 /* In the near future */ | |
| 304 return 1; | |
| 305 } | |
| 306 if (expected < actual + ACCEPTABLE_SEQ_NO_OFFSET) | |
| 307 { | |
| 308 /* In the recent past */ | |
| 309 return -1; | |
| 310 } | |
| 311 } | |
| 312 else | |
| 313 { | |
| 314 if (expected + ACCEPTABLE_SEQ_NO_OFFSET > actual) | |
| 315 { | |
| 316 /* In the near future */ | |
| 317 return 1; | |
| 318 } | |
| 319 if (expected + 0x10000 - ACCEPTABLE_SEQ_NO_OFFSET < actual) | |
| 320 { | |
| 321 /* In the recent past */ | |
| 322 return -1; | |
| 323 } | |
| 324 } | |
| 325 /* There has been a huge step in the sequence */ | |
| 326 return 0; | |
| 327 } | |
| 328 /*- End of function --------------------------------------------------------*/ | |
| 329 | |
| 330 SPAN_DECLARE(int) t38_core_rx_ifp_packet(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t seq_no) | |
| 331 { | |
| 332 int i; | |
| 333 int t30_indicator; | |
| 334 int t30_data; | |
| 335 int ptr; | |
| 336 int other_half; | |
| 337 int numocts; | |
| 338 int log_seq_no; | |
| 339 const uint8_t *msg; | |
| 340 unsigned int count; | |
| 341 unsigned int t30_field_type; | |
| 342 uint8_t type; | |
| 343 uint8_t data_field_present; | |
| 344 uint8_t field_data_present; | |
| 345 char tag[20]; | |
| 346 | |
| 347 log_seq_no = (s->check_sequence_numbers) ? seq_no : s->rx_expected_seq_no; | |
| 348 | |
| 349 if (span_log_test(&s->logging, SPAN_LOG_FLOW)) | |
| 350 { | |
| 351 sprintf(tag, "Rx %5d: IFP", log_seq_no); | |
| 352 span_log_buf(&s->logging, SPAN_LOG_FLOW, tag, buf, len); | |
| 353 } | |
| 354 if (len < 1) | |
| 355 { | |
| 356 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Bad packet length - %d\n", log_seq_no, len); | |
| 357 return -1; | |
| 358 } | |
| 359 if (s->check_sequence_numbers) | |
| 360 { | |
| 361 seq_no &= 0xFFFF; | |
| 362 if (seq_no != s->rx_expected_seq_no) | |
| 363 { | |
| 364 /* An expected value of -1 indicates this is the first received packet, and will accept | |
| 365 anything for that. We can't assume they will start from zero, even though they should. */ | |
| 366 if (s->rx_expected_seq_no != -1) | |
| 367 { | |
| 368 /* We have a packet with a serial number that is not in sequence. The cause could be: | |
| 369 - 1. a repeat copy of a recent packet. Many T.38 implementations can preduce quite a lot of these. | |
| 370 - 2. a late packet, whose point in the sequence we have already passed. | |
| 371 - 3. the result of a hop in the sequence numbers cause by something weird from the other | |
| 372 end. Stream switching might cause this | |
| 373 - 4. missing packets. | |
| 374 | |
| 375 In cases 1 and 2 we need to drop this packet. In case 2 it might make sense to try to do | |
| 376 something with it in the terminal case. Currently we don't. For gateway operation it will be | |
| 377 too late to do anything useful. | |
| 378 */ | |
| 379 if (((seq_no + 1) & 0xFFFF) == s->rx_expected_seq_no) | |
| 380 { | |
| 381 /* Assume this is truly a repeat packet, and don't bother checking its contents. */ | |
| 382 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: Repeat packet number\n", log_seq_no); | |
| 383 return 0; | |
| 384 } | |
| 385 /* Distinguish between a little bit out of sequence, and a huge hop. */ | |
| 386 switch (classify_seq_no_offset(s->rx_expected_seq_no, seq_no)) | |
| 387 { | |
| 388 case -1: | |
| 389 /* This packet is in the near past, so its late. */ | |
| 390 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: Late packet - expected %d\n", log_seq_no, s->rx_expected_seq_no); | |
| 391 return 0; | |
| 392 case 1: | |
| 393 /* This packet is in the near future, so some packets have been lost */ | |
| 394 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: Missing from %d\n", log_seq_no, s->rx_expected_seq_no); | |
| 395 s->rx_missing_handler(s, s->rx_user_data, s->rx_expected_seq_no, seq_no); | |
| 396 s->missing_packets += (seq_no - s->rx_expected_seq_no); | |
| 397 break; | |
| 398 default: | |
| 399 /* The sequence has jumped wildly */ | |
| 400 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: Sequence restart\n", log_seq_no); | |
| 401 s->rx_missing_handler(s, s->rx_user_data, -1, -1); | |
| 402 s->missing_packets++; | |
| 403 break; | |
| 404 } | |
| 405 } | |
| 406 s->rx_expected_seq_no = seq_no; | |
| 407 } | |
| 408 } | |
| 409 /* The sequence numbering is defined as rolling from 0xFFFF to 0x0000. Some implementations | |
| 410 of T.38 roll from 0xFFFF to 0x0001. Isn't standardisation a wonderful thing? The T.38 | |
| 411 document specifies only a small fraction of what it should, yet then they actually nail | |
| 412 something properly, people ignore it. Developers in this industry truly deserves the **** | |
| 413 **** **** **** **** **** documents they have to live with. Anyway, when the far end has a | |
| 414 broken rollover behaviour we will get a hiccup at the rollover point. Don't worry too | |
| 415 much. We will just treat the message in progress as one with some missing data. With any | |
| 416 luck a retry will ride over the problem. Rollovers don't occur that often. It takes quite | |
| 417 a few FAX pages to reach rollover. */ | |
| 418 s->rx_expected_seq_no = (s->rx_expected_seq_no + 1) & 0xFFFF; | |
| 419 data_field_present = (buf[0] >> 7) & 1; | |
| 420 type = (buf[0] >> 6) & 1; | |
| 421 ptr = 0; | |
| 422 switch (type) | |
| 423 { | |
| 424 case T38_TYPE_OF_MSG_T30_INDICATOR: | |
| 425 /* Indicators should never have a data field */ | |
| 426 if (data_field_present) | |
| 427 { | |
| 428 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Data field with indicator\n", log_seq_no); | |
| 429 return -1; | |
| 430 } | |
| 431 /* Any received indicator should mean we no longer have a valid concept of "last received data/field type". */ | |
| 432 s->current_rx_data_type = -1; | |
| 433 s->current_rx_field_type = -1; | |
| 434 if ((buf[0] & 0x20)) | |
| 435 { | |
| 436 /* Extension */ | |
| 437 if (len != 2) | |
| 438 { | |
| 439 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for indicator (A)\n", log_seq_no); | |
| 440 return -1; | |
| 441 } | |
| 442 t30_indicator = T38_IND_V8_ANSAM + (((buf[0] << 2) & 0x3C) | ((buf[1] >> 6) & 0x3)); | |
| 443 if (t30_indicator > T38_IND_V33_14400_TRAINING) | |
| 444 { | |
| 445 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown indicator - %d\n", log_seq_no, t30_indicator); | |
| 446 return -1; | |
| 447 } | |
| 448 } | |
| 449 else | |
| 450 { | |
| 451 if (len != 1) | |
| 452 { | |
| 453 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for indicator (B)\n", log_seq_no); | |
| 454 return -1; | |
| 455 } | |
| 456 t30_indicator = (buf[0] >> 1) & 0xF; | |
| 457 } | |
| 458 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: indicator %s\n", log_seq_no, t38_indicator_to_str(t30_indicator)); | |
| 459 s->rx_indicator_handler(s, s->rx_user_data, t30_indicator); | |
| 460 /* This must come after the indicator handler, so the handler routine sees the existing state of the | |
| 461 indicator. */ | |
| 462 s->current_rx_indicator = t30_indicator; | |
| 463 break; | |
| 464 case T38_TYPE_OF_MSG_T30_DATA: | |
| 465 if ((buf[0] & 0x20)) | |
| 466 { | |
| 467 /* Extension */ | |
| 468 if (len < 2) | |
| 469 { | |
| 470 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for data (A)\n", log_seq_no); | |
| 471 return -1; | |
| 472 } | |
| 473 t30_data = T38_DATA_V8 + (((buf[0] << 2) & 0x3C) | ((buf[1] >> 6) & 0x3)); | |
| 474 if (t30_data > T38_DATA_V33_14400) | |
| 475 { | |
| 476 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown data type - %d\n", log_seq_no, t30_data); | |
| 477 return -1; | |
| 478 } | |
| 479 ptr = 2; | |
| 480 } | |
| 481 else | |
| 482 { | |
| 483 t30_data = (buf[0] >> 1) & 0xF; | |
| 484 if (t30_data > T38_DATA_V17_14400) | |
| 485 { | |
| 486 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown data type - %d\n", log_seq_no, t30_data); | |
| 487 return -1; | |
| 488 } | |
| 489 ptr = 1; | |
| 490 } | |
| 491 if (!data_field_present) | |
| 492 { | |
| 493 /* This is kinda weird, but I guess if the length checks out we accept it. */ | |
| 494 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Data type with no data field\n", log_seq_no); | |
| 495 if (ptr != len) | |
| 496 { | |
| 497 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for data (B)\n", log_seq_no); | |
| 498 return -1; | |
| 499 } | |
| 500 break; | |
| 501 } | |
| 502 if (ptr >= len) | |
| 503 { | |
| 504 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for data (C)\n", log_seq_no); | |
| 505 return -1; | |
| 506 } | |
| 507 count = buf[ptr++]; | |
| 508 //printf("Count is %d\n", count); | |
| 509 other_half = FALSE; | |
| 510 t30_field_type = 0; | |
| 511 for (i = 0; i < (int) count; i++) | |
| 512 { | |
| 513 if (ptr >= len) | |
| 514 { | |
| 515 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for data (D)\n", log_seq_no); | |
| 516 return -1; | |
| 517 } | |
| 518 if (s->t38_version == 0) | |
| 519 { | |
| 520 /* The original version of T.38 with a typo in the ASN.1 spec. */ | |
| 521 if (other_half) | |
| 522 { | |
| 523 /* The lack of a data field in the previous message means | |
| 524 we are currently in the middle of an octet. */ | |
| 525 field_data_present = (buf[ptr] >> 3) & 1; | |
| 526 /* Decode field_type */ | |
| 527 t30_field_type = buf[ptr] & 0x7; | |
| 528 ptr++; | |
| 529 other_half = FALSE; | |
| 530 } | |
| 531 else | |
| 532 { | |
| 533 field_data_present = (buf[ptr] >> 7) & 1; | |
| 534 /* Decode field_type */ | |
| 535 t30_field_type = (buf[ptr] >> 4) & 0x7; | |
| 536 if (field_data_present) | |
| 537 ptr++; | |
| 538 else | |
| 539 other_half = TRUE; | |
| 540 } | |
| 541 if (t30_field_type > T38_FIELD_T4_NON_ECM_SIG_END) | |
| 542 { | |
| 543 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown field type - %d\n", log_seq_no, t30_field_type); | |
| 544 return -1; | |
| 545 } | |
| 546 } | |
| 547 else | |
| 548 { | |
| 549 field_data_present = (buf[ptr] >> 7) & 1; | |
| 550 /* Decode field_type */ | |
| 551 if ((buf[ptr] & 0x40)) | |
| 552 { | |
| 553 if (ptr > len - 2) | |
| 554 { | |
| 555 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for data (E)\n", log_seq_no); | |
| 556 return -1; | |
| 557 } | |
| 558 t30_field_type = T38_FIELD_CM_MESSAGE + (((buf[ptr] << 2) & 0x3C) | ((buf[ptr + 1] >> 6) & 0x3)); | |
| 559 if (t30_field_type > T38_FIELD_V34RATE) | |
| 560 { | |
| 561 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown field type - %d\n", log_seq_no, t30_field_type); | |
| 562 return -1; | |
| 563 } | |
| 564 ptr += 2; | |
| 565 } | |
| 566 else | |
| 567 { | |
| 568 t30_field_type = (buf[ptr++] >> 3) & 0x7; | |
| 569 } | |
| 570 } | |
| 571 /* Decode field_data */ | |
| 572 if (field_data_present) | |
| 573 { | |
| 574 if (ptr > len - 2) | |
| 575 { | |
| 576 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for data (F)\n", log_seq_no); | |
| 577 return -1; | |
| 578 } | |
| 579 numocts = ((buf[ptr] << 8) | buf[ptr + 1]) + 1; | |
| 580 msg = buf + ptr + 2; | |
| 581 ptr += numocts + 2; | |
| 582 } | |
| 583 else | |
| 584 { | |
| 585 numocts = 0; | |
| 586 msg = NULL; | |
| 587 } | |
| 588 if (ptr > len) | |
| 589 { | |
| 590 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for data (G)\n", log_seq_no); | |
| 591 return -1; | |
| 592 } | |
| 593 span_log(&s->logging, | |
| 594 SPAN_LOG_FLOW, | |
| 595 "Rx %5d: (%d) data %s/%s + %d byte(s)\n", | |
| 596 log_seq_no, | |
| 597 i, | |
| 598 t38_data_type_to_str(t30_data), | |
| 599 t38_field_type_to_str(t30_field_type), | |
| 600 numocts); | |
| 601 s->rx_data_handler(s, s->rx_user_data, t30_data, t30_field_type, msg, numocts); | |
| 602 s->current_rx_data_type = t30_data; | |
| 603 s->current_rx_field_type = t30_field_type; | |
| 604 } | |
| 605 if (ptr != len) | |
| 606 { | |
| 607 if (s->t38_version != 0 || ptr != (len - 1) || !other_half) | |
| 608 { | |
| 609 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for data (H) - %d %d\n", log_seq_no, ptr, len); | |
| 610 return -1; | |
| 611 } | |
| 612 } | |
| 613 break; | |
| 614 } | |
| 615 return 0; | |
| 616 } | |
| 617 /*- End of function --------------------------------------------------------*/ | |
| 618 | |
| 619 static int t38_encode_indicator(t38_core_state_t *s, uint8_t buf[], int indicator) | |
| 620 { | |
| 621 int len; | |
| 622 | |
| 623 /* Build the IFP packet */ | |
| 624 /* Data field not present */ | |
| 625 /* Indicator packet */ | |
| 626 /* Type of indicator */ | |
| 627 if (indicator <= T38_IND_V17_14400_LONG_TRAINING) | |
| 628 { | |
| 629 buf[0] = (uint8_t) (indicator << 1); | |
| 630 len = 1; | |
| 631 } | |
| 632 else if (s->t38_version != 0 && indicator <= T38_IND_V33_14400_TRAINING) | |
| 633 { | |
| 634 buf[0] = (uint8_t) (0x20 | (((indicator - T38_IND_V8_ANSAM) & 0xF) >> 2)); | |
| 635 buf[1] = (uint8_t) (((indicator - T38_IND_V8_ANSAM) << 6) & 0xFF); | |
| 636 len = 2; | |
| 637 } | |
| 638 else | |
| 639 { | |
| 640 len = -1; | |
| 641 } | |
| 642 return len; | |
| 643 } | |
| 644 /*- End of function --------------------------------------------------------*/ | |
| 645 | |
| 646 static int t38_encode_data(t38_core_state_t *s, uint8_t buf[], int data_type, const t38_data_field_t field[], int fields) | |
| 647 { | |
| 648 int len; | |
| 649 int i; | |
| 650 int enclen; | |
| 651 int multiplier; | |
| 652 int data_field_no; | |
| 653 const t38_data_field_t *q; | |
| 654 unsigned int encoded_len; | |
| 655 unsigned int fragment_len; | |
| 656 unsigned int value; | |
| 657 uint8_t data_field_present; | |
| 658 uint8_t field_data_present; | |
| 659 char tag[20]; | |
| 660 | |
| 661 /* Build the IFP packet */ | |
| 662 | |
| 663 /* There seems no valid reason why a packet would ever be generated without a data field present */ | |
| 664 data_field_present = TRUE; | |
| 665 | |
| 666 for (data_field_no = 0; data_field_no < fields; data_field_no++) | |
| 667 { | |
| 668 span_log(&s->logging, | |
| 669 SPAN_LOG_FLOW, | |
| 670 "Tx %5d: (%d) data %s/%s + %d byte(s)\n", | |
| 671 s->tx_seq_no, | |
| 672 data_field_no, | |
| 673 t38_data_type_to_str(data_type), | |
| 674 t38_field_type_to_str(field[data_field_no].field_type), | |
| 675 field[data_field_no].field_len); | |
| 676 } | |
| 677 | |
| 678 data_field_no = 0; | |
| 679 len = 0; | |
| 680 /* Data field present */ | |
| 681 /* Data packet */ | |
| 682 /* Type of data */ | |
| 683 if (data_type <= T38_DATA_V17_14400) | |
| 684 { | |
| 685 buf[len++] = (uint8_t) ((data_field_present << 7) | 0x40 | (data_type << 1)); | |
| 686 } | |
| 687 else if (s->t38_version != 0 && data_type <= T38_DATA_V33_14400) | |
| 688 { | |
| 689 buf[len++] = (uint8_t) ((data_field_present << 7) | 0x60 | (((data_type - T38_DATA_V8) & 0xF) >> 2)); | |
| 690 buf[len++] = (uint8_t) (((data_type - T38_DATA_V8) << 6) & 0xFF); | |
| 691 } | |
| 692 else | |
| 693 { | |
| 694 return -1; | |
| 695 } | |
| 696 if (data_field_present) | |
| 697 { | |
| 698 encoded_len = 0; | |
| 699 data_field_no = 0; | |
| 700 do | |
| 701 { | |
| 702 value = fields - encoded_len; | |
| 703 if (value < 0x80) | |
| 704 { | |
| 705 /* 1 octet case */ | |
| 706 buf[len++] = (uint8_t) value; | |
| 707 enclen = value; | |
| 708 } | |
| 709 else if (value < 0x4000) | |
| 710 { | |
| 711 /* 2 octet case */ | |
| 712 buf[len++] = (uint8_t) (0x80 | ((value >> 8) & 0xFF)); | |
| 713 buf[len++] = (uint8_t) (value & 0xFF); | |
| 714 enclen = value; | |
| 715 } | |
| 716 else | |
| 717 { | |
| 718 /* Fragmentation case */ | |
| 719 multiplier = (value/0x4000 < 4) ? value/0x4000 : 4; | |
| 720 buf[len++] = (uint8_t) (0xC0 | multiplier); | |
| 721 enclen = 0x4000*multiplier; | |
| 722 } | |
| 723 | |
| 724 fragment_len = enclen; | |
| 725 encoded_len += fragment_len; | |
| 726 /* Encode the elements */ | |
| 727 for (i = 0; i < (int) encoded_len; i++) | |
| 728 { | |
| 729 q = &field[data_field_no]; | |
| 730 field_data_present = (uint8_t) (q->field_len > 0); | |
| 731 /* Encode field_type */ | |
| 732 if (s->t38_version == 0) | |
| 733 { | |
| 734 /* Original version of T.38 with a typo */ | |
| 735 if (q->field_type > T38_FIELD_T4_NON_ECM_SIG_END) | |
| 736 return -1; | |
| 737 buf[len++] = (uint8_t) ((field_data_present << 7) | (q->field_type << 4)); | |
| 738 } | |
| 739 else | |
| 740 { | |
| 741 if (q->field_type <= T38_FIELD_T4_NON_ECM_SIG_END) | |
| 742 { | |
| 743 buf[len++] = (uint8_t) ((field_data_present << 7) | (q->field_type << 3)); | |
| 744 } | |
| 745 else if (q->field_type <= T38_FIELD_V34RATE) | |
| 746 { | |
| 747 buf[len++] = (uint8_t) ((field_data_present << 7) | 0x40 | ((q->field_type - T38_FIELD_CM_MESSAGE) >> 2)); | |
| 748 buf[len++] = (uint8_t) (((q->field_type - T38_FIELD_CM_MESSAGE) << 6) & 0xC0); | |
| 749 } | |
| 750 else | |
| 751 { | |
| 752 return -1; | |
| 753 } | |
| 754 } | |
| 755 /* Encode field_data */ | |
| 756 if (field_data_present) | |
| 757 { | |
| 758 if (q->field_len < 1 || q->field_len > 65535) | |
| 759 return -1; | |
| 760 buf[len++] = (uint8_t) (((q->field_len - 1) >> 8) & 0xFF); | |
| 761 buf[len++] = (uint8_t) ((q->field_len - 1) & 0xFF); | |
| 762 memcpy(buf + len, q->field, q->field_len); | |
| 763 len += q->field_len; | |
| 764 } | |
| 765 data_field_no++; | |
| 766 } | |
| 767 } | |
| 768 while (fields != (int) encoded_len || fragment_len >= 16384); | |
| 769 } | |
| 770 | |
| 771 if (span_log_test(&s->logging, SPAN_LOG_FLOW)) | |
| 772 { | |
| 773 sprintf(tag, "Tx %5d: IFP", s->tx_seq_no); | |
| 774 span_log_buf(&s->logging, SPAN_LOG_FLOW, tag, buf, len); | |
| 775 } | |
| 776 return len; | |
| 777 } | |
| 778 /*- End of function --------------------------------------------------------*/ | |
| 779 | |
| 780 SPAN_DECLARE(int) t38_core_send_indicator(t38_core_state_t *s, int indicator) | |
| 781 { | |
| 782 uint8_t buf[100]; | |
| 783 int len; | |
| 784 int delay; | |
| 785 | |
| 786 delay = 0; | |
| 787 /* Only send an indicator if it represents a change of state. */ | |
| 788 if (s->current_tx_indicator != indicator) | |
| 789 { | |
| 790 /* Zero is a valid count, to suppress the transmission of indicators when the | |
| 791 transport means they are not needed - e.g. TPKT/TCP. */ | |
| 792 if (s->category_control[T38_PACKET_CATEGORY_INDICATOR]) | |
| 793 { | |
| 794 if ((len = t38_encode_indicator(s, buf, indicator)) < 0) | |
| 795 { | |
| 796 span_log(&s->logging, SPAN_LOG_FLOW, "T.38 indicator len is %d\n", len); | |
| 797 return len; | |
| 798 } | |
| 799 span_log(&s->logging, SPAN_LOG_FLOW, "Tx %5d: indicator %s\n", s->tx_seq_no, t38_indicator_to_str(indicator)); | |
| 800 s->tx_packet_handler(s, s->tx_packet_user_data, buf, len, s->category_control[T38_PACKET_CATEGORY_INDICATOR]); | |
| 801 s->tx_seq_no = (s->tx_seq_no + 1) & 0xFFFF; | |
| 802 delay = modem_startup_time[indicator].training; | |
| 803 if (s->allow_for_tep) | |
| 804 delay += modem_startup_time[indicator].tep; | |
| 805 } | |
| 806 s->current_tx_indicator = indicator; | |
| 807 } | |
| 808 return delay; | |
| 809 } | |
| 810 /*- End of function --------------------------------------------------------*/ | |
| 811 | |
| 812 SPAN_DECLARE(int) t38_core_send_flags_delay(t38_core_state_t *s, int indicator) | |
| 813 { | |
| 814 return modem_startup_time[indicator].flags; | |
| 815 } | |
| 816 /*- End of function --------------------------------------------------------*/ | |
| 817 | |
| 818 SPAN_DECLARE(int) t38_core_send_data(t38_core_state_t *s, int data_type, int field_type, const uint8_t field[], int field_len, int category) | |
| 819 { | |
| 820 t38_data_field_t field0; | |
| 821 uint8_t buf[1000]; | |
| 822 int len; | |
| 823 | |
| 824 field0.field_type = field_type; | |
| 825 field0.field = field; | |
| 826 field0.field_len = field_len; | |
| 827 if ((len = t38_encode_data(s, buf, data_type, &field0, 1)) < 0) | |
| 828 { | |
| 829 span_log(&s->logging, SPAN_LOG_FLOW, "T.38 data len is %d\n", len); | |
| 830 return len; | |
| 831 } | |
| 832 s->tx_packet_handler(s, s->tx_packet_user_data, buf, len, s->category_control[category]); | |
| 833 s->tx_seq_no = (s->tx_seq_no + 1) & 0xFFFF; | |
| 834 return 0; | |
| 835 } | |
| 836 /*- End of function --------------------------------------------------------*/ | |
| 837 | |
| 838 SPAN_DECLARE(int) t38_core_send_data_multi_field(t38_core_state_t *s, int data_type, const t38_data_field_t field[], int fields, int category) | |
| 839 { | |
| 840 uint8_t buf[1000]; | |
| 841 int len; | |
| 842 | |
| 843 if ((len = t38_encode_data(s, buf, data_type, field, fields)) < 0) | |
| 844 { | |
| 845 span_log(&s->logging, SPAN_LOG_FLOW, "T.38 data len is %d\n", len); | |
| 846 return len; | |
| 847 } | |
| 848 s->tx_packet_handler(s, s->tx_packet_user_data, buf, len, s->category_control[category]); | |
| 849 s->tx_seq_no = (s->tx_seq_no + 1) & 0xFFFF; | |
| 850 return 0; | |
| 851 } | |
| 852 /*- End of function --------------------------------------------------------*/ | |
| 853 | |
| 854 SPAN_DECLARE(void) t38_set_data_rate_management_method(t38_core_state_t *s, int method) | |
| 855 { | |
| 856 s->data_rate_management_method = method; | |
| 857 } | |
| 858 /*- End of function --------------------------------------------------------*/ | |
| 859 | |
| 860 SPAN_DECLARE(void) t38_set_data_transport_protocol(t38_core_state_t *s, int data_transport_protocol) | |
| 861 { | |
| 862 s->data_transport_protocol = data_transport_protocol; | |
| 863 } | |
| 864 /*- End of function --------------------------------------------------------*/ | |
| 865 | |
| 866 SPAN_DECLARE(void) t38_set_fill_bit_removal(t38_core_state_t *s, int fill_bit_removal) | |
| 867 { | |
| 868 s->fill_bit_removal = fill_bit_removal; | |
| 869 } | |
| 870 /*- End of function --------------------------------------------------------*/ | |
| 871 | |
| 872 SPAN_DECLARE(void) t38_set_mmr_transcoding(t38_core_state_t *s, int mmr_transcoding) | |
| 873 { | |
| 874 s->mmr_transcoding = mmr_transcoding; | |
| 875 } | |
| 876 /*- End of function --------------------------------------------------------*/ | |
| 877 | |
| 878 SPAN_DECLARE(void) t38_set_jbig_transcoding(t38_core_state_t *s, int jbig_transcoding) | |
| 879 { | |
| 880 s->jbig_transcoding = jbig_transcoding; | |
| 881 } | |
| 882 /*- End of function --------------------------------------------------------*/ | |
| 883 | |
| 884 SPAN_DECLARE(void) t38_set_max_buffer_size(t38_core_state_t *s, int max_buffer_size) | |
| 885 { | |
| 886 s->max_buffer_size = max_buffer_size; | |
| 887 } | |
| 888 /*- End of function --------------------------------------------------------*/ | |
| 889 | |
| 890 SPAN_DECLARE(void) t38_set_max_datagram_size(t38_core_state_t *s, int max_datagram_size) | |
| 891 { | |
| 892 s->max_datagram_size = max_datagram_size; | |
| 893 } | |
| 894 /*- End of function --------------------------------------------------------*/ | |
| 895 | |
| 896 SPAN_DECLARE(void) t38_set_t38_version(t38_core_state_t *s, int t38_version) | |
| 897 { | |
| 898 s->t38_version = t38_version; | |
| 899 } | |
| 900 /*- End of function --------------------------------------------------------*/ | |
| 901 | |
| 902 SPAN_DECLARE(void) t38_set_sequence_number_handling(t38_core_state_t *s, int check) | |
| 903 { | |
| 904 s->check_sequence_numbers = check; | |
| 905 } | |
| 906 /*- End of function --------------------------------------------------------*/ | |
| 907 | |
| 908 SPAN_DECLARE(void) t38_set_tep_handling(t38_core_state_t *s, int allow_for_tep) | |
| 909 { | |
| 910 s->allow_for_tep = allow_for_tep; | |
| 911 } | |
| 912 /*- End of function --------------------------------------------------------*/ | |
| 913 | |
| 914 SPAN_DECLARE(void) t38_set_redundancy_control(t38_core_state_t *s, int category, int setting) | |
| 915 { | |
| 916 s->category_control[category] = setting; | |
| 917 } | |
| 918 /*- End of function --------------------------------------------------------*/ | |
| 919 | |
| 920 SPAN_DECLARE(void) t38_set_fastest_image_data_rate(t38_core_state_t *s, int max_rate) | |
| 921 { | |
| 922 s->fastest_image_data_rate = max_rate; | |
| 923 } | |
| 924 /*- End of function --------------------------------------------------------*/ | |
| 925 | |
| 926 SPAN_DECLARE(int) t38_get_fastest_image_data_rate(t38_core_state_t *s) | |
| 927 { | |
| 928 return s->fastest_image_data_rate; | |
| 929 } | |
| 930 /*- End of function --------------------------------------------------------*/ | |
| 931 | |
| 932 SPAN_DECLARE(logging_state_t *) t38_core_get_logging_state(t38_core_state_t *s) | |
| 933 { | |
| 934 return &s->logging; | |
| 935 } | |
| 936 /*- End of function --------------------------------------------------------*/ | |
| 937 | |
| 938 SPAN_DECLARE(t38_core_state_t *) t38_core_init(t38_core_state_t *s, | |
| 939 t38_rx_indicator_handler_t *rx_indicator_handler, | |
| 940 t38_rx_data_handler_t *rx_data_handler, | |
| 941 t38_rx_missing_handler_t *rx_missing_handler, | |
| 942 void *rx_user_data, | |
| 943 t38_tx_packet_handler_t *tx_packet_handler, | |
| 944 void *tx_packet_user_data) | |
| 945 { | |
| 946 if (s == NULL) | |
| 947 { | |
| 948 if ((s = (t38_core_state_t *) malloc(sizeof(*s))) == NULL) | |
| 949 return NULL; | |
| 950 } | |
| 951 memset(s, 0, sizeof(*s)); | |
| 952 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
| 953 span_log_set_protocol(&s->logging, "T.38"); | |
| 954 | |
| 955 /* Set some defaults for the parameters configurable from outside the | |
| 956 T.38 domain - e.g. from SDP data. */ | |
| 957 s->data_rate_management_method = T38_DATA_RATE_MANAGEMENT_TRANSFERRED_TCF; | |
| 958 s->data_transport_protocol = T38_TRANSPORT_UDPTL; | |
| 959 s->fill_bit_removal = FALSE; | |
| 960 s->mmr_transcoding = FALSE; | |
| 961 s->jbig_transcoding = FALSE; | |
| 962 s->max_buffer_size = 400; | |
| 963 s->max_datagram_size = 100; | |
| 964 s->t38_version = 0; | |
| 965 s->check_sequence_numbers = TRUE; | |
| 966 | |
| 967 /* Set some defaults */ | |
| 968 s->category_control[T38_PACKET_CATEGORY_INDICATOR] = 1; | |
| 969 s->category_control[T38_PACKET_CATEGORY_CONTROL_DATA] = 1; | |
| 970 s->category_control[T38_PACKET_CATEGORY_CONTROL_DATA_END] = 1; | |
| 971 s->category_control[T38_PACKET_CATEGORY_IMAGE_DATA] = 1; | |
| 972 s->category_control[T38_PACKET_CATEGORY_IMAGE_DATA_END] = 1; | |
| 973 | |
| 974 /* Set the initial current receive states to something invalid, so the | |
| 975 first data received is seen as a change of state. */ | |
| 976 s->current_rx_indicator = -1; | |
| 977 s->current_rx_data_type = -1; | |
| 978 s->current_rx_field_type = -1; | |
| 979 | |
| 980 /* Set the initial current indicator state to something invalid, so the | |
| 981 first attempt to send an indicator will work. */ | |
| 982 s->current_tx_indicator = -1; | |
| 983 | |
| 984 s->rx_indicator_handler = rx_indicator_handler; | |
| 985 s->rx_data_handler = rx_data_handler; | |
| 986 s->rx_missing_handler = rx_missing_handler; | |
| 987 s->rx_user_data = rx_user_data; | |
| 988 s->tx_packet_handler = tx_packet_handler; | |
| 989 s->tx_packet_user_data = tx_packet_user_data; | |
| 990 | |
| 991 /* We have no initial expectation of the received packet sequence number. | |
| 992 They most often start at 0 or 1 for a UDPTL transport, but random | |
| 993 starting numbers are possible. */ | |
| 994 s->rx_expected_seq_no = -1; | |
| 995 return s; | |
| 996 } | |
| 997 /*- End of function --------------------------------------------------------*/ | |
| 998 | |
| 999 SPAN_DECLARE(int) t38_core_release(t38_core_state_t *s) | |
| 1000 { | |
| 1001 return 0; | |
| 1002 } | |
| 1003 /*- End of function --------------------------------------------------------*/ | |
| 1004 | |
| 1005 SPAN_DECLARE(int) t38_core_free(t38_core_state_t *s) | |
| 1006 { | |
| 1007 if (s) | |
| 1008 free(s); | |
| 1009 return 0; | |
| 1010 } | |
| 1011 /*- End of function --------------------------------------------------------*/ | |
| 1012 /*- End of file ------------------------------------------------------------*/ |
