Data-Over-Voice
[lcr.git] / mISDN.cpp
index 0b4c8b0..a7facb6 100644 (file)
--- a/mISDN.cpp
+++ b/mISDN.cpp
 
 #include "main.h"
 #include "myisdn.h"
 
 #include "main.h"
 #include "myisdn.h"
-
-extern "C" {
-#define MISDN_OLD_AF_COMPATIBILITY 1
-#include <compat_af_isdn.h>
-}
-#include <q931.h>
+#include <mISDN/q931.h>
 
 #undef offsetof
 #ifdef __compiler_offsetof
 
 #undef offsetof
 #ifdef __compiler_offsetof
@@ -25,9 +20,11 @@ extern "C" {
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 #endif
 
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 #endif
 
+#ifndef container_of
 #define container_of(ptr, type, member) ({                     \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})
 #define container_of(ptr, type, member) ({                     \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
 
 // timeouts if activating/deactivating response from mISDN got lost
 #define B_TIMER_ACTIVATING 1 // seconds
 
 // timeouts if activating/deactivating response from mISDN got lost
 #define B_TIMER_ACTIVATING 1 // seconds
@@ -40,40 +37,62 @@ struct mISDNport *mISDNport_first;
 unsigned char mISDN_rand[256];
 int mISDN_rand_count = 0;
 
 unsigned char mISDN_rand[256];
 int mISDN_rand_count = 0;
 
+#ifdef OLD_MT_ASSIGN
 unsigned int mt_assign_pid = ~0;
 unsigned int mt_assign_pid = ~0;
+#endif
 
 int mISDNsocket = -1;
 
 int mISDNsocket = -1;
+static int upqueue_pipe[2];
+static struct lcr_fd upqueue_fd;
+int upqueue_avail = 0;
+
+static int mISDN_upqueue(struct lcr_fd *fd, unsigned int what, void *instance, int i);
+static int mISDN_timeout(struct lcr_timer *timer, void *instance, int i);
+
+static int my_mISDNlib_debug(const char *file, int line, const char *func, int level, const char *fmt, va_list va)
+{
+        int ret = 0;
+
+        if (debug_fp > 0)
+               ret = vfprintf(debug_fp, fmt, va);
+       return ret;
+}
+
+static struct mi_ext_fn_s myfn;
 
 int mISDN_initialize(void)
 {
        char filename[256];
 
 
 int mISDN_initialize(void)
 {
        char filename[256];
 
-       init_af_isdn();
-
        /* try to open raw socket to check kernel */
        mISDNsocket = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
        /* try to open raw socket to check kernel */
        mISDNsocket = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
-       if (mISDNsocket < 0)
-       {
-               fprintf(stderr, "Cannot open mISDN due to '%s'. (Does your Kernel support socket based mISDN?)\n", strerror(errno));
+       if (mISDNsocket < 0) {
+               fprintf(stderr, "Cannot open mISDN due to '%s'. (Does your Kernel support socket based mISDN? Protocol family is %d.)\n", strerror(errno), PF_ISDN);
                return(-1);
        }
 
        /* init mlayer3 */
                return(-1);
        }
 
        /* init mlayer3 */
-       init_layer3(4); // buffer of 4
+       // set debug printout function
+       myfn.prt_debug = my_mISDNlib_debug;
+
+       init_layer3(4, &myfn); // buffer of 4
 
        /* open debug, if enabled and not only stack debugging */
 
        /* open debug, if enabled and not only stack debugging */
-       if (options.deb)
-       {
-               SPRINT(filename, "%s/debug.log", INSTALL_DATA);
+       if (options.deb) {
+               SPRINT(filename, "%s/debug.log", LOG_DIR);
                debug_fp = fopen(filename, "a");
        }
 
        if (options.deb & DEBUG_STACK)
                debug_fp = fopen(filename, "a");
        }
 
        if (options.deb & DEBUG_STACK)
-       {
-               SPRINT(filename, "%s/debug_mISDN.log", INSTALL_DATA);
-               mISDN_debug_init(0xffffffff, filename, filename, filename);
-       } else
-               mISDN_debug_init(0, NULL, NULL, NULL);
+               mISDN_set_debug_level(0xfffffeff);
+       else
+               mISDN_set_debug_level(0);
+
+       if (pipe(upqueue_pipe) < 0)
+               FATAL("Failed to open pipe\n");
+       memset(&upqueue_fd, 0, sizeof(upqueue_fd));
+       upqueue_fd.fd = upqueue_pipe[0];
+       register_fd(&upqueue_fd, LCR_FD_READ, mISDN_upqueue, NULL, 0);
 
        return(0);
 }
 
        return(0);
 }
@@ -82,20 +101,27 @@ void mISDN_deinitialize(void)
 {
        cleanup_layer3();
 
 {
        cleanup_layer3();
 
-       mISDN_debug_close();
-
        if (debug_fp)
                fclose(debug_fp);
        debug_fp = NULL;
 
        if (mISDNsocket > -1)
                close(mISDNsocket);
        if (debug_fp)
                fclose(debug_fp);
        debug_fp = NULL;
 
        if (mISDNsocket > -1)
                close(mISDNsocket);
+
+       if (upqueue_fd.inuse) {
+               unregister_fd(&upqueue_fd);
+               close(upqueue_pipe[0]);
+               close(upqueue_pipe[1]);
+       }
+       upqueue_avail = 0;
 }
 
 }
 
+static int load_timer(struct lcr_timer *timer, void *instance, int index);
+
 /*
  * constructor
  */
 /*
  * 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;
 {
        p_m_mISDNport = mISDNport;
        p_m_portnum = mISDNport->portnum;
@@ -104,25 +130,30 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti
        p_m_b_exclusive = 0;
        p_m_b_reserve = 0;
        p_m_b_mode = mode;
        p_m_b_exclusive = 0;
        p_m_b_reserve = 0;
        p_m_b_mode = mode;
-       p_m_delete = 0;
        p_m_hold = 0;
        p_m_tx_gain = mISDNport->ifport->interface->tx_gain;
        p_m_rx_gain = mISDNport->ifport->interface->rx_gain;
        p_m_conf = 0;
        p_m_hold = 0;
        p_m_tx_gain = mISDNport->ifport->interface->tx_gain;
        p_m_rx_gain = mISDNport->ifport->interface->rx_gain;
        p_m_conf = 0;
+       p_m_mute = 0;
        p_m_txdata = 0;
        p_m_delay = 0;
        p_m_txdata = 0;
        p_m_delay = 0;
+       p_m_tx_dejitter = 0;
+       p_m_preload = ISDN_LOAD;
+       p_m_disable_dejitter = 0;
        p_m_echo = 0;
        p_m_tone = 0;
        p_m_rxoff = 0;
        p_m_echo = 0;
        p_m_tone = 0;
        p_m_rxoff = 0;
-       p_m_joindata = 0;
+       p_m_inband_send_on = 0;
+       p_m_inband_receive_on = 0;
        p_m_dtmf = !mISDNport->ifport->nodtmf;
        p_m_dtmf = !mISDNport->ifport->nodtmf;
-       p_m_timeout = 0;
-       p_m_timer = 0;
-       p_m_remote_ref = 0; /* channel shall be exported to given remote */
-       p_m_remote_id = 0; /* remote admin socket */
+       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);
        
        /* audio */
        SCPY(p_m_pipeline, mISDNport->ifport->interface->pipeline);
        
        /* audio */
+       memset(&p_m_loadtimer, 0, sizeof(p_m_loadtimer));
+       add_timer(&p_m_loadtimer, load_timer, this, 0);
        p_m_load = 0;
        p_m_last_tv_sec = 0;
 
        p_m_load = 0;
        p_m_last_tv_sec = 0;
 
@@ -140,16 +171,14 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti
        p_m_crypt_listen_len = 0;
        p_m_crypt_listen_msg[0] = '\0';
        p_m_crypt_listen_crc = 0;
        p_m_crypt_listen_len = 0;
        p_m_crypt_listen_msg[0] = '\0';
        p_m_crypt_listen_crc = 0;
-       if (mISDNport->ifport->interface->bf_len >= 4 && mISDNport->ifport->interface->bf_len <= 56)
-       {
+       if (mISDNport->ifport->interface->bf_len >= 4 && mISDNport->ifport->interface->bf_len <= 56) {
                memcpy(p_m_crypt_key, mISDNport->ifport->interface->bf_key, p_m_crypt_key_len);
                p_m_crypt_key_len = mISDNport->ifport->interface->bf_len;
                p_m_crypt = 1;
        }
 
        /* if any channel requested by constructor */
                memcpy(p_m_crypt_key, mISDNport->ifport->interface->bf_key, p_m_crypt_key_len);
                p_m_crypt_key_len = mISDNport->ifport->interface->bf_len;
                p_m_crypt = 1;
        }
 
        /* if any channel requested by constructor */
-       if (channel == CHANNEL_ANY)
-       {
+       if (channel == CHANNEL_ANY) {
                /* reserve channel */
                p_m_b_reserve = 1;
                mISDNport->b_reserved++;
                /* reserve channel */
                p_m_b_reserve = 1;
                mISDNport->b_reserved++;
@@ -162,6 +191,7 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti
        /* we increase the number of objects: */
        mISDNport->use++;
        PDEBUG(DEBUG_ISDN, "Created new mISDNPort(%s). Currently %d objects use, port #%d\n", portname, mISDNport->use, p_m_portnum);
        /* we increase the number of objects: */
        mISDNport->use++;
        PDEBUG(DEBUG_ISDN, "Created new mISDNPort(%s). Currently %d objects use, port #%d\n", portname, mISDNport->use, p_m_portnum);
+//inband_receive_on();
 }
 
 
 }
 
 
@@ -172,12 +202,14 @@ PmISDN::~PmISDN()
 {
        struct lcr_msg *message;
 
 {
        struct lcr_msg *message;
 
+       del_timer(&p_m_timeout);
+       del_timer(&p_m_loadtimer);
+
        /* remove bchannel relation */
        drop_bchannel();
 
        /* release epoint */
        /* remove bchannel relation */
        drop_bchannel();
 
        /* release epoint */
-       while (p_epointlist)
-       {
+       while (p_epointlist) {
                PDEBUG(DEBUG_ISDN, "destroy mISDNPort(%s). endpoint still exists, releaseing.\n", p_name);
                message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
                message->param.disconnectinfo.cause = 16;
                PDEBUG(DEBUG_ISDN, "destroy mISDNPort(%s). endpoint still exists, releaseing.\n", p_name);
                message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
                message->param.disconnectinfo.cause = 16;
@@ -221,7 +253,7 @@ static struct isdn_message {
        {"PH_DEACTIVATE", L1_DEACTIVATE_REQ},
        {"DL_ESTABLISH", L2_ESTABLISH_REQ},
        {"DL_RELEASE", L2_RELEASE_REQ},
        {"PH_DEACTIVATE", L1_DEACTIVATE_REQ},
        {"DL_ESTABLISH", L2_ESTABLISH_REQ},
        {"DL_RELEASE", L2_RELEASE_REQ},
-       {"UNKNOWN", L3_UNKNOWN},
+       {"UNKNOWN", L3_UNKNOWN_REQ},
        {"MT_TIMEOUT", L3_TIMEOUT_REQ},
        {"MT_SETUP", L3_SETUP_REQ},
        {"MT_SETUP_ACK", L3_SETUP_ACKNOWLEDGE_REQ},
        {"MT_TIMEOUT", L3_TIMEOUT_REQ},
        {"MT_SETUP", L3_SETUP_REQ},
        {"MT_SETUP_ACK", L3_SETUP_ACKNOWLEDGE_REQ},
@@ -268,10 +300,9 @@ void l1l2l3_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsign
        SCPY(msgtext, "<<UNKNOWN MESSAGE>>");
        /* select message and primitive text */
        i = 0;
        SCPY(msgtext, "<<UNKNOWN MESSAGE>>");
        /* select message and primitive text */
        i = 0;
-       while(isdn_message[i].name)
-       {
-               if (isdn_message[i].value == (msg&0xffffff00))
-               {
+       while(isdn_message[i].name) {
+//             if (msg == L3_NOTIFY_REQ) printf("val = %x %s\n", isdn_message[i].value, isdn_message[i].name);
+               if (isdn_message[i].value == (msg&0xffffff00)) {
                        SCPY(msgtext, isdn_message[i].name);
                        break;
                }
                        SCPY(msgtext, isdn_message[i].name);
                        break;
                }
@@ -280,18 +311,14 @@ void l1l2l3_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsign
        SCAT(msgtext, isdn_prim[msg&0x00000003]);
 
        /* add direction */
        SCAT(msgtext, isdn_prim[msg&0x00000003]);
 
        /* add direction */
-       if (direction && (msg&0xffffff00)!=L3_NEW_L3ID_REQ && (msg&0xffffff00)!=L3_RELEASE_L3ID_REQ)
-       {
-               if (mISDNport)
-               {
-                       if (mISDNport->ntmode)
-                       {
+       if (direction && (msg&0xffffff00)!=L3_NEW_L3ID_REQ && (msg&0xffffff00)!=L3_RELEASE_L3ID_REQ) {
+               if (mISDNport) {
+                       if (mISDNport->ntmode) {
                                if (direction == DIRECTION_OUT)
                                        SCAT(msgtext, " N->U");
                                else
                                        SCAT(msgtext, " N<-U");
                                if (direction == DIRECTION_OUT)
                                        SCAT(msgtext, " N->U");
                                else
                                        SCAT(msgtext, " N<-U");
-                       } else
-                       {
+                       } else {
                                if (direction == DIRECTION_OUT)
                                        SCAT(msgtext, " U->N");
                                else
                                if (direction == DIRECTION_OUT)
                                        SCAT(msgtext, " U->N");
                                else
@@ -321,15 +348,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;
        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 (sock < 0)
                return;
 
+       if (c1 == DTMF_TONE_START && c2 == 0) {
+               len = 4;
+       }
+
        ctrl->prim = PH_CONTROL_REQ;
        ctrl->id = 0;
        *d++ = c1;
        *d++ = c2;
        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);
        if (ret <= 0)
                PERROR("Failed to send to socket %d\n", sock);
        chan_trace_header(mISDNport, isdnport, "BCHANNEL control", DIRECTION_OUT);
@@ -362,6 +394,7 @@ void ph_control_block(struct mISDNport *mISDNport, class PmISDN *isdnport, int s
        end_trace();
 }
 
        end_trace();
 }
 
+static int b_sock_callback(struct lcr_fd *fd, unsigned int what, void *instance, int i);
 
 /*
  * subfunction for bchannel_event
 
 /*
  * subfunction for bchannel_event
@@ -370,51 +403,40 @@ void ph_control_block(struct mISDNport *mISDNport, class PmISDN *isdnport, int s
 static int _bchannel_create(struct mISDNport *mISDNport, int i)
 {
        int ret;
 static int _bchannel_create(struct mISDNport *mISDNport, int i)
 {
        int ret;
-       unsigned int on = 1;
        struct sockaddr_mISDN addr;
 
        struct sockaddr_mISDN addr;
 
-       if (mISDNport->b_socket[i] > -1)
-       {
+       if (mISDNport->b_sock[i].inuse) {
                PERROR("Error: Socket already created for index %d\n", i);
                return(0);
        }
 
        /* open socket */
 //#warning testing without DSP
                PERROR("Error: Socket already created for index %d\n", i);
                return(0);
        }
 
        /* open socket */
 //#warning testing without DSP
-//     mISDNport->b_socket[i] = socket(PF_ISDN, SOCK_DGRAM, (mISDNport->b_mode[i]==B_MODE_HDLC)?ISDN_P_B_HDLC:ISDN_P_B_RAW);
-       mISDNport->b_socket[i] = socket(PF_ISDN, SOCK_DGRAM, (mISDNport->b_mode[i]==B_MODE_HDLC)?ISDN_P_B_L2DSPHDLC:ISDN_P_B_L2DSP);
-       if (mISDNport->b_socket[i] < 0)
-       {
+//     mISDNport->b_sock[i].fd = socket(PF_ISDN, SOCK_DGRAM, (mISDNport->b_mode[i]==B_MODE_HDLC)?ISDN_P_B_HDLC:ISDN_P_B_RAW);
+       mISDNport->b_sock[i].fd = socket(PF_ISDN, SOCK_DGRAM, (mISDNport->b_mode[i]==B_MODE_HDLC)?ISDN_P_B_L2DSPHDLC:ISDN_P_B_L2DSP);
+       if (mISDNport->b_sock[i].fd < 0) {
                PERROR("Error: Failed to open bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDN_dsp.ko?\n", i);
                return(0);
        }
                PERROR("Error: Failed to open bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDN_dsp.ko?\n", i);
                return(0);
        }
-       
-       /* set nonblocking io */
-       ret = ioctl(mISDNport->b_socket[i], FIONBIO, &on);
-       if (ret < 0)
-       {
-               PERROR("Error: Failed to set bchannel-socket index %d into nonblocking IO\n", i);
-               close(mISDNport->b_socket[i]);
-               mISDNport->b_socket[i] = -1;
-               return(0);
-       }
 
 
+       /* register callback for read */
+       register_fd(&mISDNport->b_sock[i], LCR_FD_READ, b_sock_callback, mISDNport, i);
+       
        /* bind socket to bchannel */
        addr.family = AF_ISDN;
        addr.dev = mISDNport->portnum;
        addr.channel = i+1+(i>=15);
        /* bind socket to bchannel */
        addr.family = AF_ISDN;
        addr.dev = mISDNport->portnum;
        addr.channel = i+1+(i>=15);
-       ret = bind(mISDNport->b_socket[i], (struct sockaddr *)&addr, sizeof(addr));
-       if (ret < 0)
-       {
-               PERROR("Error: Failed to bind bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDN_dsp.ko?\n", i);
-               close(mISDNport->b_socket[i]);
-               mISDNport->b_socket[i] = -1;
+       ret = bind(mISDNport->b_sock[i].fd, (struct sockaddr *)&addr, sizeof(addr));
+       if (ret < 0) {
+               PERROR("Error: Failed to bind bchannel-socket for index %d with mISDN-DSP layer (errno=%d). Did you load mISDN_dsp.ko?\n", i, errno);
+               close(mISDNport->b_sock[i].fd);
+               unregister_fd(&mISDNport->b_sock[i]);
                return(0);
        }
 
        chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL create socket", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
                return(0);
        }
 
        chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL create socket", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
-       add_trace("socket", NULL, "%d", mISDNport->b_socket[i]);
+       add_trace("socket", NULL, "%d", mISDNport->b_sock[i].fd);
        end_trace();
 
        return(1);
        end_trace();
 
        return(1);
@@ -425,23 +447,23 @@ static int _bchannel_create(struct mISDNport *mISDNport, int i)
  * subfunction for bchannel_event
  * activate / deactivate request
  */
  * subfunction for bchannel_event
  * activate / deactivate request
  */
-static void _bchannel_activate(struct mISDNport *mISDNport, int i, int activate)
+static void _bchannel_activate(struct mISDNport *mISDNport, int i, int activate, int timeout)
 {
        struct mISDNhead act;
        int ret;
 
 {
        struct mISDNhead act;
        int ret;
 
-       if (mISDNport->b_socket[i] < 0)
+       if (!mISDNport->b_sock[i].inuse)
                return;
        act.prim = (activate)?PH_ACTIVATE_REQ:PH_DEACTIVATE_REQ; 
        act.id = 0;
                return;
        act.prim = (activate)?PH_ACTIVATE_REQ:PH_DEACTIVATE_REQ; 
        act.id = 0;
-       ret = sendto(mISDNport->b_socket[i], &act, MISDN_HEADER_LEN, 0, NULL, 0);
+       ret = sendto(mISDNport->b_sock[i].fd, &act, MISDN_HEADER_LEN, 0, NULL, 0);
        if (ret <= 0)
        if (ret <= 0)
-               PERROR("Failed to send to socket %d\n", mISDNport->b_socket[i]);
+               PERROR("Failed to send to socket %d\n", mISDNport->b_sock[i].fd);
 
        /* trace */
        chan_trace_header(mISDNport, mISDNport->b_port[i], activate ? "BCHANNEL activate" : "BCHANNEL deactivate", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
 
        /* trace */
        chan_trace_header(mISDNport, mISDNport->b_port[i], activate ? "BCHANNEL activate" : "BCHANNEL deactivate", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
-       if (mISDNport->b_timer[i])
+       if (timeout)
                add_trace("event", NULL, "timeout recovery");
        end_trace();
 }
                add_trace("event", NULL, "timeout recovery");
        end_trace();
 }
@@ -456,13 +478,12 @@ static void _bchannel_configure(struct mISDNport *mISDNport, int i)
        struct PmISDN *port;
        int handle, mode;
 
        struct PmISDN *port;
        int handle, mode;
 
-       if (mISDNport->b_socket[i] < 0)
+       if (!mISDNport->b_sock[i].inuse)
                return;
                return;
-       handle = mISDNport->b_socket[i];
+       handle = mISDNport->b_sock[i].fd;
        port = mISDNport->b_port[i];
        mode = mISDNport->b_mode[i];
        port = mISDNport->b_port[i];
        mode = mISDNport->b_mode[i];
-       if (!port)
-       {
+       if (!port) {
                PERROR("bchannel index i=%d not associated with a port object\n", i);
                return;
        }
                PERROR("bchannel index i=%d not associated with a port object\n", i);
                return;
        }
@@ -472,13 +493,15 @@ static void _bchannel_configure(struct mISDNport *mISDNport, int i)
                ph_control(mISDNport, port, handle, (port->p_m_txdata)?DSP_TXDATA_ON:DSP_TXDATA_OFF, 0, "DSP-TXDATA", port->p_m_txdata);
        if (port->p_m_delay && mode == B_MODE_TRANSPARENT)
                ph_control(mISDNport, port, handle, DSP_DELAY, port->p_m_delay, "DSP-DELAY", port->p_m_delay);
                ph_control(mISDNport, port, handle, (port->p_m_txdata)?DSP_TXDATA_ON:DSP_TXDATA_OFF, 0, "DSP-TXDATA", port->p_m_txdata);
        if (port->p_m_delay && mode == B_MODE_TRANSPARENT)
                ph_control(mISDNport, port, handle, DSP_DELAY, port->p_m_delay, "DSP-DELAY", port->p_m_delay);
+       if (port->p_m_tx_dejitter && mode == B_MODE_TRANSPARENT)
+               ph_control(mISDNport, port, handle, DSP_TX_DEJITTER, port->p_m_tx_dejitter, "DSP-TX_DEJITTER", port->p_m_tx_dejitter);
        if (port->p_m_tx_gain && mode == B_MODE_TRANSPARENT)
                ph_control(mISDNport, port, handle, DSP_VOL_CHANGE_TX, port->p_m_tx_gain, "DSP-TX_GAIN", port->p_m_tx_gain);
        if (port->p_m_rx_gain && mode == B_MODE_TRANSPARENT)
                ph_control(mISDNport, port, handle, DSP_VOL_CHANGE_RX, port->p_m_rx_gain, "DSP-RX_GAIN", port->p_m_rx_gain);
        if (port->p_m_pipeline[0] && mode == B_MODE_TRANSPARENT)
                ph_control_block(mISDNport, port, handle, DSP_PIPELINE_CFG, port->p_m_pipeline, strlen(port->p_m_pipeline)+1, "DSP-PIPELINE", 0);
        if (port->p_m_tx_gain && mode == B_MODE_TRANSPARENT)
                ph_control(mISDNport, port, handle, DSP_VOL_CHANGE_TX, port->p_m_tx_gain, "DSP-TX_GAIN", port->p_m_tx_gain);
        if (port->p_m_rx_gain && mode == B_MODE_TRANSPARENT)
                ph_control(mISDNport, port, handle, DSP_VOL_CHANGE_RX, port->p_m_rx_gain, "DSP-RX_GAIN", port->p_m_rx_gain);
        if (port->p_m_pipeline[0] && mode == B_MODE_TRANSPARENT)
                ph_control_block(mISDNport, port, handle, DSP_PIPELINE_CFG, port->p_m_pipeline, strlen(port->p_m_pipeline)+1, "DSP-PIPELINE", 0);
-       if (port->p_m_conf)
+       if (port->p_m_conf && !port->p_m_mute)
                ph_control(mISDNport, port, handle, DSP_CONF_JOIN, port->p_m_conf, "DSP-CONF", port->p_m_conf);
        if (port->p_m_echo)
                ph_control(mISDNport, port, handle, DSP_ECHO_ON, 0, "DSP-ECHO", 1);
                ph_control(mISDNport, port, handle, DSP_CONF_JOIN, port->p_m_conf, "DSP-CONF", port->p_m_conf);
        if (port->p_m_echo)
                ph_control(mISDNport, port, handle, DSP_ECHO_ON, 0, "DSP-ECHO", 1);
@@ -489,25 +512,38 @@ 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)
 //     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);
 }
 
        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);
 }
 
+
+void PmISDN::set_conf(int oldconf, int newconf)
+{
+               if (oldconf != newconf) {
+                       PDEBUG(DEBUG_BCHANNEL, "we change conference from conf=%d to conf=%d.\n", oldconf, newconf);
+                       if (p_m_b_index > -1)
+                       if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
+                               ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, (newconf)?DSP_CONF_JOIN:DSP_CONF_SPLIT, newconf, "DSP-CONF", newconf);
+               } else
+                       PDEBUG(DEBUG_BCHANNEL, "we already have conf=%d.\n", newconf);
+}
+
+
 /*
  * subfunction for bchannel_event
  * destroy stack
  */
 static void _bchannel_destroy(struct mISDNport *mISDNport, int i)
 {
 /*
  * subfunction for bchannel_event
  * destroy stack
  */
 static void _bchannel_destroy(struct mISDNport *mISDNport, int i)
 {
-       if (mISDNport->b_socket[i] < 0)
+       if (!mISDNport->b_sock[i].inuse)
                return;
        chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL remove socket", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
                return;
        chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL remove socket", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
-       add_trace("socket", NULL, "%d", mISDNport->b_socket[i]);
+       add_trace("socket", NULL, "%d", mISDNport->b_sock[i].fd);
        end_trace();
        end_trace();
-       close(mISDNport->b_socket[i]);
-       mISDNport->b_socket[i] = -1;
+       close(mISDNport->b_sock[i].fd);
+       unregister_fd(&mISDNport->b_sock[i]);
 }
 
 
 }
 
 
@@ -537,22 +573,6 @@ It may be linked to a Port class, that likes to reactivate it.
 See above.
 After deactivating bchannel, and if not used, the bchannel becomes idle again.
 
 See above.
 After deactivating bchannel, and if not used, the bchannel becomes idle again.
 
-Also the bchannel may be exported, but only if the state is or becomes idle:
-
-- B_STATE_EXPORTING
-The bchannel assignment has been sent to the remove application.
-
-- B_STATE_REMOTE
-The bchannel assignment is acknowledged by the remote application.
-
-- B_STATE_IMPORTING
-The bchannel is re-imported by mISDN port object.
-
-- B_STATE_IDLE
-See above.
-After re-importing bchannel, and if not used, the bchannel becomes idle again.
-
-
 A bchannel can have the following events:
 
 - B_EVENT_USE
 A bchannel can have the following events:
 
 - B_EVENT_USE
@@ -567,33 +587,8 @@ The bchannel is not required by Port class anymore
 - B_EVENT_DEACTIVATED
 The bchannel becomes inactive.
 
 - B_EVENT_DEACTIVATED
 The bchannel becomes inactive.
 
-- B_EVENT_EXPORTED
-The bchannel is now used by remote application.
-
-- B_EVENT_IMPORTED
-The bchannel is not used by remote application.
-
-- B_EVENT_EXPORTREQUEST
-The bchannel shall be exported to the remote application.
-
-- B_EVENT_IMPORTREQUEST
-The bchannel is released from the remote application.
-
 All actions taken on these events depend on the current bchannel's state and if it is linked to a Port class.
 
 All actions taken on these events depend on the current bchannel's state and if it is linked to a Port class.
 
-if an export request is receive by remote application, p_m_remote_* is set.
-the b_remote_*[index] indicates if and where the channel is exported to. (set from the point on, where export is initiated, until imported is acknowledged.)
-- set on export request from remote application (if port is assigned)
-- set on channel use, if requested by remote application (p_m_remote_*)
-- cleared on drop request
-
-the bchannel will be exported with ref and stack given. remote application uses the ref to link bchannel to the call.
-the bchannel will be imported with stack given only. remote application must store stack id with the bchannel process.
-the bchannel import/export is acknowledged with stack given.
-
-if exporting, b_remote_*[index] is set to the remote socket id.
-if importing has been acknowledged. b_remote_*[index] is cleared.
-
 */
 
 /*
 */
 
 /*
@@ -607,21 +602,16 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
 {
        class PmISDN *b_port = mISDNport->b_port[i];
        int state = mISDNport->b_state[i];
 {
        class PmISDN *b_port = mISDNport->b_port[i];
        int state = mISDNport->b_state[i];
-       double timer = mISDNport->b_timer[i];
-       unsigned int p_m_remote_ref = 0;
-       unsigned int p_m_remote_id = 0;
+       int timer = -1; // no change
+#if 0
        int p_m_tx_gain = 0;
        int p_m_rx_gain = 0;
        char *p_m_pipeline = NULL;
        unsigned char *p_m_crypt_key = NULL;
        int p_m_crypt_key_len = 0;
        int p_m_crypt_key_type = 0;
        int p_m_tx_gain = 0;
        int p_m_rx_gain = 0;
        char *p_m_pipeline = NULL;
        unsigned char *p_m_crypt_key = NULL;
        int p_m_crypt_key_len = 0;
        int p_m_crypt_key_type = 0;
-       unsigned int portid = (mISDNport->portnum<<8) + i+1+(i>=15);
 
 
-       if (b_port)
-       {
-               p_m_remote_id = b_port->p_m_remote_id;
-               p_m_remote_ref = b_port->p_m_remote_ref;
+       if (b_port) {
                p_m_tx_gain = b_port->p_m_tx_gain;
                p_m_rx_gain = b_port->p_m_rx_gain;
                p_m_pipeline = b_port->p_m_pipeline;
                p_m_tx_gain = b_port->p_m_tx_gain;
                p_m_rx_gain = b_port->p_m_rx_gain;
                p_m_pipeline = b_port->p_m_pipeline;
@@ -629,191 +619,51 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
                p_m_crypt_key_len = b_port->p_m_crypt_key_len;
                p_m_crypt_key_type = /*b_port->p_m_crypt_key_type*/1;
        }
                p_m_crypt_key_len = b_port->p_m_crypt_key_len;
                p_m_crypt_key_type = /*b_port->p_m_crypt_key_type*/1;
        }
+#endif
 
 
-       switch(event)
-       {
+       switch(event) {
                case B_EVENT_USE:
                /* port must be linked in order to allow activation */
                if (!b_port)
                        FATAL("bchannel must be linked to a Port class\n");
                case B_EVENT_USE:
                /* port must be linked in order to allow activation */
                if (!b_port)
                        FATAL("bchannel must be linked to a Port class\n");
-               switch(state)
-               {
+               switch(state) {
                        case B_STATE_IDLE:
                        case B_STATE_IDLE:
-                       if (p_m_remote_ref)
-                       {
-                               /* export bchannel */
-                               message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type);
-                               chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
-                               add_trace("type", NULL, "assign");
-                               add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
-                               end_trace();
-                               state = B_STATE_EXPORTING;
-                               mISDNport->b_remote_id[i] = p_m_remote_id;
-                               mISDNport->b_remote_ref[i] = p_m_remote_ref;
-                       } else
-                       {
-                               /* create stack and send activation request */
-                               if (_bchannel_create(mISDNport, i))
-                               {
-                                       _bchannel_activate(mISDNport, i, 1);
-                                       state = B_STATE_ACTIVATING;
-                                       timer = now_d + B_TIMER_ACTIVATING;
-                               }
+                       /* create stack and send activation request */
+                       if (_bchannel_create(mISDNport, i)) {
+                               _bchannel_activate(mISDNport, i, 1, 0);
+                               state = B_STATE_ACTIVATING;
+                               timer = B_TIMER_ACTIVATING;
                        }
                        break;
 
                        case B_STATE_ACTIVATING:
                        }
                        break;
 
                        case B_STATE_ACTIVATING:
-                       case B_STATE_EXPORTING:
                        /* do nothing, because it is already activating */
                        break;
 
                        /* do nothing, because it is already activating */
                        break;
 
-                       case B_STATE_DEACTIVATING:
-                       case B_STATE_IMPORTING:
-                       /* do nothing, because we must wait until we can reactivate */
-                       break;
-
                        default:
                        /* problems that might ocurr:
                         * B_EVENT_USE is received when channel already in use.
                        default:
                        /* problems that might ocurr:
                         * B_EVENT_USE is received when channel already in use.
-                        * bchannel exported, but not freed by other port
-                        */
-                       PERROR("Illegal event %d at state %d, please correct.\n", event, state);
-               }
-               break;
-
-               case B_EVENT_EXPORTREQUEST:
-               /* special case where the bchannel is requested by remote */
-               if (!p_m_remote_ref)
-               {
-                       PERROR("export request without remote channel set, please correct.\n");
-                       break;
-               }
-               switch(state)
-               {
-                       case B_STATE_IDLE:
-                       /* in case, the bchannel is exported right after seize_bchannel */
-                       /* export bchannel */
-                       /* p_m_remote_id is set, when this event happens. */
-                       message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type);
-                       chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
-                       add_trace("type", NULL, "assign");
-                       add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
-                       end_trace();
-                       state = B_STATE_EXPORTING;
-                       mISDNport->b_remote_id[i] = p_m_remote_id;
-                       mISDNport->b_remote_ref[i] = p_m_remote_ref;
-                       break;
-
-                       case B_STATE_ACTIVATING:
-                       case B_STATE_EXPORTING:
-                       /* do nothing, because it is already activating */
-                       break;
-
-                       case B_STATE_DEACTIVATING:
-                       case B_STATE_IMPORTING:
-                       /* do nothing, because we must wait until we can reactivate */
-                       break;
-
-                       case B_STATE_ACTIVE:
-                       /* bchannel is active, so we deactivate */
-                       _bchannel_activate(mISDNport, i, 0);
-                       state = B_STATE_DEACTIVATING;
-                       timer = now_d + B_TIMER_DEACTIVATING;
-                       break;
-
-                       default:
-                       /* problems that might ocurr:
-                        * ... when channel already in use.
-                        * bchannel exported, but not freed by other port
                         */
                        PERROR("Illegal event %d at state %d, please correct.\n", event, state);
                }
                break;
 
                         */
                        PERROR("Illegal event %d at state %d, please correct.\n", event, state);
                }
                break;
 
-               case B_EVENT_IMPORTREQUEST:
-               /* special case where the bchannel is released by remote */
-               if (p_m_remote_ref)
-               {
-                       PERROR("import request with remote channel set, please correct.\n");
-                       break;
-               }
-               switch(state)
-               {
-                       case B_STATE_IDLE:
-                       case B_STATE_ACTIVE:
-                       /* bchannel is not exported */
-                       break;
-
-                       case B_STATE_ACTIVATING:
-                       case B_STATE_EXPORTING:
-                       /* do nothing because we must wait until bchanenl is active before deactivating */
-                       break;
-
-                       case B_STATE_REMOTE:
-                       /* bchannel is exported, so we re-import */
-                       message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0);
-                       chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
-                       add_trace("type", NULL, "remove");
-                       add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
-                       end_trace();
-                       state = B_STATE_IMPORTING;
-                       break;
-
-                       case B_STATE_DEACTIVATING:
-                       case B_STATE_IMPORTING:
-                       /* we may have taken an already deactivating bchannel, but do not require it anymore, so we do nothing */
-                       break;
-
-                       default:
-                       PERROR("Illegal event %d at state %d, please correct.\n", event, state);
-               }
-               break;
 
                case B_EVENT_ACTIVATED:
                timer = 0;
 
                case B_EVENT_ACTIVATED:
                timer = 0;
-               switch(state)
-               {
+               switch(state) {
                        case B_STATE_ACTIVATING:
                        case B_STATE_ACTIVATING:
-                       if (b_port && !p_m_remote_id)
-                       {
+                       if (b_port) {
                                /* bchannel is active and used by Port class, so we configure bchannel */
                                _bchannel_configure(mISDNport, i);
                                state = B_STATE_ACTIVE;
                                b_port->p_m_load = 0;
                                /* bchannel is active and used by Port class, so we configure bchannel */
                                _bchannel_configure(mISDNport, i);
                                state = B_STATE_ACTIVE;
                                b_port->p_m_load = 0;
-                       } else
-                       {
-                               /* bchannel is active, but exported OR not used anymore (or has wrong stack config), so we deactivate */
-                               _bchannel_activate(mISDNport, i, 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);
                                state = B_STATE_DEACTIVATING;
                                state = B_STATE_DEACTIVATING;
-                               timer = now_d + B_TIMER_DEACTIVATING;
-                       }
-                       break;
-
-                       default:
-                       PERROR("Illegal event %d at state %d, please correct.\n", event, state);
-               }
-               break;
-
-               case B_EVENT_EXPORTED:
-               switch(state)
-               {
-                       case B_STATE_EXPORTING:
-                       if (b_port && p_m_remote_ref && p_m_remote_ref==mISDNport->b_remote_ref[i])
-                       {
-                               /* remote export done */
-                               state = B_STATE_REMOTE;
-                       } else
-                       {
-                               /* bchannel is now exported, but we need bchannel back
-                                * OR bchannel is not used anymore
-                                * OR bchannel has been exported to an obsolete ref,
-                                * so reimport, to later export to new remote */
-                               message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0);
-                               chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
-                               add_trace("type", NULL, "remove");
-                               add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
-                               end_trace();
-                               state = B_STATE_IMPORTING;
+                               timer = B_TIMER_DEACTIVATING;
                        }
                        break;
 
                        }
                        break;
 
@@ -825,36 +675,23 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
                case B_EVENT_DROP:
                if (!b_port)
                        FATAL("bchannel must be linked to a Port class\n");
                case B_EVENT_DROP:
                if (!b_port)
                        FATAL("bchannel must be linked to a Port class\n");
-               switch(state)
-               {
+               switch(state) {
                        case B_STATE_IDLE:
                        /* bchannel is idle due to an error, so we do nothing */
                        break;
 
                        case B_STATE_ACTIVATING:
                        case B_STATE_IDLE:
                        /* bchannel is idle due to an error, so we do nothing */
                        break;
 
                        case B_STATE_ACTIVATING:
-                       case B_STATE_EXPORTING:
                        /* do nothing because we must wait until bchanenl is active before deactivating */
                        break;
 
                        case B_STATE_ACTIVE:
                        /* bchannel is active, so we deactivate */
                        /* do nothing because we must wait until bchanenl is active before deactivating */
                        break;
 
                        case B_STATE_ACTIVE:
                        /* bchannel is active, so we deactivate */
-                       _bchannel_activate(mISDNport, i, 0);
+                       _bchannel_activate(mISDNport, i, 0, 0);
                        state = B_STATE_DEACTIVATING;
                        state = B_STATE_DEACTIVATING;
-                       timer = now_d + B_TIMER_DEACTIVATING;
-                       break;
-
-                       case B_STATE_REMOTE:
-                       /* bchannel is exported, so we re-import */
-                       message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0);
-                       chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
-                       add_trace("type", NULL, "remove");
-                       add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
-                       end_trace();
-                       state = B_STATE_IMPORTING;
+                       timer = B_TIMER_DEACTIVATING;
                        break;
 
                        case B_STATE_DEACTIVATING:
                        break;
 
                        case B_STATE_DEACTIVATING:
-                       case B_STATE_IMPORTING:
                        /* we may have taken an already deactivating bchannel, but do not require it anymore, so we do nothing */
                        break;
 
                        /* we may have taken an already deactivating bchannel, but do not require it anymore, so we do nothing */
                        break;
 
@@ -865,8 +702,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
 
                case B_EVENT_DEACTIVATED:
                timer = 0;
 
                case B_EVENT_DEACTIVATED:
                timer = 0;
-               switch(state)
-               {
+               switch(state) {
                        case B_STATE_IDLE:
                        /* ignore due to deactivation confirm after unloading */
                        break;
                        case B_STATE_IDLE:
                        /* ignore due to deactivation confirm after unloading */
                        break;
@@ -874,27 +710,12 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
                        case B_STATE_DEACTIVATING:
                        _bchannel_destroy(mISDNport, i);
                        state = B_STATE_IDLE;
                        case B_STATE_DEACTIVATING:
                        _bchannel_destroy(mISDNport, i);
                        state = B_STATE_IDLE;
-                       if (b_port)
-                       {
-                               /* bchannel is now deactivate, but is requied by Port class, so we reactivate / export */
-                               if (p_m_remote_ref)
-                               {
-                                       message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type);
-                                       chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
-                                       add_trace("type", NULL, "assign");
-                                       add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
-                                       end_trace();
-                                       state = B_STATE_EXPORTING;
-                                       mISDNport->b_remote_id[i] = p_m_remote_id;
-                                       mISDNport->b_remote_ref[i] = p_m_remote_ref;
-                               } else
-                               {
-                                       if (_bchannel_create(mISDNport, i))
-                                       {
-                                               _bchannel_activate(mISDNport, i, 1);
-                                               state = B_STATE_ACTIVATING;
-                                               timer = now_d + B_TIMER_ACTIVATING;
-                                       }
+                       if (b_port) {
+                               /* bchannel is now deactivate, but is requied by Port class, so we reactivate */
+                               if (_bchannel_create(mISDNport, i)) {
+                                       _bchannel_activate(mISDNport, i, 1, 0);
+                                       state = B_STATE_ACTIVATING;
+                                       timer = B_TIMER_ACTIVATING;
                                }
                        }
                        break;
                                }
                        }
                        break;
@@ -904,60 +725,21 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
                }
                break;
 
                }
                break;
 
-               case B_EVENT_IMPORTED:
-               switch(state)
-               {
-                       case B_STATE_IMPORTING:
-                       state = B_STATE_IDLE;
-                       mISDNport->b_remote_id[i] = 0;
-                       mISDNport->b_remote_ref[i] = 0;
-                       if (b_port)
-                       {
-                               /* bchannel is now imported, but is requied by Port class, so we reactivate / export */
-                               if (p_m_remote_ref)
-                               {
-                                       message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type);
-                                       chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
-                                       add_trace("type", NULL, "assign");
-                                       add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
-                                       end_trace();
-                                       state = B_STATE_EXPORTING;
-                                       mISDNport->b_remote_id[i] = p_m_remote_id;
-                                       mISDNport->b_remote_ref[i] = p_m_remote_ref;
-                               } else
-                               {
-                                       if (_bchannel_create(mISDNport, i))
-                                       {
-                                               _bchannel_activate(mISDNport, i, 1);
-                                               state = B_STATE_ACTIVATING;
-                                               timer = now_d + B_TIMER_ACTIVATING;
-                                       }
-                               }
-                       }
-                       break;
-
-                       default:
-                       /* ignore, because not assigned */
-                       ;
-               }
-               break;
-
                case B_EVENT_TIMEOUT:
                timer = 0;
                case B_EVENT_TIMEOUT:
                timer = 0;
-               switch(state)
-               {
+               switch(state) {
                        case B_STATE_IDLE:
                        /* ignore due to deactivation confirm after unloading */
                        break;
 
                        case B_STATE_ACTIVATING:
                        case B_STATE_IDLE:
                        /* ignore due to deactivation confirm after unloading */
                        break;
 
                        case B_STATE_ACTIVATING:
-                       _bchannel_activate(mISDNport, i, 1);
-                       timer = now_d + B_TIMER_ACTIVATING;
+                       _bchannel_activate(mISDNport, i, 1, 1);
+                       timer = B_TIMER_ACTIVATING;
                        break;
 
                        case B_STATE_DEACTIVATING:
                        break;
 
                        case B_STATE_DEACTIVATING:
-                       _bchannel_activate(mISDNport, i, 0);
-                       timer = now_d + B_TIMER_DEACTIVATING;
+                       _bchannel_activate(mISDNport, i, 0, 1);
+                       timer = B_TIMER_DEACTIVATING;
                        break;
 
                        default:
                        break;
 
                        default:
@@ -970,7 +752,10 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
        }
 
        mISDNport->b_state[i] = state;
        }
 
        mISDNport->b_state[i] = state;
-       mISDNport->b_timer[i] = timer;
+       if (timer == 0)
+               unsched_timer(&mISDNport->b_timer[i]);
+       else if (timer > 0)
+               schedule_timer(&mISDNport->b_timer[i], timer, 0);
 }
 
 
 }
 
 
@@ -1006,8 +791,7 @@ int PmISDN::seize_bchannel(int channel, int exclusive)
                return(-6); /* channel unacceptable */
 
        /* request exclusive channel */
                return(-6); /* channel unacceptable */
 
        /* request exclusive channel */
-       if (exclusive && channel>0)
-       {
+       if (exclusive && channel>0) {
                i = channel-1-(channel>16);
                if (p_m_mISDNport->b_port[i])
                        return(-44); /* requested channel not available */
                i = channel-1-(channel>16);
                if (p_m_mISDNport->b_port[i])
                        return(-44); /* requested channel not available */
@@ -1015,8 +799,7 @@ int PmISDN::seize_bchannel(int channel, int exclusive)
        }
 
        /* ask for channel */
        }
 
        /* ask for channel */
-       if (channel>0)
-       {
+       if (channel>0) {
                i = channel-1-(channel>16);
                if (p_m_mISDNport->b_port[i] == NULL)
                        goto seize;
                i = channel-1-(channel>16);
                if (p_m_mISDNport->b_port[i] == NULL)
                        goto seize;
@@ -1024,10 +807,8 @@ int PmISDN::seize_bchannel(int channel, int exclusive)
 
        /* search for channel */
        i = 0;
 
        /* search for channel */
        i = 0;
-       while(i < p_m_mISDNport->b_num)
-       {
-               if (!p_m_mISDNport->b_port[i])
-               {
+       while(i < p_m_mISDNport->b_num) {
+               if (!p_m_mISDNport->b_port[i]) {
                        channel = i+1+(i>=15);
                        goto seize;
                }
                        channel = i+1+(i>=15);
                        goto seize;
                }
@@ -1046,8 +827,7 @@ seize:
        p_m_mISDNport->b_mode[i] = p_m_b_mode;
 
        /* reserve channel */
        p_m_mISDNport->b_mode[i] = p_m_b_mode;
 
        /* reserve channel */
-       if (!p_m_b_reserve)
-       {
+       if (!p_m_b_reserve) {
                p_m_b_reserve = 1;
                p_m_mISDNport->b_reserved++;
        }
                p_m_b_reserve = 1;
                p_m_mISDNport->b_reserved++;
        }
@@ -1083,115 +863,6 @@ void PmISDN::drop_bchannel(void)
        p_m_b_exclusive = 0;
 }
 
        p_m_b_exclusive = 0;
 }
 
-/* process bchannel export/import message from join */
-void message_bchannel_from_remote(class JoinRemote *joinremote, int type, unsigned int handle)
-{
-       class Endpoint *epoint;
-       class Port *port;
-       class PmISDN *isdnport;
-       struct mISDNport *mISDNport;
-       int i, ii;
-
-       switch(type)
-       {
-               case BCHANNEL_REQUEST:
-               /* find the port object for the join object ref */
-               if (!(epoint = find_epoint_id(joinremote->j_epoint_id)))
-               {
-                       PDEBUG(DEBUG_BCHANNEL, "join %d has no endpoint (anymore)\n", joinremote->j_serial);
-                       return;
-               }
-               if (!epoint->ep_portlist)
-               {
-                       PDEBUG(DEBUG_BCHANNEL, "join %d has no port (anymore in portlist)\n", joinremote->j_serial);
-                       return;
-               }
-               if (epoint->ep_portlist->next)
-               {
-                       PERROR("join %d has enpoint %d with more than one port. this shall not happen to remote joins.\n", joinremote->j_serial, epoint->ep_serial);
-               }
-               if (!(port = find_port_id(epoint->ep_portlist->port_id)))
-               {
-                       PDEBUG(DEBUG_BCHANNEL, "join %d has no port (anymore as object)\n", joinremote->j_serial);
-                       return;
-               }
-               if ((port->p_type&PORT_CLASS_MASK) != PORT_CLASS_mISDN)
-               {
-                       PERROR("join %d has port %d not of mISDN type. This shall not happen.\n", joinremote->j_serial, port->p_serial);
-               }
-               isdnport = (class PmISDN *)port;
-
-               /* assign */
-               if (isdnport->p_m_remote_id)
-               {
-                       PERROR("join %d recevied bchannel request from remote, but channel is already assinged.\n", joinremote->j_serial);
-                       break;
-               }
-               mISDNport = isdnport->p_m_mISDNport;
-               i = isdnport->p_m_b_index;
-               chan_trace_header(mISDNport, isdnport, "MESSAGE_BCHANNEL (from remote application)", DIRECTION_NONE);
-               add_trace("type", NULL, "export request");
-               end_trace();
-               isdnport->p_m_remote_ref = joinremote->j_serial;
-               isdnport->p_m_remote_id = joinremote->j_remote_id;
-               if (mISDNport && i>=0)
-               {
-                       bchannel_event(mISDNport, i, B_EVENT_EXPORTREQUEST);
-               }
-               break;
-
-               case BCHANNEL_RELEASE:
-               case BCHANNEL_ASSIGN_ACK:
-               case BCHANNEL_REMOVE_ACK:
-               /* find mISDNport for stack ID */
-               mISDNport = mISDNport_first;
-               while(mISDNport)
-               {
-                       i = 0;
-                       ii = mISDNport->b_num;
-                       while(i < ii)
-                       {
-                               if ((unsigned int)(mISDNport->portnum<<8)+i+1+(i>=15) == handle)
-                                       break;
-                               i++;
-                       }
-                       if (i != ii)
-                               break;
-                       mISDNport = mISDNport->next;
-               }
-               if (!mISDNport)
-               {
-                       PERROR("received assign/remove ack for bchannel's handle=%x, but handle does not exist in any mISDNport structure.\n", handle);
-                       break;
-               }
-               
-               if (type!=BCHANNEL_RELEASE)
-               {
-                       /* ack */
-                       chan_trace_header(mISDNport, mISDNport->b_port[i], "MESSAGE_BCHANNEL (from remote application)", DIRECTION_NONE);
-                       add_trace("type", NULL, (type==BCHANNEL_ASSIGN_ACK)?"assign_ack":"remove_ack");
-                       end_trace();
-                       bchannel_event(mISDNport, i, (type==BCHANNEL_ASSIGN_ACK)?B_EVENT_EXPORTED:B_EVENT_IMPORTED);
-               } else
-               {
-                       /* release */
-                       isdnport = mISDNport->b_port[i];
-                       chan_trace_header(mISDNport, isdnport, "MESSAGE_BCHANNEL (from remote application)", DIRECTION_NONE);
-                       add_trace("type", NULL, "import request");
-                       end_trace();
-                       if (isdnport)
-                       {
-                               isdnport->p_m_remote_ref = 0;
-                               isdnport->p_m_remote_id = 0;
-                       }
-                       bchannel_event(mISDNport, i, B_EVENT_IMPORTREQUEST);
-               }
-               break;
-               default:
-               PERROR("received wrong bchannel message type %d from remote\n", type);
-       }
-}
-
 
 /*
  * handler
 
 /*
  * handler
@@ -1218,83 +889,97 @@ times have no skew.
 
 * levels
 there are two levels:
 
 * levels
 there are two levels:
-ISDN_LOAD will give the load that have to be kept in dsp.
-ISDN_MAXLOAD will give the maximum load before dropping.
+p_m_preload will give the load that have to be kept in dsp.
+ISDN_MAXLOAD (2*p_m_preload) will give the maximum load before dropping.
 
 * procedure for low priority data
 see txfromup() for procedure
 in short: remote data is ignored during high priority tones
 
 * procedure for high priority data
 
 * procedure for low priority data
 see txfromup() for procedure
 in short: remote data is ignored during high priority tones
 
 * procedure for high priority data
-whenever load is below ISDN_LOAD, load is filled up to ISDN_LOAD
+whenever load is below p_m_preload, load is filled up to p_m_preload
 if no more data is available, load becomes empty again.
 
 'load' variable:
 if no more data is available, load becomes empty again.
 
 'load' variable:
-0                    ISDN_LOAD           ISDN_MAXLOAD
+0                    p_m_preload           ISDN_MAXLOAD
 +--------------------+----------------------+
 |                    |                      |
 +--------------------+----------------------+
 
 +--------------------+----------------------+
 |                    |                      |
 +--------------------+----------------------+
 
-on empty load or on load below ISDN_LOAD, the load is inceased to ISDN_LOAD:
-0                    ISDN_LOAD           ISDN_MAXLOAD
+on empty load or on load below p_m_preload, the load is inceased to p_m_preload:
+0                    p_m_preload           ISDN_MAXLOAD
 +--------------------+----------------------+
 |TTTTTTTTTTTTTTTTTTTT|                      |
 +--------------------+----------------------+
 
 +--------------------+----------------------+
 |TTTTTTTTTTTTTTTTTTTT|                      |
 +--------------------+----------------------+
 
-on empty load, remote-audio causes the load with the remote audio to be increased to ISDN_LOAD.
-0                    ISDN_LOAD           ISDN_MAXLOAD
+on empty load, remote-audio causes the load with the remote audio to be increased to p_m_preload.
+0                    p_m_preload           ISDN_MAXLOAD
 +--------------------+----------------------+
 |TTTTTTTTTTTTTTTTTTTTRRRRR                  |
 +--------------------+----------------------+
 
  */
 +--------------------+----------------------+
 |TTTTTTTTTTTTTTTTTTTTRRRRR                  |
 +--------------------+----------------------+
 
  */
-int PmISDN::handler(void)
+void PmISDN::update_load(void)
+{
+       /* don't trigger load event if event already active */
+       if (p_m_loadtimer.active)
+               return;
+
+       schedule_timer(&p_m_loadtimer, 0, 0); /* no delay the first time */
+}
+
+static int load_timer(struct lcr_timer *timer, void *instance, int index)
+{
+       class PmISDN *isdnport = (class PmISDN *)instance;
+
+       isdnport->load_tx();
+
+       return 0;
+}
+
+void PmISDN::load_tx(void)
 {
 {
-       struct lcr_msg *message;
        int elapsed = 0;
        int ret;
        int elapsed = 0;
        int ret;
-
-       if ((ret = Port::handler()))
-               return(ret);
+       struct timeval current_time;
 
        /* get elapsed */
 
        /* get elapsed */
-       if (p_m_last_tv_sec)
-       {
-               elapsed = 8000 * (now_tv.tv_sec - p_m_last_tv_sec)
-                       + 8 * (now_tv.tv_usec/1000 - p_m_last_tv_msec);
-       } else
-       {
-               /* set clock of first process ever in this instance */
-               p_m_last_tv_sec = now_tv.tv_sec;
-               p_m_last_tv_msec = now_tv.tv_usec/1000;
-       }
-       /* process only if we have a minimum of samples, to make packets not too small */
-       if (elapsed >= ISDN_TRANSMIT
-        && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
-       {
-               /* set clock of last process! */
-               p_m_last_tv_sec = now_tv.tv_sec;
-               p_m_last_tv_msec = now_tv.tv_usec/1000;
+       gettimeofday(&current_time, NULL);
+       if (p_m_last_tv_sec) {
+               elapsed = 8000 * (current_time.tv_sec - p_m_last_tv_sec)
+                       + 8 * (current_time.tv_usec/1000 - p_m_last_tv_msec);
+       }
+       /* set clock of last process! */
+       p_m_last_tv_sec = current_time.tv_sec;
+       p_m_last_tv_msec = current_time.tv_usec/1000;
+
+       /* process only if we have samples and we are 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;
                else
                        p_m_load = 0;
 
                /* update load */
                if (elapsed < p_m_load)
                        p_m_load -= elapsed;
                else
                        p_m_load = 0;
 
-               /* to send data, tone must be active OR crypt messages must be on */
-               if ((p_tone_name[0] || p_m_crypt_msg_loops)
-                && (p_m_load < ISDN_LOAD)
-                && (p_state==PORT_STATE_CONNECT || p_m_mISDNport->tones))
-               {
-                       int tosend = ISDN_LOAD - p_m_load, length; 
+               /* to send data, tone must be on */
+               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; 
                        unsigned char buf[MISDN_HEADER_LEN+tosend];
                        struct mISDNhead *frm = (struct mISDNhead *)buf;
                        unsigned char *p = buf+MISDN_HEADER_LEN;
 
                        unsigned char buf[MISDN_HEADER_LEN+tosend];
                        struct mISDNhead *frm = (struct mISDNhead *)buf;
                        unsigned char *p = buf+MISDN_HEADER_LEN;
 
+                       /* copy inband signalling (e.g. used by ss5) */
+                       if (p_m_inband_send_on && tosend) {
+                               tosend -= inband_send(p, tosend);
+                       }
+
                        /* copy crypto loops */
                        /* copy crypto loops */
-                       while (p_m_crypt_msg_loops && tosend)
-                       {
+                       while (p_m_crypt_msg_loops && tosend) {
                                /* how much do we have to send */
                                length = p_m_crypt_msg_len - p_m_crypt_msg_current;
 
                                /* how much do we have to send */
                                length = p_m_crypt_msg_len - p_m_crypt_msg_current;
 
@@ -1307,11 +992,12 @@ int PmISDN::handler(void)
 
                                /* new position */
                                p_m_crypt_msg_current += length;
 
                                /* new position */
                                p_m_crypt_msg_current += length;
-                               if (p_m_crypt_msg_current == p_m_crypt_msg_len)
-                               {
+                               if (p_m_crypt_msg_current == p_m_crypt_msg_len) {
                                        /* next loop */
                                        p_m_crypt_msg_current = 0;
                                        p_m_crypt_msg_loops--;
                                        /* next loop */
                                        p_m_crypt_msg_current = 0;
                                        p_m_crypt_msg_loops--;
+                                       if (!p_m_crypt_msg_loops)
+                                               update_rxoff();
 //                                     puts("eine loop weniger");
                                }
 
 //                                     puts("eine loop weniger");
                                }
 
@@ -1319,45 +1005,46 @@ int PmISDN::handler(void)
                                tosend -= length;
                        }
 
                                tosend -= length;
                        }
 
+                       /* copy dov */
+                       if (p_dov_tx) {
+                               tosend -= dov_tx(p, tosend);
+                       }
+
                        /* copy tones */
                        /* copy tones */
-                       if (p_tone_name[0] && tosend)
-                       {
+                       if (p_tone_name[0] && tosend) {
                                tosend -= read_audio(p, tosend);
                        }
 
                        /* send data */
                                tosend -= read_audio(p, tosend);
                        }
 
                        /* send data */
-                       if (ISDN_LOAD - p_m_load - tosend > 0)
-                       {
+                       if (p_m_preload - p_m_load - tosend > 0) {
                                frm->prim = PH_DATA_REQ;
                                frm->id = 0;
                                frm->prim = PH_DATA_REQ;
                                frm->id = 0;
-                               ret = sendto(p_m_mISDNport->b_socket[p_m_b_index], buf, MISDN_HEADER_LEN+ISDN_LOAD-p_m_load-tosend, 0, NULL, 0);
+                               ret = sendto(p_m_mISDNport->b_sock[p_m_b_index].fd, buf, MISDN_HEADER_LEN+p_m_preload-p_m_load-tosend, 0, NULL, 0);
                                if (ret <= 0)
                                if (ret <= 0)
-                                       PERROR("Failed to send to socket %d (samples = %d)\n", p_m_mISDNport->b_socket[p_m_b_index], ISDN_LOAD-p_m_load-tosend);
-                               p_m_load += ISDN_LOAD - p_m_load - tosend;
+                                       PERROR("Failed to send to socket %d (samples = %d)\n", p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_preload-p_m_load-tosend);
+                               p_m_load += p_m_preload - p_m_load - tosend;
                        }
                }
        }
 
                        }
                }
        }
 
-       // NOTE: deletion is done by the child class
-
-       /* handle timeouts */
-       if (p_m_timeout)
-       {
-               if (p_m_timer+p_m_timeout < now_d)
-               {
-                       PDEBUG(DEBUG_ISDN, "(%s) timeout after %d seconds detected (state=%d).\n", p_name, p_m_timeout, p_state);
-                       p_m_timeout = 0;
-                       /* send timeout to endpoint */
-                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_TIMEOUT);
-                       message->param.state = p_state;
-                       message_put(message);
-                       return(1);
-               }
-       }
-       
-       return(0); /* nothing done */
+       schedule_timer(&p_m_loadtimer, 0, PORT_TRANSMIT * 125);
 }
 
 }
 
+/* handle timeouts */
+static int mISDN_timeout(struct lcr_timer *timer, void *instance, int i)
+{
+       class PmISDN *isdnport = (class PmISDN *)instance;
+       struct lcr_msg *message;
+
+       PDEBUG(DEBUG_ISDN, "(%s) timeout after %d seconds detected (state=%d).\n", isdnport->p_name, isdnport->p_m_timeout.timeout.tv_sec, isdnport->p_state);
+       /* send timeout to endpoint */
+       message = message_create(isdnport->p_serial, ACTIVE_EPOINT(isdnport->p_epointlist), PORT_TO_EPOINT, MESSAGE_TIMEOUT);
+       message->param.state = isdnport->p_state;
+       message_put(message);
+
+       return 0;
+}
+       
 
 /*
  * whenever we get audio data from bchannel, we process it here
 
 /*
  * whenever we get audio data from bchannel, we process it here
@@ -1365,32 +1052,43 @@ int PmISDN::handler(void)
 void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len)
 {
        unsigned int cont = *((unsigned int *)data);
 void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len)
 {
        unsigned int cont = *((unsigned int *)data);
-       unsigned char *data_temp;
-       unsigned int length_temp;
        struct lcr_msg *message;
        unsigned char *p;
        int l;
 
        struct lcr_msg *message;
        unsigned char *p;
        int l;
 
-       if (hh->prim == PH_CONTROL_IND)
-       {
-               if (len < 4)
-               {
+       if (hh->prim == PH_CONTROL_IND) {
+               if (len < 4) {
                        PERROR("SHORT READ OF PH_CONTROL INDICATION\n");
                        return;
                }
                        PERROR("SHORT READ OF PH_CONTROL INDICATION\n");
                        return;
                }
-               if ((cont&(~DTMF_TONE_MASK)) == DTMF_TONE_VAL)
-               {
+               if ((cont&(~DTMF_TONE_MASK)) == DTMF_TONE_VAL) {
                        chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
                        add_trace("DTMF", NULL, "%c", cont & DTMF_TONE_MASK);
                        chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
                        add_trace("DTMF", NULL, "%c", cont & DTMF_TONE_MASK);
+                       if (!p_m_dtmf)
+                               add_trace("info", NULL, "DTMF is disabled");
                        end_trace();
                        end_trace();
-                       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_m_dtmf)
+                               return;
+                       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;
                }
                        return;
                }
-               switch(cont)
-               {
+               switch(cont) {
                        case DSP_BF_REJECT:
                        chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
                        add_trace("DSP-CRYPT", NULL, "error");
                        case DSP_BF_REJECT:
                        chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
                        add_trace("DSP-CRYPT", NULL, "error");
@@ -1418,10 +1116,8 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
                }
                return;
        }
                }
                return;
        }
-       if (hh->prim == PH_CONTROL_IND)
-       {
-               switch(hh->id)
-               {
+       if (hh->prim == PH_CONTROL_IND) {
+               switch(hh->id) {
                        default:
                        chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
                        add_trace("unknown", NULL, "0x%x", hh->id);
                        default:
                        chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
                        add_trace("unknown", NULL, "0x%x", hh->id);
@@ -1429,10 +1125,8 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
                }
                return;
        }
                }
                return;
        }
-       if (hh->prim == PH_DATA_REQ || hh->prim == DL_DATA_REQ)
-       {
-               if (!p_m_txdata)
-               {
+       if (hh->prim == PH_DATA_REQ || hh->prim == DL_DATA_REQ) {
+               if (!p_m_txdata) {
                        /* if tx is off, it may happen that fifos send us pending informations, we just ignore them */
                        PDEBUG(DEBUG_BCHANNEL, "PmISDN(%s) ignoring tx data, because 'txdata' is turned off\n", p_name);
                        return;
                        /* if tx is off, it may happen that fifos send us pending informations, we just ignore them */
                        PDEBUG(DEBUG_BCHANNEL, "PmISDN(%s) ignoring tx data, because 'txdata' is turned off\n", p_name);
                        return;
@@ -1444,14 +1138,26 @@ 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
 //             printf(".");fflush(stdout);return;
                if (p_record)
                        record(data, len, 1); // from up
+               if (p_tap)
+                       tap(data, len, 1); // from up
                return;
        }
                return;
        }
-       if (hh->prim != PH_DATA_IND && hh->prim != DL_DATA_IND)
-       {
+       if (hh->prim != PH_DATA_IND && hh->prim != DL_DATA_IND) {
                PERROR("Bchannel received unknown primitve: 0x%x\n", hh->prim);
                return;
        }
 
                PERROR("Bchannel received unknown primitve: 0x%x\n", hh->prim);
                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);
+
+       /* send to remote, if bridged */
+       bridge_tx(data, len);
+
        /* calls will not process any audio data unless
         * the call is connected OR tones feature is enabled.
         */
        /* calls will not process any audio data unless
         * the call is connected OR tones feature is enabled.
         */
@@ -1470,8 +1176,7 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
 #endif
 
        /* if rx is off, it may happen that fifos send us pending informations, we just ignore them */
 #endif
 
        /* if rx is off, it may happen that fifos send us pending informations, we just ignore them */
-       if (p_m_rxoff)
-       {
+       if (p_m_rxoff) {
                PDEBUG(DEBUG_BCHANNEL, "PmISDN(%s) ignoring data, because rx is turned off\n", p_name);
                return;
        }
                PDEBUG(DEBUG_BCHANNEL, "PmISDN(%s) ignoring data, because rx is turned off\n", p_name);
                return;
        }
@@ -1479,10 +1184,11 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
        /* record data */
        if (p_record)
                record(data, len, 0); // from down
        /* 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 */
 
        /* randomize and listen to crypt message if enabled */
-       if (p_m_crypt_listen)
-       {
+       if (p_m_crypt_listen) {
                /* the noisy randomizer */
                p = data;
                l = len;
                /* the noisy randomizer */
                p = data;
                l = len;
@@ -1491,26 +1197,6 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
 
                cryptman_listen_bch(data, len);
        }
 
                cryptman_listen_bch(data, len);
        }
-
-       p = data;
-
-       /* send data to epoint */
-       if (p_m_joindata && ACTIVE_EPOINT(p_epointlist)) /* only if we have an epoint object */
-       {
-               length_temp = len;
-               data_temp = p;
-               while(length_temp)
-               {
-                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
-                       message->param.data.len = (length_temp>sizeof(message->param.data.data))?sizeof(message->param.data.data):length_temp;
-                       memcpy(message->param.data.data, data_temp, message->param.data.len);
-                       message_put(message);
-                       if (length_temp <= sizeof(message->param.data.data))
-                               break;
-                       data_temp += sizeof(message->param.data.data);
-                       length_temp -= sizeof(message->param.data.data);
-               }
-       }
 }
 
 
 }
 
 
@@ -1519,13 +1205,12 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
  */
 void PmISDN::set_echotest(int echo)
 {
  */
 void PmISDN::set_echotest(int echo)
 {
-       if (p_m_echo != echo)
-       {
+       if (p_m_echo != echo) {
                p_m_echo = echo;
                PDEBUG(DEBUG_ISDN, "we set echo to echo=%d.\n", p_m_echo);
                if (p_m_b_channel)
                        if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
                p_m_echo = echo;
                PDEBUG(DEBUG_ISDN, "we set echo to echo=%d.\n", p_m_echo);
                if (p_m_b_channel)
                        if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
-                               ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], p_m_echo?DSP_ECHO_ON:DSP_ECHO_OFF, 0, "DSP-ECHO", p_m_echo);
+                               ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_echo?DSP_ECHO_ON:DSP_ECHO_OFF, 0, "DSP-ECHO", p_m_echo);
        }
 }
 
        }
 }
 
@@ -1534,76 +1219,82 @@ void PmISDN::set_echotest(int echo)
  */
 void PmISDN::set_tone(const char *dir, const char *tone)
 {
  */
 void PmISDN::set_tone(const char *dir, const char *tone)
 {
-       int id;
+       int id = TONE_OFF;
+       int dsp = DSP_NONE;
+
+       /* if no directory is given (by extension), we use interface.conf or options.conf */
+       if (!dir || !dir[0]) {
+               if (p_m_mISDNport->ifport->tones_dir[0])
+                       dir = p_m_mISDNport->ifport->tones_dir;
+               else if (options.tones_dir[0])
+                       dir = options.tones_dir;
+       }
 
        if (!tone)
                tone = "";
        PDEBUG(DEBUG_ISDN, "isdn port now plays tone:'%s'.\n", tone);
 
        if (!tone)
                tone = "";
        PDEBUG(DEBUG_ISDN, "isdn port now plays tone:'%s'.\n", tone);
-       if (!tone[0])
-       {
+       if (!tone[0]) {
                id = TONE_OFF;
                goto setdsp;
        }
 
                id = TONE_OFF;
                goto setdsp;
        }
 
+       /* check for dsp tones */
+       if (!strcmp(dir, "american"))
+               dsp = DSP_AMERICAN;
+       if (!strcmp(dir, "german"))
+               dsp = DSP_GERMAN;
+       if (!strcmp(dir, "oldgerman"))
+               dsp = DSP_OLDGERMAN;
+
        /* check if we NOT really have to use a dsp-tone */
        /* check if we NOT really have to use a dsp-tone */
-       if (!options.dsptones)
-       {
+       if (dsp == DSP_NONE) {
                nodsp:
                if (p_m_tone)
                if (p_m_b_index > -1)
                nodsp:
                if (p_m_tone)
                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)
-               {
+               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) {
                        PDEBUG(DEBUG_ISDN, "we reset tone from id=%d to OFF.\n", p_m_tone);
                        PDEBUG(DEBUG_ISDN, "we reset tone from id=%d to OFF.\n", p_m_tone);
-                       ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], DSP_TONE_PATT_OFF, 0, "DSP-TONE", 0);
+                       ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_TONE_PATT_OFF, 0, "DSP-TONE", 0);
                }
                p_m_tone = 0;
                Port::set_tone(dir, tone);
                return;
        }
                }
                p_m_tone = 0;
                Port::set_tone(dir, tone);
                return;
        }
-       if (p_tone_dir[0])
-               goto nodsp;
 
        /* now we USE dsp-tone, convert name */
 
        /* now we USE dsp-tone, convert name */
-       else if (!strcmp(tone, "dialtone"))
-       {
-               switch(options.dsptones) {
+       if (!strcmp(tone, "dialtone")) {
+               switch(dsp) {
                case DSP_AMERICAN: id = TONE_AMERICAN_DIALTONE; break;
                case DSP_GERMAN: id = TONE_GERMAN_DIALTONE; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDDIALTONE; break;
                }
                case DSP_AMERICAN: id = TONE_AMERICAN_DIALTONE; break;
                case DSP_GERMAN: id = TONE_GERMAN_DIALTONE; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDDIALTONE; break;
                }
-       } else if (!strcmp(tone, "dialpbx"))
-       {
-               switch(options.dsptones) {
+       } else if (!strcmp(tone, "dialpbx")) {
+               switch(dsp) {
                case DSP_AMERICAN: id = TONE_AMERICAN_DIALPBX; break;
                case DSP_GERMAN: id = TONE_GERMAN_DIALPBX; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDDIALPBX; break;
                }
                case DSP_AMERICAN: id = TONE_AMERICAN_DIALPBX; break;
                case DSP_GERMAN: id = TONE_GERMAN_DIALPBX; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDDIALPBX; break;
                }
-       } else if (!strcmp(tone, "ringing"))
-       {
-               switch(options.dsptones) {
+       } else if (!strcmp(tone, "ringing")) {
+               switch(dsp) {
                case DSP_AMERICAN: id = TONE_AMERICAN_RINGING; break;
                case DSP_GERMAN: id = TONE_GERMAN_RINGING; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDRINGING; break;
                }
                case DSP_AMERICAN: id = TONE_AMERICAN_RINGING; break;
                case DSP_GERMAN: id = TONE_GERMAN_RINGING; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDRINGING; break;
                }
-       } else if (!strcmp(tone, "ringpbx"))
-       {
-               switch(options.dsptones) {
+       } else if (!strcmp(tone, "ringpbx")) {
+               switch(dsp) {
                case DSP_AMERICAN: id = TONE_AMERICAN_RINGPBX; break;
                case DSP_GERMAN: id = TONE_GERMAN_RINGPBX; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDRINGPBX; break;
                }
                case DSP_AMERICAN: id = TONE_AMERICAN_RINGPBX; break;
                case DSP_GERMAN: id = TONE_GERMAN_RINGPBX; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDRINGPBX; break;
                }
-       } else if (!strcmp(tone, "busy"))
-       {
+       } else if (!strcmp(tone, "busy")) {
                busy:
                busy:
-               switch(options.dsptones) {
+               switch(dsp) {
                case DSP_AMERICAN: id = TONE_AMERICAN_BUSY; break;
                case DSP_GERMAN: id = TONE_GERMAN_BUSY; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDBUSY; break;
                }
                case DSP_AMERICAN: id = TONE_AMERICAN_BUSY; break;
                case DSP_GERMAN: id = TONE_GERMAN_BUSY; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDBUSY; break;
                }
-       } else if (!strcmp(tone, "release"))
-       {
+       } else if (!strcmp(tone, "release")) {
                hangup:
                hangup:
-               switch(options.dsptones) {
+               switch(dsp) {
                case DSP_AMERICAN: id = TONE_AMERICAN_HANGUP; break;
                case DSP_GERMAN: id = TONE_GERMAN_HANGUP; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDHANGUP; break;
                case DSP_AMERICAN: id = TONE_AMERICAN_HANGUP; break;
                case DSP_GERMAN: id = TONE_GERMAN_HANGUP; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDHANGUP; break;
@@ -1612,9 +1303,8 @@ void PmISDN::set_tone(const char *dir, const char *tone)
                goto hangup;
        else if (!strcmp(tone, "cause_11"))
                goto busy;
                goto hangup;
        else if (!strcmp(tone, "cause_11"))
                goto busy;
-       else if (!strcmp(tone, "cause_22"))
-       {
-               switch(options.dsptones) {
+       else if (!strcmp(tone, "cause_22")) {
+               switch(dsp) {
                case DSP_AMERICAN: id = TONE_SPECIAL_INFO; break;
                case DSP_GERMAN: id = TONE_GERMAN_GASSENBESETZT; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDBUSY; break;
                case DSP_AMERICAN: id = TONE_SPECIAL_INFO; break;
                case DSP_GERMAN: id = TONE_GERMAN_GASSENBESETZT; break;
                case DSP_OLDGERMAN: id = TONE_GERMAN_OLDBUSY; break;
@@ -1629,14 +1319,13 @@ void PmISDN::set_tone(const char *dir, const char *tone)
                goto nodsp;
 
        setdsp:
                goto nodsp;
 
        setdsp:
-       if (p_m_tone != id)
-       {
+       if (p_m_tone != id) {
                /* set new tone */
                p_m_tone = id;
                PDEBUG(DEBUG_ISDN, "we set tone to id=%d.\n", p_m_tone);
                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)
                /* set new tone */
                p_m_tone = id;
                PDEBUG(DEBUG_ISDN, "we set tone to id=%d.\n", p_m_tone);
                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_socket[p_m_b_index], p_m_tone?DSP_TONE_PATT_ON:DSP_TONE_PATT_OFF, p_m_tone, "DSP-TONE", p_m_tone);
+                       ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_tone?DSP_TONE_PATT_ON:DSP_TONE_PATT_OFF, p_m_tone, "DSP-TONE", p_m_tone);
        }
        /* turn user-space tones off in cases of no tone OR dsp tone */
        Port::set_tone("",NULL);
        }
        /* turn user-space tones off in cases of no tone OR dsp tone */
        Port::set_tone("",NULL);
@@ -1647,63 +1336,41 @@ void PmISDN::set_tone(const char *dir, const char *tone)
 //extern struct lcr_msg *dddebug;
 void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union parameter *param)
 {
 //extern struct lcr_msg *dddebug;
 void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union parameter *param)
 {
-       switch(param->mISDNsignal.message)
-       {
+       int oldconf, newconf;
+       switch(param->mISDNsignal.message) {
                case mISDNSIGNAL_VOLUME:
                case mISDNSIGNAL_VOLUME:
-               if (p_m_tx_gain != param->mISDNsignal.tx_gain)
-               {
+               if (p_m_tx_gain != param->mISDNsignal.tx_gain) {
                        p_m_tx_gain = param->mISDNsignal.tx_gain;
                        PDEBUG(DEBUG_BCHANNEL, "we change tx-volume to shift=%d.\n", p_m_tx_gain);
                        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)
                        p_m_tx_gain = param->mISDNsignal.tx_gain;
                        PDEBUG(DEBUG_BCHANNEL, "we change tx-volume to shift=%d.\n", p_m_tx_gain);
                        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_socket[p_m_b_index], DSP_VOL_CHANGE_TX, p_m_tx_gain, "DSP-TX_GAIN", p_m_tx_gain);
+                               ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_VOL_CHANGE_TX, p_m_tx_gain, "DSP-TX_GAIN", p_m_tx_gain);
                } else
                        PDEBUG(DEBUG_BCHANNEL, "we already have tx-volume shift=%d.\n", p_m_rx_gain);
                } else
                        PDEBUG(DEBUG_BCHANNEL, "we already have tx-volume shift=%d.\n", p_m_rx_gain);
-               if (p_m_rx_gain != param->mISDNsignal.rx_gain)
-               {
+               if (p_m_rx_gain != param->mISDNsignal.rx_gain) {
                        p_m_rx_gain = param->mISDNsignal.rx_gain;
                        PDEBUG(DEBUG_BCHANNEL, "we change rx-volume to shift=%d.\n", p_m_rx_gain);
                        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)
                        p_m_rx_gain = param->mISDNsignal.rx_gain;
                        PDEBUG(DEBUG_BCHANNEL, "we change rx-volume to shift=%d.\n", p_m_rx_gain);
                        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_socket[p_m_b_index], DSP_VOL_CHANGE_RX, p_m_rx_gain, "DSP-RX_GAIN", p_m_rx_gain);
+                               ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_VOL_CHANGE_RX, p_m_rx_gain, "DSP-RX_GAIN", p_m_rx_gain);
                } else
                        PDEBUG(DEBUG_BCHANNEL, "we already have rx-volume shift=%d.\n", p_m_rx_gain);
                break;
 
                case mISDNSIGNAL_CONF:
                } else
                        PDEBUG(DEBUG_BCHANNEL, "we already have rx-volume shift=%d.\n", p_m_rx_gain);
                break;
 
                case mISDNSIGNAL_CONF:
-//if (dddebug) PDEBUG(DEBUG_ISDN, "dddebug = %d\n", dddebug->type);
-//tone         if (!p_m_tone && p_m_conf!=param->mISDNsignal.conf)
-               if (p_m_conf != param->mISDNsignal.conf)
-               {
-                       p_m_conf = param->mISDNsignal.conf;
-                       PDEBUG(DEBUG_BCHANNEL, "we change conference to conf=%d.\n", p_m_conf);
-                       if (p_m_b_index > -1)
-                       if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
-                               ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], (p_m_conf)?DSP_CONF_JOIN:DSP_CONF_SPLIT, p_m_conf, "DSP-CONF", p_m_conf);
-               } else
-                       PDEBUG(DEBUG_BCHANNEL, "we already have conf=%d.\n", p_m_conf);
-               /* we must set, even if currently tone forbids conf */
+               oldconf = p_m_mute?0:p_m_conf;
                p_m_conf = param->mISDNsignal.conf;
                p_m_conf = param->mISDNsignal.conf;
-//if (dddebug) PDEBUG(DEBUG_ISDN, "dddebug = %d\n", dddebug->type);
+               newconf = p_m_mute?0:p_m_conf;
+               set_conf(oldconf, newconf);
                break;
 
                break;
 
-               case mISDNSIGNAL_JOINDATA:
-               if (p_m_joindata != param->mISDNsignal.joindata)
-               {
-                       p_m_joindata = param->mISDNsignal.joindata;
-                       PDEBUG(DEBUG_BCHANNEL, "we change to joindata=%d.\n", p_m_joindata);
-               } else
-                       PDEBUG(DEBUG_BCHANNEL, "we already have joindata=%d.\n", p_m_joindata);
-               break;
-               
                case mISDNSIGNAL_DELAY:
                case mISDNSIGNAL_DELAY:
-               if (p_m_delay != param->mISDNsignal.delay)
-               {
+               if (p_m_delay != param->mISDNsignal.delay) {
                        p_m_delay = param->mISDNsignal.delay;
                        PDEBUG(DEBUG_BCHANNEL, "we change delay mode to delay=%d.\n", p_m_delay);
                        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)
                        p_m_delay = param->mISDNsignal.delay;
                        PDEBUG(DEBUG_BCHANNEL, "we change delay mode to delay=%d.\n", p_m_delay);
                        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_socket[p_m_b_index], p_m_delay?DSP_DELAY:DSP_JITTER, p_m_delay, "DSP-DELAY", p_m_delay);
+                               ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_delay?DSP_DELAY:DSP_JITTER, p_m_delay, "DSP-DELAY", p_m_delay);
                } else
                        PDEBUG(DEBUG_BCHANNEL, "we already have delay=%d.\n", p_m_delay);
                break;
                } else
                        PDEBUG(DEBUG_BCHANNEL, "we already have delay=%d.\n", p_m_delay);
                break;
@@ -1718,13 +1385,11 @@ void PmISDN::message_crypt(unsigned int epoint_id, int message_id, union paramet
 {
        struct lcr_msg *message;
 
 {
        struct lcr_msg *message;
 
-       switch(param->crypt.type)
-       {
+       switch(param->crypt.type) {
                case CC_ACTBF_REQ:           /* activate blowfish */
                p_m_crypt = 1;
                p_m_crypt_key_len = param->crypt.len;
                case CC_ACTBF_REQ:           /* activate blowfish */
                p_m_crypt = 1;
                p_m_crypt_key_len = param->crypt.len;
-               if (p_m_crypt_key_len > (int)sizeof(p_m_crypt_key))
-               {
+               if (p_m_crypt_key_len > (int)sizeof(p_m_crypt_key)) {
                        PERROR("PmISDN(%s) key too long %d > %d\n", p_name, p_m_crypt_key_len, sizeof(p_m_crypt_key));
                        message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CRYPT);
                        message->param.crypt.type = CC_ERROR_IND;
                        PERROR("PmISDN(%s) key too long %d > %d\n", p_name, p_m_crypt_key_len, sizeof(p_m_crypt_key));
                        message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CRYPT);
                        message->param.crypt.type = CC_ERROR_IND;
@@ -1736,7 +1401,7 @@ void PmISDN::message_crypt(unsigned int epoint_id, int message_id, union paramet
                PDEBUG(DEBUG_BCHANNEL, "we set encryption to crypt=%d. (0 means OFF)\n", p_m_crypt);
                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)
                PDEBUG(DEBUG_BCHANNEL, "we set encryption to crypt=%d. (0 means OFF)\n", p_m_crypt);
                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_block(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], p_m_crypt?DSP_BF_ENABLE_KEY:DSP_BF_DISABLE, p_m_crypt_key, p_m_crypt_key_len, "DSP-CRYPT", p_m_crypt_key_len);
+                       ph_control_block(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_crypt?DSP_BF_ENABLE_KEY:DSP_BF_DISABLE, p_m_crypt_key, p_m_crypt_key_len, "DSP-CRYPT", p_m_crypt_key_len);
                break;
 
                case CC_DACT_REQ:            /* deactivate session encryption */
                break;
 
                case CC_DACT_REQ:            /* deactivate session encryption */
@@ -1746,28 +1411,29 @@ void PmISDN::message_crypt(unsigned int epoint_id, int message_id, union paramet
 
                case CR_LISTEN_REQ:          /* start listening to messages */
                p_m_crypt_listen = 1;
 
                case CR_LISTEN_REQ:          /* start listening to messages */
                p_m_crypt_listen = 1;
+               update_rxoff();
                p_m_crypt_listen_state = 0;
                break;
 
                case CR_UNLISTEN_REQ:        /* stop listening to messages */
                p_m_crypt_listen = 0;
                p_m_crypt_listen_state = 0;
                break;
 
                case CR_UNLISTEN_REQ:        /* stop listening to messages */
                p_m_crypt_listen = 0;
+               update_rxoff();
                break;
 
                case CR_MESSAGE_REQ:         /* send message */
                p_m_crypt_msg_len = cryptman_encode_bch(param->crypt.data, param->crypt.len, p_m_crypt_msg, sizeof(p_m_crypt_msg));
                break;
 
                case CR_MESSAGE_REQ:         /* send message */
                p_m_crypt_msg_len = cryptman_encode_bch(param->crypt.data, param->crypt.len, p_m_crypt_msg, sizeof(p_m_crypt_msg));
-               if (!p_m_crypt_msg_len)
-               {
+               if (!p_m_crypt_msg_len) {
                        PERROR("PmISDN(%s) message too long %d > %d\n", p_name, param->crypt.len-1, sizeof(p_m_crypt_msg));
                        break;
                }
                p_m_crypt_msg_current = 0; /* reset */
                p_m_crypt_msg_loops = 6; /* enable */
                        PERROR("PmISDN(%s) message too long %d > %d\n", p_name, param->crypt.len-1, sizeof(p_m_crypt_msg));
                        break;
                }
                p_m_crypt_msg_current = 0; /* reset */
                p_m_crypt_msg_loops = 6; /* enable */
+               update_rxoff();
 #if 0
                /* disable txmix, or we get corrupt data due to audio process */
 #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)
-               {
+               if (p_m_txmix && p_m_b_index>=0 && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) {
                        PDEBUG(DEBUG_BCHANNEL, "for sending CR_MESSAGE_REQ, we reset txmix from txmix=%d.\n", p_m_txmix);
                        PDEBUG(DEBUG_BCHANNEL, "for sending CR_MESSAGE_REQ, we reset txmix from txmix=%d.\n", p_m_txmix);
-                       ph_control(p_m_mISDNport, this, p_mISDNport->b_socket[p_m_b_index], DSP_MIX_OFF, 0, "DSP-TXMIX", 0);
+                       ph_control(p_m_mISDNport, this, p_mISDNport->b_sock[p_m_b_index].fd, DSP_MIX_OFF, 0, "DSP-TXMIX", 0);
                }
 #endif
                break;
                }
 #endif
                break;
@@ -1778,184 +1444,150 @@ void PmISDN::message_crypt(unsigned int epoint_id, int message_id, union paramet
 
 }
 
 
 }
 
+/* MESSAGE_VOOTP */
+void PmISDN::message_vootp(unsigned int epoint_id, int message_id, union parameter *param)
+{
+       struct lcr_msg *message;
+
+       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_UPDATEBRIDGE);
+       message_put(message);
+
+#if 0
+does not make sense, since remote port may dejitter
+       if (param->vootp.enable) {
+               PDEBUG(DEBUG_ISDN, "PmISDN(%s) received vootp enable order, so we disable de-jitter.\n", p_name);
+               p_m_disable_dejitter = 1;
+       }
+#endif
+       update_rxoff();
+}
+
 /*
  * endpoint sends messages to the port
  */
 int PmISDN::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
 {
 /*
  * endpoint sends messages to the port
  */
 int PmISDN::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
 {
-       if (Port::message_epoint(epoint_id, message_id, param))
-               return(1);
+       /* messages also handled by base class */
+       switch(message_id) {
+               case MESSAGE_VOOTP: /* crypt control command */
+               PDEBUG(DEBUG_ISDN, "PmISDN(%s) received VoOTP encryption\n", p_name);
+               message_vootp(epoint_id, message_id, param);
+               break;
+       }
 
 
-       switch(message_id)
-       {
-               case MESSAGE_DATA: /* tx-data from upper layer */
-               txfromup(param->data.data, param->data.len);
-               return(1);
+       if (Port::message_epoint(epoint_id, message_id, param)) {
+               if (message_id == MESSAGE_BRIDGE)
+                       update_rxoff();
+               return 1;
+       }
 
 
+       /* messages not handled by base class */
+       switch(message_id) {
                case MESSAGE_mISDNSIGNAL: /* user command */
                PDEBUG(DEBUG_ISDN, "PmISDN(%s) received special ISDN SIGNAL %d.\n", p_name, param->mISDNsignal.message);
                message_mISDNsignal(epoint_id, message_id, param);
                case MESSAGE_mISDNSIGNAL: /* user command */
                PDEBUG(DEBUG_ISDN, "PmISDN(%s) received special ISDN SIGNAL %d.\n", p_name, param->mISDNsignal.message);
                message_mISDNsignal(epoint_id, message_id, param);
-               return(1);
+               return 1;
 
                case MESSAGE_CRYPT: /* crypt control command */
                PDEBUG(DEBUG_ISDN, "PmISDN(%s) received encryption command '%d'.\n", p_name, param->crypt.type);
                message_crypt(epoint_id, message_id, param);
 
                case MESSAGE_CRYPT: /* crypt control command */
                PDEBUG(DEBUG_ISDN, "PmISDN(%s) received encryption command '%d'.\n", p_name, param->crypt.type);
                message_crypt(epoint_id, message_id, param);
-               return(1);
+               return 1;
+
+               case MESSAGE_DISABLE_DEJITTER:
+               PDEBUG(DEBUG_ISDN, "PmISDN(%s) received de-jitter disable order.\n", p_name);
+               p_m_disable_dejitter = 1;
+               p_m_preload = param->queue;
+               update_rxoff();
+               return 1;
        }
 
        }
 
-       return(0);
+       return 0;
 }
 
 }
 
+void PmISDN::update_rxoff(void)
+{
+       int tx_dejitter = 0;
+
+       /* call bridges in user space OR crypto OR recording */
+       if (p_bridge || p_m_crypt_msg_loops || p_m_crypt_listen || p_record || p_tap || p_m_inband_receive_on || p_dov_rx) {
+               /* rx IS required */
+               if (p_m_rxoff) {
+                       /* turn on RX */
+                       p_m_rxoff = 0;
+                       PDEBUG(DEBUG_BCHANNEL, "%s: receive data is required, so we turn them on\n", __FUNCTION__);
+                       if (p_m_b_index > -1)
+                               if (p_m_mISDNport->b_port[p_m_b_index] && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
+                                       ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_RECEIVE_ON, 0, "DSP-RXOFF", 0);
+               }
+       } else {
+               /* rx NOT required */
+               if (!p_m_rxoff) {
+                       /* turn off RX */
+                       p_m_rxoff = 1;
+                       PDEBUG(DEBUG_BCHANNEL, "%s: receive data is not required, so we turn them off\n", __FUNCTION__);
+                       if (p_m_b_index > -1)
+                               if (p_m_mISDNport->b_port[p_m_b_index] && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
+                                       ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_RECEIVE_OFF, 0, "DSP-RXOFF", 1);
+               }
+       }
+       /* recording / tapping */
+       if (p_record || p_tap) {
+               /* txdata IS required */
+               if (!p_m_txdata) {
+                       /* turn on RX */
+                       p_m_txdata = 1;
+                       PDEBUG(DEBUG_BCHANNEL, "%s: transmit data is required, so we turn them on\n", __FUNCTION__);
+                       if (p_m_b_index > -1)
+                               if (p_m_mISDNport->b_port[p_m_b_index] && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
+                                       ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_TXDATA_ON, 0, "DSP-TXDATA", 1);
+               }
+       } else {
+               /* txdata NOT required */
+               if (p_m_txdata) {
+                       /* turn off RX */
+                       p_m_txdata = 0;
+                       PDEBUG(DEBUG_BCHANNEL, "%s: transmit data is not required, so we turn them off\n", __FUNCTION__);
+                       if (p_m_b_index > -1)
+                               if (p_m_mISDNport->b_port[p_m_b_index] && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE)
+                                       ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_TXDATA_OFF, 0, "DSP-TXDATA", 0);
+               }
+       }
+       /* dejitter on bridge */
+       if (p_bridge && !p_m_disable_dejitter)
+               tx_dejitter = 1;
+       if (p_m_tx_dejitter != tx_dejitter) {
+               p_m_tx_dejitter = 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);
+       }
+}
 
 
-/*
- * main loop for processing messages from mISDN
- */
-int mISDN_handler(void)
+static int mISDN_upqueue(struct lcr_fd *fd, unsigned int what, void *instance, int i)
 {
 {
-       int ret, work = 0;
        struct mISDNport *mISDNport;
        struct mISDNport *mISDNport;
-       class PmISDN *isdnport;
-       int i;
-       unsigned char buffer[2048+MISDN_HEADER_LEN];
-       struct mISDNhead *hh = (struct mISDNhead *)buffer;
        struct mbuffer *mb;
        struct l3_msg *l3m;
        struct mbuffer *mb;
        struct l3_msg *l3m;
+       char byte;
+       int __attribute__((__unused__)) ret;
+
+       /* unset global semaphore */
+       upqueue_avail = 0;
+       // with a very small incident, upqueue_avail may be set by mISDN thread and
+       // another byte may be sent to the pipe, which causes a call to this function
+       // again with nothing in the upqueue. this is no problem.
+       ret = read(fd->fd, &byte, 1);
 
        /* process all ports */
        mISDNport = mISDNport_first;
 
        /* process all ports */
        mISDNport = mISDNport_first;
-       while(mISDNport)
-       {
-               /* process all bchannels */
-               i = 0;
-               while(i < mISDNport->b_num)
-               {
-                       /* process timer events for bchannel handling */
-                       if (mISDNport->b_timer[i])
-                       {
-                               if (mISDNport->b_timer[i] <= now_d)
-                                       bchannel_event(mISDNport, i, B_EVENT_TIMEOUT);
-                       }
-                       /* handle port of bchannel */
-                       isdnport=mISDNport->b_port[i];
-                       if (isdnport)
-                       {
-                               /* call bridges in user space OR crypto OR recording */
-                               if (isdnport->p_m_joindata || isdnport->p_m_crypt_msg_loops || isdnport->p_m_crypt_listen || isdnport->p_record)
-                               {
-                                       /* rx IS required */
-                                       if (isdnport->p_m_rxoff)
-                                       {
-                                               /* turn on RX */
-                                               isdnport->p_m_rxoff = 0;
-                                               PDEBUG(DEBUG_BCHANNEL, "%s: receive data is required, so we turn them on\n", __FUNCTION__);
-                                               if (mISDNport->b_port[i] && mISDNport->b_state[i] == B_STATE_ACTIVE)
-                                                       ph_control(mISDNport, isdnport, mISDNport->b_socket[i], DSP_RECEIVE_ON, 0, "DSP-RXOFF", 0);
-                                               return(1);
-                                       }
-                               } else
-                               {
-                                       /* rx NOT required */
-                                       if (!isdnport->p_m_rxoff)
-                                       {
-                                               /* turn off RX */
-                                               isdnport->p_m_rxoff = 1;
-                                               PDEBUG(DEBUG_BCHANNEL, "%s: receive data is not required, so we turn them off\n", __FUNCTION__);
-                                               if (mISDNport->b_port[i] && mISDNport->b_state[i] == B_STATE_ACTIVE)
-                                                       ph_control(mISDNport, isdnport, mISDNport->b_socket[i], DSP_RECEIVE_OFF, 0, "DSP-RXOFF", 1);
-                                               return(1);
-                                       }
-                               }
-                               /* recording */
-                               if (isdnport->p_record)
-                               {
-                                       /* txdata IS required */
-                                       if (!isdnport->p_m_txdata)
-                                       {
-                                               /* turn on RX */
-                                               isdnport->p_m_txdata = 1;
-                                               PDEBUG(DEBUG_BCHANNEL, "%s: transmit data is required, so we turn them on\n", __FUNCTION__);
-                                               if (mISDNport->b_port[i] && mISDNport->b_state[i] == B_STATE_ACTIVE)
-                                                       ph_control(mISDNport, isdnport, mISDNport->b_socket[i], DSP_TXDATA_ON, 0, "DSP-TXDATA", 1);
-                                               return(1);
-                                       }
-                               } else
-                               {
-                                       /* txdata NOT required */
-                                       if (isdnport->p_m_txdata)
-                                       {
-                                               /* turn off RX */
-                                               isdnport->p_m_txdata = 0;
-                                               PDEBUG(DEBUG_BCHANNEL, "%s: transmit data is not required, so we turn them off\n", __FUNCTION__);
-                                               if (mISDNport->b_port[i] && mISDNport->b_state[i] == B_STATE_ACTIVE)
-                                                       ph_control(mISDNport, isdnport, mISDNport->b_socket[i], DSP_TXDATA_OFF, 0, "DSP-TXDATA", 0);
-                                               return(1);
-                                       }
-                               }
-                       }
-
-                       /* handle message from bchannel */
-                       if (mISDNport->b_socket[i] > -1)
-                       {
-                               ret = recv(mISDNport->b_socket[i], buffer, sizeof(buffer), 0);
-                               if (ret >= (int)MISDN_HEADER_LEN)
-                               {
-                                       work = 1;
-                                       switch(hh->prim)
-                                       {
-                                               /* we don't care about confirms, we use rx data to sync tx */
-                                               case PH_DATA_CNF:
-                                               break;
-
-                                               /* we receive audio data, we respond to it AND we send tones */
-                                               case PH_DATA_IND:
-                                               case DL_DATA_IND:
-                                               case PH_DATA_REQ:
-                                               case DL_DATA_REQ:
-                                               case PH_CONTROL_IND:
-                                               if (mISDNport->b_port[i])
-                                                       mISDNport->b_port[i]->bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN);
-                                               else
-                                                       PDEBUG(DEBUG_BCHANNEL, "b-channel is not associated to an ISDNPort (socket %d), ignoring.\n", mISDNport->b_socket[i]);
-                                               break;
-
-                                               case PH_ACTIVATE_IND:
-                                               case DL_ESTABLISH_IND:
-                                               case PH_ACTIVATE_CNF:
-                                               case DL_ESTABLISH_CNF:
-                                               PDEBUG(DEBUG_BCHANNEL, "DL_ESTABLISH confirm: bchannel is now activated (socket %d).\n", mISDNport->b_socket[i]);
-                                               bchannel_event(mISDNport, i, B_EVENT_ACTIVATED);
-                                               break;
-
-                                               case PH_DEACTIVATE_IND:
-                                               case DL_RELEASE_IND:
-                                               case PH_DEACTIVATE_CNF:
-                                               case DL_RELEASE_CNF:
-                                               PDEBUG(DEBUG_BCHANNEL, "DL_RELEASE confirm: bchannel is now de-activated (socket %d).\n", mISDNport->b_socket[i]);
-                                               bchannel_event(mISDNport, i, B_EVENT_DEACTIVATED);
-                                               break;
-
-                                               default:
-                                               PERROR("child message not handled: prim(0x%x) socket(%d) msg->len(%d)\n", hh->prim, mISDNport->b_socket[i], ret-MISDN_HEADER_LEN);
-                                       }
-                               } else
-                               {
-                                       if (ret < 0 && errno != EWOULDBLOCK)
-                                               PERROR("Read from port %d, index %d failed with return code %d\n", mISDNport->portnum, i, ret);
-                               }
-                       }
-                       
-                       i++;
-               }
-
+       while(mISDNport) {
                /* handle queued up-messages (d-channel) */
                /* handle queued up-messages (d-channel) */
-               while ((mb = mdequeue(&mISDNport->upqueue)))
-               {
+               while ((mb = mdequeue(&mISDNport->upqueue))) {
                        l3m = &mb->l3;
                        l3m = &mb->l3;
-                       switch(l3m->type)
-                       {
+                       switch(l3m->type) {
                                case MPH_ACTIVATE_IND:
                                case MPH_ACTIVATE_IND:
-                               if (mISDNport->l1link != 1)
-                               {
+                               if (mISDNport->l1link != 1) {
                                        l1l2l3_trace_header(mISDNport, NULL, L1_ACTIVATE_IND, DIRECTION_IN);
                                        end_trace();
                                        mISDNport->l1link = 1;
                                        l1l2l3_trace_header(mISDNport, NULL, L1_ACTIVATE_IND, DIRECTION_IN);
                                        end_trace();
                                        mISDNport->l1link = 1;
@@ -1963,8 +1595,7 @@ int mISDN_handler(void)
                                break;
        
                                case MPH_DEACTIVATE_IND:
                                break;
        
                                case MPH_DEACTIVATE_IND:
-                               if (mISDNport->l1link != 0)
-                               {
+                               if (mISDNport->l1link != 0) {
                                        l1l2l3_trace_header(mISDNport, NULL, L1_DEACTIVATE_IND, DIRECTION_IN);
                                        end_trace();
                                        mISDNport->l1link = 0;
                                        l1l2l3_trace_header(mISDNport, NULL, L1_DEACTIVATE_IND, DIRECTION_IN);
                                        end_trace();
                                        mISDNport->l1link = 0;
@@ -1973,8 +1604,7 @@ int mISDN_handler(void)
 
                                case MPH_INFORMATION_IND:
                                PDEBUG(DEBUG_ISDN, "Received MPH_INFORMATION_IND for port %d (%s).\n", mISDNport->portnum, mISDNport->ifport->interface->name);
 
                                case MPH_INFORMATION_IND:
                                PDEBUG(DEBUG_ISDN, "Received MPH_INFORMATION_IND for port %d (%s).\n", mISDNport->portnum, mISDNport->ifport->interface->name);
-                               switch (l3m->pid)
-                               {
+                               switch (l3m->pid) {
                                        case L1_SIGNAL_LOS_ON:
                                        mISDNport->los = 1;
                                        break;
                                        case L1_SIGNAL_LOS_ON:
                                        mISDNport->los = 1;
                                        break;
@@ -2006,31 +1636,32 @@ int mISDN_handler(void)
                                l1l2l3_trace_header(mISDNport, NULL, L2_ESTABLISH_IND, DIRECTION_IN);
                                add_trace("tei", NULL, "%d", l3m->pid);
                                end_trace();
                                l1l2l3_trace_header(mISDNport, NULL, L2_ESTABLISH_IND, DIRECTION_IN);
                                add_trace("tei", NULL, "%d", l3m->pid);
                                end_trace();
-                               if ((!mISDNport->ntmode || mISDNport->ptp) && l3m->pid < 127)
-                               {
-                                       if (mISDNport->l2establish)
-                                       {
-                                               mISDNport->l2establish = 0;
+                               mISDNport->l2link = 1;
+                               if (l3m->pid < 128)
+                                       mISDNport->l2mask[l3m->pid >> 3] |= (1 << (l3m->pid & 7));
+                               if ((!mISDNport->ntmode || mISDNport->ptp) && l3m->pid < 127) {
+                                       if (mISDNport->l2establish.active) {
+                                               unsched_timer(&mISDNport->l2establish);
                                                PDEBUG(DEBUG_ISDN, "the link became active before l2establish timer expiry.\n");
                                        }
                                                PDEBUG(DEBUG_ISDN, "the link became active before l2establish timer expiry.\n");
                                        }
-                                       mISDNport->l2link = 1;
                                }
                                break;
 
                                case MT_L2RELEASE:
                                }
                                break;
 
                                case MT_L2RELEASE:
-                               if (!mISDNport->l2establish)
-                               {
+                               if (l3m->pid < 128)
+                                       mISDNport->l2mask[l3m->pid >> 3] &= ~(1 << (l3m->pid & 7));
+                               if (!mISDNport->l2establish.active) {
                                        l1l2l3_trace_header(mISDNport, NULL, L2_RELEASE_IND, DIRECTION_IN);
                                        add_trace("tei", NULL, "%d", l3m->pid);
                                        end_trace();
                                        l1l2l3_trace_header(mISDNport, NULL, L2_RELEASE_IND, DIRECTION_IN);
                                        add_trace("tei", NULL, "%d", l3m->pid);
                                        end_trace();
+                                       /* down if not nt-ptmp */ 
+                                       if (!mISDNport->ntmode || mISDNport->ptp)
+                                               mISDNport->l2link = 0;
                                }
                                }
-                               if ((!mISDNport->ntmode || mISDNport->ptp) && l3m->pid < 127)
-                               {
-                                       mISDNport->l2link = 0;
-                                       if (!mISDNport->l2establish && mISDNport->l2hold)
-                                       {
+                               if ((!mISDNport->ntmode || mISDNport->ptp) && l3m->pid < 127) {
+                                       if (!mISDNport->l2establish.active && mISDNport->l2hold) {
                                                PDEBUG(DEBUG_ISDN, "set timer and establish.\n");
                                                PDEBUG(DEBUG_ISDN, "set timer and establish.\n");
-                                               time(&mISDNport->l2establish);
+                                               schedule_timer(&mISDNport->l2establish, 5, 0);
                                                mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL);
                                        }
                                }
                                                mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL);
                                        }
                                }
@@ -2043,39 +1674,93 @@ int mISDN_handler(void)
                        /* free message */
                        free_l3_msg(l3m);
                }
                        /* free message */
                        free_l3_msg(l3m);
                }
+               mISDNport = mISDNport->next;
+       }
+       return 0;
+}
 
 
-#if 0
-               if (mISDNport->l1timeout && now>mISDNport->l1timeout)
-               { ---}
-                       PDEBUG(DEBUG_ISDN, "the L1 establish timer expired, we release all pending messages.\n", mISDNport->portnum);
-                       mISDNport->l1timeout = 0;
-#endif
+/* l2 establish timer fires */
+static int l2establish_timeout(struct lcr_timer *timer, void *instance, int i)
+{
+       struct mISDNport *mISDNport = (struct mISDNport *)instance;
 
 
-               /* layer 2 establish timer */
-               if (mISDNport->l2establish)
-               {
-                       if (now-mISDNport->l2establish > 5)
-                       {
-                               mISDNport->l2establish = 0;
-                               if (mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode))
-                               {
-
-                                       PDEBUG(DEBUG_ISDN, "the L2 establish timer expired, we try to establish the link portnum=%d.\n", mISDNport->portnum);
-                                       mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL);
-                                       time(&mISDNport->l2establish);
-                                       return(1);
-                               }
-                       }
-               }
+       if (mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) {
+               PDEBUG(DEBUG_ISDN, "the L2 establish timer expired, we try to establish the link portnum=%d.\n", mISDNport->portnum);
+               mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL);
+               schedule_timer(&mISDNport->l2establish, 5, 0); /* 5 seconds */
+       }
 
 
+       return 0;
+}
 
 
-               mISDNport = mISDNport->next;
+/* handle frames from bchannel */
+static int b_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;
+       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) {
+               /* we don't care about confirms, we use rx data to sync tx */
+               case PH_DATA_CNF:
+               break;
+
+               /* we receive audio data, we respond to it AND we send tones */
+               case PH_DATA_IND:
+               case DL_DATA_IND:
+               case PH_DATA_REQ:
+               case DL_DATA_REQ:
+               case PH_CONTROL_IND:
+               if (mISDNport->b_port[i])
+                       mISDNport->b_port[i]->bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN);
+               else
+                       PDEBUG(DEBUG_BCHANNEL, "b-channel is not associated to an ISDNPort (socket %d), ignoring.\n", fd->fd);
+               break;
+
+               case PH_ACTIVATE_IND:
+               case DL_ESTABLISH_IND:
+               case PH_ACTIVATE_CNF:
+               case DL_ESTABLISH_CNF:
+               PDEBUG(DEBUG_BCHANNEL, "DL_ESTABLISH confirm: bchannel is now activated (socket %d).\n", fd->fd);
+               bchannel_event(mISDNport, i, B_EVENT_ACTIVATED);
+               break;
+
+               case PH_DEACTIVATE_IND:
+               case DL_RELEASE_IND:
+               case PH_DEACTIVATE_CNF:
+               case DL_RELEASE_CNF:
+               PDEBUG(DEBUG_BCHANNEL, "DL_RELEASE confirm: bchannel is now de-activated (socket %d).\n", fd->fd);
+               bchannel_event(mISDNport, i, B_EVENT_DEACTIVATED);
+               break;
+
+               default:
+               PERROR("child message not handled: prim(0x%x) socket(%d) msg->len(%d)\n", hh->prim, fd->fd, ret-MISDN_HEADER_LEN);
        }
 
        }
 
-       /* if we received at least one b-frame, we will return 1 */
-       return(work);
+       return 0;
 }
 
 }
 
+/* process timer events for bchannel handling */
+static int b_timer_timeout(struct lcr_timer *timer, void *instance, int i)
+{
+       struct mISDNport *mISDNport = (struct mISDNport *)instance;
+
+       bchannel_event(mISDNport, i, B_EVENT_TIMEOUT);
+
+       return 0;
+}
+
+
 int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3_msg *l3m)
 {
        /* IMPORTAINT:
 int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3_msg *l3m)
 {
        /* IMPORTAINT:
@@ -2086,6 +1771,7 @@ int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3
        struct mISDNport *mISDNport = (struct mISDNport *)ml3->priv;
        struct mbuffer *mb;
 
        struct mISDNport *mISDNport = (struct mISDNport *)ml3->priv;
        struct mbuffer *mb;
 
+#ifdef OLD_MT_ASSIGN
        /* special MT_ASSIGN handling:
         *
         * if we request a PID from mlayer, we always do it while lcr is locked.
        /* special MT_ASSIGN handling:
         *
         * if we request a PID from mlayer, we always do it while lcr is locked.
@@ -2096,18 +1782,16 @@ int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3
         * we must check if we get a reply and we know that we lcr is currently
         * locked.
         */
         * we must check if we get a reply and we know that we lcr is currently
         * locked.
         */
-       if (cmd==MT_ASSIGN && (pid&MISDN_PID_CR_FLAG) && (pid>>16)==MISDN_CES_MASTER)
-       {
+       if (cmd==MT_ASSIGN && (pid&MISDN_PID_CR_FLAG) && (pid>>16)==MISDN_CES_MASTER) {
                /* let's do some checking if someone changes stack behaviour */
                if (mt_assign_pid != 0)
                        FATAL("someone played with the mISDNuser stack. MT_ASSIGN not currently expected.\n");
                mt_assign_pid = pid;
                return(0);
        }
                /* let's do some checking if someone changes stack behaviour */
                if (mt_assign_pid != 0)
                        FATAL("someone played with the mISDNuser stack. MT_ASSIGN not currently expected.\n");
                mt_assign_pid = pid;
                return(0);
        }
-       
+#endif
        /* queue message, create, if required */
        /* queue message, create, if required */
-       if (!l3m)
-       {
+       if (!l3m) {
                l3m = alloc_l3_msg();
                if (!l3m)
                        FATAL("No memory for layer 3 message\n");
                l3m = alloc_l3_msg();
                if (!l3m)
                        FATAL("No memory for layer 3 message\n");
@@ -2116,17 +1800,91 @@ int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3
        l3m->type = cmd;
        l3m->pid = pid;
        mqueue_tail(&mISDNport->upqueue, mb);
        l3m->type = cmd;
        l3m->pid = pid;
        mqueue_tail(&mISDNport->upqueue, mb);
+       if (!upqueue_avail) {
+               // multiple threads may cause multiple calls of this section, but this
+               // results only in multiple processing of the upqueue read.
+               // this is no problem.
+               upqueue_avail = 1;
+               char byte = 0;
+               int __attribute__((__unused__)) ret;
+               ret = write(upqueue_pipe[1], &byte, 1);
+       }
        return 0;
 }
 
        return 0;
 }
 
+int mISDN_getportbyname(int sock, int cnt, char *portname)
+{
+       struct mISDN_devinfo devinfo;
+       int port = 0, ret;
+
+       /* resolve name */
+       while (port < cnt) {
+               devinfo.id = port;
+               ret = ioctl(sock, IMGETDEVINFO, &devinfo);
+               if (ret < 0)
+                       return ret;
+               if (!strcasecmp(devinfo.name, portname))
+                       break;
+               port++;
+       }
+       if (port == cnt)
+               return -EIO;
+
+       return (port);
+}
+
+#ifdef ISDN_P_FXS_POTS
+/* 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;
+}
+#endif
 
 /*
  * global function to add a new card (port)
  */
 
 /*
  * global function to add a new card (port)
  */
-struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt, int l2hold, struct interface *interface)
+struct mISDNport *mISDNport_open(struct interface_port *ifport)
 {
        int ret;
        struct mISDNport *mISDNport, **mISDNportp;
 {
        int ret;
        struct mISDNport *mISDNport, **mISDNportp;
+       int port = ifport->portnum;
+       int ptp = ifport->ptp;
+       int force_nt = ifport->nt;
+       int l1hold = ifport->l1hold;
+       int l2hold = ifport->l2hold;
+       int ss5 = ifport->ss5;
        int i, cnt;
        int pri, bri, pots;
        int nt, te;
        int i, cnt;
        int pri, bri, pots;
        int nt, te;
@@ -2136,43 +1894,24 @@ struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt
 
        /* check port counts */
        ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt);
 
        /* check port counts */
        ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt);
-       if (ret < 0)
-       {
+       if (ret < 0) {
                fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret);
                return(NULL);
        }
 
                fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret);
                return(NULL);
        }
 
-       if (cnt <= 0)
-       {
+       if (cnt <= 0) {
                PERROR_RUNTIME("Found no card. Please be sure to load card drivers.\n");
                return(NULL);
        }
                PERROR_RUNTIME("Found no card. Please be sure to load card drivers.\n");
                return(NULL);
        }
-       if (port < 0)
-       {
-               /* resolve name */
-               port = 0;
-               while (port < cnt)
-               {
-                       devinfo.id = port;
-                       ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo);
-                       if (ret < 0)
-                       {
-                               PERROR_RUNTIME("Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", port, ret);
-                               return(NULL);
-                       }
-                       if (!strcasecmp(devinfo.name, portname))
-                               break;
-                       port++;
-               }
-               if (port == cnt)
-               {
-                       PERROR_RUNTIME("Port name '%s' not found, use 'misdn_info' tool to list all existing ports.\n", portname);
+       if (port < 0) {
+               port = mISDN_getportbyname(mISDNsocket, cnt, ifport->portname);
+               if (port < 0) {
+                       PERROR_RUNTIME("Port name '%s' not found, use 'misdn_info' tool to list all existing ports.\n", ifport->portname);
                        return(NULL);
                }
                // note: 'port' has still the port number
        }
                        return(NULL);
                }
                // note: 'port' has still the port number
        }
-       if (port>cnt || port<0)
-       {
+       if (port>cnt || port<0) {
                PERROR_RUNTIME("Port (%d) given at 'ports' (options.conf) is out of existing port range (%d-%d)\n", port, 0, cnt);
                return(NULL);
        }
                PERROR_RUNTIME("Port (%d) given at 'ports' (options.conf) is out of existing port range (%d-%d)\n", port, 0, cnt);
                return(NULL);
        }
@@ -2181,67 +1920,52 @@ struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt
        pri = bri = pots = nt = te = 0;
        devinfo.id = port;
        ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo);
        pri = bri = pots = nt = te = 0;
        devinfo.id = port;
        ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo);
-       if (ret < 0)
-       {
+       if (ret < 0) {
                PERROR_RUNTIME("Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", port, ret);
                return(NULL);
        }
                PERROR_RUNTIME("Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", port, ret);
                return(NULL);
        }
-       if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0))
-       {
+       if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) {
                bri = 1;
                te = 1;
        }
                bri = 1;
                te = 1;
        }
-       if (devinfo.Dprotocols & (1 << ISDN_P_NT_S0))
-       {
+       if (devinfo.Dprotocols & (1 << ISDN_P_NT_S0)) {
                bri = 1;
                nt = 1;
        }
                bri = 1;
                nt = 1;
        }
-       if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1))
-       {
+       if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) {
                pri = 1;
                te = 1;
        }
                pri = 1;
                te = 1;
        }
-       if (devinfo.Dprotocols & (1 << ISDN_P_NT_E1))
-       {
+       if (devinfo.Dprotocols & (1 << ISDN_P_NT_E1)) {
                pri = 1;
                nt = 1;
        }
                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;
        }
                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
                pots = 1;
                nt = 1;
        }
 #endif
-       if (force_nt && !nt)
-       {
-               PERROR_RUNTIME("Port %d does not support NT-mode\n", port);
+       if (force_nt && !nt) {
+               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);
        }
                return(NULL);
        }
-       if (bri && pri)
-       {
+       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);
        }
                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);
+       if (!bri && !pri && !pots) {
+               PERROR_RUNTIME("Port %d does not support BRI nor PRI nor POTS!\n", port);
                return(NULL);
        }
                return(NULL);
        }
-       if (!bri && !pri)
-       {
-               PERROR_RUNTIME("Port %d does not support BRI nor PRI!\n", port);
-               return(NULL);
-       }
-       if (!nt && !te)
-       {
+       if (!nt && !te) {
                PERROR_RUNTIME("Port %d does not support NT-mode nor TE-mode!\n", port);
                return(NULL);
        }
                PERROR_RUNTIME("Port %d does not support NT-mode nor TE-mode!\n", port);
                return(NULL);
        }
@@ -2251,41 +1975,75 @@ struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt
        /* if TE an NT is supported (and not forced to NT), turn off NT */
        if (te && nt)
                nt = 0;
        /* 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 */
 
        /* check for double use of port */
-       if (nt)
-       {
+       if (nt) {
                mISDNport = mISDNport_first;
                mISDNport = mISDNport_first;
-               while(mISDNport)
-               {
+               while(mISDNport) {
                        if (mISDNport->portnum == port)
                                break;
                        mISDNport = mISDNport->next;
                }
                        if (mISDNport->portnum == port)
                                break;
                        mISDNport = mISDNport->next;
                }
-               if (mISDNport)
-               {
+               if (mISDNport) {
                        PERROR_RUNTIME("Port %d already in use by LCR. You can't use a NT port multiple times.\n", port);
                        return(NULL);
                }
        }
 
                        PERROR_RUNTIME("Port %d already in use by LCR. You can't use a NT port multiple times.\n", port);
                        return(NULL);
                }
        }
 
+       /* check for continous channelmap with no bchannel on slot 16 */
+       if (test_channelmap(0, devinfo.channelmap)) {
+               PERROR_RUNTIME("Port %d provides channel 0, but we cannot access it!\n", port);
+               return(NULL);
+       }
+       i = 1;
+       while(i < (int)devinfo.nrbchan + 1) {
+               if (i == 16) {
+                       if (test_channelmap(i, devinfo.channelmap)) {
+                               PERROR("Port %d provides bchannel 16. Pleas upgrade mISDN, if this port is mISDN loopback interface.\n", port);
+                               return(NULL);
+                       }
+               } else {
+                       if (!test_channelmap(i, devinfo.channelmap)) {
+                               PERROR_RUNTIME("Port %d has no channel on slot %d!\n", port, i);
+                               return(NULL);
+                       }
+               }
+               i++;
+       }
 
        /* add mISDNport structure */
        mISDNportp = &mISDNport_first;
        while(*mISDNportp)
                mISDNportp = &((*mISDNportp)->next);
        mISDNport = (struct mISDNport *)MALLOC(sizeof(struct mISDNport));
 
        /* add mISDNport structure */
        mISDNportp = &mISDNport_first;
        while(*mISDNportp)
                mISDNportp = &((*mISDNportp)->next);
        mISDNport = (struct mISDNport *)MALLOC(sizeof(struct mISDNport));
-       mISDNport->l1link = -1;
+       add_timer(&mISDNport->l2establish, l2establish_timeout, mISDNport, 0);
+       if (ss5) {
+               /* ss5 link is always active */
+               mISDNport->l1link = 1;
+               mISDNport->l2link = 1;
+       } else {
+               mISDNport->l1link = -1;
+               mISDNport->l2link = -1;
+       }
        pmemuse++;
        *mISDNportp = mISDNport;
 
        /* if pri, must set PTP */
        if (pri)
                ptp = 1;
        pmemuse++;
        *mISDNportp = mISDNport;
 
        /* if pri, must set PTP */
        if (pri)
                ptp = 1;
-       
+
+       /* set ss5 params */
+       if (ss5) {
+               /* try to keep interface enabled */
+               l1hold = 1;
+               l2hold = 0;
+       }
        /* set l2hold */
        /* set l2hold */
-       switch (l2hold)
-       {
+       switch (l2hold) {
                case -1: // off
                l2hold = 0;
                break;
                case -1: // off
                l2hold = 0;
                break;
@@ -2301,98 +2059,154 @@ struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt
        }
                
        /* allocate ressources of port */
        }
                
        /* allocate ressources of port */
-       /* open layer 3 and init upqueue */
-       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 (l2hold) // supports layer 2 hold
-              prop |= (1 << MISDN_FLG_L2_HOLD);
-       /* 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,
-                       interface,
-                       NULL,
-                       NULL,
-                       DIRECTION_NONE,
-                       CATEGORY_CH,
-                       0,
-                       "PORT (open failed)");
-               end_trace();
-               mISDNport_close(mISDNport);
-               return(NULL);
-       }
-
-#if 0
-       /* if ntmode, establish L1 to send the tei removal during start */
-       if (mISDNport->ntmode)
-       {
-               iframe_t act;
-               /* L1 */
-               act.prim = PH_ACTIVATE | REQUEST; 
-               act.addr = mISDNport->upper_id | FLG_MSG_DOWN;
-               printf("UPPER ID 0x%x, addr 0x%x\n",mISDNport->upper_id, act.addr);
-               act.dinfo = 0;
-               act.len = 0;
-               mISDN_write(mISDNdevice, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
-               usleep(10000); /* to be sure, that l1 is up */
-       }
+       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
 #endif
+       }
 
        SCPY(mISDNport->name, devinfo.name);
        mISDNport->b_num = devinfo.nrbchan;
        mISDNport->portnum = port;
        mISDNport->ntmode = nt;
 
        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;
        mISDNport->pri = pri;
        mISDNport->ptp = ptp;
+       mISDNport->l1hold = l1hold;
        mISDNport->l2hold = l2hold;
        mISDNport->l2hold = l2hold;
+       mISDNport->ss5 = ss5;
        PDEBUG(DEBUG_ISDN, "Port has %d b-channels.\n", mISDNport->b_num);
        i = 0;
        PDEBUG(DEBUG_ISDN, "Port has %d b-channels.\n", mISDNport->b_num);
        i = 0;
-       while(i < mISDNport->b_num)
-       {
+       while(i < mISDNport->b_num) {
                mISDNport->b_state[i] = B_STATE_IDLE;
                mISDNport->b_state[i] = B_STATE_IDLE;
-               mISDNport->b_socket[i] = -1;
+               add_timer(&mISDNport->b_timer[i], b_timer_timeout, mISDNport, i);
                i++;
        }
 
        /* if ptp, pull up the link */
                i++;
        }
 
        /* 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);
                end_trace();
                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);
                end_trace();
-               time(&mISDNport->l2establish);
+               schedule_timer(&mISDNport->l2establish, 5, 0); /* 5 seconds */
        }
 
        }
 
-       /* initially, we assume that the link is down, exept for nt-ptmp */
-       mISDNport->l2link = (mISDNport->ntmode && !mISDNport->ptp)?1:0;
+       /* 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");
 
        start_trace(mISDNport->portnum,
 
        PDEBUG(DEBUG_BCHANNEL, "using 'mISDN_dsp.o' module\n");
 
        start_trace(mISDNport->portnum,
-                   interface,
+                   ifport->interface,
                    NULL,
                    NULL,
                    DIRECTION_NONE,
                    CATEGORY_CH,
                    0,
                    "PORT (open)");
                    NULL,
                    NULL,
                    DIRECTION_NONE,
                    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);
        add_trace("channels", NULL, "%d", mISDNport->b_num);
+       if (mISDNport->ss5)
+               add_trace("ccitt#5", NULL, "enabled");
        end_trace();
        end_trace();
+
        return(mISDNport);
 }
 
 
 /*
        return(mISDNport);
 }
 
 
 /*
+ * load static port instances, if required by mISDNport
+ */
+void mISDNport_static(struct mISDNport *mISDNport)
+{
+       int i;
+
+       i = 0;
+       while(i < mISDNport->b_num) {
+#ifdef WITH_SS5
+               if (mISDNport->ss5)
+                       ss5_create_channel(mISDNport, i);
+#endif
+               i++;
+       }
+}
+
+
+/*
  * function to free ALL cards (ports)
  */
 void mISDNport_close_all(void)
  * function to free ALL cards (ports)
  */
 void mISDNport_close_all(void)
@@ -2413,24 +2227,22 @@ void mISDNport_close(struct mISDNport *mISDNport)
        int i;
 
        /* remove all port instance that are linked to this mISDNport */
        int i;
 
        /* remove all port instance that are linked to this mISDNport */
+       again:
        port = port_first;
        port = port_first;
-       while(port)
-       {
-               if ((port->p_type&PORT_CLASS_MASK) == PORT_CLASS_mISDN)
-               {
+       while(port) {
+               if ((port->p_type&PORT_CLASS_MASK) == PORT_CLASS_mISDN) {
                        isdnport = (class PmISDN *)port;
                        isdnport = (class PmISDN *)port;
-                       if (isdnport->p_m_mISDNport)
-                       {
+                       if (isdnport->p_m_mISDNport && isdnport->p_m_mISDNport == mISDNport) {
                                PDEBUG(DEBUG_ISDN, "port %s uses mISDNport %d, destroying it.\n", isdnport->p_name, mISDNport->portnum);
                                delete isdnport;
                                PDEBUG(DEBUG_ISDN, "port %s uses mISDNport %d, destroying it.\n", isdnport->p_name, mISDNport->portnum);
                                delete isdnport;
+                               goto again;
                        }
                }
                port = port->next;
        }
 
        /* only if we are already part of interface */
                        }
                }
                port = port->next;
        }
 
        /* only if we are already part of interface */
-       if (mISDNport->ifport)
-       {
+       if (mISDNport->ifport) {
                start_trace(mISDNport->portnum,
                            mISDNport->ifport->interface,
                            NULL,
                start_trace(mISDNport->portnum,
                            mISDNport->ifport->interface,
                            NULL,
@@ -2444,29 +2256,36 @@ void mISDNport_close(struct mISDNport *mISDNport)
 
        /* free bchannels */
        i = 0;
 
        /* free bchannels */
        i = 0;
-       while(i < mISDNport->b_num)
-       {
-               if (mISDNport->b_socket[i] > -1)
-               {
+       while(i < mISDNport->b_num) {
+               if (mISDNport->b_sock[i].inuse) {
                        _bchannel_destroy(mISDNport, i);
                        PDEBUG(DEBUG_BCHANNEL, "freeing %s port %d bchannel (index %d).\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, i);
                }
                        _bchannel_destroy(mISDNport, i);
                        PDEBUG(DEBUG_BCHANNEL, "freeing %s port %d bchannel (index %d).\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, i);
                }
+               if (mISDNport->b_timer[i].inuse) {
+                       del_timer(&mISDNport->b_timer[i]);
+               }
                i++;
        }
                i++;
        }
+       del_timer(&mISDNport->l2establish);
 
 
-       /* close layer 3, if open and purge upqueue */
-       if (mISDNport->ml3)
-       {
+       /* close layer 3, if open */
+       if (mISDNport->ml3) {
                close_layer3(mISDNport->ml3);
                close_layer3(mISDNport->ml3);
-               mqueue_purge(&mISDNport->upqueue);
        }
 
        }
 
+       /* 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);
+
        /* remove from list */
        mISDNportp = &mISDNport_first;
        /* remove from list */
        mISDNportp = &mISDNport_first;
-       while(*mISDNportp)
-       {
-               if (*mISDNportp == mISDNport)
-               {
+       while(*mISDNportp) {
+               if (*mISDNportp == mISDNport) {
                        *mISDNportp = (*mISDNportp)->next;
                        mISDNportp = NULL;
                        break;
                        *mISDNportp = (*mISDNportp)->next;
                        mISDNportp = NULL;
                        break;
@@ -2484,53 +2303,122 @@ void mISDNport_close(struct mISDNport *mISDNport)
 
 
 /*
 
 
 /*
- * enque data from upper buffer
+ * enque data from remote port
  */
  */
-void PmISDN::txfromup(unsigned char *data, int length)
+int PmISDN::bridge_rx(unsigned char *data, int length)
 {
 {
-       unsigned char buf[MISDN_HEADER_LEN+((length>ISDN_LOAD)?length:ISDN_LOAD)];
+       unsigned char buf[MISDN_HEADER_LEN+((length>p_m_preload)?length:p_m_preload)];
        struct mISDNhead *hh = (struct mISDNhead *)buf;
        int ret;
 
        struct mISDNhead *hh = (struct mISDNhead *)buf;
        int ret;
 
+       if ((ret = Port::bridge_rx(data, length)))
+               return ret;
+
        if (p_m_b_index < 0)
        if (p_m_b_index < 0)
-               return;
+               return -EIO;
        if (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;
+               return -EINVAL;
 
        /* 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)
-               return;
+       if (p_tone_name[0] || p_dov_tx || p_m_crypt_msg_loops || p_m_inband_send_on)
+               return -EBUSY;
 
        /* preload procedure
         * if transmit buffer in DSP module is empty,
         * preload it to DSP_LOAD to prevent jitter gaps.
 
        /* 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_m_load==0 && ISDN_LOAD>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;
                hh->prim = PH_DATA_REQ; 
                hh->id = 0;
-               memset(buf+MISDN_HEADER_LEN, (options.law=='a')?0x2a:0xff, ISDN_LOAD);
-               ret = sendto(p_m_mISDNport->b_socket[p_m_b_index], buf, MISDN_HEADER_LEN+ISDN_LOAD, 0, NULL, 0);
+               memset(buf+MISDN_HEADER_LEN, silence, p_m_preload);
+               ret = sendto(p_m_mISDNport->b_sock[p_m_b_index].fd, buf, MISDN_HEADER_LEN+p_m_preload, 0, NULL, 0);
                if (ret <= 0)
                if (ret <= 0)
-                       PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_socket[p_m_b_index]);
-               p_m_load += ISDN_LOAD;
+                       PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_sock[p_m_b_index].fd);
+               p_m_load += p_m_preload;
+               schedule_timer(&p_m_loadtimer, 0, PORT_TRANSMIT * 125);
        }
 
        /* drop if load would exceed ISDN_MAXLOAD
         * this keeps the delay not too high
         */
        }
 
        /* drop if load would exceed ISDN_MAXLOAD
         * this keeps the delay not too high
         */
-       if (p_m_load+length > ISDN_MAXLOAD)
-               return;
+//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 */
        hh->prim = PH_DATA_REQ;
        hh->id = 0;
        memcpy(buf+MISDN_HEADER_LEN, data, length);
 
        /* make and send frame */
        hh->prim = PH_DATA_REQ;
        hh->id = 0;
        memcpy(buf+MISDN_HEADER_LEN, data, length);
-       ret = sendto(p_m_mISDNport->b_socket[p_m_b_index], buf, MISDN_HEADER_LEN+length, 0, NULL, 0);
+       ret = sendto(p_m_mISDNport->b_sock[p_m_b_index].fd, buf, MISDN_HEADER_LEN+length, 0, NULL, 0);
        if (ret <= 0)
        if (ret <= 0)
-               PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_socket[p_m_b_index]);
+               PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_sock[p_m_b_index].fd);
        p_m_load += length;
        p_m_load += length;
+
+       return 0;
 }
 
 }
 
+int PmISDN::inband_send(unsigned char *buffer, int len)
+{
+       PERROR("this function must be derived to function!\n");
+       return 0;
+}
+
+void PmISDN::inband_send_on(void)
+{
+       PDEBUG(DEBUG_PORT, "turning inband signalling send on.\n");
+       p_m_inband_send_on = 1;
+}
+
+void PmISDN::inband_send_off(void)
+{
+       PDEBUG(DEBUG_PORT, "turning inband signalling send off.\n");
+       p_m_inband_send_on = 0;
+}
+
+void PmISDN::inband_receive(unsigned char *buffer, int len)
+{
+//
+//     if (len >= SS5_DECODER_NPOINTS)
+//             ss5_decode(buffer, SS5_DECODER_NPOINTS);
+       PERROR("this function must be derived to function!\n");
+}
+
+void PmISDN::inband_receive_on(void)
+{
+       /* this must work during constructor, see ss5.cpp */
+       PDEBUG(DEBUG_PORT, "turning inband signalling receive on.\n");
+       p_m_inband_receive_on = 1;
+       update_rxoff();
+}
+
+void PmISDN::inband_receive_off(void)
+{
+       PDEBUG(DEBUG_PORT, "turning inband signalling receive off.\n");
+       p_m_inband_receive_on = 0;
+       update_rxoff();
+}
+
+void PmISDN::mute_on(void)
+{
+       if (p_m_mute)
+               return;
+       PDEBUG(DEBUG_PORT, "turning mute on.\n");
+       p_m_mute = 1;
+       set_conf(p_m_conf, 0);
+}
+
+void PmISDN::mute_off(void)
+{
+       if (!p_m_mute)
+               return;
+       PDEBUG(DEBUG_PORT, "turning mute off.\n");
+       p_m_mute = 0;
+       set_conf(0, p_m_conf);
+}
+
+