alpha phase is open, this means:
[lcr.git] / mISDN.cpp
index d82e513..128faef 100644 (file)
--- a/mISDN.cpp
+++ b/mISDN.cpp
@@ -53,7 +53,6 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti
        p_m_b_channel = 0;
        p_m_b_exclusive = 0;
        p_m_b_reserve = 0;
-       p_m_jittercheck = 0;
        p_m_delete = 0;
        p_m_hold = 0;
        p_m_txvol = p_m_rxvol = 0;
@@ -63,14 +62,14 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti
        p_m_echo = 0;
        p_m_tone = 0;
        p_m_rxoff = 0;
-       p_m_calldata = 0;
+       p_m_joindata = 0;
        p_m_dtmf = !mISDNport->ifport->nodtmf;
        p_m_timeout = 0;
        p_m_timer = 0;
 
-       /* audio from up */
-       p_m_fromup_buffer_readp = 0;
-       p_m_fromup_buffer_writep = 0;
+       /* audio */
+       p_m_load = 0;
+       p_m_last_tv_sec = 0;
 
        /* crypt */
        p_m_crypt = 0;
@@ -270,7 +269,10 @@ void ph_control(struct mISDNport *mISDNport, class PmISDN *isdnport, unsigned lo
        *d++ = c2;
        mISDN_write(mISDNdevice, ctrl, mISDN_HEADER_LEN+ctrl->len, TIMEOUT_1SEC);
        chan_trace_header(mISDNport, isdnport, "BCHANNEL control", DIRECTION_OUT);
-       add_trace(trace_name, NULL, "%d", trace_value);
+       if (c1 == CMX_CONF_JOIN)
+               add_trace(trace_name, NULL, "0x%08x", trace_value);
+       else
+               add_trace(trace_name, NULL, "%d", trace_value);
        end_trace();
 }
 
@@ -363,8 +365,8 @@ static int _bchannel_create(struct mISDNport *mISDNport, int i)
                goto stack_error;
        chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL create stack", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
-       add_trace("stack", "id", "0x%8x", mISDNport->b_stid[i]);
-       add_trace("stack", "address", "0x%8x", mISDNport->b_addr[i]);
+       add_trace("stack", "id", "0x%08x", mISDNport->b_stid[i]);
+       add_trace("stack", "address", "0x%08x", mISDNport->b_addr[i]);
        end_trace();
 
        return(1);
@@ -444,13 +446,12 @@ static void _bchannel_configure(struct mISDNport *mISDNport, int i)
 static void _bchannel_deactivate(struct mISDNport *mISDNport, int i)
 {
        iframe_t dact;
-       int addr;
        
        chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL deactivate", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
        end_trace();
        dact.prim = DL_RELEASE | REQUEST; 
-       dact.addr = addr | FLG_MSG_DOWN;
+       dact.addr = mISDNport->b_addr[i] | FLG_MSG_DOWN;
        dact.dinfo = 0;
        dact.len = 0;
        mISDN_write(mISDNdevice, &dact, mISDN_HEADER_LEN+dact.len, TIMEOUT_1SEC);
@@ -466,8 +467,8 @@ static void _bchannel_destroy(struct mISDNport *mISDNport, int i)
 
        chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL remove stack", DIRECTION_OUT);
        add_trace("channel", NULL, "%d", i+1+(i>=15));
-       add_trace("stack", "id", "0x%8x", mISDNport->b_stid[i]);
-       add_trace("stack", "address", "0x%8x", mISDNport->b_addr[i]);
+       add_trace("stack", "id", "0x%08x", mISDNport->b_stid[i]);
+       add_trace("stack", "address", "0x%08x", mISDNport->b_addr[i]);
        end_trace();
        /* remove our stack only if set */
        PDEBUG(DEBUG_BCHANNEL, "free stack (b_addr=0x%x)\n", mISDNport->b_addr[i]);
@@ -534,16 +535,12 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event)
 {
        int state = mISDNport->b_state[i];
 
-printf("event=%d state=%d\n", event, state);   
        switch(event)
        {
                case B_EVENT_ACTIVATE:
                /* port must be linked in order to allow activation */
                if (!mISDNport->b_port[i])
-               {
-                       PERROR("SOFTWARE ERROR: bchannel must be linked to a Port class\n");
-                       exit(-1);
-               }
+                       FATAL("bchannel must be linked to a Port class\n");
                switch(state)
                {
                        case B_STATE_IDLE:
@@ -592,10 +589,7 @@ printf("event=%d state=%d\n", event, state);
 
                case B_EVENT_DEACTIVATE:
                if (!mISDNport->b_port[i])
-               {
-                       PERROR("SOFTWARE ERROR: bchannel must be linked to a Port class\n");
-                       exit(-1);
-               }
+                       FATAL("bchannel must be linked to a Port class\n");
                switch(state)
                {
                        case B_STATE_IDLE:
@@ -622,11 +616,15 @@ printf("event=%d state=%d\n", event, state);
                break;
 
                case B_EVENT_DEACTIVATED:
-               _bchannel_destroy(mISDNport, i);
-               state = B_STATE_IDLE;
                switch(state)
                {
+                       case B_STATE_IDLE:
+                       /* ignore due to deactivation confirm after unloading */
+                       break;
+
                        case B_STATE_DEACTIVATING:
+                       _bchannel_destroy(mISDNport, i);
+                       state = B_STATE_IDLE;
                        if (mISDNport->b_port[i])
                        {
                                /* bchannel is now deactivate, but is requied by Port class, so we reactivate */
@@ -720,7 +718,6 @@ seize:
        p_m_b_index = i;
        p_m_b_channel = channel;
        p_m_b_exclusive = exclusive;
-       p_m_jittercheck = 0;
 
        /* reserve channel */
        if (!p_m_b_reserve)
@@ -763,128 +760,141 @@ void PmISDN::drop_bchannel(void)
 
 /*
  * handler
+
+audio transmission procedure:
+-----------------------------
+
+* priority
+three sources of audio transmission:
+- crypto-data high priority
+- tones high priority (also high)
+- remote-data low priority
+
+* elapsed
+a variable that temporarily shows the number of samples elapsed since last transmission process.
+p_m_last_tv_* is used to store that last timestamp. this is used to calculate the time elapsed.
+
+* load
+a variable that is increased whenever data is transmitted.
+it is decreased while time elapses. it stores the number of samples that
+are currently loaded to dsp module.
+since clock in dsp module is the same clock for user space process, these 
+times have no skew.
+
+* 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.
+
+* 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
+if no more data is available, load becomes empty again.
+
+'load' variable:
+0                    ISDN_LOAD           ISDN_MAXLOAD
++--------------------+----------------------+
+|                    |                      |
++--------------------+----------------------+
+
+on empty load or on load below ISDN_LOAD, the load is inceased to ISDN_LOAD:
+0                    ISDN_LOAD           ISDN_MAXLOAD
++--------------------+----------------------+
+|TTTTTTTTTTTTTTTTTTTT|                      |
++--------------------+----------------------+
+
+on empty load, remote-audio causes the load with the remote audio to be increased to ISDN_LOAD.
+0                    ISDN_LOAD           ISDN_MAXLOAD
++--------------------+----------------------+
+|TTTTTTTTTTTTTTTTTTTTRRRRR                  |
++--------------------+----------------------+
+
  */
 int PmISDN::handler(void)
 {
        struct message *message;
-       int elapsed, length;
+       int elapsed = 0;
        int ret;
-       int inbuffer;
 
        if ((ret = Port::handler()))
                return(ret);
 
-       inbuffer = (p_m_fromup_buffer_writep - p_m_fromup_buffer_readp) & FROMUP_BUFFER_MASK;
-       /* send tone data to isdn device only if we have data */
-       if (p_tone_name[0] || p_m_crypt_msg_loops || inbuffer)
+       /* get elapsed */
+       if (p_m_last_tv_sec)
        {
-               /* calculate how much to transmit */
-               if (!p_last_tv_sec)
-               {
-                       elapsed = ISDN_PRELOAD; /* preload for the first time */
-               } else
-               {
-                       elapsed = 8000 * (now_tv.tv_sec - p_last_tv_sec)
-                               + 8 * (now_tv.tv_usec/1000 - p_last_tv_msec);
-                       /* gap was greater preload, so only fill up to preload level */
-                       if (elapsed > ISDN_PRELOAD)
-                       {
-                               elapsed = ISDN_PRELOAD;
-                       }
-               }
-               if (elapsed >= ISDN_TRANSMIT)
+               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)
+       {
+               /* set clock of last process! */
+               p_m_last_tv_sec = now_tv.tv_sec;
+               p_m_last_tv_msec = now_tv.tv_usec/1000;
+
+               /* 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)
                {
-                       unsigned char buf[mISDN_HEADER_LEN+ISDN_PRELOAD];
+                       int tosend = ISDN_LOAD - p_m_load, length; 
+                       unsigned char buf[mISDN_HEADER_LEN+tosend];
                        iframe_t *frm = (iframe_t *)buf;
                        unsigned char *p = buf+mISDN_HEADER_LEN;
 
-                       p_last_tv_sec = now_tv.tv_sec;
-                       p_last_tv_msec = now_tv.tv_usec/1000;
-
-                       /* read tones or fill with silence */
-                       length = read_audio(p, elapsed);
-
-                       /*
-                        * get data from up
-                        * the fromup_buffer data is written to the beginning of the buffer
-                        * the part that is filles with tones (length) is skipped, so tones have priority
-                        * the length value is increased by the number of data copied from fromup_buffer
-                        */
-                       if (inbuffer)
-                       {
-                               printf("nix\n");
-                               /* inbuffer might be less than we skip due to audio */
-                               if (inbuffer <= length)
-                               {
-                                       /* clear buffer */
-                                       p_m_fromup_buffer_readp = p_m_fromup_buffer_writep;
-                                       inbuffer = 0;
-                               } else
-                               {
-                                       /* skip what we already have with tones */
-                                       p_m_fromup_buffer_readp = (p_m_fromup_buffer_readp + length) & FROMUP_BUFFER_MASK;
-                                       inbuffer -= length;
-                                       p += length;
-                               }
-                               /* if we have more in buffer, than we send this time */
-                               if (inbuffer > (elapsed-length))
-                                       inbuffer = elapsed - length;
-                               /* set length to what we actually have */
-                               length = length + inbuffer;
-                               /* now fill up with fromup_buffer */
-                               while (inbuffer)
-                               {
-                                       *p++ = p_m_fromup_buffer[p_m_fromup_buffer_readp];
-                                       p_m_fromup_buffer_readp = (p_m_fromup_buffer_readp + 1) & FROMUP_BUFFER_MASK;
-                                       inbuffer--;
-                               }
-                       }
-
-                       /* overwrite buffer with crypto stuff */
-                       if (p_m_crypt_msg_loops)
+                       /* copy crypto loops */
+                       while (p_m_crypt_msg_loops && tosend)
                        {
-                               printf("nix2\n");
-                               /* send pending message */
-                               int tosend;
-
                                /* how much do we have to send */
-                               tosend = p_m_crypt_msg_len - p_m_crypt_msg_current;
-                               if (tosend > elapsed)
-                                       tosend = elapsed;
+                               length = p_m_crypt_msg_len - p_m_crypt_msg_current;
 
-                               /* our length increases, if less */
-                               if (length < tosend)
+                               /* clip tosend */
+                               if (length > tosend)
                                        length = tosend;
 
                                /* copy message (part) to buffer */
-                               memcpy(p, p_m_crypt_msg+p_m_crypt_msg_current, tosend);
-                               p_m_crypt_msg_current += tosend;
+                               memcpy(p, p_m_crypt_msg+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)
                                {
+                                       /* next loop */
                                        p_m_crypt_msg_current = 0;
                                        p_m_crypt_msg_loops--;
                                }
+
+                               /* new length */
+                               tosend -= length;
+                       }
+
+                       /* copy tones */
+                       if (p_tone_name[0] && tosend)
+                       {
+                               tosend -= read_audio(p, tosend);
                        }
+
+                       /* send data */
                        frm->prim = DL_DATA | REQUEST; 
                        frm->addr = p_m_mISDNport->b_addr[p_m_b_index] | FLG_MSG_DOWN;
                        frm->dinfo = 0;
-                       frm->len = length;
-                       mISDN_write(mISDNdevice, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC);
-
-                       if (p_debug_nothingtosend)
-                       {
-                               p_debug_nothingtosend = 0;
-                               PDEBUG((DEBUG_PORT | DEBUG_BCHANNEL), "PmISDN(%s) start sending, because we have tones and/or remote audio.\n", p_name);
-                       } 
-                       return(1);
+                       frm->len = ISDN_LOAD - p_m_load - tosend;
+                       if (frm->len)
+                               mISDN_write(mISDNdevice, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC);
+                       p_m_load += frm->len;
                }
-       } else
-       {
-               if (!p_debug_nothingtosend)
-               {
-                       p_debug_nothingtosend = 1;
-                       PDEBUG((DEBUG_PORT | DEBUG_BCHANNEL), "PmISDN(%s) stop sending, because we have only silence.\n", p_name);
-               } 
        }
 
        // NOTE: deletion is done by the child class
@@ -900,8 +910,8 @@ int PmISDN::handler(void)
                        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(1);
        }
        
        return(0); /* nothing done */
@@ -919,28 +929,6 @@ void PmISDN::bchannel_receive(iframe_t *frm)
        unsigned char *p;
        int l;
        unsigned long cont;
-//     iframe_t rsp; /* response to possible indication */
-#if 0
-#warning BCHANNEL-DEBUG
-{
-       // check if we are part of all ports */
-       class Port *port = port_first;
-       while(port)
-       {
-               if (port==this)
-                       break;
-               port=port->next;
-       }
-       if (!port)
-       {
-               PERROR_RUNTIME("**************************************************\n");
-               PERROR_RUNTIME("*** BCHANNEL-DEBUG: !this! is not in list of ports\n");
-               PERROR_RUNTIME("**************************************************\n");
-               return;
-       }
-}
-#endif
-
 
        if (frm->prim == (PH_CONTROL | INDICATION))
        {
@@ -950,7 +938,6 @@ void PmISDN::bchannel_receive(iframe_t *frm)
                        return;
                }
                cont = *((unsigned long *)&frm->data.p);
-               // PDEBUG(DEBUG_PORT, "PmISDN(%s) received a PH_CONTROL INDICATION 0x%x\n", p_name, cont);
                if ((cont&(~DTMF_TONE_MASK)) == DTMF_TONE_VAL)
                {
                        chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
@@ -987,8 +974,8 @@ void PmISDN::bchannel_receive(iframe_t *frm)
                        case CMX_TX_DATA:
                        if (!p_m_txdata)
                        {
-                               /* if rx is off, it may happen that fifos send us pending informations, we just ignore them */
-                               PDEBUG(DEBUG_BCHANNEL, "PmISDN(%s) ignoring rx data, because 'txdata' is turned off\n", p_name);
+                               /* 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 (p_record)
@@ -1001,25 +988,20 @@ void PmISDN::bchannel_receive(iframe_t *frm)
                        end_trace();
                }
                return;
-       }       
-       if (frm->prim != (PH_DATA | INDICATION))
+       }
+       if (frm->prim != (PH_DATA | INDICATION) && frm->prim != (DL_DATA | INDICATION))
        {
                PERROR("Bchannel received unknown primitve: 0x%x\n", frm->prim);
                return;
        }
 
        /* calls will not process any audio data unless
-        * the call is connected OR tones feature is enabled.
+        * the call is connected OR interface features audio during call setup.
         */
+//printf("%d -> %d prim=%x joindata=%d tones=%d\n", p_serial, ACTIVE_EPOINT(p_epointlist), frm->prim, p_m_joindata, p_m_mISDNport->earlyb);    
+#ifndef DEBUG_COREBRIDGE
        if (p_state!=PORT_STATE_CONNECT
-        && !p_m_mISDNport->tones)
-               return;
-
-#if 0
-       /* the bearer capability must be audio in order to send and receive
-        * audio prior or after connect.
-        */
-       if (!(p_bearerinfo.capability&CLASS_CAPABILITY_AUDIO) && p_state!=PORT_STATE_CONNECT)
+        && !p_m_mISDNport->earlyb)
                return;
 #endif
 
@@ -1049,9 +1031,8 @@ void PmISDN::bchannel_receive(iframe_t *frm)
        p = (unsigned char *)&frm->data.p;
 
        /* send data to epoint */
-       if (p_m_calldata && ACTIVE_EPOINT(p_epointlist)) /* only if we have an epoint object */
+       if (p_m_joindata && ACTIVE_EPOINT(p_epointlist)) /* only if we have an epoint object */
        {
-//printf("we are port %s and sending to epoint %d\n", p_m_cardname, p_epoint->serial);
                length_temp = frm->len;
                data_temp = p;
                while(length_temp)
@@ -1066,15 +1047,6 @@ void PmISDN::bchannel_receive(iframe_t *frm)
                        length_temp -= sizeof(message->param.data.data);
                }
        }
-#if 0
-       /* response to the data indication */
-       rsp.prim = frm->prim & 0xfffffffc | RESPONSE; 
-       rsp.addr = frm->addr & INST_ID_MASK | FLG_MSG_DOWN;
-       rsp.dinfo = frm->dinfo;
-       rsp.len = 0;
-       mISDN_write(mISDNdevice, &rsp, mISDN_HEADER_LEN+rsp.len, TIMEOUT_1SEC);
-//PDEBUG(DEBUG_ISDN, "written %d bytes.\n", length);
-#endif
 }
 
 
@@ -1220,7 +1192,8 @@ void PmISDN::message_mISDNsignal(unsigned long epoint_id, int message_id, union
                        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_addr[p_m_b_index], VOL_CHANGE_TX, p_m_txvol, "DSP-TXVOL", p_m_txvol);
-               }
+               } else
+                       PDEBUG(DEBUG_BCHANNEL, "we already have tx-volume shift=%d.\n", p_m_rxvol);
                if (p_m_rxvol != param->mISDNsignal.rxvol)
                {
                        p_m_rxvol = param->mISDNsignal.rxvol;
@@ -1228,7 +1201,8 @@ void PmISDN::message_mISDNsignal(unsigned long epoint_id, int message_id, union
                        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_addr[p_m_b_index], VOL_CHANGE_RX, p_m_rxvol, "DSP-RXVOL", p_m_rxvol);
-               }
+               } else
+                       PDEBUG(DEBUG_BCHANNEL, "we already have rx-volume shift=%d.\n", p_m_rxvol);
                break;
 
                case mISDNSIGNAL_CONF:
@@ -1241,18 +1215,20 @@ void PmISDN::message_mISDNsignal(unsigned long epoint_id, int message_id, union
                        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_addr[p_m_b_index], (p_m_conf)?CMX_CONF_JOIN:CMX_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 */
                p_m_conf = param->mISDNsignal.conf;
 //if (dddebug) PDEBUG(DEBUG_ISDN, "dddebug = %d\n", dddebug->type);
                break;
 
-               case mISDNSIGNAL_CALLDATA:
-               if (p_m_calldata != param->mISDNsignal.calldata)
+               case mISDNSIGNAL_JOINDATA:
+               if (p_m_joindata != param->mISDNsignal.joindata)
                {
-                       p_m_calldata = param->mISDNsignal.calldata;
-                       PDEBUG(DEBUG_BCHANNEL, "we change to calldata=%d.\n", p_m_calldata);
-               }
+                       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:
@@ -1263,7 +1239,8 @@ void PmISDN::message_mISDNsignal(unsigned long epoint_id, int message_id, union
                        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_addr[p_m_b_index], p_m_delay?CMX_DELAY:CMX_JITTER, p_m_delay, "DSP-DELAY", p_m_delay);
-               }
+               } else
+                       PDEBUG(DEBUG_BCHANNEL, "we already have delay=%d.\n", p_m_delay);
                break;
 
                default:
@@ -1393,7 +1370,7 @@ int mISDN_handler(void)
                        if (isdnport)
                        {
                                /* call bridges in user space OR crypto OR recording */
-                               if (isdnport->p_m_calldata || isdnport->p_m_crypt_msg_loops || isdnport->p_m_crypt_listen || isdnport->p_record)
+                               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)
@@ -1517,8 +1494,7 @@ int mISDN_handler(void)
                free_msg(msg);
                if (errno == EAGAIN)
                        return(0);
-               PERROR("FATAL ERROR: failed to do mISDN_read()\n");
-               exit(-1); 
+               FATAL("Failed to do mISDN_read()\n");
        }
        if (!ret)
        {
@@ -1537,245 +1513,260 @@ int mISDN_handler(void)
                case MGR_ADDTIMER | CONFIRM:
                case MGR_DELTIMER | CONFIRM:
                case MGR_REMOVETIMER | CONFIRM:
-//             if (options.deb & DEBUG_ISDN)
-//                     PDEBUG(DEBUG_ISDN, "timer-confirm\n");
                free_msg(msg);
                return(1);
        }
 
-       /* find the port */
-       mISDNport = mISDNport_first;
-       while(mISDNport)
+       /* handle timer events from mISDN for NT-stack
+        * note: they do not associate with a stack */
+       if (frm->prim == (MGR_TIMER | INDICATION))
        {
-               if ((frm->prim==(MGR_TIMER | INDICATION)) && mISDNport->ntmode)
-               {
-                       itimer_t *it = mISDNport->nst.tlist;
+               itimer_t *it;
 
-                       /* find timer */
-                       while(it)
+               /* find mISDNport */
+               mISDNport = mISDNport_first;
+               while(mISDNport)
+               {
+                       /* nt mode only */
+                       if (mISDNport->ntmode)
                        {
-                               if (it->id == (int)frm->addr)
+                               it = mISDNport->nst.tlist;
+                               /* find timer */
+                               while(it)
+                               {
+                                       if (it->id == (int)frm->addr)
+                                               break;
+                                       it = it->next;
+                               }
+                               if (it)
                                        break;
-                               it = it->next;
-                       }
-                       if (it)
-                       {
-                               mISDN_write_frame(mISDNdevice, msg->data, mISDNport->upper_id | FLG_MSG_DOWN,
-                                       MGR_TIMER | RESPONSE, 0, 0, NULL, TIMEOUT_1SEC);
-
-                               PDEBUG(DEBUG_ISDN, "timer-indication %s port %d it=%p\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, it);
-                               test_and_clear_bit(FLG_TIMER_RUNING, (long unsigned int *)&it->Flags);
-                               ret = it->function(it->data);
-                               break;
                        }
-                       /* we will continue here because we have a timer for a different mISDNport */
+                       mISDNport = mISDNport->next;
                }
-//printf("comparing frm->addr %x with upper_id %x\n", frm->addr, mISDNport->upper_id);
-               if ((frm->addr&STACK_ID_MASK) == (unsigned int)(mISDNport->upper_id&STACK_ID_MASK))
+               if (mISDNport)
                {
-                       /* d-message */
-                       switch(frm->prim)
-                       {
-                               case MGR_SHORTSTATUS | INDICATION:
-                               case MGR_SHORTSTATUS | CONFIRM:
-                               switch(frm->dinfo) {
-                                       case SSTATUS_L1_ACTIVATED:
-                                       l1l2l3_trace_header(mISDNport, NULL, PH_ACTIVATE | (frm->prim & 0x3), DIRECTION_IN);
-                                       end_trace();
-                                       goto ss_act;
-                                       case SSTATUS_L1_DEACTIVATED:
-                                       l1l2l3_trace_header(mISDNport, NULL, PH_DEACTIVATE | (frm->prim & 0x3), DIRECTION_IN);
-                                       end_trace();
-                                       goto ss_deact;
-                                       case SSTATUS_L2_ESTABLISHED:
-                                       l1l2l3_trace_header(mISDNport, NULL, DL_ESTABLISH | (frm->prim & 0x3), DIRECTION_IN);
-                                       end_trace();
-                                       goto ss_estab;
-                                       case SSTATUS_L2_RELEASED:
-                                       l1l2l3_trace_header(mISDNport, NULL, DL_RELEASE | (frm->prim & 0x3), DIRECTION_IN);
-                                       end_trace();
-                                       goto ss_rel;
-                               }
-                               break;
+                       mISDN_write_frame(mISDNdevice, msg->data, mISDNport->upper_id | FLG_MSG_DOWN,
+                               MGR_TIMER | RESPONSE, 0, 0, NULL, TIMEOUT_1SEC);
+
+                       PDEBUG(DEBUG_ISDN, "timer-indication port %d it=%p\n", mISDNport->portnum, it);
+                       test_and_clear_bit(FLG_TIMER_RUNING, (long unsigned int *)&it->Flags);
+                       ret = it->function(it->data);
+               } else
+               {
+                       PDEBUG(DEBUG_ISDN, "timer-indication not handled\n");
+               }
+               goto out;
+       }
 
-                               case PH_ACTIVATE | CONFIRM:
-                               case PH_ACTIVATE | INDICATION:
-                               l1l2l3_trace_header(mISDNport, NULL, frm->prim, DIRECTION_IN);
+       /* find the mISDNport that belongs to the stack */
+       mISDNport = mISDNport_first;
+       while(mISDNport)
+       {
+               if ((frm->addr&MASTER_ID_MASK) == (unsigned int)(mISDNport->upper_id&MASTER_ID_MASK))
+                       break;
+               mISDNport = mISDNport->next;
+       } 
+       if (!mISDNport)
+       {
+               PERROR("message belongs to no mISDNport: prim(0x%x) addr(0x%x) msg->len(%d)\n", frm->prim, frm->addr, msg->len);
+               goto out;
+       }
+
+       /* master stack */
+       if (!(frm->addr&FLG_CHILD_STACK))
+       {
+               /* d-message */
+               switch(frm->prim)
+               {
+                       case MGR_SHORTSTATUS | INDICATION:
+                       case MGR_SHORTSTATUS | CONFIRM:
+                       switch(frm->dinfo) {
+                               case SSTATUS_L1_ACTIVATED:
+                               l1l2l3_trace_header(mISDNport, NULL, PH_ACTIVATE | (frm->prim & 0x3), DIRECTION_IN);
                                end_trace();
-                               if (mISDNport->ntmode)
-                               {
-                                       mISDNport->l1link = 1;
-                                       setup_queue(mISDNport, 1);
-                                       goto l1_msg;
-                               }
-                               ss_act:
+                               goto ss_act;
+                               case SSTATUS_L1_DEACTIVATED:
+                               l1l2l3_trace_header(mISDNport, NULL, PH_DEACTIVATE | (frm->prim & 0x3), DIRECTION_IN);
+                               end_trace();
+                               goto ss_deact;
+                               case SSTATUS_L2_ESTABLISHED:
+                               l1l2l3_trace_header(mISDNport, NULL, DL_ESTABLISH | (frm->prim & 0x3), DIRECTION_IN);
+                               end_trace();
+                               goto ss_estab;
+                               case SSTATUS_L2_RELEASED:
+                               l1l2l3_trace_header(mISDNport, NULL, DL_RELEASE | (frm->prim & 0x3), DIRECTION_IN);
+                               end_trace();
+                               goto ss_rel;
+                       }
+                       break;
+
+                       case PH_ACTIVATE | CONFIRM:
+                       case PH_ACTIVATE | INDICATION:
+                       l1l2l3_trace_header(mISDNport, NULL, frm->prim, DIRECTION_IN);
+                       end_trace();
+                       if (mISDNport->ntmode)
+                       {
                                mISDNport->l1link = 1;
                                setup_queue(mISDNport, 1);
-                               break;
+                               goto l1_msg;
+                       }
+                       ss_act:
+                       mISDNport->l1link = 1;
+                       setup_queue(mISDNport, 1);
+                       break;
 
-                               case PH_DEACTIVATE | CONFIRM:
-                               case PH_DEACTIVATE | INDICATION:
-                               l1l2l3_trace_header(mISDNport, NULL, frm->prim, DIRECTION_IN);
-                               end_trace();
-                               if (mISDNport->ntmode)
-                               {
-                                       mISDNport->l1link = 0;
-                                       setup_queue(mISDNport, 0);
-                                       goto l1_msg;
-                               }
-                               ss_deact:
+                       case PH_DEACTIVATE | CONFIRM:
+                       case PH_DEACTIVATE | INDICATION:
+                       l1l2l3_trace_header(mISDNport, NULL, frm->prim, DIRECTION_IN);
+                       end_trace();
+                       if (mISDNport->ntmode)
+                       {
                                mISDNport->l1link = 0;
                                setup_queue(mISDNport, 0);
-                               break;
-
-                               case PH_CONTROL | CONFIRM:
-                               case PH_CONTROL | INDICATION:
-                               PDEBUG(DEBUG_ISDN, "Received PH_CONTROL for port %d (%s).\n", mISDNport->portnum, mISDNport->ifport->interface->name);
-                               break;
+                               goto l1_msg;
+                       }
+                       ss_deact:
+                       mISDNport->l1link = 0;
+                       setup_queue(mISDNport, 0);
+                       break;
 
-                               case DL_ESTABLISH | INDICATION:
-                               case DL_ESTABLISH | CONFIRM:
-                               l1l2l3_trace_header(mISDNport, NULL, frm->prim, DIRECTION_IN);
-                               end_trace();
-                               if (!mISDNport->ntmode) break; /* !!!!!!!!!!!!!!!! */
-                               ss_estab:
-                               if (mISDNport->l2establish)
-                               {
-                                       mISDNport->l2establish = 0;
-                                       PDEBUG(DEBUG_ISDN, "the link became active before l2establish timer expiry.\n");
-                               }
-                               mISDNport->l2link = 1;
-                               break;
+                       case PH_CONTROL | CONFIRM:
+                       case PH_CONTROL | INDICATION:
+                       PDEBUG(DEBUG_ISDN, "Received PH_CONTROL for port %d (%s).\n", mISDNport->portnum, mISDNport->ifport->interface->name);
+                       break;
 
-                               case DL_RELEASE | INDICATION:
-                               case DL_RELEASE | CONFIRM:
-                               l1l2l3_trace_header(mISDNport, NULL, frm->prim, DIRECTION_IN);
-                               end_trace();
-                               if (!mISDNport->ntmode) break; /* !!!!!!!!!!!!!!!! */
-                               ss_rel:
-                               mISDNport->l2link = 0;
-                               if (mISDNport->ptp)
-                               {
-                                       time(&mISDNport->l2establish);
-                                       PDEBUG(DEBUG_ISDN, "because we are ptp, we set a l2establish timer.\n");
-                               }
-                               break;
+                       case DL_ESTABLISH | INDICATION:
+                       case DL_ESTABLISH | CONFIRM:
+                       l1l2l3_trace_header(mISDNport, NULL, frm->prim, DIRECTION_IN);
+                       end_trace();
+                       if (!mISDNport->ntmode) break; /* !!!!!!!!!!!!!!!! */
+                       ss_estab:
+                       if (mISDNport->l2establish)
+                       {
+                               mISDNport->l2establish = 0;
+                               PDEBUG(DEBUG_ISDN, "the link became active before l2establish timer expiry.\n");
+                       }
+                       mISDNport->l2link = 1;
+                       break;
 
-                               default:
-                               l1_msg:
-                               PDEBUG(DEBUG_STACK, "GOT d-msg from %s port %d prim 0x%x dinfo 0x%x addr 0x%x\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, frm->prim, frm->dinfo, frm->addr);
-                               if (frm->dinfo==(signed long)0xffffffff && frm->prim==(PH_DATA|CONFIRM))
-                               {
-                                       PERROR("SERIOUS BUG, dinfo == 0xffffffff, prim == PH_DATA | CONFIRM !!!!\n");
-                               }
-                               /* d-message */
-                               if (mISDNport->ntmode)
-                               {
-                                       /* l1-data enters the nt-mode library */
-                                       nst = &mISDNport->nst;
-                                       if (nst->l1_l2(nst, msg))
-                                               free_msg(msg);
-                                       return(1);
-                               } else
-                               {
-                                       /* l3-data is sent to pbx */
-                                       if (stack2manager_te(mISDNport, msg))
-                                               free_msg(msg);
-                                       return(1);
-                               }
+                       case DL_RELEASE | INDICATION:
+                       case DL_RELEASE | CONFIRM:
+                       l1l2l3_trace_header(mISDNport, NULL, frm->prim, DIRECTION_IN);
+                       end_trace();
+                       if (!mISDNport->ntmode) break; /* !!!!!!!!!!!!!!!! */
+                       ss_rel:
+                       mISDNport->l2link = 0;
+                       if (mISDNport->ptp)
+                       {
+                               time(&mISDNport->l2establish);
+                               PDEBUG(DEBUG_ISDN, "because we are ptp, we set a l2establish timer.\n");
                        }
                        break;
+
+                       default:
+                       l1_msg:
+                       PDEBUG(DEBUG_STACK, "GOT d-msg from %s port %d prim 0x%x dinfo 0x%x addr 0x%x\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, frm->prim, frm->dinfo, frm->addr);
+                       if (frm->dinfo==(signed long)0xffffffff && frm->prim==(PH_DATA|CONFIRM))
+                       {
+                               PERROR("SERIOUS BUG, dinfo == 0xffffffff, prim == PH_DATA | CONFIRM !!!!\n");
+                       }
+                       /* d-message */
+                       if (mISDNport->ntmode)
+                       {
+                               /* l1-data enters the nt-mode library */
+                               nst = &mISDNport->nst;
+                               if (nst->l1_l2(nst, msg))
+                                       free_msg(msg);
+                               return(1);
+                       } else
+                       {
+                               /* l3-data is sent to pbx */
+                               if (stack2manager_te(mISDNport, msg))
+                                       free_msg(msg);
+                               return(1);
+                       }
                }
-//PDEBUG(DEBUG_ISDN, "flg:%d upper_id=%x addr=%x\n", (frm->addr&FLG_CHILD_STACK), (mISDNport->b_addr[0])&(~IF_CHILDMASK), (frm->addr)&(~IF_CHILDMASK));
-               /* check if child, and if parent stack match */
-               if ((frm->addr&FLG_CHILD_STACK) && (((unsigned int)(mISDNport->b_addr[0])&(~CHILD_ID_MASK)&STACK_ID_MASK) == ((frm->addr)&(~CHILD_ID_MASK)&STACK_ID_MASK)))
+       } else
+       /* child stack */
+       {
+               /* b-message */
+               switch(frm->prim)
                {
-                       /* b-message */
-                       switch(frm->prim)
-                       {
-                               /* we don't care about confirms, we use rx data to sync tx */
-                               case PH_DATA | CONFIRM:
-                               case DL_DATA | CONFIRM:
-                               break;
+                       /* we don't care about confirms, we use rx data to sync tx */
+                       case PH_DATA | CONFIRM:
+                       case DL_DATA | CONFIRM:
+                       break;
 
-                               /* we receive audio data, we respond to it AND we send tones */
-                               case PH_DATA | INDICATION:
-                               case DL_DATA | INDICATION:
-                               case PH_CONTROL | INDICATION:
-                               i = 0;
-                               while(i < mISDNport->b_num)
-                               {
-                                       if ((unsigned int)(mISDNport->b_addr[i]&STACK_ID_MASK) == (frm->addr&STACK_ID_MASK))
-                                               break;
-                                       i++;
-                               }
-                               if (i == mISDNport->b_num)
-                               {
-                                       PERROR("unhandled b-message (address 0x%x).\n", frm->addr);
+                       /* we receive audio data, we respond to it AND we send tones */
+                       case PH_DATA | INDICATION:
+                       case DL_DATA | INDICATION:
+                       case PH_CONTROL | INDICATION:
+                       i = 0;
+                       while(i < mISDNport->b_num)
+                       {
+                               if ((unsigned int)(mISDNport->b_addr[i]&STACK_ID_MASK) == (frm->addr&STACK_ID_MASK))
                                        break;
-                               }
-                               if (mISDNport->b_port[i])
-                               {
-//PERROR("port sech: %s data\n", mISDNport->b_port[i]->p_name);
-                                       mISDNport->b_port[i]->bchannel_receive(frm);
-                               } else
-                                       PDEBUG(DEBUG_BCHANNEL, "b-channel is not associated to an ISDNPort (address 0x%x), ignoring.\n", frm->addr);
+                               i++;
+                       }
+                       if (i == mISDNport->b_num)
+                       {
+                               PERROR("unhandled b-message (prim 0x%x address 0x%x).\n", frm->prim, frm->addr);
                                break;
+                       }
+                       if (mISDNport->b_port[i])
+                       {
+//PERROR("port sech: %s data\n", mISDNport->b_port[i]->p_name);
+                               mISDNport->b_port[i]->bchannel_receive(frm);
+                       } else
+                               PDEBUG(DEBUG_BCHANNEL, "b-channel is not associated to an ISDNPort (address 0x%x), ignoring.\n", frm->addr);
+                       break;
 
-                               case PH_ACTIVATE | INDICATION:
-                               case DL_ESTABLISH | INDICATION:
-                               case PH_ACTIVATE | CONFIRM:
-                               case DL_ESTABLISH | CONFIRM:
-                               PDEBUG(DEBUG_BCHANNEL, "DL_ESTABLISH confirm: bchannel is now activated (address 0x%x).\n", frm->addr);
-                               i = 0;
-                               while(i < mISDNport->b_num)
-                               {
-                                       if ((unsigned int)(mISDNport->b_addr[i]&STACK_ID_MASK) == (frm->addr&STACK_ID_MASK))
-                                               break;
-                                       i++;
-                               }
-                               if (i == mISDNport->b_num)
-                               {
-                                       PERROR("unhandled b-establish (address 0x%x).\n", frm->addr);
+                       case PH_ACTIVATE | INDICATION:
+                       case DL_ESTABLISH | INDICATION:
+                       case PH_ACTIVATE | CONFIRM:
+                       case DL_ESTABLISH | CONFIRM:
+                       PDEBUG(DEBUG_BCHANNEL, "DL_ESTABLISH confirm: bchannel is now activated (address 0x%x).\n", frm->addr);
+                       i = 0;
+                       while(i < mISDNport->b_num)
+                       {
+                               if ((unsigned int)(mISDNport->b_addr[i]&STACK_ID_MASK) == (frm->addr&STACK_ID_MASK))
                                        break;
-                               }
-                               bchannel_event(mISDNport, i, B_EVENT_ACTIVATED);
+                               i++;
+                       }
+                       if (i == mISDNport->b_num)
+                       {
+                               PERROR("unhandled b-establish (prim 0x%x address 0x%x).\n", frm->prim, frm->addr);
                                break;
+                       }
+                       bchannel_event(mISDNport, i, B_EVENT_ACTIVATED);
+                       break;
 
-                               case PH_DEACTIVATE | INDICATION:
-                               case DL_RELEASE | INDICATION:
-                               case PH_DEACTIVATE | CONFIRM:
-                               case DL_RELEASE | CONFIRM:
-                               PDEBUG(DEBUG_BCHANNEL, "DL_RELEASE confirm: bchannel is now de-activated (address 0x%x).\n", frm->addr);
-                               i = 0;
-                               while(i < mISDNport->b_num)
-                               {
-                                       if ((unsigned int)(mISDNport->b_addr[i]&STACK_ID_MASK) == (frm->addr&STACK_ID_MASK))
-                                               break;
-                                       i++;
-                               }
-                               if (i == mISDNport->b_num)
-                               {
-                                       PERROR("unhandled b-release (address 0x%x).\n", frm->addr);
+                       case PH_DEACTIVATE | INDICATION:
+                       case DL_RELEASE | INDICATION:
+                       case PH_DEACTIVATE | CONFIRM:
+                       case DL_RELEASE | CONFIRM:
+                       PDEBUG(DEBUG_BCHANNEL, "DL_RELEASE confirm: bchannel is now de-activated (address 0x%x).\n", frm->addr);
+                       i = 0;
+                       while(i < mISDNport->b_num)
+                       {
+                               if ((unsigned int)(mISDNport->b_addr[i]&STACK_ID_MASK) == (frm->addr&STACK_ID_MASK))
                                        break;
-                               }
-                               bchannel_event(mISDNport, i, B_EVENT_DEACTIVATED);
+                               i++;
+                       }
+                       if (i == mISDNport->b_num)
+                       {
+                               PERROR("unhandled b-release (prim 0x%x address 0x%x).\n", frm->prim, frm->addr);
                                break;
                        }
+                       bchannel_event(mISDNport, i, B_EVENT_DEACTIVATED);
                        break;
-               }
 
-               mISDNport = mISDNport->next;
-       } 
-       if (!mISDNport)
-       {
-               if (frm->prim == (MGR_TIMER | INDICATION))
-                       PERROR("unhandled timer indication message: prim(0x%x) addr(0x%x) msg->len(%d)\n", frm->prim, frm->addr, msg->len);
-               else
-                       PERROR("unhandled message: prim(0x%x) addr(0x%x) msg->len(%d)\n", frm->prim, frm->addr, msg->len);
-//             PERROR("test: is_child: %x  of stack %x == %x (baddr %x frm %x)\n", (frm->addr&FLG_CHILD_STACK), ((unsigned int)(mISDNport_first->b_addr[0])&(~CHILD_ID_MASK)&STACK_ID_MASK), ((frm->addr)&(~CHILD_ID_MASK)&STACK_ID_MASK), mISDNport_first->b_addr[0], frm->addr);
+                       default:
+                       PERROR("child message not handled: prim(0x%x) addr(0x%x) msg->len(%d)\n", frm->prim, frm->addr, msg->len);
+               }
        }
 
+       out:
        free_msg(msg);
        return(1);
 }
@@ -1817,8 +1808,7 @@ struct mISDNport *mISDNport_open(int port, int ptp, int ptmp, struct interface *
                if (ret < (int)mISDN_HEADER_LEN)
                {
                        noentity:
-                       fprintf(stderr, "cannot request MGR_NEWENTITY from mISDN. Exitting due to software bug.");
-                       exit(-1);
+                       FATAL("Cannot request MGR_NEWENTITY from mISDN. Exitting due to software bug.");
                }
                entity = frm->dinfo & 0xffff;
                if (!entity)
@@ -1920,14 +1910,8 @@ struct mISDNport *mISDNport_open(int port, int ptp, int ptmp, struct interface *
        mISDNportp = &mISDNport_first;
        while(*mISDNportp)
                mISDNportp = &((*mISDNportp)->next);
-       mISDNport = (struct mISDNport *)calloc(1, sizeof(struct mISDNport));
-       if (!mISDNport)
-       {
-               PERROR("Cannot alloc mISDNport structure\n");
-               return(NULL);
-       }
+       mISDNport = (struct mISDNport *)MALLOC(sizeof(struct mISDNport));
        pmemuse++;
-       memset(mISDNport, 0, sizeof(mISDNport));
        *mISDNportp = mISDNport;
 
        /* allocate ressources of port */
@@ -2176,7 +2160,7 @@ void mISDNport_close(struct mISDNport *mISDNport)
                        /* phd */
                        msg_queue_purge(&nst->down_queue);
                        if (nst->phd_down_msg)
-                               free(nst->phd_down_msg);
+                               FREE(nst->phd_down_msg, 0);
                }
        }
 
@@ -2201,13 +2185,9 @@ void mISDNport_close(struct mISDNport *mISDNport)
        }
 
        if (mISDNportp)
-       {
-               PERROR("software error, mISDNport not in list\n");
-               exit(-1);
-       }
+               FATAL("mISDNport not in list\n");
        
-       memset(mISDNport, 0, sizeof(struct mISDNport));
-       free(mISDNport);
+       FREE(mISDNport, sizeof(struct mISDNport));
        pmemuse--;
 
        /* close mISDNdevice, if no port */
@@ -2240,7 +2220,7 @@ void mISDN_port_info(void)
        if ((device = mISDN_open()) < 0)
        {
                fprintf(stderr, "cannot open mISDN device ret=%d errno=%d (%s) Check for mISDN modules!\nAlso did you create \"/dev/mISDN\"? Do: \"mknod /dev/mISDN c 46 0\"\n", device, errno, strerror(errno));
-               exit(-1);
+               exit(EXIT_FAILURE);
        }
 
        /* get number of stacks */
@@ -2389,10 +2369,7 @@ void mISDN_port_info(void)
 
        /* close mISDN */
        if ((err = mISDN_close(device)))
-       {
-               fprintf(stderr, "mISDN_close() failed: err=%d '%s'\n", err, strerror(err));
-               exit(-1);
-       }
+               FATAL("mISDN_close() failed: err=%d '%s'\n", err, strerror(err));
 }
 
 
@@ -2401,26 +2378,44 @@ void mISDN_port_info(void)
  */
 void PmISDN::txfromup(unsigned char *data, int length)
 {
-       int avail;
-       /* no data */
-       if (!length)
-               return;
+       unsigned char buf[mISDN_HEADER_LEN+((length>ISDN_LOAD)?length:ISDN_LOAD)];
+       iframe_t *frm = (iframe_t *)buf;
 
-       /* get free samples in buffer */
-       avail = ((p_m_fromup_buffer_readp - p_m_fromup_buffer_writep - 1) & FROMUP_BUFFER_MASK);
-       if (avail < length)
-       {
-               PDEBUG(DEBUG_PORT, "Port(%d): fromup_buffer overflows, this shall not happen under normal conditions\n", p_serial);
+       /* configure frame */
+       frm->prim = DL_DATA | REQUEST; 
+       frm->addr = p_m_mISDNport->b_addr[p_m_b_index] | FLG_MSG_DOWN;
+       frm->dinfo = 0;
+
+       /* check if high priority tones exist
+        * ignore data in this case
+        */
+       if (p_tone_name[0] || p_m_crypt_msg_loops)
                return;
-       }
 
-       /* write data to buffer and return */
-       while(length)
+       /* preload procedure
+        * if transmit buffer in DSP module is empty,
+        * preload it to DSP_LOAD to prevent jitter gaps.
+        */
+       if (p_m_load==0 && ISDN_LOAD>0)
        {
-               p_m_fromup_buffer[p_m_fromup_buffer_writep] = *data++;
-               p_m_fromup_buffer_writep = (p_m_fromup_buffer_writep + 1) & FROMUP_BUFFER_MASK;
-               length--;
+
+               memcpy(buf+mISDN_HEADER_LEN, data, ISDN_LOAD);
+               frm->len = ISDN_LOAD;
+               mISDN_write(mISDNdevice, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC);
+               p_m_load += frm->len;
        }
-       return; // must return, because length is 0
+
+       /* drop if load would exceed ISDN_MAXLOAD
+        * this keeps the delay not too high
+        */
+       if (p_m_load+length > ISDN_MAXLOAD)
+               return;
+
+       /* load data to buffer
+        */
+       memcpy(buf+mISDN_HEADER_LEN, data, length);
+       frm->len = length;
+       mISDN_write(mISDNdevice, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC);
+       p_m_load += frm->len;
 }