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

 @(#) m2pa_slm.c,v 0.7.8.1 2001/12/11 13:15:14 brian Exp

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

 Copyright (C) 2001  OpenSS7 Corporation <http://www.openss7.com>

 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).

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

 Last Modified 2001/12/11 13:15:14 by brian

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

#indent "@(#) SS7AlphaRelease(0.7.8.1) 2001/12/11 13:15:14"

static char const ident[] = "SS7AlphaRelease(0.7.8.1) 2001/12/11 13:15:14";


/*
 *  M2PA SLM - Signalling Link Manager for M2PA
 *  -------------------------------------------------------------------------
 *  This is a signalling link manager for M2PA.  It provides configuration
 *  management for SCTP associations and M2PA signalling links.  It presents
 *  an SL Type 2 interface based on Signalling Data Terminal and Signallign
 *  Data Link identifiers.
 */

#include <linux/config.h>
#include <linux/version.h>
#include <linux/modversions.h>
#include <linux/module.h>

#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/cmn_err.h>
#include <sys/dki.h>

#include <sys/npi.h>
#include <sys/npi_sctp.h>

#include <ss7/lmi.h>
#include <ss7/lmi_ioctl.h>
#include <ss7/devi.h>
#include <ss7/devi_ioctl.h>
#include <ss7/sdli.h>
#include <ss7/sdli_ioctl.h>
#include <ss7/sdti.h>
#include <ss7/sdti_ioctl.h>
#include <ss7/sli.h>
#include <ss7/sli_ioctl.h>

#include "../debug.h"
#include "../bufq.h"

#define M2PA_DESCRIP	"M2PA/SCTP SIGNALLING LINK (SL) STREAMS MULTIPLEXING DRIVER."
#define M2PA_COPYRIGHT	"Copyright (c) 2001 OpenSS7 Corp.  All Rights Reserved."
#define M2PA_DEVICE	"Part of the OpenSS7 Stack for LiS STREAMS."
#define M2PA_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define M2PA_BANNER	M2PA_DESCRIP	"\n" \
			M2PA_COPYRIGHT	"\n" \
			M2PA_DEVICE	"\n" \
			M2PA_CONTACT	"\n"

MODULE_AUTHOR(M2PA_CONTACT);
MODULE_DESCRIPTION(M2PA_DESCRIP);
MODULE_SUPPORTED_DEVICE(M2PA_DEVICE);

#ifndef INT
#define INT void
#endif

typedef void (*bufcall_fnc_t)(long);

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
static struct module_info sl_minfo =
{
	0,		/* Module ID number		*/
	"m2pa-sl",	/* Module name			*/
	0,		/* Min packet size accepted	*/  /* FIXME */
	INFPSZ,		/* Max packet size accepted	*/  /* FIXME */
	1<<15,		/* Hi water mark		*/  /* FIXME */
	1<<10		/* Lo water mark		*/  /* FIXME */
};

static int sl_open (queue_t *, dev_t *, int, int, cred_t *);
static int sl_close(queue_t *, int, cred_t *);
static INT sl_wput (queue_t *, mblk_t *);

static struct qinit sl_u_winit =
{
	sl_wput,	/* Write put (msg from above)	*/
	NULL,		/* Write queue service		*/
	NULL,		/* Each open			*/
	NULL,		/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&sl_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};
static struct qinit sl_u_rinit =
{
	NULL,		/* Read put (msg from below)	*/
	NULL,		/* Read queue service		*/
	sl_open,	/* Each open			*/
	sl_close,	/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&sl_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};

static INT sl_l_wput(queue_t *, mblk_t *);
static INT sl_l_wsrv(queue_t *);
static INT sl_l_rput(queue_t *, mblk_t *);
static INT sl_l_rsrv(queue_t *);

static struct qinit sl_l_winit =
{
	sl_l_wput,	/* Write put (msg from above)	*/
	sl_l_wsrv,	/* Write queue service		*/
	NULL,		/* Each open			*/
	NULL,		/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&sl_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};
static struct qinit sl_l_rinit =
{
	sl_l_rput,	/* Read put (msg from below)	*/
	sl_l_rsrv,	/* Read queue service		*/
	NULL,		/* Each open			*/
	NULL,		/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&sl_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};

static struct streamtab sl_info =
{
	&sl_u_rinit,	/* Upper read  queue		*/
	&sl_u_winit,	/* Upper write queue		*/
	&sl_l_rinit,	/* Lower read  queue		*/
	&sl_l_winit	/* Lower write queue		*/
};

/*
 *  =========================================================================
 *
 *  M2PA-SL Private Structure
 *
 *  =========================================================================
 */
typedef struct sl {
	struct sl	*next;
	queue_t		*rq;		/* read queue			*/
	queue_t		*wq;		/* write queue			*/
	queue_t		*mq;		/* management queue		*/
	uint		i_state;	/* SL interface state		*/
	uint		version;	/* version executing		*/
	uint		flags;		/* interface flags		*/
	ulong		token;		/* my bind token		*/

	bufq_t		 rb;		/* receive buffer		*/
	bufq_t		 tb;		/* transmission buffer		*/
	bufq_t		rtb;		/* retransmission buffer	*/

	uint		state;		/* link state			*/

	uint		timer_t1;	/* t1 timer			*/
	uint		timer_t2;	/* t2 timer			*/
	uint		timer_t3;	/* t3 timer			*/
	uint		timer_t4;	/* t4 timer			*/
#if 0
	uint		timer_t5;	/* t5 timer			*/
#endif
	uint		timer_t6;	/* t6 timer			*/
	uint		timer_t7;	/* t7 timer			*/
#if 0
	uint		timer_t8;	/* t8 timer			*/
	uint		timer_t9;	/* t9 timer			*/
#endif

	uint		rmsu;		/* received    unacked msus	*/
	uint		tmsu;		/* transmitted unacked msus	*/
	uint		rack;		/* received    acked msus	*/
	uint		tack;		/* transmitted acked msus	*/
	uint		fsnr;		/* received    msu seq number	*/
	uint		fsnt;		/* transmitted msu seq number	*/
	uint		back;		/* bad acks			*/

	sl_statem_t	statem;		/* state machine variables	*/
	lmi_option_t	option;		/* protocol and variant options	*/
	sl_config_t	config;		/* SL  configuration options	*/
	sl_notify_t	notify;		/* SL  notification  options	*/
	sl_stats_t	stats;		/* SL  statistics		*/
	sl_stats_t	stamp;		/* SL  statistics timestamps	*/
	sdt_config_t	sdt_conf;	/* SDT configuration options	*/
	sdl_config_t	sdl_conf;	/* SDL configuration options	*/
	dev_device_t	dev_conf;	/* DEV configuration options	*/
} sl_t;
#define M2PA_VERSION_DRAFT3	3
#define M2PA_VERSION_DRAFT4	4
#define M2PA_VERSION_DEFAULT	M2PA_VERSION_DRAFT4

/*
 *  =========================================================================
 *
 *  M2PA PDU Message Definitions
 *
 *  =========================================================================
 */
#define M2PA_PPI		5
#define M2PA_MESSAGE_CLASS	11
#define M2PA_VERSION		1
#define M2PA_MTYPE_DATA		1
#define M2PA_MTYPE_STATUS	2
#define M2PA_MTYPE_PROVING	3
#define M2PA_MTYPE_ACK		4
#define M2PA_DATA_MESSAGE	\
	__constant_htonl((M2PA_VERSION<<24)|(M2PA_MESSAGE_CLASS<<8)|M2PA_MTYPE_DATA)
#define M2PA_STATUS_MESSAGE	\
	__constant_htonl((M2PA_VERSION<<24)|(M2PA_MESSAGE_CLASS<<8)|M2PA_MTYPE_STATUS)
#define M2PA_PROVING_MESSAGE	\
	__constant_htonl((M2PA_VERSION<<24)|(M2PA_MESSAGE_CLASS<<8)|M2PA_MTYPE_PROVING)
#define M2PA_ACK_MESSAGE	\
	__constant_htonl((M2PA_VERSION<<24)|(M2PA_MESSAGE_CLASS<<8)|M2PA_MTYPE_ACK)
#define M2PA_STATUS_STREAM	0
#define M2PA_DATA_STREAM	1
#define	M2PA_STATUS_OUT_OF_SERVICE		(__constant_htonl(0))	/* XXX */
#define M2PA_STATUS_ALIGNMENT			(__constant_htonl(1))
#define M2PA_STATUS_PROVING_NORMAL		(__constant_htonl(2))
#define M2PA_STATUS_PROVING_EMERGENCY		(__constant_htonl(3))
#define M2PA_STATUS_IN_SERVICE			(__constant_htonl(4))
#define M2PA_STATUS_PROCESSOR_OUTAGE		(__constant_htonl(5))
#define M2PA_STATUS_PROCESSOR_OUTAGE_ENDED	(__constant_htonl(6))
#define M2PA_STATUS_BUSY			(__constant_htonl(7))
#define M2PA_STATUS_BUSY_ENDED			(__constant_htonl(8))

/*
 *  =========================================================================
 *
 *  OUTPUT Events
 *
 *  =========================================================================
 *  -------------------------------------------------------------------------
 *
 *  SL Provider (M2PA) -> SL User Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  LMI_INFO_ACK
 *  ---------------------------------------------
 */
static int
lmi_info_ack(sl_t *sl)
{
	mblk_t *mp;
	lmi_info_ack_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_info_ack_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_INFO_ACK;
		p->lmi_version		= 1;
		p->lmi_state		= sl->i_state;
		p->lmi_max_sdu		= -1;
		p->lmi_min_sdu		= 0;
		p->lmi_header_len	= 0;
		p->lmi_ppa_style	= LMI_STYLE1;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
}
/*
 *  LMI_OK_ACK
 *  ---------------------------------------------
 */
static int
lmi_ok_ack(sl_t *sl, long prim)
{
	mblk_t *mp;
	lmi_ok_ack_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_ok_ack_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_OK_ACK;
		p->lmi_correct_primitive= prim;
		switch ( sl->i_state )
		{
			case LMI_ATTACH_PENDING: sl->i_state = LMI_DISABLED;  break;
			case LMI_DETACH_PENDING: sl->i_state = LMI_UNATTACHED; break;
			/* default is don't change state */
		}
		p->lmi_state		= sl->i_state;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
}
/*
 *  LMI_ERROR_ACK
 *  ---------------------------------------------
 */
static int
lmi_error_ack(sl_t *sl, long prim, long err)
{
	mblk_t *mp;
	lmi_error_ack_t *p;
	switch ( err )
	{
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
		case -ENOBUFS:
			seldom(); return(err);
	}
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_error_ack_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_ERROR_ACK;
		p->lmi_errno		= err<0 ? -err       : 0;
		p->lmi_reason		= err<0 ? LMI_SYSERR : err;
		p->lmi_error_primitive	= prim;
		switch ( sl->i_state )
		{
			case LMI_ATTACH_PENDING:    sl->i_state = LMI_UNATTACHED;   break;
			case LMI_DETACH_PENDING:    sl->i_state = LMI_DISABLED;	    break;
			case LMI_ENABLE_PENDING:    sl->i_state = LMI_DISABLED;	    break;
			case LMI_DISABLE_PENDING:   sl->i_state = LMI_ENABLED;	    break;
			/*
			 *  Default is not to change state.
			 */
		}
		p->lmi_state		= sl->i_state;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
}
/*
 *  LMI_ENABLE_CON
 *  ---------------------------------------------
 */
static int
lmi_enable_con(sl_t *sl)
{
	mblk_t *mp;
	lmi_enable_con_t *p;
	ensure( sl->i_state == LMI_ENABLE_PENDING, return(-EFAULT) );
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((lmi_enable_con_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_ENABLE_CON;
		p->lmi_state		= sl->i_state = LMI_ENABLED;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} rare();   return(-EBUSY);
}
/*
 *  LMI_DISABLE_CON
 *  ---------------------------------------------
 */
static int
lmi_disable_con(sl_t *sl)
{
	mblk_t *mp;
	lmi_disable_con_t *p;
	ensure( sl->i_state == LMI_DISABLE_PENDING, return(-EFAULT) );
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((lmi_disable_con_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_DISABLE_CON;
		p->lmi_state		= sl->i_state = LMI_DISABLED;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} rare();   return(-EBUSY);
}
/*
 *  LMI_OPTMGMT_ACK
 *  ---------------------------------------------
 */
#if 0
static int
lmi_optmgmt_ack(sl_t *sl, ulong flags, void *opt_ptr, size_t opt_len)
{
	mblk_t *mp;
	lmi_optmgmt_ack_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_optmgmt_ack_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_OPTMGMT_ACK;
		p->lmi_opt_length	= opt_len;
		p->lmi_opt_offset	= opt_len?sizeof(*p):0;
		p->lmi_mgmt_flags	= flags;
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
}
#endif
/*
 *  LMI_ERROR_IND
 *  ---------------------------------------------
 */
static int
lmi_error_ind(sl_t *sl, long err)
{
	mblk_t *mp;
	lmi_error_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_error_ind_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_ERROR_IND;
		p->lmi_errno		= err<0 ? -err       : 0;
		p->lmi_reason		= err<0 ? LMI_SYSERR : err;
		p->lmi_state		= sl->i_state;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
}
/*
 *  LMI_STATS_IND
 *  ---------------------------------------------
 */
static int
lmi_stats_ind(sl_t *sl, ulong interval, ulong timestamp)
{
	mblk_t *mp;
	lmi_stats_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((lmi_stats_ind_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_STATS_IND;
		p->lmi_interval		= interval;
		p->lmi_timestamp	= timestamp;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  LMI_EVENT_IND
 *  ---------------------------------------------
 */
static int
lmi_event_ind(sl_t *sl, ulong oid, ulong severity, void *inf_ptr, size_t inf_len)
{
	mblk_t *mp;
	lmi_event_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p)+inf_len, BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((lmi_event_ind_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_EVENT_IND;
		p->lmi_objectid		= oid;
		p->lmi_timestamp	= jiffies;
		p->lmi_severity		= severity;
		bcopy(mp->b_wptr, inf_ptr, inf_len);
		mp->b_wptr += inf_len;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_PDU_IND
 *  ---------------------------------------------
 */
static int
sl_pdu_ind(sl_t *sl, mblk_t *dp)
{
	mblk_t *mp;
	sl_pdu_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_pdu_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_PDU_IND;
		mp->b_cont = dp;
		ptrace(("Delivering mp = %u\n",(uint)mp));
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_LINK_CONGESTED_IND
 *  ---------------------------------------------
 */
static int
sl_link_congested_ind(sl_t *sl, ulong cong, ulong disc)
{
	mblk_t *mp;
	sl_link_cong_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_link_cong_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_LINK_CONGESTED_IND;
		p->sl_timestamp		= jiffies;
		p->sl_cong_status	= cong;
		p->sl_disc_status	= disc;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_LINK_CONGESTION_CEASED_IND
 *  ---------------------------------------------
 */
static int
sl_link_congestion_ceased_ind(sl_t *sl, ulong cong, ulong disc)
{
	mblk_t *mp;
	sl_link_cong_ceased_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_link_cong_ceased_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_LINK_CONGESTION_CEASED_IND;
		p->sl_timestamp		= jiffies;
		p->sl_cong_status	= cong;
		p->sl_disc_status	= disc;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_RETRIEVED_MESSAGE_IND
 *  ---------------------------------------------
 */
static int
sl_retrieved_message_ind(sl_t *sl, mblk_t *dp)
{
	mblk_t *mp;
	sl_retrieved_msg_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_retrieved_msg_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_RETRIEVED_MESSAGE_IND;
		mp->b_cont = dp;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_RETRIEVAL_COMPLETE_IND
 *  ---------------------------------------------
 */
static int
sl_retrieval_complete_ind(sl_t *sl)
{
	mblk_t *mp;
	sl_retrieval_comp_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_retrieval_comp_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_RETRIEVAL_COMPLETE_IND;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_RB_CLEARED_IND
 *  ---------------------------------------------
 */
static int
sl_rb_cleared_ind(sl_t *sl)
{
	mblk_t *mp;
	sl_rb_cleared_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_rb_cleared_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_RB_CLEARED_IND;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_BSNT_IND
 *  ---------------------------------------------
 */
static int
sl_bsnt_ind(sl_t *sl, ulong bsnt)
{
	mblk_t *mp;
	sl_bsnt_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_bsnt_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_BSNT_IND;
		p->sl_bsnt		= bsnt;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_IN_SERVICE_IND
 *  ---------------------------------------------
 */
static int
sl_in_service_ind(sl_t *sl)
{
	mblk_t *mp;
	sl_in_service_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_in_service_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_IN_SERVICE_IND;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_OUT_OF_SERVICE_IND
 *  ---------------------------------------------
 */
static int
sl_out_of_service_ind(sl_t *sl, ulong reason)
{
	mblk_t *mp;
	sl_out_of_service_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_out_of_service_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_OUT_OF_SERVICE_IND;
		p->sl_timestamp		= jiffies;
		p->sl_reason		= reason;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_REMOTE_PROCESSOR_OUTAGE_IND
 *  ---------------------------------------------
 */
static int
sl_remote_processor_outage_ind(sl_t *sl)
{
	mblk_t *mp;
	sl_rem_proc_out_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_rem_proc_out_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_REMOTE_PROCESSOR_OUTAGE_IND;
		p->sl_timestamp		= jiffies;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_REMOTE_PROCESSOR_RECOVERED_IND
 *  ---------------------------------------------
 */
static int
sl_remote_processor_recovered_ind(sl_t *sl)
{
	mblk_t *mp;
	sl_rem_proc_recovered_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_rem_proc_recovered_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_REMOTE_PROCESSOR_RECOVERED_IND;
		p->sl_timestamp		= jiffies;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_RTB_CLEARED_IND
 *  ---------------------------------------------
 */
static int
sl_rtb_cleared_ind(sl_t *sl)
{
	mblk_t *mp;
	sl_rtb_cleared_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_rtb_cleared_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_RTB_CLEARED_IND;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_RETRIEVAL_NOT_POSSIBLE_IND
 *  ---------------------------------------------
 */
static int
sl_retrieval_not_possible_ind(sl_t *sl)
{
	mblk_t *mp;
	sl_retrieval_not_poss_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_retrieval_not_poss_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_RETRIEVAL_NOT_POSSIBLE_IND;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  SL_BSNT_NOT_RETRIEVABLE_IND
 *  ---------------------------------------------
 */
static int
sl_bsnt_not_retrievable_ind(sl_t *sl, ulong bsnt)
{
	mblk_t *mp;
	sl_bsnt_not_retr_ind_t *p;
	if ( canputnext(sl->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((sl_bsnt_not_retr_ind_t *)mp->b_wptr)++;
		p->sl_primitive		= SL_BSNT_NOT_RETRIEVABLE_IND;
		p->sl_bsnt		= bsnt;
		putnext(sl->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  NPI User (M2PA) -> NPI Provider (SCTP) Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  N_DATA_REQ
 *  ---------------------------------------------
 */
static int
n_data_req(sl_t *sl, ulong flags, void *qos_ptr, size_t qos_len, mblk_t *dp)
{
	mblk_t *mp;
	N_data_req_t *p;
	if ( canputnext(sl->wq) ) {
	if ( (mp = allocb(sizeof(*p)+qos_len, BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((N_data_req_t *)mp->b_wptr)++;
		p->PRIM_type		= N_DATA_REQ;
		p->DATA_xfer_flags	= flags;
		bcopy(qos_ptr, mp->b_wptr, qos_len);
		mp->b_wptr += qos_len;
		mp->b_cont = dp;
		putnext(sl->wq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  N_EXDATA_REQ
 *  ---------------------------------------------
 */
static int
n_exdata_req(sl_t *sl, void *qos_ptr, size_t qos_len, mblk_t *dp)
{
	mblk_t *mp;
	N_exdata_req_t *p;
	if ( bcanputnext(sl->wq, 1) ) {
	if ( (mp = allocb(sizeof(*p)+qos_len, BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((N_exdata_req_t *)mp->b_wptr)++;
		p->PRIM_type		= N_EXDATA_REQ;
		bcopy(qos_ptr, mp->b_wptr, qos_len);
		mp->b_wptr += qos_len;
		mp->b_cont = dp;
		putnext(sl->wq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  SL Peer (M2PA) -> SL Peer Sent Messages
 *
 *  -------------------------------------------------------------------------
 *
 *  M2PA SEND PROVING
 *  ---------------------------------------------
 */
static int
sl_send_proving(sl_t *sl, size_t plen)
{
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
	if ( (mp = allocb(2*sizeof(uint32_t)+plen, BPRI_MED)) )
	{
		mp->b_datap->db_type = M_DATA;
		*((uint32_t *)mp->b_wptr)++ = M2PA_PROVING_MESSAGE;
		*((uint32_t *)mp->b_wptr)++ = __constant_htonl(2*sizeof(uint32_t)+plen);
		fixme(("We should include a test pattern.\n"));
		bzero(mp->b_wptr, plen);
		mp->b_wptr += plen;
		if ( !(err = n_data_req(sl, 0, &qos, sizeof(qos), mp)) )
			return(0);
		freeb(mp);
		return(err);
	}
	return(-ENOBUFS);
}
/*
 *  M2PA SEND STATUS
 *  ---------------------------------------------
 */
static int
sl_send_status(sl_t *sl, uint32_t status)
{
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_STATUS_STREAM, };
	if ( (mp = allocb(3*sizeof(uint32_t), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_DATA;
		*((uint32_t *)mp->b_wptr)++ = M2PA_STATUS_MESSAGE;
		*((uint32_t *)mp->b_wptr)++ = __constant_htonl(3*sizeof(uint32_t));
		*((uint32_t *)mp->b_wptr)++ = status;
		if ( !(err = n_data_req(sl, 0, &qos, sizeof(qos), mp)) )
			return(0);
		assert(mp->b_datap->db_ref);
		freeb(mp);
		return(err);
	}
	return(-ENOBUFS);
}
/*
 *  M2PA SEND ACK
 *  ---------------------------------------------
 */
static int
sl_send_ack(sl_t *sl, ulong count)
{
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
	if ( (mp = allocb(3*sizeof(uint32_t), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_DATA;
		*((uint32_t *)mp->b_wptr)++ = M2PA_ACK_MESSAGE;
		*((uint32_t *)mp->b_wptr)++ = __constant_htonl(3*sizeof(uint32_t));
		*((uint32_t *)mp->b_wptr)++ = htonl(count);
		if ( (err = n_exdata_req(sl, &qos, sizeof(qos), mp)) )
		{
			assert(mp->b_datap->db_ref);
			freeb(mp);
			return(err);
		}
		return(0);
	}
	return(-ENOBUFS);
}
/*
 *  M2PA SEND DATA
 *  ---------------------------------------------
 */
static int
sl_send_data(sl_t *sl, mblk_t *dp)
{
	int err;
	mblk_t *mp, *db;
	ulong rcpt = sl->version<M2PA_VERSION_DRAFT4?N_RC_FLAG:0;
	size_t dlen = msgdsize(dp);
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
	if ( !(db = dupmsg(dp)) )
		return(-ENOBUFS);
	if ( (mp = allocb(2*sizeof(uint32_t), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_DATA;
		*((uint32_t *)mp->b_wptr)++ = M2PA_DATA_MESSAGE;
		*((uint32_t *)mp->b_wptr)++ = __constant_htonl(dlen + 2*sizeof(uint32_t));
		mp->b_cont = db;
		pullupmsg(mp, -1);
		if ( (err = n_data_req(sl, rcpt, &qos, sizeof(qos), mp)) )
		{
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			return(err);
		}
		return(0);
	}
	freemsg(db);
	return(-ENOBUFS);
}

/*
 *  =========================================================================
 *
 *  M2PA State Machines
 *
 *  =========================================================================
 */

#define MF_LPO			0x00000001
#define MF_RPO			0x00000002
#define MF_LOC_INS		0x00000004
#define MF_REM_INS		0x00000008
#define MF_LOC_BUSY		0x00000010
#define MF_REM_BUSY		0x00000020
#define MF_LOC_EMERG		0x00000040
#define MF_REM_EMERG		0x00000080
#define MF_RECV_MSU		0x00000100
#define MF_SEND_MSU		0x00000200
#define MF_CONG_ACCEPT		0x00000400
#define MF_CONG_DISCARD		0x00000800
#define MF_RTB_FULL		0x00001000
#define MF_L3_CONG_DETECT	0x00002000
#define MF_L2_CONG_DETECT	0x00004000
#define MF_CONTINUE		0x00008000
#define MF_CLEAR_RTB		0x00010000
#define MF_NEED_FLUSH		0x00020000

#define MS_POWER_OFF		0
#define MS_OUT_OF_SERVICE	1
#define MS_NOT_ALIGNED		2
#define MS_ALIGNED		3
#define MS_PROVING		4
#define MS_ALIGNED_NOT_READY	5
#define MS_ALIGNED_READY	6
#define MS_IN_SERVICE		7
#define MS_PROCESSOR_OUTAGE	8

/*
 *  -------------------------------------------------------------------------
 *
 *  Timers
 *
 *  -------------------------------------------------------------------------
 */

enum { t1 = 1, t2, t3, t4, t5, t6, t7, t8, t9 };

#define SL_EVENT_T1_EXPIRY	t1
static void sl_t1_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t1 ) return; else sl->timer_t1 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T1_EXPIRY);
}
#define SL_EVENT_T2_EXPIRY	t2
static void sl_t2_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t2 ) return; else sl->timer_t2 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T2_EXPIRY);
}
#define SL_EVENT_T3_EXPIRY	t3
static void sl_t3_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t3 ) return; else sl->timer_t3 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T3_EXPIRY);
}
#define SL_EVENT_T4_EXPIRY	t4
static void sl_t4_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t4 ) return; else sl->timer_t4 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T4_EXPIRY);
}
#if 0
#define SL_EVENT_T5_EXPIRY	t5
static void sl_t5_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t5 ) return; else sl->timer_t5 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T5_EXPIRY);
}
#endif
#define SL_EVENT_T6_EXPIRY	t6
static void sl_t6_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t6 ) return; else sl->timer_t6 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T6_EXPIRY);
}
#define SL_EVENT_T7_EXPIRY	t7
static void sl_t7_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t7 ) return; else sl->timer_t7 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T7_EXPIRY);
}
#if 0
#define SL_EVENT_T8_EXPIRY	t8
static void sl_t8_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t8 ) return; else sl->timer_t8 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T8_EXPIRY);
}
#define SL_EVENT_T9_EXPIRY	t9
static void sl_t9_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *)data;
	if ( !sl->timer_t9 ) return; else sl->timer_t9 = 0;
	putctl1(sl->rq, M_CTL, SL_EVENT_T9_EXPIRY);
}
#endif

static inline void sl_timer_stop(sl_t *sl, uint t)
{
	switch ( t )
	{
		case t1: if ( sl->timer_t1 ) untimeout(xchg(&sl->timer_t1,0)); break;
		case t2: if ( sl->timer_t2 ) untimeout(xchg(&sl->timer_t2,0)); break;
		case t3: if ( sl->timer_t3 ) untimeout(xchg(&sl->timer_t3,0)); break;
		case t4: if ( sl->timer_t4 ) untimeout(xchg(&sl->timer_t4,0)); break;
#if 0
		case t5: if ( sl->timer_t5 ) untimeout(xchg(&sl->timer_t5,0)); break;
#endif
		case t6: if ( sl->timer_t6 ) untimeout(xchg(&sl->timer_t6,0)); break;
		case t7: if ( sl->timer_t7 ) untimeout(xchg(&sl->timer_t7,0)); break;
#if 0
		case t8: if ( sl->timer_t8 ) untimeout(xchg(&sl->timer_t8,0)); break;
		case t9: if ( sl->timer_t9 ) untimeout(xchg(&sl->timer_t9,0)); break;
#endif
	}
}
static inline void sl_timer_start(sl_t *sl, uint t)
{
	switch ( t )
	{
		case t1: sl_timer_stop(sl, t); sl->timer_t1 = timeout(&sl_t1_timeout, (caddr_t)sl, sl->config.t1); break;
		case t2: sl_timer_stop(sl, t); sl->timer_t2 = timeout(&sl_t2_timeout, (caddr_t)sl, sl->config.t2); break;
		case t3: sl_timer_stop(sl, t); sl->timer_t3 = timeout(&sl_t3_timeout, (caddr_t)sl, sl->config.t3); break;
		case t4: sl_timer_stop(sl, t);
			 if ( sl->flags & (MF_LOC_EMERG|MF_REM_EMERG) )
				sl->timer_t4 = timeout(&sl_t4_timeout, (caddr_t)sl, sl->config.t4e);
			 else	sl->timer_t4 = timeout(&sl_t4_timeout, (caddr_t)sl, sl->config.t4n);
			 break;
#if 0
		case t5: sl_timer_stop(sl, t); sl->timer_t5 = timeout(&sl_t5_timeout, (caddr_t)sl, sl->config.t5); break;
#endif
		case t6: sl_timer_stop(sl, t); sl->timer_t6 = timeout(&sl_t6_timeout, (caddr_t)sl, sl->config.t6); break;
		case t7: sl_timer_stop(sl, t); sl->timer_t7 = timeout(&sl_t7_timeout, (caddr_t)sl, sl->config.t7); break;
#if 0
		case t8: sl_timer_stop(sl, t); sl->timer_t8 = timeout(&sl_t8_timeout, (caddr_t)sl, 1); break;
		case t9: sl_timer_stop(sl, t); sl->timer_t9 = timeout(&sl_t9_timeout, (caddr_t)sl, 1); break;
#endif
	}
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Error Monitoring
 *
 *  -------------------------------------------------------------------------
 */

static void sl_suerm_start(sl_t *sl)
{
	/* SCTP does this for us */
	return;
}
static void sl_suerm_stop(sl_t *sl)
{
	/* SCTP does this for us */
	return;
}
static void sl_aerm_start(sl_t *sl)
{
	(void)sl_send_proving;
	printd(("Start Proving...\n"));
	return;
}
static void sl_aerm_stop(sl_t *sl)
{
	printd(("Stop Proving...\n"));
	return;
}
static int sl_lsc_out_of_service(sl_t *sl, ulong reason);
#if 0
static int sl_iac_abort_proving(sl_t *sl) 
{
	int err;
	if ( sl->state == MS_PROVING )
	{
		sl->statem.Cp++;
		sl_timer_stop(sl, t4);
		sl_aerm_stop(sl);
		if ( sl->statem.Cp == sl->config.M ) {
			if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_ALIGNMENT_NOT_POSSIBLE)) )
				return(err);
			if ( sl->notify.events & SL_EVT_FAIL_ALIGNMENT )
				if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_ALIGNMENT, 0, NULL, 0)) )
					return(err);
			ptrace(("Link failed: Alignement not possible\n"));
			sl->stats.sl_fail_align_or_proving++;
			return(0);
		}
		sl_timer_start(sl, t4);
		sl_aerm_start(sl);
		return(0);
	}
	ptrace(("Received signal iac-abort-proving in unexpected state %u.\n", sl->state));
	return(-EFAULT);
}
#endif

static int sl_t5_expiry(sl_t *sl)
{
	return(-EOPNOTSUPP);
}

#if 0
static int sl_t8_expiry(sl_t *sl)
{
	return(-EOPNOTSUPP);
}

static int sl_t9_expiry(sl_t *sl)
{
	(void)sl_iac_abort_proving;
	return(-EOPNOTSUPP);
}
#endif

/*
 *  -----------------------------------------------------------------------
 *
 *  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.
 *
 *  -----------------------------------------------------------------------
 */

/*
 *  -----------------------------------------------------------------------
 *
 *  Duration Statistics
 *
 *  -----------------------------------------------------------------------
 */

static void sl_is_stats(sl_t *sl)
{
	if ( sl->stamp.sl_dur_unavail		)
		sl->stats.sl_dur_unavail
			+= jiffies - xchg(&sl->stamp.sl_dur_unavail,0);
	if ( sl->stamp.sl_dur_unavail_rpo	)
		sl->stats.sl_dur_unavail_rpo
			+= jiffies - xchg(&sl->stamp.sl_dur_unavail_rpo,0);
	if ( sl->stamp.sl_dur_unavail_failed	)
		sl->stats.sl_dur_unavail_failed
			+= jiffies - xchg(&sl->stamp.sl_dur_unavail_failed,0);
	sl->stamp.sl_dur_in_service = jiffies;
}
static void sl_oos_stats(sl_t *sl)
{
	if ( sl->stamp.sl_dur_in_service	)
		sl->stats.sl_dur_in_service
			+= jiffies - xchg(&sl->stamp.sl_dur_in_service,0);
	if ( sl->stamp.sl_dur_unavail_rpo	)
		sl->stats.sl_dur_unavail_rpo
			+= jiffies - xchg(&sl->stamp.sl_dur_unavail_rpo,0);
	if ( sl->stamp.sl_dur_unavail_failed	)
		sl->stats.sl_dur_unavail_failed
			+= jiffies - xchg(&sl->stamp.sl_dur_unavail_failed,0);
	sl->stamp.sl_dur_unavail = jiffies;
}
static void sl_rpo_stats(sl_t *sl)
{
	if ( sl->stamp.sl_dur_in_service	)
		sl->stats.sl_dur_in_service
			+= jiffies - xchg(&sl->stamp.sl_dur_in_service,0);
	sl->stamp.sl_dur_unavail_rpo = jiffies;
}
static void sl_rpr_stats(sl_t *sl)
{
	if ( sl->stamp.sl_dur_unavail_rpo	)
		sl->stats.sl_dur_unavail_rpo
			+= jiffies - xchg(&sl->stamp.sl_dur_unavail_rpo,0);
}

static int sl_lsc_stop(sl_t *sl)
{
	int err;
	if ( sl->state != MS_OUT_OF_SERVICE )
	{
		if ( (err = sl_send_status(sl, M2PA_STATUS_OUT_OF_SERVICE)) )
			return(err);
		sl_timer_stop(sl, t1);
		sl_timer_stop(sl, t2);
		sl_timer_stop(sl, t3);
		sl_timer_stop(sl, t4);
		sl_timer_stop(sl, t6);
		sl_timer_stop(sl, t7);
		sl_aerm_stop(sl);
		sl_suerm_stop(sl);
		sl->flags &= ~MF_LPO;
		if ( sl->flags & MF_RPO ) {
			if ( sl->notify.events & SL_EVT_RPO_END )
				if ( (err = lmi_event_ind(sl, SL_EVT_RPO_END, 0, NULL, 0)) )
					return(err);
			ptrace(("RPO Ends\n"));
			sl_rpr_stats(sl);
			sl->flags &= ~MF_RPO;
		}
		sl->flags &= ~MF_LOC_INS;
		sl->flags &= ~MF_REM_INS;
		sl->flags &= ~MF_LOC_BUSY;
		sl->flags &= ~MF_REM_BUSY;
		sl->flags &= ~MF_LOC_EMERG;
		sl->flags &= ~MF_REM_EMERG;
		sl_oos_stats(sl);
		sl->state = MS_OUT_OF_SERVICE;
		return(0);
	}
	return(0);
}

static int sl_lsc_out_of_service(sl_t *sl, ulong reason)
{
	int err;
	if ( sl->state != MS_OUT_OF_SERVICE )
	{
		if ( (err = sl_out_of_service_ind(sl, reason)) )
			return(err);
		if ( (err = sl_lsc_stop(sl)) )
			return(err);
		sl->stamp.sl_dur_unavail_failed = jiffies;
		return(0);
	}
	return(0);
}

static int sl_lsc_power_on(sl_t *sl) 
{
	int err;
	if ( sl->state == MS_POWER_OFF )
	{
		sl->flags = 0;
		if ( (err = sl_send_status(sl, M2PA_STATUS_OUT_OF_SERVICE)) )
			return(err);
		sl->state = MS_OUT_OF_SERVICE;
		return(0);
	}
	ptrace(("Received primitive SL_POWER_ON_REQ in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_start(sl_t *sl)
{
	int err;
	if ( sl->state == MS_OUT_OF_SERVICE )
	{
		sl->rmsu = 0;
		sl->rack = 0;
		sl->fsnr = 0;
		sl->tmsu = 0;
		sl->tack = 0;
		sl->fsnt = 0;
		sl->back = 0;
//		sl->flags = 0;
		if ( (err = sl_send_status(sl, M2PA_STATUS_ALIGNMENT)) )
			return(err);
		sl_timer_start(sl, t2);
		sl->state = MS_NOT_ALIGNED;
		return(0);
	}
	ptrace(("Received primitive SL_START_REQ in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_t2_expiry(sl_t *sl) 
{
	int err;
	if ( sl->state == MS_NOT_ALIGNED ) {
		if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_ALIGNMENT_NOT_POSSIBLE)) )
			return(err);
		if ( sl->notify.events & SL_EVT_FAIL_ALIGNMENT )
			if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_ALIGNMENT, 0, NULL, 0)) )
				return(err);
		ptrace(("Link failed: Alignment not possible\n"));
		sl->stats.sl_fail_align_or_proving++;
		return(0);
	}
	return(0);
}

static int sl_lsc_status_alignment(sl_t *sl) 
{
	int err;
	switch ( sl->state)
	{
		case MS_OUT_OF_SERVICE:
			return(0);
		case MS_NOT_ALIGNED:
			if ( sl->flags & MF_LOC_EMERG )
				err = sl_send_status(sl, M2PA_STATUS_PROVING_EMERGENCY);
			else	err = sl_send_status(sl, M2PA_STATUS_PROVING_NORMAL);
			if ( err ) return(err);
			sl_timer_stop(sl, t2);
			sl_timer_start(sl, t3);
			sl->state = MS_ALIGNED;
			return(0);
		case MS_ALIGNED:
			return(0);
		case MS_PROVING:
			sl_aerm_stop(sl);
			sl_timer_stop(sl, t4);
			sl_timer_start(sl, t3);
			sl->state = MS_ALIGNED;
			return(0);
		default:
			if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_RECEIVED_SIO)) )
				return(err);
			if ( sl->notify.events & SL_EVT_FAIL_RECEIVED_SIO )
				if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_RECEIVED_SIO, 0, NULL, 0)) )
					return(err);
			ptrace(("Link failed: Received SIO\n"));
			return(0);
	}
}

static int sl_lsc_emergency(sl_t *sl)
{
	int err;
	sl->flags |= MF_LOC_EMERG;
	switch ( sl->state )
	{
		case MS_PROVING:
			if ( (err = sl_send_status(sl, M2PA_STATUS_PROVING_EMERGENCY)) )
				return(err);
			sl_timer_stop(sl, t4);
			sl_aerm_stop(sl);
			sl_timer_start(sl, t4);
			sl_aerm_start(sl);
			return(0);
		case MS_ALIGNED:
			if ( (err = sl_send_status(sl, M2PA_STATUS_PROVING_EMERGENCY)) )
				return(err);
			return(0);
	}
	ptrace(("Received primitive SL_EMERGENCY_REQ in unexpected state %u\n", sl->state));
	return(-EPROTO);
}

static int sl_lsc_emergency_ceases(sl_t *sl)
{
	sl->flags &= ~MF_LOC_EMERG;
	return(0);
}

static int sl_lsc_status_proving_normal(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_NOT_ALIGNED:
			if ( sl->flags & MF_LOC_EMERG )
				err = sl_send_status(sl, M2PA_STATUS_PROVING_EMERGENCY);
			else	err = sl_send_status(sl, M2PA_STATUS_PROVING_NORMAL);
			if ( err ) return(err);
			sl_timer_stop(sl, t2);
			sl_timer_start(sl, t3);
			sl->state = MS_ALIGNED;
			/* fall thru */
		case MS_ALIGNED:
			sl->flags &= ~MF_REM_EMERG;
			sl_timer_stop(sl, t3);
			sl_aerm_start(sl);
			sl_timer_start(sl, t4);
			sl->statem.Cp = 0;
			sl->state = MS_PROVING;
			return(0);
		case MS_IN_SERVICE:
		case MS_PROCESSOR_OUTAGE:
			if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_RECEIVED_SIN)) )
				return(err);
			if ( sl->notify.events & SL_EVT_FAIL_RECEIVED_SIN )
				if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_RECEIVED_SIN, 0, NULL, 0)) )
					return(err);
			ptrace(("Link failed: Received SIN\n"));
			return(0);
	}
	ptrace(("Received status PROVING_NORMAL in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_status_proving_emergency(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_NOT_ALIGNED:
			sl_timer_stop(sl, t2);
			if ( sl->flags & MF_LOC_EMERG )
				err = sl_send_status(sl, M2PA_STATUS_PROVING_EMERGENCY);
			else	err = sl_send_status(sl, M2PA_STATUS_PROVING_NORMAL);
			if ( err ) return(err);
			sl_timer_start(sl, t3);
			sl->state = MS_ALIGNED;
			/* fall thru */
		case MS_ALIGNED:
			sl->flags |= MF_REM_EMERG;
			sl_timer_stop(sl, t3);
			sl_aerm_start(sl);
			sl_timer_start(sl, t4);
			sl->statem.Cp = 0;
			sl->state = MS_PROVING;
			return(0);
		case MS_PROVING:
			sl->flags |= MF_REM_EMERG;
			sl_timer_stop(sl, t4);
			sl_aerm_stop(sl);
			sl_timer_start(sl, t4);
			sl_aerm_start(sl);
			return(0);
		case MS_IN_SERVICE:
		case MS_PROCESSOR_OUTAGE :
			if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_RECEIVED_SIE)) )
				return(err);
			if ( sl->notify.events & SL_EVT_FAIL_RECEIVED_SIE )
				if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_RECEIVED_SIE, 0, NULL, 0)) )
					return(err);
			ptrace(("Link failed: Received SIE\n"));
			return(0);
	}
	ptrace(("Received status PROVING_EMERGENCY in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_t1_expiry(sl_t *sl) 
{
	int err;
	if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_T1_TIMEOUT)) )
		return(err);
	if ( sl->notify.events & SL_EVT_FAIL_T1_TIMEOUT )
		if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_T1_TIMEOUT, 0, NULL, 0)) )
			return(err);
	ptrace(("Link failed: T1 Timeout\n"));
	sl->stats.sl_fail_align_or_proving++;
	return(0);
}

static int sl_t3_expiry(sl_t *sl) 
{
	int err;
	if ( sl->state == MS_ALIGNED ) {
		if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_ALIGNMENT_NOT_POSSIBLE)) )
			return(err);
		if ( sl->notify.events & SL_EVT_FAIL_ALIGNMENT )
			if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_ALIGNMENT, 0, NULL, 0)) )
				return(err);
		ptrace(("Link failed: T3 Timeout\n"));
		sl->stats.sl_fail_align_or_proving++;
		return(0);
	}
	return(0);
}

static int sl_t4_expiry(sl_t *sl) 
{
	int err;
	if ( sl->state == MS_PROVING )
	{
		sl_aerm_stop(sl);
		sl->flags &= ~MF_LOC_EMERG;
		sl->flags &= ~MF_REM_EMERG;
		sl_suerm_start(sl);
		sl->flags |= MF_LOC_INS;
		if ( sl->flags & MF_LPO )
		{
			if ( (err = sl_send_status(sl, M2PA_STATUS_PROCESSOR_OUTAGE)) )
				return(err);
			if ( sl->flags & MF_REM_INS )
			{
				if ( (err = sl_in_service_ind(sl)) )
					return(err);
				sl->flags &= ~MF_SEND_MSU;
				sl->flags &= ~MF_RECV_MSU;
				sl_oos_stats(sl);
				sl->state = MS_PROCESSOR_OUTAGE;
			}
			else
			{
				sl_timer_start(sl, t1);
				sl->state = MS_ALIGNED_NOT_READY;
			}
		}
		else
		{
			mblk_t *mp;
			if ( (mp = sl->tb.q_head) )
			{
				if ( (err = sl_send_data(sl, mp)) )
					return(err);
				sl->tmsu++;
				sl->fsnt++;
				bufq_queue(&sl->rtb, bufq_dequeue(&sl->tb));
				sl->tmsu--; sl->tack++;
				sl->stats.sl_tran_msus++;
				sl->stats.sl_tran_sio_sif_octets += mp->b_wptr - mp->b_rptr - 1;
				if ( sl->rtb.q_msgs >= sl->config.N1
						|| sl->rtb.q_count >= sl->config.N2
						|| sl->tmsu+sl->tack > 0x7ffffff )
					sl->flags |= MF_RTB_FULL;
			}
			else
			{
				if ( (err = sl_send_status(sl, M2PA_STATUS_IN_SERVICE)) )
					return(err);
			}
			if ( sl->flags & MF_REM_INS )
			{
				if ( (err = sl_in_service_ind(sl)) )
					return(err);
				sl->flags |= MF_SEND_MSU;
				sl->flags |= MF_RECV_MSU;
				sl_is_stats(sl);
				sl->state = MS_IN_SERVICE;
			}
			else
			{
				sl_timer_start(sl, t1);
				sl->state = MS_ALIGNED_READY;
			}
		}
		return(0);
	}
	ptrace(("Received timeout t4 in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_status_in_service(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_ALIGNED:
		case MS_PROVING:
			sl->flags |= MF_REM_INS;
			return(0);
		case MS_ALIGNED_NOT_READY:
			if ( (err = sl_in_service_ind(sl)) )
				return(err);
			sl_timer_stop(sl, t1);
			sl->flags &= ~MF_SEND_MSU;
			sl->flags &= ~MF_RECV_MSU;
			sl_oos_stats(sl);
			sl->state = MS_PROCESSOR_OUTAGE;
			return(0);
		case MS_ALIGNED_READY:
			if ( (err = sl_in_service_ind(sl)) )
				return(err);
			sl_timer_stop(sl, t1);
			sl->flags |= MF_SEND_MSU;
			sl->flags |= MF_RECV_MSU;
			sl_is_stats(sl);
			sl->state = MS_IN_SERVICE;
			return(0);
		case MS_IN_SERVICE:
		case MS_PROCESSOR_OUTAGE:
		case MS_OUT_OF_SERVICE:
			printd(("Received status IN_SERVICE in unusual state %u\n",sl->state));
			return(0);
	}
	ptrace(("Received status IN_SERVICE in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_rb_congestion_function(sl_t *sl);

static int sl_rx_wakeup(queue_t *q)
{
	int err;
	mblk_t *mp;
	sl_t *sl = (sl_t *)q->q_ptr;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( sl, return((INT)(-EFAULT)) );
	switch ( sl->state )
	{
		default:
			if ( sl->flags & MF_RECV_MSU
					&& sl->rb.q_head
					&& canputnext(sl->rq) )
			{
				while( (mp = bufq_dequeue(&sl->rb)) )
				{
					if ( (err = sl_pdu_ind(sl, mp)) ) {
						bufq_queue_head(&sl->rb, mp);
						rare();
						return(err);
					}
					sl->stats.sl_recv_msus++;
					sl->stats.sl_recv_sio_sif_octets += mp->b_wptr - mp->b_rptr - 1;
					sl->rmsu++;
					sl->fsnr++;
					sl_rb_congestion_function(sl);
					if ( !(sl->flags & MF_CONG_ACCEPT) )
						sl->rack += xchg(&sl->rmsu,0);
				}
			}
		case MS_POWER_OFF:
		case MS_OUT_OF_SERVICE:
			return(0);
	}
}

static int sl_rc_signal_unit(sl_t *sl, mblk_t *mp) 
{
	int err;
	switch ( sl->state )
	{
		case MS_ALIGNED_READY:
			if ( (err = sl_in_service_ind(sl)) )
				return(err);
			sl_timer_stop(sl, t1);
			sl->flags |= MF_SEND_MSU;
			sl->flags |= MF_RECV_MSU;
			sl_is_stats(sl);
			sl->state = MS_IN_SERVICE;
		default:
			if ( sl->flags & MF_RECV_MSU 
					&& !(sl->flags & MF_CONG_DISCARD) )
			{
				bufq_queue(&sl->rb, mp);
				sl_rb_congestion_function(sl);
				return(0);
			}
			else seldom();
			return(-EBUSY);
		case MS_POWER_OFF:
		case MS_OUT_OF_SERVICE:
			seldom();
			return(0);
	}
}

static int sl_tx_wakeup(queue_t *q)
{
	int err;
	mblk_t *mp;
	sl_t *sl = (sl_t *)q->q_ptr;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( sl, return((INT)(-EFAULT)) );
	switch ( sl->state )
	{
		default:
			if ( sl->rack )
			{
				if ( (err = sl_send_ack(sl, sl->rack)) )
					return(err);
				sl->rack = 0;
			}
			if ( sl->flags & MF_SEND_MSU && sl->tb.q_head
					&& !(sl->flags & MF_RTB_FULL) )
			{
				if ( !sl->rtb.q_count ) {
					if ( sl->flags & MF_REM_BUSY )
						sl_timer_start(sl, t6);
					else	sl_timer_start(sl, t7);
				}
				while ( (mp = sl->tb.q_head) )
				{
					if ( (err = sl_send_data(sl, mp)) )
						return(err);
					sl->tmsu++;
					sl->fsnt++;
					bufq_queue(&sl->rtb, bufq_dequeue(&sl->tb));
					sl->tmsu--; sl->tack++;
					sl->stats.sl_tran_msus++;
					sl->stats.sl_tran_sio_sif_octets += mp->b_wptr - mp->b_rptr - 1;
					if ( sl->rtb.q_msgs >= sl->config.N1
							|| sl->rtb.q_count >= sl->config.N2
							|| sl->tmsu+sl->tack > 0x7ffffff )
					{
						sl->flags |= MF_RTB_FULL;
						break;
					}
				}
			}
			return(0);
		case MS_POWER_OFF:
		case MS_OUT_OF_SERVICE:
			return(0);
	}
}

static int sl_check_congestion(sl_t *sl);

static int sl_txc_datack(sl_t *sl, ulong count) 
{
	int err;
	switch ( sl->state )
	{
		case MS_IN_SERVICE:
		case MS_PROCESSOR_OUTAGE:
		case MS_ALIGNED_READY:
		case MS_ALIGNED_NOT_READY:
			while ( count-- )
			{
				if ( sl->rtb.q_head ) {
					freemsg(bufq_dequeue(&sl->rtb));
					sl->tack--;
					qenable(sl->wq);
				} else {
					if ( sl->back )
					{
						if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_ABNORMAL_FIBR)) )
							return(err);
						if ( sl->notify.events & SL_EVT_FAIL_ABNORMAL_FIBR )
							if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_ABNORMAL_FIBR, 0, NULL, 0)) )
								return(err);
						ptrace(("Link failed: Abnormal FIBR\n"));
						return(0);
					}
					ptrace(("Received bad acknowledgement acks = %lu\n",count+1));
					sl->back++;
					return(0);
				}
			}
			sl->back = 0;
			if ( sl->rtb.q_count == 0 )
			{
				sl_timer_stop(sl, t7);
				sl_timer_stop(sl, t6);
			}
			else
			if ( !(sl->flags & MF_REM_BUSY) )
				sl_timer_start(sl, t7);
			sl->flags &= ~MF_RTB_FULL;
			sl_check_congestion(sl);
			return(0);
			/* let tx_wakeup clear up new transmissions later */
		default:
			seldom();
			return(0);
	}
}

static int sl_lsc_pdu(sl_t *sl, mblk_t *mp)
{
	int err;
	bufq_queue(&sl->tb, mp);
	if ( (err = sl_check_congestion(sl)) )
		return(err);
	return(0);
	/* let tx_wakeup clear up the tb later */
}

static int sl_t7_expiry(sl_t *sl) 
{
	int err;
	if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_ACK_TIMEOUT)) )
		return(err);
	if ( sl->notify.events & SL_EVT_FAIL_ACK_TIMEOUT )
		if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_ACK_TIMEOUT, 0, NULL, 0)) )
			return(err);
	ptrace(("Link failed: T7 Timeout\n"));
	return(0);
}

static int sl_lsc_status_busy(sl_t *sl)
{
	switch ( sl->state )
	{
		case MS_IN_SERVICE:
			if ( !(sl->flags & MF_REM_BUSY) ) {
				sl->flags |= MF_REM_BUSY;
				if ( sl->rtb.q_count )
					sl_timer_start(sl, t6);
				sl_timer_stop(sl, t7);
			}
			return(0);
		case MS_PROCESSOR_OUTAGE:
			sl->flags |= MF_REM_BUSY;
			return(0);
		case MS_POWER_OFF:
		case MS_OUT_OF_SERVICE:
			return(0);
	}
	ptrace(("Received status BUSY in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_congestion_discard(sl_t *sl) 
{
	int err;
	if ( !(sl->flags & MF_LOC_BUSY) ) {
		if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY)) )
			return(err);
		sl->stats.sl_sibs_sent++;
		sl->flags |= MF_LOC_BUSY;
	}
	sl->flags |= MF_CONG_DISCARD;
	sl->flags |= MF_L3_CONG_DETECT;
	return(0);
}

static int sl_lsc_congestion_accept(sl_t *sl) 
{
	int err;
	if ( !(sl->flags & MF_LOC_BUSY) ) {
		if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY)) )
			return(err);
		sl->stats.sl_sibs_sent++;
		sl->flags |= MF_LOC_BUSY;
	}
	sl->flags |= MF_CONG_ACCEPT;
	sl->flags |= MF_L3_CONG_DETECT;
	return(0);
}

static int sl_lsc_status_busy_ended(sl_t *sl)
{
	switch ( sl->state )
	{
		default:
			ptrace(("Received status BUSY_ENDED in unexpected state %u\n",sl->state));
		case MS_IN_SERVICE:
			sl->flags &= ~MF_REM_BUSY;
			sl_timer_stop(sl, t6);
			if ( sl->rtb.q_count )
				sl_timer_start(sl, t7);
			return(0);
		case MS_POWER_OFF:
		case MS_OUT_OF_SERVICE:
			return(0);
	}
}

static int sl_t6_expiry(sl_t *sl) 
{
	int err;
	if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_CONG_TIMEOUT)) )
		return(err);
	if ( sl->notify.events & SL_EVT_FAIL_CONG_TIMEOUT )
		if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_CONG_TIMEOUT, 0, NULL, 0)) )
			return(err);
	ptrace(("Link failed: T6 Timeout\n"));
	return(0);
}

static int sl_lsc_no_congestion(sl_t *sl) 
{
	int err;
	if ( sl->flags & MF_LOC_BUSY )
		if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY_ENDED)) )
			return(err);
	sl->flags &= ~MF_LOC_BUSY;
	sl->flags &= ~MF_CONG_DISCARD;
	sl->flags &= ~MF_CONG_ACCEPT;
	sl->flags &= ~MF_L3_CONG_DETECT;
	sl->rack += xchg(&sl->rmsu,0);
	return(0);
}

static int sl_lsc_status_processor_outage(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_OUT_OF_SERVICE:
		case MS_NOT_ALIGNED:
		case MS_ALIGNED:
		case MS_PROVING:
			/* just remember for later */
			if ( sl->notify.events & SL_EVT_RPO_BEGIN )
				if ( (err = lmi_event_ind(sl, SL_EVT_RPO_BEGIN, 0, NULL, 0)) )
					return(err);
			sl->flags |= MF_RPO;
			ptrace(("RPO Begins\n"));
			return(0);
		case MS_ALIGNED_READY:
		case MS_ALIGNED_NOT_READY:
			if ( !(sl->flags & MF_RPO) ) {
				if ( (err = sl_remote_processor_outage_ind(sl)) )
					return(err);
				sl->flags |= MF_RPO;
			}
			if ( sl->notify.events & SL_EVT_RPO_BEGIN )
				if ( (err = lmi_event_ind(sl, SL_EVT_RPO_BEGIN, 0, NULL, 0)) )
					return(err);
			sl_rpo_stats(sl);
			ptrace(("RPO Begins\n"));
			sl_timer_stop(sl, t1);
			sl->state = MS_PROCESSOR_OUTAGE;
			return(0);
		case MS_IN_SERVICE:
			if ( !(sl->flags & MF_RPO) ) {
				if ( (err = sl_remote_processor_outage_ind(sl)) )
					return(err);
				sl->flags |= MF_RPO;
			}
			if ( sl->notify.events & SL_EVT_RPO_BEGIN )
				if ( (err = lmi_event_ind(sl, SL_EVT_RPO_BEGIN, 0, NULL, 0)) )
					return(err);
			ptrace(("RPO Begins\n"));
			sl_timer_stop(sl, t7);
			sl_timer_stop(sl, t6);
			sl->flags &= ~MF_SEND_MSU;
			switch ( sl->option.pvar )
			{
				default:
				case SS7_PVAR_ITUT_93:
				case SS7_PVAR_ITUT_96:
					sl->flags &= ~MF_CONTINUE;
					break;
				case SS7_PVAR_ANSI_92:
					sl->rack += xchg(&sl->rmsu,0);
					fixme(("Bad idea\n"));
					if ( sl->flags & MF_LOC_BUSY )
						if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY_ENDED)) )
							return(err);
					sl->flags &= ~MF_LOC_BUSY;
					break;
			}
			sl_rpo_stats(sl);
			sl->state = MS_PROCESSOR_OUTAGE;
			return(0);
		case MS_PROCESSOR_OUTAGE:
			if ( !(sl->flags & MF_RPO) ) {
				if ( (err = sl_remote_processor_outage_ind(sl)) )
					return(err);
				sl->flags |= MF_RPO;
			}
			if ( sl->notify.events & SL_EVT_RPO_BEGIN )
				if ( (err = lmi_event_ind(sl, SL_EVT_RPO_BEGIN, 0, NULL, 0)) )
					return(err);
			ptrace(("RPO Begins\n"));
			return(0);
	}
	ptrace(("Received status PROCESSOR_OUTAGE in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_local_processor_outage(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_OUT_OF_SERVICE:
		case MS_NOT_ALIGNED:
		case MS_ALIGNED:
		case MS_PROVING:
			/* just remember for later */
			sl->flags |= MF_LPO;
			return(0);
		case MS_ALIGNED_READY:
			if ( (err = sl_send_status(sl, M2PA_STATUS_PROCESSOR_OUTAGE)) )
				return(err);
			sl->flags |= MF_LPO;
			sl_timer_stop(sl, t7);
			sl_timer_stop(sl, t6);
			sl->flags &= ~MF_RECV_MSU;
			sl->state = MS_ALIGNED_NOT_READY;
			return(0);
		case MS_IN_SERVICE:
			if ( (err = sl_send_status(sl, M2PA_STATUS_PROCESSOR_OUTAGE)) )
				return(err);
			sl->flags |= MF_LPO;
			sl_timer_stop(sl, t7);
			sl_timer_stop(sl, t6);
			sl->flags &= ~MF_RECV_MSU;
			switch ( sl->option.pvar )
			{
				default:
				case SS7_PVAR_ITUT_93:
				case SS7_PVAR_ITUT_96:
					sl->flags &= ~MF_CONTINUE;
					break;
				case SS7_PVAR_ANSI_92:
					sl->rack += xchg(&sl->rmsu,0);
					fixme(("Bad idea\n"));
					if ( sl->flags & MF_LOC_BUSY )
						if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY_ENDED)) )
							return(err);
					sl->flags &= ~MF_LOC_BUSY;
					break;
			}
			sl_rpo_stats(sl);
			sl->state = MS_PROCESSOR_OUTAGE;
			return(0);
		case MS_PROCESSOR_OUTAGE:
			if ( !(sl->flags & MF_LPO) )
				if ( (err = sl_send_status(sl, M2PA_STATUS_PROCESSOR_OUTAGE)) )
					return(err);
			sl->flags |= MF_LPO;
			sl_timer_stop(sl, t7);  /* ??? */
			sl_timer_stop(sl, t6);  /* ??? */
			return(0);
	}
	ptrace(("Received primitive SL_LOCAL_PROCESSOR_OUTAGE_REQ in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_clear_rtb(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_PROCESSOR_OUTAGE:
			bufq_purge(&sl->rtb);
			sl->tack = 0;
			sl->flags &= ~MF_RTB_FULL;
			sl->flags &= ~MF_CLEAR_RTB;
			if ( (err = sl_rtb_cleared_ind(sl)) )
				return(err);
			if ( (err = sl_check_congestion(sl)) )
				return(err);
			sl_timer_stop(sl, t7);
			sl_timer_stop(sl, t6);
			sl->flags &= ~MF_LPO;
			if ( sl->flags & MF_RPO ) {
				if ( sl->notify.events & SL_EVT_RPO_END )
					if ( (err = lmi_event_ind(sl, SL_EVT_RPO_END, 0, NULL, 0)) )
						return(err);
				ptrace(("RPO Ends\n"));
				sl_rpr_stats(sl);
				sl->flags &= ~MF_RPO;
			}
			sl->flags &= ~MF_SEND_MSU;
			sl->flags |= MF_SEND_MSU;
			if ( (err = sl_remote_processor_recovered_ind(sl)) )
				return(err);
			sl_is_stats(sl);
			sl->state = MS_IN_SERVICE;
			return(0);
	}
	ptrace(("Received primitive SL_CLEAR_RTB_REQ in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_clear_buffers(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_OUT_OF_SERVICE:
		case MS_NOT_ALIGNED:
		case MS_ALIGNED:
		case MS_PROVING:
			switch ( sl->option.pvar )
			{
				default:
				case SS7_PVAR_ITUT_93:
				case SS7_PVAR_ITUT_96:
					return(0);
				case SS7_PVAR_ANSI_92:
					if ( (err = sl_rtb_cleared_ind(sl)) )
						return(err);
					sl->flags &= ~MF_LPO;  /* ??? */
					return(0);
			}
			return(0);
		case MS_ALIGNED_NOT_READY:
			switch ( sl->option.pvar )
			{
				default:
				case SS7_PVAR_ITUT_93:
				case SS7_PVAR_ITUT_96:
					return(0);
				case SS7_PVAR_ANSI_92:
					if ( (err = sl_rtb_cleared_ind(sl)) )
						return(err);
					sl->flags &= ~MF_LPO;
					sl_timer_stop(sl, t7);  /* ??? */
					sl_timer_stop(sl, t6);  /* ??? */
					sl->flags &= ~MF_SEND_MSU;
					sl->state = MS_ALIGNED_READY;
					return(0);
			}
			return(0);
		case MS_PROCESSOR_OUTAGE:
			bufq_purge(&sl->rtb);
			sl->tack = 0;
			bufq_purge(&sl->tb);
			flushq(sl->wq, FLUSHDATA);
			sl_timer_stop(sl, t7);
			sl_timer_stop(sl, t6);
			sl->flags &= ~MF_RTB_FULL;
			sl->flags &= ~MF_CLEAR_RTB;
			if ( (err = sl_check_congestion(sl)) )
				return(err);
			switch ( sl->option.pvar )
			{
				default:
				case SS7_PVAR_ITUT_93:
				case SS7_PVAR_ITUT_96:
					sl->flags &= ~MF_NEED_FLUSH;
					if ( sl->flags & MF_CONTINUE ) {
						sl->flags |= MF_NEED_FLUSH;
						break;
					}
					return(0);
				case SS7_PVAR_ANSI_92:
					if ( (err = sl_rtb_cleared_ind(sl)) )
						return(err);
					bufq_purge(&sl->rb);
					flushq(sl->rq, FLUSHDATA);
					if ( (err = sl_rb_cleared_ind(sl)) )
						return(err);
					if ( sl->flags & MF_RPO ) {
						if ( (err = sl_remote_processor_recovered_ind(sl)) )
							return(err);
						sl->flags &= ~MF_RPO;
					}
					if ( sl->notify.events & SL_EVT_RPO_END )
						if ( (err = lmi_event_ind(sl, SL_EVT_RPO_END, 0, NULL, 0)) )
							return(err);
					ptrace(("RPO Ends\n"));
					sl_rpr_stats(sl);
					break;
			}
			sl->flags |= MF_RECV_MSU;
			sl->flags |= MF_SEND_MSU;
			sl->flags &= ~MF_LPO;
			sl_is_stats(sl);
			sl->state = MS_IN_SERVICE;
			return(0);
	}
	ptrace(("Received primitive SL_CLEAR_BUFFERS_REQ in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static void sl_lsc_no_processor_outage(sl_t* sl)
{
	switch ( sl->state )
	{
		case MS_PROCESSOR_OUTAGE:
			sl->flags |= MF_CONTINUE;
			if ( !(sl->flags & MF_NEED_FLUSH) )
			{
				sl->flags &= ~MF_LPO;
				sl->flags |= MF_NEED_FLUSH;
				sl->flags |= MF_RECV_MSU;
				sl->flags |= MF_SEND_MSU;
				if ( sl->rtb.q_count ) {
					if ( sl->flags & MF_REM_BUSY )
						sl_timer_start(sl, t6);
					else	sl_timer_start(sl, t7);
				}
				sl_is_stats(sl);
				sl->state = MS_IN_SERVICE;
			}

	}
}

#if 0
/* This was the blue book way of doing things... */
static int sl_lsc_continue(sl_t* sl, mblk_t *mp)
{
	switch ( sl->state )
	{
		case MS_PROCESSOR_OUTAGE:
			if ( sl->flags & MF_CONTINUE )
			{
				sl->flags |= MF_NEED_FLUSH;
				sl->flags |= MF_RECV_MSU;
				sl->flags |= MF_SEND_MSU;
				if ( sl->rtb.q_count ) {
					if ( sl->flags & MF_REM_BUSY )
						sl_timer_start(sl, t6);
					else	sl_timer_start(sl, t7);
				}
				sl->flags &= ~MF_LPO;
				sl_is_stats(sl);
				sl->state = MS_IN_SERVICE;
			}
			return(0);

	}
	ptrace(("Received primitive SL_CONTINUE_REQ in unexpected state %u\n",sl->state));
	return(-EPROTO);
}
#endif

static int sl_lsc_status_processor_outage_ended(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_PROCESSOR_OUTAGE:
			if ( sl->flags & MF_RPO ) {
				if ( (err = sl_remote_processor_recovered_ind(sl)) )
					return(err);
				if ( sl->notify.events & SL_EVT_RPO_END )
					if ( (err = lmi_event_ind(sl, SL_EVT_RPO_END, 0, NULL, 0)) )
						return(err);
				sl->flags &= ~MF_RPO;
				ptrace(("RPO Ends\n"));
			}
			sl_rpr_stats(sl);
			switch ( sl->option.pvar )
			{
				default:
				case SS7_PVAR_ITUT_93:
				case SS7_PVAR_ITUT_96:
					/* auto recover */
					if ( !(sl->flags & MF_LPO) )
						sl_lsc_no_processor_outage(sl);
					break;
				case SS7_PVAR_ANSI_92:
					/* leave state latched */
					break;
			}
			return(0);
	}
	ptrace(("Received status PROCESSOR_OUTAGE_ENDED in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_resume(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_OUT_OF_SERVICE:
		case MS_NOT_ALIGNED:
		case MS_ALIGNED:
		case MS_PROVING:
			/* remember for later */
			sl->flags &= ~MF_LPO;
			return(0);
		case MS_ALIGNED_READY :
			fixme(("This is really an error...\n"));
			return(0);
		case MS_ALIGNED_NOT_READY :
			sl->flags &= ~MF_LPO;
			switch (sl->option.pvar )
			{
				case SS7_PVAR_ITUT_96:
					if ( !(sl->flags & MF_RPO) )
						sl_lsc_no_processor_outage(sl);
					sl->flags |= MF_RECV_MSU;	/* ??? */
					break;
				case SS7_PVAR_ANSI_92:
					break;
				default:
					if ( !(sl->flags & MF_RPO) )
						sl_lsc_no_processor_outage(sl);
					break;
			}
			sl_timer_stop(sl, t7);
			sl_timer_stop(sl, t6);
			sl->flags &= ~MF_SEND_MSU;
			sl->state = MS_ALIGNED_READY;
			return(0);
		case MS_PROCESSOR_OUTAGE:
			/*
			 *  This is where the really wierd behavior begins...
			 */
			switch ( sl->option.pvar )
			{
				default:
				case SS7_PVAR_ITUT_93:
				case SS7_PVAR_ITUT_96:
					if ( sl->flags & MF_LPO ) {
						if ( (err = sl_send_status(sl, M2PA_STATUS_PROCESSOR_OUTAGE_ENDED)) )
							return(err);
						sl->flags &= ~MF_LPO;
					}
					if ( !(sl->flags & MF_RPO) )
						sl_lsc_no_processor_outage(sl);
					sl->rack += xchg(&sl->rmsu,0);
					sl->flags &= ~MF_CONG_DISCARD;
					sl->flags &= ~MF_CONG_ACCEPT;
					if ( sl->flags & MF_LOC_BUSY )
						if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY_ENDED)) )
							return(err);
					sl->flags &= ~MF_LOC_BUSY;
					sl_timer_stop(sl, t7);
					sl_timer_stop(sl, t6);
					sl->flags &= ~MF_SEND_MSU;
					return(0);
				case SS7_PVAR_ANSI_92:
					/* XXX do we do this now??? */
					if ( sl->flags & MF_LPO ) {
						if ( (err = sl_send_status(sl, M2PA_STATUS_PROCESSOR_OUTAGE_ENDED)) )
							return(err);
						sl->flags &= ~MF_LPO;
					}
					sl->flags |= MF_RECV_MSU;
					if ( sl->flags & MF_RPO )
					{
						sl_timer_stop(sl, t7);
						sl_timer_stop(sl, t6);
						sl->flags &= ~MF_SEND_MSU;
						fixme(("We should not do this...\n"));
						if ( (err = sl_remote_processor_outage_ind(sl)) )
							return(err);
						return(0);
					}
					sl->flags |= MF_SEND_MSU;
					if ( sl->rtb.q_count ) {
						if ( sl->flags & MF_REM_BUSY )
							sl_timer_start(sl, t6);
						else	sl_timer_start(sl, t7);
					}
					break;
			}
			sl_is_stats(sl);
			sl->state = MS_IN_SERVICE;
			return(0);
	}
	ptrace(("Received primitive SL_RESUME_REQ in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_status_out_of_service(sl_t *sl) 
{
	int err;
	switch ( sl->state )
	{
		case MS_ALIGNED_READY:
		case MS_ALIGNED_NOT_READY:
		case MS_IN_SERVICE:
		case MS_PROCESSOR_OUTAGE:
			sl->flags &= ~MF_REM_INS;
			if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_RECEIVED_SIOS)) )
				return(err);
			if ( sl->notify.events & SL_EVT_FAIL_RECEIVED_SIOS )
				if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_RECEIVED_SIOS, 0, NULL, 0)) )
					return(err);
			ptrace(("Link failed: Received SIOS\n"));
			return(0);
		case MS_ALIGNED:
		case MS_PROVING:
			sl->flags &= ~MF_REM_INS;
			if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_ALIGNMENT_NOT_POSSIBLE)) )
				return(err);
			if ( sl->notify.events & SL_EVT_FAIL_ALIGNMENT )
				if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_ALIGNMENT, 0, NULL, 0)) )
					return(err);
			ptrace(("Link failed: Alignment not possible\n"));
			sl->stats.sl_fail_align_or_proving++;
			return(0);
		case MS_POWER_OFF:
		case MS_OUT_OF_SERVICE:
		case MS_NOT_ALIGNED:
			return(0);
	}
	ptrace(("Received status OUT_OF_SERVICE in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_retrieve_bsnt(sl_t *sl)
{
	switch ( sl->state )
	{
		case MS_OUT_OF_SERVICE:
		case MS_PROCESSOR_OUTAGE:
			sl_bsnt_ind(sl, sl->fsnr);
			return(0);
	}
	ptrace(("Received primitive SL_RETRIEVE_BSNT_REQ in unexpected state %u\n",sl->state));
	return(-EPROTO);
}

static int sl_lsc_retrieval_request_and_fsnc(sl_t *sl, ulong fsnc)
{
	int err;
	mblk_t *mp;
	switch ( sl->state )
	{
		case MS_OUT_OF_SERVICE:
		case MS_PROCESSOR_OUTAGE:
			/*
			 *  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".
			 */
			fixme(("Fix this check...\n"));
			/*
			 *  this will pretty much clear the rtb if there is a
			 *  problem with the FSNC
			 */
			while ( sl->rtb.q_head && sl->fsnt != fsnc )
			{
				freemsg(bufq_dequeue(&sl->rtb));
				sl->fsnt--;
			}
			while ( (mp = bufq_dequeue(&sl->rtb)) )
			{
				if ( (err = sl_retrieved_message_ind(sl, mp)) ) 
				{
					bufq_queue_head(&sl->rtb, mp);
					return(err);
				}
				sl->flags &= ~MF_RTB_FULL;
			}
			while ( (mp = bufq_dequeue(&sl->tb)) )
			{
				if ( (err = sl_retrieved_message_ind(sl, mp)) )
				{
					bufq_queue_head(&sl->tb, mp);
					return(err);
				}
			}
			if ( (err = sl_retrieval_complete_ind(sl)) )
				return(err);
			return(0);
	}
	ptrace(("Received primitive SL_RETRIEVAL_REQUEST_AND_FSNC_REQ in unexpected state %u\n",sl->state));
	if ( (err = sl_retrieval_not_possible_ind(sl)) )
		return(err);
	return(-EPROTO);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  RECEIVE CONGESTION ALGORITHM
 *
 *  --------------------------------------------------------------------------
 *
 *  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 int sl_rb_congestion_function(sl_t *sl) 
{
	int err;
	if ( !(sl->flags & MF_L3_CONG_DETECT) )
	{
		if ( sl->flags & MF_L2_CONG_DETECT )
		{
			if ( sl->rb.q_msgs <= sl->config.rb_abate && canputnext(sl->rq) )
			{
				if ( sl->flags & MF_LOC_BUSY )
					if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY_ENDED)) )
						return(err);
				sl->flags &= ~MF_LOC_BUSY;
				sl->flags &= ~MF_CONG_DISCARD;
				sl->flags &= ~MF_CONG_ACCEPT;
				sl->rack += xchg(&sl->rmsu,0);
				sl->flags &= ~MF_L2_CONG_DETECT;
			}
		}
		else
		{
			if ( sl->rb.q_msgs >= sl->config.rb_discard || !canput(sl->rq) )
			{
				sl->flags |= MF_CONG_DISCARD;
				if ( !(sl->flags & MF_LOC_BUSY) ) {
					if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY)) )
						return(err);
					printd(("Receive congestion: congestion discard\n"));
					printd(("  sl->rb.q_msgs           = %12u\n",sl->rb.q_msgs));
					printd(("  sl->config.rb_discard   = %12lu\n",sl->config.rb_discard));
					printd(("  canput                  = %12s\n",canput(sl->rq)?"YES":"NO"));
					sl->stats.sl_sibs_sent++;
					sl->flags |= MF_LOC_BUSY;
				}
				sl->flags |= MF_L2_CONG_DETECT;
			}
			else
			{
				if ( sl->rb.q_msgs  >= sl->config.rb_accept || !canputnext(sl->rq) )
				{
					sl->flags |= MF_CONG_ACCEPT;
					if ( !(sl->flags & MF_LOC_BUSY) ) {
						if ( (err = sl_send_status(sl, M2PA_STATUS_BUSY)) )
							return(err);
						printd(("Receive congestion: congestion accept\n"));
						printd(("  sl->rb.q_msgs           = %12u\n",sl->rb.q_msgs));
						printd(("  sl->config.rb_accept    = %12lu\n",sl->config.rb_accept));
						printd(("  cantputnext?            = %12s\n",canputnext(sl->rq)?"YES":"NO"));
						sl->stats.sl_sibs_sent++;
						sl->flags |= MF_LOC_BUSY;
					}
					sl->flags |= MF_L2_CONG_DETECT;
				}
			}
		}
	}
	return(0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  TRANSMIT CONGESTION ALGORITHM
 *
 *  -------------------------------------------------------------------------
 *
 *  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 int sl_check_congestion(sl_t *sl)
{
	int err;
	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_link_congestion_ceased_ind(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]++ )
					if ( (err = lmi_event_ind(sl, SL_EVT_CONGEST_ONSET_IND, 0, &sl->statem.cong_status, sizeof(sl->statem.cong_status))) )
						return(err);
					ptrace(("Congestion onset: level %ld\n", sl->statem.cong_status));
			}
			else
			{
				if ( sl->notify.events & SL_EVT_CONGEST_DISCD_IND &&
						!sl->stats.sl_cong_discd_ind[sl->statem.disc_status]++ )
					if ( (err = lmi_event_ind(sl, SL_EVT_CONGEST_DISCD_IND, 0, &sl->statem.disc_status, sizeof(sl->statem.disc_status))) )
						return(err);
					ptrace(("Congestion discard: level %ld\n", sl->statem.cong_status));
			}
			sl_link_congested_ind(sl, sl->statem.cong_status, sl->statem.disc_status);
		}
	}
	return(0);
}

/*
 *  =========================================================================
 *
 *  INPUT Events
 *
 *  =========================================================================
 *  -------------------------------------------------------------------------
 *
 *  SL Peer -> SL Peer (M2PA) Received Messages
 *
 *  -------------------------------------------------------------------------
 *
 *  M2PA RECV PROVING
 *  ---------------------------------------------
 */
static int
sl_recv_proving(sl_t *sl, mblk_t *mp, size_t mlen)
{
	(void)sl; (void)mp; (void)mlen;
	/* just ignore proving packets */
	return(0);
}
/*
 *  M2PA RECV STATUS
 *  ---------------------------------------------
 */
static int
sl_recv_status(sl_t *sl, mblk_t *mp, size_t mlen)
{
	if ( mlen == sizeof(uint32_t) ) {
		switch ( *((uint32_t *)mp->b_rptr)++ )
		{
			case M2PA_STATUS_BUSY_ENDED:
				sl_lsc_status_busy_ended(sl);
				break;
			case M2PA_STATUS_IN_SERVICE:
				sl_lsc_status_in_service(sl);
				break;
			case M2PA_STATUS_PROCESSOR_OUTAGE_ENDED:
				sl_lsc_status_processor_outage_ended(sl);
				break;
			case M2PA_STATUS_OUT_OF_SERVICE:
				sl_lsc_status_out_of_service(sl);
				break;
			case M2PA_STATUS_PROCESSOR_OUTAGE:
				sl_lsc_status_processor_outage(sl);
				break;
			case M2PA_STATUS_BUSY:
				sl_lsc_status_busy(sl);
				break;
			case M2PA_STATUS_ALIGNMENT:
				sl_lsc_status_alignment(sl);
				break;
			case M2PA_STATUS_PROVING_NORMAL:
				sl_lsc_status_proving_normal(sl);
				break;
			case M2PA_STATUS_PROVING_EMERGENCY:
				sl_lsc_status_proving_emergency(sl);
				break;
			default: rare(); return(-EPROTO);
		}
		return(0);
	} rare(); return (-EMSGSIZE);
}
/*
 *  M2PA RECV ACK
 *  ---------------------------------------------
 */
static int
sl_recv_datack(sl_t *sl, mblk_t *mp, size_t mlen)
{
	int err;
	uint count;
	if ( mlen >= sizeof(uint32_t) ) {
	if ( (count = ntohl(*((uint32_t *)mp->b_rptr)++)) ) {

		if ( (err = sl_txc_datack(sl, count)) )
			return(err);
		return(0);

	} rare(); return(-EPROTO);
	} rare(); return(-EMSGSIZE);
}
/*
 *  M2PA RECV DATA
 *  ---------------------------------------------
 */
static int
sl_recv_data(sl_t *sl, mblk_t *mp, size_t mlen)
{
	int err;
	if ( mlen > sizeof(uint32_t) ) {
		if ( (err = sl_rc_signal_unit(sl, mp)) )
			return(err);
		return(1);  /* absorbed mblk */
	} rare(); return(-EMSGSIZE);
}
/*
 *  M2PA RECV
 *  ---------------------------------------------
 */
static int
sl_recv_msg(sl_t *sl, mblk_t *mp)
{
	size_t mlen;
	uint32_t type;
	if ( mp->b_wptr >= mp->b_rptr + 2*sizeof(uint32_t) ) {
	type = ((uint32_t *)mp->b_rptr)[0];
	mlen = ntohl(((uint32_t *)mp->b_rptr)[1]);
	if ( mp->b_wptr >= mp->b_rptr + mlen ) {
	mp->b_rptr += 2*sizeof(uint32_t);
	mlen -= 2*sizeof(uint32_t);
	switch ( type ) {
		case M2PA_DATA_MESSAGE:	    return sl_recv_data   (sl, mp, mlen);
		case M2PA_STATUS_MESSAGE:   return sl_recv_status (sl, mp, mlen);
		case M2PA_PROVING_MESSAGE:  return sl_recv_proving(sl, mp, mlen);
		case M2PA_ACK_MESSAGE:	    return sl_recv_datack (sl, mp, mlen);
	} rare(); return(-EPROTO);
	} rare(); return(-EMSGSIZE);
	} rare(); return(-EPROTO);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  SL User -> SL Provider (M2PA) Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  LMI_INFO_REQ
 *  ---------------------------------------------
 */
static int
lmi_info_req(sl_t *sl, mblk_t *mp)
{
	(void)mp;
	return lmi_info_ack(sl);
}
/*
 *  LMI_ATTACH_REQ
 *  ---------------------------------------------
 */
static int
lmi_attach_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_attach_req_t *p = (lmi_attach_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( sl->i_state == LMI_UNATTACHED ) { sl->i_state = LMI_ATTACH_PENDING;
		lmi_error_ack(sl, LMI_ATTACH_REQ, LMI_NOTSUPP);
	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(sl, LMI_ATTACH_REQ, err);
}
/*
 *  LMI_DETACH_REQ
 *  ---------------------------------------------
 */
static int
lmi_detach_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_detach_req_t *p = (lmi_detach_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( sl->i_state == LMI_DISABLED ) { sl->i_state = LMI_DETACH_PENDING;
		lmi_error_ack(sl, LMI_DETACH_REQ, LMI_NOTSUPP);
	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(sl, LMI_DETACH_REQ, err);
}
/*
 *  LMI_ENABLE_REQ
 *  ---------------------------------------------
 */
static int
lmi_enable_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_enable_req_t *p = (lmi_enable_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( sl->i_state == LMI_DISABLED ) { sl->i_state = LMI_ENABLE_PENDING;
		return lmi_enable_con(sl);
	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(sl, LMI_ENABLE_REQ, err);
}
/*
 *  LMI_DISABLE_REQ
 *  ---------------------------------------------
 */
static int
lmi_disable_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_disable_req_t *p = (lmi_disable_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( sl->i_state == LMI_ENABLED || sl->i_state == LMI_ENABLE_PENDING ) {
		if ( (err = sl_lsc_stop(sl)) )
			return(err);
		sl->i_state = LMI_DISABLE_PENDING;
		return lmi_disable_con(sl);
	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(sl, LMI_DISABLE_REQ, err);
}
/*
 *  LMI_OPTMGMT_REQ
 *  ---------------------------------------------
 */
static int
lmi_optmgmt_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_optmgmt_req_t *p = (lmi_optmgmt_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		lmi_error_ack(sl, LMI_OPTMGMT_REQ, LMI_NOTSUPP);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(sl, LMI_OPTMGMT_REQ, err);
}
/*
 *  SL_PDU_REQ
 *  ---------------------------------------------
 */
#ifndef abs
#define abs(x) ((x)<0 ? -(x):(x))
#endif
static int
m_error_reply(sl_t *sl, int err)
{
	mblk_t *mp;
	switch ( err )
	{
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
		case -ENOBUFS:
			seldom(); return(err);
		case 0:
		case 1:
		case 2:
			never(); return(err);
	}
	if ( (mp = allocb(2, BPRI_HI)) )
	{
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = abs(err);
		*(mp->b_wptr)++ = abs(err);
		putnext(sl->rq, mp);
		return(0);
	}
	rare(); return(-ENOBUFS);
}
static int
sl_pdu_req(sl_t *sl, mblk_t *mp)
{
	int err;
	mblk_t *dp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_pdu_req_t *p = (sl_pdu_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( (dp = mp->b_cont) ) {
		if ( (err = sl_lsc_pdu(sl, dp)) )
			return(err);
		mp->b_cont = NULL;  /* data absorbed */
		return(0);
	
	} seldom(); err = LMI_BADPRIM;	    break;  /* No M_DATA block		    */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_EMERGENCY_REQ
 *  ---------------------------------------------
 */
static int
sl_emergency_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_emergency_req_t *p = (sl_emergency_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_emergency(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_EMERGENCY_CEASES_REQ
 *  ---------------------------------------------
 */
static int
sl_emergency_ceases_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_emergency_ceases_req_t *p = (sl_emergency_ceases_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_emergency_ceases(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_START_REQ
 *  ---------------------------------------------
 */
static int
sl_start_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_start_req_t *p = (sl_start_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_start(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_STOP_REQ
 *  ---------------------------------------------
 */
static int
sl_stop_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_stop_req_t *p = (sl_stop_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_stop(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_RETRIEVE_BSNT_REQ
 *  ---------------------------------------------
 */
static int
sl_retrieve_bsnt_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_retrieve_bsnt_req_t *p = (sl_retrieve_bsnt_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_retrieve_bsnt(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_RETRIEVAL_REQUEST_AND_FSNC_REQ
 *  ---------------------------------------------
 */
static int
sl_retrieval_request_and_fsnc_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_retrieval_req_and_fsnc_t *p = (sl_retrieval_req_and_fsnc_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_retrieval_request_and_fsnc(sl, p->sl_fsnc);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_RESUME_REQ
 *  ---------------------------------------------
 */
static int
sl_resume_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_resume_req_t *p = (sl_resume_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_resume(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_CLEAR_BUFFERS_REQ
 *  ---------------------------------------------
 */
static int
sl_clear_buffers_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_clear_buffers_req_t *p = (sl_clear_buffers_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_clear_buffers(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_CLEAR_RTB_REQ
 *  ---------------------------------------------
 */
static int
sl_clear_rtb_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_clear_rtb_req_t *p = (sl_clear_rtb_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_clear_rtb(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_LOCAL_PROCESSOR_OUTAGE_REQ
 *  ---------------------------------------------
 */
static int
sl_local_processor_outage_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_local_proc_outage_req_t *p = (sl_local_proc_outage_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_local_processor_outage(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_CONGESTION_DISCARD_REQ
 *  ---------------------------------------------
 */
static int
sl_congestion_discard_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_cong_discard_req_t *p = (sl_cong_discard_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_congestion_discard(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_CONGESTION_ACCEPT_REQ
 *  ---------------------------------------------
 */
static int
sl_congestion_accept_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_cong_accept_req_t *p = (sl_cong_accept_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_congestion_accept(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_NO_CONGESTION_REQ
 *  ---------------------------------------------
 */
static int
sl_no_congestion_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_no_cong_req_t *p = (sl_no_cong_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_no_congestion(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  SL_POWER_ON_REQ
 *  ---------------------------------------------
 */
static int
sl_power_on_req(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sl_power_on_req_t *p = (sl_power_on_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
		return sl_lsc_power_on(sl);
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return m_error_reply(sl, err);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  NPI Provider -> NPI User (M2PA) Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  N_DATA_IND
 *  ---------------------------------------------
 */
static int
n_data_ind(sl_t *sl, mblk_t *mp)
{
	int err;
	mblk_t *dp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_data_ind_t *p = (N_data_ind_t *)mp->b_rptr;
	ensure( mlen >= sizeof(*p), return(-EFAULT) );
	if ( (dp = mp->b_cont) ) {
	if ( sl->i_state == LMI_ENABLED ) {
		if ( (err = sl_recv_msg(sl, dp)) )
			return(err);
		mp->b_cont = NULL; /* absorbed data */
		return(0);
	} rare(); return(-EPROTO);	/* ignore data in other states	*/
	} rare(); return(-EFAULT);
}
/*
 *  N_EXDATA_IND
 *  ---------------------------------------------
 */
static int
n_exdata_ind(sl_t *sl, mblk_t *mp)
{
	int err;
	mblk_t *dp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_exdata_ind_t *p = (N_exdata_ind_t *)mp->b_rptr;
	ensure( mlen >= sizeof(*p), return(-EFAULT) );
	if ( (dp = mp->b_cont) ) {
	if ( sl->i_state == LMI_ENABLED ) {
		if ( (err = sl_recv_msg(sl, dp)) )
			return(err);
		mp->b_cont = NULL; /* absorbed data */
		return(0);
	} rare(); return(-EPROTO);	/* ignore data in other states */
	} rare(); return(-EFAULT);
}
/*
 *  N_DATACK_IND
 *  ---------------------------------------------
 */
static int
n_datack_ind(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_datack_ind_t *p = (N_datack_ind_t *)mp->b_rptr;
	ensure( mlen >= sizeof(*p), return(-EFAULT) );
	if ( sl->version < M2PA_VERSION_DRAFT4 ) {
		if ( (err = sl_txc_datack(sl, 1)) )
			return(err);
		return(0);
	} rare(); return(-EFAULT);	/* shouldn't be getting these	*/
}
/*
 *  N_DISCON_IND
 *  ---------------------------------------------
 */
static int
n_discon_ind(sl_t *sl, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_discon_ind_t *p = (N_discon_ind_t *)mp->b_rptr;
	ensure( mlen >= sizeof(*p), return(-EFAULT) );
	if ( (err = sl_lsc_out_of_service(sl, SL_FAIL_SUERM_EIM)) )
		return(err);
	if ( sl->notify.events & SL_EVT_FAIL_SUERM_EIM )
		if ( (err = lmi_event_ind(sl, SL_EVT_FAIL_SUERM_EIM, 0, NULL, 0)) )
			return(err);
	ptrace(("Link failed: SUERM/EIM\n"));
	fixme(("Do something here... send a HANGUP or something...\n"));
	return(0);
}
/*
 *  N_RESET_IND
 *  ---------------------------------------------
 */
static int
n_reset_ind(sl_t *sl, mblk_t *mp)
{
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_reset_ind_t *p = (N_reset_ind_t *)mp->b_rptr;
	ensure( mlen >= sizeof(*p), return(-EFAULT) );
	fixme(("Do something here...\n"));
	return(-EFAULT);
}
/*
 *  Others...
 *  ---------------------------------------------
 */
static int
n_other_ind(sl_t *sl, mblk_t *mp)
{
	(void)sl; (void)mp;
	fixme(("Do something here...\n"));
	return(-EFAULT);
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static int
sl_w_ioctl(queue_t *q, mblk_t *mp)
{
	sl_t *sl = (sl_t *)q->q_ptr;
	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
	void *arg = mp->b_cont?mp->b_cont->b_rptr:NULL;
	int cmd = iocp->ioc_cmd, count = iocp->ioc_count;
	struct linkblk *lp = (struct linkblk *)arg;
	int ret	    = 0;
	int type    = _IOC_TYPE(cmd);
	int nr	    = _IOC_NR(cmd);
	int size    = _IOC_SIZE(cmd);
	
	(void)nr;
	switch ( type )
	{
	case __SID:
		switch ( cmd )
		{
			default:
				ptrace(("Unknown IOCTL %x\n",cmd));
			case I_STR:
			case I_LINK:
			case I_PLINK:
			case I_UNLINK:
			case I_PUNLINK:
				rare();
				(void)lp;
				ret = -EINVAL;
				break;
		}
		ret = -EOPNOTSUPP;
		break;
	case  SL_IOC_MAGIC:
		if ( count >= size )
		{
			switch ( cmd )
			{
				case SL_IOCGOPTIONS:	/* lmi_option_t	*/
					*(lmi_option_t *)arg = sl->option;
					break;
				case SL_IOCSOPTIONS:	/* lmi_option_t	*/
					fixme(("Check these first.\n"));
					sl->option = *(lmi_option_t *)arg;
					break;
				case SL_IOCGCONFIG :	/*  sl_config_t	*/
					*(sl_config_t *)arg = sl->config;
					break;
				case SL_IOCSCONFIG :	/*  sl_config_t	*/
					fixme(("Check these first.\n"));
					sl->config = *(sl_config_t *)arg ;
					break;
				case SL_IOCTCONFIG :	/*  sl_config_t	*/
				case SL_IOCCCONFIG :	/*  sl_config_t	*/
				case SL_IOCGSTATEM :	/*  sl_statem_t	*/
				case SL_IOCCMRESET :	/*  sl_statem_t	*/
				case SL_IOCGSTATSP :	/*    lmi_sta_t	*/
				case SL_IOCSSTATSP :	/*    lmi_sta_t	*/
				case SL_IOCGSTATS  :	/*   sl_stats_t	*/
				case SL_IOCCSTATS  :	/*   sl_stats_t	*/
				case SL_IOCGNOTIFY :	/*  sl_notify_t	*/
				case SL_IOCSNOTIFY :	/*  sl_notify_t	*/
				case SL_IOCCNOTIFY :	/*  sl_notify_t	*/
				default:
					ret = -EOPNOTSUPP;
					break;
			}
		}
		else
			ret = -EINVAL;
		break;
	case SDT_IOC_MAGIC:
		if ( count >= size )
		{
			switch ( cmd )
			{
				case SDT_IOCGOPTIONS:	/* lmi_option_t	*/
					*(lmi_option_t *)arg = sl->option;
					break;
				case SDT_IOCSOPTIONS:	/* lmi_option_t	*/
					fixme(("Check these first.\n"));
					sl->option = *(lmi_option_t *)arg;
					break;
				case SDT_IOCGCONFIG:	/* sdt_config_t	*/
					*(sdt_config_t *)arg = sl->sdt_conf;
					break;
				case SDT_IOCSCONFIG:	/* sdt_config_t	*/
					fixme(("Check these first.\n"));
					sl->sdt_conf = *(sdt_config_t *)arg ;
					break;
				case SDT_IOCTCONFIG:	/* sdt_config_t	*/
				case SDT_IOCCCONFIG:	/* sdt_config_t	*/
				case SDT_IOCGSTATEM:	/* sdt_statem_t	*/
				case SDT_IOCCMRESET:	/* sdt_statem_t	*/
				case SDT_IOCGSTATSP:	/*    lmi_sta_t	*/
				case SDT_IOCSSTATSP:	/*    lmi_sta_t	*/
				case SDT_IOCGSTATS:	/*  sdt_stats_t	*/
				case SDT_IOCCSTATS:	/*  sdt_stats_t	*/
				case SDT_IOCGNOTIFY:	/* sdt_notify_t	*/
				case SDT_IOCSNOTIFY:	/* sdt_notify_t	*/
				case SDT_IOCCNOTIFY:	/* sdt_notify_t	*/
				default:
					ret = -EOPNOTSUPP;
					break;
			}
		}
		else
			ret = -EINVAL;
		break;
	case SDL_IOC_MAGIC:
		if ( count >= size )
		{
			switch ( cmd )
			{
				case SDL_IOCGOPTIONS:	/* lmi_option_t	*/
					*(lmi_option_t *)arg = sl->option;
					break;
				case SDL_IOCSOPTIONS:	/* lmi_option_t	*/
					fixme(("Check the values first.\n"));
					sl->option = *(lmi_option_t *)arg;
					break;
				case SDL_IOCGCONFIG:	/* sdl_config_t	*/
					*(sdl_config_t *)arg = sl->sdl_conf;
					break;
				case SDL_IOCSCONFIG:	/* sdl_config_t	*/
					fixme(("Check the values first.\n"));
					sl->sdl_conf = *(sdl_config_t *)arg;
					break;
				case SDL_IOCTCONFIG:	/* sdl_config_t	*/
				case SDL_IOCCCONFIG:	/* sdl_config_t	*/
				case SDL_IOCGSTATEM:	/* sdl_statem_t	*/
				case SDL_IOCCMRESET:	/* sdl_statem_t	*/
				case SDL_IOCGSTATSP:	/*  sdl_stats_t	*/
				case SDL_IOCSSTATSP:	/*  sdl_stats_t	*/
				case SDL_IOCGSTATS:	/*  sdl_stats_t	*/
				case SDL_IOCCSTATS:	/*  sdl_stats_t	*/
				case SDL_IOCGNOTIFY:	/* sdl_notify_t	*/
				case SDL_IOCSNOTIFY:	/* sdl_notify_t	*/
				case SDL_IOCCNOTIFY:	/* sdl_notify_t	*/
				default:
					ret = -EOPNOTSUPP;
					break;
			}
		}
		else
			ret = -EINVAL;
		break;
	case DEV_IOC_MAGIC:
		if ( count >= size )
		{
			switch ( cmd )
			{
				case DEV_IOCGIFCLOCK:	/* dev_ulong	*/
					*(dev_ulong *)arg = sl->dev_conf.ifclock;
					break;
				case DEV_IOCSIFCLOCK:	/* dev_ulong	*/
					sl->dev_conf.ifclock = *(dev_ulong *)arg;
					break;
				case DEV_IOCCIFRESET:	/*		*/
				case DEV_IOCGIFFLAGS:	/* dev_ulong	*/
				case DEV_IOCSIFFLAGS:	/* dev_ulong	*/
				case DEV_IOCGIFTYPE:	/* dev_ulong	*/
				case DEV_IOCSIFTYPE:	/* dev_ulong	*/
				case DEV_IOCGGRPTYPE:	/* dev_ulong	*/
				case DEV_IOCSGRPTYPE:	/* dev_ulong	*/
				case DEV_IOCGIFMODE:	/* dev_ulong	*/
				case DEV_IOCSIFMODE:	/* dev_ulong	*/
				case DEV_IOCGIFRATE:	/* dev_ulong	*/
				case DEV_IOCSIFRATE:	/* dev_ulong	*/
				case DEV_IOCGIFCODING:	/* dev_ulong	*/
				case DEV_IOCSIFCODING:	/* dev_ulong	*/
				case DEV_IOCGIFLEADS:	/* dev_ulong	*/
				case DEV_IOCSIFLEADS:	/* dev_ulong	*/
				case DEV_IOCCIFLEADS:	/* dev_ulong	*/
				case DEV_IOCCDISCTX:	/*		*/
				case DEV_IOCCCONNTX:	/*		*/
				default:
					ret = -EOPNOTSUPP;
					break;
			}
		}
		else
			ret = -EINVAL;
		break;
	default:
		if ( q->q_next )
		{
			putnext(q, mp);
			return(1);
		}
		ret = -EOPNOTSUPP;
		break;
	}
	if ( ret >= 0 )
	{
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval	= 0;
	}
	else
	{
		seldom();
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval	= -1;
	}
	qreply(q, mp);
	return(1);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
static int
sl_w_proto(queue_t *q, mblk_t *mp)
{
	int rtn;
	ulong prim;
	sl_t *sl = (sl_t *)q->q_ptr;
	ulong oldstate = sl->i_state;
	switch ( (prim = *((ulong *)mp->b_rptr)) )
	{
		case LMI_INFO_REQ:			rtn = lmi_info_req			(sl, mp); break;
		case LMI_ATTACH_REQ:			rtn = lmi_attach_req			(sl, mp); break;
		case LMI_DETACH_REQ:			rtn = lmi_detach_req			(sl, mp); break;
		case LMI_ENABLE_REQ:			rtn = lmi_enable_req			(sl, mp); break;
		case LMI_DISABLE_REQ:			rtn = lmi_disable_req			(sl, mp); break;
		case LMI_OPTMGMT_REQ:			rtn = lmi_optmgmt_req			(sl, mp); break;
		case SL_PDU_REQ:			rtn = sl_pdu_req			(sl, mp); break;
		case SL_EMERGENCY_REQ:			rtn = sl_emergency_req			(sl, mp); break;
		case SL_EMERGENCY_CEASES_REQ:		rtn = sl_emergency_ceases_req		(sl, mp); break;
		case SL_START_REQ:			rtn = sl_start_req			(sl, mp); break;
		case SL_STOP_REQ:			rtn = sl_stop_req			(sl, mp); break;
		case SL_RETRIEVE_BSNT_REQ:		rtn = sl_retrieve_bsnt_req		(sl, mp); break;
		case SL_RETRIEVAL_REQUEST_AND_FSNC_REQ:	rtn = sl_retrieval_request_and_fsnc_req	(sl, mp); break;
		case SL_RESUME_REQ:			rtn = sl_resume_req			(sl, mp); break;
		case SL_CLEAR_BUFFERS_REQ:		rtn = sl_clear_buffers_req		(sl, mp); break;
		case SL_CLEAR_RTB_REQ:			rtn = sl_clear_rtb_req			(sl, mp); break;
		case SL_LOCAL_PROCESSOR_OUTAGE_REQ:	rtn = sl_local_processor_outage_req	(sl, mp); break;
		case SL_CONGESTION_DISCARD_REQ:		rtn = sl_congestion_discard_req		(sl, mp); break;
		case SL_CONGESTION_ACCEPT_REQ:		rtn = sl_congestion_accept_req		(sl, mp); break;
		case SL_NO_CONGESTION_REQ:		rtn = sl_no_congestion_req		(sl, mp); break;
		case SL_POWER_ON_REQ:			rtn = sl_power_on_req			(sl, mp); break;
		default:				rtn = -EOPNOTSUPP;				  break;
	}
	if ( rtn < 0 ) { seldom(); sl->i_state = oldstate; }
	return(rtn);
}
static int
sl_r_proto(sl_t *sl, mblk_t *mp)
{
	int rtn;
	ulong prim;
	sl_t *sl = (sl_t *)q->q_ptr;
	switch ( (prim = *((ulong *)mp->b-rptr)) )
	{
		case N_DATA_IND:	rtn = n_data_ind	(sl, mp); break;
		case N_EXDATA_IND:	rtn = n_exdata_ind	(sl, mp); break;
		case N_DATACK_IND:	rtn = n_datack_ind	(sl, mp); break;
		case N_DISCON_IND:	rtn = n_discon_ind	(sl, mp); break;
		case N_RESET_IND:
		case N_CONN_IND:
		case N_CONN_RES:
		case N_INFO_ACK:
		case N_BIND_ACK:
		case N_OK_ACK:
		case N_ERROR_ACK:
		case N_RESET_CON:	rtn = n_other_ind	(sl, mp); break;
		default:		rtn = -EOPNOTSUPP;		  break;
	}
	if ( rtn < 0 ) { seldom(); }
	return(rtn);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static int
sl_w_data(queue_t *q, mblk_t *mp)
{
	int err;
	sl_t *sl = (sl_t *)q->q_ptr;
	if ( (err = sl_lsc_pdu(sl, mp)) )
		return(err);
	return(1);  /* data absorbed */
}
static int
sl_r_data(queue_t *q, mblk_t *mp)
{
	int err;
	sl_t *sl = (sl_t *)q->q_ptr;
	if ( (err = sl_recv_msg(sl, mp)) )
		return(err);
	return(1);  /* data absorbed */
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling (Events)
 *
 *  -------------------------------------------------------------------------
 */
static int
sl_r_ctl(queue_t *q, mblk_t *mp)
{
	int rtn;
	sl_t *sl = (sl_t *)q->q_ptr;
	switch ( *mp->b_rptr )
	{
		case SL_EVENT_T1_EXPIRY:	rtn = sl_t1_expiry(sl);	break;
		case SL_EVENT_T2_EXPIRY:	rtn = sl_t2_expiry(sl);	break;
		case SL_EVENT_T3_EXPIRY:	rtn = sl_t3_expiry(sl);	break;
		case SL_EVENT_T4_EXPIRY:	rtn = sl_t4_expiry(sl);	break;
#if 0
		case SL_EVENT_T5_EXPIRY:	rtn = sl_t5_expiry(sl);	break;
#endif
		case SL_EVENT_T6_EXPIRY:	rtn = sl_t6_expiry(sl);	break;
		case SL_EVENT_T7_EXPIRY:	rtn = sl_t7_expiry(sl);	break;
#if 0
		case SL_EVENT_T8_EXPIRY:	rtn = sl_t8_expiry(sl);	break;
		case SL_EVENT_T9_EXPIRY:	rtn = sl_t9_expiry(sl);	break;
#endif
		default:			rtn = -EFAULT;		break;
	}
	if ( rtn < 0 ) { rare(); }
	return(rtn);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
static int
sl_r_error(queue_t *q, mblk_t *mp)
{
	putnext(q, mp);
	return(1);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static int
sl_m_flush(queue_t *q, mblk_t *mp, const uint8_t mflag)
{
	if ( *mp->b_rptr & mflag )
	{
		if ( *mp->b_rptr & FLUSHBAND )
			flushband(q, mp->b_rptr[1], FLUSHALL);
		else	flushq(q, FLUSHALL);
	}
	putnext(q, mp);
	return(1);
}
static int
sl_w_flush(queue_t *q, mblk_t *mp)
{
	return sl_m_flush(q, mp, FLUSHW);
}
static int
sl_r_flush(queue_t *q, mblk_t *mp)
{
	return sl_m_flush(q, mp, FLUSHR);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  Other messages (e.g. M_IOCACK)
 *
 *  -------------------------------------------------------------------------
 */
static int
sl_m_other(queue_t *q, mblk_t *mp)
{
	rare();
	if ( mp->b_datap->db_type >= QPCTL || bcanputnext(q, mp->b_band) )
	{
		putnext(q, mp);
		return(1);
	}
	else return(-EBUSY);
}

/*
 *  =========================================================================
 *
 *  M2PA-SL Private Structure
 *
 *  =========================================================================
 *
 *  WPUT - Message from above on Management Stream or Normal Stream
 */
static INT
sl_wput(queue_t *q, mblk_t *mp)
{
	int rtn;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( mp, return((INT)(-EFAULT)) );
	if ( q != sl_ctrlq )
	{
		switch ( mp->b_datap->db_type )
		{
			case M_DATA:	rtn = sl_u_data    (q, mp); break;
			case M_PROTO:
			case M_PCPROTO:	rtn = sl_u_proto   (q, mp); break;
			case M_FLUSH:	rtn = sl_u_flush   (q, mp); break;
			case M_IOCTL:	rtn = sl_u_ioctl   (q, mp); break;
			case M_IOCDATA:	rtn = sl_u_iocdata (q, mp); break;
			default:	rtn = sl_u_other   (q, mp); break;
		}
		switch ( rtn )
		{
			case 0:	freemsg(mp);
			case 1:	break;
			case 2:	freeb(mp);
				break;
			default:
				rare();
				freemsg(mp);
				sl_tx_wakeup(q);
				return(INT)(rtn);
		}
		sl_tx_wakeup(q);
		return(INT)(0);
	}
	else
	{
		switch ( mp->b_datap->db_type )
		{
			case M_DATA:	rtn = sl_m_data    (q, mp); break;
			case M_PROTO:
			case M_PCPROTO:	rtn = sl_m_proto   (q, mp); break;
			case M_FLUSH:	rtn = sl_m_flush   (q, mp); break;
			case M_IOCTL:	rtn = sl_m_ioctl   (q, mp); break;
			case M_IOCDATA:	rtn = sl_m_iocdata (q, mp); break;
			default:	rtn = sl_m_other   (q, mp); break;
		}
		switch ( rtn )
		{
			case 0:	freemsg(mp);
			case 1:	break;
			case 2:	freeb(mp);
				break;
			default:
				rare();
				freemsg(mp);
				return(INT)(rtn);
		}
		return(INT)(0);
	}
}
/*
 *  Lower PUT and SRV routines.
 */
static INT
sl_l_wput(queue_t *q, mblk_t *mp)
{
	int rtn;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( mp, return((INT)(-EFAULT)) );
	if ( mp->b_datap->db_type < QPCTL && q->q_count )
	{
		seldom();
		putq(q, mp);
		return(INT)(0);
	}
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:	rtn = sl_w_data	    (q, mp); break;
		case M_PROTO:
		case M_PCPROTO:	rtn = sl_w_proto    (q, mp); break;
		case M_FLUSH:	rtn = sl_w_flush    (q, mp); break;
		case M_IOCTL:	rtn = sl_w_ioctl    (q, mp); break;
		case M_IOCDATA:	rtn = sl_w_iocdata  (q, mp); break;
		default:	rtn = sl_m_other    (q, mp); break;
	}
	switch ( rtn )
	{
		case 0:	assert(mp->b_datap->db_ref);
			freemsg(mp);
		case 1: break;
		case 2: assert(mp->b_datap->db_ref);
			freeb(mp);
			break;
		case -ENOBUFS:
			/* should set up bufcall */
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
			seldom();
			putq(q, mp);
			sl_tx_wakeup(q);
			return(INT)(0);
		default:
			rare();
			ptrace(("Error = %d\n", rtn));
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			sl_tx_wakeup(q);
			return(INT)(rtn);
	}
	sl_tx_wakeup(q);
	return(INT)(0);
}
static INT
sl_wsrv(queue_t *q)
{
	int rtn;
	mblk_t *mp;
	ensure( q,  return((INT)(-EFAULT)) );
	while ( (mp = getq(q)) )
	{
		switch ( mp->b_datap->db_type )
		{
			case M_DATA:	rtn = sl_w_data (q, mp); break;
			case M_PROTO:
			case M_PCPROTO:	rtn = sl_w_proto(q, mp); break;
			case M_FLUSH:	rtn = sl_w_flush(q, mp); break;
			case M_IOCTL:	rtn = sl_w_ioctl(q, mp); break;
			default:	rtn = sl_m_other(q, mp); break;
		}
		switch ( rtn )
		{
			case 0: assert(mp->b_datap->db_ref);
				freemsg(mp);
			case 1:	continue;
			case 2: assert(mp->b_datap->db_ref);
				freeb(mp);
				continue;
			case -ENOBUFS:
				/* should set up bufcall */
			case -EBUSY:
			case -EAGAIN:
			case -ENOMEM:
				seldom();
				if ( mp->b_datap->db_type < QPCTL )
				{
					putbq(q, mp);
					sl_tx_wakeup(q);
					return(INT)(0);
				}
			default:
				rare();
				ptrace(("Error = %d\n", rtn));
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				continue;
		}
	}
	sl_tx_wakeup(q);
	return(INT)(0);
}

/*
 *  SCTP Read Put and Service
 */
static INT
sl_l_rput(queue_t *q, mblk_t *mp)
{
	int rtn = 0;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( mp, return((INT)(-EFAULT)) );
	if ( mp->b_datap->db_type < QPCTL && q->q_count )
	{
		seldom();
		putq(q, mp);
		return(INT)(0);
	}
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:	rtn = sl_r_data (q, mp); break;
		case M_PROTO:
		case M_PCPROTO:	rtn = sl_r_proto(q, mp); break;
		case M_CTL:	rtn = sl_r_ctl  (q, mp); break;
		case M_ERROR:	rtn = sl_r_error(q, mp); break;
		case M_FLUSH:	rtn = sl_r_flush(q, mp); break;
		default:	rtn = sl_m_other(q, mp); break;
	}
	switch ( rtn )
	{
		case 0: assert(mp->b_datap->db_ref);
			freemsg(mp);
		case 1: break;
		case 2: assert(mp->b_datap->db_ref);
			freeb(mp);
			break;
		case -ENOBUFS:
			/* should set up bufcall */
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
			seldom();
			putq(q, mp);
			sl_rx_wakeup(q);
			sl_tx_wakeup(q);
			return(INT)(0);
		default:
			rare();
			ptrace(("Error = %d\n", rtn));
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			sl_rx_wakeup(q);
			sl_tx_wakeup(q);
			return(INT)(rtn);
	}
	sl_rx_wakeup(q);
	sl_tx_wakeup(q);
	return(INT)(0);
}
static INT
sl_l_rsrv(queue_t *q)
{
	int rtn = 0;
	mblk_t *mp;
	ensure( q,  return((INT)(-EFAULT)) );
	while ( (mp = getq(q)) )
	{
		switch ( mp->b_datap->db_type )
		{
			case M_DATA:	rtn = sl_r_data (q, mp); break;
			case M_PROTO:
			case M_PCPROTO:	rtn = sl_r_proto(q, mp); break;
			case M_CTL:	rtn = sl_r_ctl  (q, mp); break;
			case M_ERROR:	rtn = sl_r_error(q, mp); break;
			case M_FLUSH:	rtn = sl_r_flush(q, mp); break;
			default:	rtn = sl_m_other(q, mp); break;
		}
		switch ( rtn )
		{
			case 0: assert(mp->b_datap->db_ref);
				freemsg(mp);
			case 1: continue;
			case 2: assert(mp->b_datap->db_ref);
				freeb(mp);
				continue;
			case -ENOBUFS:
				/* should set up bufcall */
			case -EBUSY:
			case -EAGAIN:
			case -ENOMEM:
				seldom();
				if ( mp->b_datap->db_type < QPCTL )
				{
					putbq(q, mp);
					sl_rx_wakeup(q);
					sl_tx_wakeup(q);
					return(INT)(0);
				}
			default:
				rare();
				ptrace(("Error = %d\n", rtn));
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				continue;
		}
	}
	sl_rx_wakeup(q);
	sl_tx_wakeup(q);
	return(INT)(0);
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
static int
sl_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);
	sl_t *sl, **slp = &sl_list;
	if ( q->q_ptr != NULL )	    /* already open */
		return(0);
	if ( sflag == MODOPEN || WR(q)->q_next ) {
		rare();
		return(EIO);	    /* can't open as module */
	}
	if ( !cminor )
	{
		if ( sl_ctrlq || crp->cr_uid != 0 )
			sflag = CLONEOPEN;
		else
		{
			sl_ctrlq = q;
			return(0);
		}
	}
	if ( sflag == CLONEOPEN )
		cminor = 1;
	for ( ; *slp && (*slp)->cmajor < cmajor;   slp = &(*slp)->next );
	for ( ; *slp && cminor <= M2PA_SLM_NMINOR; slp = &(*slp)->next )
	{
		ushort dminor = (*slp)->cminor;
		if ( cminor <  dminor ) break;
		if ( cminor == dminor )
		{
			if ( sflag != CLONEOPEN ) {
				rare();
				return(ENXIO);	/* requested device in use */
			}
			cminor++;
		}
	}
	if ( cminor > M2PA_SLM_NMINOR ) {
		rare();
		return(ENXIO);
	}
	*devp = makedevice(cmajor, cminor);
	if ( !(sl = sl_alloc_priv(q, slp, cmajor, cminor)) ) {
		rare();
		return(ENOMEM);
	}
	return(0);
}
static int
sl_close(queue_t *q, int flag, cred_t *crp)
{
	(void)flag;
	(void)crp;

	sl_free_priv(q);
	return(0);
}
/*
 *  =========================================================================
 *
 *  Lis Module Initialization
 *
 *  =========================================================================
 */
void
sl_init(void)
{
	int cmajor;
	unless( sl_minfo.mi_idnum, return );
	cmn_err(CE_NOTE, M2PA_BANNER);	/* console splash */
	sl_init_caches();
	if ( !(cmajor = lis_register_strdev( M2PA_SLM_CMAJOR, &sl_info,
					M2PA_SLM_NMINOR, sl_minfo.mi_idname)) )
	{
		sl_minfo.mi_idnum = 0;
		rare();
		cmn_err(CE_NOTE, "m2pa-slm: couldn't register driver\n");
		return;
	}
	sl_minfo.mi_idnum = cmajor;
	return;
}
void
sl_terminate(void)
{
	ensure( sl_minfo.mi_idnum, return );
	if ( (sl_minfo.mi_idnum = lis_unregister_strdev(sl_minfo.mi_idnum)) )
	{
		sl_minfo.id_num = 0;
		rare();
		cmn_err(CE_WARN, "m2pa-slm: couldn't unregister driver!\n");
	}
	sl_term_caches();
	return;
}

/*
 *  =========================================================================
 *
 *  Kernel Module Initialization
 *
 *  =========================================================================
 */
int init_module(void)
{
	sl_init();
	return(0);
}
void cleanup_module(void)
{
	sl_terminate();
	return;
}
