lcr/dov.cpp

613 lines
13 KiB
C++

/*****************************************************************************\
** **
** Linux-Call-Router **
** **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg **
** **
** data-over-voice **
** **
\*****************************************************************************/
/*
Protocol description:
PCM: A bit is defined as sample value. A 1 is positive level, a 0 negative.
The bit rate is 8000 Hz.
PWM: A bit is defined by a duration between polarity change of signal. 4
samples duration is 0, 12 samples duration is 1.
GGGGGGGGGGGGG....
0LLLLLLLL
0DDDDDDDD
0DDDDDDDD
....
0CCCCCCCC
0CCCCCCCC
0CCCCCCCC
0CCCCCCCC
GGGGGGGGGGGGG....
G=guard / sync sequnce (bit=1)
L=length information (lsb first)
D=data (lsb first)
C=CRC (lsb first, network byte order)
*/
#include "main.h"
//#define DEBUG_DOV
#define DOV_PWM_LEVEL 819
#define DOV_PCM_LEVEL 100
#define DOV_PCM_GUARD 400
#define DOV_PWM_GUARD 34
#define DOV_TX_SEND_DELAY 3, 0
#define DOV_RX_LISTEN_TIMEOUT 30, 0
static unsigned int dov_crc32_table[256];
inline unsigned int dov_crc_reflect(unsigned int ref, unsigned char ch)
{
unsigned int value = 0;
int i;
for (i = 1; i < ch + 1; i++) {
if ((ref & 1))
value |= 1 << (ch - i);
ref >>= 1;
}
return value;
}
/*
* initialize CRC table
*/
void dov_crc_init(void)
{
unsigned int ulPolynomial = 0x04c11db7;
int i, j;
for (i = 0; i < 256; i++) {
dov_crc32_table[i] = dov_crc_reflect(i, 8) << 24;
for (j = 0; j < 8; j++)
dov_crc32_table[i] = (dov_crc32_table[i] << 1) ^
(dov_crc32_table[i] & (1 << 31) ?
ulPolynomial : 0);
dov_crc32_table[i] =
dov_crc_reflect(dov_crc32_table[i], 32);
}
}
/*
* calculate CRC 32 of given data
*
* data: pointer to data
* length: length of data
* return: CRC 32
*/
unsigned int dov_crc32(unsigned char *data, int length)
{
unsigned int crc = 0xffffffff;
while (length--)
crc = (crc >> 8) ^ dov_crc32_table[(crc & 0xff) ^ *data++];
return crc ^ 0xffffffff;
}
int dov_tx_timer(struct lcr_timer *timer, void *instance, int index);
int dov_rx_timer(struct lcr_timer *timer, void *instance, int index);
void Port::dov_init(void)
{
#ifdef DEBUG_DOV
printf("DOV: init\n");
#endif
dov_crc_init();
p_dov_tx = 0;
p_dov_rx = 0;
p_dov_tx_data = NULL;
p_dov_rx_data = NULL;
memset(&p_dov_tx_timer, 0, sizeof(p_dov_tx_timer));
add_timer(&p_dov_tx_timer, dov_tx_timer, this, 0);
memset(&p_dov_rx_timer, 0, sizeof(p_dov_rx_timer));
add_timer(&p_dov_rx_timer, dov_rx_timer, this, 0);
}
void Port::dov_reset_tx(void)
{
#ifdef DEBUG_DOV
printf("DOV: reset TX\n");
#endif
if (p_dov_tx_data)
FREE(p_dov_tx_data, p_dov_tx_data_length);
p_dov_tx_data = NULL;
p_dov_tx = 0;
unsched_timer(&p_dov_tx_timer);
}
void Port::dov_reset_rx(void)
{
#ifdef DEBUG_DOV
printf("DOV: reset RX\n");
#endif
if (p_dov_rx_data)
FREE(p_dov_rx_data, 255 + 5);
p_dov_rx_data = NULL;
p_dov_rx = 0;
update_rxoff();
unsched_timer(&p_dov_rx_timer);
}
void Port::dov_exit(void)
{
#ifdef DEBUG_DOV
printf("DOV: exit\n");
#endif
dov_reset_tx();
del_timer(&p_dov_tx_timer);
dov_reset_rx();
del_timer(&p_dov_rx_timer);
}
void Port::dov_sendmsg(unsigned char *data, int length, enum dov_type type, int level)
{
unsigned int crc;
#ifdef DEBUG_DOV
printf("DOV: send message, start timer\n");
#endif
dov_reset_tx();
if (!length)
return;
p_dov_tx_data = (unsigned char *)MALLOC(length + 5);
p_dov_tx_data[0] = length;
memcpy(p_dov_tx_data + 1, data, length);
crc = dov_crc32(data, length);
p_dov_tx_data[length+1] = crc >> 24;
p_dov_tx_data[length+2] = crc >> 16;
p_dov_tx_data[length+3] = crc >> 8;
p_dov_tx_data[length+4] = crc;
p_dov_tx_data_length = length + 5;
p_dov_tx_data_pos = 0;
p_dov_tx_sync = 1;
p_dov_tx_bit_pos = 0;
p_dov_tx_pwm_pos = 0;
p_dov_tx_type = type;
if (level) {
p_dov_up = audio_s16_to_law[(level) & 0xffff];
p_dov_down = audio_s16_to_law[(-level) & 0xffff];
} else if (type == DOV_TYPE_PWM) {
p_dov_up = audio_s16_to_law[(DOV_PWM_LEVEL) & 0xffff];
p_dov_down = audio_s16_to_law[(-DOV_PWM_LEVEL) & 0xffff];
} else {
p_dov_up = audio_s16_to_law[(DOV_PCM_LEVEL) & 0xffff];
p_dov_down = audio_s16_to_law[(-DOV_PCM_LEVEL) & 0xffff];
}
schedule_timer(&p_dov_tx_timer, DOV_TX_SEND_DELAY);
}
int dov_tx_timer(struct lcr_timer *timer, void *instance, int index)
{
class Port *port = (class Port *)instance;
#ifdef DEBUG_DOV
printf("DOV: timer fires, now sending\n");
#endif
port->p_dov_tx = 1;
return 0;
}
int Port::dov_tx(unsigned char *data, int length)
{
int left = 0;
if (!p_dov_tx)
return 0;
switch (p_dov_tx_type) {
case DOV_TYPE_PWM:
#ifdef DEBUG_DOV
printf("DOV: prepare %d bytes of PWM data\n", length);
#endif
left = dov_tx_pwm(data, length);
break;
case DOV_TYPE_PCM:
#ifdef DEBUG_DOV
printf("DOV: prepare %d bytes of PCM data\n", length);
#endif
left = dov_tx_pcm(data, length);
break;
}
return length - left;
}
int Port::dov_tx_pwm(unsigned char *data, int length)
{
while (length) {
/* send sync / guard sequence */
if (p_dov_tx_sync) {
if (p_dov_tx_up) {
while (p_dov_tx_pwm_pos < 12) {
*data++ = p_dov_up;
p_dov_tx_pwm_pos++;
if (--length == 0)
return 0;
}
p_dov_tx_up = 0;
} else {
while (p_dov_tx_pwm_pos < 12) {
*data++ = p_dov_down;
p_dov_tx_pwm_pos++;
if (--length == 0)
return 0;
}
p_dov_tx_up = 1;
}
p_dov_tx_pwm_pos = 0;
if (++p_dov_tx_bit_pos == DOV_PWM_GUARD) {
#ifdef DEBUG_DOV
printf("DOV: TX, done with guard\n");
#endif
p_dov_tx_bit_pos = -1;
if (p_dov_tx_sync == 2) {
dov_reset_tx();
return length;
}
p_dov_tx_sync = 0;
}
continue;
}
/* send start of byte */
if (p_dov_tx_data_length == -1) {
if (p_dov_tx_up) {
while (p_dov_tx_pwm_pos < 4) {
*data++ = p_dov_up;
p_dov_tx_pwm_pos++;
if (--length == 0)
return 0;
}
p_dov_tx_up = 0;
} else {
while (p_dov_tx_pwm_pos < 4) {
*data++ = p_dov_down;
p_dov_tx_pwm_pos++;
if (--length == 0)
return 0;
}
p_dov_tx_up = 1;
}
p_dov_tx_pwm_pos = 0;
p_dov_tx_bit_pos = 0;
continue;
}
/* send data */
if ((p_dov_tx_data[p_dov_tx_data_pos] >> p_dov_tx_bit_pos) & 1) {
if (p_dov_tx_up) {
while (p_dov_tx_pwm_pos < 12) {
*data++ = p_dov_up;
p_dov_tx_pwm_pos++;
if (--length == 0)
return 0;
}
p_dov_tx_up = 0;
} else {
while (p_dov_tx_pwm_pos < 12) {
*data++ = p_dov_down;
p_dov_tx_pwm_pos++;
if (--length == 0)
return 0;
}
p_dov_tx_up = 1;
}
} else {
if (p_dov_tx_up) {
while (p_dov_tx_pwm_pos < 4) {
*data++ = p_dov_up;
p_dov_tx_pwm_pos++;
if (--length == 0)
return 0;
}
p_dov_tx_up = 0;
} else {
while (p_dov_tx_pwm_pos < 4) {
*data++ = p_dov_down;
p_dov_tx_pwm_pos++;
if (--length == 0)
return 0;
}
p_dov_tx_up = 1;
}
}
p_dov_tx_pwm_pos = 0;
if (++p_dov_tx_bit_pos == 8) {
p_dov_tx_bit_pos = -1;
#ifdef DEBUG_DOV
printf("DOV: TX, done with byte %d\n", p_dov_tx_data[p_dov_tx_data_pos]);
#endif
if (p_dov_tx_data_pos++ == p_dov_tx_data_length) {
p_dov_tx_sync = 2;
}
}
}
return 0;
}
int Port::dov_tx_pcm(unsigned char *data, int length)
{
while (length--) {
/* send sync / guard sequence */
if (p_dov_tx_sync) {
*data++ = p_dov_up;
if (++p_dov_tx_bit_pos == DOV_PCM_GUARD) {
#ifdef DEBUG_DOV
printf("DOV: TX, done with guard\n");
#endif
p_dov_tx_bit_pos = -1;
if (p_dov_tx_sync == 2) {
dov_reset_tx();
return length;
}
p_dov_tx_sync = 0;
}
continue;
}
/* send start of byte */
if (p_dov_tx_data_length == -1) {
*data++ = p_dov_down;
p_dov_tx_bit_pos = 0;
continue;
}
/* send data */
*data++ = (((p_dov_tx_data[p_dov_tx_data_pos] >> p_dov_tx_bit_pos) & 1)) ? p_dov_up : p_dov_down;
if (++p_dov_tx_bit_pos == 8) {
p_dov_tx_bit_pos = -1;
#ifdef DEBUG_DOV
printf("DOV: TX, done with byte %d\n", p_dov_tx_data[p_dov_tx_data_pos]);
#endif
if (p_dov_tx_data_pos++ == p_dov_tx_data_length) {
p_dov_tx_sync = 2;
}
}
}
return 0;
}
void Port::dov_listen(enum dov_type type)
{
#ifdef DEBUG_DOV
printf("DOV: start listening, start timer\n");
#endif
dov_reset_rx();
p_dov_rx_data = (unsigned char *)MALLOC(255 + 5);
p_dov_rx_data_pos = 0;
p_dov_rx_sync = 1;
p_dov_rx_bit_pos = 0;
p_dov_rx_pwm_pos = 0;
p_dov_rx_pwm_duration = 0;
p_dov_rx_pwm_polarity = 0;
p_dov_rx_sync_word = 0;
p_dov_rx_type = type;
p_dov_rx = 1;
update_rxoff();
schedule_timer(&p_dov_rx_timer, DOV_RX_LISTEN_TIMEOUT);
}
int dov_rx_timer(struct lcr_timer *timer, void *instance, int index)
{
class Port *port = (class Port *)instance;
#ifdef DEBUG_DOV
printf("DOV: timer fires, now stop listening\n");
#endif
port->dov_reset_rx();
return 0;
}
void Port::dov_rx(unsigned char *data, int length)
{
if (!p_dov_rx)
return;
switch (p_dov_rx_type) {
case DOV_TYPE_PWM:
#ifdef DEBUG_DOV
printf("DOV: received %d bytes of PWM data\n", length);
#endif
dov_rx_pwm(data, length);
break;
case DOV_TYPE_PCM:
#ifdef DEBUG_DOV
printf("DOV: received %d bytes of PCM data\n", length);
#endif
dov_rx_pcm(data, length);
break;
}
}
void Port::dov_rx_pwm(unsigned char *data, int length)
{
signed int sample;
signed int level;
while (length--) {
sample = audio_law_to_s32[*data++];
p_dov_rx_pwm_duration++;
if (p_dov_rx_pwm_polarity == 1) {
if (sample > 0)
continue;
p_dov_rx_pwm_polarity = 0;
if (p_dov_rx_pwm_duration < 8)
level = 0;
else
level = 1;
p_dov_rx_pwm_duration = 0;
} else {
if (sample <= 0)
continue;
p_dov_rx_pwm_polarity = 1;
if (p_dov_rx_pwm_duration < 8)
level = 0;
else
level = 1;
p_dov_rx_pwm_duration = 0;
}
/* catch sync */
p_dov_rx_sync_word <<= 1;
if (level > 0)
p_dov_rx_sync_word |= 1;
if ((p_dov_rx_sync_word & 0x1ff) == 0x1ff) {
p_dov_rx_bit_pos = -1;
p_dov_rx_sync = 1;
p_dov_rx_data_pos = 0;
continue;
}
/* wait for sync */
if (!p_dov_rx_sync) {
continue;
}
/* read start bit */
if (p_dov_rx_bit_pos == -1) {
/* check violation of start bit */
if (level > 0) {
p_dov_rx_sync = 0;
continue;
}
p_dov_rx_bit_pos = 0;
continue;
}
/* read data */
p_dov_rx_data[p_dov_rx_data_pos] >>= 1;
if (level > 0)
p_dov_rx_data[p_dov_rx_data_pos] |= 128;
if (++p_dov_rx_bit_pos == 8) {
#ifdef DEBUG_DOV
printf("DOV: RX byte %d\n", p_dov_rx_data[p_dov_rx_data_pos]);
#endif
p_dov_rx_bit_pos = -1;
/* check for length,data,crc32 */
if (++p_dov_rx_data_pos == p_dov_rx_data[0] + 5) {
dov_message(p_dov_rx_data + 1, p_dov_rx_data[0]);
p_dov_rx_sync = 0;
}
}
}
}
void Port::dov_rx_pcm(unsigned char *data, int length)
{
signed int level;
while (length--) {
level = audio_law_to_s32[*data++];
/* catch sync */
p_dov_rx_sync_word <<= 1;
if (level > 0)
p_dov_rx_sync_word |= 1;
if ((p_dov_rx_sync_word & 0x1ff) == 0x1ff) {
p_dov_rx_bit_pos = -1;
p_dov_rx_sync = 1;
p_dov_rx_data_pos = 0;
continue;
}
/* wait for sync */
if (!p_dov_rx_sync) {
continue;
}
/* read start bit */
if (p_dov_rx_bit_pos == -1) {
/* check violation of start bit */
if (level > 0) {
p_dov_rx_sync = 0;
continue;
}
p_dov_rx_bit_pos = 0;
continue;
}
/* read data */
p_dov_rx_data[p_dov_rx_data_pos] >>= 1;
if (level > 0)
p_dov_rx_data[p_dov_rx_data_pos] |= 128;
if (++p_dov_rx_bit_pos == 8) {
#ifdef DEBUG_DOV
printf("DOV: RX byte %d\n", p_dov_rx_data[p_dov_rx_data_pos]);
#endif
p_dov_rx_bit_pos = -1;
/* check for length,data,crc32 */
if (++p_dov_rx_data_pos == p_dov_rx_data[0] + 5) {
dov_message(p_dov_rx_data + 1, p_dov_rx_data[0]);
p_dov_rx_sync = 0;
}
}
}
}
void Port::dov_message(unsigned char *data, int length)
{
unsigned int crc;
struct lcr_msg *message;
/* prevent receiving zeroes (due to line noise). this would cause 0 crc, which seems correct. */
if (length == 0)
return;
#ifdef DEBUG_DOV
printf("DOV: received message\n");
#endif
crc = dov_crc32(p_dov_rx_data + 1, p_dov_rx_data[0]);
if (crc != (unsigned int) ( ((p_dov_rx_data[length+1]) << 24) |
((p_dov_rx_data[length+2]) << 16) |
((p_dov_rx_data[length+3]) << 8) |
(p_dov_rx_data[length+4]) ))
return;
#ifdef DEBUG_DOV
printf("DOV: crc OK\n");
#endif
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DOV_INDICATION);
message->param.dov.type = p_dov_rx_type;
message->param.dov.length = p_dov_rx_data[0];
memcpy(message->param.dov.data, p_dov_rx_data + 1, p_dov_rx_data[0]);
PDEBUG(DEBUG_PORT, "PmISDN(%s) Data-Over-Voice message received (len=%d)\n", p_name, message->param.dov.length);
message_put(message);
dov_reset_rx();
}