/*****************************************************************************

 @(#) $Id: sl_sm.h,v 0.7.2.8 2001/02/03 05:48:50 brian Test $

 -----------------------------------------------------------------------------

 Copyright (C) 1997-2001  Brian Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 Last Modified $Date: 2001/02/03 05:48:50 $ by $Author: brian $

 *****************************************************************************/

#ifndef __SL_SM_H__
#define __SL_SM_H__

/*
 *  =======================================================================
 *
 *  PROTOCOL STATE MACHINE FUNCTIONS
 *
 *  =======================================================================
 */

#define sl_timer_stop(tim) \
{ \
    if (sl->timers.tim) \
        untimeout(sl->timers.tim); \
}

#define sl_timer_start(tim) \
{ \
    sl_timer_stop(tim); \
    sl->timers.tim= \
      timeout((timo_fcn_t *)sl_ ## tim ## _timeout,(caddr_t)sl,sl->config.tim); \
}

#define sl_timer_start_t4(val) \
{ \
    sl_timer_stop(t4); \
    sl->timers.t4 = \
      timeout((timo_fcn_t *)sl_t4_timeout,(caddr_t)sl,sl->statem.val); \
}

static inline void sl_daedt_fisu(sl_t *sl)
{
    mblk_t *mp;
    DTRACE;
    if ( (mp = allocb(6, BPRI_HI)) ) {
        if ( sl->option.popt & SS7_POPT_XSN ) {
            *((sl_ushort *)mp->b_wptr)++ = htons(sl->statem.tx.N.bsn|sl->statem.tx.N.bib);
            *((sl_ushort *)mp->b_wptr)++ = htons(sl->statem.tx.N.fsn|sl->statem.tx.N.fib);
            *((sl_ushort *)mp->b_wptr)++ = 0;
        } else {
            *((sl_uchar *)mp->b_wptr)++ = (sl->statem.tx.N.bsn|sl->statem.tx.N.bib);
            *((sl_uchar *)mp->b_wptr)++ = (sl->statem.tx.N.fsn|sl->statem.tx.N.fib);
            *((sl_uchar *)mp->b_wptr)++ = 0;
        }
        DPRINT(0,("tx: bsn=%x, bib=%x, fsn=%x, fib=%x, len=%d, sio=%d\n",
                    sl->statem.tx.N.bsn,
                    sl->statem.tx.N.bib,
                    sl->statem.tx.N.fsn,
                    sl->statem.tx.N.fib,
                    0,
                    0
                    ));
        sl->dcalls->daedt_xmit(sl, mp);
    }
    sl->statem.txc_state = SL_STATE_SLEEPING;
}

static inline void sl_daedt_lssu(sl_t *sl)
{
    mblk_t *mp = allocb(7, BPRI_HI);
    DTRACE;
    if ( mp ) {
        if ( sl->option.popt & SS7_POPT_XSN ) {
            *((sl_ushort *)mp->b_wptr)++ = htons(sl->statem.tx.N.bsn|sl->statem.tx.N.bib);
            *((sl_ushort *)mp->b_wptr)++ = htons(sl->statem.tx.N.fsn|sl->statem.tx.N.fib);
            *((sl_ushort *)mp->b_wptr)++ = htons(1);
        } else {
            *((sl_uchar *)mp->b_wptr)++ = (sl->statem.tx.N.bsn|sl->statem.tx.N.bib);
            *((sl_uchar *)mp->b_wptr)++ = (sl->statem.tx.N.fsn|sl->statem.tx.N.fib);
            *((sl_uchar *)mp->b_wptr)++ = 1;
        }
        *((sl_uchar *)mp->b_wptr)++ = (sl->statem.tx.sio);
        DPRINT(0,("tx: bsn=%x, bib=%x, fsn=%x, fib=%x, len=%d, sio=%d\n",
                    sl->statem.tx.N.bsn,
                    sl->statem.tx.N.bib,
                    sl->statem.tx.N.fsn,
                    sl->statem.tx.N.fib,
                    1,
                    sl->statem.tx.sio
                    ));
        sl->dcalls->daedt_xmit(sl, mp);
    }
    if ( sl->statem.lssu_available )
        sl->statem.txc_state = SL_STATE_SLEEPING;
}

static inline void sl_daedt_msu(sl_t *sl, mblk_t *md)
{
    mblk_t *mp = dupb(md);
    DTRACE;
    if ( mp ) {
        int len = msgdsize(mp);
        if ( sl->option.popt & SS7_POPT_XSN ) {
            ((sl_ushort *)mp->b_rptr)[0] = htons(sl->statem.tx.N.bsn|sl->statem.tx.N.bib);
            ((sl_ushort *)mp->b_rptr)[1] = htons(sl->statem.tx.N.fsn|sl->statem.tx.N.fib);
            ((sl_ushort *)mp->b_rptr)[2] = htons(len-6<512?len-6:511);
        } else {
        ((sl_uchar *)mp->b_rptr)[0] = (sl->statem.tx.N.bsn|sl->statem.tx.N.bib);
            ((sl_uchar *)mp->b_rptr)[1] = (sl->statem.tx.N.fsn|sl->statem.tx.N.fib);
            ((sl_uchar *)mp->b_rptr)[2] = (len-3<64?len-3:63);
        }
        DPRINT(0,("tx: bsn=%x, bib=%x, fsn=%x, fib=%x, len=%d, sio=%d\n",
                    sl->statem.tx.N.bsn,
                    sl->statem.tx.N.bib,
                    sl->statem.tx.N.fsn,
                    sl->statem.tx.N.fib,
                    len,
                    0
                    ));
        sl->dcalls->daedt_xmit(sl, mp);
    }
}

/*
 *  -----------------------------------------------------------------------
 *
 *  STATE MACHINES:- The order of the state machine primitives below may seem
 *  somewhat disorganized at first glance; however, they have been ordered by
 *  dependency because they are all inline functions.  You see, the L2 state
 *  machine does not required multiple threading because there is never a
 *  requirement to invoke the individual state machines concurrently.  This
 *  works out good for the driver, because a primitive action expands inline
 *  to the necessary procedure, while the source still takes the appearance of
 *  the SDL diagrams in the SS7 specification for inspection and debugging.
 *
 *  -----------------------------------------------------------------------
 */

#define sl_cc_stop sl_cc_normal
static inline void sl_cc_normal(sl_t *sl) 
{
    DTRACE;
    sl_timer_stop(t5);
    sl->statem.cc_state = SL_STATE_IDLE;
}

static inline void sl_rc_stop(sl_t *sl) 
{
    DTRACE;
    sl_cc_normal(sl);
    sl->statem.rc_state = SL_STATE_IDLE;
}

static inline void sl_iac_stop(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.iac_state != SL_STATE_IDLE ) {
            sl_timer_stop(t3);
            sl_timer_stop(t2);
            sl_timer_stop(t4);
            sl->dcalls->aerm_stop(sl);
            sl->statem.emergency = 0;
            sl->statem.iac_state = SL_STATE_IDLE;
    }
}

static void sl_txc_transmission_request(sl_t *sl);

static inline void sl_daedt_transmitter_wakeup(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.txc_state == SL_STATE_SLEEPING ) {
        sl->statem.txc_state = SL_STATE_IN_SERVICE;
        sl_txc_transmission_request(sl);
    }
}

static inline void sl_txc_send_sios(sl_t *sl) 
{
    DTRACE;
    sl_timer_stop(t7);
    if ( sl->option.pvar == SS7_PVAR_ANSI_92 )
        sl_timer_stop(t6);
    sl->statem.lssu_available = 1;
    sl->statem.tx.sio = LSSU_SIOS;
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_poc_stop(sl_t* sl)
{
    DTRACE;
    sl->statem.poc_state = SL_STATE_IDLE;
}

static inline void sl_lsc_link_failure(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.lsc_state != SL_STATE_OUT_OF_SERVICE ) {
        sl_l3_out_of_service(sl, sl->statem.failure_reason);
        sl_iac_stop(sl);                        /* ok if not aligning */
        sl_timer_stop(t1);                      /* ok if not running */
        sl->dcalls->suerm_stop(sl);             /* ok if not running */
        sl_rc_stop(sl);
        sl_txc_send_sios(sl);
        sl_poc_stop(sl);                        /* ok if not ITUT */
        sl->statem.emergency = 0;
        sl->statem.local_processor_outage = 0;
        sl->statem.remote_processor_outage = 0; /* ok if not ANSI */
        sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
    }
}

static void sl_t6_timeout(sl_t *sl) 
{
    DTRACE;
    sl->statem.failure_reason = SL_FAIL_CONG_TIMEOUT;
    sl_lsc_link_failure(sl);
    sl->statem.sib_received = 0;
    sl_timer_stop(t7);
}

static void sl_t7_timeout(sl_t *sl) 
{
    DTRACE;
    sl->statem.failure_reason = SL_FAIL_ACK_TIMEOUT;
    sl_lsc_link_failure(sl);
    sl_timer_stop(t6);
    if ( sl->option.pvar == SS7_PVAR_ITUT_96 )
        sl->statem.sib_received = 0;
}

static inline void sl_txc_send_sib(sl_t *sl) 
{
    DTRACE;
    sl->statem.lssu_available = 1;
    sl->statem.tx.sio = LSSU_SIB;
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_txc_send_sipo(sl_t *sl) 
{
    DTRACE;
    sl_timer_stop(t7);
    if ( sl->option.pvar == SS7_PVAR_ANSI_92 ) sl_timer_stop(t6);
    sl->statem.lssu_available = 1;
    sl->statem.tx.sio = LSSU_SIPO;
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_txc_send_sio(sl_t *sl) 
{
    DTRACE;
    sl->statem.lssu_available = 1;
    sl->statem.tx.sio = LSSU_SIO;
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_txc_send_sin(sl_t *sl) 
{
    DTRACE;
    sl->statem.lssu_available = 1;
    sl->statem.tx.sio = LSSU_SIN;
    sl_daedt_transmitter_wakeup(sl);
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_txc_send_sie(sl_t *sl) 
{
    DTRACE;
    sl->statem.lssu_available = 1;
    sl->statem.tx.sio = LSSU_SIE;
    sl_daedt_transmitter_wakeup(sl);
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_txc_send_msu(sl_t *sl) 
{
    DTRACE;
    if ( sl->rtb.q_count ) sl_timer_start(t7);
    sl->statem.msu_inhibited = 0;
    sl->statem.lssu_available = 0;
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_txc_send_fisu(sl_t *sl) 
{
    DTRACE;
    sl_timer_stop(t7);
    if ( sl->option.pvar == SS7_PVAR_ANSI_92 && !(sl->option.popt & SS7_POPT_PCR) )
        sl_timer_stop(t6);
    sl->statem.msu_inhibited = 1;
    sl->statem.lssu_available = 0;
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_txc_fsnx_value(sl_t *sl) 
{
    DTRACE;
    sl->statem.tx.X.fsn = sl->statem.rx.X.fsn;
}

static inline void sl_txc_nack_to_be_sent(sl_t *sl) 
{
    DTRACE;
    sl->statem.tx.N.bib = sl->statem.tx.N.bib ?0:sl->statem.ib_mask;
}

static inline void sl_lsc_rtb_cleared(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.lsc_state == SL_STATE_PROCESSOR_OUTAGE ) {
        sl->statem.remote_processor_outage = 0;
        if ( sl->statem.local_processor_outage ) return;
        sl_l3_remote_processor_recovered(sl);
        sl_l3_rtb_cleared(sl);
        sl_txc_send_msu(sl);
        sl->statem.lsc_state = SL_STATE_IN_SERVICE;
    }
}

static void sl_check_congestion(sl_t *sl);

static inline void sl_txc_bsnr_and_bibr(sl_t *sl) 
{
    DTRACE;
    sl->statem.tx.R.bsn = sl->statem.rx.R.bsn;
    sl->statem.tx.R.bib = sl->statem.rx.R.bib;
    if ( sl->statem.clear_rtb ) {
        bufq_purge(&sl->rtb);
        sl->statem.Ct = 0;
        sl_check_congestion(sl);
        sl->statem.tx.F.fsn = (sl->statem.tx.R.bsn +1)&sl->statem.sn_mask;
        sl->statem.tx.L.fsn = sl->statem.tx.R.bsn;
        sl->statem.tx.N.fsn = sl->statem.tx.R.bsn;
        sl->statem.tx.N.fib = sl->statem.tx.R.bib;
        sl->statem.Z = (sl->statem.tx.R.bsn +1)&sl->statem.sn_mask;
        sl->statem.z_ptr = NULL;
        sl_lsc_rtb_cleared(sl);
        sl->statem.clear_rtb = 0;
        sl->statem.rtb_full = 0;
        return;
    }
    if ( sl->statem.tx.F.fsn != ((sl->statem.tx.R.bsn +1)&sl->statem.sn_mask) ) {
        if ( sl->statem.sib_received ) {
            sl->statem.sib_received = 0;
            sl_timer_stop(t6);
        }
        do {
            freemsg(bufq_dequeue(&sl->rtb));
            sl->statem.Ct--;
            sl->statem.tx.F.fsn = (sl->statem.tx.F.fsn +1)&sl->statem.sn_mask;
        } while ( sl->statem.tx.F.fsn != ((sl->statem.tx.R.bsn +1)&sl->statem.sn_mask) );
        sl_check_congestion(sl);
        sl_daedt_transmitter_wakeup(sl);
        if ( sl->rtb.q_count == 0 ) {
            sl_timer_stop(t7);
        } else {
            sl_timer_start(t7);
        }
        sl->statem.rtb_full = 0;

#define SN_OUTSIDE(lower,middle,upper) \
    (  ( lower < upper ) \
       ? ( ( middle < lower ) || ( middle > upper ) ) \
       : ( ( middle < lower ) && ( middle > upper ) ) \
       )
        if ( SN_OUTSIDE(sl->statem.tx.F.fsn, sl->statem.Z, sl->statem.tx.L.fsn) )
            sl->statem.Z = sl->statem.tx.F.fsn;
    }
    if ( sl->option.popt & SS7_POPT_PCR ) return;
    if ( sl->statem.tx.N.fib != sl->statem.tx.R.bib ) {
        if ( sl->statem.sib_received ) {
            sl->statem.sib_received = 0;
            sl_timer_stop(t6);
        }
        sl->statem.tx.N.fib = sl->statem.tx.R.bib;
        sl->statem.tx.N.fsn = (sl->statem.tx.F.fsn -1)&sl->statem.sn_mask;
        if ( (sl->statem.z_ptr = sl->rtb.q_first) != NULL )
            sl->statem.retrans_cycle = 1;
        sl_daedt_transmitter_wakeup(sl);
        return;
    }
}

static inline void sl_txc_sib_received(sl_t *sl) 
{
    DTRACE;
    /* FIXME: consider these variations for all */
    if ( sl->option.pvar == SS7_PVAR_ANSI_92 && sl->statem.lssu_available )
        if ( sl->statem.tx.sio != LSSU_SIB ) return;
    if ( sl->option.pvar != SS7_PVAR_ITUT_93 && !sl->rtb.q_count )
        return;
    if ( ! sl->statem.sib_received ) {
        sl_timer_start(t6);
        sl->statem.sib_received = 1;
    }
    sl_timer_start(t7);
}

static inline void sl_txc_clear_rtb(sl_t *sl) 
{
    DTRACE;
    bufq_purge(&sl->rtb);
    sl->statem.Ct = 0;
    sl->statem.clear_rtb = 1;
    sl->statem.rtb_full = 0;  /* added */
    /* FIXME: should probably follow more of the ITUT flush_buffers stuff like
     * reseting Z and FSNF, FSNL, FSNT. */
    sl_check_congestion(sl);
}

static inline void sl_txc_clear_tb(sl_t *sl) 
{
    DTRACE;
    bufq_purge(&sl->tb);
    sl->statem.Cm = 0;
    sl_check_congestion(sl);
}

static inline void sl_txc_flush_buffers(sl_t *sl)
{
    DTRACE;
    bufq_purge(&sl->rtb);
    sl->statem.Ct = 0;
    bufq_purge(&sl->tb);
    sl->statem.rtb_full = 0;
    sl->statem.Cm = 0;
    sl->statem.Z = 0; sl->statem.z_ptr = NULL;
    /* Z =0 error in ITUT 93 and ANSI */
    sl->statem.Z = sl->statem.tx.F.fsn = (sl->statem.tx.R.bsn +1)&sl->statem.sn_mask;
    sl->statem.tx.L.fsn = sl->statem.rx.R.bsn;
    sl->statem.rx.T.fsn = sl->statem.rx.R.bsn;
    sl_timer_stop(t7);
    return;
}

static inline void sl_rc_fsnt_value(sl_t *sl) 
{
    DTRACE;
    sl->statem.rx.T.fsn = sl->statem.tx.N.fsn;
}

static inline void sl_txc_retrieval_request_and_fsnc(sl_t *sl, sl_ulong fsnc)
{
    mblk_t *mp;
    DTRACE;
    sl->statem.tx.C.fsn = fsnc & (sl->statem.sn_mask);
    while ( sl->rtb.q_count && sl->statem.tx.F.fsn != ((fsnc +1)&sl->statem.sn_mask) ) {
        freemsg(bufq_dequeue(&sl->rtb));
        sl->statem.Ct--;
        sl->statem.tx.F.fsn = (sl->statem.tx.F.fsn +1)&sl->statem.sn_mask;
    }
    while ( (mp = bufq_dequeue(&sl->tb)) ) {
        sl->statem.Cm--;
        bufq_queue(&sl->rtb, mp);
        sl->statem.Ct++;
    }
    sl->statem.Z = sl->statem.tx.F.fsn = (sl->statem.tx.C.fsn +1)&sl->statem.sn_mask;
    while ( (mp = bufq_dequeue(&sl->rtb)) ) {
        sl->statem.Ct--;
        sl_l3_retrieved_message(sl, mp);
    }
    sl->statem.rtb_full = 0;
    sl_l3_retrieval_complete(sl);
    sl->statem.Cm = 0;
    sl->statem.Ct = 0;
    sl->statem.tx.N.fsn = sl->statem.tx.L.fsn = sl->statem.tx.C.fsn;
}

static void sl_txc_transmission_request(sl_t *sl) 
{
    int pcr;
    DTRACE;
    if ( sl->statem.txc_state != SL_STATE_IN_SERVICE )
        return;
    pcr = sl->option.popt & SS7_POPT_PCR;
    if ( sl->statem.lssu_available ) {
        if ( sl->statem.tx.sio == LSSU_SIB )
            sl->statem.lssu_available = 0;
        sl->statem.tx.N.fsn = sl->statem.tx.L.fsn;
     /* sl->statem.tx.N.bib = sl->statem.tx.N.bib; */
        sl->statem.tx.N.bsn = (sl->statem.tx.X.fsn - 1)&sl->statem.sn_mask;
     /* sl->statem.tx.N.fib = sl->statem.tx.N.fib; */
        sl_daedt_lssu(sl);
        return;
    }
    if ( sl->statem.msu_inhibited ) {
        sl->statem.tx.N.fsn = sl->statem.tx.L.fsn;
     /* sl->statem.tx.N.bib = sl->statem.tx.N.bib; */
        sl->statem.tx.N.bsn = (sl->statem.tx.X.fsn - 1)&sl->statem.sn_mask;
     /* sl->statem.tx.N.fib = sl->statem.tx.N.fib; */
        sl_daedt_fisu(sl);
        return;
    }
    if ( pcr ) {
        if ( !sl->rtb.q_count ) {
            sl->statem.forced_retransmission = 0;
            sl->statem.rtb_full = 0;
        }
        if ( sl->tb.q_count && sl->statem.rtb_full ) {
            sl->statem.forced_retransmission = 1;
        }
    }
    if ( (!pcr && sl->statem.retrans_cycle) ||
         ( pcr && ( sl->statem.forced_retransmission || ( !sl->tb.q_count && sl->rtb.q_count ) ) ) ) {
        mblk_t *mp = sl->statem.z_ptr;
        sl->statem.z_ptr = mp->b_next;
        sl->statem.tx.N.fsn = (sl->statem.tx.N.fsn + 1)&sl->statem.sn_mask;
     /* sl->statem.tx.N.bib = sl->statem.tx.N.bib; */
        sl->statem.tx.N.bsn = (sl->statem.tx.X.fsn - 1)&sl->statem.sn_mask;
     /* sl->statem.tx.N.fib = sl->statem.tx.N.fib; */
        sl_daedt_msu(sl, mp);
        if ( sl->statem.tx.N.fsn == sl->statem.tx.L.fsn || sl->statem.z_ptr == NULL )
            sl->statem.retrans_cycle = 0;
        return;
    }
    if ( (!pcr && (!sl->tb.q_count || sl->statem.rtb_full) ) ||
         ( pcr && (!sl->tb.q_count && !sl->rtb.q_count ) ) ) {
        sl->statem.tx.N.fsn = sl->statem.tx.L.fsn;
        sl->statem.tx.N.bib = sl->statem.tx.N.bib;
        sl->statem.tx.N.bsn = (sl->statem.tx.X.fsn - 1)&sl->statem.sn_mask;
        sl->statem.tx.N.fib = sl->statem.tx.N.fib;
        sl_daedt_fisu(sl);
        return;
    } else {
        mblk_t *mp;
        if ( !(mp = bufq_dequeue(&sl->tb)) ) return;
        sl->statem.Cm--;
        sl->statem.tx.L.fsn = (sl->statem.tx.L.fsn + 1)&sl->statem.sn_mask;
        sl->statem.tx.N.fsn = sl->statem.tx.L.fsn;
        if ( !sl->rtb.q_count )
            sl_timer_start(t7);
        bufq_queue(&sl->rtb, mp);
        sl->statem.Ct++;
        sl_rc_fsnt_value(sl);
        if ( pcr ) {
            if ( ( sl->statem.Ct >= sl->config.N1 ) || ( sl->rtb.q_count > sl->config.N2 ) )
            {
                sl->statem.rtb_full = 1;
                sl->statem.forced_retransmission = 1;
            }
        } else {
            if ( ( sl->statem.Ct >= sl->config.N1 ) || ( sl->rtb.q_count > sl->config.N2) ||
                    ( sl->statem.tx.L.fsn == ((sl->statem.tx.F.fsn - 2)&sl->statem.sn_mask) ) )
                sl->statem.rtb_full = 1;
        }
     /* sl->statem.tx.N.bib = sl->statem.tx.N.bib; */
        sl->statem.tx.N.bsn = (sl->statem.tx.X.fsn - 1)&sl->statem.sn_mask;
     /* sl->statem.tx.N.fib = sl->statem.tx.N.fib; */
        sl_daedt_msu(sl, mp);
    }
}

static inline void sl_rc_start(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.rc_state == SL_STATE_IDLE ) {
        sl->statem.rx.X.fsn = 0;
        sl->statem.rx.X.fib = sl->statem.ib_mask;
        sl->statem.rx.F.fsn = 0;
        sl->statem.rx.T.fsn = sl->statem.sn_mask;
        sl->statem.rtr = 0;             /* Basic only (note 1). */
        if ( sl->option.pvar == SS7_PVAR_ANSI_92 )
            sl->statem.msu_fisu_accepted = 1;
        else
            sl->statem.msu_fisu_accepted = 0;
        sl->statem.abnormal_bsnr = 0;
        sl->statem.abnormal_fibr = 0;   /* Basic only (note 1). */
        sl->statem.congestion_discard = 0;
        sl->statem.congestion_accept = 0;
        sl->dcalls->daedr_start(sl);
        sl->statem.rc_state = SL_STATE_IN_SERVICE;
        return;
        /*
         * Note 1 - Although rtr and abnormal_fibr are only applicable to the
         *          Basic procedure (and not PCR), these state machine
         *          variables are never examined by PCR routines, so PCR and
         *          basic can share the same start procedures.
         */
    }
}

static inline void sl_rc_reject_msu_fisu(sl_t *sl) 
{
    DTRACE;
    sl->statem.msu_fisu_accepted = 0;
}

static inline void sl_rc_accept_msu_fisu(sl_t *sl) 
{
    DTRACE;
    sl->statem.msu_fisu_accepted = 1;
}

static inline void sl_rc_retrieve_fsnx(sl_t *sl)
{
    DTRACE;
    sl_txc_fsnx_value(sl);  /* error in 93 spec */
    sl->statem.congestion_discard = 0;
    sl->statem.congestion_accept = 0;
    sl_cc_normal(sl);
    sl->statem.rtr = 0; /* basic only */
}

static inline void sl_rc_align_fsnx(sl_t *sl) 
{
    DTRACE;
    sl_txc_fsnx_value(sl);
}

static inline void sl_rc_clear_rb(sl_t *sl) 
{
    DTRACE;
    flushq(sl->rq, FLUSHDATA);
    sl_l3_rb_cleared(sl);
}

static inline void sl_rc_retrieve_bsnt(sl_t *sl) 
{
    DTRACE;
    sl->statem.rx.T.bsn = (sl->statem.rx.X.fsn - 1)&0x7F;
    sl_l3_bsnt(sl, sl->statem.rx.T.bsn);
}

static void sl_t5_timeout(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.cc_state == SL_STATE_BUSY ) {
            sl_txc_send_sib(sl);
            sl_timer_start(t5);
    }
}

static inline void sl_cc_busy(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.cc_state == SL_STATE_NORMAL) {
            sl_txc_send_sib(sl);
            sl_timer_start(t5);
            sl->statem.cc_state = SL_STATE_BUSY;
    }
}

static inline void sl_rc_congestion_discard(sl_t *sl) 
{
    DTRACE;
    sl->statem.congestion_discard = 1;
    sl_cc_busy(sl);
}

static inline void sl_rc_congestion_accept(sl_t *sl) 
{
    DTRACE;
    sl->statem.congestion_accept = 1;
    sl_cc_busy(sl);
}

static inline void sl_rc_no_congestion(sl_t *sl) 
{
    DTRACE;
    sl->statem.congestion_discard = 0;
    sl->statem.congestion_accept = 0;
    sl_cc_normal(sl);
    sl_txc_fsnx_value(sl);
    if ( sl->statem.rtr == 1 ) { /* rtr never set for PCR */
        sl_txc_nack_to_be_sent(sl);
        sl->statem.rx.X.fib = sl->statem.rx.X.fib ?0:sl->statem.ib_mask;
    }
}

static inline void sl_lsc_congestion_discard(sl_t *sl) 
{
    DTRACE;
    sl_rc_congestion_discard(sl);
    sl->statem.l3_congestion_detect = 1;
}

static inline void sl_lsc_congestion_accept(sl_t *sl) 
{
    DTRACE;
    sl_rc_congestion_accept(sl);
    sl->statem.l3_congestion_detect = 1;
}

static inline void sl_lsc_no_congestion(sl_t *sl) 
{
    DTRACE;
    sl_rc_no_congestion(sl);
    sl->statem.l3_congestion_detect = 0;
}

/*
 *  --------------------------------------------------------------------------
 *
 *  These congestion functions are implementation dependent.  We should define
 *  a congestion onset level and set congestion accept at that point.  We
 *  should also define a second congestion onset level and set congestion
 *  discard at that point.  For STREAMS, the upstream congestion can be
 *  detected in two ways: 1) canputnext(): is the upstream module flow
 *  controlled; and, 2) canput(): are we flow controlled.  If the upstream
 *  module is flow controlled, then we can accept MSUs and place them on our
 *  own read queue.  If we are flow contolled, then we have no choice but to
 *  discard the message.  In addition, and because upstream message processing
 *  times are likely more sensitive to the number of backlogged messages than
 *  they are to the number of backlogged message octets, we have some
 *  configurable thresholds of backlogging and keep track of backlogged
 *  messages.
 *
 *  --------------------------------------------------------------------------
 */

static inline void sl_rb_congestion_function(sl_t *sl) 
{
    DTRACE;
    if ( !sl->statem.l3_congestion_detect ) {
        if ( sl->statem.l2_congestion_detect ) {
            if ( sl->statem.Cr <= sl->config.rb_abate && canputnext(sl->rq) ) {
                sl_rc_no_congestion(sl);
                sl->statem.l2_congestion_detect = 0;
            }
        } else {
            if ( sl->statem.Cr >= sl->config.rb_discard || !canput(sl->rq) ) {
                sl_rc_congestion_discard(sl);
                sl->statem.l2_congestion_detect = 1;
            } else
            if ( sl->statem.Cr >= sl->config.rb_accept || !canputnext(sl->rq) ) {
                sl_rc_congestion_accept(sl);
                sl->statem.l2_congestion_detect = 1;
            }
        }
    }
}

static inline void sl_lsc_sio(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.lsc_state) {
        case SL_STATE_OUT_OF_SERVICE:
        case SL_STATE_INITIAL_ALIGNMENT:
            break;
        default:
            sl_timer_stop(t1);  /* ok if no running */
            sl->statem.failure_reason = SL_FAIL_RECEIVED_SIO;
            sl_l3_out_of_service(sl, sl->statem.failure_reason);
            sl_rc_stop(sl);
            sl->dcalls->suerm_stop(sl);
            sl_poc_stop(sl);    /* ok if ANSI */
            sl_txc_send_sios(sl);
            sl->statem.emergency = 0;
            /* FIXME: reinspect */
            sl->statem.local_processor_outage = 0;
            sl->statem.remote_processor_outage = 0; /* ok if ITUT */
            sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
            break;
    }
}

static inline void sl_lsc_alignment_not_possible(sl_t *sl) 
{
    DTRACE;
    sl->statem.failure_reason = SL_FAIL_ALIGNMENT_NOT_POSSIBLE;
    sl_l3_out_of_service(sl, sl->statem.failure_reason);
    sl_rc_stop(sl);
    sl_txc_send_sios(sl);
    sl->statem.local_processor_outage = 0;
    sl->statem.emergency = 0;
    sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
}

static void sl_t3_timeout(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.iac_state == SL_STATE_ALIGNED ) {
            sl_lsc_alignment_not_possible(sl);
            sl->statem.emergency = 0;
            sl->statem.iac_state = SL_STATE_IDLE;
    }
}

static inline void sl_iac_sio(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.iac_state ) {
        case SL_STATE_NOT_ALIGNED :
            sl_timer_stop(t2);
            if ( sl->statem.emergency ) {
                sl->statem.t4v = sl->config.t4e;
                sl_txc_send_sie(sl);
            } else {
                sl->statem.t4v = sl->config.t4n;
                sl_txc_send_sin(sl);
            }
            break;
        case SL_STATE_PROVING :
            sl_timer_stop(t4);
            sl->dcalls->aerm_stop(sl);
            break;
    }
    sl_timer_start(t3);
    sl->statem.iac_state = SL_STATE_ALIGNED;
}

static inline void sl_iac_sios(sl_t *sl) 
{
    DTRACE;
    sl_timer_stop(t4);  /* ok if not running */
    sl_lsc_alignment_not_possible(sl);
    sl->dcalls->aerm_stop(sl); /* ok if not running */
    sl_timer_stop(t3);  /* ok if not running */
    sl->statem.emergency = 0;
    sl->statem.iac_state = SL_STATE_IDLE;
}

static inline void sl_lsc_sios(sl_t *sl) 
{
    DTRACE;
    sl_timer_stop(t1);  /* ok to stop if not running */
    sl->statem.failure_reason = SL_FAIL_RECEIVED_SIOS;
    sl_l3_out_of_service(sl, sl->statem.failure_reason);
    sl->dcalls->suerm_stop(sl);  /* ok to stop if not running */
    sl_rc_stop(sl);
    sl_poc_stop(sl);    /* ok if ANSI */
    sl_txc_send_sios(sl);
    sl->statem.emergency = 0;
    sl->statem.local_processor_outage = 0;
    sl->statem.remote_processor_outage = 0; /* ok if ITU */
    sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
    return;
}

static inline void sl_lsc_no_processor_outage(sl_t* sl)
{
    DTRACE;
    if ( sl->statem.lsc_state == SL_STATE_PROCESSOR_OUTAGE ) {
        sl->statem.processor_outage = 0;
        if ( !sl->statem.l3_indication_received ) return;
        sl->statem.l3_indication_received = 0;
        sl_txc_send_msu(sl);
        sl->statem.local_processor_outage = 0;
        sl_rc_accept_msu_fisu(sl);
        sl->statem.lsc_state = SL_STATE_IN_SERVICE;
    }
}

static inline void sl_poc_remote_processor_recovered(sl_t* sl)
{
    DTRACE;
    switch ( sl->statem.poc_state ) {
        case SL_STATE_REMOTE_PROCESSOR_OUTAGE:
            sl_lsc_no_processor_outage(sl);
            sl->statem.poc_state = SL_STATE_IDLE;
            return;
        case SL_STATE_BOTH_PROCESSORS_OUT:
            sl->statem.poc_state = SL_STATE_LOCAL_PROCESSOR_OUTAGE;
            return;
    }
}

static inline void sl_lsc_fisu_msu_received(sl_t *sl) 
{
    DTRACE;
    switch (sl->statem.lsc_state ) {
        case SL_STATE_ALIGNED_READY :
            sl_l3_in_service(sl);
            if ( sl->option.pvar == SS7_PVAR_ITUT_93 )
                sl_rc_accept_msu_fisu(sl); /* unnecessary */
            sl_timer_stop(t1);
            sl_txc_send_msu(sl);
            sl->statem.lsc_state = SL_STATE_IN_SERVICE;
            return;
        case SL_STATE_ALIGNED_NOT_READY :
            sl_l3_in_service(sl);
            sl_timer_stop(t1);
            sl->statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
            return;
        case SL_STATE_PROCESSOR_OUTAGE :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    sl_poc_remote_processor_recovered(sl);
                    sl_l3_remote_processor_recovered(sl);
                    return;
                case SS7_PVAR_ANSI_92:
                    sl->statem.remote_processor_outage = 0;
                    sl_l3_remote_processor_recovered(sl);
                    return;
                default:
                    /*
                     *  A deviation from the SDLs has been placed here to
                     *  limit the number of remote processor recovered
                     *  indications which are delivered to L3.  One
                     *  indication is sufficient.
                     */
                    if ( sl->statem.remote_processor_outage ) {
                        sl->statem.remote_processor_outage = 0;
                        sl_l3_remote_processor_recovered(sl);
                    }
                    return;
            }
    }
}

static inline void sl_poc_remote_processor_outage(sl_t* sl)
{
    DTRACE;
    switch ( sl->statem.poc_state ) {
        case SL_STATE_IDLE:
            sl->statem.poc_state = SL_STATE_REMOTE_PROCESSOR_OUTAGE;
            return;
        case SL_STATE_LOCAL_PROCESSOR_OUTAGE:
            sl->statem.poc_state = SL_STATE_BOTH_PROCESSORS_OUT;
            return;
    }
}

static inline void sl_lsc_sipo(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_ALIGNED_READY :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    sl_timer_stop(t1);
                    sl_l3_remote_processor_outage(sl);
                    sl_poc_remote_processor_outage(sl);
                    sl->statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
                    return;
                case SS7_PVAR_ANSI_92:
                    sl_timer_stop(t1);
                    sl_l3_remote_processor_outage(sl);
                    sl->statem.remote_processor_outage = 1;
                    sl->statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
                    return;
            }
        case SL_STATE_ALIGNED_NOT_READY :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    sl_l3_remote_processor_outage(sl);
                    sl_poc_remote_processor_outage(sl);
                    sl_timer_stop(t1);
                    sl->statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
                    return;
                case SS7_PVAR_ANSI_92:
                    sl_l3_remote_processor_outage(sl);
                    sl->statem.remote_processor_outage = 1;
                    sl_timer_stop(t1);
                    sl->statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
                    return;
            }
        case SL_STATE_IN_SERVICE :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    sl_txc_send_fisu(sl);
                    sl_l3_remote_processor_outage(sl);
                    sl_poc_remote_processor_outage(sl);
                    sl->statem.processor_outage = 1; /* remote? */
                    sl->statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
                    return;
                case SS7_PVAR_ANSI_92:
                    sl_txc_send_fisu(sl);
                    sl_l3_remote_processor_outage(sl);
                    sl->statem.remote_processor_outage = 1;
                    sl_rc_align_fsnx(sl);
                    sl_cc_stop(sl);
                    sl->statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
                    return;
            }
        case SL_STATE_PROCESSOR_OUTAGE :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    sl_l3_remote_processor_outage(sl);
                    sl_poc_remote_processor_outage(sl);
                    return;
                case SS7_PVAR_ANSI_92:
                    sl->statem.remote_processor_outage = 1;
                    sl_l3_remote_processor_outage(sl);
                    return;
                default:
                    /*
                     *  A deviation from the SDLs has been placed here to
                     *  limit the number of remote processor outage
                     *  indications which are delivered to L3.  One
                     *  indication is sufficient.
                     */
                    if ( !sl->statem.remote_processor_outage ) {
                        sl->statem.remote_processor_outage = 1;
                        sl_l3_remote_processor_outage(sl);
                    }
                    return;
            }
    }
}

static void sl_t2_timeout(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.iac_state == SL_STATE_NOT_ALIGNED ) {
            sl_lsc_alignment_not_possible(sl);
            sl->statem.emergency = 0;
            sl->statem.iac_state = SL_STATE_IDLE;
    }
}

static void sl_t1_timeout(sl_t *sl) 
{
    DTRACE;
    sl->statem.failure_reason = SL_FAIL_T1_TIMEOUT;
    sl_l3_out_of_service(sl, sl->statem.failure_reason);
    sl_rc_stop(sl);
    sl->dcalls->suerm_stop(sl);
    sl_txc_send_sios(sl);
    sl->statem.emergency = 0;
    if ( sl->statem.lsc_state == SL_STATE_ALIGNED_NOT_READY ) {
        sl_poc_stop(sl);        /* ok if ANSI */
        sl->statem.local_processor_outage = 0;
    }
    sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
}

static inline void sl_poc_local_processor_outage(sl_t* sl)
{
    DTRACE;
    switch ( sl->statem.poc_state ) {
        case SL_STATE_IDLE:
            sl->statem.poc_state = SL_STATE_LOCAL_PROCESSOR_OUTAGE;
            return;
        case SL_STATE_REMOTE_PROCESSOR_OUTAGE:
            sl->statem.poc_state = SL_STATE_BOTH_PROCESSORS_OUT;
            return;
    }
}

static inline void sl_lsc_alignment_complete(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.lsc_state == SL_STATE_INITIAL_ALIGNMENT ) {
        sl->dcalls->suerm_start(sl);
        sl_timer_start(t1);
        if ( sl->statem.local_processor_outage ) {
            if ( sl->option.pvar != SS7_PVAR_ANSI_92 )
                sl_poc_local_processor_outage(sl);
            sl_txc_send_sipo(sl);
            if ( sl->option.pvar != SS7_PVAR_ITUT_93 )  /* possible error */
            sl_rc_reject_msu_fisu(sl);
            sl->statem.lsc_state = SL_STATE_ALIGNED_NOT_READY;
        } else {
            sl_txc_send_fisu(sl);
            sl_rc_accept_msu_fisu(sl); /* error in ANSI spec? */
            sl->statem.lsc_state = SL_STATE_ALIGNED_READY;
        }
    }
}

static void sl_t4_timeout(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.iac_state == SL_STATE_PROVING ) {
        if ( sl->statem.further_proving ) {
            sl->dcalls->aerm_start(sl);
            sl->statem.further_proving = 0;
            sl_timer_start_t4(t4v);
        } else {
            sl_lsc_alignment_complete(sl);
            sl->dcalls->aerm_stop(sl);
            sl->statem.emergency = 0;
            sl->statem.iac_state = SL_STATE_IDLE;
        }
    }
}

static inline void sl_lsc_sin(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_IN_SERVICE :
            sl->statem.failure_reason = SL_FAIL_RECEIVED_SIN;
            sl_l3_out_of_service(sl, sl->statem.failure_reason);
            sl->dcalls->suerm_stop(sl);
            sl_rc_stop(sl);
            sl_txc_send_sios(sl);
            sl->statem.emergency = 0;
            sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
            return;
        case SL_STATE_PROCESSOR_OUTAGE :
            sl->statem.failure_reason = SL_FAIL_RECEIVED_SIN;
            sl_l3_out_of_service(sl, sl->statem.failure_reason);
            sl->dcalls->suerm_stop(sl);
            sl_rc_stop(sl);
            sl_poc_stop(sl);                        /* ok if not ITUT */
            sl_txc_send_sios(sl);
            sl->statem.emergency = 0;
            sl->statem.local_processor_outage = 0;
            sl->statem.remote_processor_outage = 0; /* ok if not ANSI */
            sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
            return;
    }
}

static inline void sl_iac_sin(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.iac_state ) {
        case SL_STATE_NOT_ALIGNED :
            sl_timer_stop(t2);
            if ( sl->statem.emergency ) {
                sl->statem.t4v = sl->config.t4e;
                sl_txc_send_sie(sl);
            } else {
                sl->statem.t4v = sl->config.t4n;
                sl_txc_send_sin(sl);
            }
            sl_timer_start(t3);
            sl->statem.iac_state = SL_STATE_ALIGNED;
            return;
        case SL_STATE_ALIGNED :
            sl_timer_stop(t3);
            if ( sl->statem.t4v == sl->config.t4e )
                sl->dcalls->aerm_set_tie(sl);
            sl->dcalls->aerm_start(sl);
            sl_timer_start_t4(t4v);
            sl->statem.Cp = 0;
            sl->statem.further_proving = 0;
            sl->statem.iac_state = SL_STATE_PROVING;
            return;
    }
}

static inline void sl_lsc_sie(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_IN_SERVICE :
            sl->statem.failure_reason = SL_FAIL_RECEIVED_SIE;
            sl_l3_out_of_service(sl, sl->statem.failure_reason);
            sl->dcalls->suerm_stop(sl);
            sl_rc_stop(sl);
            sl_txc_send_sios(sl);
            sl->statem.emergency = 0;
            sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
            return;
        case SL_STATE_PROCESSOR_OUTAGE :
            sl->statem.failure_reason = SL_FAIL_RECEIVED_SIE;
            sl_l3_out_of_service(sl, sl->statem.failure_reason);
            sl->dcalls->suerm_stop(sl);
            sl_rc_stop(sl);
            sl_poc_stop(sl);                            /* ok if not ITUT */
            sl_txc_send_sios(sl);
            sl->statem.emergency = 0;
            sl->statem.local_processor_outage = 0;
            sl->statem.remote_processor_outage = 0;     /* ok if not ANSI */
            sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
            return;
    }
}

static inline void sl_iac_sie(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.iac_state ) {
        case SL_STATE_NOT_ALIGNED :
            sl_timer_stop(t2);
            if ( sl->statem.emergency ) {
                sl->statem.t4v = sl->config.t4e;
                sl_txc_send_sie(sl);
            } else {
                sl->statem.t4v = sl->config.t4e; /* yes e */
                sl_txc_send_sin(sl);
            }
            sl_timer_start(t3);
            sl->statem.iac_state = SL_STATE_ALIGNED;
            return;
        case SL_STATE_ALIGNED :
            sl->statem.t4v = sl->config.t4e;
            sl_timer_stop(t3);
            sl->dcalls->aerm_set_tie(sl);
            sl->dcalls->aerm_start(sl);
            sl_timer_start_t4(t4v);
            sl->statem.Cp = 0;
            sl->statem.further_proving = 0;
            sl->statem.iac_state = SL_STATE_PROVING;
            return;
        case SL_STATE_PROVING :
            if ( sl->statem.t4v == sl->config.t4e )
                return;
            sl_timer_stop(t4);
            sl->statem.t4v = sl->config.t4e;
            sl->dcalls->aerm_stop(sl);
            sl->dcalls->aerm_set_tie(sl);
            sl->dcalls->aerm_start(sl);
            sl->statem.further_proving = 0;
            sl_timer_start_t4(t4v);
            return;
    }
}

static inline void sl_rc_signal_unit(sl_t *sl, mblk_t *mp) 
{
    DTRACE;
    /*
     *  Note: the driver must check that the length of the frame is within
     *  appropriate bounds as specified by the DAEDR in Q.703.  If the
     *  length of the frame is incorrect, it should indicate daedr_error-
     *  _frame rather than daedr_received_frame.
     */
    if ( sl->option.popt & SS7_POPT_XSN ) {
        sl->statem.rx.R.bsn = ntohs(*((sl_ushort *)mp->b_rptr)  ) & 0x0fff;
        sl->statem.rx.R.bib = ntohs(*((sl_ushort *)mp->b_rptr)++) & 0x8000;
        sl->statem.rx.R.fsn = ntohs(*((sl_ushort *)mp->b_rptr)  ) & 0x0fff;
        sl->statem.rx.R.fib = ntohs(*((sl_ushort *)mp->b_rptr)++) & 0x8000;
        sl->statem.rx.len   = ntohs(*((sl_ushort *)mp->b_rptr)++) & 0x01ff;
        sl->statem.rx.sio   = *mp->b_rptr++ & 0x07;
    } else {
        sl->statem.rx.R.bsn = *mp->b_rptr   & 0x7f;
        sl->statem.rx.R.bib = *mp->b_rptr++ & 0x80;
        sl->statem.rx.R.fsn = *mp->b_rptr   & 0x7f;
        sl->statem.rx.R.fib = *mp->b_rptr++ & 0x80;
        sl->statem.rx.len   = *mp->b_rptr++ & 0x3f;
        sl->statem.rx.sio   = *mp->b_rptr++ & 0x07;
    }
    DPRINT(0,("rx: bsn=%x, bib=%x, fsn=%x, fib=%x, len=%d, sio=%d\n",
                sl->statem.rx.R.bsn,
                sl->statem.rx.R.bib,
                sl->statem.rx.R.fsn,
                sl->statem.rx.R.fib,
                sl->statem.rx.len,
                sl->statem.rx.sio
                ));
    if ( sl->statem.rc_state != SL_STATE_IN_SERVICE ) { freemsg(mp); return; }
    if ( ((sl->statem.rx.len )==1)||((sl->statem.rx.len )==2) ) {
        switch ( sl->statem.rx.sio ) {
            case LSSU_SIO : { sl_lsc_sio (sl); sl_iac_sio (sl); break; }
            case LSSU_SIN : { sl_lsc_sin (sl); sl_iac_sin (sl); break; }
            case LSSU_SIE : { sl_lsc_sie (sl); sl_iac_sie (sl); break; }
            case LSSU_SIOS: { sl_lsc_sios(sl); sl_iac_sios(sl); break; }
            case LSSU_SIPO: { sl_lsc_sipo(sl);                  break; }
            case LSSU_SIB : { sl_txc_sib_received(sl);          break; }
        }
        freemsg(mp);
        return;
    }
    if ( SN_OUTSIDE(((sl->statem.rx.F.fsn-1)&sl->statem.sn_mask), sl->statem.rx.R.bsn, sl->statem.rx.T.fsn) ) {
        freemsg(mp);
        if ( sl->statem.abnormal_bsnr ) {
            sl->statem.failure_reason = SL_FAIL_ABNORMAL_BSNR;
            sl_lsc_link_failure(sl);
            sl->statem.rc_state = SL_STATE_IDLE;
            freemsg(mp);
            return;
        } else {
            sl->statem.abnormal_bsnr = 1;
            sl->statem.unb = 0;
            freemsg(mp);
            return;
        }
    }
    if ( sl->statem.abnormal_bsnr ) {
        if ( sl->statem.unb == 1 ) {
            sl->statem.abnormal_bsnr = 0;
        } else {
            sl->statem.unb = 1;
            freemsg(mp);
            return;
        }
    }
    if ( sl->option.popt & SS7_POPT_PCR ) {
        sl_lsc_fisu_msu_received(sl);
        sl_txc_bsnr_and_bibr(sl);
        sl->statem.rx.F.fsn = (sl->statem.rx.R.bsn +1)&sl->statem.sn_mask;
        if ( ! sl->statem.msu_fisu_accepted ) {
            freemsg(mp);
            return;
        }
        sl_rb_congestion_function(sl);
        if ( sl->statem.congestion_discard ) {
            freemsg(mp);
            sl_cc_busy(sl);
            return;
        }
        if ( (sl->statem.rx.R.fsn == sl->statem.rx.X.fsn ) && (sl->statem.rx.len > 2) ) {
            sl_l3_pdu(sl, mp);
            sl->statem.rx.X.fsn = (sl->statem.rx.X.fsn +1)&sl->statem.sn_mask;
            if ( sl->statem.congestion_accept )
                sl_cc_busy(sl);
            else sl_txc_fsnx_value(sl);
            return;
        } else {
            freemsg(mp);
            return;
        }
    }
    if ( sl->statem.rx.R.fib == sl->statem.rx.X.fib ) {
        if ( sl->statem.abnormal_fibr ) {
            if ( sl->statem.unf == 1 ) {
                sl->statem.abnormal_fibr = 0;
            } else {
                sl->statem.unf = 1;
                freemsg(mp);
                return;
            }
        }

        sl_lsc_fisu_msu_received(sl);
        sl_txc_bsnr_and_bibr(sl);
        sl->statem.rx.F.fsn = (sl->statem.rx.R.bsn +1)&sl->statem.sn_mask;
        if ( ! sl->statem.msu_fisu_accepted ) {
            freemsg(mp);
            return;
        }
        sl_rb_congestion_function(sl);
        if ( sl->statem.congestion_discard ) {
            sl->statem.rtr = 1;
            freemsg(mp);
            sl_cc_busy(sl);
            return;
        }
        if ( (sl->statem.rx.R.fsn == sl->statem.rx.X.fsn ) && (sl->statem.rx.len > 2) ) {
            sl->statem.rx.X.fsn = (sl->statem.rx.X.fsn +1)&sl->statem.sn_mask;
            sl->statem.rtr = 0;
            sl_l3_pdu(sl, mp);
            if ( sl->statem.congestion_accept )
                 sl_cc_busy(sl);
            else sl_txc_fsnx_value(sl);
            return;
        }
        if ( (sl->statem.rx.R.fsn == ((sl->statem.rx.X.fsn -1)&sl->statem.sn_mask)) ) {
            freemsg(mp);
            return;
        } else {

            if ( sl->statem.congestion_accept ) {
                sl->statem.rtr = 1;
                sl_cc_busy(sl); /* not required? */
                freemsg(mp);
                return;
            } else {
                sl_txc_nack_to_be_sent(sl);
                sl->statem.rtr = 1;
                sl->statem.rx.X.fib = sl->statem.rx.X.fib ?0:sl->statem.ib_mask;
                freemsg(mp);
                return;
            }
        }
    } else {
        freemsg(mp);
        if ( sl->statem.abnormal_fibr ) {
            sl->statem.failure_reason = SL_FAIL_ABNORMAL_FIBR;
            sl_lsc_link_failure(sl);
            return;
        }
        if ( sl->statem.rtr == 1 ) {
            sl_txc_bsnr_and_bibr(sl);
            sl->statem.rx.F.fsn = (sl->statem.rx.R.bsn +1)&sl->statem.sn_mask;
            return;
        }
        sl->statem.abnormal_fibr = 1;
        sl->statem.unf = 0;
        return;
    }
}

static inline void sl_lsc_stop(sl_t *sl)
{
    if ( sl->statem.lsc_state != SL_STATE_OUT_OF_SERVICE ) {
        sl_iac_stop(sl);                /* ok if not running */
        sl_timer_stop(t1);              /* ok if not running */
        sl_rc_stop(sl);
        sl->dcalls->suerm_stop(sl);     /* ok if not running */
        sl_poc_stop(sl);                /* ok if not running or not ITUT */
        sl_txc_send_sios(sl);
        sl->statem.emergency = 0;
        sl->statem.local_processor_outage = 0;
        sl->statem.remote_processor_outage = 0;     /* ok of not ANSI */
        sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
    }
}

static inline void sl_lsc_clear_rtb(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.lsc_state == SL_STATE_PROCESSOR_OUTAGE ) {
        sl->statem.local_processor_outage = 0;
        sl_txc_send_fisu(sl);
        sl_txc_clear_rtb(sl);
    }
}

static inline void sl_iac_correct_su(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.iac_state == SL_STATE_PROVING ) {
        if ( sl->statem.further_proving ) {
            sl_timer_stop(t4);
            sl->dcalls->aerm_start(sl);
            sl->statem.further_proving = 0;
            sl_timer_start_t4(t4v);
        }
    }
}

static inline void sl_iac_abort_proving(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.iac_state == SL_STATE_PROVING ) {
        sl->statem.Cp ++;
        if ( sl->statem.Cp == sl->config.M ) {
            sl_lsc_alignment_not_possible(sl);
            sl_timer_stop(t4);
            sl->dcalls->aerm_stop(sl);
            sl->statem.emergency = 0;
            sl->statem.iac_state = SL_STATE_IDLE;
            return;
        }
        sl->statem.further_proving = 1;
    }
}

#define sl_lsc_flush_buffers sl_lsc_clear_buffers
static inline void sl_lsc_clear_buffers(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_OUT_OF_SERVICE :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    return;
                case SS7_PVAR_ANSI_92:
                    sl_l3_rtb_cleared(sl);
                    sl->statem.local_processor_outage = 0; /* ??? */
                    return;
            }
        case SL_STATE_INITIAL_ALIGNMENT :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    return;
                case SS7_PVAR_ANSI_92:
                    sl_l3_rtb_cleared(sl);
                    sl->statem.local_processor_outage = 0;
                    return;
            }
        case SL_STATE_ALIGNED_NOT_READY :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    return;
                case SS7_PVAR_ANSI_92:
                    sl_l3_rtb_cleared(sl);
                    sl->statem.local_processor_outage = 0;
                    sl_txc_send_fisu(sl);
                    sl->statem.lsc_state = SL_STATE_ALIGNED_READY;
                    return;
            }
        case SL_STATE_PROCESSOR_OUTAGE :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    sl_txc_flush_buffers(sl);
                    sl->statem.l3_indication_received = 1;
                    if ( sl->statem.processor_outage )
                        return;
                    sl->statem.l3_indication_received = 0;
                    sl_txc_send_msu(sl);
                    sl->statem.local_processor_outage = 0;
                    sl_rc_accept_msu_fisu(sl);
                    sl->statem.lsc_state = SL_STATE_IN_SERVICE;
                    return;
                case SS7_PVAR_ANSI_92:
                    sl->statem.local_processor_outage = 0;
                    sl_rc_clear_rb(sl);
                    sl_rc_accept_msu_fisu(sl);
                    sl_txc_send_fisu(sl);
                    sl_txc_clear_tb(sl);
                    sl_txc_clear_rtb(sl);
                    return;
            }
    }
}

static inline void sl_lsc_continue(sl_t* sl, mblk_t *mp)
{
    DTRACE;
    if ( sl->statem.lsc_state == SL_STATE_PROCESSOR_OUTAGE ) {
        if ( sl->statem.processor_outage ) return;
        sl->statem.l3_indication_received = 0;
        sl_txc_send_msu(sl);
        sl->statem.local_processor_outage = 0;
        sl_rc_accept_msu_fisu(sl);
        sl->statem.lsc_state = SL_STATE_IN_SERVICE;
    }
}

static inline void sl_poc_local_processor_recovered(sl_t* sl)
{
    DTRACE;
    switch ( sl->statem.poc_state ) {
        case SL_STATE_LOCAL_PROCESSOR_OUTAGE:
            sl_lsc_no_processor_outage(sl);
            sl->statem.poc_state = SL_STATE_IDLE;
            return;
        case SL_STATE_BOTH_PROCESSORS_OUT:
            sl->statem.poc_state = SL_STATE_REMOTE_PROCESSOR_OUTAGE;
            return;
    }
}

#define sl_lsc_resume sl_lsc_local_processor_recovered
static inline void sl_lsc_local_processor_recovered(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_OUT_OF_SERVICE :
            sl->statem.local_processor_outage = 0;
            return;
        case SL_STATE_INITIAL_ALIGNMENT :
            sl->statem.local_processor_outage = 0;
            return;
        case SL_STATE_ALIGNED_READY :
            return;
        case SL_STATE_ALIGNED_NOT_READY :
            if ( sl->option.pvar != SS7_PVAR_ANSI_92 )
                sl_poc_local_processor_recovered(sl);
            sl->statem.local_processor_outage = 0;
            sl_txc_send_fisu(sl);
            if ( sl->option.pvar == SS7_PVAR_ITUT_96 )
                sl_rc_accept_msu_fisu(sl);
            sl->statem.lsc_state = SL_STATE_ALIGNED_READY;
            return;
        case SL_STATE_PROCESSOR_OUTAGE :
            switch ( sl->option.pvar ) {
                case SS7_PVAR_ITUT_93:
                case SS7_PVAR_ITUT_96:
                    sl_poc_local_processor_recovered(sl);
                    sl_rc_retrieve_fsnx(sl);
                    sl_txc_send_fisu(sl); /* note 3: in fisu BSN <= FSNX-1 */
                    sl->statem.lsc_state = SL_STATE_IN_SERVICE;
                case SS7_PVAR_ANSI_92:
                    sl->statem.local_processor_outage = 0;
                    sl_rc_accept_msu_fisu(sl);
                    if ( sl->statem.remote_processor_outage ) {
                        sl_txc_send_fisu(sl);
                        sl_l3_remote_processor_outage(sl);
                        return;
                    }
                    sl_txc_send_msu(sl);
                    sl->statem.lsc_state = SL_STATE_IN_SERVICE;
                    return;
            }
    }
}

#define sl_lsc_level_3_failure sl_lsc_local_processor_outage
static inline void sl_lsc_local_processor_outage(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_OUT_OF_SERVICE :
        case SL_STATE_INITIAL_ALIGNMENT :
            sl->statem.local_processor_outage = 1;
            return;
        case SL_STATE_ALIGNED_READY :
            if ( sl->option.pvar == SS7_PVAR_ANSI_92 )
                 sl->statem.local_processor_outage = 1;
            else sl_poc_local_processor_outage(sl);
            sl_txc_send_sipo(sl);
            if ( sl->option.pvar != SS7_PVAR_ITUT_93 ) /* possible error 93 specs */
                sl_rc_reject_msu_fisu(sl);
            sl->statem.lsc_state = SL_STATE_ALIGNED_NOT_READY;
            return;
        case SL_STATE_IN_SERVICE :
            if ( sl->option.pvar == SS7_PVAR_ANSI_92 ) {
                sl->statem.local_processor_outage = 1;
            } else {
                sl_poc_local_processor_outage(sl);
                sl->statem.processor_outage = 1;
            }
            sl_txc_send_sipo(sl);
            sl_rc_reject_msu_fisu(sl);
            if ( sl->option.pvar == SS7_PVAR_ANSI_92 ) {
                sl_rc_align_fsnx(sl);
                sl_cc_stop(sl);
            }
            sl->statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
            return;
        case SL_STATE_PROCESSOR_OUTAGE :
            if ( sl->option.pvar == SS7_PVAR_ANSI_92 )
                sl->statem.local_processor_outage = 1;
            else
                sl_poc_local_processor_outage(sl);
            sl_txc_send_sipo(sl);
            return;
    }
}

static inline void sl_iac_emergency(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.iac_state ) {
        case SL_STATE_PROVING :
            sl_txc_send_sie(sl);
            sl_timer_stop(t4);
            sl->statem.t4v = sl->config.t4e;
            sl->dcalls->aerm_stop(sl);
            sl->dcalls->aerm_set_tie(sl);
            sl->dcalls->aerm_start(sl);
            sl->statem.further_proving = 0;
            sl_timer_start_t4(t4v);
            return;
        case SL_STATE_ALIGNED :
            sl_txc_send_sie(sl);
            sl->statem.t4v = sl->config.t4e;
            return;
        case SL_STATE_IDLE:
        case SL_STATE_NOT_ALIGNED:
            sl->statem.emergency = 1;
    }
}

static inline void sl_lsc_emergency(sl_t *sl)
{
    DTRACE;
    sl->statem.emergency = 1;
}

static inline void sl_lsc_emergency_ceases(sl_t *sl)
{
    DTRACE;
    sl->statem.emergency = 0;
}

static inline void sl_iac_start(sl_t *sl) 
{
    DTRACE;
    if ( sl->statem.iac_state == SL_STATE_IDLE ) {
        sl_txc_send_sio(sl);
        sl_timer_start(t2);
        sl->statem.iac_state = SL_STATE_NOT_ALIGNED;
    }
}

static inline void sl_txc_start(sl_t *sl) 
{
    DTRACE;
    sl->statem.forced_retransmission = 0;           /* ok if basic */
    sl->statem.sib_received = 0;
    sl->statem.Ct = 0;
    sl->statem.rtb_full = 0;
    sl->statem.clear_rtb = 0;                       /* ok if ITU */
    if ( sl->option.pvar == SS7_PVAR_ANSI_92 ) {
        sl->statem.tx.sio = LSSU_SIOS;
        sl->statem.lssu_available = 1;
    }
    sl->statem.msu_inhibited = 0;
    sl->statem.tx.L.fsn = sl->statem.tx.N.fsn = sl->statem.sn_mask;
    sl->statem.tx.X.fsn = 0;
    sl->statem.tx.N.fib = sl->statem.tx.N.bib = sl->statem.ib_mask;
    sl->statem.tx.F.fsn = 0;
    sl->statem.Cm = 0;
    sl->statem.Z = 0; sl->statem.z_ptr = NULL;      /* ok if basic */
    if ( sl->statem.txc_state == SL_STATE_IDLE ) {
        if ( sl->option.pvar != SS7_PVAR_ANSI_92 )
            sl->statem.lssu_available = 0;
        sl->dcalls->daedt_start(sl);
    }
    sl->statem.txc_state = SL_STATE_SLEEPING;
    return;
}

static inline void sl_lsc_start(sl_t *sl)
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_OUT_OF_SERVICE:
            sl_rc_start(sl);
            sl_txc_start(sl); /* Note 2 */
            if ( sl->statem.emergency ) sl_iac_emergency(sl);
            sl_iac_start(sl);
            sl->statem.lsc_state = SL_STATE_INITIAL_ALIGNMENT;
    }
}
    /*
     *  Note 2: There is a difference here between ANSI_92 and ITUT_93/96 in
     *          that the transmitters in the ANSI_92 case may transmit one
     *          or two SIOSs before transmitting the first SIO of the
     *          initial alignment procedure.  ITUT will continue idling FISU
     *          or LSSU as before the start, then transmit the first SIO.
     *          These are equivalent.  Because the LSC is in the OUT OF
     *          SERVICE state, the transmitters should be idling SIOS
     *          anyway.
     */

static inline void sl_lsc_retrieve_bsnt(sl_t *sl)
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_OUT_OF_SERVICE:
        case SL_STATE_PROCESSOR_OUTAGE:
            sl_rc_retrieve_bsnt(sl);
    }
}

static inline void sl_lsc_retrieval_request_and_fsnc(sl_t *sl, sl_ulong fsnc)
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_OUT_OF_SERVICE:
        case SL_STATE_PROCESSOR_OUTAGE:
            sl_txc_retrieval_request_and_fsnc(sl, fsnc);
    }
}

/*
 *  This power-on sequence should only be performed once, regardless of how
 *  many times the device driver is opened or closed.  This initializes the
 *  transmitters to send SIOS and should never be changed hence.
 */

static inline void sl_lsc_power_on(sl_t *sl) 
{
    DTRACE;
    switch ( sl->statem.lsc_state ) {
        case SL_STATE_POWER_OFF:
            sl_txc_start(sl);           /* Note 3 */
            sl_txc_send_sios(sl);       /* not necessary for ANSI */
            sl->dcalls->aerm_set_tin(sl);
            sl->statem.local_processor_outage = 0;
            sl->statem.emergency = 0;
            sl->statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
    }
}
    /*
     *  Note 3: There is a difference here between ANSI_92 and ITUT_93/96 in
     *          that the transmitters in the ITUT case may transmit one or
     *          two FISUs before transmitting SIOS on initial power-up.
     *          ANSI will send SIOS on power-up.  ANSI is the correct
     *          procedure as transmitters should always idle SIOS on
     *          power-up.
     */

/*
 *  The transmit congestion algorithm is an implementation dependent
 *  algorithm but is suggested as being based on TB and/or RTB buffer
 *  occupancy.  With STREAMS we can use octet count buffer occupancy over
 *  message count occupancy, because congestion in transmission is more
 *  related to octet count (because it determines transmission latency).
 *
 *  We check the total buffer occupancy and apply the necessary congestion
 *  control signal as per configured abatement, onset and discard
 *  thresholds.
 */
static void sl_check_congestion(sl_t *sl)
{
    unsigned int occupancy = sl->tb.q_count + sl->rtb.q_count;
    int old_cong_level = sl->statem.cong_level;
    int old_disc_level = sl->statem.disc_level;
    int multi = sl->option.popt & SS7_POPT_MPLEV;

    DTRACE;
    switch (sl->statem.cong_level) {
        case 0:
            if ( occupancy >= sl->config.tb_onset_1 ) {
                sl->statem.cong_level = 1;
                if ( occupancy >= sl->config.tb_discd_1 ) {
                    sl->statem.disc_level = 1;
                    if ( !multi ) break;
                    if ( occupancy >= sl->config.tb_onset_2 ) {
                        sl->statem.cong_level = 2;
                        if ( occupancy >= sl->config.tb_discd_2 ) {
                            sl->statem.disc_level = 2;
                            if ( occupancy >= sl->config.tb_onset_3 ) {
                                sl->statem.cong_level = 3;
                                if ( occupancy >= sl->config.tb_discd_3 ) {
                                    sl->statem.disc_level = 3; } } } } } }
            break;
        case 1:
            if ( occupancy <  sl->config.tb_abate_1 ) {
                sl->statem.cong_level = 0;
                sl->statem.disc_level = 0;
            } else {
                if ( !multi ) break;
                if ( occupancy >= sl->config.tb_onset_2 ) {
                    sl->statem.cong_level = 2;
                    if ( occupancy >= sl->config.tb_discd_2 ) {
                        sl->statem.disc_level = 2;
                        if ( occupancy >= sl->config.tb_onset_3 ) {
                            sl->statem.cong_level = 3;
                            if ( occupancy >= sl->config.tb_discd_3 ) {
                                sl->statem.disc_level = 3; } } } } }
            break;
        case 2:
            if ( !multi ) {
                sl->statem.cong_level = 1;
                sl->statem.disc_level = 1;
                break;
            }
            if ( occupancy <  sl->config.tb_abate_2 ) {
                sl->statem.cong_level = 1;
                sl->statem.disc_level = 1;
                if ( occupancy <  sl->config.tb_abate_1 ) {
                    sl->statem.cong_level = 0;
                    sl->statem.disc_level = 0; } }
            else
            if ( occupancy >= sl->config.tb_onset_3 ) {
                sl->statem.cong_level = 3;
                if ( occupancy >= sl->config.tb_discd_3 ) {
                    sl->statem.disc_level = 3; } }
            break;
        case 3:
            if ( !multi ) {
                sl->statem.cong_level = 1;
                sl->statem.disc_level = 1;
                break;
            }
            if ( occupancy <  sl->config.tb_abate_3 ) {
                sl->statem.cong_level = 2;
                sl->statem.disc_level = 2;
                if ( occupancy <  sl->config.tb_abate_2 ) {
                    sl->statem.cong_level = 1;
                    sl->statem.disc_level = 1;
                    if ( occupancy <  sl->config.tb_abate_1 ) {
                        sl->statem.cong_level = 0;
                        sl->statem.disc_level = 0; } } }
            break;
    }
    if ( sl->statem.cong_level != old_cong_level
      || sl->statem.disc_level != old_disc_level ) {
        if ( sl->statem.cong_level < old_cong_level )
            sl_l3_link_congestion_ceased(sl, sl->statem.cong_level, sl->statem.disc_level);
        else {
            if ( sl->statem.cong_level > old_cong_level ) {
                if ( sl->notify.events & SL_EVT_CONGEST_ONSET_IND &&
                        !sl->stats.sl_cong_onset_ind[sl->statem.cong_level]++ ) {
                    sl_l3_link_congested(sl, sl->statem.cong_level, sl->statem.disc_level);
                    return;
                }
            } else {
                if ( sl->notify.events & SL_EVT_CONGEST_DISCD_IND &&
                        !sl->stats.sl_cong_discd_ind[sl->statem.disc_level]++ ) {
                    sl_l3_link_congested(sl, sl->statem.cong_level, sl->statem.disc_level);
                    return;
                }
            }
            sl_l3_link_congested(sl, sl->statem.cong_level, sl->statem.disc_level);
        }
    }
}

static inline void sl_txc_message_for_transmission(sl_t *sl, mblk_t *mp)
{
    DTRACE;
    bufq_queue(&sl->tb, mp);
    sl->statem.Cm++;
    sl_check_congestion(sl);
    sl_daedt_transmitter_wakeup(sl);
}

static inline void sl_lsc_pdu(sl_t *sl, mblk_t *mp)
{
    mblk_t *md;
    DTRACE;
    if (mp->b_datap->db_type != M_DATA) {
        md = unlinkb(mp);
        freemsg(mp);
    } else
        md = mp;
    if ( md ) {
        int hlen = (sl->option.popt&SS7_POPT_XSN)?6:3;
        if ( md->b_datap->db_type != M_DATA ) {
            freemsg(md);
            return;
        }
        if ( (md->b_datap->db_base - md->b_rptr) >= hlen )
            md->b_rptr -= hlen;
        else {
            mblk_t *mh = allocb(hlen, BPRI_HI);
            if ( mh ) {
                mh->b_wptr += hlen;
                linkb(mh, md);
                md = mh;
            } else {
                freemsg(md);
                return;
            }
        }
        sl_txc_message_for_transmission(sl, md);
    }
}

/*
 *  Calls from Management:
 *
 *  sl_lsc_power_on
 *  sl_lsc_local_processor_outage
 *  sl_lsc_level_3_failure
 *
 *
 *
 */

#endif  __SL_SM_H__

