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