$(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)
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);
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_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);
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_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_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);
}
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);
}
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);
+}
+
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 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);
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);
};
--- /dev/null
+/*****************************************************************************\
+** **
+** 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();
+}
+
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);
}
}
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);
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);
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;
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);
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);
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 */
/* 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
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 */
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 */
};
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[] = { \
"MESSAGE_DISABLE_DEJITTER", \
"MESSAGE_UPDATEBRIDGE", \
"MESSAGE_VOOTP", \
+ "MESSAGE_DOV_INDIVATION", \
+ "MESSAGE_DOV_REQUEST", \
+ "MESSAGE_DOV_LISTEN", \
};
#ifdef WITH_VOOTP
p_vootp = NULL;
#endif
+ /* D-O-V */
+ dov_init();
/* append port to chain */
next = NULL;
if (p_record)
close_record(0, 0);
+ dov_exit();
+
classuse--;
/* disconnect port from endpoint */
set_vootp(¶m->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;
extern struct port_bridge *p_bridge_first;
+enum dov_type {
+ DOV_TYPE_PWM,
+ DOV_TYPE_PCM,
+};
+
/* generic port class */
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);
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
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;
}
while(n--)
*to++ = flip[*from++];
+ if (psip->p_dov_rx)
+ psip->dov_rx(payload, payload_len);
psip->bridge_tx(payload, payload_len);
return 0;
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;
/* 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;
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();
/* 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");