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

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.