Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/src/adsi.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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.6pre17/src/adsi.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,1108 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * adsi.c - Analogue display service interfaces of various types, including + * ADSI, TDD and most caller ID formats. + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2003 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: adsi.c,v 1.77 2009/11/02 13:25:20 steveu Exp $ + */ + +/*! \file */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#if defined(HAVE_TGMATH_H) +#include <tgmath.h> +#endif +#if defined(HAVE_MATH_H) +#include <math.h> +#endif +#include "floating_fudge.h" +#include <assert.h> + +#include "spandsp/telephony.h" +#include "spandsp/fast_convert.h" +#include "spandsp/logging.h" +#include "spandsp/queue.h" +#include "spandsp/complex.h" +#include "spandsp/dds.h" +#include "spandsp/power_meter.h" +#include "spandsp/async.h" +#include "spandsp/crc.h" +#include "spandsp/fsk.h" +#include "spandsp/tone_detect.h" +#include "spandsp/tone_generate.h" +#include "spandsp/super_tone_rx.h" +#include "spandsp/dtmf.h" +#include "spandsp/adsi.h" + +#include "spandsp/private/logging.h" +#include "spandsp/private/queue.h" +#include "spandsp/private/tone_generate.h" +#include "spandsp/private/async.h" +#include "spandsp/private/fsk.h" +#include "spandsp/private/dtmf.h" +#include "spandsp/private/adsi.h" + +/*! The baudot code to shift from alpha to digits and symbols */ +#define BAUDOT_FIGURE_SHIFT 0x1B +/*! The baudot code to shift from digits and symbols to alpha */ +#define BAUDOT_LETTER_SHIFT 0x1F + +enum +{ + SOH = 0x01, + STX = 0x02, + ETX = 0x03, + DLE = 0x10, + SUB = 0x1A +}; + +static uint16_t adsi_encode_baudot(adsi_tx_state_t *s, uint8_t ch); +static uint8_t adsi_decode_baudot(adsi_rx_state_t *s, uint8_t ch); + +static int adsi_tx_get_bit(void *user_data) +{ + int bit; + adsi_tx_state_t *s; + + s = (adsi_tx_state_t *) user_data; + /* This is similar to the async. handling code in fsk.c, but a few special + things are needed in the preamble, and postamble of an ADSI message. */ + if (s->bit_no < s->preamble_len) + { + /* Alternating bit preamble */ + bit = s->bit_no & 1; + s->bit_no++; + } + else if (s->bit_no < s->preamble_len + s->preamble_ones_len) + { + /* All 1s for receiver conditioning */ + /* NB: The receiver is an async one. It needs a rest after the + alternating 1/0 sequence so it can reliably pick up on + the next start bit, and sync to the byte stream. */ + /* The length of this period varies with the circumstance */ + bit = 1; + s->bit_no++; + } + else if (s->bit_no <= s->preamble_len + s->preamble_ones_len) + { + /* Push out the 8 bit async. chars, with an appropriate number of stop bits */ + if (s->bit_pos == 0) + { + /* Start bit */ + bit = 0; + s->bit_pos++; + } + else if (s->bit_pos < 1 + 8) + { + bit = (s->msg[s->byte_no] >> (s->bit_pos - 1)) & 1; + s->bit_pos++; + } + else if (s->bit_pos < 1 + 8 + s->stop_bits - 1) + { + /* Stop bit */ + bit = 1; + s->bit_pos++; + } + else + { + /* Stop bit */ + bit = 1; + s->bit_pos = 0; + if (++s->byte_no >= s->msg_len) + s->bit_no++; + } + } + else if (s->bit_no <= s->preamble_len + s->preamble_ones_len + s->postamble_ones_len) + { + /* Extra stop bits beyond the last character, to meet the specs., and ensure + all bits are out of the DSP before we shut off the FSK modem. */ + bit = 1; + s->bit_no++; + } + else + { + bit = SIG_STATUS_END_OF_DATA; + if (s->tx_signal_on) + { + /* The FSK should now be switched off. */ + s->tx_signal_on = FALSE; + s->msg_len = 0; + } + } + //printf("Tx bit %d\n", bit); + return bit; +} +/*- End of function --------------------------------------------------------*/ + +static int adsi_tdd_get_async_byte(void *user_data) +{ + adsi_tx_state_t *s; + + s = (adsi_tx_state_t *) user_data; + if (s->byte_no < s->msg_len) + return s->msg[s->byte_no++]; + if (s->tx_signal_on) + { + /* The FSK should now be switched off. */ + s->tx_signal_on = FALSE; + s->msg_len = 0; + } + return 0x1F; +} +/*- End of function --------------------------------------------------------*/ + +static void adsi_rx_put_bit(void *user_data, int bit) +{ + adsi_rx_state_t *s; + int i; + int sum; + + s = (adsi_rx_state_t *) user_data; + if (bit < 0) + { + /* Special conditions */ + span_log(&s->logging, SPAN_LOG_FLOW, "ADSI signal status is %s (%d)\n", signal_status_to_str(bit), bit); + switch (bit) + { + case SIG_STATUS_CARRIER_UP: + s->consecutive_ones = 0; + s->bit_pos = 0; + s->in_progress = 0; + s->msg_len = 0; + break; + case SIG_STATUS_CARRIER_DOWN: + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected special put bit value - %d!\n", bit); + break; + } + return; + } + bit &= 1; + if (s->bit_pos == 0) + { + if (bit == 0) + { + /* Start bit */ + s->bit_pos++; + if (s->consecutive_ones > 10) + { + /* This is a line idle condition, which means we should + restart message acquisition */ + s->msg_len = 0; + } + s->consecutive_ones = 0; + } + else + { + s->consecutive_ones++; + } + } + else if (s->bit_pos <= 8) + { + s->in_progress >>= 1; + if (bit) + s->in_progress |= 0x80; + s->bit_pos++; + } + else + { + /* Stop bit */ + if (bit) + { + if (s->msg_len < 256) + { + if (s->standard == ADSI_STANDARD_JCLIP) + { + if (s->msg_len == 0) + { + /* A message should start DLE SOH, but let's just check + we are starting with a DLE for now */ + if (s->in_progress == (0x80 | DLE)) + s->msg[s->msg_len++] = (uint8_t) s->in_progress; + } + else + { + s->msg[s->msg_len++] = (uint8_t) s->in_progress; + } + if (s->msg_len >= 11 && s->msg_len == ((s->msg[6] & 0x7F) + 11)) + { + /* Test the CRC-16 */ + if (crc_itu16_calc(s->msg + 2, s->msg_len - 2, 0) == 0) + { + /* Strip off the parity bits. It doesn't seem + worthwhile actually checking the parity if a + CRC check has succeeded. */ + for (i = 0; i < s->msg_len - 2; i++) + s->msg[i] &= 0x7F; + /* Put everything, except the CRC octets */ + s->put_msg(s->user_data, s->msg, s->msg_len - 2); + } + else + { + span_log(&s->logging, SPAN_LOG_WARNING, "CRC failed\n"); + } + s->msg_len = 0; + } + } + else + { + s->msg[s->msg_len++] = (uint8_t) s->in_progress; + if (s->msg_len >= 3 && s->msg_len == (s->msg[1] + 3)) + { + /* Test the checksum */ + sum = 0; + for (i = 0; i < s->msg_len - 1; i++) + sum += s->msg[i]; + if ((-sum & 0xFF) == s->msg[i]) + s->put_msg(s->user_data, s->msg, s->msg_len - 1); + else + span_log(&s->logging, SPAN_LOG_WARNING, "Sumcheck failed\n"); + s->msg_len = 0; + } + } + } + } + else + { + s->framing_errors++; + } + s->bit_pos = 0; + s->in_progress = 0; + } +} +/*- End of function --------------------------------------------------------*/ + +static void adsi_tdd_put_async_byte(void *user_data, int byte) +{ + adsi_rx_state_t *s; + uint8_t octet; + + s = (adsi_rx_state_t *) user_data; + //printf("Rx bit %x\n", bit); + if (byte < 0) + { + /* Special conditions */ + span_log(&s->logging, SPAN_LOG_FLOW, "ADSI signal status is %s (%d)\n", signal_status_to_str(byte), byte); + switch (byte) + { + case SIG_STATUS_CARRIER_UP: + s->consecutive_ones = 0; + s->bit_pos = 0; + s->in_progress = 0; + s->msg_len = 0; + break; + case SIG_STATUS_CARRIER_DOWN: + if (s->msg_len > 0) + { + /* Whatever we have to date constitutes the message */ + s->put_msg(s->user_data, s->msg, s->msg_len); + s->msg_len = 0; + } + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected special put byte value - %d!\n", byte); + break; + } + return; + } + if ((octet = adsi_decode_baudot(s, (uint8_t) (byte & 0x1F)))) + s->msg[s->msg_len++] = octet; + if (s->msg_len >= 256) + { + s->put_msg(s->user_data, s->msg, s->msg_len); + s->msg_len = 0; + } +} +/*- End of function --------------------------------------------------------*/ + +static void adsi_rx_dtmf(void *user_data, const char *digits, int len) +{ + adsi_rx_state_t *s; + + s = (adsi_rx_state_t *) user_data; + if (s->msg_len == 0) + { + /* Message starting. Start a 10s timeout, to make things more noise + tolerant for a detector running continuously when on hook. */ + s->in_progress = 80000; + } + /* It seems all the DTMF variants are a string of digits and letters, + terminated by a "#", or a "C". It appears these are unambiguous, and + non-conflicting. */ + for ( ; len && s->msg_len < 256; len--) + { + s->msg[s->msg_len++] = *digits; + if (*digits == '#' || *digits == 'C') + { + s->put_msg(s->user_data, s->msg, s->msg_len); + s->msg_len = 0; + } + digits++; + } +} +/*- End of function --------------------------------------------------------*/ + +static void start_tx(adsi_tx_state_t *s) +{ + switch (s->standard) + { + case ADSI_STANDARD_CLASS: + fsk_tx_init(&(s->fsktx), &preset_fsk_specs[FSK_BELL202], adsi_tx_get_bit, s); + break; + case ADSI_STANDARD_CLIP: + case ADSI_STANDARD_ACLIP: + case ADSI_STANDARD_JCLIP: + fsk_tx_init(&(s->fsktx), &preset_fsk_specs[FSK_V23CH1], adsi_tx_get_bit, s); + break; + case ADSI_STANDARD_CLIP_DTMF: + dtmf_tx_init(&(s->dtmftx)); + break; + case ADSI_STANDARD_TDD: + fsk_tx_init(&(s->fsktx), &preset_fsk_specs[FSK_WEITBRECHT], async_tx_get_bit, &(s->asynctx)); + async_tx_init(&(s->asynctx), 5, ASYNC_PARITY_NONE, 2, FALSE, adsi_tdd_get_async_byte, s); + /* Schedule an explicit shift at the start of baudot transmission */ + s->baudot_shift = 2; + break; + } + s->tx_signal_on = TRUE; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_rx(adsi_rx_state_t *s, const int16_t amp[], int len) +{ + switch (s->standard) + { + case ADSI_STANDARD_CLIP_DTMF: + /* Apply a message timeout. */ + s->in_progress -= len; + if (s->in_progress <= 0) + s->msg_len = 0; + dtmf_rx(&(s->dtmfrx), amp, len); + break; + default: + fsk_rx(&(s->fskrx), amp, len); + break; + } + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(adsi_rx_state_t *) adsi_rx_init(adsi_rx_state_t *s, + int standard, + put_msg_func_t put_msg, + void *user_data) +{ + if (s == NULL) + { + if ((s = (adsi_rx_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + memset(s, 0, sizeof(*s)); + s->put_msg = put_msg; + s->user_data = user_data; + switch (standard) + { + case ADSI_STANDARD_CLASS: + fsk_rx_init(&(s->fskrx), &preset_fsk_specs[FSK_BELL202], FSK_FRAME_MODE_ASYNC, adsi_rx_put_bit, s); + break; + case ADSI_STANDARD_CLIP: + case ADSI_STANDARD_ACLIP: + case ADSI_STANDARD_JCLIP: + fsk_rx_init(&(s->fskrx), &preset_fsk_specs[FSK_V23CH1], FSK_FRAME_MODE_ASYNC, adsi_rx_put_bit, s); + break; + case ADSI_STANDARD_CLIP_DTMF: + dtmf_rx_init(&(s->dtmfrx), adsi_rx_dtmf, s); + break; + case ADSI_STANDARD_TDD: + /* TDD uses 5 bit data, no parity and 1.5 stop bits. We scan for the first stop bit, and + ride over the fraction. */ + fsk_rx_init(&(s->fskrx), &preset_fsk_specs[FSK_WEITBRECHT], FSK_FRAME_MODE_5N1_FRAMES, adsi_tdd_put_async_byte, s); + break; + } + s->standard = standard; + span_log_init(&s->logging, SPAN_LOG_NONE, NULL); + return s; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_rx_release(adsi_rx_state_t *s) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_rx_free(adsi_rx_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_tx(adsi_tx_state_t *s, int16_t amp[], int max_len) +{ + int len; + int lenx; + + len = tone_gen(&(s->alert_tone_gen), amp, max_len); + if (s->tx_signal_on) + { + switch (s->standard) + { + case ADSI_STANDARD_CLIP_DTMF: + if (len < max_len) + len += dtmf_tx(&(s->dtmftx), amp, max_len - len); + break; + default: + if (len < max_len) + { + if ((lenx = fsk_tx(&(s->fsktx), amp + len, max_len - len)) <= 0) + s->tx_signal_on = FALSE; + len += lenx; + } + break; + } + } + return len; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) adsi_tx_send_alert_tone(adsi_tx_state_t *s) +{ + tone_gen_init(&(s->alert_tone_gen), &(s->alert_tone_desc)); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) adsi_tx_set_preamble(adsi_tx_state_t *s, + int preamble_len, + int preamble_ones_len, + int postamble_ones_len, + int stop_bits) +{ + if (preamble_len < 0) + { + if (s->standard == ADSI_STANDARD_JCLIP) + s->preamble_len = 0; + else + s->preamble_len = 300; + } + else + { + s->preamble_len = preamble_len; + } + if (preamble_ones_len < 0) + { + if (s->standard == ADSI_STANDARD_JCLIP) + s->preamble_ones_len = 75; + else + s->preamble_ones_len = 80; + } + else + { + s->preamble_ones_len = preamble_ones_len; + } + if (postamble_ones_len < 0) + { + if (s->standard == ADSI_STANDARD_JCLIP) + s->postamble_ones_len = 5; + else + s->postamble_ones_len = 5; + } + else + { + s->postamble_ones_len = postamble_ones_len; + } + if (stop_bits < 0) + { + if (s->standard == ADSI_STANDARD_JCLIP) + s->stop_bits = 4; + else + s->stop_bits = 1; + } + else + { + s->stop_bits = stop_bits; + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_tx_put_message(adsi_tx_state_t *s, const uint8_t *msg, int len) +{ + int i; + int j; + int k; + int byte; + int parity; + int sum; + size_t ii; + uint16_t crc_value; + + /* Don't inject a new message when a previous one is still in progress */ + if (s->msg_len > 0) + return 0; + if (!s->tx_signal_on) + { + /* We need to restart the modem */ + start_tx(s); + } + switch (s->standard) + { + case ADSI_STANDARD_CLIP_DTMF: + if (len >= 128) + return -1; + len -= (int) dtmf_tx_put(&(s->dtmftx), (char *) msg, len); + break; + case ADSI_STANDARD_JCLIP: + if (len > 128 - 9) + return -1; + i = 0; + s->msg[i++] = DLE; + s->msg[i++] = SOH; + s->msg[i++] = 0x07; //header + s->msg[i++] = DLE; + s->msg[i++] = STX; + s->msg[i++] = msg[0]; + s->msg[i++] = (uint8_t) (len - 2); + /* We might need to byte stuff the overall length, but the rest of the + message should already be stuffed. */ + if (len - 2 == DLE) + s->msg[i++] = DLE; + memcpy(&s->msg[i], &msg[2], len - 2); + i += len - 2; + s->msg[i++] = DLE; + s->msg[i++] = ETX; + + /* Set the parity bits */ + for (j = 0; j < i; j++) + { + byte = s->msg[j]; + parity = 0; + for (k = 1; k <= 7; k++) + parity ^= (byte << k); + s->msg[j] = (s->msg[j] & 0x7F) | ((uint8_t) parity & 0x80); + } + + crc_value = crc_itu16_calc(s->msg + 2, i - 2, 0); + s->msg[i++] = (uint8_t) (crc_value & 0xFF); + s->msg[i++] = (uint8_t) ((crc_value >> 8) & 0xFF); + s->msg_len = i; + break; + case ADSI_STANDARD_TDD: + if (len > 255) + return -1; + memcpy(s->msg, msg, len); + s->msg_len = len; + break; + default: + if (len > 255) + return -1; + memcpy(s->msg, msg, len); + /* Force the length in case it is wrong */ + s->msg[1] = (uint8_t) (len - 2); + /* Add the sumcheck */ + sum = 0; + for (ii = 0; ii < (size_t) len; ii++) + sum += s->msg[ii]; + s->msg[len] = (uint8_t) ((-sum) & 0xFF); + s->msg_len = len + 1; + break; + } + /* Prepare the bit sequencing */ + s->byte_no = 0; + s->bit_pos = 0; + s->bit_no = 0; + return len; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(adsi_tx_state_t *) adsi_tx_init(adsi_tx_state_t *s, int standard) +{ + if (s == NULL) + { + if ((s = (adsi_tx_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + memset(s, 0, sizeof(*s)); + make_tone_gen_descriptor(&(s->alert_tone_desc), + 2130, + -13, + 2750, + -13, + 110, + 60, + 0, + 0, + FALSE); + s->standard = standard; + adsi_tx_set_preamble(s, -1, -1, -1, -1); + span_log_init(&s->logging, SPAN_LOG_NONE, NULL); + start_tx(s); + return s; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_tx_release(adsi_tx_state_t *s) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_tx_free(adsi_tx_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static uint16_t adsi_encode_baudot(adsi_tx_state_t *s, uint8_t ch) +{ + static const uint8_t conv[128] = + { + 0x00, /* NUL */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0x42, /* LF */ + 0xFF, /* */ + 0xFF, /* */ + 0x48, /* CR */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0xFF, /* */ + 0x44, /* */ + 0xFF, /* ! */ + 0xFF, /* " */ + 0x94, /* # */ + 0x89, /* $ */ + 0xFF, /* % */ + 0xFF, /* & */ + 0x85, /* ' */ + 0x8F, /* ( */ + 0x92, /* ) */ + 0x8B, /* * */ + 0x91, /* + */ + 0x8C, /* , */ + 0x83, /* - */ + 0x9C, /* . */ + 0x9D, /* / */ + 0x96, /* 0 */ + 0x97, /* 1 */ + 0x93, /* 2 */ + 0x81, /* 3 */ + 0x8A, /* 4 */ + 0x90, /* 5 */ + 0x95, /* 6 */ + 0x87, /* 7 */ + 0x86, /* 8 */ + 0x98, /* 9 */ + 0x8E, /* : */ + 0xFF, /* ; */ + 0xFF, /* < */ + 0x9E, /* = */ + 0xFF, /* > */ + 0x99, /* ? */ + 0xFF, /* @ */ + 0x03, /* A */ + 0x19, /* B */ + 0x0E, /* C */ + 0x09, /* D */ + 0x01, /* E */ + 0x0D, /* F */ + 0x1A, /* G */ + 0x14, /* H */ + 0x06, /* I */ + 0x0B, /* J */ + 0x0F, /* K */ + 0x12, /* L */ + 0x1C, /* M */ + 0x0C, /* N */ + 0x18, /* O */ + 0x16, /* P */ + 0x17, /* Q */ + 0x0A, /* R */ + 0x05, /* S */ + 0x10, /* T */ + 0x07, /* U */ + 0x1E, /* V */ + 0x13, /* W */ + 0x1D, /* X */ + 0x15, /* Y */ + 0x11, /* Z */ + 0xFF, /* [ */ + 0xFF, /* \ */ + 0xFF, /* ] */ + 0x9B, /* ^ */ + 0xFF, /* _ */ + 0xFF, /* ` */ + 0x03, /* a */ + 0x19, /* b */ + 0x0E, /* c */ + 0x09, /* d */ + 0x01, /* e */ + 0x0D, /* f */ + 0x1A, /* g */ + 0x14, /* h */ + 0x06, /* i */ + 0x0B, /* j */ + 0x0F, /* k */ + 0x12, /* l */ + 0x1C, /* m */ + 0x0C, /* n */ + 0x18, /* o */ + 0x16, /* p */ + 0x17, /* q */ + 0x0A, /* r */ + 0x05, /* s */ + 0x10, /* t */ + 0x07, /* u */ + 0x1E, /* v */ + 0x13, /* w */ + 0x1D, /* x */ + 0x15, /* y */ + 0x11, /* z */ + 0xFF, /* { */ + 0xFF, /* | */ + 0xFF, /* } */ + 0xFF, /* ~ */ + 0xFF, /* DEL */ + }; + uint16_t shift; + + ch = conv[ch]; + if (ch == 0xFF) + return 0; + if ((ch & 0x40)) + return ch & 0x1F; + if ((ch & 0x80)) + { + if (s->baudot_shift == 1) + return ch & 0x1F; + s->baudot_shift = 1; + shift = BAUDOT_FIGURE_SHIFT; + } + else + { + if (s->baudot_shift == 0) + return ch & 0x1F; + s->baudot_shift = 0; + shift = BAUDOT_LETTER_SHIFT; + } + return (shift << 5) | (ch & 0x1F); +} +/*- End of function --------------------------------------------------------*/ + +static uint8_t adsi_decode_baudot(adsi_rx_state_t *s, uint8_t ch) +{ + static const uint8_t conv[2][32] = + { + {"\000E\nA SIU\rDRJNFCKTZLWHYPQOBG^MXV^"}, + {"\0003\n- '87\r$4*,*:(5+)2#6019?*^./=^"} + }; + + switch (ch) + { + case BAUDOT_FIGURE_SHIFT: + s->baudot_shift = 1; + break; + case BAUDOT_LETTER_SHIFT: + s->baudot_shift = 0; + break; + default: + return conv[s->baudot_shift][ch]; + } + /* return 0 if we did not produce a character */ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_next_field(adsi_rx_state_t *s, const uint8_t *msg, int msg_len, int pos, uint8_t *field_type, uint8_t const **field_body, int *field_len) +{ + int i; + + /* Return -1 for no more fields. Return -2 for message structure corrupt. */ + switch (s->standard) + { + case ADSI_STANDARD_CLASS: + case ADSI_STANDARD_CLIP: + case ADSI_STANDARD_ACLIP: + if (pos >= msg_len) + return -1; + /* For MDMF type messages, these standards all use "IE" type fields - type, + length, contents - and similar headers */ + if (pos <= 0) + { + /* Return the message type */ + *field_type = msg[0]; + *field_len = 0; + *field_body = NULL; + pos = 2; + } + else + { + if ((msg[0] & 0x80)) + { + /* MDMF messages seem to always have a message type with the MSB set. Is that + guaranteed? */ + *field_type = msg[pos++]; + *field_len = msg[pos++]; + *field_body = msg + pos; + } + else + { + /* SDMF */ + *field_type = 0; + *field_len = msg_len - pos; + *field_body = msg + pos; + } + pos += *field_len; + } + if (pos > msg_len) + return -2; + break; + case ADSI_STANDARD_JCLIP: + if (pos >= msg_len - 2) + return -1; + if (pos <= 0) + { + /* Return the message type */ + pos = 5; + *field_type = msg[pos++]; + if (*field_type == DLE) + pos++; + if (msg[pos++] == DLE) + pos++; + *field_len = 0; + *field_body = NULL; + } + else + { + *field_type = msg[pos++]; + if (*field_type == DLE) + pos++; + *field_len = msg[pos++]; + if (*field_len == DLE) + pos++; + /* TODO: we assume here that the body contains no DLE's that would have been stuffed */ + *field_body = msg + pos; + pos += *field_len; + } + if (pos > msg_len - 2) + return -2; + break; + case ADSI_STANDARD_CLIP_DTMF: + if (pos > msg_len) + return -1; + if (pos <= 0) + { + pos = 1; + *field_type = msg[msg_len - 1]; + *field_len = 0; + *field_body = NULL; + } + else + { + /* Remove bias on the pos value */ + pos--; + if (msg[pos] >= '0' && msg[pos] <= '9') + *field_type = CLIP_DTMF_HASH_UNSPECIFIED; + else + *field_type = msg[pos++]; + *field_body = msg + pos; + i = pos; + while (i < msg_len && msg[i] >= '0' && msg[i] <= '9') + i++; + *field_len = i - pos; + pos = i; + /* Check if we have reached the end of message marker. */ + if (msg[pos] == '#' || msg[pos] == 'C') + pos++; + if (pos > msg_len) + return -2; + /* Bias the pos value, so we don't return 0 inappropriately */ + pos++; + } + break; + case ADSI_STANDARD_TDD: + if (pos >= msg_len) + return -1; + *field_type = 0; + *field_body = msg; + *field_len = msg_len; + pos = msg_len; + break; + } + return pos; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) adsi_add_field(adsi_tx_state_t *s, uint8_t *msg, int len, uint8_t field_type, uint8_t const *field_body, int field_len) +{ + int i; + int x; + + switch (s->standard) + { + case ADSI_STANDARD_CLASS: + case ADSI_STANDARD_CLIP: + case ADSI_STANDARD_ACLIP: + /* These standards all use "IE" type fields - type, length, value - and similar headers */ + if (len <= 0) + { + /* Initialise a new message. The field type is actually the message type. */ + msg[0] = field_type; + msg[1] = 0; + len = 2; + } + else + { + /* Add to a message in progress. */ + if (field_type) + { + msg[len++] = field_type; + msg[len++] = (uint8_t) field_len; + if (field_len == DLE) + msg[len++] = (uint8_t) field_len; + memcpy(msg + len, field_body, field_len); + len += field_len; + } + else + { + /* No field type or length, for restricted single message formats */ + memcpy(msg + len, field_body, field_len); + len += field_len; + } + } + break; + case ADSI_STANDARD_JCLIP: + /* This standard uses "IE" type fields - type, length, value - but escapes DLE characters, + to prevent immitation of a control octet. */ + if (len <= 0) + { + /* Initialise a new message. The field type is actually the message type. */ + msg[0] = field_type; + msg[1] = 0; + len = 2; + } + else + { + /* Add to a message in progress. */ + msg[len++] = field_type; + if (field_type == DLE) + msg[len++] = field_type; + msg[len++] = (uint8_t) field_len; + if (field_len == DLE) + msg[len++] = (uint8_t) field_len; + for (i = 0; i < field_len; i++) + { + msg[len++] = field_body[i]; + if (field_body[i] == DLE) + msg[len++] = field_body[i]; + } + } + break; + case ADSI_STANDARD_CLIP_DTMF: + if (len <= 0) + { + /* Initialise a new message. The field type is actually the message type. */ + msg[0] = field_type; + len = 1; + } + else + { + /* Save and reuse the terminator/message type */ + x = msg[--len]; + if (field_type != CLIP_DTMF_HASH_UNSPECIFIED) + msg[len++] = field_type; + memcpy(msg + len, field_body, field_len); + msg[len + field_len] = (uint8_t) x; + len += (field_len + 1); + } + break; + case ADSI_STANDARD_TDD: + if (len < 0) + len = 0; + for (i = 0; i < field_len; i++) + { + if ((x = adsi_encode_baudot(s, field_body[i]))) + { + if ((x & 0x3E0)) + msg[len++] = (uint8_t) ((x >> 5) & 0x1F); + msg[len++] = (uint8_t) (x & 0x1F); + } + } + break; + } + + return len; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(const char *) adsi_standard_to_str(int standard) +{ + switch (standard) + { + case ADSI_STANDARD_CLASS: + return "CLASS"; + case ADSI_STANDARD_CLIP: + return "CLIP"; + case ADSI_STANDARD_ACLIP: + return "A-CLIP"; + case ADSI_STANDARD_JCLIP: + return "J-CLIP"; + case ADSI_STANDARD_CLIP_DTMF: + return "CLIP-DTMF"; + case ADSI_STANDARD_TDD: + return "TDD"; + } + return "???"; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/