Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v8.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 * v8.c - V.8 modem negotiation processing. | |
5 * | |
6 * Written by Steve Underwood <steveu@coppice.org> | |
7 * | |
8 * Copyright (C) 2004 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: v8.c,v 1.42.4.3 2009/12/28 12:20:47 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 <memory.h> | |
38 #if defined(HAVE_TGMATH_H) | |
39 #include <tgmath.h> | |
40 #endif | |
41 #if defined(HAVE_MATH_H) | |
42 #include <math.h> | |
43 #endif | |
44 #include "floating_fudge.h" | |
45 | |
46 #include "spandsp/telephony.h" | |
47 #include "spandsp/logging.h" | |
48 #include "spandsp/queue.h" | |
49 #include "spandsp/async.h" | |
50 #include "spandsp/vector_int.h" | |
51 #include "spandsp/complex.h" | |
52 #include "spandsp/dds.h" | |
53 #include "spandsp/tone_detect.h" | |
54 #include "spandsp/tone_generate.h" | |
55 #include "spandsp/super_tone_rx.h" | |
56 #include "spandsp/power_meter.h" | |
57 #include "spandsp/fsk.h" | |
58 #include "spandsp/modem_connect_tones.h" | |
59 #include "spandsp/v8.h" | |
60 | |
61 #include "spandsp/private/logging.h" | |
62 #include "spandsp/private/fsk.h" | |
63 #include "spandsp/private/modem_connect_tones.h" | |
64 #include "spandsp/private/v8.h" | |
65 | |
66 enum | |
67 { | |
68 V8_WAIT_1S, | |
69 V8_CI_ON, | |
70 V8_CI_OFF, | |
71 V8_HEARD_ANSAM, | |
72 V8_CM_ON, | |
73 V8_CJ_ON, | |
74 V8_CM_WAIT, | |
75 | |
76 V8_SIGC, | |
77 V8_JM_ON, | |
78 V8_SIGA, | |
79 | |
80 V8_PARKED | |
81 } v8_states_e; | |
82 | |
83 enum | |
84 { | |
85 V8_SYNC_UNKNOWN = 0, | |
86 V8_SYNC_CI, | |
87 V8_SYNC_CM_JM, | |
88 V8_SYNC_V92 | |
89 } v8_sync_types_e; | |
90 | |
91 enum | |
92 { | |
93 V8_CALL_FUNCTION_TAG = 0x01, | |
94 V8_MODULATION_TAG = 0x05, | |
95 V8_PROTOCOLS_TAG = 0x0A, | |
96 V8_PSTN_ACCESS_TAG = 0x0D, | |
97 V8_NSF_TAG = 0x0F, | |
98 V8_PCM_MODEM_AVAILABILITY_TAG = 0x07, | |
99 V8_T66_TAG = 0x0E | |
100 }; | |
101 | |
102 enum | |
103 { | |
104 V8_CI_SYNC_OCTET = 0x00, | |
105 V8_CM_JM_SYNC_OCTET = 0xE0 | |
106 }; | |
107 | |
108 SPAN_DECLARE(const char *) v8_call_function_to_str(int call_function) | |
109 { | |
110 switch (call_function) | |
111 { | |
112 case V8_CALL_TBS: | |
113 return "TBS"; | |
114 case V8_CALL_H324: | |
115 return "H.324 PSTN multimedia terminal"; | |
116 case V8_CALL_V18: | |
117 return "V.18 textphone"; | |
118 case V8_CALL_T101: | |
119 return "T.101 videotext"; | |
120 case V8_CALL_T30_TX: | |
121 return "T.30 Tx FAX"; | |
122 case V8_CALL_T30_RX: | |
123 return "T.30 Rx FAX"; | |
124 case V8_CALL_V_SERIES: | |
125 return "V series modem data"; | |
126 case V8_CALL_FUNCTION_EXTENSION: | |
127 return "Call function is in extention octet"; | |
128 } | |
129 return "???"; | |
130 } | |
131 /*- End of function --------------------------------------------------------*/ | |
132 | |
133 SPAN_DECLARE(const char *) v8_modulation_to_str(int modulation_scheme) | |
134 { | |
135 switch (modulation_scheme) | |
136 { | |
137 case V8_MOD_V17: | |
138 return "V.17 half-duplex"; | |
139 case V8_MOD_V21: | |
140 return "V.21 duplex"; | |
141 case V8_MOD_V22: | |
142 return "V.22/V.22bis duplex"; | |
143 case V8_MOD_V23HALF: | |
144 return "V.23 half-duplex"; | |
145 case V8_MOD_V23: | |
146 return "V.23 duplex"; | |
147 case V8_MOD_V26BIS: | |
148 return "V.26bis duplex"; | |
149 case V8_MOD_V26TER: | |
150 return "V.26ter duplex"; | |
151 case V8_MOD_V27TER: | |
152 return "V.27ter duplex"; | |
153 case V8_MOD_V29: | |
154 return "V.29 half-duplex"; | |
155 case V8_MOD_V32: | |
156 return "V.32/V.32bis duplex"; | |
157 case V8_MOD_V34HALF: | |
158 return "V.34 half-duplex"; | |
159 case V8_MOD_V34: | |
160 return "V.34 duplex"; | |
161 case V8_MOD_V90: | |
162 return "V.90 duplex"; | |
163 case V8_MOD_V92: | |
164 return "V.92 duplex"; | |
165 case V8_MOD_FAILED: | |
166 return "negotiation failed"; | |
167 } | |
168 return "???"; | |
169 } | |
170 /*- End of function --------------------------------------------------------*/ | |
171 | |
172 SPAN_DECLARE(const char *) v8_protocol_to_str(int protocol) | |
173 { | |
174 switch (protocol) | |
175 { | |
176 case V8_PROTOCOL_NONE: | |
177 return "None"; | |
178 case V8_PROTOCOL_LAPM_V42: | |
179 return "LAPM"; | |
180 case V8_PROTOCOL_EXTENSION: | |
181 return "Extension"; | |
182 } | |
183 return "Undefined"; | |
184 } | |
185 /*- End of function --------------------------------------------------------*/ | |
186 | |
187 SPAN_DECLARE(const char *) v8_pstn_access_to_str(int pstn_access) | |
188 { | |
189 switch (pstn_access) | |
190 { | |
191 case V8_PSTN_ACCESS_CALL_DCE_CELLULAR: | |
192 return "Calling modem on cellular"; | |
193 case V8_PSTN_ACCESS_ANSWER_DCE_CELLULAR: | |
194 return "Answering modem on cellular"; | |
195 case V8_PSTN_ACCESS_ANSWER_DCE_CELLULAR | V8_PSTN_ACCESS_CALL_DCE_CELLULAR: | |
196 return "Answering and calling modems on cellular"; | |
197 case V8_PSTN_ACCESS_DCE_ON_DIGITAL: | |
198 return "DCE on digital"; | |
199 case V8_PSTN_ACCESS_DCE_ON_DIGITAL | V8_PSTN_ACCESS_CALL_DCE_CELLULAR: | |
200 return "DCE on digital, and calling modem on cellular"; | |
201 case V8_PSTN_ACCESS_DCE_ON_DIGITAL | V8_PSTN_ACCESS_ANSWER_DCE_CELLULAR: | |
202 return "DCE on digital, answering modem on cellular"; | |
203 case V8_PSTN_ACCESS_DCE_ON_DIGITAL | V8_PSTN_ACCESS_ANSWER_DCE_CELLULAR | V8_PSTN_ACCESS_CALL_DCE_CELLULAR: | |
204 return "DCE on digital, and answering and calling modems on cellular"; | |
205 } | |
206 return "???"; | |
207 } | |
208 /*- End of function --------------------------------------------------------*/ | |
209 | |
210 SPAN_DECLARE(const char *) v8_nsf_to_str(int nsf) | |
211 { | |
212 switch (nsf) | |
213 { | |
214 case 0: | |
215 return "???"; | |
216 } | |
217 return "???"; | |
218 } | |
219 /*- End of function --------------------------------------------------------*/ | |
220 | |
221 SPAN_DECLARE(const char *) v8_pcm_modem_availability_to_str(int pcm_modem_availability) | |
222 { | |
223 switch (pcm_modem_availability) | |
224 { | |
225 case 0: | |
226 return "PCM unavailable"; | |
227 case V8_PSTN_PCM_MODEM_V90_V92_ANALOGUE: | |
228 return "V.90/V.92 analogue available"; | |
229 case V8_PSTN_PCM_MODEM_V90_V92_DIGITAL: | |
230 return "V.90/V.92 digital available"; | |
231 case V8_PSTN_PCM_MODEM_V90_V92_DIGITAL | V8_PSTN_PCM_MODEM_V90_V92_ANALOGUE: | |
232 return "V.90/V.92 digital/analogue available"; | |
233 case V8_PSTN_PCM_MODEM_V91: | |
234 return "V.91 available"; | |
235 case V8_PSTN_PCM_MODEM_V91 | V8_PSTN_PCM_MODEM_V90_V92_ANALOGUE: | |
236 return "V.91 and V.90/V.92 analogue available"; | |
237 case V8_PSTN_PCM_MODEM_V91 | V8_PSTN_PCM_MODEM_V90_V92_DIGITAL: | |
238 return "V.91 and V.90/V.92 digital available"; | |
239 case V8_PSTN_PCM_MODEM_V91 | V8_PSTN_PCM_MODEM_V90_V92_DIGITAL | V8_PSTN_PCM_MODEM_V90_V92_ANALOGUE: | |
240 return "V.91 and V.90/V.92 digital/analogue available"; | |
241 } | |
242 return "???"; | |
243 } | |
244 /*- End of function --------------------------------------------------------*/ | |
245 | |
246 SPAN_DECLARE(const char *) v8_t66_to_str(int t66) | |
247 { | |
248 /* T.66 doesn't really define any V.8 values. The bits are all reserved. */ | |
249 switch (t66) | |
250 { | |
251 case 0: | |
252 return "???"; | |
253 case 1: | |
254 return "Reserved TIA"; | |
255 case 2: | |
256 return "Reserved"; | |
257 case 3: | |
258 return "Reserved TIA + others"; | |
259 case 4: | |
260 return "Reserved"; | |
261 case 5: | |
262 return "Reserved TIA + others"; | |
263 case 6: | |
264 return "Reserved"; | |
265 case 7: | |
266 return "Reserved TIA + others"; | |
267 } | |
268 return "???"; | |
269 } | |
270 /*- End of function --------------------------------------------------------*/ | |
271 | |
272 SPAN_DECLARE(void) v8_log_supported_modulations(v8_state_t *s, int modulation_schemes) | |
273 { | |
274 const char *comma; | |
275 int i; | |
276 | |
277 comma = ""; | |
278 span_log(&s->logging, SPAN_LOG_FLOW, ""); | |
279 for (i = 0; i < 32; i++) | |
280 { | |
281 if ((modulation_schemes & (1 << i))) | |
282 { | |
283 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s%s", comma, v8_modulation_to_str(modulation_schemes & (1 << i))); | |
284 comma = ", "; | |
285 } | |
286 } | |
287 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, " supported\n"); | |
288 } | |
289 /*- End of function --------------------------------------------------------*/ | |
290 | |
291 static const uint8_t *process_call_function(v8_state_t *s, const uint8_t *p) | |
292 { | |
293 s->result.call_function = (*p >> 5) & 0x07; | |
294 span_log(&s->logging, SPAN_LOG_FLOW, "%s\n", v8_call_function_to_str(s->result.call_function)); | |
295 return ++p; | |
296 } | |
297 /*- End of function --------------------------------------------------------*/ | |
298 | |
299 static const uint8_t *process_modulation_mode(v8_state_t *s, const uint8_t *p) | |
300 { | |
301 unsigned int far_end_modulations; | |
302 | |
303 /* Modulation mode octet */ | |
304 far_end_modulations = 0; | |
305 if (*p & 0x80) | |
306 far_end_modulations |= V8_MOD_V34HALF; | |
307 if (*p & 0x40) | |
308 far_end_modulations |= V8_MOD_V34; | |
309 if (*p & 0x20) | |
310 far_end_modulations |= V8_MOD_V90; | |
311 | |
312 /* Check for an extension octet */ | |
313 if ((*++p & 0x38) == 0x10) | |
314 { | |
315 if (*p & 0x80) | |
316 far_end_modulations |= V8_MOD_V27TER; | |
317 if (*p & 0x40) | |
318 far_end_modulations |= V8_MOD_V29; | |
319 if (*p & 0x04) | |
320 far_end_modulations |= V8_MOD_V17; | |
321 if (*p & 0x02) | |
322 far_end_modulations |= V8_MOD_V22; | |
323 if (*p & 0x01) | |
324 far_end_modulations |= V8_MOD_V32; | |
325 | |
326 /* Check for an extension octet */ | |
327 if ((*++p & 0x38) == 0x10) | |
328 { | |
329 if (*p & 0x80) | |
330 far_end_modulations |= V8_MOD_V21; | |
331 if (*p & 0x40) | |
332 far_end_modulations |= V8_MOD_V23HALF; | |
333 if (*p & 0x04) | |
334 far_end_modulations |= V8_MOD_V23; | |
335 if (*p & 0x02) | |
336 far_end_modulations |= V8_MOD_V26BIS; | |
337 if (*p & 0x01) | |
338 far_end_modulations |= V8_MOD_V26TER; | |
339 } | |
340 } | |
341 s->far_end_modulations = | |
342 s->result.modulations = far_end_modulations; | |
343 v8_log_supported_modulations(s, far_end_modulations); | |
344 return ++p; | |
345 } | |
346 /*- End of function --------------------------------------------------------*/ | |
347 | |
348 static const uint8_t *process_protocols(v8_state_t *s, const uint8_t *p) | |
349 { | |
350 s->result.protocol = (*p >> 5) & 0x07; | |
351 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s\n", v8_protocol_to_str(s->result.protocol)); | |
352 return ++p; | |
353 } | |
354 /*- End of function --------------------------------------------------------*/ | |
355 | |
356 static const uint8_t *process_pstn_access(v8_state_t *s, const uint8_t *p) | |
357 { | |
358 s->result.pstn_access = (*p >> 5) & 0x07; | |
359 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s\n", v8_pstn_access_to_str(s->result.pstn_access)); | |
360 return ++p; | |
361 } | |
362 /*- End of function --------------------------------------------------------*/ | |
363 | |
364 static const uint8_t *process_non_standard_facilities(v8_state_t *s, const uint8_t *p) | |
365 { | |
366 s->result.nsf = (*p >> 5) & 0x07; | |
367 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s\n", v8_nsf_to_str(s->result.nsf)); | |
368 return p; | |
369 } | |
370 /*- End of function --------------------------------------------------------*/ | |
371 | |
372 static const uint8_t *process_pcm_modem_availability(v8_state_t *s, const uint8_t *p) | |
373 { | |
374 s->result.pcm_modem_availability = (*p >> 5) & 0x07; | |
375 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s\n", v8_pcm_modem_availability_to_str(s->result.pcm_modem_availability)); | |
376 return ++p; | |
377 } | |
378 /*- End of function --------------------------------------------------------*/ | |
379 | |
380 static const uint8_t *process_t66(v8_state_t *s, const uint8_t *p) | |
381 { | |
382 s->result.t66 = (*p >> 5) & 0x07; | |
383 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s\n", v8_t66_to_str(s->result.t66)); | |
384 return ++p; | |
385 } | |
386 /*- End of function --------------------------------------------------------*/ | |
387 | |
388 static void ci_decode(v8_state_t *s) | |
389 { | |
390 if ((s->rx_data[0] & 0x1F) == V8_CALL_FUNCTION_TAG) | |
391 process_call_function(s, &s->rx_data[0]); | |
392 } | |
393 /*- End of function --------------------------------------------------------*/ | |
394 | |
395 static void cm_jm_decode(v8_state_t *s) | |
396 { | |
397 const uint8_t *p; | |
398 | |
399 if (s->got_cm_jm) | |
400 return; | |
401 | |
402 /* We must receive two consecutive identical CM or JM sequences to accept it. */ | |
403 if (s->cm_jm_len <= 0 | |
404 || | |
405 s->cm_jm_len != s->rx_data_ptr | |
406 || | |
407 memcmp(s->cm_jm_data, s->rx_data, s->rx_data_ptr)) | |
408 { | |
409 /* Save the current CM or JM sequence */ | |
410 s->cm_jm_len = s->rx_data_ptr; | |
411 memcpy(s->cm_jm_data, s->rx_data, s->rx_data_ptr); | |
412 return; | |
413 } | |
414 /* We have a matching pair of CMs or JMs, so we are happy this is correct. */ | |
415 s->got_cm_jm = TRUE; | |
416 | |
417 span_log(&s->logging, SPAN_LOG_FLOW, "Decoding\n"); | |
418 | |
419 /* Zero indicates the end */ | |
420 s->cm_jm_data[s->cm_jm_len] = 0; | |
421 | |
422 s->result.modulations = 0; | |
423 p = s->cm_jm_data; | |
424 | |
425 while (*p) | |
426 { | |
427 switch (*p & 0x1F) | |
428 { | |
429 case V8_CALL_FUNCTION_TAG: | |
430 p = process_call_function(s, p); | |
431 break; | |
432 case V8_MODULATION_TAG: | |
433 p = process_modulation_mode(s, p); | |
434 break; | |
435 case V8_PROTOCOLS_TAG: | |
436 p = process_protocols(s, p); | |
437 break; | |
438 case V8_PSTN_ACCESS_TAG: | |
439 p = process_pstn_access(s, p); | |
440 break; | |
441 case V8_NSF_TAG: | |
442 p = process_non_standard_facilities(s, p); | |
443 break; | |
444 case V8_PCM_MODEM_AVAILABILITY_TAG: | |
445 p = process_pcm_modem_availability(s, p); | |
446 break; | |
447 case V8_T66_TAG: | |
448 p = process_t66(s, p); | |
449 break; | |
450 default: | |
451 p++; | |
452 break; | |
453 } | |
454 /* Skip any future extensions we do not understand */ | |
455 while ((*p & 0x38) == 0x10) | |
456 p++; | |
457 } | |
458 } | |
459 /*- End of function --------------------------------------------------------*/ | |
460 | |
461 static void put_bit(void *user_data, int bit) | |
462 { | |
463 v8_state_t *s; | |
464 int new_preamble_type; | |
465 const char *tag; | |
466 uint8_t data; | |
467 | |
468 s = user_data; | |
469 if (bit < 0) | |
470 { | |
471 /* Special conditions */ | |
472 switch (bit) | |
473 { | |
474 case SIG_STATUS_CARRIER_UP: | |
475 case SIG_STATUS_CARRIER_DOWN: | |
476 case SIG_STATUS_TRAINING_SUCCEEDED: | |
477 case SIG_STATUS_TRAINING_FAILED: | |
478 break; | |
479 default: | |
480 break; | |
481 } | |
482 return; | |
483 } | |
484 //span_log(&s->logging, SPAN_LOG_FLOW, "Bit %d\n", bit); | |
485 /* Wait until we sync. */ | |
486 s->bit_stream = (s->bit_stream >> 1) | (bit << 19); | |
487 /* CI preamble is 10 ones then a framed 0x00 | |
488 CM/JM preamble is 10 ones then a framed 0x07 | |
489 V.92 preamble is 10 ones then a framed 0x55 | |
490 Should we look at all 10 ones? The first couple might be | |
491 settling down. */ | |
492 /* The preamble + synchronisation bit sequence should be unique in | |
493 any bit stream, so we can rely on seeing this at any time as being | |
494 a real sync code. */ | |
495 switch (s->bit_stream) | |
496 { | |
497 case 0x803FF: | |
498 new_preamble_type = V8_SYNC_CI; | |
499 break; | |
500 case 0xF03FF: | |
501 new_preamble_type = V8_SYNC_CM_JM; | |
502 break; | |
503 case 0xAABFF: | |
504 new_preamble_type = V8_SYNC_V92; | |
505 break; | |
506 default: | |
507 new_preamble_type = V8_SYNC_UNKNOWN; | |
508 break; | |
509 } | |
510 if (new_preamble_type != V8_SYNC_UNKNOWN) | |
511 { | |
512 /* We have seen a fresh sync code */ | |
513 /* Debug */ | |
514 if (span_log_test(&s->logging, SPAN_LOG_FLOW)) | |
515 { | |
516 if (s->preamble_type != V8_SYNC_UNKNOWN) | |
517 { | |
518 switch (s->preamble_type) | |
519 { | |
520 case V8_SYNC_CI: | |
521 tag = "CI: "; | |
522 break; | |
523 case V8_SYNC_CM_JM: | |
524 tag = (s->calling_party) ? "JM: " : "CM: "; | |
525 break; | |
526 case V8_SYNC_V92: | |
527 tag = "V92: "; | |
528 break; | |
529 default: | |
530 tag = "??: "; | |
531 break; | |
532 } | |
533 span_log_buf(&s->logging, SPAN_LOG_FLOW, tag, s->rx_data, s->rx_data_ptr); | |
534 } | |
535 } | |
536 /* If we were handling a valid sync code then we should process what has been | |
537 received to date. */ | |
538 switch (s->preamble_type) | |
539 { | |
540 case V8_SYNC_CI: | |
541 ci_decode(s); | |
542 break; | |
543 case V8_SYNC_CM_JM: | |
544 cm_jm_decode(s); | |
545 break; | |
546 } | |
547 s->preamble_type = new_preamble_type; | |
548 s->bit_cnt = 0; | |
549 s->rx_data_ptr = 0; | |
550 } | |
551 | |
552 if (s->preamble_type != V8_SYNC_UNKNOWN) | |
553 { | |
554 /* Parse octets with 1 bit start, 1 bit stop */ | |
555 s->bit_cnt++; | |
556 /* Start, stop? */ | |
557 if ((s->bit_stream & 0x80400) == 0x80000 && s->bit_cnt >= 10) | |
558 { | |
559 /* Store the available data */ | |
560 data = (uint8_t) ((s->bit_stream >> 11) & 0xFF); | |
561 /* CJ (3 successive zero octets) detection */ | |
562 if (data == 0) | |
563 { | |
564 if (++s->zero_byte_count == 3) | |
565 s->got_cj = TRUE; | |
566 } | |
567 else | |
568 { | |
569 s->zero_byte_count = 0; | |
570 } | |
571 | |
572 if (s->rx_data_ptr < (int) (sizeof(s->rx_data) - 1)) | |
573 s->rx_data[s->rx_data_ptr++] = data; | |
574 s->bit_cnt = 0; | |
575 } | |
576 } | |
577 } | |
578 /*- End of function --------------------------------------------------------*/ | |
579 | |
580 static void v8_decode_init(v8_state_t *s) | |
581 { | |
582 fsk_rx_init(&s->v21rx, | |
583 &preset_fsk_specs[(s->calling_party) ? FSK_V21CH2 : FSK_V21CH1], | |
584 FSK_FRAME_MODE_ASYNC, | |
585 put_bit, | |
586 s); | |
587 fsk_rx_signal_cutoff(&s->v21rx, -45.5f); | |
588 s->preamble_type = V8_SYNC_UNKNOWN; | |
589 s->bit_stream = 0; | |
590 s->cm_jm_len = 0; | |
591 s->got_cm_jm = FALSE; | |
592 s->got_cj = FALSE; | |
593 s->zero_byte_count = 0; | |
594 s->rx_data_ptr = 0; | |
595 } | |
596 /*- End of function --------------------------------------------------------*/ | |
597 | |
598 static int get_bit(void *user_data) | |
599 { | |
600 v8_state_t *s; | |
601 uint8_t bit; | |
602 | |
603 s = user_data; | |
604 if (queue_read(s->tx_queue, &bit, 1) <= 0) | |
605 return SIG_STATUS_END_OF_DATA; | |
606 return bit; | |
607 } | |
608 /*- End of function --------------------------------------------------------*/ | |
609 | |
610 static void v8_put_preamble(v8_state_t *s) | |
611 { | |
612 static const uint8_t preamble[10] = | |
613 { | |
614 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 | |
615 }; | |
616 | |
617 queue_write(s->tx_queue, preamble, 10); | |
618 } | |
619 /*- End of function --------------------------------------------------------*/ | |
620 | |
621 static void v8_put_byte(v8_state_t *s, int data) | |
622 { | |
623 int i; | |
624 uint8_t bits[10]; | |
625 | |
626 /* Insert start & stop bits */ | |
627 bits[0] = 0; | |
628 for (i = 1; i < 9; i++) | |
629 { | |
630 bits[i] = (uint8_t) (data & 1); | |
631 data >>= 1; | |
632 } | |
633 bits[9] = 1; | |
634 queue_write(s->tx_queue, bits, 10); | |
635 } | |
636 /*- End of function --------------------------------------------------------*/ | |
637 | |
638 static void send_cm_jm(v8_state_t *s) | |
639 { | |
640 int val; | |
641 unsigned int offered_modulations; | |
642 | |
643 offered_modulations = s->parms.modulations & s->far_end_modulations; | |
644 | |
645 /* Send a CM, or a JM as appropriate */ | |
646 v8_put_preamble(s); | |
647 v8_put_byte(s, V8_CM_JM_SYNC_OCTET); | |
648 /* Data call */ | |
649 v8_put_byte(s, (s->result.call_function << 5) | V8_CALL_FUNCTION_TAG); | |
650 | |
651 /* Supported modulations */ | |
652 val = 0x05; | |
653 if (offered_modulations & V8_MOD_V90) | |
654 val |= 0x20; | |
655 if (offered_modulations & V8_MOD_V34) | |
656 val |= 0x40; | |
657 v8_put_byte(s, val); | |
658 | |
659 val = 0x10; | |
660 if (offered_modulations & V8_MOD_V32) | |
661 val |= 0x01; | |
662 if (offered_modulations & V8_MOD_V22) | |
663 val |= 0x02; | |
664 if (offered_modulations & V8_MOD_V17) | |
665 val |= 0x04; | |
666 if (offered_modulations & V8_MOD_V29) | |
667 val |= 0x40; | |
668 if (offered_modulations & V8_MOD_V27TER) | |
669 val |= 0x80; | |
670 v8_put_byte(s, val); | |
671 | |
672 val = 0x10; | |
673 if (offered_modulations & V8_MOD_V26TER) | |
674 val |= 0x01; | |
675 if (offered_modulations & V8_MOD_V26BIS) | |
676 val |= 0x02; | |
677 if (offered_modulations & V8_MOD_V23) | |
678 val |= 0x04; | |
679 if (offered_modulations & V8_MOD_V23HALF) | |
680 val |= 0x40; | |
681 if (offered_modulations & V8_MOD_V21) | |
682 val |= 0x80; | |
683 v8_put_byte(s, val); | |
684 | |
685 if (s->parms.protocol) | |
686 v8_put_byte(s, (s->parms.protocol << 5) | V8_PROTOCOLS_TAG); | |
687 if (s->parms.pcm_modem_availability) | |
688 v8_put_byte(s, (s->parms.pcm_modem_availability << 5) | V8_PCM_MODEM_AVAILABILITY_TAG); | |
689 if (s->parms.pstn_access) | |
690 v8_put_byte(s, (s->parms.pstn_access << 5) | V8_PSTN_ACCESS_TAG); | |
691 if (s->parms.t66 >= 0) | |
692 v8_put_byte(s, (s->parms.t66 << 5) | V8_T66_TAG); | |
693 | |
694 /* No NSF */ | |
695 //v8_put_byte(s, (0 << 5) | V8_NSF_TAG); | |
696 } | |
697 /*- End of function --------------------------------------------------------*/ | |
698 | |
699 SPAN_DECLARE_NONSTD(int) v8_tx(v8_state_t *s, int16_t *amp, int max_len) | |
700 { | |
701 int len; | |
702 | |
703 //span_log(&s->logging, SPAN_LOG_FLOW, "v8_tx state %d\n", s->state); | |
704 len = 0; | |
705 if (s->modem_connect_tone_tx_on) | |
706 { | |
707 if (s->modem_connect_tone_tx_on > ms_to_samples(75)) | |
708 { | |
709 /* Send the ANSam tone */ | |
710 len = modem_connect_tones_tx(&s->ansam_tx, amp, max_len); | |
711 if (len < max_len) | |
712 { | |
713 span_log(&s->logging, SPAN_LOG_FLOW, "ANSam or ANSam/ ended\n"); | |
714 s->modem_connect_tone_tx_on = ms_to_samples(75); | |
715 } | |
716 } | |
717 else | |
718 { | |
719 /* Send the 75ms of silence after the ANSam tone */ | |
720 if (max_len > s->modem_connect_tone_tx_on) | |
721 len = s->modem_connect_tone_tx_on; | |
722 else | |
723 len = max_len; | |
724 vec_zeroi16(amp, len); | |
725 s->modem_connect_tone_tx_on -= len; | |
726 } | |
727 } | |
728 if (s->fsk_tx_on && len < max_len) | |
729 { | |
730 max_len -= len; | |
731 len = fsk_tx(&s->v21tx, amp + len, max_len); | |
732 if (len < max_len) | |
733 { | |
734 span_log(&s->logging, SPAN_LOG_FLOW, "FSK ends\n"); | |
735 s->fsk_tx_on = FALSE; | |
736 } | |
737 } | |
738 return len; | |
739 } | |
740 /*- End of function --------------------------------------------------------*/ | |
741 | |
742 static void v8_send_ci(v8_state_t *s) | |
743 { | |
744 int i; | |
745 | |
746 /* Send 4 CI packets in a burst (the spec says at least 3) */ | |
747 for (i = 0; i < 4; i++) | |
748 { | |
749 v8_put_preamble(s); | |
750 v8_put_byte(s, V8_CI_SYNC_OCTET); | |
751 v8_put_byte(s, (s->result.call_function << 5) | V8_CALL_FUNCTION_TAG); | |
752 } | |
753 } | |
754 /*- End of function --------------------------------------------------------*/ | |
755 | |
756 static void handle_modem_connect_tone(v8_state_t *s, int tone) | |
757 { | |
758 s->result.modem_connect_tone = tone; | |
759 span_log(&s->logging, SPAN_LOG_FLOW, "'%s' recognised\n", modem_connect_tone_to_str(tone)); | |
760 if (tone == MODEM_CONNECT_TONES_ANSAM | |
761 || | |
762 tone == MODEM_CONNECT_TONES_ANSAM_PR) | |
763 { | |
764 /* Set the Te interval. The spec. says 500ms is the minimum, | |
765 but gives reasons why 1 second is a better value. */ | |
766 s->state = V8_HEARD_ANSAM; | |
767 s->ci_timer = ms_to_samples(1000); | |
768 } | |
769 else | |
770 { | |
771 /* If we found a connect tone, and it isn't one of the modulated answer tones, | |
772 indicating V.8 startup, we are not going to do V.8 processing. */ | |
773 span_log(&s->logging, SPAN_LOG_FLOW, "Non-V.8 modem connect tone detected\n"); | |
774 s->state = V8_PARKED; | |
775 if (s->result_handler) | |
776 s->result_handler(s->result_handler_user_data, &s->result); | |
777 } | |
778 } | |
779 /*- End of function --------------------------------------------------------*/ | |
780 | |
781 SPAN_DECLARE_NONSTD(int) v8_rx(v8_state_t *s, const int16_t *amp, int len) | |
782 { | |
783 int i; | |
784 int residual_samples; | |
785 int tone; | |
786 | |
787 //span_log(&s->logging, SPAN_LOG_FLOW, "v8_rx state %d\n", s->state); | |
788 residual_samples = 0; | |
789 switch (s->state) | |
790 { | |
791 case V8_WAIT_1S: | |
792 residual_samples = modem_connect_tones_rx(&s->ansam_rx, amp, len); | |
793 /* Wait 1 second before sending the first CI packet */ | |
794 if ((s->negotiation_timer -= len) > 0) | |
795 break; | |
796 fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]); | |
797 v8_send_ci(s); | |
798 s->state = V8_CI_ON; | |
799 s->fsk_tx_on = TRUE; | |
800 break; | |
801 case V8_CI_ON: | |
802 residual_samples = modem_connect_tones_rx(&s->ansam_rx, amp, len); | |
803 /* Check if an ANSam or ANSam/ tone has been detected */ | |
804 if ((tone = modem_connect_tones_rx_get(&s->ansam_rx)) != MODEM_CONNECT_TONES_NONE) | |
805 { | |
806 handle_modem_connect_tone(s, tone); | |
807 break; | |
808 } | |
809 if (queue_empty(s->tx_queue)) | |
810 { | |
811 s->state = V8_CI_OFF; | |
812 s->ci_timer = ms_to_samples(500); | |
813 break; | |
814 } | |
815 break; | |
816 case V8_CI_OFF: | |
817 residual_samples = modem_connect_tones_rx(&s->ansam_rx, amp, len); | |
818 /* Check if an ANSam or ANSam/ tone has been detected */ | |
819 if ((tone = modem_connect_tones_rx_get(&s->ansam_rx)) != MODEM_CONNECT_TONES_NONE) | |
820 { | |
821 handle_modem_connect_tone(s, tone); | |
822 break; | |
823 } | |
824 if ((s->ci_timer -= len) <= 0) | |
825 { | |
826 if (++s->ci_count >= 10) | |
827 { | |
828 /* The spec says we should give up now. */ | |
829 span_log(&s->logging, SPAN_LOG_FLOW, "Timeout waiting for modem connect tone\n"); | |
830 s->state = V8_PARKED; | |
831 if (s->result_handler) | |
832 s->result_handler(s->result_handler_user_data, NULL); | |
833 } | |
834 else | |
835 { | |
836 /* Try again */ | |
837 fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]); | |
838 v8_send_ci(s); | |
839 s->state = V8_CI_ON; | |
840 s->fsk_tx_on = TRUE; | |
841 } | |
842 } | |
843 break; | |
844 case V8_HEARD_ANSAM: | |
845 /* We have heard the ANSam or ANSam/ signal, but we still need to wait for the | |
846 end of the Te timeout period to comply with the spec. */ | |
847 if ((s->ci_timer -= len) <= 0) | |
848 { | |
849 v8_decode_init(s); | |
850 s->negotiation_timer = ms_to_samples(5000); | |
851 fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]); | |
852 send_cm_jm(s); | |
853 s->state = V8_CM_ON; | |
854 s->fsk_tx_on = TRUE; | |
855 } | |
856 break; | |
857 case V8_CM_ON: | |
858 residual_samples = fsk_rx(&s->v21rx, amp, len); | |
859 if (s->got_cm_jm) | |
860 { | |
861 span_log(&s->logging, SPAN_LOG_FLOW, "JM recognised\n"); | |
862 /* Now JM has been detected, we send CJ and wait for 75 ms | |
863 before finishing the V.8 analysis. */ | |
864 fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]); | |
865 for (i = 0; i < 3; i++) | |
866 v8_put_byte(s, 0); | |
867 s->state = V8_CJ_ON; | |
868 s->fsk_tx_on = TRUE; | |
869 break; | |
870 } | |
871 if ((s->negotiation_timer -= len) <= 0) | |
872 { | |
873 /* Timeout */ | |
874 span_log(&s->logging, SPAN_LOG_FLOW, "Timeout waiting for JM\n"); | |
875 s->state = V8_PARKED; | |
876 if (s->result_handler) | |
877 s->result_handler(s->result_handler_user_data, NULL); | |
878 } | |
879 if (queue_contents(s->tx_queue) < 10) | |
880 { | |
881 /* Send CM again */ | |
882 send_cm_jm(s); | |
883 } | |
884 break; | |
885 case V8_CJ_ON: | |
886 residual_samples = fsk_rx(&s->v21rx, amp, len); | |
887 if (queue_empty(s->tx_queue)) | |
888 { | |
889 s->negotiation_timer = ms_to_samples(75); | |
890 s->state = V8_SIGC; | |
891 } | |
892 break; | |
893 case V8_SIGC: | |
894 if ((s->negotiation_timer -= len) <= 0) | |
895 { | |
896 /* The V.8 negotiation has succeeded. */ | |
897 span_log(&s->logging, SPAN_LOG_FLOW, "Negotiation succeeded\n"); | |
898 s->state = V8_PARKED; | |
899 if (s->result_handler) | |
900 s->result_handler(s->result_handler_user_data, &s->result); | |
901 } | |
902 break; | |
903 case V8_CM_WAIT: | |
904 residual_samples = fsk_rx(&s->v21rx, amp, len); | |
905 if (s->got_cm_jm) | |
906 { | |
907 span_log(&s->logging, SPAN_LOG_FLOW, "CM recognised\n"); | |
908 | |
909 /* TODO: negotiate if the call function is acceptable */ | |
910 | |
911 /* Stop sending ANSam or ANSam/ and send JM instead */ | |
912 fsk_tx_init(&s->v21tx, &preset_fsk_specs[FSK_V21CH2], get_bit, s); | |
913 /* Set the timeout for JM */ | |
914 s->negotiation_timer = ms_to_samples(5000); | |
915 s->state = V8_JM_ON; | |
916 send_cm_jm(s); | |
917 s->modem_connect_tone_tx_on = ms_to_samples(75); | |
918 s->fsk_tx_on = TRUE; | |
919 break; | |
920 } | |
921 if ((s->negotiation_timer -= len) <= 0) | |
922 { | |
923 /* Timeout */ | |
924 span_log(&s->logging, SPAN_LOG_FLOW, "Timeout waiting for CM\n"); | |
925 s->state = V8_PARKED; | |
926 if (s->result_handler) | |
927 s->result_handler(s->result_handler_user_data, NULL); | |
928 } | |
929 break; | |
930 case V8_JM_ON: | |
931 residual_samples = fsk_rx(&s->v21rx, amp, len); | |
932 if (s->got_cj) | |
933 { | |
934 span_log(&s->logging, SPAN_LOG_FLOW, "CJ recognised\n"); | |
935 /* Stop sending JM, flushing anything left in the buffer, and wait 75 ms */ | |
936 queue_flush(s->tx_queue); | |
937 s->negotiation_timer = ms_to_samples(75); | |
938 s->state = V8_SIGA; | |
939 break; | |
940 } | |
941 if ((s->negotiation_timer -= len) <= 0) | |
942 { | |
943 /* Timeout */ | |
944 span_log(&s->logging, SPAN_LOG_FLOW, "Timeout waiting for CJ\n"); | |
945 s->state = V8_PARKED; | |
946 if (s->result_handler) | |
947 s->result_handler(s->result_handler_user_data, NULL); | |
948 break; | |
949 } | |
950 if (queue_contents(s->tx_queue) < 10) | |
951 { | |
952 /* Send JM */ | |
953 send_cm_jm(s); | |
954 } | |
955 break; | |
956 case V8_SIGA: | |
957 if ((s->negotiation_timer -= len) <= 0) | |
958 { | |
959 /* The V.8 negotiation has succeeded. */ | |
960 span_log(&s->logging, SPAN_LOG_FLOW, "Negotiation succeeded\n"); | |
961 s->state = V8_PARKED; | |
962 if (s->result_handler) | |
963 s->result_handler(s->result_handler_user_data, &s->result); | |
964 } | |
965 break; | |
966 case V8_PARKED: | |
967 residual_samples = len; | |
968 break; | |
969 } | |
970 return residual_samples; | |
971 } | |
972 /*- End of function --------------------------------------------------------*/ | |
973 | |
974 SPAN_DECLARE(logging_state_t *) v8_get_logging_state(v8_state_t *s) | |
975 { | |
976 return &s->logging; | |
977 } | |
978 /*- End of function --------------------------------------------------------*/ | |
979 | |
980 SPAN_DECLARE(int) v8_restart(v8_state_t *s, | |
981 int calling_party, | |
982 v8_parms_t *parms) | |
983 { | |
984 memcpy(&s->parms, parms, sizeof(s->parms)); | |
985 memset(&s->result, 0, sizeof(s->result)); | |
986 | |
987 s->result.call_function = s->parms.call_function; | |
988 s->result.nsf = -1; | |
989 s->result.t66 = -1; | |
990 | |
991 s->ci_timer = 0; | |
992 if (calling_party) | |
993 { | |
994 s->calling_party = TRUE; | |
995 s->state = V8_WAIT_1S; | |
996 s->negotiation_timer = ms_to_samples(1000); | |
997 s->ci_count = 0; | |
998 modem_connect_tones_rx_init(&s->ansam_rx, MODEM_CONNECT_TONES_ANS_PR, NULL, NULL); | |
999 fsk_tx_init(&s->v21tx, &preset_fsk_specs[FSK_V21CH1], get_bit, s); | |
1000 } | |
1001 else | |
1002 { | |
1003 /* Send the ANSam or ANSam/ tone */ | |
1004 s->calling_party = FALSE; | |
1005 modem_connect_tones_tx_init(&s->ansam_tx, s->parms.modem_connect_tone); | |
1006 | |
1007 v8_decode_init(s); | |
1008 s->state = V8_CM_WAIT; | |
1009 s->negotiation_timer = ms_to_samples(200 + 5000); | |
1010 s->modem_connect_tone_tx_on = ms_to_samples(75) + 1; | |
1011 } | |
1012 s->result.modem_connect_tone = MODEM_CONNECT_TONES_NONE; | |
1013 | |
1014 if ((s->tx_queue = queue_init(NULL, 1024, 0)) == NULL) | |
1015 return -1; | |
1016 return 0; | |
1017 } | |
1018 /*- End of function --------------------------------------------------------*/ | |
1019 | |
1020 SPAN_DECLARE(v8_state_t *) v8_init(v8_state_t *s, | |
1021 int calling_party, | |
1022 v8_parms_t *parms, | |
1023 v8_result_handler_t *result_handler, | |
1024 void *user_data) | |
1025 { | |
1026 if (s == NULL) | |
1027 { | |
1028 if ((s = (v8_state_t *) malloc(sizeof(*s))) == NULL) | |
1029 return NULL; | |
1030 } | |
1031 memset(s, 0, sizeof(*s)); | |
1032 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
1033 span_log_set_protocol(&s->logging, "V.8"); | |
1034 s->result_handler = result_handler; | |
1035 s->result_handler_user_data = user_data; | |
1036 | |
1037 v8_restart(s, calling_party, parms); | |
1038 | |
1039 memcpy(&s->parms, parms, sizeof(s->parms)); | |
1040 | |
1041 s->result.call_function = s->parms.call_function; | |
1042 s->result.nsf = -1; | |
1043 s->result.t66 = -1; | |
1044 | |
1045 s->ci_timer = 0; | |
1046 if (calling_party) | |
1047 { | |
1048 s->calling_party = TRUE; | |
1049 s->state = V8_WAIT_1S; | |
1050 s->negotiation_timer = ms_to_samples(1000); | |
1051 s->ci_count = 0; | |
1052 modem_connect_tones_rx_init(&s->ansam_rx, MODEM_CONNECT_TONES_ANS_PR, NULL, NULL); | |
1053 fsk_tx_init(&s->v21tx, &preset_fsk_specs[FSK_V21CH1], get_bit, s); | |
1054 } | |
1055 else | |
1056 { | |
1057 /* Send the ANSam or ANSam/ tone */ | |
1058 s->calling_party = FALSE; | |
1059 modem_connect_tones_tx_init(&s->ansam_tx, s->parms.modem_connect_tone); | |
1060 | |
1061 v8_decode_init(s); | |
1062 s->state = V8_CM_WAIT; | |
1063 s->negotiation_timer = ms_to_samples(200 + 5000); | |
1064 s->modem_connect_tone_tx_on = ms_to_samples(75) + 1; | |
1065 } | |
1066 s->result.modem_connect_tone = MODEM_CONNECT_TONES_NONE; | |
1067 | |
1068 if ((s->tx_queue = queue_init(NULL, 1024, 0)) == NULL) | |
1069 return NULL; | |
1070 return s; | |
1071 } | |
1072 /*- End of function --------------------------------------------------------*/ | |
1073 | |
1074 SPAN_DECLARE(int) v8_release(v8_state_t *s) | |
1075 { | |
1076 return queue_free(s->tx_queue); | |
1077 } | |
1078 /*- End of function --------------------------------------------------------*/ | |
1079 | |
1080 SPAN_DECLARE(int) v8_free(v8_state_t *s) | |
1081 { | |
1082 int ret; | |
1083 | |
1084 ret = queue_free(s->tx_queue); | |
1085 free(s); | |
1086 return ret; | |
1087 } | |
1088 /*- End of function --------------------------------------------------------*/ | |
1089 /*- End of file ------------------------------------------------------------*/ |