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