Data-Over-Voice

An experimental feature to send and receive an identification over
voice channel.

If a party answers, the ID is transmitted some seconds afterwards.
The calling party listens 30 seconds after receiving an answer message
for the ID.

Add to your extension's settings file:

dov_ident  <id string without white spaces>
dov_log    /path/to/log/file
dov_type   pwm|pcm
dov_level  0|level

'pwm' survives analog transcoding.
'pcm' is fast and will almost not be recognised.
'level' can be used to alter default signal amplitude (100..30000).
This commit is contained in:
Andreas Eversberg 2014-06-28 09:24:14 +02:00
parent a1c8b8d89f
commit 034d3a9140
12 changed files with 894 additions and 7 deletions

View File

@ -186,7 +186,7 @@ lcr_SOURCES = \
$(MISDN_SOURCE) $(GSM_SOURCE) $(SS5_SOURCE) $(SIP_SOURCE) $(VOOTP_SOURCE) \
endpoint.cpp endpointapp.cpp \
appbridge.cpp apppbx.cpp route.c action.cpp action_efi.cpp action_vbox.cpp extension.c mail.c \
join.cpp joinpbx.cpp
join.cpp joinpbx.cpp dov.cpp
lcr_LDADD = $(LIBCRYPTO) $(MISDN_LIB) -lpthread $(GSM_LIB) $(SIP_LIB) $(VOOTP_LIB)

View File

@ -1970,6 +1970,16 @@ void EndpointAppPBX::port_connect(struct port_list *portlist, int message_type,
message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_AUDIOPATH);
message->param.audiopath = 1;
message_put(message);
if (e_ext.dov_ident[0]) {
message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_DOV_REQUEST);
SPRINT(message->param.dov.data, "%08x ", lcr_random);
SCAT(message->param.dov.data, e_ext.dov_ident);
message->param.dov.length = strlen((char *)message->param.dov.data);
message->param.dov.type = e_ext.dov_type;
message->param.dov.level = e_ext.dov_level;
dov_msg_write(&message->param, 1);
message_put(message);
}
} else if (!e_adminid) {
/* callback */
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) we have a callback, so we create a call with cbcaller: \"%s\".\n", ea_endpoint->ep_serial, e_cbcaller);
@ -2440,6 +2450,18 @@ void EndpointAppPBX::port_disable_dejitter(struct port_list *portlist, int messa
message_put(message);
}
/* port MESSAGE_DOV_INDICATION */
void EndpointAppPBX::port_dov_indication(struct port_list *portlist, int message_type, union parameter *param)
{
struct lcr_msg *message;
logmessage(message_type, param, portlist->port_id, DIRECTION_IN);
message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_DOV_INDICATION);
memcpy(&message->param, param, sizeof(union parameter));
message_put(message);
}
/* port MESSAGE_UPDATEBRIDGE */
void EndpointAppPBX::port_updatebridge(struct port_list *portlist, int message_type, union parameter *param)
@ -2665,6 +2687,11 @@ void EndpointAppPBX::ea_message_port(unsigned int port_id, int message_type, uni
port_vootp(portlist, message_type, param);
break;
/* PORT indivated Data-Over-Voice */
case MESSAGE_DOV_INDICATION:
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') indicates Data-Over-Voice.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
port_dov_indication(portlist, message_type, param);
break;
default:
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received a wrong message: %d\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id, message_type);
@ -2884,6 +2911,13 @@ void EndpointAppPBX::join_connect(struct port_list *portlist, int message_type,
message_put(message);
time(&now);
e_start = now;
/* if the remote answered, we listen to DOV message */
if (e_ext.dov_log[0]) {
message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_DOV_LISTEN);
message->param.dov.type = e_ext.dov_type;
message_put(message);
}
}
/* join MESSAGE_DISCONNECT MESSAGE_RELEASE */
@ -3202,6 +3236,43 @@ void EndpointAppPBX::join_disable_dejitter(struct port_list *portlist, int messa
}
}
/* join MESSAGE_DOV_INDICATION */
void EndpointAppPBX::join_dov_indication(struct port_list *portlist, int message_type, union parameter *param)
{
dov_msg_write(param, 0);
}
/* join MESSAGE_DOV_REQUEST */
void EndpointAppPBX::join_dov_request(struct port_list *portlist, int message_type, union parameter *param)
{
struct lcr_msg *message;
/* don't send DOV from estension to extension */
if (e_ext.number[0])
return;
while(portlist) {
message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_DOV_REQUEST);
memcpy(&message->param, param, sizeof(union parameter));
message_put(message);
logmessage(message_type, param, portlist->port_id, DIRECTION_OUT);
portlist = portlist->next;
}
}
/* join MESSAGE_DOV_LISTEN */
void EndpointAppPBX::join_dov_listen(struct port_list *portlist, int message_type, union parameter *param)
{
struct lcr_msg *message;
while(portlist) {
message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_DOV_LISTEN);
memcpy(&message->param, param, sizeof(union parameter));
message_put(message);
portlist = portlist->next;
}
}
/* JOIN sends messages to the endpoint
*/
void EndpointAppPBX::ea_message_join(unsigned int join_id, int message_type, union parameter *param)
@ -3379,6 +3450,24 @@ void EndpointAppPBX::ea_message_join(unsigned int join_id, int message_type, uni
join_disable_dejitter(portlist, message_type, param);
break;
/* JOIN sends a Data-Over-Voice message indication */
case MESSAGE_DOV_INDICATION:
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received Data-Over-Voice indication.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
join_dov_indication(portlist, message_type, param);
break;
/* JOIN sends a Data-Over-Voice message request */
case MESSAGE_DOV_REQUEST:
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received Data-Over-Voice request.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
join_dov_request(portlist, message_type, param);
break;
/* JOIN sends a Data-Over-Voice listen order */
case MESSAGE_DOV_LISTEN:
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received Data-Over-Voice listen order.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
join_dov_listen(portlist, message_type, param);
break;
default:
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received a wrong message: #%d\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id, message_type);
}
@ -4795,6 +4884,23 @@ void EndpointAppPBX::logmessage(int message_type, union parameter *param, unsign
end_trace();
break;
case MESSAGE_DOV_INDICATION:
case MESSAGE_DOV_REQUEST:
trace_header("Data-Over-Voice", dir);
if (dir == DIRECTION_OUT)
add_trace("to", NULL, "CH(%lu)", port_id);
if (dir == DIRECTION_IN)
add_trace("from", NULL, "CH(%lu)", port_id);
{
char dov_str[param->dov.length + 1];
memcpy(dov_str, param->dov.data, param->dov.length);
dov_str[param->dov.length] = '\0';
add_trace("string", NULL, "%s", dov_str);
}
add_trace("type", NULL, "%d", param->dov.type);
end_trace();
break;
default:
PERROR("EPOINT(%d) message not of correct type (%d)\n", ea_endpoint->ep_serial, message_type);
}
@ -4828,3 +4934,38 @@ void EndpointAppPBX::message_disconnect_port(struct port_list *portlist, int cau
logmessage(message->type, &message->param, portlist->port_id, DIRECTION_OUT);
}
void EndpointAppPBX::dov_msg_write(union parameter *param, int sent)
{
FILE *fp;
struct tm *tm;
time_t ti;
int __attribute__((__unused__)) rc;
/* no write, if no log file given */
if (!e_ext.dov_log[0])
return;
fp = fopen(e_ext.dov_log, "a");
if (!fp) {
PERROR("EPOINT(%d) failed to open Data-Over-Voice log file '%s'\n", ea_endpoint->ep_serial, e_ext.dov_log);
return;
}
ti = time(NULL);
tm = localtime(&ti);
fprintf(fp, "%02d.%02d.%02d %02d:%02d:%02d ", tm->tm_mday, tm->tm_mon+1, tm->tm_year%100, tm->tm_hour, tm->tm_min, tm->tm_sec);
if (sent) {
fprintf(fp, "sent [%s] ", numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international));
} else {
fprintf(fp, "received [%s] ", e_dialinginfo.id);
}
rc = fwrite(param->dov.data, param->dov.length, 1, fp);
fprintf(fp, "\n");
fclose(fp);
}

View File

@ -219,6 +219,7 @@ class EndpointAppPBX : public EndpointApp
void port_disable_dejitter(struct port_list *portlist, int message_type, union parameter *param);
void port_updatebridge(struct port_list *portlist, int message_type, union parameter *param);
void port_vootp(struct port_list *portlist, int message_type, union parameter *param);
void port_dov_indication(struct port_list *portlist, int message_type, union parameter *param);
void ea_message_join(unsigned int join_id, int message, union parameter *param);
void join_crypt(struct port_list *portlist, int message_type, union parameter *param);
void join_mISDNsignal(struct port_list *portlist, int message_type, union parameter *param);
@ -234,6 +235,9 @@ class EndpointAppPBX : public EndpointApp
void join_facility(struct port_list *portlist, int message_type, union parameter *param);
void join_dtmf(struct port_list *portlist, int message_type, union parameter *param);
void join_disable_dejitter(struct port_list *portlist, int message_type, union parameter *param);
void join_dov_indication(struct port_list *portlist, int message_type, union parameter *param);
void join_dov_request(struct port_list *portlist, int message_type, union parameter *param);
void join_dov_listen(struct port_list *portlist, int message_type, union parameter *param);
/* epoint */
void new_state(int state);
@ -372,6 +376,9 @@ class EndpointAppPBX : public EndpointApp
void message_disconnect_port(struct port_list *portlist, int cause, int location, const char *display);
void logmessage(int message_type, union parameter *param, unsigned int port_id, int dir);
void trace_header(const char *name, int direction);
/* DOV */
void dov_msg_write(union parameter *param, int sent);
};

612
dov.cpp Normal file
View File

@ -0,0 +1,612 @@
/*****************************************************************************\
** **
** 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();
}

View File

@ -759,6 +759,31 @@ int read_extension(struct extension *ext, char *num)
if (!strcmp(option,"otp-ident")) {
SCPY(ext->otp_ident, param);
PDEBUG(DEBUG_CONFIG, "otp-ident: %s\n",param);
} else
if (!strcmp(option,"dov_ident")) {
if (param[0]) {
SCPY(ext->dov_ident, param);
PDEBUG(DEBUG_CONFIG, "dov_ident string: %s\n",param);
}
} else
if (!strcmp(option,"dov_log")) {
if (param[0]) {
SCPY(ext->dov_log, param);
PDEBUG(DEBUG_CONFIG, "dov_log filename: %s\n",param);
}
} else
if (!strcmp(option,"dov_type")) {
if (!strcasecmp(param, "pcm"))
ext->dov_type = DOV_TYPE_PCM;
else
ext->dov_type = DOV_TYPE_PWM;
PDEBUG(DEBUG_CONFIG, "given dov type: %s\n", param);
} else
if (!strcmp(option,"dov_level")) {
if (atoi(param)) {
ext->dov_level = atoi(param);
PDEBUG(DEBUG_CONFIG, "dov_level: %s\n",param);
}
} else {
PERROR_RUNTIME("Error in %s (line %d): wrong option keyword %s.\n",filename,line,option);
}
@ -1166,6 +1191,18 @@ int write_extension(struct extension *ext, char *number)
}
fprintf(fp,"\n");
fprintf(fp,"# Identify to/from remove via Data-Over-Voice feature.\n");
fprintf(fp,"dov_ident %s\n", ext->dov_ident);
fprintf(fp,"dov_log %s\n", ext->dov_log);
switch(ext->dov_type) {
case DOV_TYPE_PWM:
fprintf(fp,"dov_type pwm\n");
break;
case DOV_TYPE_PCM:
fprintf(fp,"dov_type pcm\n");
break;
}
fprintf(fp,"dov_level %d\n\n", ext->dov_level);
if (fp) fclose(fp);
return(1);

View File

@ -171,6 +171,10 @@ struct extension {
int no_seconds; /* don't include seconds in the connect message */
char otp_ident[9]; /* up to 8 bytes of ident */
char dov_ident[256]; /* ident string to be sent to remote via Data-Over-Voice */
char dov_log[256]; /* log file to store received and sent Data-Over-Voice messages */
int dov_type; /* type of modulation */
int dov_level; /* amplitude of signal */
};
int read_extension(struct extension *ext, char *number);

View File

@ -965,7 +965,7 @@ void PmISDN::load_tx(void)
p_m_load = 0;
/* to send data, tone must be on */
if ((p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on) /* what tones? */
if ((p_tone_name[0] || p_dov_tx || p_m_crypt_msg_loops || p_m_inband_send_on) /* what tones? */
&& (p_m_load < p_m_preload) /* not too much load? */
&& (p_state==PORT_STATE_CONNECT || p_m_mISDNport->tones || p_m_inband_send_on)) { /* connected or inband-tones? */
int tosend = p_m_preload - p_m_load, length;
@ -1005,6 +1005,11 @@ void PmISDN::load_tx(void)
tosend -= length;
}
/* copy dov */
if (p_dov_tx) {
tosend -= dov_tx(p, tosend);
}
/* copy tones */
if (p_tone_name[0] && tosend) {
tosend -= read_audio(p, tosend);
@ -1142,6 +1147,10 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
return;
}
/* dov is processed */
if (p_dov_rx)
dov_rx(data, len);
/* inband is processed */
if (p_m_inband_receive_on)
inband_receive(data, len);
@ -1500,7 +1509,7 @@ void PmISDN::update_rxoff(void)
int tx_dejitter = 0;
/* call bridges in user space OR crypto OR recording */
if (p_bridge || p_m_crypt_msg_loops || p_m_crypt_listen || p_record || p_tap || p_m_inband_receive_on) {
if (p_bridge || p_m_crypt_msg_loops || p_m_crypt_listen || p_record || p_tap || p_m_inband_receive_on || p_dov_rx) {
/* rx IS required */
if (p_m_rxoff) {
/* turn on RX */
@ -2313,7 +2322,7 @@ int PmISDN::bridge_rx(unsigned char *data, int length)
/* check if high priority tones exist
* ignore data in this case
*/
if (p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on)
if (p_tone_name[0] || p_dov_tx || p_m_crypt_msg_loops || p_m_inband_send_on)
return -EBUSY;
/* preload procedure

View File

@ -357,6 +357,13 @@ struct param_vootp {
char id[32];
};
struct param_dov {
int type; /* dov_type coding */
int level; /* volume of sending signals */
int length;
unsigned char data[255];
};
/* structure of message parameter */
union parameter {
struct param_tone tone; /* MESSAGE_TONE */
@ -384,6 +391,7 @@ union parameter {
unsigned int bridge_id; /* MESSAGE_BRIDGE */
struct param_traffic traffic; /* MESSAGE_TRAFFIC */
struct param_3pty threepty; /* MESSAGE_TRAFFIC */
struct param_dov dov; /* MESSAGE_DOV */
unsigned int queue; /* MESSAGE_DISABLE_DEJITTER */
struct param_vootp vootp; /* MESSAGE_VOOTP */
};
@ -445,6 +453,9 @@ enum { /* messages between entities */
MESSAGE_DISABLE_DEJITTER,/* tell (mISDN) port not to dejitter */
MESSAGE_UPDATEBRIDGE, /* tell join to update bridge. (sent by mISDN port) */
MESSAGE_VOOTP, /* enable/disable VoOTP */
MESSAGE_DOV_INDICATION, /* data over voice message received */
MESSAGE_DOV_REQUEST, /* sending data over voice message */
MESSAGE_DOV_LISTEN, /* listen order to data over voice message */
};
#define MESSAGES static const char *messages_txt[] = { \
@ -486,6 +497,9 @@ enum { /* messages between entities */
"MESSAGE_DISABLE_DEJITTER", \
"MESSAGE_UPDATEBRIDGE", \
"MESSAGE_VOOTP", \
"MESSAGE_DOV_INDIVATION", \
"MESSAGE_DOV_REQUEST", \
"MESSAGE_DOV_LISTEN", \
};

View File

@ -192,6 +192,8 @@ Port::Port(int type, const char *portname, struct port_settings *settings, struc
#ifdef WITH_VOOTP
p_vootp = NULL;
#endif
/* D-O-V */
dov_init();
/* append port to chain */
next = NULL;
@ -234,6 +236,8 @@ Port::~Port(void)
if (p_record)
close_record(0, 0);
dov_exit();
classuse--;
/* disconnect port from endpoint */
@ -660,6 +664,16 @@ int Port::message_epoint(unsigned int epoint_id, int message_id, union parameter
set_vootp(&param->vootp);
return 1;
#endif
case MESSAGE_DOV_REQUEST: /* Data-Over-Voice message */
PDEBUG(DEBUG_PORT, "PORT(%s) sending data over voice message (len=%d)\n", p_name, param->dov.length);
dov_sendmsg(param->dov.data, param->dov.length, (enum dov_type)param->dov.type, param->dov.level);
return 1;
case MESSAGE_DOV_LISTEN: /* Data-Over-Voice listen order */
PDEBUG(DEBUG_PORT, "PORT(%s) sending data over voice listen order\n", p_name);
dov_listen((enum dov_type)param->dov.type);
return 1;
}
return 0;

35
port.h
View File

@ -182,6 +182,11 @@ struct port_bridge {
extern struct port_bridge *p_bridge_first;
enum dov_type {
DOV_TYPE_PWM,
DOV_TYPE_PCM,
};
/* generic port class */
class Port
{
@ -276,6 +281,36 @@ class Port
void set_vootp(struct param_vootp *vootp);
#endif
/* DOV */
int p_dov_tx, p_dov_rx;
int p_dov_tx_sync, p_dov_rx_sync;
enum dov_type p_dov_tx_type, p_dov_rx_type;
unsigned char *p_dov_tx_data, *p_dov_rx_data;
int p_dov_tx_data_length;
int p_dov_tx_data_pos, p_dov_rx_data_pos;
int p_dov_tx_bit_pos, p_dov_rx_bit_pos;
int p_dov_tx_pwm_pos, p_dov_rx_pwm_pos;
int p_dov_rx_pwm_duration, p_dov_rx_pwm_polarity;
int p_dov_tx_up;
int p_dov_rx_sync_word;
unsigned char p_dov_up;
unsigned char p_dov_down;
void dov_init(void);
void dov_exit(void);
void dov_reset_tx(void);
void dov_reset_rx(void);
struct lcr_timer p_dov_tx_timer;
struct lcr_timer p_dov_rx_timer;
void dov_sendmsg(unsigned char *data, int length, enum dov_type type, int level);
int dov_tx(unsigned char *data, int length);
int dov_tx_pcm(unsigned char *data, int length);
int dov_tx_pwm(unsigned char *data, int length);
void dov_listen(enum dov_type type);
void dov_rx(unsigned char *data, int length);
void dov_rx_pcm(unsigned char *data, int length);
void dov_rx_pwm(unsigned char *data, int length);
void dov_message(unsigned char *data, int length);
void free_epointlist(struct epoint_list *epointlist);
void free_epointid(unsigned int epoint_id);
struct epoint_list *epointlist_new(unsigned int epoint_id);

View File

@ -140,6 +140,8 @@ void Premote::message_remote(int message_type, union parameter *param)
switch (message_type) {
case MESSAGE_TRAFFIC:
if (p_dov_rx)
dov_rx(param->traffic.data, param->traffic.len);
/* record audio */
if (p_record)
record(param->traffic.data, param->traffic.len, 0); // from down
@ -154,6 +156,10 @@ void Premote::message_remote(int message_type, union parameter *param)
if (p_tap)
tap(param->traffic.data, param->traffic.len, 1); // from up
admin_message_from_lcr(p_r_remote_id, p_r_ref, MESSAGE_TRAFFIC, param);
} else if (p_dov_tx) {
/* use receeived traffic to trigger sending DOV */
dov_tx(param->traffic.data, param->traffic.len);
admin_message_from_lcr(p_r_remote_id, p_r_ref, MESSAGE_TRAFFIC, param);
}
return;

14
sip.cpp
View File

@ -280,6 +280,8 @@ we only support alaw and ulaw!
}
while(n--)
*to++ = flip[*from++];
if (psip->p_dov_rx)
psip->dov_rx(payload, payload_len);
psip->bridge_tx(payload, payload_len);
return 0;
@ -600,7 +602,10 @@ int Psip::bridge_rx(unsigned char *data, int len)
int ret;
/* don't bridge, if tones are provided */
if (p_tone_name[0])
if (p_tone_name[0] || p_dov_tx)
return -EBUSY;
if (p_dov_tx)
return -EBUSY;
if ((ret = Port::bridge_rx(data, len)))
@ -2045,7 +2050,7 @@ void Psip::update_load(void)
return;
/* don't start timer if ... */
if (!p_tone_name[0])
if (!p_tone_name[0] && !p_dov_tx)
return;
p_s_next_tv_sec = 0;
@ -2057,7 +2062,7 @@ static int load_timer(struct lcr_timer *timer, void *instance, int index)
class Psip *psip = (class Psip *)instance;
/* stop timer if ... */
if (!psip->p_tone_name[0])
if (!psip->p_tone_name[0] && !psip->p_dov_tx)
return 0;
psip->load_tx();
@ -2111,6 +2116,9 @@ void Psip::load_tx(void)
/* copy tones */
if (p_tone_name[0]) {
tosend -= read_audio(p, tosend);
} else
if (p_dov_tx) {
tosend -= dov_tx(p, tosend);
}
if (tosend) {
PERROR("buffer is not completely filled\n");