Data-Over-Voice
authorAndreas Eversberg <jolly@eversberg.eu>
Sat, 28 Jun 2014 07:24:14 +0000 (09:24 +0200)
committerAndreas Eversberg <jolly@eversberg.eu>
Tue, 15 Dec 2015 13:27:23 +0000 (14:27 +0100)
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).

12 files changed:
Makefile.am
apppbx.cpp
apppbx.h
dov.cpp [new file with mode: 0644]
extension.c
extension.h
mISDN.cpp
message.h
port.cpp
port.h
remote.cpp
sip.cpp

index fd0cbea..cc4da0d 100644 (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 \
        $(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)
 
 
 lcr_LDADD = $(LIBCRYPTO) $(MISDN_LIB) -lpthread $(GSM_LIB) $(SIP_LIB) $(VOOTP_LIB)
 
index 7dfdf45..9752b87 100644 (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);
                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);
        } 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);
 }
 
        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)
 
 /* 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_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);
 
                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;
        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 */
 }
 
 /* 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)
 /* 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_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);
        }
                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;
 
                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);
        }
                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);
 }
 
        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);
+}
+
index aea434e..618d9ac 100644 (file)
--- a/apppbx.h
+++ b/apppbx.h
@@ -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_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);
        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_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);
 
        /* 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);
        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);
 };
 
 
 };
 
 
diff --git a/dov.cpp b/dov.cpp
new file mode 100644 (file)
index 0000000..9889357
--- /dev/null
+++ b/dov.cpp
@@ -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();
+}
+
index 7332551..72ca1aa 100644 (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);
                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);
                }
                } 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,"\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);
 
        if (fp) fclose(fp);
        return(1);
index 29f81b1..4de6952 100644 (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 */
        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);
 };
 
 int read_extension(struct extension *ext, char *number);
index bac0e84..a7facb6 100644 (file)
--- a/mISDN.cpp
+++ b/mISDN.cpp
@@ -965,7 +965,7 @@ void PmISDN::load_tx(void)
                        p_m_load = 0;
 
                /* to send data, tone must be on */
                        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; 
                 && (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;
                        }
 
                                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);
                        /* 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;
        }
 
                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);
        /* 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 */
        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 */
                /* 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
         */
        /* 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
                return -EBUSY;
 
        /* preload procedure
index 38ee5da..47952ac 100644 (file)
--- a/message.h
+++ b/message.h
@@ -357,6 +357,13 @@ struct param_vootp {
        char id[32];
 };
 
        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 */
 /* 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 */
        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 */
 };
        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_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[] = { \
 };
 
 #define MESSAGES static const char *messages_txt[] = { \
@@ -486,6 +497,9 @@ enum { /* messages between entities */
        "MESSAGE_DISABLE_DEJITTER", \
        "MESSAGE_UPDATEBRIDGE", \
        "MESSAGE_VOOTP", \
        "MESSAGE_DISABLE_DEJITTER", \
        "MESSAGE_UPDATEBRIDGE", \
        "MESSAGE_VOOTP", \
+       "MESSAGE_DOV_INDIVATION", \
+       "MESSAGE_DOV_REQUEST", \
+       "MESSAGE_DOV_LISTEN", \
 };
 
 
 };
 
 
index 16fcd72..32184a8 100644 (file)
--- a/port.cpp
+++ b/port.cpp
@@ -192,6 +192,8 @@ Port::Port(int type, const char *portname, struct port_settings *settings, struc
 #ifdef WITH_VOOTP
        p_vootp = NULL;
 #endif
 #ifdef WITH_VOOTP
        p_vootp = NULL;
 #endif
+       /* D-O-V */
+       dov_init();
 
        /* append port to chain */
        next = NULL;
 
        /* append port to chain */
        next = NULL;
@@ -234,6 +236,8 @@ Port::~Port(void)
        if (p_record)
                close_record(0, 0);
 
        if (p_record)
                close_record(0, 0);
 
+       dov_exit();
+
        classuse--;
 
        /* disconnect port from endpoint */
        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
                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;
        }
 
        return 0;
diff --git a/port.h b/port.h
index 56bd47b..cc7b071 100644 (file)
--- a/port.h
+++ b/port.h
@@ -182,6 +182,11 @@ struct port_bridge {
 
 extern struct port_bridge *p_bridge_first;
 
 
 extern struct port_bridge *p_bridge_first;
 
+enum dov_type {
+       DOV_TYPE_PWM,
+       DOV_TYPE_PCM,
+};
+
 /* generic port class */
 class Port
 {
 /* generic port class */
 class Port
 {
@@ -276,6 +281,36 @@ class Port
        void set_vootp(struct param_vootp *vootp);
 #endif
 
        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);
        void free_epointlist(struct epoint_list *epointlist);
        void free_epointid(unsigned int epoint_id);
        struct epoint_list *epointlist_new(unsigned int epoint_id);
index 273779c..cb6e528 100644 (file)
@@ -140,6 +140,8 @@ void Premote::message_remote(int message_type, union parameter *param)
 
        switch (message_type) {
        case MESSAGE_TRAFFIC:
 
        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
                /* 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);
                        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;
 
                }
                return;
 
diff --git a/sip.cpp b/sip.cpp
index 640631b..6dbb4b3 100644 (file)
--- a/sip.cpp
+++ b/sip.cpp
@@ -280,6 +280,8 @@ we only support alaw and ulaw!
        }
        while(n--)
                *to++ = flip[*from++];
        }
        while(n--)
                *to++ = flip[*from++];
+       if (psip->p_dov_rx)
+               psip->dov_rx(payload, payload_len);
        psip->bridge_tx(payload, payload_len);
 
        return 0;
        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 */
        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)))
                return -EBUSY;
 
        if ((ret = Port::bridge_rx(data, len)))
@@ -2045,7 +2050,7 @@ void Psip::update_load(void)
                return;
 
        /* don't start timer if ... */
                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;
                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 ... */
        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();
                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);
        /* 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");
        }
        if (tosend) {
                PERROR("buffer is not completely filled\n");