Added option to change DTMF decoding threshold level
[lcr.git] / mISDN.cpp
index 546b39f..bb50a58 100644 (file)
--- a/mISDN.cpp
+++ b/mISDN.cpp
@@ -117,12 +117,12 @@ void mISDN_deinitialize(void)
        upqueue_avail = 0;
 }
 
-int load_timer(struct lcr_timer *timer, void *instance, int index);
+static int load_timer(struct lcr_timer *timer, void *instance, int index);
 
 /*
  * constructor
  */
-PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode) : Port(type, portname, settings)
+PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_settings *settings, struct interface *interface, int channel, int exclusive, int mode) : Port(type, portname, settings, interface)
 {
        p_m_mISDNport = mISDNport;
        p_m_portnum = mISDNport->portnum;
@@ -147,6 +147,7 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti
        p_m_inband_send_on = 0;
        p_m_inband_receive_on = 0;
        p_m_dtmf = !mISDNport->ifport->nodtmf;
+       p_m_dtmf_threshold = mISDNport->ifport->dtmf_threshold;
        memset(&p_m_timeout, 0, sizeof(p_m_timeout));
        add_timer(&p_m_timeout, mISDN_timeout, this, 0);
        SCPY(p_m_pipeline, mISDNport->ifport->interface->pipeline);
@@ -348,15 +349,20 @@ void ph_control(struct mISDNport *mISDNport, class PmISDN *isdnport, int sock, u
        struct mISDNhead *ctrl = (struct mISDNhead *)buffer;
        unsigned int *d = (unsigned int *)(buffer+MISDN_HEADER_LEN);
        int ret;
+       int len = 8;
 
        if (sock < 0)
                return;
 
+       if (c1 == DTMF_TONE_START && c2 == 0) {
+               len = 4;
+       }
+
        ctrl->prim = PH_CONTROL_REQ;
        ctrl->id = 0;
        *d++ = c1;
        *d++ = c2;
-       ret = sendto(sock, buffer, MISDN_HEADER_LEN+sizeof(int)*2, 0, NULL, 0);
+       ret = sendto(sock, buffer, MISDN_HEADER_LEN+len, 0, NULL, 0);
        if (ret <= 0)
                PERROR("Failed to send to socket %d\n", sock);
        chan_trace_header(mISDNport, isdnport, "BCHANNEL control", DIRECTION_OUT);
@@ -507,7 +513,7 @@ static void _bchannel_configure(struct mISDNport *mISDNport, int i)
 //     if (port->p_m_txmix && mode == B_MODE_TRANSPARENT)
 //             ph_control(mISDNport, port, handle, DSP_MIX_ON, 0, "DSP-MIX", 1);
        if (port->p_m_dtmf && mode == B_MODE_TRANSPARENT)
-               ph_control(mISDNport, port, handle, DTMF_TONE_START, 0, "DSP-DTMF", 1);
+               ph_control(mISDNport, port, handle, DTMF_TONE_START, port->p_m_dtmf_threshold, "DSP-DTMF", 1);
        if (port->p_m_crypt && mode == B_MODE_TRANSPARENT)
                ph_control_block(mISDNport, port, handle, DSP_BF_ENABLE_KEY, port->p_m_crypt_key, port->p_m_crypt_key_len, "DSP-CRYPT", port->p_m_crypt_key_len);
 }
@@ -651,6 +657,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
                                _bchannel_configure(mISDNport, i);
                                state = B_STATE_ACTIVE;
                                b_port->p_m_load = 0;
+                               b_port->update_load();
                        } else {
                                /* bchannel is active, but not used anymore (or has wrong stack config), so we deactivate */
                                _bchannel_activate(mISDNport, i, 0, 0);
@@ -913,10 +920,6 @@ on empty load, remote-audio causes the load with the remote audio to be increase
  */
 void PmISDN::update_load(void)
 {
-       /* don't trigger load event if: */
-       if (!p_tone_name[0] && !p_m_crypt_msg_loops && !p_m_inband_send_on)
-               return;
-
        /* don't trigger load event if event already active */
        if (p_m_loadtimer.active)
                return;
@@ -924,7 +927,7 @@ void PmISDN::update_load(void)
        schedule_timer(&p_m_loadtimer, 0, 0); /* no delay the first time */
 }
 
-int load_timer(struct lcr_timer *timer, void *instance, int index)
+static int load_timer(struct lcr_timer *timer, void *instance, int index)
 {
        class PmISDN *isdnport = (class PmISDN *)instance;
 
@@ -950,7 +953,10 @@ void PmISDN::load_tx(void)
        p_m_last_tv_msec = current_time.tv_usec/1000;
 
        /* process only if we have samples and we are active */
-       if (elapsed && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) {
+       if (p_m_mISDNport->b_state[p_m_b_index] != B_STATE_ACTIVE)
+               return;
+
+       if (elapsed) {
                /* update load */
                if (elapsed < p_m_load)
                        p_m_load -= elapsed;
@@ -1015,9 +1021,7 @@ void PmISDN::load_tx(void)
                }
        }
 
-       if (p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on || p_m_load) {
-               schedule_timer(&p_m_loadtimer, 0, PORT_TRANSMIT * 125);
-       }
+       schedule_timer(&p_m_loadtimer, 0, PORT_TRANSMIT * 125);
 }
 
 /* handle timeouts */
@@ -1059,10 +1063,23 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
                        end_trace();
                        if (!p_m_dtmf)
                                return;
-                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DTMF);
-                       message->param.dtmf = cont & DTMF_TONE_MASK;
-                       PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION  DTMF digit '%c'\n", p_name, message->param.dtmf);
-                       message_put(message);
+                       if (p_type == PORT_TYPE_POTS_FXS_IN && p_state == PORT_STATE_IN_OVERLAP) {
+                               class Pfxs *pfxs = (class Pfxs *)this;
+                               if (!pfxs->p_m_fxs_allow_dtmf) {
+                                       PDEBUG(DEBUG_PORT, "PmISDN(%s) DTMF for FXS currently disabled\n", p_name);
+                                       return;
+                               }
+                               SCCAT(p_dialinginfo.id, cont & DTMF_TONE_MASK);
+                               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_INFORMATION);
+                               message->param.information.id[0] = cont & DTMF_TONE_MASK;
+                               PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION  INFORMATION digit '%s'\n", p_name, message->param.information.id);
+                               message_put(message);
+                       } else {
+                               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DTMF);
+                               message->param.dtmf = cont & DTMF_TONE_MASK;
+                               PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION  DTMF digit '%c'\n", p_name, message->param.dtmf);
+                               message_put(message);
+                       }
                        return;
                }
                switch(cont) {
@@ -1115,6 +1132,8 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
 //             printf(".");fflush(stdout);return;
                if (p_record)
                        record(data, len, 1); // from up
+               if (p_tap)
+                       tap(data, len, 1); // from up
                return;
        }
        if (hh->prim != PH_DATA_IND && hh->prim != DL_DATA_IND) {
@@ -1155,6 +1174,8 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
        /* record data */
        if (p_record)
                record(data, len, 0); // from down
+       if (p_tap)
+               tap(data, len, 0); // from down
 
        /* randomize and listen to crypt message if enabled */
        if (p_m_crypt_listen) {
@@ -1398,7 +1419,6 @@ void PmISDN::message_crypt(unsigned int epoint_id, int message_id, union paramet
                p_m_crypt_msg_current = 0; /* reset */
                p_m_crypt_msg_loops = 6; /* enable */
                update_rxoff();
-               update_load();
 #if 0
                /* disable txmix, or we get corrupt data due to audio process */
                if (p_m_txmix && p_m_b_index>=0 && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) {
@@ -1452,7 +1472,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_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) {
                /* rx IS required */
                if (p_m_rxoff) {
                        /* turn on RX */
@@ -1473,8 +1493,8 @@ void PmISDN::update_rxoff(void)
                                        ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_RECEIVE_OFF, 0, "DSP-RXOFF", 1);
                }
        }
-       /* recording */
-       if (p_record) {
+       /* recording / tapping */
+       if (p_record || p_tap) {
                /* txdata IS required */
                if (!p_m_txdata) {
                        /* turn on RX */
@@ -1500,7 +1520,7 @@ void PmISDN::update_rxoff(void)
                tx_dejitter = 1;
        if (p_m_tx_dejitter != tx_dejitter) {
                p_m_tx_dejitter = tx_dejitter;
-               PDEBUG(DEBUG_BCHANNEL, "we change dejitter mode to delay=%d.\n", p_m_tx_dejitter);
+               PDEBUG(DEBUG_BCHANNEL, "we change dejitter mode to %s.\n", (p_m_tx_dejitter) ? "on" : "off");
                if (p_m_b_index > -1)
                        if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT)
                                ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_TX_DEJITTER, p_m_tx_dejitter, "DSP-TX_DEJITTER", p_m_tx_dejitter);
@@ -1776,6 +1796,43 @@ int mISDN_getportbyname(int sock, int cnt, char *portname)
        return (port);
 }
 
+/* handle frames from pots */
+static int pots_sock_callback(struct lcr_fd *fd, unsigned int what, void *instance, int i)
+{
+       struct mISDNport *mISDNport = (struct mISDNport *)instance;
+       unsigned char buffer[2048+MISDN_HEADER_LEN];
+       struct mISDNhead *hh = (struct mISDNhead *)buffer;
+       unsigned int cont;
+       int ret;
+
+       ret = recv(fd->fd, buffer, sizeof(buffer), 0);
+       if (ret < 0) {
+               PERROR("read error frame, errno %d\n", errno);
+               return 0;
+       }
+       if (ret < (int)MISDN_HEADER_LEN) {
+               PERROR("read short frame, got %d, expected %d\n", ret, (int)MISDN_HEADER_LEN);
+               return 0;
+       }
+       switch(hh->prim) {
+       case PH_CONTROL_IND:
+               cont = *((unsigned int *)(buffer + MISDN_HEADER_LEN));
+               /* l1-control is sent to LCR */
+               if (mISDNport->ntmode)
+                       stack2manager_fxs(mISDNport, cont);
+               else
+                       PERROR("FXO not supported!\n");
+               break;
+       case PH_ACTIVATE_REQ:
+               break;
+
+       default:
+               PERROR("child message not handled: prim(0x%x) socket(%d) msg->len(%d)\n", hh->prim, fd->fd, ret-MISDN_HEADER_LEN);
+       }
+
+       return 0;
+}
+
 /*
  * global function to add a new card (port)
  */
@@ -1844,32 +1901,29 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
                pri = 1;
                nt = 1;
        }
-#ifdef ISDN_P_FXS
-       if (devinfo.Dprotocols & (1 << ISDN_P_FXS)) {
+#ifdef ISDN_P_FXS_POTS
+       if (devinfo.Dprotocols & (1 << ISDN_P_FXO_POTS)) {
                pots = 1;
                te = 1;
        }
-#endif
-#ifdef ISDN_P_FXO
-       if (devinfo.Dprotocols & (1 << ISDN_P_FXO)) {
+       if (devinfo.Dprotocols & (1 << ISDN_P_FXS_POTS)) {
                pots = 1;
                nt = 1;
        }
 #endif
        if (force_nt && !nt) {
-               PERROR_RUNTIME("Port %d does not support NT-mode\n", port);
+               if (!pots)
+                       PERROR_RUNTIME("Port %d does not support NT-mode\n", port);
+               else
+                       PERROR_RUNTIME("Port %d does not support FXS-mode\n", port);
                return(NULL);
        }
        if (bri && pri) {
                PERROR_RUNTIME("Port %d supports BRI and PRI?? What kind of controller is that?. (Can't use this!)\n", port);
                return(NULL);
        }
-       if (pots && !bri && !pri) {
-               PERROR_RUNTIME("Port %d supports POTS, LCR does not!\n", port);
-               return(NULL);
-       }
-       if (!bri && !pri) {
-               PERROR_RUNTIME("Port %d does not support BRI nor PRI!\n", port);
+       if (!bri && !pri && !pots) {
+               PERROR_RUNTIME("Port %d does not support BRI nor PRI nor POTS!\n", port);
                return(NULL);
        }
        if (!nt && !te) {
@@ -1882,6 +1936,10 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
        /* if TE an NT is supported (and not forced to NT), turn off NT */
        if (te && nt)
                nt = 0;
+       if (pots && te) {
+               PERROR_RUNTIME("Port %d uses FXO-mode, but not supported by LCR!\n", port);
+               return(NULL);
+       }
 
        /* check for double use of port */
        if (nt) {
@@ -1962,40 +2020,85 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
        }
                
        /* allocate ressources of port */
-       protocol = (nt)?L3_PROTOCOL_DSS1_NET:L3_PROTOCOL_DSS1_USER;
-       prop = (1 << MISDN_FLG_L2_CLEAN);
-       if (ptp) // ptp forced
-              prop |= (1 << MISDN_FLG_PTP);
-       if (nt) // supports hold/retrieve on nt-mode
-              prop |= (1 << MISDN_FLG_NET_HOLD);
-       if (l1hold) // supports layer 1 hold
-              prop |= (1 << MISDN_FLG_L1_HOLD);
-       if (l2hold) // supports layer 2 hold
-              prop |= (1 << MISDN_FLG_L2_HOLD);
-       /* open layer 3 and init upqueue */
-       /* queue must be initializes, because l3-thread may send messages during open_layer3() */
-       mqueue_init(&mISDNport->upqueue);
-       mISDNport->ml3 = open_layer3(port, protocol, prop , do_layer3, mISDNport);
-       if (!mISDNport->ml3) {
-               mqueue_purge(&mISDNport->upqueue);
-               PERROR_RUNTIME("open_layer3() failed for port %d\n", port);
-               start_trace(port,
-                       ifport->interface,
-                       NULL,
-                       NULL,
-                       DIRECTION_NONE,
-                       CATEGORY_CH,
-                       0,
-                       "PORT (open failed)");
-               end_trace();
-               mISDNport_close(mISDNport);
-               return(NULL);
+       if (!pots) {
+               /* ISDN */
+               protocol = (nt)?L3_PROTOCOL_DSS1_NET:L3_PROTOCOL_DSS1_USER;
+               prop = (1 << MISDN_FLG_L2_CLEAN);
+               if (ptp) // ptp forced
+                      prop |= (1 << MISDN_FLG_PTP);
+               if (nt) // supports hold/retrieve on nt-mode
+                      prop |= (1 << MISDN_FLG_NET_HOLD);
+               if (l1hold) // supports layer 1 hold
+                      prop |= (1 << MISDN_FLG_L1_HOLD);
+               if (l2hold) // supports layer 2 hold
+                      prop |= (1 << MISDN_FLG_L2_HOLD);
+               /* open layer 3 and init upqueue */
+               /* queue must be initializes, because l3-thread may send messages during open_layer3() */
+               mqueue_init(&mISDNport->upqueue);
+               mISDNport->ml3 = open_layer3(port, protocol, prop , do_layer3, mISDNport);
+               if (!mISDNport->ml3) {
+                       mqueue_purge(&mISDNport->upqueue);
+                       PERROR_RUNTIME("open_layer3() failed for port %d\n", port);
+                       start_trace(port,
+                               ifport->interface,
+                               NULL,
+                               NULL,
+                               DIRECTION_NONE,
+                               CATEGORY_CH,
+                               0,
+                               "PORT (open failed)");
+                       end_trace();
+                       mISDNport_close(mISDNport);
+                       return(NULL);
+               }
+       } else {
+#ifdef ISDN_P_FXS_POTS
+               /* POTS */
+               int sock, ret;
+               struct sockaddr_mISDN addr;
+               struct mISDNhead act;
+
+               /* open socket */
+               /* queue must be initializes, because even pots interfaces are checked at mISDN_upqueue loop */
+               mqueue_init(&mISDNport->upqueue);
+               sock = socket(PF_ISDN, SOCK_DGRAM, (nt) ? ISDN_P_FXS_POTS : ISDN_P_FXO_POTS);
+               if (sock < 0) {
+                       PERROR_RUNTIME("Cannot open mISDN due to '%s'.\n", strerror(errno));
+                       return NULL;
+               }
+               /* bind socket to dchannel */
+               addr.family = AF_ISDN;
+               addr.dev = port;
+               addr.channel = 0;
+               ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
+               if (ret < 0) {
+                       PERROR_RUNTIME("Error: Failed to bind pots control channel\n");
+                       start_trace(port,
+                               ifport->interface,
+                               NULL,
+                               NULL,
+                               DIRECTION_NONE,
+                               CATEGORY_CH,
+                               0,
+                               "PORT (open failed)");
+                       end_trace();
+                       return(NULL);
+               }
+               act.prim = PH_ACTIVATE_REQ; 
+               act.id = 0;
+               ret = sendto(sock, &act, MISDN_HEADER_LEN, 0, NULL, 0);
+               if (ret <= 0)
+                       PERROR("Failed to send to socket %d\n", sock);
+               mISDNport->pots_sock.fd = sock;
+               register_fd(&mISDNport->pots_sock, LCR_FD_READ, pots_sock_callback, mISDNport, i);
+#endif
        }
 
        SCPY(mISDNport->name, devinfo.name);
        mISDNport->b_num = devinfo.nrbchan;
        mISDNport->portnum = port;
        mISDNport->ntmode = nt;
+       mISDNport->pots = pots;
        mISDNport->tespecial = ifport->tespecial;
        mISDNport->pri = pri;
        mISDNport->ptp = ptp;
@@ -2011,7 +2114,7 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
        }
 
        /* if ptp, pull up the link */
-       if (mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) {
+       if (!pots && mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) {
                mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL);
                l1l2l3_trace_header(mISDNport, NULL, L2_ESTABLISH_REQ, DIRECTION_OUT);
                add_trace("tei", NULL, "%d", 0);
@@ -2019,8 +2122,8 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
                schedule_timer(&mISDNport->l2establish, 5, 0); /* 5 seconds */
        }
 
-       /* for nt-mode ptmp the link is always up */
-       if (mISDNport->ntmode && !mISDNport->ptp)
+       /* for POTS or nt-mode ptmp the link is always up */
+       if (pots || (mISDNport->ntmode && !mISDNport->ptp))
                mISDNport->l2link = 1;
 
        PDEBUG(DEBUG_BCHANNEL, "using 'mISDN_dsp.o' module\n");
@@ -2033,7 +2136,10 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
                    CATEGORY_CH,
                    0,
                    "PORT (open)");
-       add_trace("mode", NULL, (mISDNport->ntmode)?"network":"terminal");
+       if (!pots)
+               add_trace("mode", NULL, (mISDNport->ntmode)?"network":"terminal");
+       else
+               add_trace("mode", NULL, (mISDNport->ntmode)?"FXS":"FXO");
        add_trace("channels", NULL, "%d", mISDNport->b_num);
        if (mISDNport->ss5)
                add_trace("ccitt#5", NULL, "enabled");
@@ -2128,6 +2234,12 @@ void mISDNport_close(struct mISDNport *mISDNport)
                close_layer3(mISDNport->ml3);
        }
 
+       /* close layer 1, if open */
+       if (mISDNport->pots_sock.fd) {
+               unregister_fd(&mISDNport->pots_sock);
+               close(mISDNport->pots_sock.fd);
+       }
+
        /* purge upqueue */
        mqueue_purge(&mISDNport->upqueue);
 
@@ -2174,8 +2286,11 @@ int PmISDN::bridge_rx(unsigned char *data, int length)
        /* preload procedure
         * if transmit buffer in DSP module is empty,
         * preload it to DSP_LOAD to prevent jitter gaps.
+        * 
+        * if load runs empty, preload again.
         */
-       if ((!p_bridge || p_m_disable_dejitter) && p_m_load == 0 && p_m_preload > 0) {
+       if (p_m_disable_dejitter && p_m_load == 0 && p_m_preload > 0) {
+//printf("preload=%d\n", p_m_preload);
                hh->prim = PH_DATA_REQ; 
                hh->id = 0;
                memset(buf+MISDN_HEADER_LEN, silence, p_m_preload);
@@ -2189,7 +2304,8 @@ int PmISDN::bridge_rx(unsigned char *data, int length)
        /* drop if load would exceed ISDN_MAXLOAD
         * this keeps the delay not too high
         */
-       if (p_m_load+length > (p_m_preload << 1))
+//printf("load=%d len=%d 2*preload=%d\n", p_m_load, length, p_m_preload << 1);
+       if (p_m_disable_dejitter && p_m_preload > 0 && p_m_load+length > (p_m_preload << 1))
                return -EINVAL;
 
        /* make and send frame */
@@ -2214,8 +2330,6 @@ void PmISDN::inband_send_on(void)
 {
        PDEBUG(DEBUG_PORT, "turning inband signalling send on.\n");
        p_m_inband_send_on = 1;
-       /* trigger inband transmit */
-       update_load();
 }
 
 void PmISDN::inband_send_off(void)