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

 @(#) sl_sm.h,v 0.7.8.1 2001/12/11 13:15:40 brian Exp

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

 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 2001/12/11 13:15:40 by brian

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

#ifndef __SL_SM_H__
#define __SL_SM_H__

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

#define SN_OUTSIDE(lower,middle,upper) \
	(  ( (lower) <= (upper) ) \
	   ? ( ( (middle) < (lower) ) || ( (middle) > (upper) ) ) \
	   : ( ( (middle) < (lower) ) && ( (middle) > (upper) ) ) \
	   )

#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;
	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;
		}
#if 0
		ptrace(("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
					));
#endif
		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);
	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);
#if 0
		ptrace(("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
					));
#endif
		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);
	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);
		}
#if 0
		ptrace(("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
					));
#endif
		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) 
{
	sl_timer_stop(t5);
	sl->statem.cc_state = SL_STATE_IDLE;
}

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

static inline void sl_iac_stop(sl_t *sl) 
{
	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) 
{
	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) 
{
	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)
{
	sl->statem.poc_state = SL_STATE_IDLE;
}

static inline void sl_lsc_link_failure(sl_t *sl) 
{
	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) 
{
	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) 
{
	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) 
{
	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) 
{
	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) 
{
	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) 
{
	sl->statem.lssu_available = 1;
	sl->statem.tx.sio = LSSU_SIN;
	sl_daedt_transmitter_wakeup(sl);
}

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

static inline void sl_txc_send_msu(sl_t *sl) 
{
	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) 
{
	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) 
{
	if ( sl->statem.tx.X.fsn != sl->statem.rx.X.fsn )
	{
		sl->statem.tx.X.fsn = sl->statem.rx.X.fsn;
		sl_daedt_transmitter_wakeup(sl);
	}
}

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

static inline void sl_lsc_rtb_cleared(sl_t *sl) 
{
	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) 
{
	int pcr = sl->option.popt & SS7_POPT_PCR;
	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);
		}
		if ( !pcr || ( sl->rtb.q_msgs < sl->config.N1 && sl->rtb.q_count < sl->config.N2 ) )
			sl->statem.rtb_full = 0;
		if ( SN_OUTSIDE(sl->statem.tx.F.fsn, sl->statem.Z, sl->statem.tx.L.fsn) ||
				!sl->rtb.q_count)
		{
			sl->statem.Z = sl->statem.tx.F.fsn;
			sl->statem.z_ptr = sl->rtb.q_head;
		}
	}
	if ( 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_head) != NULL )
			sl->statem.retrans_cycle = 1;
		sl_daedt_transmitter_wakeup(sl);
		return;
	}
}

static inline void sl_txc_sib_received(sl_t *sl)
{
	/* 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) 
{
	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) 
{
	bufq_purge(&sl->tb);
	sl->statem.Cm = 0;
	sl_check_congestion(sl);
}

static inline void sl_txc_flush_buffers(sl_t *sl)
{
	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) 
{
	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;
	sl->statem.tx.C.fsn = fsnc & (sl->statem.sn_mask);
	/*
	 *  FIXME: Q.704/5.7.2 states:
	 *
	 *  5.7.2   If a changeover order or acknowledgement containing an
	 *  unreasonable value of the forward sequence number is received, no
	 *  buffer updating or retrieval is performed, and new traffic is started
	 *  on the alternative signalling link(s).
	 *
	 *  It will be necessary to check FSNC for "reasonableness" here and
	 *  flush RTB and TB and return retrieval-complete indication with a
	 *  return code of "unreasonable FSNC".
	 *
	 *  (Tell the SIGTRAN working group and M2UA guys about this!)
	 */
	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;
	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_msgs < sl->config.N1 ) && ( sl->rtb.q_count < sl->config.N2 ) )
		{
			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;
		if ( !mp && pcr )
		{
			mp = sl->statem.z_ptr = sl->rtb.q_head;
			sl->statem.Z = sl->statem.tx.F.fsn;
		}
		if ( mp )
		{
			sl->statem.z_ptr = mp->b_next;
			if ( pcr )
			{
				sl->statem.tx.N.fsn =  sl->statem.Z;
				/* sl->statem.tx.N.fib = sl->statem.tx.N.fib; */
				sl->statem.tx.N.bsn = (sl->statem.tx.X.fsn - 1)&sl->statem.sn_mask;
				/* sl->statem.tx.N.bib = sl->statem.tx.N.bib; */
				sl->statem.Z = (sl->statem.Z + 1)&sl->statem.sn_mask;
			}
			else
			{
				sl->statem.tx.N.fsn = (sl->statem.tx.N.fsn + 1)&sl->statem.sn_mask;
				/* sl->statem.tx.N.fib = sl->statem.tx.N.fib; */
				sl->statem.tx.N.bsn = (sl->statem.tx.X.fsn - 1)&sl->statem.sn_mask;
				/* sl->statem.tx.N.bib = sl->statem.tx.N.bib; */
			}
			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->rtb.q_msgs >= sl->config.N1 ) || ( sl->rtb.q_count >= sl->config.N2 ) )
			{
				sl->statem.rtb_full = 1;
				sl->statem.forced_retransmission = 1;
			}
		}
		else
		{
			if ( ( sl->rtb.q_msgs >= 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) 
{
	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) 
{
	sl->statem.msu_fisu_accepted = 0;
}

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

static inline void sl_rc_retrieve_fsnx(sl_t *sl)
{
	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) 
{
	sl_txc_fsnx_value(sl);
}

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

static inline void sl_rc_retrieve_bsnt(sl_t *sl) 
{
	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) 
{
	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) 
{
	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) 
{
	sl->statem.congestion_discard = 1;
	sl_cc_busy(sl);
}

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

static inline void sl_rc_no_congestion(sl_t *sl) 
{
	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) 
{
	sl_rc_congestion_discard(sl);
	sl->statem.l3_congestion_detect = 1;
}

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

static inline void sl_lsc_no_congestion(sl_t *sl) 
{
	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) 
{
	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) 
{
	switch ( sl->statem.lsc_state)
	{
		case SL_STATE_OUT_OF_SERVICE:
		case SL_STATE_INITIAL_ALIGNMENT:
			break;
		default:
			sl_timer_stop(t1);  /* ok if not 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) 
{
	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) 
{
	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) 
{
	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;
			break;
		case SL_STATE_PROVING:
			sl_timer_stop(t4);
			sl->dcalls->aerm_stop(sl);
			sl_timer_start(t3);
			sl->statem.iac_state = SL_STATE_ALIGNED;
			break;
	}
}

static inline void sl_iac_sios(sl_t *sl) 
{
	switch ( sl->statem.iac_state )
	{
		case SL_STATE_ALIGNED:
		case SL_STATE_PROVING:
			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;
			break;
	}
}

static inline void sl_lsc_sios(sl_t *sl) 
{
	switch ( sl->statem.lsc_state )
	{
		case SL_STATE_ALIGNED_READY:
		case SL_STATE_ALIGNED_NOT_READY:
			sl_timer_stop(t1);  /* ok to stop if not running */
		case SL_STATE_IN_SERVICE:
		case SL_STATE_PROCESSOR_OUTAGE:
			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;
			break;
	}
}

static inline void sl_lsc_no_processor_outage(sl_t* sl)
{
	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)
{
	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) 
{
	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)
{
	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_sib(sl_t *sl)
{
	switch ( sl->statem.lsc_state )
	{
		case SL_STATE_IN_SERVICE:
		case SL_STATE_PROCESSOR_OUTAGE:
			sl_txc_sib_received(sl);
			break;
	}
}

static inline void sl_lsc_sipo(sl_t *sl) 
{
	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) 
{
	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) 
{
	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)
{
	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) 
{
	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_msu(sl); /* changed from send_fisu for Q.781 */
			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) 
{
	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) 
{
	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) 
{
	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) 
{
	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) 
{
	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) 
{
	int pcr = sl->option.popt & SS7_POPT_PCR;
	if ( sl->statem.rc_state != SL_STATE_IN_SERVICE )
	{
		freemsg(mp);
		return;
	}
	/* 
	 *  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;
	}
	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;
	}
	if ( sl->statem.rx.len == 1 )
	{
		sl->statem.rx.sio   = *mp->b_rptr++ & 0x07;
	}
	if ( sl->statem.rx.len == 2 )
	{
		sl->statem.rx.sio   = *mp->b_rptr++ & 0x07;
		sl->statem.rx.sio   = *mp->b_rptr++ & 0x07;
	}
#if 0
	ptrace(("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
				));
#endif
	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_lsc_sib (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) )
	{
		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);
//			sl_daedt_transmitter_wakeup(sl);
			return;
		}
		else
		{
			sl->statem.abnormal_bsnr = 1;
			sl->statem.unb = 0;
			freemsg(mp);
//			sl_daedt_transmitter_wakeup(sl);
			return;
		}
	}
	if ( sl->statem.abnormal_bsnr )
	{
		if ( sl->statem.unb == 1 )
		{
			sl->statem.abnormal_bsnr = 0;
		}
		else
		{
			sl->statem.unb = 1;
			freemsg(mp);
//			sl_daedt_transmitter_wakeup(sl);
			return;
		}
	}
	if ( 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 )
		{
			sl_cc_busy(sl);
			freemsg(mp);
			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;
		}
		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);
//				sl_daedt_transmitter_wakeup(sl);
				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
	{
		if ( sl->statem.abnormal_fibr )
		{
			sl->statem.failure_reason = SL_FAIL_ABNORMAL_FIBR;
			sl_lsc_link_failure(sl);
			freemsg(mp);
			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;
			freemsg(mp);
			return;
		}
		sl->statem.abnormal_fibr = 1;
		sl->statem.unf = 0;
		freemsg(mp);
//		sl_daedt_transmitter_wakeup(sl);
		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) 
{
	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) 
{
	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) 
{
	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) 
{
	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)
{
	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)
{
	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) 
{
	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) 
{
	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) 
{
	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)
{
	sl->statem.emergency = 1;
	sl_iac_emergency(sl);   /* added to pass Q.781/Test 1.20 */
}

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

static inline void sl_iac_start(sl_t *sl) 
{
	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) 
{
	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)
{
	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)
{
    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)
{
	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) 
{
	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_status = sl->statem.cong_status;
	int old_disc_status = sl->statem.disc_status;
	int multi = sl->option.popt & SS7_POPT_MPLEV;
	switch (sl->statem.cong_status)
	{
		case 0:
			if ( occupancy >= sl->config.tb_onset_1 )
			{
				sl->statem.cong_status = 1;
				if ( occupancy >= sl->config.tb_discd_1 )
				{
					sl->statem.disc_status = 1;
					if ( !multi )
						break;
					if ( occupancy >= sl->config.tb_onset_2 )
					{
						sl->statem.cong_status = 2;
						if ( occupancy >= sl->config.tb_discd_2 )
						{
							sl->statem.disc_status = 2;
							if ( occupancy >= sl->config.tb_onset_3 )
							{
								sl->statem.cong_status = 3;
								if ( occupancy >= sl->config.tb_discd_3 )
								{
									sl->statem.disc_status = 3;
								}
							}
						}
					}
				}
			}
			break;
		case 1:
			if ( occupancy <  sl->config.tb_abate_1 )
			{
				sl->statem.cong_status = 0;
				sl->statem.disc_status = 0;
			}
			else
			{
				if ( !multi )
					break;
				if ( occupancy >= sl->config.tb_onset_2 )
				{
					sl->statem.cong_status = 2;
					if ( occupancy >= sl->config.tb_discd_2 )
					{
						sl->statem.disc_status = 2;
						if ( occupancy >= sl->config.tb_onset_3 )
						{
							sl->statem.cong_status = 3;
							if ( occupancy >= sl->config.tb_discd_3 )
							{
								sl->statem.disc_status = 3;
							}
						}
					}
				}
			}
			break;
		case 2:
			if ( !multi )
			{
				sl->statem.cong_status = 1;
				sl->statem.disc_status = 1;
				break;
			}
			if ( occupancy <  sl->config.tb_abate_2 )
			{
				sl->statem.cong_status = 1;
				sl->statem.disc_status = 1;
				if ( occupancy <  sl->config.tb_abate_1 )
				{
					sl->statem.cong_status = 0;
					sl->statem.disc_status = 0;
				}
			}
			else
				if ( occupancy >= sl->config.tb_onset_3 )
				{
					sl->statem.cong_status = 3;
					if ( occupancy >= sl->config.tb_discd_3 )
					{
						sl->statem.disc_status = 3;
					}
				}
			break;
		case 3:
			if ( !multi )
			{
				sl->statem.cong_status = 1;
				sl->statem.disc_status = 1;
				break;
			}
			if ( occupancy <  sl->config.tb_abate_3 )
			{
				sl->statem.cong_status = 2;
				sl->statem.disc_status = 2;
				if ( occupancy <  sl->config.tb_abate_2 )
				{
					sl->statem.cong_status = 1;
					sl->statem.disc_status = 1;
					if ( occupancy <  sl->config.tb_abate_1 )
					{
						sl->statem.cong_status = 0;
						sl->statem.disc_status = 0;
					}
				}
			}
			break;
	}
	if ( sl->statem.cong_status != old_cong_status
			|| sl->statem.disc_status != old_disc_status )
	{
		if ( sl->statem.cong_status < old_cong_status )
			sl_l3_link_congestion_ceased(sl, sl->statem.cong_status, sl->statem.disc_status);
		else
		{
			if ( sl->statem.cong_status > old_cong_status )
			{
				if ( sl->notify.events & SL_EVT_CONGEST_ONSET_IND &&
						!sl->stats.sl_cong_onset_ind[sl->statem.cong_status]++ )
				{
					sl_l3_link_congested(sl, sl->statem.cong_status, sl->statem.disc_status);
					return;
				}
			}
			else
			{
				if ( sl->notify.events & SL_EVT_CONGEST_DISCD_IND &&
						!sl->stats.sl_cong_discd_ind[sl->statem.disc_status]++ )
				{
					sl_l3_link_congested(sl, sl->statem.cong_status, sl->statem.disc_status);
					return;
				}
			}
			sl_l3_link_congested(sl, sl->statem.cong_status, sl->statem.disc_status);
		}
	}
}

static inline void sl_txc_message_for_transmission(sl_t *sl, mblk_t *mp)
{
	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;
	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
		{
			if ( (md->b_datap->db_lim - md->b_wptr) >= hlen )
			{
				memmove(md->b_rptr+hlen, md->b_rptr, md->b_wptr - md->b_rptr);
				md->b_wptr += hlen;
			}
			else
			{
#if 0
				/* this code doesn't seem to work later on... */
				mblk_t *mh = allocb(hlen, BPRI_HI);
				if ( mh )
				{
					mh->b_datap->db_type = M_DATA;
					mh->b_wptr += hlen;
					linkb(mh, md);
					md = mh;
				}
				else
				{
					freemsg(md);
					return;
				}
#else
				int len = md->b_wptr-md->b_rptr;
				mblk_t *m2 = allocb(hlen+len, BPRI_HI);
				if ( m2 )
				{
					bcopy(md->b_rptr, m2->b_rptr+hlen, len);
					m2->b_wptr = m2->b_rptr + hlen + len;
					md = m2;
				}
				else
				{
					freemsg(md);
					return;
				}
#endif
			}
		}
		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__

