5
|
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 ------------------------------------------------------------*/
|