Mercurial > hg > audiostuff
comparison spandsp-0.0.3/spandsp-0.0.3/src/v8.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 * 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 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: v8.c,v 1.20 2006/11/30 15:41:47 steveu Exp $ | |
26 */ | |
27 | |
28 /*! \file */ | |
29 | |
30 #ifdef 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 | |
45 #include "spandsp/telephony.h" | |
46 #include "spandsp/logging.h" | |
47 #include "spandsp/queue.h" | |
48 #include "spandsp/async.h" | |
49 #include "spandsp/dds.h" | |
50 #include "spandsp/tone_detect.h" | |
51 #include "spandsp/tone_generate.h" | |
52 #include "spandsp/super_tone_rx.h" | |
53 #include "spandsp/modem_connect_tones.h" | |
54 #include "spandsp/power_meter.h" | |
55 #include "spandsp/fsk.h" | |
56 #include "spandsp/v8.h" | |
57 | |
58 #define ms_to_samples(t) (((t)*SAMPLE_RATE)/1000) | |
59 | |
60 enum | |
61 { | |
62 V8_WAIT_1S, | |
63 V8_CI, | |
64 V8_CI_ON, | |
65 V8_CI_OFF, | |
66 V8_HEARD_ANSAM, | |
67 V8_CM_ON, | |
68 V8_CJ_ON, | |
69 V8_CM_WAIT, | |
70 | |
71 V8_SIGC, | |
72 V8_WAIT_200MS, | |
73 V8_JM_ON, | |
74 V8_SIGA, | |
75 | |
76 V8_PARKED | |
77 } v8_states_e; | |
78 | |
79 enum | |
80 { | |
81 V8_SYNC_UNKNOWN = 0, | |
82 V8_SYNC_CI, | |
83 V8_SYNC_CM_JM, | |
84 V8_SYNC_V92 | |
85 } v8_sync_types_e; | |
86 | |
87 const char *v8_call_function_to_str(int call_function) | |
88 { | |
89 switch (call_function) | |
90 { | |
91 case V8_CALL_TBS: | |
92 return "TBS"; | |
93 case V8_CALL_H324: | |
94 return "H.324 PSTN multimedia terminal"; | |
95 case V8_CALL_V18: | |
96 return "V.18 textphone"; | |
97 case V8_CALL_T101: | |
98 return "T.101 videotext"; | |
99 case V8_CALL_T30_TX: | |
100 return "T.30 Tx FAX"; | |
101 case V8_CALL_T30_RX: | |
102 return "T.30 Rx FAX"; | |
103 case V8_CALL_V_SERIES: | |
104 return "V series modem data"; | |
105 case V8_CALL_FUNCTION_EXTENSION: | |
106 return "Call function is in extention octet"; | |
107 } | |
108 return "???"; | |
109 } | |
110 /*- End of function --------------------------------------------------------*/ | |
111 | |
112 const char *v8_modulation_to_str(int modulation_scheme) | |
113 { | |
114 switch (modulation_scheme) | |
115 { | |
116 case V8_MOD_V17: | |
117 return "V.17 half-duplex"; | |
118 case V8_MOD_V21: | |
119 return "V.21 duplex"; | |
120 case V8_MOD_V22: | |
121 return "V.22/V22.bis duplex"; | |
122 case V8_MOD_V23HALF: | |
123 return "V.23 half-duplex"; | |
124 case V8_MOD_V23: | |
125 return "V.23 duplex"; | |
126 case V8_MOD_V26BIS: | |
127 return "V.23 duplex"; | |
128 case V8_MOD_V26TER: | |
129 return "V.23 duplex"; | |
130 case V8_MOD_V27TER: | |
131 return "V.23 duplex"; | |
132 case V8_MOD_V29: | |
133 return "V.29 half-duplex"; | |
134 case V8_MOD_V32: | |
135 return "V.32/V32.bis duplex"; | |
136 case V8_MOD_V34HALF: | |
137 return "V.34 half-duplex"; | |
138 case V8_MOD_V34: | |
139 return "V.34 duplex"; | |
140 case V8_MOD_V90: | |
141 return "V.90 duplex"; | |
142 case V8_MOD_V92: | |
143 return "V.92 duplex"; | |
144 case V8_MOD_FAILED: | |
145 return "negotiation failed"; | |
146 } | |
147 return "???"; | |
148 } | |
149 /*- End of function --------------------------------------------------------*/ | |
150 | |
151 const char *v8_protocol_to_str(int protocol) | |
152 { | |
153 switch (protocol) | |
154 { | |
155 case V8_PROTOCOL_NONE: | |
156 return "None"; | |
157 case V8_PROTOCOL_LAPM_V42: | |
158 return "LAPM"; | |
159 case V8_PROTOCOL_EXTENSION: | |
160 return "Extension"; | |
161 } | |
162 return "Undefined"; | |
163 } | |
164 /*- End of function --------------------------------------------------------*/ | |
165 | |
166 const char *v8_pstn_access_to_str(int pstn_access) | |
167 { | |
168 return "???"; | |
169 } | |
170 /*- End of function --------------------------------------------------------*/ | |
171 | |
172 const char *v8_pcm_modem_availability_to_str(int pcm_modem_availability) | |
173 { | |
174 return "???"; | |
175 } | |
176 /*- End of function --------------------------------------------------------*/ | |
177 | |
178 void v8_log_supported_modulations(v8_state_t *s, int modulation_schemes) | |
179 { | |
180 const char *comma; | |
181 int i; | |
182 | |
183 comma = ""; | |
184 span_log(&s->logging, SPAN_LOG_FLOW, ""); | |
185 for (i = 0; i < 32; i++) | |
186 { | |
187 if ((modulation_schemes & (1 << i))) | |
188 { | |
189 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, "%s%s", comma, v8_modulation_to_str(modulation_schemes & (1 << i))); | |
190 comma = ", "; | |
191 } | |
192 } | |
193 span_log(&s->logging, SPAN_LOG_FLOW | SPAN_LOG_SUPPRESS_LABELLING, " supported\n"); | |
194 } | |
195 /*- End of function --------------------------------------------------------*/ | |
196 | |
197 static const uint8_t *process_call_function(v8_state_t *s, const uint8_t *p) | |
198 { | |
199 int call_function; | |
200 | |
201 call_function = (*p >> 5) & 0x07; | |
202 span_log(&s->logging, SPAN_LOG_FLOW, "%s\n", v8_call_function_to_str(call_function)); | |
203 s->call_function = call_function; | |
204 return ++p; | |
205 } | |
206 /*- End of function --------------------------------------------------------*/ | |
207 | |
208 static const uint8_t *process_modulation_mode(v8_state_t *s, const uint8_t *p) | |
209 { | |
210 int far_end_modulations; | |
211 | |
212 /* Modulation mode octet */ | |
213 far_end_modulations = 0; | |
214 if (*p & 0x80) | |
215 far_end_modulations |= V8_MOD_V34HALF; | |
216 if (*p & 0x40) | |
217 far_end_modulations |= V8_MOD_V34; | |
218 if (*p & 0x20) | |
219 far_end_modulations |= V8_MOD_V90; | |
220 | |
221 if ((*++p & 0x38) == 0x10) | |
222 { | |
223 if (*p & 0x80) | |
224 far_end_modulations |= V8_MOD_V27TER; | |
225 if (*p & 0x40) | |
226 far_end_modulations |= V8_MOD_V29; | |
227 if (*p & 0x04) | |
228 far_end_modulations |= V8_MOD_V17; | |
229 if (*p & 0x02) | |
230 far_end_modulations |= V8_MOD_V22; | |
231 if (*p & 0x01) | |
232 far_end_modulations |= V8_MOD_V32; | |
233 | |
234 if ((*++p & 0x38) == 0x10) | |
235 { | |
236 if (*p & 0x80) | |
237 far_end_modulations |= V8_MOD_V21; | |
238 if (*p & 0x40) | |
239 far_end_modulations |= V8_MOD_V23HALF; | |
240 if (*p & 0x04) | |
241 far_end_modulations |= V8_MOD_V23; | |
242 if (*p & 0x02) | |
243 far_end_modulations |= V8_MOD_V26BIS; | |
244 if (*p & 0x01) | |
245 far_end_modulations |= V8_MOD_V26TER; | |
246 /* Skip any future extensions we do not understand */ | |
247 while ((*++p & 0x38) == 0x10) | |
248 /* dummy loop */; | |
249 } | |
250 } | |
251 s->far_end_modulations = far_end_modulations; | |
252 v8_log_supported_modulations(s, s->far_end_modulations); | |
253 return ++p; | |
254 } | |
255 /*- End of function --------------------------------------------------------*/ | |
256 | |
257 static const uint8_t *process_protocols(v8_state_t *s, const uint8_t *p) | |
258 { | |
259 int protocol; | |
260 | |
261 protocol = (*p >> 5) & 0x07; | |
262 span_log(&s->logging, SPAN_LOG_FLOW, "%s\n", v8_protocol_to_str(protocol)); | |
263 s->protocol = protocol; | |
264 return ++p; | |
265 } | |
266 /*- End of function --------------------------------------------------------*/ | |
267 | |
268 static const uint8_t *process_pstn_access(v8_state_t *s, const uint8_t *p) | |
269 { | |
270 int pstn_access; | |
271 | |
272 pstn_access = (*p >> 5) & 0x07; | |
273 if (pstn_access & V8_PSTN_ACCESS_DCE_ON_DIGTIAL) | |
274 span_log(&s->logging, SPAN_LOG_FLOW, "DCE on digital network connection\n"); | |
275 else | |
276 span_log(&s->logging, SPAN_LOG_FLOW, "DCE on analogue network connection\n"); | |
277 if (pstn_access & V8_PSTN_ACCESS_ANSWER_DCE_CELLULAR) | |
278 span_log(&s->logging, SPAN_LOG_FLOW, "Answer DCE on cellular connection\n"); | |
279 if (pstn_access & V8_PSTN_ACCESS_CALL_DCE_CELLULAR) | |
280 span_log(&s->logging, SPAN_LOG_FLOW, "Call DCE on cellular connection\n"); | |
281 return ++p; | |
282 } | |
283 /*- End of function --------------------------------------------------------*/ | |
284 | |
285 static const uint8_t *process_non_standard_facilities(v8_state_t *s, const uint8_t *p) | |
286 { | |
287 ++p; | |
288 p += *p; | |
289 return p; | |
290 } | |
291 /*- End of function --------------------------------------------------------*/ | |
292 | |
293 static const uint8_t *process_pcm_modem_availability(v8_state_t *s, const uint8_t *p) | |
294 { | |
295 int pcm_availability; | |
296 | |
297 pcm_availability = (*p >> 5) & 0x07; | |
298 if (pcm_availability & V8_PSTN_PCM_MODEM_V91) | |
299 span_log(&s->logging, SPAN_LOG_FLOW, "V.91 available\n"); | |
300 if (pcm_availability & V8_PSTN_PCM_MODEM_V90_V92_DIGITAL) | |
301 span_log(&s->logging, SPAN_LOG_FLOW, "V.90 or V.92 digital modem available\n"); | |
302 if (pcm_availability & V8_PSTN_PCM_MODEM_V90_V92_ANALOGUE) | |
303 span_log(&s->logging, SPAN_LOG_FLOW, "V.90 or V.92 analogue modem available\n"); | |
304 return ++p; | |
305 } | |
306 /*- End of function --------------------------------------------------------*/ | |
307 | |
308 static const uint8_t *process_t66(v8_state_t *s, const uint8_t *p) | |
309 { | |
310 return ++p; | |
311 } | |
312 /*- End of function --------------------------------------------------------*/ | |
313 | |
314 static void ci_decode(v8_state_t *s) | |
315 { | |
316 if ((s->rx_data[0] & 0x1F) == 0x01) | |
317 process_call_function(s, &s->rx_data[0]); | |
318 } | |
319 /*- End of function --------------------------------------------------------*/ | |
320 | |
321 static void cm_jm_decode(v8_state_t *s) | |
322 { | |
323 const uint8_t *p; | |
324 | |
325 if (s->got_cm_jm) | |
326 return; | |
327 | |
328 /* We must receive two consecutive identical CM or JM sequences to accept it. */ | |
329 if (s->cm_jm_count <= 0 | |
330 || | |
331 s->cm_jm_count != s->rx_data_ptr | |
332 || | |
333 memcmp(s->cm_jm_data, s->rx_data, s->rx_data_ptr)) | |
334 { | |
335 /* Save the current CM or JM sequence */ | |
336 s->cm_jm_count = s->rx_data_ptr; | |
337 memcpy(s->cm_jm_data, s->rx_data, s->rx_data_ptr); | |
338 return; | |
339 } | |
340 /* We have a pair of matching CMs or JMs */ | |
341 s->got_cm_jm = TRUE; | |
342 | |
343 span_log(&s->logging, SPAN_LOG_FLOW, "Decoding\n"); | |
344 | |
345 /* Zero indicates the end */ | |
346 s->cm_jm_data[s->cm_jm_count] = 0; | |
347 | |
348 s->far_end_modulations = 0; | |
349 p = s->cm_jm_data; | |
350 | |
351 while (*p) | |
352 { | |
353 switch (*p & 0x1F) | |
354 { | |
355 case 0x01: | |
356 p = process_call_function(s, p); | |
357 break; | |
358 case 0x05: | |
359 p = process_modulation_mode(s, p); | |
360 break; | |
361 case 0x0A: | |
362 p = process_protocols(s, p); | |
363 break; | |
364 case 0x0D: | |
365 p = process_pstn_access(s, p); | |
366 break; | |
367 case 0x0F: | |
368 p = process_non_standard_facilities(s, p); | |
369 break; | |
370 case 0x07: | |
371 p = process_pcm_modem_availability(s, p); | |
372 break; | |
373 case 0x0E: | |
374 p = process_t66(s, p); | |
375 break; | |
376 default: | |
377 p++; | |
378 break; | |
379 } | |
380 } | |
381 } | |
382 /*- End of function --------------------------------------------------------*/ | |
383 | |
384 static void put_bit(void *user_data, int bit) | |
385 { | |
386 v8_state_t *s; | |
387 int new_preamble_type; | |
388 const char *tag; | |
389 uint8_t data; | |
390 | |
391 s = user_data; | |
392 if (bit < 0) | |
393 { | |
394 /* Special conditions */ | |
395 switch (bit) | |
396 { | |
397 case PUTBIT_CARRIER_UP: | |
398 case PUTBIT_CARRIER_DOWN: | |
399 case PUTBIT_TRAINING_SUCCEEDED: | |
400 case PUTBIT_TRAINING_FAILED: | |
401 break; | |
402 default: | |
403 break; | |
404 } | |
405 return; | |
406 } | |
407 /* Wait until we sync. */ | |
408 s->bit_stream = (s->bit_stream >> 1) | (bit << 19); | |
409 if (s->bit_stream == 0x803FF) | |
410 new_preamble_type = V8_SYNC_CI; | |
411 else if (s->bit_stream == 0xF03FF) | |
412 new_preamble_type = V8_SYNC_CM_JM; | |
413 else if (s->bit_stream == 0xAABFF) | |
414 new_preamble_type = V8_SYNC_V92; | |
415 else | |
416 new_preamble_type = V8_SYNC_UNKNOWN; | |
417 if (new_preamble_type) | |
418 { | |
419 /* Debug */ | |
420 if (span_log_test(&s->logging, SPAN_LOG_FLOW)) | |
421 { | |
422 if (s->preamble_type == V8_SYNC_CI) | |
423 { | |
424 tag = "CI: "; | |
425 } | |
426 else if (s->preamble_type == V8_SYNC_CM_JM) | |
427 { | |
428 if (s->caller) | |
429 tag = "JM: "; | |
430 else | |
431 tag = "CM: "; | |
432 } | |
433 else if (s->preamble_type == V8_SYNC_V92) | |
434 { | |
435 tag = "V92: "; | |
436 } | |
437 else | |
438 { | |
439 tag = "??: "; | |
440 } | |
441 span_log_buf(&s->logging, SPAN_LOG_FLOW, tag, s->rx_data, s->rx_data_ptr); | |
442 } | |
443 /* Decode previous sequence */ | |
444 switch (s->preamble_type) | |
445 { | |
446 case V8_SYNC_CI: | |
447 ci_decode(s); | |
448 break; | |
449 case V8_SYNC_CM_JM: | |
450 cm_jm_decode(s); | |
451 break; | |
452 } | |
453 s->preamble_type = new_preamble_type; | |
454 s->bit_cnt = 0; | |
455 s->rx_data_ptr = 0; | |
456 } | |
457 | |
458 /* Parse octets with 1 bit start, 1 bit stop */ | |
459 if (s->preamble_type) | |
460 { | |
461 s->bit_cnt++; | |
462 /* Start, stop? */ | |
463 if ((s->bit_stream & 0x80400) == 0x80000 && s->bit_cnt >= 10) | |
464 { | |
465 /* Store the available data */ | |
466 data = (uint8_t) ((s->bit_stream >> 11) & 0xFF); | |
467 /* CJ detection */ | |
468 if (data == 0) | |
469 { | |
470 if (++s->zero_byte_count == 3) | |
471 s->got_cj = TRUE; | |
472 } | |
473 else | |
474 { | |
475 s->zero_byte_count = 0; | |
476 } | |
477 | |
478 if (s->rx_data_ptr < (int) (sizeof(s->rx_data) - 1)) | |
479 s->rx_data[s->rx_data_ptr++] = data; | |
480 s->bit_cnt = 0; | |
481 } | |
482 } | |
483 } | |
484 /*- End of function --------------------------------------------------------*/ | |
485 | |
486 static void v8_decode_init(v8_state_t *s) | |
487 { | |
488 if (s->caller) | |
489 fsk_rx_init(&s->v21rx, &preset_fsk_specs[FSK_V21CH2], FALSE, put_bit, s); | |
490 else | |
491 fsk_rx_init(&s->v21rx, &preset_fsk_specs[FSK_V21CH1], FALSE, put_bit, s); | |
492 s->preamble_type = 0; | |
493 s->bit_stream = 0; | |
494 s->cm_jm_count = 0; | |
495 s->got_cm_jm = FALSE; | |
496 s->got_cj = FALSE; | |
497 s->zero_byte_count = 0; | |
498 s->rx_data_ptr = 0; | |
499 } | |
500 /*- End of function --------------------------------------------------------*/ | |
501 | |
502 static int get_bit(void *user_data) | |
503 { | |
504 v8_state_t *s; | |
505 uint8_t bit; | |
506 | |
507 s = user_data; | |
508 if (queue_read(&s->tx_queue, &bit, 1) <= 0) | |
509 bit = 1; | |
510 return bit; | |
511 } | |
512 /*- End of function --------------------------------------------------------*/ | |
513 | |
514 static void v8_put_byte(v8_state_t *s, int data) | |
515 { | |
516 int i; | |
517 uint8_t bits[10]; | |
518 | |
519 /* Insert start & stop bits */ | |
520 bits[0] = 0; | |
521 for (i = 1; i < 9; i++) | |
522 { | |
523 bits[i] = (uint8_t) (data & 1); | |
524 data >>= 1; | |
525 } | |
526 bits[9] = 1; | |
527 queue_write(&s->tx_queue, bits, 10); | |
528 } | |
529 /*- End of function --------------------------------------------------------*/ | |
530 | |
531 static void send_cm_jm(v8_state_t *s, int mod_mask) | |
532 { | |
533 int val; | |
534 static const uint8_t preamble[20] = | |
535 { | |
536 /* 10 1's (0x3FF), then 10 bits of CM sync (0x00F) */ | |
537 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 | |
538 }; | |
539 | |
540 /* Send a CM, or a JM as appropriate */ | |
541 queue_write(&s->tx_queue, preamble, 20); | |
542 | |
543 /* Data call */ | |
544 v8_put_byte(s, (V8_CALL_V_SERIES << 5) | 0x01); | |
545 | |
546 /* Supported modulations */ | |
547 val = 0x05; | |
548 if (mod_mask & V8_MOD_V90) | |
549 val |= 0x20; | |
550 if (mod_mask & V8_MOD_V34) | |
551 val |= 0x40; | |
552 v8_put_byte(s, val); | |
553 | |
554 val = 0x10; | |
555 if (mod_mask & V8_MOD_V32) | |
556 val |= 0x01; | |
557 if (mod_mask & V8_MOD_V22) | |
558 val |= 0x02; | |
559 if (mod_mask & V8_MOD_V17) | |
560 val |= 0x04; | |
561 if (mod_mask & V8_MOD_V29) | |
562 val |= 0x40; | |
563 if (mod_mask & V8_MOD_V27TER) | |
564 val |= 0x80; | |
565 v8_put_byte(s, val); | |
566 | |
567 val = 0x10; | |
568 if (mod_mask & V8_MOD_V26TER) | |
569 val |= 0x01; | |
570 if (mod_mask & V8_MOD_V26BIS) | |
571 val |= 0x02; | |
572 if (mod_mask & V8_MOD_V23) | |
573 val |= 0x04; | |
574 if (mod_mask & V8_MOD_V23HALF) | |
575 val |= 0x40; | |
576 if (mod_mask & V8_MOD_V21) | |
577 val |= 0x80; | |
578 v8_put_byte(s, val); | |
579 | |
580 v8_put_byte(s, (0 << 5) | 0x07); | |
581 | |
582 v8_put_byte(s, (V8_PROTOCOL_LAPM_V42 << 5) | 0x0A); | |
583 | |
584 /* No cellular right now */ | |
585 v8_put_byte(s, (0 << 5) | 0x0D); | |
586 } | |
587 /*- End of function --------------------------------------------------------*/ | |
588 | |
589 static int select_modulation(int mask) | |
590 { | |
591 if (mask & V8_MOD_V90) | |
592 return V8_MOD_V90; | |
593 if (mask & V8_MOD_V34) | |
594 return V8_MOD_V34; | |
595 if (mask & V8_MOD_V32) | |
596 return V8_MOD_V32; | |
597 if (mask & V8_MOD_V23) | |
598 return V8_MOD_V23; | |
599 if (mask & V8_MOD_V21) | |
600 return V8_MOD_V21; | |
601 return V8_MOD_FAILED; | |
602 } | |
603 /*- End of function --------------------------------------------------------*/ | |
604 | |
605 int v8_tx(v8_state_t *s, int16_t *amp, int max_len) | |
606 { | |
607 int len; | |
608 | |
609 //span_log(&s->logging, SPAN_LOG_FLOW, "v8_tx state %d\n", s->state); | |
610 len = 0; | |
611 switch (s->state) | |
612 { | |
613 case V8_CI_ON: | |
614 case V8_CM_ON: | |
615 case V8_JM_ON: | |
616 case V8_CJ_ON: | |
617 len = fsk_tx(&s->v21tx, amp, max_len); | |
618 break; | |
619 case V8_CM_WAIT: | |
620 /* Send the ANSam tone */ | |
621 len = modem_connect_tones_tx(&s->ec_dis_tx, amp, max_len); | |
622 break; | |
623 } | |
624 return len; | |
625 } | |
626 /*- End of function --------------------------------------------------------*/ | |
627 | |
628 int v8_rx(v8_state_t *s, const int16_t *amp, int len) | |
629 { | |
630 int i; | |
631 int residual_samples; | |
632 v8_result_t result; | |
633 static const uint8_t preamble[20] = | |
634 { | |
635 /* 10 1's (0x3FF), then 10 bits of CI sync (0x001) */ | |
636 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 | |
637 }; | |
638 | |
639 //span_log(&s->logging, SPAN_LOG_FLOW, "v8_rx state %d\n", s->state); | |
640 residual_samples = 0; | |
641 switch (s->state) | |
642 { | |
643 case V8_WAIT_1S: | |
644 /* Wait 1 second before sending the first CI packet */ | |
645 if ((s->negotiation_timer -= len) > 0) | |
646 break; | |
647 s->state = V8_CI; | |
648 s->ci_count = 0; | |
649 modem_connect_tones_rx_init(&s->ec_dis_rx, MODEM_CONNECT_TONES_EC_DISABLE, NULL, NULL); | |
650 fsk_tx_init(&s->v21tx, &preset_fsk_specs[FSK_V21CH1], get_bit, s); | |
651 /* Fall through to the next state */ | |
652 case V8_CI: | |
653 residual_samples = modem_connect_tones_rx(&s->ec_dis_rx, amp, len); | |
654 /* Send 4 CI packets in a burst (the spec says at least 3) */ | |
655 for (i = 0; i < 4; i++) | |
656 { | |
657 /* 10 1's (0x3FF), then CI sync (0x001) */ | |
658 queue_write(&s->tx_queue, preamble, 20); | |
659 v8_put_byte(s, (V8_CALL_V_SERIES << 5) | 0x01); | |
660 } | |
661 s->state = V8_CI_ON; | |
662 break; | |
663 case V8_CI_ON: | |
664 residual_samples = modem_connect_tones_rx(&s->ec_dis_rx, amp, len); | |
665 if (queue_empty(&s->tx_queue)) | |
666 { | |
667 s->state = V8_CI_OFF; | |
668 s->ci_timer = ms_to_samples(500); | |
669 } | |
670 break; | |
671 case V8_CI_OFF: | |
672 residual_samples = modem_connect_tones_rx(&s->ec_dis_rx, amp, len); | |
673 /* Check if an ANSam tone has been detected */ | |
674 if (modem_connect_tones_rx_get(&s->ec_dis_rx)) | |
675 { | |
676 /* Set the Te interval. The spec. says 500ms is the minimum, | |
677 but gives reasons why 1 second is a better value. */ | |
678 s->ci_timer = ms_to_samples(1000); | |
679 s->state = V8_HEARD_ANSAM; | |
680 break; | |
681 } | |
682 if ((s->ci_timer -= len) <= 0) | |
683 { | |
684 if (++s->ci_count >= 10) | |
685 { | |
686 /* The spec says we should give up now. */ | |
687 s->state = V8_PARKED; | |
688 if (s->result_handler) | |
689 s->result_handler(s->result_handler_user_data, NULL); | |
690 } | |
691 else | |
692 { | |
693 /* Try again */ | |
694 s->state = V8_CI; | |
695 } | |
696 } | |
697 break; | |
698 case V8_HEARD_ANSAM: | |
699 /* We have heard the ANSam signal, but we still need to wait for the | |
700 end of the Te timeout period to comply with the spec. */ | |
701 if ((s->ci_timer -= len) <= 0) | |
702 { | |
703 v8_decode_init(s); | |
704 s->state = V8_CM_ON; | |
705 s->negotiation_timer = ms_to_samples(5000); | |
706 send_cm_jm(s, s->available_modulations); | |
707 } | |
708 break; | |
709 case V8_CM_ON: | |
710 residual_samples = fsk_rx(&s->v21rx, amp, len); | |
711 if (s->got_cm_jm) | |
712 { | |
713 /* Now JM has been detected we send CJ and wait for 75 ms | |
714 before finishing the V.8 analysis. */ | |
715 s->negotiated_modulation = select_modulation(s->far_end_modulations); | |
716 | |
717 queue_flush(&s->tx_queue); | |
718 for (i = 0; i < 9; i++) | |
719 v8_put_byte(s, 0); | |
720 s->state = V8_CJ_ON; | |
721 break; | |
722 } | |
723 if ((s->negotiation_timer -= len) <= 0) | |
724 { | |
725 /* Timeout */ | |
726 s->state = V8_PARKED; | |
727 if (s->result_handler) | |
728 s->result_handler(s->result_handler_user_data, NULL); | |
729 } | |
730 if (queue_empty(&s->tx_queue)) | |
731 { | |
732 /* Send CM again */ | |
733 send_cm_jm(s, s->available_modulations); | |
734 } | |
735 break; | |
736 case V8_CJ_ON: | |
737 residual_samples = fsk_rx(&s->v21rx, amp, len); | |
738 if (queue_empty(&s->tx_queue)) | |
739 { | |
740 s->negotiation_timer = ms_to_samples(75); | |
741 s->state = V8_SIGC; | |
742 } | |
743 break; | |
744 case V8_SIGC: | |
745 if ((s->negotiation_timer -= len) <= 0) | |
746 { | |
747 /* The V.8 negotiation has succeeded. */ | |
748 s->state = V8_PARKED; | |
749 if (s->result_handler) | |
750 { | |
751 result.call_function = s->call_function; | |
752 result.available_modulations = s->far_end_modulations; | |
753 result.negotiated_modulation = s->negotiated_modulation; | |
754 result.protocol = s->protocol; | |
755 result.pstn_access = s->pstn_access; | |
756 result.nsf_seen = s->nsf_seen; | |
757 result.pcm_modem_availability = s->pcm_modem_availability; | |
758 result.t66_seen = s->t66_seen; | |
759 s->result_handler(s->result_handler_user_data, &result); | |
760 } | |
761 } | |
762 break; | |
763 case V8_WAIT_200MS: | |
764 if ((s->negotiation_timer -= len) <= 0) | |
765 { | |
766 /* Send the ANSam tone */ | |
767 modem_connect_tones_tx_init(&s->ec_dis_tx, MODEM_CONNECT_TONES_EC_DISABLE_MOD); | |
768 | |
769 v8_decode_init(s); | |
770 s->state = V8_CM_WAIT; | |
771 s->negotiation_timer = ms_to_samples(5000); | |
772 } | |
773 break; | |
774 case V8_CM_WAIT: | |
775 residual_samples = fsk_rx(&s->v21rx, amp, len); | |
776 if (s->got_cm_jm) | |
777 { | |
778 /* Stop sending ANSam and send JM instead */ | |
779 fsk_tx_init(&s->v21tx, &preset_fsk_specs[FSK_V21CH2], get_bit, s); | |
780 /* Set the timeout for JM */ | |
781 s->negotiation_timer = ms_to_samples(5000); | |
782 s->state = V8_JM_ON; | |
783 s->common_modulations = s->available_modulations & s->far_end_modulations; | |
784 s->negotiated_modulation = select_modulation(s->common_modulations); | |
785 send_cm_jm(s, s->common_modulations); | |
786 break; | |
787 } | |
788 if ((s->negotiation_timer -= len) <= 0) | |
789 { | |
790 /* Timeout */ | |
791 s->state = V8_PARKED; | |
792 if (s->result_handler) | |
793 s->result_handler(s->result_handler_user_data, NULL); | |
794 } | |
795 break; | |
796 case V8_JM_ON: | |
797 residual_samples = fsk_rx(&s->v21rx, amp, len); | |
798 if (s->got_cj) | |
799 { | |
800 /* Stop sending JM, and wait 75 ms */ | |
801 s->negotiation_timer = ms_to_samples(75); | |
802 s->state = V8_SIGA; | |
803 break; | |
804 } | |
805 if ((s->negotiation_timer -= len) <= 0) | |
806 { | |
807 /* Timeout */ | |
808 s->state = V8_PARKED; | |
809 if (s->result_handler) | |
810 s->result_handler(s->result_handler_user_data, NULL); | |
811 break; | |
812 } | |
813 if (queue_empty(&s->tx_queue)) | |
814 { | |
815 /* Send JM */ | |
816 send_cm_jm(s, s->common_modulations); | |
817 } | |
818 break; | |
819 case V8_SIGA: | |
820 if ((s->negotiation_timer -= len) <= 0) | |
821 { | |
822 /* The V.8 negotiation has succeeded. */ | |
823 s->state = V8_PARKED; | |
824 if (s->result_handler) | |
825 { | |
826 result.call_function = s->call_function; | |
827 result.available_modulations = s->far_end_modulations; | |
828 result.negotiated_modulation = s->negotiated_modulation; | |
829 result.protocol = s->protocol; | |
830 result.pstn_access = s->pstn_access; | |
831 result.nsf_seen = s->nsf_seen; | |
832 result.pcm_modem_availability = s->pcm_modem_availability; | |
833 result.t66_seen = s->t66_seen; | |
834 s->result_handler(s->result_handler_user_data, &result); | |
835 } | |
836 } | |
837 break; | |
838 case V8_PARKED: | |
839 residual_samples = len; | |
840 break; | |
841 } | |
842 return residual_samples; | |
843 } | |
844 /*- End of function --------------------------------------------------------*/ | |
845 | |
846 v8_state_t *v8_init(v8_state_t *s, | |
847 int caller, | |
848 int available_modulations, | |
849 v8_result_handler_t *result_handler, | |
850 void *user_data) | |
851 { | |
852 memset(s, 0, sizeof(*s)); | |
853 s->caller = caller; | |
854 s->available_modulations = available_modulations; | |
855 s->result_handler = result_handler; | |
856 s->result_handler_user_data = user_data; | |
857 | |
858 s->ci_timer = 0; | |
859 if (s->caller) | |
860 { | |
861 s->state = V8_WAIT_1S; | |
862 s->negotiation_timer = ms_to_samples(1000); | |
863 } | |
864 else | |
865 { | |
866 s->state = V8_WAIT_200MS; | |
867 s->negotiation_timer = ms_to_samples(200); | |
868 } | |
869 if (queue_create(&s->tx_queue, 1024, 0)) | |
870 return NULL; | |
871 return s; | |
872 } | |
873 /*- End of function --------------------------------------------------------*/ | |
874 | |
875 int v8_release(v8_state_t *s) | |
876 { | |
877 return queue_delete(&s->tx_queue); | |
878 } | |
879 /*- End of function --------------------------------------------------------*/ | |
880 /*- End of file ------------------------------------------------------------*/ |