/*****************************************************************************\ ** ** ** 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(); }