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

 @(#) sl_sm.h,v SS7preBetaRelease(0.8) 2002/04/02 08:21:00

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

 Copyright (c) 2001-2002  OpenSS7 Corporation <http://www.openss7.com>
 Copyright (c) 1997-2000  Brian F. G. 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.

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

 U.S. GOVERNMENT RESTRICTED RIGHTS.  If you are licensing this Software on
 behalf of the U.S. Government ("Government"), the following provisions apply
 to you.  If the Software is supplied by the Department of Defense ("DoD"), it
 is classified as "Commercial Computer Software" under paragraph 252.227-7014
 of the DoD Supplement to the Federal Acquisition Regulations ("DFARS") (or any
 successor regulations) and the Government is acquiring only the license rights
 granted herein (the license rights customarily provided to non-Government
 users).  If the Software is supplied to any unit or agency of the Government
 other than DoD, it is classified as "Restricted Computer Software" and the
 Government's rights in the Software are defined in paragraph 52.227-19 of the
 Federal Acquisition Regulations ("FAR") (or any success regulations) or, in
 the cases of NASA, in paragraph 18.52.227-86 of the NASA Supplement to the FAR
 (or any successor regulations).

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

 Commercial licensing and support of this software is available from OpenSS7
 Corporation at a fee.  See http://www.openss7.com/

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

 Last Modified 2002/04/02 08:21:00 by brian

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

#ifndef __SL_SM_H__
#define __SL_SM_H__

#ident "@(#) sl_sm.h,v SS7preBetaRelease(0.8) 2002/04/02 08:21:00"

/*
 *  =======================================================================
 *
 *  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__ */

